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