Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: 'while do done' hangs interactive zsh

On Sun, May 16, 2021 at 7:28 AM Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
> > Even more, if the user enters "while do; done" in an interactive zsh
> > instance, the busy loop is not interruptible by ^C, ^\ or ^Z; the shell
> > has to be killed via some external means.
> I can NOT reproduce this in the latest revision from git, but I can
> reproduce it in zsh-5.8.

Specifically, the shell is interruptible by ^C.  I haven't attempted
to bisect where this was introduced; there are no obvious deltas to
signals.c, loop.c, or parse.c to explain it.

It is impossible to interrupt the parser with SIGINT.  zhandler is
entered with signal queueing enabled; when signals are allowed again,
zhandler is called again and we pass through this code:
675        if (list_pipe || chline || simple_pline) {
676            breaks = loops;
677            errflag |= ERRFLAG_INT;
678            inerrflush();
679            check_cursh_sig(SIGINT);
680        }
681        lastval = 128 + SIGINT;
Because we're still in the parser, none of (list_pipe || chline ||
simple_pline) is true, so we never set breaks or errflag, only
lastval.  I'm not immediately sure what to do about that; perhaps just
move the errflag setting outside that test?

The attached makes both "while do" and "do done" into parse errors; I
didn't want to try messing with the tokenizer, so better solutions are

I mention tokenizing because in "for do" the string "do" is not a
token; "while do;" could conceivably mean to execute the command "do"
as the test condition.
diff --git a/Src/parse.c b/Src/parse.c
index b09c7989a..153633bb1 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1525,10 +1525,16 @@ par_while(int *cmplx)
     incmdpos = 1;
     while (tok == SEPER)
-    if (tok == DOLOOP) {
+    /*
+     * If the very first thing after "while" is "do",
+     * ecused has advanced only two positions.  This
+     * is not a valid while-do construction.
+     */
+    if (tok == DOLOOP && (ecused - oecused) > 2) {
-	if (tok != DONE)
+	/* As above but "do done" is not valid */
+	if (tok != DONE || (ecused - oecused) < 8)
 	incmdpos = 0;

Messages sorted by: Reverse Date, Date, Thread, Author