Annotation of src/usr.bin/mail/tty.c, Revision 1.21
1.21 ! anton 1: /* $OpenBSD: tty.c,v 1.20 2014/01/17 18:42:30 okan 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);
! 98: if (tcgetattr(tty.fdin, &oldtio) < 0) {
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;
! 110: if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) < 0) {
! 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;
119: if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
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;
155: if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
156: warn("TIOCEXT: on");
157: }
1.1 deraadt 158: #endif
1.21 ! anton 159: if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) < 0)
! 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: }