b8bd46013d0f0da66a94e845f4be33ba6ac6d628
[jansson.git] / src / dump.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6
7 #include <jansson.h>
8 #include "strbuffer.h"
9
10 typedef int (*dump_func)(const char *buffer, int size, void *data);
11
12 struct string
13 {
14     char *buffer;
15     int length;
16     int size;
17 };
18
19 static int dump_to_strbuffer(const char *buffer, int size, void *data)
20 {
21     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
22 }
23
24 static int dump_to_file(const char *buffer, int size, void *data)
25 {
26     FILE *dest = (FILE *)data;
27     if(fwrite(buffer, size, 1, dest) != 1)
28         return -1;
29     return 0;
30 }
31
32 static int dump_to_fd(const char *buffer, int size, void *data)
33 {
34     int *fd = (int *)data;
35     if(write(*fd, buffer, size) != size)
36         return -1;
37     return 0;
38 }
39
40 static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
41 {
42     if(JSON_INDENT(flags) > 0)
43     {
44         char *ws_buffer;
45         int ws_count = JSON_INDENT(flags) * depth;
46
47         if(dump("\n", 1, data))
48             return -1;
49
50         if(ws_count == 0)
51             return 0;
52
53         ws_buffer = alloca(ws_count);
54         memset(ws_buffer, ' ', ws_count);
55         return dump(ws_buffer, ws_count, data);
56     }
57     else
58         return dump(" ", 1, data);
59 }
60
61 static int dump_string(const char *str, dump_func dump, void *data)
62 {
63     const char *end;
64
65     if(dump("\"", 1, data))
66         return -1;
67
68     end = str;
69     while(1)
70     {
71         const char *text;
72         char seq[7];
73         int length;
74
75         while(*end && *end != '\\' && *end != '"' && (*end < 0 || *end > 0x1F))
76             end++;
77
78         if(end != str) {
79             if(dump(str, end - str, data))
80                 return -1;
81         }
82
83         if(!*end)
84             break;
85
86         /* handle \, ", and control codes */
87         length = 2;
88         switch(*end)
89         {
90             case '\\': text = "\\\\"; break;
91             case '\"': text = "\\\""; break;
92             case '\b': text = "\\b"; break;
93             case '\f': text = "\\f"; break;
94             case '\n': text = "\\n"; break;
95             case '\r': text = "\\r"; break;
96             case '\t': text = "\\t"; break;
97             default:
98             {
99                 sprintf(seq, "\\u00%02x", *end);
100                 text = seq;
101                 length = 6;
102                 break;
103             }
104         }
105
106         if(dump(text, length, data))
107             return -1;
108
109         end++;
110         str = end;
111     }
112
113     return dump("\"", 1, data);
114 }
115
116 static int do_dump(const json_t *json, uint32_t flags, int depth,
117                    dump_func dump, void *data)
118 {
119     switch(json_typeof(json)) {
120         case JSON_NULL:
121             return dump("null", 4, data);
122
123         case JSON_TRUE:
124             return dump("true", 4, data);
125
126         case JSON_FALSE:
127             return dump("false", 5, data);
128
129         case JSON_INTEGER:
130         {
131             char *buffer;
132             int size, ret;
133
134             size = asprintf(&buffer, "%d", json_integer_value(json));
135             if(size == -1)
136                 return -1;
137
138             ret = dump(buffer, size, data);
139             free(buffer);
140             return ret;
141         }
142
143         case JSON_REAL:
144         {
145             char *buffer;
146             int size, ret;
147
148             size = asprintf(&buffer, "%.17f", json_real_value(json));
149             if(size == -1)
150                 return -1;
151
152             ret = dump(buffer, size, data);
153             free(buffer);
154             return ret;
155         }
156
157         case JSON_STRING:
158             return dump_string(json_string_value(json), dump, data);
159
160         case JSON_ARRAY:
161         {
162             int i;
163             int n = json_array_size(json);
164
165             if(dump("[", 1, data))
166                 return -1;
167             if(n == 0)
168                 return dump("]", 1, data);
169             if(dump_indent(flags, depth + 1, dump, data))
170                 return -1;
171
172             for(i = 0; i < n; ++i) {
173                 if(do_dump(json_array_get(json, i), flags, depth + 1,
174                            dump, data))
175                     return -1;
176
177                 if(i < n - 1)
178                 {
179                     if(dump(",", 1, data) ||
180                        dump_indent(flags, depth + 1, dump, data))
181                         return -1;
182                 }
183                 else
184                 {
185                     if(dump_indent(flags, depth, dump, data))
186                         return -1;
187                 }
188             }
189             return dump("]", 1, data);
190         }
191
192         case JSON_OBJECT:
193         {
194             void *iter = json_object_iter((json_t *)json);
195
196             if(dump("{", 1, data))
197                 return -1;
198             if(!iter)
199                 return dump("}", 1, data);
200             if(dump_indent(flags, depth + 1, dump, data))
201                 return -1;
202
203             while(iter)
204             {
205                 void *next = json_object_iter_next((json_t *)json, iter);
206
207                 dump_string(json_object_iter_key(iter), dump, data);
208                 if(dump(": ", 2, data) ||
209                    do_dump(json_object_iter_value(iter), flags, depth + 1,
210                            dump, data))
211                     return -1;
212
213                 if(next)
214                 {
215                     if(dump(",", 1, data) ||
216                        dump_indent(flags, depth + 1, dump, data))
217                         return -1;
218                 }
219                 else
220                 {
221                     if(dump_indent(flags, depth, dump, data))
222                         return -1;
223                 }
224
225                 iter = next;
226             }
227             return dump("}", 1, data);
228         }
229
230         default:
231             /* not reached */
232             return -1;
233     }
234 }
235
236
237 int json_dump(const json_t *json, const char *path, uint32_t flags)
238 {
239     FILE *output = fopen(path, "w");
240     if(!output)
241         return -1;
242
243     return json_dumpf(json, output, flags);
244 }
245
246 char *json_dumps(const json_t *json, uint32_t flags)
247 {
248     strbuffer_t strbuff;
249     char *result;
250
251     if(strbuffer_init(&strbuff))
252       return NULL;
253
254     if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
255         return NULL;
256
257     if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
258         return NULL;
259
260     result = strdup(strbuffer_value(&strbuff));
261     strbuffer_close(&strbuff);
262
263     return result;
264 }
265
266 int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
267 {
268     if(do_dump(json, flags, 0, dump_to_file, (void *)output))
269         return -1;
270     return dump_to_file("\n", 1, (void *)output);
271 }
272
273 int json_dumpfd(const json_t *json, int fd, uint32_t flags)
274 {
275     if(do_dump(json, flags, 0, dump_to_fd, (void *)&fd))
276         return -1;
277     return dump_to_fd("\n", 1, (void *)&fd);
278 }