--- libelwix/src/json.c 2024/10/27 03:57:34 1.9.34.3 +++ libelwix/src/json.c 2025/08/25 13:00:37 1.13 @@ -3,7 +3,7 @@ * by Michael Pounov * * $Author: misho $ -* $Id: json.c,v 1.9.34.3 2024/10/27 03:57:34 misho Exp $ +* $Id: json.c,v 1.13 2025/08/25 13:00:37 misho Exp $ * ************************************************************************** The ELWIX and AITNET software is distributed under the following @@ -12,7 +12,7 @@ terms: All of the documentation and software included in the ELWIX and AITNET Releases is copyrighted by ELWIX - Sofia/Bulgaria -Copyright 2004 - 2024 +Copyright 2004 - 2025 by Michael Pounov . All rights reserved. Redistribution and use in source and binary forms, with or without @@ -589,6 +589,49 @@ json_findbykey(const char *jstr, const char *key, jtyp } /* + * json_findbykeyatscope() - Find token data by key at particular scope + * + * @scope = Search at object scope, =0 main object scope + * @jstr = JSON string + * @key = Search key + * @type = Search key for particular token type, if is J_UNDEF this mean any type + * @toks = Parsed tokens + * @toksnum = Number of parsed tokens + * return: =NULL error or !=NULL data token found + */ +jtok_t * +json_findbykeyatscope(long scope, const char *jstr, const char *key, jtype_t type, jtok_t * __restrict toks, int toksnum) +{ + jtok_t *tok = NULL; + register int i; + int klen; + + if (!jstr || !key || !toks) + return NULL; + else + klen = strlen(key); + + for (i = 1; i < toksnum; i++) { + if (toks[i].tok_type == J_STRING && toks[i].tok_size == 1 && + toks[i].tok_parent == scope && + klen == toks[i].tok_end - toks[i].tok_start && + !strncmp(jstr + toks[i].tok_start, key, klen)) { + if (type != J_UNDEF) { + if (toks[i + 1].tok_type == type) { + tok = toks + i + 1; + break; + } + } else { + tok = toks + i + 1; + break; + } + } + } + + return tok; +} + +/* * json_findbypos() - Find token by position on JSON string * * @pos = Offset from begin of JSON string @@ -615,7 +658,7 @@ json_findbypos(u_long pos, jtok_t * __restrict toks, i } /* - * json_token2array() - Convert token to string array + * json_token2vars() - Convert token to string variable array * * @jstr = JSON string * @tok = Token for convert @@ -623,7 +666,7 @@ json_findbypos(u_long pos, jtok_t * __restrict toks, i * after use should be ait_freeVars() */ array_t * -json_token2array(const char *jstr, jtok_t * __restrict tok) +json_token2vars(const char *jstr, jtok_t * __restrict tok) { array_t *arr = NULL; register int i, j; @@ -685,9 +728,108 @@ json_token2array(const char *jstr, jtok_t * __restrict return arr; } +/* + * json_token2array() - Convert token to string array + * + * @jstr = JSON string + * @tok = Token for convert + * return: =NULL error or !=NULL allocated array with strings, + * after use should be ait_freearray() + */ +array_t * +json_token2array(const char *jstr, jtok_t * __restrict tok) +{ + array_t *arr = NULL; + register int i, j; + int siz; + char *str; + jtok_t *t; + if (!jstr || !tok) + return NULL; + siz = tok->tok_size; + if (!siz && json_toktype(tok) != J_ARRAY && json_toktype(tok) != J_OBJECT) + siz++; + + arr = array_Init(siz); + if (!arr) + return NULL; + + if (tok->tok_type == J_STRING || tok->tok_type == J_VALUE) { + str = e_malloc(json_toklen(tok) + 1); + if (!str) { + array_Destroy(&arr); + return NULL; + } else + json_tokstrcpy(str, jstr, tok); + if (array_Push(arr, str, 0) == -1) { + e_free(str); + array_Destroy(&arr); + return NULL; + } + } else if (tok->tok_type == J_ARRAY) { + for (i = 0, j = 1; i < tok->tok_size; i++) { + t = &tok[i + j]; + str = e_malloc(json_toklen(t) + 1); + if (!str) { + json_freearray(&arr); + return NULL; + } else + json_tokstrcpy(str, jstr, t); + if (array_Push(arr, str, 0) == -1) { + e_free(str); + json_freearray(&arr); + return NULL; + } + + /* if there we have array from objects should parse all object tokens */ + while (i < tok->tok_size - 1 && tok->tok_idx != tok[i + j + 1].tok_parent) + j++; + } + } else if (tok->tok_type == J_OBJECT) { + for (i = 0; tok->tok_idx <= tok[i + 1].tok_parent; i++) { + t = &tok[i + 1]; + str = e_malloc(json_toklen(t) + 1); + if (!str) { + json_freearray(&arr); + return NULL; + } else + json_tokstrcpy(str, jstr, t); + if (array_Push(arr, str, 0) == -1) { + e_free(str); + json_freearray(&arr); + return NULL; + } + } + } else { + elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]); + array_Destroy(&arr); + return NULL; + } + + return arr; +} + /* + * json_freearray() - Free & destroy allocated array from json_token2array function + * + * @parr = Array + * return -1 error or 0 ok + */ +int +json_freearray(array_t **parr) +{ + if (!parr) + return -1; + + array_Free(*parr); + array_Destroy(parr); + return 0; +} + + +/* * json_add_begin_object() - Adds begin of object { * * @jstr = JSON string @@ -1246,4 +1388,147 @@ json_dump(FILE *f, const char *jstr, jtok_t *toks, int } return 0; +} + +/* + * json_objscope() - Find object scope of key + * + * @key = Key of object, if it is =NULL, then return 0 (default scope) + * @jstr = JSON string + * @toks = JSON tokens + * @toksnum = Number of tokens + * return: -1 on error or >=0 scope of object + */ +long +json_objscope(const char *key, const char *jstr, jtok_t * __restrict toks, int toksnum) +{ + long scope = 0; + register int i; + int klen; + + if (!key) + return 0; /* default scope */ + + if (!jstr || !toks) + return -1; + else + klen = strlen(key); + + for (i = 1; i < toksnum; i++) + if (toks[i].tok_type == J_STRING && toks[i].tok_size == 1 && + klen == toks[i].tok_end - toks[i].tok_start && + !strncmp(jstr + toks[i].tok_start, key, klen)) + if (toks[i + 1].tok_type == J_OBJECT) { + scope = toks[i + 1].tok_idx; + break; + } + + return scope; +} + +/* + * json_validate() - Validate JSON + * + * @jstr = JSON string + * @started = if started != NULL then here will return start position of found JSON + * return: -1 error or >=0 where valid JSON ends + */ +int +json_validate(const char *jstr, int *started) +{ + register int o = 0, a = 0, q = 0, pos = 0; + + if (!jstr) + return -1; + + if (started) + *started = 0; + + while (jstr[pos] && jstr[pos] != '{' && jstr[pos] != '[') { + pos++; + if (started) + (*started)++; + } + + do { + switch (jstr[pos++]) { + case '{': + o++; + break; + case '}': + o--; + break; + case '[': + a++; + break; + case ']': + a--; + break; + case '"': + q = ~q; + break; + case '\\': + pos++; + break; + case 0: /* string ends without valid JSON */ + if (started) + *started = 0; + return 0; + } + } while (q || a || o); + + return pos; +} + +/* + * json_marshaling() - Prepare JSON to one continues line + * + * @jstr = JSON string + * @space = if it is 0 then spaces will be removed + * return NULL error or !=NULL ready JSON for proceeding + */ +char * +json_marshaling(char * __restrict jstr, int space) +{ + int started, ended, len; + char *str, *pos, *js; + register int q = 0; + + if (!jstr) + return NULL; + else + js = jstr; + + ended = json_validate(jstr, &started); + if (!ended) + return NULL; + len = ended - started + 1; + str = e_malloc(len); + if (!str) + return NULL; + else { + memset(str, 0, len); + pos = str; + } + + js += started; + while (*js && js < jstr + ended && --len) { + if (*js < 0x20) { + js++; + continue; + } + if (space && !q && *js == 0x20) { + js++; + continue; + } + if (*js == '"') + q = ~q; + *pos++ = *js++; + } + *pos = 0; + + len = strlen(jstr) + 1; + strlcpy(jstr, str, len); + e_free(str); + return jstr; }