commit
46e27ae6d5
@ -304,6 +304,13 @@ U+10FFFF are allowed.
|
||||
Returns a new JSON string, or *NULL* on error. *value* must be a
|
||||
valid UTF-8 encoded Unicode string.
|
||||
|
||||
.. function:: json_t *json_stringn(const char *value, size_t len)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Like :func:`json_string`, but with explicit length, so *value* may
|
||||
contain null characters or not be null terminated.
|
||||
|
||||
.. function:: json_t *json_string_nocheck(const char *value)
|
||||
|
||||
.. refcounting:: new
|
||||
@ -312,6 +319,13 @@ U+10FFFF are allowed.
|
||||
UTF-8. Use this function only if you are certain that this really
|
||||
is the case (e.g. you have already checked it by other means).
|
||||
|
||||
.. function:: json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Like :func:`json_string_nocheck`, but with explicit length, so
|
||||
*value* may contain null characters or not be null terminated.
|
||||
|
||||
.. function:: const char *json_string_value(const json_t *string)
|
||||
|
||||
Returns the associated value of *string* as a null terminated UTF-8
|
||||
@ -321,12 +335,22 @@ U+10FFFF are allowed.
|
||||
the user. It is valid as long as *string* exists, i.e. as long as
|
||||
its reference count has not dropped to zero.
|
||||
|
||||
.. function:: size_t json_string_length(const json_t *string)
|
||||
|
||||
Returns the length of *string* as a UTF-8 encoded string, or zero
|
||||
if *string* is not a JSON string.
|
||||
|
||||
.. function:: int json_string_set(const json_t *string, const char *value)
|
||||
|
||||
Sets the associated value of *string* to *value*. *value* must be a
|
||||
valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. function:: int json_string_setn(json_t *string, const char *value, size_t len)
|
||||
|
||||
Like :func:`json_string_set`, but with explicit length, so *value*
|
||||
may contain null characters or not be null terminated.
|
||||
|
||||
.. function:: int json_string_set_nocheck(const json_t *string, const char *value)
|
||||
|
||||
Like :func:`json_string_set`, but doesn't check that *value* is
|
||||
@ -334,6 +358,11 @@ U+10FFFF are allowed.
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. function:: int json_string_setn_nocheck(json_t *string, const char *value, size_t len)
|
||||
|
||||
Like :func:`json_string_set_nocheck`, but with explicit length,
|
||||
so *value* may contain null characters or not be null terminated.
|
||||
|
||||
|
||||
Number
|
||||
======
|
||||
|
20
src/dump.c
20
src/dump.c
@ -65,24 +65,25 @@ static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
|
||||
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
|
||||
{
|
||||
const char *pos, *end;
|
||||
const char *pos, *end, *lim;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = pos = str;
|
||||
lim = str + len;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end)
|
||||
while(end < lim)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
end = utf8_iterate(pos, lim - pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
@ -126,7 +127,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
sprintf(seq, "\\u%04X", codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
@ -139,7 +140,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
sprintf(seq, "\\u%04X\\u%04X", first, last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
@ -215,7 +216,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), dump, data, flags);
|
||||
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
@ -336,7 +337,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
@ -372,8 +373,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
const char *key = json_object_iter_key(iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
|
@ -67,23 +67,25 @@ typedef long json_int_t;
|
||||
#endif
|
||||
|
||||
#define json_typeof(json) ((json)->type)
|
||||
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) (json && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
|
||||
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE)
|
||||
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
|
||||
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
|
||||
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL)
|
||||
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
|
||||
|
||||
/* construction, destruction, reference counting */
|
||||
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_stringn(const char *value, size_t len);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len);
|
||||
json_t *json_integer(json_int_t value);
|
||||
json_t *json_real(double value);
|
||||
json_t *json_true(void);
|
||||
@ -199,16 +201,18 @@ int json_array_insert(json_t *array, size_t ind, json_t *value)
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
size_t json_string_length(const json_t *string);
|
||||
json_int_t json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_setn(json_t *string, const char *value, size_t len);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
|
||||
int json_integer_set(json_t *integer, json_int_t value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
|
||||
/* pack, unpack */
|
||||
|
||||
json_t *json_pack(const char *fmt, ...);
|
||||
|
@ -49,6 +49,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
size_t length;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
@ -67,6 +68,10 @@ typedef struct {
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
/* Create a string by taking ownership of an existing buffer */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
|
||||
|
||||
/* Error message formatting */
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
@ -83,6 +88,7 @@ void* jsonp_malloc(size_t size);
|
||||
void jsonp_free(void *ptr);
|
||||
char *jsonp_strndup(const char *str, size_t length);
|
||||
char *jsonp_strdup(const char *str);
|
||||
char *jsonp_strndup(const char *str, size_t len);
|
||||
|
||||
/* Windows compatibility */
|
||||
#ifdef _WIN32
|
||||
|
80
src/load.c
80
src/load.c
@ -63,7 +63,10 @@ typedef struct {
|
||||
strbuffer_t saved_text;
|
||||
int token;
|
||||
union {
|
||||
char *string;
|
||||
struct {
|
||||
char *val;
|
||||
size_t len;
|
||||
} string;
|
||||
json_int_t integer;
|
||||
double real;
|
||||
} value;
|
||||
@ -279,6 +282,13 @@ static void lex_save_cached(lex_t *lex)
|
||||
}
|
||||
}
|
||||
|
||||
static void lex_free_string(lex_t *lex)
|
||||
{
|
||||
jsonp_free(lex->value.string.val);
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
@ -297,7 +307,7 @@ static int32_t decode_unicode_escape(const char *str)
|
||||
else if(l_isupper(c))
|
||||
value += c - 'A' + 10;
|
||||
else
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
@ -310,7 +320,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
char *t;
|
||||
int i;
|
||||
|
||||
lex->value.string = NULL;
|
||||
lex->value.string.val = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
@ -365,14 +375,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
|
||||
are converted to 4 bytes
|
||||
*/
|
||||
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!lex->value.string) {
|
||||
t = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!t) {
|
||||
/* this is not very nice, since TOKEN_INVALID is returned */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the target */
|
||||
t = lex->value.string;
|
||||
lex->value.string.val = t;
|
||||
|
||||
/* + 1 to skip the " */
|
||||
p = strbuffer_value(&lex->saved_text) + 1;
|
||||
@ -381,17 +389,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == '\\') {
|
||||
p++;
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
size_t length;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
if(value < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
if(value2 < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
@ -420,16 +435,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
|
||||
goto out;
|
||||
}
|
||||
else if(value == 0)
|
||||
{
|
||||
error_set(error, lex, "\\u0000 is not allowed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(utf8_encode(value, buffer, &length))
|
||||
if(utf8_encode(value, t, &length))
|
||||
assert(0);
|
||||
|
||||
memcpy(t, buffer, length);
|
||||
t += length;
|
||||
}
|
||||
else {
|
||||
@ -451,11 +459,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
*(t++) = *(p++);
|
||||
}
|
||||
*t = '\0';
|
||||
lex->value.string.len = t - lex->value.string.val;
|
||||
lex->token = TOKEN_STRING;
|
||||
return;
|
||||
|
||||
out:
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
}
|
||||
|
||||
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
|
||||
@ -571,10 +580,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
jsonp_free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
if(lex->token == TOKEN_STRING)
|
||||
lex_free_string(lex);
|
||||
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
@ -635,13 +642,14 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
static char *lex_steal_string(lex_t *lex, size_t *out_len)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
result = lex->value.string.val;
|
||||
*out_len = lex->value.string.len;
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -659,7 +667,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static void lex_close(lex_t *lex)
|
||||
{
|
||||
if(lex->token == TOKEN_STRING)
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
strbuffer_close(&lex->saved_text);
|
||||
}
|
||||
|
||||
@ -680,6 +688,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
while(1) {
|
||||
char *key;
|
||||
size_t len;
|
||||
json_t *value;
|
||||
|
||||
if(lex->token != TOKEN_STRING) {
|
||||
@ -687,9 +696,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = lex_steal_string(lex);
|
||||
key = lex_steal_string(lex, &len);
|
||||
if(!key)
|
||||
return NULL;
|
||||
if (memchr(key, len, '\0')) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "nul char in object key not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
@ -788,7 +802,11 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
switch(lex->token) {
|
||||
case TOKEN_STRING: {
|
||||
json = json_string_nocheck(lex->value.string);
|
||||
json = jsonp_stringn_nocheck_own(lex->value.string.val, lex->value.string.len);
|
||||
if(json) {
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
17
src/memory.c
17
src/memory.c
@ -12,6 +12,10 @@
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
|
||||
/* C89 allows these to be macros */
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
/* memory function pointers */
|
||||
static json_malloc_t do_malloc = malloc;
|
||||
static json_free_t do_free = free;
|
||||
@ -34,18 +38,19 @@ void jsonp_free(void *ptr)
|
||||
|
||||
char *jsonp_strdup(const char *str)
|
||||
{
|
||||
char *new_str;
|
||||
size_t len;
|
||||
return jsonp_strndup(str, strlen(str));
|
||||
}
|
||||
|
||||
len = strlen(str);
|
||||
if(len == (size_t)-1)
|
||||
return NULL;
|
||||
char *jsonp_strndup(const char *str, size_t len)
|
||||
{
|
||||
char *new_str;
|
||||
|
||||
new_str = jsonp_malloc(len + 1);
|
||||
if(!new_str)
|
||||
return NULL;
|
||||
|
||||
memcpy(new_str, str, len + 1);
|
||||
memcpy(new_str, str, len);
|
||||
new_str[len] = '\0';
|
||||
return new_str;
|
||||
}
|
||||
|
||||
|
@ -125,19 +125,18 @@ 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, int *ours)
|
||||
const char *purpose, size_t *out_len, int *ours)
|
||||
{
|
||||
char t;
|
||||
strbuffer_t strbuff;
|
||||
const char *str;
|
||||
size_t length;
|
||||
char *result;
|
||||
|
||||
next_token(s);
|
||||
t = token(s);
|
||||
prev_token(s);
|
||||
|
||||
if(t != '#' && t != '+') {
|
||||
if(t != '#' && t != '%' && t != '+') {
|
||||
/* Optimize the simple case */
|
||||
str = va_arg(*ap, const char *);
|
||||
|
||||
@ -146,11 +145,14 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(str, -1)) {
|
||||
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;
|
||||
}
|
||||
@ -170,6 +172,9 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
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);
|
||||
@ -188,15 +193,15 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
}
|
||||
}
|
||||
|
||||
result = strbuffer_steal_value(&strbuff);
|
||||
|
||||
if(!utf8_check_string(result, -1)) {
|
||||
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 result;
|
||||
return strbuffer_steal_value(&strbuff);
|
||||
}
|
||||
|
||||
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
@ -206,6 +211,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
|
||||
while(token(s) != '}') {
|
||||
char *key;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *value;
|
||||
|
||||
@ -219,15 +225,19 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = read_string(s, ap, "object key", &ours);
|
||||
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)
|
||||
@ -290,20 +300,20 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
case '[':
|
||||
return pack_array(s, ap);
|
||||
|
||||
case 's': { /* string */
|
||||
case 's': /* string */
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *result;
|
||||
|
||||
str = read_string(s, ap, "string", &ours);
|
||||
str = read_string(s, ap, "string", &len, &ours);
|
||||
if(!str)
|
||||
return NULL;
|
||||
|
||||
result = json_string_nocheck(str);
|
||||
if (ours)
|
||||
jsonp_free(str);
|
||||
|
||||
return result;
|
||||
return jsonp_stringn_nocheck_own(str, len);
|
||||
else
|
||||
return json_stringn_nocheck(str, len);
|
||||
}
|
||||
|
||||
case 'n': /* null */
|
||||
@ -523,16 +533,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;
|
||||
|
||||
|
25
src/utf.c
25
src/utf.c
@ -8,7 +8,7 @@
|
||||
#include <string.h>
|
||||
#include "utf.h"
|
||||
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utf8_check_first(char byte)
|
||||
size_t utf8_check_first(char byte)
|
||||
{
|
||||
unsigned char u = (unsigned char)byte;
|
||||
|
||||
@ -80,9 +80,9 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
size_t count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
if(!bufsize)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
if(count > bufsize || !utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -162,16 +162,13 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
int utf8_check_string(const char *string, size_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(length == -1)
|
||||
length = strlen(string);
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
int count = utf8_check_first(string[i]);
|
||||
size_t count = utf8_check_first(string[i]);
|
||||
if(count == 0)
|
||||
return 0;
|
||||
else if(count > 1)
|
||||
|
10
src/utf.h
10
src/utf.h
@ -28,12 +28,12 @@ typedef int int32_t;
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
int utf8_encode(int codepoint, char *buffer, size_t *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
size_t utf8_check_first(char byte);
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
int utf8_check_string(const char *string, size_t length);
|
||||
|
||||
#endif
|
||||
|
108
src/value.c
108
src/value.c
@ -108,7 +108,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
if(!key || !utf8_check_string(key, strlen(key)))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@ -620,33 +620,68 @@ static json_t *json_array_deep_copy(const json_t *array)
|
||||
|
||||
/*** string ***/
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
static json_t *string_create(const char *value, size_t len, int own)
|
||||
{
|
||||
char *v;
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
|
||||
string->value = jsonp_strdup(value);
|
||||
if(!string->value) {
|
||||
jsonp_free(string);
|
||||
if(own)
|
||||
v = (char *)value;
|
||||
else {
|
||||
v = jsonp_strndup(value, len);
|
||||
if(!v)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string) {
|
||||
if(!own)
|
||||
jsonp_free(v);
|
||||
return NULL;
|
||||
}
|
||||
json_init(&string->json, JSON_STRING);
|
||||
string->value = v;
|
||||
string->length = len;
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
return string_create(value, strlen(value), 0);
|
||||
}
|
||||
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 0);
|
||||
}
|
||||
|
||||
/* this is private; "steal" is not a public API concept */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 1);
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_stringn(value, strlen(value));
|
||||
}
|
||||
|
||||
json_t *json_stringn(const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return NULL;
|
||||
|
||||
return json_stringn_nocheck(value, len);
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *json)
|
||||
@ -657,7 +692,23 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
size_t json_string_length(const json_t *json)
|
||||
{
|
||||
if(!json_is_string(json))
|
||||
return 0;
|
||||
|
||||
return json_to_string(json)->length;
|
||||
}
|
||||
|
||||
int json_string_set_nocheck(json_t *json, const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
@ -665,7 +716,7 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
dup = jsonp_strndup(value, len);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
@ -678,10 +729,18 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
|
||||
int json_string_set(json_t *json, const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_set_nocheck(json, value);
|
||||
return json_string_setn(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, len);
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
@ -692,12 +751,25 @@ static void json_delete_string(json_string_t *string)
|
||||
|
||||
static int json_string_equal(json_t *string1, json_t *string2)
|
||||
{
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
json_string_t *s1, *s2;
|
||||
|
||||
if(!json_is_string(string1) || !json_is_string(string2))
|
||||
return 0;
|
||||
|
||||
s1 = json_to_string(string1);
|
||||
s2 = json_to_string(string2);
|
||||
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
|
||||
}
|
||||
|
||||
static json_t *json_string_copy(const json_t *string)
|
||||
{
|
||||
return json_string_nocheck(json_string_value(string));
|
||||
json_string_t *s;
|
||||
|
||||
if(!json_is_string(string))
|
||||
return NULL;
|
||||
|
||||
s = json_to_string(string);
|
||||
return json_stringn_nocheck(s->value, s->length);
|
||||
}
|
||||
|
||||
|
||||
|
@ -86,8 +86,9 @@ static void read_conf(FILE *conffile)
|
||||
char *buffer, *line, *val;
|
||||
|
||||
buffer = loadfile(conffile);
|
||||
line = strtok(buffer, "\r\n");
|
||||
while (line) {
|
||||
for (line = strtok(buffer, "\r\n"); line; line = strtok(NULL, "\r\n")) {
|
||||
if (!strncmp(line, "export ", 7))
|
||||
continue;
|
||||
val = strchr(line, '=');
|
||||
if (!val) {
|
||||
printf("invalid configuration line\n");
|
||||
@ -107,8 +108,6 @@ static void read_conf(FILE *conffile)
|
||||
conf.sort_keys = atoi(val);
|
||||
if (!strcmp(line, "STRIP"))
|
||||
conf.strip = atoi(val);
|
||||
|
||||
line = strtok(NULL, "\r\n");
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
@ -83,7 +83,7 @@ static void run_tests()
|
||||
fail("json_pack string refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length */
|
||||
/* string and length (int) */
|
||||
value = json_pack("s#", "test asdf", 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length failed");
|
||||
@ -91,14 +91,30 @@ static void run_tests()
|
||||
fail("json_pack string and length refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length, non-NUL terminated string */
|
||||
value = json_pack("s#", buffer, 4);
|
||||
/* string and length (size_t) */
|
||||
value = json_pack("s%", "test asdf", (size_t)4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (int), non-NUL terminated string */
|
||||
value = json_pack("s#", buffer, 4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length (int) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length (int) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string and length (size_t), non-NUL terminated string */
|
||||
value = json_pack("s%", buffer, (size_t)4);
|
||||
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||
fail("json_pack string and length (size_t) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string and length (size_t) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation */
|
||||
value = json_pack("s++", "te", "st", "ing");
|
||||
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
|
||||
@ -107,12 +123,20 @@ static void run_tests()
|
||||
fail("json_pack string concatenation refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation and length */
|
||||
/* string concatenation and length (int) */
|
||||
value = json_pack("s#+#+", "test", 1, "test", 2, "test");
|
||||
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
|
||||
fail("json_pack string concatenation and length failed");
|
||||
fail("json_pack string concatenation and length (int) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string concatenation and length refcount failed");
|
||||
fail("json_pack string concatenation and length (int) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* string concatenation and length (size_t) */
|
||||
value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
|
||||
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
|
||||
fail("json_pack string concatenation and length (size_t) failed");
|
||||
if(value->refcount != (size_t)1)
|
||||
fail("json_pack string concatenation and length (size_t) refcount failed");
|
||||
json_decref(value);
|
||||
|
||||
/* empty object */
|
||||
|
@ -17,6 +17,7 @@ static void run_tests()
|
||||
int i1, i2, i3;
|
||||
json_int_t I1;
|
||||
int rv;
|
||||
size_t z;
|
||||
double f;
|
||||
char *s;
|
||||
|
||||
@ -81,6 +82,13 @@ static void run_tests()
|
||||
fail("json_unpack string failed");
|
||||
json_decref(j);
|
||||
|
||||
/* string with length (size_t) */
|
||||
j = json_string("foo");
|
||||
rv = json_unpack(j, "s%", &s, &z);
|
||||
if(rv || strcmp(s, "foo") || z != 3)
|
||||
fail("json_unpack string with length (size_t) failed");
|
||||
json_decref(j);
|
||||
|
||||
/* empty object */
|
||||
j = json_object();
|
||||
if(json_unpack(j, "{}"))
|
||||
|
@ -1 +1 @@
|
||||
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
|
||||
["foo", "\u00E5 \u00E4 \u00F6", "foo \u00E5\u00E4", "\u00E5\u00E4 foo", "\u00E5 foo \u00E4", "clef g: \uD834\uDD1E"]
|
@ -1,2 +0,0 @@
|
||||
1 33 33
|
||||
\u0000 is not allowed
|
@ -1 +0,0 @@
|
||||
["\u0000 (null byte not allowed)"]
|
1
test/suites/valid/escaped-null-byte-in-string/input
Normal file
1
test/suites/valid/escaped-null-byte-in-string/input
Normal file
@ -0,0 +1 @@
|
||||
["null char \u0000 in string"]
|
1
test/suites/valid/escaped-null-byte-in-string/output
Normal file
1
test/suites/valid/escaped-null-byte-in-string/output
Normal file
@ -0,0 +1 @@
|
||||
["null char \u0000 in string"]
|
Loading…
Reference in New Issue
Block a user