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.
This commit is contained in:
kre 2017-11-19 03:23:01 +00:00
parent bfa0b331f0
commit ffc64c6374
7 changed files with 258 additions and 55 deletions

View File

@ -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, struct backcmd *backcmd)
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, struct backcmd *backcmd)
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, struct backcmd *backcmd)
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. */

View File

@ -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 startup files
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

View File

@ -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);

View File

@ -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/11/19 03:22:55 kre Exp $");
#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/11/19 03:22:55 kre Exp $");
#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, char *arg)
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));
}

View File

@ -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 *, ...) __printflike(3, 4);
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

View File

@ -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

View File

@ -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);