/* * Copyright (c) 2012 Rogerz Zhang * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * * source here https://github.com/rogerz/jansson/blob/json_path/src/path.c */ #include #include #include "jansson.h" #include "jansson_path.h" ////// memory.c private functions /* C89 allows these to be macros */ #undef malloc #undef free /* memory function pointers */ static json_malloc_t do_malloc = malloc; static json_free_t do_free = free; static void *jsonp_malloc(size_t size) { if (!size) return NULL; return (*do_malloc)(size); } static void jsonp_free(void *ptr) { if (!ptr) return; (*do_free)(ptr); } static char *jsonp_strndup(const char *str, size_t len) { char *new_str = jsonp_malloc(len + 1); if (!new_str) return NULL; memcpy(new_str, str, len); new_str[len] = '\0'; return new_str; } static char *jsonp_strdup(const char *str) { return jsonp_strndup(str, strlen(str)); } ////// error.c private functions static void jsonp_error_set_source(json_error_t *error, const char *source) { size_t length; if (!error || !source) return; length = strlen(source); if (length < JSON_ERROR_SOURCE_LENGTH) { strncpy(error->source, source, JSON_ERROR_SOURCE_LENGTH - 1); } else { size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; memcpy(error->source, "...", 3); strncpy(error->source + 3, source + extra, length - extra + 1); } } static void jsonp_error_init(json_error_t *error, const char *source) { if (error) { error->text[0] = '\0'; error->line = -1; error->column = -1; error->position = 0; if (source) jsonp_error_set_source(error, source); else error->source[0] = '\0'; } } static void jsonp_error_vset(json_error_t *error, int line, int column, size_t position, enum json_error_code code, const char *msg, va_list ap) { if (!error) return; if (error->text[0] != '\0') { /* error already set */ return; } error->line = line; error->column = column; error->position = (int)position; vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap); error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0'; error->text[JSON_ERROR_TEXT_LENGTH - 1] = code; } static void jsonp_error_set(json_error_t *error, int line, int column, size_t position, enum json_error_code code, const char *msg, ...) { va_list ap; va_start(ap, msg); jsonp_error_vset(error, line, column, position, code, msg, ap); va_end(ap); } // original path.c from jansson fork json_t *json_path_get(const json_t *json, const char *path) { static const char root_chr = '$', array_open = '['; static const char *path_delims = ".[", *array_close = "]"; const json_t *cursor; char *token, *buf, *peek, *endptr, delim = '\0'; const char *expect; if (!json || !path || path[0] != root_chr) return NULL; else buf = jsonp_strdup(path); peek = buf + 1; cursor = json; token = NULL; expect = path_delims; while (peek && *peek && cursor) { char *last_peek = peek; peek = strpbrk(peek, expect); if (peek) { if (!token && peek != last_peek) goto fail; delim = *peek; *peek++ = '\0'; } else if (expect != path_delims || !token) { goto fail; } if (expect == path_delims) { if (token) { cursor = json_object_get(cursor, token); } expect = (delim == array_open ? array_close : path_delims); token = peek; } else if (expect == array_close) { size_t index = strtol(token, &endptr, 0); if (*endptr) goto fail; cursor = json_array_get(cursor, index); token = NULL; expect = path_delims; } else { goto fail; } } jsonp_free(buf); return (json_t *)cursor; fail: jsonp_free(buf); return NULL; } int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) { static const char root_chr = '$', array_open = '[', object_delim = '.'; static const char *const path_delims = ".[", *array_close = "]"; json_t *cursor, *parent = NULL; char *token, *buf = NULL, *peek, delim = '\0'; const char *expect; int index_saved = -1; jsonp_error_init(error, ""); if (!json || !path || flags || !value) { jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument"); goto fail; } else { buf = jsonp_strdup(path); } if (buf[0] != root_chr) { jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $"); goto fail; } peek = buf + 1; cursor = json; token = NULL; expect = path_delims; while (peek && *peek && cursor) { char *last_peek = peek; peek = strpbrk(last_peek, expect); if (peek) { if (!token && peek != last_peek) { jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars"); goto fail; } delim = *peek; *peek++ = '\0'; } else { // end of path if (expect == path_delims) { break; } else { jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "missing ']'?"); goto fail; } } if (expect == path_delims) { if (token) { if (token[0] == '\0') { jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token"); goto fail; } parent = cursor; cursor = json_object_get(parent, token); if (!cursor) { if (!json_is_object(parent)) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); goto fail; } if (delim == object_delim) { cursor = json_object(); json_object_set_new(parent, token, cursor); } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed"); goto fail; } } } expect = (delim == array_open ? array_close : path_delims); token = peek; } else if (expect == array_close) { char *endptr; size_t index; parent = cursor; if (!json_is_array(parent)) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected"); goto fail; } index = strtol(token, &endptr, 0); if (*endptr) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index"); goto fail; } cursor = json_array_get(parent, index); if (!cursor) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound"); goto fail; } index_saved = index; token = NULL; expect = path_delims; } else { assert(1); jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move"); goto fail; } } if (token) { if (json_is_object(cursor)) { json_object_set(cursor, token, value); } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); goto fail; } cursor = json_object_get(cursor, token); } else if (index_saved != -1 && json_is_array(parent)) { json_array_set(parent, index_saved, value); cursor = json_array_get(parent, index_saved); } else { jsonp_error_set(error, -1, -1, 0, json_error_item_not_found, "invalid path"); goto fail; } json_decref(value); jsonp_free(buf); return 0; fail: json_decref(value); jsonp_free(buf); return -1; }