[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.22

1.22    ! deraadt     1: /*     $OpenBSD: tty.c,v 1.21 2017/06/28 14:58:23 anton 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: /*ARGSUSED*/
                    285: void
1.14      millert   286: ttyint(int s)
1.1       deraadt   287: {
1.13      millert   288:
                    289:        ttysignal = s;
1.21      anton     290: }
                    291:
                    292: static void
                    293: tty_flush(struct tty *t)
                    294: {
                    295:        size_t  i, len;
                    296:        int     c;
                    297:
                    298:        if (t->cursor < t->len) {
                    299:                for (; t->cursor < t->len; t->cursor++)
                    300:                        tty_visc(t, t->buf[t->cursor]);
                    301:        } else if (t->cursor > t->len) {
                    302:                len = t->cursor - t->len;
                    303:                for (i = len; i > 0; i--) {
                    304:                        c = t->buf[--t->cursor];
                    305:                        if (c == '\t')
                    306:                                len += TABWIDTH - 1;
                    307:                        else if (iscntrl(c))
                    308:                                len++;  /* account for leading ^ */
                    309:                }
                    310:                for (i = 0; i < len; i++)
                    311:                        tty_putc(t, '\b');
                    312:                for (i = 0; i < len; i++)
                    313:                        tty_putc(t, ' ');
                    314:                for (i = 0; i < len; i++)
                    315:                        tty_putc(t, '\b');
                    316:                t->cursor = t->len;
                    317:        }
                    318:
                    319:        t->buf[t->len] = '\0';
                    320: }
                    321:
                    322: static int
                    323: tty_getc(struct tty *t)
                    324: {
                    325:        ssize_t         n;
                    326:        unsigned char   c;
                    327:
                    328:        n = read(t->fdin, &c, 1);
                    329:        switch (n) {
                    330:        case -1:
                    331:                t->flags |= TTY_ERR;
                    332:                /* FALLTHROUGH */
                    333:        case 0:
                    334:                return 0;
                    335:        default:
                    336:                return c & 0x7f;
                    337:        }
                    338: }
                    339:
                    340: static int
                    341: tty_insert(struct tty *t, int c, int nocntrl)
                    342: {
                    343:        const unsigned char     *ws = " \t";
                    344:
                    345:        if (CCEQ(t->keys[VERASE], c)) {
                    346:                if (nocntrl)
                    347:                        return 0;
                    348:                if (t->len > 0)
                    349:                        t->len--;
                    350:        } else if (CCEQ(t->keys[VWERASE], c)) {
                    351:                if (nocntrl)
                    352:                        return 0;
                    353:                for (; t->len > 0; t->len--)
                    354:                        if (strchr(ws, t->buf[t->len - 1]) == NULL
                    355:                            && ((t->flags & TTY_ALTWERASE) == 0
                    356:                                    || isalpha(t->buf[t->len - 1])))
                    357:                                break;
                    358:                for (; t->len > 0; t->len--)
                    359:                        if (strchr(ws, t->buf[t->len - 1]) != NULL
                    360:                            || ((t->flags & TTY_ALTWERASE)
                    361:                                    && !isalpha(t->buf[t->len - 1])))
                    362:                                break;
                    363:        } else if (CCEQ(t->keys[VKILL], c)) {
                    364:                if (nocntrl)
                    365:                        return 0;
                    366:                t->len = 0;
                    367:        } else {
                    368:                if (t->len == t->size)
                    369:                        return 1;
                    370:                t->buf[t->len++] = c;
                    371:        }
                    372:
                    373:        return 0;
                    374: }
                    375:
                    376: static void
                    377: tty_putc(struct tty *t, int c)
                    378: {
                    379:        unsigned char   cc = c;
                    380:
                    381:        write(t->fdout, &cc, 1);
                    382: }
                    383:
                    384: static void
                    385: tty_reset(struct tty *t)
                    386: {
                    387:        memset(t->buf, 0, t->len);
                    388:        t->len = t->cursor = 0;
                    389: }
                    390:
                    391: static void
                    392: tty_visc(struct tty *t, int c)
                    393: {
                    394:        int     i;
                    395:
                    396:        if (c == '\t') {
                    397:                for (i = 0; i < TABWIDTH; i++)
                    398:                        tty_putc(t, ' ');
                    399:        } else if (iscntrl(c)) {
                    400:                tty_putc(t, '^');
                    401:                if (c == 0x7F)
                    402:                        tty_putc(t, '?');
                    403:                else
                    404:                        tty_putc(t, (c | 0x40));
                    405:        } else {
                    406:                tty_putc(t, c);
                    407:        }
1.1       deraadt   408: }