2 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
3 * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
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.
14 #include "jansson_private.h"
16 static void error_init(json_error_t *error)
20 error->text[0] = '\0';
25 static void error_set(json_error_t *error, const int line, const char *msg, ...)
29 if(!error || error->text[0] != '\0') {
30 /* error already set */
37 vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
41 json_t *json_pack(json_error_t *error, const char *fmt, ...) {
42 int fmt_length = strlen(fmt);
45 /* Keep a stack of containers (lists and objects) */
47 json_t **stack = NULL;
49 /* Keep a list of objects we create in case of error */
51 json_t **free_list = NULL;
53 json_t *cur = NULL; /* Current container */
54 json_t *root = NULL; /* root object */
57 char *key = NULL; /* Current key in an object */
62 /* Allocation provisioned for worst case */
63 stack = calloc(fmt_length, sizeof(json_t *));
64 free_list = calloc(fmt_length, sizeof(json_t *));
68 if(!stack || !free_list)
78 case ' ': /* Whitespace */
81 case ',': /* Element spacer */
84 error_set(error, line,
85 "Unexpected COMMA precedes root element!");
92 error_set(error, line,
93 "Unexpected COMMA outside a list or object!");
100 error_set(error, line, "Expected KEY, got COMMA!");
106 case ':': /* Key/value separator */
109 error_set(error, line, "Got key/value separator without "
110 "a key preceding it!");
115 if(!json_is_object(cur))
117 error_set(error, line, "Got a key/value separator "
118 "(':') outside an object!");
125 case ']': /* Close array or object */
130 error_set(error, line, "OBJECT or ARRAY ended with an "
131 "incomplete key/value pair!");
138 error_set(error, line,
139 "Too many close-brackets '%c'", *fmt);
144 if(*fmt == ']' && !json_is_array(cur))
146 error_set(error, line,
147 "Stray close-array ']' character");
152 if(*fmt == '}' && !json_is_object(cur))
154 error_set(error, line,
155 "Stray close-object '}' character");
160 cur = stack[--depth];
171 case 's': /* string */
172 s = va_arg(ap, char*);
176 error_set(error, line,
177 "Refusing to handle a NULL string");
182 if(json_is_object(cur) && !key)
189 obj = json_string(s);
196 case 'b': /* boolean */
197 obj = va_arg(ap, int) ?
198 json_true() : json_false();
201 case 'i': /* integer */
202 obj = json_integer(va_arg(ap, int));
205 case 'f': /* double-precision float */
206 obj = json_real(va_arg(ap, double));
209 case 'O': /* a json_t object; increments refcount */
210 obj = va_arg(ap, json_t *);
214 case 'o': /* a json_t object; doesn't increment refcount */
215 obj = va_arg(ap, json_t *);
218 obj_common: free_list[free_count++] = obj;
220 /* Root this object to its parent */
221 if(json_is_object(cur)) {
224 error_set(error, line,
225 "Expected key, got identifier '%c'!", *fmt);
230 json_object_set_new(cur, key, obj);
233 else if(json_is_array(cur))
235 json_array_append_new(cur, obj);
244 error_set(error, line, "Can't figure out where to attach "
245 "'%c' object!", *fmt);
250 /* If it was a container ('[' or '{'), descend on the stack */
251 if(json_is_array(obj) || json_is_object(obj))
253 stack[depth++] = cur;
264 error_set(error, line,
265 "Missing object or array close-brackets in format string");
270 /* Success: don't free everything we just built! */
275 json_decref(free_list[--free_count]);
286 int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
289 int rv=0; /* Return value */
290 int line = 1; /* Line number */
292 /* Keep a stack of containers (lists and objects) */
297 char *key = NULL; /* Current key in an object */
299 json_t *cur = NULL; /* Current container */
302 int fmt_length = strlen(fmt);
306 /* Allocation provisioned for worst case */
307 stack = calloc(fmt_length, sizeof(json_t *));
310 error_set(error, line, "Out of memory!");
315 /* Even if we're successful, we need to know if the number of
316 * arguments provided matches the number of JSON objects.
317 * We can do this by counting the elements in every array or
318 * object we open up, and decrementing the count as we visit
327 case ' ': /* Whitespace */
330 case '\n': /* Line break */
334 case ',': /* Element spacer */
338 error_set(error, line,
339 "Unexpected COMMA outside a list or object!");
346 error_set(error, line, "Expected KEY, got COMMA!");
352 case ':': /* Key/value separator */
353 if(!json_is_object(cur) || !key)
355 error_set(error, line, "Unexpected ':'");
368 else if(json_is_object(cur))
372 error_set(error, line, "Objects can't be keys");
376 obj = json_object_get(cur, key);
380 else if(json_is_array(cur))
382 obj = json_array_get(cur, array_index);
391 /* Make sure we got what we expected */
392 if(*fmt=='{' && !json_is_object(obj))
398 if(*fmt=='[' && !json_is_array(obj))
404 unvisited += json_is_object(obj) ?
405 json_object_size(obj) :
406 json_array_size(obj);
409 stack[depth++] = cur;
420 if(json_is_array(cur) && *fmt!=']')
422 error_set(error, line, "Missing ']'");
427 if(json_is_object(cur) && *fmt!='}')
429 error_set(error, line, "Missing '}'");
436 error_set(error, line, "Unexpected '%c'", *fmt);
443 error_set(error, line, "Unexpected '%c'", *fmt);
448 cur = stack[--depth];
453 if(!key && json_is_object(cur))
455 /* constant string for key */
456 key = va_arg(ap, char*);
461 case 'i': /* integer */
462 case 'f': /* double-precision float */
463 case 'O': /* a json_t object; increments refcount */
464 case 'o': /* a json_t object; borrowed reference */
465 case 'b': /* boolean */
473 else if(json_is_object(cur))
477 error_set(error, line,
478 "Only strings may be used as keys!");
483 obj = json_object_get(cur, key);
487 else if(json_is_array(cur))
489 obj = json_array_get(cur, array_index);
495 error_set(error, line,
496 "Unsure how to retrieve JSON object '%c'",
505 if(!json_is_string(obj))
507 error_set(error, line,
508 "Type mismatch! Object wasn't a string.");
512 *va_arg(ap, const char**) = json_string_value(obj);
516 if(!json_is_integer(obj))
518 error_set(error, line,
519 "Type mismatch! Object wasn't an integer.");
523 *va_arg(ap, int*) = json_integer_value(obj);
527 if(!json_is_boolean(obj))
529 error_set(error, line,
530 "Type mismatch! Object wasn't a boolean.");
534 *va_arg(ap, int*) = json_is_true(obj);
538 if(!json_is_number(obj))
540 error_set(error, line,
541 "Type mismatch! Object wasn't a real.");
545 *va_arg(ap, double*) = json_number_value(obj);
553 *va_arg(ap, json_t**) = obj;
557 /* Don't actually assign anything; we're just happy
558 * the null turned up as promised in the format
563 error_set(error, line,
564 "Unknown format character '%c'", *fmt);
572 /* Return 0 if everything was matched; otherwise the number of JSON
573 * objects we didn't get to. */
585 /* vim: ts=4:expandtab:sw=4