[{"id":1773032,"web_url":"http://patchwork.ozlabs.org/comment/1773032/","msgid":"<CAM_1_KzV_sYLiUY9QqXbM_NrkNU9Na-vwQeNcBFQ2V=rF6o6zA@mail.gmail.com>","list_archive_url":null,"date":"2017-09-21T17:47:41","subject":"Re: [PATCH iproute2 master 1/2] json: move json printer to common\n\tlibrary","submitter":{"id":70625,"url":"http://patchwork.ozlabs.org/api/people/70625/","name":"Julien Fortin","email":"julien@cumulusnetworks.com"},"content":"On Thu, Sep 21, 2017 at 1:42 AM, Daniel Borkmann <daniel@iogearbox.net> wrote:\n> Move the json printer which is based on json writer into the\n> iproute2 library, so it can be used by library code and tools\n> other than ip. Should probably have been done from the beginning\n> like that given json writer is in the library already anyway.\n> No functional changes.\n>\n> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>\nAcked-by: Julien Fortin <julien@cumulusnetworks.com>\n> ---\n>  include/json_print.h |  71 ++++++++++++++++\n>  ip/Makefile          |   2 +-\n>  ip/ip_common.h       |  65 ++------------\n>  ip/ip_print.c        | 233 ---------------------------------------------------\n>  lib/Makefile         |   2 +-\n>  lib/json_print.c     | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++\n>  6 files changed, 312 insertions(+), 292 deletions(-)\n>  create mode 100644 include/json_print.h\n>  delete mode 100644 ip/ip_print.c\n>  create mode 100644 lib/json_print.c\n>\n> diff --git a/include/json_print.h b/include/json_print.h\n> new file mode 100644\n> index 0000000..44cf5ac\n> --- /dev/null\n> +++ b/include/json_print.h\n> @@ -0,0 +1,71 @@\n> +/*\n> + * json_print.h                \"print regular or json output, based on json_writer\".\n> + *\n> + *             This program is free software; you can redistribute it and/or\n> + *             modify it under the terms of the GNU General Public License\n> + *             as published by the Free Software Foundation; either version\n> + *             2 of the License, or (at your option) any later version.\n> + *\n> + * Authors:    Julien Fortin, <julien@cumulusnetworks.com>\n> + */\n> +\n> +#ifndef _JSON_PRINT_H_\n> +#define _JSON_PRINT_H_\n> +\n> +#include \"json_writer.h\"\n> +#include \"color.h\"\n> +\n> +json_writer_t *get_json_writer(void);\n> +\n> +/*\n> + * use:\n> + *      - PRINT_ANY for context based output\n> + *      - PRINT_FP for non json specific output\n> + *      - PRINT_JSON for json specific output\n> + */\n> +enum output_type {\n> +       PRINT_FP = 1,\n> +       PRINT_JSON = 2,\n> +       PRINT_ANY = 4,\n> +};\n> +\n> +void new_json_obj(int json, FILE *fp);\n> +void delete_json_obj(void);\n> +\n> +bool is_json_context(void);\n> +\n> +void set_current_fp(FILE *fp);\n> +\n> +void fflush_fp(void);\n> +\n> +void open_json_object(const char *str);\n> +void close_json_object(void);\n> +void open_json_array(enum output_type type, const char *delim);\n> +void close_json_array(enum output_type type, const char *delim);\n> +\n> +#define _PRINT_FUNC(type_name, type)                                   \\\n> +       void print_color_##type_name(enum output_type t,                \\\n> +                                    enum color_attr color,             \\\n> +                                    const char *key,                   \\\n> +                                    const char *fmt,                   \\\n> +                                    type value);                       \\\n> +                                                                       \\\n> +       static inline void print_##type_name(enum output_type t,        \\\n> +                                            const char *key,           \\\n> +                                            const char *fmt,           \\\n> +                                            type value)                \\\n> +       {                                                               \\\n> +               print_color_##type_name(t, -1, key, fmt, value);        \\\n> +       }\n> +_PRINT_FUNC(int, int);\n> +_PRINT_FUNC(bool, bool);\n> +_PRINT_FUNC(null, const char*);\n> +_PRINT_FUNC(string, const char*);\n> +_PRINT_FUNC(uint, uint64_t);\n> +_PRINT_FUNC(hu, unsigned short);\n> +_PRINT_FUNC(hex, unsigned int);\n> +_PRINT_FUNC(0xhex, unsigned int);\n> +_PRINT_FUNC(lluint, unsigned long long int);\n> +#undef _PRINT_FUNC\n> +\n> +#endif /* _JSON_PRINT_H_ */\n> diff --git a/ip/Makefile b/ip/Makefile\n> index 52c9a2e..5a1c7ad 100644\n> --- a/ip/Makefile\n> +++ b/ip/Makefile\n> @@ -9,7 +9,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \\\n>      link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \\\n>      iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \\\n>      iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \\\n> -    ipvrf.o iplink_xstats.o ipseg6.o ip_print.o\n> +    ipvrf.o iplink_xstats.o ipseg6.o\n>\n>  RTMONOBJ=rtmon.o\n>\n> diff --git a/ip/ip_common.h b/ip/ip_common.h\n> index efc789c..4b8b0a7 100644\n> --- a/ip/ip_common.h\n> +++ b/ip/ip_common.h\n> @@ -1,3 +1,10 @@\n> +#ifndef _IP_COMMON_H_\n> +#define _IP_COMMON_H_\n> +\n> +#include <stdbool.h>\n> +\n> +#include \"json_print.h\"\n> +\n>  struct link_filter {\n>         int ifindex;\n>         int family;\n> @@ -101,8 +108,6 @@ static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)\n>\n>  extern struct rtnl_handle rth;\n>\n> -#include <stdbool.h>\n> -\n>  struct link_util {\n>         struct link_util        *next;\n>         const char              *id;\n> @@ -141,58 +146,4 @@ int name_is_vrf(const char *name);\n>\n>  void print_num(FILE *fp, unsigned int width, uint64_t count);\n>\n> -#include \"json_writer.h\"\n> -\n> -json_writer_t   *get_json_writer(void);\n> -/*\n> - * use:\n> - *      - PRINT_ANY for context based output\n> - *      - PRINT_FP for non json specific output\n> - *      - PRINT_JSON for json specific output\n> - */\n> -enum output_type {\n> -       PRINT_FP = 1,\n> -       PRINT_JSON = 2,\n> -       PRINT_ANY = 4,\n> -};\n> -\n> -void new_json_obj(int json, FILE *fp);\n> -void delete_json_obj(void);\n> -\n> -bool is_json_context(void);\n> -\n> -void set_current_fp(FILE *fp);\n> -\n> -void fflush_fp(void);\n> -\n> -void open_json_object(const char *str);\n> -void close_json_object(void);\n> -void open_json_array(enum output_type type, const char *delim);\n> -void close_json_array(enum output_type type, const char *delim);\n> -\n> -#include \"color.h\"\n> -\n> -#define _PRINT_FUNC(type_name, type)                                   \\\n> -       void print_color_##type_name(enum output_type t,                \\\n> -                                    enum color_attr color,             \\\n> -                                    const char *key,                   \\\n> -                                    const char *fmt,                   \\\n> -                                    type value);                       \\\n> -                                                                       \\\n> -       static inline void print_##type_name(enum output_type t,        \\\n> -                                            const char *key,           \\\n> -                                            const char *fmt,           \\\n> -                                            type value)                \\\n> -       {                                                               \\\n> -               print_color_##type_name(t, -1, key, fmt, value);        \\\n> -       }\n> -_PRINT_FUNC(int, int);\n> -_PRINT_FUNC(bool, bool);\n> -_PRINT_FUNC(null, const char*);\n> -_PRINT_FUNC(string, const char*);\n> -_PRINT_FUNC(uint, uint64_t);\n> -_PRINT_FUNC(hu, unsigned short);\n> -_PRINT_FUNC(hex, unsigned int);\n> -_PRINT_FUNC(0xhex, unsigned int);\n> -_PRINT_FUNC(lluint, unsigned long long int);\n> -#undef _PRINT_FUNC\n> +#endif /* _IP_COMMON_H_ */\n> diff --git a/ip/ip_print.c b/ip/ip_print.c\n> deleted file mode 100644\n> index 4cd6a0b..0000000\n> --- a/ip/ip_print.c\n> +++ /dev/null\n> @@ -1,233 +0,0 @@\n> -/*\n> - * ip_print.c          \"ip print regular or json output\".\n> - *\n> - *             This program is free software; you can redistribute it and/or\n> - *             modify it under the terms of the GNU General Public License\n> - *             as published by the Free Software Foundation; either version\n> - *             2 of the License, or (at your option) any later version.\n> - *\n> - * Authors:    Julien Fortin, <julien@cumulusnetworks.com>\n> - *\n> - */\n> -\n> -#include <stdarg.h>\n> -#include <stdio.h>\n> -\n> -#include \"utils.h\"\n> -#include \"ip_common.h\"\n> -#include \"json_writer.h\"\n> -\n> -static json_writer_t *_jw;\n> -static FILE *_fp;\n> -\n> -#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)\n> -#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))\n> -\n> -void new_json_obj(int json, FILE *fp)\n> -{\n> -       if (json) {\n> -               _jw = jsonw_new(fp);\n> -               if (!_jw) {\n> -                       perror(\"json object\");\n> -                       exit(1);\n> -               }\n> -               jsonw_pretty(_jw, true);\n> -               jsonw_start_array(_jw);\n> -       }\n> -       set_current_fp(fp);\n> -}\n> -\n> -void delete_json_obj(void)\n> -{\n> -       if (_jw) {\n> -               jsonw_end_array(_jw);\n> -               jsonw_destroy(&_jw);\n> -       }\n> -}\n> -\n> -bool is_json_context(void)\n> -{\n> -       return _jw != NULL;\n> -}\n> -\n> -void set_current_fp(FILE *fp)\n> -{\n> -       if (!fp) {\n> -               fprintf(stderr, \"Error: invalid file pointer.\\n\");\n> -               exit(1);\n> -       }\n> -       _fp = fp;\n> -}\n> -\n> -json_writer_t *get_json_writer(void)\n> -{\n> -       return _jw;\n> -}\n> -\n> -void open_json_object(const char *str)\n> -{\n> -       if (_IS_JSON_CONTEXT(PRINT_JSON)) {\n> -               if (str)\n> -                       jsonw_name(_jw, str);\n> -               jsonw_start_object(_jw);\n> -       }\n> -}\n> -\n> -void close_json_object(void)\n> -{\n> -       if (_IS_JSON_CONTEXT(PRINT_JSON))\n> -               jsonw_end_object(_jw);\n> -}\n> -\n> -/*\n> - * Start json array or string array using\n> - * the provided string as json key (if not null)\n> - * or as array delimiter in non-json context.\n> - */\n> -void open_json_array(enum output_type type, const char *str)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               if (str)\n> -                       jsonw_name(_jw, str);\n> -               jsonw_start_array(_jw);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               fprintf(_fp, \"%s\", str);\n> -       }\n> -}\n> -\n> -/*\n> - * End json array or string array\n> - */\n> -void close_json_array(enum output_type type, const char *str)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               jsonw_pretty(_jw, false);\n> -               jsonw_end_array(_jw);\n> -               jsonw_pretty(_jw, true);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               fprintf(_fp, \"%s\", str);\n> -       }\n> -}\n> -\n> -/*\n> - * pre-processor directive to generate similar\n> - * functions handling different types\n> - */\n> -#define _PRINT_FUNC(type_name, type)                                   \\\n> -       void print_color_##type_name(enum output_type t,                \\\n> -                                    enum color_attr color,             \\\n> -                                    const char *key,                   \\\n> -                                    const char *fmt,                   \\\n> -                                    type value)                        \\\n> -       {                                                               \\\n> -               if (_IS_JSON_CONTEXT(t)) {                              \\\n> -                       if (!key)                                       \\\n> -                               jsonw_##type_name(_jw, value);          \\\n> -                       else                                            \\\n> -                               jsonw_##type_name##_field(_jw, key, value); \\\n> -               } else if (_IS_FP_CONTEXT(t)) {                         \\\n> -                       color_fprintf(_fp, color, fmt, value);          \\\n> -               }                                                       \\\n> -       }\n> -_PRINT_FUNC(int, int);\n> -_PRINT_FUNC(hu, unsigned short);\n> -_PRINT_FUNC(uint, uint64_t);\n> -_PRINT_FUNC(lluint, unsigned long long int);\n> -#undef _PRINT_FUNC\n> -\n> -void print_color_string(enum output_type type,\n> -                       enum color_attr color,\n> -                       const char *key,\n> -                       const char *fmt,\n> -                       const char *value)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               if (key && !value)\n> -                       jsonw_name(_jw, key);\n> -               else if (!key && value)\n> -                       jsonw_string(_jw, value);\n> -               else\n> -                       jsonw_string_field(_jw, key, value);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               color_fprintf(_fp, color, fmt, value);\n> -       }\n> -}\n> -\n> -/*\n> - * value's type is bool. When using this function in FP context you can't pass\n> - * a value to it, you will need to use \"is_json_context()\" to have different\n> - * branch for json and regular output. grep -r \"print_bool\" for example\n> - */\n> -void print_color_bool(enum output_type type,\n> -                     enum color_attr color,\n> -                     const char *key,\n> -                     const char *fmt,\n> -                     bool value)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               if (key)\n> -                       jsonw_bool_field(_jw, key, value);\n> -               else\n> -                       jsonw_bool(_jw, value);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               color_fprintf(_fp, color, fmt, value ? \"true\" : \"false\");\n> -       }\n> -}\n> -\n> -/*\n> - * In JSON context uses hardcode %#x format: 42 -> 0x2a\n> - */\n> -void print_color_0xhex(enum output_type type,\n> -                      enum color_attr color,\n> -                      const char *key,\n> -                      const char *fmt,\n> -                      unsigned int hex)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               SPRINT_BUF(b1);\n> -\n> -               snprintf(b1, sizeof(b1), \"%#x\", hex);\n> -               print_string(PRINT_JSON, key, NULL, b1);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               color_fprintf(_fp, color, fmt, hex);\n> -       }\n> -}\n> -\n> -void print_color_hex(enum output_type type,\n> -                    enum color_attr color,\n> -                    const char *key,\n> -                    const char *fmt,\n> -                    unsigned int hex)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               SPRINT_BUF(b1);\n> -\n> -               snprintf(b1, sizeof(b1), \"%x\", hex);\n> -               if (key)\n> -                       jsonw_string_field(_jw, key, b1);\n> -               else\n> -                       jsonw_string(_jw, b1);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               color_fprintf(_fp, color, fmt, hex);\n> -       }\n> -}\n> -\n> -/*\n> - * In JSON context we don't use the argument \"value\" we simply call jsonw_null\n> - * whereas FP context can use \"value\" to output anything\n> - */\n> -void print_color_null(enum output_type type,\n> -                     enum color_attr color,\n> -                     const char *key,\n> -                     const char *fmt,\n> -                     const char *value)\n> -{\n> -       if (_IS_JSON_CONTEXT(type)) {\n> -               if (key)\n> -                       jsonw_null_field(_jw, key);\n> -               else\n> -                       jsonw_null(_jw);\n> -       } else if (_IS_FP_CONTEXT(type)) {\n> -               color_fprintf(_fp, color, fmt, value);\n> -       }\n> -}\n> diff --git a/lib/Makefile b/lib/Makefile\n> index 5e9f72f..0fbdf4c 100644\n> --- a/lib/Makefile\n> +++ b/lib/Makefile\n> @@ -3,7 +3,7 @@ include ../config.mk\n>  CFLAGS += -fPIC\n>\n>  UTILOBJ = utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o \\\n> -       inet_proto.o namespace.o json_writer.o \\\n> +       inet_proto.o namespace.o json_writer.o json_print.o \\\n>         names.o color.o bpf.o exec.o fs.o\n>\n>  NLOBJ=libgenl.o ll_map.o libnetlink.o\n> diff --git a/lib/json_print.c b/lib/json_print.c\n> new file mode 100644\n> index 0000000..93b4119\n> --- /dev/null\n> +++ b/lib/json_print.c\n> @@ -0,0 +1,231 @@\n> +/*\n> + * json_print.c                \"print regular or json output, based on json_writer\".\n> + *\n> + *             This program is free software; you can redistribute it and/or\n> + *             modify it under the terms of the GNU General Public License\n> + *             as published by the Free Software Foundation; either version\n> + *             2 of the License, or (at your option) any later version.\n> + *\n> + * Authors:    Julien Fortin, <julien@cumulusnetworks.com>\n> + */\n> +\n> +#include <stdarg.h>\n> +#include <stdio.h>\n> +\n> +#include \"utils.h\"\n> +#include \"json_print.h\"\n> +\n> +static json_writer_t *_jw;\n> +static FILE *_fp;\n> +\n> +#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)\n> +#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))\n> +\n> +void new_json_obj(int json, FILE *fp)\n> +{\n> +       if (json) {\n> +               _jw = jsonw_new(fp);\n> +               if (!_jw) {\n> +                       perror(\"json object\");\n> +                       exit(1);\n> +               }\n> +               jsonw_pretty(_jw, true);\n> +               jsonw_start_array(_jw);\n> +       }\n> +       set_current_fp(fp);\n> +}\n> +\n> +void delete_json_obj(void)\n> +{\n> +       if (_jw) {\n> +               jsonw_end_array(_jw);\n> +               jsonw_destroy(&_jw);\n> +       }\n> +}\n> +\n> +bool is_json_context(void)\n> +{\n> +       return _jw != NULL;\n> +}\n> +\n> +void set_current_fp(FILE *fp)\n> +{\n> +       if (!fp) {\n> +               fprintf(stderr, \"Error: invalid file pointer.\\n\");\n> +               exit(1);\n> +       }\n> +       _fp = fp;\n> +}\n> +\n> +json_writer_t *get_json_writer(void)\n> +{\n> +       return _jw;\n> +}\n> +\n> +void open_json_object(const char *str)\n> +{\n> +       if (_IS_JSON_CONTEXT(PRINT_JSON)) {\n> +               if (str)\n> +                       jsonw_name(_jw, str);\n> +               jsonw_start_object(_jw);\n> +       }\n> +}\n> +\n> +void close_json_object(void)\n> +{\n> +       if (_IS_JSON_CONTEXT(PRINT_JSON))\n> +               jsonw_end_object(_jw);\n> +}\n> +\n> +/*\n> + * Start json array or string array using\n> + * the provided string as json key (if not null)\n> + * or as array delimiter in non-json context.\n> + */\n> +void open_json_array(enum output_type type, const char *str)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               if (str)\n> +                       jsonw_name(_jw, str);\n> +               jsonw_start_array(_jw);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               fprintf(_fp, \"%s\", str);\n> +       }\n> +}\n> +\n> +/*\n> + * End json array or string array\n> + */\n> +void close_json_array(enum output_type type, const char *str)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               jsonw_pretty(_jw, false);\n> +               jsonw_end_array(_jw);\n> +               jsonw_pretty(_jw, true);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               fprintf(_fp, \"%s\", str);\n> +       }\n> +}\n> +\n> +/*\n> + * pre-processor directive to generate similar\n> + * functions handling different types\n> + */\n> +#define _PRINT_FUNC(type_name, type)                                   \\\n> +       void print_color_##type_name(enum output_type t,                \\\n> +                                    enum color_attr color,             \\\n> +                                    const char *key,                   \\\n> +                                    const char *fmt,                   \\\n> +                                    type value)                        \\\n> +       {                                                               \\\n> +               if (_IS_JSON_CONTEXT(t)) {                              \\\n> +                       if (!key)                                       \\\n> +                               jsonw_##type_name(_jw, value);          \\\n> +                       else                                            \\\n> +                               jsonw_##type_name##_field(_jw, key, value); \\\n> +               } else if (_IS_FP_CONTEXT(t)) {                         \\\n> +                       color_fprintf(_fp, color, fmt, value);          \\\n> +               }                                                       \\\n> +       }\n> +_PRINT_FUNC(int, int);\n> +_PRINT_FUNC(hu, unsigned short);\n> +_PRINT_FUNC(uint, uint64_t);\n> +_PRINT_FUNC(lluint, unsigned long long int);\n> +#undef _PRINT_FUNC\n> +\n> +void print_color_string(enum output_type type,\n> +                       enum color_attr color,\n> +                       const char *key,\n> +                       const char *fmt,\n> +                       const char *value)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               if (key && !value)\n> +                       jsonw_name(_jw, key);\n> +               else if (!key && value)\n> +                       jsonw_string(_jw, value);\n> +               else\n> +                       jsonw_string_field(_jw, key, value);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               color_fprintf(_fp, color, fmt, value);\n> +       }\n> +}\n> +\n> +/*\n> + * value's type is bool. When using this function in FP context you can't pass\n> + * a value to it, you will need to use \"is_json_context()\" to have different\n> + * branch for json and regular output. grep -r \"print_bool\" for example\n> + */\n> +void print_color_bool(enum output_type type,\n> +                     enum color_attr color,\n> +                     const char *key,\n> +                     const char *fmt,\n> +                     bool value)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               if (key)\n> +                       jsonw_bool_field(_jw, key, value);\n> +               else\n> +                       jsonw_bool(_jw, value);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               color_fprintf(_fp, color, fmt, value ? \"true\" : \"false\");\n> +       }\n> +}\n> +\n> +/*\n> + * In JSON context uses hardcode %#x format: 42 -> 0x2a\n> + */\n> +void print_color_0xhex(enum output_type type,\n> +                      enum color_attr color,\n> +                      const char *key,\n> +                      const char *fmt,\n> +                      unsigned int hex)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               SPRINT_BUF(b1);\n> +\n> +               snprintf(b1, sizeof(b1), \"%#x\", hex);\n> +               print_string(PRINT_JSON, key, NULL, b1);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               color_fprintf(_fp, color, fmt, hex);\n> +       }\n> +}\n> +\n> +void print_color_hex(enum output_type type,\n> +                    enum color_attr color,\n> +                    const char *key,\n> +                    const char *fmt,\n> +                    unsigned int hex)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               SPRINT_BUF(b1);\n> +\n> +               snprintf(b1, sizeof(b1), \"%x\", hex);\n> +               if (key)\n> +                       jsonw_string_field(_jw, key, b1);\n> +               else\n> +                       jsonw_string(_jw, b1);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               color_fprintf(_fp, color, fmt, hex);\n> +       }\n> +}\n> +\n> +/*\n> + * In JSON context we don't use the argument \"value\" we simply call jsonw_null\n> + * whereas FP context can use \"value\" to output anything\n> + */\n> +void print_color_null(enum output_type type,\n> +                     enum color_attr color,\n> +                     const char *key,\n> +                     const char *fmt,\n> +                     const char *value)\n> +{\n> +       if (_IS_JSON_CONTEXT(type)) {\n> +               if (key)\n> +                       jsonw_null_field(_jw, key);\n> +               else\n> +                       jsonw_null(_jw);\n> +       } else if (_IS_FP_CONTEXT(type)) {\n> +               color_fprintf(_fp, color, fmt, value);\n> +       }\n> +}\n> --\n> 1.9.3\n>","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=cumulusnetworks.com\n\theader.i=@cumulusnetworks.com header.b=\"PWIeTDty\"; \n\tdkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xykbr1WmLz9t3Z\n\tfor <patchwork-incoming@ozlabs.org>;\n\tFri, 22 Sep 2017 03:48:08 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751809AbdIURsG (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tThu, 21 Sep 2017 13:48:06 -0400","from mail-vk0-f51.google.com ([209.85.213.51]:53599 \"EHLO\n\tmail-vk0-f51.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751693AbdIURsD (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Thu, 21 Sep 2017 13:48:03 -0400","by mail-vk0-f51.google.com with SMTP id m140so3506171vka.10\n\tfor <netdev@vger.kernel.org>; Thu, 21 Sep 2017 10:48:03 -0700 (PDT)","by 10.159.50.194 with HTTP; Thu, 21 Sep 2017 10:47:41 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=cumulusnetworks.com; s=google;\n\th=mime-version:in-reply-to:references:from:date:message-id:subject:to\n\t:cc; bh=6bokc1rUNFu/Oa2XFMjZKsCSFTA4HkN221i0ccGklqA=;\n\tb=PWIeTDtyxZF/ssYnQ5SdrVHKmKeGb1v2RqeDMaxbLH6VZ/IRqXtx+zUymAk8OQCE8D\n\tK+N1gM48wQfAXo9MxGEIzma4H6oY/cmpu6DZarxgxOJwZ0JVKKvGtIHanXtQsfWWpa/K\n\t+cd5doXzTulqsVTMJU/c7J8//2+QnV0sZXwcM=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:in-reply-to:references:from:date\n\t:message-id:subject:to:cc;\n\tbh=6bokc1rUNFu/Oa2XFMjZKsCSFTA4HkN221i0ccGklqA=;\n\tb=IqTceSuUtCG6dAsfc6dplzVaGba69h13/tTx543gd+8QJdaLZw/ludCu/OksPFhkTb\n\tB3Jr+XuGwIUtJA3dmhdXZRxYSpK4+UJoQnBJNp6mpJ7x+0wr28k2gPFmRyXbD1VLlIeD\n\t7xCF9LUVuwiXZWfs6BwCplh/RlZmL6GDDtqa+Fx1eEsc6Gyx2XTPawDwbZk3C5VA+bxZ\n\tu9DHQ4D/HL6YDuiKX4FxvVdvAtdyHNoZadilvK37C7jtobKfh9VQaDfmBcURk4yMgoH6\n\t0ShP3HqVNO+io8Ke7gi0ZvvoOtX2QAJnux7nvCM5sE4KtxpNVfuFFpXUlXXS7wsS84oX\n\tjAXw==","X-Gm-Message-State":"AHPjjUjgdx0A+RPBgqP2vxnu1evMt0CHrQAkpjWiIxqshSNgvMjyeKgg\n\tXKCVUTjzQmPwNEQR9rnCc0AUdpwXQwdZXdL8gjqg+KU=","X-Google-Smtp-Source":"AOwi7QB3r2i7GrhvKvqRlgL+Bj7Rpbb9mSE6T/6HjEdUuSTPbZIvZXc/61z0tVPfYR9XYvErc1sL6CibA+/GQxyB2cM=","X-Received":"by 10.31.147.204 with SMTP id v195mr2453109vkd.137.1506016082193;\n\tThu, 21 Sep 2017 10:48:02 -0700 (PDT)","MIME-Version":"1.0","In-Reply-To":"<635a857b7b32cd0df79805f8e32b20a2674a675d.1505956723.git.daniel@iogearbox.net>","References":"<cover.1505956723.git.daniel@iogearbox.net>\n\t<635a857b7b32cd0df79805f8e32b20a2674a675d.1505956723.git.daniel@iogearbox.net>","From":"Julien Fortin <julien@cumulusnetworks.com>","Date":"Thu, 21 Sep 2017 10:47:41 -0700","Message-ID":"<CAM_1_KzV_sYLiUY9QqXbM_NrkNU9Na-vwQeNcBFQ2V=rF6o6zA@mail.gmail.com>","Subject":"Re: [PATCH iproute2 master 1/2] json: move json printer to common\n\tlibrary","To":"Daniel Borkmann <daniel@iogearbox.net>","Cc":"Stephen Hemminger <stephen@networkplumber.org>, ast@fb.com,\n\tnetdev@vger.kernel.org","Content-Type":"text/plain; charset=\"UTF-8\"","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}}]