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