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
18 static json_writer_t *_jw;
19
20 #define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)
21 #define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))
22
new_json_obj(int json)23 void new_json_obj(int json)
24 {
25 if (json) {
26 _jw = jsonw_new(stdout);
27 if (!_jw) {
28 perror("json object");
29 exit(1);
30 }
31 jsonw_pretty(_jw, true);
32 jsonw_start_array(_jw);
33 }
34 }
35
delete_json_obj(void)36 void delete_json_obj(void)
37 {
38 if (_jw) {
39 jsonw_end_array(_jw);
40 jsonw_destroy(&_jw);
41 }
42 }
43
is_json_context(void)44 bool is_json_context(void)
45 {
46 return _jw != NULL;
47 }
48
get_json_writer(void)49 json_writer_t *get_json_writer(void)
50 {
51 return _jw;
52 }
53
open_json_object(const char * str)54 void open_json_object(const char *str)
55 {
56 if (_IS_JSON_CONTEXT(PRINT_JSON)) {
57 if (str)
58 jsonw_name(_jw, str);
59 jsonw_start_object(_jw);
60 }
61 }
62
close_json_object(void)63 void close_json_object(void)
64 {
65 if (_IS_JSON_CONTEXT(PRINT_JSON))
66 jsonw_end_object(_jw);
67 }
68
69 /*
70 * Start json array or string array using
71 * the provided string as json key (if not null)
72 * or as array delimiter in non-json context.
73 */
open_json_array(enum output_type type,const char * str)74 void open_json_array(enum output_type type, const char *str)
75 {
76 if (_IS_JSON_CONTEXT(type)) {
77 if (str)
78 jsonw_name(_jw, str);
79 jsonw_start_array(_jw);
80 } else if (_IS_FP_CONTEXT(type)) {
81 printf("%s", str);
82 }
83 }
84
85 /*
86 * End json array or string array
87 */
close_json_array(enum output_type type,const char * str)88 void close_json_array(enum output_type type, const char *str)
89 {
90 if (_IS_JSON_CONTEXT(type)) {
91 jsonw_pretty(_jw, false);
92 jsonw_end_array(_jw);
93 jsonw_pretty(_jw, true);
94 } else if (_IS_FP_CONTEXT(type)) {
95 printf("%s", str);
96 }
97 }
98
99 /*
100 * pre-processor directive to generate similar
101 * functions handling different types
102 */
103 #define _PRINT_FUNC(type_name, type) \
104 void print_color_##type_name(enum output_type t, \
105 enum color_attr color, \
106 const char *key, \
107 const char *fmt, \
108 type value) \
109 { \
110 if (_IS_JSON_CONTEXT(t)) { \
111 if (!key) \
112 jsonw_##type_name(_jw, value); \
113 else \
114 jsonw_##type_name##_field(_jw, key, value); \
115 } else if (_IS_FP_CONTEXT(t)) { \
116 color_fprintf(stdout, color, fmt, value); \
117 } \
118 }
119 _PRINT_FUNC(int, int);
120 _PRINT_FUNC(hu, unsigned short);
121 _PRINT_FUNC(uint, uint64_t);
122 _PRINT_FUNC(lluint, unsigned long long int);
123 #undef _PRINT_FUNC
124
print_color_string(enum output_type type,enum color_attr color,const char * key,const char * fmt,const char * value)125 void print_color_string(enum output_type type,
126 enum color_attr color,
127 const char *key,
128 const char *fmt,
129 const char *value)
130 {
131 if (_IS_JSON_CONTEXT(type)) {
132 if (key && !value)
133 jsonw_name(_jw, key);
134 else if (!key && value)
135 jsonw_string(_jw, value);
136 else
137 jsonw_string_field(_jw, key, value);
138 } else if (_IS_FP_CONTEXT(type)) {
139 color_fprintf(stdout, color, fmt, value);
140 }
141 }
142
143 /*
144 * value's type is bool. When using this function in FP context you can't pass
145 * a value to it, you will need to use "is_json_context()" to have different
146 * branch for json and regular output. grep -r "print_bool" for example
147 */
print_color_bool(enum output_type type,enum color_attr color,const char * key,const char * fmt,bool value)148 void print_color_bool(enum output_type type,
149 enum color_attr color,
150 const char *key,
151 const char *fmt,
152 bool value)
153 {
154 if (_IS_JSON_CONTEXT(type)) {
155 if (key)
156 jsonw_bool_field(_jw, key, value);
157 else
158 jsonw_bool(_jw, value);
159 } else if (_IS_FP_CONTEXT(type)) {
160 color_fprintf(stdout, color, fmt, value ? "true" : "false");
161 }
162 }
163
164 /*
165 * In JSON context uses hardcode %#x format: 42 -> 0x2a
166 */
print_color_0xhex(enum output_type type,enum color_attr color,const char * key,const char * fmt,unsigned int hex)167 void print_color_0xhex(enum output_type type,
168 enum color_attr color,
169 const char *key,
170 const char *fmt,
171 unsigned int hex)
172 {
173 if (_IS_JSON_CONTEXT(type)) {
174 SPRINT_BUF(b1);
175
176 snprintf(b1, sizeof(b1), "%#x", hex);
177 print_string(PRINT_JSON, key, NULL, b1);
178 } else if (_IS_FP_CONTEXT(type)) {
179 color_fprintf(stdout, color, fmt, hex);
180 }
181 }
182
print_color_hex(enum output_type type,enum color_attr color,const char * key,const char * fmt,unsigned int hex)183 void print_color_hex(enum output_type type,
184 enum color_attr color,
185 const char *key,
186 const char *fmt,
187 unsigned int hex)
188 {
189 if (_IS_JSON_CONTEXT(type)) {
190 SPRINT_BUF(b1);
191
192 snprintf(b1, sizeof(b1), "%x", hex);
193 if (key)
194 jsonw_string_field(_jw, key, b1);
195 else
196 jsonw_string(_jw, b1);
197 } else if (_IS_FP_CONTEXT(type)) {
198 color_fprintf(stdout, color, fmt, hex);
199 }
200 }
201
202 /*
203 * In JSON context we don't use the argument "value" we simply call jsonw_null
204 * whereas FP context can use "value" to output anything
205 */
print_color_null(enum output_type type,enum color_attr color,const char * key,const char * fmt,const char * value)206 void print_color_null(enum output_type type,
207 enum color_attr color,
208 const char *key,
209 const char *fmt,
210 const char *value)
211 {
212 if (_IS_JSON_CONTEXT(type)) {
213 if (key)
214 jsonw_null_field(_jw, key);
215 else
216 jsonw_null(_jw);
217 } else if (_IS_FP_CONTEXT(type)) {
218 color_fprintf(stdout, color, fmt, value);
219 }
220 }
221