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, 2011
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.51 2011/09/07 15:24:21 tg Exp $");
26
27 #define INDENT 8
28
29 static void ptree(struct op *, int, struct shf *);
30 static void pioact(struct shf *, int, struct ioword *);
31 static const char *wdvarput(struct shf *, const char *, int, int);
32 static void vfptreef(struct shf *, int, const char *, va_list);
33 static struct ioword **iocopy(struct ioword **, Area *);
34 static void iofree(struct ioword **, Area *);
35
36 /* "foo& ; bar" and "foo |& ; bar" are invalid */
37 static bool prevent_semicolon;
38
39 /*
40 * print a command tree
41 */
42 static void
ptree(struct op * t,int indent,struct shf * shf)43 ptree(struct op *t, int indent, struct shf *shf)
44 {
45 const char **w;
46 struct ioword **ioact;
47 struct op *t1;
48 int i;
49
50 Chain:
51 if (t == NULL)
52 return;
53 switch (t->type) {
54 case TCOM:
55 if (t->vars) {
56 w = (const char **)t->vars;
57 while (*w)
58 fptreef(shf, indent, "%S ", *w++);
59 } else
60 shf_puts("#no-vars# ", shf);
61 if (t->args) {
62 w = t->args;
63 while (*w)
64 fptreef(shf, indent, "%S ", *w++);
65 } else
66 shf_puts("#no-args# ", shf);
67 prevent_semicolon = false;
68 break;
69 case TEXEC:
70 t = t->left;
71 goto Chain;
72 case TPAREN:
73 fptreef(shf, indent + 2, "( %T) ", t->left);
74 break;
75 case TPIPE:
76 fptreef(shf, indent, "%T| ", t->left);
77 t = t->right;
78 goto Chain;
79 case TLIST:
80 fptreef(shf, indent, "%T%;", t->left);
81 t = t->right;
82 goto Chain;
83 case TOR:
84 case TAND:
85 fptreef(shf, indent, "%T%s %T",
86 t->left, (t->type == TOR) ? "||" : "&&", t->right);
87 break;
88 case TBANG:
89 shf_puts("! ", shf);
90 prevent_semicolon = false;
91 t = t->right;
92 goto Chain;
93 case TDBRACKET:
94 w = t->args;
95 shf_puts("[[", shf);
96 while (*w)
97 fptreef(shf, indent, " %S", *w++);
98 shf_puts(" ]] ", shf);
99 break;
100 case TSELECT:
101 case TFOR:
102 fptreef(shf, indent, "%s %s ",
103 (t->type == TFOR) ? "for" : Tselect, t->str);
104 if (t->vars != NULL) {
105 shf_puts("in ", shf);
106 w = (const char **)t->vars;
107 while (*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 w = (const char **)t1->vars;
119 while (*w) {
120 fptreef(shf, indent, "%S%c", *w,
121 (w[1] != NULL) ? '|' : ')');
122 ++w;
123 }
124 fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
125 t1->u.charflag);
126 }
127 fptreef(shf, indent, "%Nesac ");
128 break;
129 #ifndef MKSH_NO_DEPRECATED_WARNING
130 case TELIF:
131 internal_errorf("TELIF in tree.c:ptree() unexpected");
132 /* FALLTHROUGH */
133 #endif
134 case TIF:
135 i = 2;
136 goto process_TIF;
137 do {
138 t = t->right;
139 i = 0;
140 fptreef(shf, indent, "%;");
141 process_TIF:
142 /* 5 == strlen("elif ") */
143 fptreef(shf, indent + 5 - i, "elif %T" + i, t->left);
144 t = t->right;
145 if (t->left != NULL) {
146 fptreef(shf, indent, "%;");
147 fptreef(shf, indent + INDENT, "%s%N%T",
148 "then", t->left);
149 }
150 } while (t->right && t->right->type == TELIF);
151 if (t->right != NULL) {
152 fptreef(shf, indent, "%;");
153 fptreef(shf, indent + INDENT, "%s%N%T",
154 "else", t->right);
155 }
156 fptreef(shf, indent, "%;fi ");
157 break;
158 case TWHILE:
159 case TUNTIL:
160 /* 6 == strlen("while"/"until") */
161 fptreef(shf, indent + 6, "%s %T",
162 (t->type == TWHILE) ? "while" : "until",
163 t->left);
164 fptreef(shf, indent, "%;");
165 fptreef(shf, indent + INDENT, "do%N%T", t->right);
166 fptreef(shf, indent, "%;done ");
167 break;
168 case TBRACE:
169 fptreef(shf, indent + INDENT, "{%N%T", t->left);
170 fptreef(shf, indent, "%;} ");
171 break;
172 case TCOPROC:
173 fptreef(shf, indent, "%T|& ", t->left);
174 prevent_semicolon = true;
175 break;
176 case TASYNC:
177 fptreef(shf, indent, "%T& ", t->left);
178 prevent_semicolon = true;
179 break;
180 case TFUNCT:
181 fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
182 break;
183 case TTIME:
184 fptreef(shf, indent, "%s %T", "time", t->left);
185 break;
186 default:
187 shf_puts("<botch>", shf);
188 prevent_semicolon = false;
189 break;
190 }
191 if ((ioact = t->ioact) != NULL) {
192 bool need_nl = false;
193
194 while (*ioact != NULL)
195 pioact(shf, indent, *ioact++);
196 /* Print here documents after everything else... */
197 ioact = t->ioact;
198 while (*ioact != NULL) {
199 struct ioword *iop = *ioact++;
200
201 /* heredoc is NULL when tracing (set -x) */
202 if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
203 iop->heredoc) {
204 shf_putc('\n', shf);
205 shf_puts(iop->heredoc, shf);
206 fptreef(shf, indent, "%s",
207 iop->flag & IONDELIM ? "<<" :
208 evalstr(iop->delim, 0));
209 need_nl = true;
210 }
211 }
212 /*
213 * Last delimiter must be followed by a newline (this
214 * often leads to an extra blank line, but it's not
215 * worth worrying about)
216 */
217 if (need_nl)
218 shf_putc('\n', shf);
219 }
220 }
221
222 static void
pioact(struct shf * shf,int indent,struct ioword * iop)223 pioact(struct shf *shf, int indent, struct ioword *iop)
224 {
225 int flag = iop->flag;
226 int type = flag & IOTYPE;
227 int expected;
228
229 expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
230 (type == IOCAT || type == IOWRITE) ? 1 :
231 (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
232 iop->unit + 1;
233 if (iop->unit != expected)
234 shf_fprintf(shf, "%d", iop->unit);
235
236 switch (type) {
237 case IOREAD:
238 shf_puts("<", shf);
239 break;
240 case IOHERE:
241 shf_puts(flag & IOSKIP ? "<<-" : "<<", shf);
242 break;
243 case IOCAT:
244 shf_puts(">>", shf);
245 break;
246 case IOWRITE:
247 shf_puts(flag & IOCLOB ? ">|" : ">", shf);
248 break;
249 case IORDWR:
250 shf_puts("<>", shf);
251 break;
252 case IODUP:
253 shf_puts(flag & IORDUP ? "<&" : ">&", shf);
254 break;
255 }
256 /* name/delim are NULL when printing syntax errors */
257 if (type == IOHERE) {
258 if (iop->delim)
259 fptreef(shf, indent, "%S ", iop->delim);
260 else
261 shf_putc(' ', shf);
262 } else if (iop->name)
263 fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
264 iop->name);
265 prevent_semicolon = false;
266 }
267
268 /* variant of fputs for ptreef and wdstrip */
269 static const char *
wdvarput(struct shf * shf,const char * wp,int quotelevel,int opmode)270 wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
271 {
272 int c;
273
274 /*-
275 * problems:
276 * `...` -> $(...)
277 * 'foo' -> "foo"
278 * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
279 * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ
280 * could change encoding to:
281 * OQUOTE ["'] ... CQUOTE ["']
282 * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
283 */
284 while (/* CONSTCOND */ 1)
285 switch (*wp++) {
286 case EOS:
287 return (--wp);
288 case ADELIM:
289 case CHAR:
290 c = *wp++;
291 if ((opmode & WDS_MAGIC) &&
292 (ISMAGIC(c) || c == '[' || c == NOT ||
293 c == '-' || c == ']' || c == '*' || c == '?'))
294 shf_putc(MAGIC, shf);
295 shf_putc(c, shf);
296 break;
297 case QCHAR: {
298 bool doq;
299
300 c = *wp++;
301 doq = (c == '"' || c == '`' || c == '$' || c == '\\');
302 if (opmode & WDS_TPUTS) {
303 if (quotelevel == 0)
304 doq = true;
305 } else {
306 if (!(opmode & WDS_KEEPQ))
307 doq = false;
308 }
309 if (doq)
310 shf_putc('\\', shf);
311 shf_putc(c, shf);
312 break;
313 }
314 case COMSUB:
315 shf_puts("$(", shf);
316 while ((c = *wp++) != 0)
317 shf_putc(c, shf);
318 shf_putc(')', shf);
319 break;
320 case EXPRSUB:
321 shf_puts("$((", shf);
322 while ((c = *wp++) != 0)
323 shf_putc(c, shf);
324 shf_puts("))", shf);
325 break;
326 case OQUOTE:
327 if (opmode & WDS_TPUTS) {
328 quotelevel++;
329 shf_putc('"', shf);
330 }
331 break;
332 case CQUOTE:
333 if (opmode & WDS_TPUTS) {
334 if (quotelevel)
335 quotelevel--;
336 shf_putc('"', shf);
337 }
338 break;
339 case OSUBST:
340 shf_putc('$', shf);
341 if (*wp++ == '{')
342 shf_putc('{', shf);
343 while ((c = *wp++) != 0)
344 shf_putc(c, shf);
345 wp = wdvarput(shf, wp, 0, opmode);
346 break;
347 case CSUBST:
348 if (*wp++ == '}')
349 shf_putc('}', shf);
350 return (wp);
351 case OPAT:
352 if (opmode & WDS_MAGIC) {
353 shf_putc(MAGIC, shf);
354 shf_putchar(*wp++ | 0x80, shf);
355 } else {
356 shf_putchar(*wp++, shf);
357 shf_putc('(', shf);
358 }
359 break;
360 case SPAT:
361 c = '|';
362 if (0)
363 case CPAT:
364 c = /*(*/ ')';
365 if (opmode & WDS_MAGIC)
366 shf_putc(MAGIC, shf);
367 shf_putc(c, shf);
368 break;
369 }
370 }
371
372 /*
373 * this is the _only_ way to reliably handle
374 * variable args with an ANSI compiler
375 */
376 /* VARARGS */
377 void
fptreef(struct shf * shf,int indent,const char * fmt,...)378 fptreef(struct shf *shf, int indent, const char *fmt, ...)
379 {
380 va_list va;
381
382 va_start(va, fmt);
383 vfptreef(shf, indent, fmt, va);
384 va_end(va);
385 }
386
387 /* VARARGS */
388 char *
snptreef(char * s,ssize_t n,const char * fmt,...)389 snptreef(char *s, ssize_t n, const char *fmt, ...)
390 {
391 va_list va;
392 struct shf shf;
393
394 shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
395
396 va_start(va, fmt);
397 vfptreef(&shf, 0, fmt, va);
398 va_end(va);
399
400 /* shf_sclose NUL terminates */
401 return (shf_sclose(&shf));
402 }
403
404 static void
vfptreef(struct shf * shf,int indent,const char * fmt,va_list va)405 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
406 {
407 int c;
408
409 while ((c = *fmt++)) {
410 if (c == '%') {
411 switch ((c = *fmt++)) {
412 case 'c':
413 /* character (octet, probably) */
414 shf_putchar(va_arg(va, int), shf);
415 break;
416 case 's':
417 /* string */
418 shf_puts(va_arg(va, char *), shf);
419 break;
420 case 'S':
421 /* word */
422 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
423 break;
424 case 'd':
425 /* signed decimal */
426 shf_fprintf(shf, "%d", va_arg(va, int));
427 break;
428 case 'u':
429 /* unsigned decimal */
430 shf_fprintf(shf, "%u", va_arg(va, unsigned int));
431 break;
432 case 'T':
433 /* format tree */
434 ptree(va_arg(va, struct op *), indent, shf);
435 goto dont_trash_prevent_semicolon;
436 case ';':
437 /* newline or ; */
438 case 'N':
439 /* newline or space */
440 if (shf->flags & SHF_STRING) {
441 if (c == ';' && !prevent_semicolon)
442 shf_putc(';', shf);
443 shf_putc(' ', shf);
444 } else {
445 int i;
446
447 shf_putc('\n', shf);
448 i = indent;
449 while (i >= 8) {
450 shf_putc('\t', shf);
451 i -= 8;
452 }
453 while (i--)
454 shf_putc(' ', shf);
455 }
456 break;
457 case 'R':
458 /* I/O redirection */
459 pioact(shf, indent, va_arg(va, struct ioword *));
460 break;
461 default:
462 shf_putc(c, shf);
463 break;
464 }
465 } else
466 shf_putc(c, shf);
467 prevent_semicolon = false;
468 dont_trash_prevent_semicolon:
469 ;
470 }
471 }
472
473 /*
474 * copy tree (for function definition)
475 */
476 struct op *
tcopy(struct op * t,Area * ap)477 tcopy(struct op *t, Area *ap)
478 {
479 struct op *r;
480 const char **tw;
481 char **rw;
482
483 if (t == NULL)
484 return (NULL);
485
486 r = alloc(sizeof(struct op), ap);
487
488 r->type = t->type;
489 r->u.evalflags = t->u.evalflags;
490
491 if (t->type == TCASE)
492 r->str = wdcopy(t->str, ap);
493 else
494 strdupx(r->str, t->str, ap);
495
496 if (t->vars == NULL)
497 r->vars = NULL;
498 else {
499 tw = (const char **)t->vars;
500 while (*tw)
501 ++tw;
502 rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
503 sizeof(*tw), ap);
504 tw = (const char **)t->vars;
505 while (*tw)
506 *rw++ = wdcopy(*tw++, ap);
507 *rw = NULL;
508 }
509
510 if (t->args == NULL)
511 r->args = NULL;
512 else {
513 tw = t->args;
514 while (*tw)
515 ++tw;
516 r->args = (const char **)(rw = alloc2(tw - t->args + 1,
517 sizeof(*tw), ap));
518 tw = t->args;
519 while (*tw)
520 *rw++ = wdcopy(*tw++, ap);
521 *rw = NULL;
522 }
523
524 r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
525
526 r->left = tcopy(t->left, ap);
527 r->right = tcopy(t->right, ap);
528 r->lineno = t->lineno;
529
530 return (r);
531 }
532
533 char *
wdcopy(const char * wp,Area * ap)534 wdcopy(const char *wp, Area *ap)
535 {
536 size_t len;
537
538 len = wdscan(wp, EOS) - wp;
539 return (memcpy(alloc(len, ap), wp, len));
540 }
541
542 /* return the position of prefix c in wp plus 1 */
543 const char *
wdscan(const char * wp,int c)544 wdscan(const char *wp, int c)
545 {
546 int nest = 0;
547
548 while (/* CONSTCOND */ 1)
549 switch (*wp++) {
550 case EOS:
551 return (wp);
552 case ADELIM:
553 if (c == ADELIM)
554 return (wp + 1);
555 /* FALLTHROUGH */
556 case CHAR:
557 case QCHAR:
558 wp++;
559 break;
560 case COMSUB:
561 case EXPRSUB:
562 while (*wp++ != 0)
563 ;
564 break;
565 case OQUOTE:
566 case CQUOTE:
567 break;
568 case OSUBST:
569 nest++;
570 while (*wp++ != '\0')
571 ;
572 break;
573 case CSUBST:
574 wp++;
575 if (c == CSUBST && nest == 0)
576 return (wp);
577 nest--;
578 break;
579 case OPAT:
580 nest++;
581 wp++;
582 break;
583 case SPAT:
584 case CPAT:
585 if (c == wp[-1] && nest == 0)
586 return (wp);
587 if (wp[-1] == CPAT)
588 nest--;
589 break;
590 default:
591 internal_warningf(
592 "wdscan: unknown char 0x%x (carrying on)",
593 wp[-1]);
594 }
595 }
596
597 /*
598 * return a copy of wp without any of the mark up characters and with
599 * quote characters (" ' \) stripped. (string is allocated from ATEMP)
600 */
601 char *
wdstrip(const char * wp,int opmode)602 wdstrip(const char *wp, int opmode)
603 {
604 struct shf shf;
605
606 shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
607 wdvarput(&shf, wp, 0, opmode);
608 /* shf_sclose NUL terminates */
609 return (shf_sclose(&shf));
610 }
611
612 static struct ioword **
iocopy(struct ioword ** iow,Area * ap)613 iocopy(struct ioword **iow, Area *ap)
614 {
615 struct ioword **ior;
616 int i;
617
618 ior = iow;
619 while (*ior)
620 ++ior;
621 ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
622
623 for (i = 0; iow[i] != NULL; i++) {
624 struct ioword *p, *q;
625
626 p = iow[i];
627 q = alloc(sizeof(struct ioword), ap);
628 ior[i] = q;
629 *q = *p;
630 if (p->name != NULL)
631 q->name = wdcopy(p->name, ap);
632 if (p->delim != NULL)
633 q->delim = wdcopy(p->delim, ap);
634 if (p->heredoc != NULL)
635 strdupx(q->heredoc, p->heredoc, ap);
636 }
637 ior[i] = NULL;
638
639 return (ior);
640 }
641
642 /*
643 * free tree (for function definition)
644 */
645 void
tfree(struct op * t,Area * ap)646 tfree(struct op *t, Area *ap)
647 {
648 char **w;
649
650 if (t == NULL)
651 return;
652
653 if (t->str != NULL)
654 afree(t->str, ap);
655
656 if (t->vars != NULL) {
657 for (w = t->vars; *w != NULL; w++)
658 afree(*w, ap);
659 afree(t->vars, ap);
660 }
661
662 if (t->args != NULL) {
663 /*XXX we assume the caller is right */
664 union mksh_ccphack cw;
665
666 cw.ro = t->args;
667 for (w = cw.rw; *w != NULL; w++)
668 afree(*w, ap);
669 afree(t->args, ap);
670 }
671
672 if (t->ioact != NULL)
673 iofree(t->ioact, ap);
674
675 tfree(t->left, ap);
676 tfree(t->right, ap);
677
678 afree(t, ap);
679 }
680
681 static void
iofree(struct ioword ** iow,Area * ap)682 iofree(struct ioword **iow, Area *ap)
683 {
684 struct ioword **iop;
685 struct ioword *p;
686
687 iop = iow;
688 while ((p = *iop++) != NULL) {
689 if (p->name != NULL)
690 afree(p->name, ap);
691 if (p->delim != NULL)
692 afree(p->delim, ap);
693 if (p->heredoc != NULL)
694 afree(p->heredoc, ap);
695 afree(p, ap);
696 }
697 afree(iow, ap);
698 }
699
700 void
fpFUNCTf(struct shf * shf,int i,bool isksh,const char * k,struct op * v)701 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
702 {
703 if (isksh)
704 fptreef(shf, i, "%s %s %T", Tfunction, k, v);
705 else
706 fptreef(shf, i, "%s() %T", k, v);
707 }
708
709
710 /* for jobs.c */
711 void
vistree(char * dst,size_t sz,struct op * t)712 vistree(char *dst, size_t sz, struct op *t)
713 {
714 int c;
715 char *cp, *buf;
716
717 buf = alloc(sz, ATEMP);
718 snptreef(buf, sz, "%T", t);
719 cp = buf;
720 while ((c = *cp++)) {
721 if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
722 /* C0 or C1 control character or DEL */
723 if (!--sz)
724 break;
725 *dst++ = (c & 0x80) ? '$' : '^';
726 c = (c & 0x7F) ^ 0x40;
727 }
728 if (!--sz)
729 break;
730 *dst++ = c;
731 }
732 *dst = '\0';
733 afree(buf, ATEMP);
734 }
735
736 #ifdef DEBUG
737 void
dumpchar(struct shf * shf,int c)738 dumpchar(struct shf *shf, int c)
739 {
740 if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
741 /* C0 or C1 control character or DEL */
742 shf_putc((c & 0x80) ? '$' : '^', shf);
743 c = (c & 0x7F) ^ 0x40;
744 }
745 shf_putc(c, shf);
746 }
747
748 /* see: wdvarput */
749 static const char *
dumpwdvar_(struct shf * shf,const char * wp,int quotelevel)750 dumpwdvar_(struct shf *shf, const char *wp, int quotelevel)
751 {
752 int c;
753
754 while (/* CONSTCOND */ 1) {
755 switch(*wp++) {
756 case EOS:
757 shf_puts("EOS", shf);
758 return (--wp);
759 case ADELIM:
760 shf_puts("ADELIM=", shf);
761 if (0)
762 case CHAR:
763 shf_puts("CHAR=", shf);
764 dumpchar(shf, *wp++);
765 break;
766 case QCHAR:
767 shf_puts("QCHAR<", shf);
768 c = *wp++;
769 if (quotelevel == 0 ||
770 (c == '"' || c == '`' || c == '$' || c == '\\'))
771 shf_putc('\\', shf);
772 dumpchar(shf, c);
773 goto closeandout;
774 case COMSUB:
775 shf_puts("COMSUB<", shf);
776 dumpsub:
777 while ((c = *wp++) != 0)
778 dumpchar(shf, c);
779 closeandout:
780 shf_putc('>', shf);
781 break;
782 case EXPRSUB:
783 shf_puts("EXPRSUB<", shf);
784 goto dumpsub;
785 case OQUOTE:
786 shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
787 break;
788 case CQUOTE:
789 shf_fprintf(shf, "%d}CQUOTE", quotelevel);
790 if (quotelevel)
791 quotelevel--;
792 else
793 shf_puts("(err)", shf);
794 break;
795 case OSUBST:
796 shf_puts("OSUBST(", shf);
797 dumpchar(shf, *wp++);
798 shf_puts(")[", shf);
799 while ((c = *wp++) != 0)
800 dumpchar(shf, c);
801 shf_putc('|', shf);
802 wp = dumpwdvar_(shf, wp, 0);
803 break;
804 case CSUBST:
805 shf_puts("]CSUBST(", shf);
806 dumpchar(shf, *wp++);
807 shf_putc(')', shf);
808 return (wp);
809 case OPAT:
810 shf_puts("OPAT=", shf);
811 dumpchar(shf, *wp++);
812 break;
813 case SPAT:
814 shf_puts("SPAT", shf);
815 break;
816 case CPAT:
817 shf_puts("CPAT", shf);
818 break;
819 default:
820 shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
821 break;
822 }
823 shf_putc(' ', shf);
824 }
825 }
826 void
dumpwdvar(struct shf * shf,const char * wp)827 dumpwdvar(struct shf *shf, const char *wp)
828 {
829 dumpwdvar_(shf, wp, 0);
830 }
831
832 void
dumptree(struct shf * shf,struct op * t)833 dumptree(struct shf *shf, struct op *t)
834 {
835 int i;
836 const char **w, *name;
837 struct op *t1;
838 static int nesting = 0;
839
840 for (i = 0; i < nesting; ++i)
841 shf_putc('\t', shf);
842 ++nesting;
843 shf_puts("{tree:" /*}*/, shf);
844 if (t == NULL) {
845 name = "(null)";
846 goto out;
847 }
848 switch (t->type) {
849 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
850
851 OPEN(TCOM)
852 if (t->vars) {
853 i = 0;
854 w = (const char **)t->vars;
855 while (*w) {
856 shf_putc('\n', shf);
857 for (int j = 0; j < nesting; ++j)
858 shf_putc('\t', shf);
859 shf_fprintf(shf, " var%d<", i++);
860 dumpwdvar(shf, *w++);
861 shf_putc('>', shf);
862 }
863 } else
864 shf_puts(" #no-vars#", shf);
865 if (t->args) {
866 i = 0;
867 w = t->args;
868 while (*w) {
869 shf_putc('\n', shf);
870 for (int j = 0; j < nesting; ++j)
871 shf_putc('\t', shf);
872 shf_fprintf(shf, " arg%d<", i++);
873 dumpwdvar(shf, *w++);
874 shf_putc('>', shf);
875 }
876 } else
877 shf_puts(" #no-args#", shf);
878 break;
879 OPEN(TEXEC)
880 dumpleftandout:
881 t = t->left;
882 dumpandout:
883 shf_putc('\n', shf);
884 dumptree(shf, t);
885 break;
886 OPEN(TPAREN)
887 goto dumpleftandout;
888 OPEN(TPIPE)
889 dumpleftmidrightandout:
890 shf_putc('\n', shf);
891 dumptree(shf, t->left);
892 /* middumprightandout: (unused) */
893 shf_fprintf(shf, "/%s:", name);
894 dumprightandout:
895 t = t->right;
896 goto dumpandout;
897 OPEN(TLIST)
898 goto dumpleftmidrightandout;
899 OPEN(TOR)
900 goto dumpleftmidrightandout;
901 OPEN(TAND)
902 goto dumpleftmidrightandout;
903 OPEN(TBANG)
904 goto dumprightandout;
905 OPEN(TDBRACKET)
906 i = 0;
907 w = t->args;
908 while (*w) {
909 shf_putc('\n', shf);
910 for (int j = 0; j < nesting; ++j)
911 shf_putc('\t', shf);
912 shf_fprintf(shf, " arg%d<", i++);
913 dumpwdvar(shf, *w++);
914 shf_putc('>', shf);
915 }
916 break;
917 OPEN(TFOR)
918 dumpfor:
919 shf_fprintf(shf, " str<%s>", t->str);
920 if (t->vars != NULL) {
921 i = 0;
922 w = (const char **)t->vars;
923 while (*w) {
924 shf_putc('\n', shf);
925 for (int j = 0; j < nesting; ++j)
926 shf_putc('\t', shf);
927 shf_fprintf(shf, " var%d<", i++);
928 dumpwdvar(shf, *w++);
929 shf_putc('>', shf);
930 }
931 }
932 goto dumpleftandout;
933 OPEN(TSELECT)
934 goto dumpfor;
935 OPEN(TCASE)
936 shf_fprintf(shf, " str<%s>", t->str);
937 i = 0;
938 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
939 shf_putc('\n', shf);
940 for (int j = 0; j < nesting; ++j)
941 shf_putc('\t', shf);
942 shf_fprintf(shf, " sub%d[(", i);
943 w = (const char **)t1->vars;
944 while (*w) {
945 dumpwdvar(shf, *w);
946 if (w[1] != NULL)
947 shf_putc('|', shf);
948 ++w;
949 }
950 shf_putc(')', shf);
951 shf_putc('\n', shf);
952 dumptree(shf, t1->left);
953 shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
954 }
955 break;
956 OPEN(TWHILE)
957 goto dumpleftmidrightandout;
958 OPEN(TUNTIL)
959 goto dumpleftmidrightandout;
960 OPEN(TBRACE)
961 goto dumpleftandout;
962 OPEN(TCOPROC)
963 goto dumpleftandout;
964 OPEN(TASYNC)
965 goto dumpleftandout;
966 OPEN(TFUNCT)
967 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
968 t->u.ksh_func ? "yes" : "no");
969 goto dumpleftandout;
970 OPEN(TTIME)
971 goto dumpleftandout;
972 OPEN(TIF)
973 dumpif:
974 shf_putc('\n', shf);
975 dumptree(shf, t->left);
976 t = t->right;
977 if (t->left != NULL) {
978 shf_puts(" /TTHEN:\n", shf);
979 dumptree(shf, t->left);
980 }
981 if (t->right && t->right->type == TELIF) {
982 shf_puts(" /TELIF:", shf);
983 t = t->right;
984 goto dumpif;
985 }
986 if (t->right != NULL) {
987 shf_puts(" /TELSE:\n", shf);
988 dumptree(shf, t->right);
989 }
990 break;
991 OPEN(TEOF)
992 dumpunexpected:
993 shf_puts("unexpected", shf);
994 break;
995 OPEN(TELIF)
996 goto dumpunexpected;
997 OPEN(TPAT)
998 goto dumpunexpected;
999 default:
1000 name = "TINVALID";
1001 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1002 goto dumpunexpected;
1003
1004 #undef OPEN
1005 }
1006 out:
1007 shf_fprintf(shf, /*{*/ " /%s}\n", name);
1008 --nesting;
1009 }
1010 #endif
1011