linux/tools/bpf/bpftool/json_writer.c
<<
>>
Prefs
   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
  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("\\n", 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
 122void jsonw_reset(json_writer_t *self)
 123{
 124        assert(self->depth == 0);
 125        self->sep = '\0';
 126}
 127
 128/* Basic blocks */
 129static void jsonw_begin(json_writer_t *self, int c)
 130{
 131        jsonw_eor(self);
 132        putc(c, self->out);
 133        ++self->depth;
 134        self->sep = '\0';
 135}
 136
 137static void jsonw_end(json_writer_t *self, int c)
 138{
 139        assert(self->depth > 0);
 140
 141        --self->depth;
 142        if (self->sep != '\0')
 143                jsonw_eol(self);
 144        putc(c, self->out);
 145        self->sep = ',';
 146}
 147
 148
 149/* Add a JSON property name */
 150void jsonw_name(json_writer_t *self, const char *name)
 151{
 152        jsonw_eor(self);
 153        jsonw_eol(self);
 154        self->sep = '\0';
 155        jsonw_puts(self, name);
 156        putc(':', self->out);
 157        if (self->pretty)
 158                putc(' ', self->out);
 159}
 160
 161void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
 162{
 163        jsonw_eor(self);
 164        putc('"', self->out);
 165        vfprintf(self->out, fmt, ap);
 166        putc('"', self->out);
 167}
 168
 169void jsonw_printf(json_writer_t *self, const char *fmt, ...)
 170{
 171        va_list ap;
 172
 173        va_start(ap, fmt);
 174        jsonw_eor(self);
 175        vfprintf(self->out, fmt, ap);
 176        va_end(ap);
 177}
 178
 179/* Collections */
 180void jsonw_start_object(json_writer_t *self)
 181{
 182        jsonw_begin(self, '{');
 183}
 184
 185void jsonw_end_object(json_writer_t *self)
 186{
 187        jsonw_end(self, '}');
 188}
 189
 190void jsonw_start_array(json_writer_t *self)
 191{
 192        jsonw_begin(self, '[');
 193}
 194
 195void jsonw_end_array(json_writer_t *self)
 196{
 197        jsonw_end(self, ']');
 198}
 199
 200/* JSON value types */
 201void jsonw_string(json_writer_t *self, const char *value)
 202{
 203        jsonw_eor(self);
 204        jsonw_puts(self, value);
 205}
 206
 207void jsonw_bool(json_writer_t *self, bool val)
 208{
 209        jsonw_printf(self, "%s", val ? "true" : "false");
 210}
 211
 212void jsonw_null(json_writer_t *self)
 213{
 214        jsonw_printf(self, "null");
 215}
 216
 217void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
 218{
 219        jsonw_printf(self, fmt, num);
 220}
 221
 222#ifdef notused
 223void jsonw_float(json_writer_t *self, double num)
 224{
 225        jsonw_printf(self, "%g", num);
 226}
 227#endif
 228
 229void jsonw_hu(json_writer_t *self, unsigned short num)
 230{
 231        jsonw_printf(self, "%hu", num);
 232}
 233
 234void jsonw_uint(json_writer_t *self, uint64_t num)
 235{
 236        jsonw_printf(self, "%"PRIu64, num);
 237}
 238
 239void jsonw_lluint(json_writer_t *self, unsigned long long int num)
 240{
 241        jsonw_printf(self, "%llu", num);
 242}
 243
 244void jsonw_int(json_writer_t *self, int64_t num)
 245{
 246        jsonw_printf(self, "%"PRId64, num);
 247}
 248
 249/* Basic name/value objects */
 250void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
 251{
 252        jsonw_name(self, prop);
 253        jsonw_string(self, val);
 254}
 255
 256void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
 257{
 258        jsonw_name(self, prop);
 259        jsonw_bool(self, val);
 260}
 261
 262#ifdef notused
 263void jsonw_float_field(json_writer_t *self, const char *prop, double val)
 264{
 265        jsonw_name(self, prop);
 266        jsonw_float(self, val);
 267}
 268#endif
 269
 270void jsonw_float_field_fmt(json_writer_t *self,
 271                           const char *prop,
 272                           const char *fmt,
 273                           double val)
 274{
 275        jsonw_name(self, prop);
 276        jsonw_float_fmt(self, fmt, val);
 277}
 278
 279void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
 280{
 281        jsonw_name(self, prop);
 282        jsonw_uint(self, num);
 283}
 284
 285void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
 286{
 287        jsonw_name(self, prop);
 288        jsonw_hu(self, num);
 289}
 290
 291void jsonw_lluint_field(json_writer_t *self,
 292                        const char *prop,
 293                        unsigned long long int num)
 294{
 295        jsonw_name(self, prop);
 296        jsonw_lluint(self, num);
 297}
 298
 299void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
 300{
 301        jsonw_name(self, prop);
 302        jsonw_int(self, num);
 303}
 304
 305void jsonw_null_field(json_writer_t *self, const char *prop)
 306{
 307        jsonw_name(self, prop);
 308        jsonw_null(self);
 309}
 310
 311#ifdef TEST
 312int main(int argc, char **argv)
 313{
 314        json_writer_t *wr = jsonw_new(stdout);
 315
 316        jsonw_start_object(wr);
 317        jsonw_pretty(wr, true);
 318        jsonw_name(wr, "Vyatta");
 319        jsonw_start_object(wr);
 320        jsonw_string_field(wr, "url", "http://vyatta.com");
 321        jsonw_uint_field(wr, "downloads", 2000000ul);
 322        jsonw_float_field(wr, "stock", 8.16);
 323
 324        jsonw_name(wr, "ARGV");
 325        jsonw_start_array(wr);
 326        while (--argc)
 327                jsonw_string(wr, *++argv);
 328        jsonw_end_array(wr);
 329
 330        jsonw_name(wr, "empty");
 331        jsonw_start_array(wr);
 332        jsonw_end_array(wr);
 333
 334        jsonw_name(wr, "NIL");
 335        jsonw_start_object(wr);
 336        jsonw_end_object(wr);
 337
 338        jsonw_null_field(wr, "my_null");
 339
 340        jsonw_name(wr, "special chars");
 341        jsonw_start_array(wr);
 342        jsonw_string_field(wr, "slash", "/");
 343        jsonw_string_field(wr, "newline", "\n");
 344        jsonw_string_field(wr, "tab", "\t");
 345        jsonw_string_field(wr, "ff", "\f");
 346        jsonw_string_field(wr, "quote", "\"");
 347        jsonw_string_field(wr, "tick", "\'");
 348        jsonw_string_field(wr, "backslash", "\\");
 349        jsonw_end_array(wr);
 350
 351        jsonw_end_object(wr);
 352
 353        jsonw_end_object(wr);
 354        jsonw_destroy(&wr);
 355        return 0;
 356}
 357
 358#endif
 359