3f02a19f8ac35dc4e12fb2aa6feddc2d37136c12
[jansson.git] / src / variadic.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 <stdarg.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include <jansson.h>
14 #include "jansson_private.h"
15
16 typedef struct {
17     const char *fmt;
18     char token;
19     json_error_t *error;
20     int line;
21     int column;
22 } scanner_t;
23
24 static void next_token(scanner_t *s)
25 {
26     const char *t = s->fmt;
27     s->column++;
28
29     /* skip space and ignored chars */
30     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
31         if(*t == '\n') {
32             s->line++;
33             s->column = 1;
34         }
35         else
36             s->column++;
37
38         t++;
39     }
40
41     s->token = *t;
42
43     t++;
44     s->fmt = t;
45 }
46
47 static void set_error(scanner_t *s, const char *fmt, ...)
48 {
49     va_list ap;
50     va_start(ap, fmt);
51     jsonp_error_vset(s->error, s->line, s->column, fmt, ap);
52     va_end(ap);
53 }
54
55 static json_t *pack(scanner_t *s, va_list *ap);
56
57 static json_t *pack_object(scanner_t *s, va_list *ap)
58 {
59     json_t *object = json_object();
60     next_token(s);
61
62     while(s->token != '}') {
63         const char *key;
64         json_t *value;
65
66         if(!s->token) {
67             set_error(s, "Unexpected end of format string");
68             goto error;
69         }
70
71         if(s->token != 's') {
72             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
73             goto error;
74         }
75
76         key = va_arg(*ap, const char *);
77         if(!key) {
78             set_error(s, "NULL object key");
79             goto error;
80         }
81
82         next_token(s);
83
84         value = pack(s, ap);
85         if(!value)
86             goto error;
87
88         if(json_object_set_new(object, key, value)) {
89             set_error(s, "Unable to add key \"%s\"", key);
90             goto error;
91         }
92
93         next_token(s);
94     }
95
96     return object;
97
98 error:
99     json_decref(object);
100     return NULL;
101 }
102
103 static json_t *pack_array(scanner_t *s, va_list *ap)
104 {
105     json_t *array = json_array();
106     next_token(s);
107
108     while(s->token != ']') {
109         json_t *value;
110
111         if(!s->token) {
112             set_error(s, "Unexpected end of format string");
113             goto error;
114         }
115
116         value = pack(s, ap);
117         if(!value)
118             goto error;
119
120         if(json_array_append_new(array, value)) {
121             set_error(s, "Unable to append to array");
122             goto error;
123         }
124
125         next_token(s);
126     }
127     return array;
128
129 error:
130     json_decref(array);
131     return NULL;
132 }
133
134 static json_t *pack(scanner_t *s, va_list *ap)
135 {
136     switch(s->token) {
137         case '{':
138             return pack_object(s, ap);
139
140         case '[':
141             return pack_array(s, ap);
142
143         case 's': /* string */
144         {
145             const char *str = va_arg(*ap, const char *);
146             if(!str)
147             {
148                 set_error(s, "NULL string");
149                 return NULL;
150             }
151             return json_string(str);
152         }
153
154         case 'n': /* null */
155             return json_null();
156
157         case 'b': /* boolean */
158             return va_arg(*ap, int) ? json_true() : json_false();
159
160         case 'i': /* integer */
161             return json_integer(va_arg(*ap, int));
162
163         case 'f': /* double-precision float */
164             return json_real(va_arg(*ap, double));
165
166         case 'O': /* a json_t object; increments refcount */
167             return json_incref(va_arg(*ap, json_t *));
168
169         case 'o': /* a json_t object; doesn't increment refcount */
170             return va_arg(*ap, json_t *);
171
172         default: /* Whoops! */
173             set_error(s, "Unrecognized format character '%c'", s->token);
174             return NULL;
175     }
176 }
177
178 static int json_vnunpack(json_t *root, json_error_t *error, ssize_t size, const char *fmt, va_list *ap)
179 {
180
181     int rv = 0; /* Return value */
182     int line = 1; /* Line number */
183     int column = 1; /* Column */
184
185     /* Position markers for arrays or objects */
186     int array_index = 0;
187     char *key = NULL;
188
189     const char **s;
190
191     /* Scanner variables */
192     const char *tok = fmt;
193     const char *etok;
194     int etok_depth;
195
196     json_t *obj;
197
198     /* If we're successful, we need to know if the number of arguments
199      * provided matches the number of JSON objects.  We can do this by
200      * counting the elements in every array or object we open up, and
201      * decrementing the count as we visit their children. */
202     int unvisited = 0;
203
204     /* Skip whitespace at the beginning of the string. */
205     while(size && *tok == ' ') {
206         tok++;
207         size--;
208         column++;
209     }
210
211     if(size <= 0) {
212         jsonp_error_set(error, 1, 1, "Empty format string!");
213         return -2;
214     }
215
216     /* tok must contain either a container type, or a length-1 string for a
217      * simple type. */
218     if(*tok != '[' && *tok != '{')
219     {
220         /* Simple object. Permit trailing spaces, otherwise complain. */
221         if((ssize_t)strspn(tok+1, " ") < size-1)
222         {
223             jsonp_error_set(error, 1, 1,
224                     "Expected a single object, got %i", size);
225             return -1;
226         }
227
228         switch(*tok)
229         {
230             case 's':
231                 if(!json_is_string(root))
232                 {
233                     jsonp_error_set(error, line, column,
234                             "Type mismatch! Object (%i) wasn't a string.",
235                             json_typeof(root));
236                     return -2;
237                 }
238                 s = va_arg(*ap, const char **);
239                 if(!s) {
240                     jsonp_error_set(error, line, column, "Passed a NULL string pointer!");
241                     return -2;
242                 }
243                 *s = json_string_value(root);
244                 return 0;
245
246             case 'i':
247                 if(!json_is_integer(root))
248                 {
249                     jsonp_error_set(error, line, column,
250                             "Type mismatch! Object (%i) wasn't an integer.",
251                             json_typeof(root));
252                     return -2;
253                 }
254                 *va_arg(*ap, int*) = json_integer_value(root);
255                 return 0;
256
257             case 'b':
258                 if(!json_is_boolean(root))
259                 {
260                     jsonp_error_set(error, line, column,
261                             "Type mismatch! Object (%i) wasn't a boolean.",
262                             json_typeof(root));
263                     return -2;
264                 }
265                 *va_arg(*ap, int*) = json_is_true(root);
266                 return 0;
267
268             case 'f':
269                 if(!json_is_number(root))
270                 {
271                     jsonp_error_set(error, line, column,
272                             "Type mismatch! Object (%i) wasn't a real.",
273                             json_typeof(root));
274                     return -2;
275                 }
276                 *va_arg(*ap, double*) = json_number_value(root);
277                 return 0;
278
279             case 'O':
280                 json_incref(root);
281                 /* Fall through */
282
283             case 'o':
284                 *va_arg(*ap, json_t**) = root;
285                 return 0;
286
287             case 'n':
288                 /* Don't actually assign anything; we're just happy
289                  * the null turned up as promised in the format
290                  * string. */
291                 return 0;
292
293             default:
294                 jsonp_error_set(error, line, column,
295                         "Unknown format character '%c'", *tok);
296                 return -1;
297         }
298     }
299
300     /* Move past container opening token */
301     tok++;
302
303     while(tok-fmt < size) {
304         switch(*tok) {
305             case '\n':
306                 line++;
307                 column = 0;
308                 break;
309
310             case ' ': /* Whitespace */
311                 break;
312
313             case ',': /* Element spacer */
314                 if(key)
315                 {
316                     jsonp_error_set(error, line, column,
317                               "Expected KEY, got COMMA!");
318                     return -2;
319                 }
320                 break;
321
322             case ':': /* Key/value separator */
323                 if(!key)
324                 {
325                     jsonp_error_set(error, line, column,
326                               "Got key/value separator without "
327                               "a key preceding it!");
328                     return -2;
329                 }
330
331                 if(!json_is_object(root))
332                 {
333                     jsonp_error_set(error, line, column,
334                               "Got a key/value separator "
335                               "(':') outside an object!");
336                     return -2;
337                 }
338
339                 break;
340
341             case ']': /* Close array or object */
342             case '}':
343
344                 if(tok-fmt + (ssize_t)strspn(tok+1, " ") != size-1)
345                 {
346                     jsonp_error_set(error, line, column,
347                               "Unexpected close-bracket '%c'", *tok);
348                     return -2;
349                 }
350
351                 if((*tok == ']' && !json_is_array(root)) ||
352                    (*tok == '}' && !json_is_object(root)))
353                 {
354                     jsonp_error_set(error, line, column,
355                               "Stray close-array '%c' character", *tok);
356                     return -2;
357                 }
358                 return unvisited;
359
360             case '[':
361             case '{':
362
363                 /* Find corresponding close bracket */
364                 etok = tok+1;
365                 etok_depth = 1;
366                 while(etok_depth) {
367
368                     if(!*etok || etok-fmt >= size) {
369                         jsonp_error_set(error, line, column,
370                                 "Couldn't find matching close bracket for '%c'",
371                                 *tok);
372                         return -2;
373                     }
374
375                     if(*tok == *etok)
376                         etok_depth++;
377                     else if(*tok == '[' && *etok == ']') {
378                         etok_depth--;
379                         break;
380                     } else if(*tok == '{' && *etok == '}') {
381                         etok_depth--;
382                         break;
383                     }
384
385                     etok++;
386                 }
387
388                 /* Recurse */
389                 if(json_is_array(root)) {
390                     rv = json_vnunpack(json_object_get(root, key),
391                             error, etok-tok+1, tok, ap);
392                 } else {
393                     rv = json_vnunpack(json_array_get(root, array_index++),
394                             error, etok-tok+1, tok, ap);
395                 }
396
397                 if(rv < 0) {
398                     /* error should already be set */
399                     error->column += column-1;
400                     error->line += line-1;
401                     return rv;
402                 }
403
404                 unvisited += rv;
405                 column += etok-tok;
406                 tok = etok;
407                 break;
408
409             case 's':
410                 /* Handle strings specially, since they're used for both keys
411                  * and values */
412
413                 if(json_is_object(root) && !key)
414                 {
415                     /* It's a key */
416                     key = va_arg(*ap, char *);
417
418                     if(!key)
419                     {
420                         jsonp_error_set(error, line, column,
421                                   "Refusing to handle a NULL key");
422                         return -2;
423                     }
424                     break;
425                 }
426
427                 /* Fall through */
428
429             default:
430
431                 /* Fetch the element from the JSON container */
432                 if(json_is_object(root))
433                     obj = json_object_get(root, key);
434                 else
435                     obj = json_array_get(root, array_index++);
436
437                 if(!obj) {
438                     jsonp_error_set(error, line, column,
439                             "Array/object entry didn't exist!");
440                     return -1;
441                 }
442
443                 rv = json_vnunpack(obj, error, 1, tok, ap);
444                 if(rv != 0)
445                     return rv;
446
447                 break;
448         }
449         tok++;
450         column++;
451     }
452
453     /* Whoops -- we didn't match the close bracket! */
454     jsonp_error_set(error, line, column, "Missing close array or object!");
455     return -2;
456 }
457
458 json_t *json_pack(json_error_t *error, const char *fmt, ...)
459 {
460     scanner_t s;
461     json_t *value;
462     va_list ap;
463
464     jsonp_error_init(error, "");
465
466     if(!fmt || !*fmt) {
467         jsonp_error_set(error, 1, 1, "Null or empty format string!");
468         return NULL;
469     }
470
471     s.error = error;
472     s.fmt = fmt;
473     s.line = 1;
474     s.column = 0;
475
476     next_token(&s);
477
478     va_start(ap, fmt);
479     value = pack(&s, &ap);
480     va_end(ap);
481
482     next_token(&s);
483     if(s.token) {
484         set_error(&s, "Garbage after format string");
485         return NULL;
486     }
487
488     return value;
489 }
490
491 int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...)
492 {
493     va_list ap;
494     int rv;
495
496     jsonp_error_init(error, "");
497
498     if(!fmt || !*fmt) {
499         jsonp_error_set(error, 1, 1, "Null or empty format string!");
500         return -2;;
501     }
502
503     va_start(ap, fmt);
504     rv = json_vnunpack(root, error, strlen(fmt), fmt, &ap);
505     va_end(ap);
506
507     return rv;
508 }