From: anton Date: Wed, 28 Jun 2017 14:58:23 +0000 (+0000) Subject: Replace usage of TIOCSTI in mail while editing headers using a more common X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=baecf150995d4609cd147948779361c3152f355d;p=openbsd Replace usage of TIOCSTI in mail while editing headers using a more common IO-loop where ICANON is disabled and a single char of input is read at a time. This requires the line editing capabilities provided when ICANON is enabled to be implemented. ok deraadt@ --- diff --git a/usr.bin/mail/tty.c b/usr.bin/mail/tty.c index 194281448c1..4f7057b3a42 100644 --- a/usr.bin/mail/tty.c +++ b/usr.bin/mail/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.20 2014/01/17 18:42:30 okan Exp $ */ +/* $OpenBSD: tty.c,v 1.21 2017/06/28 14:58:23 anton Exp $ */ /* $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $ */ /* @@ -40,12 +40,31 @@ #include "extern.h" #include #include +#include -static cc_t c_erase; /* Current erase char */ -static cc_t c_kill; /* Current kill char */ -#ifndef TIOCSTI -static int ttyset; /* We must now do erase/kill */ -#endif +#define TABWIDTH 8 + +struct tty { + int fdin; + 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? */ /* @@ -54,14 +73,10 @@ static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ int grabh(struct header *hp, int gflags) { - struct termios ttybuf; -#ifndef TIOCSTI - struct sigaction savequit; -#else -# ifdef TIOCEXT + struct termios newtio, oldtio; +#ifdef TIOCEXT int extproc; int flag; -# endif /* TIOCEXT */ #endif struct sigaction savetstp; struct sigaction savettou; @@ -77,67 +92,53 @@ grabh(struct header *hp, int gflags) (void)sigaction(SIGTTOU, &act, &savettou); (void)sigaction(SIGTTIN, &act, &savettin); error = 1; -#ifndef TIOCSTI - ttyset = 0; -#endif - if (tcgetattr(fileno(stdin), &ttybuf) < 0) { + memset(&tty, 0, sizeof(tty)); + tty.fdin = fileno(stdin); + tty.fdout = fileno(stdout); + if (tcgetattr(tty.fdin, &oldtio) < 0) { warn("tcgetattr"); return(-1); } - c_erase = ttybuf.c_cc[VERASE]; - c_kill = ttybuf.c_cc[VKILL]; -#ifndef TIOCSTI - ttybuf.c_cc[VERASE] = 0; - ttybuf.c_cc[VKILL] = 0; - act.sa_handler = SIG_IGN; - if (sigaction(SIGQUIT, &act, &savequit) == 0 && - savequit.sa_handler == SIG_DFL) - (void)sigaction(SIGQUIT, &savequit, NULL); -#else -# ifdef TIOCEXT - extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); + tty.keys = oldtio.c_cc; + if (oldtio.c_lflag & ALTWERASE) + tty.flags |= TTY_ALTWERASE; + + newtio = oldtio; + newtio.c_lflag &= ~(ECHO | ICANON); + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) < 0) { + warn("tcsetattr"); + return(-1); + } + +#ifdef TIOCEXT + extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0); if (extproc) { flag = 0; if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) warn("TIOCEXT: off"); } -# endif /* TIOCEXT */ #endif 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)); if (s == NULL) goto out; hp->h_to = extract(s, GTO); } if (gflags & GSUBJECT) { -#ifndef TIOCSTI - if (!ttyset && hp->h_subject != NULL) - ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); -#endif s = readtty("Subject: ", hp->h_subject); if (s == NULL) goto out; hp->h_subject = s; } 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)); if (s == NULL) goto out; hp->h_cc = extract(s, GCC); } 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)); if (s == NULL) goto out; @@ -148,21 +149,15 @@ out: (void)sigaction(SIGTSTP, &savetstp, NULL); (void)sigaction(SIGTTOU, &savettou, NULL); (void)sigaction(SIGTTIN, &savettin, NULL); -#ifndef TIOCSTI - 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 +#ifdef TIOCEXT if (extproc) { flag = 1; if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) warn("TIOCEXT: on"); } -# endif /* TIOCEXT */ #endif + if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) < 0) + warn("tcsetattr"); return(error); } @@ -176,38 +171,30 @@ char * readtty(char *pr, char *src) { struct sigaction act, saveint; - char ch, canonb[BUFSIZ]; - char *cp, *cp2; + unsigned char canonb[BUFSIZ]; + char *cp; sigset_t oset; - int c; + int c, done; + + memset(canonb, 0, sizeof(canonb)); + 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); - fputs(pr, stdout); - fflush(stdout); if (src != NULL && strlen(src) > sizeof(canonb) - 2) { puts("too long to edit"); return(src); } -#ifndef TIOCSTI - if (src != NULL) - cp = copy(src, canonb); /* safe, bounds checked above */ - else - 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); + if (src != NULL) { + for (cp = src; *cp != '\0'; cp++) + tty_insert(&tty, *cp, 1); + tty_flush(&tty); } - cp = canonb; - *cp = 0; -#endif + sigemptyset(&act.sa_mask); act.sa_flags = 0; /* Note: will not restart syscalls */ act.sa_handler = ttyint; @@ -217,15 +204,11 @@ readtty(char *pr, char *src) (void)sigaction(SIGTTOU, &act, NULL); (void)sigaction(SIGTTIN, &act, NULL); (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); - clearerr(stdin); - memset(cp, 0, canonb + sizeof(canonb) - cp); - for (cp2 = cp; cp2 < canonb + sizeof(canonb) - 1; ) { - c = getc(stdin); + for (;;) { + c = tty_getc(&tty); switch (ttysignal) { case SIGINT: - ttysignal = 0; - cp2 = NULL; - c = EOF; + tty_visc(&tty, '\003'); /* output ^C */ /* FALLTHROUGH */ case 0: break; @@ -233,9 +216,17 @@ readtty(char *pr, char *src) ttysignal = 0; 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; - *cp2++ = c; } act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); @@ -245,48 +236,17 @@ readtty(char *pr, char *src) (void)sigaction(SIGTTOU, &act, NULL); (void)sigaction(SIGTTIN, &act, NULL); (void)sigaction(SIGINT, &saveint, NULL); - if (cp2 == NULL) - return(NULL); /* user hit ^C */ - *cp2 = '\0'; - if (c == EOF && ferror(stdin)) { + if (tty.flags & TTY_ERR) { + if (ttysignal == SIGINT) { + ttysignal = 0; + return(NULL); /* user hit ^C */ + } + redo: cp = strlen(canonb) > 0 ? canonb : NULL; - clearerr(stdin); /* XXX - make iterative, not recursive */ 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)) return(""); return(savestr(canonb)); @@ -328,3 +288,121 @@ ttyint(int 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); + } +}