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

Reply via email to