Tweak (outof(): FIX and recode.., 2012-10-04)..
[s-mailx.git] / tty.c
blob5cc468a5fda85e8d47d7c0a2add205352bceeec4
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 (mime_name_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;
259 #ifndef TIOCSTI
260 sighandler_type savequit;
261 #endif
262 sighandler_type savetstp;
263 sighandler_type savettou;
264 sighandler_type savettin;
265 int errs;
266 int comma;
268 (void) &saveint;
269 savetstp = safe_signal(SIGTSTP, SIG_DFL);
270 savettou = safe_signal(SIGTTOU, SIG_DFL);
271 savettin = safe_signal(SIGTTIN, SIG_DFL);
272 errs = 0;
273 comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA;
274 #ifndef TIOCSTI
275 ttyset = 0;
276 #endif
277 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
278 perror("tcgetattr");
279 return(-1);
281 c_erase = ttybuf.c_cc[VERASE];
282 c_kill = ttybuf.c_cc[VKILL];
283 #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF)
284 if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0)
285 vdis = '\377';
286 #elif defined (_POSIX_VDISABLE)
287 vdis = _POSIX_VDISABLE;
288 #else
289 vdis = '\377';
290 #endif
291 #ifndef TIOCSTI
292 ttybuf.c_cc[VERASE] = 0;
293 ttybuf.c_cc[VKILL] = 0;
294 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
295 safe_signal(SIGINT, SIG_DFL);
296 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
297 safe_signal(SIGQUIT, SIG_DFL);
298 #else /* TIOCSTI */
299 saveint = safe_signal(SIGINT, SIG_IGN);
300 if (sigsetjmp(intjmp, 1)) {
301 /* avoid garbled output with C-c */
302 printf("\n");
303 fflush(stdout);
304 goto out;
306 if (saveint != SIG_IGN)
307 safe_signal(SIGINT, ttyint);
308 #endif /* TIOCSTI */
309 if (gflags & GTO)
310 hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL);
311 if (subjfirst)
312 GRAB_SUBJECT
313 if (gflags & GCC)
314 hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL);
315 if (gflags & GBCC)
316 hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL);
317 if (gflags & GEXTRA) {
318 if (hp->h_from == NULL)
319 hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL);
320 hp->h_from = grabaddrs("From: ", hp->h_from, comma,
321 GEXTRA|GFULL);
322 if (hp->h_replyto == NULL)
323 hp->h_replyto = sextract(value("replyto"),
324 GEXTRA|GFULL);
325 hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma,
326 GEXTRA|GFULL);
327 if (hp->h_sender == NULL)
328 hp->h_sender = sextract(value("sender"),
329 GEXTRA|GFULL);
330 hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma,
331 GEXTRA|GFULL);
332 if (hp->h_organization == NULL)
333 hp->h_organization = value("ORGANIZATION");
334 TTYSET_CHECK(hp->h_organization);
335 hp->h_organization = rtty_internal("Organization: ",
336 hp->h_organization);
338 if (!subjfirst)
339 GRAB_SUBJECT
340 out:
341 safe_signal(SIGTSTP, savetstp);
342 safe_signal(SIGTTOU, savettou);
343 safe_signal(SIGTTIN, savettin);
344 #ifndef TIOCSTI
345 ttybuf.c_cc[VERASE] = c_erase;
346 ttybuf.c_cc[VKILL] = c_kill;
347 if (ttyset)
348 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
349 safe_signal(SIGQUIT, savequit);
350 #endif
351 safe_signal(SIGINT, saveint);
352 return(errs);
356 * Read a line from tty; to be called from elsewhere
359 char *
360 readtty(char *prefix, char *string)
362 char *ret = NULL;
363 struct termios ttybuf;
364 sighandler_type saveint = SIG_DFL;
365 #ifndef TIOCSTI
366 sighandler_type savequit;
367 #endif
368 sighandler_type savetstp;
369 sighandler_type savettou;
370 sighandler_type savettin;
372 (void) &saveint;
373 (void) &ret;
374 savetstp = safe_signal(SIGTSTP, SIG_DFL);
375 savettou = safe_signal(SIGTTOU, SIG_DFL);
376 savettin = safe_signal(SIGTTIN, SIG_DFL);
377 #ifndef TIOCSTI
378 ttyset = 0;
379 #endif
380 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
381 perror("tcgetattr");
382 return NULL;
384 c_erase = ttybuf.c_cc[VERASE];
385 c_kill = ttybuf.c_cc[VKILL];
386 #ifndef TIOCSTI
387 ttybuf.c_cc[VERASE] = 0;
388 ttybuf.c_cc[VKILL] = 0;
389 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
390 safe_signal(SIGINT, SIG_DFL);
391 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
392 safe_signal(SIGQUIT, SIG_DFL);
393 #else
394 if (sigsetjmp(intjmp, 1)) {
395 /* avoid garbled output with C-c */
396 printf("\n");
397 fflush(stdout);
398 goto out2;
400 saveint = safe_signal(SIGINT, ttyint);
401 #endif
402 TTYSET_CHECK(string)
403 ret = rtty_internal(prefix, string);
404 if (ret != NULL && *ret == '\0')
405 ret = NULL;
406 out2:
407 safe_signal(SIGTSTP, savetstp);
408 safe_signal(SIGTTOU, savettou);
409 safe_signal(SIGTTIN, savettin);
410 #ifndef TIOCSTI
411 ttybuf.c_cc[VERASE] = c_erase;
412 ttybuf.c_cc[VKILL] = c_kill;
413 if (ttyset)
414 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
415 safe_signal(SIGQUIT, savequit);
416 #endif
417 safe_signal(SIGINT, saveint);
418 return ret;
421 int
422 yorn(char *msg)
424 char *cp;
426 if (value("interactive") == NULL)
427 return 1;
429 cp = readtty(msg, NULL);
430 while (cp == NULL ||
431 (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N'));
432 return *cp == 'y' || *cp == 'Y';