• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/json-c/json_pointer.c

  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