Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
0002 /*
0003  * Simple streaming JSON writer
0004  *
0005  * This takes care of the annoying bits of JSON syntax like the commas
0006  * after elements
0007  *
0008  * Authors: Stephen Hemminger <stephen@networkplumber.org>
0009  */
0010 
0011 #include <stdio.h>
0012 #include <stdbool.h>
0013 #include <stdarg.h>
0014 #include <assert.h>
0015 #include <malloc.h>
0016 #include <inttypes.h>
0017 #include <stdint.h>
0018 
0019 #include "json_writer.h"
0020 
0021 struct json_writer {
0022     FILE        *out;   /* output file */
0023     unsigned    depth;  /* nesting */
0024     bool        pretty; /* optional whitepace */
0025     char        sep;    /* either nul or comma */
0026 };
0027 
0028 /* indentation for pretty print */
0029 static void jsonw_indent(json_writer_t *self)
0030 {
0031     unsigned i;
0032     for (i = 0; i < self->depth; ++i)
0033         fputs("    ", self->out);
0034 }
0035 
0036 /* end current line and indent if pretty printing */
0037 static void jsonw_eol(json_writer_t *self)
0038 {
0039     if (!self->pretty)
0040         return;
0041 
0042     putc('\n', self->out);
0043     jsonw_indent(self);
0044 }
0045 
0046 /* If current object is not empty print a comma */
0047 static void jsonw_eor(json_writer_t *self)
0048 {
0049     if (self->sep != '\0')
0050         putc(self->sep, self->out);
0051     self->sep = ',';
0052 }
0053 
0054 
0055 /* Output JSON encoded string */
0056 /* Handles C escapes, does not do Unicode */
0057 static void jsonw_puts(json_writer_t *self, const char *str)
0058 {
0059     putc('"', self->out);
0060     for (; *str; ++str)
0061         switch (*str) {
0062         case '\t':
0063             fputs("\\t", self->out);
0064             break;
0065         case '\n':
0066             fputs("\\n", self->out);
0067             break;
0068         case '\r':
0069             fputs("\\r", self->out);
0070             break;
0071         case '\f':
0072             fputs("\\f", self->out);
0073             break;
0074         case '\b':
0075             fputs("\\b", self->out);
0076             break;
0077         case '\\':
0078             fputs("\\n", self->out);
0079             break;
0080         case '"':
0081             fputs("\\\"", self->out);
0082             break;
0083         case '\'':
0084             fputs("\\\'", self->out);
0085             break;
0086         default:
0087             putc(*str, self->out);
0088         }
0089     putc('"', self->out);
0090 }
0091 
0092 /* Create a new JSON stream */
0093 json_writer_t *jsonw_new(FILE *f)
0094 {
0095     json_writer_t *self = malloc(sizeof(*self));
0096     if (self) {
0097         self->out = f;
0098         self->depth = 0;
0099         self->pretty = false;
0100         self->sep = '\0';
0101     }
0102     return self;
0103 }
0104 
0105 /* End output to JSON stream */
0106 void jsonw_destroy(json_writer_t **self_p)
0107 {
0108     json_writer_t *self = *self_p;
0109 
0110     assert(self->depth == 0);
0111     fputs("\n", self->out);
0112     fflush(self->out);
0113     free(self);
0114     *self_p = NULL;
0115 }
0116 
0117 void jsonw_pretty(json_writer_t *self, bool on)
0118 {
0119     self->pretty = on;
0120 }
0121 
0122 void jsonw_reset(json_writer_t *self)
0123 {
0124     assert(self->depth == 0);
0125     self->sep = '\0';
0126 }
0127 
0128 /* Basic blocks */
0129 static void jsonw_begin(json_writer_t *self, int c)
0130 {
0131     jsonw_eor(self);
0132     putc(c, self->out);
0133     ++self->depth;
0134     self->sep = '\0';
0135 }
0136 
0137 static void jsonw_end(json_writer_t *self, int c)
0138 {
0139     assert(self->depth > 0);
0140 
0141     --self->depth;
0142     if (self->sep != '\0')
0143         jsonw_eol(self);
0144     putc(c, self->out);
0145     self->sep = ',';
0146 }
0147 
0148 
0149 /* Add a JSON property name */
0150 void jsonw_name(json_writer_t *self, const char *name)
0151 {
0152     jsonw_eor(self);
0153     jsonw_eol(self);
0154     self->sep = '\0';
0155     jsonw_puts(self, name);
0156     putc(':', self->out);
0157     if (self->pretty)
0158         putc(' ', self->out);
0159 }
0160 
0161 void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
0162 {
0163     jsonw_eor(self);
0164     putc('"', self->out);
0165     vfprintf(self->out, fmt, ap);
0166     putc('"', self->out);
0167 }
0168 
0169 void jsonw_printf(json_writer_t *self, const char *fmt, ...)
0170 {
0171     va_list ap;
0172 
0173     va_start(ap, fmt);
0174     jsonw_eor(self);
0175     vfprintf(self->out, fmt, ap);
0176     va_end(ap);
0177 }
0178 
0179 /* Collections */
0180 void jsonw_start_object(json_writer_t *self)
0181 {
0182     jsonw_begin(self, '{');
0183 }
0184 
0185 void jsonw_end_object(json_writer_t *self)
0186 {
0187     jsonw_end(self, '}');
0188 }
0189 
0190 void jsonw_start_array(json_writer_t *self)
0191 {
0192     jsonw_begin(self, '[');
0193 }
0194 
0195 void jsonw_end_array(json_writer_t *self)
0196 {
0197     jsonw_end(self, ']');
0198 }
0199 
0200 /* JSON value types */
0201 void jsonw_string(json_writer_t *self, const char *value)
0202 {
0203     jsonw_eor(self);
0204     jsonw_puts(self, value);
0205 }
0206 
0207 void jsonw_bool(json_writer_t *self, bool val)
0208 {
0209     jsonw_printf(self, "%s", val ? "true" : "false");
0210 }
0211 
0212 void jsonw_null(json_writer_t *self)
0213 {
0214     jsonw_printf(self, "null");
0215 }
0216 
0217 void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
0218 {
0219     jsonw_printf(self, fmt, num);
0220 }
0221 
0222 #ifdef notused
0223 void jsonw_float(json_writer_t *self, double num)
0224 {
0225     jsonw_printf(self, "%g", num);
0226 }
0227 #endif
0228 
0229 void jsonw_hu(json_writer_t *self, unsigned short num)
0230 {
0231     jsonw_printf(self, "%hu", num);
0232 }
0233 
0234 void jsonw_uint(json_writer_t *self, uint64_t num)
0235 {
0236     jsonw_printf(self, "%"PRIu64, num);
0237 }
0238 
0239 void jsonw_lluint(json_writer_t *self, unsigned long long int num)
0240 {
0241     jsonw_printf(self, "%llu", num);
0242 }
0243 
0244 void jsonw_int(json_writer_t *self, int64_t num)
0245 {
0246     jsonw_printf(self, "%"PRId64, num);
0247 }
0248 
0249 /* Basic name/value objects */
0250 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
0251 {
0252     jsonw_name(self, prop);
0253     jsonw_string(self, val);
0254 }
0255 
0256 void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
0257 {
0258     jsonw_name(self, prop);
0259     jsonw_bool(self, val);
0260 }
0261 
0262 #ifdef notused
0263 void jsonw_float_field(json_writer_t *self, const char *prop, double val)
0264 {
0265     jsonw_name(self, prop);
0266     jsonw_float(self, val);
0267 }
0268 #endif
0269 
0270 void jsonw_float_field_fmt(json_writer_t *self,
0271                const char *prop,
0272                const char *fmt,
0273                double val)
0274 {
0275     jsonw_name(self, prop);
0276     jsonw_float_fmt(self, fmt, val);
0277 }
0278 
0279 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
0280 {
0281     jsonw_name(self, prop);
0282     jsonw_uint(self, num);
0283 }
0284 
0285 void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
0286 {
0287     jsonw_name(self, prop);
0288     jsonw_hu(self, num);
0289 }
0290 
0291 void jsonw_lluint_field(json_writer_t *self,
0292             const char *prop,
0293             unsigned long long int num)
0294 {
0295     jsonw_name(self, prop);
0296     jsonw_lluint(self, num);
0297 }
0298 
0299 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
0300 {
0301     jsonw_name(self, prop);
0302     jsonw_int(self, num);
0303 }
0304 
0305 void jsonw_null_field(json_writer_t *self, const char *prop)
0306 {
0307     jsonw_name(self, prop);
0308     jsonw_null(self);
0309 }
0310 
0311 #ifdef TEST
0312 int main(int argc, char **argv)
0313 {
0314     json_writer_t *wr = jsonw_new(stdout);
0315 
0316     jsonw_start_object(wr);
0317     jsonw_pretty(wr, true);
0318     jsonw_name(wr, "Vyatta");
0319     jsonw_start_object(wr);
0320     jsonw_string_field(wr, "url", "http://vyatta.com");
0321     jsonw_uint_field(wr, "downloads", 2000000ul);
0322     jsonw_float_field(wr, "stock", 8.16);
0323 
0324     jsonw_name(wr, "ARGV");
0325     jsonw_start_array(wr);
0326     while (--argc)
0327         jsonw_string(wr, *++argv);
0328     jsonw_end_array(wr);
0329 
0330     jsonw_name(wr, "empty");
0331     jsonw_start_array(wr);
0332     jsonw_end_array(wr);
0333 
0334     jsonw_name(wr, "NIL");
0335     jsonw_start_object(wr);
0336     jsonw_end_object(wr);
0337 
0338     jsonw_null_field(wr, "my_null");
0339 
0340     jsonw_name(wr, "special chars");
0341     jsonw_start_array(wr);
0342     jsonw_string_field(wr, "slash", "/");
0343     jsonw_string_field(wr, "newline", "\n");
0344     jsonw_string_field(wr, "tab", "\t");
0345     jsonw_string_field(wr, "ff", "\f");
0346     jsonw_string_field(wr, "quote", "\"");
0347     jsonw_string_field(wr, "tick", "\'");
0348     jsonw_string_field(wr, "backslash", "\\");
0349     jsonw_end_array(wr);
0350 
0351     jsonw_end_object(wr);
0352 
0353     jsonw_end_object(wr);
0354     jsonw_destroy(&wr);
0355     return 0;
0356 }
0357 
0358 #endif