Line | Count | Source |
1 | | /* $OpenBSD: json.c,v 1.1 2026/09/08 08:33:10 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2026 Dane Jensen <dhcjensen@gmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <sys/types.h> |
20 | | |
21 | | #include <ctype.h> |
22 | | #include <errno.h> |
23 | | #include <stdint.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | |
27 | | #include "tmux.h" |
28 | | |
29 | | /* |
30 | | * Parse a subset of JSON. The subset accepted is: |
31 | | * |
32 | | * - Arrays may only hold objects. |
33 | | * - The top-level value must be an object. |
34 | | * - Numbers are 64-bit signed integers in base 10; there are no fractions and |
35 | | * no exponents. |
36 | | * - There is no null, and a string may not be empty. |
37 | | * - Escapes are validated but not decoded. |
38 | | * - A key may not appear twice in the same object. Note that because escapes |
39 | | * are not decoded, duplicate keys may go undetected. |
40 | | * - Objects may only be parsed to a fixed maximum depth. |
41 | | */ |
42 | | |
43 | 0 | #define ERROR_CTX_LEN 8 |
44 | 0 | #define PARSE_DEPTH_MAX 200 |
45 | | |
46 | | /* JSON token types. */ |
47 | | enum json_token_type { |
48 | | TOK_OPENOBJECT, |
49 | | TOK_CLOSEOBJECT, |
50 | | TOK_OPENARRAY, |
51 | | TOK_CLOSEARRAY, |
52 | | TOK_COMMA, |
53 | | TOK_COLON, |
54 | | TOK_QUOTE, |
55 | | TOK_VALUE, |
56 | | TOK_EOF |
57 | | }; |
58 | | |
59 | | /* JSON token. */ |
60 | | struct json_token { |
61 | | enum json_token_type type; |
62 | | int offset; |
63 | | int len; |
64 | | }; |
65 | | |
66 | | /* JSON tokens. */ |
67 | | struct json_tokens { |
68 | | int size; |
69 | | int capacity; |
70 | | struct json_token *toks; |
71 | | }; |
72 | | |
73 | | /* JSON node type. */ |
74 | | enum json_node_type { |
75 | | NODE_STRING, |
76 | | NODE_NUMBER, |
77 | | NODE_BOOLEAN, |
78 | | NODE_OBJECT, |
79 | | NODE_ARRAY |
80 | | }; |
81 | | |
82 | | /* JSON field tree. */ |
83 | | RB_HEAD(json_fields, json_node); |
84 | | |
85 | | /* JSON array queue. */ |
86 | | TAILQ_HEAD(json_members, json_node); |
87 | | |
88 | | /* JSON parse context. */ |
89 | | struct json_parse_ctx { |
90 | | const char *input; |
91 | | char **cause; |
92 | | int depth; |
93 | | }; |
94 | | |
95 | | /* JSON node. */ |
96 | | struct json_node { |
97 | | enum json_node_type type; |
98 | | char *key; |
99 | | struct json_node *parent; |
100 | | union { |
101 | | char *str; |
102 | | int64_t num; |
103 | | int boolean; |
104 | | struct json_fields fields; |
105 | | struct json_members members; |
106 | | }; |
107 | | RB_ENTRY(json_node) oentry; |
108 | | TAILQ_ENTRY(json_node) aentry; |
109 | | }; |
110 | | |
111 | | static int |
112 | | json_node_cmp(struct json_node *a, struct json_node *b) |
113 | 0 | { |
114 | 0 | return (strcmp(a->key, b->key)); |
115 | 0 | } |
116 | 0 | RB_GENERATE_STATIC(json_fields, json_node, oentry, json_node_cmp); Unexecuted instantiation: json.c:json_fields_RB_FIND Unexecuted instantiation: json.c:json_fields_RB_MINMAX Unexecuted instantiation: json.c:json_fields_RB_REMOVE Unexecuted instantiation: json.c:json_fields_RB_REMOVE_COLOR Unexecuted instantiation: json.c:json_fields_RB_INSERT |
117 | 0 |
|
118 | 0 | static struct json_tokens *json_tokenize_input(const char *, char **); |
119 | 0 | static struct json_tokens *json_create_tokens(void); |
120 | 0 | static void json_destroy_tokens(struct json_tokens *); |
121 | 0 | static void json_add_token(struct json_tokens *, |
122 | 0 | enum json_token_type, const char *, const char *, |
123 | 0 | int); |
124 | 0 | static int json_tokenize_value(struct json_tokens *, |
125 | 0 | const char *); |
126 | 0 | static void json_error(char **, const char *, const char *); |
127 | 0 | static struct json_node *json_create_node(struct json_node *, |
128 | 0 | enum json_node_type, const char *, void *); |
129 | 0 | static void json_assign_value(struct json_node *, void *); |
130 | 0 | static struct json_node *json_parse_tokens(struct json_tokens **, |
131 | 0 | struct json_parse_ctx *); |
132 | 0 | static char *json_parse_key(struct json_token **, |
133 | 0 | struct json_parse_ctx *); |
134 | 0 | static struct json_node *json_parse_object(struct json_token **, |
135 | 0 | struct json_parse_ctx *, const char *, |
136 | 0 | struct json_node *); |
137 | 0 | static struct json_node *json_parse_array(struct json_token **, |
138 | 0 | struct json_parse_ctx *, const char *, |
139 | 0 | struct json_node *); |
140 | 0 | static struct json_node *json_parse_string(struct json_token **, |
141 | 0 | struct json_parse_ctx *, const char *, |
142 | 0 | struct json_node *); |
143 | 0 | static struct json_node *json_parse_number(struct json_token **, |
144 | 0 | struct json_parse_ctx *, const char *, |
145 | 0 | struct json_node *); |
146 | 0 | static struct json_node *json_parse_boolean(struct json_token **, |
147 | 0 | struct json_parse_ctx *, const char *, |
148 | 0 | struct json_node *); |
149 | 0 |
|
150 | 0 | /* Parse an input string into JSON. */ |
151 | 0 | struct json_node * |
152 | 0 | json_parse(const char *input, char **cause) |
153 | 0 | { |
154 | 0 | struct json_tokens *tokens; |
155 | 0 | struct json_parse_ctx pctx; |
156 | |
|
157 | 0 | if (*input == '\0') { |
158 | 0 | json_error(cause, "empty input", NULL); |
159 | 0 | return (NULL); |
160 | 0 | } |
161 | | |
162 | 0 | if ((tokens = json_tokenize_input(input, cause)) == NULL) |
163 | 0 | return (NULL); |
164 | | |
165 | 0 | pctx.input = input; |
166 | 0 | pctx.cause = cause; |
167 | 0 | pctx.depth = 0; |
168 | |
|
169 | 0 | return (json_parse_tokens(&tokens, &pctx)); |
170 | 0 | } |
171 | | |
172 | | /* Returns a field node from an object node. */ |
173 | | struct json_node * |
174 | | json_find(struct json_node *jn, const char *key) |
175 | 0 | { |
176 | 0 | struct json_node *node = (struct json_node *)jn, tmp = { 0 }; |
177 | |
|
178 | 0 | if (jn->type != NODE_OBJECT) |
179 | 0 | return (NULL); |
180 | | |
181 | 0 | tmp.key = (char *)key; |
182 | 0 | return (RB_FIND(json_fields, &node->fields, &tmp)); |
183 | 0 | } |
184 | | |
185 | | /* Returns the first member of an array node. */ |
186 | | struct json_node * |
187 | | json_array_first(struct json_node *jn) |
188 | 0 | { |
189 | 0 | if (jn->type != NODE_ARRAY) |
190 | 0 | return (NULL); |
191 | | |
192 | 0 | return (TAILQ_FIRST(&jn->members)); |
193 | 0 | } |
194 | | |
195 | | /* Returns the next member of an array's member node. */ |
196 | | struct json_node * |
197 | | json_array_next(struct json_node *member) |
198 | 0 | { |
199 | 0 | if (member == NULL || |
200 | 0 | member->parent == NULL || |
201 | 0 | member->parent->type != NODE_ARRAY) |
202 | 0 | return (NULL); |
203 | 0 | return (TAILQ_NEXT(member, aentry)); |
204 | 0 | } |
205 | | |
206 | | /* Returns the string value from a node. */ |
207 | | int |
208 | | json_get_string(struct json_node *jn, const char **s) |
209 | 0 | { |
210 | 0 | if (jn->type != NODE_STRING) |
211 | 0 | return (-1); |
212 | | |
213 | 0 | *s = jn->str; |
214 | 0 | return (0); |
215 | 0 | } |
216 | | |
217 | | /* Returns the number value from a node. */ |
218 | | int |
219 | | json_get_number(struct json_node *jn, int64_t *i) |
220 | 0 | { |
221 | 0 | if (jn->type != NODE_NUMBER) |
222 | 0 | return (-1); |
223 | | |
224 | 0 | *i = jn->num; |
225 | 0 | return (0); |
226 | 0 | } |
227 | | |
228 | | /* Returns the boolean value from a node. */ |
229 | | int |
230 | | json_get_boolean(struct json_node *jn, int *b) |
231 | 0 | { |
232 | 0 | if (jn->type != NODE_BOOLEAN) |
233 | 0 | return (-1); |
234 | | |
235 | 0 | *b = jn->boolean; |
236 | 0 | return (0); |
237 | 0 | } |
238 | | |
239 | | /* Returns the object value from a node. */ |
240 | | int |
241 | | json_get_object(struct json_node *jn, struct json_node **o) |
242 | 0 | { |
243 | 0 | if (jn->type != NODE_OBJECT) |
244 | 0 | return (-1); |
245 | | |
246 | 0 | *o = jn; |
247 | 0 | return (0); |
248 | 0 | } |
249 | | |
250 | | /* Returns the array value from a node. */ |
251 | | int |
252 | | json_get_array(struct json_node *jn, struct json_node **a) |
253 | 0 | { |
254 | 0 | if (jn->type != NODE_ARRAY) |
255 | 0 | return (-1); |
256 | | |
257 | 0 | *a = jn; |
258 | 0 | return (0); |
259 | 0 | } |
260 | | |
261 | | /* Returns the string value from a given key in an object node. */ |
262 | | int |
263 | | json_find_string(struct json_node *jn, const char *key, const char **out, |
264 | | char **cause) |
265 | 0 | { |
266 | 0 | struct json_node *field; |
267 | |
|
268 | 0 | if ((field = json_find(jn, key)) == NULL) { |
269 | 0 | if (cause != NULL) |
270 | 0 | xasprintf(cause, "key \"%s\" not found", key); |
271 | 0 | return (-1); |
272 | 0 | } |
273 | 0 | if (field->type != NODE_STRING) { |
274 | 0 | if (cause != NULL) |
275 | 0 | xasprintf(cause, "key \"%s\" expected a string", key); |
276 | 0 | return (-1); |
277 | 0 | } |
278 | 0 | *out = field->str; |
279 | |
|
280 | 0 | return (0); |
281 | 0 | } |
282 | | |
283 | | /* Returns the number value from a given key in an object node. */ |
284 | | int |
285 | | json_find_number(struct json_node *jn, const char *key, int64_t *out, |
286 | | char **cause) |
287 | 0 | { |
288 | 0 | struct json_node *field; |
289 | |
|
290 | 0 | if ((field = json_find(jn, key)) == NULL) { |
291 | 0 | if (cause != NULL) |
292 | 0 | xasprintf(cause, "key \"%s\" not found", key); |
293 | 0 | return (-1); |
294 | 0 | } |
295 | 0 | if (field->type != NODE_NUMBER) { |
296 | 0 | if (cause != NULL) |
297 | 0 | xasprintf(cause, "key \"%s\" expected a number", key); |
298 | 0 | return (-1); |
299 | 0 | } |
300 | 0 | *out = field->num; |
301 | |
|
302 | 0 | return (0); |
303 | 0 | } |
304 | | |
305 | | /* Returns the boolean value from a given key in an object node. */ |
306 | | int |
307 | | json_find_boolean(struct json_node *jn, const char *key, int *out, char **cause) |
308 | 0 | { |
309 | 0 | struct json_node *field; |
310 | |
|
311 | 0 | if ((field = json_find(jn, key)) == NULL) { |
312 | 0 | if (cause != NULL) |
313 | 0 | xasprintf(cause, "key \"%s\" not found", key); |
314 | 0 | return (-1); |
315 | 0 | } |
316 | 0 | if (field->type != NODE_BOOLEAN) { |
317 | 0 | if (cause != NULL) |
318 | 0 | xasprintf(cause, "key \"%s\" expected a boolean", key); |
319 | 0 | return (-1); |
320 | 0 | } |
321 | 0 | *out = field->boolean; |
322 | |
|
323 | 0 | return (0); |
324 | 0 | } |
325 | | |
326 | | /* Returns the object value from a given key in an object node. */ |
327 | | int |
328 | | json_find_object(struct json_node *jn, const char *key, struct json_node **out, |
329 | | char **cause) |
330 | 0 | { |
331 | 0 | struct json_node *field; |
332 | |
|
333 | 0 | if ((field = json_find(jn, key)) == NULL) { |
334 | 0 | if (cause != NULL) |
335 | 0 | xasprintf(cause, "key \"%s\" not found", key); |
336 | 0 | return (-1); |
337 | 0 | } |
338 | 0 | if (field->type != NODE_OBJECT) { |
339 | 0 | if (cause != NULL) |
340 | 0 | xasprintf(cause, "key \"%s\" expected an object", key); |
341 | 0 | return (-1); |
342 | 0 | } |
343 | 0 | *out = field; |
344 | |
|
345 | 0 | return (0); |
346 | 0 | } |
347 | | |
348 | | /* Returns the array value from a given key in an object node. */ |
349 | | int |
350 | | json_find_array(struct json_node *jn, const char *key, struct json_node **out, |
351 | | char **cause) |
352 | 0 | { |
353 | 0 | struct json_node *field; |
354 | |
|
355 | 0 | if ((field = json_find(jn, key)) == NULL) { |
356 | 0 | if (cause != NULL) |
357 | 0 | xasprintf(cause, "key \"%s\" not found", key); |
358 | 0 | return (-1); |
359 | 0 | } |
360 | 0 | if (field->type != NODE_ARRAY) { |
361 | 0 | if (cause != NULL) |
362 | 0 | xasprintf(cause, "key \"%s\" expected an array", key); |
363 | 0 | return (-1); |
364 | 0 | } |
365 | 0 | *out = field; |
366 | |
|
367 | 0 | return (0); |
368 | 0 | } |
369 | | |
370 | | /* Fill an error cause. */ |
371 | | static void |
372 | | json_error(char **cause, const char *reason, const char *loc) |
373 | 0 | { |
374 | 0 | const char *ellipsis = "..."; |
375 | 0 | int i; |
376 | |
|
377 | 0 | if (cause == NULL) |
378 | 0 | return; |
379 | 0 | if (loc == NULL || *loc == '\0') { |
380 | 0 | xasprintf(cause, "%s", reason); |
381 | 0 | return; |
382 | 0 | } |
383 | | |
384 | 0 | for (i = 0; i < ERROR_CTX_LEN + 1; i++) { |
385 | 0 | if (loc[i] == '\0') { |
386 | 0 | ellipsis = ""; |
387 | 0 | break; |
388 | 0 | } |
389 | 0 | } |
390 | |
|
391 | 0 | xasprintf(cause, "%s: %.*s%s", reason, ERROR_CTX_LEN, loc, ellipsis); |
392 | 0 | } |
393 | | |
394 | | /* Tokenize the json string. */ |
395 | | static struct json_tokens * |
396 | | json_tokenize_input(const char *input, char **cause) |
397 | 0 | { |
398 | 0 | struct json_tokens *tokens; |
399 | 0 | enum json_token_type type; |
400 | 0 | const char *loc, *start = input; |
401 | 0 | int in_string = 0, scan; |
402 | |
|
403 | 0 | tokens = json_create_tokens(); |
404 | 0 | while (*input != '\0') { |
405 | 0 | loc = input; |
406 | 0 | scan = 1; |
407 | |
|
408 | 0 | if (in_string && *input != '"') { |
409 | 0 | type = TOK_VALUE; |
410 | 0 | } else { |
411 | 0 | switch (*input) { |
412 | 0 | case ' ': |
413 | 0 | case '\t': |
414 | 0 | case '\n': |
415 | 0 | case '\r': |
416 | 0 | input++; |
417 | 0 | continue; |
418 | 0 | case '{': |
419 | 0 | type = TOK_OPENOBJECT; |
420 | 0 | break; |
421 | 0 | case '}': |
422 | 0 | type = TOK_CLOSEOBJECT; |
423 | 0 | break; |
424 | 0 | case '[': |
425 | 0 | type = TOK_OPENARRAY; |
426 | 0 | break; |
427 | 0 | case ']': |
428 | 0 | type = TOK_CLOSEARRAY; |
429 | 0 | break; |
430 | 0 | case '"': |
431 | 0 | type = TOK_QUOTE; |
432 | 0 | break; |
433 | 0 | case ':': |
434 | 0 | type = TOK_COLON; |
435 | 0 | break; |
436 | 0 | case ',': |
437 | 0 | type = TOK_COMMA; |
438 | 0 | break; |
439 | 0 | default: |
440 | 0 | type = TOK_VALUE; |
441 | 0 | break; |
442 | 0 | } |
443 | 0 | } |
444 | 0 | if (type == TOK_VALUE) { |
445 | 0 | scan = json_tokenize_value(tokens, loc); |
446 | 0 | if (scan == -1) |
447 | 0 | goto fail; |
448 | 0 | input += scan - 1; |
449 | 0 | } |
450 | 0 | json_add_token(tokens, type, start, loc, scan); |
451 | 0 | if (type == TOK_QUOTE) |
452 | 0 | in_string = !in_string; |
453 | |
|
454 | 0 | input++; |
455 | 0 | } |
456 | 0 | json_add_token(tokens, TOK_EOF, start, loc, 0); |
457 | |
|
458 | 0 | return (tokens); |
459 | | |
460 | 0 | fail: |
461 | 0 | json_error(cause, "tokenization error", loc); |
462 | 0 | json_destroy_tokens(tokens); |
463 | 0 | return (NULL); |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * Tokenize a value from the input string. Strings are terminated by a '"', and |
468 | | * numbers/booleans are terminated by a ',', ']', '}', or whitespace. |
469 | | */ |
470 | | static int |
471 | | json_tokenize_value(struct json_tokens *tokens, const char *loc) |
472 | 0 | { |
473 | 0 | struct json_token *prev; |
474 | 0 | int i, scan = 0; |
475 | |
|
476 | 0 | if (tokens->size == 0) |
477 | 0 | return (-1); |
478 | 0 | prev = &tokens->toks[tokens->size - 1]; |
479 | |
|
480 | 0 | if (prev->type == TOK_QUOTE) { |
481 | 0 | while (loc[scan] != '"') { |
482 | 0 | if (loc[scan] == '\0' || (u_char)loc[scan] < 0x20) |
483 | 0 | return (-1); |
484 | 0 | if (loc[scan] != '\\') { |
485 | 0 | scan++; |
486 | 0 | continue; |
487 | 0 | } |
488 | 0 | scan++; |
489 | 0 | switch (loc[scan]) { |
490 | 0 | case '"': |
491 | 0 | case '\\': |
492 | 0 | case '/': |
493 | 0 | case 'b': |
494 | 0 | case 'f': |
495 | 0 | case 'n': |
496 | 0 | case 'r': |
497 | 0 | case 't': |
498 | 0 | scan++; |
499 | 0 | break; |
500 | 0 | case 'u': |
501 | 0 | for (i = 1; i <= 4; i++) { |
502 | 0 | if (!isxdigit((u_char)loc[scan + i])) |
503 | 0 | return (-1); |
504 | 0 | } |
505 | 0 | scan += 5; |
506 | 0 | break; |
507 | 0 | default: |
508 | 0 | return (-1); |
509 | 0 | } |
510 | 0 | } |
511 | 0 | } else if (prev->type == TOK_COLON) { |
512 | 0 | do { |
513 | 0 | if (loc[scan] == '\0') |
514 | 0 | return (-1); |
515 | 0 | scan++; |
516 | 0 | } while (loc[scan] != ']' && loc[scan] != '}' && |
517 | 0 | loc[scan] != ',' && !isspace((u_char) loc[scan])); |
518 | 0 | } else |
519 | 0 | return (-1); |
520 | | |
521 | 0 | return (scan); |
522 | 0 | } |
523 | | |
524 | | /* Create a new token container. */ |
525 | | static struct json_tokens * |
526 | | json_create_tokens(void) |
527 | 0 | { |
528 | 0 | struct json_tokens *tokens; |
529 | |
|
530 | 0 | tokens = xmalloc(sizeof *tokens); |
531 | 0 | tokens->size = 0; |
532 | 0 | tokens->capacity = 1024; |
533 | 0 | tokens->toks = xmalloc(tokens->capacity * sizeof *tokens->toks); |
534 | |
|
535 | 0 | return (tokens); |
536 | 0 | } |
537 | | |
538 | | /* Free a token container. */ |
539 | | static void |
540 | | json_destroy_tokens(struct json_tokens *tokens) |
541 | 0 | { |
542 | 0 | free(tokens->toks); |
543 | 0 | tokens->toks = NULL; |
544 | 0 | free(tokens); |
545 | 0 | } |
546 | | |
547 | | /* Add a token to tokens. */ |
548 | | static void |
549 | | json_add_token(struct json_tokens *tokens, enum json_token_type type, |
550 | | const char *input, const char *loc, int len) |
551 | 0 | { |
552 | 0 | struct json_token *tok; |
553 | |
|
554 | 0 | while (tokens->size >= tokens->capacity) { |
555 | 0 | tokens->capacity *= 2; |
556 | 0 | tokens->toks = xrealloc(tokens->toks, |
557 | 0 | sizeof *tokens->toks * tokens->capacity); |
558 | 0 | } |
559 | |
|
560 | 0 | tok = &tokens->toks[tokens->size++]; |
561 | 0 | tok->type = type; |
562 | 0 | tok->offset = loc - input; |
563 | 0 | tok->len = len; |
564 | 0 | } |
565 | | |
566 | | /* Create a node and assign given values. */ |
567 | | static struct json_node * |
568 | | json_create_node(struct json_node *parent, enum json_node_type type, |
569 | | const char *key, void *val) |
570 | 0 | { |
571 | 0 | struct json_node *node; |
572 | |
|
573 | 0 | node = xcalloc(1, sizeof *node); |
574 | 0 | node->parent = parent; |
575 | 0 | if (key != NULL) |
576 | 0 | node->key = xstrdup(key); |
577 | 0 | node->type = type; |
578 | 0 | if (type == NODE_OBJECT) |
579 | 0 | RB_INIT(&node->fields); |
580 | 0 | else if (type == NODE_ARRAY) |
581 | 0 | TAILQ_INIT(&node->members); |
582 | 0 | if (val != NULL) |
583 | 0 | json_assign_value(node, val); |
584 | |
|
585 | 0 | return (node); |
586 | 0 | } |
587 | | |
588 | | /* Destroy a node and all of the node's fields. */ |
589 | | void |
590 | | json_destroy_node(struct json_node *node) |
591 | 0 | { |
592 | 0 | struct json_node *field, *field1, *member; |
593 | |
|
594 | 0 | if (node == NULL) |
595 | 0 | return; |
596 | | |
597 | 0 | switch (node->type) { |
598 | 0 | case NODE_STRING: |
599 | 0 | free(node->str); |
600 | 0 | break; |
601 | 0 | case NODE_NUMBER: |
602 | 0 | case NODE_BOOLEAN: |
603 | 0 | break; |
604 | 0 | case NODE_OBJECT: |
605 | 0 | RB_FOREACH_SAFE(field, json_fields, &node->fields, field1) { |
606 | 0 | RB_REMOVE(json_fields, &node->fields, field); |
607 | 0 | json_destroy_node(field); |
608 | 0 | } |
609 | 0 | break; |
610 | 0 | case NODE_ARRAY: |
611 | 0 | while (!TAILQ_EMPTY(&node->members)) { |
612 | 0 | member = TAILQ_FIRST(&node->members); |
613 | 0 | TAILQ_REMOVE(&node->members, member, aentry); |
614 | 0 | json_destroy_node(member); |
615 | 0 | } |
616 | 0 | break; |
617 | 0 | } |
618 | | |
619 | 0 | if (node->key != NULL) |
620 | 0 | free(node->key); |
621 | 0 | free(node); |
622 | 0 | } |
623 | | |
624 | | /* Assign a value to a node. */ |
625 | | static void |
626 | | json_assign_value(struct json_node *node, void *val) |
627 | 0 | { |
628 | 0 | struct json_node *child = val; |
629 | |
|
630 | 0 | switch (node->type) { |
631 | 0 | case NODE_STRING: |
632 | 0 | node->str = val; |
633 | 0 | break; |
634 | 0 | case NODE_NUMBER: |
635 | 0 | node->num = *(int64_t *)val; |
636 | 0 | break; |
637 | 0 | case NODE_BOOLEAN: |
638 | 0 | node->boolean = *(int *)val; |
639 | 0 | break; |
640 | 0 | case NODE_OBJECT: |
641 | 0 | RB_INSERT(json_fields, &node->fields, child); |
642 | 0 | break; |
643 | 0 | case NODE_ARRAY: |
644 | 0 | TAILQ_INSERT_TAIL(&node->members, child, aentry); |
645 | 0 | break; |
646 | 0 | default: |
647 | 0 | fatalx("unknown node type"); |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | /* Parse a stream of tokens into nodes. Consumes the tokens. */ |
652 | | static struct json_node * |
653 | | json_parse_tokens(struct json_tokens **tokens, struct json_parse_ctx *pctx) |
654 | 0 | { |
655 | 0 | struct json_token *tok = (*tokens)->toks; |
656 | 0 | struct json_node *jn = NULL; |
657 | |
|
658 | 0 | if (tok->type == TOK_OPENOBJECT) |
659 | 0 | jn = json_parse_object(&tok, pctx, NULL, NULL); |
660 | 0 | else { |
661 | 0 | json_error(pctx->cause, "expected object", |
662 | 0 | pctx->input + tok->offset); |
663 | 0 | goto fail; |
664 | 0 | } |
665 | 0 | if (jn == NULL) |
666 | 0 | goto fail; |
667 | | |
668 | 0 | if (tok->type != TOK_EOF) { |
669 | 0 | json_error(pctx->cause, "unexpected trailing data", |
670 | 0 | pctx->input + tok->offset); |
671 | 0 | goto fail; |
672 | 0 | } |
673 | 0 | json_destroy_tokens(*tokens); |
674 | 0 | *tokens = NULL; |
675 | |
|
676 | 0 | return (jn); |
677 | | |
678 | 0 | fail: |
679 | 0 | if (jn != NULL) |
680 | 0 | json_destroy_node(jn); |
681 | 0 | json_destroy_tokens(*tokens); |
682 | 0 | *tokens = NULL; |
683 | 0 | return (NULL); |
684 | 0 | } |
685 | | |
686 | | /* Parse and return a key string, and advance the token pointer. */ |
687 | | static char * |
688 | | json_parse_key(struct json_token **tok, struct json_parse_ctx *pctx) |
689 | 0 | { |
690 | 0 | int len; |
691 | 0 | const char *loc, *start = pctx->input + (*tok)->offset; |
692 | 0 | char *key; |
693 | |
|
694 | 0 | if ((*tok)->type != TOK_QUOTE) |
695 | 0 | goto fail; |
696 | 0 | (*tok)++; |
697 | |
|
698 | 0 | loc = pctx->input + (*tok)->offset; |
699 | 0 | len = (*tok)->len; |
700 | |
|
701 | 0 | if ((*tok)->type != TOK_VALUE) |
702 | 0 | goto fail; |
703 | 0 | (*tok)++; |
704 | 0 | if ((*tok)->type != TOK_QUOTE) |
705 | 0 | goto fail; |
706 | | |
707 | 0 | key = xstrndup(loc, len); |
708 | 0 | (*tok)++; |
709 | |
|
710 | 0 | return (key); |
711 | | |
712 | 0 | fail: |
713 | 0 | json_error(pctx->cause, "invalid key", start); |
714 | 0 | return (NULL); |
715 | 0 | } |
716 | | |
717 | | /* Parse an object value, return the node, and advance the token pointer. */ |
718 | | static struct json_node * |
719 | | json_parse_object(struct json_token **tok, struct json_parse_ctx *pctx, |
720 | | const char *key, struct json_node *parent) |
721 | 0 | { |
722 | 0 | struct json_node *object, *field; |
723 | 0 | char *fkey = NULL; |
724 | 0 | u_char *valstr; |
725 | |
|
726 | 0 | if ((*tok)->type != TOK_OPENOBJECT) |
727 | 0 | return (NULL); |
728 | | |
729 | 0 | pctx->depth++; |
730 | 0 | if (pctx->depth > PARSE_DEPTH_MAX) { |
731 | 0 | json_error(pctx->cause, "parse depth exceeded", |
732 | 0 | pctx->input + (*tok)->offset); |
733 | 0 | return (NULL); |
734 | 0 | } |
735 | | |
736 | 0 | (*tok)++; |
737 | |
|
738 | 0 | object = json_create_node(parent, NODE_OBJECT, key, NULL); |
739 | 0 | while ((*tok)->type != TOK_CLOSEOBJECT) { |
740 | 0 | if ((fkey = json_parse_key(tok, pctx)) == NULL) |
741 | 0 | goto fail; |
742 | 0 | if (json_find(object, fkey) != NULL) { |
743 | 0 | json_error(pctx->cause, "duplicate key", |
744 | 0 | pctx->input + (*tok)->offset); |
745 | 0 | goto fail; |
746 | 0 | } |
747 | 0 | if ((*tok)->type != TOK_COLON) { |
748 | 0 | json_error(pctx->cause, "missing colon", |
749 | 0 | pctx->input + (*tok)->offset); |
750 | 0 | goto fail; |
751 | 0 | } |
752 | 0 | (*tok)++; |
753 | |
|
754 | 0 | switch ((*tok)->type) { |
755 | 0 | case TOK_QUOTE: |
756 | 0 | field = json_parse_string(tok, pctx, fkey, object); |
757 | 0 | break; |
758 | 0 | case TOK_VALUE: |
759 | 0 | valstr = (u_char *)(pctx->input + (*tok)->offset); |
760 | 0 | if ((*valstr == '-' && isdigit(valstr[1])) || |
761 | 0 | isdigit(*valstr)) { |
762 | 0 | field = json_parse_number(tok, pctx, fkey, |
763 | 0 | object); |
764 | 0 | } else { |
765 | 0 | field = json_parse_boolean(tok, pctx, fkey, |
766 | 0 | object); |
767 | 0 | } |
768 | 0 | break; |
769 | 0 | case TOK_OPENOBJECT: |
770 | 0 | field = json_parse_object(tok, pctx, fkey, object); |
771 | 0 | break; |
772 | 0 | case TOK_OPENARRAY: |
773 | 0 | field = json_parse_array(tok, pctx, fkey, object); |
774 | 0 | break; |
775 | 0 | default: |
776 | 0 | json_error(pctx->cause, |
777 | 0 | "unexpected value when parsing object", |
778 | 0 | pctx->input + (*tok)->offset); |
779 | 0 | goto fail; |
780 | 0 | } |
781 | 0 | if (field == NULL) |
782 | 0 | goto fail; |
783 | | |
784 | 0 | json_assign_value(object, field); |
785 | 0 | if ((*tok)->type == TOK_COMMA) { |
786 | 0 | if ((*tok)[1].type == TOK_CLOSEOBJECT) { |
787 | 0 | json_error(pctx->cause, "invalid object", |
788 | 0 | pctx->input + (*tok)->offset); |
789 | 0 | goto fail; |
790 | 0 | } |
791 | 0 | (*tok)++; |
792 | 0 | } else if ((*tok)->type != TOK_CLOSEOBJECT) { |
793 | 0 | json_error(pctx->cause, "invalid object", |
794 | 0 | pctx->input + (*tok)->offset); |
795 | 0 | goto fail; |
796 | 0 | } |
797 | 0 | free(fkey); |
798 | 0 | } |
799 | 0 | (*tok)++; |
800 | 0 | pctx->depth--; |
801 | 0 | return (object); |
802 | | |
803 | 0 | fail: |
804 | 0 | if (fkey != NULL) |
805 | 0 | free(fkey); |
806 | 0 | json_destroy_node(object); |
807 | 0 | return (NULL); |
808 | 0 | } |
809 | | |
810 | | /* Parse an array value, return the node, and advance the token pointer. */ |
811 | | static struct json_node * |
812 | | json_parse_array(struct json_token **tok, struct json_parse_ctx *pctx, |
813 | | const char *key, struct json_node *parent) |
814 | 0 | { |
815 | 0 | struct json_node *array, *member; |
816 | |
|
817 | 0 | if ((*tok)->type != TOK_OPENARRAY) |
818 | 0 | return (NULL); |
819 | 0 | (*tok)++; |
820 | |
|
821 | 0 | array = json_create_node(parent, NODE_ARRAY, key, NULL); |
822 | 0 | while ((*tok)->type != TOK_CLOSEARRAY) { |
823 | 0 | switch ((*tok)->type) { |
824 | 0 | case TOK_OPENOBJECT: |
825 | 0 | member = json_parse_object(tok, pctx, NULL, array); |
826 | 0 | break; |
827 | 0 | default: |
828 | 0 | json_error(pctx->cause, "invalid array member", |
829 | 0 | pctx->input + (*tok)->offset); |
830 | 0 | goto fail; |
831 | 0 | } |
832 | 0 | if (member == NULL) |
833 | 0 | goto fail; |
834 | | |
835 | 0 | json_assign_value(array, member); |
836 | |
|
837 | 0 | if ((*tok)->type == TOK_COMMA) { |
838 | 0 | if ((*tok)[1].type == TOK_CLOSEARRAY) { |
839 | 0 | json_error(pctx->cause, "invalid array", |
840 | 0 | pctx->input + (*tok)->offset); |
841 | 0 | goto fail; |
842 | 0 | } |
843 | 0 | (*tok)++; |
844 | 0 | } else if ((*tok)->type != TOK_CLOSEARRAY) { |
845 | 0 | json_error(pctx->cause, "invalid array", |
846 | 0 | pctx->input + (*tok)->offset); |
847 | 0 | goto fail; |
848 | 0 | } |
849 | 0 | } |
850 | 0 | (*tok)++; |
851 | 0 | return (array); |
852 | | |
853 | 0 | fail: |
854 | 0 | json_destroy_node(array); |
855 | 0 | return (NULL); |
856 | 0 | } |
857 | | |
858 | | /* Parse a string value, return the node, and advance the token pointer. */ |
859 | | static struct json_node * |
860 | | json_parse_string(struct json_token **tok, struct json_parse_ctx *pctx, |
861 | | const char *key, struct json_node *parent) |
862 | 0 | { |
863 | 0 | const char *loc, *start = pctx->input + (*tok)->offset; |
864 | 0 | char *str; |
865 | 0 | int len; |
866 | |
|
867 | 0 | if ((*tok)->type != TOK_QUOTE) |
868 | 0 | goto fail; |
869 | 0 | (*tok)++; |
870 | 0 | if ((*tok)->type != TOK_VALUE) |
871 | 0 | goto fail; |
872 | | |
873 | 0 | loc = pctx->input + (*tok)->offset; |
874 | 0 | len = (*tok)->len; |
875 | 0 | (*tok)++; |
876 | |
|
877 | 0 | if ((*tok)->type != TOK_QUOTE) |
878 | 0 | goto fail; |
879 | 0 | (*tok)++; |
880 | |
|
881 | 0 | str = xstrndup(loc, len); |
882 | 0 | return (json_create_node(parent, NODE_STRING, key, str)); |
883 | | |
884 | 0 | fail: |
885 | 0 | json_error(pctx->cause, "invalid string", start); |
886 | 0 | return (NULL); |
887 | 0 | } |
888 | | |
889 | | /* Parse a number value, return the node, and advance the token pointer. */ |
890 | | static struct json_node * |
891 | | json_parse_number(struct json_token **tok, struct json_parse_ctx *pctx, |
892 | | const char *key, struct json_node *parent) |
893 | 0 | { |
894 | 0 | const char *start = pctx->input + (*tok)->offset; |
895 | 0 | char *endptr; |
896 | 0 | int64_t num; |
897 | 0 | int len = (*tok)->len; |
898 | |
|
899 | 0 | if ((start[0] == '0' && len != 1) || |
900 | 0 | (start[0] == '-' && start[1] == '0' && len != 2)) |
901 | 0 | goto fail; |
902 | | |
903 | 0 | errno = 0; |
904 | 0 | num = strtoll(start, &endptr, 10); |
905 | 0 | if (errno != 0 || endptr != start + len) |
906 | 0 | goto fail; |
907 | 0 | (*tok)++; |
908 | |
|
909 | 0 | return (json_create_node(parent, NODE_NUMBER, key, &num)); |
910 | | |
911 | 0 | fail: |
912 | 0 | json_error(pctx->cause, "invalid number", start); |
913 | 0 | return (NULL); |
914 | 0 | } |
915 | | |
916 | | /* Parse a boolean value, return the node, and advance the token pointer. */ |
917 | | static struct json_node * |
918 | | json_parse_boolean(struct json_token **tok, struct json_parse_ctx *pctx, |
919 | | const char *key, struct json_node *parent) |
920 | 0 | { |
921 | 0 | int len = (*tok)->len, boolean; |
922 | 0 | const char *start = pctx->input + (*tok)->offset; |
923 | |
|
924 | 0 | if (strncmp(start, "true", len) == 0 && len == 4) |
925 | 0 | boolean = 1; |
926 | 0 | else if (strncmp(start, "false", len) == 0 && len == 5) |
927 | 0 | boolean = 0; |
928 | 0 | else |
929 | 0 | goto fail; |
930 | 0 | (*tok)++; |
931 | |
|
932 | 0 | return (json_create_node(parent, NODE_BOOLEAN, key, &boolean)); |
933 | | |
934 | 0 | fail: |
935 | 0 | json_error(pctx->cause, "invalid boolean", start); |
936 | 0 | return (NULL); |
937 | 0 | } |
938 | | |
939 | | /* Append a node as JSON. */ |
940 | | static void |
941 | | json_string_append(struct evbuffer *buffer, struct json_node *node) |
942 | 0 | { |
943 | 0 | struct json_node *field, *member; |
944 | 0 | const char *s; |
945 | 0 | int comma = 0; |
946 | |
|
947 | 0 | switch (node->type) { |
948 | 0 | case NODE_STRING: |
949 | 0 | evbuffer_add_printf(buffer, "\"%s\"", node->str); |
950 | 0 | break; |
951 | 0 | case NODE_NUMBER: |
952 | 0 | evbuffer_add_printf(buffer, "%lld", (long long)node->num); |
953 | 0 | break; |
954 | 0 | case NODE_BOOLEAN: |
955 | 0 | if (node->boolean) |
956 | 0 | s = "true"; |
957 | 0 | else |
958 | 0 | s = "false"; |
959 | 0 | evbuffer_add(buffer, s, strlen(s)); |
960 | 0 | break; |
961 | 0 | case NODE_OBJECT: |
962 | 0 | evbuffer_add(buffer, "{", 1); |
963 | 0 | RB_FOREACH(field, json_fields, &node->fields) { |
964 | 0 | if (comma) |
965 | 0 | evbuffer_add(buffer, ",", 1); |
966 | 0 | evbuffer_add_printf(buffer, "\"%s\":", field->key); |
967 | 0 | json_string_append(buffer, field); |
968 | 0 | comma = 1; |
969 | 0 | } |
970 | 0 | evbuffer_add(buffer, "}", 1); |
971 | 0 | break; |
972 | 0 | case NODE_ARRAY: |
973 | 0 | evbuffer_add(buffer, "[", 1); |
974 | 0 | TAILQ_FOREACH(member, &node->members, aentry) { |
975 | 0 | if (comma) |
976 | 0 | evbuffer_add(buffer, ",", 1); |
977 | 0 | json_string_append(buffer, member); |
978 | 0 | comma = 1; |
979 | 0 | } |
980 | 0 | evbuffer_add(buffer, "]", 1); |
981 | 0 | break; |
982 | 0 | } |
983 | 0 | } |
984 | | |
985 | | /* Convert a node back to JSON. */ |
986 | | char * |
987 | | json_to_string(struct json_node *node) |
988 | 0 | { |
989 | 0 | struct evbuffer *buffer; |
990 | 0 | char *out; |
991 | |
|
992 | 0 | if (node == NULL) |
993 | 0 | return (NULL); |
994 | 0 | buffer = evbuffer_new(); |
995 | 0 | if (buffer == NULL) |
996 | 0 | fatalx("out of memory"); |
997 | 0 | json_string_append(buffer, node); |
998 | 0 | out = xmemdup(EVBUFFER_DATA(buffer), EVBUFFER_LENGTH(buffer)); |
999 | 0 | evbuffer_free(buffer); |
1000 | 0 | return (out); |
1001 | 0 | } |