GCC Code Coverage Report


Directory: ./
File: submodules/json-c/json_pointer.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 155 0.0%
Branches: 0 86 0.0%

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