xref: /openbsd-src/bin/ksh/tree.c (revision 517d3880010591498a8b274d9d8fd7e787d73ebe)
1*517d3880Stobias /*	$OpenBSD: tree.c,v 1.34 2018/04/09 17:53:36 tobias Exp $	*/
27cb960a2Sdownsj 
37cb960a2Sdownsj /*
47cb960a2Sdownsj  * command tree climbing
57cb960a2Sdownsj  */
67cb960a2Sdownsj 
756018212Smmcc #include <string.h>
856018212Smmcc 
97cb960a2Sdownsj #include "sh.h"
107cb960a2Sdownsj 
117cb960a2Sdownsj #define INDENT	4
127cb960a2Sdownsj 
137cb960a2Sdownsj #define tputc(c, shf)	shf_putchar(c, shf);
14c5d5393cSotto static void	ptree(struct op *, int, struct shf *);
15c5d5393cSotto static void	pioact(struct shf *, int, struct ioword *);
16c5d5393cSotto static void	tputC(int, struct shf *);
17c5d5393cSotto static void	tputS(char *, struct shf *);
18c5d5393cSotto static void	vfptreef(struct shf *, int, const char *, va_list);
19c5d5393cSotto static struct ioword **iocopy(struct ioword **, Area *);
20c5d5393cSotto static void     iofree(struct ioword **, Area *);
217cb960a2Sdownsj 
227cb960a2Sdownsj /*
237cb960a2Sdownsj  * print a command tree
247cb960a2Sdownsj  */
257cb960a2Sdownsj 
267cb960a2Sdownsj static void
ptree(struct op * t,int indent,struct shf * shf)27c5d5393cSotto ptree(struct op *t, int indent, struct shf *shf)
287cb960a2Sdownsj {
297894b443Smillert 	char **w;
307cb960a2Sdownsj 	struct ioword **ioact;
317cb960a2Sdownsj 	struct op *t1;
327cb960a2Sdownsj 
337cb960a2Sdownsj  Chain:
347cb960a2Sdownsj 	if (t == NULL)
357cb960a2Sdownsj 		return;
367cb960a2Sdownsj 	switch (t->type) {
377cb960a2Sdownsj 	case TCOM:
387cb960a2Sdownsj 		if (t->vars)
397cb960a2Sdownsj 			for (w = t->vars; *w != NULL; )
407cb960a2Sdownsj 				fptreef(shf, indent, "%S ", *w++);
417cb960a2Sdownsj 		else
427cb960a2Sdownsj 			fptreef(shf, indent, "#no-vars# ");
437cb960a2Sdownsj 		if (t->args)
447cb960a2Sdownsj 			for (w = t->args; *w != NULL; )
457cb960a2Sdownsj 				fptreef(shf, indent, "%S ", *w++);
467cb960a2Sdownsj 		else
477cb960a2Sdownsj 			fptreef(shf, indent, "#no-args# ");
487cb960a2Sdownsj 		break;
497cb960a2Sdownsj 	case TEXEC:
507cb960a2Sdownsj 		t = t->left;
517cb960a2Sdownsj 		goto Chain;
527cb960a2Sdownsj 	case TPAREN:
537cb960a2Sdownsj 		fptreef(shf, indent + 2, "( %T) ", t->left);
547cb960a2Sdownsj 		break;
557cb960a2Sdownsj 	case TPIPE:
567cb960a2Sdownsj 		fptreef(shf, indent, "%T| ", t->left);
577cb960a2Sdownsj 		t = t->right;
587cb960a2Sdownsj 		goto Chain;
597cb960a2Sdownsj 	case TLIST:
607cb960a2Sdownsj 		fptreef(shf, indent, "%T%;", t->left);
617cb960a2Sdownsj 		t = t->right;
627cb960a2Sdownsj 		goto Chain;
637cb960a2Sdownsj 	case TOR:
647cb960a2Sdownsj 	case TAND:
657cb960a2Sdownsj 		fptreef(shf, indent, "%T%s %T",
667cb960a2Sdownsj 		    t->left, (t->type==TOR) ? "||" : "&&", t->right);
677cb960a2Sdownsj 		break;
687cb960a2Sdownsj 	case TBANG:
697cb960a2Sdownsj 		fptreef(shf, indent, "! ");
707cb960a2Sdownsj 		t = t->right;
717cb960a2Sdownsj 		goto Chain;
727cb960a2Sdownsj 	case TDBRACKET:
737cb960a2Sdownsj 	  {
747cb960a2Sdownsj 		int i;
757cb960a2Sdownsj 
767cb960a2Sdownsj 		fptreef(shf, indent, "[[");
777cb960a2Sdownsj 		for (i = 0; t->args[i]; i++)
787cb960a2Sdownsj 			fptreef(shf, indent, " %S", t->args[i]);
797cb960a2Sdownsj 		fptreef(shf, indent, " ]] ");
807cb960a2Sdownsj 		break;
817cb960a2Sdownsj 	  }
827cb960a2Sdownsj 	case TSELECT:
837cb960a2Sdownsj 		fptreef(shf, indent, "select %s ", t->str);
847eab881bSjaredy 		/* FALLTHROUGH */
857cb960a2Sdownsj 	case TFOR:
867cb960a2Sdownsj 		if (t->type == TFOR)
877cb960a2Sdownsj 			fptreef(shf, indent, "for %s ", t->str);
887cb960a2Sdownsj 		if (t->vars != NULL) {
897cb960a2Sdownsj 			fptreef(shf, indent, "in ");
907cb960a2Sdownsj 			for (w = t->vars; *w; )
917cb960a2Sdownsj 				fptreef(shf, indent, "%S ", *w++);
927cb960a2Sdownsj 			fptreef(shf, indent, "%;");
937cb960a2Sdownsj 		}
947cb960a2Sdownsj 		fptreef(shf, indent + INDENT, "do%N%T", t->left);
957cb960a2Sdownsj 		fptreef(shf, indent, "%;done ");
967cb960a2Sdownsj 		break;
977cb960a2Sdownsj 	case TCASE:
987cb960a2Sdownsj 		fptreef(shf, indent, "case %S in", t->str);
997cb960a2Sdownsj 		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1007cb960a2Sdownsj 			fptreef(shf, indent, "%N(");
1017cb960a2Sdownsj 			for (w = t1->vars; *w != NULL; w++)
1027cb960a2Sdownsj 				fptreef(shf, indent, "%S%c", *w,
1037cb960a2Sdownsj 				    (w[1] != NULL) ? '|' : ')');
1047cb960a2Sdownsj 			fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
1057cb960a2Sdownsj 		}
1067cb960a2Sdownsj 		fptreef(shf, indent, "%Nesac ");
1077cb960a2Sdownsj 		break;
1087cb960a2Sdownsj 	case TIF:
1097cb960a2Sdownsj 	case TELIF:
1107cb960a2Sdownsj 		/* 3 == strlen("if ") */
1117cb960a2Sdownsj 		fptreef(shf, indent + 3, "if %T", t->left);
1127cb960a2Sdownsj 		for (;;) {
1137cb960a2Sdownsj 			t = t->right;
1147cb960a2Sdownsj 			if (t->left != NULL) {
1157cb960a2Sdownsj 				fptreef(shf, indent, "%;");
1167cb960a2Sdownsj 				fptreef(shf, indent + INDENT, "then%N%T",
1177cb960a2Sdownsj 				    t->left);
1187cb960a2Sdownsj 			}
1197cb960a2Sdownsj 			if (t->right == NULL || t->right->type != TELIF)
1207cb960a2Sdownsj 				break;
1217cb960a2Sdownsj 			t = t->right;
1227cb960a2Sdownsj 			fptreef(shf, indent, "%;");
1237cb960a2Sdownsj 			/* 5 == strlen("elif ") */
1247cb960a2Sdownsj 			fptreef(shf, indent + 5, "elif %T", t->left);
1257cb960a2Sdownsj 		}
1267cb960a2Sdownsj 		if (t->right != NULL) {
1277cb960a2Sdownsj 			fptreef(shf, indent, "%;");
1287cb960a2Sdownsj 			fptreef(shf, indent + INDENT, "else%;%T", t->right);
1297cb960a2Sdownsj 		}
1307cb960a2Sdownsj 		fptreef(shf, indent, "%;fi ");
1317cb960a2Sdownsj 		break;
1327cb960a2Sdownsj 	case TWHILE:
1337cb960a2Sdownsj 	case TUNTIL:
1347cb960a2Sdownsj 		/* 6 == strlen("while"/"until") */
1357cb960a2Sdownsj 		fptreef(shf, indent + 6, "%s %T",
1367cb960a2Sdownsj 		    (t->type==TWHILE) ? "while" : "until",
1377cb960a2Sdownsj 		    t->left);
1387cb960a2Sdownsj 		fptreef(shf, indent, "%;do");
1397cb960a2Sdownsj 		fptreef(shf, indent + INDENT, "%;%T", t->right);
1407cb960a2Sdownsj 		fptreef(shf, indent, "%;done ");
1417cb960a2Sdownsj 		break;
1427cb960a2Sdownsj 	case TBRACE:
1437cb960a2Sdownsj 		fptreef(shf, indent + INDENT, "{%;%T", t->left);
1447cb960a2Sdownsj 		fptreef(shf, indent, "%;} ");
1457cb960a2Sdownsj 		break;
1467cb960a2Sdownsj 	case TCOPROC:
1477cb960a2Sdownsj 		fptreef(shf, indent, "%T|& ", t->left);
1487cb960a2Sdownsj 		break;
1497cb960a2Sdownsj 	case TASYNC:
1507cb960a2Sdownsj 		fptreef(shf, indent, "%T& ", t->left);
1517cb960a2Sdownsj 		break;
1527cb960a2Sdownsj 	case TFUNCT:
1533b015934Smillert 		fptreef(shf, indent,
1543b015934Smillert 		    t->u.ksh_func ? "function %s %T" : "%s() %T",
1553b015934Smillert 		    t->str, t->left);
1567cb960a2Sdownsj 		break;
1577cb960a2Sdownsj 	case TTIME:
1587cb960a2Sdownsj 		fptreef(shf, indent, "time %T", t->left);
1597cb960a2Sdownsj 		break;
1607cb960a2Sdownsj 	default:
1617cb960a2Sdownsj 		fptreef(shf, indent, "<botch>");
1627cb960a2Sdownsj 		break;
1637cb960a2Sdownsj 	}
1647cb960a2Sdownsj 	if ((ioact = t->ioact) != NULL) {
1657cb960a2Sdownsj 		int	need_nl = 0;
1667cb960a2Sdownsj 
1677cb960a2Sdownsj 		while (*ioact != NULL)
1687cb960a2Sdownsj 			pioact(shf, indent, *ioact++);
1697cb960a2Sdownsj 		/* Print here documents after everything else... */
1707cb960a2Sdownsj 		for (ioact = t->ioact; *ioact != NULL; ) {
1717cb960a2Sdownsj 			struct ioword *iop = *ioact++;
1727cb960a2Sdownsj 
173f00c5086Smillert 			/* heredoc is 0 when tracing (set -x) */
174f00c5086Smillert 			if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) {
1757cb960a2Sdownsj 				tputc('\n', shf);
176f00c5086Smillert 				shf_puts(iop->heredoc, shf);
177f00c5086Smillert 				fptreef(shf, indent, "%s",
178f00c5086Smillert 				    evalstr(iop->delim, 0));
1797cb960a2Sdownsj 				need_nl = 1;
1807cb960a2Sdownsj 			}
1817cb960a2Sdownsj 		}
1827cb960a2Sdownsj 		/* Last delimiter must be followed by a newline (this often
1837cb960a2Sdownsj 		 * leads to an extra blank line, but its not worth worrying
1847cb960a2Sdownsj 		 * about)
1857cb960a2Sdownsj 		 */
1867cb960a2Sdownsj 		if (need_nl)
1877cb960a2Sdownsj 			tputc('\n', shf);
1887cb960a2Sdownsj 	}
1897cb960a2Sdownsj }
1907cb960a2Sdownsj 
1917cb960a2Sdownsj static void
pioact(struct shf * shf,int indent,struct ioword * iop)192c5d5393cSotto pioact(struct shf *shf, int indent, struct ioword *iop)
1937cb960a2Sdownsj {
1947cb960a2Sdownsj 	int flag = iop->flag;
1957cb960a2Sdownsj 	int type = flag & IOTYPE;
1967cb960a2Sdownsj 	int expected;
1977cb960a2Sdownsj 
1987a8124d8Sderaadt 	expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
1997a8124d8Sderaadt 	    (type == IOCAT || type == IOWRITE) ? 1 :
2007a8124d8Sderaadt 	    (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
2017a8124d8Sderaadt 	    iop->unit + 1;
2027cb960a2Sdownsj 	if (iop->unit != expected)
2037cb960a2Sdownsj 		tputc('0' + iop->unit, shf);
2047cb960a2Sdownsj 
2057cb960a2Sdownsj 	switch (type) {
2067cb960a2Sdownsj 	case IOREAD:
2077cb960a2Sdownsj 		fptreef(shf, indent, "< ");
2087cb960a2Sdownsj 		break;
2097cb960a2Sdownsj 	case IOHERE:
2107cb960a2Sdownsj 		if (flag&IOSKIP)
2117cb960a2Sdownsj 			fptreef(shf, indent, "<<- ");
2127cb960a2Sdownsj 		else
2137cb960a2Sdownsj 			fptreef(shf, indent, "<< ");
2147cb960a2Sdownsj 		break;
2157cb960a2Sdownsj 	case IOCAT:
2167cb960a2Sdownsj 		fptreef(shf, indent, ">> ");
2177cb960a2Sdownsj 		break;
2187cb960a2Sdownsj 	case IOWRITE:
2197cb960a2Sdownsj 		if (flag&IOCLOB)
2207cb960a2Sdownsj 			fptreef(shf, indent, ">| ");
2217cb960a2Sdownsj 		else
2227cb960a2Sdownsj 			fptreef(shf, indent, "> ");
2237cb960a2Sdownsj 		break;
2247cb960a2Sdownsj 	case IORDWR:
2257cb960a2Sdownsj 		fptreef(shf, indent, "<> ");
2267cb960a2Sdownsj 		break;
2277cb960a2Sdownsj 	case IODUP:
2287cb960a2Sdownsj 		if (flag & IORDUP)
2297cb960a2Sdownsj 			fptreef(shf, indent, "<&");
2307cb960a2Sdownsj 		else
2317cb960a2Sdownsj 			fptreef(shf, indent, ">&");
2327cb960a2Sdownsj 		break;
2337cb960a2Sdownsj 	}
2347cb960a2Sdownsj 	/* name/delim are 0 when printing syntax errors */
2357cb960a2Sdownsj 	if (type == IOHERE) {
2367cb960a2Sdownsj 		if (iop->delim)
2377cb960a2Sdownsj 			fptreef(shf, indent, "%S ", iop->delim);
2387cb960a2Sdownsj 	} else if (iop->name)
2397cb960a2Sdownsj 		fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
2407cb960a2Sdownsj 		    iop->name);
2417cb960a2Sdownsj }
2427cb960a2Sdownsj 
2437cb960a2Sdownsj 
2447cb960a2Sdownsj /*
2457cb960a2Sdownsj  * variants of fputc, fputs for ptreef and snptreef
2467cb960a2Sdownsj  */
2477cb960a2Sdownsj 
2487cb960a2Sdownsj static void
tputC(int c,struct shf * shf)249c5d5393cSotto tputC(int c, struct shf *shf)
2507cb960a2Sdownsj {
2517cb960a2Sdownsj 	if ((c&0x60) == 0) {		/* C0|C1 */
2527cb960a2Sdownsj 		tputc((c&0x80) ? '$' : '^', shf);
2537cb960a2Sdownsj 		tputc(((c&0x7F)|0x40), shf);
2547cb960a2Sdownsj 	} else if ((c&0x7F) == 0x7F) {	/* DEL */
2557cb960a2Sdownsj 		tputc((c&0x80) ? '$' : '^', shf);
2567cb960a2Sdownsj 		tputc('?', shf);
2577cb960a2Sdownsj 	} else
2587cb960a2Sdownsj 		tputc(c, shf);
2597cb960a2Sdownsj }
2607cb960a2Sdownsj 
2617cb960a2Sdownsj static void
tputS(char * wp,struct shf * shf)262c5d5393cSotto tputS(char *wp, struct shf *shf)
2637cb960a2Sdownsj {
2647894b443Smillert 	int c, quoted=0;
2657cb960a2Sdownsj 
2663b015934Smillert 	/* problems:
2673b015934Smillert 	 *	`...` -> $(...)
2683b015934Smillert 	 *	'foo' -> "foo"
2693b015934Smillert 	 * could change encoding to:
2703b015934Smillert 	 *	OQUOTE ["'] ... CQUOTE ["']
2713b015934Smillert 	 *	COMSUB [(`] ...\0	(handle $ ` \ and maybe " in `...` case)
2723b015934Smillert 	 */
2737cb960a2Sdownsj 	while (1)
2747cb960a2Sdownsj 		switch ((c = *wp++)) {
2757cb960a2Sdownsj 		case EOS:
2767cb960a2Sdownsj 			return;
2777cb960a2Sdownsj 		case CHAR:
2787cb960a2Sdownsj 			tputC(*wp++, shf);
2797cb960a2Sdownsj 			break;
2807cb960a2Sdownsj 		case QCHAR:
2817cb960a2Sdownsj 			c = *wp++;
2827cb960a2Sdownsj 			if (!quoted || (c == '"' || c == '`' || c == '$'))
2837cb960a2Sdownsj 				tputc('\\', shf);
2847cb960a2Sdownsj 			tputC(c, shf);
2857cb960a2Sdownsj 			break;
2867cb960a2Sdownsj 		case COMSUB:
2877cb960a2Sdownsj 			tputc('$', shf);
2887cb960a2Sdownsj 			tputc('(', shf);
2897cb960a2Sdownsj 			while (*wp != 0)
2907cb960a2Sdownsj 				tputC(*wp++, shf);
2917cb960a2Sdownsj 			tputc(')', shf);
292040161f7Smillert 			wp++;
2937cb960a2Sdownsj 			break;
2947cb960a2Sdownsj 		case EXPRSUB:
2957cb960a2Sdownsj 			tputc('$', shf);
2967cb960a2Sdownsj 			tputc('(', shf);
2977cb960a2Sdownsj 			tputc('(', shf);
2987cb960a2Sdownsj 			while (*wp != 0)
2997cb960a2Sdownsj 				tputC(*wp++, shf);
3007cb960a2Sdownsj 			tputc(')', shf);
3017cb960a2Sdownsj 			tputc(')', shf);
302040161f7Smillert 			wp++;
3037cb960a2Sdownsj 			break;
3047cb960a2Sdownsj 		case OQUOTE:
3057cb960a2Sdownsj 			quoted = 1;
3067cb960a2Sdownsj 			tputc('"', shf);
3077cb960a2Sdownsj 			break;
3087cb960a2Sdownsj 		case CQUOTE:
3097cb960a2Sdownsj 			quoted = 0;
3107cb960a2Sdownsj 			tputc('"', shf);
3117cb960a2Sdownsj 			break;
3127cb960a2Sdownsj 		case OSUBST:
3137cb960a2Sdownsj 			tputc('$', shf);
3143b015934Smillert 			if (*wp++ == '{')
3157cb960a2Sdownsj 				tputc('{', shf);
3167cb960a2Sdownsj 			while ((c = *wp++) != 0)
3177cb960a2Sdownsj 				tputC(c, shf);
3187cb960a2Sdownsj 			break;
3197cb960a2Sdownsj 		case CSUBST:
3203b015934Smillert 			if (*wp++ == '}')
3217cb960a2Sdownsj 				tputc('}', shf);
3227cb960a2Sdownsj 			break;
3237cb960a2Sdownsj 		case OPAT:
3247cb960a2Sdownsj 			tputc(*wp++, shf);
3257cb960a2Sdownsj 			tputc('(', shf);
3267cb960a2Sdownsj 			break;
3277cb960a2Sdownsj 		case SPAT:
3287cb960a2Sdownsj 			tputc('|', shf);
3297cb960a2Sdownsj 			break;
3307cb960a2Sdownsj 		case CPAT:
3317cb960a2Sdownsj 			tputc(')', shf);
3327cb960a2Sdownsj 			break;
3337cb960a2Sdownsj 		}
3347cb960a2Sdownsj }
3357cb960a2Sdownsj 
336dde5a284Sotto void
fptreef(struct shf * shf,int indent,const char * fmt,...)3377cb960a2Sdownsj fptreef(struct shf *shf, int indent, const char *fmt, ...)
3387cb960a2Sdownsj {
3397cb960a2Sdownsj   va_list	va;
3407cb960a2Sdownsj 
34169b9f96bSmillert   va_start(va, fmt);
3427cb960a2Sdownsj   vfptreef(shf, indent, fmt, va);
3437cb960a2Sdownsj   va_end(va);
3447cb960a2Sdownsj }
3457cb960a2Sdownsj 
3467cb960a2Sdownsj char *
snptreef(char * s,int n,const char * fmt,...)3477cb960a2Sdownsj snptreef(char *s, int n, const char *fmt, ...)
3487cb960a2Sdownsj {
3497cb960a2Sdownsj   va_list va;
3507cb960a2Sdownsj   struct shf shf;
3517cb960a2Sdownsj 
3527cb960a2Sdownsj   shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
3537cb960a2Sdownsj 
35469b9f96bSmillert   va_start(va, fmt);
3557cb960a2Sdownsj   vfptreef(&shf, 0, fmt, va);
3567cb960a2Sdownsj   va_end(va);
3577cb960a2Sdownsj 
3587cb960a2Sdownsj   return shf_sclose(&shf); /* null terminates */
3597cb960a2Sdownsj }
3607cb960a2Sdownsj 
3617cb960a2Sdownsj static void
vfptreef(struct shf * shf,int indent,const char * fmt,va_list va)362c5d5393cSotto vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
3637cb960a2Sdownsj {
3647894b443Smillert 	int c;
3657cb960a2Sdownsj 
3667a8124d8Sderaadt 	while ((c = *fmt++)) {
3677cb960a2Sdownsj 		if (c == '%') {
368*517d3880Stobias 			int64_t n;
3697894b443Smillert 			char *p;
3707cb960a2Sdownsj 			int neg;
3717cb960a2Sdownsj 
3727cb960a2Sdownsj 			switch ((c = *fmt++)) {
3737cb960a2Sdownsj 			case 'c':
3747cb960a2Sdownsj 				tputc(va_arg(va, int), shf);
3757cb960a2Sdownsj 				break;
3764c503bcdSmillert 			case 'd': /* decimal */
3774c503bcdSmillert 				n = va_arg(va, int);
3784c503bcdSmillert 				neg = n < 0;
379*517d3880Stobias 				p = u64ton(neg ? -n : n, 10);
3804c503bcdSmillert 				if (neg)
3814c503bcdSmillert 					*--p = '-';
3824c503bcdSmillert 				while (*p)
3834c503bcdSmillert 					tputc(*p++, shf);
3844c503bcdSmillert 				break;
3857cb960a2Sdownsj 			case 's':
3867cb960a2Sdownsj 				p = va_arg(va, char *);
3877cb960a2Sdownsj 				while (*p)
3887cb960a2Sdownsj 					tputc(*p++, shf);
3897cb960a2Sdownsj 				break;
3907cb960a2Sdownsj 			case 'S':	/* word */
3917cb960a2Sdownsj 				p = va_arg(va, char *);
3927cb960a2Sdownsj 				tputS(p, shf);
3937cb960a2Sdownsj 				break;
3944c503bcdSmillert 			case 'u': /* unsigned decimal */
395*517d3880Stobias 				p = u64ton(va_arg(va, unsigned int), 10);
3967cb960a2Sdownsj 				while (*p)
3977cb960a2Sdownsj 					tputc(*p++, shf);
3987cb960a2Sdownsj 				break;
3997cb960a2Sdownsj 			case 'T':	/* format tree */
4007cb960a2Sdownsj 				ptree(va_arg(va, struct op *), indent, shf);
4017cb960a2Sdownsj 				break;
4027cb960a2Sdownsj 			case ';':	/* newline or ; */
4037cb960a2Sdownsj 			case 'N':	/* newline or space */
4047cb960a2Sdownsj 				if (shf->flags & SHF_STRING) {
4057cb960a2Sdownsj 					if (c == ';')
4067cb960a2Sdownsj 						tputc(';', shf);
4077cb960a2Sdownsj 					tputc(' ', shf);
4087cb960a2Sdownsj 				} else {
4097cb960a2Sdownsj 					int i;
4107cb960a2Sdownsj 
4117cb960a2Sdownsj 					tputc('\n', shf);
4127cb960a2Sdownsj 					for (i = indent; i >= 8; i -= 8)
4137cb960a2Sdownsj 						tputc('\t', shf);
4147cb960a2Sdownsj 					for (; i > 0; --i)
4157cb960a2Sdownsj 						tputc(' ', shf);
4167cb960a2Sdownsj 				}
4177cb960a2Sdownsj 				break;
4187cb960a2Sdownsj 			case 'R':
4197cb960a2Sdownsj 				pioact(shf, indent, va_arg(va, struct ioword *));
4207cb960a2Sdownsj 				break;
4217cb960a2Sdownsj 			default:
4227cb960a2Sdownsj 				tputc(c, shf);
4237cb960a2Sdownsj 				break;
4247cb960a2Sdownsj 			}
4257cb960a2Sdownsj 		} else
4267cb960a2Sdownsj 			tputc(c, shf);
4277cb960a2Sdownsj 	}
4287a8124d8Sderaadt }
4297cb960a2Sdownsj 
4307cb960a2Sdownsj /*
4317cb960a2Sdownsj  * copy tree (for function definition)
4327cb960a2Sdownsj  */
4337cb960a2Sdownsj 
4347cb960a2Sdownsj struct op *
tcopy(struct op * t,Area * ap)435c5d5393cSotto tcopy(struct op *t, Area *ap)
4367cb960a2Sdownsj {
4377894b443Smillert 	struct op *r;
4387894b443Smillert 	char **tw, **rw;
4397cb960a2Sdownsj 
4407cb960a2Sdownsj 	if (t == NULL)
4417cb960a2Sdownsj 		return NULL;
4427cb960a2Sdownsj 
4438c046d24Snicm 	r = alloc(sizeof(struct op), ap);
4447cb960a2Sdownsj 
4457cb960a2Sdownsj 	r->type = t->type;
446dcacb757Sdownsj 	r->u.evalflags = t->u.evalflags;
4477cb960a2Sdownsj 
4487cb960a2Sdownsj 	r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap);
4497cb960a2Sdownsj 
4507cb960a2Sdownsj 	if (t->vars == NULL)
4517cb960a2Sdownsj 		r->vars = NULL;
4527cb960a2Sdownsj 	else {
4537cb960a2Sdownsj 		for (tw = t->vars; *tw++ != NULL; )
4547cb960a2Sdownsj 			;
455d67c3782Smmcc 		rw = r->vars = areallocarray(NULL, tw - t->vars + 1,
456d67c3782Smmcc 		    sizeof(*tw), ap);
4577cb960a2Sdownsj 		for (tw = t->vars; *tw != NULL; )
4587cb960a2Sdownsj 			*rw++ = wdcopy(*tw++, ap);
4597cb960a2Sdownsj 		*rw = NULL;
4607cb960a2Sdownsj 	}
4617cb960a2Sdownsj 
4627cb960a2Sdownsj 	if (t->args == NULL)
4637cb960a2Sdownsj 		r->args = NULL;
4647cb960a2Sdownsj 	else {
4657cb960a2Sdownsj 		for (tw = t->args; *tw++ != NULL; )
4667cb960a2Sdownsj 			;
467d67c3782Smmcc 		rw = r->args = areallocarray(NULL, tw - t->args + 1,
468d67c3782Smmcc 		    sizeof(*tw), ap);
4697cb960a2Sdownsj 		for (tw = t->args; *tw != NULL; )
4707cb960a2Sdownsj 			*rw++ = wdcopy(*tw++, ap);
4717cb960a2Sdownsj 		*rw = NULL;
4727cb960a2Sdownsj 	}
4737cb960a2Sdownsj 
4747cb960a2Sdownsj 	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
4757cb960a2Sdownsj 
4767cb960a2Sdownsj 	r->left = tcopy(t->left, ap);
4777cb960a2Sdownsj 	r->right = tcopy(t->right, ap);
478f00c5086Smillert 	r->lineno = t->lineno;
4797cb960a2Sdownsj 
4807cb960a2Sdownsj 	return r;
4817cb960a2Sdownsj }
4827cb960a2Sdownsj 
4837cb960a2Sdownsj char *
wdcopy(const char * wp,Area * ap)484c5d5393cSotto wdcopy(const char *wp, Area *ap)
4857cb960a2Sdownsj {
4867cb960a2Sdownsj 	size_t len = wdscan(wp, EOS) - wp;
4877cb960a2Sdownsj 	return memcpy(alloc(len, ap), wp, len);
4887cb960a2Sdownsj }
4897cb960a2Sdownsj 
4907cb960a2Sdownsj /* return the position of prefix c in wp plus 1 */
4917cb960a2Sdownsj char *
wdscan(const char * wp,int c)492c5d5393cSotto wdscan(const char *wp, int c)
4937cb960a2Sdownsj {
4947894b443Smillert 	int nest = 0;
4957cb960a2Sdownsj 
4967cb960a2Sdownsj 	while (1)
4977cb960a2Sdownsj 		switch (*wp++) {
4987cb960a2Sdownsj 		case EOS:
4997cb960a2Sdownsj 			return (char *) wp;
5007cb960a2Sdownsj 		case CHAR:
5017cb960a2Sdownsj 		case QCHAR:
5027cb960a2Sdownsj 			wp++;
5037cb960a2Sdownsj 			break;
5047cb960a2Sdownsj 		case COMSUB:
5057cb960a2Sdownsj 		case EXPRSUB:
5067cb960a2Sdownsj 			while (*wp++ != 0)
5077cb960a2Sdownsj 				;
5087cb960a2Sdownsj 			break;
5097cb960a2Sdownsj 		case OQUOTE:
5107cb960a2Sdownsj 		case CQUOTE:
5117cb960a2Sdownsj 			break;
5127cb960a2Sdownsj 		case OSUBST:
5137cb960a2Sdownsj 			nest++;
5147cb960a2Sdownsj 			while (*wp++ != '\0')
5157cb960a2Sdownsj 				;
5167cb960a2Sdownsj 			break;
5177cb960a2Sdownsj 		case CSUBST:
5183b015934Smillert 			wp++;
5197cb960a2Sdownsj 			if (c == CSUBST && nest == 0)
5207cb960a2Sdownsj 				return (char *) wp;
5217cb960a2Sdownsj 			nest--;
5227cb960a2Sdownsj 			break;
5237cb960a2Sdownsj 		case OPAT:
5247cb960a2Sdownsj 			nest++;
5257cb960a2Sdownsj 			wp++;
5267cb960a2Sdownsj 			break;
5277cb960a2Sdownsj 		case SPAT:
5287cb960a2Sdownsj 		case CPAT:
5297cb960a2Sdownsj 			if (c == wp[-1] && nest == 0)
5307cb960a2Sdownsj 				return (char *) wp;
5317cb960a2Sdownsj 			if (wp[-1] == CPAT)
5327cb960a2Sdownsj 				nest--;
5337cb960a2Sdownsj 			break;
534f00c5086Smillert 		default:
5356c72b531Sjca 			internal_warningf(
53697445c82Santon 			    "%s: unknown char 0x%x (carrying on)",
53797445c82Santon 			    __func__, wp[-1]);
5387cb960a2Sdownsj 		}
5397cb960a2Sdownsj }
5407cb960a2Sdownsj 
5413b015934Smillert /* return a copy of wp without any of the mark up characters and
5423b015934Smillert  * with quote characters (" ' \) stripped.
5433b015934Smillert  * (string is allocated from ATEMP)
5443b015934Smillert  */
5453b015934Smillert char *
wdstrip(const char * wp)546c5d5393cSotto wdstrip(const char *wp)
5473b015934Smillert {
5483b015934Smillert 	struct shf shf;
5493b015934Smillert 	int c;
5503b015934Smillert 
551355ffa75Stedu 	shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
5523b015934Smillert 
5533b015934Smillert 	/* problems:
5543b015934Smillert 	 *	`...` -> $(...)
5553b015934Smillert 	 *	x${foo:-"hi"} -> x${foo:-hi}
5563b015934Smillert 	 *	x${foo:-'hi'} -> x${foo:-hi}
5573b015934Smillert 	 */
5583b015934Smillert 	while (1)
5593b015934Smillert 		switch ((c = *wp++)) {
5603b015934Smillert 		case EOS:
5613b015934Smillert 			return shf_sclose(&shf); /* null terminates */
5623b015934Smillert 		case CHAR:
5633b015934Smillert 		case QCHAR:
5643b015934Smillert 			shf_putchar(*wp++, &shf);
5653b015934Smillert 			break;
5663b015934Smillert 		case COMSUB:
5673b015934Smillert 			shf_putchar('$', &shf);
5683b015934Smillert 			shf_putchar('(', &shf);
5693b015934Smillert 			while (*wp != 0)
5703b015934Smillert 				shf_putchar(*wp++, &shf);
5713b015934Smillert 			shf_putchar(')', &shf);
5723b015934Smillert 			break;
5733b015934Smillert 		case EXPRSUB:
5743b015934Smillert 			shf_putchar('$', &shf);
5753b015934Smillert 			shf_putchar('(', &shf);
5763b015934Smillert 			shf_putchar('(', &shf);
5773b015934Smillert 			while (*wp != 0)
5783b015934Smillert 				shf_putchar(*wp++, &shf);
5793b015934Smillert 			shf_putchar(')', &shf);
5803b015934Smillert 			shf_putchar(')', &shf);
5813b015934Smillert 			break;
5823b015934Smillert 		case OQUOTE:
5833b015934Smillert 			break;
5843b015934Smillert 		case CQUOTE:
5853b015934Smillert 			break;
5863b015934Smillert 		case OSUBST:
5873b015934Smillert 			shf_putchar('$', &shf);
5883b015934Smillert 			if (*wp++ == '{')
5893b015934Smillert 			    shf_putchar('{', &shf);
5903b015934Smillert 			while ((c = *wp++) != 0)
5913b015934Smillert 				shf_putchar(c, &shf);
5923b015934Smillert 			break;
5933b015934Smillert 		case CSUBST:
5943b015934Smillert 			if (*wp++ == '}')
5953b015934Smillert 				shf_putchar('}', &shf);
5963b015934Smillert 			break;
5973b015934Smillert 		case OPAT:
5983b015934Smillert 			shf_putchar(*wp++, &shf);
5993b015934Smillert 			shf_putchar('(', &shf);
6003b015934Smillert 			break;
6013b015934Smillert 		case SPAT:
6023b015934Smillert 			shf_putchar('|', &shf);
6033b015934Smillert 			break;
6043b015934Smillert 		case CPAT:
6053b015934Smillert 			shf_putchar(')', &shf);
6063b015934Smillert 			break;
6073b015934Smillert 		}
6083b015934Smillert }
6093b015934Smillert 
6107cb960a2Sdownsj static	struct ioword **
iocopy(struct ioword ** iow,Area * ap)611c5d5393cSotto iocopy(struct ioword **iow, Area *ap)
6127cb960a2Sdownsj {
6137894b443Smillert 	struct ioword **ior;
6147894b443Smillert 	int i;
6157cb960a2Sdownsj 
6167cb960a2Sdownsj 	for (ior = iow; *ior++ != NULL; )
6177cb960a2Sdownsj 		;
618d67c3782Smmcc 	ior = areallocarray(NULL, ior - iow + 1, sizeof(*ior), ap);
6197cb960a2Sdownsj 
6207cb960a2Sdownsj 	for (i = 0; iow[i] != NULL; i++) {
6217894b443Smillert 		struct ioword *p, *q;
6227cb960a2Sdownsj 
6237cb960a2Sdownsj 		p = iow[i];
6248c046d24Snicm 		q = alloc(sizeof(*p), ap);
6257cb960a2Sdownsj 		ior[i] = q;
6267cb960a2Sdownsj 		*q = *p;
627355ffa75Stedu 		if (p->name != NULL)
6287cb960a2Sdownsj 			q->name = wdcopy(p->name, ap);
629355ffa75Stedu 		if (p->delim != NULL)
6307cb960a2Sdownsj 			q->delim = wdcopy(p->delim, ap);
631355ffa75Stedu 		if (p->heredoc != NULL)
632f00c5086Smillert 			q->heredoc = str_save(p->heredoc, ap);
6337cb960a2Sdownsj 	}
6347cb960a2Sdownsj 	ior[i] = NULL;
6357cb960a2Sdownsj 
6367cb960a2Sdownsj 	return ior;
6377cb960a2Sdownsj }
6387cb960a2Sdownsj 
6397cb960a2Sdownsj /*
6407cb960a2Sdownsj  * free tree (for function definition)
6417cb960a2Sdownsj  */
6427cb960a2Sdownsj 
6437cb960a2Sdownsj void
tfree(struct op * t,Area * ap)644c5d5393cSotto tfree(struct op *t, Area *ap)
6457cb960a2Sdownsj {
6467894b443Smillert 	char **w;
6477cb960a2Sdownsj 
6487cb960a2Sdownsj 	if (t == NULL)
6497cb960a2Sdownsj 		return;
6507cb960a2Sdownsj 
651bfd561bcStedu 	afree(t->str, ap);
6527cb960a2Sdownsj 
6537cb960a2Sdownsj 	if (t->vars != NULL) {
6547cb960a2Sdownsj 		for (w = t->vars; *w != NULL; w++)
655bfd561bcStedu 			afree(*w, ap);
656bfd561bcStedu 		afree(t->vars, ap);
6577cb960a2Sdownsj 	}
6587cb960a2Sdownsj 
6597cb960a2Sdownsj 	if (t->args != NULL) {
6607cb960a2Sdownsj 		for (w = t->args; *w != NULL; w++)
661bfd561bcStedu 			afree(*w, ap);
662bfd561bcStedu 		afree(t->args, ap);
6637cb960a2Sdownsj 	}
6647cb960a2Sdownsj 
6657cb960a2Sdownsj 	if (t->ioact != NULL)
6667cb960a2Sdownsj 		iofree(t->ioact, ap);
6677cb960a2Sdownsj 
6687cb960a2Sdownsj 	tfree(t->left, ap);
6697cb960a2Sdownsj 	tfree(t->right, ap);
6707cb960a2Sdownsj 
671bfd561bcStedu 	afree(t, ap);
6727cb960a2Sdownsj }
6737cb960a2Sdownsj 
6747cb960a2Sdownsj static	void
iofree(struct ioword ** iow,Area * ap)675c5d5393cSotto iofree(struct ioword **iow, Area *ap)
6767cb960a2Sdownsj {
6777894b443Smillert 	struct ioword **iop;
6787894b443Smillert 	struct ioword *p;
6797cb960a2Sdownsj 
6807cb960a2Sdownsj 	for (iop = iow; (p = *iop++) != NULL; ) {
681bfd561bcStedu 		afree(p->name, ap);
682bfd561bcStedu 		afree(p->delim, ap);
683bfd561bcStedu 		afree(p->heredoc, ap);
684bfd561bcStedu 		afree(p, ap);
6857cb960a2Sdownsj 	}
6867b1fe930Sjaredy 	afree(iow, ap);
6877cb960a2Sdownsj }
688