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