1*1c3b0983Skre /* $NetBSD: show.c,v 1.59 2024/11/11 22:57:42 kre Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1991, 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 75dd8362aSkre * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved. 85dd8362aSkre * 961f28255Scgd * This code is derived from software contributed to Berkeley by 1061f28255Scgd * Kenneth Almquist. 1161f28255Scgd * 1261f28255Scgd * Redistribution and use in source and binary forms, with or without 1361f28255Scgd * modification, are permitted provided that the following conditions 1461f28255Scgd * are met: 1561f28255Scgd * 1. Redistributions of source code must retain the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer. 1761f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1861f28255Scgd * notice, this list of conditions and the following disclaimer in the 1961f28255Scgd * documentation and/or other materials provided with the distribution. 209d15d213Skre * 3. Neither the name of the University nor the names of its contributors 219d15d213Skre * may be used to endorse or promote products derived from this software 229d15d213Skre * without specific prior written permission. 2361f28255Scgd * 249d15d213Skre * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 259d15d213Skre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 269d15d213Skre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 279d15d213Skre * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2861f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2961f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3061f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3161f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3261f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3361f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3461f28255Scgd * SUCH DAMAGE. 3561f28255Scgd */ 3661f28255Scgd 37cd799663Schristos #include <sys/cdefs.h> 3861f28255Scgd #ifndef lint 3949f0ad86Scgd #if 0 4007bae7edSchristos static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; 4149f0ad86Scgd #else 42*1c3b0983Skre __RCSID("$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 kre Exp $"); 4349f0ad86Scgd #endif 4461f28255Scgd #endif /* not lint */ 4561f28255Scgd 4661f28255Scgd #include <stdio.h> 4707bae7edSchristos #include <stdarg.h> 48c0cf1d6bSdsl #include <stdlib.h> 49d452d7e7Schristos #include <unistd.h> 505dd8362aSkre #include <fcntl.h> 515dd8362aSkre #include <errno.h> 52ab36694aSkre #include <limits.h> 535dd8362aSkre 545dd8362aSkre #include <sys/types.h> 555dd8362aSkre #include <sys/uio.h> 5607bae7edSchristos 5761f28255Scgd #include "shell.h" 5861f28255Scgd #include "parser.h" 5961f28255Scgd #include "nodes.h" 6061f28255Scgd #include "mystring.h" 6107bae7edSchristos #include "show.h" 62c02b3bbdSchristos #include "options.h" 635dd8362aSkre #include "redir.h" 645dd8362aSkre #include "error.h" 65ab36694aSkre #include "syntax.h" 668ffd1099Skre #include "input.h" 67ab36694aSkre #include "output.h" 68727a69dcSkre #include "var.h" 69ab36694aSkre #include "builtins.h" 705dd8362aSkre 71ed090bddSchristos #define DEFINE_NODENAMES 725dd8362aSkre #include "nodenames.h" /* does almost nothing if !defined(DEBUG) */ 735dd8362aSkre 745dd8362aSkre #define TR_STD_WIDTH 60 /* tend to fold lines wider than this */ 755dd8362aSkre #define TR_IOVECS 10 /* number of lines or trace (max) / write */ 765dd8362aSkre 775dd8362aSkre typedef struct traceinfo { 785dd8362aSkre int tfd; /* file descriptor for open trace file */ 795dd8362aSkre int nxtiov; /* the buffer we should be writing to */ 805dd8362aSkre char lastc; /* the last non-white character output */ 81d13b5926Skre uint8_t supr; /* char classes to suppress after \n */ 825dd8362aSkre pid_t pid; /* process id of process that opened that file */ 835dd8362aSkre size_t llen; /* number of chars in current output line */ 845dd8362aSkre size_t blen; /* chars used in current buffer being filled */ 855dd8362aSkre char * tracefile; /* name of the tracefile */ 865dd8362aSkre struct iovec lines[TR_IOVECS]; /* filled, flling, pending buffers */ 875dd8362aSkre } TFILE; 885dd8362aSkre 895dd8362aSkre /* These are auto turned off when non white space is printed */ 905dd8362aSkre #define SUP_NL 0x01 /* don't print \n */ 91d13b5926Skre #define SUP_SP 0x03 /* suppress spaces */ 92d13b5926Skre #define SUP_WSP 0x04 /* suppress all white space */ 935dd8362aSkre 945dd8362aSkre #ifdef DEBUG /* from here to end of file ... */ 955dd8362aSkre 965dd8362aSkre TFILE tracedata, *tracetfile; 975dd8362aSkre FILE *tracefile; /* just for histedit */ 985dd8362aSkre 995dd8362aSkre uint64_t DFlags; /* currently enabled debug flags */ 1005dd8362aSkre int ShNest; /* depth of shell (internal) nesting */ 1015dd8362aSkre 1025dd8362aSkre static void shtree(union node *, int, int, int, TFILE *); 1035dd8362aSkre static void shcmd(union node *, TFILE *); 1045dd8362aSkre static void shsubsh(union node *, TFILE *); 1055dd8362aSkre static void shredir(union node *, TFILE *, int); 1065dd8362aSkre static void sharg(union node *, TFILE *); 1075dd8362aSkre static void indent(int, TFILE *); 1085dd8362aSkre static void trstring(const char *); 1095dd8362aSkre static void trace_putc(char, TFILE *); 1105dd8362aSkre static void trace_puts(const char *, TFILE *); 1115dd8362aSkre static void trace_flush(TFILE *, int); 1125dd8362aSkre static char *trace_id(TFILE *); 1135dd8362aSkre static void trace_fd_swap(int, int); 1145dd8362aSkre 1155dd8362aSkre inline static int trlinelen(TFILE *); 116d58ec7fcSkre 117d58ec7fcSkre 1185dd8362aSkre /* 1195dd8362aSkre * These functions are the externally visible interface 1205dd8362aSkre * (but only for a DEBUG shell.) 1215dd8362aSkre */ 1229d15d213Skre 1239d15d213Skre void 1245dd8362aSkre opentrace(void) 1259d15d213Skre { 1265dd8362aSkre char *s; 1275dd8362aSkre int fd; 1289d15d213Skre int i; 1295dd8362aSkre pid_t pid; 1309d15d213Skre 1315dd8362aSkre if (debug != 1) { 1325dd8362aSkre /* leave fd open because libedit might be using it */ 1335dd8362aSkre if (tracefile) 1345dd8362aSkre fflush(tracefile); 1355dd8362aSkre if (tracetfile) 1365dd8362aSkre trace_flush(tracetfile, 1); 13761f28255Scgd return; 13861f28255Scgd } 1395dd8362aSkre #if DBG_PID == 1 /* using old shell.h, old tracing method */ 1405dd8362aSkre DFlags = DBG_PID; /* just force DBG_PID on, and leave it ... */ 1419d15d213Skre #endif 1425dd8362aSkre pid = getpid(); 1435dd8362aSkre if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) { 1445dd8362aSkre debug = 0; 1455dd8362aSkre error("Cannot asprintf tracefilename"); 1465dd8362aSkre }; 1475dd8362aSkre 1485dd8362aSkre fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666); 1495dd8362aSkre if (fd == -1) { 1505dd8362aSkre debug = 0; 1515dd8362aSkre error("Can't open tracefile: %s (%s)\n", s, strerror(errno)); 1525dd8362aSkre } 1535dd8362aSkre fd = to_upper_fd(fd); 1545dd8362aSkre if (fd <= 2) { 1555dd8362aSkre (void) close(fd); 1565dd8362aSkre debug = 0; 1575dd8362aSkre error("Attempt to use fd %d as tracefile thwarted\n", fd); 1585dd8362aSkre } 1595dd8362aSkre register_sh_fd(fd, trace_fd_swap); 1605dd8362aSkre 1615dd8362aSkre /* 1625dd8362aSkre * This stuff is just so histedit has a FILE * to use 1635dd8362aSkre */ 1645dd8362aSkre if (tracefile) 1655dd8362aSkre (void) fclose(tracefile); /* also closes tfd */ 166*1c3b0983Skre tracefile = fdopen(fd, "ae"); /* don't care if it is NULL */ 1675dd8362aSkre if (tracefile) /* except here... */ 1685dd8362aSkre setlinebuf(tracefile); 1695dd8362aSkre 1705dd8362aSkre /* 1715dd8362aSkre * Now the real tracing setup 1725dd8362aSkre */ 1735dd8362aSkre if (tracedata.tfd > 0 && tracedata.tfd != fd) 1745dd8362aSkre (void) close(tracedata.tfd); /* usually done by fclose() */ 1755dd8362aSkre 1765dd8362aSkre tracedata.tfd = fd; 1775dd8362aSkre tracedata.pid = pid; 1785dd8362aSkre tracedata.nxtiov = 0; 1795dd8362aSkre tracedata.blen = 0; 1805dd8362aSkre tracedata.llen = 0; 1815dd8362aSkre tracedata.lastc = '\0'; 1825dd8362aSkre tracedata.supr = SUP_NL | SUP_WSP; 1835dd8362aSkre 1845dd8362aSkre #define replace(f, v) do { \ 1855dd8362aSkre if (tracedata.f != NULL) \ 1865dd8362aSkre free(tracedata.f); \ 1875dd8362aSkre tracedata.f = v; \ 1884cb87529Srillig } while (0) 1895dd8362aSkre 1905dd8362aSkre replace(tracefile, s); 1915dd8362aSkre 1925dd8362aSkre for (i = 0; i < TR_IOVECS; i++) { 1935dd8362aSkre replace(lines[i].iov_base, NULL); 1945dd8362aSkre tracedata.lines[i].iov_len = 0; 1955dd8362aSkre } 1965dd8362aSkre 1975dd8362aSkre #undef replace 1985dd8362aSkre 1995dd8362aSkre tracetfile = &tracedata; 2005dd8362aSkre 2015dd8362aSkre trace_puts("\nTracing started.\n", tracetfile); 2025dd8362aSkre } 20361f28255Scgd 20407bae7edSchristos void 20507bae7edSchristos trace(const char *fmt, ...) 20661f28255Scgd { 20707bae7edSchristos va_list va; 2085dd8362aSkre char *s; 2090b398b28Swiz 2105dd8362aSkre if (debug != 1 || !tracetfile) 211c02b3bbdSchristos return; 21207bae7edSchristos va_start(va, fmt); 2135dd8362aSkre (void) vasprintf(&s, fmt, va); 21407bae7edSchristos va_end(va); 2155dd8362aSkre 2165dd8362aSkre trace_puts(s, tracetfile); 2175dd8362aSkre free(s); 2185dd8362aSkre if (tracetfile->llen == 0) 2195dd8362aSkre trace_flush(tracetfile, 0); 22061f28255Scgd } 22161f28255Scgd 222e314f958Sdsl void 223e314f958Sdsl tracev(const char *fmt, va_list va) 224e314f958Sdsl { 225d452d7e7Schristos va_list ap; 2265dd8362aSkre char *s; 2275dd8362aSkre 2285dd8362aSkre if (debug != 1 || !tracetfile) 229e314f958Sdsl return; 230d452d7e7Schristos va_copy(ap, va); 2315dd8362aSkre (void) vasprintf(&s, fmt, ap); 232d452d7e7Schristos va_end(ap); 2335dd8362aSkre 2345dd8362aSkre trace_puts(s, tracetfile); 2355dd8362aSkre free(s); 2365dd8362aSkre if (tracetfile->llen == 0) 2375dd8362aSkre trace_flush(tracetfile, 0); 2389d15d213Skre } 239d58ec7fcSkre 240d58ec7fcSkre 2419d15d213Skre void 2429d15d213Skre trputs(const char *s) 243d58ec7fcSkre { 2445dd8362aSkre if (debug != 1 || !tracetfile) 245d58ec7fcSkre return; 2465dd8362aSkre trace_puts(s, tracetfile); 2475dd8362aSkre } 2485dd8362aSkre 2495dd8362aSkre void 2505dd8362aSkre trputc(int c) 2515dd8362aSkre { 2525dd8362aSkre if (debug != 1 || !tracetfile) 2535dd8362aSkre return; 2545dd8362aSkre trace_putc(c, tracetfile); 2555dd8362aSkre } 2565dd8362aSkre 2575dd8362aSkre void 2585dd8362aSkre showtree(union node *n) 2595dd8362aSkre { 2605dd8362aSkre TFILE *fp; 2615dd8362aSkre 2625dd8362aSkre if ((fp = tracetfile) == NULL) 2635dd8362aSkre return; 2645dd8362aSkre 2655dd8362aSkre trace_puts("showtree(", fp); 2665dd8362aSkre if (n == NULL) 2675dd8362aSkre trace_puts("NULL", fp); 2685dd8362aSkre else if (n == NEOF) 2695dd8362aSkre trace_puts("NEOF", fp); 2705dd8362aSkre else 2715dd8362aSkre trace("%p", n); 2725dd8362aSkre trace_puts(") called\n", fp); 2735dd8362aSkre if (n != NULL && n != NEOF) 2745dd8362aSkre shtree(n, 1, 1, 1, fp); 2755dd8362aSkre } 2765dd8362aSkre 2775dd8362aSkre void 2785dd8362aSkre trargs(char **ap) 2795dd8362aSkre { 2805dd8362aSkre if (debug != 1 || !tracetfile) 2815dd8362aSkre return; 2825dd8362aSkre while (*ap) { 2835dd8362aSkre trstring(*ap++); 2845dd8362aSkre if (*ap) 2855dd8362aSkre trace_putc(' ', tracetfile); 2865dd8362aSkre } 2876f4f3666Skre trace_putc('\n', tracetfile); 2885dd8362aSkre } 2895dd8362aSkre 29019986c5fSkre void 29119986c5fSkre trargstr(union node *n) 29219986c5fSkre { 29319986c5fSkre sharg(n, tracetfile); 29419986c5fSkre } 29519986c5fSkre 2965dd8362aSkre 2975dd8362aSkre /* 2985dd8362aSkre * Beyond here we just have the implementation of all of that 2995dd8362aSkre */ 3005dd8362aSkre 3015dd8362aSkre 3025dd8362aSkre inline static int 3035dd8362aSkre trlinelen(TFILE * fp) 3045dd8362aSkre { 3055dd8362aSkre return fp->llen; 3065dd8362aSkre } 3075dd8362aSkre 3085dd8362aSkre static void 3095dd8362aSkre shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp) 3105dd8362aSkre { 3115dd8362aSkre struct nodelist *lp; 3125dd8362aSkre const char *s; 3135dd8362aSkre 3145dd8362aSkre if (n == NULL) { 3155dd8362aSkre if (nl) 3165dd8362aSkre trace_putc('\n', fp); 3175dd8362aSkre return; 3185dd8362aSkre } 3195dd8362aSkre 3205dd8362aSkre indent(ind, fp); 3215dd8362aSkre switch (n->type) { 3225dd8362aSkre case NSEMI: 3235dd8362aSkre s = NULL; 3245dd8362aSkre goto binop; 3255dd8362aSkre case NAND: 3265dd8362aSkre s = " && "; 3275dd8362aSkre goto binop; 3285dd8362aSkre case NOR: 3295dd8362aSkre s = " || "; 3305dd8362aSkre binop: 3315dd8362aSkre shtree(n->nbinary.ch1, 0, ilvl, 0, fp); 3325dd8362aSkre if (s != NULL) 3335dd8362aSkre trace_puts(s, fp); 3345dd8362aSkre if (trlinelen(fp) >= TR_STD_WIDTH) { 3355dd8362aSkre trace_putc('\n', fp); 3365dd8362aSkre indent(ind < 0 ? 2 : ind + 1, fp); 3375dd8362aSkre } else if (s == NULL) { 3385dd8362aSkre if (fp->lastc != '&') 3395dd8362aSkre trace_puts("; ", fp); 3405dd8362aSkre else 3415dd8362aSkre trace_putc(' ', fp); 3425dd8362aSkre } 3435dd8362aSkre shtree(n->nbinary.ch2, 0, ilvl, nl, fp); 3445dd8362aSkre break; 3455dd8362aSkre case NCMD: 3465dd8362aSkre shcmd(n, fp); 3475dd8362aSkre if (n->ncmd.backgnd) 3485dd8362aSkre trace_puts(" &", fp); 3495dd8362aSkre if (nl && trlinelen(fp) > 0) 3505dd8362aSkre trace_putc('\n', fp); 3515dd8362aSkre break; 3525dd8362aSkre case NPIPE: 3535dd8362aSkre for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 3545dd8362aSkre shtree(lp->n, 0, ilvl, 0, fp); 3555dd8362aSkre if (lp->next) { 3565dd8362aSkre trace_puts(" |", fp); 3575dd8362aSkre if (trlinelen(fp) >= TR_STD_WIDTH) { 3585dd8362aSkre trace_putc('\n', fp); 3595dd8362aSkre indent((ind < 0 ? ilvl : ind) + 1, fp); 3605dd8362aSkre } else 3615dd8362aSkre trace_putc(' ', fp); 3625dd8362aSkre } 3635dd8362aSkre } 3645dd8362aSkre if (n->npipe.backgnd) 3655dd8362aSkre trace_puts(" &", fp); 3665dd8362aSkre if (nl || trlinelen(fp) >= TR_STD_WIDTH) 3675dd8362aSkre trace_putc('\n', fp); 3685dd8362aSkre break; 3695dd8362aSkre case NBACKGND: 3705dd8362aSkre case NSUBSHELL: 3715dd8362aSkre shsubsh(n, fp); 3725dd8362aSkre if (n->type == NBACKGND) 3735dd8362aSkre trace_puts(" &", fp); 3745dd8362aSkre if (nl && trlinelen(fp) > 0) 3755dd8362aSkre trace_putc('\n', fp); 3765dd8362aSkre break; 3775dd8362aSkre case NDEFUN: 3785dd8362aSkre trace_puts(n->narg.text, fp); 3795dd8362aSkre trace_puts("() {\n", fp); 3805dd8362aSkre indent(ind, fp); 3815dd8362aSkre shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp); 3825dd8362aSkre indent(ind, fp); 3835dd8362aSkre trace_puts("}\n", fp); 3845dd8362aSkre break; 3855dd8362aSkre case NDNOT: 3865dd8362aSkre trace_puts("! ", fp); 3875dd8362aSkre /* FALLTHROUGH */ 3885dd8362aSkre case NNOT: 3895dd8362aSkre trace_puts("! ", fp); 3905dd8362aSkre shtree(n->nnot.com, -1, ilvl, nl, fp); 3915dd8362aSkre break; 3925dd8362aSkre case NREDIR: 3935dd8362aSkre shtree(n->nredir.n, -1, ilvl, 0, fp); 3945dd8362aSkre shredir(n->nredir.redirect, fp, n->nredir.n == NULL); 3955dd8362aSkre if (nl) 3965dd8362aSkre trace_putc('\n', fp); 3975dd8362aSkre break; 3985dd8362aSkre 3995dd8362aSkre case NIF: 4005dd8362aSkre itsif: 4015dd8362aSkre trace_puts("if ", fp); 4025dd8362aSkre shtree(n->nif.test, -1, ilvl, 0, fp); 4035dd8362aSkre if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 4045dd8362aSkre if (fp->lastc != '&') 4055dd8362aSkre trace_puts(" ;", fp); 4065dd8362aSkre } else 4075dd8362aSkre indent(ilvl, fp); 4085dd8362aSkre trace_puts(" then ", fp); 4095dd8362aSkre if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 4105dd8362aSkre indent(ilvl+1, fp); 4115dd8362aSkre shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp); 4125dd8362aSkre if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 4135dd8362aSkre if (fp->lastc != '&') 4145dd8362aSkre trace_puts(" ;", fp); 4155dd8362aSkre } else 4165dd8362aSkre indent(ilvl, fp); 4175dd8362aSkre if (n->nif.elsepart && n->nif.elsepart->type == NIF) { 4185dd8362aSkre if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 4195dd8362aSkre indent(ilvl, fp); 4205dd8362aSkre n = n->nif.elsepart; 4215dd8362aSkre trace_puts(" el", fp); 4225dd8362aSkre goto itsif; 4235dd8362aSkre } 4245dd8362aSkre if (n->nif.elsepart) { 4255dd8362aSkre if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) 4265dd8362aSkre indent(ilvl+1, fp); 4275dd8362aSkre trace_puts(" else ", fp); 4285dd8362aSkre shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp); 4295dd8362aSkre if (fp->lastc != '&') 4305dd8362aSkre trace_puts(" ;", fp); 4315dd8362aSkre } 4325dd8362aSkre trace_puts(" fi", fp); 4335dd8362aSkre if (nl) 4345dd8362aSkre trace_putc('\n', fp); 4355dd8362aSkre break; 4365dd8362aSkre 4375dd8362aSkre case NWHILE: 4385dd8362aSkre trace_puts("while ", fp); 4395dd8362aSkre goto aloop; 4405dd8362aSkre case NUNTIL: 4415dd8362aSkre trace_puts("until ", fp); 4425dd8362aSkre aloop: 4435dd8362aSkre shtree(n->nbinary.ch1, -1, ilvl, 0, fp); 4445dd8362aSkre if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 4455dd8362aSkre if (fp->lastc != '&') 4465dd8362aSkre trace_puts(" ;", fp); 4475dd8362aSkre } else 4485dd8362aSkre trace_putc('\n', fp); 4495dd8362aSkre trace_puts(" do ", fp); 4505dd8362aSkre shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp); 4515dd8362aSkre trace_puts(" done ", fp); 4525dd8362aSkre if (nl) 4535dd8362aSkre trace_putc('\n', fp); 4545dd8362aSkre break; 4555dd8362aSkre 4565dd8362aSkre case NFOR: 4575dd8362aSkre trace_puts("for ", fp); 4585dd8362aSkre trace_puts(n->nfor.var, fp); 4595dd8362aSkre if (n->nfor.args) { 4605dd8362aSkre union node *argp; 4615dd8362aSkre 4625dd8362aSkre trace_puts(" in ", fp); 4635dd8362aSkre for (argp = n->nfor.args; argp; argp=argp->narg.next) { 4645dd8362aSkre sharg(argp, fp); 4655dd8362aSkre trace_putc(' ', fp); 4665dd8362aSkre } 4675dd8362aSkre if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { 4685dd8362aSkre if (fp->lastc != '&') 4695dd8362aSkre trace_putc(';', fp); 4705dd8362aSkre } else 4715dd8362aSkre trace_putc('\n', fp); 4725dd8362aSkre } 4735dd8362aSkre trace_puts(" do ", fp); 4745dd8362aSkre shtree(n->nfor.body, -1, ilvl + 1, 0, fp); 4755dd8362aSkre if (fp->lastc != '&') 4765dd8362aSkre trace_putc(';', fp); 4775dd8362aSkre trace_puts(" done", fp); 4785dd8362aSkre if (nl) 4795dd8362aSkre trace_putc('\n', fp); 4805dd8362aSkre break; 4815dd8362aSkre 4825dd8362aSkre case NCASE: 4835dd8362aSkre trace_puts("case ", fp); 4845dd8362aSkre sharg(n->ncase.expr, fp); 4855dd8362aSkre trace_puts(" in", fp); 4865dd8362aSkre if (nl) 4875dd8362aSkre trace_putc('\n', fp); 4885dd8362aSkre { 4895dd8362aSkre union node *cp; 4905dd8362aSkre 4915dd8362aSkre for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) { 4925dd8362aSkre union node *patp; 4935dd8362aSkre 4945dd8362aSkre if (nl || trlinelen(fp) > TR_STD_WIDTH - 16) 4955dd8362aSkre indent(ilvl, fp); 4965dd8362aSkre else 4975dd8362aSkre trace_putc(' ', fp); 4985dd8362aSkre trace_putc('(', fp); 4995dd8362aSkre patp = cp->nclist.pattern; 5005dd8362aSkre while (patp != NULL) { 5015dd8362aSkre trace_putc(' ', fp); 5025dd8362aSkre sharg(patp, fp); 5035dd8362aSkre trace_putc(' ', fp); 5045dd8362aSkre if ((patp = patp->narg.next) != NULL) 5055dd8362aSkre trace_putc('|', fp); 5065dd8362aSkre } 5075dd8362aSkre trace_putc(')', fp); 5085dd8362aSkre if (nl) 5095dd8362aSkre indent(ilvl + 1, fp); 5105dd8362aSkre else 5115dd8362aSkre trace_putc(' ', fp); 5125dd8362aSkre shtree(cp->nclist.body, -1, ilvl+2, 0, fp); 5135dd8362aSkre if (cp->type == NCLISTCONT) 5145dd8362aSkre trace_puts(" ;&", fp); 5155dd8362aSkre else 5165dd8362aSkre trace_puts(" ;;", fp); 5175dd8362aSkre if (nl) 5185dd8362aSkre trace_putc('\n', fp); 5195dd8362aSkre } 5205dd8362aSkre } 5215dd8362aSkre if (nl) { 5225dd8362aSkre trace_putc('\n', fp); 5235dd8362aSkre indent(ind, fp); 5245dd8362aSkre } else 5255dd8362aSkre trace_putc(' ', fp); 5265dd8362aSkre trace_puts("esac", fp); 5275dd8362aSkre if (nl) 5285dd8362aSkre trace_putc('\n', fp); 5295dd8362aSkre break; 5305dd8362aSkre 5315dd8362aSkre default: { 5325dd8362aSkre char *str; 5335dd8362aSkre 5345dd8362aSkre asprintf(&str, "<node type %d [%s]>", n->type, 5355dd8362aSkre NODETYPENAME(n->type)); 5365dd8362aSkre trace_puts(str, fp); 5375dd8362aSkre free(str); 5385dd8362aSkre if (nl) 5395dd8362aSkre trace_putc('\n', fp); 5405dd8362aSkre } 5415dd8362aSkre break; 5425dd8362aSkre } 54361f28255Scgd } 54461f28255Scgd 54561f28255Scgd 5464ce0d34aScgd static void 5475dd8362aSkre shcmd(union node *cmd, TFILE *fp) 5485dd8362aSkre { 5495dd8362aSkre union node *np; 5505dd8362aSkre int first; 5515dd8362aSkre 5525dd8362aSkre first = 1; 5535dd8362aSkre for (np = cmd->ncmd.args ; np ; np = np->narg.next) { 5545dd8362aSkre if (! first) 5555dd8362aSkre trace_putc(' ', fp); 5565dd8362aSkre sharg(np, fp); 5575dd8362aSkre first = 0; 5585dd8362aSkre } 5595dd8362aSkre shredir(cmd->ncmd.redirect, fp, first); 5605dd8362aSkre } 5615dd8362aSkre 5625dd8362aSkre static void 5635dd8362aSkre shsubsh(union node *cmd, TFILE *fp) 5645dd8362aSkre { 5655dd8362aSkre trace_puts(" ( ", fp); 5665dd8362aSkre shtree(cmd->nredir.n, -1, 3, 0, fp); 5675dd8362aSkre trace_puts(" ) ", fp); 5685dd8362aSkre shredir(cmd->ncmd.redirect, fp, 1); 5695dd8362aSkre } 5705dd8362aSkre 5715dd8362aSkre static void 5725dd8362aSkre shredir(union node *np, TFILE *fp, int first) 5735dd8362aSkre { 5745dd8362aSkre const char *s; 5755dd8362aSkre int dftfd; 5765dd8362aSkre char buf[106]; 5775dd8362aSkre 5785dd8362aSkre for ( ; np ; np = np->nfile.next) { 5795dd8362aSkre if (! first) 5805dd8362aSkre trace_putc(' ', fp); 5815dd8362aSkre switch (np->nfile.type) { 5825dd8362aSkre case NTO: s = ">"; dftfd = 1; break; 5835dd8362aSkre case NCLOBBER: s = ">|"; dftfd = 1; break; 5845dd8362aSkre case NAPPEND: s = ">>"; dftfd = 1; break; 5855dd8362aSkre case NTOFD: s = ">&"; dftfd = 1; break; 5865dd8362aSkre case NFROM: s = "<"; dftfd = 0; break; 5875dd8362aSkre case NFROMFD: s = "<&"; dftfd = 0; break; 5885dd8362aSkre case NFROMTO: s = "<>"; dftfd = 0; break; 5895dd8362aSkre case NXHERE: /* FALLTHROUGH */ 5905dd8362aSkre case NHERE: s = "<<"; dftfd = 0; break; 5915dd8362aSkre default: s = "*error*"; dftfd = 0; break; 5925dd8362aSkre } 5935dd8362aSkre if (np->nfile.fd != dftfd) { 5945dd8362aSkre sprintf(buf, "%d", np->nfile.fd); 5955dd8362aSkre trace_puts(buf, fp); 5965dd8362aSkre } 5975dd8362aSkre trace_puts(s, fp); 5985dd8362aSkre if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { 5995dd8362aSkre if (np->ndup.vname) 6005dd8362aSkre sharg(np->ndup.vname, fp); 6015dd8362aSkre else { 6023a41fe18Skre if (np->ndup.dupfd < 0) 6033a41fe18Skre trace_puts("-", fp); 6043a41fe18Skre else { 6055dd8362aSkre sprintf(buf, "%d", np->ndup.dupfd); 6065dd8362aSkre trace_puts(buf, fp); 6075dd8362aSkre } 6083a41fe18Skre } 6095dd8362aSkre } else 6105dd8362aSkre if (np->nfile.type == NHERE || np->nfile.type == NXHERE) { 6115dd8362aSkre if (np->nfile.type == NHERE) 6125dd8362aSkre trace_putc('\\', fp); 6135dd8362aSkre trace_puts("!!!\n", fp); 6145dd8362aSkre s = np->nhere.doc->narg.text; 6155dd8362aSkre if (strlen(s) > 100) { 6165dd8362aSkre memmove(buf, s, 100); 6175dd8362aSkre buf[100] = '\0'; 6185dd8362aSkre strcat(buf, " ...\n"); 6195dd8362aSkre s = buf; 6205dd8362aSkre } 6215dd8362aSkre trace_puts(s, fp); 6225dd8362aSkre trace_puts("!!! ", fp); 6235dd8362aSkre } else { 6245dd8362aSkre sharg(np->nfile.fname, fp); 6255dd8362aSkre } 6265dd8362aSkre first = 0; 6275dd8362aSkre } 6285dd8362aSkre } 6295dd8362aSkre 6305dd8362aSkre static void 6315dd8362aSkre sharg(union node *arg, TFILE *fp) 6325dd8362aSkre { 6335dd8362aSkre char *p, *s; 6345dd8362aSkre struct nodelist *bqlist; 6355dd8362aSkre int subtype = 0; 6365dd8362aSkre int quoted = 0; 6375dd8362aSkre 6385dd8362aSkre if (arg->type != NARG) { 6395dd8362aSkre asprintf(&s, "<node type %d> ! NARG\n", arg->type); 6405dd8362aSkre trace_puts(s, fp); 6415dd8362aSkre abort(); /* no need to free s, better not to */ 6425dd8362aSkre } 6435dd8362aSkre 6445dd8362aSkre bqlist = arg->narg.backquote; 6455dd8362aSkre for (p = arg->narg.text ; *p ; p++) { 6465dd8362aSkre switch (*p) { 6475dd8362aSkre case CTLESC: 6483e2349baSkre if (BASESYNTAX[(int)p[1]] != CCTL) 6495dd8362aSkre trace_putc('\\', fp); 6505dd8362aSkre trace_putc(*++p, fp); 6515dd8362aSkre break; 6525dd8362aSkre 653727a69dcSkre case CTLNONL: 654727a69dcSkre trace_putc('\\', fp); 655727a69dcSkre trace_putc('\n', fp); 656727a69dcSkre break; 657727a69dcSkre 6585dd8362aSkre case CTLVAR: 6595dd8362aSkre subtype = *++p; 6605dd8362aSkre if (!quoted != !(subtype & VSQUOTE)) 6615dd8362aSkre trace_putc('"', fp); 6625dd8362aSkre trace_putc('$', fp); 663727a69dcSkre trace_putc('{', fp); /*}*/ 6645dd8362aSkre if ((subtype & VSTYPE) == VSLENGTH) 6655dd8362aSkre trace_putc('#', fp); 6665dd8362aSkre if (subtype & VSLINENO) 6675dd8362aSkre trace_puts("LINENO=", fp); 6685dd8362aSkre 6695dd8362aSkre while (*++p != '=') 6705dd8362aSkre trace_putc(*p, fp); 6715dd8362aSkre 6725dd8362aSkre if (subtype & VSNUL) 6735dd8362aSkre trace_putc(':', fp); 6745dd8362aSkre 6755dd8362aSkre switch (subtype & VSTYPE) { 6765dd8362aSkre case VSNORMAL: 6775dd8362aSkre /* { */ 6785dd8362aSkre trace_putc('}', fp); 6795dd8362aSkre if (!quoted != !(subtype & VSQUOTE)) 6805dd8362aSkre trace_putc('"', fp); 6815dd8362aSkre break; 6825dd8362aSkre case VSMINUS: 6835dd8362aSkre trace_putc('-', fp); 6845dd8362aSkre break; 6855dd8362aSkre case VSPLUS: 6865dd8362aSkre trace_putc('+', fp); 6875dd8362aSkre break; 6885dd8362aSkre case VSQUESTION: 6895dd8362aSkre trace_putc('?', fp); 6905dd8362aSkre break; 6915dd8362aSkre case VSASSIGN: 6925dd8362aSkre trace_putc('=', fp); 6935dd8362aSkre break; 6945dd8362aSkre case VSTRIMLEFTMAX: 6955dd8362aSkre trace_putc('#', fp); 6965dd8362aSkre /* FALLTHROUGH */ 6975dd8362aSkre case VSTRIMLEFT: 6985dd8362aSkre trace_putc('#', fp); 6995dd8362aSkre break; 7005dd8362aSkre case VSTRIMRIGHTMAX: 7015dd8362aSkre trace_putc('%', fp); 7025dd8362aSkre /* FALLTHROUGH */ 7035dd8362aSkre case VSTRIMRIGHT: 7045dd8362aSkre trace_putc('%', fp); 7055dd8362aSkre break; 7065dd8362aSkre case VSLENGTH: 7075dd8362aSkre break; 7085dd8362aSkre default: { 7095dd8362aSkre char str[32]; 7105dd8362aSkre 7115dd8362aSkre snprintf(str, sizeof str, 7125dd8362aSkre "<subtype %d>", subtype); 7135dd8362aSkre trace_puts(str, fp); 7145dd8362aSkre } 7155dd8362aSkre break; 7165dd8362aSkre } 7175dd8362aSkre break; 7185dd8362aSkre case CTLENDVAR: 7195dd8362aSkre /* { */ 7205dd8362aSkre trace_putc('}', fp); 7215dd8362aSkre if (!quoted != !(subtype & VSQUOTE)) 7225dd8362aSkre trace_putc('"', fp); 7235dd8362aSkre subtype = 0; 7245dd8362aSkre break; 7255dd8362aSkre 7265dd8362aSkre case CTLBACKQ|CTLQUOTE: 7275dd8362aSkre if (!quoted) 7285dd8362aSkre trace_putc('"', fp); 7295dd8362aSkre /* FALLTHRU */ 7305dd8362aSkre case CTLBACKQ: 7315dd8362aSkre trace_putc('$', fp); 7325dd8362aSkre trace_putc('(', fp); 7335dd8362aSkre if (bqlist) { 7345dd8362aSkre shtree(bqlist->n, -1, 3, 0, fp); 7355dd8362aSkre bqlist = bqlist->next; 7365dd8362aSkre } else 7375dd8362aSkre trace_puts("???", fp); 7385dd8362aSkre trace_putc(')', fp); 7395dd8362aSkre if (!quoted && *p == (CTLBACKQ|CTLQUOTE)) 7405dd8362aSkre trace_putc('"', fp); 7415dd8362aSkre break; 7425dd8362aSkre 7435dd8362aSkre case CTLQUOTEMARK: 7445dd8362aSkre if (subtype != 0 || !quoted) { 7455dd8362aSkre trace_putc('"', fp); 7465dd8362aSkre quoted++; 7475dd8362aSkre } 7485dd8362aSkre break; 7495dd8362aSkre case CTLQUOTEEND: 7505dd8362aSkre trace_putc('"', fp); 7515dd8362aSkre quoted--; 7525dd8362aSkre break; 7535dd8362aSkre case CTLARI: 754727a69dcSkre if (*p == ' ') 755727a69dcSkre p++; 7565dd8362aSkre trace_puts("$(( ", fp); 7575dd8362aSkre break; 7585dd8362aSkre case CTLENDARI: 7595dd8362aSkre trace_puts(" ))", fp); 7605dd8362aSkre break; 7615dd8362aSkre 7625dd8362aSkre default: 7635dd8362aSkre if (*p == '$') 7645dd8362aSkre trace_putc('\\', fp); 7655dd8362aSkre trace_putc(*p, fp); 7665dd8362aSkre break; 7675dd8362aSkre } 7685dd8362aSkre } 7695dd8362aSkre if (quoted) 7705dd8362aSkre trace_putc('"', fp); 7715dd8362aSkre } 7725dd8362aSkre 7735dd8362aSkre 7745dd8362aSkre static void 7755dd8362aSkre indent(int amount, TFILE *fp) 7765dd8362aSkre { 7775dd8362aSkre int i; 7785dd8362aSkre 7795dd8362aSkre if (amount <= 0) 7805dd8362aSkre return; 7815dd8362aSkre 7825dd8362aSkre amount <<= 2; /* indent slots -> chars */ 7835dd8362aSkre 7845dd8362aSkre i = trlinelen(fp); 7855dd8362aSkre fp->supr = SUP_NL; 7865dd8362aSkre if (i > amount) { 7875dd8362aSkre trace_putc('\n', fp); 7885dd8362aSkre i = 0; 7895dd8362aSkre } 7905dd8362aSkre fp->supr = 0; 7915dd8362aSkre for (; i < amount - 7 ; i++) { 7925dd8362aSkre trace_putc('\t', fp); 7935dd8362aSkre i |= 7; 7945dd8362aSkre } 7955dd8362aSkre while (i < amount) { 7965dd8362aSkre trace_putc(' ', fp); 7975dd8362aSkre i++; 7985dd8362aSkre } 7995dd8362aSkre fp->supr = SUP_WSP; 8005dd8362aSkre } 8015dd8362aSkre 8025dd8362aSkre static void 8035dd8362aSkre trace_putc(char c, TFILE *fp) 80461f28255Scgd { 80548250187Stls char *p; 8065dd8362aSkre 8075dd8362aSkre if (c == '\0') 8085dd8362aSkre return; 8095dd8362aSkre if (debug == 0 || fp == NULL) 8105dd8362aSkre return; 8115dd8362aSkre 8125dd8362aSkre if (fp->llen == 0) { 8135dd8362aSkre if (fp->blen != 0) 8145dd8362aSkre abort(); 8155dd8362aSkre 8165dd8362aSkre if ((fp->supr & SUP_NL) && c == '\n') 8175dd8362aSkre return; 8185dd8362aSkre if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ') 8195dd8362aSkre return; 8205dd8362aSkre if ((fp->supr & SUP_WSP) && c == '\t') 8215dd8362aSkre return; 8225dd8362aSkre 8235dd8362aSkre if (fp->nxtiov >= TR_IOVECS - 1) /* should be rare */ 8245dd8362aSkre trace_flush(fp, 0); 8255dd8362aSkre 8265dd8362aSkre p = trace_id(fp); 8275dd8362aSkre if (p != NULL) { 8285dd8362aSkre fp->lines[fp->nxtiov].iov_base = p; 8295dd8362aSkre fp->lines[fp->nxtiov].iov_len = strlen(p); 8305dd8362aSkre fp->nxtiov++; 8315dd8362aSkre } 8325dd8362aSkre } else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) { 8335dd8362aSkre fp->blen = 0; 8345dd8362aSkre if (++fp->nxtiov >= TR_IOVECS) 8355dd8362aSkre trace_flush(fp, 0); 8365dd8362aSkre } 8375dd8362aSkre 8385dd8362aSkre if (fp->lines[fp->nxtiov].iov_len == 0) { 8395dd8362aSkre p = (char *)malloc(2 * TR_STD_WIDTH); 8405dd8362aSkre if (p == NULL) { 8415dd8362aSkre trace_flush(fp, 1); 8425dd8362aSkre debug = 0; 8435dd8362aSkre return; 8445dd8362aSkre } 8455dd8362aSkre *p = '\0'; 8465dd8362aSkre fp->lines[fp->nxtiov].iov_base = p; 8475dd8362aSkre fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH; 8485dd8362aSkre fp->blen = 0; 8495dd8362aSkre } 8505dd8362aSkre 8515dd8362aSkre p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++; 8525dd8362aSkre *p++ = c; 8535dd8362aSkre *p = 0; 8545dd8362aSkre 8555dd8362aSkre if (c != ' ' && c != '\t' && c != '\n') { 8565dd8362aSkre fp->lastc = c; 8575dd8362aSkre fp->supr = 0; 8585dd8362aSkre } 8595dd8362aSkre 8605dd8362aSkre if (c == '\n') { 8615dd8362aSkre fp->lines[fp->nxtiov++].iov_len = fp->blen; 8625dd8362aSkre fp->blen = 0; 8635dd8362aSkre fp->llen = 0; 8645dd8362aSkre fp->supr |= SUP_NL; 8655dd8362aSkre return; 8665dd8362aSkre } 8675dd8362aSkre 8685dd8362aSkre if (c == '\t') 8695dd8362aSkre fp->llen |= 7; 8705dd8362aSkre fp->llen++; 8715dd8362aSkre } 8725dd8362aSkre 8735dd8362aSkre void 8745dd8362aSkre trace_flush(TFILE *fp, int all) 8755dd8362aSkre { 8765dd8362aSkre int niov, i; 8775dd8362aSkre ssize_t written; 8785dd8362aSkre 8795dd8362aSkre niov = fp->nxtiov; 8805dd8362aSkre if (all && fp->blen > 0) { 8815dd8362aSkre fp->lines[niov].iov_len = fp->blen; 8825dd8362aSkre fp->blen = 0; 8835dd8362aSkre fp->llen = 0; 8845dd8362aSkre niov++; 8855dd8362aSkre } 8865dd8362aSkre if (niov == 0) 8875dd8362aSkre return; 8885dd8362aSkre if (fp->blen > 0 && --niov == 0) 8895dd8362aSkre return; 8905dd8362aSkre written = writev(fp->tfd, fp->lines, niov); 8915dd8362aSkre for (i = 0; i < niov; i++) { 8925dd8362aSkre free(fp->lines[i].iov_base); 8935dd8362aSkre fp->lines[i].iov_base = NULL; 8945dd8362aSkre fp->lines[i].iov_len = 0; 8955dd8362aSkre } 8965dd8362aSkre if (written == -1) { 8975dd8362aSkre if (fp->blen > 0) { 8985dd8362aSkre free(fp->lines[niov].iov_base); 8995dd8362aSkre fp->lines[niov].iov_base = NULL; 9005dd8362aSkre fp->lines[niov].iov_len = 0; 9015dd8362aSkre } 9025dd8362aSkre debug = 0; 9035dd8362aSkre fp->blen = 0; 9045dd8362aSkre fp->llen = 0; 9055dd8362aSkre return; 9065dd8362aSkre } 9075dd8362aSkre if (fp->blen > 0) { 9085dd8362aSkre fp->lines[0].iov_base = fp->lines[niov].iov_base; 9095dd8362aSkre fp->lines[0].iov_len = fp->lines[niov].iov_len; 9105dd8362aSkre fp->lines[niov].iov_base = NULL; 9115dd8362aSkre fp->lines[niov].iov_len = 0; 9125dd8362aSkre } 9135dd8362aSkre fp->nxtiov = 0; 9145dd8362aSkre } 9155dd8362aSkre 9165dd8362aSkre void 9175dd8362aSkre trace_puts(const char *s, TFILE *fp) 9185dd8362aSkre { 91961f28255Scgd char c; 92061f28255Scgd 9215dd8362aSkre while ((c = *s++) != '\0') 9225dd8362aSkre trace_putc(c, fp); 9235dd8362aSkre } 9245dd8362aSkre 9255dd8362aSkre inline static char * 9265dd8362aSkre trace_id(TFILE *tf) 9275dd8362aSkre { 9285dd8362aSkre int i; 9295dd8362aSkre char indent[16]; 9305dd8362aSkre char *p; 9318ffd1099Skre int lno; 9320ed8885aSkre char c; 9335dd8362aSkre 9345dd8362aSkre if (DFlags & DBG_NEST) { 9355f9a3bbeSkre if ((unsigned)ShNest >= sizeof indent - 1) { 9365f9a3bbeSkre (void) snprintf(indent, sizeof indent, 9375f9a3bbeSkre "### %*d ###", (int)(sizeof indent) - 9, ShNest); 9385f9a3bbeSkre p = strchr(indent, '\0'); 9395f9a3bbeSkre } else { 9405dd8362aSkre p = indent; 9415dd8362aSkre for (i = 0; i < 6; i++) 9425dd8362aSkre *p++ = (i < ShNest) ? '#' : ' '; 9435dd8362aSkre while (i++ < ShNest && p < &indent[sizeof indent - 1]) 9445dd8362aSkre *p++ = '#'; 9455dd8362aSkre *p = '\0'; 9465f9a3bbeSkre } 9475dd8362aSkre } else 9485dd8362aSkre indent[0] = '\0'; 9495dd8362aSkre 950727a69dcSkre /* 951727a69dcSkre * If we are in the parser, then plinno is the current line 952727a69dcSkre * number being processed (parser line no). 953727a69dcSkre * If we are elsewhere, then line_number gives the source 954727a69dcSkre * line of whatever we are currently doing (close enough.) 955727a69dcSkre */ 956727a69dcSkre if (parsing) 957727a69dcSkre lno = plinno; 958727a69dcSkre else 959727a69dcSkre lno = line_number; 9608ffd1099Skre 9610ed8885aSkre c = ((i = getpid()) == tf->pid) ? ':' : '='; 9620ed8885aSkre 9635dd8362aSkre if (DFlags & DBG_PID) { 9648ffd1099Skre if (DFlags & DBG_LINE) 9650ed8885aSkre (void) asprintf(&p, "%5d%c%s\t%4d%c@\t", i, c, 9660ed8885aSkre indent, lno, parsing?'-':'+'); 9678ffd1099Skre else 9680ed8885aSkre (void) asprintf(&p, "%5d%c%s\t", i, c, indent); 9695dd8362aSkre return p; 9705dd8362aSkre } else if (DFlags & DBG_NEST) { 9718ffd1099Skre if (DFlags & DBG_LINE) 9720ed8885aSkre (void) asprintf(&p, "%c%s\t%4d%c@\t", c, indent, lno, 9730ed8885aSkre parsing?'-':'+'); 9748ffd1099Skre else 9750ed8885aSkre (void) asprintf(&p, "%c%s\t", c, indent); 9765dd8362aSkre return p; 9778ffd1099Skre } else if (DFlags & DBG_LINE) { 9780ed8885aSkre (void) asprintf(&p, "%c%4d%c@\t", c, lno, parsing?'-':'+'); 9798ffd1099Skre return p; 9805dd8362aSkre } 9815dd8362aSkre return NULL; 9825dd8362aSkre } 9835dd8362aSkre 9845dd8362aSkre /* 9855dd8362aSkre * Used only from trargs(), which itself is used only to print 9865dd8362aSkre * arg lists (argv[]) either that passed into this shell, or 9875dd8362aSkre * the arg list about to be given to some other command (incl 9885dd8362aSkre * builtin, and function) as their argv[]. If any of the CTL* 9895dd8362aSkre * chars seem to appear, they really should be just treated as data, 9905dd8362aSkre * not special... But this is just debug, so, who cares! 9915dd8362aSkre */ 9925dd8362aSkre static void 9935dd8362aSkre trstring(const char *s) 9945dd8362aSkre { 9955dd8362aSkre const char *p; 9965dd8362aSkre char c; 9975dd8362aSkre TFILE *fp; 9985dd8362aSkre 9995dd8362aSkre if (debug != 1 || !tracetfile) 100061f28255Scgd return; 10015dd8362aSkre fp = tracetfile; 10025dd8362aSkre trace_putc('"', fp); 100361f28255Scgd for (p = s ; *p ; p++) { 100461f28255Scgd switch (*p) { 100561f28255Scgd case '\n': c = 'n'; goto backslash; 100661f28255Scgd case '\t': c = 't'; goto backslash; 100761f28255Scgd case '\r': c = 'r'; goto backslash; 100861f28255Scgd case '"': c = '"'; goto backslash; 100961f28255Scgd case '\\': c = '\\'; goto backslash; 101061f28255Scgd case CTLESC: c = 'e'; goto backslash; 101161f28255Scgd case CTLVAR: c = 'v'; goto backslash; 101261f28255Scgd case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; 101361f28255Scgd case CTLBACKQ: c = 'q'; goto backslash; 101461f28255Scgd case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; 10155dd8362aSkre backslash: trace_putc('\\', fp); 10165dd8362aSkre trace_putc(c, fp); 101761f28255Scgd break; 101861f28255Scgd default: 101961f28255Scgd if (*p >= ' ' && *p <= '~') 10205dd8362aSkre trace_putc(*p, fp); 102161f28255Scgd else { 10225dd8362aSkre trace_putc('\\', fp); 10235dd8362aSkre trace_putc(*p >> 6 & 03, fp); 10245dd8362aSkre trace_putc(*p >> 3 & 07, fp); 10255dd8362aSkre trace_putc(*p & 07, fp); 102661f28255Scgd } 102761f28255Scgd break; 102861f28255Scgd } 102961f28255Scgd } 10305dd8362aSkre trace_putc('"', fp); 10319d15d213Skre } 10329d15d213Skre 10335dd8362aSkre /* 10345dd8362aSkre * deal with the user "accidentally" picking our fd to use. 10355dd8362aSkre */ 10365dd8362aSkre static void 10375dd8362aSkre trace_fd_swap(int from, int to) 10389d15d213Skre { 10395dd8362aSkre if (tracetfile == NULL || from == to) 10409d15d213Skre return; 104161f28255Scgd 10425dd8362aSkre tracetfile->tfd = to; 10439d15d213Skre 10445dd8362aSkre /* 10455dd8362aSkre * This is just so histedit has a stdio FILE* to use. 10465dd8362aSkre */ 10479d15d213Skre if (tracefile) 10485dd8362aSkre fclose(tracefile); 10495dd8362aSkre tracefile = fdopen(to, "a"); 10505dd8362aSkre if (tracefile) 10519d15d213Skre setlinebuf(tracefile); 10529d15d213Skre } 10535dd8362aSkre 1054ab36694aSkre 1055ab36694aSkre static struct debug_flag { 1056ab36694aSkre char label; 1057ab36694aSkre uint64_t flag; 1058ab36694aSkre } debug_flags[] = { 1059ab36694aSkre { 'a', DBG_ARITH }, /* arithmetic ( $(( )) ) */ 1060ab36694aSkre { 'c', DBG_CMDS }, /* command searching, ... */ 1061ab36694aSkre { 'e', DBG_EVAL }, /* evaluation of the parse tree */ 1062ab36694aSkre { 'f', DBG_REDIR }, /* file descriptors & redirections */ 1063bcacfd9aSkre { 'g', DBG_MATCH }, /* pattern matching (glob) */ 1064ab36694aSkre { 'h', DBG_HISTORY }, /* history & cmd line editing */ 1065ab36694aSkre { 'i', DBG_INPUT }, /* shell input routines */ 1066ab36694aSkre { 'j', DBG_JOBS }, /* job control, structures */ 1067a672c6e1Skre { 'l', DBG_LEXER }, /* lexical analysis */ 1068ab36694aSkre { 'm', DBG_MEM }, /* memory management */ 1069ab36694aSkre { 'o', DBG_OUTPUT }, /* output routines */ 1070ab36694aSkre { 'p', DBG_PROCS }, /* process management, fork, ... */ 1071ab36694aSkre { 'r', DBG_PARSE }, /* parser, lexer, ... tree building */ 1072ab36694aSkre { 's', DBG_SIG }, /* signals and everything related */ 1073ab36694aSkre { 't', DBG_TRAP }, /* traps & signals */ 1074ab36694aSkre { 'v', DBG_VARS }, /* variables and parameters */ 1075ab36694aSkre { 'w', DBG_WAIT }, /* waits for processes to finish */ 1076ab36694aSkre { 'x', DBG_EXPAND }, /* word expansion ${} $() $(( )) */ 1077ab36694aSkre { 'z', DBG_ERRS }, /* error control, jumps, cleanup */ 1078ab36694aSkre 1079ab36694aSkre { '0', DBG_U0 }, /* ad-hoc temp debug flag #0 */ 1080ab36694aSkre { '1', DBG_U1 }, /* ad-hoc temp debug flag #1 */ 1081ab36694aSkre { '2', DBG_U2 }, /* ad-hoc temp debug flag #2 */ 1082f2dc4639Skre { '3', DBG_U3 }, /* ad-hoc temp debug flag #3 */ 1083ab36694aSkre 10848ffd1099Skre { '@', DBG_LINE }, /* prefix trace lines with line# */ 1085ab36694aSkre { '$', DBG_PID }, /* prefix trace lines with sh pid */ 1086ab36694aSkre { '^', DBG_NEST }, /* show shell nesting level */ 1087ab36694aSkre 1088a672c6e1Skre /* alpha options only - but not DBG_LEXER */ 1089f2dc4639Skre { '_', DBG_PARSE | DBG_EVAL | DBG_EXPAND | DBG_JOBS | DBG_SIG | 1090ab36694aSkre DBG_PROCS | DBG_REDIR | DBG_CMDS | DBG_ERRS | 1091f2dc4639Skre DBG_WAIT | DBG_TRAP | DBG_VARS | DBG_MEM | DBG_MATCH | 1092ab36694aSkre DBG_INPUT | DBG_OUTPUT | DBG_ARITH | DBG_HISTORY }, 1093ab36694aSkre 1094ab36694aSkre /* { '*', DBG_ALLVERBOSE }, is handled in the code */ 1095ab36694aSkre 1096f2dc4639Skre { '#', DBG_U0 | DBG_U1 | DBG_U2 | DBG_U3 }, 1097ab36694aSkre 1098ab36694aSkre { 0, 0 } 1099ab36694aSkre }; 1100ab36694aSkre 1101ab36694aSkre void 1102ab36694aSkre set_debug(const char * flags, int on) 1103ab36694aSkre { 1104ab36694aSkre char f; 1105ab36694aSkre struct debug_flag *df; 1106ab36694aSkre int verbose; 1107ab36694aSkre 1108ab36694aSkre while ((f = *flags++) != '\0') { 1109ab36694aSkre verbose = 0; 1110ab36694aSkre if (is_upper(f)) { 1111ab36694aSkre verbose = 1; 1112ab36694aSkre f += 'a' - 'A'; 1113ab36694aSkre } 1114ab36694aSkre if (f == '*') 1115ab36694aSkre f = '_', verbose = 1; 1116ab36694aSkre if (f == '+') { 1117ab36694aSkre if (*flags == '+') 1118ab36694aSkre flags++, verbose=1; 1119ab36694aSkre } 1120ab36694aSkre 1121ab36694aSkre /* 1122ab36694aSkre * Note: turning on any debug option also enables DBG_ALWAYS 1123ab36694aSkre * turning on any verbose option also enables DBG_VERBOSE 1124ab36694aSkre * Once enabled, those flags cannot be disabled. 1125ab36694aSkre * (tracing can still be turned off with "set +o debug") 1126ab36694aSkre */ 1127ab36694aSkre for (df = debug_flags; df->label != '\0'; df++) { 1128ab36694aSkre if (f == '+' || df->label == f) { 1129ab36694aSkre if (on) { 1130ab36694aSkre DFlags |= DBG_ALWAYS | df->flag; 1131ab36694aSkre if (verbose) 1132ab36694aSkre DFlags |= DBG_VERBOSE | 1133ab36694aSkre (df->flag << DBG_VBOSE_SHIFT); 1134ab36694aSkre } else { 1135ab36694aSkre DFlags &= ~(df->flag<<DBG_VBOSE_SHIFT); 1136ab36694aSkre if (!verbose) 1137ab36694aSkre DFlags &= ~df->flag; 1138ab36694aSkre } 1139ab36694aSkre } 1140ab36694aSkre } 1141ab36694aSkre } 1142ab36694aSkre } 1143ab36694aSkre 1144ab36694aSkre 1145ab36694aSkre int 1146ab36694aSkre debugcmd(int argc, char **argv) 1147ab36694aSkre { 1148ab36694aSkre if (argc == 1) { 1149ab36694aSkre struct debug_flag *df; 1150ab36694aSkre 11515f9a3bbeSkre out1fmt("Debug: %sabled. Flags: ", debug ? "en" : "dis"); 1152ab36694aSkre for (df = debug_flags; df->label != '\0'; df++) { 1153ab36694aSkre if (df->flag & (df->flag - 1)) 1154ab36694aSkre continue; 1155ab36694aSkre if (is_alpha(df->label) && 1156ab36694aSkre (df->flag << DBG_VBOSE_SHIFT) & DFlags) 1157ab36694aSkre out1c(df->label - ('a' - 'A')); 1158ab36694aSkre else if (df->flag & DFlags) 1159ab36694aSkre out1c(df->label); 1160ab36694aSkre } 1161ab36694aSkre out1c('\n'); 1162ab36694aSkre return 0; 1163ab36694aSkre } 1164ab36694aSkre 1165ab36694aSkre while (*++argv) { 1166ab36694aSkre if (**argv == '-') 1167ab36694aSkre set_debug(*argv + 1, 1); 1168ab36694aSkre else if (**argv == '+') 1169ab36694aSkre set_debug(*argv + 1, 0); 1170ab36694aSkre else 1171ab36694aSkre return 1; 1172ab36694aSkre } 1173ab36694aSkre return 0; 1174ab36694aSkre } 1175ab36694aSkre 117668b1dceaSchristos #endif /* DEBUG */ 1177