Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [BUG] precmd not executed after Ctrl-C on some builtin
- X-seq: zsh-workers 54525
- From: Mikael Magnusson <mikachu@xxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: Re: [BUG] precmd not executed after Ctrl-C on some builtin
- Date: Wed, 13 May 2026 12:53:31 +0200
- Arc-authentication-results: i=1; mx.google.com; arc=none
- Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=BNlYJgX6fZpJziZGh6vHp1kvlHJu6bAphPrZjZrjt4g=; fh=SbTlPuNNxBzTkRlwWtqw/TXBY0HvGvtE97RpPp3sJPM=; b=MJLTT2ifeIcpSGdpF25/FeRQIN9JWWO8F4zrFX0ewXWcUtsyzCFIO1J2/objlwOfjw LOYP+kvVuNQxRwyIuevI06yxtAdMDHp6etz9zO+hSFxJokdHNPpIytK9g0HCHfbOc9x7 2fV1VfrTz52RdrnLZWIc28noaz+dqXokNRjs/ebNlBO0od6Ya9G0zDPULnpRCGZgM6UB b+uRQ6j3aEwCw8tWblRFMd6VT5wEA1yxl47ZX8zviLV08IzX+Yyc7xGMul2KPCcaZt7s aXxRlHFsUDsHUjTvdsfM2J2JNNDTTJDNbIxcd9CRI+GoQ/SjeEUgwBZMDhhlzSLJdwth 0R6w==; darn=zsh.org
- Arc-seal: i=1; a=rsa-sha256; t=1778669624; cv=none; d=google.com; s=arc-20240605; b=J8yArCcI2Abkcq8BYaP3cUpcksUoQr1nsTfaFklP42cCGTd72CaWtu1qyj5cGbvJrU Q9ejwbGjomGo5QKP3S0JLsggwSLhEq2AnxtbgDv4J75ZBm+4JvYkcMaGD//RojVZ16h2 DRvETC+sCxTIboWrHsc3NgSCKp4m4cCusyPoPDgaAQzmlE1/vHU+S0+jrbBHEMqG+ter B6YAmUaoPFYE6Fg/xhfv7Ca6w7le5V5O0BGZqIUWK7yQmfdCLBRvHBHUBfWsskypVH2w ybasHma8Nh0tQ293SMb6sOBpkFQSFiBpZflqLtsFnO+4X3n504vIKwsqCbGEdpXPJcgM OIYg==
- Archived-at: <https://zsh.org/workers/54525>
- In-reply-to: <20260512232900.GA1652469@qaa.vinc17.org>
- List-id: <zsh-workers.zsh.org>
- References: <20260512232900.GA1652469@qaa.vinc17.org>
On Wed, May 13, 2026 at 1:35 AM Vincent Lefevre <vincent@xxxxxxxxxx> wrote:
>
> With zsh 5.9 and 5.9.0.3-test (1328291abbb80e90dc4473a4396daffb0e919827),
> after interrupting some commands with Ctrl-C, precmd is not executed
> before the prompt is displayed:
>
> qaa% mkfifo fifo
> qaa% precmd() { echo precmd > /dev/tty }
> precmd
> qaa% pwd
> /home/vinc17
> precmd
> qaa% pwd >> fifo
> ^C%
> qaa% read
> ^C%
> precmd
> qaa% { pwd >> fifo }
> ^C%
> precmd
> qaa%
>
> The issue occurs with commands of the form "some_builtin >> fifo".
> As seen with "read" (for which the issue does not occur), the issue
> is not just that a builtin is blocking.
With the following debug print in place,
diff --git i/Src/utils.c w/Src/utils.c
index 94844e4229..799bb099d1 100644
--- i/Src/utils.c
+++ w/Src/utils.c
@@ -1575,6 +1575,9 @@ preprompt(void)
if (errflag)
return;
/* If a shell function named "precmd" exists, *
* then execute it. */
+ dputs("DEBUG: queueing_enabled=%d front=%d rear=%d",
+ queueing_enabled, queue_front, queue_rear);
+
callhookfunc("precmd", NULL, 1, NULL);
if (errflag)
return;
We get this output for the above tests:
% precmd() { echo precmd > /dev/tty }
DEBUG: queueing_enabled=1 front=0 rear=0
precmd
% read
^C%
DEBUG: queueing_enabled=1 front=0 rear=0
precmd
% echo >> fifo
^C%
DEBUG: queueing_enabled=1 front=0 rear=1 <- probably not good?
% # i just pressed enter here without this comment
DEBUG: queueing_enabled=1 front=1 rear=1
precmd
We can also notice that if we set a TRAPINT() { true } handler, the
precmd runs normally too. In signals.c, we set errflag |= ERRFLAG_INT
when there's no trap handler, otherwise we don't. So the theory is
that the signal handler runs when we execute the precmd hook, and we
abort execution, even though we just checked that errflag is 0 before
trying to run it (I manually added it in the context in the diff
above).
There's a bit in init.c that resets all errors, seen in the patch
below, and the patch makes it so that this test works as expected, and
the rest of the tests also pass. I'm not totally sure this is the
right thing to do though, I usually don't mess with signal handler
stuff.
% precmd() { echo precmd > /dev/tty }
DEBUG: queueing_enabled=1 front=0 rear=0
precmd
% read
^C%
DEBUG: queueing_enabled=1 front=0 rear=0
precmd
% echo >> fifo
^C%
DEBUG: queueing_enabled=1 front=1 rear=1
precmd
diff --git a/Src/init.c b/Src/init.c
index 7c3b824611..a422036c71 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -117,35 +117,38 @@ loop(int toplevel, int justonce)
queue_signals();
pushheap();
if (!toplevel)
zcontext_save();
for (;;) {
freeheap();
if (stophist == 3) /* re-entry via preprompt() */
hend(NULL);
hbegin(1); /* init history mech */
if (isset(SHINSTDIN)) {
setblock_stdin();
if (interact && toplevel) {
- int hstop = stophist;
+ int hstop = stophist, q;
stophist = 3;
/*
* Reset all errors including the interrupt error status
* immediately, so preprompt runs regardless of what
* just happened. We'll reset again below as a
* precaution to ensure we get back to the command line
* no matter what.
*/
+ q = queue_signal_level();
+ dont_queue_signals();
+ restore_queue_signals(q);
errflag = 0;
preprompt();
if (stophist != 3)
hbegin(1);
else
stophist = hstop;
/*
* Reset all errors, including user interrupts.
* This is what allows ^C in an interactive shell
* to return us to the command line.
*/
errflag = 0;
}
--
Mikael Magnusson
Messages sorted by:
Reverse Date,
Date,
Thread,
Author