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