chg 'hf mf chk':
[proxmark3-svn] / client / jansson / pack_unpack.c
0 / 952 (  0%)
CommitLineData
1/*
2 * Copyright (c) 2009-2016 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
14typedef struct {
15 int line;
16 int column;
17 size_t pos;
18 char token;
19} token_t;
20
21typedef struct {
22 const char *start;
23 const char *fmt;
24 token_t prev_token;
25 token_t token;
26 token_t next_token;
27 json_error_t *error;
28 size_t flags;
29 int line;
30 int column;
31 size_t pos;
32 int has_error;
33} scanner_t;
34
35#define token(scanner) ((scanner)->token.token)
36
37static const char * const type_names[] = {
38 "object",
39 "array",
40 "string",
41 "integer",
42 "real",
43 "true",
44 "false",
45 "null"
46};
47
48#define type_name(x) type_names[json_typeof(x)]
49
50static const char unpack_value_starters[] = "{[siIbfFOon";
51
52static void scanner_init(scanner_t *s, json_error_t *error,
53 size_t flags, const char *fmt)
54{
55 s->error = error;
56 s->flags = flags;
57 s->fmt = s->start = fmt;
58 memset(&s->prev_token, 0, sizeof(token_t));
59 memset(&s->token, 0, sizeof(token_t));
60 memset(&s->next_token, 0, sizeof(token_t));
61 s->line = 1;
62 s->column = 0;
63 s->pos = 0;
64 s->has_error = 0;
65}
66
67static void next_token(scanner_t *s)
68{
69 const char *t;
70 s->prev_token = s->token;
71
72 if(s->next_token.line) {
73 s->token = s->next_token;
74 s->next_token.line = 0;
75 return;
76 }
77
78 if (!token(s) && !*s->fmt)
79 return;
80
81 t = s->fmt;
82 s->column++;
83 s->pos++;
84
85 /* skip space and ignored chars */
86 while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
87 if(*t == '\n') {
88 s->line++;
89 s->column = 1;
90 }
91 else
92 s->column++;
93
94 s->pos++;
95 t++;
96 }
97
98 s->token.token = *t;
99 s->token.line = s->line;
100 s->token.column = s->column;
101 s->token.pos = s->pos;
102
103 if (*t) t++;
104 s->fmt = t;
105}
106
107static void prev_token(scanner_t *s)
108{
109 s->next_token = s->token;
110 s->token = s->prev_token;
111}
112
113static void set_error(scanner_t *s, const char *source, enum json_error_code code,
114 const char *fmt, ...)
115{
116 va_list ap;
117 va_start(ap, fmt);
118
119 jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
120 code, fmt, ap);
121
122 jsonp_error_set_source(s->error, source);
123
124 va_end(ap);
125}
126
127static json_t *pack(scanner_t *s, va_list *ap);
128
129
130/* ours will be set to 1 if jsonp_free() must be called for the result
131 afterwards */
132static char *read_string(scanner_t *s, va_list *ap,
133 const char *purpose, size_t *out_len, int *ours, int optional)
134{
135 char t;
136 strbuffer_t strbuff;
137 const char *str;
138 size_t length;
139
140 next_token(s);
141 t = token(s);
142 prev_token(s);
143
144 *ours = 0;
145 if(t != '#' && t != '%' && t != '+') {
146 /* Optimize the simple case */
147 str = va_arg(*ap, const char *);
148
149 if(!str) {
150 if (!optional) {
151 set_error(s, "<args>", json_error_null_value, "NULL %s", purpose);
152 s->has_error = 1;
153 }
154 return NULL;
155 }
156
157 length = strlen(str);
158
159 if(!utf8_check_string(str, length)) {
160 set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
161 s->has_error = 1;
162 return NULL;
163 }
164
165 *out_len = length;
166 return (char *)str;
167 } else if (optional) {
168 set_error(s, "<format>", json_error_invalid_format, "Cannot use '%c' on optional strings", t);
169 s->has_error = 1;
170
171 return NULL;
172 }
173
174 if(strbuffer_init(&strbuff)) {
175 set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
176 s->has_error = 1;
177 }
178
179 while(1) {
180 str = va_arg(*ap, const char *);
181 if(!str) {
182 set_error(s, "<args>", json_error_null_value, "NULL %s", purpose);
183 s->has_error = 1;
184 }
185
186 next_token(s);
187
188 if(token(s) == '#') {
189 length = va_arg(*ap, int);
190 }
191 else if(token(s) == '%') {
192 length = va_arg(*ap, size_t);
193 }
194 else {
195 prev_token(s);
196 length = s->has_error ? 0 : strlen(str);
197 }
198
199 if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) {
200 set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
201 s->has_error = 1;
202 }
203
204 next_token(s);
205 if(token(s) != '+') {
206 prev_token(s);
207 break;
208 }
209 }
210
211 if(s->has_error) {
212 strbuffer_close(&strbuff);
213 return NULL;
214 }
215
216 if(!utf8_check_string(strbuff.value, strbuff.length)) {
217 set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
218 strbuffer_close(&strbuff);
219 s->has_error = 1;
220 return NULL;
221 }
222
223 *out_len = strbuff.length;
224 *ours = 1;
225 return strbuffer_steal_value(&strbuff);
226}
227
228static json_t *pack_object(scanner_t *s, va_list *ap)
229{
230 json_t *object = json_object();
231 next_token(s);
232
233 while(token(s) != '}') {
234 char *key;
235 size_t len;
236 int ours;
237 json_t *value;
238 char valueOptional;
239
240 if(!token(s)) {
241 set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
242 goto error;
243 }
244
245 if(token(s) != 's') {
246 set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
247 goto error;
248 }
249
250 key = read_string(s, ap, "object key", &len, &ours, 0);
251
252 next_token(s);
253
254 next_token(s);
255 valueOptional = token(s);
256 prev_token(s);
257
258 value = pack(s, ap);
259 if(!value) {
260 if(ours)
261 jsonp_free(key);
262
263 if(valueOptional != '*') {
264 set_error(s, "<args>", json_error_null_value, "NULL object value");
265 s->has_error = 1;
266 }
267
268 next_token(s);
269 continue;
270 }
271
272 if(s->has_error)
273 json_decref(value);
274
275 if(!s->has_error && json_object_set_new_nocheck(object, key, value)) {
276 set_error(s, "<internal>", json_error_out_of_memory, "Unable to add key \"%s\"", key);
277 s->has_error = 1;
278 }
279
280 if(ours)
281 jsonp_free(key);
282
283 next_token(s);
284 }
285
286 if(!s->has_error)
287 return object;
288
289error:
290 json_decref(object);
291 return NULL;
292}
293
294static json_t *pack_array(scanner_t *s, va_list *ap)
295{
296 json_t *array = json_array();
297 next_token(s);
298
299 while(token(s) != ']') {
300 json_t *value;
301 char valueOptional;
302
303 if(!token(s)) {
304 set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
305 /* Format string errors are unrecoverable. */
306 goto error;
307 }
308
309 next_token(s);
310 valueOptional = token(s);
311 prev_token(s);
312
313 value = pack(s, ap);
314 if(!value) {
315 if(valueOptional != '*') {
316 s->has_error = 1;
317 }
318
319 next_token(s);
320 continue;
321 }
322
323 if(s->has_error)
324 json_decref(value);
325
326 if(!s->has_error && json_array_append_new(array, value)) {
327 set_error(s, "<internal>", json_error_out_of_memory, "Unable to append to array");
328 s->has_error = 1;
329 }
330
331 next_token(s);
332 }
333
334 if(!s->has_error)
335 return array;
336
337error:
338 json_decref(array);
339 return NULL;
340}
341
342static json_t *pack_string(scanner_t *s, va_list *ap)
343{
344 char *str;
345 char t;
346 size_t len;
347 int ours;
348 int optional;
349
350 next_token(s);
351 t = token(s);
352 optional = t == '?' || t == '*';
353 if (!optional)
354 prev_token(s);
355
356 str = read_string(s, ap, "string", &len, &ours, optional);
357
358 if (!str)
359 return t == '?' && !s->has_error ? json_null() : NULL;
360
361 if (s->has_error) {
362 /* It's impossible to reach this point if ours != 0, do not free str. */
363 return NULL;
364 }
365
366 if (ours)
367 return jsonp_stringn_nocheck_own(str, len);
368
369 return json_stringn_nocheck(str, len);
370}
371
372static json_t *pack_object_inter(scanner_t *s, va_list *ap, int need_incref)
373{
374 json_t *json;
375 char ntoken;
376
377 next_token(s);
378 ntoken = token(s);
379
380 if (ntoken != '?' && ntoken != '*')
381 prev_token(s);
382
383 json = va_arg(*ap, json_t *);
384
385 if (json)
386 return need_incref ? json_incref(json) : json;
387
388 switch (ntoken) {
389 case '?':
390 return json_null();
391 case '*':
392 return NULL;
393 default:
394 break;
395 }
396
397 set_error(s, "<args>", json_error_null_value, "NULL object");
398 s->has_error = 1;
399 return NULL;
400}
401
402static json_t *pack_integer(scanner_t *s, json_int_t value)
403{
404 json_t *json = json_integer(value);
405
406 if (!json) {
407 set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
408 s->has_error = 1;
409 }
410
411 return json;
412}
413
414static json_t *pack_real(scanner_t *s, double value)
415{
416 /* Allocate without setting value so we can identify OOM error. */
417 json_t *json = json_real(0.0);
418
419 if (!json) {
420 set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
421 s->has_error = 1;
422
423 return NULL;
424 }
425
426 if (json_real_set(json, value)) {
427 json_decref(json);
428
429 set_error(s, "<args>", json_error_numeric_overflow, "Invalid floating point value");
430 s->has_error = 1;
431
432 return NULL;
433 }
434
435 return json;
436}
437
438static json_t *pack(scanner_t *s, va_list *ap)
439{
440 switch(token(s)) {
441 case '{':
442 return pack_object(s, ap);
443
444 case '[':
445 return pack_array(s, ap);
446
447 case 's': /* string */
448 return pack_string(s, ap);
449
450 case 'n': /* null */
451 return json_null();
452
453 case 'b': /* boolean */
454 return va_arg(*ap, int) ? json_true() : json_false();
455
456 case 'i': /* integer from int */
457 return pack_integer(s, va_arg(*ap, int));
458
459 case 'I': /* integer from json_int_t */
460 return pack_integer(s, va_arg(*ap, json_int_t));
461
462 case 'f': /* real */
463 return pack_real(s, va_arg(*ap, double));
464
465 case 'O': /* a json_t object; increments refcount */
466 return pack_object_inter(s, ap, 1);
467
468 case 'o': /* a json_t object; doesn't increment refcount */
469 return pack_object_inter(s, ap, 0);
470
471 default:
472 set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
473 token(s));
474 s->has_error = 1;
475 return NULL;
476 }
477}
478
479static int unpack(scanner_t *s, json_t *root, va_list *ap);
480
481static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
482{
483 int ret = -1;
484 int strict = 0;
485 int gotopt = 0;
486
487 /* Use a set (emulated by a hashtable) to check that all object
488 keys are accessed. Checking that the correct number of keys
489 were accessed is not enough, as the same key can be unpacked
490 multiple times.
491 */
492 hashtable_t key_set;
493
494 if(hashtable_init(&key_set)) {
495 set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
496 return -1;
497 }
498
499 if(root && !json_is_object(root)) {
500 set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s",
501 type_name(root));
502 goto out;
503 }
504 next_token(s);
505
506 while(token(s) != '}') {
507 const char *key;
508 json_t *value;
509 int opt = 0;
510
511 if(strict != 0) {
512 set_error(s, "<format>", json_error_invalid_format, "Expected '}' after '%c', got '%c'",
513 (strict == 1 ? '!' : '*'), token(s));
514 goto out;
515 }
516
517 if(!token(s)) {
518 set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
519 goto out;
520 }
521
522 if(token(s) == '!' || token(s) == '*') {
523 strict = (token(s) == '!' ? 1 : -1);
524 next_token(s);
525 continue;
526 }
527
528 if(token(s) != 's') {
529 set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
530 goto out;
531 }
532
533 key = va_arg(*ap, const char *);
534 if(!key) {
535 set_error(s, "<args>", json_error_null_value, "NULL object key");
536 goto out;
537 }
538
539 next_token(s);
540
541 if(token(s) == '?') {
542 opt = gotopt = 1;
543 next_token(s);
544 }
545
546 if(!root) {
547 /* skipping */
548 value = NULL;
549 }
550 else {
551 value = json_object_get(root, key);
552 if(!value && !opt) {
553 set_error(s, "<validation>", json_error_item_not_found, "Object item not found: %s", key);
554 goto out;
555 }
556 }
557
558 if(unpack(s, value, ap))
559 goto out;
560
561 hashtable_set(&key_set, key, json_null());
562 next_token(s);
563 }
564
565 if(strict == 0 && (s->flags & JSON_STRICT))
566 strict = 1;
567
568 if(root && strict == 1) {
569 /* We need to check that all non optional items have been parsed */
570 const char *key;
571 /* keys_res is 1 for uninitialized, 0 for success, -1 for error. */
572 int keys_res = 1;
573 strbuffer_t unrecognized_keys;
574 json_t *value;
575 long unpacked = 0;
576
577 if (gotopt || json_object_size(root) != key_set.size) {
578 json_object_foreach(root, key, value) {
579 if(!hashtable_get(&key_set, key)) {
580 unpacked++;
581
582 /* Save unrecognized keys for the error message */
583 if (keys_res == 1) {
584 keys_res = strbuffer_init(&unrecognized_keys);
585 } else if (!keys_res) {
586 keys_res = strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
587 }
588
589 if (!keys_res)
590 keys_res = strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
591 }
592 }
593 }
594 if (unpacked) {
595 set_error(s, "<validation>", json_error_end_of_input_expected,
596 "%li object item(s) left unpacked: %s",
597 unpacked,
598 keys_res ? "<unknown>" : strbuffer_value(&unrecognized_keys));
599 strbuffer_close(&unrecognized_keys);
600 goto out;
601 }
602 }
603
604 ret = 0;
605
606out:
607 hashtable_close(&key_set);
608 return ret;
609}
610
611static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
612{
613 size_t i = 0;
614 int strict = 0;
615
616 if(root && !json_is_array(root)) {
617 set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", type_name(root));
618 return -1;
619 }
620 next_token(s);
621
622 while(token(s) != ']') {
623 json_t *value;
624
625 if(strict != 0) {
626 set_error(s, "<format>", json_error_invalid_format, "Expected ']' after '%c', got '%c'",
627 (strict == 1 ? '!' : '*'),
628 token(s));
629 return -1;
630 }
631
632 if(!token(s)) {
633 set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
634 return -1;
635 }
636
637 if(token(s) == '!' || token(s) == '*') {
638 strict = (token(s) == '!' ? 1 : -1);
639 next_token(s);
640 continue;
641 }
642
643 if(!strchr(unpack_value_starters, token(s))) {
644 set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
645 token(s));
646 return -1;
647 }
648
649 if(!root) {
650 /* skipping */
651 value = NULL;
652 }
653 else {
654 value = json_array_get(root, i);
655 if(!value) {
656 set_error(s, "<validation>", json_error_index_out_of_range, "Array index %lu out of range",
657 (unsigned long)i);
658 return -1;
659 }
660 }
661
662 if(unpack(s, value, ap))
663 return -1;
664
665 next_token(s);
666 i++;
667 }
668
669 if(strict == 0 && (s->flags & JSON_STRICT))
670 strict = 1;
671
672 if(root && strict == 1 && i != json_array_size(root)) {
673 long diff = (long)json_array_size(root) - (long)i;
674 set_error(s, "<validation>", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff);
675 return -1;
676 }
677
678 return 0;
679}
680
681static int unpack(scanner_t *s, json_t *root, va_list *ap)
682{
683 switch(token(s))
684 {
685 case '{':
686 return unpack_object(s, root, ap);
687
688 case '[':
689 return unpack_array(s, root, ap);
690
691 case 's':
692 if(root && !json_is_string(root)) {
693 set_error(s, "<validation>", json_error_wrong_type, "Expected string, got %s",
694 type_name(root));
695 return -1;
696 }
697
698 if(!(s->flags & JSON_VALIDATE_ONLY)) {
699 const char **str_target;
700 size_t *len_target = NULL;
701
702 str_target = va_arg(*ap, const char **);
703 if(!str_target) {
704 set_error(s, "<args>", json_error_null_value, "NULL string argument");
705 return -1;
706 }
707
708 next_token(s);
709
710 if(token(s) == '%') {
711 len_target = va_arg(*ap, size_t *);
712 if(!len_target) {
713 set_error(s, "<args>", json_error_null_value, "NULL string length argument");
714 return -1;
715 }
716 }
717 else
718 prev_token(s);
719
720 if(root) {
721 *str_target = json_string_value(root);
722 if(len_target)
723 *len_target = json_string_length(root);
724 }
725 }
726 return 0;
727
728 case 'i':
729 if(root && !json_is_integer(root)) {
730 set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
731 type_name(root));
732 return -1;
733 }
734
735 if(!(s->flags & JSON_VALIDATE_ONLY)) {
736 int *target = va_arg(*ap, int*);
737 if(root)
738 *target = (int)json_integer_value(root);
739 }
740
741 return 0;
742
743 case 'I':
744 if(root && !json_is_integer(root)) {
745 set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
746 type_name(root));
747 return -1;
748 }
749
750 if(!(s->flags & JSON_VALIDATE_ONLY)) {
751 json_int_t *target = va_arg(*ap, json_int_t*);
752 if(root)
753 *target = json_integer_value(root);
754 }
755
756 return 0;
757
758 case 'b':
759 if(root && !json_is_boolean(root)) {
760 set_error(s, "<validation>", json_error_wrong_type, "Expected true or false, got %s",
761 type_name(root));
762 return -1;
763 }
764
765 if(!(s->flags & JSON_VALIDATE_ONLY)) {
766 int *target = va_arg(*ap, int*);
767 if(root)
768 *target = json_is_true(root);
769 }
770
771 return 0;
772
773 case 'f':
774 if(root && !json_is_real(root)) {
775 set_error(s, "<validation>", json_error_wrong_type, "Expected real, got %s",
776 type_name(root));
777 return -1;
778 }
779
780 if(!(s->flags & JSON_VALIDATE_ONLY)) {
781 double *target = va_arg(*ap, double*);
782 if(root)
783 *target = json_real_value(root);
784 }
785
786 return 0;
787
788 case 'F':
789 if(root && !json_is_number(root)) {
790 set_error(s, "<validation>", json_error_wrong_type, "Expected real or integer, got %s",
791 type_name(root));
792 return -1;
793 }
794
795 if(!(s->flags & JSON_VALIDATE_ONLY)) {
796 double *target = va_arg(*ap, double*);
797 if(root)
798 *target = json_number_value(root);
799 }
800
801 return 0;
802
803 case 'O':
804 if(root && !(s->flags & JSON_VALIDATE_ONLY))
805 json_incref(root);
806 /* Fall through */
807
808 case 'o':
809 if(!(s->flags & JSON_VALIDATE_ONLY)) {
810 json_t **target = va_arg(*ap, json_t**);
811 if(root)
812 *target = root;
813 }
814
815 return 0;
816
817 case 'n':
818 /* Never assign, just validate */
819 if(root && !json_is_null(root)) {
820 set_error(s, "<validation>", json_error_wrong_type, "Expected null, got %s",
821 type_name(root));
822 return -1;
823 }
824 return 0;
825
826 default:
827 set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
828 token(s));
829 return -1;
830 }
831}
832
833json_t *json_vpack_ex(json_error_t *error, size_t flags,
834 const char *fmt, va_list ap)
835{
836 scanner_t s;
837 va_list ap_copy;
838 json_t *value;
839
840 if(!fmt || !*fmt) {
841 jsonp_error_init(error, "<format>");
842 jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
843 return NULL;
844 }
845 jsonp_error_init(error, NULL);
846
847 scanner_init(&s, error, flags, fmt);
848 next_token(&s);
849
850 va_copy(ap_copy, ap);
851 value = pack(&s, &ap_copy);
852 va_end(ap_copy);
853
854 /* This will cover all situations where s.has_error is true */
855 if(!value)
856 return NULL;
857
858 next_token(&s);
859 if(token(&s)) {
860 json_decref(value);
861 set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
862 return NULL;
863 }
864
865 return value;
866}
867
868json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
869{
870 json_t *value;
871 va_list ap;
872
873 va_start(ap, fmt);
874 value = json_vpack_ex(error, flags, fmt, ap);
875 va_end(ap);
876
877 return value;
878}
879
880json_t *json_pack(const char *fmt, ...)
881{
882 json_t *value;
883 va_list ap;
884
885 va_start(ap, fmt);
886 value = json_vpack_ex(NULL, 0, fmt, ap);
887 va_end(ap);
888
889 return value;
890}
891
892int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
893 const char *fmt, va_list ap)
894{
895 scanner_t s;
896 va_list ap_copy;
897
898 if(!root) {
899 jsonp_error_init(error, "<root>");
900 jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value");
901 return -1;
902 }
903
904 if(!fmt || !*fmt) {
905 jsonp_error_init(error, "<format>");
906 jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
907 return -1;
908 }
909 jsonp_error_init(error, NULL);
910
911 scanner_init(&s, error, flags, fmt);
912 next_token(&s);
913
914 va_copy(ap_copy, ap);
915 if(unpack(&s, root, &ap_copy)) {
916 va_end(ap_copy);
917 return -1;
918 }
919 va_end(ap_copy);
920
921 next_token(&s);
922 if(token(&s)) {
923 set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
924 return -1;
925 }
926
927 return 0;
928}
929
930int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
931{
932 int ret;
933 va_list ap;
934
935 va_start(ap, fmt);
936 ret = json_vunpack_ex(root, error, flags, fmt, ap);
937 va_end(ap);
938
939 return ret;
940}
941
942int json_unpack(json_t *root, const char *fmt, ...)
943{
944 int ret;
945 va_list ap;
946
947 va_start(ap, fmt);
948 ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
949 va_end(ap);
950
951 return ret;
952}
Impressum, Datenschutz