1 | /* |
2 | * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> |
3 | * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> |
4 | * |
5 | * Jansson is free software; you can redistribute it and/or modify |
6 | * it under the terms of the MIT license. See LICENSE for details. |
7 | */ |
8 | |
9 | #include <string.h> |
10 | #include "jansson.h" |
11 | #include "jansson_private.h" |
12 | #include "utf.h" |
13 | |
14 | typedef struct { |
15 | const char *start; |
16 | const char *fmt; |
17 | char token; |
18 | json_error_t *error; |
19 | size_t flags; |
20 | int line; |
21 | int column; |
22 | } scanner_t; |
23 | |
24 | static const char * const type_names[] = { |
25 | "object" , |
26 | "array" , |
27 | "string" , |
28 | "integer" , |
29 | "real" , |
30 | "true" , |
31 | "false" , |
32 | "null" |
33 | }; |
34 | |
35 | #define type_name(x) type_names[json_typeof(x)] |
36 | |
37 | static const char unpack_value_starters[] = "{[siIbfFOon" ; |
38 | |
39 | |
40 | static void scanner_init(scanner_t *s, json_error_t *error, |
41 | size_t flags, const char *fmt) |
42 | { |
43 | s->error = error; |
44 | s->flags = flags; |
45 | s->fmt = s->start = fmt; |
46 | s->line = 1; |
47 | s->column = 0; |
48 | } |
49 | |
50 | static void next_token(scanner_t *s) |
51 | { |
52 | const char *t = s->fmt; |
53 | s->column++; |
54 | |
55 | /* skip space and ignored chars */ |
56 | while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { |
57 | if(*t == '\n') { |
58 | s->line++; |
59 | s->column = 1; |
60 | } |
61 | else |
62 | s->column++; |
63 | |
64 | t++; |
65 | } |
66 | |
67 | s->token = *t; |
68 | |
69 | t++; |
70 | s->fmt = t; |
71 | } |
72 | |
73 | static void set_error(scanner_t *s, const char *source, const char *fmt, ...) |
74 | { |
75 | va_list ap; |
76 | size_t pos; |
77 | va_start(ap, fmt); |
78 | |
79 | pos = (size_t)(s->fmt - s->start); |
80 | jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); |
81 | |
82 | jsonp_error_set_source(s->error, source); |
83 | |
84 | va_end(ap); |
85 | } |
86 | |
87 | static json_t *pack(scanner_t *s, va_list *ap); |
88 | |
89 | static json_t *pack_object(scanner_t *s, va_list *ap) |
90 | { |
91 | json_t *object = json_object(); |
92 | next_token(s); |
93 | |
94 | while(s->token != '}') { |
95 | const char *key; |
96 | json_t *value; |
97 | |
98 | if(!s->token) { |
99 | set_error(s, "<format>" , "Unexpected end of format string" ); |
100 | goto error; |
101 | } |
102 | |
103 | if(s->token != 's') { |
104 | set_error(s, "<format>" , "Expected format 's', got '%c'" , s->token); |
105 | goto error; |
106 | } |
107 | |
108 | key = va_arg(*ap, const char *); |
109 | if(!key) { |
110 | set_error(s, "<args>" , "NULL object key" ); |
111 | goto error; |
112 | } |
113 | |
114 | if(!utf8_check_string(key, -1)) { |
115 | set_error(s, "<args>" , "Invalid UTF-8 in object key" ); |
116 | goto error; |
117 | } |
118 | |
119 | next_token(s); |
120 | |
121 | value = pack(s, ap); |
122 | if(!value) |
123 | goto error; |
124 | |
125 | if(json_object_set_new_nocheck(object, key, value)) { |
126 | set_error(s, "<internal>" , "Unable to add key \"%s\"" , key); |
127 | goto error; |
128 | } |
129 | |
130 | next_token(s); |
131 | } |
132 | |
133 | return object; |
134 | |
135 | error: |
136 | json_decref(object); |
137 | return NULL; |
138 | } |
139 | |
140 | static json_t *pack_array(scanner_t *s, va_list *ap) |
141 | { |
142 | json_t *array = json_array(); |
143 | next_token(s); |
144 | |
145 | while(s->token != ']') { |
146 | json_t *value; |
147 | |
148 | if(!s->token) { |
149 | set_error(s, "<format>" , "Unexpected end of format string" ); |
150 | goto error; |
151 | } |
152 | |
153 | value = pack(s, ap); |
154 | if(!value) |
155 | goto error; |
156 | |
157 | if(json_array_append_new(array, value)) { |
158 | set_error(s, "<internal>" , "Unable to append to array" ); |
159 | goto error; |
160 | } |
161 | |
162 | next_token(s); |
163 | } |
164 | return array; |
165 | |
166 | error: |
167 | json_decref(array); |
168 | return NULL; |
169 | } |
170 | |
171 | static json_t *pack(scanner_t *s, va_list *ap) |
172 | { |
173 | switch(s->token) { |
174 | case '{': |
175 | return pack_object(s, ap); |
176 | |
177 | case '[': |
178 | return pack_array(s, ap); |
179 | |
180 | case 's': /* string */ |
181 | { |
182 | const char *str = va_arg(*ap, const char *); |
183 | if(!str) { |
184 | set_error(s, "<args>" , "NULL string argument" ); |
185 | return NULL; |
186 | } |
187 | if(!utf8_check_string(str, -1)) { |
188 | set_error(s, "<args>" , "Invalid UTF-8 string" ); |
189 | return NULL; |
190 | } |
191 | return json_string_nocheck(str); |
192 | } |
193 | |
194 | case 'n': /* null */ |
195 | return json_null(); |
196 | |
197 | case 'b': /* boolean */ |
198 | return va_arg(*ap, int) ? json_true() : json_false(); |
199 | |
200 | case 'i': /* integer from int */ |
201 | return json_integer(va_arg(*ap, int)); |
202 | |
203 | case 'I': /* integer from json_int_t */ |
204 | return json_integer(va_arg(*ap, json_int_t)); |
205 | |
206 | case 'f': /* real */ |
207 | return json_real(va_arg(*ap, double)); |
208 | |
209 | case 'O': /* a json_t object; increments refcount */ |
210 | return json_incref(va_arg(*ap, json_t *)); |
211 | |
212 | case 'o': /* a json_t object; doesn't increment refcount */ |
213 | return va_arg(*ap, json_t *); |
214 | |
215 | default: |
216 | set_error(s, "<format>" , "Unexpected format character '%c'" , |
217 | s->token); |
218 | return NULL; |
219 | } |
220 | } |
221 | |
222 | static int unpack(scanner_t *s, json_t *root, va_list *ap); |
223 | |
224 | static int unpack_object(scanner_t *s, json_t *root, va_list *ap) |
225 | { |
226 | int ret = -1; |
227 | int strict = 0; |
228 | |
229 | /* Use a set (emulated by a hashtable) to check that all object |
230 | keys are accessed. Checking that the correct number of keys |
231 | were accessed is not enough, as the same key can be unpacked |
232 | multiple times. |
233 | */ |
234 | hashtable_t key_set; |
235 | |
236 | if(hashtable_init(&key_set)) { |
237 | set_error(s, "<internal>" , "Out of memory" ); |
238 | return -1; |
239 | } |
240 | |
241 | if(root && !json_is_object(root)) { |
242 | set_error(s, "<validation>" , "Expected object, got %s" , |
243 | type_name(root)); |
244 | goto out; |
245 | } |
246 | next_token(s); |
247 | |
248 | while(s->token != '}') { |
249 | const char *key; |
250 | json_t *value; |
251 | int opt = 0; |
252 | |
253 | if(strict != 0) { |
254 | set_error(s, "<format>" , "Expected '}' after '%c', got '%c'" , |
255 | (strict == 1 ? '!' : '*'), s->token); |
256 | goto out; |
257 | } |
258 | |
259 | if(!s->token) { |
260 | set_error(s, "<format>" , "Unexpected end of format string" ); |
261 | goto out; |
262 | } |
263 | |
264 | if(s->token == '!' || s->token == '*') { |
265 | strict = (s->token == '!' ? 1 : -1); |
266 | next_token(s); |
267 | continue; |
268 | } |
269 | |
270 | if(s->token != 's') { |
271 | set_error(s, "<format>" , "Expected format 's', got '%c'" , s->token); |
272 | goto out; |
273 | } |
274 | |
275 | key = va_arg(*ap, const char *); |
276 | if(!key) { |
277 | set_error(s, "<args>" , "NULL object key" ); |
278 | goto out; |
279 | } |
280 | |
281 | next_token(s); |
282 | |
283 | if(s->token == '?') { |
284 | opt = 1; |
285 | next_token(s); |
286 | } |
287 | |
288 | if(!root) { |
289 | /* skipping */ |
290 | value = NULL; |
291 | } |
292 | else { |
293 | value = json_object_get(root, key); |
294 | if(!value && !opt) { |
295 | set_error(s, "<validation>" , "Object item not found: %s" , key); |
296 | goto out; |
297 | } |
298 | } |
299 | |
300 | if(unpack(s, value, ap)) |
301 | goto out; |
302 | |
303 | hashtable_set(&key_set, key, 0, json_null()); |
304 | next_token(s); |
305 | } |
306 | |
307 | if(strict == 0 && (s->flags & JSON_STRICT)) |
308 | strict = 1; |
309 | |
310 | if(root && strict == 1 && key_set.size != json_object_size(root)) { |
311 | long diff = (long)json_object_size(root) - (long)key_set.size; |
312 | set_error(s, "<validation>" , "%li object item(s) left unpacked" , diff); |
313 | goto out; |
314 | } |
315 | |
316 | ret = 0; |
317 | |
318 | out: |
319 | hashtable_close(&key_set); |
320 | return ret; |
321 | } |
322 | |
323 | static int unpack_array(scanner_t *s, json_t *root, va_list *ap) |
324 | { |
325 | size_t i = 0; |
326 | int strict = 0; |
327 | |
328 | if(root && !json_is_array(root)) { |
329 | set_error(s, "<validation>" , "Expected array, got %s" , type_name(root)); |
330 | return -1; |
331 | } |
332 | next_token(s); |
333 | |
334 | while(s->token != ']') { |
335 | json_t *value; |
336 | |
337 | if(strict != 0) { |
338 | set_error(s, "<format>" , "Expected ']' after '%c', got '%c'" , |
339 | (strict == 1 ? '!' : '*'), |
340 | s->token); |
341 | return -1; |
342 | } |
343 | |
344 | if(!s->token) { |
345 | set_error(s, "<format>" , "Unexpected end of format string" ); |
346 | return -1; |
347 | } |
348 | |
349 | if(s->token == '!' || s->token == '*') { |
350 | strict = (s->token == '!' ? 1 : -1); |
351 | next_token(s); |
352 | continue; |
353 | } |
354 | |
355 | if(!strchr(unpack_value_starters, s->token)) { |
356 | set_error(s, "<format>" , "Unexpected format character '%c'" , |
357 | s->token); |
358 | return -1; |
359 | } |
360 | |
361 | if(!root) { |
362 | /* skipping */ |
363 | value = NULL; |
364 | } |
365 | else { |
366 | value = json_array_get(root, i); |
367 | if(!value) { |
368 | set_error(s, "<validation>" , "Array index %lu out of range" , |
369 | (unsigned long)i); |
370 | return -1; |
371 | } |
372 | } |
373 | |
374 | if(unpack(s, value, ap)) |
375 | return -1; |
376 | |
377 | next_token(s); |
378 | i++; |
379 | } |
380 | |
381 | if(strict == 0 && (s->flags & JSON_STRICT)) |
382 | strict = 1; |
383 | |
384 | if(root && strict == 1 && i != json_array_size(root)) { |
385 | long diff = (long)json_array_size(root) - (long)i; |
386 | set_error(s, "<validation>" , "%li array item(s) left unpacked" , diff); |
387 | return -1; |
388 | } |
389 | |
390 | return 0; |
391 | } |
392 | |
393 | static int unpack(scanner_t *s, json_t *root, va_list *ap) |
394 | { |
395 | switch(s->token) |
396 | { |
397 | case '{': |
398 | return unpack_object(s, root, ap); |
399 | |
400 | case '[': |
401 | return unpack_array(s, root, ap); |
402 | |
403 | case 's': |
404 | if(root && !json_is_string(root)) { |
405 | set_error(s, "<validation>" , "Expected string, got %s" , |
406 | type_name(root)); |
407 | return -1; |
408 | } |
409 | |
410 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
411 | const char **target; |
412 | |
413 | target = va_arg(*ap, const char **); |
414 | if(!target) { |
415 | set_error(s, "<args>" , "NULL string argument" ); |
416 | return -1; |
417 | } |
418 | |
419 | if(root) |
420 | *target = json_string_value(root); |
421 | } |
422 | return 0; |
423 | |
424 | case 'i': |
425 | if(root && !json_is_integer(root)) { |
426 | set_error(s, "<validation>" , "Expected integer, got %s" , |
427 | type_name(root)); |
428 | return -1; |
429 | } |
430 | |
431 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
432 | int *target = va_arg(*ap, int*); |
433 | if(root) |
434 | *target = (int)json_integer_value(root); |
435 | } |
436 | |
437 | return 0; |
438 | |
439 | case 'I': |
440 | if(root && !json_is_integer(root)) { |
441 | set_error(s, "<validation>" , "Expected integer, got %s" , |
442 | type_name(root)); |
443 | return -1; |
444 | } |
445 | |
446 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
447 | json_int_t *target = va_arg(*ap, json_int_t*); |
448 | if(root) |
449 | *target = json_integer_value(root); |
450 | } |
451 | |
452 | return 0; |
453 | |
454 | case 'b': |
455 | if(root && !json_is_boolean(root)) { |
456 | set_error(s, "<validation>" , "Expected true or false, got %s" , |
457 | type_name(root)); |
458 | return -1; |
459 | } |
460 | |
461 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
462 | int *target = va_arg(*ap, int*); |
463 | if(root) |
464 | *target = json_is_true(root); |
465 | } |
466 | |
467 | return 0; |
468 | |
469 | case 'f': |
470 | if(root && !json_is_real(root)) { |
471 | set_error(s, "<validation>" , "Expected real, got %s" , |
472 | type_name(root)); |
473 | return -1; |
474 | } |
475 | |
476 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
477 | double *target = va_arg(*ap, double*); |
478 | if(root) |
479 | *target = json_real_value(root); |
480 | } |
481 | |
482 | return 0; |
483 | |
484 | case 'F': |
485 | if(root && !json_is_number(root)) { |
486 | set_error(s, "<validation>" , "Expected real or integer, got %s" , |
487 | type_name(root)); |
488 | return -1; |
489 | } |
490 | |
491 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
492 | double *target = va_arg(*ap, double*); |
493 | if(root) |
494 | *target = json_number_value(root); |
495 | } |
496 | |
497 | return 0; |
498 | |
499 | case 'O': |
500 | if(root && !(s->flags & JSON_VALIDATE_ONLY)) |
501 | json_incref(root); |
502 | /* Fall through */ |
503 | |
504 | case 'o': |
505 | if(!(s->flags & JSON_VALIDATE_ONLY)) { |
506 | json_t **target = va_arg(*ap, json_t**); |
507 | if(root) |
508 | *target = root; |
509 | } |
510 | |
511 | return 0; |
512 | |
513 | case 'n': |
514 | /* Never assign, just validate */ |
515 | if(root && !json_is_null(root)) { |
516 | set_error(s, "<validation>" , "Expected null, got %s" , |
517 | type_name(root)); |
518 | return -1; |
519 | } |
520 | return 0; |
521 | |
522 | default: |
523 | set_error(s, "<format>" , "Unexpected format character '%c'" , |
524 | s->token); |
525 | return -1; |
526 | } |
527 | } |
528 | |
529 | json_t *json_vpack_ex(json_error_t *error, size_t flags, |
530 | const char *fmt, va_list ap) |
531 | { |
532 | scanner_t s; |
533 | va_list ap_copy; |
534 | json_t *value; |
535 | |
536 | if(!fmt || !*fmt) { |
537 | jsonp_error_init(error, "<format>" ); |
538 | jsonp_error_set(error, -1, -1, 0, "NULL or empty format string" ); |
539 | return NULL; |
540 | } |
541 | jsonp_error_init(error, NULL); |
542 | |
543 | scanner_init(&s, error, flags, fmt); |
544 | next_token(&s); |
545 | |
546 | va_copy(ap_copy, ap); |
547 | value = pack(&s, &ap_copy); |
548 | va_end(ap_copy); |
549 | |
550 | if(!value) |
551 | return NULL; |
552 | |
553 | next_token(&s); |
554 | if(s.token) { |
555 | json_decref(value); |
556 | set_error(&s, "<format>" , "Garbage after format string" ); |
557 | return NULL; |
558 | } |
559 | |
560 | return value; |
561 | } |
562 | |
563 | json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) |
564 | { |
565 | json_t *value; |
566 | va_list ap; |
567 | |
568 | va_start(ap, fmt); |
569 | value = json_vpack_ex(error, flags, fmt, ap); |
570 | va_end(ap); |
571 | |
572 | return value; |
573 | } |
574 | |
575 | json_t *json_pack(const char *fmt, ...) |
576 | { |
577 | json_t *value; |
578 | va_list ap; |
579 | |
580 | va_start(ap, fmt); |
581 | value = json_vpack_ex(NULL, 0, fmt, ap); |
582 | va_end(ap); |
583 | |
584 | return value; |
585 | } |
586 | |
587 | int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, |
588 | const char *fmt, va_list ap) |
589 | { |
590 | scanner_t s; |
591 | va_list ap_copy; |
592 | |
593 | if(!root) { |
594 | jsonp_error_init(error, "<root>" ); |
595 | jsonp_error_set(error, -1, -1, 0, "NULL root value" ); |
596 | return -1; |
597 | } |
598 | |
599 | if(!fmt || !*fmt) { |
600 | jsonp_error_init(error, "<format>" ); |
601 | jsonp_error_set(error, -1, -1, 0, "NULL or empty format string" ); |
602 | return -1; |
603 | } |
604 | jsonp_error_init(error, NULL); |
605 | |
606 | scanner_init(&s, error, flags, fmt); |
607 | next_token(&s); |
608 | |
609 | va_copy(ap_copy, ap); |
610 | if(unpack(&s, root, &ap_copy)) { |
611 | va_end(ap_copy); |
612 | return -1; |
613 | } |
614 | va_end(ap_copy); |
615 | |
616 | next_token(&s); |
617 | if(s.token) { |
618 | set_error(&s, "<format>" , "Garbage after format string" ); |
619 | return -1; |
620 | } |
621 | |
622 | return 0; |
623 | } |
624 | |
625 | int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) |
626 | { |
627 | int ret; |
628 | va_list ap; |
629 | |
630 | va_start(ap, fmt); |
631 | ret = json_vunpack_ex(root, error, flags, fmt, ap); |
632 | va_end(ap); |
633 | |
634 | return ret; |
635 | } |
636 | |
637 | int json_unpack(json_t *root, const char *fmt, ...) |
638 | { |
639 | int ret; |
640 | va_list ap; |
641 | |
642 | va_start(ap, fmt); |
643 | ret = json_vunpack_ex(root, NULL, 0, fmt, ap); |
644 | va_end(ap); |
645 | |
646 | return ret; |
647 | } |
648 | |