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