From 97dd7d5103f4038f3333b54615f5d233852acdb4 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 16 Nov 2016 11:34:29 +0200 Subject: [PATCH 1/5] json_pointer.c: fix whitespace Signed-off-by: Alexandru Ardelean --- json_pointer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_pointer.c b/json_pointer.c index f06aed0..a445e3b 100644 --- a/json_pointer.c +++ b/json_pointer.c @@ -159,7 +159,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; From 742e059da1f5bd3c458f38bfbeb6e46ea1145f7b Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 16 Nov 2016 11:55:41 +0200 Subject: [PATCH 2/5] json_pointer: add json_pointer_getf/setf() function variants These include support for printf() style args for path. Adds support for calling with 'json_pointer_getf(obj, &res, "/foo/%d/%s", 0, bar)' style args. Makes it easier for doing more dynamic stuff/magic, without needing to use vasprintf() externally. Signed-off-by: Alexandru Ardelean --- json_pointer.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ json_pointer.h | 46 +++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/json_pointer.c b/json_pointer.c index a445e3b..582ca28 100644 --- a/json_pointer.c +++ b/json_pointer.c @@ -8,6 +8,7 @@ #include "config.h" +#include #include #include #include @@ -192,6 +193,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 +269,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 From c0da680f13a089a124c1c841f55288045a814243 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 16 Nov 2016 16:22:13 +0200 Subject: [PATCH 3/5] test_json_pointer: update test with a few printf variants Signed-off-by: Alexandru Ardelean --- tests/test_json_pointer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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"); From 8cb86a583afcb858d14b68216e3232f4e8f3962a Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 16 Nov 2016 17:00:45 +0200 Subject: [PATCH 4/5] strdup_compat.h: re-spin this compat header ; use math_compat.h as template Signed-off-by: Alexandru Ardelean --- CMakeLists.txt | 1 + Makefile.am | 1 + json-c.vcxproj | 3 ++- json-c.vcxproj.filters | 5 ++++- json_object.c | 8 +------- json_pointer.c | 1 + json_tokener.c | 8 +------- strdup_compat.h | 11 +++++++++++ 8 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 strdup_compat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd38ad..473e9b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ set(JSON_C_HEADERS ./json_util.h ./linkhash.h ./math_compat.h + ./strdup_compat.h ./printbuf.h ./random_seed.h ) diff --git a/Makefile.am b/Makefile.am index 41b9e6e..64b1a3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ libjson_cinclude_HEADERS = \ json_visit.h \ linkhash.h \ math_compat.h \ + strdup_compat.h \ printbuf.h \ random_seed.h diff --git a/json-c.vcxproj b/json-c.vcxproj index 8a4f265..ab7b5e1 100644 --- a/json-c.vcxproj +++ b/json-c.vcxproj @@ -151,6 +151,7 @@ copy json_config.h.win32 json_config.h + @@ -164,4 +165,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..3c6f43d 100644 --- a/json-c.vcxproj.filters +++ b/json-c.vcxproj.filters @@ -80,6 +80,9 @@ Header Files + + Header Files + Header Files @@ -93,4 +96,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 582ca28..9cf3f16 100644 --- a/json_pointer.c +++ b/json_pointer.c @@ -16,6 +16,7 @@ #include #include "json_pointer.h" +#include "strdup_compat.h" /** * JavaScript Object Notation (JSON) Pointer 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/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 From 47f32a76ef9d79fd498a6890392497ec674f10ef Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 16 Nov 2016 17:04:41 +0200 Subject: [PATCH 5/5] vasprintf_compat.h: spin-off this compat header ; use math_compat.h as template Signed-off-by: Alexandru Ardelean --- CMakeLists.txt | 1 + Makefile.am | 1 + json-c.vcxproj | 1 + json-c.vcxproj.filters | 3 +++ json_pointer.c | 1 + printbuf.c | 42 +-------------------------------------- vasprintf_compat.h | 45 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 vasprintf_compat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 473e9b7..8ae0950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ set(JSON_C_HEADERS ./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 64b1a3d..126ceeb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ libjson_cinclude_HEADERS = \ 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 ab7b5e1..3a6da61 100644 --- a/json-c.vcxproj +++ b/json-c.vcxproj @@ -152,6 +152,7 @@ copy json_config.h.win32 json_config.h + diff --git a/json-c.vcxproj.filters b/json-c.vcxproj.filters index 3c6f43d..7fd4dd0 100644 --- a/json-c.vcxproj.filters +++ b/json-c.vcxproj.filters @@ -83,6 +83,9 @@ Header Files + + Header Files + Header Files diff --git a/json_pointer.c b/json_pointer.c index 9cf3f16..3648f8d 100644 --- a/json_pointer.c +++ b/json_pointer.c @@ -17,6 +17,7 @@ #include "json_pointer.h" #include "strdup_compat.h" +#include "vasprintf_compat.h" /** * JavaScript Object Notation (JSON) Pointer 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/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 */