brintos

brintos / linux-shallow public Read only

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