1/*	$OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $	*/
2
3/*-
4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5 *		 2011, 2012, 2013, 2015, 2016, 2017
6 *	mirabilos <m@mirbsd.org>
7 *
8 * Provided that these terms and disclaimer and all copyright notices
9 * are retained or reproduced in an accompanying document, permission
10 * is granted to deal in this work without restriction, including un-
11 * limited rights to use, publicly perform, distribute, sell, modify,
12 * merge, give away, or sublicence.
13 *
14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 * the utmost extent permitted by applicable law, neither express nor
16 * implied; without malicious intent or gross negligence. In no event
17 * may a licensor, author or contributor be held liable for indirect,
18 * direct, other damage, loss, or other issues arising in any way out
19 * of dealing in the work, even if advised of the possibility of such
20 * damage or existence of a defect, except proven that it results out
21 * of said person's immediate fault when using the work as intended.
22 */
23
24#include "sh.h"
25
26__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.95 2018/01/14 00:03:05 tg Exp $");
27
28#define INDENT	8
29
30static void ptree(struct op *, int, struct shf *);
31static void pioact(struct shf *, struct ioword *);
32static const char *wdvarput(struct shf *, const char *, int, int);
33static void vfptreef(struct shf *, int, const char *, va_list);
34static struct ioword **iocopy(struct ioword **, Area *);
35static void iofree(struct ioword **, Area *);
36
37/* "foo& ; bar" and "foo |& ; bar" are invalid */
38static bool prevent_semicolon;
39
40static const char Telif_pT[] = "elif %T";
41
42/*
43 * print a command tree
44 */
45static void
46ptree(struct op *t, int indent, struct shf *shf)
47{
48	const char **w;
49	struct ioword **ioact;
50	struct op *t1;
51	int i;
52	const char *ccp;
53
54 Chain:
55	if (t == NULL)
56		return;
57	switch (t->type) {
58	case TCOM:
59		prevent_semicolon = false;
60		/* special-case 'var=<<EOF' (cf. exec.c:execute) */
61		if (t->args &&
62		    /* we have zero arguments, i.e. no program to run */
63		    t->args[0] == NULL &&
64		    /* we have exactly one variable assignment */
65		    t->vars[0] != NULL && t->vars[1] == NULL &&
66		    /* we have exactly one I/O redirection */
67		    t->ioact != NULL && t->ioact[0] != NULL &&
68		    t->ioact[1] == NULL &&
69		    /* of type "here document" (or "here string") */
70		    (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
71		    /* the variable assignment begins with a valid varname */
72		    (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
73		    /* and has no right-hand side (i.e. "varname=") */
74		    ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
75		    /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
76		    ccp[3] == '=' && ccp[4] == EOS))) {
77			fptreef(shf, indent, Tf_S, t->vars[0]);
78			break;
79		}
80
81		if (t->vars) {
82			w = (const char **)t->vars;
83			while (*w)
84				fptreef(shf, indent, Tf_S_, *w++);
85		} else
86			shf_puts("#no-vars# ", shf);
87		if (t->args) {
88			w = t->args;
89			if (*w && **w == CHAR) {
90				char *cp = wdstrip(*w++, WDS_TPUTS);
91
92				if (valid_alias_name(cp))
93					shf_putc('\\', shf);
94				shf_puts(cp, shf);
95				shf_putc(' ', shf);
96				afree(cp, ATEMP);
97			}
98			while (*w)
99				fptreef(shf, indent, Tf_S_, *w++);
100		} else
101			shf_puts("#no-args# ", shf);
102		break;
103	case TEXEC:
104		t = t->left;
105		goto Chain;
106	case TPAREN:
107		fptreef(shf, indent + 2, "( %T) ", t->left);
108		break;
109	case TPIPE:
110		fptreef(shf, indent, "%T| ", t->left);
111		t = t->right;
112		goto Chain;
113	case TLIST:
114		fptreef(shf, indent, "%T%;", t->left);
115		t = t->right;
116		goto Chain;
117	case TOR:
118	case TAND:
119		fptreef(shf, indent, "%T%s %T",
120		    t->left, (t->type == TOR) ? "||" : "&&", t->right);
121		break;
122	case TBANG:
123		shf_puts("! ", shf);
124		prevent_semicolon = false;
125		t = t->right;
126		goto Chain;
127	case TDBRACKET:
128		w = t->args;
129		shf_puts("[[", shf);
130		while (*w)
131			fptreef(shf, indent, Tf__S, *w++);
132		shf_puts(" ]] ", shf);
133		break;
134	case TSELECT:
135	case TFOR:
136		fptreef(shf, indent, "%s %s ",
137		    (t->type == TFOR) ? "for" : Tselect, t->str);
138		if (t->vars != NULL) {
139			shf_puts("in ", shf);
140			w = (const char **)t->vars;
141			while (*w)
142				fptreef(shf, indent, Tf_S_, *w++);
143			fptreef(shf, indent, Tft_end);
144		}
145		fptreef(shf, indent + INDENT, "do%N%T", t->left);
146		fptreef(shf, indent, "%;done ");
147		break;
148	case TCASE:
149		fptreef(shf, indent, "case %S in", t->str);
150		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
151			fptreef(shf, indent, "%N(");
152			w = (const char **)t1->vars;
153			while (*w) {
154				fptreef(shf, indent, "%S%c", *w,
155				    (w[1] != NULL) ? '|' : ')');
156				++w;
157			}
158			fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
159			    t1->u.charflag);
160		}
161		fptreef(shf, indent, "%Nesac ");
162		break;
163	case TELIF:
164		internal_errorf(TELIF_unexpected);
165		/* FALLTHROUGH */
166	case TIF:
167		i = 2;
168		t1 = t;
169		goto process_TIF;
170		do {
171			t1 = t1->right;
172			i = 0;
173			fptreef(shf, indent, Tft_end);
174 process_TIF:
175			/* 5 == strlen("elif ") */
176			fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
177			t1 = t1->right;
178			if (t1->left != NULL) {
179				fptreef(shf, indent, Tft_end);
180				fptreef(shf, indent + INDENT, "%s%N%T",
181				    "then", t1->left);
182			}
183		} while (t1->right && t1->right->type == TELIF);
184		if (t1->right != NULL) {
185			fptreef(shf, indent, Tft_end);
186			fptreef(shf, indent + INDENT, "%s%N%T",
187			    "else", t1->right);
188		}
189		fptreef(shf, indent, "%;fi ");
190		break;
191	case TWHILE:
192	case TUNTIL:
193		/* 6 == strlen("while "/"until ") */
194		fptreef(shf, indent + 6, Tf_s_T,
195		    (t->type == TWHILE) ? "while" : "until",
196		    t->left);
197		fptreef(shf, indent, Tft_end);
198		fptreef(shf, indent + INDENT, "do%N%T", t->right);
199		fptreef(shf, indent, "%;done ");
200		break;
201	case TBRACE:
202		fptreef(shf, indent + INDENT, "{%N%T", t->left);
203		fptreef(shf, indent, "%;} ");
204		break;
205	case TCOPROC:
206		fptreef(shf, indent, "%T|& ", t->left);
207		prevent_semicolon = true;
208		break;
209	case TASYNC:
210		fptreef(shf, indent, "%T& ", t->left);
211		prevent_semicolon = true;
212		break;
213	case TFUNCT:
214		fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
215		break;
216	case TTIME:
217		fptreef(shf, indent, Tf_s_T, Ttime, t->left);
218		break;
219	default:
220		shf_puts("<botch>", shf);
221		prevent_semicolon = false;
222		break;
223	}
224	if ((ioact = t->ioact) != NULL) {
225		bool need_nl = false;
226
227		while (*ioact != NULL)
228			pioact(shf, *ioact++);
229		/* Print here documents after everything else... */
230		ioact = t->ioact;
231		while (*ioact != NULL) {
232			struct ioword *iop = *ioact++;
233
234			/* heredoc is NULL when tracing (set -x) */
235			if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
236			    iop->heredoc) {
237				shf_putc('\n', shf);
238				shf_puts(iop->heredoc, shf);
239				fptreef(shf, indent, Tf_s,
240				    evalstr(iop->delim, 0));
241				need_nl = true;
242			}
243		}
244		/*
245		 * Last delimiter must be followed by a newline (this
246		 * often leads to an extra blank line, but it's not
247		 * worth worrying about)
248		 */
249		if (need_nl) {
250			shf_putc('\n', shf);
251			prevent_semicolon = true;
252		}
253	}
254}
255
256static void
257pioact(struct shf *shf, struct ioword *iop)
258{
259	unsigned short flag = iop->ioflag;
260	unsigned short type = flag & IOTYPE;
261	short expected;
262
263	expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
264	    (type == IOCAT || type == IOWRITE) ? 1 :
265	    (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
266	    iop->unit + 1;
267	if (iop->unit != expected)
268		shf_fprintf(shf, Tf_d, (int)iop->unit);
269
270	switch (type) {
271	case IOREAD:
272		shf_putc('<', shf);
273		break;
274	case IOHERE:
275		shf_puts("<<", shf);
276		if (flag & IOSKIP)
277			shf_putc('-', shf);
278		else if (flag & IOHERESTR)
279			shf_putc('<', shf);
280		break;
281	case IOCAT:
282		shf_puts(">>", shf);
283		break;
284	case IOWRITE:
285		shf_putc('>', shf);
286		if (flag & IOCLOB)
287			shf_putc('|', shf);
288		break;
289	case IORDWR:
290		shf_puts("<>", shf);
291		break;
292	case IODUP:
293		shf_puts(flag & IORDUP ? "<&" : ">&", shf);
294		break;
295	}
296	/* name/delim are NULL when printing syntax errors */
297	if (type == IOHERE) {
298		if (iop->delim && !(iop->ioflag & IONDELIM))
299			wdvarput(shf, iop->delim, 0, WDS_TPUTS);
300	} else if (iop->ioname) {
301		if (flag & IONAMEXP)
302			print_value_quoted(shf, iop->ioname);
303		else
304			wdvarput(shf, iop->ioname, 0, WDS_TPUTS);
305	}
306	shf_putc(' ', shf);
307	prevent_semicolon = false;
308}
309
310/* variant of fputs for ptreef and wdstrip */
311static const char *
312wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
313{
314	int c;
315	const char *cs;
316
317	/*-
318	 * problems:
319	 *	`...` -> $(...)
320	 *	'foo' -> "foo"
321	 *	x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
322	 *	x${foo:-'hi'} -> x${foo:-hi}
323	 * could change encoding to:
324	 *	OQUOTE ["'] ... CQUOTE ["']
325	 *	COMSUB [(`] ...\0	(handle $ ` \ and maybe " in `...` case)
326	 */
327	while (/* CONSTCOND */ 1)
328		switch (*wp++) {
329		case EOS:
330			return (--wp);
331		case ADELIM:
332			if (ord(*wp) == ORD(/*{*/ '}')) {
333				++wp;
334				goto wdvarput_csubst;
335			}
336			/* FALLTHROUGH */
337		case CHAR:
338			c = ord(*wp++);
339			shf_putc(c, shf);
340			break;
341		case QCHAR:
342			c = ord(*wp++);
343			if (opmode & WDS_TPUTS)
344				switch (c) {
345				case ORD('\n'):
346					if (quotelevel == 0) {
347						c = ORD('\'');
348						shf_putc(c, shf);
349						shf_putc(ORD('\n'), shf);
350					}
351					break;
352				default:
353					if (quotelevel == 0)
354						/* FALLTHROUGH */
355				case ORD('"'):
356				case ORD('`'):
357				case ORD('$'):
358				case ORD('\\'):
359					  shf_putc(ORD('\\'), shf);
360					break;
361				}
362			shf_putc(c, shf);
363			break;
364		case COMASUB:
365		case COMSUB:
366			shf_puts("$(", shf);
367			cs = ")";
368			if (ord(*wp) == ORD('(' /*)*/))
369				shf_putc(' ', shf);
370 pSUB:
371			while ((c = *wp++) != 0)
372				shf_putc(c, shf);
373			shf_puts(cs, shf);
374			break;
375		case FUNASUB:
376		case FUNSUB:
377			c = ORD(' ');
378			if (0)
379				/* FALLTHROUGH */
380		case VALSUB:
381			  c = ORD('|');
382			shf_putc('$', shf);
383			shf_putc('{', shf);
384			shf_putc(c, shf);
385			cs = ";}";
386			goto pSUB;
387		case EXPRSUB:
388			shf_puts("$((", shf);
389			cs = "))";
390			goto pSUB;
391		case OQUOTE:
392			if (opmode & WDS_TPUTS) {
393				quotelevel++;
394				shf_putc('"', shf);
395			}
396			break;
397		case CQUOTE:
398			if (opmode & WDS_TPUTS) {
399				if (quotelevel)
400					quotelevel--;
401				shf_putc('"', shf);
402			}
403			break;
404		case OSUBST:
405			shf_putc('$', shf);
406			if (ord(*wp++) == ORD('{'))
407				shf_putc('{', shf);
408			while ((c = *wp++) != 0)
409				shf_putc(c, shf);
410			wp = wdvarput(shf, wp, 0, opmode);
411			break;
412		case CSUBST:
413			if (ord(*wp++) == ORD('}')) {
414 wdvarput_csubst:
415				shf_putc('}', shf);
416			}
417			return (wp);
418		case OPAT:
419			shf_putchar(*wp++, shf);
420			shf_putc('(', shf);
421			break;
422		case SPAT:
423			c = ORD('|');
424			if (0)
425				/* FALLTHROUGH */
426		case CPAT:
427			  c = ORD(/*(*/ ')');
428			shf_putc(c, shf);
429			break;
430		}
431}
432
433/*
434 * this is the _only_ way to reliably handle
435 * variable args with an ANSI compiler
436 */
437/* VARARGS */
438void
439fptreef(struct shf *shf, int indent, const char *fmt, ...)
440{
441	va_list va;
442
443	va_start(va, fmt);
444	vfptreef(shf, indent, fmt, va);
445	va_end(va);
446}
447
448/* VARARGS */
449char *
450snptreef(char *s, ssize_t n, const char *fmt, ...)
451{
452	va_list va;
453	struct shf shf;
454
455	shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
456
457	va_start(va, fmt);
458	vfptreef(&shf, 0, fmt, va);
459	va_end(va);
460
461	/* shf_sclose NUL terminates */
462	return (shf_sclose(&shf));
463}
464
465static void
466vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
467{
468	int c;
469
470	while ((c = ord(*fmt++))) {
471		if (c == '%') {
472			switch ((c = ord(*fmt++))) {
473			case ORD('c'):
474				/* character (octet, probably) */
475				shf_putchar(va_arg(va, int), shf);
476				break;
477			case ORD('s'):
478				/* string */
479				shf_puts(va_arg(va, char *), shf);
480				break;
481			case ORD('S'):
482				/* word */
483				wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
484				break;
485			case ORD('d'):
486				/* signed decimal */
487				shf_fprintf(shf, Tf_d, va_arg(va, int));
488				break;
489			case ORD('u'):
490				/* unsigned decimal */
491				shf_fprintf(shf, "%u", va_arg(va, unsigned int));
492				break;
493			case ORD('T'):
494				/* format tree */
495				ptree(va_arg(va, struct op *), indent, shf);
496				goto dont_trash_prevent_semicolon;
497			case ORD(';'):
498				/* newline or ; */
499			case ORD('N'):
500				/* newline or space */
501				if (shf->flags & SHF_STRING) {
502					if ((unsigned int)c == ORD(';') &&
503					    !prevent_semicolon)
504						shf_putc(';', shf);
505					shf_putc(' ', shf);
506				} else {
507					int i;
508
509					shf_putc('\n', shf);
510					i = indent;
511					while (i >= 8) {
512						shf_putc('\t', shf);
513						i -= 8;
514					}
515					while (i--)
516						shf_putc(' ', shf);
517				}
518				break;
519			case ORD('R'):
520				/* I/O redirection */
521				pioact(shf, va_arg(va, struct ioword *));
522				break;
523			default:
524				shf_putc(c, shf);
525				break;
526			}
527		} else
528			shf_putc(c, shf);
529		prevent_semicolon = false;
530 dont_trash_prevent_semicolon:
531		;
532	}
533}
534
535/*
536 * copy tree (for function definition)
537 */
538struct op *
539tcopy(struct op *t, Area *ap)
540{
541	struct op *r;
542	const char **tw;
543	char **rw;
544
545	if (t == NULL)
546		return (NULL);
547
548	r = alloc(sizeof(struct op), ap);
549
550	r->type = t->type;
551	r->u.evalflags = t->u.evalflags;
552
553	if (t->type == TCASE)
554		r->str = wdcopy(t->str, ap);
555	else
556		strdupx(r->str, t->str, ap);
557
558	if (t->vars == NULL)
559		r->vars = NULL;
560	else {
561		tw = (const char **)t->vars;
562		while (*tw)
563			++tw;
564		rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
565		    sizeof(*tw), ap);
566		tw = (const char **)t->vars;
567		while (*tw)
568			*rw++ = wdcopy(*tw++, ap);
569		*rw = NULL;
570	}
571
572	if (t->args == NULL)
573		r->args = NULL;
574	else {
575		tw = t->args;
576		while (*tw)
577			++tw;
578		r->args = (const char **)(rw = alloc2(tw - t->args + 1,
579		    sizeof(*tw), ap));
580		tw = t->args;
581		while (*tw)
582			*rw++ = wdcopy(*tw++, ap);
583		*rw = NULL;
584	}
585
586	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
587
588	r->left = tcopy(t->left, ap);
589	r->right = tcopy(t->right, ap);
590	r->lineno = t->lineno;
591
592	return (r);
593}
594
595char *
596wdcopy(const char *wp, Area *ap)
597{
598	size_t len;
599
600	len = wdscan(wp, EOS) - wp;
601	return (memcpy(alloc(len, ap), wp, len));
602}
603
604/* return the position of prefix c in wp plus 1 */
605const char *
606wdscan(const char *wp, int c)
607{
608	int nest = 0;
609
610	while (/* CONSTCOND */ 1)
611		switch (*wp++) {
612		case EOS:
613			return (wp);
614		case ADELIM:
615			if (c == ADELIM && nest == 0)
616				return (wp + 1);
617			if (ord(*wp) == ORD(/*{*/ '}'))
618				goto wdscan_csubst;
619			/* FALLTHROUGH */
620		case CHAR:
621		case QCHAR:
622			wp++;
623			break;
624		case COMASUB:
625		case COMSUB:
626		case FUNASUB:
627		case FUNSUB:
628		case VALSUB:
629		case EXPRSUB:
630			while (*wp++ != 0)
631				;
632			break;
633		case OQUOTE:
634		case CQUOTE:
635			break;
636		case OSUBST:
637			nest++;
638			while (*wp++ != '\0')
639				;
640			break;
641		case CSUBST:
642 wdscan_csubst:
643			wp++;
644			if (c == CSUBST && nest == 0)
645				return (wp);
646			nest--;
647			break;
648		case OPAT:
649			nest++;
650			wp++;
651			break;
652		case SPAT:
653		case CPAT:
654			if (c == wp[-1] && nest == 0)
655				return (wp);
656			if (wp[-1] == CPAT)
657				nest--;
658			break;
659		default:
660			internal_warningf(
661			    "wdscan: unknown char 0x%X (carrying on)",
662			    (unsigned char)wp[-1]);
663		}
664}
665
666/*
667 * return a copy of wp without any of the mark up characters and with
668 * quote characters (" ' \) stripped. (string is allocated from ATEMP)
669 */
670char *
671wdstrip(const char *wp, int opmode)
672{
673	struct shf shf;
674
675	shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
676	wdvarput(&shf, wp, 0, opmode);
677	/* shf_sclose NUL terminates */
678	return (shf_sclose(&shf));
679}
680
681static struct ioword **
682iocopy(struct ioword **iow, Area *ap)
683{
684	struct ioword **ior;
685	int i;
686
687	ior = iow;
688	while (*ior)
689		++ior;
690	ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
691
692	for (i = 0; iow[i] != NULL; i++) {
693		struct ioword *p, *q;
694
695		p = iow[i];
696		q = alloc(sizeof(struct ioword), ap);
697		ior[i] = q;
698		*q = *p;
699		if (p->ioname != NULL)
700			q->ioname = wdcopy(p->ioname, ap);
701		if (p->delim != NULL)
702			q->delim = wdcopy(p->delim, ap);
703		if (p->heredoc != NULL)
704			strdupx(q->heredoc, p->heredoc, ap);
705	}
706	ior[i] = NULL;
707
708	return (ior);
709}
710
711/*
712 * free tree (for function definition)
713 */
714void
715tfree(struct op *t, Area *ap)
716{
717	char **w;
718
719	if (t == NULL)
720		return;
721
722	afree(t->str, ap);
723
724	if (t->vars != NULL) {
725		for (w = t->vars; *w != NULL; w++)
726			afree(*w, ap);
727		afree(t->vars, ap);
728	}
729
730	if (t->args != NULL) {
731		/*XXX we assume the caller is right */
732		union mksh_ccphack cw;
733
734		cw.ro = t->args;
735		for (w = cw.rw; *w != NULL; w++)
736			afree(*w, ap);
737		afree(t->args, ap);
738	}
739
740	if (t->ioact != NULL)
741		iofree(t->ioact, ap);
742
743	tfree(t->left, ap);
744	tfree(t->right, ap);
745
746	afree(t, ap);
747}
748
749static void
750iofree(struct ioword **iow, Area *ap)
751{
752	struct ioword **iop;
753	struct ioword *p;
754
755	iop = iow;
756	while ((p = *iop++) != NULL) {
757		afree(p->ioname, ap);
758		afree(p->delim, ap);
759		afree(p->heredoc, ap);
760		afree(p, ap);
761	}
762	afree(iow, ap);
763}
764
765void
766fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
767{
768	if (isksh)
769		fptreef(shf, i, "%s %s %T", Tfunction, k, v);
770	else if (ktsearch(&keywords, k, hash(k)))
771		fptreef(shf, i, "%s %s() %T", Tfunction, k, v);
772	else
773		fptreef(shf, i, "%s() %T", k, v);
774}
775
776
777/* for jobs.c */
778void
779vistree(char *dst, size_t sz, struct op *t)
780{
781	unsigned int c;
782	char *cp, *buf;
783	size_t n;
784
785	buf = alloc(sz + 16, ATEMP);
786	snptreef(buf, sz + 16, Tf_T, t);
787	cp = buf;
788 vist_loop:
789	if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
790		if (c == 0 || n >= sz)
791			/* NUL or not enough free space */
792			goto vist_out;
793		/* copy multibyte char */
794		sz -= n;
795		while (n--)
796			*dst++ = *cp++;
797		goto vist_loop;
798	}
799	if (--sz == 0 || (c = ord(*cp++)) == 0)
800		/* NUL or not enough free space */
801		goto vist_out;
802	if (ksh_isctrl(c)) {
803		/* C0 or C1 control character or DEL */
804		if (--sz == 0)
805			/* not enough free space for two chars */
806			goto vist_out;
807		*dst++ = '^';
808		c = ksh_unctrl(c);
809	} else if (UTFMODE && rtt2asc(c) > 0x7F) {
810		/* better not try to display broken multibyte chars */
811		/* also go easy on the Unicode: no U+FFFD here */
812		c = ORD('?');
813	}
814	*dst++ = c;
815	goto vist_loop;
816
817 vist_out:
818	*dst = '\0';
819	afree(buf, ATEMP);
820}
821
822#ifdef DEBUG
823void
824dumpchar(struct shf *shf, int c)
825{
826	if (ksh_isctrl(c)) {
827		/* C0 or C1 control character or DEL */
828		shf_putc('^', shf);
829		c = ksh_unctrl(c);
830	}
831	shf_putc(c, shf);
832}
833
834/* see: wdvarput */
835static const char *
836dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
837{
838	int c;
839
840	while (/* CONSTCOND */ 1) {
841		switch(*wp++) {
842		case EOS:
843			shf_puts("EOS", shf);
844			return (--wp);
845		case ADELIM:
846			if (ord(*wp) == ORD(/*{*/ '}')) {
847				shf_puts(/*{*/ "]ADELIM(})", shf);
848				return (wp + 1);
849			}
850			shf_puts("ADELIM=", shf);
851			if (0)
852				/* FALLTHROUGH */
853		case CHAR:
854			  shf_puts("CHAR=", shf);
855			dumpchar(shf, *wp++);
856			break;
857		case QCHAR:
858			shf_puts("QCHAR<", shf);
859			c = ord(*wp++);
860			if (quotelevel == 0 || c == ORD('"') ||
861			    c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE))
862				shf_putc('\\', shf);
863			dumpchar(shf, c);
864			goto closeandout;
865		case COMASUB:
866			shf_puts("COMASUB<", shf);
867			goto dumpsub;
868		case COMSUB:
869			shf_puts("COMSUB<", shf);
870 dumpsub:
871			while ((c = *wp++) != 0)
872				dumpchar(shf, c);
873 closeandout:
874			shf_putc('>', shf);
875			break;
876		case FUNASUB:
877			shf_puts("FUNASUB<", shf);
878			goto dumpsub;
879		case FUNSUB:
880			shf_puts("FUNSUB<", shf);
881			goto dumpsub;
882		case VALSUB:
883			shf_puts("VALSUB<", shf);
884			goto dumpsub;
885		case EXPRSUB:
886			shf_puts("EXPRSUB<", shf);
887			goto dumpsub;
888		case OQUOTE:
889			shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
890			break;
891		case CQUOTE:
892			shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
893			if (quotelevel)
894				quotelevel--;
895			else
896				shf_puts("(err)", shf);
897			break;
898		case OSUBST:
899			shf_puts("OSUBST(", shf);
900			dumpchar(shf, *wp++);
901			shf_puts(")[", shf);
902			while ((c = *wp++) != 0)
903				dumpchar(shf, c);
904			shf_putc('|', shf);
905			wp = dumpwdvar_i(shf, wp, 0);
906			break;
907		case CSUBST:
908			shf_puts("]CSUBST(", shf);
909			dumpchar(shf, *wp++);
910			shf_putc(')', shf);
911			return (wp);
912		case OPAT:
913			shf_puts("OPAT=", shf);
914			dumpchar(shf, *wp++);
915			break;
916		case SPAT:
917			shf_puts("SPAT", shf);
918			break;
919		case CPAT:
920			shf_puts("CPAT", shf);
921			break;
922		default:
923			shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
924			break;
925		}
926		shf_putc(' ', shf);
927	}
928}
929void
930dumpwdvar(struct shf *shf, const char *wp)
931{
932	dumpwdvar_i(shf, wp, 0);
933}
934
935void
936dumpioact(struct shf *shf, struct op *t)
937{
938	struct ioword **ioact, *iop;
939
940	if ((ioact = t->ioact) == NULL)
941		return;
942
943	shf_puts("{IOACT", shf);
944	while ((iop = *ioact++) != NULL) {
945		unsigned short type = iop->ioflag & IOTYPE;
946#define DT(x) case x: shf_puts(#x, shf); break;
947#define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
948
949		shf_putc(';', shf);
950		switch (type) {
951		DT(IOREAD)
952		DT(IOWRITE)
953		DT(IORDWR)
954		DT(IOHERE)
955		DT(IOCAT)
956		DT(IODUP)
957		default:
958			shf_fprintf(shf, "unk%d", type);
959		}
960		DB(IOEVAL)
961		DB(IOSKIP)
962		DB(IOCLOB)
963		DB(IORDUP)
964		DB(IONAMEXP)
965		DB(IOBASH)
966		DB(IOHERESTR)
967		DB(IONDELIM)
968		shf_fprintf(shf, ",unit=%d", (int)iop->unit);
969		if (iop->delim && !(iop->ioflag & IONDELIM)) {
970			shf_puts(",delim<", shf);
971			dumpwdvar(shf, iop->delim);
972			shf_putc('>', shf);
973		}
974		if (iop->ioname) {
975			if (iop->ioflag & IONAMEXP) {
976				shf_puts(",name=", shf);
977				print_value_quoted(shf, iop->ioname);
978			} else {
979				shf_puts(",name<", shf);
980				dumpwdvar(shf, iop->ioname);
981				shf_putc('>', shf);
982			}
983		}
984		if (iop->heredoc) {
985			shf_puts(",heredoc=", shf);
986			print_value_quoted(shf, iop->heredoc);
987		}
988#undef DT
989#undef DB
990	}
991	shf_putc('}', shf);
992}
993
994void
995dumptree(struct shf *shf, struct op *t)
996{
997	int i, j;
998	const char **w, *name;
999	struct op *t1;
1000	static int nesting;
1001
1002	for (i = 0; i < nesting; ++i)
1003		shf_putc('\t', shf);
1004	++nesting;
1005	shf_puts("{tree:" /*}*/, shf);
1006	if (t == NULL) {
1007		name = "(null)";
1008		goto out;
1009	}
1010	dumpioact(shf, t);
1011	switch (t->type) {
1012#define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
1013
1014	OPEN(TCOM)
1015		if (t->vars) {
1016			i = 0;
1017			w = (const char **)t->vars;
1018			while (*w) {
1019				shf_putc('\n', shf);
1020				for (j = 0; j < nesting; ++j)
1021					shf_putc('\t', shf);
1022				shf_fprintf(shf, " var%d<", i++);
1023				dumpwdvar(shf, *w++);
1024				shf_putc('>', shf);
1025			}
1026		} else
1027			shf_puts(" #no-vars#", shf);
1028		if (t->args) {
1029			i = 0;
1030			w = t->args;
1031			while (*w) {
1032				shf_putc('\n', shf);
1033				for (j = 0; j < nesting; ++j)
1034					shf_putc('\t', shf);
1035				shf_fprintf(shf, " arg%d<", i++);
1036				dumpwdvar(shf, *w++);
1037				shf_putc('>', shf);
1038			}
1039		} else
1040			shf_puts(" #no-args#", shf);
1041		break;
1042	OPEN(TEXEC)
1043 dumpleftandout:
1044		t = t->left;
1045 dumpandout:
1046		shf_putc('\n', shf);
1047		dumptree(shf, t);
1048		break;
1049	OPEN(TPAREN)
1050		goto dumpleftandout;
1051	OPEN(TPIPE)
1052 dumpleftmidrightandout:
1053		shf_putc('\n', shf);
1054		dumptree(shf, t->left);
1055/* middumprightandout: (unused) */
1056		shf_fprintf(shf, "/%s:", name);
1057 dumprightandout:
1058		t = t->right;
1059		goto dumpandout;
1060	OPEN(TLIST)
1061		goto dumpleftmidrightandout;
1062	OPEN(TOR)
1063		goto dumpleftmidrightandout;
1064	OPEN(TAND)
1065		goto dumpleftmidrightandout;
1066	OPEN(TBANG)
1067		goto dumprightandout;
1068	OPEN(TDBRACKET)
1069		i = 0;
1070		w = t->args;
1071		while (*w) {
1072			shf_putc('\n', shf);
1073			for (j = 0; j < nesting; ++j)
1074				shf_putc('\t', shf);
1075			shf_fprintf(shf, " arg%d<", i++);
1076			dumpwdvar(shf, *w++);
1077			shf_putc('>', shf);
1078		}
1079		break;
1080	OPEN(TFOR)
1081 dumpfor:
1082		shf_fprintf(shf, " str<%s>", t->str);
1083		if (t->vars != NULL) {
1084			i = 0;
1085			w = (const char **)t->vars;
1086			while (*w) {
1087				shf_putc('\n', shf);
1088				for (j = 0; j < nesting; ++j)
1089					shf_putc('\t', shf);
1090				shf_fprintf(shf, " var%d<", i++);
1091				dumpwdvar(shf, *w++);
1092				shf_putc('>', shf);
1093			}
1094		}
1095		goto dumpleftandout;
1096	OPEN(TSELECT)
1097		goto dumpfor;
1098	OPEN(TCASE)
1099		shf_fprintf(shf, " str<%s>", t->str);
1100		i = 0;
1101		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1102			shf_putc('\n', shf);
1103			for (j = 0; j < nesting; ++j)
1104				shf_putc('\t', shf);
1105			shf_fprintf(shf, " sub%d[(", i);
1106			w = (const char **)t1->vars;
1107			while (*w) {
1108				dumpwdvar(shf, *w);
1109				if (w[1] != NULL)
1110					shf_putc('|', shf);
1111				++w;
1112			}
1113			shf_putc(')', shf);
1114			dumpioact(shf, t);
1115			shf_putc('\n', shf);
1116			dumptree(shf, t1->left);
1117			shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1118		}
1119		break;
1120	OPEN(TWHILE)
1121		goto dumpleftmidrightandout;
1122	OPEN(TUNTIL)
1123		goto dumpleftmidrightandout;
1124	OPEN(TBRACE)
1125		goto dumpleftandout;
1126	OPEN(TCOPROC)
1127		goto dumpleftandout;
1128	OPEN(TASYNC)
1129		goto dumpleftandout;
1130	OPEN(TFUNCT)
1131		shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1132		    t->u.ksh_func ? Ttrue : Tfalse);
1133		goto dumpleftandout;
1134	OPEN(TTIME)
1135		goto dumpleftandout;
1136	OPEN(TIF)
1137 dumpif:
1138		shf_putc('\n', shf);
1139		dumptree(shf, t->left);
1140		t = t->right;
1141		dumpioact(shf, t);
1142		if (t->left != NULL) {
1143			shf_puts(" /TTHEN:\n", shf);
1144			dumptree(shf, t->left);
1145		}
1146		if (t->right && t->right->type == TELIF) {
1147			shf_puts(" /TELIF:", shf);
1148			t = t->right;
1149			dumpioact(shf, t);
1150			goto dumpif;
1151		}
1152		if (t->right != NULL) {
1153			shf_puts(" /TELSE:\n", shf);
1154			dumptree(shf, t->right);
1155		}
1156		break;
1157	OPEN(TEOF)
1158 dumpunexpected:
1159		shf_puts(Tunexpected, shf);
1160		break;
1161	OPEN(TELIF)
1162		goto dumpunexpected;
1163	OPEN(TPAT)
1164		goto dumpunexpected;
1165	default:
1166		name = "TINVALID";
1167		shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1168		goto dumpunexpected;
1169
1170#undef OPEN
1171	}
1172 out:
1173	shf_fprintf(shf, /*{*/ " /%s}\n", name);
1174	--nesting;
1175}
1176#endif
1177