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