xref: /openbsd-src/usr.bin/mail/tty.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: tty.c,v 1.18 2005/07/11 14:08:23 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.18 2005/07/11 14:08:23 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) > sizeof(canonb) - 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 	sigemptyset(&act.sa_mask);
220 	act.sa_flags = 0;		/* Note: will not restart syscalls */
221 	act.sa_handler = ttyint;
222 	(void)sigaction(SIGINT, &act, &saveint);
223 	act.sa_handler = ttystop;
224 	(void)sigaction(SIGTSTP, &act, NULL);
225 	(void)sigaction(SIGTTOU, &act, NULL);
226 	(void)sigaction(SIGTTIN, &act, NULL);
227 	(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
228 	clearerr(stdin);
229 	memset(cp, 0, canonb + sizeof(canonb) - cp);
230 	for (cp2 = cp; cp2 < canonb + sizeof(canonb) - 1; ) {
231 		c = getc(stdin);
232 		switch (ttysignal) {
233 			case SIGINT:
234 				ttysignal = 0;
235 				cp2 = NULL;
236 				c = EOF;
237 				/* FALLTHROUGH */
238 			case 0:
239 				break;
240 			default:
241 				ttysignal = 0;
242 				goto redo;
243 		}
244 		if (c == EOF || c == '\n')
245 			break;
246 		*cp2++ = c;
247 	}
248 	act.sa_handler = SIG_DFL;
249 	sigemptyset(&act.sa_mask);
250 	act.sa_flags = SA_RESTART;
251 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
252 	(void)sigaction(SIGTSTP, &act, NULL);
253 	(void)sigaction(SIGTTOU, &act, NULL);
254 	(void)sigaction(SIGTTIN, &act, NULL);
255 	(void)sigaction(SIGINT, &saveint, NULL);
256 	if (cp2 == NULL)
257 		return(NULL);			/* user hit ^C */
258 	*cp2 = '\0';
259 	if (c == EOF && ferror(stdin)) {
260 redo:
261 		cp = strlen(canonb) > 0 ? canonb : NULL;
262 		clearerr(stdin);
263 		/* XXX - make iterative, not recursive */
264 		return(readtty(pr, cp));
265 	}
266 #ifndef TIOCSTI
267 	if (cp == NULL || *cp == '\0')
268 		return(src);
269 	cp2 = cp;
270 	if (!ttyset)
271 		return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
272 	while (*cp != '\0') {
273 		c = *cp++;
274 		if (c_erase != _POSIX_VDISABLE && c == c_erase) {
275 			if (cp2 == canonb)
276 				continue;
277 			if (cp2[-1] == '\\') {
278 				cp2[-1] = c;
279 				continue;
280 			}
281 			cp2--;
282 			continue;
283 		}
284 		if (c_kill != _POSIX_VDISABLE && c == c_kill) {
285 			if (cp2 == canonb)
286 				continue;
287 			if (cp2[-1] == '\\') {
288 				cp2[-1] = c;
289 				continue;
290 			}
291 			cp2 = canonb;
292 			continue;
293 		}
294 		*cp2++ = c;
295 	}
296 	*cp2 = '\0';
297 #endif
298 	if (equal("", canonb))
299 		return("");
300 	return(savestr(canonb));
301 }
302 
303 /*
304  * Receipt continuation.
305  */
306 void
307 ttystop(int s)
308 {
309 	struct sigaction act, oact;
310 	sigset_t nset;
311 	int save_errno;
312 
313 	/*
314 	 * Save old handler and set to default.
315 	 * Unblock receipt of 's' and then resend it.
316 	 */
317 	save_errno = errno;
318 	(void)sigemptyset(&act.sa_mask);
319 	act.sa_flags = SA_RESTART;
320 	act.sa_handler = SIG_DFL;
321 	(void)sigaction(s, &act, &oact);
322 	(void)sigemptyset(&nset);
323 	(void)sigaddset(&nset, s);
324 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
325 	(void)kill(0, s);
326 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
327 	(void)sigaction(s, &oact, NULL);
328 	ttysignal = s;
329 	errno = save_errno;
330 }
331 
332 /*ARGSUSED*/
333 void
334 ttyint(int s)
335 {
336 
337 	ttysignal = s;
338 }
339