1 /* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */ 2 3 /*- 4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 5 * 2011, 2012, 2013, 2015, 2016 6 * mirabilos <m (at) mirbsd.org> 7 * 8 * Provided that these terms and disclaimer and all copyright notices 9 * are retained or reproduced in an accompanying document, permission 10 * is granted to deal in this work without restriction, including un- 11 * limited rights to use, publicly perform, distribute, sell, modify, 12 * merge, give away, or sublicence. 13 * 14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 15 * the utmost extent permitted by applicable law, neither express nor 16 * implied; without malicious intent or gross negligence. In no event 17 * may a licensor, author or contributor be held liable for indirect, 18 * direct, other damage, loss, or other issues arising in any way out 19 * of dealing in the work, even if advised of the possibility of such 20 * damage or existence of a defect, except proven that it results out 21 * of said person's immediate fault when using the work as intended. 22 */ 23 24 #include "sh.h" 25 26 __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.86 2016/07/25 00:04:48 tg Exp $"); 27 28 #define INDENT 8 29 30 static void ptree(struct op *, int, struct shf *); 31 static void pioact(struct shf *, struct ioword *); 32 static const char *wdvarput(struct shf *, const char *, int, int); 33 static void vfptreef(struct shf *, int, const char *, va_list); 34 static struct ioword **iocopy(struct ioword **, Area *); 35 static void iofree(struct ioword **, Area *); 36 37 /* "foo& ; bar" and "foo |& ; bar" are invalid */ 38 static bool prevent_semicolon; 39 40 static const char Telif_pT[] = "elif %T"; 41 42 /* 43 * print a command tree 44 */ 45 static void 46 ptree(struct op *t, int indent, struct shf *shf) 47 { 48 const char **w; 49 struct ioword **ioact; 50 struct op *t1; 51 int i; 52 const char *ccp; 53 54 Chain: 55 if (t == NULL) 56 return; 57 switch (t->type) { 58 case TCOM: 59 prevent_semicolon = false; 60 /* special-case 'var=<<EOF' (cf. exec.c:execute) */ 61 if ( 62 /* we have zero arguments, i.e. no program to run */ 63 t->args[0] == NULL && 64 /* we have exactly one variable assignment */ 65 t->vars[0] != NULL && t->vars[1] == NULL && 66 /* we have exactly one I/O redirection */ 67 t->ioact != NULL && t->ioact[0] != NULL && 68 t->ioact[1] == NULL && 69 /* of type "here document" (or "here string") */ 70 (t->ioact[0]->ioflag & IOTYPE) == IOHERE && 71 /* the variable assignment begins with a valid varname */ 72 (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && 73 /* and has no right-hand side (i.e. "varname=") */ 74 ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) || 75 /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR && 76 ccp[3] == '=' && ccp[4] == EOS))) { 77 fptreef(shf, indent, Tf_S, t->vars[0]); 78 break; 79 } 80 81 if (t->vars) { 82 w = (const char **)t->vars; 83 while (*w) 84 fptreef(shf, indent, Tf_S_, *w++); 85 } else 86 shf_puts("#no-vars# ", shf); 87 if (t->args) { 88 w = t->args; 89 while (*w) 90 fptreef(shf, indent, Tf_S_, *w++); 91 } else 92 shf_puts("#no-args# ", shf); 93 break; 94 case TEXEC: 95 t = t->left; 96 goto Chain; 97 case TPAREN: 98 fptreef(shf, indent + 2, "( %T) ", t->left); 99 break; 100 case TPIPE: 101 fptreef(shf, indent, "%T| ", t->left); 102 t = t->right; 103 goto Chain; 104 case TLIST: 105 fptreef(shf, indent, "%T%;", t->left); 106 t = t->right; 107 goto Chain; 108 case TOR: 109 case TAND: 110 fptreef(shf, indent, "%T%s %T", 111 t->left, (t->type == TOR) ? "||" : "&&", t->right); 112 break; 113 case TBANG: 114 shf_puts("! ", shf); 115 prevent_semicolon = false; 116 t = t->right; 117 goto Chain; 118 case TDBRACKET: 119 w = t->args; 120 shf_puts("[[", shf); 121 while (*w) 122 fptreef(shf, indent, Tf__S, *w++); 123 shf_puts(" ]] ", shf); 124 break; 125 case TSELECT: 126 case TFOR: 127 fptreef(shf, indent, "%s %s ", 128 (t->type == TFOR) ? "for" : Tselect, t->str); 129 if (t->vars != NULL) { 130 shf_puts("in ", shf); 131 w = (const char **)t->vars; 132 while (*w) 133 fptreef(shf, indent, Tf_S_, *w++); 134 fptreef(shf, indent, Tft_end); 135 } 136 fptreef(shf, indent + INDENT, "do%N%T", t->left); 137 fptreef(shf, indent, "%;done "); 138 break; 139 case TCASE: 140 fptreef(shf, indent, "case %S in", t->str); 141 for (t1 = t->left; t1 != NULL; t1 = t1->right) { 142 fptreef(shf, indent, "%N("); 143 w = (const char **)t1->vars; 144 while (*w) { 145 fptreef(shf, indent, "%S%c", *w, 146 (w[1] != NULL) ? '|' : ')'); 147 ++w; 148 } 149 fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left, 150 t1->u.charflag); 151 } 152 fptreef(shf, indent, "%Nesac "); 153 break; 154 case TELIF: 155 internal_errorf(TELIF_unexpected); 156 /* FALLTHROUGH */ 157 case TIF: 158 i = 2; 159 t1 = t; 160 goto process_TIF; 161 do { 162 t1 = t1->right; 163 i = 0; 164 fptreef(shf, indent, Tft_end); 165 process_TIF: 166 /* 5 == strlen("elif ") */ 167 fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left); 168 t1 = t1->right; 169 if (t1->left != NULL) { 170 fptreef(shf, indent, Tft_end); 171 fptreef(shf, indent + INDENT, "%s%N%T", 172 "then", t1->left); 173 } 174 } while (t1->right && t1->right->type == TELIF); 175 if (t1->right != NULL) { 176 fptreef(shf, indent, Tft_end); 177 fptreef(shf, indent + INDENT, "%s%N%T", 178 "else", t1->right); 179 } 180 fptreef(shf, indent, "%;fi "); 181 break; 182 case TWHILE: 183 case TUNTIL: 184 /* 6 == strlen("while "/"until ") */ 185 fptreef(shf, indent + 6, Tf_s_T, 186 (t->type == TWHILE) ? "while" : "until", 187 t->left); 188 fptreef(shf, indent, Tft_end); 189 fptreef(shf, indent + INDENT, "do%N%T", t->right); 190 fptreef(shf, indent, "%;done "); 191 break; 192 case TBRACE: 193 fptreef(shf, indent + INDENT, "{%N%T", t->left); 194 fptreef(shf, indent, "%;} "); 195 break; 196 case TCOPROC: 197 fptreef(shf, indent, "%T|& ", t->left); 198 prevent_semicolon = true; 199 break; 200 case TASYNC: 201 fptreef(shf, indent, "%T& ", t->left); 202 prevent_semicolon = true; 203 break; 204 case TFUNCT: 205 fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left); 206 break; 207 case TTIME: 208 fptreef(shf, indent, Tf_s_T, Ttime, t->left); 209 break; 210 default: 211 shf_puts("<botch>", shf); 212 prevent_semicolon = false; 213 break; 214 } 215 if ((ioact = t->ioact) != NULL) { 216 bool need_nl = false; 217 218 while (*ioact != NULL) 219 pioact(shf, *ioact++); 220 /* Print here documents after everything else... */ 221 ioact = t->ioact; 222 while (*ioact != NULL) { 223 struct ioword *iop = *ioact++; 224 225 /* heredoc is NULL when tracing (set -x) */ 226 if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE && 227 iop->heredoc) { 228 shf_putc('\n', shf); 229 shf_puts(iop->heredoc, shf); 230 fptreef(shf, indent, Tf_s, 231 evalstr(iop->delim, 0)); 232 need_nl = true; 233 } 234 } 235 /* 236 * Last delimiter must be followed by a newline (this 237 * often leads to an extra blank line, but it's not 238 * worth worrying about) 239 */ 240 if (need_nl) { 241 shf_putc('\n', shf); 242 prevent_semicolon = true; 243 } 244 } 245 } 246 247 static void 248 pioact(struct shf *shf, struct ioword *iop) 249 { 250 unsigned short flag = iop->ioflag; 251 unsigned short type = flag & IOTYPE; 252 short expected; 253 254 expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : 255 (type == IOCAT || type == IOWRITE) ? 1 : 256 (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : 257 iop->unit + 1; 258 if (iop->unit != expected) 259 shf_fprintf(shf, Tf_d, (int)iop->unit); 260 261 switch (type) { 262 case IOREAD: 263 shf_putc('<', shf); 264 break; 265 case IOHERE: 266 shf_puts("<<", shf); 267 if (flag & IOSKIP) 268 shf_putc('-', shf); 269 else if (flag & IOHERESTR) 270 shf_putc('<', shf); 271 break; 272 case IOCAT: 273 shf_puts(">>", shf); 274 break; 275 case IOWRITE: 276 shf_putc('>', shf); 277 if (flag & IOCLOB) 278 shf_putc('|', shf); 279 break; 280 case IORDWR: 281 shf_puts("<>", shf); 282 break; 283 case IODUP: 284 shf_puts(flag & IORDUP ? "<&" : ">&", shf); 285 break; 286 } 287 /* name/delim are NULL when printing syntax errors */ 288 if (type == IOHERE) { 289 if (iop->delim && !(iop->ioflag & IONDELIM)) 290 wdvarput(shf, iop->delim, 0, WDS_TPUTS); 291 } else if (iop->ioname) { 292 if (flag & IONAMEXP) 293 print_value_quoted(shf, iop->ioname); 294 else 295 wdvarput(shf, iop->ioname, 0, WDS_TPUTS); 296 } 297 shf_putc(' ', shf); 298 prevent_semicolon = false; 299 } 300 301 /* variant of fputs for ptreef and wdstrip */ 302 static const char * 303 wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) 304 { 305 int c; 306 const char *cs; 307 308 /*- 309 * problems: 310 * `...` -> $(...) 311 * 'foo' -> "foo" 312 * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS 313 * x${foo:-'hi'} -> x${foo:-hi} 314 * could change encoding to: 315 * OQUOTE ["'] ... CQUOTE ["'] 316 * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) 317 */ 318 while (/* CONSTCOND */ 1) 319 switch (*wp++) { 320 case EOS: 321 return (--wp); 322 case ADELIM: 323 if (*wp == /*{*/'}') { 324 ++wp; 325 goto wdvarput_csubst; 326 } 327 /* FALLTHROUGH */ 328 case CHAR: 329 c = *wp++; 330 shf_putc(c, shf); 331 break; 332 case QCHAR: 333 c = *wp++; 334 if (opmode & WDS_TPUTS) 335 switch (c) { 336 case '\n': 337 if (quotelevel == 0) { 338 c = '\''; 339 shf_putc(c, shf); 340 shf_putc('\n', shf); 341 } 342 break; 343 default: 344 if (quotelevel == 0) 345 /* FALLTHROUGH */ 346 case '"': 347 case '`': 348 case '$': 349 case '\\': 350 shf_putc('\\', shf); 351 break; 352 } 353 shf_putc(c, shf); 354 break; 355 case COMSUB: 356 shf_puts("$(", shf); 357 cs = ")"; 358 pSUB: 359 while ((c = *wp++) != 0) 360 shf_putc(c, shf); 361 shf_puts(cs, shf); 362 break; 363 case FUNSUB: 364 c = ' '; 365 if (0) 366 /* FALLTHROUGH */ 367 case VALSUB: 368 c = '|'; 369 shf_putc('$', shf); 370 shf_putc('{', shf); 371 shf_putc(c, shf); 372 cs = ";}"; 373 goto pSUB; 374 case EXPRSUB: 375 shf_puts("$((", shf); 376 cs = "))"; 377 goto pSUB; 378 case OQUOTE: 379 if (opmode & WDS_TPUTS) { 380 quotelevel++; 381 shf_putc('"', shf); 382 } 383 break; 384 case CQUOTE: 385 if (opmode & WDS_TPUTS) { 386 if (quotelevel) 387 quotelevel--; 388 shf_putc('"', shf); 389 } 390 break; 391 case OSUBST: 392 shf_putc('$', shf); 393 if (*wp++ == '{') 394 shf_putc('{', shf); 395 while ((c = *wp++) != 0) 396 shf_putc(c, shf); 397 wp = wdvarput(shf, wp, 0, opmode); 398 break; 399 case CSUBST: 400 if (*wp++ == '}') { 401 wdvarput_csubst: 402 shf_putc('}', shf); 403 } 404 return (wp); 405 case OPAT: 406 shf_putchar(*wp++, shf); 407 shf_putc('(', shf); 408 break; 409 case SPAT: 410 c = '|'; 411 if (0) 412 case CPAT: 413 c = /*(*/ ')'; 414 shf_putc(c, shf); 415 break; 416 } 417 } 418 419 /* 420 * this is the _only_ way to reliably handle 421 * variable args with an ANSI compiler 422 */ 423 /* VARARGS */ 424 void 425 fptreef(struct shf *shf, int indent, const char *fmt, ...) 426 { 427 va_list va; 428 429 va_start(va, fmt); 430 vfptreef(shf, indent, fmt, va); 431 va_end(va); 432 } 433 434 /* VARARGS */ 435 char * 436 snptreef(char *s, ssize_t n, const char *fmt, ...) 437 { 438 va_list va; 439 struct shf shf; 440 441 shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf); 442 443 va_start(va, fmt); 444 vfptreef(&shf, 0, fmt, va); 445 va_end(va); 446 447 /* shf_sclose NUL terminates */ 448 return (shf_sclose(&shf)); 449 } 450 451 static void 452 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va) 453 { 454 int c; 455 456 while ((c = *fmt++)) { 457 if (c == '%') { 458 switch ((c = *fmt++)) { 459 case 'c': 460 /* character (octet, probably) */ 461 shf_putchar(va_arg(va, int), shf); 462 break; 463 case 's': 464 /* string */ 465 shf_puts(va_arg(va, char *), shf); 466 break; 467 case 'S': 468 /* word */ 469 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS); 470 break; 471 case 'd': 472 /* signed decimal */ 473 shf_fprintf(shf, Tf_d, va_arg(va, int)); 474 break; 475 case 'u': 476 /* unsigned decimal */ 477 shf_fprintf(shf, "%u", va_arg(va, unsigned int)); 478 break; 479 case 'T': 480 /* format tree */ 481 ptree(va_arg(va, struct op *), indent, shf); 482 goto dont_trash_prevent_semicolon; 483 case ';': 484 /* newline or ; */ 485 case 'N': 486 /* newline or space */ 487 if (shf->flags & SHF_STRING) { 488 if (c == ';' && !prevent_semicolon) 489 shf_putc(';', shf); 490 shf_putc(' ', shf); 491 } else { 492 int i; 493 494 shf_putc('\n', shf); 495 i = indent; 496 while (i >= 8) { 497 shf_putc('\t', shf); 498 i -= 8; 499 } 500 while (i--) 501 shf_putc(' ', shf); 502 } 503 break; 504 case 'R': 505 /* I/O redirection */ 506 pioact(shf, va_arg(va, struct ioword *)); 507 break; 508 default: 509 shf_putc(c, shf); 510 break; 511 } 512 } else 513 shf_putc(c, shf); 514 prevent_semicolon = false; 515 dont_trash_prevent_semicolon: 516 ; 517 } 518 } 519 520 /* 521 * copy tree (for function definition) 522 */ 523 struct op * 524 tcopy(struct op *t, Area *ap) 525 { 526 struct op *r; 527 const char **tw; 528 char **rw; 529 530 if (t == NULL) 531 return (NULL); 532 533 r = alloc(sizeof(struct op), ap); 534 535 r->type = t->type; 536 r->u.evalflags = t->u.evalflags; 537 538 if (t->type == TCASE) 539 r->str = wdcopy(t->str, ap); 540 else 541 strdupx(r->str, t->str, ap); 542 543 if (t->vars == NULL) 544 r->vars = NULL; 545 else { 546 tw = (const char **)t->vars; 547 while (*tw) 548 ++tw; 549 rw = r->vars = alloc2(tw - (const char **)t->vars + 1, 550 sizeof(*tw), ap); 551 tw = (const char **)t->vars; 552 while (*tw) 553 *rw++ = wdcopy(*tw++, ap); 554 *rw = NULL; 555 } 556 557 if (t->args == NULL) 558 r->args = NULL; 559 else { 560 tw = t->args; 561 while (*tw) 562 ++tw; 563 r->args = (const char **)(rw = alloc2(tw - t->args + 1, 564 sizeof(*tw), ap)); 565 tw = t->args; 566 while (*tw) 567 *rw++ = wdcopy(*tw++, ap); 568 *rw = NULL; 569 } 570 571 r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap); 572 573 r->left = tcopy(t->left, ap); 574 r->right = tcopy(t->right, ap); 575 r->lineno = t->lineno; 576 577 return (r); 578 } 579 580 char * 581 wdcopy(const char *wp, Area *ap) 582 { 583 size_t len; 584 585 len = wdscan(wp, EOS) - wp; 586 return (memcpy(alloc(len, ap), wp, len)); 587 } 588 589 /* return the position of prefix c in wp plus 1 */ 590 const char * 591 wdscan(const char *wp, int c) 592 { 593 int nest = 0; 594 595 while (/* CONSTCOND */ 1) 596 switch (*wp++) { 597 case EOS: 598 return (wp); 599 case ADELIM: 600 if (c == ADELIM && nest == 0) 601 return (wp + 1); 602 if (*wp == /*{*/'}') 603 goto wdscan_csubst; 604 /* FALLTHROUGH */ 605 case CHAR: 606 case QCHAR: 607 wp++; 608 break; 609 case COMSUB: 610 case FUNSUB: 611 case VALSUB: 612 case EXPRSUB: 613 while (*wp++ != 0) 614 ; 615 break; 616 case OQUOTE: 617 case CQUOTE: 618 break; 619 case OSUBST: 620 nest++; 621 while (*wp++ != '\0') 622 ; 623 break; 624 case CSUBST: 625 wdscan_csubst: 626 wp++; 627 if (c == CSUBST && nest == 0) 628 return (wp); 629 nest--; 630 break; 631 case OPAT: 632 nest++; 633 wp++; 634 break; 635 case SPAT: 636 case CPAT: 637 if (c == wp[-1] && nest == 0) 638 return (wp); 639 if (wp[-1] == CPAT) 640 nest--; 641 break; 642 default: 643 internal_warningf( 644 "wdscan: unknown char 0x%X (carrying on)", 645 (unsigned char)wp[-1]); 646 } 647 } 648 649 /* 650 * return a copy of wp without any of the mark up characters and with 651 * quote characters (" ' \) stripped. (string is allocated from ATEMP) 652 */ 653 char * 654 wdstrip(const char *wp, int opmode) 655 { 656 struct shf shf; 657 658 shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf); 659 wdvarput(&shf, wp, 0, opmode); 660 /* shf_sclose NUL terminates */ 661 return (shf_sclose(&shf)); 662 } 663 664 static struct ioword ** 665 iocopy(struct ioword **iow, Area *ap) 666 { 667 struct ioword **ior; 668 int i; 669 670 ior = iow; 671 while (*ior) 672 ++ior; 673 ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap); 674 675 for (i = 0; iow[i] != NULL; i++) { 676 struct ioword *p, *q; 677 678 p = iow[i]; 679 q = alloc(sizeof(struct ioword), ap); 680 ior[i] = q; 681 *q = *p; 682 if (p->ioname != NULL) 683 q->ioname = wdcopy(p->ioname, ap); 684 if (p->delim != NULL) 685 q->delim = wdcopy(p->delim, ap); 686 if (p->heredoc != NULL) 687 strdupx(q->heredoc, p->heredoc, ap); 688 } 689 ior[i] = NULL; 690 691 return (ior); 692 } 693 694 /* 695 * free tree (for function definition) 696 */ 697 void 698 tfree(struct op *t, Area *ap) 699 { 700 char **w; 701 702 if (t == NULL) 703 return; 704 705 afree(t->str, ap); 706 707 if (t->vars != NULL) { 708 for (w = t->vars; *w != NULL; w++) 709 afree(*w, ap); 710 afree(t->vars, ap); 711 } 712 713 if (t->args != NULL) { 714 /*XXX we assume the caller is right */ 715 union mksh_ccphack cw; 716 717 cw.ro = t->args; 718 for (w = cw.rw; *w != NULL; w++) 719 afree(*w, ap); 720 afree(t->args, ap); 721 } 722 723 if (t->ioact != NULL) 724 iofree(t->ioact, ap); 725 726 tfree(t->left, ap); 727 tfree(t->right, ap); 728 729 afree(t, ap); 730 } 731 732 static void 733 iofree(struct ioword **iow, Area *ap) 734 { 735 struct ioword **iop; 736 struct ioword *p; 737 738 iop = iow; 739 while ((p = *iop++) != NULL) { 740 afree(p->ioname, ap); 741 afree(p->delim, ap); 742 afree(p->heredoc, ap); 743 afree(p, ap); 744 } 745 afree(iow, ap); 746 } 747 748 void 749 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v) 750 { 751 if (isksh) 752 fptreef(shf, i, "%s %s %T", Tfunction, k, v); 753 else if (ktsearch(&keywords, k, hash(k))) 754 fptreef(shf, i, "%s %s() %T", Tfunction, k, v); 755 else 756 fptreef(shf, i, "%s() %T", k, v); 757 } 758 759 760 /* for jobs.c */ 761 void 762 vistree(char *dst, size_t sz, struct op *t) 763 { 764 unsigned int c; 765 char *cp, *buf; 766 size_t n; 767 768 buf = alloc(sz + 16, ATEMP); 769 snptreef(buf, sz + 16, Tf_T, t); 770 cp = buf; 771 vist_loop: 772 if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) { 773 if (c == 0 || n >= sz) 774 /* NUL or not enough free space */ 775 goto vist_out; 776 /* copy multibyte char */ 777 sz -= n; 778 while (n--) 779 *dst++ = *cp++; 780 goto vist_loop; 781 } 782 if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0) 783 /* NUL or not enough free space */ 784 goto vist_out; 785 if (ISCTRL(c & 0x7F)) { 786 /* C0 or C1 control character or DEL */ 787 if (--sz == 0) 788 /* not enough free space for two chars */ 789 goto vist_out; 790 *dst++ = (c & 0x80) ? '$' : '^'; 791 c = UNCTRL(c & 0x7F); 792 } else if (UTFMODE && c > 0x7F) { 793 /* better not try to display broken multibyte chars */ 794 /* also go easy on the Unicode: no U+FFFD here */ 795 c = '?'; 796 } 797 *dst++ = c; 798 goto vist_loop; 799 800 vist_out: 801 *dst = '\0'; 802 afree(buf, ATEMP); 803 } 804 805 #ifdef DEBUG 806 void 807 dumpchar(struct shf *shf, int c) 808 { 809 if (ISCTRL(c & 0x7F)) { 810 /* C0 or C1 control character or DEL */ 811 shf_putc((c & 0x80) ? '$' : '^', shf); 812 c = UNCTRL(c & 0x7F); 813 } 814 shf_putc(c, shf); 815 } 816 817 /* see: wdvarput */ 818 static const char * 819 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) 820 { 821 int c; 822 823 while (/* CONSTCOND */ 1) { 824 switch(*wp++) { 825 case EOS: 826 shf_puts("EOS", shf); 827 return (--wp); 828 case ADELIM: 829 if (*wp == /*{*/'}') { 830 shf_puts(/*{*/ "]ADELIM(})", shf); 831 return (wp + 1); 832 } 833 shf_puts("ADELIM=", shf); 834 if (0) 835 case CHAR: 836 shf_puts("CHAR=", shf); 837 dumpchar(shf, *wp++); 838 break; 839 case QCHAR: 840 shf_puts("QCHAR<", shf); 841 c = *wp++; 842 if (quotelevel == 0 || 843 (c == '"' || c == '`' || c == '$' || c == '\\')) 844 shf_putc('\\', shf); 845 dumpchar(shf, c); 846 goto closeandout; 847 case COMSUB: 848 shf_puts("COMSUB<", shf); 849 dumpsub: 850 while ((c = *wp++) != 0) 851 dumpchar(shf, c); 852 closeandout: 853 shf_putc('>', shf); 854 break; 855 case FUNSUB: 856 shf_puts("FUNSUB<", shf); 857 goto dumpsub; 858 case VALSUB: 859 shf_puts("VALSUB<", shf); 860 goto dumpsub; 861 case EXPRSUB: 862 shf_puts("EXPRSUB<", shf); 863 goto dumpsub; 864 case OQUOTE: 865 shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel); 866 break; 867 case CQUOTE: 868 shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel); 869 if (quotelevel) 870 quotelevel--; 871 else 872 shf_puts("(err)", shf); 873 break; 874 case OSUBST: 875 shf_puts("OSUBST(", shf); 876 dumpchar(shf, *wp++); 877 shf_puts(")[", shf); 878 while ((c = *wp++) != 0) 879 dumpchar(shf, c); 880 shf_putc('|', shf); 881 wp = dumpwdvar_i(shf, wp, 0); 882 break; 883 case CSUBST: 884 shf_puts("]CSUBST(", shf); 885 dumpchar(shf, *wp++); 886 shf_putc(')', shf); 887 return (wp); 888 case OPAT: 889 shf_puts("OPAT=", shf); 890 dumpchar(shf, *wp++); 891 break; 892 case SPAT: 893 shf_puts("SPAT", shf); 894 break; 895 case CPAT: 896 shf_puts("CPAT", shf); 897 break; 898 default: 899 shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]); 900 break; 901 } 902 shf_putc(' ', shf); 903 } 904 } 905 void 906 dumpwdvar(struct shf *shf, const char *wp) 907 { 908 dumpwdvar_i(shf, wp, 0); 909 } 910 911 void 912 dumpioact(struct shf *shf, struct op *t) 913 { 914 struct ioword **ioact, *iop; 915 916 if ((ioact = t->ioact) == NULL) 917 return; 918 919 shf_puts("{IOACT", shf); 920 while ((iop = *ioact++) != NULL) { 921 unsigned short type = iop->ioflag & IOTYPE; 922 #define DT(x) case x: shf_puts(#x, shf); break; 923 #define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf); 924 925 shf_putc(';', shf); 926 switch (type) { 927 DT(IOREAD) 928 DT(IOWRITE) 929 DT(IORDWR) 930 DT(IOHERE) 931 DT(IOCAT) 932 DT(IODUP) 933 default: 934 shf_fprintf(shf, "unk%d", type); 935 } 936 DB(IOEVAL) 937 DB(IOSKIP) 938 DB(IOCLOB) 939 DB(IORDUP) 940 DB(IONAMEXP) 941 DB(IOBASH) 942 DB(IOHERESTR) 943 DB(IONDELIM) 944 shf_fprintf(shf, ",unit=%d", (int)iop->unit); 945 if (iop->delim && !(iop->ioflag & IONDELIM)) { 946 shf_puts(",delim<", shf); 947 dumpwdvar(shf, iop->delim); 948 shf_putc('>', shf); 949 } 950 if (iop->ioname) { 951 if (iop->ioflag & IONAMEXP) { 952 shf_puts(",name=", shf); 953 print_value_quoted(shf, iop->ioname); 954 } else { 955 shf_puts(",name<", shf); 956 dumpwdvar(shf, iop->ioname); 957 shf_putc('>', shf); 958 } 959 } 960 if (iop->heredoc) { 961 shf_puts(",heredoc=", shf); 962 print_value_quoted(shf, iop->heredoc); 963 } 964 #undef DT 965 #undef DB 966 } 967 shf_putc('}', shf); 968 } 969 970 void 971 dumptree(struct shf *shf, struct op *t) 972 { 973 int i, j; 974 const char **w, *name; 975 struct op *t1; 976 static int nesting; 977 978 for (i = 0; i < nesting; ++i) 979 shf_putc('\t', shf); 980 ++nesting; 981 shf_puts("{tree:" /*}*/, shf); 982 if (t == NULL) { 983 name = "(null)"; 984 goto out; 985 } 986 dumpioact(shf, t); 987 switch (t->type) { 988 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/ 989 990 OPEN(TCOM) 991 if (t->vars) { 992 i = 0; 993 w = (const char **)t->vars; 994 while (*w) { 995 shf_putc('\n', shf); 996 for (j = 0; j < nesting; ++j) 997 shf_putc('\t', shf); 998 shf_fprintf(shf, " var%d<", i++); 999 dumpwdvar(shf, *w++); 1000 shf_putc('>', shf); 1001 } 1002 } else 1003 shf_puts(" #no-vars#", shf); 1004 if (t->args) { 1005 i = 0; 1006 w = t->args; 1007 while (*w) { 1008 shf_putc('\n', shf); 1009 for (j = 0; j < nesting; ++j) 1010 shf_putc('\t', shf); 1011 shf_fprintf(shf, " arg%d<", i++); 1012 dumpwdvar(shf, *w++); 1013 shf_putc('>', shf); 1014 } 1015 } else 1016 shf_puts(" #no-args#", shf); 1017 break; 1018 OPEN(TEXEC) 1019 dumpleftandout: 1020 t = t->left; 1021 dumpandout: 1022 shf_putc('\n', shf); 1023 dumptree(shf, t); 1024 break; 1025 OPEN(TPAREN) 1026 goto dumpleftandout; 1027 OPEN(TPIPE) 1028 dumpleftmidrightandout: 1029 shf_putc('\n', shf); 1030 dumptree(shf, t->left); 1031 /* middumprightandout: (unused) */ 1032 shf_fprintf(shf, "/%s:", name); 1033 dumprightandout: 1034 t = t->right; 1035 goto dumpandout; 1036 OPEN(TLIST) 1037 goto dumpleftmidrightandout; 1038 OPEN(TOR) 1039 goto dumpleftmidrightandout; 1040 OPEN(TAND) 1041 goto dumpleftmidrightandout; 1042 OPEN(TBANG) 1043 goto dumprightandout; 1044 OPEN(TDBRACKET) 1045 i = 0; 1046 w = t->args; 1047 while (*w) { 1048 shf_putc('\n', shf); 1049 for (j = 0; j < nesting; ++j) 1050 shf_putc('\t', shf); 1051 shf_fprintf(shf, " arg%d<", i++); 1052 dumpwdvar(shf, *w++); 1053 shf_putc('>', shf); 1054 } 1055 break; 1056 OPEN(TFOR) 1057 dumpfor: 1058 shf_fprintf(shf, " str<%s>", t->str); 1059 if (t->vars != NULL) { 1060 i = 0; 1061 w = (const char **)t->vars; 1062 while (*w) { 1063 shf_putc('\n', shf); 1064 for (j = 0; j < nesting; ++j) 1065 shf_putc('\t', shf); 1066 shf_fprintf(shf, " var%d<", i++); 1067 dumpwdvar(shf, *w++); 1068 shf_putc('>', shf); 1069 } 1070 } 1071 goto dumpleftandout; 1072 OPEN(TSELECT) 1073 goto dumpfor; 1074 OPEN(TCASE) 1075 shf_fprintf(shf, " str<%s>", t->str); 1076 i = 0; 1077 for (t1 = t->left; t1 != NULL; t1 = t1->right) { 1078 shf_putc('\n', shf); 1079 for (j = 0; j < nesting; ++j) 1080 shf_putc('\t', shf); 1081 shf_fprintf(shf, " sub%d[(", i); 1082 w = (const char **)t1->vars; 1083 while (*w) { 1084 dumpwdvar(shf, *w); 1085 if (w[1] != NULL) 1086 shf_putc('|', shf); 1087 ++w; 1088 } 1089 shf_putc(')', shf); 1090 dumpioact(shf, t); 1091 shf_putc('\n', shf); 1092 dumptree(shf, t1->left); 1093 shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++); 1094 } 1095 break; 1096 OPEN(TWHILE) 1097 goto dumpleftmidrightandout; 1098 OPEN(TUNTIL) 1099 goto dumpleftmidrightandout; 1100 OPEN(TBRACE) 1101 goto dumpleftandout; 1102 OPEN(TCOPROC) 1103 goto dumpleftandout; 1104 OPEN(TASYNC) 1105 goto dumpleftandout; 1106 OPEN(TFUNCT) 1107 shf_fprintf(shf, " str<%s> ksh<%s>", t->str, 1108 t->u.ksh_func ? Ttrue : Tfalse); 1109 goto dumpleftandout; 1110 OPEN(TTIME) 1111 goto dumpleftandout; 1112 OPEN(TIF) 1113 dumpif: 1114 shf_putc('\n', shf); 1115 dumptree(shf, t->left); 1116 t = t->right; 1117 dumpioact(shf, t); 1118 if (t->left != NULL) { 1119 shf_puts(" /TTHEN:\n", shf); 1120 dumptree(shf, t->left); 1121 } 1122 if (t->right && t->right->type == TELIF) { 1123 shf_puts(" /TELIF:", shf); 1124 t = t->right; 1125 dumpioact(shf, t); 1126 goto dumpif; 1127 } 1128 if (t->right != NULL) { 1129 shf_puts(" /TELSE:\n", shf); 1130 dumptree(shf, t->right); 1131 } 1132 break; 1133 OPEN(TEOF) 1134 dumpunexpected: 1135 shf_puts(Tunexpected, shf); 1136 break; 1137 OPEN(TELIF) 1138 goto dumpunexpected; 1139 OPEN(TPAT) 1140 goto dumpunexpected; 1141 default: 1142 name = "TINVALID"; 1143 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type); 1144 goto dumpunexpected; 1145 1146 #undef OPEN 1147 } 1148 out: 1149 shf_fprintf(shf, /*{*/ " /%s}\n", name); 1150 --nesting; 1151 } 1152 #endif 1153