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 (at) 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 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 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 * 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 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 * 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 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 * 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 * 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 * 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 * 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 ** 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 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 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 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 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 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 * 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 827 dumpwdvar(struct shf *shf, const char *wp) 828 { 829 dumpwdvar_(shf, wp, 0); 830 } 831 832 void 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