diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 4131d66..e07107d 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -301,8 +301,8 @@ called at the same point; these are so-called `hook functions'. The shell function tt(add-zsh-hook) provides a simple way of adding or removing functions from the array. -var(hook) is one of tt(chpwd), tt(periodic), tt(precmd) or tt(preexec), -the special functions in question. +var(hook) is one of tt(atexec), tt(chpwd), tt(periodic), tt(precmd), +or tt(preexec), the special functions in question. var(functions) is name of an ordinary shell function. If no options are given this will be added to the array of functions to be executed. diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index 5f8df99..7689647 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -208,6 +208,15 @@ causes an immediately following tt(periodic) function not to run (though it may run at the next opportunity). startitem() +findex(atexec) +vindex(atexec_functions) +item(tt(atexec))( +Executed immediately before each individual command executed by the user. +The first argument is the name of the command being executed. The remaining +arguments are the arguments given to the command. Unlike tt(preexec), all +substitutions have been made. The hook function will not be run for +backgrounded commands or command substitution. +) findex(chpwd) vindex(chpwd_functions) item(tt(chpwd))( diff --git a/Src/exec.c b/Src/exec.c index e682379..8d7c65f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2233,6 +2233,15 @@ resolvebuiltin(const char *cmdarg, HashNode hn) return hn; } +/* Controls whether or not we want to run the atexec hook + * function(s). For example, we don't want to run it for + * backgrounded commands. We also don't want it to run for + * command substitution, otherwise anything atexec prints + * will be output to the line editor. + +/**/ +int atexec = 1; + /**/ static void execcmd(Estate state, int input, int output, int how, int last1) @@ -2742,6 +2751,8 @@ execcmd(Estate state, int input, int output, int how, int last1) } /* pid == 0 */ close(synch[0]); + if (how & Z_ASYNC) + atexec = 0; flags = ((how & Z_ASYNC) ? ESUB_ASYNC : 0) | ESUB_PGRP; if ((type != WC_SUBSH) && !(how & Z_ASYNC)) flags |= ESUB_KEEPTRAP; @@ -2779,6 +2790,35 @@ execcmd(Estate state, int input, int output, int how, int last1) goto err; } + /* Execute atexec and/or atexec_functions. + * + * Notice that we do this after prefork substitutions; + * this means we get to see exactly whats being executed. + * + * atexec is run when + * 1. The shell is interactive (though it might be useful as + * a debugging tool in scripts?) + * 2. Command was called by user + * 3. Not doing process substitution (otherwise if atexec prints + * anything, it is pushed onto the command line) + * 4. Non-background command is executed. No point + * in running atexec on something you wanted to hide. */ + if (interact && + atexec && + !sourcelevel && + !sfcontext && + args && + (getshfunc("atexec") || + paramtab->getnode(paramtab, "atexec" HOOK_SUFFIX))) { + /* Temporarily add a node at the beginning of the args + * link list to indicate we are calling atexec, + * and remove it afterwards. */ + pushnode(args, "atexec"); + callhookfunc("atexec", args, 1, NULL); + uremnode(args, firstnode(args)); + errflag = 0; + } + /* Make a copy of stderr for xtrace output before redirecting */ fflush(xtrerr); if (isset(XTRACE) && xtrerr == stderr && @@ -3524,6 +3564,7 @@ getoutput(char *cmd, int qt) child_unblock(); zclose(pipes[0]); redup(pipes[1], 1); + atexec = 0; /* Don't run atexec. */ entersubsh(ESUB_PGRP|ESUB_NOMONITOR); cmdpush(CS_CMDSUBST); execode(prog, 0, 1);