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