xref: /netbsd-src/bin/sh/show.c (revision 1c3b098334123732efc3e38acf3c8a83909e00b2)
1*1c3b0983Skre /*	$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
75dd8362aSkre  * Copyright (c) 2017 The NetBSD Foundation, Inc.  All rights reserved.
85dd8362aSkre  *
961f28255Scgd  * This code is derived from software contributed to Berkeley by
1061f28255Scgd  * Kenneth Almquist.
1161f28255Scgd  *
1261f28255Scgd  * Redistribution and use in source and binary forms, with or without
1361f28255Scgd  * modification, are permitted provided that the following conditions
1461f28255Scgd  * are met:
1561f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1761f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1861f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1961f28255Scgd  *    documentation and/or other materials provided with the distribution.
209d15d213Skre  * 3. Neither the name of the University nor the names of its contributors
219d15d213Skre  *    may be used to endorse or promote products derived from this software
229d15d213Skre  *    without specific prior written permission.
2361f28255Scgd  *
249d15d213Skre  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
259d15d213Skre  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
269d15d213Skre  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
279d15d213Skre  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2861f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2961f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3061f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3161f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3261f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3361f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3461f28255Scgd  * SUCH DAMAGE.
3561f28255Scgd  */
3661f28255Scgd 
37cd799663Schristos #include <sys/cdefs.h>
3861f28255Scgd #ifndef lint
3949f0ad86Scgd #if 0
4007bae7edSchristos static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95";
4149f0ad86Scgd #else
42*1c3b0983Skre __RCSID("$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 kre Exp $");
4349f0ad86Scgd #endif
4461f28255Scgd #endif /* not lint */
4561f28255Scgd 
4661f28255Scgd #include <stdio.h>
4707bae7edSchristos #include <stdarg.h>
48c0cf1d6bSdsl #include <stdlib.h>
49d452d7e7Schristos #include <unistd.h>
505dd8362aSkre #include <fcntl.h>
515dd8362aSkre #include <errno.h>
52ab36694aSkre #include <limits.h>
535dd8362aSkre 
545dd8362aSkre #include <sys/types.h>
555dd8362aSkre #include <sys/uio.h>
5607bae7edSchristos 
5761f28255Scgd #include "shell.h"
5861f28255Scgd #include "parser.h"
5961f28255Scgd #include "nodes.h"
6061f28255Scgd #include "mystring.h"
6107bae7edSchristos #include "show.h"
62c02b3bbdSchristos #include "options.h"
635dd8362aSkre #include "redir.h"
645dd8362aSkre #include "error.h"
65ab36694aSkre #include "syntax.h"
668ffd1099Skre #include "input.h"
67ab36694aSkre #include "output.h"
68727a69dcSkre #include "var.h"
69ab36694aSkre #include "builtins.h"
705dd8362aSkre 
71ed090bddSchristos #define DEFINE_NODENAMES
725dd8362aSkre #include "nodenames.h"		/* does almost nothing if !defined(DEBUG) */
735dd8362aSkre 
745dd8362aSkre #define	TR_STD_WIDTH	60	/* tend to fold lines wider than this */
755dd8362aSkre #define	TR_IOVECS	10	/* number of lines or trace (max) / write */
765dd8362aSkre 
775dd8362aSkre typedef struct traceinfo {
785dd8362aSkre 	int	tfd;	/* file descriptor for open trace file */
795dd8362aSkre 	int	nxtiov;	/* the buffer we should be writing to */
805dd8362aSkre 	char	lastc;	/* the last non-white character output */
81d13b5926Skre 	uint8_t	supr;	/* char classes to suppress after \n */
825dd8362aSkre 	pid_t	pid;	/* process id of process that opened that file */
835dd8362aSkre 	size_t	llen;	/* number of chars in current output line */
845dd8362aSkre 	size_t	blen;	/* chars used in current buffer being filled */
855dd8362aSkre 	char *	tracefile;		/* name of the tracefile */
865dd8362aSkre 	struct iovec lines[TR_IOVECS];	/* filled, flling, pending buffers */
875dd8362aSkre } TFILE;
885dd8362aSkre 
895dd8362aSkre /* These are auto turned off when non white space is printed */
905dd8362aSkre #define	SUP_NL	0x01	/* don't print \n */
91d13b5926Skre #define	SUP_SP	0x03	/* suppress spaces */
92d13b5926Skre #define	SUP_WSP	0x04	/* suppress all white space */
935dd8362aSkre 
945dd8362aSkre #ifdef DEBUG		/* from here to end of file ... */
955dd8362aSkre 
965dd8362aSkre TFILE tracedata, *tracetfile;
975dd8362aSkre FILE *tracefile;		/* just for histedit */
985dd8362aSkre 
995dd8362aSkre uint64_t	DFlags;		/* currently enabled debug flags */
1005dd8362aSkre int ShNest;			/* depth of shell (internal) nesting */
1015dd8362aSkre 
1025dd8362aSkre static void shtree(union node *, int, int, int, TFILE *);
1035dd8362aSkre static void shcmd(union node *, TFILE *);
1045dd8362aSkre static void shsubsh(union node *, TFILE *);
1055dd8362aSkre static void shredir(union node *, TFILE *, int);
1065dd8362aSkre static void sharg(union node *, TFILE *);
1075dd8362aSkre static void indent(int, TFILE *);
1085dd8362aSkre static void trstring(const char *);
1095dd8362aSkre static void trace_putc(char, TFILE *);
1105dd8362aSkre static void trace_puts(const char *, TFILE *);
1115dd8362aSkre static void trace_flush(TFILE *, int);
1125dd8362aSkre static char *trace_id(TFILE *);
1135dd8362aSkre static void trace_fd_swap(int, int);
1145dd8362aSkre 
1155dd8362aSkre inline static int trlinelen(TFILE *);
116d58ec7fcSkre 
117d58ec7fcSkre 
1185dd8362aSkre /*
1195dd8362aSkre  * These functions are the externally visible interface
1205dd8362aSkre  *  (but only for a DEBUG shell.)
1215dd8362aSkre  */
1229d15d213Skre 
1239d15d213Skre void
1245dd8362aSkre opentrace(void)
1259d15d213Skre {
1265dd8362aSkre 	char *s;
1275dd8362aSkre 	int fd;
1289d15d213Skre 	int i;
1295dd8362aSkre 	pid_t pid;
1309d15d213Skre 
1315dd8362aSkre 	if (debug != 1) {
1325dd8362aSkre 		/* leave fd open because libedit might be using it */
1335dd8362aSkre 		if (tracefile)
1345dd8362aSkre 			fflush(tracefile);
1355dd8362aSkre 		if (tracetfile)
1365dd8362aSkre 			trace_flush(tracetfile, 1);
13761f28255Scgd 		return;
13861f28255Scgd 	}
1395dd8362aSkre #if DBG_PID == 1		/* using old shell.h, old tracing method */
1405dd8362aSkre 	DFlags = DBG_PID;	/* just force DBG_PID on, and leave it ... */
1419d15d213Skre #endif
1425dd8362aSkre 	pid = getpid();
1435dd8362aSkre 	if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) {
1445dd8362aSkre 		debug = 0;
1455dd8362aSkre 		error("Cannot asprintf tracefilename");
1465dd8362aSkre 	};
1475dd8362aSkre 
1485dd8362aSkre 	fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666);
1495dd8362aSkre 	if (fd == -1) {
1505dd8362aSkre 		debug = 0;
1515dd8362aSkre 		error("Can't open tracefile: %s (%s)\n", s, strerror(errno));
1525dd8362aSkre 	}
1535dd8362aSkre 	fd = to_upper_fd(fd);
1545dd8362aSkre 	if (fd <= 2) {
1555dd8362aSkre 		(void) close(fd);
1565dd8362aSkre 		debug = 0;
1575dd8362aSkre 		error("Attempt to use fd %d as tracefile thwarted\n", fd);
1585dd8362aSkre 	}
1595dd8362aSkre 	register_sh_fd(fd, trace_fd_swap);
1605dd8362aSkre 
1615dd8362aSkre 	/*
1625dd8362aSkre 	 * This stuff is just so histedit has a FILE * to use
1635dd8362aSkre 	 */
1645dd8362aSkre 	if (tracefile)
1655dd8362aSkre 		(void) fclose(tracefile);	/* also closes tfd */
166*1c3b0983Skre 	tracefile = fdopen(fd, "ae");	/* don't care if it is NULL */
1675dd8362aSkre 	if (tracefile)			/* except here... */
1685dd8362aSkre 		setlinebuf(tracefile);
1695dd8362aSkre 
1705dd8362aSkre 	/*
1715dd8362aSkre 	 * Now the real tracing setup
1725dd8362aSkre 	 */
1735dd8362aSkre 	if (tracedata.tfd > 0 && tracedata.tfd != fd)
1745dd8362aSkre 		(void) close(tracedata.tfd);	/* usually done by fclose() */
1755dd8362aSkre 
1765dd8362aSkre 	tracedata.tfd = fd;
1775dd8362aSkre 	tracedata.pid = pid;
1785dd8362aSkre 	tracedata.nxtiov = 0;
1795dd8362aSkre 	tracedata.blen = 0;
1805dd8362aSkre 	tracedata.llen = 0;
1815dd8362aSkre 	tracedata.lastc = '\0';
1825dd8362aSkre 	tracedata.supr = SUP_NL | SUP_WSP;
1835dd8362aSkre 
1845dd8362aSkre #define	replace(f, v) do {				\
1855dd8362aSkre 		if (tracedata.f != NULL)		\
1865dd8362aSkre 			free(tracedata.f);		\
1875dd8362aSkre 		tracedata.f = v;			\
1884cb87529Srillig 	} while (0)
1895dd8362aSkre 
1905dd8362aSkre 	replace(tracefile, s);
1915dd8362aSkre 
1925dd8362aSkre 	for (i = 0; i < TR_IOVECS; i++) {
1935dd8362aSkre 		replace(lines[i].iov_base, NULL);
1945dd8362aSkre 		tracedata.lines[i].iov_len = 0;
1955dd8362aSkre 	}
1965dd8362aSkre 
1975dd8362aSkre #undef replace
1985dd8362aSkre 
1995dd8362aSkre 	tracetfile = &tracedata;
2005dd8362aSkre 
2015dd8362aSkre 	trace_puts("\nTracing started.\n", tracetfile);
2025dd8362aSkre }
20361f28255Scgd 
20407bae7edSchristos void
20507bae7edSchristos trace(const char *fmt, ...)
20661f28255Scgd {
20707bae7edSchristos 	va_list va;
2085dd8362aSkre 	char *s;
2090b398b28Swiz 
2105dd8362aSkre 	if (debug != 1 || !tracetfile)
211c02b3bbdSchristos 		return;
21207bae7edSchristos 	va_start(va, fmt);
2135dd8362aSkre 	(void) vasprintf(&s, fmt, va);
21407bae7edSchristos 	va_end(va);
2155dd8362aSkre 
2165dd8362aSkre 	trace_puts(s, tracetfile);
2175dd8362aSkre 	free(s);
2185dd8362aSkre 	if (tracetfile->llen == 0)
2195dd8362aSkre 		trace_flush(tracetfile, 0);
22061f28255Scgd }
22161f28255Scgd 
222e314f958Sdsl void
223e314f958Sdsl tracev(const char *fmt, va_list va)
224e314f958Sdsl {
225d452d7e7Schristos 	va_list ap;
2265dd8362aSkre 	char *s;
2275dd8362aSkre 
2285dd8362aSkre 	if (debug != 1 || !tracetfile)
229e314f958Sdsl 		return;
230d452d7e7Schristos 	va_copy(ap, va);
2315dd8362aSkre 	(void) vasprintf(&s, fmt, ap);
232d452d7e7Schristos 	va_end(ap);
2335dd8362aSkre 
2345dd8362aSkre 	trace_puts(s, tracetfile);
2355dd8362aSkre 	free(s);
2365dd8362aSkre 	if (tracetfile->llen == 0)
2375dd8362aSkre 		trace_flush(tracetfile, 0);
2389d15d213Skre }
239d58ec7fcSkre 
240d58ec7fcSkre 
2419d15d213Skre void
2429d15d213Skre trputs(const char *s)
243d58ec7fcSkre {
2445dd8362aSkre 	if (debug != 1 || !tracetfile)
245d58ec7fcSkre 		return;
2465dd8362aSkre 	trace_puts(s, tracetfile);
2475dd8362aSkre }
2485dd8362aSkre 
2495dd8362aSkre void
2505dd8362aSkre trputc(int c)
2515dd8362aSkre {
2525dd8362aSkre 	if (debug != 1 || !tracetfile)
2535dd8362aSkre 		return;
2545dd8362aSkre 	trace_putc(c, tracetfile);
2555dd8362aSkre }
2565dd8362aSkre 
2575dd8362aSkre void
2585dd8362aSkre showtree(union node *n)
2595dd8362aSkre {
2605dd8362aSkre 	TFILE *fp;
2615dd8362aSkre 
2625dd8362aSkre 	if ((fp = tracetfile) == NULL)
2635dd8362aSkre 		return;
2645dd8362aSkre 
2655dd8362aSkre 	trace_puts("showtree(", fp);
2665dd8362aSkre 		if (n == NULL)
2675dd8362aSkre 			trace_puts("NULL", fp);
2685dd8362aSkre 		else if (n == NEOF)
2695dd8362aSkre 			trace_puts("NEOF", fp);
2705dd8362aSkre 		else
2715dd8362aSkre 			trace("%p", n);
2725dd8362aSkre 	trace_puts(") called\n", fp);
2735dd8362aSkre 	if (n != NULL && n != NEOF)
2745dd8362aSkre 		shtree(n, 1, 1, 1, fp);
2755dd8362aSkre }
2765dd8362aSkre 
2775dd8362aSkre void
2785dd8362aSkre trargs(char **ap)
2795dd8362aSkre {
2805dd8362aSkre 	if (debug != 1 || !tracetfile)
2815dd8362aSkre 		return;
2825dd8362aSkre 	while (*ap) {
2835dd8362aSkre 		trstring(*ap++);
2845dd8362aSkre 		if (*ap)
2855dd8362aSkre 			trace_putc(' ', tracetfile);
2865dd8362aSkre 	}
2876f4f3666Skre 	trace_putc('\n', tracetfile);
2885dd8362aSkre }
2895dd8362aSkre 
29019986c5fSkre void
29119986c5fSkre trargstr(union node *n)
29219986c5fSkre {
29319986c5fSkre 	sharg(n, tracetfile);
29419986c5fSkre }
29519986c5fSkre 
2965dd8362aSkre 
2975dd8362aSkre /*
2985dd8362aSkre  * Beyond here we just have the implementation of all of that
2995dd8362aSkre  */
3005dd8362aSkre 
3015dd8362aSkre 
3025dd8362aSkre inline static int
3035dd8362aSkre trlinelen(TFILE * fp)
3045dd8362aSkre {
3055dd8362aSkre 	return fp->llen;
3065dd8362aSkre }
3075dd8362aSkre 
3085dd8362aSkre static void
3095dd8362aSkre shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp)
3105dd8362aSkre {
3115dd8362aSkre 	struct nodelist *lp;
3125dd8362aSkre 	const char *s;
3135dd8362aSkre 
3145dd8362aSkre 	if (n == NULL) {
3155dd8362aSkre 		if (nl)
3165dd8362aSkre 			trace_putc('\n', fp);
3175dd8362aSkre 		return;
3185dd8362aSkre 	}
3195dd8362aSkre 
3205dd8362aSkre 	indent(ind, fp);
3215dd8362aSkre 	switch (n->type) {
3225dd8362aSkre 	case NSEMI:
3235dd8362aSkre 		s = NULL;
3245dd8362aSkre 		goto binop;
3255dd8362aSkre 	case NAND:
3265dd8362aSkre 		s = " && ";
3275dd8362aSkre 		goto binop;
3285dd8362aSkre 	case NOR:
3295dd8362aSkre 		s = " || ";
3305dd8362aSkre binop:
3315dd8362aSkre 		shtree(n->nbinary.ch1, 0, ilvl, 0, fp);
3325dd8362aSkre 		if (s != NULL)
3335dd8362aSkre 			trace_puts(s, fp);
3345dd8362aSkre 		if (trlinelen(fp) >= TR_STD_WIDTH) {
3355dd8362aSkre 			trace_putc('\n', fp);
3365dd8362aSkre 			indent(ind < 0 ? 2 : ind + 1, fp);
3375dd8362aSkre 		} else if (s == NULL) {
3385dd8362aSkre 			if (fp->lastc != '&')
3395dd8362aSkre 				trace_puts("; ", fp);
3405dd8362aSkre 			else
3415dd8362aSkre 				trace_putc(' ', fp);
3425dd8362aSkre 		}
3435dd8362aSkre 		shtree(n->nbinary.ch2, 0, ilvl, nl, fp);
3445dd8362aSkre 		break;
3455dd8362aSkre 	case NCMD:
3465dd8362aSkre 		shcmd(n, fp);
3475dd8362aSkre 		if (n->ncmd.backgnd)
3485dd8362aSkre 			trace_puts(" &", fp);
3495dd8362aSkre 		if (nl && trlinelen(fp) > 0)
3505dd8362aSkre 			trace_putc('\n', fp);
3515dd8362aSkre 		break;
3525dd8362aSkre 	case NPIPE:
3535dd8362aSkre 		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
3545dd8362aSkre 			shtree(lp->n, 0, ilvl, 0, fp);
3555dd8362aSkre 			if (lp->next) {
3565dd8362aSkre 				trace_puts(" |", fp);
3575dd8362aSkre 				if (trlinelen(fp) >= TR_STD_WIDTH)  {
3585dd8362aSkre 					trace_putc('\n', fp);
3595dd8362aSkre 					indent((ind < 0 ? ilvl : ind) + 1, fp);
3605dd8362aSkre 				} else
3615dd8362aSkre 					trace_putc(' ', fp);
3625dd8362aSkre 			}
3635dd8362aSkre 		}
3645dd8362aSkre 		if (n->npipe.backgnd)
3655dd8362aSkre 			trace_puts(" &", fp);
3665dd8362aSkre 		if (nl || trlinelen(fp) >= TR_STD_WIDTH)
3675dd8362aSkre 			trace_putc('\n', fp);
3685dd8362aSkre 		break;
3695dd8362aSkre 	case NBACKGND:
3705dd8362aSkre 	case NSUBSHELL:
3715dd8362aSkre 		shsubsh(n, fp);
3725dd8362aSkre 		if (n->type == NBACKGND)
3735dd8362aSkre 			trace_puts(" &", fp);
3745dd8362aSkre 		if (nl && trlinelen(fp) > 0)
3755dd8362aSkre 			trace_putc('\n', fp);
3765dd8362aSkre 		break;
3775dd8362aSkre 	case NDEFUN:
3785dd8362aSkre 		trace_puts(n->narg.text, fp);
3795dd8362aSkre 		trace_puts("() {\n", fp);
3805dd8362aSkre 		indent(ind, fp);
3815dd8362aSkre 		shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp);
3825dd8362aSkre 		indent(ind, fp);
3835dd8362aSkre 		trace_puts("}\n", fp);
3845dd8362aSkre 		break;
3855dd8362aSkre 	case NDNOT:
3865dd8362aSkre 		trace_puts("! ", fp);
3875dd8362aSkre 		/* FALLTHROUGH */
3885dd8362aSkre 	case NNOT:
3895dd8362aSkre 		trace_puts("! ", fp);
3905dd8362aSkre 		shtree(n->nnot.com, -1, ilvl, nl, fp);
3915dd8362aSkre 		break;
3925dd8362aSkre 	case NREDIR:
3935dd8362aSkre 		shtree(n->nredir.n, -1, ilvl, 0, fp);
3945dd8362aSkre 		shredir(n->nredir.redirect, fp, n->nredir.n == NULL);
3955dd8362aSkre 		if (nl)
3965dd8362aSkre 			trace_putc('\n', fp);
3975dd8362aSkre 		break;
3985dd8362aSkre 
3995dd8362aSkre 	case NIF:
4005dd8362aSkre 	itsif:
4015dd8362aSkre 		trace_puts("if ", fp);
4025dd8362aSkre 		shtree(n->nif.test, -1, ilvl, 0, fp);
4035dd8362aSkre 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
4045dd8362aSkre 			if (fp->lastc != '&')
4055dd8362aSkre 				trace_puts(" ;", fp);
4065dd8362aSkre 		} else
4075dd8362aSkre 			indent(ilvl, fp);
4085dd8362aSkre 		trace_puts(" then ", fp);
4095dd8362aSkre 		if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
4105dd8362aSkre 			indent(ilvl+1, fp);
4115dd8362aSkre 		shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp);
4125dd8362aSkre 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
4135dd8362aSkre 			if (fp->lastc != '&')
4145dd8362aSkre 				trace_puts(" ;", fp);
4155dd8362aSkre 		} else
4165dd8362aSkre 			indent(ilvl, fp);
4175dd8362aSkre 		if (n->nif.elsepart && n->nif.elsepart->type == NIF) {
4185dd8362aSkre 			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
4195dd8362aSkre 				indent(ilvl, fp);
4205dd8362aSkre 			n = n->nif.elsepart;
4215dd8362aSkre 			trace_puts(" el", fp);
4225dd8362aSkre 			goto itsif;
4235dd8362aSkre 		}
4245dd8362aSkre 		if (n->nif.elsepart) {
4255dd8362aSkre 			if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
4265dd8362aSkre 				indent(ilvl+1, fp);
4275dd8362aSkre 			trace_puts(" else ", fp);
4285dd8362aSkre 			shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp);
4295dd8362aSkre 			if (fp->lastc != '&')
4305dd8362aSkre 				trace_puts(" ;", fp);
4315dd8362aSkre 		}
4325dd8362aSkre 		trace_puts(" fi", fp);
4335dd8362aSkre 		if (nl)
4345dd8362aSkre 			trace_putc('\n', fp);
4355dd8362aSkre 		break;
4365dd8362aSkre 
4375dd8362aSkre 	case NWHILE:
4385dd8362aSkre 		trace_puts("while ", fp);
4395dd8362aSkre 		goto aloop;
4405dd8362aSkre 	case NUNTIL:
4415dd8362aSkre 		trace_puts("until ", fp);
4425dd8362aSkre 	aloop:
4435dd8362aSkre 		shtree(n->nbinary.ch1, -1, ilvl, 0, fp);
4445dd8362aSkre 		if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
4455dd8362aSkre 			if (fp->lastc != '&')
4465dd8362aSkre 				trace_puts(" ;", fp);
4475dd8362aSkre 		} else
4485dd8362aSkre 			trace_putc('\n', fp);
4495dd8362aSkre 		trace_puts(" do ", fp);
4505dd8362aSkre 		shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp);
4515dd8362aSkre 		trace_puts(" done ", fp);
4525dd8362aSkre 		if (nl)
4535dd8362aSkre 			trace_putc('\n', fp);
4545dd8362aSkre 		break;
4555dd8362aSkre 
4565dd8362aSkre 	case NFOR:
4575dd8362aSkre 		trace_puts("for ", fp);
4585dd8362aSkre 		trace_puts(n->nfor.var, fp);
4595dd8362aSkre 		if (n->nfor.args) {
4605dd8362aSkre 			union node *argp;
4615dd8362aSkre 
4625dd8362aSkre 			trace_puts(" in ", fp);
4635dd8362aSkre 			for (argp = n->nfor.args; argp; argp=argp->narg.next) {
4645dd8362aSkre 				sharg(argp, fp);
4655dd8362aSkre 				trace_putc(' ', fp);
4665dd8362aSkre 			}
4675dd8362aSkre 			if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
4685dd8362aSkre 				if (fp->lastc != '&')
4695dd8362aSkre 					trace_putc(';', fp);
4705dd8362aSkre 			} else
4715dd8362aSkre 				trace_putc('\n', fp);
4725dd8362aSkre 		}
4735dd8362aSkre 		trace_puts(" do ", fp);
4745dd8362aSkre 		shtree(n->nfor.body, -1, ilvl + 1, 0, fp);
4755dd8362aSkre 		if (fp->lastc != '&')
4765dd8362aSkre 			trace_putc(';', fp);
4775dd8362aSkre 		trace_puts(" done", fp);
4785dd8362aSkre 		if (nl)
4795dd8362aSkre 			trace_putc('\n', fp);
4805dd8362aSkre 		break;
4815dd8362aSkre 
4825dd8362aSkre 	case NCASE:
4835dd8362aSkre 		trace_puts("case ", fp);
4845dd8362aSkre 		sharg(n->ncase.expr, fp);
4855dd8362aSkre 		trace_puts(" in", fp);
4865dd8362aSkre 		if (nl)
4875dd8362aSkre 			trace_putc('\n', fp);
4885dd8362aSkre 		{
4895dd8362aSkre 			union node *cp;
4905dd8362aSkre 
4915dd8362aSkre 			for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
4925dd8362aSkre 				union node *patp;
4935dd8362aSkre 
4945dd8362aSkre 				if (nl || trlinelen(fp) > TR_STD_WIDTH - 16)
4955dd8362aSkre 					indent(ilvl, fp);
4965dd8362aSkre 				else
4975dd8362aSkre 					trace_putc(' ', fp);
4985dd8362aSkre 				trace_putc('(', fp);
4995dd8362aSkre 				patp = cp->nclist.pattern;
5005dd8362aSkre 				while (patp != NULL) {
5015dd8362aSkre 				    trace_putc(' ', fp);
5025dd8362aSkre 				    sharg(patp, fp);
5035dd8362aSkre 				    trace_putc(' ', fp);
5045dd8362aSkre 				    if ((patp = patp->narg.next) != NULL)
5055dd8362aSkre 					trace_putc('|', fp);
5065dd8362aSkre 				}
5075dd8362aSkre 				trace_putc(')', fp);
5085dd8362aSkre 				if (nl)
5095dd8362aSkre 					indent(ilvl + 1, fp);
5105dd8362aSkre 				else
5115dd8362aSkre 					trace_putc(' ', fp);
5125dd8362aSkre 				shtree(cp->nclist.body, -1, ilvl+2, 0, fp);
5135dd8362aSkre 				if (cp->type == NCLISTCONT)
5145dd8362aSkre 					trace_puts(" ;&", fp);
5155dd8362aSkre 				else
5165dd8362aSkre 					trace_puts(" ;;", fp);
5175dd8362aSkre 				if (nl)
5185dd8362aSkre 					trace_putc('\n', fp);
5195dd8362aSkre 			}
5205dd8362aSkre 		}
5215dd8362aSkre 		if (nl) {
5225dd8362aSkre 			trace_putc('\n', fp);
5235dd8362aSkre 			indent(ind, fp);
5245dd8362aSkre 		} else
5255dd8362aSkre 			trace_putc(' ', fp);
5265dd8362aSkre 		trace_puts("esac", fp);
5275dd8362aSkre 		if (nl)
5285dd8362aSkre 			trace_putc('\n', fp);
5295dd8362aSkre 		break;
5305dd8362aSkre 
5315dd8362aSkre 	default: {
5325dd8362aSkre 			char *str;
5335dd8362aSkre 
5345dd8362aSkre 			asprintf(&str, "<node type %d [%s]>", n->type,
5355dd8362aSkre 			    NODETYPENAME(n->type));
5365dd8362aSkre 			trace_puts(str, fp);
5375dd8362aSkre 			free(str);
5385dd8362aSkre 			if (nl)
5395dd8362aSkre 				trace_putc('\n', fp);
5405dd8362aSkre 		}
5415dd8362aSkre 		break;
5425dd8362aSkre 	}
54361f28255Scgd }
54461f28255Scgd 
54561f28255Scgd 
5464ce0d34aScgd static void
5475dd8362aSkre shcmd(union node *cmd, TFILE *fp)
5485dd8362aSkre {
5495dd8362aSkre 	union node *np;
5505dd8362aSkre 	int first;
5515dd8362aSkre 
5525dd8362aSkre 	first = 1;
5535dd8362aSkre 	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
5545dd8362aSkre 		if (! first)
5555dd8362aSkre 			trace_putc(' ', fp);
5565dd8362aSkre 		sharg(np, fp);
5575dd8362aSkre 		first = 0;
5585dd8362aSkre 	}
5595dd8362aSkre 	shredir(cmd->ncmd.redirect, fp, first);
5605dd8362aSkre }
5615dd8362aSkre 
5625dd8362aSkre static void
5635dd8362aSkre shsubsh(union node *cmd, TFILE *fp)
5645dd8362aSkre {
5655dd8362aSkre 	trace_puts(" ( ", fp);
5665dd8362aSkre 	shtree(cmd->nredir.n, -1, 3, 0, fp);
5675dd8362aSkre 	trace_puts(" ) ", fp);
5685dd8362aSkre 	shredir(cmd->ncmd.redirect, fp, 1);
5695dd8362aSkre }
5705dd8362aSkre 
5715dd8362aSkre static void
5725dd8362aSkre shredir(union node *np, TFILE *fp, int first)
5735dd8362aSkre {
5745dd8362aSkre 	const char *s;
5755dd8362aSkre 	int dftfd;
5765dd8362aSkre 	char buf[106];
5775dd8362aSkre 
5785dd8362aSkre 	for ( ; np ; np = np->nfile.next) {
5795dd8362aSkre 		if (! first)
5805dd8362aSkre 			trace_putc(' ', fp);
5815dd8362aSkre 		switch (np->nfile.type) {
5825dd8362aSkre 			case NTO:	s = ">";  dftfd = 1; break;
5835dd8362aSkre 			case NCLOBBER:	s = ">|"; dftfd = 1; break;
5845dd8362aSkre 			case NAPPEND:	s = ">>"; dftfd = 1; break;
5855dd8362aSkre 			case NTOFD:	s = ">&"; dftfd = 1; break;
5865dd8362aSkre 			case NFROM:	s = "<";  dftfd = 0; break;
5875dd8362aSkre 			case NFROMFD:	s = "<&"; dftfd = 0; break;
5885dd8362aSkre 			case NFROMTO:	s = "<>"; dftfd = 0; break;
5895dd8362aSkre 			case NXHERE:	/* FALLTHROUGH */
5905dd8362aSkre 			case NHERE:	s = "<<"; dftfd = 0; break;
5915dd8362aSkre 			default:   s = "*error*"; dftfd = 0; break;
5925dd8362aSkre 		}
5935dd8362aSkre 		if (np->nfile.fd != dftfd) {
5945dd8362aSkre 			sprintf(buf, "%d", np->nfile.fd);
5955dd8362aSkre 			trace_puts(buf, fp);
5965dd8362aSkre 		}
5975dd8362aSkre 		trace_puts(s, fp);
5985dd8362aSkre 		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
5995dd8362aSkre 			if (np->ndup.vname)
6005dd8362aSkre 				sharg(np->ndup.vname, fp);
6015dd8362aSkre 			else {
6023a41fe18Skre 				if (np->ndup.dupfd < 0)
6033a41fe18Skre 					trace_puts("-", fp);
6043a41fe18Skre 				else {
6055dd8362aSkre 					sprintf(buf, "%d", np->ndup.dupfd);
6065dd8362aSkre 					trace_puts(buf, fp);
6075dd8362aSkre 				}
6083a41fe18Skre 			}
6095dd8362aSkre 		} else
6105dd8362aSkre 		    if (np->nfile.type == NHERE || np->nfile.type == NXHERE) {
6115dd8362aSkre 			if (np->nfile.type == NHERE)
6125dd8362aSkre 				trace_putc('\\', fp);
6135dd8362aSkre 			trace_puts("!!!\n", fp);
6145dd8362aSkre 			s = np->nhere.doc->narg.text;
6155dd8362aSkre 			if (strlen(s) > 100) {
6165dd8362aSkre 				memmove(buf, s, 100);
6175dd8362aSkre 				buf[100] = '\0';
6185dd8362aSkre 				strcat(buf, " ...\n");
6195dd8362aSkre 				s = buf;
6205dd8362aSkre 			}
6215dd8362aSkre 			trace_puts(s, fp);
6225dd8362aSkre 			trace_puts("!!! ", fp);
6235dd8362aSkre 		} else {
6245dd8362aSkre 			sharg(np->nfile.fname, fp);
6255dd8362aSkre 		}
6265dd8362aSkre 		first = 0;
6275dd8362aSkre 	}
6285dd8362aSkre }
6295dd8362aSkre 
6305dd8362aSkre static void
6315dd8362aSkre sharg(union node *arg, TFILE *fp)
6325dd8362aSkre {
6335dd8362aSkre 	char *p, *s;
6345dd8362aSkre 	struct nodelist *bqlist;
6355dd8362aSkre 	int subtype = 0;
6365dd8362aSkre 	int quoted = 0;
6375dd8362aSkre 
6385dd8362aSkre 	if (arg->type != NARG) {
6395dd8362aSkre 		asprintf(&s, "<node type %d> ! NARG\n", arg->type);
6405dd8362aSkre 		trace_puts(s, fp);
6415dd8362aSkre 		abort();	/* no need to free s, better not to */
6425dd8362aSkre 	}
6435dd8362aSkre 
6445dd8362aSkre 	bqlist = arg->narg.backquote;
6455dd8362aSkre 	for (p = arg->narg.text ; *p ; p++) {
6465dd8362aSkre 		switch (*p) {
6475dd8362aSkre 		case CTLESC:
6483e2349baSkre 			if (BASESYNTAX[(int)p[1]] != CCTL)
6495dd8362aSkre 				trace_putc('\\', fp);
6505dd8362aSkre 			trace_putc(*++p, fp);
6515dd8362aSkre 			break;
6525dd8362aSkre 
653727a69dcSkre 		case CTLNONL:
654727a69dcSkre 			trace_putc('\\', fp);
655727a69dcSkre 			trace_putc('\n', fp);
656727a69dcSkre 			break;
657727a69dcSkre 
6585dd8362aSkre 		case CTLVAR:
6595dd8362aSkre 			subtype = *++p;
6605dd8362aSkre 			if (!quoted != !(subtype & VSQUOTE))
6615dd8362aSkre 				trace_putc('"', fp);
6625dd8362aSkre 			trace_putc('$', fp);
663727a69dcSkre 			trace_putc('{', fp);	/*}*/
6645dd8362aSkre 			if ((subtype & VSTYPE) == VSLENGTH)
6655dd8362aSkre 				trace_putc('#', fp);
6665dd8362aSkre 			if (subtype & VSLINENO)
6675dd8362aSkre 				trace_puts("LINENO=", fp);
6685dd8362aSkre 
6695dd8362aSkre 			while (*++p != '=')
6705dd8362aSkre 				trace_putc(*p, fp);
6715dd8362aSkre 
6725dd8362aSkre 			if (subtype & VSNUL)
6735dd8362aSkre 				trace_putc(':', fp);
6745dd8362aSkre 
6755dd8362aSkre 			switch (subtype & VSTYPE) {
6765dd8362aSkre 			case VSNORMAL:
6775dd8362aSkre 				/* { */
6785dd8362aSkre 				trace_putc('}', fp);
6795dd8362aSkre 				if (!quoted != !(subtype & VSQUOTE))
6805dd8362aSkre 					trace_putc('"', fp);
6815dd8362aSkre 				break;
6825dd8362aSkre 			case VSMINUS:
6835dd8362aSkre 				trace_putc('-', fp);
6845dd8362aSkre 				break;
6855dd8362aSkre 			case VSPLUS:
6865dd8362aSkre 				trace_putc('+', fp);
6875dd8362aSkre 				break;
6885dd8362aSkre 			case VSQUESTION:
6895dd8362aSkre 				trace_putc('?', fp);
6905dd8362aSkre 				break;
6915dd8362aSkre 			case VSASSIGN:
6925dd8362aSkre 				trace_putc('=', fp);
6935dd8362aSkre 				break;
6945dd8362aSkre 			case VSTRIMLEFTMAX:
6955dd8362aSkre 				trace_putc('#', fp);
6965dd8362aSkre 				/* FALLTHROUGH */
6975dd8362aSkre 			case VSTRIMLEFT:
6985dd8362aSkre 				trace_putc('#', fp);
6995dd8362aSkre 				break;
7005dd8362aSkre 			case VSTRIMRIGHTMAX:
7015dd8362aSkre 				trace_putc('%', fp);
7025dd8362aSkre 				/* FALLTHROUGH */
7035dd8362aSkre 			case VSTRIMRIGHT:
7045dd8362aSkre 				trace_putc('%', fp);
7055dd8362aSkre 				break;
7065dd8362aSkre 			case VSLENGTH:
7075dd8362aSkre 				break;
7085dd8362aSkre 			default: {
7095dd8362aSkre 					char str[32];
7105dd8362aSkre 
7115dd8362aSkre 					snprintf(str, sizeof str,
7125dd8362aSkre 					    "<subtype %d>", subtype);
7135dd8362aSkre 					trace_puts(str, fp);
7145dd8362aSkre 				}
7155dd8362aSkre 				break;
7165dd8362aSkre 			}
7175dd8362aSkre 			break;
7185dd8362aSkre 		case CTLENDVAR:
7195dd8362aSkre 			/* { */
7205dd8362aSkre 			trace_putc('}', fp);
7215dd8362aSkre 			if (!quoted != !(subtype & VSQUOTE))
7225dd8362aSkre 				trace_putc('"', fp);
7235dd8362aSkre 			subtype = 0;
7245dd8362aSkre 			break;
7255dd8362aSkre 
7265dd8362aSkre 		case CTLBACKQ|CTLQUOTE:
7275dd8362aSkre 			if (!quoted)
7285dd8362aSkre 				trace_putc('"', fp);
7295dd8362aSkre 			/* FALLTHRU */
7305dd8362aSkre 		case CTLBACKQ:
7315dd8362aSkre 			trace_putc('$', fp);
7325dd8362aSkre 			trace_putc('(', fp);
7335dd8362aSkre 			if (bqlist) {
7345dd8362aSkre 				shtree(bqlist->n, -1, 3, 0, fp);
7355dd8362aSkre 				bqlist = bqlist->next;
7365dd8362aSkre 			} else
7375dd8362aSkre 				trace_puts("???", fp);
7385dd8362aSkre 			trace_putc(')', fp);
7395dd8362aSkre 			if (!quoted && *p == (CTLBACKQ|CTLQUOTE))
7405dd8362aSkre 				trace_putc('"', fp);
7415dd8362aSkre 			break;
7425dd8362aSkre 
7435dd8362aSkre 		case CTLQUOTEMARK:
7445dd8362aSkre 			if (subtype != 0 || !quoted) {
7455dd8362aSkre 				trace_putc('"', fp);
7465dd8362aSkre 				quoted++;
7475dd8362aSkre 			}
7485dd8362aSkre 			break;
7495dd8362aSkre 		case CTLQUOTEEND:
7505dd8362aSkre 			trace_putc('"', fp);
7515dd8362aSkre 			quoted--;
7525dd8362aSkre 			break;
7535dd8362aSkre 		case CTLARI:
754727a69dcSkre 			if (*p == ' ')
755727a69dcSkre 				p++;
7565dd8362aSkre 			trace_puts("$(( ", fp);
7575dd8362aSkre 			break;
7585dd8362aSkre 		case CTLENDARI:
7595dd8362aSkre 			trace_puts(" ))", fp);
7605dd8362aSkre 			break;
7615dd8362aSkre 
7625dd8362aSkre 		default:
7635dd8362aSkre 			if (*p == '$')
7645dd8362aSkre 				trace_putc('\\', fp);
7655dd8362aSkre 			trace_putc(*p, fp);
7665dd8362aSkre 			break;
7675dd8362aSkre 		}
7685dd8362aSkre 	}
7695dd8362aSkre 	if (quoted)
7705dd8362aSkre 		trace_putc('"', fp);
7715dd8362aSkre }
7725dd8362aSkre 
7735dd8362aSkre 
7745dd8362aSkre static void
7755dd8362aSkre indent(int amount, TFILE *fp)
7765dd8362aSkre {
7775dd8362aSkre 	int i;
7785dd8362aSkre 
7795dd8362aSkre 	if (amount <= 0)
7805dd8362aSkre 		return;
7815dd8362aSkre 
7825dd8362aSkre 	amount <<= 2;	/* indent slots -> chars */
7835dd8362aSkre 
7845dd8362aSkre 	i = trlinelen(fp);
7855dd8362aSkre 	fp->supr = SUP_NL;
7865dd8362aSkre 	if (i > amount) {
7875dd8362aSkre 		trace_putc('\n', fp);
7885dd8362aSkre 		i = 0;
7895dd8362aSkre 	}
7905dd8362aSkre 	fp->supr = 0;
7915dd8362aSkre 	for (; i < amount - 7 ; i++) {
7925dd8362aSkre 		trace_putc('\t', fp);
7935dd8362aSkre 		i |= 7;
7945dd8362aSkre 	}
7955dd8362aSkre 	while (i < amount) {
7965dd8362aSkre 		trace_putc(' ', fp);
7975dd8362aSkre 		i++;
7985dd8362aSkre 	}
7995dd8362aSkre 	fp->supr = SUP_WSP;
8005dd8362aSkre }
8015dd8362aSkre 
8025dd8362aSkre static void
8035dd8362aSkre trace_putc(char c, TFILE *fp)
80461f28255Scgd {
80548250187Stls 	char *p;
8065dd8362aSkre 
8075dd8362aSkre 	if (c == '\0')
8085dd8362aSkre 		return;
8095dd8362aSkre 	if (debug == 0 || fp == NULL)
8105dd8362aSkre 		return;
8115dd8362aSkre 
8125dd8362aSkre 	if (fp->llen == 0) {
8135dd8362aSkre 		if (fp->blen != 0)
8145dd8362aSkre 			abort();
8155dd8362aSkre 
8165dd8362aSkre 		if ((fp->supr & SUP_NL) && c == '\n')
8175dd8362aSkre 			return;
8185dd8362aSkre 		if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ')
8195dd8362aSkre 			return;
8205dd8362aSkre 		if ((fp->supr & SUP_WSP) && c == '\t')
8215dd8362aSkre 			return;
8225dd8362aSkre 
8235dd8362aSkre 		if (fp->nxtiov >= TR_IOVECS - 1)	/* should be rare */
8245dd8362aSkre 			trace_flush(fp, 0);
8255dd8362aSkre 
8265dd8362aSkre 		p = trace_id(fp);
8275dd8362aSkre 		if (p != NULL) {
8285dd8362aSkre 			fp->lines[fp->nxtiov].iov_base = p;
8295dd8362aSkre 			fp->lines[fp->nxtiov].iov_len = strlen(p);
8305dd8362aSkre 			fp->nxtiov++;
8315dd8362aSkre 		}
8325dd8362aSkre 	} else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) {
8335dd8362aSkre 		fp->blen = 0;
8345dd8362aSkre 		if (++fp->nxtiov >= TR_IOVECS)
8355dd8362aSkre 			trace_flush(fp, 0);
8365dd8362aSkre 	}
8375dd8362aSkre 
8385dd8362aSkre 	if (fp->lines[fp->nxtiov].iov_len == 0) {
8395dd8362aSkre 		p = (char *)malloc(2 * TR_STD_WIDTH);
8405dd8362aSkre 		if (p == NULL) {
8415dd8362aSkre 			trace_flush(fp, 1);
8425dd8362aSkre 			debug = 0;
8435dd8362aSkre 			return;
8445dd8362aSkre 		}
8455dd8362aSkre 		*p = '\0';
8465dd8362aSkre 		fp->lines[fp->nxtiov].iov_base = p;
8475dd8362aSkre 		fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH;
8485dd8362aSkre 		fp->blen = 0;
8495dd8362aSkre 	}
8505dd8362aSkre 
8515dd8362aSkre 	p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++;
8525dd8362aSkre 	*p++ = c;
8535dd8362aSkre 	*p = 0;
8545dd8362aSkre 
8555dd8362aSkre 	if (c != ' ' && c != '\t' && c != '\n') {
8565dd8362aSkre 		fp->lastc = c;
8575dd8362aSkre 		fp->supr = 0;
8585dd8362aSkre 	}
8595dd8362aSkre 
8605dd8362aSkre 	if (c == '\n') {
8615dd8362aSkre 		fp->lines[fp->nxtiov++].iov_len = fp->blen;
8625dd8362aSkre 		fp->blen = 0;
8635dd8362aSkre 		fp->llen = 0;
8645dd8362aSkre 		fp->supr |= SUP_NL;
8655dd8362aSkre 		return;
8665dd8362aSkre 	}
8675dd8362aSkre 
8685dd8362aSkre 	if (c == '\t')
8695dd8362aSkre 		fp->llen |=  7;
8705dd8362aSkre 	fp->llen++;
8715dd8362aSkre }
8725dd8362aSkre 
8735dd8362aSkre void
8745dd8362aSkre trace_flush(TFILE *fp, int all)
8755dd8362aSkre {
8765dd8362aSkre 	int niov, i;
8775dd8362aSkre 	ssize_t written;
8785dd8362aSkre 
8795dd8362aSkre 	niov = fp->nxtiov;
8805dd8362aSkre 	if (all && fp->blen > 0) {
8815dd8362aSkre 		fp->lines[niov].iov_len = fp->blen;
8825dd8362aSkre 		fp->blen = 0;
8835dd8362aSkre 		fp->llen = 0;
8845dd8362aSkre 		niov++;
8855dd8362aSkre 	}
8865dd8362aSkre 	if (niov == 0)
8875dd8362aSkre 		return;
8885dd8362aSkre 	if (fp->blen > 0 && --niov == 0)
8895dd8362aSkre 		return;
8905dd8362aSkre 	written = writev(fp->tfd, fp->lines, niov);
8915dd8362aSkre 	for (i = 0; i < niov; i++) {
8925dd8362aSkre 		free(fp->lines[i].iov_base);
8935dd8362aSkre 		fp->lines[i].iov_base = NULL;
8945dd8362aSkre 		fp->lines[i].iov_len = 0;
8955dd8362aSkre 	}
8965dd8362aSkre 	if (written == -1) {
8975dd8362aSkre 		if (fp->blen > 0) {
8985dd8362aSkre 			free(fp->lines[niov].iov_base);
8995dd8362aSkre 			fp->lines[niov].iov_base = NULL;
9005dd8362aSkre 			fp->lines[niov].iov_len = 0;
9015dd8362aSkre 		}
9025dd8362aSkre 		debug = 0;
9035dd8362aSkre 		fp->blen = 0;
9045dd8362aSkre 		fp->llen = 0;
9055dd8362aSkre 		return;
9065dd8362aSkre 	}
9075dd8362aSkre 	if (fp->blen > 0) {
9085dd8362aSkre 		fp->lines[0].iov_base = fp->lines[niov].iov_base;
9095dd8362aSkre 		fp->lines[0].iov_len = fp->lines[niov].iov_len;
9105dd8362aSkre 		fp->lines[niov].iov_base = NULL;
9115dd8362aSkre 		fp->lines[niov].iov_len = 0;
9125dd8362aSkre 	}
9135dd8362aSkre 	fp->nxtiov = 0;
9145dd8362aSkre }
9155dd8362aSkre 
9165dd8362aSkre void
9175dd8362aSkre trace_puts(const char *s, TFILE *fp)
9185dd8362aSkre {
91961f28255Scgd 	char c;
92061f28255Scgd 
9215dd8362aSkre 	while ((c = *s++) != '\0')
9225dd8362aSkre 		trace_putc(c, fp);
9235dd8362aSkre }
9245dd8362aSkre 
9255dd8362aSkre inline static char *
9265dd8362aSkre trace_id(TFILE *tf)
9275dd8362aSkre {
9285dd8362aSkre 	int i;
9295dd8362aSkre 	char indent[16];
9305dd8362aSkre 	char *p;
9318ffd1099Skre 	int lno;
9320ed8885aSkre 	char c;
9335dd8362aSkre 
9345dd8362aSkre 	if (DFlags & DBG_NEST) {
9355f9a3bbeSkre 		if ((unsigned)ShNest >= sizeof indent - 1) {
9365f9a3bbeSkre 			(void) snprintf(indent, sizeof indent,
9375f9a3bbeSkre 			    "### %*d ###", (int)(sizeof indent) - 9, ShNest);
9385f9a3bbeSkre 			p = strchr(indent, '\0');
9395f9a3bbeSkre 		} else {
9405dd8362aSkre 			p = indent;
9415dd8362aSkre 			for (i = 0; i < 6; i++)
9425dd8362aSkre 				*p++ = (i < ShNest) ? '#' : ' ';
9435dd8362aSkre 			while (i++ < ShNest && p < &indent[sizeof indent - 1])
9445dd8362aSkre 				*p++ = '#';
9455dd8362aSkre 			*p = '\0';
9465f9a3bbeSkre 		}
9475dd8362aSkre 	} else
9485dd8362aSkre 		indent[0] = '\0';
9495dd8362aSkre 
950727a69dcSkre 	/*
951727a69dcSkre 	 * If we are in the parser, then plinno is the current line
952727a69dcSkre 	 * number being processed (parser line no).
953727a69dcSkre 	 * If we are elsewhere, then line_number gives the source
954727a69dcSkre 	 * line of whatever we are currently doing (close enough.)
955727a69dcSkre 	 */
956727a69dcSkre 	if (parsing)
957727a69dcSkre 		lno = plinno;
958727a69dcSkre 	else
959727a69dcSkre 		lno = line_number;
9608ffd1099Skre 
9610ed8885aSkre 	c = ((i = getpid()) == tf->pid) ? ':' : '=';
9620ed8885aSkre 
9635dd8362aSkre 	if (DFlags & DBG_PID) {
9648ffd1099Skre 		if (DFlags & DBG_LINE)
9650ed8885aSkre 			(void) asprintf(&p, "%5d%c%s\t%4d%c@\t", i, c,
9660ed8885aSkre 			    indent, lno, parsing?'-':'+');
9678ffd1099Skre 		else
9680ed8885aSkre 			(void) asprintf(&p, "%5d%c%s\t", i, c, indent);
9695dd8362aSkre 		return p;
9705dd8362aSkre 	} else if (DFlags & DBG_NEST) {
9718ffd1099Skre 		if (DFlags & DBG_LINE)
9720ed8885aSkre 			(void) asprintf(&p, "%c%s\t%4d%c@\t", c, indent, lno,
9730ed8885aSkre 			    parsing?'-':'+');
9748ffd1099Skre 		else
9750ed8885aSkre 			(void) asprintf(&p, "%c%s\t", c, indent);
9765dd8362aSkre 		return p;
9778ffd1099Skre 	} else if (DFlags & DBG_LINE) {
9780ed8885aSkre 		(void) asprintf(&p, "%c%4d%c@\t", c, lno, parsing?'-':'+');
9798ffd1099Skre 		return p;
9805dd8362aSkre 	}
9815dd8362aSkre 	return NULL;
9825dd8362aSkre }
9835dd8362aSkre 
9845dd8362aSkre /*
9855dd8362aSkre  * Used only from trargs(), which itself is used only to print
9865dd8362aSkre  * arg lists (argv[]) either that passed into this shell, or
9875dd8362aSkre  * the arg list about to be given to some other command (incl
9885dd8362aSkre  * builtin, and function) as their argv[].  If any of the CTL*
9895dd8362aSkre  * chars seem to appear, they really should be just treated as data,
9905dd8362aSkre  * not special...   But this is just debug, so, who cares!
9915dd8362aSkre  */
9925dd8362aSkre static void
9935dd8362aSkre trstring(const char *s)
9945dd8362aSkre {
9955dd8362aSkre 	const char *p;
9965dd8362aSkre 	char c;
9975dd8362aSkre 	TFILE *fp;
9985dd8362aSkre 
9995dd8362aSkre 	if (debug != 1 || !tracetfile)
100061f28255Scgd 		return;
10015dd8362aSkre 	fp = tracetfile;
10025dd8362aSkre 	trace_putc('"', fp);
100361f28255Scgd 	for (p = s ; *p ; p++) {
100461f28255Scgd 		switch (*p) {
100561f28255Scgd 		case '\n':  c = 'n';  goto backslash;
100661f28255Scgd 		case '\t':  c = 't';  goto backslash;
100761f28255Scgd 		case '\r':  c = 'r';  goto backslash;
100861f28255Scgd 		case '"':  c = '"';  goto backslash;
100961f28255Scgd 		case '\\':  c = '\\';  goto backslash;
101061f28255Scgd 		case CTLESC:  c = 'e';  goto backslash;
101161f28255Scgd 		case CTLVAR:  c = 'v';  goto backslash;
101261f28255Scgd 		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
101361f28255Scgd 		case CTLBACKQ:  c = 'q';  goto backslash;
101461f28255Scgd 		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
10155dd8362aSkre backslash:		trace_putc('\\', fp);
10165dd8362aSkre 			trace_putc(c, fp);
101761f28255Scgd 			break;
101861f28255Scgd 		default:
101961f28255Scgd 			if (*p >= ' ' && *p <= '~')
10205dd8362aSkre 				trace_putc(*p, fp);
102161f28255Scgd 			else {
10225dd8362aSkre 				trace_putc('\\', fp);
10235dd8362aSkre 				trace_putc(*p >> 6 & 03, fp);
10245dd8362aSkre 				trace_putc(*p >> 3 & 07, fp);
10255dd8362aSkre 				trace_putc(*p & 07, fp);
102661f28255Scgd 			}
102761f28255Scgd 			break;
102861f28255Scgd 		}
102961f28255Scgd 	}
10305dd8362aSkre 	trace_putc('"', fp);
10319d15d213Skre }
10329d15d213Skre 
10335dd8362aSkre /*
10345dd8362aSkre  * deal with the user "accidentally" picking our fd to use.
10355dd8362aSkre  */
10365dd8362aSkre static void
10375dd8362aSkre trace_fd_swap(int from, int to)
10389d15d213Skre {
10395dd8362aSkre 	if (tracetfile == NULL || from == to)
10409d15d213Skre 		return;
104161f28255Scgd 
10425dd8362aSkre 	tracetfile->tfd = to;
10439d15d213Skre 
10445dd8362aSkre 	/*
10455dd8362aSkre 	 * This is just so histedit has a stdio FILE* to use.
10465dd8362aSkre 	 */
10479d15d213Skre 	if (tracefile)
10485dd8362aSkre 		fclose(tracefile);
10495dd8362aSkre 	tracefile = fdopen(to, "a");
10505dd8362aSkre 	if (tracefile)
10519d15d213Skre 		setlinebuf(tracefile);
10529d15d213Skre }
10535dd8362aSkre 
1054ab36694aSkre 
1055ab36694aSkre static struct debug_flag {
1056ab36694aSkre 	char		label;
1057ab36694aSkre 	uint64_t	flag;
1058ab36694aSkre } debug_flags[] = {
1059ab36694aSkre 	{ 'a',	DBG_ARITH	},	/* arithmetic ( $(( )) ) */
1060ab36694aSkre 	{ 'c',	DBG_CMDS	},	/* command searching, ... */
1061ab36694aSkre 	{ 'e',	DBG_EVAL	},	/* evaluation of the parse tree */
1062ab36694aSkre 	{ 'f',	DBG_REDIR	},	/* file descriptors & redirections */
1063bcacfd9aSkre 	{ 'g',	DBG_MATCH	},	/* pattern matching (glob) */
1064ab36694aSkre 	{ 'h',	DBG_HISTORY	},	/* history & cmd line editing */
1065ab36694aSkre 	{ 'i',	DBG_INPUT	},	/* shell input routines */
1066ab36694aSkre 	{ 'j',	DBG_JOBS	},	/* job control, structures */
1067a672c6e1Skre 	{ 'l',	DBG_LEXER	},	/* lexical analysis */
1068ab36694aSkre 	{ 'm',	DBG_MEM		},	/* memory management */
1069ab36694aSkre 	{ 'o',	DBG_OUTPUT	},	/* output routines */
1070ab36694aSkre 	{ 'p',	DBG_PROCS	},	/* process management, fork, ... */
1071ab36694aSkre 	{ 'r',	DBG_PARSE	},	/* parser, lexer, ... tree building */
1072ab36694aSkre 	{ 's',	DBG_SIG		},	/* signals and everything related */
1073ab36694aSkre 	{ 't',	DBG_TRAP	},	/* traps & signals */
1074ab36694aSkre 	{ 'v',	DBG_VARS	},	/* variables and parameters */
1075ab36694aSkre 	{ 'w',	DBG_WAIT	},	/* waits for processes to finish */
1076ab36694aSkre 	{ 'x',	DBG_EXPAND	},	/* word expansion ${} $() $(( )) */
1077ab36694aSkre 	{ 'z',	DBG_ERRS	},	/* error control, jumps, cleanup */
1078ab36694aSkre 
1079ab36694aSkre 	{ '0',	DBG_U0		},	/* ad-hoc temp debug flag #0 */
1080ab36694aSkre 	{ '1',	DBG_U1		},	/* ad-hoc temp debug flag #1 */
1081ab36694aSkre 	{ '2',	DBG_U2		},	/* ad-hoc temp debug flag #2 */
1082f2dc4639Skre 	{ '3',	DBG_U3		},	/* ad-hoc temp debug flag #3 */
1083ab36694aSkre 
10848ffd1099Skre 	{ '@',	DBG_LINE	},	/* prefix trace lines with line# */
1085ab36694aSkre 	{ '$',	DBG_PID		},	/* prefix trace lines with sh pid */
1086ab36694aSkre 	{ '^',	DBG_NEST	},	/* show shell nesting level */
1087ab36694aSkre 
1088a672c6e1Skre 			/* alpha options only - but not DBG_LEXER */
1089f2dc4639Skre 	{ '_',	DBG_PARSE | DBG_EVAL | DBG_EXPAND | DBG_JOBS | DBG_SIG |
1090ab36694aSkre 		    DBG_PROCS | DBG_REDIR | DBG_CMDS | DBG_ERRS |
1091f2dc4639Skre 		    DBG_WAIT | DBG_TRAP | DBG_VARS | DBG_MEM | DBG_MATCH |
1092ab36694aSkre 		    DBG_INPUT | DBG_OUTPUT | DBG_ARITH | DBG_HISTORY },
1093ab36694aSkre 
1094ab36694aSkre    /*   { '*',	DBG_ALLVERBOSE	}, 	   is handled in the code */
1095ab36694aSkre 
1096f2dc4639Skre 	{ '#',	DBG_U0 | DBG_U1 | DBG_U2 | DBG_U3 },
1097ab36694aSkre 
1098ab36694aSkre 	{ 0,	0		}
1099ab36694aSkre };
1100ab36694aSkre 
1101ab36694aSkre void
1102ab36694aSkre set_debug(const char * flags, int on)
1103ab36694aSkre {
1104ab36694aSkre 	char f;
1105ab36694aSkre 	struct debug_flag *df;
1106ab36694aSkre 	int verbose;
1107ab36694aSkre 
1108ab36694aSkre 	while ((f = *flags++) != '\0') {
1109ab36694aSkre 		verbose = 0;
1110ab36694aSkre 		if (is_upper(f)) {
1111ab36694aSkre 			verbose = 1;
1112ab36694aSkre 			f += 'a' - 'A';
1113ab36694aSkre 		}
1114ab36694aSkre 		if (f == '*')
1115ab36694aSkre 			f = '_', verbose = 1;
1116ab36694aSkre 		if (f == '+') {
1117ab36694aSkre 			if (*flags == '+')
1118ab36694aSkre 				flags++, verbose=1;
1119ab36694aSkre 		}
1120ab36694aSkre 
1121ab36694aSkre 		/*
1122ab36694aSkre 		 * Note: turning on any debug option also enables DBG_ALWAYS
1123ab36694aSkre 		 * turning on any verbose option also enables DBG_VERBOSE
1124ab36694aSkre 		 * Once enabled, those flags cannot be disabled.
1125ab36694aSkre 		 * (tracing can still be turned off with "set +o debug")
1126ab36694aSkre 		 */
1127ab36694aSkre 		for (df = debug_flags; df->label != '\0'; df++) {
1128ab36694aSkre 			if (f == '+' || df->label == f) {
1129ab36694aSkre 				if (on) {
1130ab36694aSkre 					DFlags |= DBG_ALWAYS | df->flag;
1131ab36694aSkre 					if (verbose)
1132ab36694aSkre 					    DFlags |= DBG_VERBOSE |
1133ab36694aSkre 						(df->flag << DBG_VBOSE_SHIFT);
1134ab36694aSkre 				} else {
1135ab36694aSkre 					DFlags &= ~(df->flag<<DBG_VBOSE_SHIFT);
1136ab36694aSkre 					if (!verbose)
1137ab36694aSkre 						DFlags &= ~df->flag;
1138ab36694aSkre 				}
1139ab36694aSkre 			}
1140ab36694aSkre 		}
1141ab36694aSkre 	}
1142ab36694aSkre }
1143ab36694aSkre 
1144ab36694aSkre 
1145ab36694aSkre int
1146ab36694aSkre debugcmd(int argc, char **argv)
1147ab36694aSkre {
1148ab36694aSkre 	if (argc == 1) {
1149ab36694aSkre 		struct debug_flag *df;
1150ab36694aSkre 
11515f9a3bbeSkre 		out1fmt("Debug: %sabled.  Flags: ", debug ? "en" : "dis");
1152ab36694aSkre 		for (df = debug_flags; df->label != '\0'; df++) {
1153ab36694aSkre 			if (df->flag & (df->flag - 1))
1154ab36694aSkre 				continue;
1155ab36694aSkre 			if (is_alpha(df->label) &&
1156ab36694aSkre 			    (df->flag << DBG_VBOSE_SHIFT) & DFlags)
1157ab36694aSkre 				out1c(df->label - ('a' - 'A'));
1158ab36694aSkre 			else if (df->flag & DFlags)
1159ab36694aSkre 				out1c(df->label);
1160ab36694aSkre 		}
1161ab36694aSkre 		out1c('\n');
1162ab36694aSkre 		return 0;
1163ab36694aSkre 	}
1164ab36694aSkre 
1165ab36694aSkre 	while (*++argv) {
1166ab36694aSkre 		if (**argv == '-')
1167ab36694aSkre 			set_debug(*argv + 1, 1);
1168ab36694aSkre 		else if (**argv == '+')
1169ab36694aSkre 			set_debug(*argv + 1, 0);
1170ab36694aSkre 		else
1171ab36694aSkre 			return 1;
1172ab36694aSkre 	}
1173ab36694aSkre 	return 0;
1174ab36694aSkre }
1175ab36694aSkre 
117668b1dceaSchristos #endif /* DEBUG */
1177