nvi forgets to switch tty from ex mode on q when there are other screens
commit72c797015c3a470a59106cd86bc18e63a7b2fd4e
authorAl Viro <viro@ZenIV.linux.org.uk>
Sat, 13 Dec 2014 07:41:59 +0000 (13 07:41 +0000)
committerSven Verdoolaege <skimo@kotnet.org>
Wed, 17 Dec 2014 13:21:40 +0000 (17 14:21 +0100)
treee4fcb7f4c90ba181ebc449df397c156b78a876a1
parent9afcab2ae805b93f04cb4ec303ea6a554afb1971
nvi forgets to switch tty from ex mode on q when there are other screens

nvi forgets to switch tty mode if, in ex mode, you give it 'q'
with more than one screen present.  The minimal reproducer is
:Vi<enter>Qq<enter>
After that you are back in vi mode, but tty is still set up for ex - input
is echoed and line-buffered.  In that state Qvi<enter> recovers the normal
state; :vi<enter> does not.  Bug goes _way_ back - at the very least to 1.79,
and I wouldn't be surprised if it was as old as the introduction of multi-screen
support.  The same fix applies to older versions.

ex mode interpreter treats SC_EXIT flag set by ex_quit() as "find the
next screen, leave it for caller to handle after we return, kill the current
one and return to caller".  Then the caller (editor()) proceeds to call either
vi() or ex() it, depending on SC_EX flag for the new screen.  Both call
->scr_screen(), passing it the desired mode.  Unfortunately, cl_screen()
(normal ->scr_screen() callback) checks the current tty mode (SC_SCR_{EX,VI})
in flags, and if the desired mode matches what we (think we) have it does
nothing.  In this case we end up with tty mode matching the SC_SCR_... flags
for *old* screen.  The one we switch to has SC_SCR_VI and SC_VI, so we proceed
to call vi(), which calls cl_screen(new_screen, SC_VI).  Which decides that
there's nothing for it to do.  As the result we are left in vi mode with
buggered tty settings.

Entering Qvi<enter> recovers things since handling 'Q' sets SC_EX and
leaves vi(), with ex() getting called on the same screen.  That calls
cl_screen(screen, SC_EX), which does (needless) switch of tty setting for ex
mode and sets SC_SCR_EX.  Handling of "vi" sets SC_VI and leaves ex().  Now we
call vi() again, but this time around we do have SC_SCR_EX, so cl_screen()
called by vi() *does* switch tty mode properly.

Fix is obvious - transfer SC_SCR_... flags from old screen to
new one, when ex() sees SC_EXIT/SC_EXIT_FORCE.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
ex/ex.c