tty.c revision 1.15
1/*	$OpenBSD: tty.c,v 1.15 2001/11/21 20:41:56 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. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static const char sccsid[] = "@(#)tty.c	8.2 (Berkeley) 4/20/95";
40#else
41static const char rcsid[] = "$OpenBSD: tty.c,v 1.15 2001/11/21 20:41:56 millert Exp $";
42#endif
43#endif /* not lint */
44
45/*
46 * Mail -- a mail program
47 *
48 * Generally useful tty stuff.
49 */
50
51#include "rcv.h"
52#include "extern.h"
53#include <sys/ioctl.h>
54#include <errno.h>
55
56static	cc_t		c_erase;	/* Current erase char */
57static	cc_t		c_kill;		/* Current kill char */
58#ifndef TIOCSTI
59static	int		ttyset;		/* We must now do erase/kill */
60#endif
61static	volatile sig_atomic_t	ttysignal;	/* Interrupted by a signal? */
62
63/*
64 * Read all relevant header fields.
65 */
66int
67grabh(struct header *hp, int gflags)
68{
69	struct termios ttybuf;
70#ifndef TIOCSTI
71	struct sigaction savequit;
72#else
73# ifdef	TIOCEXT
74	int extproc;
75	int flag;
76# endif /* TIOCEXT */
77#endif
78	struct sigaction savetstp;
79	struct sigaction savettou;
80	struct sigaction savettin;
81	struct sigaction act;
82	char *s;
83	int error;
84
85	sigemptyset(&act.sa_mask);
86	act.sa_flags = SA_RESTART;
87	act.sa_handler = SIG_DFL;
88	(void)sigaction(SIGTSTP, &act, &savetstp);
89	(void)sigaction(SIGTTOU, &act, &savettou);
90	(void)sigaction(SIGTTIN, &act, &savettin);
91	error = 1;
92#ifndef TIOCSTI
93	ttyset = 0;
94#endif
95	if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
96		warn("tcgetattr");
97		return(-1);
98	}
99	c_erase = ttybuf.c_cc[VERASE];
100	c_kill = ttybuf.c_cc[VKILL];
101#ifndef TIOCSTI
102	ttybuf.c_cc[VERASE] = 0;
103	ttybuf.c_cc[VKILL] = 0;
104	act.sa_handler = SIG_IGN;
105	if (sigaction(SIGQUIT, &act, &savequit) == 0 &&
106	    savequit.sa_handler == SIG_DFL)
107		(void)sigaction(SIGQUIT, &savequit, NULL);
108#else
109# ifdef	TIOCEXT
110	extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0);
111	if (extproc) {
112		flag = 0;
113		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
114			warn("TIOCEXT: off");
115	}
116# endif /* TIOCEXT */
117#endif
118	if (gflags & GTO) {
119#ifndef TIOCSTI
120		if (!ttyset && hp->h_to != NULL)
121			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
122#endif
123		s = readtty("To: ", detract(hp->h_to, 0));
124		if (s == NULL)
125			goto out;
126		hp->h_to = extract(s, GTO);
127	}
128	if (gflags & GSUBJECT) {
129#ifndef TIOCSTI
130		if (!ttyset && hp->h_subject != NULL)
131			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
132#endif
133		s = readtty("Subject: ", hp->h_subject);
134		if (s == NULL)
135			goto out;
136		hp->h_subject = s;
137	}
138	if (gflags & GCC) {
139#ifndef TIOCSTI
140		if (!ttyset && hp->h_cc != NULL)
141			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
142#endif
143		s = readtty("Cc: ", detract(hp->h_cc, 0));
144		if (s == NULL)
145			goto out;
146		hp->h_cc = extract(s, GCC);
147	}
148	if (gflags & GBCC) {
149#ifndef TIOCSTI
150		if (!ttyset && hp->h_bcc != NULL)
151			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
152#endif
153		s = readtty("Bcc: ", detract(hp->h_bcc, 0));
154		if (s == NULL)
155			goto out;
156		hp->h_bcc = extract(s, GBCC);
157	}
158	error = 0;
159out:
160	(void)sigaction(SIGTSTP, &savetstp, NULL);
161	(void)sigaction(SIGTTOU, &savettou, NULL);
162	(void)sigaction(SIGTTIN, &savettin, NULL);
163#ifndef TIOCSTI
164	ttybuf.c_cc[VERASE] = c_erase;
165	ttybuf.c_cc[VKILL] = c_kill;
166	if (ttyset)
167		tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
168	(void)sigaction(SIGQUIT, &savequit, NULL);
169#else
170# ifdef	TIOCEXT
171	if (extproc) {
172		flag = 1;
173		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
174			warn("TIOCEXT: on");
175	}
176# endif /* TIOCEXT */
177#endif
178	return(error);
179}
180
181/*
182 * Read up a header from standard input.
183 * The source string has the preliminary contents to
184 * be read.
185 *
186 */
187char *
188readtty(char *pr, char *src)
189{
190	struct sigaction act, oact;
191	char ch, canonb[BUFSIZ];
192	char *cp, *cp2;
193	sigset_t oset;
194	int c;
195
196	fputs(pr, stdout);
197	fflush(stdout);
198	if (src != NULL && strlen(src) > BUFSIZ - 2) {
199		puts("too long to edit");
200		return(src);
201	}
202#ifndef TIOCSTI
203	if (src != NULL)
204		cp = copy(src, canonb);	/* safe, bounds checked above */
205	else
206		cp = copy("", canonb);
207	fputs(canonb, stdout);
208	fflush(stdout);
209#else
210	cp = src == NULL ? "" : src;
211	while ((c = *cp++) != '\0') {
212		if ((c_erase != _POSIX_VDISABLE && c == c_erase) ||
213		    (c_kill != _POSIX_VDISABLE && c == c_kill)) {
214			ch = '\\';
215			ioctl(0, TIOCSTI, &ch);
216		}
217		ch = c;
218		ioctl(0, TIOCSTI, &ch);
219	}
220	cp = canonb;
221	*cp = 0;
222#endif
223	cp2 = cp;
224	while (cp2 < canonb + BUFSIZ)
225		*cp2++ = 0;
226	cp2 = cp;
227	sigemptyset(&act.sa_mask);
228	act.sa_flags = 0;		/* Note: will not restart syscalls */
229	act.sa_handler = ttyint;
230	(void)sigaction(SIGINT, &act, &oact);
231	act.sa_handler = ttystop;
232	(void)sigaction(SIGTSTP, &act, NULL);
233	(void)sigaction(SIGTTOU, &act, NULL);
234	(void)sigaction(SIGTTIN, &act, NULL);
235	(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
236	clearerr(stdin);
237	while (cp2 < canonb + BUFSIZ) {
238		c = getc(stdin);
239		switch (ttysignal) {
240			case SIGINT:
241				ttysignal = 0;
242				cp2 = NULL;
243				c = EOF;
244				/* FALLTHROUGH */
245			case 0:
246				break;
247			default:
248				ttysignal = 0;
249				goto redo;
250		}
251		if (c == EOF || c == '\n')
252			break;
253		*cp2++ = c;
254	}
255	act.sa_handler = SIG_DFL;
256	sigemptyset(&act.sa_mask);
257	act.sa_flags = SA_RESTART;
258	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
259	(void)sigaction(SIGTSTP, &act, NULL);
260	(void)sigaction(SIGTTOU, &act, NULL);
261	(void)sigaction(SIGTTIN, &act, NULL);
262	(void)sigaction(SIGTTIN, &oact, NULL);
263	if (cp2 == NULL)
264		return(NULL);			/* user hit ^C */
265	*cp2 = '\0';
266	if (c == EOF && ferror(stdin)) {
267redo:
268		cp = strlen(canonb) > 0 ? canonb : NULL;
269		clearerr(stdin);
270		/* XXX - make iterative, not recursive */
271		return(readtty(pr, cp));
272	}
273#ifndef TIOCSTI
274	if (cp == NULL || *cp == '\0')
275		return(src);
276	cp2 = cp;
277	if (!ttyset)
278		return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
279	while (*cp != '\0') {
280		c = *cp++;
281		if (c_erase != _POSIX_VDISABLE && c == c_erase) {
282			if (cp2 == canonb)
283				continue;
284			if (cp2[-1] == '\\') {
285				cp2[-1] = c;
286				continue;
287			}
288			cp2--;
289			continue;
290		}
291		if (c_kill != _POSIX_VDISABLE && c == c_kill) {
292			if (cp2 == canonb)
293				continue;
294			if (cp2[-1] == '\\') {
295				cp2[-1] = c;
296				continue;
297			}
298			cp2 = canonb;
299			continue;
300		}
301		*cp2++ = c;
302	}
303	*cp2 = '\0';
304#endif
305	if (equal("", canonb))
306		return("");
307	return(savestr(canonb));
308}
309
310/*
311 * Receipt continuation.
312 */
313void
314ttystop(int s)
315{
316	struct sigaction act, oact;
317	sigset_t nset;
318	int save_errno;
319
320	/*
321	 * Save old handler and set to default.
322	 * Unblock receipt of 's' and then resend it.
323	 */
324	save_errno = errno;
325	(void)sigemptyset(&act.sa_mask);
326	act.sa_flags = SA_RESTART;
327	act.sa_handler = SIG_DFL;
328	(void)sigaction(s, &act, &oact);
329	(void)sigemptyset(&nset);
330	(void)sigaddset(&nset, s);
331	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
332	(void)kill(0, s);
333	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
334	(void)sigaction(s, &oact, NULL);
335	ttysignal = s;
336	errno = save_errno;
337}
338
339/*ARGSUSED*/
340void
341ttyint(int s)
342{
343
344	ttysignal = s;
345}
346