Hello :)
I managed to write a test for the ZSH_XTRACEFD feature in A04redirect.ztst,
and am planning to add more tests, but maybe you'd have some hints on what
I should test?
Patch in attachment.
On Tue, Aug 13, 2019 at 5:39 PM Peter Stephenson
wrote:
> How about this?
>
> I've tried to make it so that if the fd is 2 or less it always uses
> the standard file, if it's greater it always creates or frees one, and
> it only creates a new file when it's requested to switch.
>
> One other fix is that we only need to fclose(xtrerr) which then closes
> the file descriptor. There's no way of just closing the file and not
> the file descriptor --- we'll need to document that the file descriptor
> is closed if ZSH_XTRACEFD changes again since it's a bit illogical given
> that the user opened the fd in the first place.
>
> pws
>
> diff --git a/Src/exec.c b/Src/exec.c
> index e81053d67..fe702b644 100644
> --- a/Src/exec.c
> +++ b/Src/exec.c
> @@ -5394,7 +5394,7 @@ execshfunc(Shfunc shf, LinkList args)
> cmdsp = 0;
> if ((osfc = sfcontext) == SFC_NONE)
> sfcontext = SFC_DIRECT;
> - xtrerr = stderr;
> + xtrerr = xtrace_file;
>
> doshfunc(shf, args, 0);
>
> diff --git a/Src/init.c b/Src/init.c
> index 445cd3937..c51197079 100644
> --- a/Src/init.c
> +++ b/Src/init.c
> @@ -609,8 +609,10 @@ init_io(char *cmd)
> SHTTY = -1;
> }
>
> - /* Send xtrace output to stderr -- see execcmd() */
> - xtrerr = stderr;
> + /* Send xtrace output to zsh_xtracefd file descriptor -- see
> execcmd() */
> + if (zsh_xtracefd == 0)
> + zsh_xtracefd = 2;
> + xtracefdassign();
>
> /* Make sure the tty is opened read/write. */
> if (isatty(0)) {
> diff --git a/Src/params.c b/Src/params.c
> index 1499e3a40..2347ee9c3 100644
> --- a/Src/params.c
> +++ b/Src/params.c
> @@ -102,7 +102,8 @@ zlong lastval, /* $? */
> zterm_lines, /* $LINES */
> rprompt_indent, /* $ZLE_RPROMPT_INDENT */
> ppid, /* $PPID */
> - zsh_subshell; /* $ZSH_SUBSHELL */
> + zsh_subshell, /* $ZSH_SUBSHELL */
> + zsh_xtracefd; /* $ZSH_XTRACEFD */
>
> /* $FUNCNEST */
> /**/
> @@ -264,6 +265,9 @@ static const struct gsu_array pipestatus_gsu =
> static const struct gsu_integer rprompt_indent_gsu =
> { intvargetfn, zlevarsetfn, rprompt_indent_unsetfn };
>
> +static const struct gsu_integer xtracefd_gsu =
> +{ intvargetfn, xtracefdsetfn, xtracefdunsetfn };
> +
> /* Nodes for special parameters for parameter hash table */
>
> #ifdef HAVE_UNION_INIT
> @@ -353,6 +357,7 @@ IPDEF5("LINES", &zterm_lines, zlevar_gsu),
> IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
> IPDEF5("SHLVL", &shlvl, varinteger_gsu),
> IPDEF5("FUNCNEST", &zsh_funcnest, varinteger_gsu),
> +IPDEF5("ZSH_XTRACEFD", &zsh_xtracefd, xtracefd_gsu),
>
> /* Don't import internal integer status variables. */
> #define IPDEF6(A,B,F)
> {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void
> *)B),GSU(F),10,0,NULL,NULL,NULL,0}
> @@ -4387,6 +4392,71 @@ setsecondstype(Param pm, int on, int off)
> return 0;
> }
>
> +/* Open / assign the XTRACE fd */
> +
> +/**/
> +void xtracefdassign(void)
> +{
> + int fd = (int)zsh_xtracefd;
> + switch (fd)
> + {
> + case 0: /* bizarre, but handle for consistency */
> + xtrerr = stdin;
> + break;
> +
> + case 1:
> + xtrerr = stdout;
> + break;
> +
> + case 2:
> + xtrerr = stderr;
> + break;
> +
> + default:
> + xtrerr = fdopen(fd, "w");
> + break;
> + }
> + xtrace_file = xtrerr;
> +}
> +
> +/* Function to set value of special parameter `ZSH_XTRACEFD' */
> +
> +/**/
> +void
> +xtracefdsetfn(Param pm, zlong fd)
> +{
> + int current_fd;
> +
> + /* Check that the given file descriptor is valid */
> + if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
> + current_fd = intvargetfn(pm);
> + /* We never close file descriptors 0 (stdin), 1 (stdout) or 2
> stderr) */
> + if (current_fd > 2)
> + fclose(xtrerr);
> + intvarsetfn(pm, fd);
> + xtracefdassign();
> + } else
> + zwarn("file descriptor %d is not valid", fd);
> +
> +}
> +
> +/* Function to unset value of special parameter `ZSH_XTRACEFD' */
> +
> +/**/
> +void
> +xtracefdunsetfn(Param pm, UNUSED(int exp))
> +{
> + int current_fd = intvargetfn(pm);
> + if (current_fd == 2) /* Nothing to do, already using stderr */
> + return;
> + else { /* Reset to file descriptor 2 (stderr) */
> + intvarsetfn(pm, 2);
> + if (current_fd > 2)
> + fclose(xtrerr); /* Never close standard descriptors */
> + xtrerr = xtrace_file = stderr;
> + }
> +}
> +
> /* Function to get value for special parameter `USERNAME' */
>
> /**/
> diff --git a/Src/utils.c b/Src/utils.c
> index 46cf7bcf6..2eee27697 100644
> --- a/Src/utils.c
> +++ b/Src/utils.c
> @@ -1760,12 +1760,19 @@ checkmailpath(char **s)
> /**/
> FILE *xtrerr = 0;
>
> +/* This records the last file XTRACE was open too.
> + * It's used for restoring XTRACE after a possible redirection.gggg
> + */
> +
> +/**/
> +FILE *xtrace_file;
> +
> /**/
> void
> printprompt4(void)
> {
> if (!xtrerr)
> - xtrerr = stderr;
> + xtracefdassign();
> if (prompt4) {
> int l, t = opts[XTRACE];
> char *s = dupstring(prompt4);
>