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