--- libelwix/src/json.c	2017/12/03 21:50:23	1.3
+++ libelwix/src/json.c	2024/12/04 17:47:28	1.11
@@ -3,7 +3,7 @@
 *  by Michael Pounov <misho@elwix.org>
 *
 * $Author: misho $
-* $Id: json.c,v 1.3 2017/12/03 21:50:23 misho Exp $
+* $Id: json.c,v 1.11 2024/12/04 17:47:28 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 <info@elwix.org>
 
-Copyright 2004 - 2017
+Copyright 2004 - 2024
 	by Michael Pounov <misho@elwix.org>.  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -224,7 +224,7 @@ json_parse_value(json_t * __restrict json, const char 
 			case '}':
 				goto found;
 		}
-		if (jstr[json->h_pos] < 32 || jstr[json->h_pos] > 127) {
+		if (jstr[json->h_pos] < 32 || (u_char) jstr[json->h_pos] > 127) {
 			json->h_pos = pos;
 			elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
 			return -1;
@@ -427,7 +427,10 @@ json_token2val(const char *jstr, jtok_t * __restrict t
 		return NULL;
 
 	AIT_SET_STRSIZ(v, json_toklen(tok));
-	strncpy(AIT_GET_STR(v), json_tokstr(jstr, tok), AIT_LEN(v) - 1);
+	if (AIT_GET_STR(v))
+		strncpy(AIT_GET_STR(v), json_tokstr(jstr, tok), AIT_LEN(v) - 1);
+	else
+		ait_freeVar(&v);
 
 	return v;
 }
@@ -437,24 +440,30 @@ json_token2val(const char *jstr, jtok_t * __restrict t
  *
  * @jstr = JSON string
  * @tok = Token for convert
- * @return =NULL error or !=NULL allocated str, after use should be e_free()
+ * @return =NULL error or !=NULL allocated str, after use should be json_freestr()|e_free()
  */
 char *
 json_token2str(const char *jstr, jtok_t * __restrict tok)
 {
-	char *str = NULL;
+	char *s, *s2, *wrk, *str = NULL;
 	size_t len;
 
 	if (!jstr || !tok)
 		return NULL;
 
+
 	len = json_toklen(tok);
 	str = e_malloc(len + 1);
 	if (!str)
 		return NULL;
 	else {
-		strncpy(str, json_tokstr(jstr, tok), len);
-		str[len] = 0;
+		memset(str, 0, len + 1);
+
+		wrk = e_strdup(json_tokstr(jstr, tok));
+		wrk[len] = 0;
+		for (s = wrk, s2 = str; *s; s++)
+			*s2++ = (*s != '\\') ? *s : *++s;
+		e_free(wrk);
 	}
 
 	return str;
@@ -483,16 +492,73 @@ json_token2num(const char *jstr, jtok_t * __restrict t
 }
 
 /*
- * json_findbykey() - Find data by key
+ * json_token2dbl() - Return token to double
  *
  * @jstr = JSON string
+ * @tok = Token for convert
+ * @return number
+ */
+double
+json_token2dbl(const char *jstr, jtok_t * __restrict tok)
+{
+	double ret = 0;
+	char *str;
+
+	str = json_token2str(jstr, tok);
+	if (!str)
+		return 0;
+
+	ret = strtod(str, NULL);
+	e_free(str);
+	return ret;
+}
+
+/*
+ * json_token2bool() - Return token to bool int
+ *
+ * @jstr = JSON string
+ * @tok = Token for convert
+ * @return 0 for FALSE and !=0 for TRUE
+ */
+int
+json_token2bool(const char *jstr, jtok_t * __restrict tok)
+{
+	double ret = 0;
+	char *str;
+
+	str = json_token2str(jstr, tok);
+	if (!str)
+		return 0;
+
+	switch (*str) {
+		case 't':
+		case 'T':
+			ret = 1;
+			break;
+		case 'f':
+		case 'F':
+			ret = 0;
+			break;
+		default:
+			ret = (int) strtol(str, NULL, 10);
+			break;
+	}
+	e_free(str);
+	return ret;
+}
+
+/*
+ * json_findbykey() - Find token data by key
+ *
+ * @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_findbykey(const char *jstr, const char *key, jtok_t * __restrict toks, int toksnum)
+json_findbykey(const char *jstr, const char *key, jtype_t type, jtok_t * __restrict toks, int toksnum)
 {
 	jtok_t *tok = NULL;
 	register int i;
@@ -504,11 +570,18 @@ json_findbykey(const char *jstr, const char *key, jtok
 		klen = strlen(key);
 
 	for (i = 1; i < toksnum; i++) {
-		if (toks[i].tok_type == J_STRING && 
+		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)) {
-			tok = toks + i + 1;
-			break;
+			if (type != J_UNDEF) {
+				if (toks[i + 1].tok_type == type) {
+					tok = toks + i + 1;
+					break;
+				}
+			} else {
+				tok = toks + i + 1;
+				break;
+			}
 		}
 	}
 
@@ -516,6 +589,75 @@ json_findbykey(const char *jstr, const char *key, jtok
 }
 
 /*
+ * 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
+ * @toks = Parsed tokens
+ * @toksnum = Number of parsed tokens
+ * return: =NULL error or !=NULL token found 
+ */
+jtok_t *
+json_findbypos(u_long pos, jtok_t * __restrict toks, int toksnum)
+{
+	jtok_t *tok = NULL;
+	register int i;
+
+	if (!toks)
+		return NULL;
+
+	for (i = 1; i < toksnum; i++)
+		if (pos <= toks[i].tok_end && pos >= toks[i].tok_start) {
+			tok = toks + i;
+			break;
+		}
+
+	return tok;
+}
+
+/*
  * json_token2array() - Convert token to string array
  *
  * @jstr = JSON string
@@ -527,7 +669,7 @@ array_t *
 json_token2array(const char *jstr, jtok_t * __restrict tok)
 {
 	array_t *arr = NULL;
-	register int i;
+	register int i, j;
 	int siz;
 	ait_val_t *v;
 	jtok_t *t;
@@ -546,20 +688,36 @@ json_token2array(const char *jstr, jtok_t * __restrict
 	if (tok->tok_type == J_STRING || tok->tok_type == J_VALUE) {
 		v = ait_getVars(&arr, 0);
 		AIT_SET_STRSIZ(v, json_toklen(tok) + 1);
-		json_tokstrcpy(AIT_GET_STR(v), jstr, tok);
+		if (AIT_GET_STR(v)) {
+			json_tokstrcpy(AIT_GET_STR(v), jstr, tok);
+		} else {
+			ait_freeVar(&v);
+		}
 	} else if (tok->tok_type == J_ARRAY) {
-		for (i = 0; i < tok->tok_size; i++) {
-			t = &tok[i + 1];
+		for (i = 0, j = 1; i < tok->tok_size; i++) {
+			t = &tok[i + j];
 			v = ait_getVars(&arr, i);
 			AIT_SET_STRSIZ(v, json_toklen(t) + 1);
-			json_tokstrcpy(AIT_GET_STR(v), jstr, t);
+			if (AIT_GET_STR(v)) {
+				json_tokstrcpy(AIT_GET_STR(v), jstr, t);
+			} else {
+				ait_freeVar(&v);
+			}
+
+			/* 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];
 			v = ait_getVars(&arr, i);
 			AIT_SET_STRSIZ(v, json_toklen(t) + 1);
-			json_tokstrcpy(AIT_GET_STR(v), jstr, t);
+			if (AIT_GET_STR(v)) {
+				json_tokstrcpy(AIT_GET_STR(v), jstr, t);
+			} else {
+				ait_freeVar(&v);
+			}
 		}
 	} else {
 		elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]);
@@ -931,6 +1089,48 @@ json_add_pair(char * __restrict jstr, int jlen, int ws
 }
 
 /*
+ * json_add_pair2() - Adds key/value pair with formated args
+ *
+ * @jstr = JSON string
+ * @jlen = JSON string length
+ * @wspace = whitespace include
+ * @key = Key string
+ * @fmt = Format string for values
+ * return: -1 error or !=-1 actual JSON string length
+ */
+int
+json_add_pair2(char * __restrict jstr, int jlen, int wspace, const char *key, const char *fmt, ...)
+{
+	int len = -1;
+	size_t eos;
+	va_list lst;
+	char szStr[BUFSIZ] = { [0 ... BUFSIZ - 1] = 0 };
+
+	if (!jstr || !key || !fmt)
+		return -1;
+	else
+		eos = strlen(jstr);
+
+	if (json_add_string(jstr, jlen, 0, key) == -1) {
+		jstr[eos] = 0;
+		return -1;
+	}
+	if (json_add_colon(jstr, jlen, wspace) == -1) {
+		jstr[eos] = 0;
+		return -1;
+	}
+	va_start(lst, fmt);
+	vsnprintf(szStr, sizeof szStr, fmt, lst);
+	va_end(lst);
+	if ((len = json_add_string(jstr, jlen, 0, szStr)) == -1) {
+		jstr[eos] = 0;
+		return -1;
+	}
+
+	return len;
+}
+
+/*
  * json_add_array() - Adds array
  *
  * @jstr = JSON string
@@ -999,7 +1199,7 @@ json_dump_yaml(FILE *f, const char *jstr, jtok_t *toks
 {
 	register int i, j, k;
 
-	if (!toksnum)
+	if (!toksnum || !toks)
 		return 0;
 
 	if (toks->tok_type == J_VALUE) {
@@ -1032,4 +1232,97 @@ json_dump_yaml(FILE *f, const char *jstr, jtok_t *toks
 	}
 
 	return 0;
+}
+
+/*
+ * json_dump() - Dump parsed JSON string to structure format
+ *
+ * @f = Output handler
+ * @jstr = JSON string
+ * @toks = JSON tokens
+ * @toksnum = Number of tokens
+ * @indent = Start indent spaces
+ * return: 0 done and 1 added one more item
+ */
+int
+json_dump(FILE *f, const char *jstr, jtok_t *toks, int toksnum, int indent)
+{
+	register int i, j, k;
+
+	if (!toksnum)
+		return 0;
+
+	if (toks->tok_type == J_VALUE) {
+		fprintf(f, "[idx=%ld type=VALUE start=%ld end=%ld size=%ld parent=%ld] = %.*s", 
+				toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent, 
+				(int) json_toklen(toks), json_tokstr(jstr, toks));
+		return 1;
+	} else if (toks->tok_type == J_STRING) {
+		fprintf(f, "[idx=%ld type=STRING start=%ld end=%ld size=%ld parent=%ld] = %.*s", 
+				toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent, 
+				(int) json_toklen(toks), json_tokstr(jstr, toks));
+		return 1;
+	} else if (toks->tok_type == J_OBJECT) {
+		fprintf(f, "\n");
+		fprintf(f, "object:: [idx=%ld type=OBJECT start=%ld end=%ld size=%ld parent=%ld]\n", 
+				toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent);
+		for (j = i = 0; i < json_toksize(toks); i++) {
+			for (k = 0; k < indent; k++)
+				fprintf(f, "  ");
+			j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
+			fprintf(f, ": ");
+			j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
+			fprintf(f, "\n");
+		}
+		return j + 1;
+	} else if (toks->tok_type == J_ARRAY) {
+		fprintf(f, "\n");
+		fprintf(f, "array[] [idx=%ld type=ARRAY start=%ld end=%ld size=%ld parent=%ld]", 
+				toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent);
+		for (j = i = 0; i < json_toksize(toks); i++) {
+			for (k = 0; k < indent - 1; k++)
+				fprintf(f, "  ");
+			j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
+			fprintf(f, "\n");
+		}
+		return j + 1;
+	}
+
+	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;
 }