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