xref: /netbsd-src/external/bsd/file/dist/src/is_json.c (revision ddb176824c39fb0db5ceef3e9e40dcaa273aec38)
1*ddb17682Schristos /*	$NetBSD: is_json.c,v 1.6 2023/08/18 19:00:11 christos Exp $	*/
2006f8008Schristos 
3006f8008Schristos /*-
4006f8008Schristos  * Copyright (c) 2018 Christos Zoulas
5006f8008Schristos  * All rights reserved.
6006f8008Schristos  *
7006f8008Schristos  * Redistribution and use in source and binary forms, with or without
8006f8008Schristos  * modification, are permitted provided that the following conditions
9006f8008Schristos  * are met:
10006f8008Schristos  * 1. Redistributions of source code must retain the above copyright
11006f8008Schristos  *    notice, this list of conditions and the following disclaimer.
12006f8008Schristos  * 2. Redistributions in binary form must reproduce the above copyright
13006f8008Schristos  *    notice, this list of conditions and the following disclaimer in the
14006f8008Schristos  *    documentation and/or other materials provided with the distribution.
15006f8008Schristos  *
16006f8008Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17006f8008Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18006f8008Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19006f8008Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20006f8008Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21006f8008Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22006f8008Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23006f8008Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24006f8008Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25006f8008Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26006f8008Schristos  * POSSIBILITY OF SUCH DAMAGE.
27006f8008Schristos  */
28006f8008Schristos 
29006f8008Schristos /*
30006f8008Schristos  * Parse JSON object serialization format (RFC-7159)
31006f8008Schristos  */
32006f8008Schristos 
33006f8008Schristos #ifndef TEST
34006f8008Schristos #include "file.h"
35006f8008Schristos 
36006f8008Schristos #ifndef lint
37006f8008Schristos #if 0
38*ddb17682Schristos FILE_RCSID("@(#)$File: is_json.c,v 1.30 2022/09/27 19:12:40 christos Exp $")
39006f8008Schristos #else
40*ddb17682Schristos __RCSID("$NetBSD: is_json.c,v 1.6 2023/08/18 19:00:11 christos Exp $");
41006f8008Schristos #endif
42006f8008Schristos #endif
43006f8008Schristos 
44006f8008Schristos #include "magic.h"
451d4cb158Schristos #else
461d4cb158Schristos #include <stdio.h>
471d4cb158Schristos #include <stddef.h>
48006f8008Schristos #endif
491d4cb158Schristos #include <string.h>
50006f8008Schristos 
51006f8008Schristos #ifdef DEBUG
52006f8008Schristos #include <stdio.h>
53006f8008Schristos #define DPRINTF(a, b, c)	\
541d4cb158Schristos     printf("%*s%s [%.2x/%c] %.*s\n", (int)lvl, "", (a), *(b), *(b), \
551d4cb158Schristos 	(int)(b - c), (const char *)(c))
561d4cb158Schristos #define __file_debugused
57006f8008Schristos #else
58bdab9e2dSchristos #define DPRINTF(a, b, c)	do { } while (/*CONSTCOND*/0)
591d4cb158Schristos #define __file_debugused __attribute__((__unused__))
60006f8008Schristos #endif
61006f8008Schristos 
62006f8008Schristos #define JSON_ARRAY	0
63006f8008Schristos #define JSON_CONSTANT	1
64006f8008Schristos #define JSON_NUMBER	2
65006f8008Schristos #define JSON_OBJECT	3
66006f8008Schristos #define JSON_STRING	4
67d0c65b7bSchristos #define JSON_ARRAYN	5
68d0c65b7bSchristos #define JSON_MAX	6
69006f8008Schristos 
70006f8008Schristos /*
71006f8008Schristos  * if JSON_COUNT != 0:
72006f8008Schristos  *	count all the objects, require that we have the whole data file
73006f8008Schristos  * otherwise:
74006f8008Schristos  *	stop if we find an object or an array
75006f8008Schristos  */
76006f8008Schristos #ifndef JSON_COUNT
77006f8008Schristos #define JSON_COUNT 0
78006f8008Schristos #endif
79006f8008Schristos 
80006f8008Schristos static int json_parse(const unsigned char **, const unsigned char *, size_t *,
81006f8008Schristos 	size_t);
82006f8008Schristos 
83006f8008Schristos static int
json_isspace(const unsigned char uc)84006f8008Schristos json_isspace(const unsigned char uc)
85006f8008Schristos {
86006f8008Schristos 	switch (uc) {
87006f8008Schristos 	case ' ':
88006f8008Schristos 	case '\n':
89006f8008Schristos 	case '\r':
90006f8008Schristos 	case '\t':
91006f8008Schristos 		return 1;
92006f8008Schristos 	default:
93006f8008Schristos 		return 0;
94006f8008Schristos 	}
95006f8008Schristos }
96006f8008Schristos 
97006f8008Schristos static int
json_isdigit(unsigned char uc)98006f8008Schristos json_isdigit(unsigned char uc)
99006f8008Schristos {
100006f8008Schristos 	switch (uc) {
101006f8008Schristos 	case '0': case '1': case '2': case '3': case '4':
102006f8008Schristos 	case '5': case '6': case '7': case '8': case '9':
103006f8008Schristos 		return 1;
104006f8008Schristos 	default:
105006f8008Schristos 		return 0;
106006f8008Schristos 	}
107006f8008Schristos }
108006f8008Schristos 
109006f8008Schristos static int
json_isxdigit(unsigned char uc)110006f8008Schristos json_isxdigit(unsigned char uc)
111006f8008Schristos {
112006f8008Schristos 	if (json_isdigit(uc))
113006f8008Schristos 		return 1;
114006f8008Schristos 	switch (uc) {
115006f8008Schristos 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
116006f8008Schristos 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
117006f8008Schristos 		return 1;
118006f8008Schristos 	default:
119006f8008Schristos 		return 0;
120006f8008Schristos 	}
121006f8008Schristos }
122006f8008Schristos 
123006f8008Schristos static const unsigned char *
json_skip_space(const unsigned char * uc,const unsigned char * ue)124006f8008Schristos json_skip_space(const unsigned char *uc, const unsigned char *ue)
125006f8008Schristos {
126006f8008Schristos 	while (uc < ue && json_isspace(*uc))
127006f8008Schristos 		uc++;
128006f8008Schristos 	return uc;
129006f8008Schristos }
130006f8008Schristos 
1311d4cb158Schristos /*ARGSUSED*/
132006f8008Schristos static int
json_parse_string(const unsigned char ** ucp,const unsigned char * ue,size_t lvl __file_debugused)1331d4cb158Schristos json_parse_string(const unsigned char **ucp, const unsigned char *ue,
1341d4cb158Schristos     size_t lvl __file_debugused)
135006f8008Schristos {
136006f8008Schristos 	const unsigned char *uc = *ucp;
137006f8008Schristos 	size_t i;
138006f8008Schristos 
139006f8008Schristos 	DPRINTF("Parse string: ", uc, *ucp);
140006f8008Schristos 	while (uc < ue) {
141006f8008Schristos 		switch (*uc++) {
142006f8008Schristos 		case '\0':
143006f8008Schristos 			goto out;
144006f8008Schristos 		case '\\':
145006f8008Schristos 			if (uc == ue)
146006f8008Schristos 				goto out;
147006f8008Schristos 			switch (*uc++) {
148006f8008Schristos 			case '\0':
149006f8008Schristos 				goto out;
150006f8008Schristos 			case '"':
151006f8008Schristos 			case '\\':
152006f8008Schristos 			case '/':
153006f8008Schristos 			case 'b':
154006f8008Schristos 			case 'f':
155006f8008Schristos 			case 'n':
156006f8008Schristos 			case 'r':
157006f8008Schristos 			case 't':
158006f8008Schristos 				continue;
159006f8008Schristos 			case 'u':
160006f8008Schristos 				if (ue - uc < 4) {
161006f8008Schristos 					uc = ue;
162006f8008Schristos 					goto out;
163006f8008Schristos 				}
164006f8008Schristos 				for (i = 0; i < 4; i++)
165006f8008Schristos 					if (!json_isxdigit(*uc++))
166006f8008Schristos 						goto out;
167006f8008Schristos 				continue;
168006f8008Schristos 			default:
169006f8008Schristos 				goto out;
170006f8008Schristos 			}
171006f8008Schristos 		case '"':
17229faeba7Schristos 			DPRINTF("Good string: ", uc, *ucp);
1731d4cb158Schristos 			*ucp = uc;
174006f8008Schristos 			return 1;
175006f8008Schristos 		default:
176006f8008Schristos 			continue;
177006f8008Schristos 		}
178006f8008Schristos 	}
179006f8008Schristos out:
180006f8008Schristos 	DPRINTF("Bad string: ", uc, *ucp);
181006f8008Schristos 	*ucp = uc;
182006f8008Schristos 	return 0;
183006f8008Schristos }
184006f8008Schristos 
185006f8008Schristos static int
json_parse_array(const unsigned char ** ucp,const unsigned char * ue,size_t * st,size_t lvl)186006f8008Schristos json_parse_array(const unsigned char **ucp, const unsigned char *ue,
187006f8008Schristos 	size_t *st, size_t lvl)
188006f8008Schristos {
189006f8008Schristos 	const unsigned char *uc = *ucp;
190006f8008Schristos 
191006f8008Schristos 	DPRINTF("Parse array: ", uc, *ucp);
192006f8008Schristos 	while (uc < ue) {
1931d4cb158Schristos 		uc = json_skip_space(uc, ue);
1941d4cb158Schristos 		if (uc == ue)
1951d4cb158Schristos 			goto out;
19629faeba7Schristos 		if (*uc == ']')
19729faeba7Schristos 			goto done;
198006f8008Schristos 		if (!json_parse(&uc, ue, st, lvl + 1))
199006f8008Schristos 			goto out;
200006f8008Schristos 		if (uc == ue)
201006f8008Schristos 			goto out;
202006f8008Schristos 		switch (*uc) {
203006f8008Schristos 		case ',':
204006f8008Schristos 			uc++;
205006f8008Schristos 			continue;
206006f8008Schristos 		case ']':
20729faeba7Schristos 		done:
208d0c65b7bSchristos 			st[JSON_ARRAYN]++;
20929faeba7Schristos 			DPRINTF("Good array: ", uc, *ucp);
2101d4cb158Schristos 			*ucp = uc + 1;
211006f8008Schristos 			return 1;
212006f8008Schristos 		default:
213006f8008Schristos 			goto out;
214006f8008Schristos 		}
215006f8008Schristos 	}
216006f8008Schristos out:
217006f8008Schristos 	DPRINTF("Bad array: ", uc,  *ucp);
218006f8008Schristos 	*ucp = uc;
219006f8008Schristos 	return 0;
220006f8008Schristos }
221006f8008Schristos 
222006f8008Schristos static int
json_parse_object(const unsigned char ** ucp,const unsigned char * ue,size_t * st,size_t lvl)223006f8008Schristos json_parse_object(const unsigned char **ucp, const unsigned char *ue,
224006f8008Schristos 	size_t *st, size_t lvl)
225006f8008Schristos {
226006f8008Schristos 	const unsigned char *uc = *ucp;
227006f8008Schristos 	DPRINTF("Parse object: ", uc, *ucp);
228006f8008Schristos 	while (uc < ue) {
229006f8008Schristos 		uc = json_skip_space(uc, ue);
230006f8008Schristos 		if (uc == ue)
231006f8008Schristos 			goto out;
23229faeba7Schristos 		if (*uc == '}') {
23329faeba7Schristos 			uc++;
23429faeba7Schristos 			goto done;
23529faeba7Schristos 		}
236006f8008Schristos 		if (*uc++ != '"') {
237006f8008Schristos 			DPRINTF("not string", uc, *ucp);
238006f8008Schristos 			goto out;
239006f8008Schristos 		}
240006f8008Schristos 		DPRINTF("next field", uc, *ucp);
2411d4cb158Schristos 		if (!json_parse_string(&uc, ue, lvl)) {
242006f8008Schristos 			DPRINTF("not string", uc, *ucp);
243006f8008Schristos 			goto out;
244006f8008Schristos 		}
245006f8008Schristos 		uc = json_skip_space(uc, ue);
246006f8008Schristos 		if (uc == ue)
247006f8008Schristos 			goto out;
248006f8008Schristos 		if (*uc++ != ':') {
249006f8008Schristos 			DPRINTF("not colon", uc, *ucp);
250006f8008Schristos 			goto out;
251006f8008Schristos 		}
252006f8008Schristos 		if (!json_parse(&uc, ue, st, lvl + 1)) {
253006f8008Schristos 			DPRINTF("not json", uc, *ucp);
254006f8008Schristos 			goto out;
255006f8008Schristos 		}
256006f8008Schristos 		if (uc == ue)
257006f8008Schristos 			goto out;
258006f8008Schristos 		switch (*uc++) {
259006f8008Schristos 		case ',':
260006f8008Schristos 			continue;
261006f8008Schristos 		case '}': /* { */
26229faeba7Schristos 		done:
263006f8008Schristos 			DPRINTF("Good object: ", uc, *ucp);
2641d4cb158Schristos 			*ucp = uc;
265006f8008Schristos 			return 1;
266006f8008Schristos 		default:
267006f8008Schristos 			DPRINTF("not more", uc, *ucp);
2681d4cb158Schristos 			*ucp = uc - 1;
269006f8008Schristos 			goto out;
270006f8008Schristos 		}
271006f8008Schristos 	}
272006f8008Schristos out:
273006f8008Schristos 	DPRINTF("Bad object: ", uc, *ucp);
274006f8008Schristos 	*ucp = uc;
275006f8008Schristos 	return 0;
276006f8008Schristos }
277006f8008Schristos 
2781d4cb158Schristos /*ARGSUSED*/
279006f8008Schristos static int
json_parse_number(const unsigned char ** ucp,const unsigned char * ue,size_t lvl __file_debugused)2801d4cb158Schristos json_parse_number(const unsigned char **ucp, const unsigned char *ue,
2811d4cb158Schristos     size_t lvl __file_debugused)
282006f8008Schristos {
283006f8008Schristos 	const unsigned char *uc = *ucp;
284006f8008Schristos 	int got = 0;
285006f8008Schristos 
286006f8008Schristos 	DPRINTF("Parse number: ", uc, *ucp);
287006f8008Schristos 	if (uc == ue)
288006f8008Schristos 		return 0;
289006f8008Schristos 	if (*uc == '-')
290006f8008Schristos 		uc++;
291006f8008Schristos 
292006f8008Schristos 	for (; uc < ue; uc++) {
293006f8008Schristos 		if (!json_isdigit(*uc))
294006f8008Schristos 			break;
295006f8008Schristos 		got = 1;
296006f8008Schristos 	}
297006f8008Schristos 	if (uc == ue)
298006f8008Schristos 		goto out;
299006f8008Schristos 	if (*uc == '.')
300006f8008Schristos 		uc++;
301006f8008Schristos 	for (; uc < ue; uc++) {
302006f8008Schristos 		if (!json_isdigit(*uc))
303006f8008Schristos 			break;
304006f8008Schristos 		got = 1;
305006f8008Schristos 	}
306006f8008Schristos 	if (uc == ue)
307006f8008Schristos 		goto out;
308006f8008Schristos 	if (got && (*uc == 'e' || *uc == 'E')) {
309006f8008Schristos 		uc++;
310006f8008Schristos 		got = 0;
311006f8008Schristos 		if (uc == ue)
312006f8008Schristos 			goto out;
313006f8008Schristos 		if (*uc == '+' || *uc == '-')
314006f8008Schristos 			uc++;
315006f8008Schristos 		for (; uc < ue; uc++) {
316006f8008Schristos 			if (!json_isdigit(*uc))
317006f8008Schristos 				break;
318006f8008Schristos 			got = 1;
319006f8008Schristos 		}
320006f8008Schristos 	}
321006f8008Schristos out:
322006f8008Schristos 	if (!got)
323006f8008Schristos 		DPRINTF("Bad number: ", uc, *ucp);
324006f8008Schristos 	else
325006f8008Schristos 		DPRINTF("Good number: ", uc, *ucp);
326006f8008Schristos 	*ucp = uc;
327006f8008Schristos 	return got;
328006f8008Schristos }
329006f8008Schristos 
3301d4cb158Schristos /*ARGSUSED*/
331006f8008Schristos static int
json_parse_const(const unsigned char ** ucp,const unsigned char * ue,const char * str,size_t len,size_t lvl __file_debugused)332006f8008Schristos json_parse_const(const unsigned char **ucp, const unsigned char *ue,
3331d4cb158Schristos     const char *str, size_t len, size_t lvl __file_debugused)
334006f8008Schristos {
335006f8008Schristos 	const unsigned char *uc = *ucp;
336006f8008Schristos 
337006f8008Schristos 	DPRINTF("Parse const: ", uc, *ucp);
3381d4cb158Schristos 	*ucp += --len - 1;
3391d4cb158Schristos 	if (*ucp > ue)
3401d4cb158Schristos 		*ucp = ue;
3411d4cb158Schristos 	for (; uc < ue && --len;) {
3421d4cb158Schristos 		if (*uc++ != *++str) {
343006f8008Schristos 			DPRINTF("Bad const: ", uc, *ucp);
3441d4cb158Schristos 			return 0;
3451d4cb158Schristos 		}
3461d4cb158Schristos 	}
3471d4cb158Schristos 	DPRINTF("Good const: ", uc, *ucp);
3481d4cb158Schristos 	return 1;
349006f8008Schristos }
350006f8008Schristos 
351006f8008Schristos static int
json_parse(const unsigned char ** ucp,const unsigned char * ue,size_t * st,size_t lvl)352006f8008Schristos json_parse(const unsigned char **ucp, const unsigned char *ue,
353006f8008Schristos     size_t *st, size_t lvl)
354006f8008Schristos {
3551d4cb158Schristos 	const unsigned char *uc, *ouc;
356006f8008Schristos 	int rv = 0;
357006f8008Schristos 	int t;
358006f8008Schristos 
3591d4cb158Schristos 	ouc = uc = json_skip_space(*ucp, ue);
360006f8008Schristos 	if (uc == ue)
361006f8008Schristos 		goto out;
362006f8008Schristos 
363006f8008Schristos 	// Avoid recursion
3641d4cb158Schristos 	if (lvl > 500) {
3651d4cb158Schristos 		DPRINTF("Too many levels", uc, *ucp);
366006f8008Schristos 		return 0;
3671d4cb158Schristos 	}
368006f8008Schristos #if JSON_COUNT
369006f8008Schristos 	/* bail quickly if not counting */
370d0c65b7bSchristos 	if (lvl > 1 && (st[JSON_OBJECT] || st[JSON_ARRAYN]))
371006f8008Schristos 		return 1;
372006f8008Schristos #endif
373006f8008Schristos 
374006f8008Schristos 	DPRINTF("Parse general: ", uc, *ucp);
375006f8008Schristos 	switch (*uc++) {
376006f8008Schristos 	case '"':
3771d4cb158Schristos 		rv = json_parse_string(&uc, ue, lvl + 1);
378006f8008Schristos 		t = JSON_STRING;
379006f8008Schristos 		break;
380006f8008Schristos 	case '[':
381006f8008Schristos 		rv = json_parse_array(&uc, ue, st, lvl + 1);
382006f8008Schristos 		t = JSON_ARRAY;
383006f8008Schristos 		break;
384006f8008Schristos 	case '{': /* '}' */
385006f8008Schristos 		rv = json_parse_object(&uc, ue, st, lvl + 1);
386006f8008Schristos 		t = JSON_OBJECT;
387006f8008Schristos 		break;
388006f8008Schristos 	case 't':
3891d4cb158Schristos 		rv = json_parse_const(&uc, ue, "true", sizeof("true"), lvl + 1);
390006f8008Schristos 		t = JSON_CONSTANT;
391006f8008Schristos 		break;
392006f8008Schristos 	case 'f':
3931d4cb158Schristos 		rv = json_parse_const(&uc, ue, "false", sizeof("false"),
3941d4cb158Schristos 		    lvl + 1);
395006f8008Schristos 		t = JSON_CONSTANT;
396006f8008Schristos 		break;
397006f8008Schristos 	case 'n':
3981d4cb158Schristos 		rv = json_parse_const(&uc, ue, "null", sizeof("null"), lvl + 1);
399006f8008Schristos 		t = JSON_CONSTANT;
400006f8008Schristos 		break;
401006f8008Schristos 	default:
402006f8008Schristos 		--uc;
4031d4cb158Schristos 		rv = json_parse_number(&uc, ue, lvl + 1);
404006f8008Schristos 		t = JSON_NUMBER;
405006f8008Schristos 		break;
406006f8008Schristos 	}
407006f8008Schristos 	if (rv)
408006f8008Schristos 		st[t]++;
409006f8008Schristos 	uc = json_skip_space(uc, ue);
410006f8008Schristos out:
411006f8008Schristos 	DPRINTF("End general: ", uc, *ucp);
4121d4cb158Schristos 	*ucp = uc;
4131d4cb158Schristos 	if (lvl == 0) {
4141d4cb158Schristos 		if (!rv)
4151d4cb158Schristos 			return 0;
4161d4cb158Schristos 		if (uc == ue)
4171d4cb158Schristos 			return (st[JSON_ARRAYN] || st[JSON_OBJECT]) ? 1 : 0;
4181d4cb158Schristos 		if (*ouc == *uc && json_parse(&uc, ue, st, 1))
4191d4cb158Schristos 			return (st[JSON_ARRAYN] || st[JSON_OBJECT]) ? 2 : 0;
4201d4cb158Schristos 		else
4211d4cb158Schristos 			return 0;
4221d4cb158Schristos 	}
423006f8008Schristos 	return rv;
424006f8008Schristos }
425006f8008Schristos 
426006f8008Schristos #ifndef TEST
427006f8008Schristos int
file_is_json(struct magic_set * ms,const struct buffer * b)428006f8008Schristos file_is_json(struct magic_set *ms, const struct buffer *b)
429006f8008Schristos {
430006f8008Schristos 	const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
431006f8008Schristos 	const unsigned char *ue = uc + b->flen;
432006f8008Schristos 	size_t st[JSON_MAX];
433006f8008Schristos 	int mime = ms->flags & MAGIC_MIME;
4341d4cb158Schristos 	int jt;
435006f8008Schristos 
436006f8008Schristos 
437006f8008Schristos 	if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
438006f8008Schristos 		return 0;
439006f8008Schristos 
440006f8008Schristos 	memset(st, 0, sizeof(st));
441006f8008Schristos 
4421d4cb158Schristos 	if ((jt = json_parse(&uc, ue, st, 0)) == 0)
443006f8008Schristos 		return 0;
444006f8008Schristos 
445006f8008Schristos 	if (mime == MAGIC_MIME_ENCODING)
446006f8008Schristos 		return 1;
447006f8008Schristos 	if (mime) {
4481d4cb158Schristos 		if (file_printf(ms, "application/%s",
449*ddb17682Schristos 		    jt == 1 ? "json" : "x-ndjson") == -1)
450006f8008Schristos 			return -1;
451006f8008Schristos 		return 1;
452006f8008Schristos 	}
4531d4cb158Schristos 	if (file_printf(ms, "%sJSON text data",
4541d4cb158Schristos 	    jt == 1 ? "" : "New Line Delimited ") == -1)
455006f8008Schristos 		return -1;
456006f8008Schristos #if JSON_COUNT
457006f8008Schristos #define P(n) st[n], st[n] > 1 ? "s" : ""
458006f8008Schristos 	if (file_printf(ms, " (%" SIZE_T_FORMAT "u object%s, %" SIZE_T_FORMAT
459006f8008Schristos 	    "u array%s, %" SIZE_T_FORMAT "u string%s, %" SIZE_T_FORMAT
460d0c65b7bSchristos 	    "u constant%s, %" SIZE_T_FORMAT "u number%s, %" SIZE_T_FORMAT
461d0c65b7bSchristos 	    "u >1array%s)",
462d0c65b7bSchristos 	    P(JSON_OBJECT), P(JSON_ARRAY), P(JSON_STRING), P(JSON_CONSTANT),
463d0c65b7bSchristos 	    P(JSON_NUMBER), P(JSON_ARRAYN))
464006f8008Schristos 	    == -1)
465006f8008Schristos 		return -1;
466006f8008Schristos #endif
467006f8008Schristos 	return 1;
468006f8008Schristos }
469006f8008Schristos 
470006f8008Schristos #else
471006f8008Schristos 
472006f8008Schristos #include <sys/types.h>
473006f8008Schristos #include <sys/stat.h>
474006f8008Schristos #include <stdio.h>
475006f8008Schristos #include <fcntl.h>
476006f8008Schristos #include <unistd.h>
477006f8008Schristos #include <stdlib.h>
478006f8008Schristos #include <stdint.h>
479006f8008Schristos #include <err.h>
480006f8008Schristos 
481006f8008Schristos int
main(int argc,char * argv[])482006f8008Schristos main(int argc, char *argv[])
483006f8008Schristos {
4841d4cb158Schristos 	int fd;
485006f8008Schristos 	struct stat st;
486006f8008Schristos 	unsigned char *p;
487006f8008Schristos 	size_t stats[JSON_MAX];
488006f8008Schristos 
489006f8008Schristos 	if ((fd = open(argv[1], O_RDONLY)) == -1)
490006f8008Schristos 		err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
491006f8008Schristos 
492006f8008Schristos 	if (fstat(fd, &st) == -1)
493006f8008Schristos 		err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
494006f8008Schristos 
4951d4cb158Schristos 	if ((p = CAST(char *, malloc(st.st_size))) == NULL)
496006f8008Schristos 		err(EXIT_FAILURE, "Can't allocate %jd bytes",
497006f8008Schristos 		    (intmax_t)st.st_size);
498006f8008Schristos 	if (read(fd, p, st.st_size) != st.st_size)
499006f8008Schristos 		err(EXIT_FAILURE, "Can't read %jd bytes",
500006f8008Schristos 		    (intmax_t)st.st_size);
501006f8008Schristos 	memset(stats, 0, sizeof(stats));
502006f8008Schristos 	printf("is json %d\n", json_parse((const unsigned char **)&p,
503006f8008Schristos 	    p + st.st_size, stats, 0));
504006f8008Schristos 	return 0;
505006f8008Schristos }
506006f8008Schristos #endif
507