|
|
| version 1.1.2.1, 2017/11/24 09:53:53 | version 1.1.2.3, 2017/11/27 19:37:22 |
|---|---|
| Line 46 SUCH DAMAGE. | Line 46 SUCH DAMAGE. |
| #include "global.h" | #include "global.h" |
| /* JSON error strings */ | |
| const char *jerrstr[] = { | |
| "No error", | |
| "Not enough tokens were provided", | |
| "Invalid character", | |
| "JSON string isn't full", | |
| "Invalid parameter", | |
| NULL | |
| }; | |
| /* | |
| * json_init() - Initialize JSON handler | |
| * | |
| * @json = JSON handler, if there is NULL then dynamically will be allocated | |
| * @jstrict = JSON strict mode, when we select strict mode every unquoted value is error | |
| * return: =NULL error or !=NULL ready for use JSON handler and should be free with json_free() | |
| */ | |
| json_t * | |
| json_init(json_t * __restrict json, int jstrict) | |
| { | |
| json_t *j = json; | |
| if (!j) { | |
| j = e_malloc(sizeof(json_t)); | |
| if (!j) { | |
| LOGERR; | |
| return NULL; | |
| } | |
| } | |
| memset(j, 0, sizeof(json_t)); | |
| j->h_parent = -1; | |
| j->h_strict = jstrict; | |
| /* handler is dynamically allocated! */ | |
| if (!json) | |
| j->h_alloc = j; | |
| return j; | |
| } | |
| /* | |
| * json_free() - Free JSON handler | |
| * | |
| * @json = JSON handler | |
| * return: none | |
| */ | |
| void | |
| json_free(json_t * __restrict json) | |
| { | |
| if (json) { | |
| if (json->h_alloc) | |
| e_free(json); | |
| else | |
| memset(json, 0, sizeof(json_t)); | |
| } | |
| } | |
| static jtok_t * | |
| json_gettoken(json_t * __restrict json, jtok_t * __restrict jtoks, u_int toksnum) | |
| { | |
| jtok_t *tok; | |
| assert(json || !(!jtoks && toksnum)); | |
| if (json->h_next >= toksnum) { | |
| elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); | |
| return NULL; | |
| } else | |
| tok = &jtoks[json->h_next++]; | |
| tok->tok_start = tok->tok_end = tok->tok_parent = -1; | |
| tok->tok_size = 0; | |
| return tok; | |
| } | |
| inline void | |
| json_filltoken(jtok_t * __restrict tok, jtype_t type, long start, long end, long parent) | |
| { | |
| assert(tok); | |
| tok->tok_type = type; | |
| tok->tok_start = start; | |
| tok->tok_end = end; | |
| tok->tok_parent = parent; | |
| tok->tok_size = 0; | |
| } | |
| static int | |
| json_parse_string(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum) | |
| { | |
| jtok_t *tok; | |
| u_long pos; | |
| char ch; | |
| register int i; | |
| assert(json || jstr || !(!jtoks && toksnum)); | |
| for (pos = json->h_pos++; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) { | |
| ch = jstr[json->h_pos]; | |
| if (ch == '\"') { | |
| if (!jtoks) | |
| return 0; | |
| if (!(tok = json_gettoken(json, jtoks, toksnum))) { | |
| json->h_pos = pos; | |
| return -1; | |
| } else | |
| json_filltoken(tok, J_STRING, pos + 1, json->h_pos, json->h_parent); | |
| return 0; | |
| } | |
| if (ch == '\\' && json->h_pos + 1 < jlen) { | |
| switch (jstr[++json->h_pos]) { | |
| case '\"': | |
| case '/': | |
| case '\\': | |
| case 'b': | |
| case 'f': | |
| case 'r': | |
| case 'n': | |
| case 't': | |
| /* Allowed escaped symbols */ | |
| break; | |
| case 'u': | |
| /* Allows escaped symbol \uXXXX */ | |
| json->h_pos++; | |
| for (i = 0; i < 4 && json->h_pos < jlen && jstr[json->h_pos]; i++, json->h_pos++) { | |
| /* If it isn't a hex character we have an error */ | |
| if (!((jstr[json->h_pos] >= 48 && jstr[json->h_pos] <= 57) || /* 0-9 */ | |
| (jstr[json->h_pos] >= 65 && jstr[json->h_pos] <= 70) || /* A-F */ | |
| (jstr[json->h_pos] >= 97 && jstr[json->h_pos] <= 102))) { /* a-f */ | |
| json->h_pos = pos; | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return -1; | |
| } | |
| } | |
| json->h_pos--; | |
| break; | |
| default: | |
| /* Unexpected symbol */ | |
| json->h_pos = pos; | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return -1; | |
| } | |
| } | |
| } | |
| json->h_pos = pos; | |
| elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]); | |
| return -1; | |
| } | |
| static int | |
| json_parse_value(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum) | |
| { | |
| jtok_t *tok; | |
| u_long pos; | |
| assert(json || jstr || !(!jtoks && toksnum)); | |
| for (pos = json->h_pos; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) { | |
| switch (jstr[json->h_pos]) { | |
| case ':': | |
| if (json->h_strict) | |
| goto found; | |
| break; | |
| case '\t': | |
| case '\r': | |
| case '\n': | |
| case ' ': | |
| case ',': | |
| case ']': | |
| case '}': | |
| goto found; | |
| } | |
| if (jstr[json->h_pos] < 32 || jstr[json->h_pos] > 127) { | |
| json->h_pos = pos; | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return -1; | |
| } | |
| } | |
| if (json->h_strict) { | |
| json->h_pos = pos; | |
| elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]); | |
| return -1; | |
| } | |
| found: | |
| if (jtoks) { | |
| if (!(tok = json_gettoken(json, jtoks, toksnum))) { | |
| json->h_pos = pos; | |
| return -1; | |
| } else | |
| json_filltoken(tok, J_VALUE, pos, json->h_pos, json->h_parent); | |
| } | |
| json->h_pos--; | |
| return 0; | |
| } | |
| /* | |
| * json_parse() - Parse JSON string | |
| * | |
| * @json = JSON handler | |
| * @jstr = JSON string | |
| * @jlen = JSON string length | |
| * @jtoks = Token array | |
| * @toksnum = Token array size, return number of allocated tokens in array | |
| * return: -1 error or number of found tokens | |
| */ | |
| u_int | |
| json_parse(json_t * __restrict json, const char *jstr, size_t jlen, jtok_t * __restrict jtoks, u_int toksnum) | |
| { | |
| register int i; | |
| register u_int cx; | |
| jtype_t type; | |
| jtok_t *tok; | |
| char ch; | |
| if (!json || !jstr || (!jtoks && toksnum)) { | |
| elwix_SetErr(J_ERR_PARAM, "%s", jerrstr[J_ERR_PARAM]); | |
| return (u_int) -1; | |
| } | |
| for (cx = json->h_next; json->h_pos < jlen && jstr[json->h_pos]; json->h_pos++) { | |
| switch ((ch = jstr[json->h_pos])) { | |
| case '{': | |
| case '[': | |
| cx++; /* start new token */ | |
| if (!jtoks) | |
| break; | |
| tok = json_gettoken(json, jtoks, toksnum); | |
| if (!tok) | |
| return (u_int) -1; | |
| if (json->h_parent != -1) { | |
| jtoks[json->h_parent].tok_size++; | |
| tok->tok_parent = json->h_parent; | |
| } | |
| tok->tok_type = (ch == '{' ? J_OBJECT : J_ARRAY); | |
| tok->tok_start = json->h_pos; | |
| json->h_parent = json->h_next - 1; | |
| break; | |
| case '}': | |
| case ']': | |
| if (!jtoks) | |
| break; | |
| if (json->h_next < 1) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| type = (ch == '}' ? J_OBJECT : J_ARRAY); | |
| tok = &jtoks[json->h_next - 1]; | |
| while (42) { | |
| if (tok->tok_start != -1 && tok->tok_end == -1) { | |
| if (tok->tok_type != type) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| tok->tok_end = json->h_pos + 1; | |
| json->h_parent = tok->tok_parent; | |
| break; | |
| } | |
| if (tok->tok_parent == -1) { | |
| if (tok->tok_type != type || json->h_parent == -1) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| break; | |
| } | |
| tok = &jtoks[tok->tok_parent]; | |
| } | |
| break; | |
| case '\"': | |
| if (json_parse_string(json, jstr, jlen, jtoks, toksnum) == -1) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| cx++; | |
| if (jtoks && json->h_parent != -1) | |
| jtoks[json->h_parent].tok_size++; | |
| break; | |
| case '\t': | |
| case '\r': | |
| case '\n': | |
| case ' ': | |
| /* whitespace, skip */ | |
| break; | |
| case ':': | |
| json->h_parent = json->h_next - 1; | |
| break; | |
| case ',': | |
| if (jtoks && json->h_parent != -1 && | |
| jtoks[json->h_parent].tok_type != J_OBJECT && | |
| jtoks[json->h_parent].tok_type != J_ARRAY) | |
| json->h_parent = jtoks[json->h_parent].tok_parent; | |
| break; | |
| case '-': | |
| case '0': | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| case 't': | |
| case 'f': | |
| case 'n': | |
| if (json->h_strict) { | |
| if (jtoks && json->h_parent != -1) { | |
| /* they must not be keys of the object */ | |
| if (jtoks[json->h_parent].tok_type == J_OBJECT || | |
| (jtoks[json->h_parent].tok_type == J_STRING && | |
| jtoks[json->h_parent].tok_size)) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| } | |
| if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1) | |
| return (u_int) -1; | |
| cx++; | |
| if (jtoks && json->h_parent != -1) | |
| jtoks[json->h_parent].tok_size++; | |
| break; | |
| } | |
| default: | |
| if (json->h_strict) { | |
| elwix_SetErr(J_ERR_INVAL, "%s", jerrstr[J_ERR_INVAL]); | |
| return (u_int) -1; | |
| } | |
| if (json_parse_value(json, jstr, jlen, jtoks, toksnum) == -1) | |
| return (u_int) -1; | |
| cx++; | |
| if (jtoks && json->h_parent != -1) | |
| jtoks[json->h_parent].tok_size++; | |
| break; | |
| } | |
| } | |
| if (jtoks) { | |
| for (i = json->h_next - 1; i >= 0; i--) { | |
| /* unmatched opened object or array */ | |
| if (jtoks[i].tok_start != -1 && jtoks[i].tok_end == -1) { | |
| elwix_SetErr(J_ERR_PART, "%s", jerrstr[J_ERR_PART]); | |
| return (u_int) -1; | |
| } | |
| } | |
| } | |
| return cx; | |
| } |