f9311ba0f622093154b99994797b4eb3cf327e5f
[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 <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 unpack(scanner_t *s, json_t *root, va_list *ap);
179
180 static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
181 {
182     if(!json_is_object(root)) {
183         set_error(s, "Expected object, got %i", json_typeof(root));
184         return -1;
185     }
186     next_token(s);
187
188     while(s->token != '}') {
189         const char *key;
190         json_t *value;
191
192         if(!s->token) {
193             set_error(s, "Unexpected end of format string");
194             return -1;
195         }
196
197         if(s->token != 's') {
198             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
199             return -1;
200         }
201
202         key = va_arg(*ap, const char *);
203         if(!key) {
204             set_error(s, "NULL object key");
205             return -1;
206         }
207
208         next_token(s);
209
210         value = json_object_get(root, key);
211         if(unpack(s, value, ap))
212             return -1;
213
214         next_token(s);
215     }
216
217     return 0;
218 }
219
220 static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
221 {
222     size_t i = 0;
223
224     if(!json_is_array(root)) {
225         set_error(s, "Expected array, got %d", json_typeof(root));
226         return -1;
227     }
228     next_token(s);
229
230     while(s->token != ']') {
231         json_t *value;
232
233         if(!s->token) {
234             set_error(s, "Unexpected end of format string");
235             return -1;
236         }
237
238         value = json_array_get(root, i);
239         if(!value) {
240             set_error(s, "Array index %lu out of range", (unsigned long)i);
241             return -1;
242         }
243
244         if(unpack(s, value, ap))
245             return -1;
246
247         next_token(s);
248         i++;
249     }
250
251     if(i != json_array_size(root)) {
252         long diff = (long)json_array_size(root) - (long)i;
253         set_error(s, "%li array items were not upacked", diff);
254         return -1;
255     }
256
257     return 0;
258 }
259
260 static int unpack(scanner_t *s, json_t *root, va_list *ap)
261 {
262     switch(s->token)
263     {
264         case '{':
265             return unpack_object(s, root, ap);
266
267         case '[':
268             return unpack_array(s, root, ap);
269
270         case 's':
271         {
272             const char **str;
273
274             if(!json_is_string(root))
275             {
276                 set_error(s, "Type mismatch! Object (%i) wasn't a string.",
277                           json_typeof(root));
278                 return -1;
279             }
280
281             str = va_arg(*ap, const char **);
282             if(!str) {
283                 set_error(s, "Passed a NULL string pointer!");
284                 return -1;
285             }
286
287             *str = json_string_value(root);
288             return 0;
289         }
290
291         case 'i':
292             if(!json_is_integer(root))
293             {
294                 set_error(s, "Type mismatch! Object (%i) wasn't an integer.",
295                       json_typeof(root));
296                 return -1;
297             }
298             *va_arg(*ap, int*) = json_integer_value(root);
299             return 0;
300
301         case 'b':
302             if(!json_is_boolean(root))
303             {
304                 set_error(s, "Type mismatch! Object (%i) wasn't a boolean.",
305                       json_typeof(root));
306                 return -1;
307             }
308             *va_arg(*ap, int*) = json_is_true(root);
309             return 0;
310
311         case 'f':
312             if(!json_is_number(root))
313             {
314                 set_error(s, "Type mismatch! Object (%i) wasn't a real.",
315                       json_typeof(root));
316                 return -1;
317             }
318             *va_arg(*ap, double*) = json_number_value(root);
319             return 0;
320
321         case 'O':
322             json_incref(root);
323             /* Fall through */
324
325         case 'o':
326             *va_arg(*ap, json_t**) = root;
327             return 0;
328
329         case 'n':
330             /* Don't assign, just validate */
331             if(!json_is_null(root))
332             {
333                 set_error(s, "Type mismatch! Object (%i) wasn't null.",
334                       json_typeof(root));
335                 return -1;
336             }
337             return 0;
338
339         default:
340             set_error(s, "Unknown format character '%c'", s->token);
341             return -1;
342     }
343 }
344
345 json_t *json_pack(json_error_t *error, const char *fmt, ...)
346 {
347     scanner_t s;
348     json_t *value;
349     va_list ap;
350
351     jsonp_error_init(error, "");
352
353     if(!fmt || !*fmt) {
354         jsonp_error_set(error, 1, 1, "Null or empty format string!");
355         return NULL;
356     }
357
358     s.error = error;
359     s.fmt = fmt;
360     s.line = 1;
361     s.column = 0;
362
363     next_token(&s);
364
365     va_start(ap, fmt);
366     value = pack(&s, &ap);
367     va_end(ap);
368
369     next_token(&s);
370     if(s.token) {
371         set_error(&s, "Garbage after format string");
372         return NULL;
373     }
374
375     return value;
376 }
377
378 int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...)
379 {
380     scanner_t s;
381     va_list ap;
382     int result;
383
384     jsonp_error_init(error, "");
385
386     if(!fmt || !*fmt) {
387         jsonp_error_set(error, 1, 1, "Null or empty format string!");
388         return -1;
389     }
390
391     s.error = error;
392     s.fmt = fmt;
393     s.line = 1;
394     s.column = 0;
395
396     next_token(&s);
397
398     va_start(ap, fmt);
399     result = unpack(&s, root, &ap);
400     va_end(ap);
401
402     if(result)
403         return -1;
404
405     next_token(&s);
406     if(s.token) {
407         set_error(&s, "Garbage after format string");
408         return -1;
409     }
410
411     return 0;
412 }