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
30signed char
31lws_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
89matched:
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
123static int
124lws_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
141signed char
142lws_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
426chunk_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
466cleanup:
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
476static const char * schema[] = { "schema" };
477
478int
479lws_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
494lws_struct_serialize_t *
495lws_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
516void
517lws_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
527static void
528lws_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
542lws_struct_json_serialize_result_t
543lws_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;
819up:
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