1/*	$NetBSD: jsmn.c,v 1.6 2020/05/25 20:47:24 christos Exp $	*/
2
3#include <stdlib.h>
4
5#include "jsmn.h"
6
7/**
8 * Allocates a fresh unused token from the token pull.
9 */
10static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
11		jsmntok_t *tokens, size_t num_tokens) {
12	jsmntok_t *tok;
13	if (parser->toknext >= num_tokens) {
14		return NULL;
15	}
16	tok = &tokens[parser->toknext++];
17	tok->start = tok->end = -1;
18	tok->size = 0;
19#ifdef JSMN_PARENT_LINKS
20	tok->parent = -1;
21#endif
22	return tok;
23}
24
25/**
26 * Fills token type and boundaries.
27 */
28static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
29                            int start, int end) {
30	token->type = type;
31	token->start = start;
32	token->end = end;
33	token->size = 0;
34}
35
36/**
37 * Fills next available token with JSON primitive.
38 */
39static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
40		size_t len, jsmntok_t *tokens, size_t num_tokens) {
41	jsmntok_t *token;
42	int start;
43
44	start = parser->pos;
45
46	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
47		switch (js[parser->pos]) {
48#ifndef JSMN_STRICT
49			/* In strict mode primitive must be followed by "," or "}" or "]" */
50			case ':':
51#endif
52			case '\t' : case '\r' : case '\n' : case ' ' :
53			case ','  : case ']'  : case '}' :
54				goto found;
55		}
56		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
57			parser->pos = start;
58			return JSMN_ERROR_INVAL;
59		}
60	}
61#ifdef JSMN_STRICT
62	/* In strict mode primitive must be followed by a comma/object/array */
63	parser->pos = start;
64	return JSMN_ERROR_PART;
65#endif
66
67found:
68	if (tokens == NULL) {
69		parser->pos--;
70		return 0;
71	}
72	token = jsmn_alloc_token(parser, tokens, num_tokens);
73	if (token == NULL) {
74		parser->pos = start;
75		return JSMN_ERROR_NOMEM;
76	}
77	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
78#ifdef JSMN_PARENT_LINKS
79	token->parent = parser->toksuper;
80#endif
81	parser->pos--;
82	return 0;
83}
84
85/**
86 * Filsl next token with JSON string.
87 */
88static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
89		size_t len, jsmntok_t *tokens, size_t num_tokens) {
90	jsmntok_t *token;
91
92	int start = parser->pos;
93
94	parser->pos++;
95
96	/* Skip starting quote */
97	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
98		char c = js[parser->pos];
99
100		/* Quote: end of string */
101		if (c == '\"') {
102			if (tokens == NULL) {
103				return 0;
104			}
105			token = jsmn_alloc_token(parser, tokens, num_tokens);
106			if (token == NULL) {
107				parser->pos = start;
108				return JSMN_ERROR_NOMEM;
109			}
110			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
111#ifdef JSMN_PARENT_LINKS
112			token->parent = parser->toksuper;
113#endif
114			return 0;
115		}
116
117		/* Backslash: Quoted symbol expected */
118		if (c == '\\' && parser->pos + 1 < len) {
119			int i;
120			parser->pos++;
121			switch (js[parser->pos]) {
122				/* Allowed escaped symbols */
123				case '\"': case '/' : case '\\' : case 'b' :
124				case 'f' : case 'r' : case 'n'  : case 't' :
125					break;
126				/* Allows escaped symbol \uXXXX */
127				case 'u':
128					parser->pos++;
129					for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
130						/* If it isn't a hex character we have an error */
131						if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
132									(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
133									(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
134							parser->pos = start;
135							return JSMN_ERROR_INVAL;
136						}
137						parser->pos++;
138					}
139					parser->pos--;
140					break;
141				/* Unexpected symbol */
142				default:
143					parser->pos = start;
144					return JSMN_ERROR_INVAL;
145			}
146		}
147	}
148	parser->pos = start;
149	return JSMN_ERROR_PART;
150}
151
152/**
153 * Parse JSON string and fill tokens.
154 */
155jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
156		jsmntok_t *tokens, unsigned int num_tokens) {
157	jsmnerr_t r;
158	int i;
159	jsmntok_t *token;
160	int count = 0;
161
162	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
163		char c;
164		jsmntype_t type;
165
166		c = js[parser->pos];
167		switch (c) {
168			case '{': case '[':
169				count++;
170				if (tokens == NULL) {
171					break;
172				}
173				token = jsmn_alloc_token(parser, tokens, num_tokens);
174				if (token == NULL)
175					return JSMN_ERROR_NOMEM;
176				if (parser->toksuper != -1) {
177					tokens[parser->toksuper].size++;
178#ifdef JSMN_PARENT_LINKS
179					token->parent = parser->toksuper;
180#endif
181				}
182				token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
183				token->start = parser->pos;
184				parser->toksuper = parser->toknext - 1;
185				break;
186			case '}': case ']':
187				if (tokens == NULL)
188					break;
189				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
190#ifdef JSMN_PARENT_LINKS
191				if (parser->toknext < 1) {
192					return JSMN_ERROR_INVAL;
193				}
194				token = &tokens[parser->toknext - 1];
195				for (;;) {
196					if (token->start != -1 && token->end == -1) {
197						if (token->type != type) {
198							return JSMN_ERROR_INVAL;
199						}
200						token->end = parser->pos + 1;
201						parser->toksuper = token->parent;
202						break;
203					}
204					if (token->parent == -1) {
205						break;
206					}
207					token = &tokens[token->parent];
208				}
209#else
210				for (i = parser->toknext - 1; i >= 0; i--) {
211					token = &tokens[i];
212					if (token->start != -1 && token->end == -1) {
213						if (token->type != type) {
214							return JSMN_ERROR_INVAL;
215						}
216						parser->toksuper = -1;
217						token->end = parser->pos + 1;
218						break;
219					}
220				}
221				/* Error if unmatched closing bracket */
222				if (i == -1) return JSMN_ERROR_INVAL;
223				for (; i >= 0; i--) {
224					token = &tokens[i];
225					if (token->start != -1 && token->end == -1) {
226						parser->toksuper = i;
227						break;
228					}
229				}
230#endif
231				break;
232			case '\"':
233				r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
234				if (r < 0) return r;
235				count++;
236				if (parser->toksuper != -1 && tokens != NULL)
237					tokens[parser->toksuper].size++;
238				break;
239			case '\t' : case '\r' : case '\n' : case ' ':
240				break;
241			case ':':
242				parser->toksuper = parser->toknext - 1;
243				break;
244			case ',':
245				if (tokens != NULL &&
246						tokens[parser->toksuper].type != JSMN_ARRAY &&
247						tokens[parser->toksuper].type != JSMN_OBJECT) {
248#ifdef JSMN_PARENT_LINKS
249					parser->toksuper = tokens[parser->toksuper].parent;
250#else
251					for (i = parser->toknext - 1; i >= 0; i--) {
252						if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
253							if (tokens[i].start != -1 && tokens[i].end == -1) {
254								parser->toksuper = i;
255								break;
256							}
257						}
258					}
259#endif
260				}
261				break;
262#ifdef JSMN_STRICT
263			/* In strict mode primitives are: numbers and booleans */
264			case '-': case '0': case '1' : case '2': case '3' : case '4':
265			case '5': case '6': case '7' : case '8': case '9':
266			case 't': case 'f': case 'n' :
267				/* And they must not be keys of the object */
268				if (tokens != NULL) {
269					jsmntok_t *t = &tokens[parser->toksuper];
270					if (t->type == JSMN_OBJECT ||
271							(t->type == JSMN_STRING && t->size != 0)) {
272						return JSMN_ERROR_INVAL;
273					}
274				}
275#else
276			/* In non-strict mode every unquoted value is a primitive */
277			default:
278#endif
279				r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
280				if (r < 0) return r;
281				count++;
282				if (parser->toksuper != -1 && tokens != NULL)
283					tokens[parser->toksuper].size++;
284				break;
285
286#ifdef JSMN_STRICT
287			/* Unexpected char in strict mode */
288			default:
289				return JSMN_ERROR_INVAL;
290#endif
291		}
292	}
293	if (tokens != NULL) {
294		for (i = parser->toknext - 1; i >= 0; i--) {
295			/* Unmatched opened object or array */
296			if (tokens[i].start != -1 && tokens[i].end == -1) {
297				return JSMN_ERROR_PART;
298			}
299		}
300	}
301
302	return count;
303}
304
305/**
306 * Creates a new parser based over a given  buffer with an array of tokens
307 * available.
308 */
309void jsmn_init(jsmn_parser *parser) {
310	parser->pos = 0;
311	parser->toknext = 0;
312	parser->toksuper = -1;
313}
314
315