dump: Revise whitespace usage
[jansson.git] / src / dump.c
1 /*
2  * Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
3  *
4  * Jansson is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7
8 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <jansson.h>
14 #include "jansson_private.h"
15 #include "strbuffer.h"
16
17 #define MAX_INTEGER_STR_LENGTH  100
18 #define MAX_REAL_STR_LENGTH     100
19
20 typedef int (*dump_func)(const char *buffer, int size, void *data);
21
22 struct string
23 {
24     char *buffer;
25     int length;
26     int size;
27 };
28
29 static int dump_to_strbuffer(const char *buffer, int size, void *data)
30 {
31     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
32 }
33
34 static int dump_to_file(const char *buffer, int size, void *data)
35 {
36     FILE *dest = (FILE *)data;
37     if(fwrite(buffer, size, 1, dest) != 1)
38         return -1;
39     return 0;
40 }
41
42 /* 256 spaces (the maximum indentation size) */
43 static char whitespace[] = "                                                                                                                                                                                                                                                                ";
44
45 static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
46 {
47     if(JSON_INDENT(flags) > 0)
48     {
49         int i, ws_count = JSON_INDENT(flags);
50
51         if(dump("\n", 1, data))
52             return -1;
53
54         for(i = 0; i < depth; i++)
55         {
56             if(dump(whitespace, ws_count, data))
57                 return -1;
58         }
59     }
60     else if(space && !(flags & JSON_COMPACT))
61     {
62         return dump(" ", 1, data);
63     }
64     return 0;
65 }
66
67 static int dump_string(const char *str, dump_func dump, void *data)
68 {
69     const char *end;
70
71     if(dump("\"", 1, data))
72         return -1;
73
74     end = str;
75     while(1)
76     {
77         const char *text;
78         char seq[7];
79         int length;
80
81         while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
82             end++;
83
84         if(end != str) {
85             if(dump(str, end - str, data))
86                 return -1;
87         }
88
89         if(!*end)
90             break;
91
92         /* handle \, ", and control codes */
93         length = 2;
94         switch(*end)
95         {
96             case '\\': text = "\\\\"; break;
97             case '\"': text = "\\\""; break;
98             case '\b': text = "\\b"; break;
99             case '\f': text = "\\f"; break;
100             case '\n': text = "\\n"; break;
101             case '\r': text = "\\r"; break;
102             case '\t': text = "\\t"; break;
103             default:
104             {
105                 sprintf(seq, "\\u00%02x", *end);
106                 text = seq;
107                 length = 6;
108                 break;
109             }
110         }
111
112         if(dump(text, length, data))
113             return -1;
114
115         end++;
116         str = end;
117     }
118
119     return dump("\"", 1, data);
120 }
121
122 static int do_dump(const json_t *json, unsigned long flags, int depth,
123                    dump_func dump, void *data)
124 {
125     switch(json_typeof(json)) {
126         case JSON_NULL:
127             return dump("null", 4, data);
128
129         case JSON_TRUE:
130             return dump("true", 4, data);
131
132         case JSON_FALSE:
133             return dump("false", 5, data);
134
135         case JSON_INTEGER:
136         {
137             char buffer[MAX_INTEGER_STR_LENGTH];
138             int size;
139
140             size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
141             if(size >= MAX_INTEGER_STR_LENGTH)
142                 return -1;
143
144             return dump(buffer, size, data);
145         }
146
147         case JSON_REAL:
148         {
149             char buffer[MAX_REAL_STR_LENGTH];
150             int size;
151
152             size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
153             if(size >= MAX_REAL_STR_LENGTH)
154                 return -1;
155
156             return dump(buffer, size, data);
157         }
158
159         case JSON_STRING:
160             return dump_string(json_string_value(json), dump, data);
161
162         case JSON_ARRAY:
163         {
164             int i;
165             int n;
166             json_array_t *array;
167
168             /* detect circular references */
169             array = json_to_array(json);
170             if(array->visited)
171                 return -1;
172             array->visited = 1;
173
174             n = json_array_size(json);
175
176             if(dump("[", 1, data))
177                 return -1;
178             if(n == 0)
179                 return dump("]", 1, data);
180             if(dump_indent(flags, depth + 1, 0, dump, data))
181                 return -1;
182
183             for(i = 0; i < n; ++i) {
184                 if(do_dump(json_array_get(json, i), flags, depth + 1,
185                            dump, data))
186                     return -1;
187
188                 if(i < n - 1)
189                 {
190                     if(dump(",", 1, data) ||
191                        dump_indent(flags, depth + 1, 1, dump, data))
192                         return -1;
193                 }
194                 else
195                 {
196                     if(dump_indent(flags, depth, 0, dump, data))
197                         return -1;
198                 }
199             }
200
201             array->visited = 0;
202             return dump("]", 1, data);
203         }
204
205         case JSON_OBJECT:
206         {
207             json_object_t *object;
208             void *iter;
209             const char *separator;
210             int separator_length;
211
212             if(flags & JSON_COMPACT) {
213                 separator = ":";
214                 separator_length = 1;
215             }
216             else {
217                 separator = ": ";
218                 separator_length = 2;
219             }
220
221             /* detect circular references */
222             object = json_to_object(json);
223             if(object->visited)
224                 return -1;
225             object->visited = 1;
226
227             iter = json_object_iter((json_t *)json);
228
229             if(dump("{", 1, data))
230                 return -1;
231             if(!iter)
232                 return dump("}", 1, data);
233             if(dump_indent(flags, depth + 1, 0, dump, data))
234                 return -1;
235
236             while(iter)
237             {
238                 void *next = json_object_iter_next((json_t *)json, iter);
239
240                 dump_string(json_object_iter_key(iter), dump, data);
241                 if(dump(separator, separator_length, data) ||
242                    do_dump(json_object_iter_value(iter), flags, depth + 1,
243                            dump, data))
244                     return -1;
245
246                 if(next)
247                 {
248                     if(dump(",", 1, data) ||
249                        dump_indent(flags, depth + 1, 1, dump, data))
250                         return -1;
251                 }
252                 else
253                 {
254                     if(dump_indent(flags, depth, 0, dump, data))
255                         return -1;
256                 }
257
258                 iter = next;
259             }
260
261             object->visited = 0;
262             return dump("}", 1, data);
263         }
264
265         default:
266             /* not reached */
267             return -1;
268     }
269 }
270
271
272 char *json_dumps(const json_t *json, unsigned long flags)
273 {
274     strbuffer_t strbuff;
275     char *result;
276
277     if(!json_is_array(json) && !json_is_object(json))
278         return NULL;
279
280     if(strbuffer_init(&strbuff))
281         return NULL;
282
283     if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
284         strbuffer_close(&strbuff);
285         return NULL;
286     }
287
288     result = strdup(strbuffer_value(&strbuff));
289     strbuffer_close(&strbuff);
290
291     return result;
292 }
293
294 int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
295 {
296     if(!json_is_array(json) && !json_is_object(json))
297         return -1;
298
299     return do_dump(json, flags, 0, dump_to_file, (void *)output);
300 }
301
302 int json_dump_file(const json_t *json, const char *path, unsigned long flags)
303 {
304     int result;
305
306     FILE *output = fopen(path, "w");
307     if(!output)
308         return -1;
309
310     result = json_dumpf(json, output, flags);
311
312     fclose(output);
313     return result;
314 }