xref: /netbsd-src/external/bsd/ntp/dist/libjsmn/jsmn.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
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  */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)10 static 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  */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)28 static 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  */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)39 static 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 
67 found:
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  */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)88 static 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  */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)155 jsmnerr_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  */
jsmn_init(jsmn_parser * parser)309 void jsmn_init(jsmn_parser *parser) {
310 	parser->pos = 0;
311 	parser->toknext = 0;
312 	parser->toksuper = -1;
313 }
314 
315