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