1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

editors: fix broken SIGWINCH handling

In the emacs editor:
 1.  press ESC
 2.  change the size of your terminal window
and your screen is mysteriously cleared. (Until recent fixes, the
shell probably also crashed somewhere in the job control code.)

The cause is the way SIGWINCH is handled by ed_read() in edit.c.
For the emacs editor, it sends a Ctrl+L character to the input
buffer. The Ctrl+L command refreshes the command line. And it so
happens that ESC plus Ctrl+L is a command to clear the screen in
the emacs editor.

With the exeption of vi insert/command mode for which it uses a
shared flag, edit.c does not know the state of the editor, because
their data are internal to emacs.c and vi.c. So it doesn't know
whether you're in some mode that treats keyboard input specially.
Which means this way of dealing with SIGWINCH is fundamentally
misdesigned and is not worth fixing.

It gets sillier: in addition to sending keyboard commands, edit.c
was also communicating directly with emacs.c and vi.c via a flag,
e_nocrnl, which means 'please don't make Ctrl+L emit a linefeed'
(it normally refreshes on a new line but that is undesirable for
SIGWINCH). So there is already a hack that breaks the barrier
between edit.c and emacs.c/vi.c. Let's do that properly instead.

As of this commit, ed_read() does not send any fake keystrokes.
Instead, two extern functions, emacs_redraw() and vi_redraw(), are
defined for redrawing the command line. These are put in emacs.c
and vi.c so they have access to relevant static data and functions.
Then, instead of sending keyboard commands to the editor and
returning, ed_read() simply calls the redraw function for the
active editor, then continues and waits for input. Much cleaner.

src/cmd/ksh93/include/edit.h:
- Remove e_nocrnl flag from Edit_t struct.
- Define externs emacs_redraw() and vi_redraw(). Since Emacs_t and
  Vi_t types are not known here, we have to declare void* pointers
  and the functions will have to use typecasts.

src/cmd/ksh93/edit/edit.c:
- ed_read(): Call emacs_redraw() or vi_redraw() as per above.
- ed_getchar(): Remove comment about a nonexistent while loop.

src/cmd/ksh93/edit/emacs.c:
- Updates corresponding to removal of e_nocrnl flag.
- Add emacs_redraw(). This one is pretty simple. Refresh the
  command line, then ed_flush() to update the cursor display.

src/cmd/ksh93/edit/vi.c:
- Updates corresponding to removal of e_nocrnl flag. Also remove a
  similar internal 'nonewline' flag which is now equally redundant.
- Move the Ctrl+L handling code (minus writing the newline) into
  the vi_redraw() function.
- Change two cases where vi set nonewline and sent Ctrl+L to itself
  into simple vi_redraw() calls.
- Add vi_redraw(). This is more complicated as it incorporates the
  previous Ctrl+L code. It needs an added refresh() call with a
  check whether we're currently in command or insert mode, as those
  use different refresh methods. Luckily edit.c already maintains
  an *e_vi_insert flag in ed_getchar() that we can use. Since vi's
  refresh() already calls ed_flush(), we don't need to add that.
This commit is contained in:
Martijn Dekker 2021-02-21 23:07:30 +00:00
parent 18529b88c6
commit 83630f9d1c
6 changed files with 76 additions and 45 deletions

View file

@ -829,11 +829,14 @@ static void ed_nputchar(register Edit_t *ep, int n, int c)
/*
* Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set
* Use sfpkrd() to poll() or select() to wait for input if possible
*
* The return value is the number of bytes read, or < 0 for EOF.
*
* Unfortunately, systems that get interrupted from slow reads update
* this access time for the terminal (in violation of POSIX).
* The fixtime() macro, resets the time to the time at entry in
* The fixtime() macro resets the time to the time at entry in
* this case. This is not necessary for systems that can handle
* sfpkrd() correctly (i,e., those that support poll() or select()
* sfpkrd() correctly (i.e., those that support poll() or select()).
*/
int ed_read(void *context, int fd, char *buff, int size, int reedit)
{
@ -858,11 +861,11 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
goto done;
#if SHOPT_ESH && SHOPT_VSH
if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
if(shp->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
#elif SHOPT_ESH
if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
if(shp->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
#elif SHOPT_VSH
if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && sh_isoption(SH_VI))
if(shp->winch && sh_isstate(SH_INTERACTIVE) && sh_isoption(SH_VI))
#else
if(0)
#endif
@ -889,7 +892,6 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
while(n--)
ed_putstring(ep,CURSOR_UP);
}
ep->sh->winch = 0;
ed_flush(ep);
sh_delay(.05,0);
astwinsize(2,&rows,&newsize);
@ -898,19 +900,18 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
ep->e_winsz = MINWINDOW;
if(!ep->e_multiline && ep->e_wsize < MAXLINE)
ep->e_wsize = ep->e_winsz-2;
ep->e_nocrnl=1;
if(*ep->e_vi_insert)
{
buff[0] = ESC;
buff[1] = cntl('L');
buff[2] = 'a';
return(3);
}
buff[0] = cntl('L');
return(1);
#if SHOPT_ESH && SHOPT_VSH
if(sh_isoption(SH_VI))
vi_redraw(ep->e_vi);
else
emacs_redraw(ep->e_emacs);
#elif SHOPT_VSH
vi_redraw(ep->e_vi);
#elif SHOPT_ESH
emacs_redraw(ep->e_emacs);
#endif
}
else
ep->sh->winch = 0;
shp->winch = 0;
/* an interrupt that should be ignored */
errno = 0;
if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
@ -1072,7 +1073,6 @@ int ed_getchar(register Edit_t *ep,int mode)
{
ed_flush(ep);
ep->e_inmacro = 0;
/* The while is necessary for reads of partial multibyte chars */
*ep->e_vi_insert = (mode==-2);
if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
n = putstack(ep,readin,n,1);