xref: /freebsd/usr.bin/mail/tty.c (revision 9bd497b8354567454e075076d40c996e21bd6095)
1 /*
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)tty.c	8.2 (Berkeley) 6/6/93";
37 #endif
38 #endif /* not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 /*
43  * Mail -- a mail program
44  *
45  * Generally useful tty stuff.
46  */
47 
48 #include "rcv.h"
49 #include "extern.h"
50 
51 static	cc_t	c_erase;		/* Current erase char */
52 static	cc_t	c_kill;			/* Current kill char */
53 static	jmp_buf	rewrite;		/* Place to go when continued */
54 static	jmp_buf	intjmp;			/* Place to go when interrupted */
55 #ifndef TIOCSTI
56 static	int	ttyset;			/* We must now do erase/kill */
57 #endif
58 
59 /*
60  * Read all relevant header fields.
61  */
62 
63 int
64 grabh(hp, gflags)
65 	struct header *hp;
66 	int gflags;
67 {
68 	struct termios ttybuf;
69 	sig_t saveint;
70 	sig_t savetstp;
71 	sig_t savettou;
72 	sig_t savettin;
73 	int errs;
74 #ifndef TIOCSTI
75 	sig_t savequit;
76 #else
77 # ifdef TIOCEXT
78 	int extproc, flag;
79 # endif /* TIOCEXT */
80 #endif /* TIOCSTI */
81 
82 	savetstp = signal(SIGTSTP, SIG_DFL);
83 	savettou = signal(SIGTTOU, SIG_DFL);
84 	savettin = signal(SIGTTIN, SIG_DFL);
85 	errs = 0;
86 #ifndef TIOCSTI
87 	ttyset = 0;
88 #endif
89 	if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
90 		warn("tcgetattr(stdin)");
91 		return (-1);
92 	}
93 	c_erase = ttybuf.c_cc[VERASE];
94 	c_kill = ttybuf.c_cc[VKILL];
95 #ifndef TIOCSTI
96 	ttybuf.c_cc[VERASE] = _POSIX_VDISABLE;
97 	ttybuf.c_cc[VKILL] = _POSIX_VDISABLE;
98 	if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL)
99 		(void)signal(SIGINT, SIG_DFL);
100 	if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
101 		(void)signal(SIGQUIT, SIG_DFL);
102 #else
103 # ifdef		TIOCEXT
104 	extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0);
105 	if (extproc) {
106 		flag = 0;
107 		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
108 			warn("TIOCEXT: off");
109 	}
110 # endif	/* TIOCEXT */
111 	if (setjmp(intjmp))
112 		goto out;
113 	saveint = signal(SIGINT, ttyint);
114 #endif
115 	if (gflags & GTO) {
116 #ifndef TIOCSTI
117 		if (!ttyset && hp->h_to != NULL)
118 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
119 #endif
120 		hp->h_to =
121 			extract(readtty("To: ", detract(hp->h_to, 0)), GTO);
122 	}
123 	if (gflags & GSUBJECT) {
124 #ifndef TIOCSTI
125 		if (!ttyset && hp->h_subject != NULL)
126 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
127 #endif
128 		hp->h_subject = readtty("Subject: ", hp->h_subject);
129 	}
130 	if (gflags & GCC) {
131 #ifndef TIOCSTI
132 		if (!ttyset && hp->h_cc != NULL)
133 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
134 #endif
135 		hp->h_cc =
136 			extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC);
137 	}
138 	if (gflags & GBCC) {
139 #ifndef TIOCSTI
140 		if (!ttyset && hp->h_bcc != NULL)
141 			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
142 #endif
143 		hp->h_bcc =
144 			extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC);
145 	}
146 out:
147 	(void)signal(SIGTSTP, savetstp);
148 	(void)signal(SIGTTOU, savettou);
149 	(void)signal(SIGTTIN, savettin);
150 #ifndef TIOCSTI
151 	ttybuf.c_cc[VERASE] = c_erase;
152 	ttybuf.c_cc[VKILL] = c_kill;
153 	if (ttyset)
154 		tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
155 	(void)signal(SIGQUIT, savequit);
156 #else
157 # ifdef		TIOCEXT
158 	if (extproc) {
159 		flag = 1;
160 		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
161 			warn("TIOCEXT: on");
162 	}
163 # endif	/* TIOCEXT */
164 #endif
165 	(void)signal(SIGINT, saveint);
166 	return (errs);
167 }
168 
169 /*
170  * Read up a header from standard input.
171  * The source string has the preliminary contents to
172  * be read.
173  *
174  */
175 
176 char *
177 readtty(pr, src)
178 	const char *pr;
179 	char src[];
180 {
181 	char ch, canonb[BUFSIZ];
182 	int c;
183 	char *cp, *cp2;
184 
185 	fputs(pr, stdout);
186 	(void)fflush(stdout);
187 	if (src != NULL && strlen(src) > BUFSIZ - 2) {
188 		printf("too long to edit\n");
189 		return (src);
190 	}
191 #ifndef TIOCSTI
192 	if (src != NULL)
193 		strlcpy(canonb, src, sizeof(canonb));
194 	else
195 		*canonb = '\0';
196 	fputs(canonb, stdout);
197 	(void)fflush(stdout);
198 #else
199 	cp = src == NULL ? "" : src;
200 	while ((c = *cp++) != '\0') {
201 		if ((c_erase != _POSIX_VDISABLE && c == c_erase) ||
202 		    (c_kill != _POSIX_VDISABLE && c == c_kill)) {
203 			ch = '\\';
204 			ioctl(0, TIOCSTI, &ch);
205 		}
206 		ch = c;
207 		ioctl(0, TIOCSTI, &ch);
208 	}
209 	cp = canonb;
210 	*cp = '\0';
211 #endif
212 	cp2 = cp;
213 	while (cp2 < canonb + BUFSIZ)
214 		*cp2++ = '\0';
215 	cp2 = cp;
216 	if (setjmp(rewrite))
217 		goto redo;
218 	(void)signal(SIGTSTP, ttystop);
219 	(void)signal(SIGTTOU, ttystop);
220 	(void)signal(SIGTTIN, ttystop);
221 	clearerr(stdin);
222 	while (cp2 < canonb + BUFSIZ) {
223 		c = getc(stdin);
224 		if (c == EOF || c == '\n')
225 			break;
226 		*cp2++ = c;
227 	}
228 	*cp2 = '\0';
229 	(void)signal(SIGTSTP, SIG_DFL);
230 	(void)signal(SIGTTOU, SIG_DFL);
231 	(void)signal(SIGTTIN, SIG_DFL);
232 	if (c == EOF && ferror(stdin)) {
233 redo:
234 		cp = strlen(canonb) > 0 ? canonb : NULL;
235 		clearerr(stdin);
236 		return (readtty(pr, cp));
237 	}
238 #ifndef TIOCSTI
239 	if (cp == NULL || *cp == '\0')
240 		return (src);
241 	cp2 = cp;
242 	if (!ttyset)
243 		return (strlen(canonb) > 0 ? savestr(canonb) : NULL);
244 	while (*cp != '\0') {
245 		c = *cp++;
246 		if (c_erase != _POSIX_VDISABLE && c == c_erase) {
247 			if (cp2 == canonb)
248 				continue;
249 			if (cp2[-1] == '\\') {
250 				cp2[-1] = c;
251 				continue;
252 			}
253 			cp2--;
254 			continue;
255 		}
256 		if (c_kill != _POSIX_VDISABLE && c == c_kill) {
257 			if (cp2 == canonb)
258 				continue;
259 			if (cp2[-1] == '\\') {
260 				cp2[-1] = c;
261 				continue;
262 			}
263 			cp2 = canonb;
264 			continue;
265 		}
266 		*cp2++ = c;
267 	}
268 	*cp2 = '\0';
269 #endif
270 	if (equal("", canonb))
271 		return (NULL);
272 	return (savestr(canonb));
273 }
274 
275 /*
276  * Receipt continuation.
277  */
278 void
279 ttystop(s)
280 	int s;
281 {
282 	sig_t old_action = signal(s, SIG_DFL);
283 	sigset_t nset;
284 
285 	(void)sigemptyset(&nset);
286 	(void)sigaddset(&nset, s);
287 	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
288 	kill(0, s);
289 	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
290 	(void)signal(s, old_action);
291 	longjmp(rewrite, 1);
292 }
293 
294 /*ARGSUSED*/
295 void
296 ttyint(s)
297 	int s;
298 {
299 	longjmp(intjmp, 1);
300 }
301