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: 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
View File

@ -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
View File

@ -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
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 $
.\"-
.\" 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
View File

@ -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
View File

@ -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) {