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