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:
tg 2012-11-30 19:25:08 +00:00
parent 730bc915bd
commit 77c4cb88e4
6 changed files with 145 additions and 92 deletions

View File

@ -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: 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: 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 $ # $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 # http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R40 2012/11/26 @(#)MIRBSD KSH R41 2012/11/30
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -38,7 +38,7 @@ name: KSH_VERSION
category: shell:legacy-no category: shell:legacy-no
--- ---
expected-stdout: expected-stdout:
@(#)LEGACY KSH R40 2012/11/26 @(#)LEGACY KSH R41 2012/11/30
description: description:
Check version of legacy shell. Check version of legacy shell.
stdin: stdin:

62
jobs.c
View File

@ -22,7 +22,7 @@
#include "sh.h" #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 #if HAVE_KILLPG
#define mksh_killpg killpg #define mksh_killpg killpg
@ -152,6 +152,9 @@ static void put_job(Job *, int);
static void remove_job(Job *, const char *); static void remove_job(Job *, const char *);
static int kill_job(Job *, int); static int kill_job(Job *, int);
static void tty_init_talking(void);
static void tty_init_state(void);
/* initialise job control */ /* initialise job control */
void void
j_init(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)) if (Flag(FMONITOR))
j_change(); j_change();
else else
#endif #endif
if (Flag(FTALKING)) if (Flag(FTALKING)) {
tty_init(true, true); tty_init_talking();
tty_init_state();
}
} }
static int static int
@ -286,9 +291,9 @@ j_change(void)
if (Flag(FMONITOR)) { if (Flag(FMONITOR)) {
bool use_tty = Flag(FTALKING); 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) if (use_tty)
tty_init(false, true); tty_init_talking();
/* no controlling tty, no SIGT* */ /* no controlling tty, no SIGT* */
if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) { if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
@ -334,8 +339,6 @@ j_change(void)
warningf(false, "%s: %s", "warning", warningf(false, "%s: %s", "warning",
"won't have full job control"); "won't have full job control");
#endif #endif
if (tty_fd >= 0)
mksh_tcget(tty_fd, &tty_state);
} else { } else {
ttypgrp_ok = false; ttypgrp_ok = false;
if (Flag(FTALKING)) if (Flag(FTALKING))
@ -351,9 +354,8 @@ j_change(void)
SIG_IGN : SIG_DFL, SIG_IGN : SIG_DFL,
SS_RESTORE_ORIG|SS_FORCE); SS_RESTORE_ORIG|SS_FORCE);
} }
if (!Flag(FTALKING))
tty_close();
} }
tty_init_state();
} }
#endif #endif
@ -1136,7 +1138,7 @@ j_waitj(Job *j,
} }
} }
#endif #endif
if (tty_fd >= 0) { if (tty_hasstate) {
/* /*
* Only restore tty settings if job was originally * Only restore tty settings if job was originally
* started in the foreground. Problems can be * started in the foreground. Problems can be
@ -1803,3 +1805,41 @@ kill_job(Job *j, int sig)
rval = -1; rval = -1;
return (rval); 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
View File

@ -34,7 +34,7 @@
#include <locale.h> #include <locale.h>
#endif #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; extern char **environ;
@ -64,7 +64,7 @@ static const char initsubs[] =
static const char *initcoms[] = { static const char *initcoms[] = {
Ttypeset, "-r", initvsn, NULL, Ttypeset, "-r", initvsn, NULL,
Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL, Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
Ttypeset, "-i10", "SECONDS", "TMOUT", NULL, Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
Talias, Talias,
"integer=typeset -i", "integer=typeset -i",
Tlocal_typeset, Tlocal_typeset,
@ -278,12 +278,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
init_histvec(); init_histvec();
#ifdef TIOCGWINSZ /* initialise tty size before importing environment */
/* try to initialise tty size before importing environment */
tty_init(false, false);
change_winsz(); change_winsz();
tty_close();
#endif
#ifdef _PATH_DEFPATH #ifdef _PATH_DEFPATH
def_path = _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) while (*wp != NULL)
wp++; wp++;
} }
setint_n(global("COLUMNS"), 0, 10);
setint_n(global("LINES"), 0, 10);
setint_n(global("OPTIND"), 1, 10); setint_n(global("OPTIND"), 1, 10);
kshuid = getuid(); kshuid = getuid();
@ -518,7 +512,7 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp)
/* initialise job control */ /* initialise job control */
j_init(); 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 (Flag(FTALKING)) {
if (utf_flag == 2) { if (utf_flag == 2) {
#ifndef MKSH_ASSUME_UTF8 #ifndef MKSH_ASSUME_UTF8
@ -1056,75 +1050,76 @@ remove_temps(struct temp *tp)
} }
/* /*
* Initialise tty_fd. Used for saving/reseting tty modes upon * Initialise tty_fd. Used for tracking the size of the terminal,
* foreground job completion and for setting up tty process group. * 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 int
tty_init(bool init_ttystate, bool need_tty) tty_init_fd(void)
{ {
bool do_close = true; int fd, rv, eno = 0;
int tfd; bool do_close = false, is_devtty = true;
if (tty_fd >= 0) { if (tty_devtty) {
close(tty_fd); /* already got a tty which is /dev/tty */
tty_fd = -1; return (0);
} }
tty_devtty = true;
#ifdef _UWIN #ifdef _UWIN
/*XXX imake style */ /*XXX imake style */
if (isatty(3)) { if (isatty(3)) {
/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */ /* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
tfd = 3; fd = 3;
do_close = false; goto got_fd;
} 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
} }
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 #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) { eno = errno;
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);
}
void
tty_close(void)
{
if (tty_fd >= 0) { if (tty_fd >= 0) {
close(tty_fd); /* already got a non-devtty one */
tty_fd = -1; 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.) */ /* A shell error occurred (eg, syntax error, etc.) */

12
mksh.1
View File

@ -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 $ .\" $OpenBSD: ksh.1,v 1.144 2012/07/08 08:13:20 guenther Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -74,7 +74,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to .\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always. .\" 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. .\" 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 Always set, defaults to 80, unless the
value as reported by value as reported by
.Xr stty 1 .Xr stty 1
is non-zero and sane enough; similar for is non-zero and sane enough (minimum is 12x3); similar for
.Ev LINES . .Ev LINES .
This parameter is used by the interactive line editing modes, and by the This parameter is used by the interactive line editing modes, and by the
.Ic select , .Ic select ,
@ -1793,6 +1793,8 @@ This parameter is used by the interactive line editing modes, and by the
and and
.Ic kill \-l .Ic kill \-l
commands to format information columns. 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 .It Ev ENV
If this parameter is found to be set after any profile files are executed, the If this parameter is found to be set after any profile files are executed, the
expanded value is used as a shell startup file. expanded value is used as a shell startup file.
@ -1880,6 +1882,8 @@ executed.
.It Ev LINES .It Ev LINES
Set to the number of lines on the terminal or window. Set to the number of lines on the terminal or window.
Always set, defaults to 24. Always set, defaults to 24.
See
.Ev COLUMNS .
.It Ev EPOCHREALTIME .It Ev EPOCHREALTIME
Time since the epoch, as returned by Time since the epoch, as returned by
.Xr gettimeofday 2 , .Xr gettimeofday 2 ,
@ -3665,7 +3669,7 @@ Approximately the same as the
.Xr printf 1 , .Xr printf 1 ,
utility, except it uses the same utility, except it uses the same
.Sx Backslash expansion .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 . .Nm mksh .
This is not normally part of This is not normally part of
.Nm mksh ; .Nm mksh ;

8
sh.h
View File

@ -157,9 +157,9 @@
#endif #endif
#ifdef EXTERN #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 #endif
#define MKSH_VERSION "R40 2012/11/26" #define MKSH_VERSION "R41 2012/11/30"
/* arithmetic types: C implementation */ /* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES #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 int tty_fd E_INIT(-1); /* dup'd tty file descriptor */
EXTERN bool tty_devtty; /* true if tty_fd is from /dev/tty */ EXTERN bool tty_devtty; /* true if tty_fd is from /dev/tty */
EXTERN mksh_ttyst tty_state; /* saved tty state */ 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 int tty_init_fd(void); /* initialise tty_fd, tty_devtty */
extern void tty_close(void);
/* be sure not to interfere with anyone else's idea about EXTERN */ /* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED #ifdef EXTERN_DEFINED

26
var.c
View File

@ -27,7 +27,7 @@
#include <sys/sysctl.h> #include <sys/sysctl.h>
#endif #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 * Variables
@ -1090,10 +1090,8 @@ getspec(struct tbl *vp)
struct timeval tv; struct timeval tv;
switch ((st = special(vp->name))) { switch ((st = special(vp->name))) {
case V_BASHPID:
i = (mksh_ari_t)procpid;
break;
case V_COLUMNS: case V_COLUMNS:
case V_LINES:
/* /*
* Do NOT export COLUMNS/LINES. Many applications * Do NOT export COLUMNS/LINES. Many applications
* check COLUMNS/LINES before checking ws.ws_col/row, * 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 * and the window is then resized, the app won't
* see the change cause the environ doesn't change. * 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; i = x_cols;
break; break;
case V_HISTSIZE: case V_HISTSIZE:
@ -1197,9 +1204,16 @@ setspec(struct tbl *vp)
return; return;
/* common sub-cases */ /* common sub-cases */
case V_COLUMNS: 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_HISTSIZE:
case V_LINENO: case V_LINENO:
case V_LINES:
case V_OPTIND: case V_OPTIND:
case V_RANDOM: case V_RANDOM:
case V_SECONDS: case V_SECONDS:
@ -1466,7 +1480,7 @@ change_winsz(void)
{ {
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
/* check if window size has changed */ /* check if window size has changed */
if (tty_fd >= 0) { if (tty_init_fd() < 2) {
struct winsize ws; struct winsize ws;
if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {