xref: /openbsd-src/bin/ksh/tree.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: tree.c,v 1.19 2008/08/11 21:50:35 jaredy Exp $	*/
2 
3 /*
4  * command tree climbing
5  */
6 
7 #include "sh.h"
8 
9 #define INDENT	4
10 
11 #define tputc(c, shf)	shf_putchar(c, shf);
12 static void	ptree(struct op *, int, struct shf *);
13 static void	pioact(struct shf *, int, struct ioword *);
14 static void	tputC(int, struct shf *);
15 static void	tputS(char *, struct shf *);
16 static void	vfptreef(struct shf *, int, const char *, va_list);
17 static struct ioword **iocopy(struct ioword **, Area *);
18 static void     iofree(struct ioword **, Area *);
19 
20 /*
21  * print a command tree
22  */
23 
24 static void
25 ptree(struct op *t, int indent, struct shf *shf)
26 {
27 	char **w;
28 	struct ioword **ioact;
29 	struct op *t1;
30 
31  Chain:
32 	if (t == NULL)
33 		return;
34 	switch (t->type) {
35 	case TCOM:
36 		if (t->vars)
37 			for (w = t->vars; *w != NULL; )
38 				fptreef(shf, indent, "%S ", *w++);
39 		else
40 			fptreef(shf, indent, "#no-vars# ");
41 		if (t->args)
42 			for (w = t->args; *w != NULL; )
43 				fptreef(shf, indent, "%S ", *w++);
44 		else
45 			fptreef(shf, indent, "#no-args# ");
46 		break;
47 	case TEXEC:
48 #if 0 /* ?not useful - can't be called? */
49 		/* Print original vars */
50 		if (t->left->vars)
51 			for (w = t->left->vars; *w != NULL; )
52 				fptreef(shf, indent, "%S ", *w++);
53 		else
54 			fptreef(shf, indent, "#no-vars# ");
55 		/* Print expanded vars */
56 		if (t->args)
57 			for (w = t->args; *w != NULL; )
58 				fptreef(shf, indent, "%s ", *w++);
59 		else
60 			fptreef(shf, indent, "#no-args# ");
61 		/* Print original io */
62 		t = t->left;
63 #else
64 		t = t->left;
65 		goto Chain;
66 #endif
67 	case TPAREN:
68 		fptreef(shf, indent + 2, "( %T) ", t->left);
69 		break;
70 	case TPIPE:
71 		fptreef(shf, indent, "%T| ", t->left);
72 		t = t->right;
73 		goto Chain;
74 	case TLIST:
75 		fptreef(shf, indent, "%T%;", t->left);
76 		t = t->right;
77 		goto Chain;
78 	case TOR:
79 	case TAND:
80 		fptreef(shf, indent, "%T%s %T",
81 		    t->left, (t->type==TOR) ? "||" : "&&", t->right);
82 		break;
83 	case TBANG:
84 		fptreef(shf, indent, "! ");
85 		t = t->right;
86 		goto Chain;
87 	case TDBRACKET:
88 	  {
89 		int i;
90 
91 		fptreef(shf, indent, "[[");
92 		for (i = 0; t->args[i]; i++)
93 			fptreef(shf, indent, " %S", t->args[i]);
94 		fptreef(shf, indent, " ]] ");
95 		break;
96 	  }
97 	case TSELECT:
98 		fptreef(shf, indent, "select %s ", t->str);
99 		/* FALLTHROUGH */
100 	case TFOR:
101 		if (t->type == TFOR)
102 			fptreef(shf, indent, "for %s ", t->str);
103 		if (t->vars != NULL) {
104 			fptreef(shf, indent, "in ");
105 			for (w = t->vars; *w; )
106 				fptreef(shf, indent, "%S ", *w++);
107 			fptreef(shf, indent, "%;");
108 		}
109 		fptreef(shf, indent + INDENT, "do%N%T", t->left);
110 		fptreef(shf, indent, "%;done ");
111 		break;
112 	case TCASE:
113 		fptreef(shf, indent, "case %S in", t->str);
114 		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
115 			fptreef(shf, indent, "%N(");
116 			for (w = t1->vars; *w != NULL; w++)
117 				fptreef(shf, indent, "%S%c", *w,
118 				    (w[1] != NULL) ? '|' : ')');
119 			fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
120 		}
121 		fptreef(shf, indent, "%Nesac ");
122 		break;
123 	case TIF:
124 	case TELIF:
125 		/* 3 == strlen("if ") */
126 		fptreef(shf, indent + 3, "if %T", t->left);
127 		for (;;) {
128 			t = t->right;
129 			if (t->left != NULL) {
130 				fptreef(shf, indent, "%;");
131 				fptreef(shf, indent + INDENT, "then%N%T",
132 				    t->left);
133 			}
134 			if (t->right == NULL || t->right->type != TELIF)
135 				break;
136 			t = t->right;
137 			fptreef(shf, indent, "%;");
138 			/* 5 == strlen("elif ") */
139 			fptreef(shf, indent + 5, "elif %T", t->left);
140 		}
141 		if (t->right != NULL) {
142 			fptreef(shf, indent, "%;");
143 			fptreef(shf, indent + INDENT, "else%;%T", t->right);
144 		}
145 		fptreef(shf, indent, "%;fi ");
146 		break;
147 	case TWHILE:
148 	case TUNTIL:
149 		/* 6 == strlen("while"/"until") */
150 		fptreef(shf, indent + 6, "%s %T",
151 		    (t->type==TWHILE) ? "while" : "until",
152 		    t->left);
153 		fptreef(shf, indent, "%;do");
154 		fptreef(shf, indent + INDENT, "%;%T", t->right);
155 		fptreef(shf, indent, "%;done ");
156 		break;
157 	case TBRACE:
158 		fptreef(shf, indent + INDENT, "{%;%T", t->left);
159 		fptreef(shf, indent, "%;} ");
160 		break;
161 	case TCOPROC:
162 		fptreef(shf, indent, "%T|& ", t->left);
163 		break;
164 	case TASYNC:
165 		fptreef(shf, indent, "%T& ", t->left);
166 		break;
167 	case TFUNCT:
168 		fptreef(shf, indent,
169 		    t->u.ksh_func ? "function %s %T" : "%s() %T",
170 		    t->str, t->left);
171 		break;
172 	case TTIME:
173 		fptreef(shf, indent, "time %T", t->left);
174 		break;
175 	default:
176 		fptreef(shf, indent, "<botch>");
177 		break;
178 	}
179 	if ((ioact = t->ioact) != NULL) {
180 		int	need_nl = 0;
181 
182 		while (*ioact != NULL)
183 			pioact(shf, indent, *ioact++);
184 		/* Print here documents after everything else... */
185 		for (ioact = t->ioact; *ioact != NULL; ) {
186 			struct ioword *iop = *ioact++;
187 
188 			/* heredoc is 0 when tracing (set -x) */
189 			if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) {
190 				tputc('\n', shf);
191 				shf_puts(iop->heredoc, shf);
192 				fptreef(shf, indent, "%s",
193 				    evalstr(iop->delim, 0));
194 				need_nl = 1;
195 			}
196 		}
197 		/* Last delimiter must be followed by a newline (this often
198 		 * leads to an extra blank line, but its not worth worrying
199 		 * about)
200 		 */
201 		if (need_nl)
202 			tputc('\n', shf);
203 	}
204 }
205 
206 static void
207 pioact(struct shf *shf, int indent, struct ioword *iop)
208 {
209 	int flag = iop->flag;
210 	int type = flag & IOTYPE;
211 	int expected;
212 
213 	expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
214 	    (type == IOCAT || type == IOWRITE) ? 1 :
215 	    (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
216 	    iop->unit + 1;
217 	if (iop->unit != expected)
218 		tputc('0' + iop->unit, shf);
219 
220 	switch (type) {
221 	case IOREAD:
222 		fptreef(shf, indent, "< ");
223 		break;
224 	case IOHERE:
225 		if (flag&IOSKIP)
226 			fptreef(shf, indent, "<<- ");
227 		else
228 			fptreef(shf, indent, "<< ");
229 		break;
230 	case IOCAT:
231 		fptreef(shf, indent, ">> ");
232 		break;
233 	case IOWRITE:
234 		if (flag&IOCLOB)
235 			fptreef(shf, indent, ">| ");
236 		else
237 			fptreef(shf, indent, "> ");
238 		break;
239 	case IORDWR:
240 		fptreef(shf, indent, "<> ");
241 		break;
242 	case IODUP:
243 		if (flag & IORDUP)
244 			fptreef(shf, indent, "<&");
245 		else
246 			fptreef(shf, indent, ">&");
247 		break;
248 	}
249 	/* name/delim are 0 when printing syntax errors */
250 	if (type == IOHERE) {
251 		if (iop->delim)
252 			fptreef(shf, indent, "%S ", iop->delim);
253 	} else if (iop->name)
254 		fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
255 		    iop->name);
256 }
257 
258 
259 /*
260  * variants of fputc, fputs for ptreef and snptreef
261  */
262 
263 static void
264 tputC(int c, struct shf *shf)
265 {
266 	if ((c&0x60) == 0) {		/* C0|C1 */
267 		tputc((c&0x80) ? '$' : '^', shf);
268 		tputc(((c&0x7F)|0x40), shf);
269 	} else if ((c&0x7F) == 0x7F) {	/* DEL */
270 		tputc((c&0x80) ? '$' : '^', shf);
271 		tputc('?', shf);
272 	} else
273 		tputc(c, shf);
274 }
275 
276 static void
277 tputS(char *wp, struct shf *shf)
278 {
279 	int c, quoted=0;
280 
281 	/* problems:
282 	 *	`...` -> $(...)
283 	 *	'foo' -> "foo"
284 	 * could change encoding to:
285 	 *	OQUOTE ["'] ... CQUOTE ["']
286 	 *	COMSUB [(`] ...\0	(handle $ ` \ and maybe " in `...` case)
287 	 */
288 	while (1)
289 		switch ((c = *wp++)) {
290 		case EOS:
291 			return;
292 		case CHAR:
293 			tputC(*wp++, shf);
294 			break;
295 		case QCHAR:
296 			c = *wp++;
297 			if (!quoted || (c == '"' || c == '`' || c == '$'))
298 				tputc('\\', shf);
299 			tputC(c, shf);
300 			break;
301 		case COMSUB:
302 			tputc('$', shf);
303 			tputc('(', shf);
304 			while (*wp != 0)
305 				tputC(*wp++, shf);
306 			tputc(')', shf);
307 			wp++;
308 			break;
309 		case EXPRSUB:
310 			tputc('$', shf);
311 			tputc('(', shf);
312 			tputc('(', shf);
313 			while (*wp != 0)
314 				tputC(*wp++, shf);
315 			tputc(')', shf);
316 			tputc(')', shf);
317 			wp++;
318 			break;
319 		case OQUOTE:
320 			quoted = 1;
321 			tputc('"', shf);
322 			break;
323 		case CQUOTE:
324 			quoted = 0;
325 			tputc('"', shf);
326 			break;
327 		case OSUBST:
328 			tputc('$', shf);
329 			if (*wp++ == '{')
330 				tputc('{', shf);
331 			while ((c = *wp++) != 0)
332 				tputC(c, shf);
333 			break;
334 		case CSUBST:
335 			if (*wp++ == '}')
336 				tputc('}', shf);
337 			break;
338 		case OPAT:
339 			tputc(*wp++, shf);
340 			tputc('(', shf);
341 			break;
342 		case SPAT:
343 			tputc('|', shf);
344 			break;
345 		case CPAT:
346 			tputc(')', shf);
347 			break;
348 		}
349 }
350 
351 /*
352  * this is the _only_ way to reliably handle
353  * variable args with an ANSI compiler
354  */
355 /* VARARGS */
356 int
357 fptreef(struct shf *shf, int indent, const char *fmt, ...)
358 {
359   va_list	va;
360 
361   va_start(va, fmt);
362 
363   vfptreef(shf, indent, fmt, va);
364   va_end(va);
365   return 0;
366 }
367 
368 /* VARARGS */
369 char *
370 snptreef(char *s, int n, const char *fmt, ...)
371 {
372   va_list va;
373   struct shf shf;
374 
375   shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
376 
377   va_start(va, fmt);
378   vfptreef(&shf, 0, fmt, va);
379   va_end(va);
380 
381   return shf_sclose(&shf); /* null terminates */
382 }
383 
384 static void
385 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
386 {
387 	int c;
388 
389 	while ((c = *fmt++)) {
390 		if (c == '%') {
391 			long n;
392 			char *p;
393 			int neg;
394 
395 			switch ((c = *fmt++)) {
396 			case 'c':
397 				tputc(va_arg(va, int), shf);
398 				break;
399 			case 's':
400 				p = va_arg(va, char *);
401 				while (*p)
402 					tputc(*p++, shf);
403 				break;
404 			case 'S':	/* word */
405 				p = va_arg(va, char *);
406 				tputS(p, shf);
407 				break;
408 			case 'd': case 'u': /* decimal */
409 				n = (c == 'd') ? va_arg(va, int) :
410 				    va_arg(va, unsigned int);
411 				neg = c=='d' && n<0;
412 				p = ulton((neg) ? -n : n, 10);
413 				if (neg)
414 					*--p = '-';
415 				while (*p)
416 					tputc(*p++, shf);
417 				break;
418 			case 'T':	/* format tree */
419 				ptree(va_arg(va, struct op *), indent, shf);
420 				break;
421 			case ';':	/* newline or ; */
422 			case 'N':	/* newline or space */
423 				if (shf->flags & SHF_STRING) {
424 					if (c == ';')
425 						tputc(';', shf);
426 					tputc(' ', shf);
427 				} else {
428 					int i;
429 
430 					tputc('\n', shf);
431 					for (i = indent; i >= 8; i -= 8)
432 						tputc('\t', shf);
433 					for (; i > 0; --i)
434 						tputc(' ', shf);
435 				}
436 				break;
437 			case 'R':
438 				pioact(shf, indent, va_arg(va, struct ioword *));
439 				break;
440 			default:
441 				tputc(c, shf);
442 				break;
443 			}
444 		} else
445 			tputc(c, shf);
446 	}
447 }
448 
449 /*
450  * copy tree (for function definition)
451  */
452 
453 struct op *
454 tcopy(struct op *t, Area *ap)
455 {
456 	struct op *r;
457 	char **tw, **rw;
458 
459 	if (t == NULL)
460 		return NULL;
461 
462 	r = (struct op *) alloc(sizeof(struct op), ap);
463 
464 	r->type = t->type;
465 	r->u.evalflags = t->u.evalflags;
466 
467 	r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap);
468 
469 	if (t->vars == NULL)
470 		r->vars = NULL;
471 	else {
472 		for (tw = t->vars; *tw++ != NULL; )
473 			;
474 		rw = r->vars = (char **)
475 			alloc((tw - t->vars + 1) * sizeof(*tw), ap);
476 		for (tw = t->vars; *tw != NULL; )
477 			*rw++ = wdcopy(*tw++, ap);
478 		*rw = NULL;
479 	}
480 
481 	if (t->args == NULL)
482 		r->args = NULL;
483 	else {
484 		for (tw = t->args; *tw++ != NULL; )
485 			;
486 		rw = r->args = (char **)
487 			alloc((tw - t->args + 1) * sizeof(*tw), ap);
488 		for (tw = t->args; *tw != NULL; )
489 			*rw++ = wdcopy(*tw++, ap);
490 		*rw = NULL;
491 	}
492 
493 	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
494 
495 	r->left = tcopy(t->left, ap);
496 	r->right = tcopy(t->right, ap);
497 	r->lineno = t->lineno;
498 
499 	return r;
500 }
501 
502 char *
503 wdcopy(const char *wp, Area *ap)
504 {
505 	size_t len = wdscan(wp, EOS) - wp;
506 	return memcpy(alloc(len, ap), wp, len);
507 }
508 
509 /* return the position of prefix c in wp plus 1 */
510 char *
511 wdscan(const char *wp, int c)
512 {
513 	int nest = 0;
514 
515 	while (1)
516 		switch (*wp++) {
517 		case EOS:
518 			return (char *) wp;
519 		case CHAR:
520 		case QCHAR:
521 			wp++;
522 			break;
523 		case COMSUB:
524 		case EXPRSUB:
525 			while (*wp++ != 0)
526 				;
527 			break;
528 		case OQUOTE:
529 		case CQUOTE:
530 			break;
531 		case OSUBST:
532 			nest++;
533 			while (*wp++ != '\0')
534 				;
535 			break;
536 		case CSUBST:
537 			wp++;
538 			if (c == CSUBST && nest == 0)
539 				return (char *) wp;
540 			nest--;
541 			break;
542 		case OPAT:
543 			nest++;
544 			wp++;
545 			break;
546 		case SPAT:
547 		case CPAT:
548 			if (c == wp[-1] && nest == 0)
549 				return (char *) wp;
550 			if (wp[-1] == CPAT)
551 				nest--;
552 			break;
553 		default:
554 			internal_errorf(0,
555 			    "wdscan: unknown char 0x%x (carrying on)",
556 			    wp[-1]);
557 		}
558 }
559 
560 /* return a copy of wp without any of the mark up characters and
561  * with quote characters (" ' \) stripped.
562  * (string is allocated from ATEMP)
563  */
564 char *
565 wdstrip(const char *wp)
566 {
567 	struct shf shf;
568 	int c;
569 
570 	shf_sopen((char *) 0, 32, SHF_WR | SHF_DYNAMIC, &shf);
571 
572 	/* problems:
573 	 *	`...` -> $(...)
574 	 *	x${foo:-"hi"} -> x${foo:-hi}
575 	 *	x${foo:-'hi'} -> x${foo:-hi}
576 	 */
577 	while (1)
578 		switch ((c = *wp++)) {
579 		case EOS:
580 			return shf_sclose(&shf); /* null terminates */
581 		case CHAR:
582 		case QCHAR:
583 			shf_putchar(*wp++, &shf);
584 			break;
585 		case COMSUB:
586 			shf_putchar('$', &shf);
587 			shf_putchar('(', &shf);
588 			while (*wp != 0)
589 				shf_putchar(*wp++, &shf);
590 			shf_putchar(')', &shf);
591 			break;
592 		case EXPRSUB:
593 			shf_putchar('$', &shf);
594 			shf_putchar('(', &shf);
595 			shf_putchar('(', &shf);
596 			while (*wp != 0)
597 				shf_putchar(*wp++, &shf);
598 			shf_putchar(')', &shf);
599 			shf_putchar(')', &shf);
600 			break;
601 		case OQUOTE:
602 			break;
603 		case CQUOTE:
604 			break;
605 		case OSUBST:
606 			shf_putchar('$', &shf);
607 			if (*wp++ == '{')
608 			    shf_putchar('{', &shf);
609 			while ((c = *wp++) != 0)
610 				shf_putchar(c, &shf);
611 			break;
612 		case CSUBST:
613 			if (*wp++ == '}')
614 				shf_putchar('}', &shf);
615 			break;
616 		case OPAT:
617 			shf_putchar(*wp++, &shf);
618 			shf_putchar('(', &shf);
619 			break;
620 		case SPAT:
621 			shf_putchar('|', &shf);
622 			break;
623 		case CPAT:
624 			shf_putchar(')', &shf);
625 			break;
626 		}
627 }
628 
629 static	struct ioword **
630 iocopy(struct ioword **iow, Area *ap)
631 {
632 	struct ioword **ior;
633 	int i;
634 
635 	for (ior = iow; *ior++ != NULL; )
636 		;
637 	ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
638 
639 	for (i = 0; iow[i] != NULL; i++) {
640 		struct ioword *p, *q;
641 
642 		p = iow[i];
643 		q = (struct ioword *) alloc(sizeof(*p), ap);
644 		ior[i] = q;
645 		*q = *p;
646 		if (p->name != (char *) 0)
647 			q->name = wdcopy(p->name, ap);
648 		if (p->delim != (char *) 0)
649 			q->delim = wdcopy(p->delim, ap);
650 		if (p->heredoc != (char *) 0)
651 			q->heredoc = str_save(p->heredoc, ap);
652 	}
653 	ior[i] = NULL;
654 
655 	return ior;
656 }
657 
658 /*
659  * free tree (for function definition)
660  */
661 
662 void
663 tfree(struct op *t, Area *ap)
664 {
665 	char **w;
666 
667 	if (t == NULL)
668 		return;
669 
670 	if (t->str != NULL)
671 		afree((void*)t->str, ap);
672 
673 	if (t->vars != NULL) {
674 		for (w = t->vars; *w != NULL; w++)
675 			afree((void*)*w, ap);
676 		afree((void*)t->vars, ap);
677 	}
678 
679 	if (t->args != NULL) {
680 		for (w = t->args; *w != NULL; w++)
681 			afree((void*)*w, ap);
682 		afree((void*)t->args, ap);
683 	}
684 
685 	if (t->ioact != NULL)
686 		iofree(t->ioact, ap);
687 
688 	tfree(t->left, ap);
689 	tfree(t->right, ap);
690 
691 	afree((void*)t, ap);
692 }
693 
694 static	void
695 iofree(struct ioword **iow, Area *ap)
696 {
697 	struct ioword **iop;
698 	struct ioword *p;
699 
700 	for (iop = iow; (p = *iop++) != NULL; ) {
701 		if (p->name != NULL)
702 			afree((void*)p->name, ap);
703 		if (p->delim != NULL)
704 			afree((void*)p->delim, ap);
705 		if (p->heredoc != NULL)
706 			afree((void*)p->heredoc, ap);
707 		afree((void*)p, ap);
708 	}
709 	afree(iow, ap);
710 }
711