Annotation of libelwix/src/json.c, revision 1.9.34.2
1.2 misho 1: /*************************************************************************
2: * (C) 2017 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.9.34.2! misho 6: * $Id: json.c,v 1.9.34.1 2024/10/26 14:55:56 misho Exp $
1.2 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
1.9.34.2! misho 15: Copyright 2004 - 2024
1.2 misho 16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: /* JSON error strings */
50: const char *jerrstr[] = {
51: "No error",
52: "Not enough tokens were provided",
53: "Invalid character",
54: "JSON string isn't full",
55: "Invalid parameter",
56: NULL
57: };
58:
59:
60: /*
61: * json_init() - Initialize JSON handler
62: *
63: * @json = JSON handler, if there is NULL then dynamically will be allocated
64: * @jstrict = JSON strict mode, when we select strict mode every unquoted value is error
65: * return: =NULL error or !=NULL ready for use JSON handler and should be free with json_free()
66: */
67: json_t *
68: json_init(json_t * __restrict json, int jstrict)
69: {
70: json_t *j = json;
71:
72: if (!j) {
73: j = e_malloc(sizeof(json_t));
74: if (!j) {
75: LOGERR;
76: return NULL;
77: }
78: }
79:
80: memset(j, 0, sizeof(json_t));
81: j->h_parent = -1;
82: j->h_strict = jstrict;
83:
84: /* handler is dynamically allocated! */
85: if (!json)
86: j->h_alloc = j;
87:
88: return j;
89: }
90:
91: /*
92: * json_free() - Free JSON handler
93: *
94: * @json = JSON handler
95: * return: none
96: */
97: void
98: json_free(json_t * __restrict json)
99: {
100: if (json) {
101: if (json->h_alloc)
102: e_free(json);
103: else
104: memset(json, 0, sizeof(json_t));
105: }
106: }
107:
108: static jtok_t *
109: json_gettoken(json_t * __restrict json, jtok_t * __restrict jtoks, u_int toksnum)
110: {
111: jtok_t *tok;
112:
113: assert(json || !(!jtoks && toksnum));
114:
115: if (json->h_next >= toksnum) {
116: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
117: return NULL;
118: } else
119: tok = &jtoks[json->h_next];
120: tok->tok_idx = json->h_next++;
121: tok->tok_start = tok->tok_end = tok->tok_parent = -1;
122: tok->tok_size = 0;
123:
124: return tok;
125: }
126:
127: inline void
128: json_filltoken(jtok_t * __restrict tok, jtype_t type, long start, long end, long parent)
129: {
130: assert(tok);
131:
132: tok->tok_type = type;
133: tok->tok_start = start;
134: tok->tok_end = end;
135: tok->tok_parent = parent;
136: tok->tok_size = 0;
137: }
138:
139: static int
140: json_parse_string(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
141: {
142: jtok_t *tok;
143: u_long pos;
144: char ch;
145: register int i;
146:
147: assert(json || jstr || !(!jtoks && toksnum));
148:
149: for (pos = json->h_pos++; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
150: ch = jstr[json->h_pos];
151:
152: if (ch == '\"') {
153: if (!jtoks)
154: return 0;
155: if (!(tok = json_gettoken(json, jtoks, toksnum))) {
156: json->h_pos = pos;
157: return -1;
158: } else
159: json_filltoken(tok, J_STRING, pos + 1, json->h_pos, json->h_parent);
160: return 0;
161: }
162:
163: if (ch == '\\' && json->h_pos + 1 < jlen) {
164: switch (jstr[++json->h_pos]) {
165: case '\"':
166: case '/':
167: case '\\':
168: case 'b':
169: case 'f':
170: case 'r':
171: case 'n':
172: case 't':
173: /* Allowed escaped symbols */
174: break;
175: case 'u':
176: /* Allows escaped symbol \uXXXX */
177: json->h_pos++;
178: for (i = 0; i < 4 && json->h_pos < jlen && jstr[json->h_pos]; i++, json->h_pos++) {
179: /* If it isn't a hex character we have an error */
180: if (!((jstr[json->h_pos] >= 48 && jstr[json->h_pos] <= 57) || /* 0-9 */
181: (jstr[json->h_pos] >= 65 && jstr[json->h_pos] <= 70) || /* A-F */
182: (jstr[json->h_pos] >= 97 && jstr[json->h_pos] <= 102))) { /* a-f */
183: json->h_pos = pos;
184: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
185: return -1;
186: }
187: }
188: json->h_pos--;
189: break;
190: default:
191: /* Unexpected symbol */
192: json->h_pos = pos;
193: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
194: return -1;
195: }
196: }
197: }
198:
199: json->h_pos = pos;
200: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
201: return -1;
202: }
203:
204: static int
205: json_parse_value(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
206: {
207: jtok_t *tok;
208: u_long pos;
209:
210: assert(json || jstr || !(!jtoks && toksnum));
211:
212: for (pos = json->h_pos; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
213: switch (jstr[json->h_pos]) {
214: case ':':
215: if (json->h_strict)
216: goto found;
217: break;
218: case '\t':
219: case '\r':
220: case '\n':
221: case ' ':
222: case ',':
223: case ']':
224: case '}':
225: goto found;
226: }
1.8 misho 227: if (jstr[json->h_pos] < 32 || (u_char) jstr[json->h_pos] > 127) {
1.2 misho 228: json->h_pos = pos;
229: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
230: return -1;
231: }
232: }
233:
234: if (json->h_strict) {
235: json->h_pos = pos;
236: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
237: return -1;
238: }
239: found:
240: if (jtoks) {
241: if (!(tok = json_gettoken(json, jtoks, toksnum))) {
242: json->h_pos = pos;
243: return -1;
244: } else
245: json_filltoken(tok, J_VALUE, pos, json->h_pos, json->h_parent);
246: }
247:
248: json->h_pos--;
249: return 0;
250: }
251:
252: /*
253: * json_parse() - Parse JSON string
254: *
255: * @json = JSON handler
256: * @jstr = JSON string
257: * @jlen = JSON string length
258: * @jtoks = Token array
259: * @toksnum = Token array size, return number of allocated tokens in array
260: * return: -1 error or number of found tokens
261: */
262: u_int
263: json_parse(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
264: {
265: register int i;
266: register u_int cx;
267: jtype_t type;
268: jtok_t *tok;
269: char ch;
270:
271: if (!json || !jstr || (!jtoks && toksnum)) {
272: elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]);
273: return (u_int) -1;
274: }
275:
276: for (cx = json->h_next; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
277: switch ((ch = jstr[json->h_pos])) {
278: case '{':
279: case '[':
280: cx++; /* start new object/array token */
281: if (!jtoks)
282: break;
283:
284: tok = json_gettoken(json, jtoks, toksnum);
285: if (!tok)
286: return (u_int) -1;
287: if (json->h_parent != -1) {
288: jtoks[json->h_parent].tok_size++;
289: tok->tok_parent = json->h_parent;
290: }
291: tok->tok_type = (ch == '{' ? J_OBJECT : J_ARRAY);
292: tok->tok_start = json->h_pos;
293: json->h_parent = json->h_next - 1;
294: break;
295: case '}':
296: case ']':
297: if (!jtoks)
298: break;
299:
300: if (json->h_next < 1) {
301: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
302: return (u_int) -1;
303: }
304:
305: type = (ch == '}' ? J_OBJECT : J_ARRAY);
306: tok = &jtoks[json->h_next - 1];
307: while (42) {
308: if (tok->tok_start != -1 && tok->tok_end == -1) {
309: if (tok->tok_type != type) {
310: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
311: return (u_int) -1;
312: }
313: tok->tok_end = json->h_pos + 1;
314: json->h_parent = tok->tok_parent;
315: break;
316: }
317: if (tok->tok_parent == -1) {
318: if (tok->tok_type != type || json->h_parent == -1) {
319: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
320: return (u_int) -1;
321: }
322: break;
323: }
324: tok = &jtoks[tok->tok_parent];
325: }
326: break;
327: case '\"':
328: if (json_parse_string(json, jstr, jlen, jtoks, toksnum) == -1)
329: return (u_int) -1;
330: cx++; /* start new string token */
331: if (jtoks && json->h_parent != -1)
332: jtoks[json->h_parent].tok_size++;
333: break;
334: case '\t':
335: case '\r':
336: case '\n':
337: case ' ':
338: /* whitespace, skip */
339: break;
340: case ':':
341: json->h_parent = json->h_next - 1;
342: break;
343: case ',':
344: if (jtoks && json->h_parent != -1 &&
345: jtoks[json->h_parent].tok_type != J_OBJECT &&
346: jtoks[json->h_parent].tok_type != J_ARRAY)
347: json->h_parent = jtoks[json->h_parent].tok_parent;
348: break;
349: case '-':
350: case '0':
351: case '1':
352: case '2':
353: case '3':
354: case '4':
355: case '5':
356: case '6':
357: case '7':
358: case '8':
359: case '9':
360: case 't':
361: case 'f':
362: case 'n':
363: if (json->h_strict) {
364: if (jtoks && json->h_parent != -1) {
365: /* they must not be keys of the object */
366: if (jtoks[json->h_parent].tok_type == J_OBJECT ||
367: (jtoks[json->h_parent].tok_type == J_STRING &&
368: jtoks[json->h_parent].tok_size)) {
369: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
370: return (u_int) -1;
371: }
372: }
373:
374: if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1)
375: return (u_int) -1;
376: cx++; /* start new value token */
377: if (jtoks && json->h_parent != -1)
378: jtoks[json->h_parent].tok_size++;
379: break;
380: }
381: default:
382: if (json->h_strict) {
383: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
384: return (u_int) -1;
385: }
386:
387: if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1)
388: return (u_int) -1;
389: cx++; /* start new value token */
390: if (jtoks && json->h_parent != -1)
391: jtoks[json->h_parent].tok_size++;
392: break;
393: }
394: }
395:
396: if (jtoks) {
397: for (i = json->h_next - 1; i >= 0; i--) {
398: /* unmatched opened object or array */
399: if (jtoks[i].tok_start != -1 && jtoks[i].tok_end == -1) {
400: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
401: return (u_int) -1;
402: }
403: }
404: } else
405: cx++; /* increment needed tokens number for termination empty token */
406:
407: return cx;
408: }
409:
410: /*
411: * json_token2val() - Return token to AIT variable
412: *
413: * @jstr = JSON string
414: * @tok = Token for convert
415: * @return =NULL error or !=NULL allocated variable, after use should be ait_freeVar()
416: */
417: ait_val_t *
418: json_token2val(const char *jstr, jtok_t * __restrict tok)
419: {
420: ait_val_t *v = NULL;
421:
422: if (!jstr || !tok)
423: return NULL;
424:
425: v = ait_allocVar();
426: if (!v)
427: return NULL;
428:
429: AIT_SET_STRSIZ(v, json_toklen(tok));
1.9.34.1 misho 430: if (AIT_GET_STR(v))
431: strncpy(AIT_GET_STR(v), json_tokstr(jstr, tok), AIT_LEN(v) - 1);
432: else
433: ait_freeVar(&v);
1.2 misho 434:
435: return v;
436: }
437:
438: /*
439: * json_token2str() - Return token to string
440: *
441: * @jstr = JSON string
442: * @tok = Token for convert
1.5 misho 443: * @return =NULL error or !=NULL allocated str, after use should be json_freestr()|e_free()
1.2 misho 444: */
445: char *
446: json_token2str(const char *jstr, jtok_t * __restrict tok)
447: {
1.8 misho 448: char *s, *s2, *wrk, *str = NULL;
1.2 misho 449: size_t len;
450:
451: if (!jstr || !tok)
452: return NULL;
453:
1.8 misho 454:
1.2 misho 455: len = json_toklen(tok);
456: str = e_malloc(len + 1);
457: if (!str)
458: return NULL;
459: else {
1.8 misho 460: memset(str, 0, len + 1);
461:
462: wrk = e_strdup(json_tokstr(jstr, tok));
463: wrk[len] = 0;
464: for (s = wrk, s2 = str; *s; s++)
465: *s2++ = (*s != '\\') ? *s : *++s;
466: e_free(wrk);
1.2 misho 467: }
468:
469: return str;
470: }
471:
472: /*
473: * json_token2num() - Return token to numeric
474: *
475: * @jstr = JSON string
476: * @tok = Token for convert
477: * @return number
478: */
479: long
480: json_token2num(const char *jstr, jtok_t * __restrict tok)
481: {
482: long ret = 0;
483: char *str;
484:
485: str = json_token2str(jstr, tok);
486: if (!str)
487: return 0;
488:
489: ret = strtol(str, NULL, 0);
490: e_free(str);
491: return ret;
492: }
493:
494: /*
1.5 misho 495: * json_token2dbl() - Return token to double
496: *
497: * @jstr = JSON string
498: * @tok = Token for convert
499: * @return number
500: */
501: double
502: json_token2dbl(const char *jstr, jtok_t * __restrict tok)
503: {
504: double ret = 0;
505: char *str;
506:
507: str = json_token2str(jstr, tok);
508: if (!str)
509: return 0;
510:
511: ret = strtod(str, NULL);
1.6 misho 512: e_free(str);
513: return ret;
514: }
515:
516: /*
517: * json_token2bool() - Return token to bool int
518: *
519: * @jstr = JSON string
520: * @tok = Token for convert
521: * @return 0 for FALSE and !=0 for TRUE
522: */
523: int
524: json_token2bool(const char *jstr, jtok_t * __restrict tok)
525: {
526: double ret = 0;
527: char *str;
528:
529: str = json_token2str(jstr, tok);
530: if (!str)
531: return 0;
532:
533: switch (*str) {
534: case 't':
535: case 'T':
536: ret = 1;
537: break;
538: case 'f':
539: case 'F':
540: ret = 0;
541: break;
542: default:
543: ret = (int) strtol(str, NULL, 10);
544: break;
545: }
1.5 misho 546: e_free(str);
547: return ret;
548: }
549:
550: /*
551: * json_findbykey() - Find token data by key
1.2 misho 552: *
553: * @jstr = JSON string
554: * @key = Search key
1.4 misho 555: * @type = Search key for particular token type, if is J_UNDEF this mean any type
1.2 misho 556: * @toks = Parsed tokens
557: * @toksnum = Number of parsed tokens
558: * return: =NULL error or !=NULL data token found
559: */
560: jtok_t *
1.4 misho 561: json_findbykey(const char *jstr, const char *key, jtype_t type, jtok_t * __restrict toks, int toksnum)
1.2 misho 562: {
563: jtok_t *tok = NULL;
564: register int i;
565: int klen;
566:
567: if (!jstr || !key || !toks)
568: return NULL;
569: else
570: klen = strlen(key);
571:
572: for (i = 1; i < toksnum; i++) {
1.9 misho 573: if (toks[i].tok_type == J_STRING && toks[i].tok_size == 1 &&
1.2 misho 574: klen == toks[i].tok_end - toks[i].tok_start &&
575: !strncmp(jstr + toks[i].tok_start, key, klen)) {
1.4 misho 576: if (type != J_UNDEF) {
577: if (toks[i + 1].tok_type == type) {
578: tok = toks + i + 1;
579: break;
580: }
581: } else {
582: tok = toks + i + 1;
583: break;
584: }
1.2 misho 585: }
586: }
587:
588: return tok;
589: }
590:
591: /*
1.5 misho 592: * json_findbypos() - Find token by position on JSON string
593: *
594: * @pos = Offset from begin of JSON string
595: * @toks = Parsed tokens
596: * @toksnum = Number of parsed tokens
597: * return: =NULL error or !=NULL token found
598: */
599: jtok_t *
600: json_findbypos(u_long pos, jtok_t * __restrict toks, int toksnum)
601: {
602: jtok_t *tok = NULL;
603: register int i;
604:
605: if (toks)
606: return NULL;
607:
608: for (i = 1; i < toksnum; i++)
609: if (pos <= toks[i].tok_end && pos >= toks[i].tok_start) {
610: tok = toks + i;
611: break;
612: }
613:
614: return tok;
615: }
616:
617: /*
1.2 misho 618: * json_token2array() - Convert token to string array
619: *
620: * @jstr = JSON string
621: * @tok = Token for convert
622: * return: =NULL error or !=NULL allocated array with string variables,
623: * after use should be ait_freeVars()
624: */
625: array_t *
626: json_token2array(const char *jstr, jtok_t * __restrict tok)
627: {
628: array_t *arr = NULL;
1.5 misho 629: register int i, j;
1.2 misho 630: int siz;
631: ait_val_t *v;
632: jtok_t *t;
633:
634: if (!jstr || !tok)
635: return NULL;
636:
637: siz = tok->tok_size;
638: if (!siz && json_toktype(tok) != J_ARRAY && json_toktype(tok) != J_OBJECT)
639: siz++;
640:
641: arr = ait_allocVars(siz);
642: if (!arr)
643: return NULL;
644:
645: if (tok->tok_type == J_STRING || tok->tok_type == J_VALUE) {
646: v = ait_getVars(&arr, 0);
647: AIT_SET_STRSIZ(v, json_toklen(tok) + 1);
648: json_tokstrcpy(AIT_GET_STR(v), jstr, tok);
649: } else if (tok->tok_type == J_ARRAY) {
1.5 misho 650: for (i = 0, j = 1; i < tok->tok_size; i++) {
651: t = &tok[i + j];
1.2 misho 652: v = ait_getVars(&arr, i);
653: AIT_SET_STRSIZ(v, json_toklen(t) + 1);
654: json_tokstrcpy(AIT_GET_STR(v), jstr, t);
1.5 misho 655:
656: /* if there we have array from objects should parse all object tokens */
657: while (i < tok->tok_size - 1 && tok->tok_idx != tok[i + j + 1].tok_parent)
658: j++;
1.2 misho 659: }
660: } else if (tok->tok_type == J_OBJECT) {
661: for (i = 0; tok->tok_idx <= tok[i + 1].tok_parent; i++) {
662: t = &tok[i + 1];
663: v = ait_getVars(&arr, i);
664: AIT_SET_STRSIZ(v, json_toklen(t) + 1);
665: json_tokstrcpy(AIT_GET_STR(v), jstr, t);
666: }
667: } else {
668: elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]);
669: ait_freeVars(&arr);
670: return NULL;
671: }
672:
673: return arr;
674: }
675:
676:
677:
678: /*
679: * json_add_begin_object() - Adds begin of object {
680: *
681: * @jstr = JSON string
682: * @jlen = JSON string length
683: * @wspace = whitespace include
684: * return: -1 error or !=-1 actual JSON string length
685: */
686: int
687: json_add_begin_object(char * __restrict jstr, int jlen, int wspace)
688: {
689: int len;
690: size_t eos;
691:
692: if (!jstr)
693: return -1;
694: else
695: eos = strlen(jstr);
696:
697:
698: if (wspace)
699: len = strlcat(jstr, "{ ", jlen);
700: else
701: len = strlcat(jstr, "{", jlen);
702:
703: if (len >= jlen) {
704: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
705: jstr[eos] = 0;
706: return -1;
707: }
708:
709: return len;
710: }
711:
712: /*
713: * json_add_end_object() - Adds end of object }
714: *
715: * @jstr = JSON string
716: * @jlen = JSON string length
717: * @wspace = whitespace include
718: * return: -1 error or !=-1 actual JSON string length
719: */
720: int
721: json_add_end_object(char * __restrict jstr, int jlen, int wspace)
722: {
723: int len;
724: size_t eos;
725:
726: if (!jstr)
727: return -1;
728: else
729: eos = strlen(jstr);
730:
731: if (wspace)
732: len = strlcat(jstr, " }", jlen);
733: else
734: len = strlcat(jstr, "}", jlen);
735:
736: if (len >= jlen) {
737: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
738: jstr[eos] = 0;
739: return -1;
740: }
741:
742: return len;
743: }
744:
745: /*
746: * json_add_begin_array() - Adds begin of array [
747: *
748: * @jstr = JSON string
749: * @jlen = JSON string length
750: * @wspace = whitespace include
751: * return: -1 error or !=-1 actual JSON string length
752: */
753: int
754: json_add_begin_array(char * __restrict jstr, int jlen, int wspace)
755: {
756: int len;
757: size_t eos;
758:
759: if (!jstr)
760: return -1;
761: else
762: eos = strlen(jstr);
763:
764: if (wspace)
765: len = strlcat(jstr, "[ ", jlen);
766: else
767: len = strlcat(jstr, "[", jlen);
768:
769: if (len >= jlen) {
770: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
771: jstr[eos] = 0;
772: return -1;
773: }
774:
775: return len;
776: }
777:
778: /*
779: * json_add_end_array() - Adds end of array ]
780: *
781: * @jstr = JSON string
782: * @jlen = JSON string length
783: * @wspace = whitespace include
784: * return: -1 error or !=-1 actual JSON string length
785: */
786: int
787: json_add_end_array(char * __restrict jstr, int jlen, int wspace)
788: {
789: int len;
790: size_t eos;
791:
792: if (!jstr)
793: return -1;
794: else
795: eos = strlen(jstr);
796:
797: if (wspace)
798: len = strlcat(jstr, " ]", jlen);
799: else
800: len = strlcat(jstr, "]", jlen);
801:
802: if (len >= jlen) {
803: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
804: jstr[eos] = 0;
805: return -1;
806: }
807:
808: return len;
809: }
810:
811: /*
812: * json_add_char() - Adds character
813: *
814: * @jstr = JSON string
815: * @jlen = JSON string length
816: * @ch = Character
817: * return: -1 error or !=-1 actual JSON string length
818: */
819: int
820: json_add_char(char * __restrict jstr, int jlen, u_char ch)
821: {
822: int len;
823:
824: if (!jstr)
825: return -1;
826:
827: len = strlen(jstr) + 1;
828: if (len >= jlen) {
829: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
830: return -1;
831: } else {
832: jstr[len++] = (char) ch;
833: jstr[len] = 0;
834: }
835:
836: return len;
837: }
838:
839: /*
840: * json_add_colon() - Adds key/value pair delimiter colon :
841: *
842: * @jstr = JSON string
843: * @jlen = JSON string length
844: * @wspace = whitespace include
845: * return: -1 error or !=-1 actual JSON string length
846: */
847: int
848: json_add_colon(char * __restrict jstr, int jlen, int wspace)
849: {
850: int len;
851: size_t eos;
852:
853: if (!jstr)
854: return -1;
855: else
856: eos = strlen(jstr);
857:
858: if (wspace)
859: len = strlcat(jstr, ": ", jlen);
860: else
861: len = strlcat(jstr, ":", jlen);
862:
863: if (len >= jlen) {
864: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
865: jstr[eos] = 0;
866: return -1;
867: }
868:
869: return len;
870: }
871:
872: /*
873: * json_add_comma() - Adds value delimiter comma ,
874: *
875: * @jstr = JSON string
876: * @jlen = JSON string length
877: * @wspace = whitespace include
878: * return: -1 error or !=-1 actual JSON string length
879: */
880: int
881: json_add_comma(char * __restrict jstr, int jlen, int wspace)
882: {
883: int len;
884: size_t eos;
885:
886: if (!jstr)
887: return -1;
888: else
889: eos = strlen(jstr);
890:
891: if (wspace)
892: len = strlcat(jstr, ", ", jlen);
893: else
894: len = strlcat(jstr, ",", jlen);
895:
896: if (len >= jlen) {
897: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
898: jstr[eos] = 0;
899: return -1;
900: }
901:
902: return len;
903: }
904:
905: /*
906: * json_add_string() - Adds string
907: *
908: * @jstr = JSON string
909: * @jlen = JSON string length
910: * @unquot = Unquoted string
911: * @str = String, it can't be NULL
912: * return: -1 error or !=-1 actual JSON string length
913: */
914: int
915: json_add_string(char * __restrict jstr, int jlen, int unquot, const char *str)
916: {
917: int len;
918: size_t eos;
919:
920: if (!jstr || !str)
921: return -1;
922: else
923: eos = strlen(jstr);
924:
925: if (!unquot) {
926: len = strlcat(jstr, "\"", jlen);
927: if (len >= jlen) {
928: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
929: jstr[eos] = 0;
930: return -1;
931: }
932: }
933: len = strlcat(jstr, str, jlen);
934: if (len >= jlen) {
935: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
936: jstr[eos] = 0;
937: return -1;
938: }
939: if (!unquot) {
940: len = strlcat(jstr, "\"", jlen);
941: if (len >= jlen) {
942: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
943: jstr[eos] = 0;
944: return -1;
945: }
946: }
947:
948: return len;
949: }
950:
951: /*
952: * json_add_value() - Adds value
953: *
954: * @jstr = JSON string
955: * @jlen = JSON string length
956: * @unquot = Unquoted number
957: * @num = Number
958: * return: -1 error or !=-1 actual JSON string length
959: */
960: int
961: json_add_value(char * __restrict jstr, int jlen, int unquot, long num)
962: {
963: int len;
964: char wrk[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
965: size_t eos;
966:
967: if (!jstr)
968: return -1;
969: else
970: eos = strlen(jstr);
971:
972: if (!unquot) {
973: len = strlcat(jstr, "\"", jlen);
974: if (len >= jlen) {
975: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
976: jstr[eos] = 0;
977: return -1;
978: }
979: }
980: snprintf(wrk, sizeof wrk, "%ld", num);
981: len = strlcat(jstr, wrk, jlen);
982: if (len >= jlen) {
983: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
984: jstr[eos] = 0;
985: return -1;
986: }
987: if (!unquot) {
988: len = strlcat(jstr, "\"", jlen);
989: if (len >= jlen) {
990: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]);
991: jstr[eos] = 0;
992: return -1;
993: }
994: }
995:
996: return len;
997: }
998:
999: /*
1000: * json_add_pair() - Adds key/value pair
1001: *
1002: * @jstr = JSON string
1003: * @jlen = JSON string length
1004: * @wspace = whitespace include
1005: * @key = Key string
1006: * @val = Value string
1007: * return: -1 error or !=-1 actual JSON string length
1008: */
1009: int
1010: json_add_pair(char * __restrict jstr, int jlen, int wspace, const char *key, const char *val)
1011: {
1012: int len = -1;
1013: size_t eos;
1014:
1015: if (!jstr || !key || !val)
1016: return -1;
1017: else
1018: eos = strlen(jstr);
1019:
1020: if (json_add_string(jstr, jlen, 0, key) == -1) {
1021: jstr[eos] = 0;
1022: return -1;
1023: }
1024: if (json_add_colon(jstr, jlen, wspace) == -1) {
1025: jstr[eos] = 0;
1026: return -1;
1027: }
1028: if ((len = json_add_string(jstr, jlen, 0, val)) == -1) {
1.7 misho 1029: jstr[eos] = 0;
1030: return -1;
1031: }
1032:
1033: return len;
1034: }
1035:
1036: /*
1037: * json_add_pair2() - Adds key/value pair with formated args
1038: *
1039: * @jstr = JSON string
1040: * @jlen = JSON string length
1041: * @wspace = whitespace include
1042: * @key = Key string
1043: * @fmt = Format string for values
1044: * return: -1 error or !=-1 actual JSON string length
1045: */
1046: int
1047: json_add_pair2(char * __restrict jstr, int jlen, int wspace, const char *key, const char *fmt, ...)
1048: {
1049: int len = -1;
1050: size_t eos;
1051: va_list lst;
1052: char szStr[BUFSIZ] = { [0 ... BUFSIZ - 1] = 0 };
1053:
1054: if (!jstr || !key || !fmt)
1055: return -1;
1056: else
1057: eos = strlen(jstr);
1058:
1059: if (json_add_string(jstr, jlen, 0, key) == -1) {
1060: jstr[eos] = 0;
1061: return -1;
1062: }
1063: if (json_add_colon(jstr, jlen, wspace) == -1) {
1064: jstr[eos] = 0;
1065: return -1;
1066: }
1067: va_start(lst, fmt);
1068: vsnprintf(szStr, sizeof szStr, fmt, lst);
1069: va_end(lst);
1070: if ((len = json_add_string(jstr, jlen, 0, szStr)) == -1) {
1.2 misho 1071: jstr[eos] = 0;
1072: return -1;
1073: }
1074:
1075: return len;
1076: }
1077:
1078: /*
1079: * json_add_array() - Adds array
1080: *
1081: * @jstr = JSON string
1082: * @jlen = JSON string length
1083: * @wspace = whitespace include
1084: * @arr = Array with variables
1085: * return: -1 error or !=-1 actual JSON string length
1086: */
1087: int
1088: json_add_array(char * __restrict jstr, int jlen, int wspace, array_t * __restrict arr)
1089: {
1090: int len = -1;
1091: register int i;
1092: ait_val_t *v;
1093: size_t eos;
1094:
1095: if (!jstr || !arr)
1096: return -1;
1097: else
1098: eos = strlen(jstr);
1099:
1100: if (json_add_begin_array(jstr, jlen, wspace) == -1) {
1101: jstr[eos] = 0;
1102: return -1;
1103: }
1104: for (i = 0; i < array_Size(arr); i++) {
1105: v = array(arr, i, ait_val_t*);
1106: if (v) {
1107: if (AIT_TYPE(v) == string) {
1108: if (json_add_string(jstr, jlen, 0, AIT_GET_STR(v)) == -1) {
1109: jstr[eos] = 0;
1110: return -1;
1111: }
1112: } else {
1113: if (json_add_value(jstr, jlen, 0, AIT_GET_LIKE(v, long)) == -1) {
1114: jstr[eos] = 0;
1115: return -1;
1116: }
1117: }
1118: if (i < array_Size(arr) - 1 && json_add_comma(jstr, jlen, wspace) == -1) {
1119: jstr[eos] = 0;
1120: return -1;
1121: }
1122: }
1123: }
1124: if ((len = json_add_end_array(jstr, jlen, wspace)) == -1) {
1125: jstr[eos] = 0;
1126: return -1;
1127: }
1128:
1129: return len;
1.3 misho 1130: }
1131:
1132: /*
1133: * json_dump_yaml() - Dump parsed JSON string to YAML format
1134: *
1135: * @f = Output handler
1136: * @jstr = JSON string
1137: * @toks = JSON tokens
1138: * @toksnum = Number of tokens
1139: * @indent = Start indent spaces
1140: * return: 0 done and 1 added one more item
1141: */
1142: int
1143: json_dump_yaml(FILE *f, const char *jstr, jtok_t *toks, int toksnum, int indent)
1144: {
1145: register int i, j, k;
1146:
1147: if (!toksnum)
1148: return 0;
1149:
1150: if (toks->tok_type == J_VALUE) {
1151: fprintf(f, "%.*s", (int) json_toklen(toks), json_tokstr(jstr, toks));
1152: return 1;
1153: } else if (toks->tok_type == J_STRING) {
1154: fprintf(f, "%.*s", (int) json_toklen(toks), json_tokstr(jstr, toks));
1155: return 1;
1156: } else if (toks->tok_type == J_OBJECT) {
1157: fprintf(f, "\n");
1158: for (j = i = 0; i < json_toksize(toks); i++) {
1159: for (k = 0; k < indent; k++)
1160: fprintf(f, " ");
1161: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1162: fprintf(f, ": ");
1163: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1164: fprintf(f, "\n");
1165: }
1166: return j + 1;
1167: } else if (toks->tok_type == J_ARRAY) {
1168: fprintf(f, "\n");
1169: for (j = i = 0; i < json_toksize(toks); i++) {
1170: for (k = 0; k < indent - 1; k++)
1171: fprintf(f, " ");
1172: fprintf(f, " - ");
1173: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1.5 misho 1174: fprintf(f, "\n");
1175: }
1176: return j + 1;
1177: }
1178:
1179: return 0;
1180: }
1181:
1182: /*
1183: * json_dump() - Dump parsed JSON string to structure format
1184: *
1185: * @f = Output handler
1186: * @jstr = JSON string
1187: * @toks = JSON tokens
1188: * @toksnum = Number of tokens
1189: * @indent = Start indent spaces
1190: * return: 0 done and 1 added one more item
1191: */
1192: int
1193: json_dump(FILE *f, const char *jstr, jtok_t *toks, int toksnum, int indent)
1194: {
1195: register int i, j, k;
1196:
1197: if (!toksnum)
1198: return 0;
1199:
1200: if (toks->tok_type == J_VALUE) {
1201: fprintf(f, "[idx=%ld type=VALUE start=%ld end=%ld size=%ld parent=%ld] = %.*s",
1202: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent,
1203: (int) json_toklen(toks), json_tokstr(jstr, toks));
1204: return 1;
1205: } else if (toks->tok_type == J_STRING) {
1206: fprintf(f, "[idx=%ld type=STRING start=%ld end=%ld size=%ld parent=%ld] = %.*s",
1207: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent,
1208: (int) json_toklen(toks), json_tokstr(jstr, toks));
1209: return 1;
1210: } else if (toks->tok_type == J_OBJECT) {
1211: fprintf(f, "\n");
1212: fprintf(f, "object:: [idx=%ld type=OBJECT start=%ld end=%ld size=%ld parent=%ld]\n",
1213: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent);
1214: for (j = i = 0; i < json_toksize(toks); i++) {
1215: for (k = 0; k < indent; k++)
1216: fprintf(f, " ");
1217: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1218: fprintf(f, ": ");
1219: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1220: fprintf(f, "\n");
1221: }
1222: return j + 1;
1223: } else if (toks->tok_type == J_ARRAY) {
1224: fprintf(f, "\n");
1225: fprintf(f, "array[] [idx=%ld type=ARRAY start=%ld end=%ld size=%ld parent=%ld]",
1226: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent);
1227: for (j = i = 0; i < json_toksize(toks); i++) {
1228: for (k = 0; k < indent - 1; k++)
1229: fprintf(f, " ");
1230: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1);
1.3 misho 1231: fprintf(f, "\n");
1232: }
1233: return j + 1;
1234: }
1235:
1236: return 0;
1.2 misho 1237: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>