iproute2/lib/json_print.c
<<
>>
Prefs
   1/*
   2 * json_print.c         "print regular or json output, based on json_writer".
   3 *
   4 *             This program is free software; you can redistribute it and/or
   5 *             modify it under the terms of the GNU General Public License
   6 *             as published by the Free Software Foundation; either version
   7 *             2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:    Julien Fortin, <julien@cumulusnetworks.com>
  10 */
  11
  12#include <stdarg.h>
  13#include <stdio.h>
  14
  15#include "utils.h"
  16#include "json_print.h"
  17
  18static json_writer_t *_jw;
  19
  20static void __new_json_obj(int json, bool have_array)
  21{
  22        if (json) {
  23                _jw = jsonw_new(stdout);
  24                if (!_jw) {
  25                        perror("json object");
  26                        exit(1);
  27                }
  28                if (pretty)
  29                        jsonw_pretty(_jw, true);
  30                if (have_array)
  31                        jsonw_start_array(_jw);
  32        }
  33}
  34
  35static void __delete_json_obj(bool have_array)
  36{
  37        if (_jw) {
  38                if (have_array)
  39                        jsonw_end_array(_jw);
  40                jsonw_destroy(&_jw);
  41        }
  42}
  43
  44void new_json_obj(int json)
  45{
  46        __new_json_obj(json, true);
  47}
  48
  49void delete_json_obj(void)
  50{
  51        __delete_json_obj(true);
  52}
  53
  54void new_json_obj_plain(int json)
  55{
  56        __new_json_obj(json, false);
  57}
  58
  59void delete_json_obj_plain(void)
  60{
  61        __delete_json_obj(false);
  62}
  63
  64bool is_json_context(void)
  65{
  66        return _jw != NULL;
  67}
  68
  69json_writer_t *get_json_writer(void)
  70{
  71        return _jw;
  72}
  73
  74void open_json_object(const char *str)
  75{
  76        if (_IS_JSON_CONTEXT(PRINT_JSON)) {
  77                if (str)
  78                        jsonw_name(_jw, str);
  79                jsonw_start_object(_jw);
  80        }
  81}
  82
  83void close_json_object(void)
  84{
  85        if (_IS_JSON_CONTEXT(PRINT_JSON))
  86                jsonw_end_object(_jw);
  87}
  88
  89/*
  90 * Start json array or string array using
  91 * the provided string as json key (if not null)
  92 * or as array delimiter in non-json context.
  93 */
  94void open_json_array(enum output_type type, const char *str)
  95{
  96        if (_IS_JSON_CONTEXT(type)) {
  97                if (str)
  98                        jsonw_name(_jw, str);
  99                jsonw_start_array(_jw);
 100        } else if (_IS_FP_CONTEXT(type)) {
 101                printf("%s", str);
 102        }
 103}
 104
 105/*
 106 * End json array or string array
 107 */
 108void close_json_array(enum output_type type, const char *str)
 109{
 110        if (_IS_JSON_CONTEXT(type)) {
 111                jsonw_end_array(_jw);
 112        } else if (_IS_FP_CONTEXT(type)) {
 113                printf("%s", str);
 114        }
 115}
 116
 117/*
 118 * pre-processor directive to generate similar
 119 * functions handling different types
 120 */
 121#define _PRINT_FUNC(type_name, type)                                    \
 122        __attribute__((format(printf, 4, 0)))                           \
 123        int print_color_##type_name(enum output_type t,                 \
 124                                    enum color_attr color,              \
 125                                    const char *key,                    \
 126                                    const char *fmt,                    \
 127                                    type value)                         \
 128        {                                                               \
 129                int ret = 0;                                            \
 130                if (_IS_JSON_CONTEXT(t)) {                              \
 131                        if (!key)                                       \
 132                                jsonw_##type_name(_jw, value);          \
 133                        else                                            \
 134                                jsonw_##type_name##_field(_jw, key, value); \
 135                } else if (_IS_FP_CONTEXT(t)) {                         \
 136                        ret = color_fprintf(stdout, color, fmt, value); \
 137                }                                                       \
 138                return ret;                                             \
 139        }
 140_PRINT_FUNC(int, int);
 141_PRINT_FUNC(s64, int64_t);
 142_PRINT_FUNC(hhu, unsigned char);
 143_PRINT_FUNC(hu, unsigned short);
 144_PRINT_FUNC(uint, unsigned int);
 145_PRINT_FUNC(u64, uint64_t);
 146_PRINT_FUNC(luint, unsigned long);
 147_PRINT_FUNC(lluint, unsigned long long);
 148_PRINT_FUNC(float, double);
 149#undef _PRINT_FUNC
 150
 151#define _PRINT_NAME_VALUE_FUNC(type_name, type, format_char)             \
 152        void print_##type_name##_name_value(const char *name, type value)\
 153        {                                                                \
 154                SPRINT_BUF(format);                                      \
 155                                                                         \
 156                snprintf(format, SPRINT_BSIZE,                           \
 157                         "%s %%"#format_char, name);                     \
 158                print_##type_name(PRINT_ANY, name, format, value);       \
 159        }
 160_PRINT_NAME_VALUE_FUNC(uint, unsigned int, u);
 161_PRINT_NAME_VALUE_FUNC(string, const char*, s);
 162#undef _PRINT_NAME_VALUE_FUNC
 163
 164int print_color_string(enum output_type type,
 165                       enum color_attr color,
 166                       const char *key,
 167                       const char *fmt,
 168                       const char *value)
 169{
 170        int ret = 0;
 171
 172        if (_IS_JSON_CONTEXT(type)) {
 173                if (key && !value)
 174                        jsonw_name(_jw, key);
 175                else if (!key && value)
 176                        jsonw_string(_jw, value);
 177                else
 178                        jsonw_string_field(_jw, key, value);
 179        } else if (_IS_FP_CONTEXT(type)) {
 180                ret = color_fprintf(stdout, color, fmt, value);
 181        }
 182
 183        return ret;
 184}
 185
 186/*
 187 * value's type is bool. When using this function in FP context you can't pass
 188 * a value to it, you will need to use "is_json_context()" to have different
 189 * branch for json and regular output. grep -r "print_bool" for example
 190 */
 191static int __print_color_bool(enum output_type type,
 192                              enum color_attr color,
 193                              const char *key,
 194                              const char *fmt,
 195                              bool value,
 196                              const char *str)
 197{
 198        int ret = 0;
 199
 200        if (_IS_JSON_CONTEXT(type)) {
 201                if (key)
 202                        jsonw_bool_field(_jw, key, value);
 203                else
 204                        jsonw_bool(_jw, value);
 205        } else if (_IS_FP_CONTEXT(type)) {
 206                ret = color_fprintf(stdout, color, fmt, str);
 207        }
 208
 209        return ret;
 210}
 211
 212int print_color_bool(enum output_type type,
 213                     enum color_attr color,
 214                     const char *key,
 215                     const char *fmt,
 216                     bool value)
 217{
 218        return __print_color_bool(type, color, key, fmt, value,
 219                                  value ? "true" : "false");
 220}
 221
 222int print_color_on_off(enum output_type type,
 223                       enum color_attr color,
 224                       const char *key,
 225                       const char *fmt,
 226                       bool value)
 227{
 228        return __print_color_bool(type, color, key, fmt, value,
 229                                  value ? "on" : "off");
 230}
 231
 232/*
 233 * In JSON context uses hardcode %#x format: 42 -> 0x2a
 234 */
 235int print_color_0xhex(enum output_type type,
 236                      enum color_attr color,
 237                      const char *key,
 238                      const char *fmt,
 239                      unsigned long long hex)
 240{
 241        int ret = 0;
 242
 243        if (_IS_JSON_CONTEXT(type)) {
 244                SPRINT_BUF(b1);
 245
 246                snprintf(b1, sizeof(b1), "%#llx", hex);
 247                print_string(PRINT_JSON, key, NULL, b1);
 248        } else if (_IS_FP_CONTEXT(type)) {
 249                ret = color_fprintf(stdout, color, fmt, hex);
 250        }
 251
 252        return ret;
 253}
 254
 255int print_color_hex(enum output_type type,
 256                    enum color_attr color,
 257                    const char *key,
 258                    const char *fmt,
 259                    unsigned int hex)
 260{
 261        int ret = 0;
 262
 263        if (_IS_JSON_CONTEXT(type)) {
 264                SPRINT_BUF(b1);
 265
 266                snprintf(b1, sizeof(b1), "%x", hex);
 267                if (key)
 268                        jsonw_string_field(_jw, key, b1);
 269                else
 270                        jsonw_string(_jw, b1);
 271        } else if (_IS_FP_CONTEXT(type)) {
 272                ret = color_fprintf(stdout, color, fmt, hex);
 273        }
 274
 275        return ret;
 276}
 277
 278/*
 279 * In JSON context we don't use the argument "value" we simply call jsonw_null
 280 * whereas FP context can use "value" to output anything
 281 */
 282int print_color_null(enum output_type type,
 283                     enum color_attr color,
 284                     const char *key,
 285                     const char *fmt,
 286                     const char *value)
 287{
 288        int ret = 0;
 289
 290        if (_IS_JSON_CONTEXT(type)) {
 291                if (key)
 292                        jsonw_null_field(_jw, key);
 293                else
 294                        jsonw_null(_jw);
 295        } else if (_IS_FP_CONTEXT(type)) {
 296                ret = color_fprintf(stdout, color, fmt, value);
 297        }
 298
 299        return ret;
 300}
 301
 302int print_color_tv(enum output_type type,
 303                   enum color_attr color,
 304                   const char *key,
 305                   const char *fmt,
 306                   const struct timeval *tv)
 307{
 308        double usecs = tv->tv_usec;
 309        double secs = tv->tv_sec;
 310        double time = secs + usecs / 1000000;
 311
 312        return print_color_float(type, color, key, fmt, time);
 313}
 314
 315/* Print line separator (if not in JSON mode) */
 316void print_nl(void)
 317{
 318        if (!_jw)
 319                printf("%s", _SL_);
 320}
 321
 322int print_color_rate(bool use_iec, enum output_type type, enum color_attr color,
 323                     const char *key, const char *fmt, unsigned long long rate)
 324{
 325        unsigned long kilo = use_iec ? 1024 : 1000;
 326        const char *str = use_iec ? "i" : "";
 327        static char *units[5] = {"", "K", "M", "G", "T"};
 328        char *buf;
 329        int rc;
 330        int i;
 331
 332        if (_IS_JSON_CONTEXT(type))
 333                return print_color_lluint(type, color, key, "%llu", rate);
 334
 335        rate <<= 3; /* bytes/sec -> bits/sec */
 336
 337        for (i = 0; i < ARRAY_SIZE(units) - 1; i++)  {
 338                if (rate < kilo)
 339                        break;
 340                if (((rate % kilo) != 0) && rate < 1000*kilo)
 341                        break;
 342                rate /= kilo;
 343        }
 344
 345        rc = asprintf(&buf, "%.0f%s%sbit", (double)rate, units[i],
 346                      i > 0 ? str : "");
 347        if (rc < 0)
 348                return -1;
 349
 350        rc = print_color_string(type, color, key, fmt, buf);
 351        free(buf);
 352        return rc;
 353}
 354