]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/jsonwrt.c
Merge branch 'clock' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / lib / jsonwrt.c
CommitLineData
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 */
29static 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
105void 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
112int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt)
113{
114 return fmt->out == NULL ? 0 : 1;
115}
116
37bcd056
KZ
117void 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
125void 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 155void 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
185void 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
196void 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
207void 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
215void 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
223void 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}