makeconfig: small tweaks
[s-mailx.git] / tty.c
blobb8a97ad92e5ec09a3535fa071311045981bc2d13
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
41 * Mail -- a mail program
43 * Generally useful tty stuff.
46 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
47 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
48 * TODO may leave child running if fdopen() fails! */
50 #include "rcv.h"
51 #include "extern.h"
52 #include <errno.h>
53 #include <termios.h>
54 #include <unistd.h>
55 #include <sys/ioctl.h>
57 static cc_t c_erase; /* Current erase char */
58 static cc_t c_kill; /* Current kill char */
59 static sigjmp_buf rewrite; /* Place to go when continued */
60 static sigjmp_buf intjmp; /* Place to go when interrupted */
61 #ifndef TIOCSTI
62 static int ttyset; /* We must now do erase/kill */
63 #endif
64 static struct termios ttybuf;
65 static long vdis; /* _POSIX_VDISABLE char */
67 static void ttystop(int s);
68 static void ttyint(int s);
69 static int safe_getc(FILE *ibuf);
70 static char *rtty_internal(const char *pr, char *src);
73 * Receipt continuation.
75 static void
76 ttystop(int s)
78 sighandler_type old_action = safe_signal(s, SIG_DFL);
79 sigset_t nset;
81 sigemptyset(&nset);
82 sigaddset(&nset, s);
83 sigprocmask(SIG_BLOCK, &nset, NULL);
84 kill(0, s);
85 sigprocmask(SIG_UNBLOCK, &nset, NULL);
86 safe_signal(s, old_action);
87 siglongjmp(rewrite, 1);
90 /*ARGSUSED*/
91 static void
92 ttyint(int s)
94 (void)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 return ((*canonb == '\0') ? NULL : savestr(canonb));
224 * Read all relevant header fields.
227 #ifndef TIOCSTI
228 #define TTYSET_CHECK(h) if (!ttyset && (h) != NULL) \
229 ttyset++, tcsetattr(0, TCSADRAIN, \
230 &ttybuf);
231 #else
232 #define TTYSET_CHECK(h)
233 #endif
235 #define GRAB_SUBJECT if (gflags & GSUBJECT) { \
236 TTYSET_CHECK(hp->h_subject) \
237 hp->h_subject = rtty_internal("Subject: ", \
238 hp->h_subject); \
241 static struct name *
242 grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags)
244 struct name *nq;
246 TTYSET_CHECK(np);
247 loop:
248 np = lextract(rtty_internal(field, detract(np, comma)), gflags);
249 for (nq = np; nq != NULL; nq = nq->n_flink)
250 if (is_addr_invalid(nq, 1))
251 goto loop;
252 return np;
255 int
256 grabh(struct header *hp, enum gfield gflags, int subjfirst)
258 sighandler_type saveint, savetstp, savettou, savettin;
259 #ifndef TIOCSTI
260 sighandler_type savequit;
261 #endif
262 int errs;
263 int volatile comma;
265 savetstp = safe_signal(SIGTSTP, SIG_DFL);
266 savettou = safe_signal(SIGTTOU, SIG_DFL);
267 savettin = safe_signal(SIGTTIN, SIG_DFL);
268 errs = 0;
269 comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA;
270 #ifndef TIOCSTI
271 ttyset = 0;
272 #endif
273 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
274 perror("tcgetattr");
275 return(-1);
277 c_erase = ttybuf.c_cc[VERASE];
278 c_kill = ttybuf.c_cc[VKILL];
279 #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF)
280 if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0)
281 vdis = '\377';
282 #elif defined (_POSIX_VDISABLE)
283 vdis = _POSIX_VDISABLE;
284 #else
285 vdis = '\377';
286 #endif
287 #ifndef TIOCSTI
288 ttybuf.c_cc[VERASE] = 0;
289 ttybuf.c_cc[VKILL] = 0;
290 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
291 safe_signal(SIGINT, SIG_DFL);
292 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
293 safe_signal(SIGQUIT, SIG_DFL);
294 #else /* TIOCSTI */
295 saveint = safe_signal(SIGINT, SIG_IGN);
296 if (sigsetjmp(intjmp, 1)) {
297 /* avoid garbled output with C-c */
298 printf("\n");
299 fflush(stdout);
300 goto out;
302 if (saveint != SIG_IGN)
303 safe_signal(SIGINT, ttyint);
304 #endif /* TIOCSTI */
305 if (gflags & GTO)
306 hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL);
307 if (subjfirst)
308 GRAB_SUBJECT
309 if (gflags & GCC)
310 hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL);
311 if (gflags & GBCC)
312 hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL);
313 if (gflags & GEXTRA) {
314 if (hp->h_from == NULL)
315 hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL);
316 hp->h_from = grabaddrs("From: ", hp->h_from, comma,
317 GEXTRA|GFULL);
318 if (hp->h_replyto == NULL)
319 hp->h_replyto = sextract(value("replyto"),
320 GEXTRA|GFULL);
321 hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma,
322 GEXTRA|GFULL);
323 if (hp->h_sender == NULL)
324 hp->h_sender = sextract(value("sender"),
325 GEXTRA|GFULL);
326 hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma,
327 GEXTRA|GFULL);
328 if (hp->h_organization == NULL)
329 hp->h_organization = value("ORGANIZATION");
330 TTYSET_CHECK(hp->h_organization);
331 hp->h_organization = rtty_internal("Organization: ",
332 hp->h_organization);
334 if (!subjfirst)
335 GRAB_SUBJECT
336 out:
337 safe_signal(SIGTSTP, savetstp);
338 safe_signal(SIGTTOU, savettou);
339 safe_signal(SIGTTIN, savettin);
340 #ifndef TIOCSTI
341 ttybuf.c_cc[VERASE] = c_erase;
342 ttybuf.c_cc[VKILL] = c_kill;
343 if (ttyset)
344 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
345 safe_signal(SIGQUIT, savequit);
346 #endif
347 safe_signal(SIGINT, saveint);
348 return(errs);
352 * Read a line from tty; to be called from elsewhere
355 char *
356 readtty(char *prefix, char *string)
358 char *ret = NULL;
359 struct termios ttybuf;
360 sighandler_type saveint = SIG_DFL;
361 #ifndef TIOCSTI
362 sighandler_type savequit;
363 #endif
364 sighandler_type savetstp;
365 sighandler_type savettou;
366 sighandler_type savettin;
368 (void) &saveint;
369 (void) &ret;
370 savetstp = safe_signal(SIGTSTP, SIG_DFL);
371 savettou = safe_signal(SIGTTOU, SIG_DFL);
372 savettin = safe_signal(SIGTTIN, SIG_DFL);
373 #ifndef TIOCSTI
374 ttyset = 0;
375 #endif
376 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
377 perror("tcgetattr");
378 return NULL;
380 c_erase = ttybuf.c_cc[VERASE];
381 c_kill = ttybuf.c_cc[VKILL];
382 #ifndef TIOCSTI
383 ttybuf.c_cc[VERASE] = 0;
384 ttybuf.c_cc[VKILL] = 0;
385 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
386 safe_signal(SIGINT, SIG_DFL);
387 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
388 safe_signal(SIGQUIT, SIG_DFL);
389 #else
390 if (sigsetjmp(intjmp, 1)) {
391 /* avoid garbled output with C-c */
392 printf("\n");
393 fflush(stdout);
394 goto out2;
396 saveint = safe_signal(SIGINT, ttyint);
397 #endif
398 TTYSET_CHECK(string)
399 ret = rtty_internal(prefix, string);
400 if (ret != NULL && *ret == '\0')
401 ret = NULL;
402 out2:
403 safe_signal(SIGTSTP, savetstp);
404 safe_signal(SIGTTOU, savettou);
405 safe_signal(SIGTTIN, savettin);
406 #ifndef TIOCSTI
407 ttybuf.c_cc[VERASE] = c_erase;
408 ttybuf.c_cc[VKILL] = c_kill;
409 if (ttyset)
410 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
411 safe_signal(SIGQUIT, savequit);
412 #endif
413 safe_signal(SIGINT, saveint);
414 return ret;
417 int
418 yorn(char *msg)
420 char *cp;
422 if (value("interactive") == NULL)
423 return 1;
425 cp = readtty(msg, NULL);
426 while (cp == NULL ||
427 (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N'));
428 return *cp == 'y' || *cp == 'Y';