--- libelwix/src/json.c 2017/11/24 09:53:53 1.1.2.1 +++ libelwix/src/json.c 2017/11/24 15:52:57 1.1.2.2 @@ -3,7 +3,7 @@ * by Michael Pounov * * $Author: misho $ -* $Id: json.c,v 1.1.2.1 2017/11/24 09:53:53 misho Exp $ +* $Id: json.c,v 1.1.2.2 2017/11/24 15:52:57 misho Exp $ * ************************************************************************** The ELWIX and AITNET software is distributed under the following @@ -46,3 +46,223 @@ SUCH DAMAGE. #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)); + } +} + +/* + * 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) { + elwix_SetErr(J_ERR_NOMEM, "%s", jerrstr[J_ERR_NOMEM]); + 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) + break; + + 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; +}