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