1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-15 04:32:24 +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

5
NEWS
View file

@ -3,6 +3,11 @@ For full details, see the git log at: https://github.com/ksh93/ksh
Any uppercase BUG_* names are modernish shell bug IDs.
2021-02-21:
- Fixed: The way that SIGWINCH was handled (i.e. the signal emitted when the
terminal window size changes) could cause strange emacs/vi editor behaviour.
2021-02-20:
- Fixed a bug introduced on 2021-01-20: if a DEBUG trap action yielded exit

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
ep->sh->winch = 0;
emacs_redraw(ep->e_emacs);
#elif SHOPT_VSH
vi_redraw(ep->e_vi);
#elif SHOPT_ESH
emacs_redraw(ep->e_emacs);
#endif
}
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);

View file

@ -614,10 +614,8 @@ update:
}
continue;
case cntl('L'):
if(!ep->ed->e_nocrnl)
ed_crlf(ep->ed);
draw(ep,REFRESH);
ep->ed->e_nocrnl = 0;
continue;
case cntl('[') :
vt220_save_repeat = oadjust;
@ -1442,8 +1440,8 @@ static void draw(register Emacs_t *ep,Draw_t option)
/***************************************
If in append mode, cursor at end of line, screen up to date,
the previous character was a 'normal' character,
and the window has room for another character.
Then output the character and adjust the screen only.
and the window has room for another character,
then output the character and adjust the screen only.
*****************************************/
@ -1555,7 +1553,7 @@ static void draw(register Emacs_t *ep,Draw_t option)
}
#endif /* SHOPT_MULTIBYTE */
}
if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0)
if(ep->ed->e_multiline && option == REFRESH)
ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
@ -1593,6 +1591,18 @@ static void draw(register Emacs_t *ep,Draw_t option)
return;
}
/*
* Print the prompt and force a total refresh.
* This is used from edit.c for redrawing the command line upon SIGWINCH.
*/
void emacs_redraw(void *vp)
{
Emacs_t *ep = (Emacs_t*)vp;
draw(ep, REFRESH);
ed_flush(ep->ed);
}
/*
* put the cursor to the <newp> position within screen buffer
* if <c> is non-zero then output this character

View file

@ -104,7 +104,6 @@ typedef struct _vi_
char last_find; /* last find command */
char last_cmd; /* last command */
char repeat_set;
char nonewline;
int findchar; /* last find char */
genchar *lastline;
int first_wind; /* first column of window */
@ -545,7 +544,7 @@ int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit
/*** start over since there may be ***/
/*** a control char, or cursor might not ***/
/*** be at left margin (this lets us know ***/
/*** where we are ***/
/*** where we are) ***/
cur_phys = 0;
window[0] = '\0';
pr_string(vp,Prompt);
@ -819,14 +818,8 @@ static int cntlmode(Vi_t *vp)
case cntl('L'): /** Redraw line **/
/*** print the prompt and ***/
/* force a total refresh */
if(vp->nonewline==0 && !vp->ed->e_nocrnl)
putchar('\n');
vp->nonewline = 0;
pr_string(vp,Prompt);
window[0] = '\0';
cur_phys = vp->first_wind;
vp->ofirst_wind = INVALID;
vp->long_line = ' ';
vi_redraw((void*)vp);
break;
case cntl('V'):
@ -907,8 +900,7 @@ static int cntlmode(Vi_t *vp)
vp->ed->hoff--;
hupdate:
ed_histlist(vp->ed,*vp->ed->hlist!=0);
vp->nonewline++;
ed_ungetchar(vp->ed,cntl('L'));
vi_redraw((void*)vp);
continue;
}
#endif /* SHOPT_EDPREDICT */
@ -951,9 +943,8 @@ static int cntlmode(Vi_t *vp)
ed_histlist(vp->ed,0);
if(c=='\n')
ed_ungetchar(vp->ed,c);
ed_ungetchar(vp->ed,cntl('L'));
vp->nonewline = 1;
cur_virt = 0;
vi_redraw((void*)vp);
}
#endif /*SHOPT_EDPREDICT */
break;
@ -1873,6 +1864,26 @@ static void putstring(register Vi_t *vp,register int col, register int nchars)
return;
}
/*{ VI_REDRAW( )
*
* Print the prompt and force a total refresh.
*
* This is invoked from edit.c for redrawing the command line
* upon SIGWINCH. It is also used by the Ctrl+L routine.
*
}*/
void vi_redraw(void *ep)
{
Vi_t *vp = (Vi_t*)ep;
pr_string(vp,Prompt);
window[0] = '\0';
cur_phys = vp->first_wind;
vp->ofirst_wind = INVALID;
vp->long_line = ' ';
refresh(vp, *vp->ed->e_vi_insert ? INPUT : CONTROL);
}
/*{ REFRESH( mode )
*
* This routine will refresh the crt so the physical image matches
@ -2075,9 +2086,8 @@ static void refresh(register Vi_t* vp, int mode)
vp->long_line = vp->long_char;
}
if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl)
if(vp->ed->e_multiline && vp->ofirst_wind==INVALID)
ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1);
vp->ed->e_nocrnl = 0;
vp->ocur_phys = ncur_phys;
vp->ocur_virt = cur_virt;
vp->ofirst_wind = first_w;
@ -2477,8 +2487,7 @@ addin:
}
else if((c=='=' || (c=='\\'&&virtual[last_virt]=='/')) && !vp->repeat_set)
{
vp->nonewline++;
ed_ungetchar(vp->ed,cntl('L'));
vi_redraw((void*)vp);
return(GOOD);
}
else

View file

@ -89,7 +89,6 @@ typedef struct edit
int e_lnext;
int e_plen; /* length of prompt string */
char e_crlf; /* zero if cannot return to beginning of line */
char e_nocrnl; /* don't put a new-line with ^L */
char e_keytrap; /* set when in keytrap */
int e_llimit; /* line length limit */
int e_hline; /* current history line number */
@ -270,4 +269,12 @@ extern int hist_expand(const char *, char **);
#endif /* SHOPT_HISTEXPAND */
#if SHOPT_ESH
extern void emacs_redraw(Void_t*);
#endif /* SHOPT_ESH */
#if SHOPT_VSH
extern void vi_redraw(Void_t*);
#endif /* SHOPT_VSH */
#endif /* !SEARCHSIZE */

View file

@ -20,7 +20,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2021-02-18" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2021-02-21" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */