diff --git a/compat/jansson/Makefile.am b/compat/jansson/Makefile.am index f007b2c9..f650fab9 100644 --- a/compat/jansson/Makefile.am +++ b/compat/jansson/Makefile.am @@ -1,21 +1,21 @@ +EXTRA_DIST = jansson.def + +include_HEADERS = jansson.h jansson_config.h noinst_LIBRARIES = libjansson.a libjansson_a_SOURCES = \ - config.h \ - jansson_config.h \ dump.c \ + error.c \ hashtable.c \ hashtable.h \ - jansson.h \ jansson_private.h \ load.c \ + memory.c \ + pack_unpack.c \ strbuffer.c \ strbuffer.h \ + strconv.c \ utf.c \ utf.h \ - util.h \ - value.c \ - memory.c \ - error.c - + value.c diff --git a/compat/jansson/config.h b/compat/jansson/config.h deleted file mode 100644 index 43858aa6..00000000 --- a/compat/jansson/config.h +++ /dev/null @@ -1,73 +0,0 @@ -/* config.h. Generated from config.h.in by configure. */ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" - -/* Name of package */ -#define PACKAGE "jansson" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "petri@digip.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "jansson" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "jansson 1.3" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "jansson" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "1.3" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Version number of package */ -#define VERSION "1.3" - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - -/* Define to the type of a signed integer type of width exactly 32 bits if - such a type exists and the standard includes do not define it. */ -/* #undef int32_t */ diff --git a/compat/jansson/dump.c b/compat/jansson/dump.c index f32f6676..2c7dee9e 100644 --- a/compat/jansson/dump.c +++ b/compat/jansson/dump.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -11,7 +11,7 @@ #include #include -#include +#include "jansson.h" #include "jansson_private.h" #include "strbuffer.h" #include "utf.h" @@ -19,21 +19,17 @@ #define MAX_INTEGER_STR_LENGTH 100 #define MAX_REAL_STR_LENGTH 100 -typedef int (*dump_func)(const char *buffer, int size, void *data); - -struct string -{ - char *buffer; - int length; - int size; +struct object_key { + size_t serial; + const char *key; }; -static int dump_to_strbuffer(const char *buffer, int size, void *data) +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) { return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); } -static int dump_to_file(const char *buffer, int size, void *data) +static int dump_to_file(const char *buffer, size_t size, void *data) { FILE *dest = (FILE *)data; if(fwrite(buffer, size, 1, dest) != 1) @@ -44,7 +40,7 @@ static int dump_to_file(const char *buffer, int size, void *data) /* 32 spaces (the maximum indentation size) */ static char whitespace[] = " "; -static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data) +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) { if(JSON_INDENT(flags) > 0) { @@ -66,7 +62,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void return 0; } -static int dump_string(const char *str, int ascii, dump_func dump, void *data) +static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags) { const char *pos, *end; int32_t codepoint; @@ -91,8 +87,12 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data) if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) break; + /* slash */ + if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + /* non-ASCII */ - if(ascii && codepoint > 0x7F) + if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) break; pos = end; @@ -106,7 +106,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data) if(end == pos) break; - /* handle \, ", and control codes */ + /* handle \, /, ", and control codes */ length = 2; switch(codepoint) { @@ -117,6 +117,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data) case '\n': text = "\\n"; break; case '\r': text = "\\r"; break; case '\t': text = "\\t"; break; + case '/': text = "\\/"; break; default: { /* codepoint is in BMP */ @@ -155,21 +156,21 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data) static int object_key_compare_keys(const void *key1, const void *key2) { - return strcmp((*(const object_key_t **)key1)->key, - (*(const object_key_t **)key2)->key); + return strcmp(((const struct object_key *)key1)->key, + ((const struct object_key *)key2)->key); } static int object_key_compare_serials(const void *key1, const void *key2) { - return (*(const object_key_t **)key1)->serial - - (*(const object_key_t **)key2)->serial; + size_t a = ((const struct object_key *)key1)->serial; + size_t b = ((const struct object_key *)key2)->serial; + + return a < b ? -1 : a == b ? 0 : 1; } static int do_dump(const json_t *json, size_t flags, int depth, - dump_func dump, void *data) + json_dump_callback_t dump, void *data) { - int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0; - switch(json_typeof(json)) { case JSON_NULL: return dump("null", 4, data); @@ -188,7 +189,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT, json_integer_value(json)); - if(size >= MAX_INTEGER_STR_LENGTH) + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) return -1; return dump(buffer, size, data); @@ -198,31 +199,17 @@ static int do_dump(const json_t *json, size_t flags, int depth, { char buffer[MAX_REAL_STR_LENGTH]; int size; + double value = json_real_value(json); - size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g", - json_real_value(json)); - if(size >= MAX_REAL_STR_LENGTH) + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value); + if(size < 0) return -1; - /* Make sure there's a dot or 'e' in the output. Otherwise - a real is converted to an integer when decoding */ - if(strchr(buffer, '.') == NULL && - strchr(buffer, 'e') == NULL) - { - if(size + 2 >= MAX_REAL_STR_LENGTH) { - /* No space to append ".0" */ - return -1; - } - buffer[size] = '.'; - buffer[size + 1] = '0'; - size += 2; - } - return dump(buffer, size, data); } case JSON_STRING: - return dump_string(json_string_value(json), ascii, dump, data); + return dump_string(json_string_value(json), dump, data, flags); case JSON_ARRAY: { @@ -308,19 +295,20 @@ static int do_dump(const json_t *json, size_t flags, int depth, if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER) { - const object_key_t **keys; + struct object_key *keys; size_t size, i; int (*cmp_func)(const void *, const void *); size = json_object_size(json); - keys = jsonp_malloc(size * sizeof(object_key_t *)); + keys = jsonp_malloc(size * sizeof(struct object_key)); if(!keys) goto object_error; i = 0; while(iter) { - keys[i] = jsonp_object_iter_fullkey(iter); + keys[i].serial = hashtable_iter_serial(iter); + keys[i].key = json_object_iter_key(iter); iter = json_object_iter_next((json_t *)json, iter); i++; } @@ -331,18 +319,18 @@ static int do_dump(const json_t *json, size_t flags, int depth, else cmp_func = object_key_compare_serials; - qsort(keys, size, sizeof(object_key_t *), cmp_func); + qsort(keys, size, sizeof(struct object_key), cmp_func); for(i = 0; i < size; i++) { const char *key; json_t *value; - key = keys[i]->key; + key = keys[i].key; value = json_object_get(json, key); assert(value); - dump_string(key, ascii, dump, data); + dump_string(key, dump, data, flags); if(dump(separator, separator_length, data) || do_dump(value, flags, depth + 1, dump, data)) { @@ -379,7 +367,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, { void *next = json_object_iter_next((json_t *)json, iter); - dump_string(json_object_iter_key(iter), ascii, dump, data); + dump_string(json_object_iter_key(iter), dump, data, flags); if(dump(separator, separator_length, data) || do_dump(json_object_iter_value(iter), flags, depth + 1, dump, data)) @@ -415,39 +403,26 @@ static int do_dump(const json_t *json, size_t flags, int depth, } } - char *json_dumps(const json_t *json, size_t flags) { strbuffer_t strbuff; char *result; - if(!(flags & JSON_ENCODE_ANY)) { - if(!json_is_array(json) && !json_is_object(json)) - return NULL; - } - if(strbuffer_init(&strbuff)) return NULL; - if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) { - strbuffer_close(&strbuff); - return NULL; - } + if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); - result = jsonp_strdup(strbuffer_value(&strbuff)); strbuffer_close(&strbuff); - return result; } int json_dumpf(const json_t *json, FILE *output, size_t flags) { - if(!(flags & JSON_ENCODE_ANY)) { - if(!json_is_array(json) && !json_is_object(json)) - return -1; - } - - return do_dump(json, flags, 0, dump_to_file, (void *)output); + return json_dump_callback(json, dump_to_file, (void *)output, flags); } int json_dump_file(const json_t *json, const char *path, size_t flags) @@ -463,3 +438,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags) fclose(output); return result; } + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) +{ + if(!(flags & JSON_ENCODE_ANY)) { + if(!json_is_array(json) && !json_is_object(json)) + return -1; + } + + return do_dump(json, flags, 0, callback, data); +} diff --git a/compat/jansson/error.c b/compat/jansson/error.c index a7c8cbb9..a544a59f 100644 --- a/compat/jansson/error.c +++ b/compat/jansson/error.c @@ -59,4 +59,5 @@ void jsonp_error_vset(json_error_t *error, int line, int column, error->position = position; vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; } diff --git a/compat/jansson/hashtable.c b/compat/jansson/hashtable.c index 9a3f73b3..76cf69bf 100644 --- a/compat/jansson/hashtable.c +++ b/compat/jansson/hashtable.c @@ -1,11 +1,12 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include +#include #include /* for JSON_INLINE */ #include "jansson_private.h" /* for container_of() */ #include "hashtable.h" @@ -16,6 +17,23 @@ typedef struct hashtable_bucket bucket_t; #define list_to_pair(list_) container_of(list_, pair_t, list) +/* From http://www.cse.yorku.ca/~oz/hash.html */ +static size_t hash_str(const void *ptr) +{ + const char *str = (const char *)ptr; + + size_t hash = 5381; + size_t c; + + while((c = (size_t)*str)) + { + hash = ((hash << 5) + hash) + c; + str++; + } + + return hash; +} + static JSON_INLINE void list_init(list_t *list) { list->next = list; @@ -62,7 +80,6 @@ static size_t primes[] = { 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; -static const size_t num_primes = sizeof(primes) / sizeof(size_t); static JSON_INLINE size_t num_buckets(hashtable_t *hashtable) { @@ -71,7 +88,7 @@ static JSON_INLINE size_t num_buckets(hashtable_t *hashtable) static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, - const void *key, size_t hash) + const char *key, size_t hash) { list_t *list; pair_t *pair; @@ -83,7 +100,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, while(1) { pair = list_to_pair(list); - if(pair->hash == hash && hashtable->cmp_keys(pair->key, key)) + if(pair->hash == hash && strcmp(pair->key, key) == 0) return pair; if(list == bucket->last) @@ -97,7 +114,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, /* returns 0 on success, -1 if key was not found */ static int hashtable_do_del(hashtable_t *hashtable, - const void *key, size_t hash) + const char *key, size_t hash) { pair_t *pair; bucket_t *bucket; @@ -120,11 +137,7 @@ static int hashtable_do_del(hashtable_t *hashtable, bucket->last = pair->list.prev; list_remove(&pair->list); - - if(hashtable->free_key) - hashtable->free_key(pair->key); - if(hashtable->free_value) - hashtable->free_value(pair->value); + json_decref(pair->value); jsonp_free(pair); hashtable->size--; @@ -141,10 +154,7 @@ static void hashtable_do_clear(hashtable_t *hashtable) { next = list->next; pair = list_to_pair(list); - if(hashtable->free_key) - hashtable->free_key(pair->key); - if(hashtable->free_value) - hashtable->free_value(pair->value); + json_decref(pair->value); jsonp_free(pair); } } @@ -184,31 +194,7 @@ static int hashtable_do_rehash(hashtable_t *hashtable) } -hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys, - free_fn free_key, free_fn free_value) -{ - hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t)); - if(!hashtable) - return NULL; - - if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value)) - { - jsonp_free(hashtable); - return NULL; - } - - return hashtable; -} - -void hashtable_destroy(hashtable_t *hashtable) -{ - hashtable_close(hashtable); - jsonp_free(hashtable); -} - -int hashtable_init(hashtable_t *hashtable, - key_hash_fn hash_key, key_cmp_fn cmp_keys, - free_fn free_key, free_fn free_value) +int hashtable_init(hashtable_t *hashtable) { size_t i; @@ -220,11 +206,6 @@ int hashtable_init(hashtable_t *hashtable, list_init(&hashtable->list); - hashtable->hash_key = hash_key; - hashtable->cmp_keys = cmp_keys; - hashtable->free_key = free_key; - hashtable->free_value = free_value; - for(i = 0; i < num_buckets(hashtable); i++) { hashtable->buckets[i].first = hashtable->buckets[i].last = @@ -240,7 +221,9 @@ void hashtable_close(hashtable_t *hashtable) jsonp_free(hashtable->buckets); } -int hashtable_set(hashtable_t *hashtable, void *key, void *value) +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value) { pair_t *pair; bucket_t *bucket; @@ -251,28 +234,29 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value) if(hashtable_do_rehash(hashtable)) return -1; - hash = hashtable->hash_key(key); + hash = hash_str(key); index = hash % num_buckets(hashtable); bucket = &hashtable->buckets[index]; pair = hashtable_find_pair(hashtable, bucket, key, hash); if(pair) { - if(hashtable->free_key) - hashtable->free_key(key); - if(hashtable->free_value) - hashtable->free_value(pair->value); + json_decref(pair->value); pair->value = value; } else { - pair = jsonp_malloc(sizeof(pair_t)); + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1); if(!pair) return -1; - pair->key = key; - pair->value = value; pair->hash = hash; + pair->serial = serial; + strcpy(pair->key, key); + pair->value = value; list_init(&pair->list); insert_to_bucket(hashtable, bucket, &pair->list); @@ -282,13 +266,13 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value) return 0; } -void *hashtable_get(hashtable_t *hashtable, const void *key) +void *hashtable_get(hashtable_t *hashtable, const char *key) { pair_t *pair; size_t hash; bucket_t *bucket; - hash = hashtable->hash_key(key); + hash = hash_str(key); bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; pair = hashtable_find_pair(hashtable, bucket, key, hash); @@ -298,9 +282,9 @@ void *hashtable_get(hashtable_t *hashtable, const void *key) return pair->value; } -int hashtable_del(hashtable_t *hashtable, const void *key) +int hashtable_del(hashtable_t *hashtable, const char *key) { - size_t hash = hashtable->hash_key(key); + size_t hash = hash_str(key); return hashtable_do_del(hashtable, key, hash); } @@ -325,13 +309,13 @@ void *hashtable_iter(hashtable_t *hashtable) return hashtable_iter_next(hashtable, &hashtable->list); } -void *hashtable_iter_at(hashtable_t *hashtable, const void *key) +void *hashtable_iter_at(hashtable_t *hashtable, const char *key) { pair_t *pair; size_t hash; bucket_t *bucket; - hash = hashtable->hash_key(key); + hash = hash_str(key); bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; pair = hashtable_find_pair(hashtable, bucket, key, hash); @@ -355,18 +339,22 @@ void *hashtable_iter_key(void *iter) return pair->key; } +size_t hashtable_iter_serial(void *iter) +{ + pair_t *pair = list_to_pair((list_t *)iter); + return pair->serial; +} + void *hashtable_iter_value(void *iter) { pair_t *pair = list_to_pair((list_t *)iter); return pair->value; } -void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value) +void hashtable_iter_set(void *iter, json_t *value) { pair_t *pair = list_to_pair((list_t *)iter); - if(hashtable->free_value) - hashtable->free_value(pair->value); - + json_decref(pair->value); pair->value = value; } diff --git a/compat/jansson/hashtable.h b/compat/jansson/hashtable.h index 5aed14f2..de1df260 100644 --- a/compat/jansson/hashtable.h +++ b/compat/jansson/hashtable.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -8,20 +8,20 @@ #ifndef HASHTABLE_H #define HASHTABLE_H -typedef size_t (*key_hash_fn)(const void *key); -typedef int (*key_cmp_fn)(const void *key1, const void *key2); -typedef void (*free_fn)(void *key); - struct hashtable_list { struct hashtable_list *prev; struct hashtable_list *next; }; +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ struct hashtable_pair { - void *key; - void *value; size_t hash; struct hashtable_list list; + json_t *value; + size_t serial; + char key[1]; }; struct hashtable_bucket { @@ -34,56 +34,23 @@ typedef struct hashtable { struct hashtable_bucket *buckets; size_t num_buckets; /* index to primes[] */ struct hashtable_list list; - - key_hash_fn hash_key; - key_cmp_fn cmp_keys; /* returns non-zero for equal keys */ - free_fn free_key; - free_fn free_value; } hashtable_t; -/** - * hashtable_create - Create a hashtable object - * - * @hash_key: The key hashing function - * @cmp_keys: The key compare function. Returns non-zero for equal and - * zero for unequal unequal keys - * @free_key: If non-NULL, called for a key that is no longer referenced. - * @free_value: If non-NULL, called for a value that is no longer referenced. - * - * Returns a new hashtable object that should be freed with - * hashtable_destroy when it's no longer used, or NULL on failure (out - * of memory). - */ -hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys, - free_fn free_key, free_fn free_value); -/** - * hashtable_destroy - Destroy a hashtable object - * - * @hashtable: The hashtable - * - * Destroys a hashtable created with hashtable_create(). - */ -void hashtable_destroy(hashtable_t *hashtable); +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->list)) /** * hashtable_init - Initialize a hashtable object * * @hashtable: The (statically allocated) hashtable object - * @hash_key: The key hashing function - * @cmp_keys: The key compare function. Returns non-zero for equal and - * zero for unequal unequal keys - * @free_key: If non-NULL, called for a key that is no longer referenced. - * @free_value: If non-NULL, called for a value that is no longer referenced. * * Initializes a statically allocated hashtable object. The object * should be cleared with hashtable_close when it's no longer used. * * Returns 0 on success, -1 on error (out of memory). */ -int hashtable_init(hashtable_t *hashtable, - key_hash_fn hash_key, key_cmp_fn cmp_keys, - free_fn free_key, free_fn free_value); +int hashtable_init(hashtable_t *hashtable); /** * hashtable_close - Release all resources used by a hashtable object @@ -99,20 +66,19 @@ void hashtable_close(hashtable_t *hashtable); * * @hashtable: The hashtable object * @key: The key + * @serial: For addition order of keys * @value: The value * * If a value with the given key already exists, its value is replaced - * with the new value. - * - * Key and value are "stealed" in the sense that hashtable frees them - * automatically when they are no longer used. The freeing is - * accomplished by calling free_key and free_value functions that were - * supplied to hashtable_new. In case one or both of the free - * functions is NULL, the corresponding item is not "stealed". + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. * * Returns 0 on success, -1 on failure (out of memory). */ -int hashtable_set(hashtable_t *hashtable, void *key, void *value); +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value); /** * hashtable_get - Get a value associated with a key @@ -122,7 +88,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value); * * Returns value if it is found, or NULL otherwise. */ -void *hashtable_get(hashtable_t *hashtable, const void *key); +void *hashtable_get(hashtable_t *hashtable, const char *key); /** * hashtable_del - Remove a value from the hashtable @@ -132,7 +98,7 @@ void *hashtable_get(hashtable_t *hashtable, const void *key); * * Returns 0 on success, or -1 if the key was not found. */ -int hashtable_del(hashtable_t *hashtable, const void *key); +int hashtable_del(hashtable_t *hashtable, const char *key); /** * hashtable_clear - Clear hashtable @@ -169,7 +135,7 @@ void *hashtable_iter(hashtable_t *hashtable); * Like hashtable_iter() but returns an iterator pointing to a * specific key. */ -void *hashtable_iter_at(hashtable_t *hashtable, const void *key); +void *hashtable_iter_at(hashtable_t *hashtable, const char *key); /** * hashtable_iter_next - Advance an iterator @@ -189,6 +155,13 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter); */ void *hashtable_iter_key(void *iter); +/** + * hashtable_iter_serial - Retrieve the serial number pointed to by an iterator + * + * @iter: The iterator + */ +size_t hashtable_iter_serial(void *iter); + /** * hashtable_iter_value - Retrieve the value pointed by an iterator * @@ -202,6 +175,6 @@ void *hashtable_iter_value(void *iter); * @iter: The iterator * @value: The value to set */ -void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value); +void hashtable_iter_set(void *iter, json_t *value); #endif diff --git a/compat/jansson/jansson.def b/compat/jansson/jansson.def new file mode 100644 index 00000000..6b2c8a72 --- /dev/null +++ b/compat/jansson/jansson.def @@ -0,0 +1,65 @@ +LIBRARY "jansson" + +EXPORTS + json_delete + json_true + json_false + json_null + json_string + json_string_nocheck + json_string_value + json_string_set + json_string_set_nocheck + json_integer + json_integer_value + json_integer_set + json_real + json_real_value + json_real_set + json_number_value + json_array + json_array_size + json_array_get + json_array_set_new + json_array_append_new + json_array_insert_new + json_array_remove + json_array_clear + json_array_extend + json_object + json_object_size + json_object_get + json_object_set_new + json_object_set_new_nocheck + json_object_del + json_object_clear + json_object_update + json_object_update_existing + json_object_update_missing + json_object_iter + json_object_iter_at + json_object_iter_next + json_object_iter_key + json_object_iter_value + json_object_iter_set_new + json_object_key_to_iter + json_dumps + json_dumpf + json_dump_file + json_dump_callback + json_loads + json_loadb + json_loadf + json_load_file + json_load_callback + json_equal + json_copy + json_deep_copy + json_pack + json_pack_ex + json_vpack_ex + json_unpack + json_unpack_ex + json_vunpack_ex + json_set_alloc_funcs + diff --git a/compat/jansson/jansson.h b/compat/jansson/jansson.h index 9372ec20..352c6ce5 100644 --- a/compat/jansson/jansson.h +++ b/compat/jansson/jansson.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -21,11 +21,11 @@ extern "C" { /* version */ #define JANSSON_MAJOR_VERSION 2 -#define JANSSON_MINOR_VERSION 1 +#define JANSSON_MINOR_VERSION 4 #define JANSSON_MICRO_VERSION 0 /* Micro version is omitted if it's 0 */ -#define JANSSON_VERSION "2.1" +#define JANSSON_VERSION "2.4" /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ @@ -52,8 +52,12 @@ typedef struct { size_t refcount; } json_t; -#if JSON_INTEGER_IS_LONG_LONG && (!defined(WIN32)) +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else #define JSON_INTEGER_FORMAT "lld" +#endif typedef long long json_int_t; #else #define JSON_INTEGER_FORMAT "ld" @@ -82,6 +86,7 @@ json_t *json_integer(json_int_t value); json_t *json_real(double value); json_t *json_true(void); json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) json_t *json_null(void); static JSON_INLINE @@ -126,13 +131,21 @@ int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); int json_object_del(json_t *object, const char *key); int json_object_clear(json_t *object); int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); void *json_object_iter(json_t *object); void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); void *json_object_iter_next(json_t *object, void *iter); const char *json_object_iter_key(void *iter); json_t *json_object_iter_value(void *iter); int json_object_iter_set_new(json_t *object, void *iter, json_t *value); +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { @@ -218,11 +231,15 @@ json_t *json_deep_copy(json_t *value); #define JSON_REJECT_DUPLICATES 0x1 #define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); json_t *json_loads(const char *input, size_t flags, json_error_t *error); json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); json_t *json_load_file(const char *path, size_t flags, json_error_t *error); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); /* encoding */ @@ -233,11 +250,14 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error); #define JSON_SORT_KEYS 0x80 #define JSON_PRESERVE_ORDER 0x100 #define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); char *json_dumps(const json_t *json, size_t flags); int json_dumpf(const json_t *json, FILE *output, size_t flags); int json_dump_file(const json_t *json, const char *path, size_t flags); - +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); /* custom memory allocation */ diff --git a/compat/jansson/jansson_config.h b/compat/jansson/jansson_config.h index b3bb72a4..e576cd1a 100644 --- a/compat/jansson/jansson_config.h +++ b/compat/jansson/jansson_config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 Petri Lehtinen + * Copyright (c) 2010-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -27,8 +27,13 @@ #define JSON_INLINE inline #endif -/* If your compiler supports the `long long` type, - JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */ +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ #define JSON_INTEGER_IS_LONG_LONG 1 +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV 1 + #endif diff --git a/compat/jansson/jansson_config.h.in b/compat/jansson/jansson_config.h.in index 7f519cd6..a566e8b6 100644 --- a/compat/jansson/jansson_config.h.in +++ b/compat/jansson/jansson_config.h.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 Petri Lehtinen + * Copyright (c) 2010-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -27,8 +27,13 @@ #define JSON_INLINE @json_inline@ #endif -/* If your compiler supports the `long long` type, - JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */ +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ #define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@ +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @json_have_localeconv@ + #endif diff --git a/compat/jansson/jansson_private.h b/compat/jansson/jansson_private.h index 452a4f08..7d6d09d0 100644 --- a/compat/jansson/jansson_private.h +++ b/compat/jansson/jansson_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -11,6 +11,7 @@ #include #include "jansson.h" #include "hashtable.h" +#include "strbuffer.h" #define container_of(ptr_, type_, member_) \ ((type_ *)((char *)ptr_ - offsetof(type_, member_))) @@ -66,16 +67,6 @@ 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) -size_t jsonp_hash_str(const void *ptr); -int jsonp_str_equal(const void *ptr1, const void *ptr2); - -typedef struct { - size_t serial; - char key[1]; -} object_key_t; - -const object_key_t *jsonp_object_iter_fullkey(void *iter); - 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,9 +74,19 @@ void jsonp_error_set(json_error_t *error, int line, int column, void jsonp_error_vset(json_error_t *error, int line, int column, size_t position, const char *msg, va_list ap); +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value); + /* Wrappers for custom memory functions */ void* jsonp_malloc(size_t size); void jsonp_free(void *ptr); char *jsonp_strdup(const char *str); +/* Windows compatibility */ +#ifdef _WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + #endif diff --git a/compat/jansson/load.c b/compat/jansson/load.c index 8a53f135..d88a7040 100644 --- a/compat/jansson/load.c +++ b/compat/jansson/load.c @@ -1,12 +1,11 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _GNU_SOURCE -#include #include #include #include @@ -14,7 +13,7 @@ #include #include -#include +#include "jansson.h" #include "jansson_private.h" #include "strbuffer.h" #include "utf.h" @@ -32,6 +31,14 @@ #define TOKEN_FALSE 260 #define TOKEN_NULL 261 +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f') + /* Read one byte from stream, convert to unsigned char, then int, and return. return EOF on end of file. This corresponds to the behaviour of fgetc(). */ @@ -41,7 +48,7 @@ typedef struct { get_func get; void *data; char buffer[5]; - int buffer_pos; + size_t buffer_pos; int state; int line; int column, last_column; @@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex, { va_list ap; char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH]; int line = -1, col = -1; size_t pos = 0; @@ -79,12 +87,12 @@ static void error_set(json_error_t *error, const lex_t *lex, va_start(ap, msg); vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; va_end(ap); if(lex) { const char *saved_text = strbuffer_value(&lex->saved_text); - char msg_with_context[JSON_ERROR_TEXT_LENGTH]; line = lex->stream.line; col = lex->stream.column; @@ -95,6 +103,7 @@ static void error_set(json_error_t *error, const lex_t *lex, if(lex->saved_text.length <= 20) { snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, "%s near '%s'", msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; result = msg_with_context; } } @@ -107,6 +116,7 @@ static void error_set(json_error_t *error, const lex_t *lex, else { snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, "%s near end of file", msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; result = msg_with_context; } } @@ -268,11 +278,11 @@ static int32_t decode_unicode_escape(const char *str) for(i = 1; i <= 4; i++) { char c = str[i]; value <<= 4; - if(isdigit(c)) + if(l_isdigit(c)) value += c - '0'; - else if(islower(c)) + else if(l_islower(c)) value += c - 'a' + 10; - else if(isupper(c)) + else if(l_isupper(c)) value += c - 'A' + 10; else assert(0); @@ -317,7 +327,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error) if(c == 'u') { c = lex_get_save(lex, error); for(i = 0; i < 4; i++) { - if(!isxdigit(c)) { + if(!l_isxdigit(c)) { error_set(error, lex, "invalid escape"); goto out; } @@ -437,7 +447,11 @@ out: } #if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER // Microsoft Visual Studio +#define json_strtoint _strtoi64 +#else #define json_strtoint strtoll +#endif #else #define json_strtoint strtol #endif @@ -455,14 +469,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) if(c == '0') { c = lex_get_save(lex, error); - if(isdigit(c)) { + if(l_isdigit(c)) { lex_unget_unsave(lex, c); goto out; } } - else if(isdigit(c)) { + else if(l_isdigit(c)) { c = lex_get_save(lex, error); - while(isdigit(c)) + while(l_isdigit(c)) c = lex_get_save(lex, error); } else { @@ -496,14 +510,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) if(c == '.') { c = lex_get(lex, error); - if(!isdigit(c)) { + if(!l_isdigit(c)) { lex_unget(lex, c); goto out; } lex_save(lex, c); c = lex_get_save(lex, error); - while(isdigit(c)) + while(l_isdigit(c)) c = lex_get_save(lex, error); } @@ -512,24 +526,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error) if(c == '+' || c == '-') c = lex_get_save(lex, error); - if(!isdigit(c)) { + if(!l_isdigit(c)) { lex_unget_unsave(lex, c); goto out; } c = lex_get_save(lex, error); - while(isdigit(c)) + while(l_isdigit(c)) c = lex_get_save(lex, error); } lex_unget_unsave(lex, c); - saved_text = strbuffer_value(&lex->saved_text); - errno = 0; - value = strtod(saved_text, &end); - assert(end == saved_text + lex->saved_text.length); - - if(errno == ERANGE && value != 0) { + if(jsonp_strtod(&lex->saved_text, &value)) { error_set(error, lex, "real number overflow"); goto out; } @@ -575,17 +584,17 @@ static int lex_scan(lex_t *lex, json_error_t *error) else if(c == '"') lex_scan_string(lex, error); - else if(isdigit(c) || c == '-') { + else if(l_isdigit(c) || c == '-') { if(lex_scan_number(lex, c, error)) goto out; } - else if(isupper(c) || islower(c)) { + else if(l_isalpha(c)) { /* eat up the whole identifier for clearer error messages */ const char *saved_text; c = lex_get_save(lex, error); - while(isupper(c) || islower(c)) + while(l_isalpha(c)) c = lex_get_save(lex, error); lex_unget_unsave(lex, c); @@ -818,9 +827,11 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) json_t *result; lex_scan(lex, error); - if(lex->token != '[' && lex->token != '{') { - error_set(error, lex, "'[' or '{' expected"); - return NULL; + if(!(flags & JSON_DECODE_ANY)) { + if(lex->token != '[' && lex->token != '{') { + error_set(error, lex, "'[' or '{' expected"); + return NULL; + } } result = parse_value(lex, flags, error); @@ -832,10 +843,15 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) if(lex->token != TOKEN_EOF) { error_set(error, lex, "end of file expected"); json_decref(result); - result = NULL; + return NULL; } } + if(error) { + /* Save the position even though there was no error */ + error->position = lex->stream.position; + } + return result; } @@ -865,13 +881,19 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error) json_t *result; string_data_t stream_data; + jsonp_error_init(error, ""); + + if (string == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + stream_data.data = string; stream_data.pos = 0; if(lex_init(&lex, string_get, (void *)&stream_data)) return NULL; - jsonp_error_init(error, ""); result = parse_json(&lex, flags, error); lex_close(&lex); @@ -903,6 +925,13 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t json_t *result; buffer_data_t stream_data; + jsonp_error_init(error, ""); + + if (buffer == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + stream_data.data = buffer; stream_data.pos = 0; stream_data.len = buflen; @@ -910,7 +939,6 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t if(lex_init(&lex, buffer_get, (void *)&stream_data)) return NULL; - jsonp_error_init(error, ""); result = parse_json(&lex, flags, error); lex_close(&lex); @@ -923,15 +951,21 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) const char *source; json_t *result; - if(lex_init(&lex, (get_func)fgetc, input)) - return NULL; - if(input == stdin) source = ""; else source = ""; jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fgetc, input)) + return NULL; + result = parse_json(&lex, flags, error); lex_close(&lex); @@ -945,7 +979,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error) jsonp_error_init(error, path); - fp = fopen(path, "r"); + if (path == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); if(!fp) { error_set(error, NULL, "unable to open %s: %s", @@ -958,3 +997,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error) fclose(fp); return result; } + +#define MAX_BUF_LEN 1024 + +typedef struct +{ + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) +{ + char c; + callback_data_t *stream = data; + + if(stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if(stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, ""); + + if (callback == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)callback_get, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} diff --git a/compat/jansson/memory.c b/compat/jansson/memory.c index 127b5ac8..543ecc4d 100644 --- a/compat/jansson/memory.c +++ b/compat/jansson/memory.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen - * Copyright (c) 2011 Basile Starynkevitch + * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2011-2012 Basile Starynkevitch * * Jansson is free software; you can redistribute it and/or modify it * under the terms of the MIT license. See LICENSE for details. @@ -9,7 +9,7 @@ #include #include -#include +#include "jansson.h" #include "jansson_private.h" /* memory function pointers */ diff --git a/compat/jansson/pack_unpack.c b/compat/jansson/pack_unpack.c new file mode 100644 index 00000000..39db9b8d --- /dev/null +++ b/compat/jansson/pack_unpack.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2011-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +typedef struct { + const char *start; + const char *fmt; + char token; + json_error_t *error; + size_t flags; + int line; + int column; +} scanner_t; + +static const char *type_names[] = { + "object", + "array", + "string", + "integer", + "real", + "true", + "false", + "null" +}; + +#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; + s->column++; + + /* skip space and ignored chars */ + while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if(*t == '\n') { + s->line++; + s->column = 1; + } + else + s->column++; + + t++; + } + + s->token = *t; + + t++; + s->fmt = t; +} + +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_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + +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; + json_t *value; + + if(!s->token) { + set_error(s, "", "Unexpected end of format string"); + goto error; + } + + if(s->token != 's') { + 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"); + goto error; + } + + if(!utf8_check_string(key, -1)) { + set_error(s, "", "Invalid UTF-8 in object key"); + goto error; + } + + next_token(s); + + value = pack(s, ap); + if(!value) + goto error; + + if(json_object_set_new_nocheck(object, key, value)) { + set_error(s, "", "Unable to add key \"%s\"", key); + goto error; + } + + next_token(s); + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) +{ + json_t *array = json_array(); + next_token(s); + + while(s->token != ']') { + json_t *value; + + if(!s->token) { + set_error(s, "", "Unexpected end of format string"); + goto error; + } + + value = pack(s, ap); + if(!value) + goto error; + + if(json_array_append_new(array, value)) { + set_error(s, "", "Unable to append to array"); + goto error; + } + + next_token(s); + } + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack(scanner_t *s, va_list *ap) +{ + switch(s->token) { + case '{': + return pack_object(s, ap); + + case '[': + return pack_array(s, ap); + + case 's': /* string */ + { + const char *str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "", "NULL string argument"); + return NULL; + } + if(!utf8_check_string(str, -1)) { + set_error(s, "", "Invalid UTF-8 string"); + return NULL; + } + return json_string_nocheck(str); + } + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return json_integer(va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return json_integer(va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return json_real(va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + return json_incref(va_arg(*ap, json_t *)); + + case 'o': /* a json_t object; doesn't increment refcount */ + return va_arg(*ap, json_t *); + + default: + set_error(s, "", "Unexpected format character '%c'", + s->token); + return NULL; + } +} + +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 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; + + if(hashtable_init(&key_set)) { + set_error(s, "", "Out of memory"); + return -1; + } + + if(root && !json_is_object(root)) { + set_error(s, "", "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while(s->token != '}') { + const char *key; + json_t *value; + int opt = 0; + + 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 out; + } + + 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'", s->token); + goto out; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "", "NULL object key"); + goto out; + } + + next_token(s); + + if(s->token == '?') { + opt = 1; + next_token(s); + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_object_get(root, key); + if(!value && !opt) { + set_error(s, "", "Object item not found: %s", key); + goto out; + } + } + + if(unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, 0, json_null()); + next_token(s); + } + + 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, "", "%li object item(s) left unpacked", diff); + goto out; + } + + ret = 0; + +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 strict = 0; + + if(root && !json_is_array(root)) { + set_error(s, "", "Expected array, got %s", type_name(root)); + return -1; + } + next_token(s); + + while(s->token != ']') { + json_t *value; + + 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"); + return -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; + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_array_get(root, i); + if(!value) { + set_error(s, "", "Array index %lu out of range", + (unsigned long)i); + return -1; + } + } + + if(unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "", "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) +{ + switch(s->token) + { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if(root && !json_is_string(root)) { + set_error(s, "", "Expected string, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + const char **target; + + target = va_arg(*ap, const char **); + if(!target) { + set_error(s, "", "NULL string argument"); + return -1; + } + + if(root) + *target = json_string_value(root); + } + return 0; + + case 'i': + if(root && !json_is_integer(root)) { + set_error(s, "", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if(root && !json_is_integer(root)) { + set_error(s, "", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t*); + if(root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if(root && !json_is_boolean(root)) { + set_error(s, "", "Expected true or false, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if(root && !json_is_real(root)) { + set_error(s, "", "Expected real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if(root && !json_is_number(root)) { + set_error(s, "", "Expected real or integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if(root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t**); + if(root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if(root && !json_is_null(root)) { + set_error(s, "", "Expected null, got %s", + type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "", "Unexpected format character '%c'", + s->token); + return -1; + } +} + +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; + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + 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"); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +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; + + if(!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL root value"); + return -1; + } + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + 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"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/compat/jansson/strbuffer.c b/compat/jansson/strbuffer.c index 1b20e2b3..6d4edd65 100644 --- a/compat/jansson/strbuffer.c +++ b/compat/jansson/strbuffer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -13,6 +13,7 @@ #define STRBUFFER_MIN_SIZE 16 #define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) int strbuffer_init(strbuffer_t *strbuff) { @@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte) return strbuffer_append_bytes(strbuff, &byte, 1); } -int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size) +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) { - if(strbuff->length + size >= strbuff->size) + if(size >= strbuff->size - strbuff->length) { size_t new_size; char *new_value; + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR + || size > STRBUFFER_SIZE_MAX - 1 + || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + new_size = max(strbuff->size * STRBUFFER_FACTOR, strbuff->length + size + 1); diff --git a/compat/jansson/strbuffer.h b/compat/jansson/strbuffer.h index b21ef8b0..23f8ffff 100644 --- a/compat/jansson/strbuffer.h +++ b/compat/jansson/strbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -10,8 +10,8 @@ typedef struct { char *value; - int length; /* bytes used */ - int size; /* bytes allocated */ + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ } strbuffer_t; int strbuffer_init(strbuffer_t *strbuff); @@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff); int strbuffer_append(strbuffer_t *strbuff, const char *string); int strbuffer_append_byte(strbuffer_t *strbuff, char byte); -int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); char strbuffer_pop(strbuffer_t *strbuff); diff --git a/compat/jansson/strconv.c b/compat/jansson/strconv.c new file mode 100644 index 00000000..caa9ab8b --- /dev/null +++ b/compat/jansson/strconv.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include "jansson_private.h" +#include "strbuffer.h" + +#if JSON_HAVE_LOCALECONV +#include + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + localeconv() and the call to sprintf() or strtod(), the result may + be wrong. setlocale() is not thread-safe and should not be used + this way. Multi-threaded programs should use uselocale() instead. +*/ + +static void to_locale(strbuffer_t *strbuffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if(pos) + *pos = *point; +} + +static void from_locale(char *buffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, *point); + if(pos) + *pos = '.'; +} +#endif + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) +{ + double value; + char *end; + +#if JSON_HAVE_LOCALECONV + to_locale(strbuffer); +#endif + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if(errno == ERANGE && value != 0) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +int jsonp_dtostr(char *buffer, size_t size, double value) +{ + int ret; + char *start, *end; + size_t length; + + ret = snprintf(buffer, size, "%.17g", value); + if(ret < 0) + return -1; + + length = (size_t)ret; + if(length >= size) + return -1; + +#if JSON_HAVE_LOCALECONV + from_locale(buffer); +#endif + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if(strchr(buffer, '.') == NULL && + strchr(buffer, 'e') == NULL) + { + if(length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if(start) { + start++; + end = start + 1; + + if(*start == '-') + start++; + + while(*end == '0') + end++; + + if(end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +} diff --git a/compat/jansson/utf.c b/compat/jansson/utf.c index f48c2e7b..0359ee2b 100644 --- a/compat/jansson/utf.c +++ b/compat/jansson/utf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/utf.h b/compat/jansson/utf.h index 0c3ab313..2495cdd1 100644 --- a/compat/jansson/utf.h +++ b/compat/jansson/utf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/value.c b/compat/jansson/value.c index 47ebb2c8..ba9908ed 100644 --- a/compat/jansson/value.c +++ b/compat/jansson/value.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011 Petri Lehtinen + * Copyright (c) 2009-2012 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -10,12 +10,20 @@ #include #include #include +#include -#include +#include "jansson.h" #include "hashtable.h" #include "jansson_private.h" #include "utf.h" +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +static JSON_INLINE int isnan(double x) { return x != x; } +#endif +#ifndef isinf +static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif static JSON_INLINE void json_init(json_t *json, json_type type) { @@ -26,50 +34,6 @@ static JSON_INLINE void json_init(json_t *json, json_type type) /*** object ***/ -/* From http://www.cse.yorku.ca/~oz/hash.html */ -size_t jsonp_hash_str(const void *ptr) -{ - const char *str = (const char *)ptr; - - size_t hash = 5381; - size_t c; - - while((c = (size_t)*str)) - { - hash = ((hash << 5) + hash) + c; - str++; - } - - return hash; -} - -int jsonp_str_equal(const void *ptr1, const void *ptr2) -{ - return strcmp((const char *)ptr1, (const char *)ptr2) == 0; -} - -/* This macro just returns a pointer that's a few bytes backwards from - string. This makes it possible to pass a pointer to object_key_t - when only the string inside it is used, without actually creating - an object_key_t instance. */ -#define string_to_key(string) container_of(string, object_key_t, key) - -static size_t hash_key(const void *ptr) -{ - return jsonp_hash_str(((const object_key_t *)ptr)->key); -} - -static int key_equal(const void *ptr1, const void *ptr2) -{ - return jsonp_str_equal(((const object_key_t *)ptr1)->key, - ((const object_key_t *)ptr2)->key); -} - -static void value_decref(void *value) -{ - json_decref((json_t *)value); -} - json_t *json_object(void) { json_object_t *object = jsonp_malloc(sizeof(json_object_t)); @@ -77,9 +41,7 @@ json_t *json_object(void) return NULL; json_init(&object->json, JSON_OBJECT); - if(hashtable_init(&object->hashtable, - hash_key, key_equal, - jsonp_free, value_decref)) + if(hashtable_init(&object->hashtable)) { jsonp_free(object); return NULL; @@ -116,38 +78,24 @@ json_t *json_object_get(const json_t *json, const char *key) return NULL; object = json_to_object(json); - return hashtable_get(&object->hashtable, string_to_key(key)); + return hashtable_get(&object->hashtable, key); } int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) { json_object_t *object; - object_key_t *k; - if(!key || !value) + if(!value) return -1; - if(!json_is_object(json) || json == value) + if(!key || !json_is_object(json) || json == value) { json_decref(value); return -1; } object = json_to_object(json); - /* offsetof(...) returns the size of object_key_t without the - last, flexible member. This way, the correct amount is - allocated. */ - k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1); - if(!k) - { - json_decref(value); - return -1; - } - - k->serial = object->serial++; - strcpy(k->key, key); - - if(hashtable_set(&object->hashtable, k, value)) + if(hashtable_set(&object->hashtable, key, object->serial++, value)) { json_decref(value); return -1; @@ -175,7 +123,7 @@ int json_object_del(json_t *json, const char *key) return -1; object = json_to_object(json); - return hashtable_del(&object->hashtable, string_to_key(key)); + return hashtable_del(&object->hashtable, key); } int json_object_clear(json_t *json) @@ -186,30 +134,56 @@ int json_object_clear(json_t *json) return -1; object = json_to_object(json); + hashtable_clear(&object->hashtable); + object->serial = 0; return 0; } int json_object_update(json_t *object, json_t *other) { - void *iter; + const char *key; + json_t *value; if(!json_is_object(object) || !json_is_object(other)) return -1; - iter = json_object_iter(other); - while(iter) { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); - + json_object_foreach(other, key, value) { if(json_object_set_nocheck(object, key, value)) return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; - iter = json_object_iter_next(other, iter); + json_object_foreach(other, key, value) { + if(!json_object_get(object, key)) + json_object_set_nocheck(object, key, value); } return 0; @@ -234,7 +208,7 @@ void *json_object_iter_at(json_t *json, const char *key) return NULL; object = json_to_object(json); - return hashtable_iter_at(&object->hashtable, string_to_key(key)); + return hashtable_iter_at(&object->hashtable, key); } void *json_object_iter_next(json_t *json, void *iter) @@ -248,20 +222,12 @@ void *json_object_iter_next(json_t *json, void *iter) return hashtable_iter_next(&object->hashtable, iter); } -const object_key_t *jsonp_object_iter_fullkey(void *iter) -{ - if(!iter) - return NULL; - - return hashtable_iter_key(iter); -} - const char *json_object_iter_key(void *iter) { if(!iter) return NULL; - return jsonp_object_iter_fullkey(iter)->key; + return hashtable_iter_key(iter); } json_t *json_object_iter_value(void *iter) @@ -274,38 +240,34 @@ json_t *json_object_iter_value(void *iter) int json_object_iter_set_new(json_t *json, void *iter, json_t *value) { - json_object_t *object; - if(!json_is_object(json) || !iter || !value) return -1; - object = json_to_object(json); - hashtable_iter_set(&object->hashtable, iter, value); - + hashtable_iter_set(iter, value); return 0; } +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + static int json_object_equal(json_t *object1, json_t *object2) { - void *iter; + const char *key; + json_t *value1, *value2; if(json_object_size(object1) != json_object_size(object2)) return 0; - iter = json_object_iter(object1); - while(iter) - { - const char *key; - json_t *value1, *value2; - - key = json_object_iter_key(iter); - value1 = json_object_iter_value(iter); + json_object_foreach(object1, key, value1) { value2 = json_object_get(object2, key); if(!json_equal(value1, value2)) return 0; - - iter = json_object_iter_next(object1, iter); } return 1; @@ -314,50 +276,34 @@ static int json_object_equal(json_t *object1, json_t *object2) static json_t *json_object_copy(json_t *object) { json_t *result; - void *iter; + + const char *key; + json_t *value; result = json_object(); if(!result) return NULL; - iter = json_object_iter(object); - while(iter) - { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); + json_object_foreach(object, key, value) json_object_set_nocheck(result, key, value); - iter = json_object_iter_next(object, iter); - } - return result; } static json_t *json_object_deep_copy(json_t *object) { json_t *result; - void *iter; + + const char *key; + json_t *value; result = json_object(); if(!result) return NULL; - iter = json_object_iter(object); - while(iter) - { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); + json_object_foreach(object, key, value) json_object_set_new_nocheck(result, key, json_deep_copy(value)); - iter = json_object_iter_next(object, iter); - } - return result; } @@ -703,6 +649,9 @@ int json_string_set_nocheck(json_t *json, const char *value) char *dup; json_string_t *string; + if(!json_is_string(json) || !value) + return -1; + dup = jsonp_strdup(value); if(!dup) return -1; @@ -790,7 +739,12 @@ static json_t *json_integer_copy(json_t *integer) json_t *json_real(double value) { - json_real_t *real = jsonp_malloc(sizeof(json_real_t)); + json_real_t *real; + + if(isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); if(!real) return NULL; json_init(&real->json, JSON_REAL); @@ -809,8 +763,8 @@ double json_real_value(const json_t *json) int json_real_set(json_t *json, double value) { - if(!json_is_real(json)) - return 0; + if(!json_is_real(json) || isnan(value) || isinf(value)) + return -1; json_to_real(json)->value = value; @@ -838,7 +792,7 @@ static json_t *json_real_copy(json_t *real) double json_number_value(const json_t *json) { if(json_is_integer(json)) - return json_integer_value(json); + return (double)json_integer_value(json); else if(json_is_real(json)) return json_real_value(json); else