diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd38ad..8ae0950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ set(JSON_C_HEADERS ./json_util.h ./linkhash.h ./math_compat.h + ./strdup_compat.h + ./vasprintf_compat.h ./printbuf.h ./random_seed.h ) diff --git a/Makefile.am b/Makefile.am index 41b9e6e..126ceeb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,8 @@ libjson_cinclude_HEADERS = \ json_visit.h \ linkhash.h \ math_compat.h \ + strdup_compat.h \ + vasprintf_compat.h \ printbuf.h \ random_seed.h diff --git a/json-c.vcxproj b/json-c.vcxproj index 8a4f265..3a6da61 100644 --- a/json-c.vcxproj +++ b/json-c.vcxproj @@ -151,6 +151,8 @@ copy json_config.h.win32 json_config.h + + @@ -164,4 +166,4 @@ copy json_config.h.win32 json_config.h - \ No newline at end of file + diff --git a/json-c.vcxproj.filters b/json-c.vcxproj.filters index 67ff360..7fd4dd0 100644 --- a/json-c.vcxproj.filters +++ b/json-c.vcxproj.filters @@ -80,6 +80,12 @@ Header Files + + Header Files + + + Header Files + Header Files @@ -93,4 +99,4 @@ - \ No newline at end of file + diff --git a/json_object.c b/json_object.c index 8a70b5d..c65941e 100644 --- a/json_object.c +++ b/json_object.c @@ -29,13 +29,7 @@ #include "json_object_private.h" #include "json_util.h" #include "math_compat.h" - -#if !defined(HAVE_STRDUP) && defined(_MSC_VER) - /* MSC has the version as _strdup */ -# define strdup _strdup -#elif !defined(HAVE_STRDUP) -# error You do not have strdup on your system. -#endif /* HAVE_STRDUP */ +#include "strdup_compat.h" #if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) /* MSC has the version as _snprintf */ diff --git a/json_pointer.c b/json_pointer.c index f06aed0..3648f8d 100644 --- a/json_pointer.c +++ b/json_pointer.c @@ -8,6 +8,7 @@ #include "config.h" +#include #include #include #include @@ -15,6 +16,8 @@ #include #include "json_pointer.h" +#include "strdup_compat.h" +#include "vasprintf_compat.h" /** * JavaScript Object Notation (JSON) Pointer @@ -159,7 +162,7 @@ static int json_pointer_get_recursive( } /* We should be at the end of the recursion here */ - if (value) + if (value) *value = obj; return 0; @@ -192,6 +195,37 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje return rc; } +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...) +{ + char *path_copy = NULL; + int rc = 0; + va_list args; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + if (res) + *res = obj; + goto out; + } + + rc = json_pointer_get_recursive(obj, path_copy, res); +out: + free(path_copy); + + return rc; +} + int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value) { const char *endp; @@ -237,3 +271,56 @@ int json_pointer_set(struct json_object **obj, const char *path, struct json_obj return json_pointer_set_single_path(set, endp, value); } +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...) +{ + char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + va_list args; + int rc = 0; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + /* pass a working copy to the recursive call */ + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + json_object_put(*obj); + *obj = value; + goto out; + } + + if (path_copy[0] != '/') { + errno = EINVAL; + rc = -1; + goto out; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path_copy, '/')) == path_copy) { + set = *obj; + goto set_single_path; + } + + *endp = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + + if (rc) + goto out; + +set_single_path: + endp++; + rc = json_pointer_set_single_path(set, endp, value); +out: + free(path_copy); + return rc; +} + diff --git a/json_pointer.h b/json_pointer.h index d661b7b..49e9c29 100644 --- a/json_pointer.h +++ b/json_pointer.h @@ -27,6 +27,11 @@ extern "C" { * Internally, this is equivalent to doing a series of 'json_object_object_get()' * and 'json_object_array_get_idx()' along the given 'path'. * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'res' param will be treated as an argument for 'path' + * Example: json_pointer_get(obj, "/foo/%d/%s", &res, 0, bar) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * * @param obj the json_object instance/tree from where to retrieve sub-objects * @param path a (RFC6901) string notation for the sub-object to retrieve * @param res a pointer where to store a reference to the json_object @@ -36,6 +41,24 @@ extern "C" { */ int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res); +/** + * This is a variant of 'json_pointer_get()' that supports printf() style arguments. + * + * Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param res a pointer where to store a reference to the json_object + * associated with the given path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...); + /** * Sets JSON object 'value' in the 'obj' tree at the location specified * by the 'path'. 'path' is JSON pointer notation as defined in RFC 6901 @@ -54,6 +77,11 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje * That also implies that 'json_pointer_set()' does not do any refcount incrementing. * (Just that single decrement that was mentioned above). * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'value' param will be treated as an argument for 'path' + * Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * * @param obj the json_object instance/tree to which to add a sub-object * @param path a (RFC6901) string notation for the sub-object to set in the tree * @param value object to set at path @@ -62,6 +90,24 @@ int json_pointer_get(struct json_object *obj, const char *path, struct json_obje */ int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value); +/** + * This is a variant of 'json_pointer_set()' that supports printf() style arguments. + * + * Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param value object to set at path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...); + + #ifdef __cplusplus } #endif diff --git a/json_tokener.c b/json_tokener.c index 65652f4..6ddea11 100644 --- a/json_tokener.c +++ b/json_tokener.c @@ -31,6 +31,7 @@ #include "json_object.h" #include "json_tokener.h" #include "json_util.h" +#include "strdup_compat.h" #ifdef HAVE_LOCALE_H #include @@ -41,13 +42,6 @@ #define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) -#if !HAVE_STRDUP && defined(_MSC_VER) - /* MSC has the version as _strdup */ -# define strdup _strdup -#elif !HAVE_STRDUP -# error You do not have strdup on your system. -#endif /* HAVE_STRDUP */ - #if !HAVE_STRNCASECMP && defined(_MSC_VER) /* MSC has the version as _strnicmp */ # define strncasecmp _strnicmp diff --git a/printbuf.c b/printbuf.c index a40b89d..2745339 100644 --- a/printbuf.c +++ b/printbuf.c @@ -27,6 +27,7 @@ #include "debug.h" #include "printbuf.h" +#include "vasprintf_compat.h" static int printbuf_extend(struct printbuf *p, int min_size); @@ -110,47 +111,6 @@ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) return 0; } -#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) -# define vsnprintf _vsnprintf -#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ -# error Need vsnprintf! -#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ - -#if !defined(HAVE_VASPRINTF) -/* CAW: compliant version of vasprintf */ -static int vasprintf(char **buf, const char *fmt, va_list ap) -{ -#ifndef WIN32 - static char _T_emptybuffer = '\0'; -#endif /* !defined(WIN32) */ - int chars; - char *b; - - if(!buf) { return -1; } - -#ifdef WIN32 - chars = _vscprintf(fmt, ap)+1; -#else /* !defined(WIN32) */ - /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite - our buffer like on some 64bit sun systems.... but hey, its time to move on */ - chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; - if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ -#endif /* defined(WIN32) */ - - b = (char*)malloc(sizeof(char)*chars); - if(!b) { return -1; } - - if((chars = vsprintf(b, fmt, ap)) < 0) - { - free(b); - } else { - *buf = b; - } - - return chars; -} -#endif /* !HAVE_VASPRINTF */ - int sprintbuf(struct printbuf *p, const char *msg, ...) { va_list ap; diff --git a/strdup_compat.h b/strdup_compat.h new file mode 100644 index 0000000..51af81e --- /dev/null +++ b/strdup_compat.h @@ -0,0 +1,11 @@ +#ifndef __strdup_compat_h +#define __strdup_compat_h + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#endif diff --git a/tests/test_json_pointer.c b/tests/test_json_pointer.c index aa495cc..f42d010 100644 --- a/tests/test_json_pointer.c +++ b/tests/test_json_pointer.c @@ -132,6 +132,10 @@ static void test_recursion_get() assert(json_object_is_type(jo2, json_type_string)); assert(0 == strcmp("1", json_object_get_string(jo2))); + assert(0 == json_pointer_getf(jo1, &jo2, "/%s/%d/%s/%d/%s", "arr", 0, "obj", 2, "obj2")); + assert(json_object_is_type(jo2, json_type_string)); + assert(0 == strcmp("1", json_object_get_string(jo2))); + assert(jo1 != NULL); assert(0 == json_pointer_get(jo1, "/obj/obj/obj/0/obj1", &jo2)); assert(json_object_is_type(jo2, json_type_int)); @@ -180,6 +184,9 @@ static void test_wrong_inputs_get() assert(0 != json_pointer_get(jo1, "/foo/a", NULL)); assert(errno == EINVAL); errno = 0; + assert(0 != json_pointer_getf(jo1, NULL, "/%s/a", "foo")); + assert(errno == EINVAL); + errno = 0; assert(0 != json_pointer_get(jo1, "/foo/-", NULL)); assert(errno == EINVAL); errno = 0; @@ -188,7 +195,10 @@ static void test_wrong_inputs_get() assert(errno == ENOENT); errno = 0; /* Test non-optimized array path */ - assert(0 != json_pointer_get(jo1, "/foo/22", NULL)); + assert(0 != json_pointer_getf(jo1, NULL, "%s", "/foo/22")); + assert(errno == ENOENT); + errno = 0; + assert(0 != json_pointer_getf(jo1, NULL, "/%s/%d", "foo", 22)); assert(errno == ENOENT); errno = 0; assert(0 != json_pointer_get(jo1, "/foo/-1", NULL)); @@ -220,6 +230,7 @@ static void test_example_set() assert(0 == json_pointer_set(&jo1, "/fud/gaw", jo2)); /* re-using jo2 from above */ printf("PASSED - SET - /fug/gaw == [1,2,3]\n"); assert(0 == json_pointer_set(&jo1, "/fud/gaw/0", json_object_new_int(0))); + assert(0 == json_pointer_setf(&jo1, json_object_new_int(0), "%s%s/%d", "/fud", "/gaw", 0)); printf("PASSED - SET - /fug/gaw == [0,2,3]\n"); assert(0 == json_pointer_set(&jo1, "/fud/gaw/-", json_object_new_int(4))); printf("PASSED - SET - /fug/gaw == [0,2,3,4]\n"); diff --git a/vasprintf_compat.h b/vasprintf_compat.h new file mode 100644 index 0000000..51e234b --- /dev/null +++ b/vasprintf_compat.h @@ -0,0 +1,45 @@ +#ifndef __vasprintf_compat_h +#define __vasprintf_compat_h + +#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) +# define vsnprintf _vsnprintf +#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +#endif /* __vasprintf_compat_h */