tty.c revision 1.27 1 /* $NetBSD: tty.c,v 1.27 2006/11/28 18:45:32 christos Exp $ */
2
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 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)tty.c 8.2 (Berkeley) 6/6/93";
36 #else
37 __RCSID("$NetBSD: tty.c,v 1.27 2006/11/28 18:45:32 christos 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 #ifdef USE_EDITLINE
50 #include "complete.h"
51 #endif
52
53 #if !defined(USE_EDITLINE) || !defined(TIOCSTI)
54 static cc_t c_erase; /* Current erase char */
55 static cc_t c_kill; /* Current kill char */
56 #endif
57 #ifndef USE_EDITLINE
58 static jmp_buf rewrite; /* Place to go when continued */
59 #endif
60 static jmp_buf intjmp; /* Place to go when interrupted */
61 #ifndef TIOCSTI
62 static int ttyset; /* We must now do erase/kill */
63 #endif
64
65
66 /*
67 * Read up a header from standard input.
68 * The source string has the preliminary contents to
69 * be read.
70 *
71 */
72 #ifdef USE_EDITLINE
73 static char *
74 readtty(const char pr[], char src[])
75 {
76 char *line;
77 line = my_gets(&elm.string, pr, src);
78 #if 0
79 return line ? savestr(line) : __UNCONST("");
80 #else
81 if (line)
82 return savestr(line);
83 else
84 return __UNCONST("");
85 #endif
86 }
87
88 #else /* USE_EDITLINE */
89
90 /*
91 * Receipt continuation.
92 */
93 static void
94 ttystop(int s)
95 {
96 sig_t old_action = signal(s, SIG_DFL);
97 sigset_t nset;
98
99 (void)sigemptyset(&nset);
100 (void)sigaddset(&nset, s);
101 (void)sigprocmask(SIG_BLOCK, &nset, NULL);
102 (void)kill(0, s);
103 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
104 (void)signal(s, old_action);
105 longjmp(rewrite, 1);
106 }
107
108 static char *
109 readtty(const char pr[], char src[])
110 {
111 /* XXX - watch for potential setjmp/longjmp clobbering!
112 * Currently there appear to be none.
113 */
114 char canonb[LINESIZE];
115 int c;
116 char *cp, *cp2;
117 #ifdef TIOCSTI
118 char ch;
119 static char empty[] = "";
120 #endif
121 (void)fputs(pr, stdout);
122 (void)fflush(stdout);
123 if (src != NULL && strlen(src) > sizeof(canonb) - 2) {
124 (void)printf("too long to edit\n");
125 return src;
126 }
127 #ifndef TIOCSTI
128 if (src != NULL)
129 cp = copy(src, canonb);
130 else
131 cp = copy("", canonb);
132 (void)fputs(canonb, stdout);
133 (void)fflush(stdout);
134 #else
135 cp = src == NULL ? empty : src;
136 while ((c = *cp++) != '\0') {
137 if ((c_erase != _POSIX_VDISABLE && c == c_erase) ||
138 (c_kill != _POSIX_VDISABLE && c == c_kill)) {
139 ch = '\\';
140 (void)ioctl(0, TIOCSTI, &ch);
141 }
142 ch = c;
143 (void)ioctl(0, TIOCSTI, &ch);
144 }
145 cp = canonb;
146 *cp = 0;
147 #endif
148 cp2 = cp;
149 while (cp2 < canonb + sizeof(canonb))
150 *cp2++ = 0;
151 cp2 = cp;
152 if (setjmp(rewrite))
153 goto redo;
154 (void)signal(SIGTSTP, ttystop);
155 (void)signal(SIGTTOU, ttystop);
156 (void)signal(SIGTTIN, ttystop);
157 clearerr(stdin);
158 while (cp2 < canonb + sizeof(canonb)) {
159 c = getc(stdin);
160 if (c == EOF || c == '\n')
161 break;
162 *cp2++ = c;
163 }
164 *cp2 = 0;
165 (void)signal(SIGTSTP, SIG_DFL);
166 (void)signal(SIGTTOU, SIG_DFL);
167 (void)signal(SIGTTIN, SIG_DFL);
168 if (c == EOF && ferror(stdin)) {
169 redo:
170 cp = strlen(canonb) > 0 ? canonb : NULL;
171 clearerr(stdin);
172 return readtty(pr, cp);
173 }
174 #ifndef TIOCSTI
175 if (cp == NULL || *cp == '\0')
176 return src;
177 cp2 = cp;
178 if (!ttyset)
179 return strlen(canonb) > 0 ? savestr(canonb) : NULL;
180 while (*cp != '\0') {
181 c = *cp++;
182 if (c_erase != _POSIX_VDISABLE && c == c_erase) {
183 if (cp2 == canonb)
184 continue;
185 if (cp2[-1] == '\\') {
186 cp2[-1] = c;
187 continue;
188 }
189 cp2--;
190 continue;
191 }
192 if (c_kill != _POSIX_VDISABLE && c == c_kill) {
193 if (cp2 == canonb)
194 continue;
195 if (cp2[-1] == '\\') {
196 cp2[-1] = c;
197 continue;
198 }
199 cp2 = canonb;
200 continue;
201 }
202 *cp2++ = c;
203 }
204 *cp2 = '\0';
205 #endif
206 if (equal("", canonb))
207 return NULL;
208 return savestr(canonb);
209 }
210 #endif /* USE_EDITLINE */
211
212
213 /*ARGSUSED*/
214 static void
215 ttyint(int s __unused)
216 {
217 longjmp(intjmp, 1);
218 }
219
220 /*
221 * Read all relevant header fields.
222 */
223 PUBLIC int
224 grabh(struct header *hp, int gflags)
225 {
226 struct termios ttybuf;
227
228 /* The following are declared volatile to avoid longjmp
229 * clobbering, though they seem safe without it! */
230 sig_t volatile saveint;
231 sig_t volatile savetstp;
232 sig_t volatile savettou;
233 sig_t volatile savettin;
234 #ifndef TIOCSTI
235 sig_t volatile savequit;
236 #else
237 # ifdef TIOCEXT
238 int volatile extproc;
239 # endif /* TIOCEXT */
240 #endif /* TIOCSTI */
241 int retval;
242
243 savetstp = signal(SIGTSTP, SIG_DFL);
244 savettou = signal(SIGTTOU, SIG_DFL);
245 savettin = signal(SIGTTIN, SIG_DFL);
246 #ifndef TIOCSTI
247 ttyset = 0;
248 #endif
249 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
250 warn("tcgetattr");
251 return -1;
252 }
253 #if !defined(USE_EDITLINE) || !defined(TIOCSTI)
254 c_erase = ttybuf.c_cc[VERASE];
255 c_kill = ttybuf.c_cc[VKILL];
256 #endif
257 #ifndef TIOCSTI
258 ttybuf.c_cc[VERASE] = _POSIX_VDISABLE;
259 ttybuf.c_cc[VKILL] = _POSIX_VDISABLE;
260 if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL)
261 (void)signal(SIGINT, SIG_DFL);
262 if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
263 (void)signal(SIGQUIT, SIG_DFL);
264 #else
265 # ifdef TIOCEXT
266 extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0);
267 if (extproc) {
268 int flag;
269 flag = 0;
270 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
271 warn("TIOCEXT: off");
272 }
273 # endif /* TIOCEXT */
274 saveint = signal(SIGINT, ttyint); /* must precede setjmp to be saved */
275 if ((retval = setjmp(intjmp)) != 0) {
276 (void)fputc('\n', stdout);
277 goto out;
278 }
279 #endif
280 if (gflags & GTO) {
281 #ifndef TIOCSTI
282 if (!ttyset && hp->h_to != NULL)
283 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
284 #endif
285 hp->h_to =
286 extract(readtty("To: ", detract(hp->h_to, 0)), GTO);
287 }
288 if (gflags & GSUBJECT) {
289 #ifndef TIOCSTI
290 if (!ttyset && hp->h_subject != NULL)
291 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
292 #endif
293 hp->h_subject = readtty("Subject: ", hp->h_subject);
294 }
295 if (gflags & GCC) {
296 #ifndef TIOCSTI
297 if (!ttyset && hp->h_cc != NULL)
298 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
299 #endif
300 hp->h_cc =
301 extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC);
302 }
303 if (gflags & GBCC) {
304 #ifndef TIOCSTI
305 if (!ttyset && hp->h_bcc != NULL)
306 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
307 #endif
308 hp->h_bcc =
309 extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC);
310 }
311 if (gflags & GSMOPTS) {
312 char *smopts;
313 #ifndef TIOCSTI
314 if (!ttyset && hp->h_smopts != NULL)
315 ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
316 #endif
317 smopts = readtty("Smopts: ", detract(hp->h_smopts, GSMOPTS));
318
319 /* Parse smopts with getrawlist() rather than expand()
320 * to get a shell-like expansion.
321 */
322 hp->h_smopts = NULL;
323 if (smopts) {
324 struct name *np, *t;
325 char *argv[MAXARGC];
326 int argc, i;
327
328 np = NULL;
329 argc = getrawlist(smopts, argv, sizeof(argv)/sizeof(*argv));
330 for (i = 0; i < argc; i++) {
331 t = nalloc(argv[i], GSMOPTS);
332 if (hp->h_smopts == NULL)
333 hp->h_smopts = t;
334 else
335 np->n_flink = t;
336 t->n_blink = np;
337 np = t;
338 }
339 }
340 #ifdef MIME_SUPPORT
341 if (hp->h_attach) {
342 struct attachment *ap;
343 int i;
344 i = 0;
345 for (ap = hp->h_attach; ap; ap = ap->a_flink)
346 i++;
347 (void)printf("Attachment%s: %d\n", i > 1 ? "s" : "", i);
348 }
349 #endif
350 }
351 #ifdef TIOCSTI
352 out:
353 #endif
354 (void)signal(SIGTSTP, savetstp);
355 (void)signal(SIGTTOU, savettou);
356 (void)signal(SIGTTIN, savettin);
357 #ifndef TIOCSTI
358 ttybuf.c_cc[VERASE] = c_erase;
359 ttybuf.c_cc[VKILL] = c_kill;
360 if (ttyset)
361 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
362 (void)signal(SIGQUIT, savequit);
363 #else
364 # ifdef TIOCEXT
365 if (extproc) {
366 int flag;
367 flag = 1;
368 if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
369 warn("TIOCEXT: on");
370 }
371 # endif /* TIOCEXT */
372 #endif
373 (void)signal(SIGINT, saveint);
374 return retval;
375 }
376