2 * Copyright (c) 2018 Christos Zoulas
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
28 * Parse JSON object serialization format (RFC-7159)
35 FILE_RCSID("@(#)$File: is_json.c,v 1.15 2020/06/07 19:05:47 christos Exp $")
44 #define DPRINTF(a, b, c) \
45 printf("%s [%.2x/%c] %.20s\n", (a), *(b), *(b), (const char *)(c))
47 #define DPRINTF(a, b, c) do { } while (/*CONSTCOND*/0)
51 #define JSON_CONSTANT 1
60 * count all the objects, require that we have the whole data file
62 * stop if we find an object or an array
68 static int json_parse(const unsigned char **, const unsigned char *, size_t *,
72 json_isspace(const unsigned char uc)
86 json_isdigit(unsigned char uc)
89 case '0': case '1': case '2': case '3': case '4':
90 case '5': case '6': case '7': case '8': case '9':
98 json_isxdigit(unsigned char uc)
100 if (json_isdigit(uc))
103 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
104 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
111 static const unsigned char *
112 json_skip_space(const unsigned char *uc, const unsigned char *ue)
114 while (uc < ue && json_isspace(*uc))
120 json_parse_string(const unsigned char **ucp, const unsigned char *ue)
122 const unsigned char *uc = *ucp;
125 DPRINTF("Parse string: ", uc, *ucp);
150 for (i = 0; i < 4; i++)
151 if (!json_isxdigit(*uc++))
159 DPRINTF("Good string: ", uc, *ucp);
166 DPRINTF("Bad string: ", uc, *ucp);
172 json_parse_array(const unsigned char **ucp, const unsigned char *ue,
173 size_t *st, size_t lvl)
175 const unsigned char *uc = *ucp;
177 DPRINTF("Parse array: ", uc, *ucp);
181 if (!json_parse(&uc, ue, st, lvl + 1))
193 DPRINTF("Good array: ", uc, *ucp);
200 DPRINTF("Bad array: ", uc, *ucp);
206 json_parse_object(const unsigned char **ucp, const unsigned char *ue,
207 size_t *st, size_t lvl)
209 const unsigned char *uc = *ucp;
210 DPRINTF("Parse object: ", uc, *ucp);
212 uc = json_skip_space(uc, ue);
220 DPRINTF("not string", uc, *ucp);
223 DPRINTF("next field", uc, *ucp);
224 if (!json_parse_string(&uc, ue)) {
225 DPRINTF("not string", uc, *ucp);
228 uc = json_skip_space(uc, ue);
232 DPRINTF("not colon", uc, *ucp);
235 if (!json_parse(&uc, ue, st, lvl + 1)) {
236 DPRINTF("not json", uc, *ucp);
247 DPRINTF("Good object: ", uc, *ucp);
251 DPRINTF("not more", uc, *ucp);
256 DPRINTF("Bad object: ", uc, *ucp);
262 json_parse_number(const unsigned char **ucp, const unsigned char *ue)
264 const unsigned char *uc = *ucp;
267 DPRINTF("Parse number: ", uc, *ucp);
273 for (; uc < ue; uc++) {
274 if (!json_isdigit(*uc))
282 for (; uc < ue; uc++) {
283 if (!json_isdigit(*uc))
289 if (got && (*uc == 'e' || *uc == 'E')) {
294 if (*uc == '+' || *uc == '-')
296 for (; uc < ue; uc++) {
297 if (!json_isdigit(*uc))
304 DPRINTF("Bad number: ", uc, *ucp);
306 DPRINTF("Good number: ", uc, *ucp);
312 json_parse_const(const unsigned char **ucp, const unsigned char *ue,
313 const char *str, size_t len)
315 const unsigned char *uc = *ucp;
317 DPRINTF("Parse const: ", uc, *ucp);
318 for (len--; uc < ue && --len;) {
323 DPRINTF("Bad const: ", uc, *ucp);
329 json_parse(const unsigned char **ucp, const unsigned char *ue,
330 size_t *st, size_t lvl)
332 const unsigned char *uc;
336 uc = json_skip_space(*ucp, ue);
344 /* bail quickly if not counting */
345 if (lvl > 1 && (st[JSON_OBJECT] || st[JSON_ARRAYN]))
349 DPRINTF("Parse general: ", uc, *ucp);
352 rv = json_parse_string(&uc, ue);
356 rv = json_parse_array(&uc, ue, st, lvl + 1);
360 rv = json_parse_object(&uc, ue, st, lvl + 1);
364 rv = json_parse_const(&uc, ue, "true", sizeof("true"));
368 rv = json_parse_const(&uc, ue, "false", sizeof("false"));
372 rv = json_parse_const(&uc, ue, "null", sizeof("null"));
377 rv = json_parse_number(&uc, ue);
383 uc = json_skip_space(uc, ue);
386 DPRINTF("End general: ", uc, *ucp);
388 return rv && (st[JSON_ARRAYN] || st[JSON_OBJECT]);
394 file_is_json(struct magic_set *ms, const struct buffer *b)
396 const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
397 const unsigned char *ue = uc + b->flen;
399 int mime = ms->flags & MAGIC_MIME;
402 if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
405 memset(st, 0, sizeof(st));
407 if (!json_parse(&uc, ue, st, 0))
410 if (mime == MAGIC_MIME_ENCODING)
413 if (file_printf(ms, "application/json") == -1)
417 if (file_printf(ms, "JSON data") == -1)
420 #define P(n) st[n], st[n] > 1 ? "s" : ""
421 if (file_printf(ms, " (%" SIZE_T_FORMAT "u object%s, %" SIZE_T_FORMAT
422 "u array%s, %" SIZE_T_FORMAT "u string%s, %" SIZE_T_FORMAT
423 "u constant%s, %" SIZE_T_FORMAT "u number%s, %" SIZE_T_FORMAT
425 P(JSON_OBJECT), P(JSON_ARRAY), P(JSON_STRING), P(JSON_CONSTANT),
426 P(JSON_NUMBER), P(JSON_ARRAYN))
435 #include <sys/types.h>
436 #include <sys/stat.h>
445 main(int argc, char *argv[])
450 size_t stats[JSON_MAX];
452 if ((fd = open(argv[1], O_RDONLY)) == -1)
453 err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
455 if (fstat(fd, &st) == -1)
456 err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
458 if ((p = malloc(st.st_size)) == NULL)
459 err(EXIT_FAILURE, "Can't allocate %jd bytes",
460 (intmax_t)st.st_size);
461 if (read(fd, p, st.st_size) != st.st_size)
462 err(EXIT_FAILURE, "Can't read %jd bytes",
463 (intmax_t)st.st_size);
464 memset(stats, 0, sizeof(stats));
465 printf("is json %d\n", json_parse((const unsigned char **)&p,
466 p + st.st_size, stats, 0));