unpack: Unify and enhance error messages
[jansson.git] / src / pack_unpack.c
1 /*
2  * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
3  * Copyright (c) 2011 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 <jansson.h>
10 #include "jansson_private.h"
11
12 typedef struct {
13     const char *fmt;
14     char token;
15     json_error_t *error;
16     size_t flags;
17     int line;
18     int column;
19 } scanner_t;
20
21 static const char *type_names[] = {
22     "object",
23     "array",
24     "string",
25     "integer",
26     "real",
27     "true",
28     "false",
29     "null"
30 };
31
32 #define type_name(x) type_names[json_typeof(x)]
33
34 static void next_token(scanner_t *s)
35 {
36     const char *t = s->fmt;
37     s->column++;
38
39     /* skip space and ignored chars */
40     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
41         if(*t == '\n') {
42             s->line++;
43             s->column = 1;
44         }
45         else
46             s->column++;
47
48         t++;
49     }
50
51     s->token = *t;
52
53     t++;
54     s->fmt = t;
55 }
56
57 static void set_error(scanner_t *s, const char *fmt, ...)
58 {
59     va_list ap;
60     va_start(ap, fmt);
61     jsonp_error_vset(s->error, s->line, s->column, fmt, ap);
62     va_end(ap);
63 }
64
65 static json_t *pack(scanner_t *s, va_list *ap);
66
67 static json_t *pack_object(scanner_t *s, va_list *ap)
68 {
69     json_t *object = json_object();
70     next_token(s);
71
72     while(s->token != '}') {
73         const char *key;
74         json_t *value;
75
76         if(!s->token) {
77             set_error(s, "Unexpected end of format string");
78             goto error;
79         }
80
81         if(s->token != 's') {
82             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
83             goto error;
84         }
85
86         key = va_arg(*ap, const char *);
87         if(!key) {
88             set_error(s, "NULL object key");
89             goto error;
90         }
91
92         next_token(s);
93
94         value = pack(s, ap);
95         if(!value)
96             goto error;
97
98         if(json_object_set_new(object, key, value)) {
99             set_error(s, "Unable to add key \"%s\"", key);
100             goto error;
101         }
102
103         next_token(s);
104     }
105
106     return object;
107
108 error:
109     json_decref(object);
110     return NULL;
111 }
112
113 static json_t *pack_array(scanner_t *s, va_list *ap)
114 {
115     json_t *array = json_array();
116     next_token(s);
117
118     while(s->token != ']') {
119         json_t *value;
120
121         if(!s->token) {
122             set_error(s, "Unexpected end of format string");
123             goto error;
124         }
125
126         value = pack(s, ap);
127         if(!value)
128             goto error;
129
130         if(json_array_append_new(array, value)) {
131             set_error(s, "Unable to append to array");
132             goto error;
133         }
134
135         next_token(s);
136     }
137     return array;
138
139 error:
140     json_decref(array);
141     return NULL;
142 }
143
144 static json_t *pack(scanner_t *s, va_list *ap)
145 {
146     switch(s->token) {
147         case '{':
148             return pack_object(s, ap);
149
150         case '[':
151             return pack_array(s, ap);
152
153         case 's': /* string */
154         {
155             const char *str = va_arg(*ap, const char *);
156             if(!str) {
157                 set_error(s, "NULL string");
158                 return NULL;
159             }
160             return json_string(str);
161         }
162
163         case 'n': /* null */
164             return json_null();
165
166         case 'b': /* boolean */
167             return va_arg(*ap, int) ? json_true() : json_false();
168
169         case 'i': /* integer */
170             return json_integer(va_arg(*ap, int));
171
172         case 'f': /* real */
173             return json_real(va_arg(*ap, double));
174
175         case 'O': /* a json_t object; increments refcount */
176             return json_incref(va_arg(*ap, json_t *));
177
178         case 'o': /* a json_t object; doesn't increment refcount */
179             return va_arg(*ap, json_t *);
180
181         default:
182             set_error(s, "Unrecognized format character '%c'", s->token);
183             return NULL;
184     }
185 }
186
187 static int unpack(scanner_t *s, json_t *root, va_list *ap);
188
189 static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
190 {
191     int ret = -1;
192     int wildcard = 0;
193
194     /* Use a set (emulated by a hashtable) to check that all object
195        keys are accessed. Checking that the correct number of keys
196        were accessed is not enough, as the same key can be unpacked
197        multiple times.
198     */
199     hashtable_t *key_set;
200
201     if(!(s->flags & JSON_UNPACK_ONLY)) {
202         key_set = hashtable_create(jsonp_hash_key, jsonp_key_equal, NULL, NULL);
203         if(!key_set) {
204             set_error(s, "Out of memory");
205             return -1;
206         }
207     }
208
209     if(!json_is_object(root)) {
210         set_error(s, "Expected object, got %s", type_name(root));
211         goto error;
212     }
213     next_token(s);
214
215     while(s->token != '}') {
216         const char *key;
217         json_t *value;
218
219         if(wildcard) {
220             set_error(s, "Expected '}' after '*', got '%c'", s->token);
221             goto error;
222         }
223
224         if(!s->token) {
225             set_error(s, "Unexpected end of format string");
226             goto error;
227         }
228
229         if(s->token == '*') {
230             wildcard = 1;
231             next_token(s);
232             continue;
233         }
234
235         if(s->token != 's') {
236             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
237             goto error;
238         }
239
240         key = va_arg(*ap, const char *);
241         if(!key) {
242             set_error(s, "NULL object key");
243             goto error;
244         }
245
246         next_token(s);
247
248         value = json_object_get(root, key);
249         if(unpack(s, value, ap))
250             goto error;
251
252         if(!(s->flags & JSON_UNPACK_ONLY))
253             hashtable_set(key_set, (void *)key, NULL);
254
255         next_token(s);
256     }
257
258     if(s->flags & JSON_UNPACK_ONLY)
259         wildcard = 1;
260
261     if(!wildcard && key_set->size != json_object_size(root)) {
262         long diff = (long)json_object_size(root) - (long)key_set->size;
263         set_error(s, "%li object items left unpacked", diff);
264         goto error;
265     }
266
267     ret = 0;
268
269 error:
270     if(!(s->flags & JSON_UNPACK_ONLY))
271         hashtable_destroy(key_set);
272
273     return ret;
274 }
275
276 static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
277 {
278     size_t i = 0;
279     int wildcard = 0;
280
281     if(!json_is_array(root)) {
282         set_error(s, "Expected array, got %s", type_name(root));
283         return -1;
284     }
285     next_token(s);
286
287     while(s->token != ']') {
288         json_t *value;
289
290         if(wildcard) {
291             set_error(s, "Expected ']' after '*', got '%c'", s->token);
292             return -1;
293         }
294
295         if(!s->token) {
296             set_error(s, "Unexpected end of format string");
297             return -1;
298         }
299
300         if(s->token == '*') {
301             wildcard = 1;
302             next_token(s);
303             continue;
304         }
305
306         value = json_array_get(root, i);
307         if(!value) {
308             set_error(s, "Array index %lu out of range", (unsigned long)i);
309             return -1;
310         }
311
312         if(unpack(s, value, ap))
313             return -1;
314
315         next_token(s);
316         i++;
317     }
318
319     if(s->flags & JSON_UNPACK_ONLY)
320         wildcard = 1;
321
322     if(!wildcard && i != json_array_size(root)) {
323         long diff = (long)json_array_size(root) - (long)i;
324         set_error(s, "%li array items left upacked", diff);
325         return -1;
326     }
327
328     return 0;
329 }
330
331 static int unpack(scanner_t *s, json_t *root, va_list *ap)
332 {
333     switch(s->token)
334     {
335         case '{':
336             return unpack_object(s, root, ap);
337
338         case '[':
339             return unpack_array(s, root, ap);
340
341         case 's':
342             if(!json_is_string(root)) {
343                 set_error(s, "Expected string, got %s", type_name(root));
344                 return -1;
345             }
346
347             if(!(s->flags & JSON_VALIDATE_ONLY)) {
348                 const char **str;
349
350                 str = va_arg(*ap, const char **);
351                 if(!str) {
352                     set_error(s, "NULL string");
353                     return -1;
354                 }
355
356                 *str = json_string_value(root);
357             }
358             return 0;
359
360         case 'i':
361             if(!json_is_integer(root)) {
362                 set_error(s, "Expected integer, got %s", type_name(root));
363                 return -1;
364             }
365
366             if(!(s->flags & JSON_VALIDATE_ONLY))
367                 *va_arg(*ap, int*) = json_integer_value(root);
368
369             return 0;
370
371         case 'b':
372             if(!json_is_boolean(root)) {
373                 set_error(s, "Expected true or false, got %s", type_name(root));
374                 return -1;
375             }
376
377             if(!(s->flags & JSON_VALIDATE_ONLY))
378                 *va_arg(*ap, int*) = json_is_true(root);
379
380             return 0;
381
382         case 'f':
383             if(!json_is_number(root)) {
384                 set_error(s, "Expected real, got %s", type_name(root));
385                 return -1;
386             }
387
388             if(!(s->flags & JSON_VALIDATE_ONLY))
389                 *va_arg(*ap, double*) = json_number_value(root);
390
391             return 0;
392
393         case 'O':
394             if(!(s->flags & JSON_VALIDATE_ONLY))
395                 json_incref(root);
396             /* Fall through */
397
398         case 'o':
399             if(!(s->flags & JSON_VALIDATE_ONLY))
400                 *va_arg(*ap, json_t**) = root;
401
402             return 0;
403
404         case 'n':
405             /* Never assign, just validate */
406             if(!json_is_null(root)) {
407                 set_error(s, "Expected null, got %s", type_name(root));
408                 return -1;
409             }
410             return 0;
411
412         default:
413             set_error(s, "Unknown format character '%c'", s->token);
414             return -1;
415     }
416 }
417
418 json_t *json_vpack_ex(json_error_t *error, size_t flags,
419                       const char *fmt, va_list ap)
420 {
421     scanner_t s;
422     json_t *value;
423
424     jsonp_error_init(error, "");
425
426     if(!fmt || !*fmt) {
427         jsonp_error_set(error, 1, 1, "Null or empty format string");
428         return NULL;
429     }
430
431     s.error = error;
432     s.flags = flags;
433     s.fmt = fmt;
434     s.line = 1;
435     s.column = 0;
436
437     next_token(&s);
438     value = pack(&s, &ap);
439
440     next_token(&s);
441     if(s.token) {
442         json_decref(value);
443         set_error(&s, "Garbage after format string");
444         return NULL;
445     }
446
447     return value;
448 }
449
450 json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
451 {
452     json_t *value;
453     va_list ap;
454
455     va_start(ap, fmt);
456     value = json_vpack_ex(error, flags, fmt, ap);
457     va_end(ap);
458
459     return value;
460 }
461
462 json_t *json_pack(const char *fmt, ...)
463 {
464     json_t *value;
465     va_list ap;
466
467     va_start(ap, fmt);
468     value = json_vpack_ex(NULL, 0, fmt, ap);
469     va_end(ap);
470
471     return value;
472 }
473
474 int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
475                     const char *fmt, va_list ap)
476 {
477     scanner_t s;
478
479     jsonp_error_init(error, "");
480
481     if(!fmt || !*fmt) {
482         jsonp_error_set(error, 1, 1, "Null or empty format string");
483         return -1;
484     }
485
486     s.error = error;
487     s.flags = flags;
488     s.fmt = fmt;
489     s.line = 1;
490     s.column = 0;
491
492     next_token(&s);
493
494     if(unpack(&s, root, &ap))
495         return -1;
496
497     next_token(&s);
498     if(s.token) {
499         set_error(&s, "Garbage after format string");
500         return -1;
501     }
502
503     return 0;
504 }
505
506 int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
507 {
508     int ret;
509     va_list ap;
510
511     va_start(ap, fmt);
512     ret = json_vunpack_ex(root, error, flags, fmt, ap);
513     va_end(ap);
514
515     return ret;
516 }
517
518 int json_unpack(json_t *root, const char *fmt, ...)
519 {
520     int ret;
521     va_list ap;
522
523     va_start(ap, fmt);
524     ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
525     va_end(ap);
526
527     return ret;
528 }