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