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