iproute2/lib/json_writer.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: (GPL-2.0 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
  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                case '\'':
  84                        fputs("\\\'", self->out);
  85                        break;
  86                default:
  87                        putc(*str, self->out);
  88                }
  89        putc('"', self->out);
  90}
  91
  92/* Create a new JSON stream */
  93json_writer_t *jsonw_new(FILE *f)
  94{
  95        json_writer_t *self = malloc(sizeof(*self));
  96        if (self) {
  97                self->out = f;
  98                self->depth = 0;
  99                self->pretty = false;
 100                self->sep = '\0';
 101        }
 102        return self;
 103}
 104
 105/* End output to JSON stream */
 106void jsonw_destroy(json_writer_t **self_p)
 107{
 108        json_writer_t *self = *self_p;
 109
 110        assert(self->depth == 0);
 111        fputs("\n", self->out);
 112        fflush(self->out);
 113        free(self);
 114        *self_p = NULL;
 115}
 116
 117void jsonw_pretty(json_writer_t *self, bool on)
 118{
 119        self->pretty = on;
 120}
 121
 122/* Basic blocks */
 123static void jsonw_begin(json_writer_t *self, int c)
 124{
 125        jsonw_eor(self);
 126        putc(c, self->out);
 127        ++self->depth;
 128        self->sep = '\0';
 129}
 130
 131static void jsonw_end(json_writer_t *self, int c)
 132{
 133        assert(self->depth > 0);
 134
 135        --self->depth;
 136        if (self->sep != '\0')
 137                jsonw_eol(self);
 138        putc(c, self->out);
 139        self->sep = ',';
 140}
 141
 142
 143/* Add a JSON property name */
 144void jsonw_name(json_writer_t *self, const char *name)
 145{
 146        jsonw_eor(self);
 147        jsonw_eol(self);
 148        self->sep = '\0';
 149        jsonw_puts(self, name);
 150        putc(':', self->out);
 151        if (self->pretty)
 152                putc(' ', self->out);
 153}
 154
 155__attribute__((format(printf, 2, 3)))
 156void jsonw_printf(json_writer_t *self, const char *fmt, ...)
 157{
 158        va_list ap;
 159
 160        va_start(ap, fmt);
 161        jsonw_eor(self);
 162        vfprintf(self->out, fmt, ap);
 163        va_end(ap);
 164}
 165
 166/* Collections */
 167void jsonw_start_object(json_writer_t *self)
 168{
 169        jsonw_begin(self, '{');
 170}
 171
 172void jsonw_end_object(json_writer_t *self)
 173{
 174        jsonw_end(self, '}');
 175}
 176
 177void jsonw_start_array(json_writer_t *self)
 178{
 179        jsonw_begin(self, '[');
 180        if (self->pretty)
 181                putc(' ', self->out);
 182}
 183
 184void jsonw_end_array(json_writer_t *self)
 185{
 186        if (self->pretty && self->sep)
 187                putc(' ', self->out);
 188        self->sep = '\0';
 189        jsonw_end(self, ']');
 190}
 191
 192/* JSON value types */
 193void jsonw_string(json_writer_t *self, const char *value)
 194{
 195        jsonw_eor(self);
 196        jsonw_puts(self, value);
 197}
 198
 199void jsonw_bool(json_writer_t *self, bool val)
 200{
 201        jsonw_printf(self, "%s", val ? "true" : "false");
 202}
 203
 204void jsonw_null(json_writer_t *self)
 205{
 206        jsonw_printf(self, "null");
 207}
 208
 209void jsonw_float(json_writer_t *self, double num)
 210{
 211        jsonw_printf(self, "%g", num);
 212}
 213
 214void jsonw_hhu(json_writer_t *self, unsigned char num)
 215{
 216        jsonw_printf(self, "%hhu", num);
 217}
 218
 219void jsonw_hu(json_writer_t *self, unsigned short num)
 220{
 221        jsonw_printf(self, "%hu", num);
 222}
 223
 224void jsonw_uint(json_writer_t *self, unsigned int num)
 225{
 226        jsonw_printf(self, "%u", num);
 227}
 228
 229void jsonw_u64(json_writer_t *self, uint64_t num)
 230{
 231        jsonw_printf(self, "%"PRIu64, num);
 232}
 233
 234void jsonw_xint(json_writer_t *self, uint64_t num)
 235{
 236        jsonw_printf(self, "%"PRIx64, num);
 237}
 238
 239void jsonw_luint(json_writer_t *self, unsigned long num)
 240{
 241        jsonw_printf(self, "%lu", num);
 242}
 243
 244void jsonw_lluint(json_writer_t *self, unsigned long long num)
 245{
 246        jsonw_printf(self, "%llu", num);
 247}
 248
 249void jsonw_int(json_writer_t *self, int num)
 250{
 251        jsonw_printf(self, "%d", num);
 252}
 253
 254void jsonw_s64(json_writer_t *self, int64_t num)
 255{
 256        jsonw_printf(self, "%"PRId64, num);
 257}
 258
 259/* Basic name/value objects */
 260void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
 261{
 262        jsonw_name(self, prop);
 263        jsonw_string(self, val);
 264}
 265
 266void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
 267{
 268        jsonw_name(self, prop);
 269        jsonw_bool(self, val);
 270}
 271
 272void jsonw_float_field(json_writer_t *self, const char *prop, double val)
 273{
 274        jsonw_name(self, prop);
 275        jsonw_float(self, val);
 276}
 277
 278void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num)
 279{
 280        jsonw_name(self, prop);
 281        jsonw_uint(self, num);
 282}
 283
 284void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num)
 285{
 286        jsonw_name(self, prop);
 287        jsonw_u64(self, num);
 288}
 289
 290void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
 291{
 292        jsonw_name(self, prop);
 293        jsonw_xint(self, num);
 294}
 295
 296void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
 297{
 298        jsonw_name(self, prop);
 299        jsonw_hhu(self, num);
 300}
 301
 302void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
 303{
 304        jsonw_name(self, prop);
 305        jsonw_hu(self, num);
 306}
 307
 308void jsonw_luint_field(json_writer_t *self,
 309                        const char *prop,
 310                        unsigned long num)
 311{
 312        jsonw_name(self, prop);
 313        jsonw_luint(self, num);
 314}
 315
 316void jsonw_lluint_field(json_writer_t *self,
 317                        const char *prop,
 318                        unsigned long long num)
 319{
 320        jsonw_name(self, prop);
 321        jsonw_lluint(self, num);
 322}
 323
 324void jsonw_int_field(json_writer_t *self, const char *prop, int num)
 325{
 326        jsonw_name(self, prop);
 327        jsonw_int(self, num);
 328}
 329
 330void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num)
 331{
 332        jsonw_name(self, prop);
 333        jsonw_s64(self, num);
 334}
 335
 336void jsonw_null_field(json_writer_t *self, const char *prop)
 337{
 338        jsonw_name(self, prop);
 339        jsonw_null(self);
 340}
 341
 342#ifdef TEST
 343int main(int argc, char **argv)
 344{
 345        json_writer_t *wr = jsonw_new(stdout);
 346
 347        jsonw_start_object(wr);
 348        jsonw_pretty(wr, true);
 349        jsonw_name(wr, "Vyatta");
 350        jsonw_start_object(wr);
 351        jsonw_string_field(wr, "url", "http://vyatta.com");
 352        jsonw_uint_field(wr, "downloads", 2000000ul);
 353        jsonw_float_field(wr, "stock", 8.16);
 354
 355        jsonw_name(wr, "ARGV");
 356        jsonw_start_array(wr);
 357        while (--argc)
 358                jsonw_string(wr, *++argv);
 359        jsonw_end_array(wr);
 360
 361        jsonw_name(wr, "empty");
 362        jsonw_start_array(wr);
 363        jsonw_end_array(wr);
 364
 365        jsonw_name(wr, "NIL");
 366        jsonw_start_object(wr);
 367        jsonw_end_object(wr);
 368
 369        jsonw_null_field(wr, "my_null");
 370
 371        jsonw_name(wr, "special chars");
 372        jsonw_start_array(wr);
 373        jsonw_string_field(wr, "slash", "/");
 374        jsonw_string_field(wr, "newline", "\n");
 375        jsonw_string_field(wr, "tab", "\t");
 376        jsonw_string_field(wr, "ff", "\f");
 377        jsonw_string_field(wr, "quote", "\"");
 378        jsonw_string_field(wr, "tick", "\'");
 379        jsonw_string_field(wr, "backslash", "\\");
 380        jsonw_end_array(wr);
 381
 382        jsonw_end_object(wr);
 383
 384        jsonw_end_object(wr);
 385        jsonw_destroy(&wr);
 386        return 0;
 387}
 388
 389#endif
 390