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