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