X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fpack_unpack.c;h=5001764e19ec1073bd1c4058bb4ff5593ce5df72;hb=42b651ef56168937d0046bde9205f1014a940941;hp=e785357f77bdcb0fbc6f5be29dd44ae0643fd2da;hpb=908c62f3278b682d879e8ffbdb71bcec60846977;p=jansson.git diff --git a/src/pack_unpack.c b/src/pack_unpack.c index e785357..5001764 100644 --- a/src/pack_unpack.c +++ b/src/pack_unpack.c @@ -6,10 +6,13 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include #include #include "jansson_private.h" +#include "utf.h" typedef struct { + const char *start; const char *fmt; char token; json_error_t *error; @@ -31,6 +34,19 @@ static const char *type_names[] = { #define type_name(x) type_names[json_typeof(x)] +static const char *unpack_value_starters = "{[siIbfFOon"; + + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + s->line = 1; + s->column = 0; +} + static void next_token(scanner_t *s) { const char *t = s->fmt; @@ -54,11 +70,17 @@ static void next_token(scanner_t *s) s->fmt = t; } -static void set_error(scanner_t *s, const char *fmt, ...) +static void set_error(scanner_t *s, const char *source, const char *fmt, ...) { va_list ap; + size_t pos; va_start(ap, fmt); - jsonp_error_vset(s->error, s->line, s->column, fmt, ap); + + pos = (size_t)(s->fmt - s->start); + jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); + + jsonp_error_set_source(s->error, source); + va_end(ap); } @@ -74,18 +96,23 @@ static json_t *pack_object(scanner_t *s, va_list *ap) json_t *value; if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); goto error; } if(s->token != 's') { - set_error(s, "Expected format 's', got '%c'\n", *s->fmt); + set_error(s, "", "Expected format 's', got '%c'", s->token); goto error; } key = va_arg(*ap, const char *); if(!key) { - set_error(s, "NULL object key"); + set_error(s, "", "NULL object key"); + goto error; + } + + if(!utf8_check_string(key, -1)) { + set_error(s, "", "Invalid UTF-8 in object key"); goto error; } @@ -95,8 +122,8 @@ static json_t *pack_object(scanner_t *s, va_list *ap) if(!value) goto error; - if(json_object_set_new(object, key, value)) { - set_error(s, "Unable to add key \"%s\"", key); + if(json_object_set_new_nocheck(object, key, value)) { + set_error(s, "", "Unable to add key \"%s\"", key); goto error; } @@ -119,7 +146,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap) json_t *value; if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); goto error; } @@ -128,7 +155,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap) goto error; if(json_array_append_new(array, value)) { - set_error(s, "Unable to append to array"); + set_error(s, "", "Unable to append to array"); goto error; } @@ -154,10 +181,14 @@ static json_t *pack(scanner_t *s, va_list *ap) { const char *str = va_arg(*ap, const char *); if(!str) { - set_error(s, "NULL string"); + set_error(s, "", "NULL string argument"); return NULL; } - return json_string(str); + if(!utf8_check_string(str, -1)) { + set_error(s, "", "Invalid UTF-8 string"); + return NULL; + } + return json_string_nocheck(str); } case 'n': /* null */ @@ -182,7 +213,8 @@ static json_t *pack(scanner_t *s, va_list *ap) return va_arg(*ap, json_t *); default: - set_error(s, "Unrecognized format character '%c'", s->token); + set_error(s, "", "Unexpected format character '%c'", + s->token); return NULL; } } @@ -192,26 +224,24 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap); static int unpack_object(scanner_t *s, json_t *root, va_list *ap) { int ret = -1; - int wildcard = 0; + int strict = 0; /* Use a set (emulated by a hashtable) to check that all object keys are accessed. Checking that the correct number of keys were accessed is not enough, as the same key can be unpacked multiple times. */ - hashtable_t *key_set; + hashtable_t key_set; - if(!(s->flags & JSON_UNPACK_ONLY)) { - key_set = hashtable_create(jsonp_hash_key, jsonp_key_equal, NULL, NULL); - if(!key_set) { - set_error(s, "Out of memory"); - return -1; - } + if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) { + set_error(s, "", "Out of memory"); + return -1; } if(!json_is_object(root)) { - set_error(s, "Expected object, got %s", type_name(root)); - goto error; + set_error(s, "", "Expected object, got %s", + type_name(root)); + goto out; } next_token(s); @@ -219,70 +249,72 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) const char *key; json_t *value; - if(wildcard) { - set_error(s, "Expected '}' after '*', got '%c'", s->token); - goto error; + if(strict != 0) { + set_error(s, "", "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), s->token); + goto out; } if(!s->token) { - set_error(s, "Unexpected end of format string"); - goto error; + set_error(s, "", "Unexpected end of format string"); + goto out; } - if(s->token == '*') { - wildcard = 1; + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); next_token(s); continue; } if(s->token != 's') { - set_error(s, "Expected format 's', got '%c'\n", *s->fmt); - goto error; + set_error(s, "", "Expected format 's', got '%c'", s->token); + goto out; } key = va_arg(*ap, const char *); if(!key) { - set_error(s, "NULL object key"); - goto error; + set_error(s, "", "NULL object key"); + goto out; } next_token(s); value = json_object_get(root, key); - if(unpack(s, value, ap)) - goto error; + if(!value) { + set_error(s, "", "Object item not found: %s", key); + goto out; + } - if(!(s->flags & JSON_UNPACK_ONLY)) - hashtable_set(key_set, (void *)key, NULL); + if(unpack(s, value, ap)) + goto out; + hashtable_set(&key_set, (void *)key, NULL); next_token(s); } - if(s->flags & JSON_UNPACK_ONLY) - wildcard = 1; + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; - if(!wildcard && key_set->size != json_object_size(root)) { - long diff = (long)json_object_size(root) - (long)key_set->size; - set_error(s, "%li object items left unpacked", diff); - goto error; + if(strict == 1 && key_set.size != json_object_size(root)) { + long diff = (long)json_object_size(root) - (long)key_set.size; + set_error(s, "", "%li object item(s) left unpacked", diff); + goto out; } ret = 0; -error: - if(!(s->flags & JSON_UNPACK_ONLY)) - hashtable_destroy(key_set); - +out: + hashtable_close(&key_set); return ret; } static int unpack_array(scanner_t *s, json_t *root, va_list *ap) { size_t i = 0; - int wildcard = 0; + int strict = 0; if(!json_is_array(root)) { - set_error(s, "Expected array, got %s", type_name(root)); + set_error(s, "", "Expected array, got %s", type_name(root)); return -1; } next_token(s); @@ -290,25 +322,34 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) while(s->token != ']') { json_t *value; - if(wildcard) { - set_error(s, "Expected ']' after '*', got '%c'", s->token); + if(strict != 0) { + set_error(s, "", "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + s->token); return -1; } if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); return -1; } - if(s->token == '*') { - wildcard = 1; + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); next_token(s); continue; } + if(!strchr(unpack_value_starters, s->token)) { + set_error(s, "", "Unexpected format character '%c'", + s->token); + return -1; + } + value = json_array_get(root, i); if(!value) { - set_error(s, "Array index %lu out of range", (unsigned long)i); + set_error(s, "", "Array index %lu out of range", + (unsigned long)i); return -1; } @@ -319,12 +360,12 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) i++; } - if(s->flags & JSON_UNPACK_ONLY) - wildcard = 1; + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; - if(!wildcard && i != json_array_size(root)) { + if(strict == 1 && i != json_array_size(root)) { long diff = (long)json_array_size(root) - (long)i; - set_error(s, "%li array items left upacked", diff); + set_error(s, "", "%li array item(s) left unpacked", diff); return -1; } @@ -343,7 +384,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 's': if(!json_is_string(root)) { - set_error(s, "Expected string, got %s", type_name(root)); + set_error(s, "", "Expected string, got %s", + type_name(root)); return -1; } @@ -352,7 +394,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) str = va_arg(*ap, const char **); if(!str) { - set_error(s, "NULL string"); + set_error(s, "", "NULL string argument"); return -1; } @@ -362,7 +404,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'i': if(!json_is_integer(root)) { - set_error(s, "Expected integer, got %s", type_name(root)); + set_error(s, "", "Expected integer, got %s", + type_name(root)); return -1; } @@ -373,7 +416,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'I': if(!json_is_integer(root)) { - set_error(s, "Expected integer, got %s", type_name(root)); + set_error(s, "", "Expected integer, got %s", + type_name(root)); return -1; } @@ -384,7 +428,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'b': if(!json_is_boolean(root)) { - set_error(s, "Expected true or false, got %s", type_name(root)); + set_error(s, "", "Expected true or false, got %s", + type_name(root)); return -1; } @@ -395,7 +440,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'f': if(!json_is_real(root)) { - set_error(s, "Expected real, got %s", type_name(root)); + set_error(s, "", "Expected real, got %s", + type_name(root)); return -1; } @@ -406,7 +452,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'F': if(!json_is_number(root)) { - set_error(s, "Expected real or integer, got %s", + set_error(s, "", "Expected real or integer, got %s", type_name(root)); return -1; } @@ -430,13 +476,15 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'n': /* Never assign, just validate */ if(!json_is_null(root)) { - set_error(s, "Expected null, got %s", type_name(root)); + set_error(s, "", "Expected null, got %s", + type_name(root)); return -1; } return 0; default: - set_error(s, "Unknown format character '%c'", s->token); + set_error(s, "", "Unexpected format character '%c'", + s->token); return -1; } } @@ -445,28 +493,30 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) { scanner_t s; + va_list ap_copy; json_t *value; - jsonp_error_init(error, ""); - if(!fmt || !*fmt) { - jsonp_error_set(error, 1, 1, "Null or empty format string"); + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); return NULL; } + jsonp_error_init(error, NULL); - s.error = error; - s.flags = flags; - s.fmt = fmt; - s.line = 1; - s.column = 0; - + scanner_init(&s, error, flags, fmt); next_token(&s); - value = pack(&s, &ap); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + if(!value) + return NULL; next_token(&s); if(s.token) { json_decref(value); - set_error(&s, "Garbage after format string"); + set_error(&s, "", "Garbage after format string"); return NULL; } @@ -501,28 +551,34 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap) { scanner_t s; + va_list ap_copy; - jsonp_error_init(error, ""); + if(!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL root value"); + return -1; + } if(!fmt || !*fmt) { - jsonp_error_set(error, 1, 1, "Null or empty format string"); + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); return -1; } + jsonp_error_init(error, NULL); - s.error = error; - s.flags = flags; - s.fmt = fmt; - s.line = 1; - s.column = 0; - + scanner_init(&s, error, flags, fmt); next_token(&s); - if(unpack(&s, root, &ap)) + va_copy(ap_copy, ap); + if(unpack(&s, root, &ap_copy)) { + va_end(ap_copy); return -1; + } + va_end(ap_copy); next_token(&s); if(s.token) { - set_error(&s, "Garbage after format string"); + set_error(&s, "", "Garbage after format string"); return -1; }