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:
parent
18529b88c6
commit
83630f9d1c
6 changed files with 76 additions and 45 deletions
5
NEWS
5
NEWS
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in a new issue