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
grabh(struct header * hp,int gflags)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 *
readtty(char * pr,char * src)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
ttystop(int s)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
ttyint(int s)334 ttyint(int s)
335 {
336
337 ttysignal = s;
338 }
339