version 1.20, 2014/01/17 18:42:30 |
version 1.21, 2017/06/28 14:58:23 |
|
|
#include "extern.h" |
#include "extern.h" |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#include <errno.h> |
#include <errno.h> |
|
#include <fcntl.h> |
|
|
static cc_t c_erase; /* Current erase char */ |
#define TABWIDTH 8 |
static cc_t c_kill; /* Current kill char */ |
|
#ifndef TIOCSTI |
struct tty { |
static int ttyset; /* We must now do erase/kill */ |
int fdin; |
#endif |
int fdout; |
|
int flags; |
|
#define TTY_ALTWERASE 0x1 |
|
#define TTY_ERR 0x2 |
|
cc_t *keys; |
|
char *buf; |
|
size_t size; |
|
size_t len; |
|
size_t cursor; |
|
}; |
|
|
|
static void tty_flush(struct tty *); |
|
static int tty_getc(struct tty *); |
|
static int tty_insert(struct tty *, int, int); |
|
static void tty_putc(struct tty *, int); |
|
static void tty_reset(struct tty *); |
|
static void tty_visc(struct tty *, int); |
|
|
|
static struct tty tty; |
static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ |
static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ |
|
|
/* |
/* |
|
|
int |
int |
grabh(struct header *hp, int gflags) |
grabh(struct header *hp, int gflags) |
{ |
{ |
struct termios ttybuf; |
struct termios newtio, oldtio; |
#ifndef TIOCSTI |
#ifdef TIOCEXT |
struct sigaction savequit; |
|
#else |
|
# ifdef TIOCEXT |
|
int extproc; |
int extproc; |
int flag; |
int flag; |
# endif /* TIOCEXT */ |
|
#endif |
#endif |
struct sigaction savetstp; |
struct sigaction savetstp; |
struct sigaction savettou; |
struct sigaction savettou; |
|
|
(void)sigaction(SIGTTOU, &act, &savettou); |
(void)sigaction(SIGTTOU, &act, &savettou); |
(void)sigaction(SIGTTIN, &act, &savettin); |
(void)sigaction(SIGTTIN, &act, &savettin); |
error = 1; |
error = 1; |
#ifndef TIOCSTI |
memset(&tty, 0, sizeof(tty)); |
ttyset = 0; |
tty.fdin = fileno(stdin); |
#endif |
tty.fdout = fileno(stdout); |
if (tcgetattr(fileno(stdin), &ttybuf) < 0) { |
if (tcgetattr(tty.fdin, &oldtio) < 0) { |
warn("tcgetattr"); |
warn("tcgetattr"); |
return(-1); |
return(-1); |
} |
} |
c_erase = ttybuf.c_cc[VERASE]; |
tty.keys = oldtio.c_cc; |
c_kill = ttybuf.c_cc[VKILL]; |
if (oldtio.c_lflag & ALTWERASE) |
#ifndef TIOCSTI |
tty.flags |= TTY_ALTWERASE; |
ttybuf.c_cc[VERASE] = 0; |
|
ttybuf.c_cc[VKILL] = 0; |
newtio = oldtio; |
act.sa_handler = SIG_IGN; |
newtio.c_lflag &= ~(ECHO | ICANON); |
if (sigaction(SIGQUIT, &act, &savequit) == 0 && |
newtio.c_cc[VMIN] = 1; |
savequit.sa_handler == SIG_DFL) |
newtio.c_cc[VTIME] = 0; |
(void)sigaction(SIGQUIT, &savequit, NULL); |
if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) < 0) { |
#else |
warn("tcsetattr"); |
# ifdef TIOCEXT |
return(-1); |
extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); |
} |
|
|
|
#ifdef TIOCEXT |
|
extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0); |
if (extproc) { |
if (extproc) { |
flag = 0; |
flag = 0; |
if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) |
if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) |
warn("TIOCEXT: off"); |
warn("TIOCEXT: off"); |
} |
} |
# endif /* TIOCEXT */ |
|
#endif |
#endif |
if (gflags & GTO) { |
if (gflags & GTO) { |
#ifndef TIOCSTI |
|
if (!ttyset && hp->h_to != NULL) |
|
ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); |
|
#endif |
|
s = readtty("To: ", detract(hp->h_to, 0)); |
s = readtty("To: ", detract(hp->h_to, 0)); |
if (s == NULL) |
if (s == NULL) |
goto out; |
goto out; |
hp->h_to = extract(s, GTO); |
hp->h_to = extract(s, GTO); |
} |
} |
if (gflags & GSUBJECT) { |
if (gflags & GSUBJECT) { |
#ifndef TIOCSTI |
|
if (!ttyset && hp->h_subject != NULL) |
|
ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); |
|
#endif |
|
s = readtty("Subject: ", hp->h_subject); |
s = readtty("Subject: ", hp->h_subject); |
if (s == NULL) |
if (s == NULL) |
goto out; |
goto out; |
hp->h_subject = s; |
hp->h_subject = s; |
} |
} |
if (gflags & GCC) { |
if (gflags & GCC) { |
#ifndef TIOCSTI |
|
if (!ttyset && hp->h_cc != NULL) |
|
ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); |
|
#endif |
|
s = readtty("Cc: ", detract(hp->h_cc, 0)); |
s = readtty("Cc: ", detract(hp->h_cc, 0)); |
if (s == NULL) |
if (s == NULL) |
goto out; |
goto out; |
hp->h_cc = extract(s, GCC); |
hp->h_cc = extract(s, GCC); |
} |
} |
if (gflags & GBCC) { |
if (gflags & GBCC) { |
#ifndef TIOCSTI |
|
if (!ttyset && hp->h_bcc != NULL) |
|
ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); |
|
#endif |
|
s = readtty("Bcc: ", detract(hp->h_bcc, 0)); |
s = readtty("Bcc: ", detract(hp->h_bcc, 0)); |
if (s == NULL) |
if (s == NULL) |
goto out; |
goto out; |
|
|
(void)sigaction(SIGTSTP, &savetstp, NULL); |
(void)sigaction(SIGTSTP, &savetstp, NULL); |
(void)sigaction(SIGTTOU, &savettou, NULL); |
(void)sigaction(SIGTTOU, &savettou, NULL); |
(void)sigaction(SIGTTIN, &savettin, NULL); |
(void)sigaction(SIGTTIN, &savettin, NULL); |
#ifndef TIOCSTI |
#ifdef TIOCEXT |
ttybuf.c_cc[VERASE] = c_erase; |
|
ttybuf.c_cc[VKILL] = c_kill; |
|
if (ttyset) |
|
tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); |
|
(void)sigaction(SIGQUIT, &savequit, NULL); |
|
#else |
|
# ifdef TIOCEXT |
|
if (extproc) { |
if (extproc) { |
flag = 1; |
flag = 1; |
if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) |
if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) |
warn("TIOCEXT: on"); |
warn("TIOCEXT: on"); |
} |
} |
# endif /* TIOCEXT */ |
|
#endif |
#endif |
|
if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) < 0) |
|
warn("tcsetattr"); |
return(error); |
return(error); |
} |
} |
|
|
|
|
readtty(char *pr, char *src) |
readtty(char *pr, char *src) |
{ |
{ |
struct sigaction act, saveint; |
struct sigaction act, saveint; |
char ch, canonb[BUFSIZ]; |
unsigned char canonb[BUFSIZ]; |
char *cp, *cp2; |
char *cp; |
sigset_t oset; |
sigset_t oset; |
int c; |
int c, done; |
|
|
fputs(pr, stdout); |
memset(canonb, 0, sizeof(canonb)); |
fflush(stdout); |
tty.buf = canonb; |
|
tty.size = sizeof(canonb) - 1; |
|
|
|
for (cp = pr; *cp != '\0'; cp++) |
|
tty_insert(&tty, *cp, 1); |
|
tty_flush(&tty); |
|
tty_reset(&tty); |
|
|
if (src != NULL && strlen(src) > sizeof(canonb) - 2) { |
if (src != NULL && strlen(src) > sizeof(canonb) - 2) { |
puts("too long to edit"); |
puts("too long to edit"); |
return(src); |
return(src); |
} |
} |
#ifndef TIOCSTI |
if (src != NULL) { |
if (src != NULL) |
for (cp = src; *cp != '\0'; cp++) |
cp = copy(src, canonb); /* safe, bounds checked above */ |
tty_insert(&tty, *cp, 1); |
else |
tty_flush(&tty); |
cp = copy("", canonb); |
|
fputs(canonb, stdout); |
|
fflush(stdout); |
|
#else |
|
cp = src == NULL ? "" : src; |
|
while ((c = (unsigned char)*cp++) != '\0') { |
|
if ((c_erase != _POSIX_VDISABLE && c == c_erase) || |
|
(c_kill != _POSIX_VDISABLE && c == c_kill)) { |
|
ch = '\\'; |
|
ioctl(0, TIOCSTI, &ch); |
|
} |
|
ch = c; |
|
ioctl(0, TIOCSTI, &ch); |
|
} |
} |
cp = canonb; |
|
*cp = 0; |
|
#endif |
|
sigemptyset(&act.sa_mask); |
sigemptyset(&act.sa_mask); |
act.sa_flags = 0; /* Note: will not restart syscalls */ |
act.sa_flags = 0; /* Note: will not restart syscalls */ |
act.sa_handler = ttyint; |
act.sa_handler = ttyint; |
|
|
(void)sigaction(SIGTTOU, &act, NULL); |
(void)sigaction(SIGTTOU, &act, NULL); |
(void)sigaction(SIGTTIN, &act, NULL); |
(void)sigaction(SIGTTIN, &act, NULL); |
(void)sigprocmask(SIG_UNBLOCK, &intset, &oset); |
(void)sigprocmask(SIG_UNBLOCK, &intset, &oset); |
clearerr(stdin); |
for (;;) { |
memset(cp, 0, canonb + sizeof(canonb) - cp); |
c = tty_getc(&tty); |
for (cp2 = cp; cp2 < canonb + sizeof(canonb) - 1; ) { |
|
c = getc(stdin); |
|
switch (ttysignal) { |
switch (ttysignal) { |
case SIGINT: |
case SIGINT: |
ttysignal = 0; |
tty_visc(&tty, '\003'); /* output ^C */ |
cp2 = NULL; |
|
c = EOF; |
|
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case 0: |
case 0: |
break; |
break; |
|
|
ttysignal = 0; |
ttysignal = 0; |
goto redo; |
goto redo; |
} |
} |
if (c == EOF || c == '\n') |
if (c == 0) { |
|
done = 1; |
|
} else if (c == '\n') { |
|
tty_putc(&tty, c); |
|
done = 1; |
|
} else { |
|
done = tty_insert(&tty, c, 0); |
|
tty_flush(&tty); |
|
} |
|
if (done) |
break; |
break; |
*cp2++ = c; |
|
} |
} |
act.sa_handler = SIG_DFL; |
act.sa_handler = SIG_DFL; |
sigemptyset(&act.sa_mask); |
sigemptyset(&act.sa_mask); |
|
|
(void)sigaction(SIGTTOU, &act, NULL); |
(void)sigaction(SIGTTOU, &act, NULL); |
(void)sigaction(SIGTTIN, &act, NULL); |
(void)sigaction(SIGTTIN, &act, NULL); |
(void)sigaction(SIGINT, &saveint, NULL); |
(void)sigaction(SIGINT, &saveint, NULL); |
if (cp2 == NULL) |
if (tty.flags & TTY_ERR) { |
return(NULL); /* user hit ^C */ |
if (ttysignal == SIGINT) { |
*cp2 = '\0'; |
ttysignal = 0; |
if (c == EOF && ferror(stdin)) { |
return(NULL); /* user hit ^C */ |
|
} |
|
|
redo: |
redo: |
cp = strlen(canonb) > 0 ? canonb : NULL; |
cp = strlen(canonb) > 0 ? canonb : NULL; |
clearerr(stdin); |
|
/* XXX - make iterative, not recursive */ |
/* XXX - make iterative, not recursive */ |
return(readtty(pr, cp)); |
return(readtty(pr, cp)); |
} |
} |
#ifndef TIOCSTI |
|
if (cp == NULL || *cp == '\0') |
|
return(src); |
|
cp2 = cp; |
|
if (!ttyset) |
|
return(strlen(canonb) > 0 ? savestr(canonb) : NULL); |
|
while (*cp != '\0') { |
|
c = (unsigned char)*cp++; |
|
if (c_erase != _POSIX_VDISABLE && c == c_erase) { |
|
if (cp2 == canonb) |
|
continue; |
|
if (cp2[-1] == '\\') { |
|
cp2[-1] = c; |
|
continue; |
|
} |
|
cp2--; |
|
continue; |
|
} |
|
if (c_kill != _POSIX_VDISABLE && c == c_kill) { |
|
if (cp2 == canonb) |
|
continue; |
|
if (cp2[-1] == '\\') { |
|
cp2[-1] = c; |
|
continue; |
|
} |
|
cp2 = canonb; |
|
continue; |
|
} |
|
*cp2++ = c; |
|
} |
|
*cp2 = '\0'; |
|
#endif |
|
if (equal("", canonb)) |
if (equal("", canonb)) |
return(""); |
return(""); |
return(savestr(canonb)); |
return(savestr(canonb)); |
|
|
{ |
{ |
|
|
ttysignal = s; |
ttysignal = s; |
|
} |
|
|
|
static void |
|
tty_flush(struct tty *t) |
|
{ |
|
size_t i, len; |
|
int c; |
|
|
|
if (t->cursor < t->len) { |
|
for (; t->cursor < t->len; t->cursor++) |
|
tty_visc(t, t->buf[t->cursor]); |
|
} else if (t->cursor > t->len) { |
|
len = t->cursor - t->len; |
|
for (i = len; i > 0; i--) { |
|
c = t->buf[--t->cursor]; |
|
if (c == '\t') |
|
len += TABWIDTH - 1; |
|
else if (iscntrl(c)) |
|
len++; /* account for leading ^ */ |
|
} |
|
for (i = 0; i < len; i++) |
|
tty_putc(t, '\b'); |
|
for (i = 0; i < len; i++) |
|
tty_putc(t, ' '); |
|
for (i = 0; i < len; i++) |
|
tty_putc(t, '\b'); |
|
t->cursor = t->len; |
|
} |
|
|
|
t->buf[t->len] = '\0'; |
|
} |
|
|
|
static int |
|
tty_getc(struct tty *t) |
|
{ |
|
ssize_t n; |
|
unsigned char c; |
|
|
|
n = read(t->fdin, &c, 1); |
|
switch (n) { |
|
case -1: |
|
t->flags |= TTY_ERR; |
|
/* FALLTHROUGH */ |
|
case 0: |
|
return 0; |
|
default: |
|
return c & 0x7f; |
|
} |
|
} |
|
|
|
static int |
|
tty_insert(struct tty *t, int c, int nocntrl) |
|
{ |
|
const unsigned char *ws = " \t"; |
|
|
|
if (CCEQ(t->keys[VERASE], c)) { |
|
if (nocntrl) |
|
return 0; |
|
if (t->len > 0) |
|
t->len--; |
|
} else if (CCEQ(t->keys[VWERASE], c)) { |
|
if (nocntrl) |
|
return 0; |
|
for (; t->len > 0; t->len--) |
|
if (strchr(ws, t->buf[t->len - 1]) == NULL |
|
&& ((t->flags & TTY_ALTWERASE) == 0 |
|
|| isalpha(t->buf[t->len - 1]))) |
|
break; |
|
for (; t->len > 0; t->len--) |
|
if (strchr(ws, t->buf[t->len - 1]) != NULL |
|
|| ((t->flags & TTY_ALTWERASE) |
|
&& !isalpha(t->buf[t->len - 1]))) |
|
break; |
|
} else if (CCEQ(t->keys[VKILL], c)) { |
|
if (nocntrl) |
|
return 0; |
|
t->len = 0; |
|
} else { |
|
if (t->len == t->size) |
|
return 1; |
|
t->buf[t->len++] = c; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void |
|
tty_putc(struct tty *t, int c) |
|
{ |
|
unsigned char cc = c; |
|
|
|
write(t->fdout, &cc, 1); |
|
} |
|
|
|
static void |
|
tty_reset(struct tty *t) |
|
{ |
|
memset(t->buf, 0, t->len); |
|
t->len = t->cursor = 0; |
|
} |
|
|
|
static void |
|
tty_visc(struct tty *t, int c) |
|
{ |
|
int i; |
|
|
|
if (c == '\t') { |
|
for (i = 0; i < TABWIDTH; i++) |
|
tty_putc(t, ' '); |
|
} else if (iscntrl(c)) { |
|
tty_putc(t, '^'); |
|
if (c == 0x7F) |
|
tty_putc(t, '?'); |
|
else |
|
tty_putc(t, (c | 0x40)); |
|
} else { |
|
tty_putc(t, c); |
|
} |
} |
} |