]>
Commit | Line | Data |
---|---|---|
37bcd056 KZ |
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> | |
64a89ada KZ |
11 | #include <ctype.h> |
12 | #include <cctype.h> | |
37bcd056 KZ |
13 | |
14 | #include "c.h" | |
37bcd056 KZ |
15 | #include "jsonwrt.h" |
16 | ||
3a07505a KZ |
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) | |
30 | { | |
31 | const char *p; | |
32 | ||
33 | fputc('"', out); | |
34 | for (p = data; p && *p; p++) { | |
35 | ||
64a89ada | 36 | const unsigned int c = (unsigned int) *p; |
3a07505a KZ |
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 | if (c == '"' || c == '\\') { | |
47 | fputc('\\', out); | |
48 | fputc(c, out); | |
49 | continue; | |
50 | } | |
51 | ||
52 | /* All non-control characters OK; do the case swap as required. */ | |
53 | if (c >= 0x20) { | |
64a89ada KZ |
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 | if (c <= 127) | |
60 | fputc(dir == 1 ? c_toupper(c) : | |
61 | dir == -1 ? c_tolower(c) : *p, out); | |
62 | else | |
63 | fputc(dir == 1 ? toupper(c) : | |
64 | dir == -1 ? tolower(c) : *p, out); | |
3a07505a KZ |
65 | continue; |
66 | } | |
67 | ||
68 | /* In addition, all chars under ' ' break Node's/V8/Chrome's, and | |
69 | * Firefox's JSON.parse function | |
70 | */ | |
71 | 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 | case '\b': | |
78 | fputs("\\b", out); | |
79 | break; | |
80 | case '\t': | |
81 | fputs("\\t", out); | |
82 | break; | |
83 | case '\n': | |
84 | fputs("\\n", out); | |
85 | break; | |
86 | case '\f': | |
87 | fputs("\\f", out); | |
88 | break; | |
89 | case '\r': | |
90 | fputs("\\r", out); | |
91 | break; | |
92 | default: | |
93 | /* Other assorted control characters */ | |
94 | fprintf(out, "\\u00%02x", c); | |
95 | break; | |
96 | } | |
97 | } | |
98 | fputc('"', out); | |
99 | } | |
100 | ||
101 | #define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) | |
102 | #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) | |
103 | #define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) | |
37bcd056 KZ |
104 | |
105 | void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) | |
106 | { | |
107 | fmt->out = out; | |
108 | fmt->indent = indent; | |
d124a780 | 109 | fmt->after_close = 0; |
37bcd056 KZ |
110 | } |
111 | ||
195993d5 KZ |
112 | int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt) |
113 | { | |
114 | return fmt->out == NULL ? 0 : 1; | |
115 | } | |
116 | ||
37bcd056 KZ |
117 | void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) |
118 | { | |
119 | int i; | |
120 | ||
121 | for (i = 0; i < fmt->indent; i++) | |
122 | fputs(" ", fmt->out); | |
123 | } | |
124 | ||
125 | void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) | |
126 | { | |
d124a780 KZ |
127 | if (name) { |
128 | if (fmt->after_close) | |
129 | fputs(",\n", fmt->out); | |
37bcd056 | 130 | ul_jsonwrt_indent(fmt); |
d124a780 KZ |
131 | fputs_quoted_json_lower(name, fmt->out); |
132 | } else { | |
133 | if (fmt->after_close) | |
134 | fputs(",", fmt->out); | |
135 | else | |
136 | ul_jsonwrt_indent(fmt); | |
37bcd056 KZ |
137 | } |
138 | ||
139 | switch (type) { | |
140 | case UL_JSON_OBJECT: | |
141 | fputs(name ? ": {\n" : "{\n", fmt->out); | |
142 | fmt->indent++; | |
143 | break; | |
144 | case UL_JSON_ARRAY: | |
3c7355dd | 145 | fputs(name ? ": [\n" : "[\n", fmt->out); |
37bcd056 KZ |
146 | fmt->indent++; |
147 | break; | |
148 | case UL_JSON_VALUE: | |
149 | fputs(name ? ": " : " ", fmt->out); | |
150 | break; | |
151 | } | |
d124a780 | 152 | fmt->after_close = 0; |
37bcd056 KZ |
153 | } |
154 | ||
d124a780 | 155 | void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) |
37bcd056 | 156 | { |
d124a780 KZ |
157 | if (fmt->indent == 1) { |
158 | fputs("\n}\n", fmt->out); | |
37bcd056 | 159 | fmt->indent--; |
d124a780 | 160 | fmt->after_close = 1; |
37bcd056 KZ |
161 | return; |
162 | } | |
163 | assert(fmt->indent > 0); | |
164 | ||
165 | switch (type) { | |
166 | case UL_JSON_OBJECT: | |
167 | fmt->indent--; | |
d124a780 | 168 | fputc('\n', fmt->out); |
37bcd056 | 169 | ul_jsonwrt_indent(fmt); |
d124a780 | 170 | fputs("}", fmt->out); |
37bcd056 KZ |
171 | break; |
172 | case UL_JSON_ARRAY: | |
173 | fmt->indent--; | |
d124a780 | 174 | fputc('\n', fmt->out); |
37bcd056 | 175 | ul_jsonwrt_indent(fmt); |
d124a780 | 176 | fputs("]", fmt->out); |
37bcd056 KZ |
177 | break; |
178 | case UL_JSON_VALUE: | |
37bcd056 KZ |
179 | break; |
180 | } | |
181 | ||
d124a780 | 182 | fmt->after_close = 1; |
37bcd056 KZ |
183 | } |
184 | ||
185 | void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, | |
d124a780 | 186 | const char *name, const char *data) |
37bcd056 KZ |
187 | { |
188 | ul_jsonwrt_value_open(fmt, name); | |
189 | if (data && *data) | |
190 | fputs(data, fmt->out); | |
191 | else | |
192 | fputs("null", fmt->out); | |
d124a780 | 193 | ul_jsonwrt_value_close(fmt); |
37bcd056 KZ |
194 | } |
195 | ||
196 | void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, | |
d124a780 | 197 | const char *name, const char *data) |
37bcd056 KZ |
198 | { |
199 | ul_jsonwrt_value_open(fmt, name); | |
200 | if (data && *data) | |
e434cf2c | 201 | fputs_quoted_json(data, fmt->out); |
37bcd056 KZ |
202 | else |
203 | fputs("null", fmt->out); | |
d124a780 | 204 | ul_jsonwrt_value_close(fmt); |
37bcd056 KZ |
205 | } |
206 | ||
207 | void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, | |
d124a780 | 208 | const char *name, uint64_t data) |
37bcd056 KZ |
209 | { |
210 | ul_jsonwrt_value_open(fmt, name); | |
211 | fprintf(fmt->out, "%"PRIu64, data); | |
d124a780 | 212 | ul_jsonwrt_value_close(fmt); |
37bcd056 KZ |
213 | } |
214 | ||
215 | void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, | |
d124a780 | 216 | const char *name, int data) |
37bcd056 KZ |
217 | { |
218 | ul_jsonwrt_value_open(fmt, name); | |
219 | fputs(data ? "true" : "false", fmt->out); | |
d124a780 | 220 | ul_jsonwrt_value_close(fmt); |
37bcd056 | 221 | } |
f48554d4 TW |
222 | |
223 | void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt, | |
224 | const char *name) | |
225 | { | |
226 | ul_jsonwrt_value_open(fmt, name); | |
227 | fputs("null", fmt->out); | |
228 | ul_jsonwrt_value_close(fmt); | |
229 | } |