mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 01:39:33 +00:00
896 lines
20 KiB
C
896 lines
20 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <libwebsockets.h>
|
|
#include <private-lib-core.h>
|
|
|
|
#include <assert.h>
|
|
|
|
signed char
|
|
lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason)
|
|
{
|
|
lws_struct_args_t *a = (lws_struct_args_t *)ctx->user;
|
|
const lws_struct_map_t *map = a->map_st[ctx->pst_sp];
|
|
size_t n = a->map_entries_st[ctx->pst_sp], imp = 0;
|
|
lejp_callback cb = map->lejp_cb;
|
|
|
|
if (reason == LEJPCB_PAIR_NAME && strcmp(ctx->path, "schema")) {
|
|
/*
|
|
* If not "schema", the schema is implicit rather than
|
|
* explicitly given, ie, he just goes ahead and starts using
|
|
* member names that imply a particular type. For example, he
|
|
* may have an implicit type normally, and a different one for
|
|
* exceptions that just starts using "error-message" or whatever
|
|
* and we can understand that's the exception type now.
|
|
*
|
|
* Let's look into each of the maps in the top level array
|
|
* and match the first one that mentions the name he gave here,
|
|
* and bind to the associated type / create a toplevel object
|
|
* of that type.
|
|
*/
|
|
|
|
while (n--) {
|
|
int m, child_members = (int)map->child_map_size;
|
|
|
|
for (m = 0; m < child_members; m++) {
|
|
const lws_struct_map_t *child = &map->child_map[m];
|
|
if (!strcmp(ctx->path, child->colname)) {
|
|
/*
|
|
* We matched on him... map is pointing
|
|
* to the right toplevel type, let's
|
|
* just pick up from there as if we
|
|
* matched the explicit schema name...
|
|
*/
|
|
ctx->path_match = 1;
|
|
imp = 1;
|
|
goto matched;
|
|
}
|
|
}
|
|
map++;
|
|
}
|
|
lwsl_notice("%s: can't match implicit schema %s\n",
|
|
__func__, ctx->path);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1)
|
|
return 0;
|
|
|
|
/* If "schema", then look for a matching name in the map array */
|
|
|
|
while (n--) {
|
|
if (strcmp(ctx->buf, map->colname)) {
|
|
map++;
|
|
continue;
|
|
}
|
|
|
|
matched:
|
|
|
|
a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size);
|
|
if (!a->dest) {
|
|
lwsl_err("%s: OOT\n", __func__);
|
|
|
|
return 1;
|
|
}
|
|
a->dest_len = map->aux;
|
|
if (!ctx->pst_sp)
|
|
a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]);
|
|
|
|
if (!cb)
|
|
cb = lws_struct_default_lejp_cb;
|
|
|
|
lejp_parser_push(ctx, a->dest, &map->child_map[0].colname,
|
|
(uint8_t)map->child_map_size, cb);
|
|
a->map_st[ctx->pst_sp] = map->child_map;
|
|
a->map_entries_st[ctx->pst_sp] = map->child_map_size;
|
|
|
|
// lwsl_notice("%s: child map ofs_clist %d\n", __func__,
|
|
// (int)a->map_st[ctx->pst_sp]->ofs_clist);
|
|
|
|
if (imp)
|
|
return cb(ctx, reason);
|
|
|
|
return 0;
|
|
}
|
|
|
|
lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args,
|
|
const lws_struct_map_t *map, uint8_t *ch)
|
|
{
|
|
lejp_callback cb = map->lejp_cb;
|
|
|
|
if (!cb)
|
|
cb = lws_struct_default_lejp_cb;
|
|
|
|
lejp_parser_push(ctx, ch, (const char * const*)map->child_map,
|
|
(uint8_t)map->child_map_size, cb);
|
|
|
|
args->map_st[ctx->pst_sp] = map->child_map;
|
|
args->map_entries_st[ctx->pst_sp] = map->child_map_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
signed char
|
|
lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason)
|
|
{
|
|
lws_struct_args_t *args = (lws_struct_args_t *)ctx->user;
|
|
const lws_struct_map_t *map, *pmap = NULL;
|
|
uint8_t *ch;
|
|
size_t n;
|
|
char *u;
|
|
|
|
if (reason == LEJPCB_ARRAY_END) {
|
|
lejp_parser_pop(ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (reason == LEJPCB_ARRAY_START) {
|
|
if (!ctx->path_match)
|
|
lwsl_err("%s: ARRAY_START with ctx->path_match 0\n", __func__);
|
|
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
|
|
|
|
if (map->type == LSMT_LIST)
|
|
lws_struct_lejp_push(ctx, args, map, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (ctx->pst_sp)
|
|
pmap = &args->map_st[ctx->pst_sp - 1]
|
|
[ctx->pst[ctx->pst_sp - 1].path_match - 1];
|
|
|
|
if (reason == LEJPCB_OBJECT_START) {
|
|
|
|
if (!ctx->path_match) {
|
|
ctx->pst[ctx->pst_sp].user = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
|
|
n = args->map_entries_st[ctx->pst_sp];
|
|
|
|
if (map->type != LSMT_CHILD_PTR && map->type != LSMT_LIST) {
|
|
ctx->pst[ctx->pst_sp].user = NULL;
|
|
|
|
return 0;
|
|
}
|
|
pmap = map;
|
|
|
|
lws_struct_lejp_push(ctx, args, map, NULL);
|
|
}
|
|
|
|
if (reason == LEJPCB_OBJECT_END && pmap) {
|
|
if (pmap->type == LSMT_CHILD_PTR)
|
|
lejp_parser_pop(ctx);
|
|
|
|
if (ctx->pst_sp)
|
|
pmap = &args->map_st[ctx->pst_sp - 1]
|
|
[ctx->pst[ctx->pst_sp - 1].path_match - 1];
|
|
}
|
|
|
|
if (!ctx->path_match)
|
|
return 0;
|
|
|
|
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
|
|
n = args->map_entries_st[ctx->pst_sp];
|
|
|
|
if (map->type == LSMT_SCHEMA) {
|
|
|
|
while (n--) {
|
|
if (strncmp(map->colname, ctx->buf, ctx->npos)) {
|
|
map++;
|
|
continue;
|
|
}
|
|
|
|
/* instantiate the correct toplevel object */
|
|
|
|
ch = lwsac_use_zero(&args->ac, map->aux,
|
|
args->ac_block_size);
|
|
if (!ch) {
|
|
lwsl_err("OOM\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
lws_struct_lejp_push(ctx, args, map, ch);
|
|
|
|
return 0;
|
|
}
|
|
lwsl_notice("%s: unknown schema %.*s, tried %d\n", __func__,
|
|
ctx->npos, ctx->buf,
|
|
(int)args->map_entries_st[ctx->pst_sp]);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!ctx->pst[ctx->pst_sp].user) {
|
|
struct lws_dll2_owner *owner;
|
|
struct lws_dll2 *list;
|
|
|
|
/* create list item object if none already */
|
|
|
|
if (!ctx->path_match || !pmap)
|
|
return 0;
|
|
|
|
map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1];
|
|
n = args->map_entries_st[ctx->pst_sp - 1];
|
|
|
|
if (!ctx->pst_sp)
|
|
return 0;
|
|
|
|
if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR)
|
|
return 1;
|
|
|
|
/* we need to create a child or array item object */
|
|
|
|
owner = (struct lws_dll2_owner *)
|
|
(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
|
|
|
|
assert(pmap->aux);
|
|
|
|
/* instantiate one of the child objects */
|
|
|
|
ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac,
|
|
pmap->aux, args->ac_block_size);
|
|
if (!ctx->pst[ctx->pst_sp].user) {
|
|
lwsl_err("OOM\n");
|
|
|
|
return 1;
|
|
}
|
|
lwsl_info("%s: created '%s' object size %d\n", __func__,
|
|
pmap->colname, (int)pmap->aux);
|
|
|
|
switch (pmap->type) {
|
|
case LSMT_LIST:
|
|
list = (struct lws_dll2 *)
|
|
((char *)ctx->pst[ctx->pst_sp].user +
|
|
pmap->ofs_clist);
|
|
|
|
lws_dll2_add_tail(list, owner);
|
|
break;
|
|
case LSMT_CHILD_PTR:
|
|
*((void **)owner) = ctx->pst[ctx->pst_sp].user;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ctx->path_match)
|
|
return 0;
|
|
|
|
if (reason == LEJPCB_VAL_STR_CHUNK) {
|
|
lejp_collation_t *coll;
|
|
|
|
/* don't cache stuff we are going to ignore */
|
|
|
|
if (map->type == LSMT_STRING_CHAR_ARRAY &&
|
|
args->chunks_length >= map->aux)
|
|
return 0;
|
|
|
|
coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll),
|
|
sizeof(*coll));
|
|
if (!coll) {
|
|
lwsl_err("%s: OOT\n", __func__);
|
|
|
|
return 1;
|
|
}
|
|
coll->chunks.prev = NULL;
|
|
coll->chunks.next = NULL;
|
|
coll->chunks.owner = NULL;
|
|
|
|
coll->len = ctx->npos;
|
|
lws_dll2_add_tail(&coll->chunks, &args->chunks_owner);
|
|
|
|
memcpy(coll->buf, ctx->buf, ctx->npos);
|
|
|
|
args->chunks_length += ctx->npos;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT &&
|
|
reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE)
|
|
return 0;
|
|
|
|
/* this is the end of the string */
|
|
|
|
if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) {
|
|
void **pp = (void **)
|
|
(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
|
|
|
|
*pp = ctx->pst[ctx->pst_sp].user;
|
|
}
|
|
|
|
u = (char *)ctx->pst[ctx->pst_sp].user;
|
|
if (!u)
|
|
u = (char *)ctx->pst[ctx->pst_sp - 1].user;
|
|
|
|
{
|
|
char **pp, *s;
|
|
size_t lim, b;
|
|
long long li;
|
|
|
|
switch (map->type) {
|
|
case LSMT_SIGNED:
|
|
if (map->aux == sizeof(signed char)) {
|
|
signed char *pc;
|
|
pc = (signed char *)(u + map->ofs);
|
|
*pc = (signed char)atoi(ctx->buf);
|
|
break;
|
|
}
|
|
if (map->aux == sizeof(int)) {
|
|
int *pi;
|
|
pi = (int *)(u + map->ofs);
|
|
*pi = atoi(ctx->buf);
|
|
break;
|
|
}
|
|
if (map->aux == sizeof(long)) {
|
|
long *pl;
|
|
pl = (long *)(u + map->ofs);
|
|
*pl = atol(ctx->buf);
|
|
} else {
|
|
long long *pll;
|
|
pll = (long long *)(u + map->ofs);
|
|
*pll = atoll(ctx->buf);
|
|
}
|
|
break;
|
|
|
|
case LSMT_UNSIGNED:
|
|
if (map->aux == sizeof(unsigned char)) {
|
|
unsigned char *pc;
|
|
pc = (unsigned char *)(u + map->ofs);
|
|
*pc = (unsigned char)(unsigned int)atoi(ctx->buf);
|
|
break;
|
|
}
|
|
if (map->aux == sizeof(unsigned int)) {
|
|
unsigned int *pi;
|
|
pi = (unsigned int *)(u + map->ofs);
|
|
*pi = (unsigned int)atoi(ctx->buf);
|
|
break;
|
|
}
|
|
if (map->aux == sizeof(unsigned long)) {
|
|
unsigned long *pl;
|
|
pl = (unsigned long *)(u + map->ofs);
|
|
*pl = (unsigned long)atol(ctx->buf);
|
|
} else {
|
|
unsigned long long *pll;
|
|
pll = (unsigned long long *)(u + map->ofs);
|
|
*pll = (unsigned long long)atoll(ctx->buf);
|
|
}
|
|
break;
|
|
|
|
case LSMT_BOOLEAN:
|
|
li = reason == LEJPCB_VAL_TRUE;
|
|
if (map->aux == sizeof(char)) {
|
|
char *pc;
|
|
pc = (char *)(u + map->ofs);
|
|
*pc = (char)li;
|
|
break;
|
|
}
|
|
if (map->aux == sizeof(int)) {
|
|
int *pi;
|
|
pi = (int *)(u + map->ofs);
|
|
*pi = (int)li;
|
|
} else {
|
|
uint64_t *p64;
|
|
p64 = (uint64_t *)(u + map->ofs);
|
|
*p64 = (uint64_t)li;
|
|
}
|
|
break;
|
|
|
|
case LSMT_STRING_CHAR_ARRAY:
|
|
s = (char *)(u + map->ofs);
|
|
lim = map->aux - 1;
|
|
goto chunk_copy;
|
|
|
|
case LSMT_STRING_PTR:
|
|
pp = (char **)(u + map->ofs);
|
|
lim = args->chunks_length + ctx->npos;
|
|
s = lwsac_use(&args->ac, lim + 1, args->ac_block_size);
|
|
if (!s)
|
|
goto cleanup;
|
|
*pp = s;
|
|
|
|
chunk_copy:
|
|
s[lim] = '\0';
|
|
/* copy up to lim from the string chunk ac first */
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
|
args->chunks_owner.head) {
|
|
lejp_collation_t *coll = (lejp_collation_t *)p;
|
|
|
|
if (lim) {
|
|
b = (unsigned int)coll->len;
|
|
if (b > lim)
|
|
b = lim;
|
|
memcpy(s, coll->buf, b);
|
|
s += b;
|
|
lim -= b;
|
|
}
|
|
} lws_end_foreach_dll_safe(p, p1);
|
|
|
|
lwsac_free(&args->ac_chunks);
|
|
args->chunks_owner.count = 0;
|
|
args->chunks_owner.head = NULL;
|
|
args->chunks_owner.tail = NULL;
|
|
|
|
if (lim) {
|
|
b = ctx->npos;
|
|
if (b > lim)
|
|
b = lim;
|
|
memcpy(s, ctx->buf, b);
|
|
s[b] = '\0';
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (args->cb)
|
|
args->cb(args->dest, args->cb_arg);
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
lwsl_notice("%s: cleanup\n", __func__);
|
|
lwsac_free(&args->ac_chunks);
|
|
args->chunks_owner.count = 0;
|
|
args->chunks_owner.head = NULL;
|
|
args->chunks_owner.tail = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const char * schema[] = { "schema" };
|
|
|
|
int
|
|
lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user)
|
|
{
|
|
/*
|
|
* By default we are looking to match on a toplevel member called
|
|
* "schema", against an LSM_SCHEMA
|
|
*/
|
|
if (!cb)
|
|
cb = lws_struct_schema_only_lejp_cb;
|
|
lejp_construct(ctx, cb, user, schema, 1);
|
|
|
|
ctx->path_stride = sizeof(lws_struct_map_t);
|
|
|
|
return 0;
|
|
}
|
|
|
|
lws_struct_serialize_t *
|
|
lws_struct_json_serialize_create(const lws_struct_map_t *map,
|
|
size_t map_entries, int flags,
|
|
const void *ptoplevel)
|
|
{
|
|
lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__);
|
|
lws_struct_serialize_st_t *j;
|
|
|
|
if (!js)
|
|
return NULL;
|
|
|
|
js->flags = flags;
|
|
|
|
j = &js->st[0];
|
|
j->map = map;
|
|
j->map_entries = map_entries;
|
|
j->obj = ptoplevel;
|
|
j->idt = 0;
|
|
|
|
return js;
|
|
}
|
|
|
|
void
|
|
lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs)
|
|
{
|
|
if (!*pjs)
|
|
return;
|
|
|
|
lws_free(*pjs);
|
|
|
|
*pjs = NULL;
|
|
}
|
|
|
|
static void
|
|
lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen)
|
|
{
|
|
if (js->flags & LSSERJ_FLAG_PRETTY) {
|
|
int n;
|
|
|
|
*(*pbuf)++ = '\n';
|
|
(*plen)--;
|
|
for (n = 0; n < js->st[js->sp].idt; n++) {
|
|
*(*pbuf)++ = ' ';
|
|
(*plen)--;
|
|
}
|
|
}
|
|
}
|
|
|
|
lws_struct_json_serialize_result_t
|
|
lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf,
|
|
size_t len, size_t *written)
|
|
{
|
|
lws_struct_serialize_st_t *j;
|
|
const lws_struct_map_t *map;
|
|
size_t budget = 0, olen = len, m;
|
|
struct lws_dll2_owner *o;
|
|
unsigned long long uli;
|
|
const char *q;
|
|
const void *p;
|
|
char dbuf[72];
|
|
long long li;
|
|
int n, used;
|
|
|
|
*written = 0;
|
|
*buf = '\0';
|
|
|
|
while (len > sizeof(dbuf) + 20) {
|
|
j = &js->st[js->sp];
|
|
map = &j->map[j->map_entry];
|
|
q = j->obj + map->ofs;
|
|
|
|
/* early check if the entry should be elided */
|
|
|
|
switch (map->type) {
|
|
case LSMT_STRING_CHAR_ARRAY:
|
|
if (!q)
|
|
goto up;
|
|
break;
|
|
case LSMT_STRING_PTR:
|
|
case LSMT_CHILD_PTR:
|
|
q = (char *)*(char **)q;
|
|
if (!q)
|
|
goto up;
|
|
break;
|
|
|
|
case LSMT_LIST:
|
|
o = (struct lws_dll2_owner *)q;
|
|
p = j->dllpos = lws_dll2_get_head(o);
|
|
if (!p)
|
|
goto up;
|
|
break;
|
|
|
|
case LSMT_BLOB_PTR:
|
|
goto up;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (j->subsequent) {
|
|
*buf++ = ',';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
}
|
|
j->subsequent = 1;
|
|
|
|
if (map->type != LSMT_SCHEMA && !js->offset) {
|
|
n = lws_snprintf((char *)buf, len, "\"%s\":",
|
|
map->colname);
|
|
buf += n;
|
|
len = len - (unsigned int)n;
|
|
if (js->flags & LSSERJ_FLAG_PRETTY) {
|
|
*buf++ = ' ';
|
|
len--;
|
|
}
|
|
}
|
|
|
|
switch (map->type) {
|
|
case LSMT_BOOLEAN:
|
|
case LSMT_UNSIGNED:
|
|
if (map->aux == sizeof(char)) {
|
|
uli = *(unsigned char *)q;
|
|
} else {
|
|
if (map->aux == sizeof(int)) {
|
|
uli = *(unsigned int *)q;
|
|
} else {
|
|
if (map->aux == sizeof(long))
|
|
uli = *(unsigned long *)q;
|
|
else
|
|
uli = *(unsigned long long *)q;
|
|
}
|
|
}
|
|
q = dbuf;
|
|
|
|
if (map->type == LSMT_BOOLEAN) {
|
|
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
|
|
"%s", uli ? "true" : "false");
|
|
} else
|
|
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
|
|
"%llu", uli);
|
|
break;
|
|
|
|
case LSMT_SIGNED:
|
|
if (map->aux == sizeof(signed char)) {
|
|
li = (long long)*(signed char *)q;
|
|
} else {
|
|
if (map->aux == sizeof(int)) {
|
|
li = (long long)*(int *)q;
|
|
} else {
|
|
if (map->aux == sizeof(long))
|
|
li = (long long)*(long *)q;
|
|
else
|
|
li = *(long long *)q;
|
|
}
|
|
}
|
|
q = dbuf;
|
|
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), "%lld", li);
|
|
break;
|
|
|
|
case LSMT_STRING_CHAR_ARRAY:
|
|
case LSMT_STRING_PTR:
|
|
if (!js->offset) {
|
|
*buf++ = '\"';
|
|
len--;
|
|
}
|
|
break;
|
|
|
|
case LSMT_LIST:
|
|
*buf++ = '[';
|
|
len--;
|
|
if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
|
|
return LSJS_RESULT_ERROR;
|
|
|
|
/* add a stack level to handle parsing array members */
|
|
|
|
o = (struct lws_dll2_owner *)q;
|
|
p = j->dllpos = lws_dll2_get_head(o);
|
|
|
|
if (!j->dllpos) {
|
|
*buf++ = ']';
|
|
len--;
|
|
goto up;
|
|
}
|
|
|
|
n = j->idt;
|
|
j = &js->st[++js->sp];
|
|
j->idt = (char)(n + 2);
|
|
j->map = map->child_map;
|
|
j->map_entries = map->child_map_size;
|
|
j->size = map->aux;
|
|
j->subsequent = 0;
|
|
j->map_entry = 0;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
*buf++ = '{';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
if (p)
|
|
j->obj = ((char *)p) - j->map->ofs_clist;
|
|
else
|
|
j->obj = NULL;
|
|
continue;
|
|
|
|
case LSMT_CHILD_PTR:
|
|
|
|
if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
|
|
return LSJS_RESULT_ERROR;
|
|
|
|
/* add a stack level to handle parsing child members */
|
|
|
|
n = j->idt;
|
|
j = &js->st[++js->sp];
|
|
j->idt = (char)(n + 2);
|
|
j->map = map->child_map;
|
|
j->map_entries = map->child_map_size;
|
|
j->size = map->aux;
|
|
j->subsequent = 0;
|
|
j->map_entry = 0;
|
|
*buf++ = '{';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
j->obj = q;
|
|
|
|
continue;
|
|
|
|
case LSMT_SCHEMA:
|
|
q = dbuf;
|
|
*buf++ = '{';
|
|
len--;
|
|
j = &js->st[++js->sp];
|
|
lws_struct_pretty(js, &buf, &len);
|
|
if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA)) {
|
|
budget = (unsigned int)lws_snprintf(dbuf, 15, "\"schema\":");
|
|
if (js->flags & LSSERJ_FLAG_PRETTY)
|
|
dbuf[budget++] = ' ';
|
|
|
|
budget += (unsigned int)lws_snprintf(dbuf + budget,
|
|
sizeof(dbuf) - budget,
|
|
"\"%s\"", map->colname);
|
|
}
|
|
|
|
|
|
if (js->sp != 1)
|
|
return LSJS_RESULT_ERROR;
|
|
j->map = map->child_map;
|
|
j->map_entries = map->child_map_size;
|
|
j->size = map->aux;
|
|
j->subsequent = 0;
|
|
j->map_entry = 0;
|
|
j->obj = js->st[js->sp - 1].obj;
|
|
j->dllpos = NULL;
|
|
if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA))
|
|
/* we're actually at the same level */
|
|
j->subsequent = 1;
|
|
j->idt = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (map->type) {
|
|
case LSMT_STRING_CHAR_ARRAY:
|
|
case LSMT_STRING_PTR:
|
|
/*
|
|
* This is a bit tricky... we have to escape the string
|
|
* which may 6x its length depending on what the
|
|
* contents are.
|
|
*
|
|
* We offset the unescaped string starting point first
|
|
*/
|
|
|
|
q += js->offset;
|
|
budget = strlen(q); /* how much unescaped is left */
|
|
|
|
/*
|
|
* This is going to escape as much as it can fit, and
|
|
* let us know the amount of input that was consumed
|
|
* in "used".
|
|
*/
|
|
|
|
lws_json_purify((char *)buf, q, (int)len, &used);
|
|
m = strlen((const char *)buf);
|
|
buf += m;
|
|
len -= m;
|
|
js->remaining = budget - (unsigned int)used;
|
|
js->offset = (unsigned int)used;
|
|
if (!js->remaining)
|
|
js->offset = 0;
|
|
|
|
break;
|
|
default:
|
|
q += js->offset;
|
|
budget -= js->remaining;
|
|
|
|
if (budget > len) {
|
|
js->remaining = budget - len;
|
|
js->offset = len;
|
|
budget = len;
|
|
} else {
|
|
js->remaining = 0;
|
|
js->offset = 0;
|
|
}
|
|
|
|
memcpy(buf, q, budget);
|
|
buf += budget;
|
|
*buf = '\0';
|
|
len -= budget;
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
switch (map->type) {
|
|
case LSMT_STRING_CHAR_ARRAY:
|
|
case LSMT_STRING_PTR:
|
|
*buf++ = '\"';
|
|
len--;
|
|
break;
|
|
case LSMT_SCHEMA:
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (js->remaining)
|
|
continue;
|
|
up:
|
|
if (++j->map_entry < j->map_entries)
|
|
continue;
|
|
|
|
if (!js->sp)
|
|
continue;
|
|
js->sp--;
|
|
if (!js->sp) {
|
|
lws_struct_pretty(js, &buf, &len);
|
|
*buf++ = '}';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
break;
|
|
}
|
|
js->offset = 0;
|
|
j = &js->st[js->sp];
|
|
map = &j->map[j->map_entry];
|
|
|
|
if (map->type == LSMT_CHILD_PTR) {
|
|
lws_struct_pretty(js, &buf, &len);
|
|
*buf++ = '}';
|
|
len--;
|
|
|
|
/* we have done the singular child pointer */
|
|
|
|
js->offset = 0;
|
|
goto up;
|
|
}
|
|
|
|
if (map->type != LSMT_LIST)
|
|
continue;
|
|
/*
|
|
* we are coming back up to an array map, it means we should
|
|
* advance to the next array member if there is one
|
|
*/
|
|
|
|
lws_struct_pretty(js, &buf, &len);
|
|
*buf++ = '}';
|
|
len--;
|
|
|
|
p = j->dllpos = j->dllpos->next;
|
|
if (j->dllpos) {
|
|
/*
|
|
* there was another item in the array to do... let's
|
|
* move on to that and do it
|
|
*/
|
|
*buf++ = ',';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
js->offset = 0;
|
|
j = &js->st[++js->sp];
|
|
j->map_entry = 0;
|
|
map = &j->map[j->map_entry];
|
|
|
|
*buf++ = '{';
|
|
len--;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
|
|
j->subsequent = 0;
|
|
j->obj = ((char *)p) - j->map->ofs_clist;
|
|
continue;
|
|
}
|
|
|
|
/* there are no further items in the array */
|
|
|
|
js->offset = 0;
|
|
lws_struct_pretty(js, &buf, &len);
|
|
*buf++ = ']';
|
|
len--;
|
|
goto up;
|
|
}
|
|
|
|
*written = olen - len;
|
|
*buf = '\0'; /* convenience, a NUL after the official end */
|
|
|
|
return LSJS_RESULT_FINISH;
|
|
}
|