xref: /openbsd/usr.bin/mail/tty.c (revision db3296cf)
1 /*	$OpenBSD: tty.c,v 1.17 2003/06/03 02:56:11 millert Exp $	*/
2 /*	$NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $	*/
3 
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.
16  * 3. Neither the name of the University nor the names of its contributors
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 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)tty.c	8.2 (Berkeley) 4/20/95";
36 #else
37 static const char rcsid[] = "$OpenBSD: tty.c,v 1.17 2003/06/03 02:56:11 millert Exp $";
38 #endif
39 #endif /* not lint */
40 
41 /*
42  * Mail -- a mail program
43  *
44  * Generally useful tty stuff.
45  */
46 
47 #include "rcv.h"
48 #include "extern.h"
49 #include <sys/ioctl.h>
50 #include <errno.h>
51 
52 static	cc_t		c_erase;	/* Current erase char */
53 static	cc_t		c_kill;		/* Current kill char */
54 #ifndef TIOCSTI
55 static	int		ttyset;		/* We must now do erase/kill */
56 #endif
57 static	volatile sig_atomic_t	ttysignal;	/* Interrupted by a signal? */
58 
59 /*
60  * Read all relevant header fields.
61  */
62 int
63 grabh(struct header *hp, int gflags)
64 {
65 	struct termios ttybuf;
66 #ifndef TIOCSTI
67 	struct sigaction savequit;
68 #else
69 # ifdef	TIOCEXT
70 	int extproc;
71 	int flag;
72 # endif /* TIOCEXT */
73 #endif
74 	struct sigaction savetstp;
75 	struct sigaction savettou;
76 	struct sigaction savettin;
77 	struct sigaction act;
78 	char *s;
79 	int error;
80 
81 	sigemptyset(&act.sa_mask);
82 	act.sa_flags = SA_RESTART;
83 	act.sa_handler = SIG_DFL;
84 	(void)sigaction(SIGTSTP, &act, &savetstp);
85 	(void)sigaction(SIGTTOU, &act, &savettou);
86 	(void)sigaction(SIGTTIN, &act, &savettin);
87 	error = 1;
88 #ifndef TIOCSTI
89 	ttyset = 0;
90 #endif
91 	if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
92 		warn("tcgetattr");
93 		return(-1);
94 	}
95 	c_erase = ttybuf.c_cc[VERASE];
96 	c_kill = ttybuf.c_cc[VKILL];
97 #ifndef TIOCSTI
98 	ttybuf.c_cc[VERASE] = 0;
99 	ttybuf.c_cc[VKILL] = 0;
100 	act.sa_handler = SIG_IGN;
101 	if (sigaction(SIGQUIT, &act, &savequit) == 0 &&
102 	    savequit.sa_handler == SIG_DFL)
103 		(void)sigaction(SIGQUIT, &savequit, NULL);
104 #else
105 # ifdef	TIOCEXT
106 	extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0);
107 	if (extproc) {
108 		flag = 0;
109 		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
110 			warn("TIOCEXT: off");
111 	}
112 # endif /* TIOCEXT */
113 #endif
114 	if (gflags & GTO) {
115 #ifndef TIOCSTI
116 		if (!ttyset && hp->h_to != NULL)
117 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
118 #endif
119 		s = readtty("To: ", detract(hp->h_to, 0));
120 		if (s == NULL)
121 			goto out;
122 		hp->h_to = extract(s, GTO);
123 	}
124 	if (gflags & GSUBJECT) {
125 #ifndef TIOCSTI
126 		if (!ttyset && hp->h_subject != NULL)
127 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
128 #endif
129 		s = readtty("Subject: ", hp->h_subject);
130 		if (s == NULL)
131 			goto out;
132 		hp->h_subject = s;
133 	}
134 	if (gflags & GCC) {
135 #ifndef TIOCSTI
136 		if (!ttyset && hp->h_cc != NULL)
137 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
138 #endif
139 		s = readtty("Cc: ", detract(hp->h_cc, 0));
140 		if (s == NULL)
141 			goto out;
142 		hp->h_cc = extract(s, GCC);
143 	}
144 	if (gflags & GBCC) {
145 #ifndef TIOCSTI
146 		if (!ttyset && hp->h_bcc != NULL)
147 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
148 #endif
149 		s = readtty("Bcc: ", detract(hp->h_bcc, 0));
150 		if (s == NULL)
151 			goto out;
152 		hp->h_bcc = extract(s, GBCC);
153 	}
154 	error = 0;
155 out:
156 	(void)sigaction(SIGTSTP, &savetstp, NULL);
157 	(void)sigaction(SIGTTOU, &savettou, NULL);
158 	(void)sigaction(SIGTTIN, &savettin, NULL);
159 #ifndef TIOCSTI
160 	ttybuf.c_cc[VERASE] = c_erase;
161 	ttybuf.c_cc[VKILL] = c_kill;
162 	if (ttyset)
163 		tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
164 	(void)sigaction(SIGQUIT, &savequit, NULL);
165 #else
166 # ifdef	TIOCEXT
167 	if (extproc) {
168 		flag = 1;
169 		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
170 			warn("TIOCEXT: on");
171 	}
172 # endif /* TIOCEXT */
173 #endif
174 	return(error);
175 }
176 
177 /*
178  * Read up a header from standard input.
179  * The source string has the preliminary contents to
180  * be read.
181  *
182  */
183 char *
184 readtty(char *pr, char *src)
185 {
186 	struct sigaction act, saveint;
187 	char ch, canonb[BUFSIZ];
188 	char *cp, *cp2;
189 	sigset_t oset;
190 	int c;
191 
192 	fputs(pr, stdout);
193 	fflush(stdout);
194 	if (src != NULL && strlen(src) > BUFSIZ - 2) {
195 		puts("too long to edit");
196 		return(src);
197 	}
198 #ifndef TIOCSTI
199 	if (src != NULL)
200 		cp = copy(src, canonb);	/* safe, bounds checked above */
201 	else
202 		cp = copy("", canonb);
203 	fputs(canonb, stdout);
204 	fflush(stdout);
205 #else
206 	cp = src == NULL ? "" : src;
207 	while ((c = *cp++) != '\0') {
208 		if ((c_erase != _POSIX_VDISABLE && c == c_erase) ||
209 		    (c_kill != _POSIX_VDISABLE && c == c_kill)) {
210 			ch = '\\';
211 			ioctl(0, TIOCSTI, &ch);
212 		}
213 		ch = c;
214 		ioctl(0, TIOCSTI, &ch);
215 	}
216 	cp = canonb;
217 	*cp = 0;
218 #endif
219 	cp2 = cp;
220 	while (cp2 < canonb + BUFSIZ)
221 		*cp2++ = 0;
222 	cp2 = cp;
223 	sigemptyset(&act.sa_mask);
224 	act.sa_flags = 0;		/* Note: will not restart syscalls */
225 	act.sa_handler = ttyint;
226 	(void)sigaction(SIGINT, &act, &saveint);
227 	act.sa_handler = ttystop;
228 	(void)sigaction(SIGTSTP, &act, NULL);
229 	(void)sigaction(SIGTTOU, &act, NULL);
230 	(void)sigaction(SIGTTIN, &act, NULL);
231 	(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
232 	clearerr(stdin);
233 	while (cp2 < canonb + BUFSIZ) {
234 		c = getc(stdin);
235 		switch (ttysignal) {
236 			case SIGINT:
237 				ttysignal = 0;
238 				cp2 = NULL;
239 				c = EOF;
240 				/* FALLTHROUGH */
241 			case 0:
242 				break;
243 			default:
244 				ttysignal = 0;
245 				goto redo;
246 		}
247 		if (c == EOF || c == '\n')
248 			break;
249 		*cp2++ = c;
250 	}
251 	act.sa_handler = SIG_DFL;
252 	sigemptyset(&act.sa_mask);
253 	act.sa_flags = SA_RESTART;
254 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
255 	(void)sigaction(SIGTSTP, &act, NULL);
256 	(void)sigaction(SIGTTOU, &act, NULL);
257 	(void)sigaction(SIGTTIN, &act, NULL);
258 	(void)sigaction(SIGINT, &saveint, NULL);
259 	if (cp2 == NULL)
260 		return(NULL);			/* user hit ^C */
261 	*cp2 = '\0';
262 	if (c == EOF && ferror(stdin)) {
263 redo:
264 		cp = strlen(canonb) > 0 ? canonb : NULL;
265 		clearerr(stdin);
266 		/* XXX - make iterative, not recursive */
267 		return(readtty(pr, cp));
268 	}
269 #ifndef TIOCSTI
270 	if (cp == NULL || *cp == '\0')
271 		return(src);
272 	cp2 = cp;
273 	if (!ttyset)
274 		return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
275 	while (*cp != '\0') {
276 		c = *cp++;
277 		if (c_erase != _POSIX_VDISABLE && c == c_erase) {
278 			if (cp2 == canonb)
279 				continue;
280 			if (cp2[-1] == '\\') {
281 				cp2[-1] = c;
282 				continue;
283 			}
284 			cp2--;
285 			continue;
286 		}
287 		if (c_kill != _POSIX_VDISABLE && c == c_kill) {
288 			if (cp2 == canonb)
289 				continue;
290 			if (cp2[-1] == '\\') {
291 				cp2[-1] = c;
292 				continue;
293 			}
294 			cp2 = canonb;
295 			continue;
296 		}
297 		*cp2++ = c;
298 	}
299 	*cp2 = '\0';
300 #endif
301 	if (equal("", canonb))
302 		return("");
303 	return(savestr(canonb));
304 }
305 
306 /*
307  * Receipt continuation.
308  */
309 void
310 ttystop(int s)
311 {
312 	struct sigaction act, oact;
313 	sigset_t nset;
314 	int save_errno;
315 
316 	/*
317 	 * Save old handler and set to default.
318 	 * Unblock receipt of 's' and then resend it.
319 	 */
320 	save_errno = errno;
321 	(void)sigemptyset(&act.sa_mask);
322 	act.sa_flags = SA_RESTART;
323 	act.sa_handler = SIG_DFL;
324 	(void)sigaction(s, &act, &oact);
325 	(void)sigemptyset(&nset);
326 	(void)sigaddset(&nset, s);
327 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
328 	(void)kill(0, s);
329 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
330 	(void)sigaction(s, &oact, NULL);
331 	ttysignal = s;
332 	errno = save_errno;
333 }
334 
335 /*ARGSUSED*/
336 void
337 ttyint(int s)
338 {
339 
340 	ttysignal = s;
341 }
342