diff options
Diffstat (limited to 'thirdparty/libwebsockets/lib/misc/lejp.c')
-rw-r--r-- | thirdparty/libwebsockets/lib/misc/lejp.c | 753 |
1 files changed, 0 insertions, 753 deletions
diff --git a/thirdparty/libwebsockets/lib/misc/lejp.c b/thirdparty/libwebsockets/lib/misc/lejp.c deleted file mode 100644 index 599a6d37ba..0000000000 --- a/thirdparty/libwebsockets/lib/misc/lejp.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Lightweight Embedded JSON Parser - * - * Copyright (C) 2013-2017 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include <libwebsockets.h> -#include "core/private.h" -#include <string.h> -#include <stdio.h> - -/** - * lejp_construct - prepare a struct lejp_ctx for use - * - * \param ctx: pointer to your struct lejp_ctx - * \param callback: your user callback which will received parsed tokens - * \param user: optional user data pointer untouched by lejp - * \param paths: your array of name elements you are interested in - * \param count_paths: LWS_ARRAY_SIZE() of @paths - * - * Prepares your context struct for use with lejp - */ - -void -lejp_construct(struct lejp_ctx *ctx, - signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user, - const char * const *paths, unsigned char count_paths) -{ - ctx->st[0].s = 0; - ctx->st[0].p = 0; - ctx->st[0].i = 0; - ctx->st[0].b = 0; - ctx->sp = 0; - ctx->ipos = 0; - ctx->ppos = 0; - ctx->path_match = 0; - ctx->path[0] = '\0'; - ctx->callback = callback; - ctx->user = user; - ctx->paths = paths; - ctx->count_paths = count_paths; - ctx->line = 1; - ctx->callback(ctx, LEJPCB_CONSTRUCTED); -} - -/** - * lejp_destruct - retire a previously constructed struct lejp_ctx - * - * \param ctx: pointer to your struct lejp_ctx - * - * lejp does not perform any allocations, but since your user code might, this - * provides a one-time LEJPCB_DESTRUCTED callback at destruction time where - * you can clean up in your callback. - */ - -void -lejp_destruct(struct lejp_ctx *ctx) -{ - /* no allocations... just let callback know what it happening */ - ctx->callback(ctx, LEJPCB_DESTRUCTED); -} - -/** - * lejp_change_callback - switch to a different callback from now on - * - * \param ctx: pointer to your struct lejp_ctx - * \param callback: your user callback which will received parsed tokens - * - * This tells the old callback it was destroyed, in case you want to take any - * action because that callback "lost focus", then changes to the new - * callback and tells it first that it was constructed, and then started. - * - * Changing callback is a cheap and powerful trick to split out handlers - * according to information earlier in the parse. For example you may have - * a JSON pair "schema" whose value defines what can be expected for the rest - * of the JSON. Rather than having one huge callback for all cases, you can - * have an initial one looking for "schema" which then calls - * lejp_change_callback() to a handler specific for the schema. - * - * Notice that afterwards, you need to construct the context again anyway to - * parse another JSON object, and the callback is reset then to the main, - * schema-interpreting one. The construction action is very lightweight. - */ - -void -lejp_change_callback(struct lejp_ctx *ctx, - signed char (*callback)(struct lejp_ctx *ctx, char reason)) -{ - ctx->callback(ctx, LEJPCB_DESTRUCTED); - ctx->callback = callback; - ctx->callback(ctx, LEJPCB_CONSTRUCTED); - ctx->callback(ctx, LEJPCB_START); -} - -static void -lejp_check_path_match(struct lejp_ctx *ctx) -{ - const char *p, *q; - int n; - - /* we only need to check if a match is not active */ - for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) { - ctx->wildcount = 0; - p = ctx->path; - q = ctx->paths[n]; - while (*p && *q) { - if (*q != '*') { - if (*p != *q) - break; - p++; - q++; - continue; - } - ctx->wild[ctx->wildcount++] = lws_ptr_diff(p, ctx->path); - q++; - /* - * if * has something after it, match to . - * if ends with *, eat everything. - * This implies match sequences must be ordered like - * x.*.* - * x.* - * if both options are possible - */ - while (*p && (*p != '.' || !*q)) - p++; - } - if (*p || *q) - continue; - - ctx->path_match = n + 1; - ctx->path_match_len = ctx->ppos; - return; - } - - if (!ctx->path_match) - ctx->wildcount = 0; -} - -int -lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len) -{ - int n; - - if (wildcard >= ctx->wildcount || !len) - return 0; - - n = ctx->wild[wildcard]; - - while (--len && n < ctx->ppos && - (n == ctx->wild[wildcard] || ctx->path[n] != '.')) - *dest++ = ctx->path[n++]; - - *dest = '\0'; - n++; - - return n - ctx->wild[wildcard]; -} - -/** - * lejp_parse - interpret some more incoming data incrementally - * - * \param ctx: previously constructed parsing context - * \param json: char buffer with the new data to interpret - * \param len: amount of data in the buffer - * - * Because lejp is a stream parser, it incrementally parses as new data - * becomes available, maintaining all state in the context struct. So an - * incomplete JSON is a normal situation, getting you a LEJP_CONTINUE - * return, signalling there's no error but to call again with more data when - * it comes to complete the parsing. Successful parsing completes with a - * 0 or positive integer indicating how much of the last input buffer was - * unused. - */ - -int -lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) -{ - unsigned char c, n, s, ret = LEJP_REJECT_UNKNOWN; - static const char esc_char[] = "\"\\/bfnrt"; - static const char esc_tran[] = "\"\\/\b\f\n\r\t"; - static const char tokens[] = "rue alse ull "; - - if (!ctx->sp && !ctx->ppos) - ctx->callback(ctx, LEJPCB_START); - - while (len--) { - c = *json++; - s = ctx->st[ctx->sp].s; - - /* skip whitespace unless we should care */ - if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') { - if (c == '\n') { - ctx->line++; - ctx->st[ctx->sp].s &= ~LEJP_FLAG_WS_COMMENTLINE; - } - if (!(s & LEJP_FLAG_WS_KEEP)) { - if (c == '#') - ctx->st[ctx->sp].s |= - LEJP_FLAG_WS_COMMENTLINE; - continue; - } - } - - if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE) - continue; - - switch (s) { - case LEJP_IDLE: - if (c != '{') { - ret = LEJP_REJECT_IDLE_NO_BRACE; - goto reject; - } - if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - ctx->st[ctx->sp].s = LEJP_MEMBERS; - break; - case LEJP_MEMBERS: - if (c == '}') { - ctx->st[ctx->sp].s = LEJP_IDLE; - ret = LEJP_REJECT_MEMBERS_NO_CLOSE; - goto reject; - } - ctx->st[ctx->sp].s = LEJP_M_P; - goto redo_character; - case LEJP_M_P: - if (c != '\"') { - ret = LEJP_REJECT_MP_NO_OPEN_QUOTE; - goto reject; - } - /* push */ - ctx->st[ctx->sp].s = LEJP_MP_DELIM; - c = LEJP_MP_STRING; - goto add_stack_level; - - case LEJP_MP_STRING: - if (c == '\"') { - if (!ctx->sp) { /* JSON can't end on quote */ - ret = LEJP_REJECT_MP_STRING_UNDERRUN; - goto reject; - } - if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { - ctx->buf[ctx->npos] = '\0'; - if (ctx->callback(ctx, - LEJPCB_VAL_STR_END) < 0) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - } - /* pop */ - ctx->sp--; - break; - } - if (c == '\\') { - ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC; - break; - } - if (c < ' ') {/* "control characters" not allowed */ - ret = LEJP_REJECT_MP_ILLEGAL_CTRL; - goto reject; - } - goto emit_string_char; - - case LEJP_MP_STRING_ESC: - if (c == 'u') { - ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1; - ctx->uni = 0; - break; - } - for (n = 0; n < sizeof(esc_char); n++) { - if (c != esc_char[n]) - continue; - /* found it */ - c = esc_tran[n]; - ctx->st[ctx->sp].s = LEJP_MP_STRING; - goto emit_string_char; - } - ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC; - /* illegal escape char */ - goto reject; - - case LEJP_MP_STRING_ESC_U1: - case LEJP_MP_STRING_ESC_U2: - case LEJP_MP_STRING_ESC_U3: - case LEJP_MP_STRING_ESC_U4: - ctx->uni <<= 4; - if (c >= '0' && c <= '9') - ctx->uni |= c - '0'; - else - if (c >= 'a' && c <= 'f') - ctx->uni = c - 'a' + 10; - else - if (c >= 'A' && c <= 'F') - ctx->uni = c - 'A' + 10; - else { - ret = LEJP_REJECT_ILLEGAL_HEX; - goto reject; - } - ctx->st[ctx->sp].s++; - switch (s) { - case LEJP_MP_STRING_ESC_U2: - if (ctx->uni < 0x08) - break; - /* - * 0x08-0xff (0x0800 - 0xffff) - * emit 3-byte UTF-8 - */ - c = 0xe0 | ((ctx->uni >> 4) & 0xf); - goto emit_string_char; - - case LEJP_MP_STRING_ESC_U3: - if (ctx->uni >= 0x080) { - /* - * 0x080 - 0xfff (0x0800 - 0xffff) - * middle 3-byte seq - * send ....XXXXXX.. - */ - c = 0x80 | ((ctx->uni >> 2) & 0x3f); - goto emit_string_char; - } - if (ctx->uni < 0x008) - break; - /* - * 0x008 - 0x7f (0x0080 - 0x07ff) - * start 2-byte seq - */ - c = 0xc0 | (ctx->uni >> 2); - goto emit_string_char; - - case LEJP_MP_STRING_ESC_U4: - if (ctx->uni >= 0x0080) - /* end of 2 or 3-byte seq */ - c = 0x80 | (ctx->uni & 0x3f); - else - /* literal */ - c = (unsigned char)ctx->uni; - - ctx->st[ctx->sp].s = LEJP_MP_STRING; - goto emit_string_char; - default: - break; - } - break; - - case LEJP_MP_DELIM: - if (c != ':') { - ret = LEJP_REJECT_MP_DELIM_MISSING_COLON; - goto reject; - } - ctx->st[ctx->sp].s = LEJP_MP_VALUE; - ctx->path[ctx->ppos] = '\0'; - - lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - break; - - case LEJP_MP_VALUE: - if (c >= '0' && c <= '9') { - ctx->npos = 0; - ctx->dcount = 0; - ctx->f = 0; - ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; - goto redo_character; - } - switch (c) { - case'\"': - /* push */ - ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - c = LEJP_MP_STRING; - ctx->npos = 0; - ctx->buf[0] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - goto add_stack_level; - - case '{': - /* push */ - ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - c = LEJP_MEMBERS; - lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - ctx->path_match = 0; - goto add_stack_level; - - case '[': - /* push */ - ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; - c = LEJP_MP_VALUE; - ctx->path[ctx->ppos++] = '['; - ctx->path[ctx->ppos++] = ']'; - ctx->path[ctx->ppos] = '\0'; - if (ctx->callback(ctx, LEJPCB_ARRAY_START)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - ctx->i[ctx->ipos++] = 0; - if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { - ret = LEJP_REJECT_MP_DELIM_ISTACK; - goto reject; - } - goto add_stack_level; - - case ']': - /* pop */ - if (!ctx->sp) { /* JSON can't end on ] */ - ret = LEJP_REJECT_MP_C_OR_E_UNDERF; - goto reject; - } - ctx->sp--; - if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { - ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; - goto reject; - } - /* drop the path [n] bit */ - if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; - } - ctx->path[ctx->ppos] = '\0'; - if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) - /* - * we shrank the path to be - * smaller than the matching point - */ - ctx->path_match = 0; - goto array_end; - - case 't': /* true */ - ctx->uni = 0; - ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; - break; - - case 'f': - ctx->uni = 4; - ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; - break; - - case 'n': - ctx->uni = 4 + 5; - ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; - break; - default: - ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START; - goto reject; - } - break; - - case LEJP_MP_VALUE_NUM_INT: - if (!ctx->npos && c == '-') { - ctx->f |= LEJP_SEEN_MINUS; - goto append_npos; - } - - if (ctx->dcount < 10 && c >= '0' && c <= '9') { - if (ctx->f & LEJP_SEEN_POINT) - ctx->f |= LEJP_SEEN_POST_POINT; - ctx->dcount++; - goto append_npos; - } - if (c == '.') { - if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { - ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; - goto reject; - } - ctx->f |= LEJP_SEEN_POINT; - goto append_npos; - } - /* - * before exponent, if we had . we must have had at - * least one more digit - */ - if ((ctx->f & - (LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) == - LEJP_SEEN_POINT) { - ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC; - goto reject; - } - if (c == 'e' || c == 'E') { - if (ctx->f & LEJP_SEEN_EXP) { - ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; - goto reject; - } - ctx->f |= LEJP_SEEN_EXP; - ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP; - goto append_npos; - } - /* if none of the above, did we even have a number? */ - if (!ctx->dcount) { - ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; - goto reject; - } - - ctx->buf[ctx->npos] = '\0'; - if (ctx->f & LEJP_SEEN_POINT) { - if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - } else { - if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - } - - /* then this is the post-number character, loop */ - ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - goto redo_character; - - case LEJP_MP_VALUE_NUM_EXP: - ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; - if (c >= '0' && c <= '9') - goto redo_character; - if (c == '+' || c == '-') - goto append_npos; - ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP; - goto reject; - - case LEJP_MP_VALUE_TOK: /* true, false, null */ - if (c != tokens[ctx->uni]) { - ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN; - goto reject; - } - ctx->uni++; - if (tokens[ctx->uni] != ' ') - break; - switch (ctx->uni) { - case 3: - ctx->buf[0] = '1'; - ctx->buf[1] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - break; - case 8: - ctx->buf[0] = '0'; - ctx->buf[1] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - break; - case 12: - ctx->buf[0] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_NULL)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - break; - } - ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - break; - - case LEJP_MP_COMMA_OR_END: - ctx->path[ctx->ppos] = '\0'; - if (c == ',') { - /* increment this stack level's index */ - ctx->st[ctx->sp].s = LEJP_M_P; - if (!ctx->sp) { - ctx->ppos = 0; - /* - * since we came back to root level, - * no path can still match - */ - ctx->path_match = 0; - break; - } - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->ppos] = '\0'; - if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) - /* - * we shrank the path to be - * smaller than the matching point - */ - ctx->path_match = 0; - - if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END) - break; - /* top level is definitely an array... */ - if (ctx->ipos) - ctx->i[ctx->ipos - 1]++; - ctx->st[ctx->sp].s = LEJP_MP_VALUE; - break; - } - if (c == ']') { - if (!ctx->sp) { /* JSON can't end on ] */ - ret = LEJP_REJECT_MP_C_OR_E_UNDERF; - goto reject; - } - /* pop */ - ctx->sp--; - if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { - ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; - goto reject; - } - /* drop the path [n] bit */ - if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; - } - ctx->path[ctx->ppos] = '\0'; - if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) - /* - * we shrank the path to be - * smaller than the matching point - */ - ctx->path_match = 0; - - /* do LEJP_MP_ARRAY_END processing */ - goto redo_character; - } - if (c == '}') { - if (!ctx->sp) { - lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - ctx->callback(ctx, LEJPCB_COMPLETE); - /* done, return unused amount */ - return len; - } - /* pop */ - ctx->sp--; - if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; - } - ctx->path[ctx->ppos] = '\0'; - if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) - /* - * we shrank the path to be - * smaller than the matching point - */ - ctx->path_match = 0; - lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - break; - } - - ret = LEJP_REJECT_MP_C_OR_E_NEITHER; - goto reject; - - case LEJP_MP_ARRAY_END: -array_end: - ctx->path[ctx->ppos] = '\0'; - if (c == ',') { - /* increment this stack level's index */ - if (ctx->ipos) - ctx->i[ctx->ipos - 1]++; - ctx->st[ctx->sp].s = LEJP_MP_VALUE; - if (ctx->sp) - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->ppos] = '\0'; - break; - } - if (c != ']') { - ret = LEJP_REJECT_MP_ARRAY_END_MISSING; - goto reject; - } - - ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - ctx->callback(ctx, LEJPCB_ARRAY_END); - break; - } - - continue; - -emit_string_char: - if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { - /* assemble the string value into chunks */ - ctx->buf[ctx->npos++] = c; - if (ctx->npos == sizeof(ctx->buf) - 1) { - if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) { - ret = LEJP_REJECT_CALLBACK; - goto reject; - } - ctx->npos = 0; - } - continue; - } - /* name part of name:value pair */ - ctx->path[ctx->ppos++] = c; - continue; - -add_stack_level: - /* push on to the object stack */ - if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && - ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) - ctx->path[ctx->ppos++] = '.'; - - ctx->st[ctx->sp].p = ctx->ppos; - ctx->st[ctx->sp].i = ctx->ipos; - if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) { - ret = LEJP_REJECT_STACK_OVERFLOW; - goto reject; - } - ctx->path[ctx->ppos] = '\0'; - ctx->st[ctx->sp].s = c; - ctx->st[ctx->sp].b = 0; - continue; - -append_npos: - if (ctx->npos >= sizeof(ctx->buf)) { - ret = LEJP_REJECT_NUM_TOO_LONG; - goto reject; - } - ctx->buf[ctx->npos++] = c; - continue; - -redo_character: - json--; - len++; - } - - return LEJP_CONTINUE; - -reject: - ctx->callback(ctx, LEJPCB_FAILED); - return ret; -} |