|
Packit Service |
def718 |
/*
|
|
Packit Service |
def718 |
* Copyright (c) 2016 Alexandru Ardelean.
|
|
Packit Service |
def718 |
*
|
|
Packit Service |
def718 |
* This is free software; you can redistribute it and/or modify
|
|
Packit Service |
def718 |
* it under the terms of the MIT license. See COPYING for details.
|
|
Packit Service |
def718 |
*
|
|
Packit Service |
def718 |
*/
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
#include "config.h"
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
#include "strerror_override.h"
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
#include <stdarg.h>
|
|
Packit Service |
def718 |
#include <stdio.h>
|
|
Packit Service |
def718 |
#include <stdlib.h>
|
|
Packit Service |
def718 |
#include <string.h>
|
|
Packit Service |
def718 |
#include <ctype.h>
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
#include "json_pointer.h"
|
|
Packit Service |
def718 |
#include "strdup_compat.h"
|
|
Packit Service |
def718 |
#include "vasprintf_compat.h"
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/**
|
|
Packit Service |
def718 |
* JavaScript Object Notation (JSON) Pointer
|
|
Packit Service |
def718 |
* RFC 6901 - https://tools.ietf.org/html/rfc6901
|
|
Packit Service |
def718 |
*/
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
int slen = strlen(s);
|
|
Packit Service |
def718 |
int skip = strlen(occur) - 1; /* length of the occurence, minus the char we're replacing */
|
|
Packit Service |
def718 |
char *p = s;
|
|
Packit Service |
def718 |
while ((p = strstr(p, occur))) {
|
|
Packit Service |
def718 |
*p = repl_char;
|
|
Packit Service |
def718 |
p++;
|
|
Packit Service |
def718 |
slen -= skip;
|
|
Packit Service |
def718 |
memmove(p, (p + skip), slen - (p - s) + 1); /* includes null char too */
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
int i, len = strlen(path);
|
|
Packit Service |
def718 |
/* this code-path optimizes a bit, for when we reference the 0-9 index range in a JSON array
|
|
Packit Service |
def718 |
and because leading zeros not allowed */
|
|
Packit Service |
def718 |
if (len == 1) {
|
|
Packit Service |
def718 |
if (isdigit((int)path[0])) {
|
|
Packit Service |
def718 |
*idx = (path[0] - '0');
|
|
Packit Service |
def718 |
goto check_oob;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
/* leading zeros not allowed per RFC */
|
|
Packit Service |
def718 |
if (path[0] == '0') {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
/* RFC states base-10 decimals */
|
|
Packit Service |
def718 |
for (i = 0; i < len; i++) {
|
|
Packit Service |
def718 |
if (!isdigit((int)path[i])) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
*idx = strtol(path, NULL, 10);
|
|
Packit Service |
def718 |
if (*idx < 0) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
check_oob:
|
|
Packit Service |
def718 |
len = json_object_array_length(jo);
|
|
Packit Service |
def718 |
if (*idx >= len) {
|
|
Packit Service |
def718 |
errno = ENOENT;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
return 1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
static int json_pointer_get_single_path(struct json_object *obj, char *path, struct json_object **value)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
if (json_object_is_type(obj, json_type_array)) {
|
|
Packit Service |
def718 |
int32_t idx;
|
|
Packit Service |
def718 |
if (!is_valid_index(obj, path, &idx))
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
obj = json_object_array_get_idx(obj, idx);
|
|
Packit Service |
def718 |
if (obj) {
|
|
Packit Service |
def718 |
if (value)
|
|
Packit Service |
def718 |
*value = obj;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
/* Entry not found */
|
|
Packit Service |
def718 |
errno = ENOENT;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* RFC states that we first must eval all ~1 then all ~0 */
|
|
Packit Service |
def718 |
string_replace_all_occurrences_with_char(path, "~1", '/');
|
|
Packit Service |
def718 |
string_replace_all_occurrences_with_char(path, "~0", '~');
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (!json_object_object_get_ex(obj, path, value)) {
|
|
Packit Service |
def718 |
errno = ENOENT;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
static int json_pointer_set_single_path(
|
|
Packit Service |
def718 |
struct json_object *parent,
|
|
Packit Service |
def718 |
const char *path,
|
|
Packit Service |
def718 |
struct json_object *value)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
if (json_object_is_type(parent, json_type_array)) {
|
|
Packit Service |
def718 |
int32_t idx;
|
|
Packit Service |
def718 |
/* RFC (Chapter 4) states that '-' may be used to add new elements to an array */
|
|
Packit Service |
def718 |
if (path[0] == '-' && path[1] == '\0')
|
|
Packit Service |
def718 |
return json_object_array_add(parent, value);
|
|
Packit Service |
def718 |
if (!is_valid_index(parent, path, &idx))
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
return json_object_array_put_idx(parent, idx, value);
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* path replacements should have been done in json_pointer_get_single_path(),
|
|
Packit Service |
def718 |
and we should still be good here */
|
|
Packit Service |
def718 |
if (json_object_is_type(parent, json_type_object))
|
|
Packit Service |
def718 |
return json_object_object_add(parent, path, value);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* Getting here means that we tried to "dereference" a primitive JSON type (like string, int, bool).
|
|
Packit Service |
def718 |
i.e. add a sub-object to it */
|
|
Packit Service |
def718 |
errno = ENOENT;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
static int json_pointer_get_recursive(
|
|
Packit Service |
def718 |
struct json_object *obj,
|
|
Packit Service |
def718 |
char *path,
|
|
Packit Service |
def718 |
struct json_object **value)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
char *endp;
|
|
Packit Service |
def718 |
int rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* All paths (on each recursion level must have a leading '/' */
|
|
Packit Service |
def718 |
if (path[0] != '/') {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
path++;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
endp = strchr(path, '/');
|
|
Packit Service |
def718 |
if (endp)
|
|
Packit Service |
def718 |
*endp = '\0';
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* If we err-ed here, return here */
|
|
Packit Service |
def718 |
if ((rc = json_pointer_get_single_path(obj, path, &obj)))
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (endp) {
|
|
Packit Service |
def718 |
*endp = '/'; /* Put the slash back, so that the sanity check passes on next recursion level */
|
|
Packit Service |
def718 |
return json_pointer_get_recursive(obj, endp, value);
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* We should be at the end of the recursion here */
|
|
Packit Service |
def718 |
if (value)
|
|
Packit Service |
def718 |
*value = obj;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
char *path_copy = NULL;
|
|
Packit Service |
def718 |
int rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (!obj || !path) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path[0] == '\0') {
|
|
Packit Service |
def718 |
if (res)
|
|
Packit Service |
def718 |
*res = obj;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* pass a working copy to the recursive call */
|
|
Packit Service |
def718 |
if (!(path_copy = strdup(path))) {
|
|
Packit Service |
def718 |
errno = ENOMEM;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
rc = json_pointer_get_recursive(obj, path_copy, res);
|
|
Packit Service |
def718 |
free(path_copy);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
char *path_copy = NULL;
|
|
Packit Service |
def718 |
int rc = 0;
|
|
Packit Service |
def718 |
va_list args;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (!obj || !path_fmt) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
va_start(args, path_fmt);
|
|
Packit Service |
def718 |
rc = vasprintf(&path_copy, path_fmt, args);
|
|
Packit Service |
def718 |
va_end(args);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (rc < 0)
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path_copy[0] == '\0') {
|
|
Packit Service |
def718 |
if (res)
|
|
Packit Service |
def718 |
*res = obj;
|
|
Packit Service |
def718 |
goto out;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
rc = json_pointer_get_recursive(obj, path_copy, res);
|
|
Packit Service |
def718 |
out:
|
|
Packit Service |
def718 |
free(path_copy);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
const char *endp;
|
|
Packit Service |
def718 |
char *path_copy = NULL;
|
|
Packit Service |
def718 |
struct json_object *set = NULL;
|
|
Packit Service |
def718 |
int rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (!obj || !path) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path[0] == '\0') {
|
|
Packit Service |
def718 |
json_object_put(*obj);
|
|
Packit Service |
def718 |
*obj = value;
|
|
Packit Service |
def718 |
return 0;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path[0] != '/') {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* If there's only 1 level to set, stop here */
|
|
Packit Service |
def718 |
if ((endp = strrchr(path, '/')) == path) {
|
|
Packit Service |
def718 |
path++;
|
|
Packit Service |
def718 |
return json_pointer_set_single_path(*obj, path, value);
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* pass a working copy to the recursive call */
|
|
Packit Service |
def718 |
if (!(path_copy = strdup(path))) {
|
|
Packit Service |
def718 |
errno = ENOMEM;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
path_copy[endp - path] = '\0';
|
|
Packit Service |
def718 |
rc = json_pointer_get_recursive(*obj, path_copy, &set);
|
|
Packit Service |
def718 |
free(path_copy);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (rc)
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
endp++;
|
|
Packit Service |
def718 |
return json_pointer_set_single_path(set, endp, value);
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...)
|
|
Packit Service |
def718 |
{
|
|
Packit Service |
def718 |
char *endp;
|
|
Packit Service |
def718 |
char *path_copy = NULL;
|
|
Packit Service |
def718 |
struct json_object *set = NULL;
|
|
Packit Service |
def718 |
va_list args;
|
|
Packit Service |
def718 |
int rc = 0;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (!obj || !path_fmt) {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
return -1;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* pass a working copy to the recursive call */
|
|
Packit Service |
def718 |
va_start(args, path_fmt);
|
|
Packit Service |
def718 |
rc = vasprintf(&path_copy, path_fmt, args);
|
|
Packit Service |
def718 |
va_end(args);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (rc < 0)
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path_copy[0] == '\0') {
|
|
Packit Service |
def718 |
json_object_put(*obj);
|
|
Packit Service |
def718 |
*obj = value;
|
|
Packit Service |
def718 |
goto out;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (path_copy[0] != '/') {
|
|
Packit Service |
def718 |
errno = EINVAL;
|
|
Packit Service |
def718 |
rc = -1;
|
|
Packit Service |
def718 |
goto out;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
/* If there's only 1 level to set, stop here */
|
|
Packit Service |
def718 |
if ((endp = strrchr(path_copy, '/')) == path_copy) {
|
|
Packit Service |
def718 |
set = *obj;
|
|
Packit Service |
def718 |
goto set_single_path;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
*endp = '\0';
|
|
Packit Service |
def718 |
rc = json_pointer_get_recursive(*obj, path_copy, &set);
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
if (rc)
|
|
Packit Service |
def718 |
goto out;
|
|
Packit Service |
def718 |
|
|
Packit Service |
def718 |
set_single_path:
|
|
Packit Service |
def718 |
endp++;
|
|
Packit Service |
def718 |
rc = json_pointer_set_single_path(set, endp, value);
|
|
Packit Service |
def718 |
out:
|
|
Packit Service |
def718 |
free(path_copy);
|
|
Packit Service |
def718 |
return rc;
|
|
Packit Service |
def718 |
}
|
|
Packit Service |
def718 |
|