Annotation of libelwix/src/json.c, revision 1.1.2.3
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.3 ! misho 6: * $Id: json.c,v 1.1.2.2 2017/11/24 15:52:57 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;
408: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>