xref: /freebsd/contrib/ntp/libjsmn/jsmn.c (revision b5ff185e19f6013ca565b2a15bc2d6abce933f46)
12b15cb3dSCy Schubert #include <stdlib.h>
22b15cb3dSCy Schubert 
32b15cb3dSCy Schubert #include "jsmn.h"
42b15cb3dSCy Schubert 
52b15cb3dSCy Schubert /**
62b15cb3dSCy Schubert  * Allocates a fresh unused token from the token pull.
72b15cb3dSCy Schubert  */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)82b15cb3dSCy Schubert static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
92b15cb3dSCy Schubert 		jsmntok_t *tokens, size_t num_tokens) {
102b15cb3dSCy Schubert 	jsmntok_t *tok;
11*276da39aSCy Schubert 	if (parser->toknext >= num_tokens) {
122b15cb3dSCy Schubert 		return NULL;
132b15cb3dSCy Schubert 	}
142b15cb3dSCy Schubert 	tok = &tokens[parser->toknext++];
152b15cb3dSCy Schubert 	tok->start = tok->end = -1;
162b15cb3dSCy Schubert 	tok->size = 0;
172b15cb3dSCy Schubert #ifdef JSMN_PARENT_LINKS
182b15cb3dSCy Schubert 	tok->parent = -1;
192b15cb3dSCy Schubert #endif
202b15cb3dSCy Schubert 	return tok;
212b15cb3dSCy Schubert }
222b15cb3dSCy Schubert 
232b15cb3dSCy Schubert /**
242b15cb3dSCy Schubert  * Fills token type and boundaries.
252b15cb3dSCy Schubert  */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)262b15cb3dSCy Schubert static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
272b15cb3dSCy Schubert                             int start, int end) {
282b15cb3dSCy Schubert 	token->type = type;
292b15cb3dSCy Schubert 	token->start = start;
302b15cb3dSCy Schubert 	token->end = end;
312b15cb3dSCy Schubert 	token->size = 0;
322b15cb3dSCy Schubert }
332b15cb3dSCy Schubert 
342b15cb3dSCy Schubert /**
352b15cb3dSCy Schubert  * Fills next available token with JSON primitive.
362b15cb3dSCy Schubert  */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)372b15cb3dSCy Schubert static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
38*276da39aSCy Schubert 		size_t len, jsmntok_t *tokens, size_t num_tokens) {
392b15cb3dSCy Schubert 	jsmntok_t *token;
402b15cb3dSCy Schubert 	int start;
412b15cb3dSCy Schubert 
422b15cb3dSCy Schubert 	start = parser->pos;
432b15cb3dSCy Schubert 
44*276da39aSCy Schubert 	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
452b15cb3dSCy Schubert 		switch (js[parser->pos]) {
462b15cb3dSCy Schubert #ifndef JSMN_STRICT
472b15cb3dSCy Schubert 			/* In strict mode primitive must be followed by "," or "}" or "]" */
482b15cb3dSCy Schubert 			case ':':
492b15cb3dSCy Schubert #endif
502b15cb3dSCy Schubert 			case '\t' : case '\r' : case '\n' : case ' ' :
512b15cb3dSCy Schubert 			case ','  : case ']'  : case '}' :
522b15cb3dSCy Schubert 				goto found;
532b15cb3dSCy Schubert 		}
542b15cb3dSCy Schubert 		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
552b15cb3dSCy Schubert 			parser->pos = start;
562b15cb3dSCy Schubert 			return JSMN_ERROR_INVAL;
572b15cb3dSCy Schubert 		}
582b15cb3dSCy Schubert 	}
592b15cb3dSCy Schubert #ifdef JSMN_STRICT
602b15cb3dSCy Schubert 	/* In strict mode primitive must be followed by a comma/object/array */
612b15cb3dSCy Schubert 	parser->pos = start;
622b15cb3dSCy Schubert 	return JSMN_ERROR_PART;
632b15cb3dSCy Schubert #endif
642b15cb3dSCy Schubert 
652b15cb3dSCy Schubert found:
66*276da39aSCy Schubert 	if (tokens == NULL) {
67*276da39aSCy Schubert 		parser->pos--;
68*276da39aSCy Schubert 		return 0;
69*276da39aSCy Schubert 	}
702b15cb3dSCy Schubert 	token = jsmn_alloc_token(parser, tokens, num_tokens);
712b15cb3dSCy Schubert 	if (token == NULL) {
722b15cb3dSCy Schubert 		parser->pos = start;
732b15cb3dSCy Schubert 		return JSMN_ERROR_NOMEM;
742b15cb3dSCy Schubert 	}
752b15cb3dSCy Schubert 	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
762b15cb3dSCy Schubert #ifdef JSMN_PARENT_LINKS
772b15cb3dSCy Schubert 	token->parent = parser->toksuper;
782b15cb3dSCy Schubert #endif
792b15cb3dSCy Schubert 	parser->pos--;
80*276da39aSCy Schubert 	return 0;
812b15cb3dSCy Schubert }
822b15cb3dSCy Schubert 
832b15cb3dSCy Schubert /**
842b15cb3dSCy Schubert  * Filsl next token with JSON string.
852b15cb3dSCy Schubert  */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)862b15cb3dSCy Schubert static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
87*276da39aSCy Schubert 		size_t len, jsmntok_t *tokens, size_t num_tokens) {
882b15cb3dSCy Schubert 	jsmntok_t *token;
892b15cb3dSCy Schubert 
902b15cb3dSCy Schubert 	int start = parser->pos;
912b15cb3dSCy Schubert 
922b15cb3dSCy Schubert 	parser->pos++;
932b15cb3dSCy Schubert 
942b15cb3dSCy Schubert 	/* Skip starting quote */
95*276da39aSCy Schubert 	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
962b15cb3dSCy Schubert 		char c = js[parser->pos];
972b15cb3dSCy Schubert 
982b15cb3dSCy Schubert 		/* Quote: end of string */
992b15cb3dSCy Schubert 		if (c == '\"') {
100*276da39aSCy Schubert 			if (tokens == NULL) {
101*276da39aSCy Schubert 				return 0;
102*276da39aSCy Schubert 			}
1032b15cb3dSCy Schubert 			token = jsmn_alloc_token(parser, tokens, num_tokens);
1042b15cb3dSCy Schubert 			if (token == NULL) {
1052b15cb3dSCy Schubert 				parser->pos = start;
1062b15cb3dSCy Schubert 				return JSMN_ERROR_NOMEM;
1072b15cb3dSCy Schubert 			}
1082b15cb3dSCy Schubert 			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
1092b15cb3dSCy Schubert #ifdef JSMN_PARENT_LINKS
1102b15cb3dSCy Schubert 			token->parent = parser->toksuper;
1112b15cb3dSCy Schubert #endif
112*276da39aSCy Schubert 			return 0;
1132b15cb3dSCy Schubert 		}
1142b15cb3dSCy Schubert 
1152b15cb3dSCy Schubert 		/* Backslash: Quoted symbol expected */
116*276da39aSCy Schubert 		if (c == '\\' && parser->pos + 1 < len) {
117*276da39aSCy Schubert 			int i;
1182b15cb3dSCy Schubert 			parser->pos++;
1192b15cb3dSCy Schubert 			switch (js[parser->pos]) {
1202b15cb3dSCy Schubert 				/* Allowed escaped symbols */
1212b15cb3dSCy Schubert 				case '\"': case '/' : case '\\' : case 'b' :
1222b15cb3dSCy Schubert 				case 'f' : case 'r' : case 'n'  : case 't' :
1232b15cb3dSCy Schubert 					break;
1242b15cb3dSCy Schubert 				/* Allows escaped symbol \uXXXX */
1252b15cb3dSCy Schubert 				case 'u':
1262b15cb3dSCy Schubert 					parser->pos++;
127*276da39aSCy Schubert 					for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
1282b15cb3dSCy Schubert 						/* If it isn't a hex character we have an error */
1292b15cb3dSCy Schubert 						if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
1302b15cb3dSCy Schubert 									(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
1312b15cb3dSCy Schubert 									(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
1322b15cb3dSCy Schubert 							parser->pos = start;
1332b15cb3dSCy Schubert 							return JSMN_ERROR_INVAL;
1342b15cb3dSCy Schubert 						}
1352b15cb3dSCy Schubert 						parser->pos++;
1362b15cb3dSCy Schubert 					}
1372b15cb3dSCy Schubert 					parser->pos--;
1382b15cb3dSCy Schubert 					break;
1392b15cb3dSCy Schubert 				/* Unexpected symbol */
1402b15cb3dSCy Schubert 				default:
1412b15cb3dSCy Schubert 					parser->pos = start;
1422b15cb3dSCy Schubert 					return JSMN_ERROR_INVAL;
1432b15cb3dSCy Schubert 			}
1442b15cb3dSCy Schubert 		}
1452b15cb3dSCy Schubert 	}
1462b15cb3dSCy Schubert 	parser->pos = start;
1472b15cb3dSCy Schubert 	return JSMN_ERROR_PART;
1482b15cb3dSCy Schubert }
1492b15cb3dSCy Schubert 
1502b15cb3dSCy Schubert /**
1512b15cb3dSCy Schubert  * Parse JSON string and fill tokens.
1522b15cb3dSCy Schubert  */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)153*276da39aSCy Schubert jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
154*276da39aSCy Schubert 		jsmntok_t *tokens, unsigned int num_tokens) {
1552b15cb3dSCy Schubert 	jsmnerr_t r;
1562b15cb3dSCy Schubert 	int i;
1572b15cb3dSCy Schubert 	jsmntok_t *token;
158*276da39aSCy Schubert 	int count = 0;
1592b15cb3dSCy Schubert 
160*276da39aSCy Schubert 	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
1612b15cb3dSCy Schubert 		char c;
1622b15cb3dSCy Schubert 		jsmntype_t type;
1632b15cb3dSCy Schubert 
1642b15cb3dSCy Schubert 		c = js[parser->pos];
1652b15cb3dSCy Schubert 		switch (c) {
1662b15cb3dSCy Schubert 			case '{': case '[':
167*276da39aSCy Schubert 				count++;
168*276da39aSCy Schubert 				if (tokens == NULL) {
169*276da39aSCy Schubert 					break;
170*276da39aSCy Schubert 				}
1712b15cb3dSCy Schubert 				token = jsmn_alloc_token(parser, tokens, num_tokens);
1722b15cb3dSCy Schubert 				if (token == NULL)
1732b15cb3dSCy Schubert 					return JSMN_ERROR_NOMEM;
1742b15cb3dSCy Schubert 				if (parser->toksuper != -1) {
1752b15cb3dSCy Schubert 					tokens[parser->toksuper].size++;
1762b15cb3dSCy Schubert #ifdef JSMN_PARENT_LINKS
1772b15cb3dSCy Schubert 					token->parent = parser->toksuper;
1782b15cb3dSCy Schubert #endif
1792b15cb3dSCy Schubert 				}
1802b15cb3dSCy Schubert 				token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
1812b15cb3dSCy Schubert 				token->start = parser->pos;
1822b15cb3dSCy Schubert 				parser->toksuper = parser->toknext - 1;
1832b15cb3dSCy Schubert 				break;
1842b15cb3dSCy Schubert 			case '}': case ']':
185*276da39aSCy Schubert 				if (tokens == NULL)
186*276da39aSCy Schubert 					break;
1872b15cb3dSCy Schubert 				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
1882b15cb3dSCy Schubert #ifdef JSMN_PARENT_LINKS
1892b15cb3dSCy Schubert 				if (parser->toknext < 1) {
1902b15cb3dSCy Schubert 					return JSMN_ERROR_INVAL;
1912b15cb3dSCy Schubert 				}
1922b15cb3dSCy Schubert 				token = &tokens[parser->toknext - 1];
1932b15cb3dSCy Schubert 				for (;;) {
1942b15cb3dSCy Schubert 					if (token->start != -1 && token->end == -1) {
1952b15cb3dSCy Schubert 						if (token->type != type) {
1962b15cb3dSCy Schubert 							return JSMN_ERROR_INVAL;
1972b15cb3dSCy Schubert 						}
1982b15cb3dSCy Schubert 						token->end = parser->pos + 1;
1992b15cb3dSCy Schubert 						parser->toksuper = token->parent;
2002b15cb3dSCy Schubert 						break;
2012b15cb3dSCy Schubert 					}
2022b15cb3dSCy Schubert 					if (token->parent == -1) {
2032b15cb3dSCy Schubert 						break;
2042b15cb3dSCy Schubert 					}
2052b15cb3dSCy Schubert 					token = &tokens[token->parent];
2062b15cb3dSCy Schubert 				}
2072b15cb3dSCy Schubert #else
2082b15cb3dSCy Schubert 				for (i = parser->toknext - 1; i >= 0; i--) {
2092b15cb3dSCy Schubert 					token = &tokens[i];
2102b15cb3dSCy Schubert 					if (token->start != -1 && token->end == -1) {
2112b15cb3dSCy Schubert 						if (token->type != type) {
2122b15cb3dSCy Schubert 							return JSMN_ERROR_INVAL;
2132b15cb3dSCy Schubert 						}
2142b15cb3dSCy Schubert 						parser->toksuper = -1;
2152b15cb3dSCy Schubert 						token->end = parser->pos + 1;
2162b15cb3dSCy Schubert 						break;
2172b15cb3dSCy Schubert 					}
2182b15cb3dSCy Schubert 				}
2192b15cb3dSCy Schubert 				/* Error if unmatched closing bracket */
2202b15cb3dSCy Schubert 				if (i == -1) return JSMN_ERROR_INVAL;
2212b15cb3dSCy Schubert 				for (; i >= 0; i--) {
2222b15cb3dSCy Schubert 					token = &tokens[i];
2232b15cb3dSCy Schubert 					if (token->start != -1 && token->end == -1) {
2242b15cb3dSCy Schubert 						parser->toksuper = i;
2252b15cb3dSCy Schubert 						break;
2262b15cb3dSCy Schubert 					}
2272b15cb3dSCy Schubert 				}
2282b15cb3dSCy Schubert #endif
2292b15cb3dSCy Schubert 				break;
2302b15cb3dSCy Schubert 			case '\"':
231*276da39aSCy Schubert 				r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
2322b15cb3dSCy Schubert 				if (r < 0) return r;
233*276da39aSCy Schubert 				count++;
234*276da39aSCy Schubert 				if (parser->toksuper != -1 && tokens != NULL)
2352b15cb3dSCy Schubert 					tokens[parser->toksuper].size++;
2362b15cb3dSCy Schubert 				break;
237*276da39aSCy Schubert 			case '\t' : case '\r' : case '\n' : case ' ':
238*276da39aSCy Schubert 				break;
239*276da39aSCy Schubert 			case ':':
240*276da39aSCy Schubert 				parser->toksuper = parser->toknext - 1;
241*276da39aSCy Schubert 				break;
242*276da39aSCy Schubert 			case ',':
243*276da39aSCy Schubert 				if (tokens != NULL &&
244*276da39aSCy Schubert 						tokens[parser->toksuper].type != JSMN_ARRAY &&
245*276da39aSCy Schubert 						tokens[parser->toksuper].type != JSMN_OBJECT) {
246*276da39aSCy Schubert #ifdef JSMN_PARENT_LINKS
247*276da39aSCy Schubert 					parser->toksuper = tokens[parser->toksuper].parent;
248*276da39aSCy Schubert #else
249*276da39aSCy Schubert 					for (i = parser->toknext - 1; i >= 0; i--) {
250*276da39aSCy Schubert 						if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
251*276da39aSCy Schubert 							if (tokens[i].start != -1 && tokens[i].end == -1) {
252*276da39aSCy Schubert 								parser->toksuper = i;
253*276da39aSCy Schubert 								break;
254*276da39aSCy Schubert 							}
255*276da39aSCy Schubert 						}
256*276da39aSCy Schubert 					}
257*276da39aSCy Schubert #endif
258*276da39aSCy Schubert 				}
2592b15cb3dSCy Schubert 				break;
2602b15cb3dSCy Schubert #ifdef JSMN_STRICT
2612b15cb3dSCy Schubert 			/* In strict mode primitives are: numbers and booleans */
2622b15cb3dSCy Schubert 			case '-': case '0': case '1' : case '2': case '3' : case '4':
2632b15cb3dSCy Schubert 			case '5': case '6': case '7' : case '8': case '9':
2642b15cb3dSCy Schubert 			case 't': case 'f': case 'n' :
265*276da39aSCy Schubert 				/* And they must not be keys of the object */
266*276da39aSCy Schubert 				if (tokens != NULL) {
267*276da39aSCy Schubert 					jsmntok_t *t = &tokens[parser->toksuper];
268*276da39aSCy Schubert 					if (t->type == JSMN_OBJECT ||
269*276da39aSCy Schubert 							(t->type == JSMN_STRING && t->size != 0)) {
270*276da39aSCy Schubert 						return JSMN_ERROR_INVAL;
271*276da39aSCy Schubert 					}
272*276da39aSCy Schubert 				}
2732b15cb3dSCy Schubert #else
2742b15cb3dSCy Schubert 			/* In non-strict mode every unquoted value is a primitive */
2752b15cb3dSCy Schubert 			default:
2762b15cb3dSCy Schubert #endif
277*276da39aSCy Schubert 				r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
2782b15cb3dSCy Schubert 				if (r < 0) return r;
279*276da39aSCy Schubert 				count++;
280*276da39aSCy Schubert 				if (parser->toksuper != -1 && tokens != NULL)
2812b15cb3dSCy Schubert 					tokens[parser->toksuper].size++;
2822b15cb3dSCy Schubert 				break;
2832b15cb3dSCy Schubert 
2842b15cb3dSCy Schubert #ifdef JSMN_STRICT
2852b15cb3dSCy Schubert 			/* Unexpected char in strict mode */
2862b15cb3dSCy Schubert 			default:
2872b15cb3dSCy Schubert 				return JSMN_ERROR_INVAL;
2882b15cb3dSCy Schubert #endif
2892b15cb3dSCy Schubert 		}
2902b15cb3dSCy Schubert 	}
291*276da39aSCy Schubert 	if (tokens != NULL) {
2922b15cb3dSCy Schubert 		for (i = parser->toknext - 1; i >= 0; i--) {
2932b15cb3dSCy Schubert 			/* Unmatched opened object or array */
2942b15cb3dSCy Schubert 			if (tokens[i].start != -1 && tokens[i].end == -1) {
2952b15cb3dSCy Schubert 				return JSMN_ERROR_PART;
2962b15cb3dSCy Schubert 			}
2972b15cb3dSCy Schubert 		}
298*276da39aSCy Schubert 	}
2992b15cb3dSCy Schubert 
300*276da39aSCy Schubert 	return count;
3012b15cb3dSCy Schubert }
3022b15cb3dSCy Schubert 
3032b15cb3dSCy Schubert /**
3042b15cb3dSCy Schubert  * Creates a new parser based over a given  buffer with an array of tokens
3052b15cb3dSCy Schubert  * available.
3062b15cb3dSCy Schubert  */
jsmn_init(jsmn_parser * parser)3072b15cb3dSCy Schubert void jsmn_init(jsmn_parser *parser) {
3082b15cb3dSCy Schubert 	parser->pos = 0;
3092b15cb3dSCy Schubert 	parser->toknext = 0;
3102b15cb3dSCy Schubert 	parser->toksuper = -1;
3112b15cb3dSCy Schubert }
3122b15cb3dSCy Schubert 
313