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