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