Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / mail / tty.c
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: src/usr.bin/mail/tty.c,v 1.2.8.3 2003/01/06 05:46:04 mikeh Exp $");
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 }