Home | History | Annotate | Line # | Download | only in sh
show.c revision 1.39
      1 /*	$NetBSD: show.c,v 1.39 2017/05/13 03:26:03 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.39 2017/05/13 03:26:03 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 
     53 #include <sys/types.h>
     54 #include <sys/uio.h>
     55 
     56 #include "shell.h"
     57 #include "parser.h"
     58 #include "nodes.h"
     59 #include "mystring.h"
     60 #include "show.h"
     61 #include "options.h"
     62 #include "redir.h"
     63 #include "error.h"
     64 
     65 #if defined(DEBUG) && !defined(DBG_PID)
     66 /*
     67  * If this is compiled, it means this is being compiled in a shell that still
     68  * has an older shell.h (a simpler TRACE() mechanism than is coming soon.)
     69  *
     70  * Compensate for as much of that as is missing and is needed here
     71  * to compile and operate at all.   After the other changes have appeared,
     72  * this little block can (and should be) deleted (sometime).
     73  *
     74  * Try to avoid waiting 22 years...
     75  */
     76 #define	DBG_PID		1
     77 #define	DBG_NEST	2
     78 #endif
     79 
     80 #define DEFINE_NODENAMES
     81 #include "nodenames.h"		/* does almost nothing if !defined(DEBUG) */
     82 
     83 #define	TR_STD_WIDTH	60	/* tend to fold lines wider than this */
     84 #define	TR_IOVECS	10	/* number of lines or trace (max) / write */
     85 
     86 typedef struct traceinfo {
     87 	int	tfd;	/* file descriptor for open trace file */
     88 	int	nxtiov;	/* the buffer we should be writing to */
     89 	char	lastc;	/* the last non-white character output */
     90 	uint8_t	supr;	/* char classes to supress after \n */
     91 	pid_t	pid;	/* process id of process that opened that file */
     92 	size_t	llen;	/* number of chars in current output line */
     93 	size_t	blen;	/* chars used in current buffer being filled */
     94 	char *	tracefile;		/* name of the tracefile */
     95 	struct iovec lines[TR_IOVECS];	/* filled, flling, pending buffers */
     96 } TFILE;
     97 
     98 /* These are auto turned off when non white space is printed */
     99 #define	SUP_NL	0x01	/* don't print \n */
    100 #define	SUP_SP	0x03	/* supress spaces */
    101 #define	SUP_WSP	0x04	/* supress all white space */
    102 
    103 #ifdef DEBUG		/* from here to end of file ... */
    104 
    105 TFILE tracedata, *tracetfile;
    106 FILE *tracefile;		/* just for histedit */
    107 
    108 uint64_t	DFlags;		/* currently enabled debug flags */
    109 int ShNest;		/* depth of shell (internal) nesting */
    110 
    111 static void shtree(union node *, int, int, int, TFILE *);
    112 static void shcmd(union node *, TFILE *);
    113 static void shsubsh(union node *, TFILE *);
    114 static void shredir(union node *, TFILE *, int);
    115 static void sharg(union node *, TFILE *);
    116 static void indent(int, TFILE *);
    117 static void trstring(const char *);
    118 static void trace_putc(char, TFILE *);
    119 static void trace_puts(const char *, TFILE *);
    120 static void trace_flush(TFILE *, int);
    121 static char *trace_id(TFILE *);
    122 static void trace_fd_swap(int, int);
    123 
    124 inline static int trlinelen(TFILE *);
    125 
    126 
    127 /*
    128  * These functions are the externally visible interface
    129  *  (but only for a DEBUG shell.)
    130  */
    131 
    132 void
    133 opentrace(void)
    134 {
    135 	char *s;
    136 	int fd;
    137 	int i;
    138 	pid_t pid;
    139 
    140 	if (debug != 1) {
    141 		/* leave fd open because libedit might be using it */
    142 		if (tracefile)
    143 			fflush(tracefile);
    144 		if (tracetfile)
    145 			trace_flush(tracetfile, 1);
    146 		return;
    147 	}
    148 #if DBG_PID == 1		/* using old shell.h, old tracing method */
    149 	DFlags = DBG_PID;	/* just force DBG_PID on, and leave it ... */
    150 #endif
    151 	pid = getpid();
    152 	if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) {
    153 		debug = 0;
    154 		error("Cannot asprintf tracefilename");
    155 	};
    156 
    157 	fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666);
    158 	if (fd == -1) {
    159 		debug = 0;
    160 		error("Can't open tracefile: %s (%s)\n", s, strerror(errno));
    161 	}
    162 	fd = to_upper_fd(fd);
    163 	if (fd <= 2) {
    164 		(void) close(fd);
    165 		debug = 0;
    166 		error("Attempt to use fd %d as tracefile thwarted\n", fd);
    167 	}
    168 	register_sh_fd(fd, trace_fd_swap);
    169 
    170 	/*
    171 	 * This stuff is just so histedit has a FILE * to use
    172 	 */
    173 	if (tracefile)
    174 		(void) fclose(tracefile);	/* also closes tfd */
    175 	tracefile = fdopen(fd, "a");	/* don't care if it is NULL */
    176 	if (tracefile)			/* except here... */
    177 		setlinebuf(tracefile);
    178 
    179 	/*
    180 	 * Now the real tracing setup
    181 	 */
    182 	if (tracedata.tfd > 0 && tracedata.tfd != fd)
    183 		(void) close(tracedata.tfd);	/* usually done by fclose() */
    184 
    185 	tracedata.tfd = fd;
    186 	tracedata.pid = pid;
    187 	tracedata.nxtiov = 0;
    188 	tracedata.blen = 0;
    189 	tracedata.llen = 0;
    190 	tracedata.lastc = '\0';
    191 	tracedata.supr = SUP_NL | SUP_WSP;
    192 
    193 #define	replace(f, v) do {				\
    194 		if (tracedata.f != NULL)		\
    195 			free(tracedata.f);		\
    196 		tracedata.f = v;			\
    197 	} while (/*CONSTCOND*/ 0)
    198 
    199 	replace(tracefile, s);
    200 
    201 	for (i = 0; i < TR_IOVECS; i++) {
    202 		replace(lines[i].iov_base, NULL);
    203 		tracedata.lines[i].iov_len = 0;
    204 	}
    205 
    206 #undef replace
    207 
    208 	tracetfile = &tracedata;
    209 
    210 	trace_puts("\nTracing started.\n", tracetfile);
    211 }
    212 
    213 void
    214 trace(const char *fmt, ...)
    215 {
    216 	va_list va;
    217 	char *s;
    218 
    219 	if (debug != 1 || !tracetfile)
    220 		return;
    221 	va_start(va, fmt);
    222 	(void) vasprintf(&s, fmt, va);
    223 	va_end(va);
    224 
    225 	trace_puts(s, tracetfile);
    226 	free(s);
    227 	if (tracetfile->llen == 0)
    228 		trace_flush(tracetfile, 0);
    229 }
    230 
    231 void
    232 tracev(const char *fmt, va_list va)
    233 {
    234 	va_list ap;
    235 	char *s;
    236 
    237 	if (debug != 1 || !tracetfile)
    238 		return;
    239 	va_copy(ap, va);
    240 	(void) vasprintf(&s, fmt, ap);
    241 	va_end(ap);
    242 
    243 	trace_puts(s, tracetfile);
    244 	free(s);
    245 	if (tracetfile->llen == 0)
    246 		trace_flush(tracetfile, 0);
    247 }
    248 
    249 
    250 void
    251 trputs(const char *s)
    252 {
    253 	if (debug != 1 || !tracetfile)
    254 		return;
    255 	trace_puts(s, tracetfile);
    256 }
    257 
    258 void
    259 trputc(int c)
    260 {
    261 	if (debug != 1 || !tracetfile)
    262 		return;
    263 	trace_putc(c, tracetfile);
    264 }
    265 
    266 void
    267 showtree(union node *n)
    268 {
    269 	TFILE *fp;
    270 
    271 	if ((fp = tracetfile) == NULL)
    272 		return;
    273 
    274 	trace_puts("showtree(", fp);
    275 		if (n == NULL)
    276 			trace_puts("NULL", fp);
    277 		else if (n == NEOF)
    278 			trace_puts("NEOF", fp);
    279 		else
    280 			trace("%p", n);
    281 	trace_puts(") called\n", fp);
    282 	if (n != NULL && n != NEOF)
    283 		shtree(n, 1, 1, 1, fp);
    284 }
    285 
    286 void
    287 trargs(char **ap)
    288 {
    289 	if (debug != 1 || !tracetfile)
    290 		return;
    291 	while (*ap) {
    292 		trstring(*ap++);
    293 		if (*ap)
    294 			trace_putc(' ', tracetfile);
    295 		else
    296 			trace_putc('\n', tracetfile);
    297 	}
    298 }
    299 
    300 
    301 /*
    302  * Beyond here we just have the implementation of all of that
    303  */
    304 
    305 
    306 inline static int
    307 trlinelen(TFILE * fp)
    308 {
    309 	return fp->llen;
    310 }
    311 
    312 static void
    313 shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp)
    314 {
    315 	struct nodelist *lp;
    316 	const char *s;
    317 
    318 	if (n == NULL) {
    319 		if (nl)
    320 			trace_putc('\n', fp);
    321 		return;
    322 	}
    323 
    324 	indent(ind, fp);
    325 	switch (n->type) {
    326 	case NSEMI:
    327 		s = NULL;
    328 		goto binop;
    329 	case NAND:
    330 		s = " && ";
    331 		goto binop;
    332 	case NOR:
    333 		s = " || ";
    334 binop:
    335 		shtree(n->nbinary.ch1, 0, ilvl, 0, fp);
    336 		if (s != NULL)
    337 			trace_puts(s, fp);
    338 		if (trlinelen(fp) >= TR_STD_WIDTH) {
    339 			trace_putc('\n', fp);
    340 			indent(ind < 0 ? 2 : ind + 1, fp);
    341 		} else if (s == NULL) {
    342 			if (fp->lastc != '&')
    343 				trace_puts("; ", fp);
    344 			else
    345 				trace_putc(' ', fp);
    346 		}
    347 		shtree(n->nbinary.ch2, 0, ilvl, nl, fp);
    348 		break;
    349 	case NCMD:
    350 		shcmd(n, fp);
    351 		if (n->ncmd.backgnd)
    352 			trace_puts(" &", fp);
    353 		if (nl && trlinelen(fp) > 0)
    354 			trace_putc('\n', fp);
    355 		break;
    356 	case NPIPE:
    357 		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
    358 			shtree(lp->n, 0, ilvl, 0, fp);
    359 			if (lp->next) {
    360 				trace_puts(" |", fp);
    361 				if (trlinelen(fp) >= TR_STD_WIDTH)  {
    362 					trace_putc('\n', fp);
    363 					indent((ind < 0 ? ilvl : ind) + 1, fp);
    364 				} else
    365 					trace_putc(' ', fp);
    366 			}
    367 		}
    368 		if (n->npipe.backgnd)
    369 			trace_puts(" &", fp);
    370 		if (nl || trlinelen(fp) >= TR_STD_WIDTH)
    371 			trace_putc('\n', fp);
    372 		break;
    373 	case NBACKGND:
    374 	case NSUBSHELL:
    375 		shsubsh(n, fp);
    376 		if (n->type == NBACKGND)
    377 			trace_puts(" &", fp);
    378 		if (nl && trlinelen(fp) > 0)
    379 			trace_putc('\n', fp);
    380 		break;
    381 	case NDEFUN:
    382 		trace_puts(n->narg.text, fp);
    383 		trace_puts("() {\n", fp);
    384 		indent(ind, fp);
    385 		shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp);
    386 		indent(ind, fp);
    387 		trace_puts("}\n", fp);
    388 		break;
    389 	case NDNOT:
    390 		trace_puts("! ", fp);
    391 		/* FALLTHROUGH */
    392 	case NNOT:
    393 		trace_puts("! ", fp);
    394 		shtree(n->nnot.com, -1, ilvl, nl, fp);
    395 		break;
    396 	case NREDIR:
    397 		shtree(n->nredir.n, -1, ilvl, 0, fp);
    398 		shredir(n->nredir.redirect, fp, n->nredir.n == NULL);
    399 		if (nl)
    400 			trace_putc('\n', fp);
    401 		break;
    402 
    403 	case NIF:
    404 	itsif:
    405 		trace_puts("if ", fp);
    406 		shtree(n->nif.test, -1, ilvl, 0, fp);
    407 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
    408 			if (fp->lastc != '&')
    409 				trace_puts(" ;", fp);
    410 		} else
    411 			indent(ilvl, fp);
    412 		trace_puts(" then ", fp);
    413 		if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
    414 			indent(ilvl+1, fp);
    415 		shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp);
    416 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
    417 			if (fp->lastc != '&')
    418 				trace_puts(" ;", fp);
    419 		} else
    420 			indent(ilvl, fp);
    421 		if (n->nif.elsepart && n->nif.elsepart->type == NIF) {
    422 			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
    423 				indent(ilvl, fp);
    424 			n = n->nif.elsepart;
    425 			trace_puts(" el", fp);
    426 			goto itsif;
    427 		}
    428 		if (n->nif.elsepart) {
    429 			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
    430 				indent(ilvl+1, fp);
    431 			trace_puts(" else ", fp);
    432 			shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp);
    433 			if (fp->lastc != '&')
    434 				trace_puts(" ;", fp);
    435 		}
    436 		trace_puts(" fi", fp);
    437 		if (nl)
    438 			trace_putc('\n', fp);
    439 		break;
    440 
    441 	case NWHILE:
    442 		trace_puts("while ", fp);
    443 		goto aloop;
    444 	case NUNTIL:
    445 		trace_puts("until ", fp);
    446 	aloop:
    447 		shtree(n->nbinary.ch1, -1, ilvl, 0, fp);
    448 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
    449 			if (fp->lastc != '&')
    450 				trace_puts(" ;", fp);
    451 		} else
    452 			trace_putc('\n', fp);
    453 		trace_puts(" do ", fp);
    454 		shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp);
    455 		trace_puts(" done ", fp);
    456 		if (nl)
    457 			trace_putc('\n', fp);
    458 		break;
    459 
    460 	case NFOR:
    461 		trace_puts("for ", fp);
    462 		trace_puts(n->nfor.var, fp);
    463 		if (n->nfor.args) {
    464 			union node *argp;
    465 
    466 			trace_puts(" in ", fp);
    467 			for (argp = n->nfor.args; argp; argp=argp->narg.next) {
    468 				sharg(argp, fp);
    469 				trace_putc(' ', fp);
    470 			}
    471 			if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
    472 				if (fp->lastc != '&')
    473 					trace_putc(';', fp);
    474 			} else
    475 				trace_putc('\n', fp);
    476 		}
    477 		trace_puts(" do ", fp);
    478 		shtree(n->nfor.body, -1, ilvl + 1, 0, fp);
    479 		if (fp->lastc != '&')
    480 			trace_putc(';', fp);
    481 		trace_puts(" done", fp);
    482 		if (nl)
    483 			trace_putc('\n', fp);
    484 		break;
    485 
    486 	case NCASE:
    487 		trace_puts("case ", fp);
    488 		sharg(n->ncase.expr, fp);
    489 		trace_puts(" in", fp);
    490 		if (nl)
    491 			trace_putc('\n', fp);
    492 		{
    493 			union node *cp;
    494 
    495 			for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
    496 				union node *patp;
    497 
    498 				if (nl || trlinelen(fp) > TR_STD_WIDTH - 16)
    499 					indent(ilvl, fp);
    500 				else
    501 					trace_putc(' ', fp);
    502 				trace_putc('(', fp);
    503 				patp = cp->nclist.pattern;
    504 				while (patp != NULL) {
    505 				    trace_putc(' ', fp);
    506 				    sharg(patp, fp);
    507 				    trace_putc(' ', fp);
    508 				    if ((patp = patp->narg.next) != NULL)
    509 					trace_putc('|', fp);
    510 				}
    511 				trace_putc(')', fp);
    512 				if (nl)
    513 					indent(ilvl + 1, fp);
    514 				else
    515 					trace_putc(' ', fp);
    516 				shtree(cp->nclist.body, -1, ilvl+2, 0, fp);
    517 				if (cp->type == NCLISTCONT)
    518 					trace_puts(" ;&", fp);
    519 				else
    520 					trace_puts(" ;;", fp);
    521 				if (nl)
    522 					trace_putc('\n', fp);
    523 			}
    524 		}
    525 		if (nl) {
    526 			trace_putc('\n', fp);
    527 			indent(ind, fp);
    528 		} else
    529 			trace_putc(' ', fp);
    530 		trace_puts("esac", fp);
    531 		if (nl)
    532 			trace_putc('\n', fp);
    533 		break;
    534 
    535 	default: {
    536 			char *str;
    537 
    538 			asprintf(&str, "<node type %d [%s]>", n->type,
    539 			    NODETYPENAME(n->type));
    540 			trace_puts(str, fp);
    541 			free(str);
    542 			if (nl)
    543 				trace_putc('\n', fp);
    544 		}
    545 		break;
    546 	}
    547 }
    548 
    549 
    550 static void
    551 shcmd(union node *cmd, TFILE *fp)
    552 {
    553 	union node *np;
    554 	int first;
    555 
    556 	first = 1;
    557 	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
    558 		if (! first)
    559 			trace_putc(' ', fp);
    560 		sharg(np, fp);
    561 		first = 0;
    562 	}
    563 	shredir(cmd->ncmd.redirect, fp, first);
    564 }
    565 
    566 static void
    567 shsubsh(union node *cmd, TFILE *fp)
    568 {
    569 	trace_puts(" ( ", fp);
    570 	shtree(cmd->nredir.n, -1, 3, 0, fp);
    571 	trace_puts(" ) ", fp);
    572 	shredir(cmd->ncmd.redirect, fp, 1);
    573 }
    574 
    575 static void
    576 shredir(union node *np, TFILE *fp, int first)
    577 {
    578 	const char *s;
    579 	int dftfd;
    580 	char buf[106];
    581 
    582 	for ( ; np ; np = np->nfile.next) {
    583 		if (! first)
    584 			trace_putc(' ', fp);
    585 		switch (np->nfile.type) {
    586 			case NTO:	s = ">";  dftfd = 1; break;
    587 			case NCLOBBER:	s = ">|"; dftfd = 1; break;
    588 			case NAPPEND:	s = ">>"; dftfd = 1; break;
    589 			case NTOFD:	s = ">&"; dftfd = 1; break;
    590 			case NFROM:	s = "<";  dftfd = 0; break;
    591 			case NFROMFD:	s = "<&"; dftfd = 0; break;
    592 			case NFROMTO:	s = "<>"; dftfd = 0; break;
    593 			case NXHERE:	/* FALLTHROUGH */
    594 			case NHERE:	s = "<<"; dftfd = 0; break;
    595 			default:   s = "*error*"; dftfd = 0; break;
    596 		}
    597 		if (np->nfile.fd != dftfd) {
    598 			sprintf(buf, "%d", np->nfile.fd);
    599 			trace_puts(buf, fp);
    600 		}
    601 		trace_puts(s, fp);
    602 		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
    603 			if (np->ndup.vname)
    604 				sharg(np->ndup.vname, fp);
    605 			else {
    606 				sprintf(buf, "%d", np->ndup.dupfd);
    607 				trace_puts(buf, fp);
    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 			trace_putc('\\', fp);
    649 			trace_putc(*++p, fp);
    650 			break;
    651 
    652 		case CTLVAR:
    653 			subtype = *++p;
    654 			if (!quoted != !(subtype & VSQUOTE))
    655 				trace_putc('"', fp);
    656 			trace_putc('$', fp);
    657 			trace_putc('{', fp);
    658 			if ((subtype & VSTYPE) == VSLENGTH)
    659 				trace_putc('#', fp);
    660 			if (subtype & VSLINENO)
    661 				trace_puts("LINENO=", fp);
    662 
    663 			while (*++p != '=')
    664 				trace_putc(*p, fp);
    665 
    666 			if (subtype & VSNUL)
    667 				trace_putc(':', fp);
    668 
    669 			switch (subtype & VSTYPE) {
    670 			case VSNORMAL:
    671 				/* { */
    672 				trace_putc('}', fp);
    673 				if (!quoted != !(subtype & VSQUOTE))
    674 					trace_putc('"', fp);
    675 				break;
    676 			case VSMINUS:
    677 				trace_putc('-', fp);
    678 				break;
    679 			case VSPLUS:
    680 				trace_putc('+', fp);
    681 				break;
    682 			case VSQUESTION:
    683 				trace_putc('?', fp);
    684 				break;
    685 			case VSASSIGN:
    686 				trace_putc('=', fp);
    687 				break;
    688 			case VSTRIMLEFTMAX:
    689 				trace_putc('#', fp);
    690 				/* FALLTHROUGH */
    691 			case VSTRIMLEFT:
    692 				trace_putc('#', fp);
    693 				break;
    694 			case VSTRIMRIGHTMAX:
    695 				trace_putc('%', fp);
    696 				/* FALLTHROUGH */
    697 			case VSTRIMRIGHT:
    698 				trace_putc('%', fp);
    699 				break;
    700 			case VSLENGTH:
    701 				break;
    702 			default: {
    703 					char str[32];
    704 
    705 					snprintf(str, sizeof str,
    706 					    "<subtype %d>", subtype);
    707 					trace_puts(str, fp);
    708 				}
    709 				break;
    710 			}
    711 			break;
    712 		case CTLENDVAR:
    713 			/* { */
    714 			trace_putc('}', fp);
    715 			if (!quoted != !(subtype & VSQUOTE))
    716 				trace_putc('"', fp);
    717 			subtype = 0;
    718 			break;
    719 
    720 		case CTLBACKQ|CTLQUOTE:
    721 			if (!quoted)
    722 				trace_putc('"', fp);
    723 			/* FALLTHRU */
    724 		case CTLBACKQ:
    725 			trace_putc('$', fp);
    726 			trace_putc('(', fp);
    727 			if (bqlist) {
    728 				shtree(bqlist->n, -1, 3, 0, fp);
    729 				bqlist = bqlist->next;
    730 			} else
    731 				trace_puts("???", fp);
    732 			trace_putc(')', fp);
    733 			if (!quoted && *p == (CTLBACKQ|CTLQUOTE))
    734 				trace_putc('"', fp);
    735 			break;
    736 
    737 		case CTLQUOTEMARK:
    738 			if (subtype != 0 || !quoted) {
    739 				trace_putc('"', fp);
    740 				quoted++;
    741 			}
    742 			break;
    743 		case CTLQUOTEEND:
    744 			trace_putc('"', fp);
    745 			quoted--;
    746 			break;
    747 		case CTLARI:
    748 			trace_puts("$(( ", fp);
    749 			break;
    750 		case CTLENDARI:
    751 			trace_puts(" ))", fp);
    752 			break;
    753 
    754 		default:
    755 			if (*p == '$')
    756 				trace_putc('\\', fp);
    757 			trace_putc(*p, fp);
    758 			break;
    759 		}
    760 	}
    761 	if (quoted)
    762 		trace_putc('"', fp);
    763 }
    764 
    765 
    766 static void
    767 indent(int amount, TFILE *fp)
    768 {
    769 	int i;
    770 
    771 	if (amount <= 0)
    772 		return;
    773 
    774 	amount <<= 2;	/* indent slots -> chars */
    775 
    776 	i = trlinelen(fp);
    777 	fp->supr = SUP_NL;
    778 	if (i > amount) {
    779 		trace_putc('\n', fp);
    780 		i = 0;
    781 	}
    782 	fp->supr = 0;
    783 	for (; i < amount - 7 ; i++) {
    784 		trace_putc('\t', fp);
    785 		i |= 7;
    786 	}
    787 	while (i < amount) {
    788 		trace_putc(' ', fp);
    789 		i++;
    790 	}
    791 	fp->supr = SUP_WSP;
    792 }
    793 
    794 static void
    795 trace_putc(char c, TFILE *fp)
    796 {
    797 	char *p;
    798 
    799 	if (c == '\0')
    800 		return;
    801 	if (debug == 0 || fp == NULL)
    802 		return;
    803 
    804 	if (fp->llen == 0) {
    805 		if (fp->blen != 0)
    806 			abort();
    807 
    808 		if ((fp->supr & SUP_NL) && c == '\n')
    809 			return;
    810 		if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ')
    811 			return;
    812 		if ((fp->supr & SUP_WSP) && c == '\t')
    813 			return;
    814 
    815 		if (fp->nxtiov >= TR_IOVECS - 1)	/* should be rare */
    816 			trace_flush(fp, 0);
    817 
    818 		p = trace_id(fp);
    819 		if (p != NULL) {
    820 			fp->lines[fp->nxtiov].iov_base = p;
    821 			fp->lines[fp->nxtiov].iov_len = strlen(p);
    822 			fp->nxtiov++;
    823 		}
    824 	} else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) {
    825 		fp->blen = 0;
    826 		if (++fp->nxtiov >= TR_IOVECS)
    827 			trace_flush(fp, 0);
    828 	}
    829 
    830 	if (fp->lines[fp->nxtiov].iov_len == 0) {
    831 		p = (char *)malloc(2 * TR_STD_WIDTH);
    832 		if (p == NULL) {
    833 			trace_flush(fp, 1);
    834 			debug = 0;
    835 			return;
    836 		}
    837 		*p = '\0';
    838 		fp->lines[fp->nxtiov].iov_base = p;
    839 		fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH;
    840 		fp->blen = 0;
    841 	}
    842 
    843 	p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++;
    844 	*p++ = c;
    845 	*p = 0;
    846 
    847 	if (c != ' ' && c != '\t' && c != '\n') {
    848 		fp->lastc = c;
    849 		fp->supr = 0;
    850 	}
    851 
    852 	if (c == '\n') {
    853 		fp->lines[fp->nxtiov++].iov_len = fp->blen;
    854 		fp->blen = 0;
    855 		fp->llen = 0;
    856 		fp->supr |= SUP_NL;
    857 		return;
    858 	}
    859 
    860 	if (c == '\t')
    861 		fp->llen |=  7;
    862 	fp->llen++;
    863 }
    864 
    865 void
    866 trace_flush(TFILE *fp, int all)
    867 {
    868 	int niov, i;
    869 	ssize_t written;
    870 
    871 	niov = fp->nxtiov;
    872 	if (all && fp->blen > 0) {
    873 		fp->lines[niov].iov_len = fp->blen;
    874 		fp->blen = 0;
    875 		fp->llen = 0;
    876 		niov++;
    877 	}
    878 	if (niov == 0)
    879 		return;
    880 	if (fp->blen > 0 && --niov == 0)
    881 		return;
    882 	written = writev(fp->tfd, fp->lines, niov);
    883 	for (i = 0; i < niov; i++) {
    884 		free(fp->lines[i].iov_base);
    885 		fp->lines[i].iov_base = NULL;
    886 		fp->lines[i].iov_len = 0;
    887 	}
    888 	if (written == -1) {
    889 		if (fp->blen > 0) {
    890 			free(fp->lines[niov].iov_base);
    891 			fp->lines[niov].iov_base = NULL;
    892 			fp->lines[niov].iov_len = 0;
    893 		}
    894 		debug = 0;
    895 		fp->blen = 0;
    896 		fp->llen = 0;
    897 		return;
    898 	}
    899 	if (fp->blen > 0) {
    900 		fp->lines[0].iov_base = fp->lines[niov].iov_base;
    901 		fp->lines[0].iov_len = fp->lines[niov].iov_len;
    902 		fp->lines[niov].iov_base = NULL;
    903 		fp->lines[niov].iov_len = 0;
    904 	}
    905 	fp->nxtiov = 0;
    906 }
    907 
    908 void
    909 trace_puts(const char *s, TFILE *fp)
    910 {
    911 	char c;
    912 
    913 	while ((c = *s++) != '\0')
    914 		trace_putc(c, fp);
    915 }
    916 
    917 inline static char *
    918 trace_id(TFILE *tf)
    919 {
    920 	int i;
    921 	char indent[16];
    922 	char *p;
    923 
    924 	if (DFlags & DBG_NEST) {
    925 		p = indent;
    926 		for (i = 0; i < 6; i++)
    927 			*p++ = (i < ShNest) ? '#' : ' ';
    928 		while (i++ < ShNest && p < &indent[sizeof indent - 1])
    929 			*p++ = '#';
    930 		*p = '\0';
    931 	} else
    932 		indent[0] = '\0';
    933 
    934 	if (DFlags & DBG_PID) {
    935 		i = getpid();
    936 		(void) asprintf(&p, "%5d%c%s\t", i,
    937 		    i == tf->pid ? ':' : '=', indent);
    938 		return p;
    939 	} else if (DFlags & DBG_NEST) {
    940 		*p++ = '\t';
    941 		*p = '\0';
    942 		(void) asprintf(&p, "%s\t", indent);
    943 		return p;
    944 	}
    945 	return NULL;
    946 }
    947 
    948 /*
    949  * Used only from trargs(), which itself is used only to print
    950  * arg lists (argv[]) either that passed into this shell, or
    951  * the arg list about to be given to some other command (incl
    952  * builtin, and function) as their argv[].  If any of the CTL*
    953  * chars seem to appear, they really should be just treated as data,
    954  * not special...   But this is just debug, so, who cares!
    955  */
    956 static void
    957 trstring(const char *s)
    958 {
    959 	const char *p;
    960 	char c;
    961 	TFILE *fp;
    962 
    963 	if (debug != 1 || !tracetfile)
    964 		return;
    965 	fp = tracetfile;
    966 	trace_putc('"', fp);
    967 	for (p = s ; *p ; p++) {
    968 		switch (*p) {
    969 		case '\n':  c = 'n';  goto backslash;
    970 		case '\t':  c = 't';  goto backslash;
    971 		case '\r':  c = 'r';  goto backslash;
    972 		case '"':  c = '"';  goto backslash;
    973 		case '\\':  c = '\\';  goto backslash;
    974 		case CTLESC:  c = 'e';  goto backslash;
    975 		case CTLVAR:  c = 'v';  goto backslash;
    976 		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
    977 		case CTLBACKQ:  c = 'q';  goto backslash;
    978 		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
    979 backslash:		trace_putc('\\', fp);
    980 			trace_putc(c, fp);
    981 			break;
    982 		default:
    983 			if (*p >= ' ' && *p <= '~')
    984 				trace_putc(*p, fp);
    985 			else {
    986 				trace_putc('\\', fp);
    987 				trace_putc(*p >> 6 & 03, fp);
    988 				trace_putc(*p >> 3 & 07, fp);
    989 				trace_putc(*p & 07, fp);
    990 			}
    991 			break;
    992 		}
    993 	}
    994 	trace_putc('"', fp);
    995 }
    996 
    997 /*
    998  * deal with the user "accidentally" picking our fd to use.
    999  */
   1000 static void
   1001 trace_fd_swap(int from, int to)
   1002 {
   1003 	if (tracetfile == NULL || from == to)
   1004 		return;
   1005 
   1006 	tracetfile->tfd = to;
   1007 
   1008 	/*
   1009 	 * This is just so histedit has a stdio FILE* to use.
   1010 	 */
   1011 	if (tracefile)
   1012 		fclose(tracefile);
   1013 	tracefile = fdopen(to, "a");
   1014 	if (tracefile)
   1015 		setlinebuf(tracefile);
   1016 }
   1017 
   1018 #endif /* DEBUG */
   1019