Annotation of libelwix/src/json.c, revision 1.1.2.4
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.4 ! misho 6: * $Id: json.c,v 1.1.2.3 2017/11/27 19:37:22 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;
! 539: if (!siz && tok->tok_type != J_ARRAY && tok->tok_type != J_OBJECT)
! 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);
! 548: AIT_SET_STRSIZ(v, tok->tok_end - tok->tok_start);
! 549: strncpy(AIT_GET_STR(v), jstr + tok->tok_start, AIT_LEN(v) - 1);
! 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);
! 554: AIT_SET_STRSIZ(v, t->tok_end - t->tok_start);
! 555: strncpy(AIT_GET_STR(v), jstr + t->tok_start, AIT_LEN(v) - 1);
! 556: }
! 557: } else {
! 558: /* todo for object */
! 559: }
! 560:
! 561: return arr;
1.1.2.2 misho 562: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>