#include <jansson.h>
#include "jansson_private.h"
-json_t *json_pack(json_error_t *error, const char *fmt, ...) {
- int fmt_length = strlen(fmt);
- va_list ap;
-
- /* Keep a stack of containers (lists and objects) */
- int depth = 0;
- json_t **stack = NULL;
-
- /* Keep a list of objects we create in case of error */
- int free_count = 0;
- json_t **free_list = NULL;
-
- json_t *cur = NULL; /* Current container */
+static json_t *json_vnpack(json_error_t *error, ssize_t size, const char * const fmt, va_list *ap)
+{
json_t *root = NULL; /* root object */
json_t *obj = NULL;
+ /* Scanner variables */
+ const char *tok = fmt;
+ const char *etok;
+ int etok_depth;
+
char *key = NULL; /* Current key in an object */
char *s;
- int line = 1;
+ int line=1;
+ int column=1;
- /* Allocation provisioned for worst case */
- stack = calloc(fmt_length, sizeof(json_t *));
- free_list = calloc(fmt_length, sizeof(json_t *));
+ /* Skip whitespace at the beginning of the string. */
+ while(size && *tok == ' ') {
+ tok++;
+ size--;
+ column++;
+ }
- jsonp_error_init(error, "");
+ if(size <= 0) {
+ jsonp_error_set(error, 1, 1, "Empty format string!");
+ return(NULL);
+ }
- if(!stack || !free_list)
- goto out;
+ /* tok must contain either a container type, or a length-1 string for a
+ * simple type. */
+ if(*tok == '[')
+ root = json_array();
+ else if(*tok == '{')
+ root = json_object();
+ else
+ {
+ /* Simple object. Permit trailing spaces, otherwise complain. */
+ if((ssize_t)strspn(tok+1, " ") < size-1)
+ {
+ jsonp_error_set(error, 1, 1,
+ "Expected a single object, got %i", size);
+ return(NULL);
+ }
- va_start(ap, fmt);
- while(*fmt) {
- switch(*fmt) {
+ switch(*tok)
+ {
+ case 's': /* string */
+ s = va_arg(*ap, char*);
+ if(!s)
+ {
+ jsonp_error_set(error, 1, 1,
+ "Refusing to handle a NULL string");
+ return(NULL);
+ }
+ return(json_string(s));
+
+ case 'n': /* null */
+ return(json_null());
+
+ case 'b': /* boolean */
+ obj = va_arg(*ap, int) ?
+ json_true() : json_false();
+ return(obj);
+
+ case 'i': /* integer */
+ return(json_integer(va_arg(*ap, int)));
+
+ case 'f': /* double-precision float */
+ return(json_real(va_arg(*ap, double)));
+
+ case 'O': /* a json_t object; increments refcount */
+ obj = va_arg(*ap, json_t *);
+ json_incref(obj);
+ return(obj);
+
+ case 'o': /* a json_t object; doesn't increment refcount */
+ obj = va_arg(*ap, json_t *);
+ return(obj);
+
+ default: /* Whoops! */
+ jsonp_error_set(error, 1, 1,
+ "Didn't understand format character '%c'",
+ *tok);
+ return(NULL);
+ }
+ }
+
+ /* Move past container opening token */
+ tok++;
+ column++;
+
+ while(tok-fmt < size) {
+ switch(*tok) {
case '\n':
line++;
+ column=0;
break;
case ' ': /* Whitespace */
break;
case ',': /* Element spacer */
- if(!root)
- {
- jsonp_error_set(error, line, -1,
- "Unexpected COMMA precedes root element!");
- root = NULL;
- goto out;
- }
-
- if(!cur)
- {
- jsonp_error_set(error, line, -1,
- "Unexpected COMMA outside a list or object!");
- root = NULL;
- goto out;
- }
-
if(key)
{
- jsonp_error_set(error, line, -1,
+ jsonp_error_set(error, line, column,
"Expected KEY, got COMMA!");
- root = NULL;
- goto out;
+ json_decref(root);
+ return(NULL);
}
break;
case ':': /* Key/value separator */
if(!key)
{
- jsonp_error_set(error, line, -1,
+ jsonp_error_set(error, line, column,
"Got key/value separator without "
"a key preceding it!");
- root = NULL;
- goto out;
+ json_decref(root);
+ return(NULL);
}
- if(!json_is_object(cur))
+ if(!json_is_object(root))
{
- jsonp_error_set(error, line, -1,
+ jsonp_error_set(error, line, column,
"Got a key/value separator "
"(':') outside an object!");
- root = NULL;
- goto out;
+ json_decref(root);
+ return(NULL);
}
break;
case ']': /* Close array or object */
case '}':
- if(key)
+ if(tok-fmt + (ssize_t)strspn(tok+1, " ") != size-1)
{
- jsonp_error_set(error, line, -1,
- "OBJECT or ARRAY ended with an "
- "incomplete key/value pair!");
- root = NULL;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Unexpected close-bracket '%c'", *tok);
+ json_decref(root);
+ return(NULL);
}
- if(depth <= 0)
+ if((*tok == ']' && !json_is_array(root)) ||
+ (*tok == '}' && !json_is_object(root)))
{
- jsonp_error_set(error, line, -1,
- "Too many close-brackets '%c'", *fmt);
- root = NULL;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Stray close-array '%c' character", *tok);
+ json_decref(root);
+ return(NULL);
}
+ return(root);
- if(*fmt == ']' && !json_is_array(cur))
- {
- jsonp_error_set(error, line, -1,
- "Stray close-array ']' character");
- root = NULL;
- goto out;
- }
+ case '[':
+ case '{':
- if(*fmt == '}' && !json_is_object(cur))
- {
- jsonp_error_set(error, line, -1,
- "Stray close-object '}' character");
- root = NULL;
- goto out;
- }
+ /* Shortcut so we don't mess up the column count in error
+ * messages */
+ if(json_is_object(root) && !key)
+ goto common;
+
+ /* Find corresponding close bracket */
+ etok = tok+1;
+ etok_depth = 1;
+ while(etok_depth) {
+
+ if(!*etok || etok-fmt >= size) {
+ jsonp_error_set(error, line, column,
+ "Couldn't find matching close bracket for '%c'",
+ *tok);
+ json_decref(root);
+ return(NULL);
+ }
- cur = stack[--depth];
- break;
+ if(*tok==*etok)
+ etok_depth++;
+ else if(*tok=='[' && *etok==']') {
+ etok_depth--;
+ break;
+ } else if(*tok=='{' && *etok=='}') {
+ etok_depth--;
+ break;
+ }
- case '[':
- obj = json_array();
- goto obj_common;
+ etok++;
+ }
- case '{':
- obj = json_object();
- goto obj_common;
+ /* Recurse */
+ obj = json_vnpack(error, etok-tok+1, tok, ap);
+ if(!obj) {
+ /* error should already be set */
+ error->column += column-1;
+ error->line += line-1;
+ json_decref(root);
+ return(NULL);
+ }
+ column += etok-tok;
+ tok = etok;
+ goto common;
- case 's': /* string */
- s = va_arg(ap, char*);
+ case 's':
+ /* Handle strings specially, since they're used for both keys
+ * and values */
+ s = va_arg(*ap, char*);
if(!s)
{
- jsonp_error_set(error, line, -1,
+ jsonp_error_set(error, line, column,
"Refusing to handle a NULL string");
- root = NULL;
- goto out;
+ json_decref(root);
+ return(NULL);
}
- if(json_is_object(cur) && !key)
+ if(json_is_object(root) && !key)
{
/* It's a key */
key = s;
}
obj = json_string(s);
- goto obj_common;
-
- case 'n': /* null */
- obj = json_null();
- goto obj_common;
-
- case 'b': /* boolean */
- obj = va_arg(ap, int) ?
- json_true() : json_false();
- goto obj_common;
-
- case 'i': /* integer */
- obj = json_integer(va_arg(ap, int));
- goto obj_common;
-
- case 'f': /* double-precision float */
- obj = json_real(va_arg(ap, double));
- goto obj_common;
-
- case 'O': /* a json_t object; increments refcount */
- obj = va_arg(ap, json_t *);
- json_incref(obj);
- goto obj_common;
-
- case 'o': /* a json_t object; doesn't increment refcount */
- obj = va_arg(ap, json_t *);
- goto obj_common;
+ goto common;
-obj_common: free_list[free_count++] = obj;
+ default:
+ obj = json_vnpack(error, 1, tok, ap);
+ if(!obj) {
+ json_decref(root);
+ return(NULL);
+ }
- /* Root this object to its parent */
- if(json_is_object(cur)) {
+common:
+ /* Add to container */
+ if(json_is_object(root)) {
if(!key)
{
- jsonp_error_set(error, line, -1,
- "Expected key, got identifier '%c'!", *fmt);
- root = NULL;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Expected key, got identifier '%c'!", *tok);
+ json_decref(root);
+ return(NULL);
}
- json_object_set_new(cur, key, obj);
+ json_object_set_new(root, key, obj);
key = NULL;
}
- else if(json_is_array(cur))
- {
- json_array_append_new(cur, obj);
- }
- else if(!root)
- {
- printf("Rooting\n");
- root = obj;
- }
else
{
- jsonp_error_set(error, line, -1,
- "Can't figure out where to attach "
- "'%c' object!", *fmt);
- root = NULL;
- goto out;
+ json_array_append_new(root, obj);
}
-
- /* If it was a container ('[' or '{'), descend on the stack */
- if(json_is_array(obj) || json_is_object(obj))
- {
- stack[depth++] = cur;
- cur = obj;
- }
-
break;
}
- fmt++;
+ tok++;
+ column++;
}
- va_end(ap);
- if(depth != 0) {
- jsonp_error_set(error, line, -1,
- "Missing object or array close-brackets in format string");
- root = NULL;
- goto out;
- }
-
- /* Success: don't free everything we just built! */
- free_count = 0;
-
-out:
- while(free_count)
- json_decref(free_list[--free_count]);
-
- if(free_list)
- free(free_list);
-
- if(stack)
- free(stack);
-
- return(root);
+ /* Whoops -- we didn't match the close bracket! */
+ jsonp_error_set(error, line, column, "Missing close array or object!");
+ json_decref(root);
+ return(NULL);
}
-int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...) {
- va_list ap;
+static int json_vnunpack(json_t *root, json_error_t *error, ssize_t size, const char *fmt, va_list *ap)
+{
int rv=0; /* Return value */
int line = 1; /* Line number */
+ int column = 1; /* Column */
- /* Keep a stack of containers (lists and objects) */
- int depth = 0;
- json_t **stack;
-
+ /* Position markers for arrays or objects */
int array_index = 0;
- char *key = NULL; /* Current key in an object */
+ char *key = NULL;
- json_t *cur = NULL; /* Current container */
- json_t *obj = NULL;
+ const char **s;
- int fmt_length = strlen(fmt);
+ /* Scanner variables */
+ const char *tok = fmt;
+ const char *etok;
+ int etok_depth;
- jsonp_error_init(error, "");
+ json_t *obj;
- /* Allocation provisioned for worst case */
- stack = calloc(fmt_length, sizeof(json_t *));
- if(!stack)
- {
- jsonp_error_set(error, line, -1, "Out of memory!");
- rv = -1;
- goto out;
+ /* If we're successful, we need to know if the number of arguments
+ * provided matches the number of JSON objects. We can do this by
+ * counting the elements in every array or object we open up, and
+ * decrementing the count as we visit their children. */
+ int unvisited = 0;
+
+ /* Skip whitespace at the beginning of the string. */
+ while(size && *tok == ' ') {
+ tok++;
+ size--;
+ column++;
}
- /* Even if we're successful, we need to know if the number of
- * arguments provided matches the number of JSON objects.
- * We can do this by counting the elements in every array or
- * object we open up, and decrementing the count as we visit
- * their children. */
- int unvisited = 0;
+ if(size <= 0) {
+ jsonp_error_set(error, 1, 1, "Empty format string!");
+ return(-2);
+ }
- va_start(ap, fmt);
- while(*fmt)
+ /* tok must contain either a container type, or a length-1 string for a
+ * simple type. */
+ if(*tok != '[' && *tok != '{')
{
- switch(*fmt)
+ /* Simple object. Permit trailing spaces, otherwise complain. */
+ if((ssize_t)strspn(tok+1, " ") < size-1)
{
- case ' ': /* Whitespace */
- break;
-
- case '\n': /* Line break */
- line++;
- break;
-
- case ',': /* Element spacer */
+ jsonp_error_set(error, 1, 1,
+ "Expected a single object, got %i", size);
+ return(-1);
+ }
- if(!cur)
+ switch(*tok)
+ {
+ case 's':
+ if(!json_is_string(root))
{
- jsonp_error_set(error, line, -1,
- "Unexpected COMMA outside a list or object!");
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Type mismatch! Object (%i) wasn't a string.",
+ json_typeof(root));
+ return(-2);
}
-
- if(key)
- {
- jsonp_error_set(error, line, -1,
- "Expected KEY, got COMMA!");
- rv = -1;
- goto out;
+ s = va_arg(*ap, const char **);
+ if(!s) {
+ jsonp_error_set(error, line, column, "Passed a NULL string pointer!");
+ return(-2);
}
- break;
+ *s = json_string_value(root);
+ return(0);
- case ':': /* Key/value separator */
- if(!json_is_object(cur) || !key)
+ case 'i':
+ if(!json_is_integer(root))
{
- jsonp_error_set(error, line, -1, "Unexpected ':'");
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Type mismatch! Object (%i) wasn't an integer.",
+ json_typeof(root));
+ return(-2);
}
- break;
+ *va_arg(*ap, int*) = json_integer_value(root);
+ return(0);
- case '[':
- case '{':
- /* Fetch object */
- if(!cur)
- {
- obj = root;
- }
- else if(json_is_object(cur))
- {
- if(!key)
- {
- jsonp_error_set(error, line, -1,
- "Objects can't be keys");
- rv = -1;
- goto out;
- }
- obj = json_object_get(cur, key);
- unvisited--;
- key = NULL;
- }
- else if(json_is_array(cur))
+ case 'b':
+ if(!json_is_boolean(root))
{
- obj = json_array_get(cur, array_index);
- unvisited--;
- array_index++;
- }
- else
- {
- assert(0);
+ jsonp_error_set(error, line, column,
+ "Type mismatch! Object (%i) wasn't a boolean.",
+ json_typeof(root));
+ return(-2);
}
+ *va_arg(*ap, int*) = json_is_true(root);
+ return(0);
- /* Make sure we got what we expected */
- if(*fmt=='{' && !json_is_object(obj))
+ case 'f':
+ if(!json_is_number(root))
{
- rv = -2;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Type mismatch! Object (%i) wasn't a real.",
+ json_typeof(root));
+ return(-2);
}
+ *va_arg(*ap, double*) = json_number_value(root);
+ return(0);
- if(*fmt=='[' && !json_is_array(obj))
- {
- rv = -2;
- goto out;
- }
+ case 'O':
+ json_incref(root);
+ /* Fall through */
+
+ case 'o':
+ *va_arg(*ap, json_t**) = root;
+ return(0);
- unvisited += json_is_object(obj) ?
- json_object_size(obj) :
- json_array_size(obj);
+ case 'n':
+ /* Don't actually assign anything; we're just happy
+ * the null turned up as promised in the format
+ * string. */
+ return(0);
- /* Descend */
- stack[depth++] = cur;
- cur = obj;
+ default:
+ jsonp_error_set(error, line, column,
+ "Unknown format character '%c'", *tok);
+ return(-1);
+ }
+ }
- key = NULL;
+ /* Move past container opening token */
+ tok++;
+ while(tok-fmt < size) {
+ switch(*tok) {
+ case '\n':
+ line++;
+ column=0;
break;
+ case ' ': /* Whitespace */
+ break;
- case ']':
- case '}':
+ case ',': /* Element spacer */
+ if(key)
+ {
+ jsonp_error_set(error, line, column,
+ "Expected KEY, got COMMA!");
+ return(-2);
+ }
+ break;
- if(json_is_array(cur) && *fmt!=']')
+ case ':': /* Key/value separator */
+ if(!key)
{
- jsonp_error_set(error, line, -1, "Missing ']'");
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Got key/value separator without "
+ "a key preceding it!");
+ return(-2);
}
- if(json_is_object(cur) && *fmt!='}')
+ if(!json_is_object(root))
{
- jsonp_error_set(error, line, -1, "Missing '}'");
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Got a key/value separator "
+ "(':') outside an object!");
+ return(-2);
}
- if(key)
+ break;
+
+ case ']': /* Close array or object */
+ case '}':
+
+ if(tok-fmt + (ssize_t)strspn(tok+1, " ") != size-1)
{
- jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Unexpected close-bracket '%c'", *tok);
+ return(-2);
}
- if(depth <= 0)
+ if((*tok == ']' && !json_is_array(root)) ||
+ (*tok == '}' && !json_is_object(root)))
{
- jsonp_error_set(error, line, -1, "Unexpected '%c'", *fmt);
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Stray close-array '%c' character", *tok);
+ return(-2);
}
+ return(unvisited);
+
+ case '[':
+ case '{':
- cur = stack[--depth];
+ /* Find corresponding close bracket */
+ etok = tok+1;
+ etok_depth = 1;
+ while(etok_depth) {
- break;
+ if(!*etok || etok-fmt >= size) {
+ jsonp_error_set(error, line, column,
+ "Couldn't find matching close bracket for '%c'",
+ *tok);
+ return(-2);
+ }
- case 's':
- if(!key && json_is_object(cur))
- {
- /* constant string for key */
- key = va_arg(ap, char*);
- break;
+ if(*tok==*etok)
+ etok_depth++;
+ else if(*tok=='[' && *etok==']') {
+ etok_depth--;
+ break;
+ } else if(*tok=='{' && *etok=='}') {
+ etok_depth--;
+ break;
+ }
+
+ etok++;
}
- /* fall through */
- case 'i': /* integer */
- case 'f': /* double-precision float */
- case 'O': /* a json_t object; increments refcount */
- case 'o': /* a json_t object; borrowed reference */
- case 'b': /* boolean */
- case 'n': /* null */
+ /* Recurse */
+ if(json_is_array(root)) {
+ rv = json_vnunpack(json_object_get(root, key),
+ error, etok-tok+1, tok, ap);
+ } else {
+ rv = json_vnunpack(json_array_get(root, array_index++),
+ error, etok-tok+1, tok, ap);
+ }
- /* Fetch object */
- if(!cur)
- {
- obj = root;
+ if(rv < 0) {
+ /* error should already be set */
+ error->column += column-1;
+ error->line += line-1;
+ return(rv);
}
- else if(json_is_object(cur))
+
+ unvisited += rv;
+ column += etok-tok;
+ tok = etok;
+ break;
+
+ case 's':
+ /* Handle strings specially, since they're used for both keys
+ * and values */
+
+ if(json_is_object(root) && !key)
{
+ /* It's a key */
+ key = va_arg(*ap, char*);
+ printf("Got key '%s'\n", key);
+
if(!key)
{
- jsonp_error_set(error, line, -1,
- "Only strings may be used as keys!");
- rv = -1;
- goto out;
+ jsonp_error_set(error, line, column,
+ "Refusing to handle a NULL key");
+ return(-2);
}
-
- obj = json_object_get(cur, key);
- unvisited--;
- key = NULL;
- }
- else if(json_is_array(cur))
- {
- obj = json_array_get(cur, array_index);
- unvisited--;
- array_index++;
+ break;
}
+
+ /* Fall through */
+
+ default:
+
+ /* Fetch the element from the JSON container */
+ if(json_is_object(root))
+ obj = json_object_get(root, key);
else
- {
- jsonp_error_set(error, line, -1,
- "Unsure how to retrieve JSON object '%c'",
- *fmt);
- rv = -1;
- goto out;
+ obj = json_array_get(root, array_index++);
+
+ if(!obj) {
+ jsonp_error_set(error, line, column,
+ "Array/object entry didn't exist!");
+ return(-1);
}
- switch(*fmt)
- {
- case 's':
- if(!json_is_string(obj))
- {
- jsonp_error_set(error, line, -1,
- "Type mismatch! Object wasn't a string.");
- rv = -2;
- goto out;
- }
- *va_arg(ap, const char**) = json_string_value(obj);
- break;
+ rv = json_vnunpack(obj, error, 1, tok, ap);
+ if(rv != 0)
+ return(rv);
- case 'i':
- if(!json_is_integer(obj))
- {
- jsonp_error_set(error, line, -1,
- "Type mismatch! Object wasn't an integer.");
- rv = -2;
- goto out;
- }
- *va_arg(ap, int*) = json_integer_value(obj);
- break;
+ break;
+ }
+ tok++;
+ column++;
+ }
- case 'b':
- if(!json_is_boolean(obj))
- {
- jsonp_error_set(error, line, -1,
- "Type mismatch! Object wasn't a boolean.");
- rv = -2;
- goto out;
- }
- *va_arg(ap, int*) = json_is_true(obj);
- break;
+ /* Whoops -- we didn't match the close bracket! */
+ jsonp_error_set(error, line, column, "Missing close array or object!");
+ return(-2);
+}
- case 'f':
- if(!json_is_number(obj))
- {
- jsonp_error_set(error, line, -1,
- "Type mismatch! Object wasn't a real.");
- rv = -2;
- goto out;
- }
- *va_arg(ap, double*) = json_number_value(obj);
- break;
+json_t *json_pack(json_error_t *error, const char *fmt, ...)
+{
+ va_list ap;
+ json_t *obj;
- case 'O':
- json_incref(obj);
- /* Fall through */
+ jsonp_error_init(error, "");
- case 'o':
- *va_arg(ap, json_t**) = obj;
- break;
+ if(!fmt || !*fmt) {
+ jsonp_error_set(error, 1, 1, "Null or empty format string!");
+ return(NULL);
+ }
- case 'n':
- /* Don't actually assign anything; we're just happy
- * the null turned up as promised in the format
- * string. */
- break;
+ va_start(ap, fmt);
+ obj = json_vnpack(error, strlen(fmt), fmt, &ap);
+ va_end(ap);
- default:
- jsonp_error_set(error, line, -1,
- "Unknown format character '%c'", *fmt);
- rv = -1;
- goto out;
- }
- }
- fmt++;
- }
+ return(obj);
+}
- /* Return 0 if everything was matched; otherwise the number of JSON
- * objects we didn't get to. */
- rv = unvisited;
+int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
-out:
- va_end(ap);
+ jsonp_error_init(error, "");
+
+ if(!fmt || !*fmt) {
+ jsonp_error_set(error, 1, 1, "Null or empty format string!");
+ return(-2);;
+ }
- if(stack)
- free(stack);
+ va_start(ap, fmt);
+ rv = json_vnunpack(root, error, strlen(fmt), fmt, &ap);
+ va_end(ap);
return(rv);
}
{
json_t *value;
int i;
+ json_error_t error;
/*
* Simple, valid json_pack cases
*/
/* true */
- value = json_pack(NULL, "b", 1);
+ value = json_pack(&error, "b", 1);
if(!json_is_true(value))
fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1)
json_decref(value);
/* false */
- value = json_pack(NULL, "b", 0);
+ value = json_pack(&error, "b", 0);
if(!json_is_false(value))
fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1)
json_decref(value);
/* null */
- value = json_pack(NULL, "n");
+ value = json_pack(&error, "n");
if(!json_is_null(value))
fail("json_pack null failed");
if(value->refcount != (ssize_t)-1)
json_decref(value);
/* integer */
- value = json_pack(NULL, "i", 1);
+ value = json_pack(&error, "i", 1);
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack integer failed");
if(value->refcount != (ssize_t)1)
/* real */
- value = json_pack(NULL, "f", 1.0);
+ value = json_pack(&error, "f", 1.0);
if(!json_is_real(value) || json_real_value(value) != 1.0)
fail("json_pack real failed");
if(value->refcount != (ssize_t)1)
json_decref(value);
/* string */
- value = json_pack(NULL, "s", "test");
+ value = json_pack(&error, "s", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string failed");
if(value->refcount != (ssize_t)1)
json_decref(value);
/* empty object */
- value = json_pack(NULL, "{}", 1.0);
+ value = json_pack(&error, "{}", 1.0);
if(!json_is_object(value) || json_object_size(value) != 0)
fail("json_pack empty object failed");
if(value->refcount != (ssize_t)1)
json_decref(value);
/* empty list */
- value = json_pack(NULL, "[]", 1.0);
+ value = json_pack(&error, "[]", 1.0);
if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty list failed");
if(value->refcount != (ssize_t)1)
json_decref(value);
/* non-incref'd object */
- value = json_pack(NULL, "o", json_integer(1));
+ value = json_pack(&error, "o", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (ssize_t)1)
json_decref(value);
/* incref'd object */
- value = json_pack(NULL, "O", json_integer(1));
+ value = json_pack(&error, "O", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (ssize_t)2)
json_decref(value);
/* simple object */
- value = json_pack(NULL, "{s:[]}", "foo");
+ value = json_pack(&error, "{s:[]}", "foo");
if(!json_is_object(value) || json_object_size(value) != 1)
- fail("json_pack object failed");
+ fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foo")))
- fail("json_pack object failed");
+ fail("json_pack array failed");
if(json_object_get(value, "foo")->refcount != (ssize_t)1)
fail("json_pack object refcount failed");
json_decref(value);
/* simple array */
- value = json_pack(NULL, "[i,i,i]", 0, 1, 2);
+ value = json_pack(&error, "[i,i,i]", 0, 1, 2);
if(!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack object failed");
for(i=0; i<3; i++)
}
json_decref(value);
+ /* Whitespace; regular string */
+ value = json_pack(&error, " s ", "test");
+ if(!json_is_string(value) || strcmp("test", json_string_value(value)))
+ fail("json_pack string (with whitespace) failed");
+ json_decref(value);
+
+ /* Whitespace; empty array */
+ value = json_pack(&error, "[ ]");
+ if(!json_is_array(value) || json_array_size(value) != 0)
+ fail("json_pack empty array (with whitespace) failed");
+ json_decref(value);
+
+ /* Whitespace; array */
+ value = json_pack(&error, "[ i , i, i ] ", 1, 2, 3);
+ if(!json_is_array(value) || json_array_size(value) != 3)
+ fail("json_pack array (with whitespace) failed");
+ json_decref(value);
+
/*
* Invalid cases
*/
-
+
/* mismatched open/close array/object */
- if(json_pack(NULL, "[}"))
+ if(json_pack(&error, "[}"))
fail("json_pack failed to catch mismatched '}'");
+ if(error.line != 1 || error.column != 2)
+ fail("json_pack didn't get the error coordinates right!");
- if(json_pack(NULL, "{]"))
+ if(json_pack(&error, "{]"))
fail("json_pack failed to catch mismatched ']'");
+ if(error.line != 1 || error.column != 2)
+ fail("json_pack didn't get the error coordinates right!");
/* missing close array */
- if(json_pack(NULL, "["))
+ if(json_pack(&error, "["))
fail("json_pack failed to catch missing ']'");
+ if(error.line != 1 || error.column != 2)
+ fail("json_pack didn't get the error coordinates right!");
/* missing close object */
- if(json_pack(NULL, "{"))
+ if(json_pack(&error, "{"))
fail("json_pack failed to catch missing '}'");
+ if(error.line != 1 || error.column != 2)
+ fail("json_pack didn't get the error coordinates right!");
/* NULL string */
- if(json_pack(NULL, "s", NULL))
- fail("json_pack failed to catch null string");
+ if(json_pack(&error, "s", NULL))
+ fail("json_pack failed to catch null argument string");
+ if(error.line != 1 || error.column != 1)
+ fail("json_pack didn't get the error coordinates right!");
+
+ /* NULL format */
+ if(json_pack(&error, NULL))
+ fail("json_pack failed to catch NULL format string");
+ if(error.line != 1 || error.column != 1)
+ fail("json_pack didn't get the error coordinates right!");
+
+ /* More complicated checks for row/columns */
+ if(json_pack(&error, "{ {}: s }", "foo"))
+ fail("json_pack failed to catch object as key");
+ if(error.line != 1 || error.column != 3)
+ fail("json_pack didn't get the error coordinates right!");
+
+ if(json_pack(&error, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13))
+ fail("json_pack failed to catch missing ]");
+ if(error.line != 1 || error.column != 13)
+ fail("json_pack didn't get the error coordinates right!");
+
+ if(json_pack(&error, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
+ fail("json_pack failed to catch missing ]");
+ if(error.line != 1 || error.column != 21)
+ fail("json_pack didn't get the error coordinates right!");
return(0);
+
+ //fprintf(stderr, "%i/%i: %s %s\n", error.line, error.column, error.source, error.text);
}
/* vim: ts=4:expandtab:sw=4
double f;
char *s;
+ json_error_t error;
+
/*
* Simple, valid json_pack cases
*/
/* true */
- rv = json_unpack(json_true(), NULL, "b", &i1);
+ rv = json_unpack(json_true(), &error, "b", &i1);
if(rv || !i1)
fail("json_unpack boolean failed");
/* false */
- rv = json_unpack(json_false(), NULL, "b", &i1);
+ rv = json_unpack(json_false(), &error, "b", &i1);
if(rv || i1)
fail("json_unpack boolean failed");
/* null */
- rv = json_unpack(json_null(), NULL, "n");
+ rv = json_unpack(json_null(), &error, "n");
if(rv)
fail("json_unpack null failed");
/* integer */
j = json_integer(1);
- rv = json_unpack(j, NULL, "i", &i1);
+ rv = json_unpack(j, &error, "i", &i1);
if(rv || i1 != 1)
fail("json_unpack integer failed");
json_decref(j);
/* real */
j = json_real(1.0);
- rv = json_unpack(j, NULL, "f", &f);
+ rv = json_unpack(j, &error, "f", &f);
if(rv || f != 1.0)
fail("json_unpack real failed");
json_decref(j);
/* string */
j = json_string("foo");
- rv = json_unpack(j, NULL, "s", &s);
+ rv = json_unpack(j, &error, "s", &s);
if(rv || strcmp(s, "foo"))
fail("json_unpack string failed");
json_decref(j);
/* empty object */
j = json_object();
- rv = json_unpack(j, NULL, "{}");
+ rv = json_unpack(j, &error, "{}");
if(rv)
fail("json_unpack empty object failed");
json_decref(j);
/* empty list */
j = json_array();
- rv = json_unpack(j, NULL, "[]");
+ rv = json_unpack(j, &error, "[]");
if(rv)
fail("json_unpack empty list failed");
json_decref(j);
/* non-incref'd object */
j = json_object();
- rv = json_unpack(j, NULL, "o", &j2);
+ rv = json_unpack(j, &error, "o", &j2);
if(j2 != j || j->refcount != (ssize_t)1)
fail("json_unpack object failed");
json_decref(j);
/* incref'd object */
j = json_object();
- rv = json_unpack(j, NULL, "O", &j2);
+ rv = json_unpack(j, &error, "O", &j2);
if(j2 != j || j->refcount != (ssize_t)2)
fail("json_unpack object failed");
json_decref(j);
json_decref(j);
/* simple object */
- j = json_pack(NULL, "{s:i}", "foo", 1);
- rv = json_unpack(j, NULL, "{s:i}", "foo", &i1);
+ j = json_pack(&error, "{s:i}", "foo", 1);
+ rv = json_unpack(j, &error, "{s:i}", "foo", &i1);
if(rv || i1!=1)
fail("json_unpack simple object failed");
json_decref(j);
/* simple array */
- j = json_pack(NULL, "[iii]", 1, 2, 3);
- rv = json_unpack(j, NULL, "[i,i,i]", &i1, &i2, &i3);
+ j = json_pack(&error, "[iii]", 1, 2, 3);
+ rv = json_unpack(j, &error, "[i,i,i]", &i1, &i2, &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack simple array failed");
json_decref(j);
+ /*
+ * Invalid cases
+ */
+
+ /* mismatched open/close array/object */
+ j = json_pack(&error, "[]");
+ rv = json_unpack(j, &error, "[}");
+ if(!rv)
+ fail("json_unpack failed to catch mismatched ']'");
+ json_decref(j);
+
+ j = json_pack(&error, "{}");
+ rv = json_unpack(j, &error, "{]");
+ if(!rv)
+ fail("json_unpack failed to catch mismatched '}'");
+ json_decref(j);
+
+ /* missing close array */
+ j = json_pack(&error, "[]");
+ rv = json_unpack(j, &error, "[");
+ if(rv >= 0)
+ fail("json_unpack failed to catch missing ']'");
+ json_decref(j);
+
+ /* missing close object */
+ j = json_pack(&error, "{}");
+ rv = json_unpack(j, &error, "{");
+ if(rv >= 0)
+ fail("json_unpack failed to catch missing '}'");
+ json_decref(j);
+
+ /* NULL format string */
+ j = json_pack(&error, "[]");
+ rv =json_unpack(j, &error, NULL);
+ if(rv >= 0)
+ fail("json_unpack failed to catch null format string");
+ json_decref(j);
+
+ /* NULL string pointer */
+ j = json_string("foobie");
+ rv =json_unpack(j, &error, "s", NULL);
+ if(rv >= 0)
+ fail("json_unpack failed to catch null string pointer");
+ json_decref(j);
+
return 0;
+
+ //fprintf(stderr, "%i/%i: %s %s\n", error.line, error.column, error.source, error.text);
}
/* vim: ts=4:expandtab:sw=4