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