Annotation of libelwix/src/json.c, revision 1.1.2.5
1.1.2.1 misho 1: /*************************************************************************
2: * (C) 2017 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.1.2.5 ! misho 6: * $Id: json.c,v 1.1.2.4 2017/11/28 00:46:53 misho Exp $
1.1.2.1 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:
15: Copyright 2004 - 2017
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:
1.1.2.2 misho 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:
1.1.2.3 misho 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_start = tok->tok_end = tok->tok_parent = -1;
121: tok->tok_size = 0;
122:
123: return tok;
124: }
125:
126: inline void
127: json_filltoken(jtok_t * __restrict tok, jtype_t type, long start, long end, long parent)
128: {
129: assert(tok);
130:
131: tok->tok_type = type;
132: tok->tok_start = start;
133: tok->tok_end = end;
134: tok->tok_parent = parent;
135: tok->tok_size = 0;
136: }
137:
138: static int
139: json_parse_string(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
140: {
141: jtok_t *tok;
142: u_long pos;
143: char ch;
144: register int i;
145:
146: assert(json || jstr || !(!jtoks && toksnum));
147:
148: for (pos = json->h_pos++; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
149: ch = jstr[json->h_pos];
150:
151: if (ch == '\"') {
152: if (!jtoks)
153: return 0;
154: if (!(tok = json_gettoken(json, jtoks, toksnum))) {
155: json->h_pos = pos;
156: return -1;
157: } else
158: json_filltoken(tok, J_STRING, pos + 1, json->h_pos, json->h_parent);
159: return 0;
160: }
161:
162: if (ch == '\\' && json->h_pos + 1 < jlen) {
163: switch (jstr[++json->h_pos]) {
164: case '\"':
165: case '/':
166: case '\\':
167: case 'b':
168: case 'f':
169: case 'r':
170: case 'n':
171: case 't':
172: /* Allowed escaped symbols */
173: break;
174: case 'u':
175: /* Allows escaped symbol \uXXXX */
176: json->h_pos++;
177: for (i = 0; i < 4 && json->h_pos < jlen && jstr[json->h_pos]; i++, json->h_pos++) {
178: /* If it isn't a hex character we have an error */
179: if (!((jstr[json->h_pos] >= 48 && jstr[json->h_pos] <= 57) || /* 0-9 */
180: (jstr[json->h_pos] >= 65 && jstr[json->h_pos] <= 70) || /* A-F */
181: (jstr[json->h_pos] >= 97 && jstr[json->h_pos] <= 102))) { /* a-f */
182: json->h_pos = pos;
183: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
184: return -1;
185: }
186: }
187: json->h_pos--;
188: break;
189: default:
190: /* Unexpected symbol */
191: json->h_pos = pos;
192: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
193: return -1;
194: }
195: }
196: }
197:
198: json->h_pos = pos;
199: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
200: return -1;
201: }
202:
203: static int
204: json_parse_value(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
205: {
206: jtok_t *tok;
207: u_long pos;
208:
209: assert(json || jstr || !(!jtoks && toksnum));
210:
211: for (pos = json->h_pos; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
212: switch (jstr[json->h_pos]) {
213: case ':':
214: if (json->h_strict)
215: goto found;
216: break;
217: case '\t':
218: case '\r':
219: case '\n':
220: case ' ':
221: case ',':
222: case ']':
223: case '}':
224: goto found;
225: }
226: if (jstr[json->h_pos] < 32 || jstr[json->h_pos] > 127) {
227: json->h_pos = pos;
228: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
229: return -1;
230: }
231: }
232:
233: if (json->h_strict) {
234: json->h_pos = pos;
235: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
236: return -1;
237: }
238: found:
239: if (jtoks) {
240: if (!(tok = json_gettoken(json, jtoks, toksnum))) {
241: json->h_pos = pos;
242: return -1;
243: } else
244: json_filltoken(tok, J_VALUE, pos, json->h_pos, json->h_parent);
245: }
246:
247: json->h_pos--;
248: return 0;
249: }
250:
1.1.2.2 misho 251: /*
252: * json_parse() - Parse JSON string
253: *
254: * @json = JSON handler
255: * @jstr = JSON string
256: * @jlen = JSON string length
257: * @jtoks = Token array
258: * @toksnum = Token array size, return number of allocated tokens in array
259: * return: -1 error or number of found tokens
260: */
261: u_int
262: json_parse(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum)
263: {
264: register int i;
265: register u_int cx;
266: jtype_t type;
267: jtok_t *tok;
268: char ch;
269:
270: if (!json || !jstr || (!jtoks && toksnum)) {
271: elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]);
272: return (u_int) -1;
273: }
274:
275: for (cx = json->h_next; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) {
276: switch ((ch = jstr[json->h_pos])) {
277: case '{':
278: case '[':
279: cx++; /* start new token */
280: if (!jtoks)
281: break;
282:
283: tok = json_gettoken(json, jtoks, toksnum);
1.1.2.3 misho 284: if (!tok)
1.1.2.2 misho 285: return (u_int) -1;
286: if (json->h_parent != -1) {
287: jtoks[json->h_parent].tok_size++;
288: tok->tok_parent = json->h_parent;
289: }
290: tok->tok_type = (ch == '{' ? J_OBJECT : J_ARRAY);
291: tok->tok_start = json->h_pos;
292: json->h_parent = json->h_next - 1;
293: break;
294: case '}':
295: case ']':
296: if (!jtoks)
297: break;
298:
299: if (json->h_next < 1) {
300: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
301: return (u_int) -1;
302: }
303:
304: type = (ch == '}' ? J_OBJECT : J_ARRAY);
305: tok = &jtoks[json->h_next - 1];
306: while (42) {
307: if (tok->tok_start != -1 && tok->tok_end == -1) {
308: if (tok->tok_type != type) {
309: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
310: return (u_int) -1;
311: }
312: tok->tok_end = json->h_pos + 1;
313: json->h_parent = tok->tok_parent;
314: break;
315: }
316: if (tok->tok_parent == -1) {
317: if (tok->tok_type != type || json->h_parent == -1) {
318: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
319: return (u_int) -1;
320: }
321: break;
322: }
323: tok = &jtoks[tok->tok_parent];
324: }
325: break;
326: case '\"':
327: if (json_parse_string(json, jstr, jlen, jtoks, toksnum) == -1) {
328: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
329: return (u_int) -1;
330: }
331: cx++;
332: if (jtoks && json->h_parent != -1)
333: jtoks[json->h_parent].tok_size++;
334: break;
335: case '\t':
336: case '\r':
337: case '\n':
338: case ' ':
339: /* whitespace, skip */
340: break;
341: case ':':
342: json->h_parent = json->h_next - 1;
343: break;
344: case ',':
345: if (jtoks && json->h_parent != -1 &&
346: jtoks[json->h_parent].tok_type != J_OBJECT &&
347: jtoks[json->h_parent].tok_type != J_ARRAY)
348: json->h_parent = jtoks[json->h_parent].tok_parent;
349: break;
350: case '-':
351: case '0':
352: case '1':
353: case '2':
354: case '3':
355: case '4':
356: case '5':
357: case '6':
358: case '7':
359: case '8':
360: case '9':
361: case 't':
362: case 'f':
363: case 'n':
1.1.2.3 misho 364: if (json->h_strict) {
365: if (jtoks && json->h_parent != -1) {
366: /* they must not be keys of the object */
367: if (jtoks[json->h_parent].tok_type == J_OBJECT ||
368: (jtoks[json->h_parent].tok_type == J_STRING &&
369: jtoks[json->h_parent].tok_size)) {
370: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
371: return (u_int) -1;
372: }
373: }
1.1.2.2 misho 374:
1.1.2.3 misho 375: if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1)
1.1.2.2 misho 376: return (u_int) -1;
1.1.2.3 misho 377: cx++;
378: if (jtoks && json->h_parent != -1)
379: jtoks[json->h_parent].tok_size++;
380: break;
1.1.2.2 misho 381: }
382: default:
383: if (json->h_strict) {
384: elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]);
385: return (u_int) -1;
386: }
387:
388: if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1)
389: return (u_int) -1;
390: cx++;
391: if (jtoks && json->h_parent != -1)
392: jtoks[json->h_parent].tok_size++;
393: break;
394: }
395: }
396:
397: if (jtoks) {
398: for (i = json->h_next - 1; i >= 0; i--) {
399: /* unmatched opened object or array */
400: if (jtoks[i].tok_start != -1 && jtoks[i].tok_end == -1) {
401: elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]);
402: return (u_int) -1;
403: }
404: }
405: }
406:
407: return cx;
1.1.2.4 misho 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, tok->tok_end - tok->tok_start);
430: strncpy(AIT_GET_STR(v), jstr + tok->tok_start, AIT_LEN(v) - 1);
431:
432: return v;
433: }
434:
435: /*
436: * json_token2str() - Return token to string
437: *
438: * @jstr = JSON string
439: * @tok = Token for convert
440: * @return =NULL error or !=NULL allocated str, after use should be e_free()
441: */
442: char *
443: json_token2str(const char *jstr, jtok_t * __restrict tok)
444: {
445: char *str = NULL;
446: size_t len;
447:
448: if (!jstr || !tok)
449: return NULL;
450:
451: len = tok->tok_end - tok->tok_start;
452: str = e_malloc(len + 1);
453: if (!str)
454: return NULL;
455: else {
456: strncpy(str, jstr + tok->tok_start, len);
457: str[len] = 0;
458: }
459:
460: return str;
461: }
462:
463: /*
464: * json_token2num() - Return token to numeric
465: *
466: * @jstr = JSON string
467: * @tok = Token for convert
468: * @return number
469: */
470: long
471: json_token2num(const char *jstr, jtok_t * __restrict tok)
472: {
473: long ret = 0;
474: char *str;
475:
476: str = json_token2str(jstr, tok);
477: if (!str)
478: return 0;
479:
480: ret = strtol(str, NULL, 0);
481: e_free(str);
482: return ret;
483: }
484:
485: /*
486: * json_findbykey() - Find data by key
487: *
488: * @jstr = JSON string
489: * @key = Search key
490: * @toks = Parsed tokens
491: * @toksnum = Number of parsed tokens
492: * return: =NULL error or !=NULL data token found
493: */
494: jtok_t *
495: json_findbykey(const char *jstr, const char *key, jtok_t * __restrict toks, int toksnum)
496: {
497: jtok_t *tok = NULL;
498: register int i;
499: int klen;
500:
501: if (!jstr || !key || !toks)
502: return NULL;
503: else
504: klen = strlen(key);
505:
506: for (i = 1; i < toksnum; i++) {
507: if (toks[i].tok_type == J_STRING &&
508: klen == toks[i].tok_end - toks[i].tok_start &&
509: !strncmp(jstr + toks[i].tok_start, key, klen)) {
510: tok = toks + i + 1;
511: break;
512: }
513: }
514:
515: return tok;
516: }
517:
518: /*
519: * json_token2array() - Convert token to array
520: *
521: * @jstr = JSON string
522: * @tok = Token for convert
523: * return: =NULL error or !=NULL allocated array with variables,
524: * after use should be ait_freeVars()
525: */
526: array_t *
527: json_token2array(const char *jstr, jtok_t * __restrict tok)
528: {
529: array_t *arr = NULL;
530: register int i;
531: int siz;
532: ait_val_t *v;
533: jtok_t *t;
534:
535: if (!jstr || !tok)
536: return NULL;
537:
538: siz = tok->tok_size;
1.1.2.5 ! misho 539: if (!siz && json_toktype(tok) != J_ARRAY && json_toktype(tok) != J_OBJECT)
1.1.2.4 misho 540: siz++;
541:
542: arr = ait_allocVars(siz);
543: if (!arr)
544: return NULL;
545:
546: if (tok->tok_type == J_STRING || tok->tok_type == J_VALUE) {
547: v = ait_getVars(&arr, 0);
1.1.2.5 ! misho 548: AIT_SET_STRSIZ(v, json_toklen(tok));
! 549: json_tokstrcpy(AIT_GET_STR(v), jstr, tok);
1.1.2.4 misho 550: } else if (tok->tok_type == J_ARRAY) {
551: for (i = 0; i < tok->tok_size; i++) {
552: t = &tok[i + 1];
553: v = ait_getVars(&arr, i);
1.1.2.5 ! misho 554: AIT_SET_STRSIZ(v, json_toklen(t));
! 555: json_tokstrcpy(AIT_GET_STR(v), jstr, t);
1.1.2.4 misho 556: }
557: } else {
558: /* todo for object */
559: }
560:
561: return arr;
1.1.2.2 misho 562: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>