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 (at) 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 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 36 void delete_json_obj(void) 37 { 38 if (_jw) { 39 jsonw_end_array(_jw); 40 jsonw_destroy(&_jw); 41 } 42 } 43 44 bool is_json_context(void) 45 { 46 return _jw != NULL; 47 } 48 49 json_writer_t *get_json_writer(void) 50 { 51 return _jw; 52 } 53 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 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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