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