blob: 6046dcab51cc538b679428def22ff377a6fada7c [file] [log] [blame]
Thomas Gleixner64cf5482019-01-18 00:14:24 +01001// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
Quentin Monnetb66e9072017-10-23 09:24:05 -07002/*
3 * Simple streaming JSON writer
4 *
5 * This takes care of the annoying bits of JSON syntax like the commas
6 * after elements
7 *
Quentin Monnetb66e9072017-10-23 09:24:05 -07008 * Authors: Stephen Hemminger <stephen@networkplumber.org>
9 */
10
11#include <stdio.h>
12#include <stdbool.h>
13#include <stdarg.h>
14#include <assert.h>
15#include <malloc.h>
16#include <inttypes.h>
17#include <stdint.h>
Quentin Monnetc1011892018-12-14 13:56:01 +000018#include <linux/compiler.h>
Quentin Monnetb66e9072017-10-23 09:24:05 -070019
20#include "json_writer.h"
21
22struct json_writer {
23 FILE *out; /* output file */
24 unsigned depth; /* nesting */
25 bool pretty; /* optional whitepace */
26 char sep; /* either nul or comma */
27};
28
29/* indentation for pretty print */
30static void jsonw_indent(json_writer_t *self)
31{
32 unsigned i;
33 for (i = 0; i < self->depth; ++i)
34 fputs(" ", self->out);
35}
36
37/* end current line and indent if pretty printing */
38static void jsonw_eol(json_writer_t *self)
39{
40 if (!self->pretty)
41 return;
42
43 putc('\n', self->out);
44 jsonw_indent(self);
45}
46
47/* If current object is not empty print a comma */
48static void jsonw_eor(json_writer_t *self)
49{
50 if (self->sep != '\0')
51 putc(self->sep, self->out);
52 self->sep = ',';
53}
54
55
56/* Output JSON encoded string */
57/* Handles C escapes, does not do Unicode */
58static void jsonw_puts(json_writer_t *self, const char *str)
59{
60 putc('"', self->out);
61 for (; *str; ++str)
62 switch (*str) {
63 case '\t':
64 fputs("\\t", self->out);
65 break;
66 case '\n':
67 fputs("\\n", self->out);
68 break;
69 case '\r':
70 fputs("\\r", self->out);
71 break;
72 case '\f':
73 fputs("\\f", self->out);
74 break;
75 case '\b':
76 fputs("\\b", self->out);
77 break;
78 case '\\':
79 fputs("\\n", self->out);
80 break;
81 case '"':
82 fputs("\\\"", self->out);
83 break;
84 case '\'':
85 fputs("\\\'", self->out);
86 break;
87 default:
88 putc(*str, self->out);
89 }
90 putc('"', self->out);
91}
92
93/* Create a new JSON stream */
94json_writer_t *jsonw_new(FILE *f)
95{
96 json_writer_t *self = malloc(sizeof(*self));
97 if (self) {
98 self->out = f;
99 self->depth = 0;
100 self->pretty = false;
101 self->sep = '\0';
102 }
103 return self;
104}
105
106/* End output to JSON stream */
107void jsonw_destroy(json_writer_t **self_p)
108{
109 json_writer_t *self = *self_p;
110
111 assert(self->depth == 0);
112 fputs("\n", self->out);
113 fflush(self->out);
114 free(self);
115 *self_p = NULL;
116}
117
118void jsonw_pretty(json_writer_t *self, bool on)
119{
120 self->pretty = on;
121}
122
123/* Basic blocks */
124static void jsonw_begin(json_writer_t *self, int c)
125{
126 jsonw_eor(self);
127 putc(c, self->out);
128 ++self->depth;
129 self->sep = '\0';
130}
131
132static void jsonw_end(json_writer_t *self, int c)
133{
134 assert(self->depth > 0);
135
136 --self->depth;
137 if (self->sep != '\0')
138 jsonw_eol(self);
139 putc(c, self->out);
140 self->sep = ',';
141}
142
143
144/* Add a JSON property name */
145void jsonw_name(json_writer_t *self, const char *name)
146{
147 jsonw_eor(self);
148 jsonw_eol(self);
149 self->sep = '\0';
150 jsonw_puts(self, name);
151 putc(':', self->out);
152 if (self->pretty)
153 putc(' ', self->out);
154}
155
Quentin Monnetc1011892018-12-14 13:56:01 +0000156void __printf(2, 0)
157jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
Quentin Monnetf05e2c32017-10-23 09:24:10 -0700158{
159 jsonw_eor(self);
160 putc('"', self->out);
161 vfprintf(self->out, fmt, ap);
162 putc('"', self->out);
163}
164
Quentin Monnetc1011892018-12-14 13:56:01 +0000165void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...)
Quentin Monnetb66e9072017-10-23 09:24:05 -0700166{
167 va_list ap;
168
169 va_start(ap, fmt);
170 jsonw_eor(self);
171 vfprintf(self->out, fmt, ap);
172 va_end(ap);
173}
174
175/* Collections */
176void jsonw_start_object(json_writer_t *self)
177{
178 jsonw_begin(self, '{');
179}
180
181void jsonw_end_object(json_writer_t *self)
182{
183 jsonw_end(self, '}');
184}
185
186void jsonw_start_array(json_writer_t *self)
187{
188 jsonw_begin(self, '[');
189}
190
191void jsonw_end_array(json_writer_t *self)
192{
193 jsonw_end(self, ']');
194}
195
196/* JSON value types */
197void jsonw_string(json_writer_t *self, const char *value)
198{
199 jsonw_eor(self);
200 jsonw_puts(self, value);
201}
202
203void jsonw_bool(json_writer_t *self, bool val)
204{
205 jsonw_printf(self, "%s", val ? "true" : "false");
206}
207
208void jsonw_null(json_writer_t *self)
209{
210 jsonw_printf(self, "null");
211}
212
213void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
214{
215 jsonw_printf(self, fmt, num);
216}
217
218#ifdef notused
219void jsonw_float(json_writer_t *self, double num)
220{
221 jsonw_printf(self, "%g", num);
222}
223#endif
224
225void jsonw_hu(json_writer_t *self, unsigned short num)
226{
227 jsonw_printf(self, "%hu", num);
228}
229
230void jsonw_uint(json_writer_t *self, uint64_t num)
231{
232 jsonw_printf(self, "%"PRIu64, num);
233}
234
235void jsonw_lluint(json_writer_t *self, unsigned long long int num)
236{
237 jsonw_printf(self, "%llu", num);
238}
239
240void jsonw_int(json_writer_t *self, int64_t num)
241{
242 jsonw_printf(self, "%"PRId64, num);
243}
244
245/* Basic name/value objects */
246void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
247{
248 jsonw_name(self, prop);
249 jsonw_string(self, val);
250}
251
252void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
253{
254 jsonw_name(self, prop);
255 jsonw_bool(self, val);
256}
257
258#ifdef notused
259void jsonw_float_field(json_writer_t *self, const char *prop, double val)
260{
261 jsonw_name(self, prop);
262 jsonw_float(self, val);
263}
264#endif
265
266void jsonw_float_field_fmt(json_writer_t *self,
267 const char *prop,
268 const char *fmt,
269 double val)
270{
271 jsonw_name(self, prop);
272 jsonw_float_fmt(self, fmt, val);
273}
274
275void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
276{
277 jsonw_name(self, prop);
278 jsonw_uint(self, num);
279}
280
281void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
282{
283 jsonw_name(self, prop);
284 jsonw_hu(self, num);
285}
286
287void jsonw_lluint_field(json_writer_t *self,
288 const char *prop,
289 unsigned long long int num)
290{
291 jsonw_name(self, prop);
292 jsonw_lluint(self, num);
293}
294
295void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
296{
297 jsonw_name(self, prop);
298 jsonw_int(self, num);
299}
300
301void jsonw_null_field(json_writer_t *self, const char *prop)
302{
303 jsonw_name(self, prop);
304 jsonw_null(self);
305}
306
307#ifdef TEST
308int main(int argc, char **argv)
309{
310 json_writer_t *wr = jsonw_new(stdout);
311
312 jsonw_start_object(wr);
313 jsonw_pretty(wr, true);
314 jsonw_name(wr, "Vyatta");
315 jsonw_start_object(wr);
316 jsonw_string_field(wr, "url", "http://vyatta.com");
317 jsonw_uint_field(wr, "downloads", 2000000ul);
318 jsonw_float_field(wr, "stock", 8.16);
319
320 jsonw_name(wr, "ARGV");
321 jsonw_start_array(wr);
322 while (--argc)
323 jsonw_string(wr, *++argv);
324 jsonw_end_array(wr);
325
326 jsonw_name(wr, "empty");
327 jsonw_start_array(wr);
328 jsonw_end_array(wr);
329
330 jsonw_name(wr, "NIL");
331 jsonw_start_object(wr);
332 jsonw_end_object(wr);
333
334 jsonw_null_field(wr, "my_null");
335
336 jsonw_name(wr, "special chars");
337 jsonw_start_array(wr);
338 jsonw_string_field(wr, "slash", "/");
339 jsonw_string_field(wr, "newline", "\n");
340 jsonw_string_field(wr, "tab", "\t");
341 jsonw_string_field(wr, "ff", "\f");
342 jsonw_string_field(wr, "quote", "\"");
343 jsonw_string_field(wr, "tick", "\'");
344 jsonw_string_field(wr, "backslash", "\\");
345 jsonw_end_array(wr);
346
347 jsonw_end_object(wr);
348
349 jsonw_end_object(wr);
350 jsonw_destroy(&wr);
351 return 0;
352}
353
354#endif