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