Module Name:    src
Committed By:   kre
Date:           Sat May 13 03:26:03 UTC 2017

Modified Files:
        src/bin/sh: eval.c shell.h show.c show.h

Log Message:
The beginnings of the great shell DEBUG (tracing) upgrade of 2017...

First, be aware that the DEBUG spoken of here has nothing whatever to
do with MKDEBUG=true type builds of NetBSD.   The only way to get a
DEBUG shell is to build it yourself manually.

That said, for non-DEBUG shells, this change makes only one slight
(trivial really) difference, which should affect nothing.

Previously some code was defined like ...

function(args)
{
#ifdef DEBUG
        /* function code goes here */
#endif
}

and called like ...

#ifdef DEBUG
        function(params);
#endif

resulting in several empty functions that are never called being
defined in non-DEBUG shells.   Those are now gone.   If you can detect
the difference any way other than using "nm" or similar, I'd be very
surprised...

For DEBUG shells, this introduces a whole new TRACE() setup to use
to assist in debugging the shell.

I have had this locally (uncommitted) for over a year...  it helps.

By itself this change is almost useless, nothing really changes, but
it provides the framework to allow other TRACE() calls to be updated
over time.   This is why I had not committed this earlier, my previous
version required a flag day, with all the shell's internal tracing
being updated a once - which I had done, but that shell version has
bit-rotted so badly now it is almost useless...

Future updates will add the mechanism to allow the new stuff to actually
be used in a productive way, and following that, over time, gradual
conversion of all the shell tracing to the updated form (as required,
or when I am bored...)

The one useful change that we do get now is that the fd that the shell
uses for tracing (which was usually 3, but not any more) is now protected
from user/script interference, like all the other shell inernal fds.

There is no doc (nor will there be) on any of this, if you are not reading
the source code it is useless to you, if you are, you know how it works.


To generate a diff of this commit:
cvs rdiff -u -r1.139 -r1.140 src/bin/sh/eval.c
cvs rdiff -u -r1.20 -r1.21 src/bin/sh/shell.h
cvs rdiff -u -r1.38 -r1.39 src/bin/sh/show.c
cvs rdiff -u -r1.9 -r1.10 src/bin/sh/show.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/eval.c
diff -u src/bin/sh/eval.c:1.139 src/bin/sh/eval.c:1.140
--- src/bin/sh/eval.c:1.139	Tue May  9 05:14:03 2017
+++ src/bin/sh/eval.c	Sat May 13 03:26:03 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: eval.c,v 1.139 2017/05/09 05:14:03 kre Exp $	*/
+/*	$NetBSD: eval.c,v 1.140 2017/05/13 03:26:03 kre Exp $	*/
 
 /*-
  * Copyright (c) 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
 #else
-__RCSID("$NetBSD: eval.c,v 1.139 2017/05/09 05:14:03 kre Exp $");
+__RCSID("$NetBSD: eval.c,v 1.140 2017/05/13 03:26:03 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -931,6 +931,7 @@ evalcommand(union node *cmd, int flgs, s
 			savelocalvars = localvars;
 			localvars = NULL;
 			vforked = 1;
+	VFORK_BLOCK
 			switch (pid = vfork()) {
 			case -1:
 				serrno = errno;
@@ -942,6 +943,7 @@ evalcommand(union node *cmd, int flgs, s
 				/* Make sure that exceptions only unwind to
 				 * after the vfork(2)
 				 */
+				SHELL_FORKED();
 				if (setjmp(jmploc.loc)) {
 					if (exception == EXSHELLPROC) {
 						/* We can't progress with the vfork,
@@ -960,6 +962,7 @@ evalcommand(union node *cmd, int flgs, s
 				forkchild(jp, cmd, mode, vforked);
 				break;
 			default:
+				VFORK_UNDO();
 				handler = savehandler;	/* restore from vfork(2) */
 				poplocalvars();
 				localvars = savelocalvars;
@@ -974,6 +977,7 @@ evalcommand(union node *cmd, int flgs, s
 				forkparent(jp, cmd, mode, pid);
 				goto parent;
 			}
+	VFORK_END
 		} else {
 normal_fork:
 #endif

Index: src/bin/sh/shell.h
diff -u src/bin/sh/shell.h:1.20 src/bin/sh/shell.h:1.21
--- src/bin/sh/shell.h:1.20	Tue Mar 21 10:52:46 2017
+++ src/bin/sh/shell.h	Sat May 13 03:26:03 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: shell.h,v 1.20 2017/03/21 10:52:46 joerg Exp $	*/
+/*	$NetBSD: shell.h,v 1.21 2017/05/13 03:26:03 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,6 @@
 /*
  * The follow should be set to reflect the type of system you have:
  *	JOBS -> 1 if you have Berkeley job control, 0 otherwise.
- *	SHORTNAMES -> 1 if your linker cannot handle long names.
  *	define BSD if you are running 4.2 BSD or later.
  *	define SYSV if you are running under System V.
  *	define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
@@ -59,7 +58,7 @@
 #endif
 
 #ifndef DO_SHAREDVFORK
-#if __NetBSD_Version__ >= 104000000
+#if defined(__NetBSD_Version__) &&  __NetBSD_Version__ >= 104000000
 #define DO_SHAREDVFORK
 #endif
 #endif
@@ -77,13 +76,140 @@ typedef void *pointer;
 
 extern const char nullstr[1];		/* null string */
 
+#ifdef	SMALL
+#undef	DEBUG
+#endif
 
 #ifdef DEBUG
-#define TRACE(param)	trace param
-#define TRACEV(param)	tracev param
-#else
-#define TRACE(param)
-#define TRACEV(param)
-#endif
+
+extern	uint64_t	DFlags;
+extern	int		ShNest;
+
+/*
+ * This is selected as there are 26 letters in ascii - not that that
+ * matters for anything, just makes it easier to assign a different
+ * command letter to each debug option.  We currently use only 18
+ * so this could be reduced, but that is of no real benefit.  It can also
+ * be increased, but that both limits the maximum value tha can be
+ * used with DBG_EXTRAS(), and causes problems with verbose option naming.
+ */
+#define	DBG_VBOSE_SHIFT		26
+#define	DBG_EXTRAS(n)	((DBG_VBOSE_SHIFT * 2) + (n))
+
+/*
+ * Unconditional tracing for compatibility with old tracing setup.
+ */
+#define TRACE(param)		do {					\
+					trace param;			\
+				} while (/*CONSTCOND*/ 0)
+#define TRACEV(param)		do {					\
+					tracev param;			\
+				} while (/*CONSTCOND*/ 0)
+
+/*
+ * and the newer conditional tracing, so the mainainer can control
+ * just how much debug output is dumped to the trace file
+ * (once the rest of the shell is converted to use it).
+ *
+ * in the X forms, "xtra" can be any legal C statement(s) without (bare) commas
+ * executed if the relevant debug flag is enabled, after any tracing output.
+ */
+#define CTRACE(when, param)	do {					\
+				    if ((DFlags & (when)) != 0)		\
+					trace param;			\
+				} while (/*CONSTCOND*/ 0)
+
+#define CTRACEV(when, param)	do {					\
+				    if ((DFlags & (when)) != 0)		\
+					tracev param;			\
+				} while (/*CONSTCOND*/ 0)
+
+#define XTRACE(when, param, xtra) do {					\
+				    if ((DFlags & (when)) != 0) {	\
+					trace param;			\
+					xtra;				\
+				    }					\
+				} while (/*CONSTCOND*/ 0)
+
+#define VTRACE(when, param)	do {					\
+				    if ((DFlags &			\
+					(when)<<DBG_VBOSE_SHIFT) != 0)	\
+					    trace param;		\
+				} while (/*CONSTCOND*/ 0)
+
+#define VTRACEV(when, param)	do {					\
+				    if ((DFlags &			\
+					(when)<<DBG_VBOSE_SHIFT) != 0)	\
+					    tracev param;		\
+				} while (/*CONSTCOND*/ 0)
+
+#define VXTRACE(when, param, xtra) do {					\
+				    if ((DFlags &			\
+					(when)<<DBG_VBOSE_SHIFT) != 0) {\
+					    trace param;		\
+					    xtra;			\
+				    }					\
+				} while (/*CONSTCOND*/ 0)
+
+#define SHELL_FORKED()	ShNest++
+#define VFORK_BLOCK	{ const int _ShNest = ShNest;
+#define VFORK_END	}
+#define VFORK_UNDO()	ShNest = _ShNest
+
+#define	DBG_ALWAYS	(1LL << 0)
+#define	DBG_PARSE	(1LL << 1)		/* r (read commands) */
+#define	DBG_EVAL	(1LL << 2)		/* e */
+#define	DBG_EXPAND	(1LL << 3)		/* x */
+#define	DBG_JOBS	(1LL << 4)		/* j */
+#define	DBG_PROCS	(1LL << 5)		/* p */
+#define	DBG_REDIR	(1LL << 6)		/* f (fds) */
+#define	DBG_CMDS	(1LL << 7)		/* c */
+#define	DBG_ERRS	(1LL << 8)		/* z (?) */
+#define	DBG_WAIT	(1LL << 9)		/* w */
+#define	DBG_TRAP	(1LL << 10)		/* t */
+#define	DBG_VARS	(1LL << 11)		/* v */
+#define	DBG_INPUT	(1LL << 12)		/* i */
+#define	DBG_OUTPUT	(1LL << 13)		/* o */
+#define	DBG_MEM		(1LL << 14)		/* m */
+#define	DBG_ARITH	(1LL << 15)		/* a */
+#define	DBG_HISTORY	(1LL << 16)		/* h */
+#define	DBG_SIG		(1LL << 17)		/* s */
+
+/*
+ * reserved extras: b=builtins l=alias
+ * still free:  d g k n q s u y
+ */
+
+	/* use VTRACE(DBG_ALWAYS, (...)) to test this one */
+#define	DBG_VERBOSE	(1LL << DBG_VBOSE_SHIFT)
+
+	/* DBG_EXTRAS 0 .. 11 (max) only  - non-alpha options (no VTRACE !!) */
+#define	DBG_U0		(1LL << DBG_EXTRAS(0))	/* 0 - ad-hoc extra flags */
+#define	DBG_U1		(1LL << DBG_EXTRAS(1))	/* 1 - for short term */
+#define	DBG_U2		(1LL << DBG_EXTRAS(2))	/* 2 - extra tracing*/
+
+#define	DBG_PID		(1LL << DBG_EXTRAS(10))	/* $ ($$) */
+#define	DBG_NEST	(1LL << DBG_EXTRAS(11))	/* ^ */
+
+extern void set_debug(const char *, int);
+
+#else	/* DEBUG */
+
+#define TRACE(param)			/* historic normal trace */
+#define TRACEV(param)			/* historic varargs trace */
+
+#define CTRACE(when, param)		/* conditional normal trace */
+#define CTRACEV(when, param)		/* conditional varargs trace */
+#define XTRACE(when, param, extra)	/* conditional trace, plus more */
+#define VTRACE(when, param)		/* conditional verbose trace */
+#define VTRACEV(when, param)		/* conditional verbose varargs trace */
+#define VXTRACE(when, param, extra)	/* cond verbose trace, plus more */
+
+#define SHELL_FORKED()
+#define VFORK_BLOCK
+#define VFORK_END
+#define VFORK_UNDO()
+
+#endif	/* DEBUG */
 
 #endif /* SHELL_H */

Index: src/bin/sh/show.c
diff -u src/bin/sh/show.c:1.38 src/bin/sh/show.c:1.39
--- src/bin/sh/show.c:1.38	Tue May  9 05:14:03 2017
+++ src/bin/sh/show.c	Sat May 13 03:26:03 2017
@@ -1,9 +1,11 @@
-/*	$NetBSD: show.c,v 1.38 2017/05/09 05:14:03 kre Exp $	*/
+/*	$NetBSD: show.c,v 1.39 2017/05/13 03:26:03 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.  All rights reserved.
+ *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  *
@@ -37,7 +39,7 @@
 #if 0
 static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: show.c,v 1.38 2017/05/09 05:14:03 kre Exp $");
+__RCSID("$NetBSD: show.c,v 1.39 2017/05/13 03:26:03 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -45,6 +47,11 @@ __RCSID("$NetBSD: show.c,v 1.38 2017/05/
 #include <stdarg.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
 
 #include "shell.h"
 #include "parser.h"
@@ -52,402 +59,911 @@ __RCSID("$NetBSD: show.c,v 1.38 2017/05/
 #include "mystring.h"
 #include "show.h"
 #include "options.h"
-#ifndef SMALL
+#include "redir.h"
+#include "error.h"
+
+#if defined(DEBUG) && !defined(DBG_PID)
+/*
+ * If this is compiled, it means this is being compiled in a shell that still
+ * has an older shell.h (a simpler TRACE() mechanism than is coming soon.)
+ *
+ * Compensate for as much of that as is missing and is needed here
+ * to compile and operate at all.   After the other changes have appeared,
+ * this little block can (and should be) deleted (sometime).
+ *
+ * Try to avoid waiting 22 years...
+ */
+#define	DBG_PID		1
+#define	DBG_NEST	2
+#endif
+
 #define DEFINE_NODENAMES
-#include "nodenames.h"
+#include "nodenames.h"		/* does almost nothing if !defined(DEBUG) */
+
+#define	TR_STD_WIDTH	60	/* tend to fold lines wider than this */
+#define	TR_IOVECS	10	/* number of lines or trace (max) / write */
+
+typedef struct traceinfo {
+	int	tfd;	/* file descriptor for open trace file */
+	int	nxtiov;	/* the buffer we should be writing to */
+	char	lastc;	/* the last non-white character output */
+	uint8_t	supr;	/* char classes to supress after \n */
+	pid_t	pid;	/* process id of process that opened that file */
+	size_t	llen;	/* number of chars in current output line */
+	size_t	blen;	/* chars used in current buffer being filled */
+	char *	tracefile;		/* name of the tracefile */
+	struct iovec lines[TR_IOVECS];	/* filled, flling, pending buffers */
+} TFILE;
+
+/* These are auto turned off when non white space is printed */
+#define	SUP_NL	0x01	/* don't print \n */
+#define	SUP_SP	0x03	/* supress spaces */
+#define	SUP_WSP	0x04	/* supress all white space */
+
+#ifdef DEBUG		/* from here to end of file ... */
+
+TFILE tracedata, *tracetfile;
+FILE *tracefile;		/* just for histedit */
+
+uint64_t	DFlags;		/* currently enabled debug flags */
+int ShNest;		/* depth of shell (internal) nesting */
+
+static void shtree(union node *, int, int, int, TFILE *);
+static void shcmd(union node *, TFILE *);
+static void shsubsh(union node *, TFILE *);
+static void shredir(union node *, TFILE *, int);
+static void sharg(union node *, TFILE *);
+static void indent(int, TFILE *);
+static void trstring(const char *);
+static void trace_putc(char, TFILE *);
+static void trace_puts(const char *, TFILE *);
+static void trace_flush(TFILE *, int);
+static char *trace_id(TFILE *);
+static void trace_fd_swap(int, int);
+
+inline static int trlinelen(TFILE *);
+
+
+/*
+ * These functions are the externally visible interface
+ *  (but only for a DEBUG shell.)
+ */
+
+void
+opentrace(void)
+{
+	char *s;
+	int fd;
+	int i;
+	pid_t pid;
+
+	if (debug != 1) {
+		/* leave fd open because libedit might be using it */
+		if (tracefile)
+			fflush(tracefile);
+		if (tracetfile)
+			trace_flush(tracetfile, 1);
+		return;
+	}
+#if DBG_PID == 1		/* using old shell.h, old tracing method */
+	DFlags = DBG_PID;	/* just force DBG_PID on, and leave it ... */
 #endif
+	pid = getpid();
+	if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) {
+		debug = 0;
+		error("Cannot asprintf tracefilename");
+	};
+
+	fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666);
+	if (fd == -1) {
+		debug = 0;
+		error("Can't open tracefile: %s (%s)\n", s, strerror(errno));
+	}
+	fd = to_upper_fd(fd);
+	if (fd <= 2) {
+		(void) close(fd);
+		debug = 0;
+		error("Attempt to use fd %d as tracefile thwarted\n", fd);
+	}
+	register_sh_fd(fd, trace_fd_swap);
+
+	/*
+	 * This stuff is just so histedit has a FILE * to use
+	 */
+	if (tracefile)
+		(void) fclose(tracefile);	/* also closes tfd */
+	tracefile = fdopen(fd, "a");	/* don't care if it is NULL */
+	if (tracefile)			/* except here... */
+		setlinebuf(tracefile);
+
+	/*
+	 * Now the real tracing setup
+	 */
+	if (tracedata.tfd > 0 && tracedata.tfd != fd)
+		(void) close(tracedata.tfd);	/* usually done by fclose() */
 
+	tracedata.tfd = fd;
+	tracedata.pid = pid;
+	tracedata.nxtiov = 0;
+	tracedata.blen = 0;
+	tracedata.llen = 0;
+	tracedata.lastc = '\0';
+	tracedata.supr = SUP_NL | SUP_WSP;
 
-FILE *tracefile;
+#define	replace(f, v) do {				\
+		if (tracedata.f != NULL)		\
+			free(tracedata.f);		\
+		tracedata.f = v;			\
+	} while (/*CONSTCOND*/ 0)
+
+	replace(tracefile, s);
+
+	for (i = 0; i < TR_IOVECS; i++) {
+		replace(lines[i].iov_base, NULL);
+		tracedata.lines[i].iov_len = 0;
+	}
+
+#undef replace
+
+	tracetfile = &tracedata;
+
+	trace_puts("\nTracing started.\n", tracetfile);
+}
+
+void
+trace(const char *fmt, ...)
+{
+	va_list va;
+	char *s;
+
+	if (debug != 1 || !tracetfile)
+		return;
+	va_start(va, fmt);
+	(void) vasprintf(&s, fmt, va);
+	va_end(va);
+
+	trace_puts(s, tracetfile);
+	free(s);
+	if (tracetfile->llen == 0)
+		trace_flush(tracetfile, 0);
+}
+
+void
+tracev(const char *fmt, va_list va)
+{
+	va_list ap;
+	char *s;
+
+	if (debug != 1 || !tracetfile)
+		return;
+	va_copy(ap, va);
+	(void) vasprintf(&s, fmt, ap);
+	va_end(ap);
+
+	trace_puts(s, tracetfile);
+	free(s);
+	if (tracetfile->llen == 0)
+		trace_flush(tracetfile, 0);
+}
+
+
+void
+trputs(const char *s)
+{
+	if (debug != 1 || !tracetfile)
+		return;
+	trace_puts(s, tracetfile);
+}
 
-#ifdef DEBUG
-static int shtree(union node *, int, int, char *, FILE*);
-static int shcmd(union node *, FILE *);
-static int shsubsh(union node *, FILE *);
-static int shredir(union node *, FILE *, int);
-static int sharg(union node *, FILE *);
-static int indent(int, char *, FILE *);
-static void trstring(char *);
+void
+trputc(int c)
+{
+	if (debug != 1 || !tracetfile)
+		return;
+	trace_putc(c, tracetfile);
+}
 
 void
 showtree(union node *n)
 {
-	FILE *fp;
+	TFILE *fp;
 
-	fp = tracefile ? tracefile : stdout;
+	if ((fp = tracetfile) == NULL)
+		return;
 
-	trputs("showtree(");
+	trace_puts("showtree(", fp);
 		if (n == NULL)
-			trputs("NULL");
+			trace_puts("NULL", fp);
 		else if (n == NEOF)
-			trputs("NEOF");
-	trputs(") called\n");
+			trace_puts("NEOF", fp);
+		else
+			trace("%p", n);
+	trace_puts(") called\n", fp);
 	if (n != NULL && n != NEOF)
-		shtree(n, 1, 1, NULL, fp);
+		shtree(n, 1, 1, 1, fp);
+}
+
+void
+trargs(char **ap)
+{
+	if (debug != 1 || !tracetfile)
+		return;
+	while (*ap) {
+		trstring(*ap++);
+		if (*ap)
+			trace_putc(' ', tracetfile);
+		else
+			trace_putc('\n', tracetfile);
+	}
 }
 
 
-static int
-shtree(union node *n, int ind, int nl, char *pfx, FILE *fp)
+/*
+ * Beyond here we just have the implementation of all of that
+ */
+
+
+inline static int
+trlinelen(TFILE * fp)
+{
+	return fp->llen;
+}
+
+static void
+shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp)
 {
 	struct nodelist *lp;
 	const char *s;
-	int len;
 
 	if (n == NULL) {
 		if (nl)
-			fputc('\n', fp);
-		return 0;
+			trace_putc('\n', fp);
+		return;
 	}
 
-	len = indent(ind, pfx, fp);
+	indent(ind, fp);
 	switch (n->type) {
 	case NSEMI:
-		s = "; ";
-		len += 2;
+		s = NULL;
 		goto binop;
 	case NAND:
 		s = " && ";
-		len += 4;
 		goto binop;
 	case NOR:
 		s = " || ";
-		len += 4;
 binop:
-		len += shtree(n->nbinary.ch1, 0, 0, NULL, fp);
-		fputs(s, fp);
-		if (len >= 60) {
-			putc('\n', fp);
-			len = indent(ind < 0 ? 2 : ind + 1, pfx, fp);
+		shtree(n->nbinary.ch1, 0, ilvl, 0, fp);
+		if (s != NULL)
+			trace_puts(s, fp);
+		if (trlinelen(fp) >= TR_STD_WIDTH) {
+			trace_putc('\n', fp);
+			indent(ind < 0 ? 2 : ind + 1, fp);
+		} else if (s == NULL) {
+			if (fp->lastc != '&')
+				trace_puts("; ", fp);
+			else
+				trace_putc(' ', fp);
 		}
-		len += shtree(n->nbinary.ch2, 0, nl, NULL, fp);
+		shtree(n->nbinary.ch2, 0, ilvl, nl, fp);
 		break;
 	case NCMD:
-		len += shcmd(n, fp);
-		if (nl && len > 0)
-			len = 0, putc('\n', fp);
-		break;
-
-	case NDNOT:
-		fputs("! ", fp);
-		len += 2;
-		/* FALLTHROUGH */
-	case NNOT:
-		fputs("! ", fp);
-		len += 2 + shtree(n->nnot.com, 0, 0, NULL, fp);
+		shcmd(n, fp);
+		if (n->ncmd.backgnd)
+			trace_puts(" &", fp);
+		if (nl && trlinelen(fp) > 0)
+			trace_putc('\n', fp);
 		break;
-
 	case NPIPE:
 		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
-			len += shtree(lp->n, 0, 0, NULL, fp);
+			shtree(lp->n, 0, ilvl, 0, fp);
 			if (lp->next) {
-				len += 3, fputs(" | ", fp);
-				if (len >= 60)  {
-					fputc('\n', fp);
-					len = indent(ind < 0 ? 2 : ind + 1,
-					    pfx, fp);
-				}
+				trace_puts(" |", fp);
+				if (trlinelen(fp) >= TR_STD_WIDTH)  {
+					trace_putc('\n', fp);
+					indent((ind < 0 ? ilvl : ind) + 1, fp);
+				} else
+					trace_putc(' ', fp);
 			}
 		}
 		if (n->npipe.backgnd)
-			len += 2, fputs(" &", fp);
-		if (nl || len >= 60)
-			len = 0, fputc('\n', fp);
+			trace_puts(" &", fp);
+		if (nl || trlinelen(fp) >= TR_STD_WIDTH)
+			trace_putc('\n', fp);
 		break;
+	case NBACKGND:
 	case NSUBSHELL:
-		len += shsubsh(n, fp);
-		if (nl && len > 0)
-			len = 0, putc('\n', fp);
-		break;
-	default:
-#ifdef NODETYPENAME
-		len += fprintf(fp, "<node type %d [%s]>", n->type,
-		    NODETYPENAME(n->type));
-#else
-		len += fprintf(fp, "<node type %d>", n->type);
-#endif
+		shsubsh(n, fp);
+		if (n->type == NBACKGND)
+			trace_puts(" &", fp);
+		if (nl && trlinelen(fp) > 0)
+			trace_putc('\n', fp);
+		break;
+	case NDEFUN:
+		trace_puts(n->narg.text, fp);
+		trace_puts("() {\n", fp);
+		indent(ind, fp);
+		shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp);
+		indent(ind, fp);
+		trace_puts("}\n", fp);
+		break;
+	case NDNOT:
+		trace_puts("! ", fp);
+		/* FALLTHROUGH */
+	case NNOT:
+		trace_puts("! ", fp);
+		shtree(n->nnot.com, -1, ilvl, nl, fp);
+		break;
+	case NREDIR:
+		shtree(n->nredir.n, -1, ilvl, 0, fp);
+		shredir(n->nredir.redirect, fp, n->nredir.n == NULL);
+		if (nl)
+			trace_putc('\n', fp);
+		break;
+
+	case NIF:
+	itsif:
+		trace_puts("if ", fp);
+		shtree(n->nif.test, -1, ilvl, 0, fp);
+		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
+			if (fp->lastc != '&')
+				trace_puts(" ;", fp);
+		} else
+			indent(ilvl, fp);
+		trace_puts(" then ", fp);
+		if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
+			indent(ilvl+1, fp);
+		shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp);
+		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
+			if (fp->lastc != '&')
+				trace_puts(" ;", fp);
+		} else
+			indent(ilvl, fp);
+		if (n->nif.elsepart && n->nif.elsepart->type == NIF) {
+			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
+				indent(ilvl, fp);
+			n = n->nif.elsepart;
+			trace_puts(" el", fp);
+			goto itsif;
+		}
+		if (n->nif.elsepart) {
+			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
+				indent(ilvl+1, fp);
+			trace_puts(" else ", fp);
+			shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp);
+			if (fp->lastc != '&')
+				trace_puts(" ;", fp);
+		}
+		trace_puts(" fi", fp);
+		if (nl)
+			trace_putc('\n', fp);
+		break;
+
+	case NWHILE:
+		trace_puts("while ", fp);
+		goto aloop;
+	case NUNTIL:
+		trace_puts("until ", fp);
+	aloop:
+		shtree(n->nbinary.ch1, -1, ilvl, 0, fp);
+		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
+			if (fp->lastc != '&')
+				trace_puts(" ;", fp);
+		} else
+			trace_putc('\n', fp);
+		trace_puts(" do ", fp);
+		shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp);
+		trace_puts(" done ", fp);
+		if (nl)
+			trace_putc('\n', fp);
+		break;
+
+	case NFOR:
+		trace_puts("for ", fp);
+		trace_puts(n->nfor.var, fp);
+		if (n->nfor.args) {
+			union node *argp;
+
+			trace_puts(" in ", fp);
+			for (argp = n->nfor.args; argp; argp=argp->narg.next) {
+				sharg(argp, fp);
+				trace_putc(' ', fp);
+			}
+			if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
+				if (fp->lastc != '&')
+					trace_putc(';', fp);
+			} else
+				trace_putc('\n', fp);
+		}
+		trace_puts(" do ", fp);
+		shtree(n->nfor.body, -1, ilvl + 1, 0, fp);
+		if (fp->lastc != '&')
+			trace_putc(';', fp);
+		trace_puts(" done", fp);
+		if (nl)
+			trace_putc('\n', fp);
+		break;
+
+	case NCASE:
+		trace_puts("case ", fp);
+		sharg(n->ncase.expr, fp);
+		trace_puts(" in", fp);
+		if (nl)
+			trace_putc('\n', fp);
+		{
+			union node *cp;
+
+			for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
+				union node *patp;
+
+				if (nl || trlinelen(fp) > TR_STD_WIDTH - 16)
+					indent(ilvl, fp);
+				else
+					trace_putc(' ', fp);
+				trace_putc('(', fp);
+				patp = cp->nclist.pattern;
+				while (patp != NULL) {
+				    trace_putc(' ', fp);
+				    sharg(patp, fp);
+				    trace_putc(' ', fp);
+				    if ((patp = patp->narg.next) != NULL)
+					trace_putc('|', fp);
+				}
+				trace_putc(')', fp);
+				if (nl)
+					indent(ilvl + 1, fp);
+				else
+					trace_putc(' ', fp);
+				shtree(cp->nclist.body, -1, ilvl+2, 0, fp);
+				if (cp->type == NCLISTCONT)
+					trace_puts(" ;&", fp);
+				else
+					trace_puts(" ;;", fp);
+				if (nl)
+					trace_putc('\n', fp);
+			}
+		}
+		if (nl) {
+			trace_putc('\n', fp);
+			indent(ind, fp);
+		} else
+			trace_putc(' ', fp);
+		trace_puts("esac", fp);
 		if (nl)
-			len = 0, putc('\n', fp);
+			trace_putc('\n', fp);
+		break;
+
+	default: {
+			char *str;
+
+			asprintf(&str, "<node type %d [%s]>", n->type,
+			    NODETYPENAME(n->type));
+			trace_puts(str, fp);
+			free(str);
+			if (nl)
+				trace_putc('\n', fp);
+		}
 		break;
 	}
-	return len;
 }
 
 
-
-static int
-shcmd(union node *cmd, FILE *fp)
+static void
+shcmd(union node *cmd, TFILE *fp)
 {
 	union node *np;
 	int first;
-	int len = 0;
 
 	first = 1;
 	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
 		if (! first)
-			len++, fputc(' ', fp);
-		len += sharg(np, fp);
+			trace_putc(' ', fp);
+		sharg(np, fp);
 		first = 0;
 	}
-	return len + shredir(cmd, fp, first);
+	shredir(cmd->ncmd.redirect, fp, first);
 }
 
-static int
-shsubsh(union node *cmd, FILE *fp)
+static void
+shsubsh(union node *cmd, TFILE *fp)
 {
-	int len = 6;
-
-	fputs(" ( ", fp);
-	len += shtree(cmd->nredir.n, -1, 0, NULL, fp);
-	fputs(" ) ", fp);
-	len += shredir(cmd, fp, 1);
-
-	return len;
+	trace_puts(" ( ", fp);
+	shtree(cmd->nredir.n, -1, 3, 0, fp);
+	trace_puts(" ) ", fp);
+	shredir(cmd->ncmd.redirect, fp, 1);
 }
 
-static int
-shredir(union node *cmd, FILE *fp, int first)
+static void
+shredir(union node *np, TFILE *fp, int first)
 {
-	union node *np;
 	const char *s;
 	int dftfd;
-	int len = 0;
 	char buf[106];
 
-	for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
+	for ( ; np ; np = np->nfile.next) {
 		if (! first)
-			len++, fputc(' ', fp);
+			trace_putc(' ', fp);
 		switch (np->nfile.type) {
-			case NTO:	s = ">";  dftfd = 1; len += 1; break;
-			case NCLOBBER:	s = ">|"; dftfd = 1; len += 2; break;
-			case NAPPEND:	s = ">>"; dftfd = 1; len += 2; break;
-			case NTOFD:	s = ">&"; dftfd = 1; len += 2; break;
-			case NFROM:	s = "<";  dftfd = 0; len += 1; break;
-			case NFROMFD:	s = "<&"; dftfd = 0; len += 2; break;
-			case NFROMTO:	s = "<>"; dftfd = 0; len += 2; break;
-			case NXHERE:	/* FALLTHROUGH */ 
-			case NHERE:	s = "<<"; dftfd = 0; len += 2; break;
-			default:   s = "*error*"; dftfd = 0; len += 7; break;
-		}
-		if (np->nfile.fd != dftfd)
-			len += fprintf(fp, "%d", np->nfile.fd);
-		fputs(s, fp);
+			case NTO:	s = ">";  dftfd = 1; break;
+			case NCLOBBER:	s = ">|"; dftfd = 1; break;
+			case NAPPEND:	s = ">>"; dftfd = 1; break;
+			case NTOFD:	s = ">&"; dftfd = 1; break;
+			case NFROM:	s = "<";  dftfd = 0; break;
+			case NFROMFD:	s = "<&"; dftfd = 0; break;
+			case NFROMTO:	s = "<>"; dftfd = 0; break;
+			case NXHERE:	/* FALLTHROUGH */
+			case NHERE:	s = "<<"; dftfd = 0; break;
+			default:   s = "*error*"; dftfd = 0; break;
+		}
+		if (np->nfile.fd != dftfd) {
+			sprintf(buf, "%d", np->nfile.fd);
+			trace_puts(buf, fp);
+		}
+		trace_puts(s, fp);
 		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
-			len += fprintf(fp, "%d", np->ndup.dupfd);
+			if (np->ndup.vname)
+				sharg(np->ndup.vname, fp);
+			else {
+				sprintf(buf, "%d", np->ndup.dupfd);
+				trace_puts(buf, fp);
+			}
 		} else
 		    if (np->nfile.type == NHERE || np->nfile.type == NXHERE) {
 			if (np->nfile.type == NHERE)
-				fputc('\\', fp);
-			fputs("!!!\n", fp);
+				trace_putc('\\', fp);
+			trace_puts("!!!\n", fp);
 			s = np->nhere.doc->narg.text;
 			if (strlen(s) > 100) {
 				memmove(buf, s, 100);
 				buf[100] = '\0';
-				strcat(buf, " ...");
+				strcat(buf, " ...\n");
 				s = buf;
 			}
-			fputs(s, fp);
-			fputs("!!!", fp);
-			len = 3;
+			trace_puts(s, fp);
+			trace_puts("!!! ", fp);
 		} else {
-			len += sharg(np->nfile.fname, fp);
+			sharg(np->nfile.fname, fp);
 		}
 		first = 0;
 	}
-	return len;
 }
 
-
-
-static int
-sharg(union node *arg, FILE *fp)
+static void
+sharg(union node *arg, TFILE *fp)
 {
-	char *p;
+	char *p, *s;
 	struct nodelist *bqlist;
-	int subtype;
-	int len = 0;
+	int subtype = 0;
+	int quoted = 0;
 
 	if (arg->type != NARG) {
-		fprintf(fp, "<node type %d>\n", arg->type);
-		abort();
+		asprintf(&s, "<node type %d> ! NARG\n", arg->type);
+		trace_puts(s, fp);
+		abort();	/* no need to free s, better not to */
 	}
+
 	bqlist = arg->narg.backquote;
 	for (p = arg->narg.text ; *p ; p++) {
 		switch (*p) {
 		case CTLESC:
-			putc(*++p, fp);
-			len++;
+			trace_putc('\\', fp);
+			trace_putc(*++p, fp);
 			break;
+
 		case CTLVAR:
-			putc('$', fp);
-			putc('{', fp);
-			len += 2;
 			subtype = *++p;
+			if (!quoted != !(subtype & VSQUOTE))
+				trace_putc('"', fp);
+			trace_putc('$', fp);
+			trace_putc('{', fp);
 			if ((subtype & VSTYPE) == VSLENGTH)
-				len++, putc('#', fp);
+				trace_putc('#', fp);
 			if (subtype & VSLINENO)
-				len += 7, fputs("LINENO=", fp);
+				trace_puts("LINENO=", fp);
 
 			while (*++p != '=')
-				len++, putc(*p, fp);
+				trace_putc(*p, fp);
 
 			if (subtype & VSNUL)
-				len++, putc(':', fp);
+				trace_putc(':', fp);
 
 			switch (subtype & VSTYPE) {
 			case VSNORMAL:
-				putc('}', fp);
-				len++;
+				/* { */
+				trace_putc('}', fp);
+				if (!quoted != !(subtype & VSQUOTE))
+					trace_putc('"', fp);
 				break;
 			case VSMINUS:
-				putc('-', fp);
-				len++;
+				trace_putc('-', fp);
 				break;
 			case VSPLUS:
-				putc('+', fp);
-				len++;
+				trace_putc('+', fp);
 				break;
 			case VSQUESTION:
-				putc('?', fp);
-				len++;
+				trace_putc('?', fp);
 				break;
 			case VSASSIGN:
-				putc('=', fp);
-				len++;
-				break;
-			case VSTRIMLEFT:
-				putc('#', fp);
-				len++;
+				trace_putc('=', fp);
 				break;
 			case VSTRIMLEFTMAX:
-				putc('#', fp);
-				putc('#', fp);
-				len += 2;
-				break;
-			case VSTRIMRIGHT:
-				putc('%', fp);
-				len++;
+				trace_putc('#', fp);
+				/* FALLTHROUGH */
+			case VSTRIMLEFT:
+				trace_putc('#', fp);
 				break;
 			case VSTRIMRIGHTMAX:
-				putc('%', fp);
-				putc('%', fp);
-				len += 2;
+				trace_putc('%', fp);
+				/* FALLTHROUGH */
+			case VSTRIMRIGHT:
+				trace_putc('%', fp);
 				break;
 			case VSLENGTH:
 				break;
-			default:
-				len += fprintf(fp, "<subtype %d>", subtype);
+			default: {
+					char str[32];
+
+					snprintf(str, sizeof str,
+					    "<subtype %d>", subtype);
+					trace_puts(str, fp);
+				}
+				break;
 			}
 			break;
 		case CTLENDVAR:
-		     putc('}', fp);
-		     len++;
-		     break;
-		case CTLBACKQ:
+			/* { */
+			trace_putc('}', fp);
+			if (!quoted != !(subtype & VSQUOTE))
+				trace_putc('"', fp);
+			subtype = 0;
+			break;
+
 		case CTLBACKQ|CTLQUOTE:
-			putc('$', fp);
-			putc('(', fp);
-			len += shtree(bqlist->n, -1, 0, NULL, fp) + 3;
-			putc(')', fp);
+			if (!quoted)
+				trace_putc('"', fp);
+			/* FALLTHRU */
+		case CTLBACKQ:
+			trace_putc('$', fp);
+			trace_putc('(', fp);
+			if (bqlist) {
+				shtree(bqlist->n, -1, 3, 0, fp);
+				bqlist = bqlist->next;
+			} else
+				trace_puts("???", fp);
+			trace_putc(')', fp);
+			if (!quoted && *p == (CTLBACKQ|CTLQUOTE))
+				trace_putc('"', fp);
 			break;
+
+		case CTLQUOTEMARK:
+			if (subtype != 0 || !quoted) {
+				trace_putc('"', fp);
+				quoted++;
+			}
+			break;
+		case CTLQUOTEEND:
+			trace_putc('"', fp);
+			quoted--;
+			break;
+		case CTLARI:
+			trace_puts("$(( ", fp);
+			break;
+		case CTLENDARI:
+			trace_puts(" ))", fp);
+			break;
+
 		default:
-			putc(*p, fp);
-			len++;
+			if (*p == '$')
+				trace_putc('\\', fp);
+			trace_putc(*p, fp);
 			break;
 		}
 	}
-	return len;
+	if (quoted)
+		trace_putc('"', fp);
 }
 
 
-static int
-indent(int amount, char *pfx, FILE *fp)
+static void
+indent(int amount, TFILE *fp)
 {
 	int i;
-	int len = 0;
 
-	/*
-	 * in practice, pfx is **always** NULL
-	 * but here, we assume if it were not, at least strlen(pfx) < 8
-	 * if that is invalid, output will look messy
-	 */
-	for (i = 0 ; i < amount ; i++) {
-		if (pfx && i == amount - 1)
-			fputs(pfx, fp);
-		putc('\t', fp);
-		len |= 7;
-		len++;
+	if (amount <= 0)
+		return;
+
+	amount <<= 2;	/* indent slots -> chars */
+
+	i = trlinelen(fp);
+	fp->supr = SUP_NL;
+	if (i > amount) {
+		trace_putc('\n', fp);
+		i = 0;
+	}
+	fp->supr = 0;
+	for (; i < amount - 7 ; i++) {
+		trace_putc('\t', fp);
+		i |= 7;
+	}
+	while (i < amount) {
+		trace_putc(' ', fp);
+		i++;
 	}
-	return len;
+	fp->supr = SUP_WSP;
 }
-#endif
 
+static void
+trace_putc(char c, TFILE *fp)
+{
+	char *p;
 
+	if (c == '\0')
+		return;
+	if (debug == 0 || fp == NULL)
+		return;
 
-/*
- * Debugging stuff.
- */
+	if (fp->llen == 0) {
+		if (fp->blen != 0)
+			abort();
 
+		if ((fp->supr & SUP_NL) && c == '\n')
+			return;
+		if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ')
+			return;
+		if ((fp->supr & SUP_WSP) && c == '\t')
+			return;
 
+		if (fp->nxtiov >= TR_IOVECS - 1)	/* should be rare */
+			trace_flush(fp, 0);
 
+		p = trace_id(fp);
+		if (p != NULL) {
+			fp->lines[fp->nxtiov].iov_base = p;
+			fp->lines[fp->nxtiov].iov_len = strlen(p);
+			fp->nxtiov++;
+		}
+	} else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) {
+		fp->blen = 0;
+		if (++fp->nxtiov >= TR_IOVECS)
+			trace_flush(fp, 0);
+	}
 
-#ifdef DEBUG
-void
-trputc(int c)
-{
-	if (debug != 1 || !tracefile)
+	if (fp->lines[fp->nxtiov].iov_len == 0) {
+		p = (char *)malloc(2 * TR_STD_WIDTH);
+		if (p == NULL) {
+			trace_flush(fp, 1);
+			debug = 0;
+			return;
+		}
+		*p = '\0';
+		fp->lines[fp->nxtiov].iov_base = p;
+		fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH;
+		fp->blen = 0;
+	}
+
+	p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++;
+	*p++ = c;
+	*p = 0;
+
+	if (c != ' ' && c != '\t' && c != '\n') {
+		fp->lastc = c;
+		fp->supr = 0;
+	}
+
+	if (c == '\n') {
+		fp->lines[fp->nxtiov++].iov_len = fp->blen;
+		fp->blen = 0;
+		fp->llen = 0;
+		fp->supr |= SUP_NL;
 		return;
-	putc(c, tracefile);
+	}
+
+	if (c == '\t')
+		fp->llen |=  7;
+	fp->llen++;
 }
-#endif
 
 void
-trace(const char *fmt, ...)
+trace_flush(TFILE *fp, int all)
 {
-#ifdef DEBUG
-	va_list va;
+	int niov, i;
+	ssize_t written;
 
-	if (debug != 1 || !tracefile)
+	niov = fp->nxtiov;
+	if (all && fp->blen > 0) {
+		fp->lines[niov].iov_len = fp->blen;
+		fp->blen = 0;
+		fp->llen = 0;
+		niov++;
+	}
+	if (niov == 0)
 		return;
-	va_start(va, fmt);
-	(void) vfprintf(tracefile, fmt, va);
-	va_end(va);
-#endif
+	if (fp->blen > 0 && --niov == 0)
+		return;
+	written = writev(fp->tfd, fp->lines, niov);
+	for (i = 0; i < niov; i++) {
+		free(fp->lines[i].iov_base);
+		fp->lines[i].iov_base = NULL;
+		fp->lines[i].iov_len = 0;
+	}
+	if (written == -1) {
+		if (fp->blen > 0) {
+			free(fp->lines[niov].iov_base);
+			fp->lines[niov].iov_base = NULL;
+			fp->lines[niov].iov_len = 0;
+		}
+		debug = 0;
+		fp->blen = 0;
+		fp->llen = 0;
+		return;
+	}
+	if (fp->blen > 0) {
+		fp->lines[0].iov_base = fp->lines[niov].iov_base;
+		fp->lines[0].iov_len = fp->lines[niov].iov_len;
+		fp->lines[niov].iov_base = NULL;
+		fp->lines[niov].iov_len = 0;
+	}
+	fp->nxtiov = 0;
 }
 
 void
-tracev(const char *fmt, va_list va)
+trace_puts(const char *s, TFILE *fp)
 {
-#ifdef DEBUG
-	va_list ap;
-	if (debug != 1 || !tracefile)
-		return;
-	va_copy(ap, va);
-	(void) vfprintf(tracefile, fmt, ap);
-	va_end(ap);
-#endif
-}
+	char c;
 
+	while ((c = *s++) != '\0')
+		trace_putc(c, fp);
+}
 
-#ifdef DEBUG
-void
-trputs(const char *s)
+inline static char *
+trace_id(TFILE *tf)
 {
-	if (debug != 1 || !tracefile)
-		return;
-	fputs(s, tracefile);
-}
+	int i;
+	char indent[16];
+	char *p;
 
+	if (DFlags & DBG_NEST) {
+		p = indent;
+		for (i = 0; i < 6; i++)
+			*p++ = (i < ShNest) ? '#' : ' ';
+		while (i++ < ShNest && p < &indent[sizeof indent - 1])
+			*p++ = '#';
+		*p = '\0';
+	} else
+		indent[0] = '\0';
+
+	if (DFlags & DBG_PID) {
+		i = getpid();
+		(void) asprintf(&p, "%5d%c%s\t", i,
+		    i == tf->pid ? ':' : '=', indent);
+		return p;
+	} else if (DFlags & DBG_NEST) {
+		*p++ = '\t';
+		*p = '\0';
+		(void) asprintf(&p, "%s\t", indent);
+		return p;
+	}
+	return NULL;
+}
 
+/*
+ * Used only from trargs(), which itself is used only to print
+ * arg lists (argv[]) either that passed into this shell, or
+ * the arg list about to be given to some other command (incl
+ * builtin, and function) as their argv[].  If any of the CTL*
+ * chars seem to appear, they really should be just treated as data,
+ * not special...   But this is just debug, so, who cares!
+ */
 static void
-trstring(char *s)
+trstring(const char *s)
 {
-	char *p;
+	const char *p;
 	char c;
+	TFILE *fp;
 
-	if (debug != 1 || !tracefile)
+	if (debug != 1 || !tracetfile)
 		return;
-	putc('"', tracefile);
+	fp = tracetfile;
+	trace_putc('"', fp);
 	for (p = s ; *p ; p++) {
 		switch (*p) {
 		case '\n':  c = 'n';  goto backslash;
@@ -460,78 +976,43 @@ trstring(char *s)
 		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
 		case CTLBACKQ:  c = 'q';  goto backslash;
 		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
-backslash:	  putc('\\', tracefile);
-			putc(c, tracefile);
+backslash:		trace_putc('\\', fp);
+			trace_putc(c, fp);
 			break;
 		default:
 			if (*p >= ' ' && *p <= '~')
-				putc(*p, tracefile);
+				trace_putc(*p, fp);
 			else {
-				putc('\\', tracefile);
-				putc(*p >> 6 & 03, tracefile);
-				putc(*p >> 3 & 07, tracefile);
-				putc(*p & 07, tracefile);
+				trace_putc('\\', fp);
+				trace_putc(*p >> 6 & 03, fp);
+				trace_putc(*p >> 3 & 07, fp);
+				trace_putc(*p & 07, fp);
 			}
 			break;
 		}
 	}
-	putc('"', tracefile);
+	trace_putc('"', fp);
 }
-#endif
-
 
-void
-trargs(char **ap)
+/*
+ * deal with the user "accidentally" picking our fd to use.
+ */
+static void
+trace_fd_swap(int from, int to)
 {
-#ifdef DEBUG
-	if (debug != 1 || !tracefile)
+	if (tracetfile == NULL || from == to)
 		return;
-	while (*ap) {
-		trstring(*ap++);
-		if (*ap)
-			putc(' ', tracefile);
-		else
-			putc('\n', tracefile);
-	}
-#endif
-}
-
 
-#ifdef DEBUG
-void
-opentrace(void)
-{
-	char s[100];
-#ifdef O_APPEND
-	int flags;
-#endif
+	tracetfile->tfd = to;
 
-	if (debug != 1) {
-		if (tracefile)
-			fflush(tracefile);
-		/* leave open because libedit might be using it */
-		return;
-	}
-	snprintf(s, sizeof(s), "./trace.%d", (int)getpid());
-	if (tracefile) {
-		if (!freopen(s, "a", tracefile)) {
-			fprintf(stderr, "Can't re-open %s\n", s);
-			tracefile = NULL;
-			debug = 0;
-			return;
-		}
-	} else {
-		if ((tracefile = fopen(s, "a")) == NULL) {
-			fprintf(stderr, "Can't open %s\n", s);
-			debug = 0;
-			return;
-		}
-	}
-#ifdef O_APPEND
-	if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
-		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
-#endif
-	setlinebuf(tracefile);
-	fputs("\nTracing started.\n", tracefile);
+	/*
+	 * This is just so histedit has a stdio FILE* to use.
+	 */
+	if (tracefile)
+		fclose(tracefile);
+	tracefile = fdopen(to, "a");
+	if (tracefile)
+		setlinebuf(tracefile);
 }
+
 #endif /* DEBUG */

Index: src/bin/sh/show.h
diff -u src/bin/sh/show.h:1.9 src/bin/sh/show.h:1.10
--- src/bin/sh/show.h:1.9	Thu Mar 16 13:21:59 2017
+++ src/bin/sh/show.h	Sat May 13 03:26:03 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: show.h,v 1.9 2017/03/16 13:21:59 kre Exp $	*/
+/*	$NetBSD: show.h,v 1.10 2017/05/13 03:26:03 kre Exp $	*/
 
 /*-
  * Copyright (c) 1995
@@ -33,12 +33,12 @@
 
 #include <stdarg.h>
 
+#ifdef DEBUG
 union node;
 void showtree(union node *);
 void trace(const char *, ...);
 void tracev(const char *, va_list);
 void trargs(char **);
-#ifdef DEBUG
 void trputc(int);
 void trputs(const char *);
 void opentrace(void);

Reply via email to