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