json_pointer: extend setter & getter with printf() style argumentstags/json-c-0.13-20171207
| @@ -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 | |||
| ) | |||
| @@ -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 | |||
| @@ -151,6 +151,8 @@ copy json_config.h.win32 json_config.h | |||
| <ClInclude Include="json_util.h" /> | |||
| <ClInclude Include="linkhash.h" /> | |||
| <ClInclude Include="math_compat.h" /> | |||
| <ClInclude Include="strdup_compat.h" /> | |||
| <ClInclude Include="vasprintf_compat.h" /> | |||
| <ClInclude Include="printbuf.h" /> | |||
| <ClInclude Include="random_seed.h" /> | |||
| </ItemGroup> | |||
| @@ -164,4 +166,4 @@ copy json_config.h.win32 json_config.h | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
| <ImportGroup Label="ExtensionTargets"> | |||
| </ImportGroup> | |||
| </Project> | |||
| </Project> | |||
| @@ -80,6 +80,12 @@ | |||
| <ClInclude Include="math_compat.h"> | |||
| <Filter>Header Files</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="strdup_compat.h"> | |||
| <Filter>Header Files</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="vasprintf_compat.h"> | |||
| <Filter>Header Files</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="random_seed.h"> | |||
| <Filter>Header Files</Filter> | |||
| </ClInclude> | |||
| @@ -93,4 +99,4 @@ | |||
| <None Include="README-WIN32.html" /> | |||
| <None Include="README.html" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| </Project> | |||
| @@ -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 */ | |||
| @@ -8,6 +8,7 @@ | |||
| #include "config.h" | |||
| #include <stdarg.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <errno.h> | |||
| @@ -15,6 +16,8 @@ | |||
| #include <ctype.h> | |||
| #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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 <locale.h> | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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"); | |||
| @@ -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 */ | |||