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