Home | History | Annotate | Line # | Download | only in sh
      1 /*	$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 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.59 2024/11/11 22:57:42 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, "ae");	/* 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 (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[(int)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