1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 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];
36 	lejp_callback cb = map->lejp_cb;
37 
38 	if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1)
39 		return 0;
40 
41 	while (n--) {
42 		if (strcmp(ctx->buf, map->colname)) {
43 			map++;
44 			continue;
45 		}
46 
47 		a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size);
48 		if (!a->dest) {
49 			lwsl_err("%s: OOT\n", __func__);
50 
51 			return 1;
52 		}
53 		a->dest_len = map->aux;
54 		if (!ctx->pst_sp)
55 			a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]);
56 
57 		if (!cb)
58 			cb = lws_struct_default_lejp_cb;
59 
60 		lejp_parser_push(ctx, a->dest, &map->child_map[0].colname,
61 				 (uint8_t)map->child_map_size, cb);
62 		a->map_st[ctx->pst_sp] = map->child_map;
63 		a->map_entries_st[ctx->pst_sp] = map->child_map_size;
64 
65 		return 0;
66 	}
67 
68 	lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf);
69 
70 	return 1;
71 }
72 
73 static int
lws_struct_lejp_push(struct lejp_ctx * ctx,lws_struct_args_t * args,const lws_struct_map_t * map,uint8_t * ch)74 lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args,
75 		     const lws_struct_map_t *map, uint8_t *ch)
76 {
77 	lejp_callback cb = map->lejp_cb;
78 
79 	if (!cb)
80 		cb = lws_struct_default_lejp_cb;
81 
82 	lejp_parser_push(ctx, ch, (const char * const*)map->child_map,
83 			 (uint8_t)map->child_map_size, cb);
84 
85 	args->map_st[ctx->pst_sp] = map->child_map;
86 	args->map_entries_st[ctx->pst_sp] = map->child_map_size;
87 
88 	return 0;
89 }
90 
91 signed char
lws_struct_default_lejp_cb(struct lejp_ctx * ctx,char reason)92 lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason)
93 {
94 	lws_struct_args_t *args = (lws_struct_args_t *)ctx->user;
95 	const lws_struct_map_t *map, *pmap = NULL;
96 	uint8_t *ch;
97 	size_t n;
98 	char *u;
99 
100 	if (reason == LEJPCB_ARRAY_END) {
101 		lejp_parser_pop(ctx);
102 
103 		return 0;
104 	}
105 
106 	if (reason == LEJPCB_ARRAY_START) {
107 		map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
108 
109 		if (map->type == LSMT_LIST)
110 			lws_struct_lejp_push(ctx, args, map, NULL);
111 
112 		return 0;
113 	}
114 
115 	if (ctx->pst_sp)
116 		pmap = &args->map_st[ctx->pst_sp - 1]
117 	                 [ctx->pst[ctx->pst_sp - 1].path_match - 1];
118 	map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
119 	n = args->map_entries_st[ctx->pst_sp];
120 
121 	if (reason == LEJPCB_OBJECT_START) {
122 
123 		if (map->type != LSMT_CHILD_PTR) {
124 			ctx->pst[ctx->pst_sp].user = NULL;
125 
126 			return 0;
127 		}
128 		pmap = map;
129 
130 		lws_struct_lejp_push(ctx, args, map, NULL);
131 		map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
132 		n = args->map_entries_st[ctx->pst_sp];
133 	}
134 
135 	if (reason == LEJPCB_OBJECT_END && pmap && pmap->type == LSMT_CHILD_PTR)
136 		lejp_parser_pop(ctx);
137 
138 	if (map->type == LSMT_SCHEMA) {
139 
140 		while (n--) {
141 			if (strcmp(map->colname, ctx->buf)) {
142 				map++;
143 				continue;
144 			}
145 
146 			/* instantiate the correct toplevel object */
147 
148 			ch = lwsac_use_zero(&args->ac, map->aux,
149 					    args->ac_block_size);
150 			if (!ch) {
151 				lwsl_err("OOM\n");
152 
153 				return 1;
154 			}
155 
156 			lws_struct_lejp_push(ctx, args, map, ch);
157 
158 			return 0;
159 		}
160 		lwsl_notice("%s: unknown schema\n", __func__);
161 
162 		goto cleanup;
163 	}
164 
165 	if (!ctx->pst[ctx->pst_sp].user) {
166 		struct lws_dll2_owner *owner;
167 		struct lws_dll2 *list;
168 
169 		/* create list item object if none already */
170 
171 		if (!ctx->path_match || !pmap)
172 			return 0;
173 
174 		map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1];
175 		n = args->map_entries_st[ctx->pst_sp - 1];
176 
177 		if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR)
178 			return 1;
179 
180 		/* we need to create a child or array item object */
181 
182 		owner = (struct lws_dll2_owner *)
183 			(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
184 
185 		assert(pmap->aux);
186 
187 		/* instantiate one of the child objects */
188 
189 		ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac,
190 						pmap->aux, args->ac_block_size);
191 		if (!ctx->pst[ctx->pst_sp].user) {
192 			lwsl_err("OOM\n");
193 
194 			return 1;
195 		}
196 		lwsl_notice("%s: created child object size %d\n", __func__,
197 				(int)pmap->aux);
198 
199 		if (pmap->type == LSMT_LIST) {
200 			list = (struct lws_dll2 *)((char *)ctx->pst[ctx->pst_sp].user +
201 				map->ofs_clist);
202 
203 			lws_dll2_add_tail(list, owner);
204 		}
205 	}
206 
207 	if (!ctx->path_match)
208 		return 0;
209 
210 	if (reason == LEJPCB_VAL_STR_CHUNK) {
211 		lejp_collation_t *coll;
212 
213 		/* don't cache stuff we are going to ignore */
214 
215 		if (map->type == LSMT_STRING_CHAR_ARRAY &&
216 		    args->chunks_length >= map->aux)
217 			return 0;
218 
219 		coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll),
220 				      sizeof(*coll));
221 		if (!coll) {
222 			lwsl_err("%s: OOT\n", __func__);
223 
224 			return 1;
225 		}
226 		coll->chunks.prev = NULL;
227 		coll->chunks.next = NULL;
228 		coll->chunks.owner = NULL;
229 
230 		coll->len = ctx->npos;
231 		lws_dll2_add_tail(&coll->chunks, &args->chunks_owner);
232 
233 		memcpy(coll->buf, ctx->buf, ctx->npos);
234 
235 		args->chunks_length += ctx->npos;
236 
237 		return 0;
238 	}
239 
240 	if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT &&
241 	    reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE)
242 		return 0;
243 
244 	/* this is the end of the string */
245 
246 	if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) {
247 		void **pp = (void **)
248 			(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
249 
250 		*pp = ctx->pst[ctx->pst_sp].user;
251 	}
252 
253 	u = (char *)ctx->pst[ctx->pst_sp].user;
254 	if (!u)
255 		u = (char *)ctx->pst[ctx->pst_sp - 1].user;
256 
257 	{
258 		char **pp, *s;
259 		size_t lim, b;
260 		long long li;
261 
262 		switch (map->type) {
263 		case LSMT_SIGNED:
264 			if (map->aux == sizeof(signed char)) {
265 				signed char *pc;
266 				pc = (signed char *)(u + map->ofs);
267 				*pc = atoi(ctx->buf);
268 				break;
269 			}
270 			if (map->aux == sizeof(int)) {
271 				int *pi;
272 				pi = (int *)(u + map->ofs);
273 				*pi = atoi(ctx->buf);
274 				break;
275 			}
276 			if (map->aux == sizeof(long)) {
277 				long *pl;
278 				pl = (long *)(u + map->ofs);
279 				*pl = atol(ctx->buf);
280 			} else {
281 				long long *pll;
282 				pll = (long long *)(u + map->ofs);
283 				*pll = atoll(ctx->buf);
284 			}
285 			break;
286 
287 		case LSMT_UNSIGNED:
288 			if (map->aux == sizeof(unsigned char)) {
289 				unsigned char *pc;
290 				pc = (unsigned char *)(u + map->ofs);
291 				*pc = atoi(ctx->buf);
292 				break;
293 			}
294 			if (map->aux == sizeof(unsigned int)) {
295 				unsigned int *pi;
296 				pi = (unsigned int *)(u + map->ofs);
297 				*pi = atoi(ctx->buf);
298 				break;
299 			}
300 			if (map->aux == sizeof(unsigned long)) {
301 				unsigned long *pl;
302 				pl = (unsigned long *)(u + map->ofs);
303 				*pl = atol(ctx->buf);
304 			} else {
305 				unsigned long long *pll;
306 				pll = (unsigned long long *)(u + map->ofs);
307 				*pll = atoll(ctx->buf);
308 			}
309 			break;
310 
311 		case LSMT_BOOLEAN:
312 			li = reason == LEJPCB_VAL_TRUE;
313 			if (map->aux == sizeof(char)) {
314 				char *pc;
315 				pc = (char *)(u + map->ofs);
316 				*pc = (char)li;
317 				break;
318 			}
319 			if (map->aux == sizeof(int)) {
320 				int *pi;
321 				pi = (int *)(u + map->ofs);
322 				*pi = (int)li;
323 			} else {
324 				uint64_t *p64;
325 				p64 = (uint64_t *)(u + map->ofs);
326 				*p64 = li;
327 			}
328 			break;
329 
330 		case LSMT_STRING_CHAR_ARRAY:
331 			s = (char *)(u + map->ofs);
332 			lim = map->aux - 1;
333 			goto chunk_copy;
334 
335 		case LSMT_STRING_PTR:
336 			pp = (char **)(u + map->ofs);
337 			lim = args->chunks_length + ctx->npos;
338 			s = lwsac_use(&args->ac, lim + 1, args->ac_block_size);
339 			if (!s)
340 				goto cleanup;
341 			*pp = s;
342 
343 chunk_copy:
344 			s[lim] = '\0';
345 			/* copy up to lim from the string chunk ac first */
346 			lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
347 						args->chunks_owner.head) {
348 				lejp_collation_t *coll = (lejp_collation_t *)p;
349 
350 				if (lim) {
351 					b = coll->len;
352 					if (b > lim)
353 						b = lim;
354 					memcpy(s, coll->buf, b);
355 					s += b;
356 					lim -= b;
357 				}
358 			} lws_end_foreach_dll_safe(p, p1);
359 
360 			lwsac_free(&args->ac_chunks);
361 			args->chunks_owner.count = 0;
362 			args->chunks_owner.head = NULL;
363 			args->chunks_owner.tail = NULL;
364 
365 			if (lim) {
366 				b = ctx->npos;
367 				if (b > lim)
368 					b = lim;
369 				memcpy(s, ctx->buf, b);
370 				s[b] = '\0';
371 			}
372 			break;
373 		default:
374 			break;
375 		}
376 	}
377 
378 	if (args->cb)
379 		args->cb(args->dest, args->cb_arg);
380 
381 	return 0;
382 
383 cleanup:
384 	lwsl_notice("%s: cleanup\n", __func__);
385 	lwsac_free(&args->ac_chunks);
386 	args->chunks_owner.count = 0;
387 	args->chunks_owner.head = NULL;
388 	args->chunks_owner.tail = NULL;
389 
390 	return 1;
391 }
392 
393 static const char * schema[] = { "schema" };
394 
395 int
lws_struct_json_init_parse(struct lejp_ctx * ctx,lejp_callback cb,void * user)396 lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user)
397 {
398 	if (!cb)
399 		cb = lws_struct_schema_only_lejp_cb;
400 	lejp_construct(ctx, cb, user, schema, 1);
401 
402 	ctx->path_stride = sizeof(lws_struct_map_t);
403 
404 	return 0;
405 }
406 
407 lws_struct_serialize_t *
lws_struct_json_serialize_create(const lws_struct_map_t * map,size_t map_entries,int flags,const void * ptoplevel)408 lws_struct_json_serialize_create(const lws_struct_map_t *map,
409 				 size_t map_entries, int flags,
410 				 const void *ptoplevel)
411 {
412 	lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__);
413 	lws_struct_serialize_st_t *j;
414 
415 	if (!js)
416 		return NULL;
417 
418 	js->flags = flags;
419 
420 	j = &js->st[0];
421 	j->map = map;
422 	j->map_entries = map_entries;
423 	j->obj = ptoplevel;
424 	j->idt = 0;
425 
426 	return js;
427 }
428 
429 void
lws_struct_json_serialize_destroy(lws_struct_serialize_t ** pjs)430 lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs)
431 {
432 	if (!*pjs)
433 		return;
434 
435 	lws_free(*pjs);
436 
437 	*pjs = NULL;
438 }
439 
440 static void
lws_struct_pretty(lws_struct_serialize_t * js,uint8_t ** pbuf,size_t * plen)441 lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen)
442 {
443 	if (js->flags & LSSERJ_FLAG_PRETTY) {
444 		int n;
445 
446 		*(*pbuf)++ = '\n';
447 		(*plen)--;
448 		for (n = 0; n < js->st[js->sp].idt; n++) {
449 			*(*pbuf)++ = ' ';
450 			(*plen)--;
451 		}
452 	}
453 }
454 
455 lws_struct_json_serialize_result_t
lws_struct_json_serialize(lws_struct_serialize_t * js,uint8_t * buf,size_t len,size_t * written)456 lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf,
457 			  size_t len, size_t *written)
458 {
459 	lws_struct_serialize_st_t *j;
460 	const lws_struct_map_t *map;
461 	size_t budget = 0, olen = len, m;
462 	struct lws_dll2_owner *o;
463 	unsigned long long uli;
464 	const char *q;
465 	const void *p;
466 	char dbuf[72];
467 	long long li;
468 	int n, used;
469 
470 	*written = 0;
471 	*buf = '\0';
472 
473 	while (len > sizeof(dbuf) + 20) {
474 		j = &js->st[js->sp];
475 		map = &j->map[j->map_entry];
476 		q = j->obj + map->ofs;
477 
478 		/* early check if the entry should be elided */
479 
480 		switch (map->type) {
481 		case LSMT_STRING_PTR:
482 		case LSMT_CHILD_PTR:
483 			q = (char *)*(char **)q;
484 			if (!q)
485 				goto up;
486 			break;
487 
488 		case LSMT_LIST:
489 			o = (struct lws_dll2_owner *)q;
490 			p = j->dllpos = lws_dll2_get_head(o);
491 			if (!p)
492 				goto up;
493 			break;
494 
495 		default:
496 			break;
497 		}
498 
499 		if (j->subsequent) {
500 			*buf++ = ',';
501 			len--;
502 			lws_struct_pretty(js, &buf, &len);
503 		}
504 		j->subsequent = 1;
505 
506 		if (map->type != LSMT_SCHEMA && !js->offset) {
507 			n = lws_snprintf((char *)buf, len, "\"%s\":",
508 					    map->colname);
509 			buf += n;
510 			len -= n;
511 			if (js->flags & LSSERJ_FLAG_PRETTY) {
512 				*buf++ = ' ';
513 				len--;
514 			}
515 		}
516 
517 		switch (map->type) {
518 		case LSMT_BOOLEAN:
519 		case LSMT_UNSIGNED:
520 			if (map->aux == sizeof(char)) {
521 				uli = *(unsigned char *)q;
522 			} else {
523 				if (map->aux == sizeof(int)) {
524 					uli = *(unsigned int *)q;
525 				} else {
526 					if (map->aux == sizeof(long))
527 						uli = *(unsigned long *)q;
528 					else
529 						uli = *(unsigned long long *)q;
530 				}
531 			}
532 			q = dbuf;
533 
534 			if (map->type == LSMT_BOOLEAN) {
535 				budget = lws_snprintf(dbuf, sizeof(dbuf),
536 						"%s", uli ? "true" : "false");
537 			} else
538 				budget = lws_snprintf(dbuf, sizeof(dbuf),
539 						      "%llu", uli);
540 			break;
541 
542 		case LSMT_SIGNED:
543 			if (map->aux == sizeof(signed char)) {
544 				li = (long long)*(signed char *)q;
545 			} else {
546 				if (map->aux == sizeof(int)) {
547 					li = (long long)*(int *)q;
548 				} else {
549 					if (map->aux == sizeof(long))
550 						li = (long long)*(long *)q;
551 					else
552 						li = *(long long *)q;
553 				}
554 			}
555 			q = dbuf;
556 			budget = lws_snprintf(dbuf, sizeof(dbuf), "%lld", li);
557 			break;
558 
559 		case LSMT_STRING_CHAR_ARRAY:
560 		case LSMT_STRING_PTR:
561 			if (!js->offset) {
562 				*buf++ = '\"';
563 				len--;
564 			}
565 			break;
566 
567 		case LSMT_LIST:
568 			*buf++ = '[';
569 			len--;
570 			if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
571 				return LSJS_RESULT_ERROR;
572 
573 			/* add a stack level to handle parsing array members */
574 
575 			o = (struct lws_dll2_owner *)q;
576 			p = j->dllpos = lws_dll2_get_head(o);
577 
578 			if (!j->dllpos) {
579 				*buf++ = ']';
580 				len--;
581 				goto up;
582 			}
583 
584 			n = j->idt;
585 			j = &js->st[++js->sp];
586 			j->idt = n + 2;
587 			j->map = map->child_map;
588 			j->map_entries = map->child_map_size;
589 			j->size = map->aux;
590 			j->subsequent = 0;
591 			j->map_entry = 0;
592 			lws_struct_pretty(js, &buf, &len);
593 			*buf++ = '{';
594 			len--;
595 			lws_struct_pretty(js, &buf, &len);
596 			if (p)
597 				j->obj = ((char *)p) - j->map->ofs_clist;
598 			else
599 				j->obj = NULL;
600 			continue;
601 
602 		case LSMT_CHILD_PTR:
603 
604 			if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
605 				return LSJS_RESULT_ERROR;
606 
607 			/* add a stack level to handle parsing child members */
608 
609 			n = j->idt;
610 			j = &js->st[++js->sp];
611 			j->idt = n + 2;
612 			j->map = map->child_map;
613 			j->map_entries = map->child_map_size;
614 			j->size = map->aux;
615 			j->subsequent = 0;
616 			j->map_entry = 0;
617 			*buf++ = '{';
618 			len--;
619 			lws_struct_pretty(js, &buf, &len);
620 			j->obj = q;
621 
622 			continue;
623 
624 		case LSMT_SCHEMA:
625 			q = dbuf;
626 			*buf++ = '{';
627 			len--;
628 			j = &js->st[++js->sp];
629 			lws_struct_pretty(js, &buf, &len);
630 			budget = lws_snprintf(dbuf, 15, "\"schema\":");
631 			if (js->flags & LSSERJ_FLAG_PRETTY)
632 				dbuf[budget++] = ' ';
633 
634 			budget += lws_snprintf(dbuf + budget,
635 					       sizeof(dbuf) - budget,
636 					      "\"%s\"", map->colname);
637 
638 
639 			if (js->sp != 1)
640 				return LSJS_RESULT_ERROR;
641 			j->map = map->child_map;
642 			j->map_entries = map->child_map_size;
643 			j->size = map->aux;
644 			j->subsequent = 0;
645 			j->map_entry = 0;
646 			j->obj = js->st[js->sp - 1].obj;
647 			j->dllpos = NULL;
648 			/* we're actually at the same level */
649 			j->subsequent = 1;
650 			j->idt = 1;
651 			break;
652 		}
653 
654 		switch (map->type) {
655 		case LSMT_STRING_CHAR_ARRAY:
656 		case LSMT_STRING_PTR:
657 			/*
658 			 * This is a bit tricky... we have to escape the string
659 			 * which may 6x its length depending on what the
660 			 * contents are.
661 			 *
662 			 * We offset the unescaped string starting point first
663 			 */
664 
665 			q += js->offset;
666 			budget = strlen(q); /* how much unescaped is left */
667 
668 			/*
669 			 * This is going to escape as much as it can fit, and
670 			 * let us know the amount of input that was consumed
671 			 * in "used".
672 			 */
673 
674 			lws_json_purify((char *)buf, q, len, &used);
675 			m = strlen((const char *)buf);
676 			buf += m;
677 			len -= m;
678 			js->remaining = budget - used;
679 			js->offset = used;
680 			if (!js->remaining)
681 				js->offset = 0;
682 
683 			break;
684 		default:
685 			q += js->offset;
686 			budget -= js->remaining;
687 
688 			if (budget > len) {
689 				js->remaining = budget - len;
690 				js->offset = len;
691 				budget = len;
692 			} else {
693 				js->remaining = 0;
694 				js->offset = 0;
695 			}
696 
697 			memcpy(buf, q, budget);
698 			buf += budget;
699 			*buf = '\0';
700 			len -= budget;
701 			break;
702 		}
703 
704 
705 
706 		switch (map->type) {
707 		case LSMT_STRING_CHAR_ARRAY:
708 		case LSMT_STRING_PTR:
709 			*buf++ = '\"';
710 			len--;
711 			break;
712 		case LSMT_SCHEMA:
713 			continue;
714 		default:
715 			break;
716 		}
717 
718 		if (js->remaining)
719 			continue;
720 up:
721 		if (++j->map_entry < j->map_entries)
722 			continue;
723 
724 		if (!js->sp)
725 			continue;
726 		js->sp--;
727 		if (!js->sp) {
728 			lws_struct_pretty(js, &buf, &len);
729 			*buf++ = '}';
730 			len--;
731 			lws_struct_pretty(js, &buf, &len);
732 			break;
733 		}
734 		js->offset = 0;
735 		j = &js->st[js->sp];
736 		map = &j->map[j->map_entry];
737 
738 		if (map->type == LSMT_CHILD_PTR) {
739 			lws_struct_pretty(js, &buf, &len);
740 			*buf++ = '}';
741 			len--;
742 
743 			/* we have done the singular child pointer */
744 
745 			js->offset = 0;
746 			goto up;
747 		}
748 
749 		if (map->type != LSMT_LIST)
750 			continue;
751 		/*
752 		 * we are coming back up to an array map, it means we should
753 		 * advance to the next array member if there is one
754 		 */
755 
756 		lws_struct_pretty(js, &buf, &len);
757 		*buf++ = '}';
758 		len--;
759 
760 		p = j->dllpos = j->dllpos->next;
761 		if (j->dllpos) {
762 			/*
763 			 * there was another item in the array to do... let's
764 			 * move on to that and do it
765 			 */
766 			*buf++ = ',';
767 			len--;
768 			lws_struct_pretty(js, &buf, &len);
769 			js->offset = 0;
770 			j = &js->st[++js->sp];
771 			j->map_entry = 0;
772 			map = &j->map[j->map_entry];
773 
774 			*buf++ = '{';
775 			len--;
776 			lws_struct_pretty(js, &buf, &len);
777 
778 			j->subsequent = 0;
779 			j->obj = ((char *)p) - j->map->ofs_clist;
780 			continue;
781 		}
782 
783 		/* there are no further items in the array */
784 
785 		js->offset = 0;
786 		lws_struct_pretty(js, &buf, &len);
787 		*buf++ = ']';
788 		len--;
789 		goto up;
790 	}
791 
792 	*written = olen - len;
793 	*buf = '\0'; /* convenience, a NUL after the official end */
794 
795 	return LSJS_RESULT_FINISH;
796 }
797