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