diff options
Diffstat (limited to 'src/jansson/pack_unpack.c')
-rw-r--r-- | src/jansson/pack_unpack.c | 290 |
1 files changed, 224 insertions, 66 deletions
diff --git a/src/jansson/pack_unpack.c b/src/jansson/pack_unpack.c index 39db9b8..af381a5 100644 --- a/src/jansson/pack_unpack.c +++ b/src/jansson/pack_unpack.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> * * Jansson is free software; you can redistribute it and/or modify @@ -12,16 +12,28 @@ #include "utf.h" typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + +typedef struct { const char *start; const char *fmt; - char token; + token_t prev_token; + token_t token; + token_t next_token; json_error_t *error; size_t flags; int line; int column; + size_t pos; } scanner_t; -static const char *type_names[] = { +#define token(scanner) ((scanner)->token.token) + +static const char * const type_names[] = { "object", "array", "string", @@ -34,7 +46,7 @@ static const char *type_names[] = { #define type_name(x) type_names[json_typeof(x)] -static const char *unpack_value_starters = "{[siIbfFOon"; +static const char unpack_value_starters[] = "{[siIbfFOon"; static void scanner_init(scanner_t *s, json_error_t *error, @@ -43,14 +55,28 @@ static void scanner_init(scanner_t *s, json_error_t *error, s->error = error; s->flags = flags; s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); s->line = 1; s->column = 0; + s->pos = 0; } static void next_token(scanner_t *s) { - const char *t = s->fmt; + const char *t; + s->prev_token = s->token; + + if(s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + t = s->fmt; s->column++; + s->pos++; /* skip space and ignored chars */ while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { @@ -61,23 +87,32 @@ static void next_token(scanner_t *s) else s->column++; + s->pos++; t++; } - s->token = *t; + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; t++; s->fmt = t; } +static void prev_token(scanner_t *s) +{ + s->next_token = s->token; + s->token = s->prev_token; +} + static void set_error(scanner_t *s, const char *source, const char *fmt, ...) { va_list ap; - size_t pos; va_start(ap, fmt); - pos = (size_t)(s->fmt - s->start); - jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, + fmt, ap); jsonp_error_set_source(s->error, source); @@ -86,47 +121,135 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...) static json_t *pack(scanner_t *s, va_list *ap); + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, + const char *purpose, size_t *out_len, int *ours) +{ + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + + next_token(s); + t = token(s); + prev_token(s); + + if(t != '#' && t != '%' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if(!str) { + set_error(s, "<args>", "NULL string argument"); + return NULL; + } + + length = strlen(str); + + if(!utf8_check_string(str, length)) { + set_error(s, "<args>", "Invalid UTF-8 %s", purpose); + return NULL; + } + + *out_len = length; + *ours = 0; + return (char *)str; + } + + strbuffer_init(&strbuff); + + while(1) { + str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "<args>", "NULL string argument"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + + if(token(s) == '#') { + length = va_arg(*ap, int); + } + else if(token(s) == '%') { + length = va_arg(*ap, size_t); + } + else { + prev_token(s); + length = strlen(str); + } + + if(strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "<internal>", "Out of memory"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + if(token(s) != '+') { + prev_token(s); + break; + } + } + + if(!utf8_check_string(strbuff.value, strbuff.length)) { + set_error(s, "<args>", "Invalid UTF-8 %s", purpose); + strbuffer_close(&strbuff); + return NULL; + } + + *out_len = strbuff.length; + *ours = 1; + return strbuffer_steal_value(&strbuff); +} + static json_t *pack_object(scanner_t *s, va_list *ap) { json_t *object = json_object(); next_token(s); - while(s->token != '}') { - const char *key; + while(token(s) != '}') { + char *key; + size_t len; + int ours; json_t *value; - if(!s->token) { + if(!token(s)) { set_error(s, "<format>", "Unexpected end of format string"); goto error; } - if(s->token != 's') { - set_error(s, "<format>", "Expected format 's', got '%c'", s->token); - goto error; - } - - key = va_arg(*ap, const char *); - if(!key) { - set_error(s, "<args>", "NULL object key"); + if(token(s) != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); goto error; } - if(!utf8_check_string(key, -1)) { - set_error(s, "<args>", "Invalid UTF-8 in object key"); + key = read_string(s, ap, "object key", &len, &ours); + if(!key) goto error; - } next_token(s); value = pack(s, ap); - if(!value) + if(!value) { + if(ours) + jsonp_free(key); + goto error; + } if(json_object_set_new_nocheck(object, key, value)) { + if(ours) + jsonp_free(key); + set_error(s, "<internal>", "Unable to add key \"%s\"", key); goto error; } + if(ours) + jsonp_free(key); + next_token(s); } @@ -142,10 +265,10 @@ static json_t *pack_array(scanner_t *s, va_list *ap) json_t *array = json_array(); next_token(s); - while(s->token != ']') { + while(token(s) != ']') { json_t *value; - if(!s->token) { + if(!token(s)) { set_error(s, "<format>", "Unexpected end of format string"); goto error; } @@ -170,7 +293,7 @@ error: static json_t *pack(scanner_t *s, va_list *ap) { - switch(s->token) { + switch(token(s)) { case '{': return pack_object(s, ap); @@ -179,16 +302,18 @@ static json_t *pack(scanner_t *s, va_list *ap) case 's': /* string */ { - const char *str = va_arg(*ap, const char *); - if(!str) { - set_error(s, "<args>", "NULL string argument"); - return NULL; - } - if(!utf8_check_string(str, -1)) { - set_error(s, "<args>", "Invalid UTF-8 string"); + char *str; + size_t len; + int ours; + + str = read_string(s, ap, "string", &len, &ours); + if(!str) return NULL; - } - return json_string_nocheck(str); + + if (ours) + return jsonp_stringn_nocheck_own(str, len); + else + return json_stringn_nocheck(str, len); } case 'n': /* null */ @@ -214,7 +339,7 @@ static json_t *pack(scanner_t *s, va_list *ap) default: set_error(s, "<format>", "Unexpected format character '%c'", - s->token); + token(s)); return NULL; } } @@ -225,6 +350,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) { int ret = -1; int strict = 0; + int gotopt = 0; /* Use a set (emulated by a hashtable) to check that all object keys are accessed. Checking that the correct number of keys @@ -245,30 +371,30 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) } next_token(s); - while(s->token != '}') { + while(token(s) != '}') { const char *key; json_t *value; int opt = 0; if(strict != 0) { set_error(s, "<format>", "Expected '}' after '%c', got '%c'", - (strict == 1 ? '!' : '*'), s->token); + (strict == 1 ? '!' : '*'), token(s)); goto out; } - if(!s->token) { + if(!token(s)) { set_error(s, "<format>", "Unexpected end of format string"); goto out; } - if(s->token == '!' || s->token == '*') { - strict = (s->token == '!' ? 1 : -1); + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); next_token(s); continue; } - if(s->token != 's') { - set_error(s, "<format>", "Expected format 's', got '%c'", s->token); + if(token(s) != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", token(s)); goto out; } @@ -280,8 +406,8 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) next_token(s); - if(s->token == '?') { - opt = 1; + if(token(s) == '?') { + opt = gotopt = 1; next_token(s); } @@ -307,10 +433,26 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) if(strict == 0 && (s->flags & JSON_STRICT)) strict = 1; - if(root && strict == 1 && key_set.size != json_object_size(root)) { - long diff = (long)json_object_size(root) - (long)key_set.size; - set_error(s, "<validation>", "%li object item(s) left unpacked", diff); - goto out; + if(root && strict == 1) { + /* We need to check that all non optional items have been parsed */ + const char *key; + json_t *value; + long unpacked = 0; + if (gotopt) { + /* We have optional keys, we need to iter on each key */ + json_object_foreach(root, key, value) { + if(!hashtable_get(&key_set, key)) { + unpacked++; + } + } + } else { + /* No optional keys, we can just compare the number of items */ + unpacked = (long)json_object_size(root) - (long)key_set.size; + } + if (unpacked) { + set_error(s, "<validation>", "%li object item(s) left unpacked", unpacked); + goto out; + } } ret = 0; @@ -331,30 +473,30 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) } next_token(s); - while(s->token != ']') { + while(token(s) != ']') { json_t *value; if(strict != 0) { set_error(s, "<format>", "Expected ']' after '%c', got '%c'", (strict == 1 ? '!' : '*'), - s->token); + token(s)); return -1; } - if(!s->token) { + if(!token(s)) { set_error(s, "<format>", "Unexpected end of format string"); return -1; } - if(s->token == '!' || s->token == '*') { - strict = (s->token == '!' ? 1 : -1); + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); next_token(s); continue; } - if(!strchr(unpack_value_starters, s->token)) { + if(!strchr(unpack_value_starters, token(s))) { set_error(s, "<format>", "Unexpected format character '%c'", - s->token); + token(s)); return -1; } @@ -392,7 +534,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) static int unpack(scanner_t *s, json_t *root, va_list *ap) { - switch(s->token) + switch(token(s)) { case '{': return unpack_object(s, root, ap); @@ -408,16 +550,32 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) } if(!(s->flags & JSON_VALIDATE_ONLY)) { - const char **target; + const char **str_target; + size_t *len_target = NULL; - target = va_arg(*ap, const char **); - if(!target) { + str_target = va_arg(*ap, const char **); + if(!str_target) { set_error(s, "<args>", "NULL string argument"); return -1; } - if(root) - *target = json_string_value(root); + next_token(s); + + if(token(s) == '%') { + len_target = va_arg(*ap, size_t *); + if(!len_target) { + set_error(s, "<args>", "NULL string length argument"); + return -1; + } + } + else + prev_token(s); + + if(root) { + *str_target = json_string_value(root); + if(len_target) + *len_target = json_string_length(root); + } } return 0; @@ -521,7 +679,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) default: set_error(s, "<format>", "Unexpected format character '%c'", - s->token); + token(s)); return -1; } } @@ -551,7 +709,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags, return NULL; next_token(&s); - if(s.token) { + if(token(&s)) { json_decref(value); set_error(&s, "<format>", "Garbage after format string"); return NULL; @@ -614,7 +772,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, va_end(ap_copy); next_token(&s); - if(s.token) { + if(token(&s)) { set_error(&s, "<format>", "Garbage after format string"); return -1; } |