54d53b1fa08208eddf01ca593a767110411c4e95
[profile/ivi/jansson.git] / src / load.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <assert.h>
7
8 #include <jansson.h>
9
10
11 #define JSON_TOKEN_INVALID         -1
12 #define JSON_TOKEN_EOF              0
13 #define JSON_TOKEN_STRING         256
14 #define JSON_TOKEN_NUMBER         257
15 #define JSON_TOKEN_TRUE           258
16 #define JSON_TOKEN_FALSE          259
17 #define JSON_TOKEN_NULL           260
18
19 typedef struct {
20     const char *input;
21     const char *start;
22     int token;
23     int line, column;
24     union {
25         char *string;
26         double number;
27     } value;
28 } json_lex;
29
30
31 /*** error reporting ***/
32
33 static __thread char *json_error_msg = NULL;
34
35 static void json_set_error(const json_lex *lex, const char *msg)
36 {
37     free(json_error_msg);
38     if(*lex->start)
39         asprintf(&json_error_msg, "%s near '%.*s' on line %d", msg,
40                  (int)(lex->input - lex->start), lex->start, lex->line);
41     else
42         asprintf(&json_error_msg, "%s near end of file", msg);
43 }
44
45 const char *json_get_error(void)
46 {
47     if(!json_error_msg)
48         json_error_msg = strdup("success");
49     return json_error_msg;
50 }
51
52
53 /*** lexical analyzer ***/
54
55 static void json_scan_string(json_lex *lex)
56 {
57     /* skip the " */
58     const char *p = lex->input + 1;
59     char *t;
60
61     lex->token = JSON_TOKEN_INVALID;
62
63     while(*p != '"') {
64         if(*p == '\0') {
65             /* unterminated string literal */
66             goto out;
67         }
68
69         if(0 <= *p && *p <= 31) {
70             /* control character */
71             goto out;
72         }
73         else if(*p == '\\') {
74             p++;
75             if(*p == 'u') {
76                 p++;
77                 for(int i = 0; i < 4; i++, p++) {
78                     if(!isxdigit(*p))
79                         goto out;
80                 }
81             }
82             if(*p == '"' || *p == '\\' || *p == '/' || *p == 'b' ||
83                *p == 'f' || *p == 'n' || *p == 'r' || *p == 't')
84                 p++;
85             else
86                 goto out;
87         }
88         else
89             p++;
90     }
91
92     /* the actual value is at most of the same length as the source
93        string */
94     lex->value.string = malloc(p - lex->start);
95     if(!lex->value.string) {
96         /* this is not very nice, since TOKEN_INVALID is returned */
97         goto out;
98     }
99
100     /* the target */
101     t = lex->value.string;
102
103     p = lex->input + 1;
104     while(*p != '"') {
105         if(*p == '\\') {
106             p++;
107             if(*p == 'u') {
108                 /* TODO: \uXXXX not supported yet */
109                 free(lex->value.string);
110                 lex->value.string = NULL;
111                 goto out;
112             } else {
113                 switch(*p) {
114                     case '"': case '\\': case '/':
115                         *t = *p; break;
116                     case 'b': *t = '\b'; break;
117                     case 'f': *t = '\f'; break;
118                     case 'n': *t = '\n'; break;
119                     case 'r': *t = '\r'; break;
120                     case 't': *t = '\t'; break;
121                     default: assert(0);
122                 }
123             }
124         }
125         else
126             *t = *p;
127
128         t++;
129         p++;
130     }
131     /* skip the " */
132     p++;
133
134     *t = '\0';
135     lex->token = JSON_TOKEN_STRING;
136
137 out:
138     lex->input = p;
139 }
140
141 static void json_scan_number(json_lex *lex)
142 {
143     const char *p = lex->input;
144     char *end;
145
146     lex->token = JSON_TOKEN_INVALID;
147
148     if(*p == '-')
149         p++;
150
151     if(*p == '0')
152         p++;
153     else /* *p != '0' */ {
154         p++;
155         while(isdigit(*p))
156             p++;
157     }
158
159     if(*p == '.') {
160         p++;
161         if(!isdigit(*(p++)))
162             goto out;
163
164         while(isdigit(*p))
165             p++;
166     }
167
168     if(*p == 'E' || *p == 'e') {
169         p++;
170         if(*p == '+' || *p == '-')
171             p++;
172
173         if(!isdigit(*(p++)))
174             goto out;
175
176         while(isdigit(*p))
177             p++;
178     }
179
180     lex->token = JSON_TOKEN_NUMBER;
181
182     lex->value.number = strtod(lex->start, &end);
183     assert(end == p);
184
185 out:
186     lex->input = p;
187 }
188
189 static int json_lex_scan(json_lex *lex)
190 {
191     char c;
192
193     if(lex->token == JSON_TOKEN_STRING) {
194       free(lex->value.string);
195       lex->value.string = NULL;
196     }
197
198     while(isspace(*lex->input)) {
199         if(*lex->input == '\n')
200             lex->line++;
201
202         lex->input++;
203     }
204
205     lex->start = lex->input;
206     c = *lex->input;
207
208     if(c == '\0')
209         lex->token = JSON_TOKEN_EOF;
210
211     else if(c == '{' || c == '}' || c == '[' || c == ']' ||
212             c == ':' || c == ',') {
213         lex->token = c;
214         lex->input++;
215     }
216
217     else if(c == '"')
218         json_scan_string(lex);
219
220     else if(isdigit(c) || c == '-')
221         json_scan_number(lex);
222
223     else if(isalpha(c)) {
224         /* eat up the whole identifier for clearer error messages */
225         int len;
226
227         while(isalpha(*lex->input))
228             lex->input++;
229         len = lex->input - lex->start;
230
231         if(strncmp(lex->start, "true", len) == 0)
232             lex->token = JSON_TOKEN_TRUE;
233         else if(strncmp(lex->start, "false", len) == 0)
234             lex->token = JSON_TOKEN_FALSE;
235         else if(strncmp(lex->start, "null", len) == 0)
236             lex->token = JSON_TOKEN_NULL;
237         else
238             lex->token = JSON_TOKEN_INVALID;
239     }
240
241     else {
242         lex->token = JSON_TOKEN_INVALID;
243         lex->input++;
244     }
245
246     return lex->token;
247 }
248
249 static int json_lex_init(json_lex *lex, const char *input)
250 {
251     lex->input = input;
252     lex->token = JSON_TOKEN_INVALID;
253     lex->line = 1;
254
255     json_lex_scan(lex);
256     return 0;
257 }
258
259 static void json_lex_close(json_lex *lex)
260 {
261     if(lex->token == JSON_TOKEN_STRING)
262         free(lex->value.string);
263 }
264
265
266 /*** parser ***/
267
268 static json_t *json_parse(json_lex *lex);
269
270 static json_t *json_parse_object(json_lex *lex)
271 {
272     json_t *object = json_object();
273     if(!object)
274         return NULL;
275
276     json_lex_scan(lex);
277     if(lex->token == '}')
278         return object;
279
280     while(lex->token) {
281         char *key;
282         json_t *value;
283
284         if(lex->token != JSON_TOKEN_STRING) {
285             json_set_error(lex, "string expected");
286             goto error;
287         }
288
289         key = strdup(lex->value.string);
290         if(!key)
291             return NULL;
292
293         json_lex_scan(lex);
294         if(lex->token != ':') {
295             json_set_error(lex, "':' expected");
296             goto error;
297         }
298
299         json_lex_scan(lex);
300
301         value = json_parse(lex);
302         if(!value)
303             goto error;
304
305         if(json_object_set(object, key, value)) {
306             json_decref(value);
307             goto error;
308         }
309
310         json_decref(value);
311         free(key);
312
313         if(lex->token != ',')
314             break;
315
316         json_lex_scan(lex);
317     }
318
319     if(lex->token != '}') {
320         json_set_error(lex, "'}' expected");
321         goto error;
322     }
323
324     return object;
325
326 error:
327     json_decref(object);
328     return NULL;
329 }
330
331 static json_t *json_parse_array(json_lex *lex)
332 {
333     json_t *array = json_array();
334     if(!array)
335         return NULL;
336
337     json_lex_scan(lex);
338     if(lex->token != ']') {
339         while(1) {
340             json_t *elem = json_parse(lex);
341             if(!elem)
342                 goto error;
343
344             if(json_array_append(array, elem)) {
345                 json_decref(elem);
346                 goto error;
347             }
348             json_decref(elem);
349
350             if(lex->token != ',')
351                 break;
352
353             json_lex_scan(lex);
354         }
355     }
356
357     if(lex->token != ']') {
358         json_set_error(lex, "']' expected");
359         goto error;
360     }
361
362     return array;
363
364 error:
365     json_decref(array);
366     return NULL;
367 }
368
369 static json_t *json_parse(json_lex *lex)
370 {
371     json_t *json;
372
373     switch(lex->token) {
374         case JSON_TOKEN_STRING: {
375             json = json_string(lex->value.string);
376             break;
377         }
378
379         case JSON_TOKEN_NUMBER: {
380             json = json_number(lex->value.number);
381             break;
382         }
383
384         case JSON_TOKEN_TRUE:
385             json = json_true();
386             break;
387
388         case JSON_TOKEN_FALSE:
389             json = json_false();
390             break;
391
392         case JSON_TOKEN_NULL:
393             json = json_null();
394             break;
395
396         case '{':
397             json = json_parse_object(lex);
398             break;
399
400         case '[':
401             json = json_parse_array(lex);
402             break;
403
404         case JSON_TOKEN_INVALID:
405             json_set_error(lex, "invalid token");
406             return NULL;
407
408         default:
409             json_set_error(lex, "unexpected token");
410             return NULL;
411     }
412
413     if(!json)
414         return NULL;
415
416     json_lex_scan(lex);
417     return json;
418 }
419
420 json_t *json_loads(const char *string)
421 {
422     json_lex lex;
423     json_t *result = NULL;
424
425     if(json_lex_init(&lex, string))
426         return NULL;
427
428     if(lex.token != '[' && lex.token != '{') {
429         json_set_error(&lex, "'[' or '{' expected");
430         goto out;
431     }
432
433     result = json_parse(&lex);
434     if(!result)
435         goto out;
436
437     if(lex.token != JSON_TOKEN_EOF) {
438         json_set_error(&lex, "end of file expected");
439         json_decref(result);
440         result = NULL;
441     }
442
443 out:
444     json_lex_close(&lex);
445     return result;
446 }