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