From: Jeremy Mates Subject: Re: vi crash whilst global search running (and nvi too?) To: tech@openbsd.org Date: Thu, 5 Feb 2026 23:55:50 +0000 On 2026-01-28 00:52:06 +0000, Jeremy Mates wrote: > $ printf 'foo\nfoo\nfoo\n' > foo > $ /usr/bin/ex foo > foo: unmodified: line 3 > :g/foo/vi So a problem is that ex_cmd() has been entered into due to the "global /^/ visual" command (for each matching line, enter visual mode), and there is some notice in the code, probably not very important, /* * We always start running the command on the top of the stack. * This means that *everything* must be resolved when we leave * this function for any reason. */ and then in visual mode if you type in ":quit" or any other ex command ex_cmd is entered for a second time (uh oh?), whereupon it sees "vi" on the top of the gp->ecq command stack, and the "quit" or whatever ex command you typed is ignored. Usually the "Global/v command running when the file/screen changed" message appears as the global started in ex mode is noticed. A subsequent ":quit" actually works and will probably cause vi to exit, sometimes with a crash, probably on account of ex_cmd having been entered more than once and the cleanup code maybe trying to do things more than once. As a horrible kluge one can use a patch such as the following, which appears to at least let you enter ":quit" the first time and have vi exit, probably with some error messages and maybe crashes from the cleanup code. A less bad patch would need to figure out how to make ex_cmd reentrant, or to ensure that the cleanup code does not close ep->db twice or free already freed memory, and to confirm that making ex_cmd reentrant does not cause problems anywhere else, which the robust test suite for vi will doubtless help with. --- usr.bin/vi/ex/ex.c +++ usr.bin/vi/ex/ex.c @@ -206,6 +206,7 @@ ex_cmd(SCR *sp) int ch, cnt, delim, isaddr, namelen; int newscreen, notempty, tmp, vi_address; char *arg1, *p, *s, *t; + static int level = 0; gp = sp->gp; exp = EXP(sp); @@ -214,8 +215,14 @@ ex_cmd(SCR *sp) * We always start running the command on the top of the stack. * This means that *everything* must be resolved when we leave * this function for any reason. + * + * !!! Not everything is resolved if from ex mode "g/^/vi" is + * run, and then an ex command is entered in visual mode. */ + ++level; loop: ecp = LIST_FIRST(&gp->ecq); + if (level > 1) + ecp = LIST_NEXT(ecp, q); /* If we're reading a command from a file, set up error information. */ if (ecp->if_name != NULL) { @@ -1603,6 +1610,8 @@ rsuccess: tmp = 0; /* Turn off the global bit. */ F_CLR(sp, SC_EX_GLOBAL); + --level; + return (tmp); }