![]() ![]() | ![]() |
adds new UT and fix bug in json_findbykey()
1: /************************************************************************* 2: * (C) 2017 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org> 3: * by Michael Pounov <misho@elwix.org> 4: * 5: * $Author: misho $ 6: * $Id: json.c,v 1.8.10.1 2020/08/22 01:18:55 misho Exp $ 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 - 2019 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: } 227: if (jstr[json->h_pos] < 32 || (u_char) jstr[json->h_pos] > 127) { 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)); 430: strncpy(AIT_GET_STR(v), json_tokstr(jstr, tok), 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 json_freestr()|e_free() 441: */ 442: char * 443: json_token2str(const char *jstr, jtok_t * __restrict tok) 444: { 445: char *s, *s2, *wrk, *str = NULL; 446: size_t len; 447: 448: if (!jstr || !tok) 449: return NULL; 450: 451: 452: len = json_toklen(tok); 453: str = e_malloc(len + 1); 454: if (!str) 455: return NULL; 456: else { 457: memset(str, 0, len + 1); 458: 459: wrk = e_strdup(json_tokstr(jstr, tok)); 460: wrk[len] = 0; 461: for (s = wrk, s2 = str; *s; s++) 462: *s2++ = (*s != '\\') ? *s : *++s; 463: e_free(wrk); 464: } 465: 466: return str; 467: } 468: 469: /* 470: * json_token2num() - Return token to numeric 471: * 472: * @jstr = JSON string 473: * @tok = Token for convert 474: * @return number 475: */ 476: long 477: json_token2num(const char *jstr, jtok_t * __restrict tok) 478: { 479: long ret = 0; 480: char *str; 481: 482: str = json_token2str(jstr, tok); 483: if (!str) 484: return 0; 485: 486: ret = strtol(str, NULL, 0); 487: e_free(str); 488: return ret; 489: } 490: 491: /* 492: * json_token2dbl() - Return token to double 493: * 494: * @jstr = JSON string 495: * @tok = Token for convert 496: * @return number 497: */ 498: double 499: json_token2dbl(const char *jstr, jtok_t * __restrict tok) 500: { 501: double ret = 0; 502: char *str; 503: 504: str = json_token2str(jstr, tok); 505: if (!str) 506: return 0; 507: 508: ret = strtod(str, NULL); 509: e_free(str); 510: return ret; 511: } 512: 513: /* 514: * json_token2bool() - Return token to bool int 515: * 516: * @jstr = JSON string 517: * @tok = Token for convert 518: * @return 0 for FALSE and !=0 for TRUE 519: */ 520: int 521: json_token2bool(const char *jstr, jtok_t * __restrict tok) 522: { 523: double ret = 0; 524: char *str; 525: 526: str = json_token2str(jstr, tok); 527: if (!str) 528: return 0; 529: 530: switch (*str) { 531: case 't': 532: case 'T': 533: ret = 1; 534: break; 535: case 'f': 536: case 'F': 537: ret = 0; 538: break; 539: default: 540: ret = (int) strtol(str, NULL, 10); 541: break; 542: } 543: e_free(str); 544: return ret; 545: } 546: 547: /* 548: * json_findbykey() - Find token data by key 549: * 550: * @jstr = JSON string 551: * @key = Search key 552: * @type = Search key for particular token type, if is J_UNDEF this mean any type 553: * @toks = Parsed tokens 554: * @toksnum = Number of parsed tokens 555: * return: =NULL error or !=NULL data token found 556: */ 557: jtok_t * 558: json_findbykey(const char *jstr, const char *key, jtype_t type, jtok_t * __restrict toks, int toksnum) 559: { 560: jtok_t *tok = NULL; 561: register int i; 562: int klen; 563: 564: if (!jstr || !key || !toks) 565: return NULL; 566: else 567: klen = strlen(key); 568: 569: for (i = 1; i < toksnum; i++) { 570: if (toks[i].tok_type == J_STRING && toks[i].tok_size == 1 && 571: klen == toks[i].tok_end - toks[i].tok_start && 572: !strncmp(jstr + toks[i].tok_start, key, klen)) { 573: if (type != J_UNDEF) { 574: if (toks[i + 1].tok_type == type) { 575: tok = toks + i + 1; 576: break; 577: } 578: } else { 579: tok = toks + i + 1; 580: break; 581: } 582: } 583: } 584: 585: return tok; 586: } 587: 588: /* 589: * json_findbypos() - Find token by position on JSON string 590: * 591: * @pos = Offset from begin of JSON string 592: * @toks = Parsed tokens 593: * @toksnum = Number of parsed tokens 594: * return: =NULL error or !=NULL token found 595: */ 596: jtok_t * 597: json_findbypos(u_long pos, jtok_t * __restrict toks, int toksnum) 598: { 599: jtok_t *tok = NULL; 600: register int i; 601: 602: if (toks) 603: return NULL; 604: 605: for (i = 1; i < toksnum; i++) 606: if (pos <= toks[i].tok_end && pos >= toks[i].tok_start) { 607: tok = toks + i; 608: break; 609: } 610: 611: return tok; 612: } 613: 614: /* 615: * json_token2array() - Convert token to string array 616: * 617: * @jstr = JSON string 618: * @tok = Token for convert 619: * return: =NULL error or !=NULL allocated array with string variables, 620: * after use should be ait_freeVars() 621: */ 622: array_t * 623: json_token2array(const char *jstr, jtok_t * __restrict tok) 624: { 625: array_t *arr = NULL; 626: register int i, j; 627: int siz; 628: ait_val_t *v; 629: jtok_t *t; 630: 631: if (!jstr || !tok) 632: return NULL; 633: 634: siz = tok->tok_size; 635: if (!siz && json_toktype(tok) != J_ARRAY && json_toktype(tok) != J_OBJECT) 636: siz++; 637: 638: arr = ait_allocVars(siz); 639: if (!arr) 640: return NULL; 641: 642: if (tok->tok_type == J_STRING || tok->tok_type == J_VALUE) { 643: v = ait_getVars(&arr, 0); 644: AIT_SET_STRSIZ(v, json_toklen(tok) + 1); 645: json_tokstrcpy(AIT_GET_STR(v), jstr, tok); 646: } else if (tok->tok_type == J_ARRAY) { 647: for (i = 0, j = 1; i < tok->tok_size; i++) { 648: t = &tok[i + j]; 649: v = ait_getVars(&arr, i); 650: AIT_SET_STRSIZ(v, json_toklen(t) + 1); 651: json_tokstrcpy(AIT_GET_STR(v), jstr, t); 652: 653: /* if there we have array from objects should parse all object tokens */ 654: while (i < tok->tok_size - 1 && tok->tok_idx != tok[i + j + 1].tok_parent) 655: j++; 656: } 657: } else if (tok->tok_type == J_OBJECT) { 658: for (i = 0; tok->tok_idx <= tok[i + 1].tok_parent; i++) { 659: t = &tok[i + 1]; 660: v = ait_getVars(&arr, i); 661: AIT_SET_STRSIZ(v, json_toklen(t) + 1); 662: json_tokstrcpy(AIT_GET_STR(v), jstr, t); 663: } 664: } else { 665: elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]); 666: ait_freeVars(&arr); 667: return NULL; 668: } 669: 670: return arr; 671: } 672: 673: 674: 675: /* 676: * json_add_begin_object() - Adds begin of object { 677: * 678: * @jstr = JSON string 679: * @jlen = JSON string length 680: * @wspace = whitespace include 681: * return: -1 error or !=-1 actual JSON string length 682: */ 683: int 684: json_add_begin_object(char * __restrict jstr, int jlen, int wspace) 685: { 686: int len; 687: size_t eos; 688: 689: if (!jstr) 690: return -1; 691: else 692: eos = strlen(jstr); 693: 694: 695: if (wspace) 696: len = strlcat(jstr, "{ ", jlen); 697: else 698: len = strlcat(jstr, "{", jlen); 699: 700: if (len >= jlen) { 701: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 702: jstr[eos] = 0; 703: return -1; 704: } 705: 706: return len; 707: } 708: 709: /* 710: * json_add_end_object() - Adds end of object } 711: * 712: * @jstr = JSON string 713: * @jlen = JSON string length 714: * @wspace = whitespace include 715: * return: -1 error or !=-1 actual JSON string length 716: */ 717: int 718: json_add_end_object(char * __restrict jstr, int jlen, int wspace) 719: { 720: int len; 721: size_t eos; 722: 723: if (!jstr) 724: return -1; 725: else 726: eos = strlen(jstr); 727: 728: if (wspace) 729: len = strlcat(jstr, " }", jlen); 730: else 731: len = strlcat(jstr, "}", jlen); 732: 733: if (len >= jlen) { 734: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 735: jstr[eos] = 0; 736: return -1; 737: } 738: 739: return len; 740: } 741: 742: /* 743: * json_add_begin_array() - Adds begin of array [ 744: * 745: * @jstr = JSON string 746: * @jlen = JSON string length 747: * @wspace = whitespace include 748: * return: -1 error or !=-1 actual JSON string length 749: */ 750: int 751: json_add_begin_array(char * __restrict jstr, int jlen, int wspace) 752: { 753: int len; 754: size_t eos; 755: 756: if (!jstr) 757: return -1; 758: else 759: eos = strlen(jstr); 760: 761: if (wspace) 762: len = strlcat(jstr, "[ ", jlen); 763: else 764: len = strlcat(jstr, "[", jlen); 765: 766: if (len >= jlen) { 767: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 768: jstr[eos] = 0; 769: return -1; 770: } 771: 772: return len; 773: } 774: 775: /* 776: * json_add_end_array() - Adds end of array ] 777: * 778: * @jstr = JSON string 779: * @jlen = JSON string length 780: * @wspace = whitespace include 781: * return: -1 error or !=-1 actual JSON string length 782: */ 783: int 784: json_add_end_array(char * __restrict jstr, int jlen, int wspace) 785: { 786: int len; 787: size_t eos; 788: 789: if (!jstr) 790: return -1; 791: else 792: eos = strlen(jstr); 793: 794: if (wspace) 795: len = strlcat(jstr, " ]", jlen); 796: else 797: len = strlcat(jstr, "]", jlen); 798: 799: if (len >= jlen) { 800: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 801: jstr[eos] = 0; 802: return -1; 803: } 804: 805: return len; 806: } 807: 808: /* 809: * json_add_char() - Adds character 810: * 811: * @jstr = JSON string 812: * @jlen = JSON string length 813: * @ch = Character 814: * return: -1 error or !=-1 actual JSON string length 815: */ 816: int 817: json_add_char(char * __restrict jstr, int jlen, u_char ch) 818: { 819: int len; 820: 821: if (!jstr) 822: return -1; 823: 824: len = strlen(jstr) + 1; 825: if (len >= jlen) { 826: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 827: return -1; 828: } else { 829: jstr[len++] = (char) ch; 830: jstr[len] = 0; 831: } 832: 833: return len; 834: } 835: 836: /* 837: * json_add_colon() - Adds key/value pair delimiter colon : 838: * 839: * @jstr = JSON string 840: * @jlen = JSON string length 841: * @wspace = whitespace include 842: * return: -1 error or !=-1 actual JSON string length 843: */ 844: int 845: json_add_colon(char * __restrict jstr, int jlen, int wspace) 846: { 847: int len; 848: size_t eos; 849: 850: if (!jstr) 851: return -1; 852: else 853: eos = strlen(jstr); 854: 855: if (wspace) 856: len = strlcat(jstr, ": ", jlen); 857: else 858: len = strlcat(jstr, ":", jlen); 859: 860: if (len >= jlen) { 861: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 862: jstr[eos] = 0; 863: return -1; 864: } 865: 866: return len; 867: } 868: 869: /* 870: * json_add_comma() - Adds value delimiter comma , 871: * 872: * @jstr = JSON string 873: * @jlen = JSON string length 874: * @wspace = whitespace include 875: * return: -1 error or !=-1 actual JSON string length 876: */ 877: int 878: json_add_comma(char * __restrict jstr, int jlen, int wspace) 879: { 880: int len; 881: size_t eos; 882: 883: if (!jstr) 884: return -1; 885: else 886: eos = strlen(jstr); 887: 888: if (wspace) 889: len = strlcat(jstr, ", ", jlen); 890: else 891: len = strlcat(jstr, ",", jlen); 892: 893: if (len >= jlen) { 894: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 895: jstr[eos] = 0; 896: return -1; 897: } 898: 899: return len; 900: } 901: 902: /* 903: * json_add_string() - Adds string 904: * 905: * @jstr = JSON string 906: * @jlen = JSON string length 907: * @unquot = Unquoted string 908: * @str = String, it can't be NULL 909: * return: -1 error or !=-1 actual JSON string length 910: */ 911: int 912: json_add_string(char * __restrict jstr, int jlen, int unquot, const char *str) 913: { 914: int len; 915: size_t eos; 916: 917: if (!jstr || !str) 918: return -1; 919: else 920: eos = strlen(jstr); 921: 922: if (!unquot) { 923: len = strlcat(jstr, "\"", jlen); 924: if (len >= jlen) { 925: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 926: jstr[eos] = 0; 927: return -1; 928: } 929: } 930: len = strlcat(jstr, str, jlen); 931: if (len >= jlen) { 932: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 933: jstr[eos] = 0; 934: return -1; 935: } 936: if (!unquot) { 937: len = strlcat(jstr, "\"", jlen); 938: if (len >= jlen) { 939: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 940: jstr[eos] = 0; 941: return -1; 942: } 943: } 944: 945: return len; 946: } 947: 948: /* 949: * json_add_value() - Adds value 950: * 951: * @jstr = JSON string 952: * @jlen = JSON string length 953: * @unquot = Unquoted number 954: * @num = Number 955: * return: -1 error or !=-1 actual JSON string length 956: */ 957: int 958: json_add_value(char * __restrict jstr, int jlen, int unquot, long num) 959: { 960: int len; 961: char wrk[STRSIZ] = { [0 ... STRSIZ - 1] = 0 }; 962: size_t eos; 963: 964: if (!jstr) 965: return -1; 966: else 967: eos = strlen(jstr); 968: 969: if (!unquot) { 970: len = strlcat(jstr, "\"", jlen); 971: if (len >= jlen) { 972: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 973: jstr[eos] = 0; 974: return -1; 975: } 976: } 977: snprintf(wrk, sizeof wrk, "%ld", num); 978: len = strlcat(jstr, wrk, jlen); 979: if (len >= jlen) { 980: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 981: jstr[eos] = 0; 982: return -1; 983: } 984: if (!unquot) { 985: len = strlcat(jstr, "\"", jlen); 986: if (len >= jlen) { 987: elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); 988: jstr[eos] = 0; 989: return -1; 990: } 991: } 992: 993: return len; 994: } 995: 996: /* 997: * json_add_pair() - Adds key/value pair 998: * 999: * @jstr = JSON string 1000: * @jlen = JSON string length 1001: * @wspace = whitespace include 1002: * @key = Key string 1003: * @val = Value string 1004: * return: -1 error or !=-1 actual JSON string length 1005: */ 1006: int 1007: json_add_pair(char * __restrict jstr, int jlen, int wspace, const char *key, const char *val) 1008: { 1009: int len = -1; 1010: size_t eos; 1011: 1012: if (!jstr || !key || !val) 1013: return -1; 1014: else 1015: eos = strlen(jstr); 1016: 1017: if (json_add_string(jstr, jlen, 0, key) == -1) { 1018: jstr[eos] = 0; 1019: return -1; 1020: } 1021: if (json_add_colon(jstr, jlen, wspace) == -1) { 1022: jstr[eos] = 0; 1023: return -1; 1024: } 1025: if ((len = json_add_string(jstr, jlen, 0, val)) == -1) { 1026: jstr[eos] = 0; 1027: return -1; 1028: } 1029: 1030: return len; 1031: } 1032: 1033: /* 1034: * json_add_pair2() - Adds key/value pair with formated args 1035: * 1036: * @jstr = JSON string 1037: * @jlen = JSON string length 1038: * @wspace = whitespace include 1039: * @key = Key string 1040: * @fmt = Format string for values 1041: * return: -1 error or !=-1 actual JSON string length 1042: */ 1043: int 1044: json_add_pair2(char * __restrict jstr, int jlen, int wspace, const char *key, const char *fmt, ...) 1045: { 1046: int len = -1; 1047: size_t eos; 1048: va_list lst; 1049: char szStr[BUFSIZ] = { [0 ... BUFSIZ - 1] = 0 }; 1050: 1051: if (!jstr || !key || !fmt) 1052: return -1; 1053: else 1054: eos = strlen(jstr); 1055: 1056: if (json_add_string(jstr, jlen, 0, key) == -1) { 1057: jstr[eos] = 0; 1058: return -1; 1059: } 1060: if (json_add_colon(jstr, jlen, wspace) == -1) { 1061: jstr[eos] = 0; 1062: return -1; 1063: } 1064: va_start(lst, fmt); 1065: vsnprintf(szStr, sizeof szStr, fmt, lst); 1066: va_end(lst); 1067: if ((len = json_add_string(jstr, jlen, 0, szStr)) == -1) { 1068: jstr[eos] = 0; 1069: return -1; 1070: } 1071: 1072: return len; 1073: } 1074: 1075: /* 1076: * json_add_array() - Adds array 1077: * 1078: * @jstr = JSON string 1079: * @jlen = JSON string length 1080: * @wspace = whitespace include 1081: * @arr = Array with variables 1082: * return: -1 error or !=-1 actual JSON string length 1083: */ 1084: int 1085: json_add_array(char * __restrict jstr, int jlen, int wspace, array_t * __restrict arr) 1086: { 1087: int len = -1; 1088: register int i; 1089: ait_val_t *v; 1090: size_t eos; 1091: 1092: if (!jstr || !arr) 1093: return -1; 1094: else 1095: eos = strlen(jstr); 1096: 1097: if (json_add_begin_array(jstr, jlen, wspace) == -1) { 1098: jstr[eos] = 0; 1099: return -1; 1100: } 1101: for (i = 0; i < array_Size(arr); i++) { 1102: v = array(arr, i, ait_val_t*); 1103: if (v) { 1104: if (AIT_TYPE(v) == string) { 1105: if (json_add_string(jstr, jlen, 0, AIT_GET_STR(v)) == -1) { 1106: jstr[eos] = 0; 1107: return -1; 1108: } 1109: } else { 1110: if (json_add_value(jstr, jlen, 0, AIT_GET_LIKE(v, long)) == -1) { 1111: jstr[eos] = 0; 1112: return -1; 1113: } 1114: } 1115: if (i < array_Size(arr) - 1 && json_add_comma(jstr, jlen, wspace) == -1) { 1116: jstr[eos] = 0; 1117: return -1; 1118: } 1119: } 1120: } 1121: if ((len = json_add_end_array(jstr, jlen, wspace)) == -1) { 1122: jstr[eos] = 0; 1123: return -1; 1124: } 1125: 1126: return len; 1127: } 1128: 1129: /* 1130: * json_dump_yaml() - Dump parsed JSON string to YAML format 1131: * 1132: * @f = Output handler 1133: * @jstr = JSON string 1134: * @toks = JSON tokens 1135: * @toksnum = Number of tokens 1136: * @indent = Start indent spaces 1137: * return: 0 done and 1 added one more item 1138: */ 1139: int 1140: json_dump_yaml(FILE *f, const char *jstr, jtok_t *toks, int toksnum, int indent) 1141: { 1142: register int i, j, k; 1143: 1144: if (!toksnum) 1145: return 0; 1146: 1147: if (toks->tok_type == J_VALUE) { 1148: fprintf(f, "%.*s", (int) json_toklen(toks), json_tokstr(jstr, toks)); 1149: return 1; 1150: } else if (toks->tok_type == J_STRING) { 1151: fprintf(f, "%.*s", (int) json_toklen(toks), json_tokstr(jstr, toks)); 1152: return 1; 1153: } else if (toks->tok_type == J_OBJECT) { 1154: fprintf(f, "\n"); 1155: for (j = i = 0; i < json_toksize(toks); i++) { 1156: for (k = 0; k < indent; k++) 1157: fprintf(f, " "); 1158: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1159: fprintf(f, ": "); 1160: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1161: fprintf(f, "\n"); 1162: } 1163: return j + 1; 1164: } else if (toks->tok_type == J_ARRAY) { 1165: fprintf(f, "\n"); 1166: for (j = i = 0; i < json_toksize(toks); i++) { 1167: for (k = 0; k < indent - 1; k++) 1168: fprintf(f, " "); 1169: fprintf(f, " - "); 1170: j += json_dump_yaml(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1171: fprintf(f, "\n"); 1172: } 1173: return j + 1; 1174: } 1175: 1176: return 0; 1177: } 1178: 1179: /* 1180: * json_dump() - Dump parsed JSON string to structure format 1181: * 1182: * @f = Output handler 1183: * @jstr = JSON string 1184: * @toks = JSON tokens 1185: * @toksnum = Number of tokens 1186: * @indent = Start indent spaces 1187: * return: 0 done and 1 added one more item 1188: */ 1189: int 1190: json_dump(FILE *f, const char *jstr, jtok_t *toks, int toksnum, int indent) 1191: { 1192: register int i, j, k; 1193: 1194: if (!toksnum) 1195: return 0; 1196: 1197: if (toks->tok_type == J_VALUE) { 1198: fprintf(f, "[idx=%ld type=VALUE start=%ld end=%ld size=%ld parent=%ld] = %.*s", 1199: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent, 1200: (int) json_toklen(toks), json_tokstr(jstr, toks)); 1201: return 1; 1202: } else if (toks->tok_type == J_STRING) { 1203: fprintf(f, "[idx=%ld type=STRING start=%ld end=%ld size=%ld parent=%ld] = %.*s", 1204: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent, 1205: (int) json_toklen(toks), json_tokstr(jstr, toks)); 1206: return 1; 1207: } else if (toks->tok_type == J_OBJECT) { 1208: fprintf(f, "\n"); 1209: fprintf(f, "object:: [idx=%ld type=OBJECT start=%ld end=%ld size=%ld parent=%ld]\n", 1210: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent); 1211: for (j = i = 0; i < json_toksize(toks); i++) { 1212: for (k = 0; k < indent; k++) 1213: fprintf(f, " "); 1214: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1215: fprintf(f, ": "); 1216: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1217: fprintf(f, "\n"); 1218: } 1219: return j + 1; 1220: } else if (toks->tok_type == J_ARRAY) { 1221: fprintf(f, "\n"); 1222: fprintf(f, "array[] [idx=%ld type=ARRAY start=%ld end=%ld size=%ld parent=%ld]", 1223: toks->tok_idx, toks->tok_start, toks->tok_end, toks->tok_size, toks->tok_parent); 1224: for (j = i = 0; i < json_toksize(toks); i++) { 1225: for (k = 0; k < indent - 1; k++) 1226: fprintf(f, " "); 1227: j += json_dump(f, jstr, toks + j + 1, toksnum - j, indent + 1); 1228: fprintf(f, "\n"); 1229: } 1230: return j + 1; 1231: } 1232: 1233: return 0; 1234: }