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