1 /* $NetBSD: show.c,v 1.53 2019/02/14 13:27:59 kre Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Kenneth Almquist. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; 41 #else 42 __RCSID("$NetBSD: show.c,v 1.53 2019/02/14 13:27:59 kre Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <stdio.h> 47 #include <stdarg.h> 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <fcntl.h> 51 #include <errno.h> 52 #include <limits.h> 53 54 #include <sys/types.h> 55 #include <sys/uio.h> 56 57 #include "shell.h" 58 #include "parser.h" 59 #include "nodes.h" 60 #include "mystring.h" 61 #include "show.h" 62 #include "options.h" 63 #include "redir.h" 64 #include "error.h" 65 #include "syntax.h" 66 #include "input.h" 67 #include "output.h" 68 #include "var.h" 69 #include "builtins.h" 70 71 #define DEFINE_NODENAMES 72 #include "nodenames.h" /* does almost nothing if !defined(DEBUG) */ 73 74 #define TR_STD_WIDTH 60 /* tend to fold lines wider than this */ 75 #define TR_IOVECS 10 /* number of lines or trace (max) / write */ 76 77 typedef struct traceinfo { 78 int tfd; /* file descriptor for open trace file */ 79 int nxtiov; /* the buffer we should be writing to */ 80 char lastc; /* the last non-white character output */ 81 uint8_t supr; /* char classes to suppress after \n */ 82 pid_t pid; /* process id of process that opened that file */ 83 size_t llen; /* number of chars in current output line */ 84 size_t blen; /* chars used in current buffer being filled */ 85 char * tracefile; /* name of the tracefile */ 86 struct iovec lines[TR_IOVECS]; /* filled, flling, pending buffers */ 87 } TFILE; 88 89 /* These are auto turned off when non white space is printed */ 90 #define SUP_NL 0x01 /* don't print \n */ 91 #define SUP_SP 0x03 /* suppress spaces */ 92 #define SUP_WSP 0x04 /* suppress all white space */ 93 94 #ifdef DEBUG /* from here to end of file ... */ 95 96 TFILE tracedata, *tracetfile; 97 FILE *tracefile; /* just for histedit */ 98 99 uint64_t DFlags; /* currently enabled debug flags */ 100 int ShNest; /* depth of shell (internal) nesting */ 101 102 static void shtree(union node *, int, int, int, TFILE *); 103 static void shcmd(union node *, TFILE *); 104 static void shsubsh(union node *, TFILE *); 105 static void shredir(union node *, TFILE *, int); 106 static void sharg(union node *, TFILE *); 107 static void indent(int, TFILE *); 108 static void trstring(const char *); 109 static void trace_putc(char, TFILE *); 110 static void trace_puts(const char *, TFILE *); 111 static void trace_flush(TFILE *, int); 112 static char *trace_id(TFILE *); 113 static void trace_fd_swap(int, int); 114 115 inline static int trlinelen(TFILE *); 116 117 118 /* 119 * These functions are the externally visible interface 120 * (but only for a DEBUG shell.) 121 */ 122 123 void 124 opentrace(void) 125 { 126 char *s; 127 int fd; 128 int i; 129 pid_t pid; 130 131 if (debug != 1) { 132 /* leave fd open because libedit might be using it */ 133 if (tracefile) 134 fflush(tracefile); 135 if (tracetfile) 136 trace_flush(tracetfile, 1); 137 return; 138 } 139 #if DBG_PID == 1 /* using old shell.h, old tracing method */ 140 DFlags = DBG_PID; /* just force DBG_PID on, and leave it ... */ 141 #endif 142 pid = getpid(); 143 if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) { 144 debug = 0; 145 error("Cannot asprintf tracefilename"); 146 }; 147 148 fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666); 149 if (fd == -1) { 150 debug = 0; 151 error("Can't open tracefile: %s (%s)\n", s, strerror(errno)); 152 } 153 fd = to_upper_fd(fd); 154 if (fd <= 2) { 155 (void) close(fd); 156 debug = 0; 157 error("Attempt to use fd %d as tracefile thwarted\n", fd); 158 } 159 register_sh_fd(fd, trace_fd_swap); 160 161 /* 162 * This stuff is just so histedit has a FILE * to use 163 */ 164 if (tracefile) 165 (void) fclose(tracefile); /* also closes tfd */ 166 tracefile = fdopen(fd, "a"); /* don't care if it is NULL */ 167 if (tracefile) /* except here... */ 168 setlinebuf(tracefile); 169 170 /* 171 * Now the real tracing setup 172 */ 173 if (tracedata.tfd > 0 && tracedata.tfd != fd) 174 (void) close(tracedata.tfd); /* usually done by fclose() */ 175 176 tracedata.tfd = fd; 177 tracedata.pid = pid; 178 tracedata.nxtiov = 0; 179 tracedata.blen = 0; 180 tracedata.llen = 0; 181 tracedata.lastc = '\0'; 182 tracedata.supr = SUP_NL | SUP_WSP; 183 184 #define replace(f, v) do { \ 185 if (tracedata.f != NULL) \ 186 free(tracedata.f); \ 187 tracedata.f = v; \ 188 } while (/*CONSTCOND*/ 0) 189 190 replace(tracefile, s); 191 192 for (i = 0; i < TR_IOVECS; i++) { 193 replace(lines[i].iov_base, NULL); 194 tracedata.lines[i].iov_len = 0; 195 } 196 197 #undef replace 198 199 tracetfile = &tracedata; 200 201 trace_puts("\nTracing started.\n", tracetfile); 202 } 203 204 void 205 trace(const char *fmt, ...) 206 { 207 va_list va; 208 char *s; 209 210 if (debug != 1 || !tracetfile) 211 return; 212 va_start(va, fmt); 213 (void) vasprintf(&s, fmt, va); 214 va_end(va); 215 216 trace_puts(s, tracetfile); 217 free(s); 218 if (tracetfile->llen == 0) 219 trace_flush(tracetfile, 0); 220 } 221 222 void 223 tracev(const char *fmt, va_list va) 224 { 225 va_list ap; 226 char *s; 227 228 if (debug != 1 || !tracetfile) 229 return; 230 va_copy(ap, va); 231 (void) vasprintf(&s, fmt, ap); 232 va_end(ap); 233 234 trace_puts(s, tracetfile); 235 free(s); 236 if (tracetfile->llen == 0) 237 trace_flush(tracetfile, 0); 238 } 239 240 241 void 242 trputs(const char *s) 243 { 244 if (debug != 1 || !tracetfile) 245 return; 246 trace_puts(s, tracetfile); 247 } 248 249 void 250 trputc(int c) 251 { 252 if (debug != 1 || !tracetfile) 253 return; 254 trace_putc(c, tracetfile); 255 } 256 257 void 258 showtree(union node *n) 259 { 260 TFILE *fp; 261 262 if ((fp = tracetfile) == NULL) 263 return; 264 265 trace_puts("showtree(", fp); 266 if (n == NULL) 267 trace_puts("NULL", fp); 268 else if (n == NEOF) 269 trace_puts("NEOF", fp); 270 else 271 trace("%p", n); 272 trace_puts(") called\n", fp); 273 if (n != NULL && n != NEOF) 274 shtree(n, 1, 1, 1, fp); 275 } 276 277 void 278 trargs(char **ap) 279 { 280 if (debug != 1 || !tracetfile) 281 return; 282 while (*ap) { 283 trstring(*ap++); 284 if (*ap) 285 trace_putc(' ', tracetfile); 286 } 287 trace_putc('\n', tracetfile); 288 } 289 290 void 291 trargstr(union node *n) 292 { 293 sharg(n, tracetfile); 294 } 295 296 297 /* 298 * Beyond here we just have the implementation of all of that 299 */ 300 301 302 inline static int 303 trlinelen(TFILE * fp) 304 { 305 return fp->llen; 306 } 307 308 static void 309 shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp) 310 { 311 struct nodelist *lp; 312 const char *s; 313 314 if (n == NULL) { 315 if (nl) 316 trace_putc('\n', fp); 317 return; 318 } 319 320 indent(ind, fp); 321 switch (n->type) { 322 case NSEMI: 323 s = NULL; 324 goto binop; 325 case NAND: 326 s = " && "; 327 goto binop; 328 case NOR: 329 s = " || "; 330 binop: 331 shtree(n->nbinary.ch1, 0, ilvl, 0, fp); 332 if (s != NULL) 333 trace_puts(s, fp); 334 if (trlinelen(fp) >= TR_STD_WIDTH) { 335 trace_putc('\n', fp); 336 indent(ind < 0 ? 2 : ind + 1, fp); 337 } else if (s == NULL) { 338 if (fp->lastc != '&') 339 trace_puts("; ", fp); 340 else 341 trace_putc(' ', fp); 342 } 343 shtree(n->nbinary.ch2, 0, ilvl, nl, fp); 344 break; 345 case NCMD: 346 shcmd(n, fp); 347 if (n->ncmd.backgnd) 348 trace_puts(" &", fp); 349 if (nl && trlinelen(fp) > 0) 350 trace_putc('\n', fp); 351 break; 352 case NPIPE: 353 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 354 shtree(lp->n, 0, ilvl, 0, fp); 355 if (lp->next) { 356 trace_puts(" |", fp); 357 if (trlinelen(fp) >= TR_STD_WIDTH) { 358 trace_putc('\n', fp); 359 indent((ind < 0 ? ilvl : ind) + 1, fp); 360 } else 361 trace_putc(' ', fp); 362 } 363 } 364 if (n->npipe.backgnd) 365 trace_puts(" &", fp); 366 if (nl || trlinelen(fp) >= TR_STD_WIDTH) 367 trace_putc('\n', fp); 368 break; 369 case NBACKGND: 370 case NSUBSHELL: 371 shsubsh(n, fp); 372 if (n->type == NBACKGND) 373 trace_puts(" &", fp); 374 if (nl && trlinelen(fp) > 0) 375 trace_putc('\n', fp); 376 break; 377 case NDEFUN: 378 trace_puts(n->narg.text, fp); 379 trace_puts("() {\n", fp); 380 indent(ind, fp); 381 shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp); 382 indent(ind, fp); 383 trace_puts("}\n", fp); 384 break; 385 case NDNOT: 386 trace_puts("! ", fp); 387 /* FALLTHROUGH */ 388 case NNOT: 389 trace_puts("! ", fp); 390 shtree(n->nnot.com, -1, ilvl, nl, fp); 391 break; 392 case NREDIR: 393 shtree(n->nredir.n, -1, ilvl, 0, fp); 394 shredir(n->nredir.redirect, fp, n->nredir.n == NULL); 395 if (nl) 396 trace_putc('\n', fp); 397 break; 398 399 case NIF: 400 itsif: 401 trace_puts("if ", fp); 402 shtree(n->nif.test, -1, ilvl, 0, fp); 403 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 404 if (fp->lastc != '&') 405 trace_puts(" ;", fp); 406 } else 407 indent(ilvl, fp); 408 trace_puts(" then ", fp); 409 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 410 indent(ilvl+1, fp); 411 shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp); 412 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 413 if (fp->lastc != '&') 414 trace_puts(" ;", fp); 415 } else 416 indent(ilvl, fp); 417 if (n->nif.elsepart && n->nif.elsepart->type == NIF) { 418 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 419 indent(ilvl, fp); 420 n = n->nif.elsepart; 421 trace_puts(" el", fp); 422 goto itsif; 423 } 424 if (n->nif.elsepart) { 425 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 426 indent(ilvl+1, fp); 427 trace_puts(" else ", fp); 428 shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp); 429 if (fp->lastc != '&') 430 trace_puts(" ;", fp); 431 } 432 trace_puts(" fi", fp); 433 if (nl) 434 trace_putc('\n', fp); 435 break; 436 437 case NWHILE: 438 trace_puts("while ", fp); 439 goto aloop; 440 case NUNTIL: 441 trace_puts("until ", fp); 442 aloop: 443 shtree(n->nbinary.ch1, -1, ilvl, 0, fp); 444 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 445 if (fp->lastc != '&') 446 trace_puts(" ;", fp); 447 } else 448 trace_putc('\n', fp); 449 trace_puts(" do ", fp); 450 shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp); 451 trace_puts(" done ", fp); 452 if (nl) 453 trace_putc('\n', fp); 454 break; 455 456 case NFOR: 457 trace_puts("for ", fp); 458 trace_puts(n->nfor.var, fp); 459 if (n->nfor.args) { 460 union node *argp; 461 462 trace_puts(" in ", fp); 463 for (argp = n->nfor.args; argp; argp=argp->narg.next) { 464 sharg(argp, fp); 465 trace_putc(' ', fp); 466 } 467 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 468 if (fp->lastc != '&') 469 trace_putc(';', fp); 470 } else 471 trace_putc('\n', fp); 472 } 473 trace_puts(" do ", fp); 474 shtree(n->nfor.body, -1, ilvl + 1, 0, fp); 475 if (fp->lastc != '&') 476 trace_putc(';', fp); 477 trace_puts(" done", fp); 478 if (nl) 479 trace_putc('\n', fp); 480 break; 481 482 case NCASE: 483 trace_puts("case ", fp); 484 sharg(n->ncase.expr, fp); 485 trace_puts(" in", fp); 486 if (nl) 487 trace_putc('\n', fp); 488 { 489 union node *cp; 490 491 for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) { 492 union node *patp; 493 494 if (nl || trlinelen(fp) > TR_STD_WIDTH - 16) 495 indent(ilvl, fp); 496 else 497 trace_putc(' ', fp); 498 trace_putc('(', fp); 499 patp = cp->nclist.pattern; 500 while (patp != NULL) { 501 trace_putc(' ', fp); 502 sharg(patp, fp); 503 trace_putc(' ', fp); 504 if ((patp = patp->narg.next) != NULL) 505 trace_putc('|', fp); 506 } 507 trace_putc(')', fp); 508 if (nl) 509 indent(ilvl + 1, fp); 510 else 511 trace_putc(' ', fp); 512 shtree(cp->nclist.body, -1, ilvl+2, 0, fp); 513 if (cp->type == NCLISTCONT) 514 trace_puts(" ;&", fp); 515 else 516 trace_puts(" ;;", fp); 517 if (nl) 518 trace_putc('\n', fp); 519 } 520 } 521 if (nl) { 522 trace_putc('\n', fp); 523 indent(ind, fp); 524 } else 525 trace_putc(' ', fp); 526 trace_puts("esac", fp); 527 if (nl) 528 trace_putc('\n', fp); 529 break; 530 531 default: { 532 char *str; 533 534 asprintf(&str, "<node type %d [%s]>", n->type, 535 NODETYPENAME(n->type)); 536 trace_puts(str, fp); 537 free(str); 538 if (nl) 539 trace_putc('\n', fp); 540 } 541 break; 542 } 543 } 544 545 546 static void 547 shcmd(union node *cmd, TFILE *fp) 548 { 549 union node *np; 550 int first; 551 552 first = 1; 553 for (np = cmd->ncmd.args ; np ; np = np->narg.next) { 554 if (! first) 555 trace_putc(' ', fp); 556 sharg(np, fp); 557 first = 0; 558 } 559 shredir(cmd->ncmd.redirect, fp, first); 560 } 561 562 static void 563 shsubsh(union node *cmd, TFILE *fp) 564 { 565 trace_puts(" ( ", fp); 566 shtree(cmd->nredir.n, -1, 3, 0, fp); 567 trace_puts(" ) ", fp); 568 shredir(cmd->ncmd.redirect, fp, 1); 569 } 570 571 static void 572 shredir(union node *np, TFILE *fp, int first) 573 { 574 const char *s; 575 int dftfd; 576 char buf[106]; 577 578 for ( ; np ; np = np->nfile.next) { 579 if (! first) 580 trace_putc(' ', fp); 581 switch (np->nfile.type) { 582 case NTO: s = ">"; dftfd = 1; break; 583 case NCLOBBER: s = ">|"; dftfd = 1; break; 584 case NAPPEND: s = ">>"; dftfd = 1; break; 585 case NTOFD: s = ">&"; dftfd = 1; break; 586 case NFROM: s = "<"; dftfd = 0; break; 587 case NFROMFD: s = "<&"; dftfd = 0; break; 588 case NFROMTO: s = "<>"; dftfd = 0; break; 589 case NXHERE: /* FALLTHROUGH */ 590 case NHERE: s = "<<"; dftfd = 0; break; 591 default: s = "*error*"; dftfd = 0; break; 592 } 593 if (np->nfile.fd != dftfd) { 594 sprintf(buf, "%d", np->nfile.fd); 595 trace_puts(buf, fp); 596 } 597 trace_puts(s, fp); 598 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { 599 if (np->ndup.vname) 600 sharg(np->ndup.vname, fp); 601 else { 602 if (np->ndup.dupfd < 0) 603 trace_puts("-", fp); 604 else { 605 sprintf(buf, "%d", np->ndup.dupfd); 606 trace_puts(buf, fp); 607 } 608 } 609 } else 610 if (np->nfile.type == NHERE || np->nfile.type == NXHERE) { 611 if (np->nfile.type == NHERE) 612 trace_putc('\\', fp); 613 trace_puts("!!!\n", fp); 614 s = np->nhere.doc->narg.text; 615 if (strlen(s) > 100) { 616 memmove(buf, s, 100); 617 buf[100] = '\0'; 618 strcat(buf, " ...\n"); 619 s = buf; 620 } 621 trace_puts(s, fp); 622 trace_puts("!!! ", fp); 623 } else { 624 sharg(np->nfile.fname, fp); 625 } 626 first = 0; 627 } 628 } 629 630 static void 631 sharg(union node *arg, TFILE *fp) 632 { 633 char *p, *s; 634 struct nodelist *bqlist; 635 int subtype = 0; 636 int quoted = 0; 637 638 if (arg->type != NARG) { 639 asprintf(&s, "<node type %d> ! NARG\n", arg->type); 640 trace_puts(s, fp); 641 abort(); /* no need to free s, better not to */ 642 } 643 644 bqlist = arg->narg.backquote; 645 for (p = arg->narg.text ; *p ; p++) { 646 switch (*p) { 647 case CTLESC: 648 if (BASESYNTAX[p[1]] != CCTL) 649 trace_putc('\\', fp); 650 trace_putc(*++p, fp); 651 break; 652 653 case CTLNONL: 654 trace_putc('\\', fp); 655 trace_putc('\n', fp); 656 break; 657 658 case CTLVAR: 659 subtype = *++p; 660 if (!quoted != !(subtype & VSQUOTE)) 661 trace_putc('"', fp); 662 trace_putc('$', fp); 663 trace_putc('{', fp); /*}*/ 664 if ((subtype & VSTYPE) == VSLENGTH) 665 trace_putc('#', fp); 666 if (subtype & VSLINENO) 667 trace_puts("LINENO=", fp); 668 669 while (*++p != '=') 670 trace_putc(*p, fp); 671 672 if (subtype & VSNUL) 673 trace_putc(':', fp); 674 675 switch (subtype & VSTYPE) { 676 case VSNORMAL: 677 /* { */ 678 trace_putc('}', fp); 679 if (!quoted != !(subtype & VSQUOTE)) 680 trace_putc('"', fp); 681 break; 682 case VSMINUS: 683 trace_putc('-', fp); 684 break; 685 case VSPLUS: 686 trace_putc('+', fp); 687 break; 688 case VSQUESTION: 689 trace_putc('?', fp); 690 break; 691 case VSASSIGN: 692 trace_putc('=', fp); 693 break; 694 case VSTRIMLEFTMAX: 695 trace_putc('#', fp); 696 /* FALLTHROUGH */ 697 case VSTRIMLEFT: 698 trace_putc('#', fp); 699 break; 700 case VSTRIMRIGHTMAX: 701 trace_putc('%', fp); 702 /* FALLTHROUGH */ 703 case VSTRIMRIGHT: 704 trace_putc('%', fp); 705 break; 706 case VSLENGTH: 707 break; 708 default: { 709 char str[32]; 710 711 snprintf(str, sizeof str, 712 "<subtype %d>", subtype); 713 trace_puts(str, fp); 714 } 715 break; 716 } 717 break; 718 case CTLENDVAR: 719 /* { */ 720 trace_putc('}', fp); 721 if (!quoted != !(subtype & VSQUOTE)) 722 trace_putc('"', fp); 723 subtype = 0; 724 break; 725 726 case CTLBACKQ|CTLQUOTE: 727 if (!quoted) 728 trace_putc('"', fp); 729 /* FALLTHRU */ 730 case CTLBACKQ: 731 trace_putc('$', fp); 732 trace_putc('(', fp); 733 if (bqlist) { 734 shtree(bqlist->n, -1, 3, 0, fp); 735 bqlist = bqlist->next; 736 } else 737 trace_puts("???", fp); 738 trace_putc(')', fp); 739 if (!quoted && *p == (CTLBACKQ|CTLQUOTE)) 740 trace_putc('"', fp); 741 break; 742 743 case CTLQUOTEMARK: 744 if (subtype != 0 || !quoted) { 745 trace_putc('"', fp); 746 quoted++; 747 } 748 break; 749 case CTLQUOTEEND: 750 trace_putc('"', fp); 751 quoted--; 752 break; 753 case CTLARI: 754 if (*p == ' ') 755 p++; 756 trace_puts("$(( ", fp); 757 break; 758 case CTLENDARI: 759 trace_puts(" ))", fp); 760 break; 761 762 default: 763 if (*p == '$') 764 trace_putc('\\', fp); 765 trace_putc(*p, fp); 766 break; 767 } 768 } 769 if (quoted) 770 trace_putc('"', fp); 771 } 772 773 774 static void 775 indent(int amount, TFILE *fp) 776 { 777 int i; 778 779 if (amount <= 0) 780 return; 781 782 amount <<= 2; /* indent slots -> chars */ 783 784 i = trlinelen(fp); 785 fp->supr = SUP_NL; 786 if (i > amount) { 787 trace_putc('\n', fp); 788 i = 0; 789 } 790 fp->supr = 0; 791 for (; i < amount - 7 ; i++) { 792 trace_putc('\t', fp); 793 i |= 7; 794 } 795 while (i < amount) { 796 trace_putc(' ', fp); 797 i++; 798 } 799 fp->supr = SUP_WSP; 800 } 801 802 static void 803 trace_putc(char c, TFILE *fp) 804 { 805 char *p; 806 807 if (c == '\0') 808 return; 809 if (debug == 0 || fp == NULL) 810 return; 811 812 if (fp->llen == 0) { 813 if (fp->blen != 0) 814 abort(); 815 816 if ((fp->supr & SUP_NL) && c == '\n') 817 return; 818 if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ') 819 return; 820 if ((fp->supr & SUP_WSP) && c == '\t') 821 return; 822 823 if (fp->nxtiov >= TR_IOVECS - 1) /* should be rare */ 824 trace_flush(fp, 0); 825 826 p = trace_id(fp); 827 if (p != NULL) { 828 fp->lines[fp->nxtiov].iov_base = p; 829 fp->lines[fp->nxtiov].iov_len = strlen(p); 830 fp->nxtiov++; 831 } 832 } else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) { 833 fp->blen = 0; 834 if (++fp->nxtiov >= TR_IOVECS) 835 trace_flush(fp, 0); 836 } 837 838 if (fp->lines[fp->nxtiov].iov_len == 0) { 839 p = (char *)malloc(2 * TR_STD_WIDTH); 840 if (p == NULL) { 841 trace_flush(fp, 1); 842 debug = 0; 843 return; 844 } 845 *p = '\0'; 846 fp->lines[fp->nxtiov].iov_base = p; 847 fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH; 848 fp->blen = 0; 849 } 850 851 p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++; 852 *p++ = c; 853 *p = 0; 854 855 if (c != ' ' && c != '\t' && c != '\n') { 856 fp->lastc = c; 857 fp->supr = 0; 858 } 859 860 if (c == '\n') { 861 fp->lines[fp->nxtiov++].iov_len = fp->blen; 862 fp->blen = 0; 863 fp->llen = 0; 864 fp->supr |= SUP_NL; 865 return; 866 } 867 868 if (c == '\t') 869 fp->llen |= 7; 870 fp->llen++; 871 } 872 873 void 874 trace_flush(TFILE *fp, int all) 875 { 876 int niov, i; 877 ssize_t written; 878 879 niov = fp->nxtiov; 880 if (all && fp->blen > 0) { 881 fp->lines[niov].iov_len = fp->blen; 882 fp->blen = 0; 883 fp->llen = 0; 884 niov++; 885 } 886 if (niov == 0) 887 return; 888 if (fp->blen > 0 && --niov == 0) 889 return; 890 written = writev(fp->tfd, fp->lines, niov); 891 for (i = 0; i < niov; i++) { 892 free(fp->lines[i].iov_base); 893 fp->lines[i].iov_base = NULL; 894 fp->lines[i].iov_len = 0; 895 } 896 if (written == -1) { 897 if (fp->blen > 0) { 898 free(fp->lines[niov].iov_base); 899 fp->lines[niov].iov_base = NULL; 900 fp->lines[niov].iov_len = 0; 901 } 902 debug = 0; 903 fp->blen = 0; 904 fp->llen = 0; 905 return; 906 } 907 if (fp->blen > 0) { 908 fp->lines[0].iov_base = fp->lines[niov].iov_base; 909 fp->lines[0].iov_len = fp->lines[niov].iov_len; 910 fp->lines[niov].iov_base = NULL; 911 fp->lines[niov].iov_len = 0; 912 } 913 fp->nxtiov = 0; 914 } 915 916 void 917 trace_puts(const char *s, TFILE *fp) 918 { 919 char c; 920 921 while ((c = *s++) != '\0') 922 trace_putc(c, fp); 923 } 924 925 inline static char * 926 trace_id(TFILE *tf) 927 { 928 int i; 929 char indent[16]; 930 char *p; 931 int lno; 932 char c; 933 934 if (DFlags & DBG_NEST) { 935 if ((unsigned)ShNest >= sizeof indent - 1) { 936 (void) snprintf(indent, sizeof indent, 937 "### %*d ###", (int)(sizeof indent) - 9, ShNest); 938 p = strchr(indent, '\0'); 939 } else { 940 p = indent; 941 for (i = 0; i < 6; i++) 942 *p++ = (i < ShNest) ? '#' : ' '; 943 while (i++ < ShNest && p < &indent[sizeof indent - 1]) 944 *p++ = '#'; 945 *p = '\0'; 946 } 947 } else 948 indent[0] = '\0'; 949 950 /* 951 * If we are in the parser, then plinno is the current line 952 * number being processed (parser line no). 953 * If we are elsewhere, then line_number gives the source 954 * line of whatever we are currently doing (close enough.) 955 */ 956 if (parsing) 957 lno = plinno; 958 else 959 lno = line_number; 960 961 c = ((i = getpid()) == tf->pid) ? ':' : '='; 962 963 if (DFlags & DBG_PID) { 964 if (DFlags & DBG_LINE) 965 (void) asprintf(&p, "%5d%c%s\t%4d%c@\t", i, c, 966 indent, lno, parsing?'-':'+'); 967 else 968 (void) asprintf(&p, "%5d%c%s\t", i, c, indent); 969 return p; 970 } else if (DFlags & DBG_NEST) { 971 if (DFlags & DBG_LINE) 972 (void) asprintf(&p, "%c%s\t%4d%c@\t", c, indent, lno, 973 parsing?'-':'+'); 974 else 975 (void) asprintf(&p, "%c%s\t", c, indent); 976 return p; 977 } else if (DFlags & DBG_LINE) { 978 (void) asprintf(&p, "%c%4d%c@\t", c, lno, parsing?'-':'+'); 979 return p; 980 } 981 return NULL; 982 } 983 984 /* 985 * Used only from trargs(), which itself is used only to print 986 * arg lists (argv[]) either that passed into this shell, or 987 * the arg list about to be given to some other command (incl 988 * builtin, and function) as their argv[]. If any of the CTL* 989 * chars seem to appear, they really should be just treated as data, 990 * not special... But this is just debug, so, who cares! 991 */ 992 static void 993 trstring(const char *s) 994 { 995 const char *p; 996 char c; 997 TFILE *fp; 998 999 if (debug != 1 || !tracetfile) 1000 return; 1001 fp = tracetfile; 1002 trace_putc('"', fp); 1003 for (p = s ; *p ; p++) { 1004 switch (*p) { 1005 case '\n': c = 'n'; goto backslash; 1006 case '\t': c = 't'; goto backslash; 1007 case '\r': c = 'r'; goto backslash; 1008 case '"': c = '"'; goto backslash; 1009 case '\\': c = '\\'; goto backslash; 1010 case CTLESC: c = 'e'; goto backslash; 1011 case CTLVAR: c = 'v'; goto backslash; 1012 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; 1013 case CTLBACKQ: c = 'q'; goto backslash; 1014 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; 1015 backslash: trace_putc('\\', fp); 1016 trace_putc(c, fp); 1017 break; 1018 default: 1019 if (*p >= ' ' && *p <= '~') 1020 trace_putc(*p, fp); 1021 else { 1022 trace_putc('\\', fp); 1023 trace_putc(*p >> 6 & 03, fp); 1024 trace_putc(*p >> 3 & 07, fp); 1025 trace_putc(*p & 07, fp); 1026 } 1027 break; 1028 } 1029 } 1030 trace_putc('"', fp); 1031 } 1032 1033 /* 1034 * deal with the user "accidentally" picking our fd to use. 1035 */ 1036 static void 1037 trace_fd_swap(int from, int to) 1038 { 1039 if (tracetfile == NULL || from == to) 1040 return; 1041 1042 tracetfile->tfd = to; 1043 1044 /* 1045 * This is just so histedit has a stdio FILE* to use. 1046 */ 1047 if (tracefile) 1048 fclose(tracefile); 1049 tracefile = fdopen(to, "a"); 1050 if (tracefile) 1051 setlinebuf(tracefile); 1052 } 1053 1054 1055 static struct debug_flag { 1056 char label; 1057 uint64_t flag; 1058 } debug_flags[] = { 1059 { 'a', DBG_ARITH }, /* arithmetic ( $(( )) ) */ 1060 { 'c', DBG_CMDS }, /* command searching, ... */ 1061 { 'e', DBG_EVAL }, /* evaluation of the parse tree */ 1062 { 'f', DBG_REDIR }, /* file descriptors & redirections */ 1063 { 'g', DBG_MATCH }, /* pattern matching (glob) */ 1064 { 'h', DBG_HISTORY }, /* history & cmd line editing */ 1065 { 'i', DBG_INPUT }, /* shell input routines */ 1066 { 'j', DBG_JOBS }, /* job control, structures */ 1067 { 'l', DBG_LEXER }, /* lexical analysis */ 1068 { 'm', DBG_MEM }, /* memory management */ 1069 { 'o', DBG_OUTPUT }, /* output routines */ 1070 { 'p', DBG_PROCS }, /* process management, fork, ... */ 1071 { 'r', DBG_PARSE }, /* parser, lexer, ... tree building */ 1072 { 's', DBG_SIG }, /* signals and everything related */ 1073 { 't', DBG_TRAP }, /* traps & signals */ 1074 { 'v', DBG_VARS }, /* variables and parameters */ 1075 { 'w', DBG_WAIT }, /* waits for processes to finish */ 1076 { 'x', DBG_EXPAND }, /* word expansion ${} $() $(( )) */ 1077 { 'z', DBG_ERRS }, /* error control, jumps, cleanup */ 1078 1079 { '0', DBG_U0 }, /* ad-hoc temp debug flag #0 */ 1080 { '1', DBG_U1 }, /* ad-hoc temp debug flag #1 */ 1081 { '2', DBG_U2 }, /* ad-hoc temp debug flag #2 */ 1082 { '3', DBG_U3 }, /* ad-hoc temp debug flag #3 */ 1083 1084 { '@', DBG_LINE }, /* prefix trace lines with line# */ 1085 { '$', DBG_PID }, /* prefix trace lines with sh pid */ 1086 { '^', DBG_NEST }, /* show shell nesting level */ 1087 1088 /* alpha options only - but not DBG_LEXER */ 1089 { '_', DBG_PARSE | DBG_EVAL | DBG_EXPAND | DBG_JOBS | DBG_SIG | 1090 DBG_PROCS | DBG_REDIR | DBG_CMDS | DBG_ERRS | 1091 DBG_WAIT | DBG_TRAP | DBG_VARS | DBG_MEM | DBG_MATCH | 1092 DBG_INPUT | DBG_OUTPUT | DBG_ARITH | DBG_HISTORY }, 1093 1094 /* { '*', DBG_ALLVERBOSE }, is handled in the code */ 1095 1096 { '#', DBG_U0 | DBG_U1 | DBG_U2 | DBG_U3 }, 1097 1098 { 0, 0 } 1099 }; 1100 1101 void 1102 set_debug(const char * flags, int on) 1103 { 1104 char f; 1105 struct debug_flag *df; 1106 int verbose; 1107 1108 while ((f = *flags++) != '\0') { 1109 verbose = 0; 1110 if (is_upper(f)) { 1111 verbose = 1; 1112 f += 'a' - 'A'; 1113 } 1114 if (f == '*') 1115 f = '_', verbose = 1; 1116 if (f == '+') { 1117 if (*flags == '+') 1118 flags++, verbose=1; 1119 } 1120 1121 /* 1122 * Note: turning on any debug option also enables DBG_ALWAYS 1123 * turning on any verbose option also enables DBG_VERBOSE 1124 * Once enabled, those flags cannot be disabled. 1125 * (tracing can still be turned off with "set +o debug") 1126 */ 1127 for (df = debug_flags; df->label != '\0'; df++) { 1128 if (f == '+' || df->label == f) { 1129 if (on) { 1130 DFlags |= DBG_ALWAYS | df->flag; 1131 if (verbose) 1132 DFlags |= DBG_VERBOSE | 1133 (df->flag << DBG_VBOSE_SHIFT); 1134 } else { 1135 DFlags &= ~(df->flag<<DBG_VBOSE_SHIFT); 1136 if (!verbose) 1137 DFlags &= ~df->flag; 1138 } 1139 } 1140 } 1141 } 1142 } 1143 1144 1145 int 1146 debugcmd(int argc, char **argv) 1147 { 1148 if (argc == 1) { 1149 struct debug_flag *df; 1150 1151 out1fmt("Debug: %sabled. Flags: ", debug ? "en" : "dis"); 1152 for (df = debug_flags; df->label != '\0'; df++) { 1153 if (df->flag & (df->flag - 1)) 1154 continue; 1155 if (is_alpha(df->label) && 1156 (df->flag << DBG_VBOSE_SHIFT) & DFlags) 1157 out1c(df->label - ('a' - 'A')); 1158 else if (df->flag & DFlags) 1159 out1c(df->label); 1160 } 1161 out1c('\n'); 1162 return 0; 1163 } 1164 1165 while (*++argv) { 1166 if (**argv == '-') 1167 set_debug(*argv + 1, 1); 1168 else if (**argv == '+') 1169 set_debug(*argv + 1, 0); 1170 else 1171 return 1; 1172 } 1173 return 0; 1174 } 1175 1176 #endif /* DEBUG */ 1177