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