Module Name: src
Committed By: kre
Date: Sun Nov 19 03:23:01 UTC 2017
Modified Files:
src/bin/sh: eval.c option.list options.c output.c output.h sh.1 var.c
Log Message:
Implement the -X option - an apparent variant of -x which sends all trace
output to the stderr which existed when the -X option was (last) enabled.
It also enables tracing by enabling -x (and when reset, +X, also resets
the 'x' flag (+x)). Note that it is still -x/+x which actually
enables/disables the trace output. Hence "apparent variant" - what -X
actually does (aside from setting -x) is just to lock the trace output,
rather than having it follow wherever stderr is later redirected.
To generate a diff of this commit:
cvs rdiff -u -r1.152 -r1.153 src/bin/sh/eval.c
cvs rdiff -u -r1.6 -r1.7 src/bin/sh/option.list
cvs rdiff -u -r1.50 -r1.51 src/bin/sh/options.c
cvs rdiff -u -r1.38 -r1.39 src/bin/sh/output.c
cvs rdiff -u -r1.25 -r1.26 src/bin/sh/output.h
cvs rdiff -u -r1.173 -r1.174 src/bin/sh/sh.1
cvs rdiff -u -r1.68 -r1.69 src/bin/sh/var.c
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.152 src/bin/sh/eval.c:1.153
--- src/bin/sh/eval.c:1.152 Fri Sep 29 17:53:57 2017
+++ src/bin/sh/eval.c Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: eval.c,v 1.152 2017/09/29 17:53:57 kre Exp $ */
+/* $NetBSD: eval.c,v 1.153 2017/11/19 03:23:01 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.152 2017/09/29 17:53:57 kre Exp $");
+__RCSID("$NetBSD: eval.c,v 1.153 2017/11/19 03:23:01 kre Exp $");
#endif
#endif /* not lint */
@@ -279,20 +279,20 @@ evaltree(union node *n, int flags)
if (xflag && n->nredir.redirect) {
union node *rn;
- out2str(expandstr(ps4val(), line_number));
- out2str("using redirections:");
+ outxstr(expandstr(ps4val(), line_number));
+ outxstr("using redirections:");
for (rn = n->nredir.redirect; rn; rn = rn->nfile.next)
- (void) outredir(&errout, rn, ' ');
- out2str(" do\n");
- flushout(&errout);
+ (void) outredir(outx, rn, ' ');
+ outxstr(" do\n");
+ flushout(outx);
}
redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP);
evaltree(n->nredir.n, flags);
popredir();
if (xflag && n->nredir.redirect) {
- out2str(expandstr(ps4val(), line_number));
- out2str("done\n");
- flushout(&errout);
+ outxstr(expandstr(ps4val(), line_number));
+ outxstr("done\n");
+ flushout(outx);
}
break;
case NSUBSHELL:
@@ -437,13 +437,13 @@ evalfor(union node *n, int flags)
f |= EV_MORE;
if (xflag) {
- out2str(expandstr(ps4val(), line_number));
- out2str("for ");
- out2str(n->nfor.var);
- out2c('=');
- out2shstr(sp->text);
- out2c('\n');
- flushout(&errout);
+ outxstr(expandstr(ps4val(), line_number));
+ outxstr("for ");
+ outxstr(n->nfor.var);
+ outxc('=');
+ outxshstr(sp->text);
+ outxc('\n');
+ flushout(outx);
}
setvar(n->nfor.var, sp->text, 0);
@@ -523,12 +523,12 @@ evalsubshell(union node *n, int flags)
if (xflag && n->nredir.redirect) {
union node *rn;
- out2str(expandstr(ps4val(), line_number));
- out2str("using redirections:");
+ outxstr(expandstr(ps4val(), line_number));
+ outxstr("using redirections:");
for (rn = n->nredir.redirect; rn; rn = rn->nfile.next)
- (void) outredir(&errout, rn, ' ');
- out2str(" do subshell\n");
- flushout(&errout);
+ (void) outredir(outx, rn, ' ');
+ outxstr(" do subshell\n");
+ flushout(outx);
}
INTOFF;
jp = makejob(n, 1);
@@ -543,9 +543,9 @@ evalsubshell(union node *n, int flags)
exitstatus = backgnd ? 0 : waitforjob(jp);
INTON;
if (!backgnd && xflag && n->nredir.redirect) {
- out2str(expandstr(ps4val(), line_number));
- out2str("done subshell\n");
- flushout(&errout);
+ outxstr(expandstr(ps4val(), line_number));
+ outxstr("done subshell\n");
+ flushout(outx);
}
}
@@ -797,7 +797,8 @@ evalcommand(union node *cmd, int flgs, s
vforked = 0;
/* First expand the arguments. */
- CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called\n", cmd, flags));
+ CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags,
+ cmd->ncmd.args ? cmd->ncmd.args->narg.text : ""));
setstackmark(&smark);
back_exitstatus = 0;
@@ -861,12 +862,12 @@ evalcommand(union node *cmd, int flgs, s
char sep = 0;
union node *rn;
- out2str(expandstr(ps4val(), line_number));
+ outxstr(expandstr(ps4val(), line_number));
for (sp = varlist.list ; sp ; sp = sp->next) {
char *p;
if (sep != 0)
- outc(sep, &errout);
+ outxc(sep);
/*
* The "var=" part should not be quoted, regardless
@@ -876,25 +877,25 @@ evalcommand(union node *cmd, int flgs, s
p = strchr(sp->text, '=');
if (p != NULL) {
*p = '\0'; /*XXX*/
- out2shstr(sp->text);
- out2c('=');
+ outxshstr(sp->text);
+ outxc('=');
*p++ = '='; /*XXX*/
} else
p = sp->text;
- out2shstr(p);
+ outxshstr(p);
sep = ' ';
}
for (sp = arglist.list ; sp ; sp = sp->next) {
if (sep != 0)
- outc(sep, &errout);
- out2shstr(sp->text);
+ outxc(sep);
+ outxshstr(sp->text);
sep = ' ';
}
for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next)
- if (outredir(&errout, rn, sep))
+ if (outredir(outx, rn, sep))
sep = ' ';
- outc('\n', &errout);
- flushout(&errout);
+ outxc('\n');
+ flushout(outx);
}
/* Now locate the command. */
Index: src/bin/sh/option.list
diff -u src/bin/sh/option.list:1.6 src/bin/sh/option.list:1.7
--- src/bin/sh/option.list:1.6 Mon Jul 24 14:17:11 2017
+++ src/bin/sh/option.list Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: option.list,v 1.6 2017/07/24 14:17:11 kre Exp $ */
+/* $NetBSD: option.list,v 1.7 2017/11/19 03:23:01 kre Exp $ */
/*
* define the shell's settable options
@@ -66,6 +66,7 @@ qflag quietprofile q # disable -v/-x in
fnline1 local_lineno L on # number lines in funcs starting at 1
promptcmds promptcmds # allow $( ) in PS1 (et al).
pipefail pipefail # pipe exit status
+Xflag Xtrace X # sticky stderr for -x (implies -x)
// editline/history related options ("vi" is standard, 'V' and others are not)
// only one of vi/emacs can be set, hence the "set" definition, value
Index: src/bin/sh/options.c
diff -u src/bin/sh/options.c:1.50 src/bin/sh/options.c:1.51
--- src/bin/sh/options.c:1.50 Mon Jul 24 12:35:37 2017
+++ src/bin/sh/options.c Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: options.c,v 1.50 2017/07/24 12:35:37 kre Exp $ */
+/* $NetBSD: options.c,v 1.51 2017/11/19 03:23:01 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95";
#else
-__RCSID("$NetBSD: options.c,v 1.50 2017/07/24 12:35:37 kre Exp $");
+__RCSID("$NetBSD: options.c,v 1.51 2017/11/19 03:23:01 kre Exp $");
#endif
#endif /* not lint */
@@ -252,10 +252,12 @@ set_opt_val(size_t i, int val)
if (optlist[j].opt_set == flag)
optlist[j].val = 0;
}
+ if (i == _SH_OPT_Xflag)
+ xtracefdsetup(val);
optlist[i].val = val;
#ifdef DEBUG
if (&optlist[i].val == &debug)
- opentrace();
+ opentrace(); /* different "trace" than the -x one... */
#endif
}
@@ -307,6 +309,8 @@ minus_o(char *name, int val)
for (i = 0; i < NOPTS; i++)
if (optlist[i].name && equal(name, optlist[i].name)) {
set_opt_val(i, val);
+ if (i == _SH_OPT_Xflag)
+ set_opt_val(_SH_OPT_xflag, val);
return;
}
error("Illegal option %co %s", "+-"[val], name);
@@ -321,7 +325,9 @@ setoption(int flag, int val)
for (i = 0; i < NOPTS; i++)
if (optlist[i].letter == flag) {
- set_opt_val( i, val );
+ set_opt_val(i, val);
+ if (i == _SH_OPT_Xflag)
+ set_opt_val(_SH_OPT_xflag, val);
return;
}
error("Illegal option %c%c", "+-"[val], flag);
Index: src/bin/sh/output.c
diff -u src/bin/sh/output.c:1.38 src/bin/sh/output.c:1.39
--- src/bin/sh/output.c:1.38 Sun Nov 19 03:22:55 2017
+++ src/bin/sh/output.c Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: output.c,v 1.38 2017/11/19 03:22:55 kre Exp $ */
+/* $NetBSD: output.c,v 1.39 2017/11/19 03:23:01 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95";
#else
-__RCSID("$NetBSD: output.c,v 1.38 2017/11/19 03:22:55 kre Exp $");
+__RCSID("$NetBSD: output.c,v 1.39 2017/11/19 03:23:01 kre Exp $");
#endif
#endif /* not lint */
@@ -67,6 +67,9 @@ __RCSID("$NetBSD: output.c,v 1.38 2017/1
#include "output.h"
#include "memalloc.h"
#include "error.h"
+#include "redir.h"
+#include "options.h"
+#include "show.h"
#define OUTBUFSIZ BUFSIZ
@@ -74,12 +77,14 @@ __RCSID("$NetBSD: output.c,v 1.38 2017/1
#define MEM_OUT -3 /* output to dynamically allocated memory */
-struct output output = {NULL, 0, OUTBUFSIZ, NULL, 1, 0};
-struct output errout = {NULL, 0, 100, NULL, 2, 0};
-struct output memout = {NULL, 0, 0, NULL, MEM_OUT, 0};
+ /* nextc nleft bufsize buf fd flags chain */
+struct output output = {NULL, 0, OUTBUFSIZ, NULL, 1, 0, NULL };
+struct output errout = {NULL, 0, 100, NULL, 2, 0, NULL };
+struct output memout = {NULL, 0, 0, NULL, MEM_OUT, 0, NULL };
struct output *out1 = &output;
struct output *out2 = &errout;
-
+struct output *outx = &errout;
+struct output *outxtop = NULL;
#ifdef mkinit
@@ -128,13 +133,21 @@ out2str(const char *p)
outstr(p, out2);
}
+void
+outxstr(const char *p)
+{
+ outstr(p, outx);
+}
+
void
outstr(const char *p, struct output *file)
{
+ char c = 0;
+
while (*p)
- outc(*p++, file);
- if (file == out2)
+ outc((c = *p++), file);
+ if (file == out2 || (file == outx && c == '\n'))
flushout(file);
}
@@ -145,6 +158,11 @@ out2shstr(const char *p)
outshstr(p, out2);
}
+void
+outxshstr(const char *p)
+{
+ outshstr(p, outx);
+}
/*
* ' is in this list, not because it does not require quoting
@@ -245,6 +263,8 @@ emptyoutbuf(struct output *dest)
dest->nextc = dest->buf;
dest->nleft = dest->bufsize;
INTON;
+ VTRACE(DBG_OUTPUT, ("emptyoutbuf now %d @%p for fd %d\n",
+ dest->nleft, dest->buf, dest->fd));
} else if (dest->fd == MEM_OUT) {
offset = dest->bufsize;
INTOFF;
@@ -274,6 +294,8 @@ flushout(struct output *dest)
if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
return;
+ VTRACE(DBG_OUTPUT, ("flushout fd=%d %zd to write\n", dest->fd,
+ (size_t)(dest->nextc - dest->buf)));
if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
dest->flags |= OUTPUT_ERR;
dest->nextc = dest->buf;
@@ -606,3 +628,129 @@ xioctl(int fd, unsigned long request, ch
while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
return i;
}
+
+static void
+xtrace_fd_swap(int from, int to)
+{
+ struct output *o = outxtop;
+
+ while (o != NULL) {
+ if (o->fd == from)
+ o->fd = to;
+ o = o->chain;
+ }
+}
+
+/*
+ * the -X flag is to be set or reset (not necessarily changed)
+ * Do what is needed to make tracing go to where it should
+ *
+ * Note: Xflag has not yet been altered, "on" indicates what it will become
+ */
+
+void
+xtracefdsetup(int on)
+{
+ if (!on) {
+ flushout(outx);
+
+ if (Xflag != 1) /* Was already +X */
+ return; /* so nothing to do */
+
+ outx = out2;
+ CTRACE(DBG_OUTPUT, ("Tracing to stderr\n"));
+ return;
+ }
+
+ if (Xflag == 1) { /* was already set */
+ /*
+ * This is a change of output file only
+ * Just close the current one, and reuse the struct output
+ */
+ if (!(outx->flags & OUTPUT_CLONE))
+ sh_close(outx->fd);
+ } else if (outxtop == NULL) {
+ /*
+ * -X is just turning on, for the forst time,
+ * need a new output struct to become outx
+ */
+ xtrace_clone(1);
+ } else
+ outx = outxtop;
+
+ if (outx != out2) {
+ outx->flags &= ~OUTPUT_CLONE;
+ outx->fd = to_upper_fd(dup(out2->fd));
+ register_sh_fd(outx->fd, xtrace_fd_swap);
+ }
+
+ CTRACE(DBG_OUTPUT, ("Tracing now to fd %d (from %d)\n",
+ outx->fd, out2->fd));
+}
+
+void
+xtrace_clone(int new)
+{
+ struct output *o;
+
+ CTRACE(DBG_OUTPUT,
+ ("xtrace_clone(%d): %sfd=%d buf=%p nleft=%d flags=%x ",
+ new, (outx == out2 ? "out2: " : ""),
+ outx->fd, outx->buf, outx->nleft, outx->flags));
+
+ flushout(outx);
+
+ if (!new && outxtop == NULL && Xflag == 0) {
+ /* following stderr, nothing to save */
+ CTRACE(DBG_OUTPUT, ("+X\n"));
+ return;
+ }
+
+ o = ckmalloc(sizeof(*o));
+ o->fd = outx->fd;
+ o->flags = OUTPUT_CLONE;
+ o->bufsize = outx->bufsize;
+ o->nleft = 0;
+ o->buf = NULL;
+ o->nextc = NULL;
+ o->chain = outxtop;
+ outx = o;
+ outxtop = o;
+
+ CTRACE(DBG_OUTPUT, ("-> fd=%d flags=%x[CLONE]\n", outx->fd, o->flags));
+}
+
+void
+xtrace_pop(void)
+{
+ struct output *o;
+
+ CTRACE(DBG_OUTPUT, ("trace_pop: fd=%d buf=%p nleft=%d flags=%x ",
+ outx->fd, outx->buf, outx->nleft, outx->flags));
+
+ flushout(outx);
+
+ if (outxtop == NULL) {
+ /*
+ * No -X has been used, so nothing much to do
+ */
+ CTRACE(DBG_OUTPUT, ("+X\n"));
+ return;
+ }
+
+ o = outxtop;
+ outx = o->chain;
+ if (outx == NULL)
+ outx = &errout;
+ outxtop = o->chain;
+ if (o != &errout) {
+ if (o->fd >= 0 && !(o->flags & OUTPUT_CLONE))
+ sh_close(o->fd);
+ if (o->buf)
+ ckfree(o->buf);
+ ckfree(o);
+ }
+
+ CTRACE(DBG_OUTPUT, ("-> fd=%d buf=%p nleft=%d flags=%x\n",
+ outx->fd, outx->buf, outx->nleft, outx->flags));
+}
Index: src/bin/sh/output.h
diff -u src/bin/sh/output.h:1.25 src/bin/sh/output.h:1.26
--- src/bin/sh/output.h:1.25 Sun Nov 19 03:22:55 2017
+++ src/bin/sh/output.h Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: output.h,v 1.25 2017/11/19 03:22:55 kre Exp $ */
+/* $NetBSD: output.h,v 1.26 2017/11/19 03:23:01 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@@ -45,22 +45,27 @@ struct output {
char *buf;
short fd;
short flags;
+ struct output *chain;
};
/* flags for ->flags */
-#define OUTPUT_ERR 01 /* error occurred on output */
+#define OUTPUT_ERR 0x01 /* error occurred on output */
+#define OUTPUT_CLONE 0x02 /* this is a clone of another */
extern struct output output;
extern struct output errout;
extern struct output memout;
extern struct output *out1;
extern struct output *out2;
+extern struct output *outx;
void open_mem(char *, int, struct output *);
void out1str(const char *);
void out2str(const char *);
+void outxstr(const char *);
void outstr(const char *, struct output *);
void out2shstr(const char *);
+void outxshstr(const char *);
void outshstr(const char *, struct output *);
void emptyoutbuf(struct output *);
void flushall(void);
@@ -75,10 +80,14 @@ void fmtstr(char *, size_t, const char *
void doformat(struct output *, const char *, va_list) __printflike(2, 0);
int xwrite(int, char *, int);
int xioctl(int, unsigned long, char *);
+void xtracefdsetup(int);
+void xtrace_clone(int);
+void xtrace_pop(void);
#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
#define out1c(c) outc(c, out1)
#define out2c(c) outc(c, out2)
+#define outxc(c) outc(c, outx)
#define OUTPUT_INCL
#endif
Index: src/bin/sh/sh.1
diff -u src/bin/sh/sh.1:1.173 src/bin/sh/sh.1:1.174
--- src/bin/sh/sh.1:1.173 Wed Nov 15 08:50:07 2017
+++ src/bin/sh/sh.1 Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: sh.1,v 1.173 2017/11/15 08:50:07 kre Exp $
+.\" $NetBSD: sh.1,v 1.174 2017/11/19 03:23:01 kre Exp $
.\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
@@ -34,7 +34,7 @@
.Dd October 24, 2017
.Dt SH 1
.\" everything except c o and s (keep them ordered)
-.ds flags abCEeFfhIiLmnpquVvx
+.ds flags abCEeFfhIiLmnpquVvXx
.Os
.Sh NAME
.Nm sh
@@ -434,10 +434,36 @@ section below.)
.It Fl v Em verbose
The shell writes its input to standard error as it is read.
Useful for debugging.
+.It Fl X Em Xtrace
+Cause output from the
+.Ic xtrace
+.Pq Fl x
+option to be sent to standard error as it exists when the
+.Fl X
+option is enabled (regardless of its previous state.)
+For example:
+.Bd -compact -literal
+ set -X 2>/tmp/trace-file
+.Ed
+will arrange for tracing output to be sent to the file named,
+instead of wherever it was previously being sent,
+until the X option is set again, or cleared.
+.Pp
+Each change (set or clear) to
+.Fl X
+is also performed upon
+.Fl x ,
+but not the converse.
.It Fl x Em xtrace
Write each command to standard error (preceded by the expanded value of
.Dq $PS4 )
before it is executed.
+Unless
+.Fl X
+is set,
+.Dq "standard error"
+means that which existed immediately before any redirections to
+be applied to the command are performed.
Useful for debugging.
.It "\ \ " Em cdprint
Make an interactive shell always print the new directory name when
@@ -2633,6 +2659,16 @@ Making
local causes any shell options that are changed via the set command inside the
function to be restored to their original values when the function
returns.
+If
+.Fl X
+option is altered after
+.Dq \-
+has been made local, then when the function returns, the previous
+destination for
+.Ic xtrace
+output (as of the time of the
+.Ic local
+command) will also be restored.
.Pp
It is an error to use
.Ic local
Index: src/bin/sh/var.c
diff -u src/bin/sh/var.c:1.68 src/bin/sh/var.c:1.69
--- src/bin/sh/var.c:1.68 Sat Oct 28 03:59:11 2017
+++ src/bin/sh/var.c Sun Nov 19 03:23:01 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.68 2017/10/28 03:59:11 kre Exp $ */
+/* $NetBSD: var.c,v 1.69 2017/11/19 03:23:01 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95";
#else
-__RCSID("$NetBSD: var.c,v 1.68 2017/10/28 03:59:11 kre Exp $");
+__RCSID("$NetBSD: var.c,v 1.69 2017/11/19 03:23:01 kre Exp $");
#endif
#endif /* not lint */
@@ -925,6 +925,7 @@ mklocal(const char *name, int flags)
p = ckmalloc(sizeof_optlist);
lvp->text = memcpy(p, optlist, sizeof_optlist);
vp = NULL;
+ xtrace_clone(0);
} else {
vp = find_var(name, &vpp, NULL);
if (vp == NULL) {
@@ -985,6 +986,7 @@ poplocalvars(void)
if (vp == NULL) { /* $- saved */
memcpy(optlist, lvp->text, sizeof_optlist);
ckfree(lvp->text);
+ xtrace_pop();
optschanged();
} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
(void)unsetvar(vp->text, 0);