[BACK]Return to tty.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mail

Annotation of src/usr.bin/mail/tty.c, Revision 1.23

1.23    ! guenther    1: /*     $OpenBSD: tty.c,v 1.22 2019/06/28 13:35:02 deraadt Exp $        */
1.3       millert     2: /*     $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $    */
1.2       deraadt     3:
1.1       deraadt     4: /*
                      5:  * Copyright (c) 1980, 1993
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
1.17      millert    16:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    17:  *    may be used to endorse or promote products derived from this software
                     18:  *    without specific prior written permission.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:  * SUCH DAMAGE.
                     31:  */
                     32:
                     33: /*
                     34:  * Mail -- a mail program
                     35:  *
                     36:  * Generally useful tty stuff.
                     37:  */
                     38:
                     39: #include "rcv.h"
                     40: #include "extern.h"
1.2       deraadt    41: #include <sys/ioctl.h>
1.13      millert    42: #include <errno.h>
1.21      anton      43: #include <fcntl.h>
1.1       deraadt    44:
1.21      anton      45: #define        TABWIDTH        8
                     46:
                     47: struct tty {
                     48:        int      fdin;
                     49:        int      fdout;
                     50:        int      flags;
                     51: #define        TTY_ALTWERASE   0x1
                     52: #define        TTY_ERR         0x2
                     53:        cc_t    *keys;
                     54:        char    *buf;
                     55:        size_t   size;
                     56:        size_t   len;
                     57:        size_t   cursor;
                     58: };
                     59:
                     60: static void    tty_flush(struct tty *);
                     61: static int     tty_getc(struct tty *);
                     62: static int     tty_insert(struct tty *, int, int);
                     63: static void    tty_putc(struct tty *, int);
                     64: static void    tty_reset(struct tty *);
                     65: static void    tty_visc(struct tty *, int);
                     66:
                     67: static struct tty              tty;
1.13      millert    68: static volatile sig_atomic_t   ttysignal;      /* Interrupted by a signal? */
1.1       deraadt    69:
                     70: /*
                     71:  * Read all relevant header fields.
                     72:  */
                     73: int
1.14      millert    74: grabh(struct header *hp, int gflags)
1.1       deraadt    75: {
1.21      anton      76:        struct termios newtio, oldtio;
                     77: #ifdef TIOCEXT
1.13      millert    78:        int extproc;
1.12      millert    79:        int flag;
1.1       deraadt    80: #endif
1.13      millert    81:        struct sigaction savetstp;
                     82:        struct sigaction savettou;
                     83:        struct sigaction savettin;
                     84:        struct sigaction act;
                     85:        char *s;
                     86:        int error;
                     87:
                     88:        sigemptyset(&act.sa_mask);
                     89:        act.sa_flags = SA_RESTART;
                     90:        act.sa_handler = SIG_DFL;
                     91:        (void)sigaction(SIGTSTP, &act, &savetstp);
                     92:        (void)sigaction(SIGTTOU, &act, &savettou);
                     93:        (void)sigaction(SIGTTIN, &act, &savettin);
                     94:        error = 1;
1.21      anton      95:        memset(&tty, 0, sizeof(tty));
                     96:        tty.fdin = fileno(stdin);
                     97:        tty.fdout = fileno(stdout);
1.22      deraadt    98:        if (tcgetattr(tty.fdin, &oldtio) == -1) {
1.3       millert    99:                warn("tcgetattr");
1.1       deraadt   100:                return(-1);
                    101:        }
1.21      anton     102:        tty.keys = oldtio.c_cc;
                    103:        if (oldtio.c_lflag & ALTWERASE)
                    104:                tty.flags |= TTY_ALTWERASE;
                    105:
                    106:        newtio = oldtio;
                    107:        newtio.c_lflag &= ~(ECHO | ICANON);
                    108:        newtio.c_cc[VMIN] = 1;
                    109:        newtio.c_cc[VTIME] = 0;
1.22      deraadt   110:        if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) == -1) {
1.21      anton     111:                warn("tcsetattr");
                    112:                return(-1);
                    113:        }
                    114:
                    115: #ifdef TIOCEXT
                    116:        extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0);
1.10      millert   117:        if (extproc) {
                    118:                flag = 0;
1.22      deraadt   119:                if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
1.10      millert   120:                        warn("TIOCEXT: off");
                    121:        }
1.1       deraadt   122: #endif
                    123:        if (gflags & GTO) {
1.13      millert   124:                s = readtty("To: ", detract(hp->h_to, 0));
                    125:                if (s == NULL)
                    126:                        goto out;
                    127:                hp->h_to = extract(s, GTO);
1.1       deraadt   128:        }
                    129:        if (gflags & GSUBJECT) {
1.13      millert   130:                s = readtty("Subject: ", hp->h_subject);
                    131:                if (s == NULL)
                    132:                        goto out;
                    133:                hp->h_subject = s;
1.1       deraadt   134:        }
                    135:        if (gflags & GCC) {
1.13      millert   136:                s = readtty("Cc: ", detract(hp->h_cc, 0));
                    137:                if (s == NULL)
                    138:                        goto out;
                    139:                hp->h_cc = extract(s, GCC);
1.1       deraadt   140:        }
                    141:        if (gflags & GBCC) {
1.13      millert   142:                s = readtty("Bcc: ", detract(hp->h_bcc, 0));
                    143:                if (s == NULL)
                    144:                        goto out;
                    145:                hp->h_bcc = extract(s, GBCC);
1.1       deraadt   146:        }
1.13      millert   147:        error = 0;
1.1       deraadt   148: out:
1.13      millert   149:        (void)sigaction(SIGTSTP, &savetstp, NULL);
                    150:        (void)sigaction(SIGTTOU, &savettou, NULL);
                    151:        (void)sigaction(SIGTTIN, &savettin, NULL);
1.21      anton     152: #ifdef TIOCEXT
1.10      millert   153:        if (extproc) {
                    154:                flag = 1;
1.22      deraadt   155:                if (ioctl(fileno(stdin), TIOCEXT, &flag) == -1)
1.10      millert   156:                        warn("TIOCEXT: on");
                    157:        }
1.1       deraadt   158: #endif
1.22      deraadt   159:        if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) == -1)
1.21      anton     160:                warn("tcsetattr");
1.13      millert   161:        return(error);
1.1       deraadt   162: }
                    163:
                    164: /*
                    165:  * Read up a header from standard input.
                    166:  * The source string has the preliminary contents to
                    167:  * be read.
                    168:  *
                    169:  */
                    170: char *
1.14      millert   171: readtty(char *pr, char *src)
1.1       deraadt   172: {
1.16      millert   173:        struct sigaction act, saveint;
1.21      anton     174:        unsigned char canonb[BUFSIZ];
                    175:        char *cp;
1.14      millert   176:        sigset_t oset;
1.21      anton     177:        int c, done;
                    178:
                    179:        memset(canonb, 0, sizeof(canonb));
                    180:        tty.buf = canonb;
                    181:        tty.size = sizeof(canonb) - 1;
                    182:
                    183:        for (cp = pr; *cp != '\0'; cp++)
                    184:                tty_insert(&tty, *cp, 1);
                    185:        tty_flush(&tty);
                    186:        tty_reset(&tty);
1.1       deraadt   187:
1.18      millert   188:        if (src != NULL && strlen(src) > sizeof(canonb) - 2) {
1.3       millert   189:                puts("too long to edit");
1.1       deraadt   190:                return(src);
                    191:        }
1.21      anton     192:        if (src != NULL) {
                    193:                for (cp = src; *cp != '\0'; cp++)
                    194:                        tty_insert(&tty, *cp, 1);
                    195:                tty_flush(&tty);
1.1       deraadt   196:        }
1.21      anton     197:
1.13      millert   198:        sigemptyset(&act.sa_mask);
                    199:        act.sa_flags = 0;               /* Note: will not restart syscalls */
                    200:        act.sa_handler = ttyint;
1.16      millert   201:        (void)sigaction(SIGINT, &act, &saveint);
1.13      millert   202:        act.sa_handler = ttystop;
                    203:        (void)sigaction(SIGTSTP, &act, NULL);
                    204:        (void)sigaction(SIGTTOU, &act, NULL);
                    205:        (void)sigaction(SIGTTIN, &act, NULL);
                    206:        (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
1.21      anton     207:        for (;;) {
                    208:                c = tty_getc(&tty);
1.13      millert   209:                switch (ttysignal) {
                    210:                        case SIGINT:
1.21      anton     211:                                tty_visc(&tty, '\003'); /* output ^C */
1.13      millert   212:                                /* FALLTHROUGH */
                    213:                        case 0:
                    214:                                break;
                    215:                        default:
                    216:                                ttysignal = 0;
                    217:                                goto redo;
                    218:                }
1.21      anton     219:                if (c == 0) {
                    220:                        done = 1;
                    221:                } else if (c == '\n') {
                    222:                        tty_putc(&tty, c);
                    223:                        done = 1;
                    224:                } else {
                    225:                        done = tty_insert(&tty, c, 0);
                    226:                        tty_flush(&tty);
                    227:                }
                    228:                if (done)
1.1       deraadt   229:                        break;
                    230:        }
1.13      millert   231:        act.sa_handler = SIG_DFL;
                    232:        sigemptyset(&act.sa_mask);
                    233:        act.sa_flags = SA_RESTART;
                    234:        (void)sigprocmask(SIG_SETMASK, &oset, NULL);
                    235:        (void)sigaction(SIGTSTP, &act, NULL);
                    236:        (void)sigaction(SIGTTOU, &act, NULL);
                    237:        (void)sigaction(SIGTTIN, &act, NULL);
1.16      millert   238:        (void)sigaction(SIGINT, &saveint, NULL);
1.21      anton     239:        if (tty.flags & TTY_ERR) {
                    240:                if (ttysignal == SIGINT) {
                    241:                        ttysignal = 0;
                    242:                        return(NULL);   /* user hit ^C */
                    243:                }
                    244:
1.1       deraadt   245: redo:
1.5       millert   246:                cp = strlen(canonb) > 0 ? canonb : NULL;
1.13      millert   247:                /* XXX - make iterative, not recursive */
1.1       deraadt   248:                return(readtty(pr, cp));
                    249:        }
                    250:        if (equal("", canonb))
1.13      millert   251:                return("");
1.1       deraadt   252:        return(savestr(canonb));
                    253: }
                    254:
                    255: /*
                    256:  * Receipt continuation.
                    257:  */
                    258: void
1.14      millert   259: ttystop(int s)
1.1       deraadt   260: {
1.13      millert   261:        struct sigaction act, oact;
1.2       deraadt   262:        sigset_t nset;
1.13      millert   263:        int save_errno;
1.1       deraadt   264:
1.13      millert   265:        /*
                    266:         * Save old handler and set to default.
                    267:         * Unblock receipt of 's' and then resend it.
                    268:         */
                    269:        save_errno = errno;
                    270:        (void)sigemptyset(&act.sa_mask);
                    271:        act.sa_flags = SA_RESTART;
                    272:        act.sa_handler = SIG_DFL;
                    273:        (void)sigaction(s, &act, &oact);
1.8       millert   274:        (void)sigemptyset(&nset);
                    275:        (void)sigaddset(&nset, s);
                    276:        (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
                    277:        (void)kill(0, s);
                    278:        (void)sigprocmask(SIG_BLOCK, &nset, NULL);
1.13      millert   279:        (void)sigaction(s, &oact, NULL);
                    280:        ttysignal = s;
                    281:        errno = save_errno;
1.1       deraadt   282: }
                    283:
                    284: void
1.14      millert   285: ttyint(int s)
1.1       deraadt   286: {
1.13      millert   287:
                    288:        ttysignal = s;
1.21      anton     289: }
                    290:
                    291: static void
                    292: tty_flush(struct tty *t)
                    293: {
                    294:        size_t  i, len;
                    295:        int     c;
                    296:
                    297:        if (t->cursor < t->len) {
                    298:                for (; t->cursor < t->len; t->cursor++)
                    299:                        tty_visc(t, t->buf[t->cursor]);
                    300:        } else if (t->cursor > t->len) {
                    301:                len = t->cursor - t->len;
                    302:                for (i = len; i > 0; i--) {
                    303:                        c = t->buf[--t->cursor];
                    304:                        if (c == '\t')
                    305:                                len += TABWIDTH - 1;
                    306:                        else if (iscntrl(c))
                    307:                                len++;  /* account for leading ^ */
                    308:                }
                    309:                for (i = 0; i < len; i++)
                    310:                        tty_putc(t, '\b');
                    311:                for (i = 0; i < len; i++)
                    312:                        tty_putc(t, ' ');
                    313:                for (i = 0; i < len; i++)
                    314:                        tty_putc(t, '\b');
                    315:                t->cursor = t->len;
                    316:        }
                    317:
                    318:        t->buf[t->len] = '\0';
                    319: }
                    320:
                    321: static int
                    322: tty_getc(struct tty *t)
                    323: {
                    324:        ssize_t         n;
                    325:        unsigned char   c;
                    326:
                    327:        n = read(t->fdin, &c, 1);
                    328:        switch (n) {
                    329:        case -1:
                    330:                t->flags |= TTY_ERR;
                    331:                /* FALLTHROUGH */
                    332:        case 0:
                    333:                return 0;
                    334:        default:
                    335:                return c & 0x7f;
                    336:        }
                    337: }
                    338:
                    339: static int
                    340: tty_insert(struct tty *t, int c, int nocntrl)
                    341: {
                    342:        const unsigned char     *ws = " \t";
                    343:
                    344:        if (CCEQ(t->keys[VERASE], c)) {
                    345:                if (nocntrl)
                    346:                        return 0;
                    347:                if (t->len > 0)
                    348:                        t->len--;
                    349:        } else if (CCEQ(t->keys[VWERASE], c)) {
                    350:                if (nocntrl)
                    351:                        return 0;
                    352:                for (; t->len > 0; t->len--)
                    353:                        if (strchr(ws, t->buf[t->len - 1]) == NULL
                    354:                            && ((t->flags & TTY_ALTWERASE) == 0
                    355:                                    || isalpha(t->buf[t->len - 1])))
                    356:                                break;
                    357:                for (; t->len > 0; t->len--)
                    358:                        if (strchr(ws, t->buf[t->len - 1]) != NULL
                    359:                            || ((t->flags & TTY_ALTWERASE)
                    360:                                    && !isalpha(t->buf[t->len - 1])))
                    361:                                break;
                    362:        } else if (CCEQ(t->keys[VKILL], c)) {
                    363:                if (nocntrl)
                    364:                        return 0;
                    365:                t->len = 0;
                    366:        } else {
                    367:                if (t->len == t->size)
                    368:                        return 1;
                    369:                t->buf[t->len++] = c;
                    370:        }
                    371:
                    372:        return 0;
                    373: }
                    374:
                    375: static void
                    376: tty_putc(struct tty *t, int c)
                    377: {
                    378:        unsigned char   cc = c;
                    379:
                    380:        write(t->fdout, &cc, 1);
                    381: }
                    382:
                    383: static void
                    384: tty_reset(struct tty *t)
                    385: {
                    386:        memset(t->buf, 0, t->len);
                    387:        t->len = t->cursor = 0;
                    388: }
                    389:
                    390: static void
                    391: tty_visc(struct tty *t, int c)
                    392: {
                    393:        int     i;
                    394:
                    395:        if (c == '\t') {
                    396:                for (i = 0; i < TABWIDTH; i++)
                    397:                        tty_putc(t, ' ');
                    398:        } else if (iscntrl(c)) {
                    399:                tty_putc(t, '^');
                    400:                if (c == 0x7F)
                    401:                        tty_putc(t, '?');
                    402:                else
                    403:                        tty_putc(t, (c | 0x40));
                    404:        } else {
                    405:                tty_putc(t, c);
                    406:        }
1.1       deraadt   407: }