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