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