--- /dev/null
+/*
+ * Copyright (c) 2012 Rogerz Zhang <rogerz.zhang@gmail.com>
+ *
+ * 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 <string.h>
+#include <assert.h>
+
+#include <jansson.h>
+#include "jansson_private.h"
+
+
+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, "<path>");
+
+ 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, 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, peek - buf, 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;
+}