File: [local] / src / usr.bin / mail / tty.c (download)
Revision 1.24, Fri Nov 3 19:32:28 2023 UTC (7 months ago) by anton
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD Changes since 1.23: +5 -2 lines
Check if a signal already has been received before entering a blocking
read as opposed of only doing it afterwards. Issue uncovered by the mail
regress tests.
With claudio@ and ok millert@
|
/* $OpenBSD: tty.c,v 1.24 2023/11/03 19:32:28 anton Exp $ */
/* $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $ */
/*
* Copyright (c) 1980, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Mail -- a mail program
*
* Generally useful tty stuff.
*/
#include "rcv.h"
#include "extern.h"
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#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? */
/*
* Read all relevant header fields.
*/
int
grabh(struct header *hp, int gflags)
{
struct termios newtio, oldtio;
#ifdef TIOCEXT
int extproc;
int flag;
#endif
struct sigaction savetstp;
struct sigaction savettou;
struct sigaction savettin;
struct sigaction act;
char *s;
int error;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = SIG_DFL;
(void)sigaction(SIGTSTP, &act, &savetstp);
(void)sigaction(SIGTTOU, &act, &savettou);
(void)sigaction(SIGTTIN, &act, &savettin);
error = 1;
memset(&tty, 0, sizeof(tty));
tty.fdin = fileno(stdin);
tty.fdout = fileno(stdout);
if (tcgetattr(tty.fdin, &oldtio) == -1) {
warn("tcgetattr");
return(-1);
}
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) == -1) {
warn("tcsetattr");
return(-1);
}
#ifdef TIOCEXT
extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0);
if (extproc) {
flag = 0;
if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
warn("TIOCEXT: off");
}
#endif
if (gflags & GTO) {
s = readtty("To: ", detract(hp->h_to, 0));
if (s == NULL)
goto out;
hp->h_to = extract(s, GTO);
}
if (gflags & GSUBJECT) {
s = readtty("Subject: ", hp->h_subject);
if (s == NULL)
goto out;
hp->h_subject = s;
}
if (gflags & GCC) {
s = readtty("Cc: ", detract(hp->h_cc, 0));
if (s == NULL)
goto out;
hp->h_cc = extract(s, GCC);
}
if (gflags & GBCC) {
s = readtty("Bcc: ", detract(hp->h_bcc, 0));
if (s == NULL)
goto out;
hp->h_bcc = extract(s, GBCC);
}
error = 0;
out:
(void)sigaction(SIGTSTP, &savetstp, NULL);
(void)sigaction(SIGTTOU, &savettou, NULL);
(void)sigaction(SIGTTIN, &savettin, NULL);
#ifdef TIOCEXT
if (extproc) {
flag = 1;
if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
warn("TIOCEXT: on");
}
#endif
if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) == -1)
warn("tcsetattr");
return(error);
}
/*
* Read up a header from standard input.
* The source string has the preliminary contents to
* be read.
*
*/
char *
readtty(char *pr, char *src)
{
struct sigaction act, saveint;
unsigned char canonb[BUFSIZ];
char *cp;
sigset_t oset;
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);
if (src != NULL && strlen(src) > sizeof(canonb) - 2) {
puts("too long to edit");
return(src);
}
if (src != NULL) {
for (cp = src; *cp != '\0'; cp++)
tty_insert(&tty, *cp, 1);
tty_flush(&tty);
}
sigemptyset(&act.sa_mask);
act.sa_flags = 0; /* Note: will not restart syscalls */
act.sa_handler = ttyint;
(void)sigaction(SIGINT, &act, &saveint);
act.sa_handler = ttystop;
(void)sigaction(SIGTSTP, &act, NULL);
(void)sigaction(SIGTTOU, &act, NULL);
(void)sigaction(SIGTTIN, &act, NULL);
(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
for (;;) {
c = tty_getc(&tty);
switch (ttysignal) {
case SIGINT:
tty_visc(&tty, '\003'); /* output ^C */
/* FALLTHROUGH */
case 0:
break;
default:
ttysignal = 0;
goto redo;
}
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;
}
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
(void)sigaction(SIGTSTP, &act, NULL);
(void)sigaction(SIGTTOU, &act, NULL);
(void)sigaction(SIGTTIN, &act, NULL);
(void)sigaction(SIGINT, &saveint, NULL);
if (tty.flags & TTY_ERR) {
if (ttysignal == SIGINT) {
ttysignal = 0;
return(NULL); /* user hit ^C */
}
redo:
cp = strlen(canonb) > 0 ? canonb : NULL;
/* XXX - make iterative, not recursive */
return(readtty(pr, cp));
}
if (equal("", canonb))
return("");
return(savestr(canonb));
}
/*
* Receipt continuation.
*/
void
ttystop(int s)
{
struct sigaction act, oact;
sigset_t nset;
int save_errno;
/*
* Save old handler and set to default.
* Unblock receipt of 's' and then resend it.
*/
save_errno = errno;
(void)sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = SIG_DFL;
(void)sigaction(s, &act, &oact);
(void)sigemptyset(&nset);
(void)sigaddset(&nset, s);
(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
(void)kill(0, s);
(void)sigprocmask(SIG_BLOCK, &nset, NULL);
(void)sigaction(s, &oact, NULL);
ttysignal = s;
errno = save_errno;
}
void
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;
if (ttysignal != 0)
n = -1;
else
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);
}
}