1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include <libwebsockets.h>
26 #include <private-lib-core.h>
27
28 #include <assert.h>
29
30 signed char
lws_struct_schema_only_lejp_cb(struct lejp_ctx * ctx,char reason)31 lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason)
32 {
33 lws_struct_args_t *a = (lws_struct_args_t *)ctx->user;
34 const lws_struct_map_t *map = a->map_st[ctx->pst_sp];
35 size_t n = a->map_entries_st[ctx->pst_sp], imp = 0;
36 lejp_callback cb = map->lejp_cb;
37
38 if (reason == LEJPCB_PAIR_NAME && strcmp(ctx->path, "schema")) {
39 /*
40 * If not "schema", the schema is implicit rather than
41 * explicitly given, ie, he just goes ahead and starts using
42 * member names that imply a particular type. For example, he
43 * may have an implicit type normally, and a different one for
44 * exceptions that just starts using "error-message" or whatever
45 * and we can understand that's the exception type now.
46 *
47 * Let's look into each of the maps in the top level array
48 * and match the first one that mentions the name he gave here,
49 * and bind to the associated type / create a toplevel object
50 * of that type.
51 */
52
53 while (n--) {
54 int m, child_members = (int)map->child_map_size;
55
56 for (m = 0; m < child_members; m++) {
57 const lws_struct_map_t *child = &map->child_map[m];
58 if (!strcmp(ctx->path, child->colname)) {
59 /*
60 * We matched on him... map is pointing
61 * to the right toplevel type, let's
62 * just pick up from there as if we
63 * matched the explicit schema name...
64 */
65 ctx->path_match = 1;
66 imp = 1;
67 goto matched;
68 }
69 }
70 map++;
71 }
72 lwsl_notice("%s: can't match implicit schema %s\n",
73 __func__, ctx->path);
74
75 return -1;
76 }
77
78 if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1)
79 return 0;
80
81 /* If "schema", then look for a matching name in the map array */
82
83 while (n--) {
84 if (strcmp(ctx->buf, map->colname)) {
85 map++;
86 continue;
87 }
88
89 matched:
90
91 a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size);
92 if (!a->dest) {
93 lwsl_err("%s: OOT\n", __func__);
94
95 return 1;
96 }
97 a->dest_len = map->aux;
98 if (!ctx->pst_sp)
99 a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]);
100
101 if (!cb)
102 cb = lws_struct_default_lejp_cb;
103
104 lejp_parser_push(ctx, a->dest, &map->child_map[0].colname,
105 (uint8_t)map->child_map_size, cb);
106 a->map_st[ctx->pst_sp] = map->child_map;
107 a->map_entries_st[ctx->pst_sp] = map->child_map_size;
108
109 // lwsl_notice("%s: child map ofs_clist %d\n", __func__,
110 // (int)a->map_st[ctx->pst_sp]->ofs_clist);
111
112 if (imp)
113 return cb(ctx, reason);
114
115 return 0;
116 }
117
118 lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf);
119
120 return 1;
121 }
122
123 static int
lws_struct_lejp_push(struct lejp_ctx * ctx,lws_struct_args_t * args,const lws_struct_map_t * map,uint8_t * ch)124 lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args,
125 const lws_struct_map_t *map, uint8_t *ch)
126 {
127 lejp_callback cb = map->lejp_cb;
128
129 if (!cb)
130 cb = lws_struct_default_lejp_cb;
131
132 lejp_parser_push(ctx, ch, (const char * const*)map->child_map,
133 (uint8_t)map->child_map_size, cb);
134
135 args->map_st[ctx->pst_sp] = map->child_map;
136 args->map_entries_st[ctx->pst_sp] = map->child_map_size;
137
138 return 0;
139 }
140
141 signed char
lws_struct_default_lejp_cb(struct lejp_ctx * ctx,char reason)142 lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason)
143 {
144 lws_struct_args_t *args = (lws_struct_args_t *)ctx->user;
145 const lws_struct_map_t *map, *pmap = NULL;
146 uint8_t *ch;
147 size_t n;
148 char *u;
149
150 if (reason == LEJPCB_ARRAY_END) {
151 lejp_parser_pop(ctx);
152
153 return 0;
154 }
155
156 if (reason == LEJPCB_ARRAY_START) {
157 if (!ctx->path_match)
158 lwsl_err("%s: ARRAY_START with ctx->path_match 0\n", __func__);
159 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
160
161 if (map->type == LSMT_LIST)
162 lws_struct_lejp_push(ctx, args, map, NULL);
163
164 return 0;
165 }
166
167 if (ctx->pst_sp)
168 pmap = &args->map_st[ctx->pst_sp - 1]
169 [ctx->pst[ctx->pst_sp - 1].path_match - 1];
170
171 if (reason == LEJPCB_OBJECT_START) {
172
173 if (!ctx->path_match) {
174 ctx->pst[ctx->pst_sp].user = NULL;
175
176 return 0;
177 }
178
179 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
180 n = args->map_entries_st[ctx->pst_sp];
181
182 if (map->type != LSMT_CHILD_PTR && map->type != LSMT_LIST) {
183 ctx->pst[ctx->pst_sp].user = NULL;
184
185 return 0;
186 }
187 pmap = map;
188
189 lws_struct_lejp_push(ctx, args, map, NULL);
190 }
191
192 if (reason == LEJPCB_OBJECT_END && pmap) {
193 if (pmap->type == LSMT_CHILD_PTR)
194 lejp_parser_pop(ctx);
195
196 if (ctx->pst_sp)
197 pmap = &args->map_st[ctx->pst_sp - 1]
198 [ctx->pst[ctx->pst_sp - 1].path_match - 1];
199 }
200
201 if (!ctx->path_match)
202 return 0;
203
204 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
205 n = args->map_entries_st[ctx->pst_sp];
206
207 if (map->type == LSMT_SCHEMA) {
208
209 while (n--) {
210 if (strncmp(map->colname, ctx->buf, ctx->npos)) {
211 map++;
212 continue;
213 }
214
215 /* instantiate the correct toplevel object */
216
217 ch = lwsac_use_zero(&args->ac, map->aux,
218 args->ac_block_size);
219 if (!ch) {
220 lwsl_err("OOM\n");
221
222 return 1;
223 }
224
225 lws_struct_lejp_push(ctx, args, map, ch);
226
227 return 0;
228 }
229 lwsl_notice("%s: unknown schema %.*s, tried %d\n", __func__,
230 ctx->npos, ctx->buf,
231 (int)args->map_entries_st[ctx->pst_sp]);
232
233 goto cleanup;
234 }
235
236 if (!ctx->pst[ctx->pst_sp].user) {
237 struct lws_dll2_owner *owner;
238 struct lws_dll2 *list;
239
240 /* create list item object if none already */
241
242 if (!ctx->path_match || !pmap)
243 return 0;
244
245 map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1];
246 n = args->map_entries_st[ctx->pst_sp - 1];
247
248 if (!ctx->pst_sp)
249 return 0;
250
251 if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR)
252 return 1;
253
254 /* we need to create a child or array item object */
255
256 owner = (struct lws_dll2_owner *)
257 (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
258
259 assert(pmap->aux);
260
261 /* instantiate one of the child objects */
262
263 ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac,
264 pmap->aux, args->ac_block_size);
265 if (!ctx->pst[ctx->pst_sp].user) {
266 lwsl_err("OOM\n");
267
268 return 1;
269 }
270 lwsl_info("%s: created '%s' object size %d\n", __func__,
271 pmap->colname, (int)pmap->aux);
272
273 switch (pmap->type) {
274 case LSMT_LIST:
275 list = (struct lws_dll2 *)
276 ((char *)ctx->pst[ctx->pst_sp].user +
277 pmap->ofs_clist);
278
279 lws_dll2_add_tail(list, owner);
280 break;
281 case LSMT_CHILD_PTR:
282 *((void **)owner) = ctx->pst[ctx->pst_sp].user;
283 break;
284 default:
285 assert(0);
286 break;
287 }
288 }
289
290 if (!ctx->path_match)
291 return 0;
292
293 if (reason == LEJPCB_VAL_STR_CHUNK) {
294 lejp_collation_t *coll;
295
296 /* don't cache stuff we are going to ignore */
297
298 if (map->type == LSMT_STRING_CHAR_ARRAY &&
299 args->chunks_length >= map->aux)
300 return 0;
301
302 coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll),
303 sizeof(*coll));
304 if (!coll) {
305 lwsl_err("%s: OOT\n", __func__);
306
307 return 1;
308 }
309 coll->chunks.prev = NULL;
310 coll->chunks.next = NULL;
311 coll->chunks.owner = NULL;
312
313 coll->len = ctx->npos;
314 lws_dll2_add_tail(&coll->chunks, &args->chunks_owner);
315
316 memcpy(coll->buf, ctx->buf, ctx->npos);
317
318 args->chunks_length += ctx->npos;
319
320 return 0;
321 }
322
323 if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT &&
324 reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE)
325 return 0;
326
327 /* this is the end of the string */
328
329 if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) {
330 void **pp = (void **)
331 (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
332
333 *pp = ctx->pst[ctx->pst_sp].user;
334 }
335
336 u = (char *)ctx->pst[ctx->pst_sp].user;
337 if (!u)
338 u = (char *)ctx->pst[ctx->pst_sp - 1].user;
339
340 {
341 char **pp, *s;
342 size_t lim, b;
343 long long li;
344
345 switch (map->type) {
346 case LSMT_SIGNED:
347 if (map->aux == sizeof(signed char)) {
348 signed char *pc;
349 pc = (signed char *)(u + map->ofs);
350 *pc = (signed char)atoi(ctx->buf);
351 break;
352 }
353 if (map->aux == sizeof(int)) {
354 int *pi;
355 pi = (int *)(u + map->ofs);
356 *pi = atoi(ctx->buf);
357 break;
358 }
359 if (map->aux == sizeof(long)) {
360 long *pl;
361 pl = (long *)(u + map->ofs);
362 *pl = atol(ctx->buf);
363 } else {
364 long long *pll;
365 pll = (long long *)(u + map->ofs);
366 *pll = atoll(ctx->buf);
367 }
368 break;
369
370 case LSMT_UNSIGNED:
371 if (map->aux == sizeof(unsigned char)) {
372 unsigned char *pc;
373 pc = (unsigned char *)(u + map->ofs);
374 *pc = (unsigned char)(unsigned int)atoi(ctx->buf);
375 break;
376 }
377 if (map->aux == sizeof(unsigned int)) {
378 unsigned int *pi;
379 pi = (unsigned int *)(u + map->ofs);
380 *pi = (unsigned int)atoi(ctx->buf);
381 break;
382 }
383 if (map->aux == sizeof(unsigned long)) {
384 unsigned long *pl;
385 pl = (unsigned long *)(u + map->ofs);
386 *pl = (unsigned long)atol(ctx->buf);
387 } else {
388 unsigned long long *pll;
389 pll = (unsigned long long *)(u + map->ofs);
390 *pll = (unsigned long long)atoll(ctx->buf);
391 }
392 break;
393
394 case LSMT_BOOLEAN:
395 li = reason == LEJPCB_VAL_TRUE;
396 if (map->aux == sizeof(char)) {
397 char *pc;
398 pc = (char *)(u + map->ofs);
399 *pc = (char)li;
400 break;
401 }
402 if (map->aux == sizeof(int)) {
403 int *pi;
404 pi = (int *)(u + map->ofs);
405 *pi = (int)li;
406 } else {
407 uint64_t *p64;
408 p64 = (uint64_t *)(u + map->ofs);
409 *p64 = (uint64_t)li;
410 }
411 break;
412
413 case LSMT_STRING_CHAR_ARRAY:
414 s = (char *)(u + map->ofs);
415 lim = map->aux - 1;
416 goto chunk_copy;
417
418 case LSMT_STRING_PTR:
419 pp = (char **)(u + map->ofs);
420 lim = args->chunks_length + ctx->npos;
421 s = lwsac_use(&args->ac, lim + 1, args->ac_block_size);
422 if (!s)
423 goto cleanup;
424 *pp = s;
425
426 chunk_copy:
427 s[lim] = '\0';
428 /* copy up to lim from the string chunk ac first */
429 lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
430 args->chunks_owner.head) {
431 lejp_collation_t *coll = (lejp_collation_t *)p;
432
433 if (lim) {
434 b = (unsigned int)coll->len;
435 if (b > lim)
436 b = lim;
437 memcpy(s, coll->buf, b);
438 s += b;
439 lim -= b;
440 }
441 } lws_end_foreach_dll_safe(p, p1);
442
443 lwsac_free(&args->ac_chunks);
444 args->chunks_owner.count = 0;
445 args->chunks_owner.head = NULL;
446 args->chunks_owner.tail = NULL;
447
448 if (lim) {
449 b = ctx->npos;
450 if (b > lim)
451 b = lim;
452 memcpy(s, ctx->buf, b);
453 s[b] = '\0';
454 }
455 break;
456 default:
457 break;
458 }
459 }
460
461 if (args->cb)
462 args->cb(args->dest, args->cb_arg);
463
464 return 0;
465
466 cleanup:
467 lwsl_notice("%s: cleanup\n", __func__);
468 lwsac_free(&args->ac_chunks);
469 args->chunks_owner.count = 0;
470 args->chunks_owner.head = NULL;
471 args->chunks_owner.tail = NULL;
472
473 return 1;
474 }
475
476 static const char * schema[] = { "schema" };
477
478 int
lws_struct_json_init_parse(struct lejp_ctx * ctx,lejp_callback cb,void * user)479 lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user)
480 {
481 /*
482 * By default we are looking to match on a toplevel member called
483 * "schema", against an LSM_SCHEMA
484 */
485 if (!cb)
486 cb = lws_struct_schema_only_lejp_cb;
487 lejp_construct(ctx, cb, user, schema, 1);
488
489 ctx->path_stride = sizeof(lws_struct_map_t);
490
491 return 0;
492 }
493
494 lws_struct_serialize_t *
lws_struct_json_serialize_create(const lws_struct_map_t * map,size_t map_entries,int flags,const void * ptoplevel)495 lws_struct_json_serialize_create(const lws_struct_map_t *map,
496 size_t map_entries, int flags,
497 const void *ptoplevel)
498 {
499 lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__);
500 lws_struct_serialize_st_t *j;
501
502 if (!js)
503 return NULL;
504
505 js->flags = flags;
506
507 j = &js->st[0];
508 j->map = map;
509 j->map_entries = map_entries;
510 j->obj = ptoplevel;
511 j->idt = 0;
512
513 return js;
514 }
515
516 void
lws_struct_json_serialize_destroy(lws_struct_serialize_t ** pjs)517 lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs)
518 {
519 if (!*pjs)
520 return;
521
522 lws_free(*pjs);
523
524 *pjs = NULL;
525 }
526
527 static void
lws_struct_pretty(lws_struct_serialize_t * js,uint8_t ** pbuf,size_t * plen)528 lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen)
529 {
530 if (js->flags & LSSERJ_FLAG_PRETTY) {
531 int n;
532
533 *(*pbuf)++ = '\n';
534 (*plen)--;
535 for (n = 0; n < js->st[js->sp].idt; n++) {
536 *(*pbuf)++ = ' ';
537 (*plen)--;
538 }
539 }
540 }
541
542 lws_struct_json_serialize_result_t
lws_struct_json_serialize(lws_struct_serialize_t * js,uint8_t * buf,size_t len,size_t * written)543 lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf,
544 size_t len, size_t *written)
545 {
546 lws_struct_serialize_st_t *j;
547 const lws_struct_map_t *map;
548 size_t budget = 0, olen = len, m;
549 struct lws_dll2_owner *o;
550 unsigned long long uli;
551 const char *q;
552 const void *p;
553 char dbuf[72];
554 long long li;
555 int n, used;
556
557 *written = 0;
558 *buf = '\0';
559
560 while (len > sizeof(dbuf) + 20) {
561 j = &js->st[js->sp];
562 map = &j->map[j->map_entry];
563 q = j->obj + map->ofs;
564
565 /* early check if the entry should be elided */
566
567 switch (map->type) {
568 case LSMT_STRING_CHAR_ARRAY:
569 if (!q)
570 goto up;
571 break;
572 case LSMT_STRING_PTR:
573 case LSMT_CHILD_PTR:
574 q = (char *)*(char **)q;
575 if (!q)
576 goto up;
577 break;
578
579 case LSMT_LIST:
580 o = (struct lws_dll2_owner *)q;
581 p = j->dllpos = lws_dll2_get_head(o);
582 if (!p)
583 goto up;
584 break;
585
586 case LSMT_BLOB_PTR:
587 goto up;
588
589 default:
590 break;
591 }
592
593 if (j->subsequent) {
594 *buf++ = ',';
595 len--;
596 lws_struct_pretty(js, &buf, &len);
597 }
598 j->subsequent = 1;
599
600 if (map->type != LSMT_SCHEMA && !js->offset) {
601 n = lws_snprintf((char *)buf, len, "\"%s\":",
602 map->colname);
603 buf += n;
604 len = len - (unsigned int)n;
605 if (js->flags & LSSERJ_FLAG_PRETTY) {
606 *buf++ = ' ';
607 len--;
608 }
609 }
610
611 switch (map->type) {
612 case LSMT_BOOLEAN:
613 case LSMT_UNSIGNED:
614 if (map->aux == sizeof(char)) {
615 uli = *(unsigned char *)q;
616 } else {
617 if (map->aux == sizeof(int)) {
618 uli = *(unsigned int *)q;
619 } else {
620 if (map->aux == sizeof(long))
621 uli = *(unsigned long *)q;
622 else
623 uli = *(unsigned long long *)q;
624 }
625 }
626 q = dbuf;
627
628 if (map->type == LSMT_BOOLEAN) {
629 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
630 "%s", uli ? "true" : "false");
631 } else
632 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
633 "%llu", uli);
634 break;
635
636 case LSMT_SIGNED:
637 if (map->aux == sizeof(signed char)) {
638 li = (long long)*(signed char *)q;
639 } else {
640 if (map->aux == sizeof(int)) {
641 li = (long long)*(int *)q;
642 } else {
643 if (map->aux == sizeof(long))
644 li = (long long)*(long *)q;
645 else
646 li = *(long long *)q;
647 }
648 }
649 q = dbuf;
650 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), "%lld", li);
651 break;
652
653 case LSMT_STRING_CHAR_ARRAY:
654 case LSMT_STRING_PTR:
655 if (!js->offset) {
656 *buf++ = '\"';
657 len--;
658 }
659 break;
660
661 case LSMT_LIST:
662 *buf++ = '[';
663 len--;
664 if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
665 return LSJS_RESULT_ERROR;
666
667 /* add a stack level to handle parsing array members */
668
669 o = (struct lws_dll2_owner *)q;
670 p = j->dllpos = lws_dll2_get_head(o);
671
672 if (!j->dllpos) {
673 *buf++ = ']';
674 len--;
675 goto up;
676 }
677
678 n = j->idt;
679 j = &js->st[++js->sp];
680 j->idt = (char)(n + 2);
681 j->map = map->child_map;
682 j->map_entries = map->child_map_size;
683 j->size = map->aux;
684 j->subsequent = 0;
685 j->map_entry = 0;
686 lws_struct_pretty(js, &buf, &len);
687 *buf++ = '{';
688 len--;
689 lws_struct_pretty(js, &buf, &len);
690 if (p)
691 j->obj = ((char *)p) - j->map->ofs_clist;
692 else
693 j->obj = NULL;
694 continue;
695
696 case LSMT_CHILD_PTR:
697
698 if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
699 return LSJS_RESULT_ERROR;
700
701 /* add a stack level to handle parsing child members */
702
703 n = j->idt;
704 j = &js->st[++js->sp];
705 j->idt = (char)(n + 2);
706 j->map = map->child_map;
707 j->map_entries = map->child_map_size;
708 j->size = map->aux;
709 j->subsequent = 0;
710 j->map_entry = 0;
711 *buf++ = '{';
712 len--;
713 lws_struct_pretty(js, &buf, &len);
714 j->obj = q;
715
716 continue;
717
718 case LSMT_SCHEMA:
719 q = dbuf;
720 *buf++ = '{';
721 len--;
722 j = &js->st[++js->sp];
723 lws_struct_pretty(js, &buf, &len);
724 if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA)) {
725 budget = (unsigned int)lws_snprintf(dbuf, 15, "\"schema\":");
726 if (js->flags & LSSERJ_FLAG_PRETTY)
727 dbuf[budget++] = ' ';
728
729 budget += (unsigned int)lws_snprintf(dbuf + budget,
730 sizeof(dbuf) - budget,
731 "\"%s\"", map->colname);
732 }
733
734
735 if (js->sp != 1)
736 return LSJS_RESULT_ERROR;
737 j->map = map->child_map;
738 j->map_entries = map->child_map_size;
739 j->size = map->aux;
740 j->subsequent = 0;
741 j->map_entry = 0;
742 j->obj = js->st[js->sp - 1].obj;
743 j->dllpos = NULL;
744 if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA))
745 /* we're actually at the same level */
746 j->subsequent = 1;
747 j->idt = 1;
748 break;
749 default:
750 break;
751 }
752
753 switch (map->type) {
754 case LSMT_STRING_CHAR_ARRAY:
755 case LSMT_STRING_PTR:
756 /*
757 * This is a bit tricky... we have to escape the string
758 * which may 6x its length depending on what the
759 * contents are.
760 *
761 * We offset the unescaped string starting point first
762 */
763
764 q += js->offset;
765 budget = strlen(q); /* how much unescaped is left */
766
767 /*
768 * This is going to escape as much as it can fit, and
769 * let us know the amount of input that was consumed
770 * in "used".
771 */
772
773 lws_json_purify((char *)buf, q, (int)len, &used);
774 m = strlen((const char *)buf);
775 buf += m;
776 len -= m;
777 js->remaining = budget - (unsigned int)used;
778 js->offset = (unsigned int)used;
779 if (!js->remaining)
780 js->offset = 0;
781
782 break;
783 default:
784 q += js->offset;
785 budget -= js->remaining;
786
787 if (budget > len) {
788 js->remaining = budget - len;
789 js->offset = len;
790 budget = len;
791 } else {
792 js->remaining = 0;
793 js->offset = 0;
794 }
795
796 memcpy(buf, q, budget);
797 buf += budget;
798 *buf = '\0';
799 len -= budget;
800 break;
801 }
802
803
804
805 switch (map->type) {
806 case LSMT_STRING_CHAR_ARRAY:
807 case LSMT_STRING_PTR:
808 *buf++ = '\"';
809 len--;
810 break;
811 case LSMT_SCHEMA:
812 continue;
813 default:
814 break;
815 }
816
817 if (js->remaining)
818 continue;
819 up:
820 if (++j->map_entry < j->map_entries)
821 continue;
822
823 if (!js->sp)
824 continue;
825 js->sp--;
826 if (!js->sp) {
827 lws_struct_pretty(js, &buf, &len);
828 *buf++ = '}';
829 len--;
830 lws_struct_pretty(js, &buf, &len);
831 break;
832 }
833 js->offset = 0;
834 j = &js->st[js->sp];
835 map = &j->map[j->map_entry];
836
837 if (map->type == LSMT_CHILD_PTR) {
838 lws_struct_pretty(js, &buf, &len);
839 *buf++ = '}';
840 len--;
841
842 /* we have done the singular child pointer */
843
844 js->offset = 0;
845 goto up;
846 }
847
848 if (map->type != LSMT_LIST)
849 continue;
850 /*
851 * we are coming back up to an array map, it means we should
852 * advance to the next array member if there is one
853 */
854
855 lws_struct_pretty(js, &buf, &len);
856 *buf++ = '}';
857 len--;
858
859 p = j->dllpos = j->dllpos->next;
860 if (j->dllpos) {
861 /*
862 * there was another item in the array to do... let's
863 * move on to that and do it
864 */
865 *buf++ = ',';
866 len--;
867 lws_struct_pretty(js, &buf, &len);
868 js->offset = 0;
869 j = &js->st[++js->sp];
870 j->map_entry = 0;
871 map = &j->map[j->map_entry];
872
873 *buf++ = '{';
874 len--;
875 lws_struct_pretty(js, &buf, &len);
876
877 j->subsequent = 0;
878 j->obj = ((char *)p) - j->map->ofs_clist;
879 continue;
880 }
881
882 /* there are no further items in the array */
883
884 js->offset = 0;
885 lws_struct_pretty(js, &buf, &len);
886 *buf++ = ']';
887 len--;
888 goto up;
889 }
890
891 *written = olen - len;
892 *buf = '\0'; /* convenience, a NUL after the official end */
893
894 return LSJS_RESULT_FINISH;
895 }
896