json-check.c - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
json-check.c (5513B)
---
1 /* JSON checker (RFC8259), but does not check numbers or UTF-8/16/32. */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #ifdef __OpenBSD__
11 #include <unistd.h>
12 #else
13 #define pledge(a,b) 0
14 #endif
15
16 enum JSONType {
17 TYPE_ARRAY = 'a',
18 TYPE_OBJECT = 'o',
19 TYPE_OTHER = 'p',
20 };
21
22 #define JSON_MAX_NODE_DEPTH 64
23
24 static size_t offset = 0, linenr = 1;
25
26 int
27 getnext(void)
28 {
29 int c;
30
31 if ((c = getchar()) == '\n') {
32 linenr++;
33 offset = 0;
34 } else {
35 offset++;
36 }
37 return c;
38 }
39
40 #define GETNEXT getnext
41
42 static int
43 hexdigit(int c)
44 {
45 if (c >= '0' && c <= '9')
46 return c - '0';
47 else if (c >= 'a' && c <= 'f')
48 return 10 + (c - 'a');
49 else if (c >= 'A' && c <= 'F')
50 return 10 + (c - 'A');
51 return 0;
52 }
53
54 #define EXPECT_VALUE "{[\"-0123456789tfn"
55 #define EXPECT_STRING "\""
56 #define EXPECT_END "}],"
57 #define EXPECT_OBJECT_STRING EXPECT_STRING "}"
58 #define EXPECT_OBJECT_KEY ":"
59 #define EXPECT_ARRAY_VALUE EXPECT_VALUE "]"
60
61 #define JSON_INVALID(s) do { fprintf(stderr, "%zu:%zu: %s\n", linenr, offset, s); return 1; } while (0);
62 #define JSON_INVALID_EXPECTED() do { fprintf(stderr, "%zu:%zu: expected one of \"%s\"\n", linenr, offset, expect); return 1; } while (0);
63
64 int
65 parsejson(void)
66 {
67 int nodes[JSON_MAX_NODE_DEPTH] = { 0 };
68 size_t depth = 0;
69 long cp, hi, lo;
70 int c, i, escape, iskey = 0;
71 const char *expect = EXPECT_VALUE;
72
73 while (1) {
74 c = GETNEXT();
75 handlechr:
76 if (c == EOF)
77 break;
78
79 /* skip JSON white-space, (NOTE: no \v, \f, \b etc) */
80 if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
81 continue;
82
83 if (!c || !strchr(expect, c))
84 JSON_INVALID_EXPECTED();
85
86 switch (c) {
87 case ':':
88 iskey = 0;
89 expect = EXPECT_VALUE;
90 break;
91 case '"':
92 nodes[depth] = TYPE_OTHER;
93 escape = 0;
94 while (1) {
95 c = GETNEXT();
96 chr:
97 /* EOF or control char: 0x7f is not defined as a control char in RFC8259 */
98 if (c < 0x20)
99 JSON_INVALID("control-character in string not allowed");
100
101 if (escape) {
102 escchr:
103 escape = 0;
104 switch (c) {
105 case '"': /* FALLTHROUGH */
106 case '\\':
107 case '/': break;
108 case 'b': c = '\b'; break;
109 case 'f': c = '\f'; break;
110 case 'n': c = '\n'; break;
111 case 'r': c = '\r'; break;
112 case 't': c = '\t'; break;
113 case 'u': /* hex hex hex hex */
114 for (i = 12, cp = 0; i >= 0; i -= 4) {
115 if ((c = GETNEXT()) == EOF || !isxdigit(c))
116 JSON_INVALID("invalid codepoint"); /* invalid codepoint */
117 cp |= (hexdigit(c) << i);
118 }
119 /* RFC8259 - 7. Strings - surrogates.
120 * 0xd800 - 0xdb7f - high surrogates */
121 if (cp >= 0xd800 && cp <= 0xdb7f) {
122 if ((c = GETNEXT()) != '\\')
123 goto chr;
124 if ((c = GETNEXT()) != 'u')
125 goto escchr;
126 for (hi = cp, i = 12, lo = 0; i >= 0; i -= 4) {
127 if ((c = GETNEXT()) == EOF || !isxdigit(c))
128 JSON_INVALID("invalid codepoint"); /* invalid codepoint */
129 lo |= (hexdigit(c) << i);
130 }
131 /* 0xdc00 - 0xdfff - low surrogates */
132 if (lo >= 0xdc00 && lo <= 0xdfff)
133 cp = (hi << 10) + lo - 56613888; /* - offset */
134 else
135 continue;
136 }
137 continue;
138 default:
139 JSON_INVALID("invalid escape character"); /* invalid escape char */
140 }
141 } else if (c == '\\') {
142 escape = 1;
143 } else if (c == '"') {
144 break;
145 }
146 }
147 if (iskey)
148 expect = EXPECT_OBJECT_KEY;
149 else
150 expect = EXPECT_END;
151 break;
152 case '[':
153 case '{':
154 if (depth + 1 >= JSON_MAX_NODE_DEPTH)
155 JSON_INVALID("array/object too deep"); /* too deep */
156
157 if (c == '[') {
158 nodes[depth] = TYPE_ARRAY;
159 expect = EXPECT_ARRAY_VALUE;
160 } else if (c == '{') {
161 iskey = 1;
162 nodes[depth] = TYPE_OBJECT;
163 expect = EXPECT_OBJECT_STRING;
164 }
165 depth++;
166 break;
167 case ']':
168 case '}':
169 if (!depth ||
170 (c == ']' && nodes[depth - 1] != TYPE_ARRAY) ||
171 (c == '}' && nodes[depth - 1] != TYPE_OBJECT))
172 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
173
174 depth--;
175 expect = EXPECT_END;
176 break;
177 case ',':
178 if (!depth)
179 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
180
181 if (nodes[depth - 1] == TYPE_OBJECT) {
182 iskey = 1;
183 expect = EXPECT_STRING;
184 } else {
185 expect = EXPECT_VALUE;
186 }
187 break;
188 case 't': /* true */
189 if (GETNEXT() != 'r' || GETNEXT() != 'u' || GETNEXT() != 'e')
190 JSON_INVALID("expected true type, but got something else");
191 nodes[depth] = TYPE_OTHER;
192 expect = EXPECT_END;
193 break;
194 case 'f': /* false */
195 if (GETNEXT() != 'a' || GETNEXT() != 'l' || GETNEXT() != 's' ||
196 GETNEXT() != 'e')
197 JSON_INVALID("expected false type, but got something else");
198 nodes[depth] = TYPE_OTHER;
199 expect = EXPECT_END;
200 break;
201 case 'n': /* null */
202 if (GETNEXT() != 'u' || GETNEXT() != 'l' || GETNEXT() != 'l')
203 JSON_INVALID("expected null type, but got something else");
204 nodes[depth] = TYPE_OTHER;
205 expect = EXPECT_END;
206 break;
207 default: /* number */
208 nodes[depth] = TYPE_OTHER;
209 expect = EXPECT_END;
210 while (1) {
211 c = GETNEXT();
212 if (c == EOF ||
213 !c || !strchr("0123456789eE+-.", c))
214 goto handlechr; /* do not read next char, handle this */
215 }
216 }
217 }
218 if (depth)
219 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
220
221 return 0;
222 }
223
224 int
225 main(void)
226 {
227 if (pledge("stdio", NULL) == -1) {
228 fprintf(stderr, "pledge stdio: %s\n", strerror(errno));
229 return 1;
230 }
231 return parsejson();
232 }