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