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: }