xref: /linux/tools/net/ynl/ynltool/json_writer.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1*b02d2290SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2*b02d2290SJakub Kicinski /*
3*b02d2290SJakub Kicinski  * Simple streaming JSON writer
4*b02d2290SJakub Kicinski  *
5*b02d2290SJakub Kicinski  * This takes care of the annoying bits of JSON syntax like the commas
6*b02d2290SJakub Kicinski  * after elements
7*b02d2290SJakub Kicinski  *
8*b02d2290SJakub Kicinski  * Authors:	Stephen Hemminger <stephen@networkplumber.org>
9*b02d2290SJakub Kicinski  */
10*b02d2290SJakub Kicinski 
11*b02d2290SJakub Kicinski #include <stdio.h>
12*b02d2290SJakub Kicinski #include <stdbool.h>
13*b02d2290SJakub Kicinski #include <stdarg.h>
14*b02d2290SJakub Kicinski #include <assert.h>
15*b02d2290SJakub Kicinski #include <malloc.h>
16*b02d2290SJakub Kicinski #include <inttypes.h>
17*b02d2290SJakub Kicinski #include <stdint.h>
18*b02d2290SJakub Kicinski 
19*b02d2290SJakub Kicinski #include "json_writer.h"
20*b02d2290SJakub Kicinski 
21*b02d2290SJakub Kicinski struct json_writer {
22*b02d2290SJakub Kicinski 	FILE		*out;
23*b02d2290SJakub Kicinski 	unsigned	depth;
24*b02d2290SJakub Kicinski 	bool		pretty;
25*b02d2290SJakub Kicinski 	char		sep;
26*b02d2290SJakub Kicinski };
27*b02d2290SJakub Kicinski 
28*b02d2290SJakub Kicinski static void jsonw_indent(json_writer_t *self)
29*b02d2290SJakub Kicinski {
30*b02d2290SJakub Kicinski 	unsigned i;
31*b02d2290SJakub Kicinski 	for (i = 0; i < self->depth; ++i)
32*b02d2290SJakub Kicinski 		fputs("    ", self->out);
33*b02d2290SJakub Kicinski }
34*b02d2290SJakub Kicinski 
35*b02d2290SJakub Kicinski static void jsonw_eol(json_writer_t *self)
36*b02d2290SJakub Kicinski {
37*b02d2290SJakub Kicinski 	if (!self->pretty)
38*b02d2290SJakub Kicinski 		return;
39*b02d2290SJakub Kicinski 
40*b02d2290SJakub Kicinski 	putc('\n', self->out);
41*b02d2290SJakub Kicinski 	jsonw_indent(self);
42*b02d2290SJakub Kicinski }
43*b02d2290SJakub Kicinski 
44*b02d2290SJakub Kicinski static void jsonw_eor(json_writer_t *self)
45*b02d2290SJakub Kicinski {
46*b02d2290SJakub Kicinski 	if (self->sep != '\0')
47*b02d2290SJakub Kicinski 		putc(self->sep, self->out);
48*b02d2290SJakub Kicinski 	self->sep = ',';
49*b02d2290SJakub Kicinski }
50*b02d2290SJakub Kicinski 
51*b02d2290SJakub Kicinski static void jsonw_puts(json_writer_t *self, const char *str)
52*b02d2290SJakub Kicinski {
53*b02d2290SJakub Kicinski 	putc('"', self->out);
54*b02d2290SJakub Kicinski 	for (; *str; ++str)
55*b02d2290SJakub Kicinski 		switch (*str) {
56*b02d2290SJakub Kicinski 		case '\t':
57*b02d2290SJakub Kicinski 			fputs("\\t", self->out);
58*b02d2290SJakub Kicinski 			break;
59*b02d2290SJakub Kicinski 		case '\n':
60*b02d2290SJakub Kicinski 			fputs("\\n", self->out);
61*b02d2290SJakub Kicinski 			break;
62*b02d2290SJakub Kicinski 		case '\r':
63*b02d2290SJakub Kicinski 			fputs("\\r", self->out);
64*b02d2290SJakub Kicinski 			break;
65*b02d2290SJakub Kicinski 		case '\f':
66*b02d2290SJakub Kicinski 			fputs("\\f", self->out);
67*b02d2290SJakub Kicinski 			break;
68*b02d2290SJakub Kicinski 		case '\b':
69*b02d2290SJakub Kicinski 			fputs("\\b", self->out);
70*b02d2290SJakub Kicinski 			break;
71*b02d2290SJakub Kicinski 		case '\\':
72*b02d2290SJakub Kicinski 			fputs("\\\\", self->out);
73*b02d2290SJakub Kicinski 			break;
74*b02d2290SJakub Kicinski 		case '"':
75*b02d2290SJakub Kicinski 			fputs("\\\"", self->out);
76*b02d2290SJakub Kicinski 			break;
77*b02d2290SJakub Kicinski 		default:
78*b02d2290SJakub Kicinski 			putc(*str, self->out);
79*b02d2290SJakub Kicinski 		}
80*b02d2290SJakub Kicinski 	putc('"', self->out);
81*b02d2290SJakub Kicinski }
82*b02d2290SJakub Kicinski 
83*b02d2290SJakub Kicinski json_writer_t *jsonw_new(FILE *f)
84*b02d2290SJakub Kicinski {
85*b02d2290SJakub Kicinski 	json_writer_t *self = malloc(sizeof(*self));
86*b02d2290SJakub Kicinski 	if (self) {
87*b02d2290SJakub Kicinski 		self->out = f;
88*b02d2290SJakub Kicinski 		self->depth = 0;
89*b02d2290SJakub Kicinski 		self->pretty = false;
90*b02d2290SJakub Kicinski 		self->sep = '\0';
91*b02d2290SJakub Kicinski 	}
92*b02d2290SJakub Kicinski 	return self;
93*b02d2290SJakub Kicinski }
94*b02d2290SJakub Kicinski 
95*b02d2290SJakub Kicinski void jsonw_destroy(json_writer_t **self_p)
96*b02d2290SJakub Kicinski {
97*b02d2290SJakub Kicinski 	json_writer_t *self = *self_p;
98*b02d2290SJakub Kicinski 
99*b02d2290SJakub Kicinski 	assert(self->depth == 0);
100*b02d2290SJakub Kicinski 	fputs("\n", self->out);
101*b02d2290SJakub Kicinski 	fflush(self->out);
102*b02d2290SJakub Kicinski 	free(self);
103*b02d2290SJakub Kicinski 	*self_p = NULL;
104*b02d2290SJakub Kicinski }
105*b02d2290SJakub Kicinski 
106*b02d2290SJakub Kicinski void jsonw_pretty(json_writer_t *self, bool on)
107*b02d2290SJakub Kicinski {
108*b02d2290SJakub Kicinski 	self->pretty = on;
109*b02d2290SJakub Kicinski }
110*b02d2290SJakub Kicinski 
111*b02d2290SJakub Kicinski void jsonw_reset(json_writer_t *self)
112*b02d2290SJakub Kicinski {
113*b02d2290SJakub Kicinski 	assert(self->depth == 0);
114*b02d2290SJakub Kicinski 	self->sep = '\0';
115*b02d2290SJakub Kicinski }
116*b02d2290SJakub Kicinski 
117*b02d2290SJakub Kicinski static void jsonw_begin(json_writer_t *self, int c)
118*b02d2290SJakub Kicinski {
119*b02d2290SJakub Kicinski 	jsonw_eor(self);
120*b02d2290SJakub Kicinski 	putc(c, self->out);
121*b02d2290SJakub Kicinski 	++self->depth;
122*b02d2290SJakub Kicinski 	self->sep = '\0';
123*b02d2290SJakub Kicinski }
124*b02d2290SJakub Kicinski 
125*b02d2290SJakub Kicinski static void jsonw_end(json_writer_t *self, int c)
126*b02d2290SJakub Kicinski {
127*b02d2290SJakub Kicinski 	assert(self->depth > 0);
128*b02d2290SJakub Kicinski 
129*b02d2290SJakub Kicinski 	--self->depth;
130*b02d2290SJakub Kicinski 	if (self->sep != '\0')
131*b02d2290SJakub Kicinski 		jsonw_eol(self);
132*b02d2290SJakub Kicinski 	putc(c, self->out);
133*b02d2290SJakub Kicinski 	self->sep = ',';
134*b02d2290SJakub Kicinski }
135*b02d2290SJakub Kicinski 
136*b02d2290SJakub Kicinski void jsonw_name(json_writer_t *self, const char *name)
137*b02d2290SJakub Kicinski {
138*b02d2290SJakub Kicinski 	jsonw_eor(self);
139*b02d2290SJakub Kicinski 	jsonw_eol(self);
140*b02d2290SJakub Kicinski 	self->sep = '\0';
141*b02d2290SJakub Kicinski 	jsonw_puts(self, name);
142*b02d2290SJakub Kicinski 	putc(':', self->out);
143*b02d2290SJakub Kicinski 	if (self->pretty)
144*b02d2290SJakub Kicinski 		putc(' ', self->out);
145*b02d2290SJakub Kicinski }
146*b02d2290SJakub Kicinski 
147*b02d2290SJakub Kicinski void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
148*b02d2290SJakub Kicinski {
149*b02d2290SJakub Kicinski 	jsonw_eor(self);
150*b02d2290SJakub Kicinski 	putc('"', self->out);
151*b02d2290SJakub Kicinski 	vfprintf(self->out, fmt, ap);
152*b02d2290SJakub Kicinski 	putc('"', self->out);
153*b02d2290SJakub Kicinski }
154*b02d2290SJakub Kicinski 
155*b02d2290SJakub Kicinski void jsonw_printf(json_writer_t *self, const char *fmt, ...)
156*b02d2290SJakub Kicinski {
157*b02d2290SJakub Kicinski 	va_list ap;
158*b02d2290SJakub Kicinski 
159*b02d2290SJakub Kicinski 	va_start(ap, fmt);
160*b02d2290SJakub Kicinski 	jsonw_eor(self);
161*b02d2290SJakub Kicinski 	vfprintf(self->out, fmt, ap);
162*b02d2290SJakub Kicinski 	va_end(ap);
163*b02d2290SJakub Kicinski }
164*b02d2290SJakub Kicinski 
165*b02d2290SJakub Kicinski void jsonw_start_object(json_writer_t *self)
166*b02d2290SJakub Kicinski {
167*b02d2290SJakub Kicinski 	jsonw_begin(self, '{');
168*b02d2290SJakub Kicinski }
169*b02d2290SJakub Kicinski 
170*b02d2290SJakub Kicinski void jsonw_end_object(json_writer_t *self)
171*b02d2290SJakub Kicinski {
172*b02d2290SJakub Kicinski 	jsonw_end(self, '}');
173*b02d2290SJakub Kicinski }
174*b02d2290SJakub Kicinski 
175*b02d2290SJakub Kicinski void jsonw_start_array(json_writer_t *self)
176*b02d2290SJakub Kicinski {
177*b02d2290SJakub Kicinski 	jsonw_begin(self, '[');
178*b02d2290SJakub Kicinski }
179*b02d2290SJakub Kicinski 
180*b02d2290SJakub Kicinski void jsonw_end_array(json_writer_t *self)
181*b02d2290SJakub Kicinski {
182*b02d2290SJakub Kicinski 	jsonw_end(self, ']');
183*b02d2290SJakub Kicinski }
184*b02d2290SJakub Kicinski 
185*b02d2290SJakub Kicinski void jsonw_string(json_writer_t *self, const char *value)
186*b02d2290SJakub Kicinski {
187*b02d2290SJakub Kicinski 	jsonw_eor(self);
188*b02d2290SJakub Kicinski 	jsonw_puts(self, value);
189*b02d2290SJakub Kicinski }
190*b02d2290SJakub Kicinski 
191*b02d2290SJakub Kicinski void jsonw_bool(json_writer_t *self, bool val)
192*b02d2290SJakub Kicinski {
193*b02d2290SJakub Kicinski 	jsonw_printf(self, "%s", val ? "true" : "false");
194*b02d2290SJakub Kicinski }
195*b02d2290SJakub Kicinski 
196*b02d2290SJakub Kicinski void jsonw_null(json_writer_t *self)
197*b02d2290SJakub Kicinski {
198*b02d2290SJakub Kicinski 	jsonw_printf(self, "null");
199*b02d2290SJakub Kicinski }
200*b02d2290SJakub Kicinski 
201*b02d2290SJakub Kicinski void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
202*b02d2290SJakub Kicinski {
203*b02d2290SJakub Kicinski 	jsonw_printf(self, fmt, num);
204*b02d2290SJakub Kicinski }
205*b02d2290SJakub Kicinski 
206*b02d2290SJakub Kicinski void jsonw_float(json_writer_t *self, double num)
207*b02d2290SJakub Kicinski {
208*b02d2290SJakub Kicinski 	jsonw_printf(self, "%g", num);
209*b02d2290SJakub Kicinski }
210*b02d2290SJakub Kicinski 
211*b02d2290SJakub Kicinski void jsonw_hu(json_writer_t *self, unsigned short num)
212*b02d2290SJakub Kicinski {
213*b02d2290SJakub Kicinski 	jsonw_printf(self, "%hu", num);
214*b02d2290SJakub Kicinski }
215*b02d2290SJakub Kicinski 
216*b02d2290SJakub Kicinski void jsonw_uint(json_writer_t *self, uint64_t num)
217*b02d2290SJakub Kicinski {
218*b02d2290SJakub Kicinski 	jsonw_printf(self, "%"PRIu64, num);
219*b02d2290SJakub Kicinski }
220*b02d2290SJakub Kicinski 
221*b02d2290SJakub Kicinski void jsonw_lluint(json_writer_t *self, unsigned long long int num)
222*b02d2290SJakub Kicinski {
223*b02d2290SJakub Kicinski 	jsonw_printf(self, "%llu", num);
224*b02d2290SJakub Kicinski }
225*b02d2290SJakub Kicinski 
226*b02d2290SJakub Kicinski void jsonw_int(json_writer_t *self, int64_t num)
227*b02d2290SJakub Kicinski {
228*b02d2290SJakub Kicinski 	jsonw_printf(self, "%"PRId64, num);
229*b02d2290SJakub Kicinski }
230*b02d2290SJakub Kicinski 
231*b02d2290SJakub Kicinski void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
232*b02d2290SJakub Kicinski {
233*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
234*b02d2290SJakub Kicinski 	jsonw_string(self, val);
235*b02d2290SJakub Kicinski }
236*b02d2290SJakub Kicinski 
237*b02d2290SJakub Kicinski void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
238*b02d2290SJakub Kicinski {
239*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
240*b02d2290SJakub Kicinski 	jsonw_bool(self, val);
241*b02d2290SJakub Kicinski }
242*b02d2290SJakub Kicinski 
243*b02d2290SJakub Kicinski void jsonw_float_field(json_writer_t *self, const char *prop, double val)
244*b02d2290SJakub Kicinski {
245*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
246*b02d2290SJakub Kicinski 	jsonw_float(self, val);
247*b02d2290SJakub Kicinski }
248*b02d2290SJakub Kicinski 
249*b02d2290SJakub Kicinski void jsonw_float_field_fmt(json_writer_t *self,
250*b02d2290SJakub Kicinski 			   const char *prop,
251*b02d2290SJakub Kicinski 			   const char *fmt,
252*b02d2290SJakub Kicinski 			   double val)
253*b02d2290SJakub Kicinski {
254*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
255*b02d2290SJakub Kicinski 	jsonw_float_fmt(self, fmt, val);
256*b02d2290SJakub Kicinski }
257*b02d2290SJakub Kicinski 
258*b02d2290SJakub Kicinski void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
259*b02d2290SJakub Kicinski {
260*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
261*b02d2290SJakub Kicinski 	jsonw_uint(self, num);
262*b02d2290SJakub Kicinski }
263*b02d2290SJakub Kicinski 
264*b02d2290SJakub Kicinski void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
265*b02d2290SJakub Kicinski {
266*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
267*b02d2290SJakub Kicinski 	jsonw_hu(self, num);
268*b02d2290SJakub Kicinski }
269*b02d2290SJakub Kicinski 
270*b02d2290SJakub Kicinski void jsonw_lluint_field(json_writer_t *self,
271*b02d2290SJakub Kicinski 			const char *prop,
272*b02d2290SJakub Kicinski 			unsigned long long int num)
273*b02d2290SJakub Kicinski {
274*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
275*b02d2290SJakub Kicinski 	jsonw_lluint(self, num);
276*b02d2290SJakub Kicinski }
277*b02d2290SJakub Kicinski 
278*b02d2290SJakub Kicinski void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
279*b02d2290SJakub Kicinski {
280*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
281*b02d2290SJakub Kicinski 	jsonw_int(self, num);
282*b02d2290SJakub Kicinski }
283*b02d2290SJakub Kicinski 
284*b02d2290SJakub Kicinski void jsonw_null_field(json_writer_t *self, const char *prop)
285*b02d2290SJakub Kicinski {
286*b02d2290SJakub Kicinski 	jsonw_name(self, prop);
287*b02d2290SJakub Kicinski 	jsonw_null(self);
288*b02d2290SJakub Kicinski }
289