/src/util-linux/lib/jsonwrt.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * JSON output formatting functions. |
3 | | * |
4 | | * No copyright is claimed. This code is in the public domain; do with |
5 | | * it what you wish. |
6 | | * |
7 | | * Written by Karel Zak <kzak@redhat.com> |
8 | | */ |
9 | | #include <stdio.h> |
10 | | #include <inttypes.h> |
11 | | #include <ctype.h> |
12 | | #include <cctype.h> |
13 | | |
14 | | #include "c.h" |
15 | | #include "jsonwrt.h" |
16 | | |
17 | | /* |
18 | | * Requirements enumerated via testing (V8, Firefox, IE11): |
19 | | * |
20 | | * var charsToEscape = []; |
21 | | * for (var i = 0; i < 65535; i += 1) { |
22 | | * try { |
23 | | * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); |
24 | | * } catch (e) { |
25 | | * charsToEscape.push(i); |
26 | | * } |
27 | | * } |
28 | | */ |
29 | | static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t size) |
30 | 0 | { |
31 | 0 | const char *p; |
32 | |
|
33 | 0 | fputc('"', out); |
34 | 0 | for (p = data; p && *p && (!size || p < data + size); p++) { |
35 | |
|
36 | 0 | const unsigned int c = (unsigned int) *p; |
37 | | |
38 | | /* From http://www.json.org |
39 | | * |
40 | | * The double-quote and backslashes would break out a string or |
41 | | * init an escape sequence if not escaped. |
42 | | * |
43 | | * Note that single-quotes and forward slashes, while they're |
44 | | * in the JSON spec, don't break double-quoted strings. |
45 | | */ |
46 | 0 | if (c == '"' || c == '\\') { |
47 | 0 | fputc('\\', out); |
48 | 0 | fputc(c, out); |
49 | 0 | continue; |
50 | 0 | } |
51 | | |
52 | | /* All non-control characters OK; do the case swap as required. */ |
53 | 0 | if (c >= 0x20) { |
54 | | /* |
55 | | * Don't use locale sensitive ctype.h functions for regular |
56 | | * ASCII chars, because for example with Turkish locale |
57 | | * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'. |
58 | | */ |
59 | 0 | if (c <= 127) |
60 | 0 | fputc(dir == 1 ? c_toupper(c) : |
61 | 0 | dir == -1 ? c_tolower(c) : *p, out); |
62 | 0 | else |
63 | 0 | fputc(dir == 1 ? toupper(c) : |
64 | 0 | dir == -1 ? tolower(c) : *p, out); |
65 | 0 | continue; |
66 | 0 | } |
67 | | |
68 | | /* In addition, all chars under ' ' break Node's/V8/Chrome's, and |
69 | | * Firefox's JSON.parse function |
70 | | */ |
71 | 0 | switch (c) { |
72 | | /* Handle short-hand cases to reduce output size. C |
73 | | * has most of the same stuff here, so if there's an |
74 | | * "Escape for C" function somewhere in the STL, we |
75 | | * should probably be using it. |
76 | | */ |
77 | 0 | case '\b': |
78 | 0 | fputs("\\b", out); |
79 | 0 | break; |
80 | 0 | case '\t': |
81 | 0 | fputs("\\t", out); |
82 | 0 | break; |
83 | 0 | case '\n': |
84 | 0 | fputs("\\n", out); |
85 | 0 | break; |
86 | 0 | case '\f': |
87 | 0 | fputs("\\f", out); |
88 | 0 | break; |
89 | 0 | case '\r': |
90 | 0 | fputs("\\r", out); |
91 | 0 | break; |
92 | 0 | default: |
93 | | /* Other assorted control characters */ |
94 | 0 | fprintf(out, "\\u00%02x", c); |
95 | 0 | break; |
96 | 0 | } |
97 | 0 | } |
98 | 0 | fputc('"', out); |
99 | 0 | } |
100 | | |
101 | 0 | #define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0, 0) |
102 | | #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0) |
103 | 0 | #define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0) |
104 | | |
105 | | void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) |
106 | 0 | { |
107 | 0 | fmt->out = out; |
108 | 0 | fmt->indent = indent; |
109 | 0 | fmt->after_close = 0; |
110 | 0 | } |
111 | | |
112 | | int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt) |
113 | 0 | { |
114 | 0 | return fmt->out == NULL ? 0 : 1; |
115 | 0 | } |
116 | | |
117 | | void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) |
118 | 0 | { |
119 | 0 | int i; |
120 | |
|
121 | 0 | for (i = 0; i < fmt->indent; i++) |
122 | 0 | fputs(" ", fmt->out); |
123 | 0 | } |
124 | | |
125 | | static void print_name(struct ul_jsonwrt *fmt, const char *name) |
126 | 0 | { |
127 | 0 | if (name) { |
128 | 0 | if (fmt->after_close) |
129 | 0 | fputs(",\n", fmt->out); |
130 | 0 | ul_jsonwrt_indent(fmt); |
131 | 0 | fputs_quoted_json_lower(name, fmt->out); |
132 | 0 | } else { |
133 | 0 | if (fmt->after_close) |
134 | 0 | fputs(",", fmt->out); |
135 | 0 | else |
136 | 0 | ul_jsonwrt_indent(fmt); |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) |
141 | 0 | { |
142 | 0 | print_name(fmt, name); |
143 | |
|
144 | 0 | switch (type) { |
145 | 0 | case UL_JSON_OBJECT: |
146 | 0 | fputs(name ? ": {\n" : "{\n", fmt->out); |
147 | 0 | fmt->indent++; |
148 | 0 | break; |
149 | 0 | case UL_JSON_ARRAY: |
150 | 0 | fputs(name ? ": [\n" : "[\n", fmt->out); |
151 | 0 | fmt->indent++; |
152 | 0 | break; |
153 | 0 | case UL_JSON_VALUE: |
154 | 0 | fputs(name ? ": " : " ", fmt->out); |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | fmt->after_close = 0; |
158 | 0 | } |
159 | | |
160 | | void ul_jsonwrt_empty(struct ul_jsonwrt *fmt, const char *name, int type) |
161 | 0 | { |
162 | 0 | print_name(fmt, name); |
163 | |
|
164 | 0 | switch (type) { |
165 | 0 | case UL_JSON_OBJECT: |
166 | 0 | fputs(name ? ": {}" : "{}", fmt->out); |
167 | 0 | break; |
168 | 0 | case UL_JSON_ARRAY: |
169 | 0 | fputs(name ? ": []" : "[]", fmt->out); |
170 | 0 | break; |
171 | 0 | case UL_JSON_VALUE: |
172 | 0 | fputs(name ? ": null" : "null", fmt->out); |
173 | 0 | break; |
174 | 0 | } |
175 | | |
176 | 0 | fmt->after_close = 1; |
177 | 0 | } |
178 | | |
179 | | void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) |
180 | 0 | { |
181 | 0 | assert(fmt->indent > 0); |
182 | | |
183 | 0 | switch (type) { |
184 | 0 | case UL_JSON_OBJECT: |
185 | 0 | fmt->indent--; |
186 | 0 | fputc('\n', fmt->out); |
187 | 0 | ul_jsonwrt_indent(fmt); |
188 | 0 | fputs("}", fmt->out); |
189 | 0 | if (fmt->indent == 0) |
190 | 0 | fputs("\n", fmt->out); |
191 | 0 | break; |
192 | 0 | case UL_JSON_ARRAY: |
193 | 0 | fmt->indent--; |
194 | 0 | fputc('\n', fmt->out); |
195 | 0 | ul_jsonwrt_indent(fmt); |
196 | 0 | fputs("]", fmt->out); |
197 | 0 | break; |
198 | 0 | case UL_JSON_VALUE: |
199 | 0 | break; |
200 | 0 | } |
201 | | |
202 | 0 | fmt->after_close = 1; |
203 | 0 | } |
204 | | |
205 | | |
206 | | void ul_jsonwrt_flush(struct ul_jsonwrt *fmt) |
207 | 0 | { |
208 | 0 | fflush(fmt->out); |
209 | 0 | } |
210 | | |
211 | | void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, |
212 | | const char *name, const char *data) |
213 | 0 | { |
214 | 0 | ul_jsonwrt_value_open(fmt, name); |
215 | 0 | if (data && *data) |
216 | 0 | fputs(data, fmt->out); |
217 | 0 | else |
218 | 0 | fputs("null", fmt->out); |
219 | 0 | ul_jsonwrt_value_close(fmt); |
220 | 0 | } |
221 | | |
222 | | void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, |
223 | | const char *name, const char *data) |
224 | 0 | { |
225 | 0 | ul_jsonwrt_value_open(fmt, name); |
226 | 0 | if (data && *data) |
227 | 0 | fputs_quoted_json(data, fmt->out); |
228 | 0 | else |
229 | 0 | fputs("null", fmt->out); |
230 | 0 | ul_jsonwrt_value_close(fmt); |
231 | 0 | } |
232 | | |
233 | | void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt, |
234 | | const char *name, const char *data, size_t size) |
235 | 0 | { |
236 | 0 | ul_jsonwrt_value_open(fmt, name); |
237 | 0 | if (data && *data) |
238 | 0 | fputs_quoted_case_json(data, fmt->out, 0, size); |
239 | 0 | else |
240 | 0 | fputs("null", fmt->out); |
241 | 0 | ul_jsonwrt_value_close(fmt); |
242 | 0 | } |
243 | | |
244 | | void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, |
245 | | const char *name, uint64_t data) |
246 | 0 | { |
247 | 0 | ul_jsonwrt_value_open(fmt, name); |
248 | 0 | fprintf(fmt->out, "%"PRIu64, data); |
249 | 0 | ul_jsonwrt_value_close(fmt); |
250 | 0 | } |
251 | | |
252 | | void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt, |
253 | | const char *name, long double data) |
254 | 0 | { |
255 | 0 | ul_jsonwrt_value_open(fmt, name); |
256 | 0 | fprintf(fmt->out, "%Lg", data); |
257 | 0 | ul_jsonwrt_value_close(fmt); |
258 | 0 | } |
259 | | |
260 | | void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, |
261 | | const char *name, int data) |
262 | 0 | { |
263 | 0 | ul_jsonwrt_value_open(fmt, name); |
264 | 0 | fputs(data ? "true" : "false", fmt->out); |
265 | 0 | ul_jsonwrt_value_close(fmt); |
266 | 0 | } |