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