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/*
33 * Mail -- a mail program
34 *
35 * Generally useful tty stuff.
36 */
37
38#include "rcv.h"
39#include "extern.h"
40
41static	cc_t	c_erase;		/* Current erase char */
42static	cc_t	c_kill;			/* Current kill char */
43static	jmp_buf	rewrite;		/* Place to go when continued */
44static	jmp_buf	intjmp;			/* Place to go when interrupted */
45#ifndef TIOCSTI
46static	int	ttyset;			/* We must now do erase/kill */
47#endif
48
49/*
50 * Read all relevant header fields.
51 */
52
53int
54grabh(struct header *hp, int gflags)
55{
56	struct termios ttybuf;
57	volatile sig_t saveint;
58	sig_t savetstp;
59	sig_t savettou;
60	sig_t savettin;
61	int errs;
62#ifndef TIOCSTI
63	sig_t savequit;
64#else
65# ifdef TIOCEXT
66	int extproc, flag;
67# endif /* TIOCEXT */
68#endif /* TIOCSTI */
69
70	savetstp = signal(SIGTSTP, SIG_DFL);
71	savettou = signal(SIGTTOU, SIG_DFL);
72	savettin = signal(SIGTTIN, SIG_DFL);
73	errs = 0;
74#ifndef TIOCSTI
75	ttyset = 0;
76#endif
77	if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
78		warn("tcgetattr(stdin)");
79		return (-1);
80	}
81	c_erase = ttybuf.c_cc[VERASE];
82	c_kill = ttybuf.c_cc[VKILL];
83#ifndef TIOCSTI
84	ttybuf.c_cc[VERASE] = _POSIX_VDISABLE;
85	ttybuf.c_cc[VKILL] = _POSIX_VDISABLE;
86	if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL)
87		(void)signal(SIGINT, SIG_DFL);
88	if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
89		(void)signal(SIGQUIT, SIG_DFL);
90#else
91# ifdef		TIOCEXT
92	extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0);
93	if (extproc) {
94		flag = 0;
95		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
96			warn("TIOCEXT: off");
97	}
98# endif	/* TIOCEXT */
99	if (setjmp(intjmp))
100		goto out;
101	saveint = signal(SIGINT, ttyint);
102#endif
103	if (gflags & GTO) {
104#ifndef TIOCSTI
105		if (!ttyset && hp->h_to != NULL)
106			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
107#endif
108		hp->h_to =
109			extract(readtty("To: ", detract(hp->h_to, 0)), GTO);
110	}
111	if (gflags & GSUBJECT) {
112#ifndef TIOCSTI
113		if (!ttyset && hp->h_subject != NULL)
114			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
115#endif
116		hp->h_subject = readtty("Subject: ", hp->h_subject);
117	}
118	if (gflags & GCC) {
119#ifndef TIOCSTI
120		if (!ttyset && hp->h_cc != NULL)
121			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
122#endif
123		hp->h_cc =
124			extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC);
125	}
126	if (gflags & GBCC) {
127#ifndef TIOCSTI
128		if (!ttyset && hp->h_bcc != NULL)
129			ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
130#endif
131		hp->h_bcc =
132			extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC);
133	}
134#ifdef TIOCSTI
135out:
136#endif
137	(void)signal(SIGTSTP, savetstp);
138	(void)signal(SIGTTOU, savettou);
139	(void)signal(SIGTTIN, savettin);
140#ifndef TIOCSTI
141	ttybuf.c_cc[VERASE] = c_erase;
142	ttybuf.c_cc[VKILL] = c_kill;
143	if (ttyset)
144		tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
145	(void)signal(SIGQUIT, savequit);
146#else
147# ifdef		TIOCEXT
148	if (extproc) {
149		flag = 1;
150		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
151			warn("TIOCEXT: on");
152	}
153# endif	/* TIOCEXT */
154#endif
155	(void)signal(SIGINT, saveint);
156	return (errs);
157}
158
159/*
160 * Read up a header from standard input.
161 * The source string has the preliminary contents to
162 * be read.
163 *
164 */
165
166char *
167readtty(const char *pr, char src[])
168{
169	char canonb[BUFSIZ];
170#ifdef TIOCSTI
171	char ch;
172#endif
173	int c;
174	char *cp, *cp2;
175
176	fputs(pr, stdout);
177	(void)fflush(stdout);
178	if (src != NULL && strlen(src) > BUFSIZ - 2) {
179		printf("too long to edit\n");
180		return (src);
181	}
182#ifndef TIOCSTI
183	if (src != NULL)
184		strlcpy(canonb, src, sizeof(canonb));
185	else
186		*canonb = '\0';
187	fputs(canonb, stdout);
188	(void)fflush(stdout);
189#else
190	cp = src == NULL ? "" : src;
191	while ((c = *cp++) != '\0') {
192		if ((c_erase != _POSIX_VDISABLE && c == c_erase) ||
193		    (c_kill != _POSIX_VDISABLE && c == c_kill)) {
194			ch = '\\';
195			ioctl(0, TIOCSTI, &ch);
196		}
197		ch = c;
198		ioctl(0, TIOCSTI, &ch);
199	}
200	cp = canonb;
201	*cp = '\0';
202#endif
203	cp2 = cp;
204	while (cp2 < canonb + BUFSIZ)
205		*cp2++ = '\0';
206	cp2 = cp;
207	if (setjmp(rewrite))
208		goto redo;
209	(void)signal(SIGTSTP, ttystop);
210	(void)signal(SIGTTOU, ttystop);
211	(void)signal(SIGTTIN, ttystop);
212	clearerr(stdin);
213	while (cp2 < canonb + BUFSIZ) {
214		c = getc(stdin);
215		if (c == EOF || c == '\n')
216			break;
217		*cp2++ = c;
218	}
219	*cp2 = '\0';
220	(void)signal(SIGTSTP, SIG_DFL);
221	(void)signal(SIGTTOU, SIG_DFL);
222	(void)signal(SIGTTIN, SIG_DFL);
223	if (c == EOF && ferror(stdin)) {
224redo:
225		cp = strlen(canonb) > 0 ? canonb : NULL;
226		clearerr(stdin);
227		return (readtty(pr, cp));
228	}
229#ifndef TIOCSTI
230	if (cp == NULL || *cp == '\0')
231		return (src);
232	cp2 = cp;
233	if (!ttyset)
234		return (strlen(canonb) > 0 ? savestr(canonb) : NULL);
235	while (*cp != '\0') {
236		c = *cp++;
237		if (c_erase != _POSIX_VDISABLE && c == c_erase) {
238			if (cp2 == canonb)
239				continue;
240			if (cp2[-1] == '\\') {
241				cp2[-1] = c;
242				continue;
243			}
244			cp2--;
245			continue;
246		}
247		if (c_kill != _POSIX_VDISABLE && c == c_kill) {
248			if (cp2 == canonb)
249				continue;
250			if (cp2[-1] == '\\') {
251				cp2[-1] = c;
252				continue;
253			}
254			cp2 = canonb;
255			continue;
256		}
257		*cp2++ = c;
258	}
259	*cp2 = '\0';
260#endif
261	if (equal("", canonb))
262		return (NULL);
263	return (savestr(canonb));
264}
265
266/*
267 * Receipt continuation.
268 */
269void
270ttystop(int s)
271{
272	sig_t old_action = signal(s, SIG_DFL);
273	sigset_t nset;
274
275	(void)sigemptyset(&nset);
276	(void)sigaddset(&nset, s);
277	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
278	kill(0, s);
279	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
280	(void)signal(s, old_action);
281	longjmp(rewrite, 1);
282}
283
284void
285ttyint(int s __unused)
286{
287	longjmp(intjmp, 1);
288}
289