implement tty tracking and bump to R41 for feature completeness
• tty_fd is now never closed • new tty_hasstate tracks tty_state (cf. thread around http://article.gmane.org/gmane.os.miros.mksh/79 and PLD bug) • as users requested, importing COLUMNS or LINES from the environment now removes its special-ness as does unsetting it • otherwise, setting COLUMNS or LINES is honoured until the next SIGWINCH arrives or change_winsz is otherwise run (e.g. before displaying the prompt in the interactive command line editing modes) • SIGWINCH is now honoured before each reading of $COLUMNS and $LINES too • change the Uhr to match – it no longer calls stty(1) ☺
This commit is contained in:
parent
730bc915bd
commit
77c4cb88e4
6
check.t
6
check.t
@ -1,4 +1,4 @@
|
||||
# $MirOS: src/bin/mksh/check.t,v 1.568 2012/11/26 23:14:44 tg Exp $
|
||||
# $MirOS: src/bin/mksh/check.t,v 1.569 2012/11/30 19:25:01 tg Exp $
|
||||
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
|
||||
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
|
||||
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
|
||||
@ -29,7 +29,7 @@
|
||||
# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
|
||||
|
||||
expected-stdout:
|
||||
@(#)MIRBSD KSH R40 2012/11/26
|
||||
@(#)MIRBSD KSH R41 2012/11/30
|
||||
description:
|
||||
Check version of shell.
|
||||
stdin:
|
||||
@ -38,7 +38,7 @@ name: KSH_VERSION
|
||||
category: shell:legacy-no
|
||||
---
|
||||
expected-stdout:
|
||||
@(#)LEGACY KSH R40 2012/11/26
|
||||
@(#)LEGACY KSH R41 2012/11/30
|
||||
description:
|
||||
Check version of legacy shell.
|
||||
stdin:
|
||||
|
62
jobs.c
62
jobs.c
@ -22,7 +22,7 @@
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.90 2012/11/30 19:20:01 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.91 2012/11/30 19:25:03 tg Exp $");
|
||||
|
||||
#if HAVE_KILLPG
|
||||
#define mksh_killpg killpg
|
||||
@ -152,6 +152,9 @@ static void put_job(Job *, int);
|
||||
static void remove_job(Job *, const char *);
|
||||
static int kill_job(Job *, int);
|
||||
|
||||
static void tty_init_talking(void);
|
||||
static void tty_init_state(void);
|
||||
|
||||
/* initialise job control */
|
||||
void
|
||||
j_init(void)
|
||||
@ -201,13 +204,15 @@ j_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* j_change() calls tty_init() */
|
||||
/* j_change() calls tty_init_talking() and tty_init_state() */
|
||||
if (Flag(FMONITOR))
|
||||
j_change();
|
||||
else
|
||||
#endif
|
||||
if (Flag(FTALKING))
|
||||
tty_init(true, true);
|
||||
if (Flag(FTALKING)) {
|
||||
tty_init_talking();
|
||||
tty_init_state();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -286,9 +291,9 @@ j_change(void)
|
||||
if (Flag(FMONITOR)) {
|
||||
bool use_tty = Flag(FTALKING);
|
||||
|
||||
/* Don't call tcgetattr() 'til we own the tty process group */
|
||||
/* don't call mksh_tcget until we own the tty process group */
|
||||
if (use_tty)
|
||||
tty_init(false, true);
|
||||
tty_init_talking();
|
||||
|
||||
/* no controlling tty, no SIGT* */
|
||||
if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
|
||||
@ -334,8 +339,6 @@ j_change(void)
|
||||
warningf(false, "%s: %s", "warning",
|
||||
"won't have full job control");
|
||||
#endif
|
||||
if (tty_fd >= 0)
|
||||
mksh_tcget(tty_fd, &tty_state);
|
||||
} else {
|
||||
ttypgrp_ok = false;
|
||||
if (Flag(FTALKING))
|
||||
@ -351,9 +354,8 @@ j_change(void)
|
||||
SIG_IGN : SIG_DFL,
|
||||
SS_RESTORE_ORIG|SS_FORCE);
|
||||
}
|
||||
if (!Flag(FTALKING))
|
||||
tty_close();
|
||||
}
|
||||
tty_init_state();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1136,7 +1138,7 @@ j_waitj(Job *j,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (tty_fd >= 0) {
|
||||
if (tty_hasstate) {
|
||||
/*
|
||||
* Only restore tty settings if job was originally
|
||||
* started in the foreground. Problems can be
|
||||
@ -1803,3 +1805,41 @@ kill_job(Job *j, int sig)
|
||||
rval = -1;
|
||||
return (rval);
|
||||
}
|
||||
|
||||
static void
|
||||
tty_init_talking(void)
|
||||
{
|
||||
switch (tty_init_fd()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
warningf(false, "%s: %s %s: %s",
|
||||
"No controlling tty", "open", "/dev/tty",
|
||||
strerror(errno));
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
warningf(false, "%s: %s", "can't find tty fd", strerror(errno));
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
warningf(false, "%s: %s %s: %s", "j_ttyinit",
|
||||
"dup of tty fd", "failed", strerror(errno));
|
||||
break;
|
||||
case 4:
|
||||
warningf(false, "%s: %s: %s", "j_ttyinit",
|
||||
"can't set close-on-exec flag", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tty_init_state(void)
|
||||
{
|
||||
if (tty_fd >= 0) {
|
||||
mksh_tcget(tty_fd, &tty_state);
|
||||
tty_hasstate = true;
|
||||
}
|
||||
}
|
||||
|
123
main.c
123
main.c
@ -34,7 +34,7 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.242 2012/11/30 19:02:09 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.243 2012/11/30 19:25:04 tg Exp $");
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@ -64,7 +64,7 @@ static const char initsubs[] =
|
||||
static const char *initcoms[] = {
|
||||
Ttypeset, "-r", initvsn, NULL,
|
||||
Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
|
||||
Ttypeset, "-i10", "SECONDS", "TMOUT", NULL,
|
||||
Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
|
||||
Talias,
|
||||
"integer=typeset -i",
|
||||
Tlocal_typeset,
|
||||
@ -278,12 +278,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
|
||||
init_histvec();
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
/* try to initialise tty size before importing environment */
|
||||
tty_init(false, false);
|
||||
/* initialise tty size before importing environment */
|
||||
change_winsz();
|
||||
tty_close();
|
||||
#endif
|
||||
|
||||
#ifdef _PATH_DEFPATH
|
||||
def_path = _PATH_DEFPATH;
|
||||
@ -368,8 +364,6 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
while (*wp != NULL)
|
||||
wp++;
|
||||
}
|
||||
setint_n(global("COLUMNS"), 0, 10);
|
||||
setint_n(global("LINES"), 0, 10);
|
||||
setint_n(global("OPTIND"), 1, 10);
|
||||
|
||||
kshuid = getuid();
|
||||
@ -518,7 +512,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
|
||||
|
||||
/* initialise job control */
|
||||
j_init();
|
||||
/* Do this after j_init(), as tty_fd is not initialised until then */
|
||||
/* do this after j_init() which calls tty_init_state() */
|
||||
if (Flag(FTALKING)) {
|
||||
if (utf_flag == 2) {
|
||||
#ifndef MKSH_ASSUME_UTF8
|
||||
@ -1056,75 +1050,76 @@ remove_temps(struct temp *tp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise tty_fd. Used for saving/reseting tty modes upon
|
||||
* foreground job completion and for setting up tty process group.
|
||||
* Initialise tty_fd. Used for tracking the size of the terminal,
|
||||
* saving/resetting tty modes upon forground job completion, and
|
||||
* for setting up the tty process group. Return values:
|
||||
* 0 = got controlling tty
|
||||
* 1 = got terminal but no controlling tty
|
||||
* 2 = cannot find a terminal
|
||||
* 3 = cannot dup fd
|
||||
* 4 = cannot make fd close-on-exec
|
||||
* An existing tty_fd is cached if no "better" one could be found,
|
||||
* i.e. if tty_devtty was already set or the new would not set it.
|
||||
*/
|
||||
void
|
||||
tty_init(bool init_ttystate, bool need_tty)
|
||||
int
|
||||
tty_init_fd(void)
|
||||
{
|
||||
bool do_close = true;
|
||||
int tfd;
|
||||
int fd, rv, eno = 0;
|
||||
bool do_close = false, is_devtty = true;
|
||||
|
||||
if (tty_fd >= 0) {
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
if (tty_devtty) {
|
||||
/* already got a tty which is /dev/tty */
|
||||
return (0);
|
||||
}
|
||||
tty_devtty = true;
|
||||
|
||||
#ifdef _UWIN
|
||||
/*XXX imake style */
|
||||
if (isatty(3)) {
|
||||
/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
|
||||
tfd = 3;
|
||||
do_close = false;
|
||||
} else
|
||||
#endif
|
||||
if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
|
||||
tty_devtty = false;
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
if (need_tty)
|
||||
warningf(false, "%s: %s %s: %s",
|
||||
"No controlling tty", "open", "/dev/tty",
|
||||
strerror(errno));
|
||||
#endif
|
||||
fd = 3;
|
||||
goto got_fd;
|
||||
}
|
||||
if (tfd < 0) {
|
||||
do_close = false;
|
||||
if (isatty(0))
|
||||
tfd = 0;
|
||||
else if (isatty(2))
|
||||
tfd = 2;
|
||||
else {
|
||||
#ifndef MKSH_DISABLE_TTY_WARNING
|
||||
if (need_tty)
|
||||
warningf(false, "can't find tty fd");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
|
||||
do_close = true;
|
||||
goto got_fd;
|
||||
}
|
||||
if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) {
|
||||
if (need_tty)
|
||||
warningf(false, "%s: %s %s: %s", "j_ttyinit",
|
||||
"dup of tty fd", "failed", strerror(errno));
|
||||
} else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) {
|
||||
if (need_tty)
|
||||
warningf(false, "%s: %s: %s", "j_ttyinit",
|
||||
"can't set close-on-exec flag", strerror(errno));
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
} else if (init_ttystate)
|
||||
mksh_tcget(tty_fd, &tty_state);
|
||||
if (do_close)
|
||||
close(tfd);
|
||||
}
|
||||
eno = errno;
|
||||
|
||||
void
|
||||
tty_close(void)
|
||||
{
|
||||
if (tty_fd >= 0) {
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
/* already got a non-devtty one */
|
||||
rv = 1;
|
||||
goto out;
|
||||
}
|
||||
is_devtty = false;
|
||||
|
||||
if (isatty((fd = 0)) || isatty((fd = 2)))
|
||||
goto got_fd;
|
||||
/* cannot find one */
|
||||
rv = 2;
|
||||
/* assert: do_close == false */
|
||||
goto out;
|
||||
|
||||
got_fd:
|
||||
if ((rv = fcntl(fd, F_DUPFD, FDBASE)) < 0) {
|
||||
eno = errno;
|
||||
rv = 3;
|
||||
goto out;
|
||||
}
|
||||
if (fcntl(rv, F_SETFD, FD_CLOEXEC) < 0) {
|
||||
eno = errno;
|
||||
close(rv);
|
||||
rv = 4;
|
||||
goto out;
|
||||
}
|
||||
tty_fd = rv;
|
||||
tty_devtty = is_devtty;
|
||||
rv = eno = 0;
|
||||
out:
|
||||
if (do_close)
|
||||
close(fd);
|
||||
errno = eno;
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/* A shell error occurred (eg, syntax error, etc.) */
|
||||
|
12
mksh.1
12
mksh.1
@ -1,4 +1,4 @@
|
||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.300 2012/11/26 22:49:49 tg Exp $
|
||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.301 2012/11/30 19:25:05 tg Exp $
|
||||
.\" $OpenBSD: ksh.1,v 1.144 2012/07/08 08:13:20 guenther Exp $
|
||||
.\"-
|
||||
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
@ -74,7 +74,7 @@
|
||||
.\" with -mandoc, it might implement .Mx itself, but we want to
|
||||
.\" use our own definition. And .Dd must come *first*, always.
|
||||
.\"
|
||||
.Dd $Mdocdate: November 26 2012 $
|
||||
.Dd $Mdocdate: November 30 2012 $
|
||||
.\"
|
||||
.\" Check which macro package we use, and do other -mdoc setup.
|
||||
.\"
|
||||
@ -1785,7 +1785,7 @@ Set to the number of columns on the terminal or window.
|
||||
Always set, defaults to 80, unless the
|
||||
value as reported by
|
||||
.Xr stty 1
|
||||
is non-zero and sane enough; similar for
|
||||
is non-zero and sane enough (minimum is 12x3); similar for
|
||||
.Ev LINES .
|
||||
This parameter is used by the interactive line editing modes, and by the
|
||||
.Ic select ,
|
||||
@ -1793,6 +1793,8 @@ This parameter is used by the interactive line editing modes, and by the
|
||||
and
|
||||
.Ic kill \-l
|
||||
commands to format information columns.
|
||||
Importing from the environment or unsetting this parameter removes the
|
||||
binding to the actual terminal size in favour of the provided value.
|
||||
.It Ev ENV
|
||||
If this parameter is found to be set after any profile files are executed, the
|
||||
expanded value is used as a shell startup file.
|
||||
@ -1880,6 +1882,8 @@ executed.
|
||||
.It Ev LINES
|
||||
Set to the number of lines on the terminal or window.
|
||||
Always set, defaults to 24.
|
||||
See
|
||||
.Ev COLUMNS .
|
||||
.It Ev EPOCHREALTIME
|
||||
Time since the epoch, as returned by
|
||||
.Xr gettimeofday 2 ,
|
||||
@ -3665,7 +3669,7 @@ Approximately the same as the
|
||||
.Xr printf 1 ,
|
||||
utility, except it uses the same
|
||||
.Sx Backslash expansion
|
||||
and I/O code as the rest of
|
||||
and I/O code and does hot handle floating point as the rest of
|
||||
.Nm mksh .
|
||||
This is not normally part of
|
||||
.Nm mksh ;
|
||||
|
8
sh.h
8
sh.h
@ -157,9 +157,9 @@
|
||||
#endif
|
||||
|
||||
#ifdef EXTERN
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.604 2012/11/26 23:14:46 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.605 2012/11/30 19:25:06 tg Exp $");
|
||||
#endif
|
||||
#define MKSH_VERSION "R40 2012/11/26"
|
||||
#define MKSH_VERSION "R41 2012/11/30"
|
||||
|
||||
/* arithmetic types: C implementation */
|
||||
#if !HAVE_CAN_INTTYPES
|
||||
@ -2043,9 +2043,9 @@ int test_parse(Test_env *);
|
||||
EXTERN int tty_fd E_INIT(-1); /* dup'd tty file descriptor */
|
||||
EXTERN bool tty_devtty; /* true if tty_fd is from /dev/tty */
|
||||
EXTERN mksh_ttyst tty_state; /* saved tty state */
|
||||
EXTERN bool tty_hasstate; /* true if tty_state is valid */
|
||||
|
||||
extern void tty_init(bool, bool);
|
||||
extern void tty_close(void);
|
||||
extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */
|
||||
|
||||
/* be sure not to interfere with anyone else's idea about EXTERN */
|
||||
#ifdef EXTERN_DEFINED
|
||||
|
26
var.c
26
var.c
@ -27,7 +27,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.160 2012/11/30 19:02:10 tg Exp $");
|
||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.161 2012/11/30 19:25:08 tg Exp $");
|
||||
|
||||
/*-
|
||||
* Variables
|
||||
@ -1090,10 +1090,8 @@ getspec(struct tbl *vp)
|
||||
struct timeval tv;
|
||||
|
||||
switch ((st = special(vp->name))) {
|
||||
case V_BASHPID:
|
||||
i = (mksh_ari_t)procpid;
|
||||
break;
|
||||
case V_COLUMNS:
|
||||
case V_LINES:
|
||||
/*
|
||||
* Do NOT export COLUMNS/LINES. Many applications
|
||||
* check COLUMNS/LINES before checking ws.ws_col/row,
|
||||
@ -1101,6 +1099,15 @@ getspec(struct tbl *vp)
|
||||
* and the window is then resized, the app won't
|
||||
* see the change cause the environ doesn't change.
|
||||
*/
|
||||
if (got_winch)
|
||||
change_winsz();
|
||||
break;
|
||||
}
|
||||
switch (st) {
|
||||
case V_BASHPID:
|
||||
i = (mksh_ari_t)procpid;
|
||||
break;
|
||||
case V_COLUMNS:
|
||||
i = x_cols;
|
||||
break;
|
||||
case V_HISTSIZE:
|
||||
@ -1197,9 +1204,16 @@ setspec(struct tbl *vp)
|
||||
return;
|
||||
/* common sub-cases */
|
||||
case V_COLUMNS:
|
||||
case V_LINES:
|
||||
if (vp->flag & IMPORT) {
|
||||
/* do not touch */
|
||||
unspecial(vp->name);
|
||||
vp->flag &= ~SPECIAL;
|
||||
return;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case V_HISTSIZE:
|
||||
case V_LINENO:
|
||||
case V_LINES:
|
||||
case V_OPTIND:
|
||||
case V_RANDOM:
|
||||
case V_SECONDS:
|
||||
@ -1466,7 +1480,7 @@ change_winsz(void)
|
||||
{
|
||||
#ifdef TIOCGWINSZ
|
||||
/* check if window size has changed */
|
||||
if (tty_fd >= 0) {
|
||||
if (tty_init_fd() < 2) {
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user