| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2020 Andy Green <[email protected]> |
| * |
| * 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; |
| } |