• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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
ptree(struct op * t,int indent,struct shf * shf)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
pioact(struct shf * shf,int indent,struct ioword * iop)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 *
wdvarput(struct shf * shf,const char * wp,int quotelevel,int opmode)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
fptreef(struct shf * shf,int indent,const char * fmt,...)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 *
snptreef(char * s,ssize_t n,const char * fmt,...)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
vfptreef(struct shf * shf,int indent,const char * fmt,va_list va)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 *
tcopy(struct op * t,Area * ap)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 *
wdcopy(const char * wp,Area * ap)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 *
wdscan(const char * wp,int c)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 *
wdstrip(const char * wp,int opmode)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 **
iocopy(struct ioword ** iow,Area * ap)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
tfree(struct op * t,Area * ap)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
iofree(struct ioword ** iow,Area * ap)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
fpFUNCTf(struct shf * shf,int i,bool isksh,const char * k,struct op * v)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
vistree(char * dst,size_t sz,struct op * t)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
dumpchar(struct shf * shf,int c)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 *
dumpwdvar_(struct shf * shf,const char * wp,int quotelevel)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
dumpwdvar(struct shf * shf,const char * wp)827 dumpwdvar(struct shf *shf, const char *wp)
828 {
829 	dumpwdvar_(shf, wp, 0);
830 }
831 
832 void
dumptree(struct shf * shf,struct op * t)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