]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/jsonwrt.c
exec_shell: use xasprintf
[thirdparty/util-linux.git] / lib / jsonwrt.c
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 {
31 const char *p;
32
33 fputc('"', out);
34 for (p = data; p && *p && (!size || p < data + size); p++) {
35
36 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 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) {
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);
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, 0)
102 #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0)
103 #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 {
107 fmt->out = out;
108 fmt->indent = indent;
109 fmt->after_close = 0;
110 }
111
112 int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt)
113 {
114 return fmt->out == NULL ? 0 : 1;
115 }
116
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 {
127 if (name) {
128 if (fmt->after_close)
129 fputs(",\n", fmt->out);
130 ul_jsonwrt_indent(fmt);
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);
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:
145 fputs(name ? ": [\n" : "[\n", fmt->out);
146 fmt->indent++;
147 break;
148 case UL_JSON_VALUE:
149 fputs(name ? ": " : " ", fmt->out);
150 break;
151 }
152 fmt->after_close = 0;
153 }
154
155 void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
156 {
157 assert(fmt->indent > 0);
158
159 switch (type) {
160 case UL_JSON_OBJECT:
161 fmt->indent--;
162 fputc('\n', fmt->out);
163 ul_jsonwrt_indent(fmt);
164 fputs("}", fmt->out);
165 if (fmt->indent == 0)
166 fputs("\n", fmt->out);
167 break;
168 case UL_JSON_ARRAY:
169 fmt->indent--;
170 fputc('\n', fmt->out);
171 ul_jsonwrt_indent(fmt);
172 fputs("]", fmt->out);
173 break;
174 case UL_JSON_VALUE:
175 break;
176 }
177
178 fmt->after_close = 1;
179 }
180
181 void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
182 const char *name, const char *data)
183 {
184 ul_jsonwrt_value_open(fmt, name);
185 if (data && *data)
186 fputs(data, fmt->out);
187 else
188 fputs("null", fmt->out);
189 ul_jsonwrt_value_close(fmt);
190 }
191
192 void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
193 const char *name, const char *data)
194 {
195 ul_jsonwrt_value_open(fmt, name);
196 if (data && *data)
197 fputs_quoted_json(data, fmt->out);
198 else
199 fputs("null", fmt->out);
200 ul_jsonwrt_value_close(fmt);
201 }
202
203 void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt,
204 const char *name, const char *data, size_t size)
205 {
206 ul_jsonwrt_value_open(fmt, name);
207 if (data && *data)
208 fputs_quoted_case_json(data, fmt->out, 0, size);
209 else
210 fputs("null", fmt->out);
211 ul_jsonwrt_value_close(fmt);
212 }
213
214 void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
215 const char *name, uint64_t data)
216 {
217 ul_jsonwrt_value_open(fmt, name);
218 fprintf(fmt->out, "%"PRIu64, data);
219 ul_jsonwrt_value_close(fmt);
220 }
221
222 void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt,
223 const char *name, long double data)
224 {
225 ul_jsonwrt_value_open(fmt, name);
226 fprintf(fmt->out, "%Lg", data);
227 ul_jsonwrt_value_close(fmt);
228 }
229
230 void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
231 const char *name, int data)
232 {
233 ul_jsonwrt_value_open(fmt, name);
234 fputs(data ? "true" : "false", fmt->out);
235 ul_jsonwrt_value_close(fmt);
236 }
237
238 void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt,
239 const char *name)
240 {
241 ul_jsonwrt_value_open(fmt, name);
242 fputs("null", fmt->out);
243 ul_jsonwrt_value_close(fmt);
244 }