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