mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-13 11:42:21 +00:00
set -b/--notify: do not mess up the command line
This commit makes three interrelated changes.
First, the code for erasing the command line before redrawing it
upon a window size change is simplified and modernised. Instead of
erasing the line with lots of spaces, it now uses the sequence
obtained from 'tput ed' (usually ESC, '[', 'J') to "erase to the
end of screen". This avoids messing up the detection and automatic
redrawing of wrapped lines on terminals such as Apple Terminal.app.
Second, the -b/--notify option is made more usable. When it is on
and either the vi or emacs/gmacs line editor is in use, 'Done' and
similar notifications are now buffered and trigger a command line
redraw as if the window size changed, and the redraw routine prints
that notify buffer between erasing and redrawing the commmnd line.
The effect is that the notification appears to magically insert
itself directly above the line you're typing. (The notification
behaviour when not in the shell line editor, e.g. while running
commands such as external editors, is unchanged.)
Third, a bug is fixed that caused -b/--notify to only report on one
job when more than one terminated at the same time. The rest was
reported on the next command line as if -b were not on. Reproducer:
$ set -b; sleep 1 & sleep 1 & sleep 1 &
This commit also includes a fair number of other window size and
$COLUMNS/$LINES handling tweaks that made all this easier, not all
of which are mentioned below.
src/cmd/ksh93/include/fault.h,
src/cmd/ksh93/sh/fault.c:
- Replace sh_update_columns_lines with a new sh_winsize() function.
It calls astwinsize() and is to be used instead of it, and
instead of nv_getval(LINES) and nv_getval(COLUMNS) calls. It:
- Allows passing one or neither of lines or cols pointers.
- Updates COLUMNS and LINES, but only if they actually changed
from the last values. This makes .set discipline functions
defined for these variables more useful.
- Sets the sh.winch flag, but only if COLUMNS changes. If only
the height changes, the command line does not need redrawing.
src/cmd/ksh93/include/io.h:
- Add sh_editor_active() that allows checking whether one of vi,
emacs or gmacs is active without onerous #if SHOPT_* directives.
src/cmd/ksh93/sh/jobs.c: job_reap():
- Remove the fix backported in fc655f1a
, which was really just a
workaround that papered over the real bug.
- Move a check for errno==ECHILD so it is only done when waitpid()
returns an error (pid < 0); the check was not correct because C
library functions that do not error out also do not change errno.
- Move the SH_NOTIFY && SH_TTYWAIT code section to within the
while(1) loop so it is run for each job, not only the
last-processed one. This fixes the bug where only one job was
notified when more than one ended at the same time.
- In that section, check if an editor is active; if so, set the
output file for job_list() to sh.notifybuffer instead of standard
error, list the jobs without the initial newline (JOB_NLFLAG),
and trigger a command line redraw by setting sh.winch.
src/cmd/ksh93/edit/edit.c:
- Obtain not just CURSOR_UP but also ERASE_EOS (renamed from
KILL_LINE) using 'tput'. The latter had the ANSI sequence
hardcoded. Define a couple of TPUT_* macros to make it easier to
deal with terminfo/termcap codes.
- Add get_tput() to make it easier to get several tput values
robustly (with SIGINT blocked, trace disabled, etc.)
- ed_crlf(): Removed. Going by those ancient #ifdefs, nothing that
93u+m will ever run on requires a '\r' before a '\n' to start a
new line on the terminal. Plus, as of 93u+, there were already
several spots in emacs.c and vi.c where it printed a sole '\n'.
- ed_read():
- Simplify/modernise command line erase using ERASE_EOS.
- Between erasing and redrawing, print the contents of the notify
buffer. This has the effect of inserting notifications above
the command line while the user is typing.
src/cmd/ksh93/features/cmds:
- To detect terminfo vs termcap codes, use all three codes we're
currently using. This matters on at least on my system (macOS
10.14.6) in which /usr/bin/tput has incomplete terminfo support
(no 'ed') but complete termcap support!
This commit is contained in:
parent
aa468f4c55
commit
bb4f23e63f
17 changed files with 205 additions and 175 deletions
5
ANNOUNCE
5
ANNOUNCE
|
@ -199,6 +199,11 @@ New command line editor features:
|
|||
mode, <ESC> 7 <left-arrow> will now move the cursor seven positions to
|
||||
the left. In vi control mode, this would be entered as: 7 <left-arrow>.
|
||||
|
||||
- When the -b/--notify shell option is on and the vi or emacs/gmacs shell
|
||||
line editor is in use, 'Done' and similar notifications from completed
|
||||
background jobs are now inserted directly above the line you're typing,
|
||||
without affecting your command line display.
|
||||
|
||||
New shell language features:
|
||||
|
||||
- The &>file redirection shorthand (for >file 2>&1) is now available for
|
||||
|
|
11
NEWS
11
NEWS
|
@ -3,6 +3,17 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
|
|||
|
||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||
|
||||
2022-07-24:
|
||||
|
||||
- New feature to make 'set -b'/'set -o notify' more usable. When that option
|
||||
is on (and either the vi or emacs/gmacs line editor is in use), 'Done' and
|
||||
similar notifications from completed background jobs no longer mess up the
|
||||
command line you're typing. Instead, the notification is inserted directly
|
||||
above the line you're typing, without affecting your command line display.
|
||||
|
||||
- Fixed a bug that caused the -b/--notify option to only notify on one job if
|
||||
more than one background job completed at the same time.
|
||||
|
||||
2022-07-21:
|
||||
|
||||
- Fixed a bug where a reproducible $RANDOM sequence (after assigning a
|
||||
|
|
|
@ -48,9 +48,17 @@
|
|||
#include "edit.h"
|
||||
#include "shlex.h"
|
||||
|
||||
static char CURSOR_UP[20] = { ESC, '[', 'A', 0 };
|
||||
static char KILL_LINE[20] = { ESC, '[', 'J', 0 };
|
||||
|
||||
static char *CURSOR_UP = Empty; /* move cursor up one line */
|
||||
static char *ERASE_EOS = Empty; /* erase to end of screen */
|
||||
#if _tput_terminfo
|
||||
#define TPUT_CURSOR_UP "cuu1"
|
||||
#define TPUT_ERASE_EOS "ed"
|
||||
#elif _tput_termcap
|
||||
#define TPUT_CURSOR_UP "up"
|
||||
#define TPUT_ERASE_EOS "cd"
|
||||
#else
|
||||
#undef _pth_tput
|
||||
#endif /* _tput_terminfo */
|
||||
|
||||
#if SHOPT_MULTIBYTE
|
||||
# define is_cntrl(c) ((c<=STRIP) && iscntrl(c))
|
||||
|
@ -499,17 +507,11 @@ int tty_alt(register int fd)
|
|||
*/
|
||||
int ed_window(void)
|
||||
{
|
||||
int rows,cols;
|
||||
register char *cp = nv_getval(COLUMNS);
|
||||
if(cp)
|
||||
cols = (int)strtol(cp, (char**)0, 10)-1;
|
||||
else
|
||||
{
|
||||
astwinsize(2,&rows,&cols);
|
||||
int cols;
|
||||
sh_winsize(NIL(int*),&cols);
|
||||
if(--cols < 0)
|
||||
cols = DFLTWINDOW - 1;
|
||||
}
|
||||
if(cols < MINWINDOW)
|
||||
else if(cols < MINWINDOW)
|
||||
cols = MINWINDOW;
|
||||
else if(cols > MAXWINDOW)
|
||||
cols = MAXWINDOW;
|
||||
|
@ -546,23 +548,32 @@ void ed_ringbell(void)
|
|||
* send a carriage return line feed to the terminal
|
||||
*/
|
||||
|
||||
void ed_crlf(register Edit_t *ep)
|
||||
#ifdef _pth_tput
|
||||
/*
|
||||
* Get or update a tput (terminfo or termcap) capability string.
|
||||
*/
|
||||
static void get_tput(char *tp, char **cpp)
|
||||
{
|
||||
#ifdef cray
|
||||
ed_putchar(ep,'\r');
|
||||
#endif /* cray */
|
||||
#ifdef u370
|
||||
ed_putchar(ep,'\r');
|
||||
#endif /* u370 */
|
||||
#ifdef VENIX
|
||||
ed_putchar(ep,'\r');
|
||||
#endif /* VENIX */
|
||||
ed_putchar(ep,'\n');
|
||||
ed_flush(ep);
|
||||
Shopt_t o = sh.options;
|
||||
char *cp;
|
||||
sigblock(SIGINT);
|
||||
sh_offoption(SH_RESTRICTED);
|
||||
sh_offoption(SH_VERBOSE);
|
||||
sh_offoption(SH_XTRACE);
|
||||
sfprintf(sh.strbuf,".sh.value=$(" _pth_tput " %s 2>/dev/null)",tp);
|
||||
sh_trap(sfstruse(sh.strbuf),0);
|
||||
if((cp = nv_getval(SH_VALNOD)) && (!*cpp || strcmp(cp,*cpp)!=0))
|
||||
{
|
||||
if(*cpp && *cpp!=Empty)
|
||||
free(*cpp);
|
||||
*cpp = *cp ? sh_strdup(cp) : Empty;
|
||||
}
|
||||
#endif /* SHOPT_ESH || SHOPT_VSH */
|
||||
nv_unset(SH_VALNOD);
|
||||
sh.options = o;
|
||||
sigrelease(SIGINT);
|
||||
}
|
||||
#endif /* _pth_tput */
|
||||
|
||||
#if SHOPT_ESH || SHOPT_VSH
|
||||
/* ED_SETUP( max_prompt_size )
|
||||
*
|
||||
* This routine sets up the prompt string
|
||||
|
@ -584,17 +595,8 @@ void ed_setup(register Edit_t *ep, int fd, int reedit)
|
|||
register int qlen = 1, qwid;
|
||||
char inquote = 0;
|
||||
ep->e_fd = fd;
|
||||
#if SHOPT_ESH && SHOPT_VSH
|
||||
ep->e_multiline = sh_isoption(SH_MULTILINE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS) || sh_isoption(SH_VI));
|
||||
#elif SHOPT_ESH
|
||||
ep->e_multiline = sh_isoption(SH_MULTILINE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS));
|
||||
#else
|
||||
ep->e_multiline = sh_isoption(SH_MULTILINE) && sh_isoption(SH_VI);
|
||||
#endif
|
||||
sh_update_columns_lines();
|
||||
#ifdef SIGWINCH
|
||||
ep->e_multiline = sh_editor_active() && sh_isoption(SH_MULTILINE);
|
||||
sh.winch = 0;
|
||||
#endif
|
||||
#if SHOPT_EDPREDICT
|
||||
ep->hlist = 0;
|
||||
ep->nhlist = 0;
|
||||
|
@ -616,18 +618,7 @@ void ed_setup(register Edit_t *ep, int fd, int reedit)
|
|||
ep->e_hismax = ep->e_hismin = ep->e_hloff = 0;
|
||||
}
|
||||
ep->e_hline = ep->e_hismax;
|
||||
#if SHOPT_ESH && SHOPT_VSH
|
||||
if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
||||
#elif SHOPT_ESH
|
||||
if(!sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
||||
#elif SHOPT_VSH
|
||||
if(!sh_isoption(SH_VI))
|
||||
#else
|
||||
if(1)
|
||||
#endif /* SHOPT_ESH && SHOPT_VSH */
|
||||
ep->e_wsize = MAXLINE;
|
||||
else
|
||||
ep->e_wsize = ed_window()-2;
|
||||
ep->e_wsize = sh_editor_active() ? ed_window()-2 : MAXLINE;
|
||||
ep->e_winsz = ep->e_wsize+2;
|
||||
ep->e_crlf = 1;
|
||||
ep->e_plen = 0;
|
||||
|
@ -789,35 +780,17 @@ void ed_setup(register Edit_t *ep, int fd, int reedit)
|
|||
#if SHOPT_ESH || SHOPT_VSH
|
||||
if(ep->e_multiline)
|
||||
{
|
||||
#if defined(_pth_tput) && (_tput_terminfo || _tput_termcap)
|
||||
#ifdef _pth_tput
|
||||
char *term;
|
||||
if(!ep->e_term)
|
||||
ep->e_term = nv_search("TERM",sh.var_tree,0);
|
||||
if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname))
|
||||
{
|
||||
Shopt_t o = sh.options;
|
||||
sigblock(SIGINT);
|
||||
sh_offoption(SH_RESTRICTED);
|
||||
sh_offoption(SH_VERBOSE);
|
||||
sh_offoption(SH_XTRACE);
|
||||
/* get the cursor up sequence from tput */
|
||||
#if _tput_terminfo
|
||||
sh_trap(".sh.subscript=$(" _pth_tput " cuu1 2>/dev/null)",0);
|
||||
#elif _tput_termcap
|
||||
sh_trap(".sh.subscript=$(" _pth_tput " up 2>/dev/null)",0);
|
||||
#else
|
||||
#error no tput method
|
||||
#endif
|
||||
if((pp = nv_getval(SH_SUBSCRNOD)) && strlen(pp) < sizeof(CURSOR_UP))
|
||||
strcopy(CURSOR_UP,pp);
|
||||
else
|
||||
CURSOR_UP[0] = '\0'; /* no escape sequence is better than a faulty one */
|
||||
nv_unset(SH_SUBSCRNOD);
|
||||
get_tput(TPUT_CURSOR_UP,&CURSOR_UP);
|
||||
get_tput(TPUT_ERASE_EOS,&ERASE_EOS);
|
||||
strcopy(ep->e_termname,term);
|
||||
sh.options = o;
|
||||
sigrelease(SIGINT);
|
||||
}
|
||||
#endif
|
||||
#endif /* _pth_tput */
|
||||
ep->e_wsize = MAXLINE - (ep->e_plen+1);
|
||||
}
|
||||
#endif /* SHOPT_ESH || SHOPT_VSH */
|
||||
|
@ -880,47 +853,40 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
|
|||
{
|
||||
if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP))
|
||||
goto done;
|
||||
#ifdef SIGWINCH
|
||||
#if SHOPT_ESH || SHOPT_VSH
|
||||
#if SHOPT_ESH && SHOPT_VSH
|
||||
if(sh.winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
|
||||
#elif SHOPT_ESH
|
||||
if(sh.winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)))
|
||||
#else
|
||||
if(sh.winch && sh_isstate(SH_INTERACTIVE) && sh_isoption(SH_VI))
|
||||
#endif
|
||||
/*
|
||||
* If sh.winch is set, the number of window columns changed and/or there is a buffered
|
||||
* job notification. When using a line editor, erase and redraw the command line.
|
||||
*/
|
||||
if(sh.winch && sh_editor_active() && sh_isstate(SH_INTERACTIVE))
|
||||
{
|
||||
/* redraw the prompt after receiving SIGWINCH */
|
||||
Edpos_t lastpos;
|
||||
int n, rows, newsize;
|
||||
/* move cursor to start of first line */
|
||||
int n, newsize;
|
||||
char *cp;
|
||||
sh_winsize(NIL(int*),&newsize);
|
||||
/*
|
||||
* Try to move cursor to start of first line and pray it works... it's very
|
||||
* failure-prone if the window size changed, especially on modern terminals
|
||||
* that break the whole terminal abstraction by rewrapping lines themselves :(
|
||||
*/
|
||||
if(ep->e_multiline)
|
||||
{
|
||||
n = (ep->e_plen + ep->e_cur) / newsize;
|
||||
while(n--)
|
||||
ed_putstring(ep,CURSOR_UP);
|
||||
}
|
||||
ed_putchar(ep,'\r');
|
||||
ed_flush(ep);
|
||||
astwinsize(2,&rows,&newsize);
|
||||
n = (ep->e_plen+ep->e_cur)/++ep->e_winsz;
|
||||
while(n--)
|
||||
ed_putstring(ep,CURSOR_UP);
|
||||
if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz))
|
||||
{
|
||||
/* clear the current command line */
|
||||
n = lastpos.line;
|
||||
while(lastpos.line--)
|
||||
{
|
||||
ed_nputchar(ep,ep->e_winsz,' ');
|
||||
ed_putchar(ep,'\n');
|
||||
}
|
||||
ed_nputchar(ep,ep->e_winsz,' ');
|
||||
while(n--)
|
||||
ed_putstring(ep,CURSOR_UP);
|
||||
}
|
||||
ed_putstring(ep,ERASE_EOS);
|
||||
ed_flush(ep);
|
||||
sh_delay(.05,0);
|
||||
astwinsize(2,&rows,&newsize);
|
||||
/* show any buffered 'set -b' job notification(s) */
|
||||
if(sh.notifybuf && (cp = sfstruse(sh.notifybuf)) && *cp)
|
||||
sfputr(sfstderr, cp, -1);
|
||||
/* update window size */
|
||||
ep->e_winsz = newsize-1;
|
||||
if(ep->e_winsz < MINWINDOW)
|
||||
ep->e_winsz = MINWINDOW;
|
||||
if(!ep->e_multiline && ep->e_wsize < MAXLINE)
|
||||
ep->e_wsize = ep->e_winsz-2;
|
||||
/* redraw command line */
|
||||
#if SHOPT_ESH && SHOPT_VSH
|
||||
if(sh_isoption(SH_VI))
|
||||
vi_redraw(ep->e_vi);
|
||||
|
@ -930,11 +896,9 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
|
|||
vi_redraw(ep->e_vi);
|
||||
#elif SHOPT_ESH
|
||||
emacs_redraw(ep->e_emacs);
|
||||
#endif
|
||||
#endif /* SHOPT_ESH && SHOPT_VSH */
|
||||
}
|
||||
#endif /* SHOPT_ESH || SHOPT_VSH */
|
||||
sh.winch = 0;
|
||||
#endif /* SIGWINCH */
|
||||
/* an interrupt that should be ignored */
|
||||
errno = 0;
|
||||
if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
|
||||
|
@ -1842,7 +1806,7 @@ void ed_histlist(Edit_t *ep,int n)
|
|||
ep->hlist = 0;
|
||||
ep->nhlist = 0;
|
||||
}
|
||||
ed_putstring(ep,KILL_LINE);
|
||||
ed_putstring(ep,ERASE_EOS);
|
||||
if(n)
|
||||
{
|
||||
for(i=1; (mp= *mpp) && i <= 16 ; i++,mpp++)
|
||||
|
|
|
@ -613,7 +613,7 @@ update:
|
|||
}
|
||||
continue;
|
||||
case cntl('L'):
|
||||
ed_crlf(ep->ed);
|
||||
putchar(ep->ed,'\n');
|
||||
draw(ep,REFRESH);
|
||||
continue;
|
||||
case ESC :
|
||||
|
@ -733,7 +733,8 @@ process:
|
|||
{
|
||||
out[eol++] = '\n';
|
||||
out[eol] = '\0';
|
||||
ed_crlf(ep->ed);
|
||||
putchar(ep->ed,'\n');
|
||||
ed_flush(ep->ed);
|
||||
}
|
||||
#if SHOPT_MULTIBYTE
|
||||
ed_external(out,buff);
|
||||
|
|
|
@ -474,7 +474,10 @@ int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit
|
|||
pr_string(vp,Prompt);
|
||||
putstring(vp,0, last_phys+1);
|
||||
if(echoctl)
|
||||
ed_crlf(vp->ed);
|
||||
{
|
||||
putchar('\n');
|
||||
ed_flush(vp->ed);
|
||||
}
|
||||
else
|
||||
while(kill_erase-- > 0)
|
||||
putchar(' ');
|
||||
|
@ -483,7 +486,10 @@ int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit
|
|||
if( term_char=='\n' )
|
||||
{
|
||||
if(!echoctl)
|
||||
ed_crlf(vp->ed);
|
||||
{
|
||||
putchar('\n');
|
||||
ed_flush(vp->ed);
|
||||
}
|
||||
virtual[++last_virt] = '\n';
|
||||
}
|
||||
vp->last_cmd = 'i';
|
||||
|
@ -596,7 +602,8 @@ int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit
|
|||
if( vp->addnl )
|
||||
{
|
||||
virtual[++last_virt] = '\n';
|
||||
ed_crlf(vp->ed);
|
||||
putchar('\n');
|
||||
ed_flush(vp->ed);
|
||||
}
|
||||
if( ++last_virt >= 0 )
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ tput_terminfo note{ does tput support terminfo codes }end run{
|
|||
\"/*/tput\")
|
||||
tput=`echo "${_pth_tput}" | sed 's/^"//; s/"$//'`
|
||||
if "$tput" sgr0 >/dev/null 2>&1 &&
|
||||
"$tput" ed >/dev/null 2>&1 &&
|
||||
"$tput" cuu1 >/dev/null 2>&1
|
||||
then echo '#define _tput_terminfo 1 /* tput supports terminfo codes */'
|
||||
else echo '#define _tput_terminfo 0 /* tput does not support terminfo codes */'
|
||||
|
@ -29,6 +30,7 @@ tput_termcap note{ does tput support termcap codes }end run{
|
|||
\"/*/tput\")
|
||||
tput=`echo "${_pth_tput}" | sed 's/^"//; s/"$//'`
|
||||
if "$tput" me >/dev/null 2>&1 &&
|
||||
"$tput" cd >/dev/null 2>&1 &&
|
||||
"$tput" up >/dev/null 2>&1
|
||||
then echo '#define _tput_termcap 1 /* tput supports termcap codes */'
|
||||
else echo '#define _tput_termcap 0 /* tput does not support termcap codes */'
|
||||
|
|
|
@ -174,7 +174,6 @@ typedef struct edit
|
|||
(c<'J'?c+1-'A':(c+10-'J'))))))))))))))))
|
||||
#endif
|
||||
|
||||
extern void ed_crlf(Edit_t*);
|
||||
extern void ed_putchar(Edit_t*, int);
|
||||
extern void ed_ringbell(void);
|
||||
extern void ed_setup(Edit_t*,int, int);
|
||||
|
|
|
@ -117,7 +117,7 @@ struct checkpt
|
|||
|
||||
extern noreturn void sh_done(int);
|
||||
extern void sh_fault(int);
|
||||
extern void sh_update_columns_lines(void);
|
||||
extern void sh_winsize(int*,int*);
|
||||
extern void sh_sigclear(int);
|
||||
extern void sh_sigdone(void);
|
||||
extern void sh_siginit(void);
|
||||
|
|
|
@ -52,6 +52,21 @@
|
|||
struct ionod;
|
||||
#endif /* !ARG_RAW */
|
||||
|
||||
/*
|
||||
* Check if there is an editor active while avoiding repetitive #if flaggery.
|
||||
* The 0 definition is used to optimize out code if no editor is compiled in.
|
||||
* (This is here and not in edit.h because io.h is far more widely included.)
|
||||
*/
|
||||
#if SHOPT_ESH && SHOPT_VSH
|
||||
#define sh_editor_active() (sh_isoption(SH_VI) || sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS))
|
||||
#elif SHOPT_ESH
|
||||
#define sh_editor_active() (sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS))
|
||||
#elif SHOPT_VSH
|
||||
#define sh_editor_active() (sh_isoption(SH_VI)!=0)
|
||||
#else
|
||||
#define sh_editor_active() 0
|
||||
#endif
|
||||
|
||||
extern int sh_iocheckfd(int);
|
||||
extern void sh_ioinit(void);
|
||||
extern int sh_iomovefd(int);
|
||||
|
|
|
@ -312,9 +312,7 @@ struct Shell_s
|
|||
char funload;
|
||||
char used_pos; /* used positional parameter */
|
||||
char universe;
|
||||
#ifdef SIGWINCH
|
||||
char winch;
|
||||
#endif
|
||||
char winch; /* set upon window size change or 'set -b' notification */
|
||||
short arithrecursion; /* current arithmetic recursion level */
|
||||
char indebug; /* set when in debug trap */
|
||||
unsigned char ignsig; /* ignored signal in subshell */
|
||||
|
@ -366,6 +364,7 @@ struct Shell_s
|
|||
void *mktype;
|
||||
Sfio_t *strbuf;
|
||||
Sfio_t *strbuf2;
|
||||
Sfio_t *notifybuf; /* for 'set -o notify' job notices */
|
||||
Dt_t *first_root;
|
||||
Dt_t *prefix_root;
|
||||
Dt_t *last_root;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
||||
#define SH_RELEASE_DATE "2022-07-21" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_DATE "2022-07-24" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
|
||||
|
||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||
|
|
|
@ -7513,6 +7513,10 @@ above) are only exported while their name space is active.
|
|||
.B \-b
|
||||
Prints job completion messages as soon as a background job changes
|
||||
state rather than waiting for the next prompt.
|
||||
If one of the shell line editors is in use (see
|
||||
.I In-line Editing Options
|
||||
above), the completion message is inserted directly above the
|
||||
command line being typed.
|
||||
.TP 8
|
||||
.B \-e
|
||||
Unless contained in a
|
||||
|
|
|
@ -74,10 +74,7 @@ void sh_fault(register int sig)
|
|||
sig &= ~SH_TRAP;
|
||||
#ifdef SIGWINCH
|
||||
if(sig==SIGWINCH)
|
||||
{
|
||||
sh_update_columns_lines();
|
||||
sh.winch = 1;
|
||||
}
|
||||
sh_winsize(NIL(int*),NIL(int*));
|
||||
#endif /* SIGWINCH */
|
||||
trap = sh.st.trapcom[sig];
|
||||
if(sh.savesig)
|
||||
|
@ -227,17 +224,35 @@ done:
|
|||
}
|
||||
|
||||
/*
|
||||
* update $COLUMNS and $LINES
|
||||
* Get window size and update LINES and COLUMNS.
|
||||
* Returns the sizes in the pointed-to ints if non-null.
|
||||
* If the number of columns changed, flags a window size change in sh.winch.
|
||||
*/
|
||||
void sh_update_columns_lines(void)
|
||||
void sh_winsize(int *linesp, int *columnsp)
|
||||
{
|
||||
int rows=0, cols=0;
|
||||
int32_t v;
|
||||
astwinsize(2,&rows,&cols);
|
||||
if(v = cols)
|
||||
nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY);
|
||||
if(v = rows)
|
||||
nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY);
|
||||
static int oldlines, oldcolumns;
|
||||
int lines = oldlines, columns = oldcolumns;
|
||||
int32_t i;
|
||||
astwinsize(2,&lines,&columns);
|
||||
if(linesp)
|
||||
*linesp = lines;
|
||||
if(columnsp)
|
||||
*columnsp = columns;
|
||||
/*
|
||||
* Update LINES and COLUMNS only when the values changed; this makes
|
||||
* LINES.set and COLUMNS.set shell discipline functions more useful.
|
||||
*/
|
||||
if((lines != oldlines || nv_isnull(LINES)) && (i = (int32_t)lines))
|
||||
{
|
||||
nv_putval(LINES, (char*)&i, NV_INT32|NV_RDONLY);
|
||||
oldlines = lines;
|
||||
}
|
||||
if((columns != oldcolumns || nv_isnull(COLUMNS)) && (i = (int32_t)columns))
|
||||
{
|
||||
nv_putval(COLUMNS, (char*)&i, NV_INT32|NV_RDONLY);
|
||||
oldcolumns = columns;
|
||||
sh.winch = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -662,18 +677,8 @@ noreturn void sh_done(register int sig)
|
|||
#if SHOPT_ACCT
|
||||
sh_accend();
|
||||
#endif /* SHOPT_ACCT */
|
||||
#if SHOPT_VSH || SHOPT_ESH
|
||||
if(mbwide()
|
||||
#if SHOPT_ESH
|
||||
|| sh_isoption(SH_EMACS)
|
||||
|| sh_isoption(SH_GMACS)
|
||||
#endif
|
||||
#if SHOPT_VSH
|
||||
|| sh_isoption(SH_VI)
|
||||
#endif
|
||||
)
|
||||
if(mbwide() && sh_editor_active())
|
||||
tty_cooked(-1);
|
||||
#endif /* SHOPT_VSH || SHOPT_ESH */
|
||||
#ifdef JOBS
|
||||
if((sh_isoption(SH_INTERACTIVE) && sh_isoption(SH_LOGIN_SHELL)) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP)))
|
||||
job_walk(sfstderr, job_hup, SIGHUP, NIL(char**));
|
||||
|
|
|
@ -2445,8 +2445,8 @@ void sh_menu(Sfio_t *outfile,int argn,char *argv[])
|
|||
register char **arg;
|
||||
int nrow, ncol=1, ndigits=1;
|
||||
int fldsize, wsize = ed_window();
|
||||
char *cp = nv_getval(sh_scoped(LINES));
|
||||
nrow = (cp?1+2*((int)strtol(cp, (char**)0, 10)/3):NROW);
|
||||
sh_winsize(&nrow,NIL(int*));
|
||||
nrow = nrow ? (2 * (nrow / 3) + 1) : NROW;
|
||||
for(i=argn;i >= 10;i /= 10)
|
||||
ndigits++;
|
||||
if(argn < nrow)
|
||||
|
|
|
@ -292,7 +292,6 @@ int job_reap(register int sig)
|
|||
int nochild = 0, oerrno = errno, wstat;
|
||||
Waitevent_f waitevent = sh.waitevent;
|
||||
static int wcontinued = WCONTINUED;
|
||||
int was_ttywait_on;
|
||||
if (vmbusy())
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_warn(0),"vmbusy() inside job_reap() -- should not happen");
|
||||
|
@ -310,18 +309,14 @@ int job_reap(register int sig)
|
|||
else
|
||||
flags = WUNTRACED|wcontinued;
|
||||
sh.waitevent = 0;
|
||||
was_ttywait_on = sh_isstate(SH_TTYWAIT); /* save tty wait state */
|
||||
while(1)
|
||||
{
|
||||
if(!(flags&WNOHANG) && !sh.intrap && job.pwlist)
|
||||
{
|
||||
sh_onstate(SH_TTYWAIT);
|
||||
if(waitevent && (*waitevent)(-1,-1L,0))
|
||||
flags |= WNOHANG;
|
||||
}
|
||||
pid = waitpid((pid_t)-1,&wstat,flags);
|
||||
if(!was_ttywait_on)
|
||||
sh_offstate(SH_TTYWAIT);
|
||||
|
||||
/*
|
||||
* some systems (Linux 2.6) may return EINVAL
|
||||
|
@ -331,11 +326,21 @@ int job_reap(register int sig)
|
|||
if (pid<0 && errno==EINVAL && (flags&WCONTINUED))
|
||||
pid = waitpid((pid_t)-1,&wstat,flags&=~WCONTINUED);
|
||||
sh_sigcheck();
|
||||
if(pid<0 && errno==EINTR && (sig||job.savesig))
|
||||
if(pid<0)
|
||||
{
|
||||
if(errno==EINTR && (sig||job.savesig))
|
||||
{
|
||||
errno = 0;
|
||||
continue;
|
||||
}
|
||||
if(errno==ECHILD)
|
||||
{
|
||||
#if SHOPT_BGX
|
||||
job.numbjob = 0;
|
||||
#endif /* SHOPT_BGX */
|
||||
nochild = 1;
|
||||
}
|
||||
}
|
||||
if(pid<=0)
|
||||
break;
|
||||
if(wstat==0)
|
||||
|
@ -474,23 +479,29 @@ int job_reap(register int sig)
|
|||
sh.sigflag[SIGCHLD] |= SH_SIGTRAP;
|
||||
sh.trapnote |= SH_SIGTRAP;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if(errno==ECHILD)
|
||||
#endif /* !SHOPT_BGX */
|
||||
/* Handle -b/--notify while waiting for command line input */
|
||||
if(sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT))
|
||||
{
|
||||
#if SHOPT_BGX
|
||||
job.numbjob = 0;
|
||||
#endif /* SHOPT_BGX */
|
||||
nochild = 1;
|
||||
if(sh_editor_active())
|
||||
{
|
||||
/* Buffer the notification for ed_read() to show */
|
||||
if(!sh.notifybuf)
|
||||
sh.notifybuf = sfstropen();
|
||||
outfile = sh.notifybuf;
|
||||
job_list(pw,JOB_NFLAG);
|
||||
sh.winch = 1;
|
||||
}
|
||||
sh.waitevent = waitevent;
|
||||
if(pw && sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT))
|
||||
else
|
||||
{
|
||||
outfile = sfstderr;
|
||||
job_list(pw,JOB_NFLAG|JOB_NLFLAG);
|
||||
job_unpost(pw,1);
|
||||
sfsync(sfstderr);
|
||||
}
|
||||
job_unpost(pw,1);
|
||||
}
|
||||
}
|
||||
sh.waitevent = waitevent;
|
||||
if(sig)
|
||||
signal(sig, job_waitsafe);
|
||||
/*
|
||||
|
@ -897,7 +908,8 @@ int job_walk(Sfio_t *file,int (*fun)(struct process*,int),int arg,char *joblist[
|
|||
/*
|
||||
* list the given job
|
||||
* flag JOB_LFLAG for long listing
|
||||
* flag JOB_NFLAG for list only jobs marked for notification
|
||||
* flag JOB_NFLAG to list only jobs marked for notification
|
||||
* flag JOB_NLFLAG to print an initial newline
|
||||
* flag JOB_PFLAG for process ID(s) only
|
||||
*/
|
||||
int job_list(struct process *pw,register int flag)
|
||||
|
|
|
@ -341,22 +341,18 @@ int sh_main(int ac, char *av[], Shinit_f userinit)
|
|||
sh_onstate(SH_INTERACTIVE);
|
||||
#if SHOPT_ESH
|
||||
/* do not leave users without a line editor */
|
||||
if(!sh_isoption(SH_GMACS)
|
||||
#if SHOPT_VSH
|
||||
&& !sh_isoption(SH_VI)
|
||||
#endif /* SHOPT_VSH */
|
||||
&& !is_option(&sh.offoptions,SH_EMACS))
|
||||
if(!sh_editor_active() && !is_option(&sh.offoptions,SH_EMACS))
|
||||
sh_onoption(SH_EMACS);
|
||||
#endif /* SHOPT_ESH */
|
||||
}
|
||||
#ifdef SIGWINCH
|
||||
else
|
||||
{
|
||||
/* keep $COLUMNS and $LINES up to date even for scripts that don't trap SIGWINCH */
|
||||
sh_update_columns_lines();
|
||||
sh_winsize(NIL(int*),NIL(int*));
|
||||
#ifdef SIGWINCH
|
||||
signal(SIGWINCH,sh_fault);
|
||||
#endif /* SIGWINCH */
|
||||
}
|
||||
#endif
|
||||
/* (Re)set PS4 and IFS, but don't export these now even if allexport is on. */
|
||||
i = (sh_isoption(SH_ALLEXPORT) != 0);
|
||||
sh_offoption(SH_ALLEXPORT);
|
||||
|
|
|
@ -1014,5 +1014,15 @@ r echo
|
|||
r ^ok\r\n$
|
||||
!
|
||||
|
||||
tst $LINENO <<"!"
|
||||
L --notify does not report all simultaneously terminated jobs
|
||||
|
||||
d 15
|
||||
p :test-1:
|
||||
w set -b; sleep .1 & sleep .1 & sleep .1 &
|
||||
u Done
|
||||
u Done
|
||||
u Done
|
||||
!
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
Loading…
Reference in a new issue