Support CONFIG-sets for make(1) invocation..
[s-mailx.git] / tty.c
blobf6dcb1d222d24942c3ed95184724730fdacf91dd
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)tty.c 2.29 (gritter) 3/9/07";
42 #endif
43 #endif /* not lint */
46 * Mail -- a mail program
48 * Generally useful tty stuff.
51 #include "rcv.h"
52 #include "extern.h"
53 #include <errno.h>
54 #include <termios.h>
55 #include <unistd.h>
56 #include <sys/ioctl.h>
58 static cc_t c_erase; /* Current erase char */
59 static cc_t c_kill; /* Current kill char */
60 static sigjmp_buf rewrite; /* Place to go when continued */
61 static sigjmp_buf intjmp; /* Place to go when interrupted */
62 #ifndef TIOCSTI
63 static int ttyset; /* We must now do erase/kill */
64 #endif
65 static struct termios ttybuf;
66 static long vdis; /* _POSIX_VDISABLE char */
68 static void ttystop(int s);
69 static void ttyint(int s);
70 static int safe_getc(FILE *ibuf);
71 static char *rtty_internal(const char *pr, char *src);
74 * Receipt continuation.
76 static void
77 ttystop(int s)
79 sighandler_type old_action = safe_signal(s, SIG_DFL);
80 sigset_t nset;
82 sigemptyset(&nset);
83 sigaddset(&nset, s);
84 sigprocmask(SIG_BLOCK, &nset, NULL);
85 kill(0, s);
86 sigprocmask(SIG_UNBLOCK, &nset, NULL);
87 safe_signal(s, old_action);
88 siglongjmp(rewrite, 1);
91 /*ARGSUSED*/
92 static void
93 ttyint(int s)
95 siglongjmp(intjmp, 1);
99 * Interrupts will cause trouble if we are inside a stdio call. As
100 * this is only relevant if input comes from a terminal, we can simply
101 * bypass it by read() then.
103 static int
104 safe_getc(FILE *ibuf)
106 if (fileno(ibuf) == 0 && is_a_tty[0]) {
107 char c;
108 int sz;
110 again:
111 if ((sz = read(0, &c, 1)) != 1) {
112 if (sz < 0 && errno == EINTR)
113 goto again;
114 return EOF;
116 return c & 0377;
117 } else
118 return getc(ibuf);
122 * Read up a header from standard input.
123 * The source string has the preliminary contents to
124 * be read.
126 static char *
127 rtty_internal(const char *pr, char *src)
129 char ch, canonb[LINESIZE];
130 int c;
131 char *cp, *cp2;
133 (void) &c;
134 (void) &cp2;
135 fputs(pr, stdout);
136 fflush(stdout);
137 if (src != NULL && strlen(src) > sizeof canonb - 2) {
138 printf(catgets(catd, CATSET, 200, "too long to edit\n"));
139 return(src);
141 #ifndef TIOCSTI
142 if (src != NULL)
143 cp = sstpcpy(canonb, src);
144 else
145 cp = sstpcpy(canonb, "");
146 fputs(canonb, stdout);
147 fflush(stdout);
148 #else
149 cp = src == NULL ? "" : src;
150 while ((c = *cp++) != '\0') {
151 if ((c_erase != vdis && c == c_erase) ||
152 (c_kill != vdis && c == c_kill)) {
153 ch = '\\';
154 ioctl(0, TIOCSTI, &ch);
156 ch = c;
157 ioctl(0, TIOCSTI, &ch);
159 cp = canonb;
160 *cp = 0;
161 #endif
162 cp2 = cp;
163 while (cp2 < canonb + sizeof canonb)
164 *cp2++ = 0;
165 cp2 = cp;
166 if (sigsetjmp(rewrite, 1))
167 goto redo;
168 safe_signal(SIGTSTP, ttystop);
169 safe_signal(SIGTTOU, ttystop);
170 safe_signal(SIGTTIN, ttystop);
171 clearerr(stdin);
172 while (cp2 < canonb + sizeof canonb - 1) {
173 c = safe_getc(stdin);
174 if (c == EOF || c == '\n')
175 break;
176 *cp2++ = c;
178 *cp2 = 0;
179 safe_signal(SIGTSTP, SIG_DFL);
180 safe_signal(SIGTTOU, SIG_DFL);
181 safe_signal(SIGTTIN, SIG_DFL);
182 if (c == EOF && ferror(stdin)) {
183 redo:
184 cp = strlen(canonb) > 0 ? canonb : NULL;
185 clearerr(stdin);
186 return(rtty_internal(pr, cp));
188 #ifndef TIOCSTI
189 if (cp == NULL || *cp == '\0')
190 return(src);
191 cp2 = cp;
192 if (!ttyset)
193 return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
194 while (*cp != '\0') {
195 c = *cp++;
196 if (c_erase != vdis && c == c_erase) {
197 if (cp2 == canonb)
198 continue;
199 if (cp2[-1] == '\\') {
200 cp2[-1] = c;
201 continue;
203 cp2--;
204 continue;
206 if (c_kill != vdis && c == c_kill) {
207 if (cp2 == canonb)
208 continue;
209 if (cp2[-1] == '\\') {
210 cp2[-1] = c;
211 continue;
213 cp2 = canonb;
214 continue;
216 *cp2++ = c;
218 *cp2 = '\0';
219 #endif
220 if (equal("", canonb))
221 return(NULL);
222 return(savestr(canonb));
226 * Read all relevant header fields.
229 #ifndef TIOCSTI
230 #define TTYSET_CHECK(h) if (!ttyset && (h) != NULL) \
231 ttyset++, tcsetattr(0, TCSADRAIN, \
232 &ttybuf);
233 #else
234 #define TTYSET_CHECK(h)
235 #endif
237 #define GRAB_SUBJECT if (gflags & GSUBJECT) { \
238 TTYSET_CHECK(hp->h_subject) \
239 hp->h_subject = rtty_internal("Subject: ", \
240 hp->h_subject); \
243 static struct name *
244 grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags)
246 struct name *nq;
248 TTYSET_CHECK(np);
249 loop:
250 np = sextract(rtty_internal(field, detract(np, comma)), gflags);
251 for (nq = np; nq != NULL; nq = nq->n_flink)
252 if (mime_name_invalid(nq->n_name, 1))
253 goto loop;
254 return np;
257 int
258 grabh(struct header *hp, enum gfield gflags, int subjfirst)
260 sighandler_type saveint;
261 #ifndef TIOCSTI
262 sighandler_type savequit;
263 #endif
264 sighandler_type savetstp;
265 sighandler_type savettou;
266 sighandler_type savettin;
267 int errs;
268 int comma;
270 (void) &comma;
271 (void) &saveint;
272 savetstp = safe_signal(SIGTSTP, SIG_DFL);
273 savettou = safe_signal(SIGTTOU, SIG_DFL);
274 savettin = safe_signal(SIGTTIN, SIG_DFL);
275 errs = 0;
276 comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA;
277 #ifndef TIOCSTI
278 ttyset = 0;
279 #endif
280 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
281 perror("tcgetattr");
282 return(-1);
284 c_erase = ttybuf.c_cc[VERASE];
285 c_kill = ttybuf.c_cc[VKILL];
286 #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF)
287 if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0)
288 vdis = '\377';
289 #elif defined (_POSIX_VDISABLE)
290 vdis = _POSIX_VDISABLE;
291 #else
292 vdis = '\377';
293 #endif
294 #ifndef TIOCSTI
295 ttybuf.c_cc[VERASE] = 0;
296 ttybuf.c_cc[VKILL] = 0;
297 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
298 safe_signal(SIGINT, SIG_DFL);
299 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
300 safe_signal(SIGQUIT, SIG_DFL);
301 #else /* TIOCSTI */
302 saveint = safe_signal(SIGINT, SIG_IGN);
303 if (sigsetjmp(intjmp, 1)) {
304 /* avoid garbled output with C-c */
305 printf("\n");
306 fflush(stdout);
307 goto out;
309 if (saveint != SIG_IGN)
310 safe_signal(SIGINT, ttyint);
311 #endif /* TIOCSTI */
312 if (gflags & GTO)
313 hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL);
314 if (subjfirst)
315 GRAB_SUBJECT
316 if (gflags & GCC)
317 hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL);
318 if (gflags & GBCC)
319 hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL);
320 if (gflags & GEXTRA) {
321 if (hp->h_from == NULL)
322 hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL);
323 hp->h_from = grabaddrs("From: ", hp->h_from, comma,
324 GEXTRA|GFULL);
325 if (hp->h_replyto == NULL)
326 hp->h_replyto = sextract(value("replyto"),
327 GEXTRA|GFULL);
328 hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma,
329 GEXTRA|GFULL);
330 if (hp->h_sender == NULL)
331 hp->h_sender = sextract(value("sender"),
332 GEXTRA|GFULL);
333 hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma,
334 GEXTRA|GFULL);
335 if (hp->h_organization == NULL)
336 hp->h_organization = value("ORGANIZATION");
337 TTYSET_CHECK(hp->h_organization);
338 hp->h_organization = rtty_internal("Organization: ",
339 hp->h_organization);
341 if (!subjfirst)
342 GRAB_SUBJECT
343 out:
344 safe_signal(SIGTSTP, savetstp);
345 safe_signal(SIGTTOU, savettou);
346 safe_signal(SIGTTIN, savettin);
347 #ifndef TIOCSTI
348 ttybuf.c_cc[VERASE] = c_erase;
349 ttybuf.c_cc[VKILL] = c_kill;
350 if (ttyset)
351 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
352 safe_signal(SIGQUIT, savequit);
353 #endif
354 safe_signal(SIGINT, saveint);
355 return(errs);
359 * Read a line from tty; to be called from elsewhere
362 char *
363 readtty(char *prefix, char *string)
365 char *ret = NULL;
366 struct termios ttybuf;
367 sighandler_type saveint = SIG_DFL;
368 #ifndef TIOCSTI
369 sighandler_type savequit;
370 #endif
371 sighandler_type savetstp;
372 sighandler_type savettou;
373 sighandler_type savettin;
375 (void) &saveint;
376 (void) &ret;
377 savetstp = safe_signal(SIGTSTP, SIG_DFL);
378 savettou = safe_signal(SIGTTOU, SIG_DFL);
379 savettin = safe_signal(SIGTTIN, SIG_DFL);
380 #ifndef TIOCSTI
381 ttyset = 0;
382 #endif
383 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
384 perror("tcgetattr");
385 return NULL;
387 c_erase = ttybuf.c_cc[VERASE];
388 c_kill = ttybuf.c_cc[VKILL];
389 #ifndef TIOCSTI
390 ttybuf.c_cc[VERASE] = 0;
391 ttybuf.c_cc[VKILL] = 0;
392 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
393 safe_signal(SIGINT, SIG_DFL);
394 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
395 safe_signal(SIGQUIT, SIG_DFL);
396 #else
397 if (sigsetjmp(intjmp, 1)) {
398 /* avoid garbled output with C-c */
399 printf("\n");
400 fflush(stdout);
401 goto out2;
403 saveint = safe_signal(SIGINT, ttyint);
404 #endif
405 TTYSET_CHECK(string)
406 ret = rtty_internal(prefix, string);
407 if (ret != NULL && *ret == '\0')
408 ret = NULL;
409 out2:
410 safe_signal(SIGTSTP, savetstp);
411 safe_signal(SIGTTOU, savettou);
412 safe_signal(SIGTTIN, savettin);
413 #ifndef TIOCSTI
414 ttybuf.c_cc[VERASE] = c_erase;
415 ttybuf.c_cc[VKILL] = c_kill;
416 if (ttyset)
417 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
418 safe_signal(SIGQUIT, savequit);
419 #endif
420 safe_signal(SIGINT, saveint);
421 return ret;
424 int
425 yorn(char *msg)
427 char *cp;
429 if (value("interactive") == NULL)
430 return 1;
432 cp = readtty(msg, NULL);
433 while (cp == NULL ||
434 *cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N');
435 return *cp == 'y' || *cp == 'Y';