#if !defined(CSONPATH_JSON) || !defined(CSONPATH_NULL) || !defined(CSONPATH_GET) || \
!defined(CSONPATH_IS_STR) || \
!defined(CSONPATH_AT) || !defined(CSONPATH_IS_OBJ) || !defined(CSONPATH_IS_ARRAY) || \
!defined(CSONPATH_CALLBACK) || !defined(CSONPATH_CALLBACK_DATA) || \
!defined(CSONPATH_EQUAL_STR) || !defined(CSONPATH_CALL_CALLBACK) || \
!defined(CSONPATH_FOREACH_EXT) || !defined(CSONPATH_APPEND_AT) || \
!defined(CSONPATH_REMOVE_CHILD) || !defined(CSONPATH_NEED_FOREACH_REDO) || \
!defined(CSONPATH_ARRAY_APPEND) || !defined(CSONPATH_REMOVE)
# error "some definitions are missing"
#endif
#ifndef CSONPATH_API_PREFIX
#define CSONPATH_API_PREFIX
#endif
#define CSONPATH_CAT_(a, b) a ## b
#define CSONPATH_CAT(a, b) CSONPATH_CAT_(a, b)
#define CSONPATH_FUNC(name) CSONPATH_CAT(CSONPATH_API_PREFIX, name)
#ifndef CSONPATH_CORE_H_
#define CSONPATH_CORE_H_
#include <stdio.h>
#include <stdlib.h>
#ifndef CSONPATH_CLEANUP_STR
# define CSONPATH_CLEANUP_STR
#endif
#ifndef CSONPATH_NO_REGEX
# if defined CSONPATH_TINY_REGEX
# include <tiny-regex-c/re.h>
typedef re_t csonpath_reg_t;
# elif defined CSONPATH_PCRE2
# define PCRE2_CODE_UNIT_WIDTH 8
# include <pcre2.h>
typedef pcre2_code * csonpath_reg_t;
# elif defined CSONPATH_PCRE2_POSIX
# include <pcre2posix.h>
typedef regex_t csonpath_reg_t;
# else
# include <regex.h>
typedef regex_t csonpath_reg_t;
# endif
#if defined CSONPATH_TINY_REGEX
# define CSONPATH_HAVE_TINY_REGEX 1
#else
# define CSONPATH_HAVE_TINY_REGEX 0
#endif
#define CSONPATH_UNUSED __attribute__((unused))
#define MAY_ALIAS __attribute__((__may_alias__))
#ifndef CSONPATH_STATINLINE
# define CSONPATH_STATINLINE static inline
#endif
enum {
CSONPATH_AUTO_ROOT = 1,
CSONPATH_NO_DETROY = (1 << 1),
CSONPATH_RETURN_EMPTY_ARRAY = (1 << 2),
CSONPATH_REG_INCOMPLETTE = (1 << 3)
};
enum csonpath_instuction_raw {
CSONPATH_INST_ROOT,
CSONPATH_INST_GET_OBJ,
CSONPATH_INST_GET_SUBPATH,
CSONPATH_INST_GET_ARRAY_SMALL,
CSONPATH_INST_GET_ARRAY_BIG,
CSONPATH_INST_FILTER_KEY_EXIST,
CSONPATH_INST_FILTER_KEY_TRUE,
CSONPATH_INST_FILTER_KEY_FALSE,
CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ,
CSONPATH_INST_FILTER_KEY_INFERIOR_EQ,
CSONPATH_INST_FILTER_KEY_SUPERIOR,
CSONPATH_INST_FILTER_KEY_INFERIOR,
CSONPATH_INST_FILTER_KEY_EQ,
CSONPATH_INST_FILTER_KEY_NOT_EQ,
CSONPATH_INST_FILTER_KEY_REG_EQ,
CSONPATH_INST_FILTER_AND,
CSONPATH_INST_GET_ALL,
CSONPATH_INST_GET_UNION,
CSONPATH_INST_UNION_JMP,
CSONPATH_INST_UNION_END,
CSONPATH_INST_FIND_ALL,
CSONPATH_INST_TYPECHECK,
CSONPATH_INST_RANGE,
CSONPATH_INST_OR,
CSONPATH_INST_END,
CSONPATH_SWITCH_ROOT,
CSONPATH_INST_BROKEN
};
static int csonpath_instuction_len[] = {
1,
-1,
1,
2,
5,
1,
1,
1,
2,
2,
2,
2,
2,
2,
2,
1,
1,
1,
1,
1,
-1,
2,
1,
1,
1,
2,
1,
};
CSONPATH_UNUSED static const char *csonpath_instuction_str[] = {
"ROOT",
"GET_OBJ",
"GET_SUBPATH",
"GET_ARRAY_SMALL",
"GET_ARRAY_BIG",
"FILTER_KEY_EXIST",
"FILTER_KEY_TRUE",
"FILTER_KEY_FALSE",
"FILTER_KEY_SUPERIOR_EQ",
"FILTER_KEY_INFERIOR_EQ",
"FILTER_KEY_SUPERIOR",
"FILTER_KEY_INFERIOR",
"FILTER_KEY_EQ",
"FILTER_KEY_NOT_EQ",
"FILTER_KEY_REG_EQ",
"FILTER_AND",
"GET_ALL",
"UNION",
"UNION_JMP",
"UNION_END",
"FIND_ALL",
"TYPECHECK",
"RANGE",
"OR",
"END",
"SWITCH_ROOT",
"BROKEN"
};
enum {
CSONPATH_NONE,
CSONPATH_INTEGER,
CSONPATH_STR
};
#ifdef CSONPATH_USE_PREFIX
#define EXTRA_ROOTS_T void *
#else
#define EXTRA_ROOTS_T CSONPATH_JSON
#endif
struct csonpath {
char *compile_error;
int flags;
EXTRA_ROOTS_T extra_roots;
void *backend_ctx;
#if !defined(CSONPATH_NO_REGEX)
int regex_cnt;
csonpath_reg_t *regexs;
#ifdef CSONPATH_PCRE2
pcre2_match_data **match_datas;
#endif
#endif
char data[];
};
# if defined CSONPATH_TINY_REGEX
static inline int csonpath_reg_compile(struct csonpath *cjp, int idx, const char *pat, char **errbuff) {
(void)errbuff;
cjp->regexs[idx] = re_compile(pat);
return cjp->regexs[idx] ? 0 : -1;
}
static inline _Bool csonpath_reg_exec(csonpath_reg_t reg, const char *str) {
int match_len = 0;
re_matchp(reg, str, &match_len);
return !!match_len;
}
static inline void csonpath_reg_free(csonpath_reg_t reg) {
free(reg);
}
# elif !defined CSONPATH_PCRE2
static inline int csonpath_reg_compile(struct csonpath *cjp, int idx, const char *pat, char **errbuff)
{
(void)errbuff;
return regcomp(&cjp->regexs[idx], pat, 0);
}
static inline _Bool csonpath_reg_exec(csonpath_reg_t reg, const char *str)
{
return regexec(®, str, 0, NULL, 0) == 0;
}
static inline void csonpath_reg_free(csonpath_reg_t reg) { regfree(®); }
# else
static inline int csonpath_reg_compile(struct csonpath *cjp, int regex_idx,
const char *pat, char **errbuff)
{
int errorcode;
PCRE2_SIZE erroroffset;
cjp->regexs[regex_idx] = pcre2_compile((unsigned char *)pat,
PCRE2_ZERO_TERMINATED, 0,
&errorcode, &erroroffset, NULL);
if (!cjp->regexs[regex_idx]) {
static PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
*errbuff = (char *)buffer;
return -1;
}
cjp->match_datas[regex_idx] = pcre2_match_data_create_from_pattern(cjp->regexs[regex_idx],
NULL);
pcre2_jit_compile(cjp->regexs[regex_idx], PCRE2_JIT_COMPLETE);
return 0;
}
# endif
#endif
struct csonpath_child_info {
int type;
MAY_ALIAS union {
int idx;
const char *key;
};
};
#define CSONPATH_ERROR_MAX_SIZE 1024
#define CSONPATH_TMP_BUF_SIZE 256
#define CSONPATH_FILTER_PUSH(dst, cnt, c, walk_ptr) do { \
if ((cnt + 1) >= CSONPATH_TMP_BUF_SIZE) { \
CSONPATH_COMPILE_ERR(tmp, walk_ptr - orig, "%s", "filter key too long"); \
goto error; \
} \
(dst)[(cnt)++] = (c); \
} while (0)
#define CSONPATH_COMPILE_ERR(tmp, idx, args...) do { \
int ltmp = strlen(tmp), lidx, oidx = idx; \
if (cjp->compile_error) \
free(cjp->compile_error); \
cjp->compile_error = malloc(ltmp * 2 + CSONPATH_ERROR_MAX_SIZE); \
if (!cjp->compile_error) \
goto error; \
lidx = snprintf(cjp->compile_error, CSONPATH_ERROR_MAX_SIZE, "column %d:\n", oidx); \
strcpy(cjp->compile_error + lidx, tmp); \
ltmp += lidx; \
cjp->compile_error[ltmp++] = '\n'; \
for (; oidx; --oidx) {cjp->compile_error[ltmp++] = ' ';} \
snprintf(cjp->compile_error + ltmp, CSONPATH_ERROR_MAX_SIZE - lidx, "\\-- "args); \
goto error; \
} while (0)
#define CSONPATH_REQUIRE_ERR(c, on) do { \
CSONPATH_COMPILE_ERR(tmp, on - orig, \
"'%c' require", c); \
goto error; \
} while (0)
#define CSONPATH_SKIP_2(c, check, on) do { \
if (check) { \
CSONPATH_REQUIRE_ERR(c, on); \
} \
++on; \
} while (0)
#define CSONPATH_SKIP(c, on) \
CSONPATH_SKIP_2(c, *on != c, on)
#define CSONPATH_BYTE_PER_INST 2
#define CSONPATH_MAX_REGEX 127
static int csonpath_compile_(struct csonpath *cjp, const char path[static 1], int);
static int csonpath_compile_do(struct csonpath *cjp, const char orig[static 1],
const char walker[static 1], char tmp[static 1],
int *inst_idx, int flag, int end_path, const char **end_sentinel);
static inline void csonpath_push_char(struct csonpath cjp[static 1], int inst, int *inst_idx)
{
cjp->data[(*inst_idx)++] = inst;
}
static const char *csonpath_walker_next_inst(const char *walker)
{
int l = csonpath_instuction_len[(int)*walker];
if (l < 0)
return &walker[strlen(walker + 1) + 2];
return &walker[l];
}
static void csonpath_print_instruction(const struct csonpath cjp[const static 1])
{
const char *walker = cjp->data, *origin = cjp->data;
if (*walker == CSONPATH_INST_BROKEN) {
printf("0: BROKEN\n");
return;
}
int cnt = 0;
int cnt_sub = 0;
for (;cnt_sub > 0 || *walker != CSONPATH_INST_END; walker = csonpath_walker_next_inst(walker)) {
for (int i = 0; i < cnt_sub; ++i) {
printf(" ");
}
printf("%d: %s\n", (int)(intptr_t)(walker - origin), csonpath_instuction_str[(int)*walker]);
if (*walker == CSONPATH_INST_END)
--cnt_sub;
else if (*walker == CSONPATH_INST_GET_SUBPATH)
++cnt_sub;
if (cnt++ == 1000) {
printf("%d: TOO MUCH INSTRUCTION, BAIL OUT\n", (int)(intptr_t)(walker - origin));
return;
}
}
printf("%d: END\n", (int)(intptr_t)(walker - origin));
}
static inline _Bool csonpath_is_dot_operand(int c)
{
return isalnum(c) || c == '_' || c == '-';
}
static void csonpath_fill_walker_with_int(struct csonpath cjp[static 1], int *inst_idx,
int num)
{
if (num < 100) {
csonpath_push_char(cjp, CSONPATH_INST_GET_ARRAY_SMALL, inst_idx);
cjp->data[(*inst_idx)++] = num;
} else {
csonpath_push_char(cjp, CSONPATH_INST_GET_ARRAY_BIG, inst_idx);
cjp->data[(*inst_idx)++] = (char)num;
cjp->data[(*inst_idx)++] = (char)(num >> 8);
cjp->data[(*inst_idx)++] = (char)(num >> 16);
cjp->data[(*inst_idx)++] = (char)(num >> 24);
}
}
static int csonpath_int_from_walker(int operand_instruction, const char **walker)
{
int ret = -1;
switch(operand_instruction) {
case CSONPATH_INST_GET_ARRAY_SMALL:
ret = (*walker)[0];
(*walker) += 1;
break;
case CSONPATH_INST_GET_ARRAY_BIG:
{
union {int n; char c[4];} to_num =
{ .c= { (*walker)[0], (*walker)[1], (*walker)[2], (*walker)[3] } };
ret = to_num.n;
*walker = &(*walker)[4];
}
default:
break;
}
return ret;
}
static int csonpath_compile_(struct csonpath *cjp, const char path[static 1], int flag)
{
const char *walker = path;
const char *orig = walker;
int inst_idx = 0;
char *tmp;
int ret;
tmp = strdup(path);
if (!tmp) {
return -1;
}
ret = csonpath_compile_do(cjp, orig, walker, tmp, &inst_idx, flag, '\0', NULL);
free(tmp);
return ret;
}
static void csonpath_push_filter_getter(struct csonpath *cjp, int *inst_idx, int nb_getter_inst,
char filter_getter[static nb_getter_inst])
{
char *filter_end = &cjp->data[(*inst_idx)++];
for (int i = 0; i < nb_getter_inst; ++i) {
csonpath_push_char(cjp, filter_getter[i], inst_idx);
}
*filter_end = (nb_getter_inst - 1);
}
static _Bool csonpath_check_tmp_walker(const char tmp_walk[static 1], int cnt_brackets)
{
if (!*tmp_walk)
return 0;
if (cnt_brackets)
return 1;
return (*tmp_walk != ']');
}
static int csonpath_compile_do(struct csonpath *cjp, const char orig[static 1],
const char walker[static 1], char tmp[static 1],
int *inst_idx, int flag, int end_path,
const char **end_sentinel)
{
const char *next;
char to_check;
int inst;
int in_union = 0;
root_again:
if (!(flag & CSONPATH_AUTO_ROOT)) {
CSONPATH_SKIP('$', walker);
}
if (isdigit((unsigned char)*walker)) {
const char *num_start = walker;
char *end;
int n = strtol(num_start, &end, 10);
walker = end;
csonpath_push_char(cjp,
n == 0 ? CSONPATH_INST_ROOT : CSONPATH_SWITCH_ROOT,
inst_idx);
if (n != 0) {
if (n - 1 > 255)
CSONPATH_COMPILE_ERR(tmp, num_start - orig,
"extra root index too big");
csonpath_push_char(cjp, (char)(n - 1), inst_idx);
}
} else {
csonpath_push_char(cjp, CSONPATH_INST_ROOT, inst_idx);
}
to_check = *walker;
again:
if (in_union) {
csonpath_push_char(cjp, CSONPATH_INST_UNION_END, inst_idx);
in_union = 0;
}
switch (to_check) {
case '@':
++walker;
if (!strncmp(walker, "string", 6)) {
csonpath_push_char(cjp, CSONPATH_INST_TYPECHECK, inst_idx);
csonpath_push_char(cjp, CSONPATH_STR, inst_idx);
walker += 6;
} else if (!strncmp(walker, "integer", 7)) {
csonpath_push_char(cjp, CSONPATH_INST_TYPECHECK, inst_idx);
csonpath_push_char(cjp, CSONPATH_INTEGER, inst_idx);
walker += 7;
} else if (!strncmp(walker, "null", 4)) {
csonpath_push_char(cjp, CSONPATH_INST_TYPECHECK, inst_idx);
csonpath_push_char(cjp, CSONPATH_NONE, inst_idx);
walker += 4;
} else {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "unknow type selector");
}
CSONPATH_SKIP('(', walker);
CSONPATH_SKIP(')', walker);
to_check = *walker;
goto again;
case '[':
{
int end;
inst = 0;
do_array:
if (!in_union) {
int cnt_brackets = 0;
const char *tmp_walk;
for (tmp_walk = walker + 1;
csonpath_check_tmp_walker(tmp_walk, cnt_brackets);
++tmp_walk) {
if (!cnt_brackets && *tmp_walk == ',') {
csonpath_push_char(cjp, CSONPATH_INST_GET_UNION, inst_idx);
in_union = 1;
break;
}
if (*tmp_walk == '[')
++cnt_brackets;
if (cnt_brackets && *tmp_walk == ']')
--cnt_brackets;
if (*tmp_walk == '"') {
for (++tmp_walk; *tmp_walk != '"' && *tmp_walk; ++tmp_walk);
}
else if (*tmp_walk == '\'') {
for (++tmp_walk; *tmp_walk != '\'' && *tmp_walk; ++tmp_walk);
}
if (!*tmp_walk) break;
}
if (!tmp_walk[0]) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "unclose union");
}
}
++walker;
for (; isblank(*walker); ++walker);
if (*walker == '*') {
if (inst == CSONPATH_INST_FIND_ALL) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "'%c' is invalide here\n",
*walker);
goto error;
}
csonpath_push_char(cjp, CSONPATH_INST_GET_ALL, inst_idx);
if (in_union && walker[1] == ',') {
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
walker += 1;
goto do_array;
}
if (walker[1] != ']') {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "%s", "unclose bracket\n");
}
walker += 2;
to_check = *walker;
goto again;
} else if (*walker == '?') {
int have_blank;
int have_parentesis = 0;
int nb_getter_inst = 0;
char filter_getter[CSONPATH_TMP_BUF_SIZE];
int regex_idx = -1;
int getter_end = 0;
int operand_instruction;
if (inst == CSONPATH_INST_FIND_ALL) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "'%c' is invalide here\n",
*walker);
goto error;
}
filter_again_root:
inst = CSONPATH_INST_GET_OBJ;
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, CSONPATH_INST_GET_OBJ, walker);
++walker;
for (; isblank(*walker); ++walker);
if (*walker == '(') {
++have_parentesis;
++walker;
CSONPATH_SKIP('@', walker);
if (*walker == '[') {
++walker;
getter_end = *walker;
} else {
CSONPATH_SKIP('.', walker);
}
for (; isblank(*walker); ++walker);
} else if (*walker == '[') {
++walker;
getter_end = *walker;
} else if (*walker == '@') {
++walker;
if (*walker == '[') {
++walker;
getter_end = *walker;
} else {
CSONPATH_SKIP('.', walker);
}
}
filter_again:
if (getter_end) {
if (getter_end != '\'' && getter_end != '"') {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"string require here, got '%c'", getter_end);
goto error;
}
++walker;
for (next = walker; *next && *next != getter_end; ++next)
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, *next, next);
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, 0, next);
} else {
for (next = walker; csonpath_is_dot_operand(*next); ++next)
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, *next, next);
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, 0, next);
}
if (!*next) {
CSONPATH_COMPILE_ERR(tmp, next - orig,
"filter miss condition");
goto error;
}
to_check = *next;
if (getter_end) {
CSONPATH_SKIP_2(getter_end, to_check != getter_end, next);
CSONPATH_SKIP(']', next);
to_check = *next;
getter_end = 0;
}
if (to_check == '.') {
walker = next + 1;
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, CSONPATH_INST_GET_OBJ, walker);
goto filter_again;
} else if (to_check == '[') {
walker = next + 1;
CSONPATH_FILTER_PUSH(filter_getter, nb_getter_inst, CSONPATH_INST_GET_OBJ, walker);
getter_end = *walker;
goto filter_again;
}
if (have_parentesis && to_check == ')') {
++next;
to_check = *next;
have_parentesis--;
}
walker = next;
have_blank = isblank(to_check);
for (++next; isblank(*next); ++next) {
have_blank = 1;
}
if (*next && have_blank &&
to_check != '=' && to_check != '!') {
to_check = *next;
++next;
}
if (to_check == '=') {
if (next[0] == '=') {
++next;
if (next[0] == '=')
++next;
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_EQ, inst_idx);
}
#ifndef CSONPATH_NO_REGEX
else if (next[0] == '~') {
if (cjp->regex_cnt >= CSONPATH_MAX_REGEX) {
CSONPATH_COMPILE_ERR(tmp, next - orig, "too many regex");
goto error;
}
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_REG_EQ, inst_idx);
regex_idx = cjp->regex_cnt++;
cjp->flags |= CSONPATH_REG_INCOMPLETTE;
++next;
}
#endif
else {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_EQ, inst_idx);
}
} else if (to_check == '!' && next[0] == '=') {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_NOT_EQ, inst_idx);
++next;
} else if (to_check == '>') {
if (next[0] == '=') {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ, inst_idx);
++next;
} else {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_SUPERIOR, inst_idx);
}
} else if (to_check == '<') {
if (next[0] == '=') {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_INFERIOR_EQ, inst_idx);
++next;
} else {
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_INFERIOR, inst_idx);
}
} else if (to_check == ']' || (in_union && to_check == ',')) {
int union_jmp = in_union && to_check == ',';
if (have_parentesis > 0)
CSONPATH_COMPILE_ERR(tmp, next - orig, "too many open parentesis");
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_EXIST, inst_idx);
for (;isblank(*next); ++next);
csonpath_push_filter_getter(cjp, inst_idx, nb_getter_inst, filter_getter);
if (union_jmp) {
walker = next - 1;
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
goto do_array;
}
walker = next;
to_check = *walker;
goto again;
} else {
CSONPATH_COMPILE_ERR(tmp, next - orig,
"'%c': unsuported operation", to_check);
goto error;
}
if (!*next) {
CSONPATH_COMPILE_ERR(tmp, next - orig,
"filter miss operand");
goto error;
}
operand_instruction = cjp->data[(*inst_idx) - 1];
for (;isblank(*next); ++next);
csonpath_push_filter_getter(cjp, inst_idx, nb_getter_inst, filter_getter);
walker = next;
if (*walker == '"' || *walker == '\'' || *walker == '/') {
char end = *walker;
++walker;
if (regex_idx < 0) {
csonpath_push_char(cjp, CSONPATH_INST_GET_OBJ, inst_idx);
for (next = walker; *next && *next != end; ++next)
csonpath_push_char(cjp, *next, inst_idx);
csonpath_push_char(cjp, 0, inst_idx);
if (!*next) {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"broken filter");
goto error;
}
}
#if !defined(CSONPATH_NO_REGEX)
else {
csonpath_push_char(cjp, CSONPATH_INST_GET_ARRAY_SMALL, inst_idx);
csonpath_push_char(cjp, regex_idx, inst_idx);
if (!regex_idx) {
cjp->regexs = malloc(sizeof *cjp->regexs * 255);
# ifdef CSONPATH_PCRE2
cjp->match_datas = malloc(sizeof *cjp->match_datas * 255);
# endif
}
cjp->flags &= ~CSONPATH_REG_INCOMPLETTE;
for (next = walker; *next && *next != end; ++next);
char *reg_tmp = malloc(next - walker + 1);
char *crawler = reg_tmp;
char *errbuff = NULL;
for (next = walker; *next && *next != end; ++next, ++crawler)
*crawler = *next;
*crawler = 0;
int e = csonpath_reg_compile(cjp, regex_idx, reg_tmp, &errbuff);
if (e) {
CSONPATH_COMPILE_ERR(tmp, next - orig, "regex has error: %s\n",
errbuff ? errbuff : "(unknow)");
goto error;
}
free(reg_tmp);
}
#endif
++next;
to_check = *next;
} else if (*walker == '$') {
const char *end_sentinel;
if (operand_instruction == CSONPATH_INST_FILTER_KEY_REG_EQ) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "subpath unsuported for regex");
goto error;
}
csonpath_push_char(cjp, CSONPATH_INST_GET_SUBPATH, inst_idx);
int ret = csonpath_compile_do(cjp, orig, walker, tmp,
inst_idx, flag, in_union ? 'u' : ']',
&end_sentinel);
if (ret < 0)
return -1;
walker = end_sentinel;
next = walker;
to_check = *next;
} else if (!strncmp(walker, "null", 4) && !isalnum(walker[4])) {
next = walker + 4;
to_check = *next;
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_EXIST, inst_idx);
} else if (!strncmp(walker, "true", 4) && !isalnum(walker[4])) {
next = walker + 4;
to_check = *next;
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_TRUE, inst_idx);
} else if (!strncmp(walker, "false", 5) && !isalnum(walker[5])) {
next = walker + 5;
to_check = *next;
csonpath_push_char(cjp, CSONPATH_INST_FILTER_KEY_FALSE, inst_idx);
} else {
int n;
if (operand_instruction == CSONPATH_INST_FILTER_KEY_REG_EQ) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "number unsuported for regex");
goto error;
}
for (next = walker; isdigit(*next); ++next);
if (next == walker) {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"'%c': broken filter with number",
*walker);
goto error;
} else if (!*next) {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"unclose filter");
goto error;
}
n = atoi(walker);
to_check = *next;
csonpath_fill_walker_with_int(cjp, inst_idx, n);
}
if (isblank(to_check)) {
for (next++; isblank(*next); ++next);
to_check = *next;
}
if (to_check == '&') {
if (next[1] == '&') {
++next;
}
walker = next + 1;
to_check = *walker;
csonpath_push_char(cjp, CSONPATH_INST_FILTER_AND, inst_idx);
nb_getter_inst = 0;
goto filter_again_root;
}
while (have_parentesis) {
CSONPATH_SKIP(')', next);
to_check = *next;
--have_parentesis;
}
if (in_union && to_check == ',') {
walker = next;
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
goto do_array;
}
if (to_check != ']') {
CSONPATH_REQUIRE_ERR(']', next);
}
walker = next + 1;
to_check = *walker;
goto again;
} else if (*walker == '$') {
const char *end_sentinel;
csonpath_push_char(cjp, CSONPATH_INST_GET_SUBPATH, inst_idx);
int ret = csonpath_compile_do(cjp, orig, walker, tmp,
inst_idx, flag,in_union ? 'u' : ']', &end_sentinel);
if (ret < 0)
return -1;
walker = end_sentinel;
if (in_union && *end_sentinel == ',') {
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
goto do_array;
}
++walker;
to_check = *walker;
goto again;
} else if (*walker != '"' && *walker != '\'') {
int num;
if (inst == CSONPATH_INST_FIND_ALL) {
CSONPATH_COMPILE_ERR(tmp, walker - orig, ".. require string\n");
goto error;
}
next = walker;
do {
if (*next == ':') {
csonpath_push_char(cjp, CSONPATH_INST_RANGE, inst_idx);
num = atoi(walker);
csonpath_fill_walker_with_int(cjp, inst_idx, num);
walker = next + 1;
++next;
continue;
}
for (; isblank(*next); ++next);
if (*next < '0' || *next > '9') {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"unexpected '%c', sting, filter or number require\n", *next);
}
next++;
if (in_union && *next == ',')
break;
} while (*next && *next != ']');
if (!*next) {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"%s", "unclose bracket\n");
}
num = atoi(walker);
csonpath_fill_walker_with_int(cjp, inst_idx, num);
if (in_union && *next == ',') {
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
walker = next;
goto do_array;
}
walker = next + 1;
to_check = *walker;
goto again;
} else {
end = *walker;
if (inst != CSONPATH_INST_FIND_ALL)
inst = CSONPATH_INST_GET_OBJ;
++walker;
next = walker;
csonpath_push_char(cjp, inst, inst_idx);
while (*next && *next != end) {
csonpath_push_char(cjp, *next, inst_idx);
++next;
while (*next == '\\')
++next;
}
if (!*next) {
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"unclosed string in bracket");
}
++next; csonpath_push_char(cjp, 0, inst_idx);
if (in_union && *next == ',') {
walker = next;
csonpath_push_char(cjp, CSONPATH_INST_UNION_JMP, inst_idx);
goto do_array;
}
if (*next != ']')
CSONPATH_COMPILE_ERR(tmp, walker - orig,
"']' require instead of '%c'\n", *next);
walker = next + 1;
to_check = *walker;
goto again;
}
}
case '.':
{
inst = CSONPATH_INST_GET_OBJ;
++walker;
if (*walker == '.') {
inst = CSONPATH_INST_FIND_ALL;
++walker;
if (*walker == '*') {
++walker;
csonpath_push_char(cjp, CSONPATH_INST_FIND_ALL, inst_idx);
csonpath_push_char(cjp, CSONPATH_INST_GET_ALL, inst_idx);
csonpath_push_char(cjp, 0, inst_idx);
to_check = *walker;
goto again;
}
} else if (*walker == '*') {
inst = CSONPATH_INST_GET_ALL;
++walker;
if (*walker != '.' && *walker != '[' && *walker != '\0' && *walker != '@') {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "unsuported characters '%c' after '*'", *walker);
goto error;
}
csonpath_push_char(cjp, inst, inst_idx);
to_check = *walker;
goto again;
}
for (next = walker; csonpath_is_dot_operand(*next); ++next);
if (next == walker) {
if (*next == '[')
goto do_array;
CSONPATH_COMPILE_ERR(tmp, walker - orig, "empty getter");
}
to_check = *next;
csonpath_push_char(cjp, inst, inst_idx);
for (; walker != next; ++walker) {
cjp->data[(*inst_idx)++] = *walker;
}
cjp->data[(*inst_idx)++] = 0;
goto again;
}
case '|':
csonpath_push_char(cjp, CSONPATH_INST_OR, inst_idx);
++walker;
goto root_again;
}
if (isblank(*walker)) {
++walker;
goto again;
}
else if ((end_path == 'u' && (*walker == ']' || *walker == ',')) || *walker == end_path) {
csonpath_push_char(cjp, CSONPATH_INST_END, inst_idx);
free(cjp->compile_error);
cjp->compile_error = NULL;
if (end_sentinel)
*end_sentinel = walker;
return 0;
} else {
CSONPATH_COMPILE_ERR(tmp, walker - orig, "unexpected char '%c'", to_check);
}
error:
cjp->data[0] = CSONPATH_INST_BROKEN;
return -1;
}
static inline _Bool csonpath_is_endish_inst(int instruction)
{
return instruction == CSONPATH_INST_END || instruction == CSONPATH_INST_OR;
}
static const char *csonpath_skipp_union_jmp(const char *walker)
{
int union_cnt = 0;
while (union_cnt || *walker != CSONPATH_INST_UNION_END) {
walker = csonpath_walker_next_inst(walker);
if (*walker == CSONPATH_INST_GET_UNION)
++union_cnt;
else if (union_cnt && *walker == CSONPATH_INST_UNION_END) {
--union_cnt;
++walker;
}
}
return walker;
}
#endif
#ifndef CSONPATH_FOREACH
# define CSONPATH_FOREACH(obj, el, code) \
CSONPATH_FOREACH_EXT(obj, el, code, key_idx)
#endif
#ifndef CSONPATH_FIND_ALL_RET
#define CSONPATH_FIND_ALL_RET CSONPATH_JSON
#endif
#ifndef CSONPATH_FIND_ALL_RET_INIT
#define CSONPATH_FIND_ALL_RET_INIT CSONPATH_NEW_ARRAY
#endif
#ifndef CSONPATH_FORMAT_EXCEPTION
#define CSONPATH_FORMAT_EXCEPTION(args...) fprintf(stderr, args)
#endif
#ifndef CSONPATH_EXCEPTION
#define CSONPATH_EXCEPTION(args...) CSONPATH_GETTER_ERR(args)
#endif
#ifndef CSONPATH_POST_CREATE_CTX
#define CSONPATH_POST_CREATE_CTX(child_info, ctx, tmp) do { ctx = tmp; } while (0)
#endif
CSONPATH_STATINLINE void CSONPATH_FUNC(csonpath_destroy)(struct csonpath *cjp)
{
if (!cjp)
return;
free(cjp->compile_error);
#if !defined CSONPATH_NO_REGEX
int is_incomplete = !!(cjp->flags & CSONPATH_REG_INCOMPLETTE);
if (cjp->regex_cnt - is_incomplete > 0) {
for (int i = 0; i < cjp->regex_cnt - is_incomplete; ++i) {
# ifdef CSONPATH_PCRE2
pcre2_code_free(cjp->regexs[i]);
pcre2_match_data_free(cjp->match_datas[i]);
# else
csonpath_reg_free(cjp->regexs[i]);
# endif
}
# ifdef CSONPATH_PCRE2
if (cjp->regex_cnt - is_incomplete > 0)
free(cjp->match_datas);
# endif
if (cjp->regex_cnt - is_incomplete > 0)
free(cjp->regexs);
}
#endif
free(cjp);
}
CSONPATH_STATINLINE struct csonpath *CSONPATH_FUNC(csonpath_new_ex)(const char path[static 1], int flag) {
int max_inst = strlen(path) / 2 + 1;
int extra = 0;
for (const char *tmp = path; *tmp; ++tmp)
if (*tmp == ',')
++extra;
struct csonpath *ret = malloc(sizeof *ret + CSONPATH_BYTE_PER_INST * max_inst + strlen(path) + 1 + extra);
if (!ret) {
return NULL;
}
memset(ret, 0, sizeof *ret);
ret->flags = flag;
if (csonpath_compile_(ret, path, flag) < 0) {
if ((flag & CSONPATH_NO_DETROY))
return ret;
fprintf(stderr, "compile error: %s\n",
ret->compile_error ? ret->compile_error : "(unknown error)");
CSONPATH_FUNC(csonpath_destroy)(ret);
return NULL;
}
ret->extra_roots = CSONPATH_NULL;
return ret;
}
CSONPATH_STATINLINE struct csonpath *CSONPATH_FUNC(csonpath_new)(const char path[static 1]) {
return CSONPATH_FUNC(csonpath_new_ex)(path, 0);
}
CSONPATH_STATINLINE struct csonpath *CSONPATH_FUNC(csonpath_set_path)(struct csonpath cjp[static 1],
const char path[static 1])
{
CSONPATH_FUNC(csonpath_destroy)(cjp);
struct csonpath *r = CSONPATH_FUNC(csonpath_new)(path);
return r;
}
static int CSONPATH_FUNC(csonpath_compile)(struct csonpath *cjp, const char path[static 1])
{
return csonpath_compile_(cjp, path, 0);
}
static inline struct csonpath_child_info *CSONPATH_FUNC(csonpath_child_info_set)(struct csonpath_child_info *child_info,
CSONPATH_JSON j, const intptr_t key)
{
if (CSONPATH_IS_OBJ(j)) {
*child_info = (struct csonpath_child_info){.key=(const char *)key, .type=CSONPATH_STR};
} else {
*child_info = (struct csonpath_child_info){.idx=key, .type=CSONPATH_INTEGER};
}
return child_info;
}
static inline CSONPATH_JSON CSONPATH_FUNC(csonpath_child_info_refetch)(
CSONPATH_JSON parent, const struct csonpath_child_info *ci)
{
if (ci->type == CSONPATH_STR)
return CSONPATH_GET(parent, ci->key);
if (ci->type == CSONPATH_INTEGER)
return CSONPATH_AT(parent, ci->idx);
return CSONPATH_NULL;
}
static _Bool CSONPATH_FUNC(csonpath_do_match)(int operand_instruction, CSONPATH_JSON el2, const char **owalker)
{
switch (operand_instruction) {
case CSONPATH_INST_FILTER_KEY_TRUE:
{
if (!CSONPATH_IS_BOOL(el2))
return 0;
return CSONPATH_GET_BOOL(el2) == 1;
}
case CSONPATH_INST_FILTER_KEY_FALSE:
{
if (!CSONPATH_IS_BOOL(el2))
return 0;
return CSONPATH_GET_BOOL(el2) == 0;
}
case CSONPATH_INST_FILTER_KEY_EXIST:
{
return CSONPATH_IS_NULL(el2);
}
case CSONPATH_INST_GET_OBJ:
{
_Bool ret = CSONPATH_EQUAL_STR(el2, *owalker);
*owalker += strlen(*owalker) + 1;
return ret;
}
case CSONPATH_INST_GET_ARRAY_SMALL:
{
_Bool ret = CSONPATH_EQUAL_NUM(el2, **owalker);
*owalker += 1;
return ret;
}
case CSONPATH_INST_GET_ARRAY_BIG:
{
int num = ((unsigned char)(*owalker)[0]) |
((unsigned char)(*owalker)[1] << 8) |
((unsigned char)(*owalker)[2] << 16) |
((unsigned char)(*owalker)[3] << 24);
*owalker += 4;
return CSONPATH_EQUAL_NUM(el2, num);
}
}
return 0;
}
static CSONPATH_JSON CSONPATH_FUNC(csonpath_crawl_filter_el)(const struct csonpath cjp[const static 1],
const char **owalker,
CSONPATH_JSON el2,
int filter_next)
{
const char *end = *owalker + filter_next;
for (; *owalker <= end; ) {
switch (**owalker) {
case CSONPATH_INST_GET_OBJ:
++*owalker;
if (el2)
el2 = CSONPATH_GET(el2, *owalker);
break;
default:
CSONPATH_FORMAT_EXCEPTION("unsuported %s inst",
csonpath_instuction_str[(int)**owalker]);
return NULL;
}
(*owalker) += (strlen(*owalker) + 1);
}
return el2;
}
static _Bool CSONPATH_FUNC(csonpath_make_match)(const struct csonpath cjp[const static 1],
CSONPATH_JSON origin, CSONPATH_JSON el2,
const char **owalker, int operation);
#define CSONPATH_GOTO_ON_RELOOP(where) \
nb_res += tret; if (need_reloop_in) goto where;
#define CSONPATH_PREPARE_RELOOP(label) \
int need_reloop_in; \
label: \
need_reloop_in = 0;
#undef CSONPATH_NONE_FOUND_RET
#define CSONPATH_NONE_FOUND_RET CSONPATH_NULL
#undef CSONPATH_GETTER_ERR
#define CSONPATH_GETTER_ERR(args...) do {\
CSONPATH_FORMAT_EXCEPTION(args);\
return CSONPATH_NULL;\
} while (0)
#define CSONPATH_DO_RET_TYPE CSONPATH_JSON
#define CSONPATH_DO_FUNC_NAME find_first
#define CSONPATH_DO_RETURN if (end_sentinel && *end_sentinel < walker) { \
*end_sentinel = walker; \
;} \
return tmp
#define CSONPATH_DO_FIND_ALL if (tret) { \
if (end_sentinel && *end_sentinel < walker) { \
*end_sentinel = walker; \
} return tret;}
#define CSONPATH_DO_FILTER_FIND if (end_sentinel && *end_sentinel < walker) *end_sentinel = owalker; return tret
#define CSONPATH_DO_FIND_ALL_OUT if (end_sentinel && *end_sentinel < walker) *end_sentinel = walker; return CSONPATH_NULL
#define CSONPATH_DO_EXTRA_DECLARATION , const char **end_sentinel
#define CSONPATH_DO_EXTRA_ARGS_IN , NULL
#define CSONPATH_DO_EXTRA_ARGS_NEESTED , end_sentinel
#include "csonpath_do.h"
#define CSONPATH_DO_PRE_OPERATION \
CSONPATH_FIND_ALL_RET ret_ar = CSONPATH_FIND_ALL_RET_INIT();
#define CSONPATH_DO_POST_OPERATION \
if (ret == CSONPATH_NULL) CSONPATH_REMOVE(ret_ar)
#define CSONPATH_DO_DECLARATION \
int nb_res = 0;
#define CSONPATH_DO_FUNC_NAME find_all
#define CSONPATH_DO_RET_TYPE CSONPATH_FIND_ALL_RET
#define CSONPATH_DO_RETURN ({CSONPATH_ARRAY_APPEND(ret_ar, tmp); return ret_ar;})
#define CSONPATH_DO_FIND_ALL \
if (tret) ++nb_res; \
#define CSONPATH_DO_FILTER_FIND CSONPATH_DO_FIND_ALL
#define CSONPATH_DO_FIND_ALL_OUT \
if (!(cjp->flags & CSONPATH_RETURN_EMPTY_ARRAY) && !nb_res) { \
return CSONPATH_NONE_FOUND_RET; \
} \
return ret_ar;
#define CSONPATH_DO_EXTRA_ARGS_IN , ret_ar
#define CSONPATH_DO_EXTRA_DECLARATION , CSONPATH_FIND_ALL_RET ret_ar
#include "csonpath_do.h"
#define CSONPATH_DO_FUNC_NAME remove
#undef CSONPATH_NONE_FOUND_RET
#undef CSONPATH_GETTER_ERR
#define CSONPATH_NONE_FOUND_RET 0
#define CSONPATH_GETTER_ERR(args...) do { \
CSONPATH_FORMAT_EXCEPTION(args); \
return -1; \
} while (0)
#define CSONPATH_DO_ON_FOUND
#define CSONPATH_DO_RET_TYPE int
#define CSONPATH_DO_RETURN \
({if (ctx == in_ctx && need_reloop && \
CSONPATH_NEED_FOREACH_REDO(ctx)) \
*need_reloop = 1; \
if (child_info.type == CSONPATH_NONE) \
return 0; \
CSONPATH_REMOVE_CHILD(ctx, child_info); return 1;})
#define CSONPATH_PRE_GET_OBJ(val) \
const char *to_del = val;
#define CSONPATH_DO_POST_FIND_ARRAY \
child_info.type = CSONPATH_INTEGER; \
child_info.idx = this_idx;
#define CSONPATH_DO_POST_FIND_OBJ \
child_info.type = CSONPATH_STR; \
child_info.key = to_del;
#define CSONPATH_DO_DECLARATION int nb_res = 0; \
CSONPATH_JSON in_ctx = ctx; \
(void)in_ctx;
#define CSONPATH_DO_FIND_ALL_OUT return nb_res;
#define CSONPATH_DO_FILTER_FIND nb_res += tret;
#define CSONPATH_DO_FIND_ALL ({ \
if (tret < 0) return -1; \
nb_res += tret; \
if (need_reloop_in){ goto again; }; \
})
#define CSONPATH_DO_RANGE ({ \
if (tret < 0) return -1; \
nb_res += tret; \
--end; \
if (need_reloop_in){ goto range_again; }; \
})
#define CSONPATH_DO_GET_UNION_POST ({ \
if (tret < 0) return -1; \
nb_res += tret; \
})
#define CSONPATH_DO_EXTRA_DECLARATION , struct csonpath_child_info child_info, int *need_reloop
#define CSONPATH_DO_EXTRA_ARGS_IN , (struct csonpath_child_info) {.type = CSONPATH_NONE}, NULL
#define CSONPATH_DO_RANGE_PRE_LOOP \
int need_reloop_in; \
range_again:
#define CSONPATH_DO_FILTER_PRE_LOOP \
int need_reloop_in;
#define CSONPATH_DO_FIND_ALL_PRE_LOOP \
int need_reloop_in; \
again:
#define CSONPATH_DO_FILTER_LOOP_PRE_SET \
CSONPATH_FUNC(csonpath_child_info_set)(&child_info, tmp, foreach_idx);
#define CSONPATH_DO_FOREACH_PRE_SET \
need_reloop_in = 0; \
CSONPATH_FUNC(csonpath_child_info_set)(&child_info, tmp, (intptr_t)key_idx);
#define CSONPATH_DO_EXTRA_ARGS_NEESTED , child_info, &need_reloop_in
#define CSONPATH_DO_EXTRA_ARGS_FIND_ALL , child_info, need_reloop
#include "csonpath_do.h"
#define CSONPATH_PRE_GET_ROOT \
int to_check = walker[1]; \
if (csonpath_is_endish_inst(to_check)) { \
if (CSONPATH_IS_OBJ(origin) && CSONPATH_IS_OBJ(to_update)) { \
return CSONPATH_FUNC(csonpath_sync_root_obj)(origin, to_update, cjp); \
} else if (CSONPATH_IS_ARRAY(origin) && CSONPATH_IS_ARRAY(to_update)) { \
return CSONPATH_FUNC(csonpath_sync_root_array)(origin, to_update, cjp); \
} else { \
CSONPATH_EXCEPTION("can't update root ($)\n"); \
} \
}
#define CSONPATH_DO_DECLARATION \
int nb_res = 0;
#define CSONPATH_DO_RET_TYPE int
#define CSONPATH_DO_FUNC_NAME update_or_create
#define CSONPATH_DO_GET_NOTFOUND(osef) goto next_inst;
#define CSONPATH_DO_RETURN \
if (child_info->type != CSONPATH_NONE) { \
if (need_reloop) *need_reloop = 1; \
if (child_info->type == CSONPATH_INTEGER) \
return CSONPATH_APPEND_AT(ctx, child_info->idx, to_update, 1); \
else \
return CSONPATH_APPEND_AT(ctx, child_info->key, to_update, 1); \
return 1; \
} \
return 0;
#define CSONPATH_DO_EXTRA_ARGS_FIND_ALL , to_update, child_info, need_reloop
#define CSONPATH_DO_EXTRA_ARGS_NEESTED , to_update, \
CSONPATH_FUNC(csonpath_child_info_set)(&(struct csonpath_child_info ){}, tmp, (intptr_t)key_idx), &need_reloop_in
#define CSONPATH_DO_EXTRA_ARGS , CSONPATH_JSON to_update
#define CSONPATH_DO_EXTRA_ARGS_IN , to_update, &(struct csonpath_child_info ){}, NULL
#define CSONPATH_DO_EXTRA_DECLARATION CSONPATH_DO_EXTRA_ARGS, struct csonpath_child_info *child_info, int *need_reloop
#define CSONPATH_DO_FOREACH_PRE_SET \
const struct csonpath_child_info csonpath_cur_child_info = \
*CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, \
(intptr_t)key_idx);
#define CSONPATH_DO_FIND_ALL do { \
if (tret < 0) return tret; \
nb_res += tret; \
el = CSONPATH_FUNC(csonpath_child_info_refetch) \
(tmp, &csonpath_cur_child_info); \
} while (0)
#define CSONPATH_DO_FILTER_FIND CSONPATH_GOTO_ON_RELOOP(filter_again)
#define CSONPATH_DO_RANGE do { \
if (tret < 0) return tret; \
nb_res += tret; \
(void)csonpath_cur_child_info; \
} while (0)
#define CSONPATH_DO_GET_UNION_POST do { if (tret < 0) return tret; nb_res += tret; } while (0)
#define CSONPATH_NEW_GUESS_CNT() \
({ \
const char *tmp_wal = csonpath_walker_next_inst(walker); \
int is_array = 1; \
for (; *tmp_wal && !csonpath_is_endish_inst(*tmp_wal); \
tmp_wal = csonpath_walker_next_inst(tmp_wal)) { \
if (*tmp_wal == CSONPATH_INST_GET_UNION) \
tmp_wal = csonpath_skipp_union_jmp(tmp_wal); \
if (*tmp_wal == CSONPATH_INST_GET_OBJ || \
(*tmp_wal >= CSONPATH_INST_FILTER_KEY_EXIST && *tmp_wal <= CSONPATH_INST_FILTER_KEY_REG_EQ)) { \
is_array = 0; \
break; \
} else if (*tmp_wal == CSONPATH_INST_GET_ARRAY_SMALL || *tmp_wal == CSONPATH_INST_GET_ARRAY_BIG) { \
break; \
} \
} \
CSONPATH_JSON r; \
if (is_array) \
r = CSONPATH_NEW_ARRAY(); \
else \
r = CSONPATH_NEW_OBJECT(); \
r; \
})
#define CSONPATH_DO_FIND_ALL_PRE_LOOP int need_reloop_in = 0; \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_GUESS_CNT);
#define CSONPATH_DO_FILTER_PRE_LOOP \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_OBJECT); \
CSONPATH_PREPARE_RELOOP(filter_again)
#define CSONPATH_DO_FIND_ALL_OUT return nb_res;
static int CSONPATH_FUNC(csonpath_sync_root_array)(CSONPATH_JSON parent, CSONPATH_JSON to_update,
const struct csonpath *cjp)
{
CSONPATH_JSON child;
intptr_t idx;
(void) idx;
CSONPATH_ARRAY_CLEAR(parent);
CSONPATH_FOREACH_ARRAY(to_update, child, idx) {
if (CSONPATH_APPEND_AT(parent, idx, child, 1) < 0)
return -1;
}
return 1;
}
static int CSONPATH_FUNC(csonpath_sync_root_obj)(CSONPATH_JSON parent, CSONPATH_JSON to_update,
const struct csonpath *cjp)
{
CSONPATH_JSON child;
const char *key;
(void )key;
CSONPATH_OBJ_CLEAR(parent);
CSONPATH_FOREACH_OBJ(to_update, child, key) {
if (CSONPATH_APPEND_AT(parent, key, child, 1) < 0)
return -1;
}
return 1;
}
#define CSONPATH_UPDATE_CHECK_EXIST(mk_cnt) \
if (tmp == CSONPATH_NULL) { \
int append_ret = 0; \
if (child_info->type == CSONPATH_NONE) \
CSONPATH_GETTER_ERR("CSONPATH_NONE is nope\n"); \
tmp = mk_cnt(); \
if (child_info->type == CSONPATH_INTEGER) \
append_ret = CSONPATH_APPEND_AT(ctx, child_info->idx, tmp, 1); \
else if (child_info->type == CSONPATH_STR) \
append_ret = CSONPATH_APPEND_AT(ctx, child_info->key, tmp, 1); \
CSONPATH_REMOVE(tmp); \
if (append_ret < 0) return append_ret; \
CSONPATH_POST_CREATE_CTX(child_info, ctx, tmp); \
}
#define CSONPATH_PRE_GET_OBJ(this_idx) \
if (tmp != CSONPATH_NULL && !CSONPATH_IS_OBJ(tmp)) { \
CSONPATH_GETTER_ERR("Unable to follow path(%s): Dict expected", this_idx); \
} \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_OBJECT); \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, (intptr_t)this_idx);
#define CSONPATH_PRE_GET(this_idx) \
if (tmp != CSONPATH_NULL && !CSONPATH_IS_ARRAY(tmp)) { \
CSONPATH_GETTER_ERR("Unable to follow path(%d): List expected", this_idx); \
} \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_ARRAY); \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, this_idx);
#include "csonpath_do.h"
#define CSONPATH_DO_DECLARATION \
int nb_res = 0;
#define CSONPATH_DO_RET_TYPE int
#define CSONPATH_DO_FUNC_NAME callback
#define CSONPATH_DO_RETURN do { \
if (CSONPATH_CALL_CALLBACK(callback, ctx, child_info, tmp, udata) < 0) \
return -1; \
return 1;} \
while (0)
#define CSONPATH_DO_PRE_OPERATION \
if (callback == NULL) { \
CSONPATH_GETTER_ERR("callback is NULL\n"); \
return -1; \
}
#define CSONPATH_DO_EXTRA_ARGS_FIND_ALL , callback, udata, child_info
#define CSONPATH_DO_EXTRA_ARGS_NEESTED , callback, udata, \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, (intptr_t)key_idx)
#define CSONPATH_DO_EXTRA_ARGS , CSONPATH_CALLBACK callback, CSONPATH_CALLBACK_DATA udata
#define CSONPATH_DO_EXTRA_ARGS_IN , callback, udata, &(struct csonpath_child_info ){}
#define CSONPATH_DO_EXTRA_DECLARATION CSONPATH_DO_EXTRA_ARGS, struct csonpath_child_info *child_info
#define CSONPATH_DO_FIND_ALL do { if (tret < 0) return tret; nb_res += tret; } while (0)
#define CSONPATH_DO_FILTER_FIND do { if (tret < 0) return tret; nb_res += tret; } while (0)
#define CSONPATH_DO_FIND_ALL_OUT return nb_res;
#define CSONPATH_PRE_GET(this_idx) \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, ctx, (intptr_t)this_idx)
#include "csonpath_do.h"
#define CSONPATH_DO_DECLARATION \
int nb_res = 0;
#define CSONPATH_DO_PRE_OPERATION \
if (callback == NULL) { \
CSONPATH_GETTER_ERR("callback is NULL\n"); \
return -1; \
}
#define CSONPATH_DO_RET_TYPE int
#define CSONPATH_DO_FUNC_NAME update_or_create_callback
#define CSONPATH_DO_RETURN \
if (need_reloop && tmp == value) { \
*need_reloop = 1; \
} \
if (CSONPATH_CALL_CALLBACK(callback, ctx, child_info, tmp, udata) < 0) \
return -1; \
return 1;
#define CSONPATH_DO_EXTRA_ARGS_FIND_ALL , callback, udata, NULL, need_reloop
#define CSONPATH_DO_EXTRA_ARGS_NEESTED , callback, udata, \
CSONPATH_FUNC(csonpath_child_info_set)(&(struct csonpath_child_info ){}, tmp, (intptr_t)key_idx), \
&need_reloop_in
#define CSONPATH_DO_EXTRA_ARGS , CSONPATH_CALLBACK callback, CSONPATH_CALLBACK_DATA udata
#define CSONPATH_DO_EXTRA_ARGS_IN , callback, udata, &(struct csonpath_child_info ){}, NULL
#define CSONPATH_DO_EXTRA_DECLARATION CSONPATH_DO_EXTRA_ARGS, struct csonpath_child_info *child_info, int *need_reloop
#define CSONPATH_DO_FOREACH_PRE_SET \
const struct csonpath_child_info csonpath_cur_child_info = \
*CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, \
(intptr_t)key_idx);
#define CSONPATH_DO_FIND_ALL do { \
if (tret < 0) return tret; \
nb_res += tret; \
el = CSONPATH_FUNC(csonpath_child_info_refetch) \
(tmp, &csonpath_cur_child_info); \
} while (0)
#define CSONPATH_DO_FILTER_FIND do { if (tret < 0) return tret; CSONPATH_GOTO_ON_RELOOP(filter_again); } while (0)
#define CSONPATH_DO_RANGE do { \
if (tret < 0) return tret; \
nb_res += tret; \
(void)csonpath_cur_child_info; \
} while (0)
#define CSONPATH_DO_GET_UNION_POST do { if (tret < 0) return tret; nb_res += tret; } while (0)
#define CSONPATH_DO_FIND_ALL_PRE_LOOP int need_reloop_in = 0;
#define CSONPATH_DO_FILTER_PRE_LOOP CSONPATH_PREPARE_RELOOP(filter_again)
#define CSONPATH_DO_FIND_ALL_OUT return nb_res;
#define CSONPATH_PRE_GET_ROOT \
int to_check = walker[1]; \
if (csonpath_is_endish_inst(to_check)) { \
CSONPATH_GETTER_ERR("can't update root ($)\n"); \
return CSONPATH_NONE_FOUND_RET; \
}
#define CSONPATH_PRE_GET_OBJ(this_idx) \
if (tmp != CSONPATH_NULL && !CSONPATH_IS_OBJ(tmp)) { \
CSONPATH_GETTER_ERR("Unable to follow path(%s): Dict expected", this_idx); \
} \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_OBJECT); \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, (intptr_t)this_idx);
#define CSONPATH_PRE_GET(this_idx) \
if (tmp != CSONPATH_NULL && !CSONPATH_IS_ARRAY(tmp)) { \
CSONPATH_GETTER_ERR("Unable to follow path(%d): List expected", this_idx); \
} \
CSONPATH_UPDATE_CHECK_EXIST(CSONPATH_NEW_ARRAY); \
CSONPATH_FUNC(csonpath_child_info_set)(child_info, tmp, this_idx);
#define CSONPATH_DO_GET_NOTFOUND(osef) goto next_inst;
#include "csonpath_do.h"
static _Bool CSONPATH_FUNC(csonpath_make_match)(const struct csonpath cjp[const static 1],
CSONPATH_JSON origin, CSONPATH_JSON el2,
const char **owalker, int operation)
{
int operand_instruction = **owalker;
if (operation == CSONPATH_INST_FILTER_KEY_EXIST) {
return el2 != CSONPATH_NULL;
}
if (operand_instruction == CSONPATH_INST_GET_SUBPATH) {
const char *end_sentinel = *owalker;
*owalker = csonpath_walker_next_inst(*owalker);
CSONPATH_JSON jret = CSONPATH_FUNC(csonpath_find_first_internal)(
cjp, origin, origin, CSONPATH_NULL, *owalker, &end_sentinel);
*owalker = end_sentinel + 1;
if (!jret)
return 0;
switch (operation) {
case CSONPATH_INST_FILTER_KEY_NOT_EQ:
if (CSONPATH_IS_NUM(el2) && CSONPATH_IS_NUM(jret)) {
return CSONPATH_GET_NUM(el2) != CSONPATH_GET_NUM(jret);
}
if (CSONPATH_IS_STR(el2) && CSONPATH_IS_STR(jret)) {
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return (!!strcmp(s1, s2));
}
return 0;
case CSONPATH_INST_FILTER_KEY_EQ:
if (CSONPATH_IS_NUM(el2) && CSONPATH_IS_NUM(jret)) {
return CSONPATH_GET_NUM(el2) == CSONPATH_GET_NUM(jret);
}
if (CSONPATH_IS_STR(el2) && CSONPATH_IS_STR(jret)) {
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return (!strcmp(s1, s2));
}
return 0;
case CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ:
case CSONPATH_INST_FILTER_KEY_SUPERIOR:
if (CSONPATH_IS_NUM(el2) && CSONPATH_IS_NUM(jret)) {
if (operation == CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ) {
return CSONPATH_GET_NUM(el2) >= CSONPATH_GET_NUM(jret);
}
return CSONPATH_GET_NUM(el2) > CSONPATH_GET_NUM(jret);
}
if (CSONPATH_IS_STR(el2) && CSONPATH_IS_STR(jret)) {
if (operation == CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ) {
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return strcmp(s1, s2) >= 0;
}
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return strcmp(s1, s2) > 0;
}
return 0;
case CSONPATH_INST_FILTER_KEY_INFERIOR:
case CSONPATH_INST_FILTER_KEY_INFERIOR_EQ:
if (CSONPATH_IS_NUM(el2) && CSONPATH_IS_NUM(jret)) {
if (operation == CSONPATH_INST_FILTER_KEY_INFERIOR_EQ) {
return CSONPATH_GET_NUM(el2) <= CSONPATH_GET_NUM(jret);
}
return CSONPATH_GET_NUM(el2) < CSONPATH_GET_NUM(jret);
}
if (CSONPATH_IS_STR(el2) && CSONPATH_IS_STR(jret)) {
if (operation == CSONPATH_INST_FILTER_KEY_INFERIOR_EQ) {
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return strcmp(s1, s2) <= 0;
}
CSONPATH_CLEANUP_STR const char *s1 = CSONPATH_GET_STR(el2);
CSONPATH_CLEANUP_STR const char *s2 = CSONPATH_GET_STR(jret);
return strcmp(s1, s2) < 0;
}
return 0;
default:
return 0;
}
}
++*owalker;
_Bool match = 0;
switch (operation) {
case CSONPATH_INST_FILTER_KEY_NOT_EQ:
match = !CSONPATH_FUNC(csonpath_do_match)(operand_instruction, el2, owalker);
break;
case CSONPATH_INST_FILTER_KEY_EQ:
match = CSONPATH_FUNC(csonpath_do_match)(operand_instruction, el2, owalker);
break;
case CSONPATH_INST_FILTER_KEY_SUPERIOR:
case CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ:
if (el2 == CSONPATH_NULL)
return 0;
if (operand_instruction == CSONPATH_INST_GET_OBJ) {
if (!CSONPATH_IS_STR(el2))
break;
CSONPATH_CLEANUP_STR const char *s = CSONPATH_GET_STR(el2);
if (operation == CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ)
match = strcmp(s, *owalker) >= 0;
else
match = strcmp(s, *owalker) > 0;
*owalker += strlen(*owalker) + 1;
} else {
if (!CSONPATH_IS_NUM(el2))
break;
if (operation == CSONPATH_INST_FILTER_KEY_SUPERIOR_EQ)
match = csonpath_int_from_walker(operand_instruction, owalker) <=
CSONPATH_GET_NUM(el2);
else
match = csonpath_int_from_walker(operand_instruction, owalker) <
CSONPATH_GET_NUM(el2);
}
break;
case CSONPATH_INST_FILTER_KEY_INFERIOR_EQ:
case CSONPATH_INST_FILTER_KEY_INFERIOR:
if (el2 == CSONPATH_NULL)
return 0;
if (operand_instruction == CSONPATH_INST_GET_OBJ) {
if (!CSONPATH_IS_STR(el2))
break;
CSONPATH_CLEANUP_STR const char *s = CSONPATH_GET_STR(el2);
if (operation == CSONPATH_INST_FILTER_KEY_INFERIOR_EQ)
match = strcmp(s, *owalker) <= 0;
else
match = strcmp(s, *owalker) < 0;
*owalker += strlen(*owalker) + 1;
} else {
if (!CSONPATH_IS_NUM(el2))
break;
if (operation == CSONPATH_INST_FILTER_KEY_INFERIOR_EQ)
match = csonpath_int_from_walker(operand_instruction, owalker) >=
CSONPATH_GET_NUM(el2);
else
match = csonpath_int_from_walker(operand_instruction, owalker) >
CSONPATH_GET_NUM(el2);
}
break;
case CSONPATH_INST_FILTER_KEY_REG_EQ:
#if defined CSONPATH_NO_REGEX
fprintf(stderr, "regex deactivate");
return 0;
#else
if (el2 == CSONPATH_NULL)
return 0;
if (CSONPATH_IS_STR(el2)) {
int regex_idx = **owalker;
++*owalker;
if (regex_idx < 0 || regex_idx >= cjp->regex_cnt)
return 0;
CSONPATH_CLEANUP_STR const char *s = CSONPATH_GET_STR(el2);
# if !defined CSONPATH_PCRE2
match = csonpath_reg_exec(cjp->regexs[regex_idx], s);
# else
const unsigned char *str = (const unsigned char *)s;
int ret = pcre2_match(cjp->regexs[regex_idx], str, PCRE2_ZERO_TERMINATED, 0,
0, cjp->match_datas[regex_idx], NULL);
if (ret >= 0) {
return 1;
} else if (ret == PCRE2_ERROR_NOMATCH) {
return 0;
} else {
fprintf(stderr, "pcre2 match error: %d\n", ret);
return 0;
}
# endif
}
break;
#endif
}
return match;
}