1/*
2 * Copyright (c) 2010 Serge A. Zaitsev
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 *
22 * Slightly modified by AK to not assume 0 terminated input.
23 *
24 */
25
26#include <stdlib.h>
27#include "jsmn.h"
28#define JSMN_STRICT
29
30/*
31 * Allocates a fresh unused token from the token pool.
32 */
33static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
34				   jsmntok_t *tokens, size_t num_tokens)
35{
36	jsmntok_t *tok;
37
38	if ((unsigned)parser->toknext >= num_tokens)
39		return NULL;
40	tok = &tokens[parser->toknext++];
41	tok->start = tok->end = -1;
42	tok->size = 0;
43	return tok;
44}
45
46/*
47 * Fills token type and boundaries.
48 */
49static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
50			    int start, int end)
51{
52	token->type = type;
53	token->start = start;
54	token->end = end;
55	token->size = 0;
56}
57
58/*
59 * Fills next available token with JSON primitive.
60 */
61static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
62				      size_t len,
63				      jsmntok_t *tokens, size_t num_tokens)
64{
65	jsmntok_t *token;
66	int start;
67
68	start = parser->pos;
69
70	for (; parser->pos < len; parser->pos++) {
71		switch (js[parser->pos]) {
72#ifndef JSMN_STRICT
73		/*
74		 * In strict mode primitive must be followed by ","
75		 * or "}" or "]"
76		 */
77		case ':':
78#endif
79		case '\t':
80		case '\r':
81		case '\n':
82		case ' ':
83		case ',':
84		case ']':
85		case '}':
86			goto found;
87		default:
88			break;
89		}
90		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
91			parser->pos = start;
92			return JSMN_ERROR_INVAL;
93		}
94	}
95#ifdef JSMN_STRICT
96	/*
97	 * In strict mode primitive must be followed by a
98	 * comma/object/array.
99	 */
100	parser->pos = start;
101	return JSMN_ERROR_PART;
102#endif
103
104found:
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_PRIMITIVE, start, parser->pos);
111	parser->pos--; /* parent sees closing brackets */
112	return JSMN_SUCCESS;
113}
114
115/*
116 * Fills next token with JSON string.
117 */
118static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
119				   size_t len,
120				   jsmntok_t *tokens, size_t num_tokens)
121{
122	jsmntok_t *token;
123	int start = parser->pos;
124
125	/* Skip starting quote */
126	parser->pos++;
127
128	for (; parser->pos < len; parser->pos++) {
129		char c = js[parser->pos];
130
131		/* Quote: end of string */
132		if (c == '\"') {
133			token = jsmn_alloc_token(parser, tokens, num_tokens);
134			if (token == NULL) {
135				parser->pos = start;
136				return JSMN_ERROR_NOMEM;
137			}
138			jsmn_fill_token(token, JSMN_STRING, start+1,
139					parser->pos);
140			return JSMN_SUCCESS;
141		}
142
143		/* Backslash: Quoted symbol expected */
144		if (c == '\\') {
145			parser->pos++;
146			switch (js[parser->pos]) {
147				/* Allowed escaped symbols */
148			case '\"':
149			case '/':
150			case '\\':
151			case 'b':
152			case 'f':
153			case 'r':
154			case 'n':
155			case 't':
156				break;
157				/* Allows escaped symbol \uXXXX */
158			case 'u':
159				/* TODO */
160				break;
161				/* Unexpected symbol */
162			default:
163				parser->pos = start;
164				return JSMN_ERROR_INVAL;
165			}
166		}
167	}
168	parser->pos = start;
169	return JSMN_ERROR_PART;
170}
171
172/*
173 * Parse JSON string and fill tokens.
174 */
175jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
176		     jsmntok_t *tokens, unsigned int num_tokens)
177{
178	jsmnerr_t r;
179	int i;
180	jsmntok_t *token;
181#ifdef JSMN_STRICT
182	/*
183	 * Keeps track of whether a new object/list/primitive is expected. New items are only
184	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
185	 * valid JSON.
186	 */
187	int expecting_item = 1;
188#endif
189
190	for (; parser->pos < len; parser->pos++) {
191		char c;
192		jsmntype_t type;
193
194		c = js[parser->pos];
195		switch (c) {
196		case '{':
197		case '[':
198#ifdef JSMN_STRICT
199			if (!expecting_item)
200				return JSMN_ERROR_INVAL;
201#endif
202			token = jsmn_alloc_token(parser, tokens, num_tokens);
203			if (token == NULL)
204				return JSMN_ERROR_NOMEM;
205			if (parser->toksuper != -1)
206				tokens[parser->toksuper].size++;
207			token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
208			token->start = parser->pos;
209			parser->toksuper = parser->toknext - 1;
210			break;
211		case '}':
212		case ']':
213#ifdef JSMN_STRICT
214			if (expecting_item)
215				return JSMN_ERROR_INVAL;
216#endif
217			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
218			for (i = parser->toknext - 1; i >= 0; i--) {
219				token = &tokens[i];
220				if (token->start != -1 && token->end == -1) {
221					if (token->type != type)
222						return JSMN_ERROR_INVAL;
223					parser->toksuper = -1;
224					token->end = parser->pos + 1;
225					break;
226				}
227			}
228			/* Error if unmatched closing bracket */
229			if (i == -1)
230				return JSMN_ERROR_INVAL;
231			for (; i >= 0; i--) {
232				token = &tokens[i];
233				if (token->start != -1 && token->end == -1) {
234					parser->toksuper = i;
235					break;
236				}
237			}
238			break;
239		case '\"':
240#ifdef JSMN_STRICT
241			if (!expecting_item)
242				return JSMN_ERROR_INVAL;
243			expecting_item = 0;
244#endif
245			r = jsmn_parse_string(parser, js, len, tokens,
246					      num_tokens);
247			if (r < 0)
248				return r;
249			if (parser->toksuper != -1)
250				tokens[parser->toksuper].size++;
251			break;
252		case '\t':
253		case '\r':
254		case '\n':
255		case ' ':
256			break;
257#ifdef JSMN_STRICT
258		case ':':
259		case ',':
260			if (expecting_item)
261				return JSMN_ERROR_INVAL;
262			expecting_item = 1;
263			break;
264			/*
265			 * In strict mode primitives are:
266			 * numbers and booleans.
267			 */
268		case '-':
269		case '0':
270		case '1':
271		case '2':
272		case '3':
273		case '4':
274		case '5':
275		case '6':
276		case '7':
277		case '8':
278		case '9':
279		case 't':
280		case 'f':
281		case 'n':
282#else
283		case ':':
284		case ',':
285			break;
286			/*
287			 * In non-strict mode every unquoted value
288			 * is a primitive.
289			 */
290			/*FALL THROUGH */
291		default:
292#endif
293
294#ifdef JSMN_STRICT
295			if (!expecting_item)
296				return JSMN_ERROR_INVAL;
297			expecting_item = 0;
298#endif
299			r = jsmn_parse_primitive(parser, js, len, tokens,
300						 num_tokens);
301			if (r < 0)
302				return r;
303			if (parser->toksuper != -1)
304				tokens[parser->toksuper].size++;
305			break;
306
307#ifdef JSMN_STRICT
308			/* Unexpected char in strict mode */
309		default:
310			return JSMN_ERROR_INVAL;
311#endif
312		}
313	}
314
315	for (i = parser->toknext - 1; i >= 0; i--) {
316		/* Unmatched opened object or array */
317		if (tokens[i].start != -1 && tokens[i].end == -1)
318			return JSMN_ERROR_PART;
319	}
320
321#ifdef JSMN_STRICT
322	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
323#else
324	return JSMN_SUCCESS;
325#endif
326}
327
328/*
329 * Creates a new parser based over a given  buffer with an array of tokens
330 * available.
331 */
332void jsmn_init(jsmn_parser *parser)
333{
334	parser->pos = 0;
335	parser->toknext = 0;
336	parser->toksuper = -1;
337}
338
339const char *jsmn_strerror(jsmnerr_t err)
340{
341	switch (err) {
342	case JSMN_ERROR_NOMEM:
343		return "No enough tokens";
344	case JSMN_ERROR_INVAL:
345		return "Invalid character inside JSON string";
346	case JSMN_ERROR_PART:
347		return "The string is not a full JSON packet, more bytes expected";
348	case JSMN_SUCCESS:
349		return "Success";
350	default:
351		return "Unknown json error";
352	}
353}
354