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