You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

json_pointer.c 6.8 kB

7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * Copyright (c) 2016 Alexandru Ardelean.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the MIT license. See COPYING for details.
  6. *
  7. */
  8. #include "config.h"
  9. #include "strerror_override.h"
  10. #include <stdarg.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include "json_pointer.h"
  16. #include "strdup_compat.h"
  17. #include "vasprintf_compat.h"
  18. /**
  19. * JavaScript Object Notation (JSON) Pointer
  20. * RFC 6901 - https://tools.ietf.org/html/rfc6901
  21. */
  22. static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char)
  23. {
  24. int slen = strlen(s);
  25. int skip = strlen(occur) - 1; /* length of the occurrence, minus the char we're replacing */
  26. char *p = s;
  27. while ((p = strstr(p, occur))) {
  28. *p = repl_char;
  29. p++;
  30. slen -= skip;
  31. memmove(p, (p + skip), slen - (p - s) + 1); /* includes null char too */
  32. }
  33. }
  34. static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx)
  35. {
  36. int i, len = strlen(path);
  37. /* this code-path optimizes a bit, for when we reference the 0-9 index range
  38. * in a JSON array and because leading zeros not allowed
  39. */
  40. if (len == 1) {
  41. if (isdigit((unsigned char)path[0])) {
  42. *idx = (path[0] - '0');
  43. goto check_oob;
  44. }
  45. errno = EINVAL;
  46. return 0;
  47. }
  48. /* leading zeros not allowed per RFC */
  49. if (path[0] == '0') {
  50. errno = EINVAL;
  51. return 0;
  52. }
  53. /* RFC states base-10 decimals */
  54. for (i = 0; i < len; i++) {
  55. if (!isdigit((unsigned char)path[i])) {
  56. errno = EINVAL;
  57. return 0;
  58. }
  59. }
  60. *idx = strtol(path, NULL, 10);
  61. if (*idx < 0) {
  62. errno = EINVAL;
  63. return 0;
  64. }
  65. check_oob:
  66. len = json_object_array_length(jo);
  67. if (*idx >= len) {
  68. errno = ENOENT;
  69. return 0;
  70. }
  71. return 1;
  72. }
  73. static int json_pointer_get_single_path(struct json_object *obj, char *path, struct json_object **value)
  74. {
  75. if (json_object_is_type(obj, json_type_array)) {
  76. int32_t idx;
  77. if (!is_valid_index(obj, path, &idx))
  78. return -1;
  79. obj = json_object_array_get_idx(obj, idx);
  80. if (obj) {
  81. if (value)
  82. *value = obj;
  83. return 0;
  84. }
  85. /* Entry not found */
  86. errno = ENOENT;
  87. return -1;
  88. }
  89. /* RFC states that we first must eval all ~1 then all ~0 */
  90. string_replace_all_occurrences_with_char(path, "~1", '/');
  91. string_replace_all_occurrences_with_char(path, "~0", '~');
  92. if (!json_object_object_get_ex(obj, path, value)) {
  93. errno = ENOENT;
  94. return -1;
  95. }
  96. return 0;
  97. }
  98. static int json_pointer_set_single_path(
  99. struct json_object *parent,
  100. const char *path,
  101. struct json_object *value)
  102. {
  103. if (json_object_is_type(parent, json_type_array)) {
  104. int32_t idx;
  105. /* RFC (Chapter 4) states that '-' may be used to add new elements to an array */
  106. if (path[0] == '-' && path[1] == '\0')
  107. return json_object_array_add(parent, value);
  108. if (!is_valid_index(parent, path, &idx))
  109. return -1;
  110. return json_object_array_put_idx(parent, idx, value);
  111. }
  112. /* path replacements should have been done in json_pointer_get_single_path(),
  113. * and we should still be good here
  114. */
  115. if (json_object_is_type(parent, json_type_object))
  116. return json_object_object_add(parent, path, value);
  117. /* Getting here means that we tried to "dereference" a primitive JSON type
  118. * (like string, int, bool).i.e. add a sub-object to it
  119. */
  120. errno = ENOENT;
  121. return -1;
  122. }
  123. static int json_pointer_get_recursive(
  124. struct json_object *obj,
  125. char *path,
  126. struct json_object **value)
  127. {
  128. char *endp;
  129. int rc;
  130. /* All paths (on each recursion level must have a leading '/' */
  131. if (path[0] != '/') {
  132. errno = EINVAL;
  133. return -1;
  134. }
  135. path++;
  136. endp = strchr(path, '/');
  137. if (endp)
  138. *endp = '\0';
  139. /* If we err-ed here, return here */
  140. if ((rc = json_pointer_get_single_path(obj, path, &obj)))
  141. return rc;
  142. if (endp) {
  143. /* Put the slash back, so that the sanity check passes on next recursion level */
  144. *endp = '/';
  145. return json_pointer_get_recursive(obj, endp, value);
  146. }
  147. /* We should be at the end of the recursion here */
  148. if (value)
  149. *value = obj;
  150. return 0;
  151. }
  152. int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res)
  153. {
  154. char *path_copy = NULL;
  155. int rc;
  156. if (!obj || !path) {
  157. errno = EINVAL;
  158. return -1;
  159. }
  160. if (path[0] == '\0') {
  161. if (res)
  162. *res = obj;
  163. return 0;
  164. }
  165. /* pass a working copy to the recursive call */
  166. if (!(path_copy = strdup(path))) {
  167. errno = ENOMEM;
  168. return -1;
  169. }
  170. rc = json_pointer_get_recursive(obj, path_copy, res);
  171. free(path_copy);
  172. return rc;
  173. }
  174. int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...)
  175. {
  176. char *path_copy = NULL;
  177. int rc = 0;
  178. va_list args;
  179. if (!obj || !path_fmt) {
  180. errno = EINVAL;
  181. return -1;
  182. }
  183. va_start(args, path_fmt);
  184. rc = vasprintf(&path_copy, path_fmt, args);
  185. va_end(args);
  186. if (rc < 0)
  187. return rc;
  188. if (path_copy[0] == '\0') {
  189. if (res)
  190. *res = obj;
  191. goto out;
  192. }
  193. rc = json_pointer_get_recursive(obj, path_copy, res);
  194. out:
  195. free(path_copy);
  196. return rc;
  197. }
  198. int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value)
  199. {
  200. const char *endp;
  201. char *path_copy = NULL;
  202. struct json_object *set = NULL;
  203. int rc;
  204. if (!obj || !path) {
  205. errno = EINVAL;
  206. return -1;
  207. }
  208. if (path[0] == '\0') {
  209. json_object_put(*obj);
  210. *obj = value;
  211. return 0;
  212. }
  213. if (path[0] != '/') {
  214. errno = EINVAL;
  215. return -1;
  216. }
  217. /* If there's only 1 level to set, stop here */
  218. if ((endp = strrchr(path, '/')) == path) {
  219. path++;
  220. return json_pointer_set_single_path(*obj, path, value);
  221. }
  222. /* pass a working copy to the recursive call */
  223. if (!(path_copy = strdup(path))) {
  224. errno = ENOMEM;
  225. return -1;
  226. }
  227. path_copy[endp - path] = '\0';
  228. rc = json_pointer_get_recursive(*obj, path_copy, &set);
  229. free(path_copy);
  230. if (rc)
  231. return rc;
  232. endp++;
  233. return json_pointer_set_single_path(set, endp, value);
  234. }
  235. int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...)
  236. {
  237. char *endp;
  238. char *path_copy = NULL;
  239. struct json_object *set = NULL;
  240. va_list args;
  241. int rc = 0;
  242. if (!obj || !path_fmt) {
  243. errno = EINVAL;
  244. return -1;
  245. }
  246. /* pass a working copy to the recursive call */
  247. va_start(args, path_fmt);
  248. rc = vasprintf(&path_copy, path_fmt, args);
  249. va_end(args);
  250. if (rc < 0)
  251. return rc;
  252. if (path_copy[0] == '\0') {
  253. json_object_put(*obj);
  254. *obj = value;
  255. goto out;
  256. }
  257. if (path_copy[0] != '/') {
  258. errno = EINVAL;
  259. rc = -1;
  260. goto out;
  261. }
  262. /* If there's only 1 level to set, stop here */
  263. if ((endp = strrchr(path_copy, '/')) == path_copy) {
  264. set = *obj;
  265. goto set_single_path;
  266. }
  267. *endp = '\0';
  268. rc = json_pointer_get_recursive(*obj, path_copy, &set);
  269. if (rc)
  270. goto out;
  271. set_single_path:
  272. endp++;
  273. rc = json_pointer_set_single_path(set, endp, value);
  274. out:
  275. free(path_copy);
  276. return rc;
  277. }