• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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