fixhead(): use ndup()
[s-mailx.git] / tty.c
blob6f63e89750264406db457f719ecd8460277171a2
1 /*
2 * Heirloom mailx - 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.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)tty.c 2.29 (gritter) 3/9/07";
43 #endif
44 #endif /* not lint */
47 * Mail -- a mail program
49 * Generally useful tty stuff.
52 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
53 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
54 * TODO may leave child running if fdopen() fails! */
56 #include "rcv.h"
57 #include "extern.h"
58 #include <errno.h>
59 #include <termios.h>
60 #include <unistd.h>
61 #include <sys/ioctl.h>
63 static cc_t c_erase; /* Current erase char */
64 static cc_t c_kill; /* Current kill char */
65 static sigjmp_buf rewrite; /* Place to go when continued */
66 static sigjmp_buf intjmp; /* Place to go when interrupted */
67 #ifndef TIOCSTI
68 static int ttyset; /* We must now do erase/kill */
69 #endif
70 static struct termios ttybuf;
71 static long vdis; /* _POSIX_VDISABLE char */
73 static void ttystop(int s);
74 static void ttyint(int s);
75 static int safe_getc(FILE *ibuf);
76 static char *rtty_internal(const char *pr, char *src);
79 * Receipt continuation.
81 static void
82 ttystop(int s)
84 sighandler_type old_action = safe_signal(s, SIG_DFL);
85 sigset_t nset;
87 sigemptyset(&nset);
88 sigaddset(&nset, s);
89 sigprocmask(SIG_BLOCK, &nset, NULL);
90 kill(0, s);
91 sigprocmask(SIG_UNBLOCK, &nset, NULL);
92 safe_signal(s, old_action);
93 siglongjmp(rewrite, 1);
96 /*ARGSUSED*/
97 static void
98 ttyint(int s)
100 (void)s;
101 siglongjmp(intjmp, 1);
105 * Interrupts will cause trouble if we are inside a stdio call. As
106 * this is only relevant if input comes from a terminal, we can simply
107 * bypass it by read() then.
109 static int
110 safe_getc(FILE *ibuf)
112 if (fileno(ibuf) == 0 && is_a_tty[0]) {
113 char c;
114 int sz;
116 again:
117 if ((sz = read(0, &c, 1)) != 1) {
118 if (sz < 0 && errno == EINTR)
119 goto again;
120 return EOF;
122 return c & 0377;
123 } else
124 return getc(ibuf);
128 * Read up a header from standard input.
129 * The source string has the preliminary contents to
130 * be read.
132 static char *
133 rtty_internal(const char *pr, char *src)
135 char ch, canonb[LINESIZE];
136 int c;
137 char *cp, *cp2;
139 (void) &c;
140 (void) &cp2;
141 fputs(pr, stdout);
142 fflush(stdout);
143 if (src != NULL && strlen(src) > sizeof canonb - 2) {
144 printf(catgets(catd, CATSET, 200, "too long to edit\n"));
145 return(src);
147 #ifndef TIOCSTI
148 if (src != NULL)
149 cp = sstpcpy(canonb, src);
150 else
151 cp = sstpcpy(canonb, "");
152 fputs(canonb, stdout);
153 fflush(stdout);
154 #else
155 cp = src == NULL ? "" : src;
156 while ((c = *cp++) != '\0') {
157 if ((c_erase != vdis && c == c_erase) ||
158 (c_kill != vdis && c == c_kill)) {
159 ch = '\\';
160 ioctl(0, TIOCSTI, &ch);
162 ch = c;
163 ioctl(0, TIOCSTI, &ch);
165 cp = canonb;
166 *cp = 0;
167 #endif
168 cp2 = cp;
169 while (cp2 < canonb + sizeof canonb)
170 *cp2++ = 0;
171 cp2 = cp;
172 if (sigsetjmp(rewrite, 1))
173 goto redo;
174 safe_signal(SIGTSTP, ttystop);
175 safe_signal(SIGTTOU, ttystop);
176 safe_signal(SIGTTIN, ttystop);
177 clearerr(stdin);
178 while (cp2 < canonb + sizeof canonb - 1) {
179 c = safe_getc(stdin);
180 if (c == EOF || c == '\n')
181 break;
182 *cp2++ = c;
184 *cp2 = 0;
185 safe_signal(SIGTSTP, SIG_DFL);
186 safe_signal(SIGTTOU, SIG_DFL);
187 safe_signal(SIGTTIN, SIG_DFL);
188 if (c == EOF && ferror(stdin)) {
189 redo:
190 cp = strlen(canonb) > 0 ? canonb : NULL;
191 clearerr(stdin);
192 return(rtty_internal(pr, cp));
194 #ifndef TIOCSTI
195 if (cp == NULL || *cp == '\0')
196 return(src);
197 cp2 = cp;
198 if (!ttyset)
199 return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
200 while (*cp != '\0') {
201 c = *cp++;
202 if (c_erase != vdis && c == c_erase) {
203 if (cp2 == canonb)
204 continue;
205 if (cp2[-1] == '\\') {
206 cp2[-1] = c;
207 continue;
209 cp2--;
210 continue;
212 if (c_kill != vdis && c == c_kill) {
213 if (cp2 == canonb)
214 continue;
215 if (cp2[-1] == '\\') {
216 cp2[-1] = c;
217 continue;
219 cp2 = canonb;
220 continue;
222 *cp2++ = c;
224 *cp2 = '\0';
225 #endif
226 if (equal("", canonb))
227 return(NULL);
228 return(savestr(canonb));
232 * Read all relevant header fields.
235 #ifndef TIOCSTI
236 #define TTYSET_CHECK(h) if (!ttyset && (h) != NULL) \
237 ttyset++, tcsetattr(0, TCSADRAIN, \
238 &ttybuf);
239 #else
240 #define TTYSET_CHECK(h)
241 #endif
243 #define GRAB_SUBJECT if (gflags & GSUBJECT) { \
244 TTYSET_CHECK(hp->h_subject) \
245 hp->h_subject = rtty_internal("Subject: ", \
246 hp->h_subject); \
249 static struct name *
250 grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags)
252 struct name *nq;
254 TTYSET_CHECK(np);
255 loop:
256 np = sextract(rtty_internal(field, detract(np, comma)), gflags);
257 for (nq = np; nq != NULL; nq = nq->n_flink)
258 if (mime_name_invalid(nq->n_name, 1))
259 goto loop;
260 return np;
263 int
264 grabh(struct header *hp, enum gfield gflags, int subjfirst)
266 sighandler_type saveint;
267 #ifndef TIOCSTI
268 sighandler_type savequit;
269 #endif
270 sighandler_type savetstp;
271 sighandler_type savettou;
272 sighandler_type savettin;
273 int errs;
274 int comma;
276 (void) &comma;
277 (void) &saveint;
278 savetstp = safe_signal(SIGTSTP, SIG_DFL);
279 savettou = safe_signal(SIGTTOU, SIG_DFL);
280 savettin = safe_signal(SIGTTIN, SIG_DFL);
281 errs = 0;
282 comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA;
283 #ifndef TIOCSTI
284 ttyset = 0;
285 #endif
286 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
287 perror("tcgetattr");
288 return(-1);
290 c_erase = ttybuf.c_cc[VERASE];
291 c_kill = ttybuf.c_cc[VKILL];
292 #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF)
293 if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0)
294 vdis = '\377';
295 #elif defined (_POSIX_VDISABLE)
296 vdis = _POSIX_VDISABLE;
297 #else
298 vdis = '\377';
299 #endif
300 #ifndef TIOCSTI
301 ttybuf.c_cc[VERASE] = 0;
302 ttybuf.c_cc[VKILL] = 0;
303 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
304 safe_signal(SIGINT, SIG_DFL);
305 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
306 safe_signal(SIGQUIT, SIG_DFL);
307 #else /* TIOCSTI */
308 saveint = safe_signal(SIGINT, SIG_IGN);
309 if (sigsetjmp(intjmp, 1)) {
310 /* avoid garbled output with C-c */
311 printf("\n");
312 fflush(stdout);
313 goto out;
315 if (saveint != SIG_IGN)
316 safe_signal(SIGINT, ttyint);
317 #endif /* TIOCSTI */
318 if (gflags & GTO)
319 hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL);
320 if (subjfirst)
321 GRAB_SUBJECT
322 if (gflags & GCC)
323 hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL);
324 if (gflags & GBCC)
325 hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL);
326 if (gflags & GEXTRA) {
327 if (hp->h_from == NULL)
328 hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL);
329 hp->h_from = grabaddrs("From: ", hp->h_from, comma,
330 GEXTRA|GFULL);
331 if (hp->h_replyto == NULL)
332 hp->h_replyto = sextract(value("replyto"),
333 GEXTRA|GFULL);
334 hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma,
335 GEXTRA|GFULL);
336 if (hp->h_sender == NULL)
337 hp->h_sender = sextract(value("sender"),
338 GEXTRA|GFULL);
339 hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma,
340 GEXTRA|GFULL);
341 if (hp->h_organization == NULL)
342 hp->h_organization = value("ORGANIZATION");
343 TTYSET_CHECK(hp->h_organization);
344 hp->h_organization = rtty_internal("Organization: ",
345 hp->h_organization);
347 if (!subjfirst)
348 GRAB_SUBJECT
349 out:
350 safe_signal(SIGTSTP, savetstp);
351 safe_signal(SIGTTOU, savettou);
352 safe_signal(SIGTTIN, savettin);
353 #ifndef TIOCSTI
354 ttybuf.c_cc[VERASE] = c_erase;
355 ttybuf.c_cc[VKILL] = c_kill;
356 if (ttyset)
357 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
358 safe_signal(SIGQUIT, savequit);
359 #endif
360 safe_signal(SIGINT, saveint);
361 return(errs);
365 * Read a line from tty; to be called from elsewhere
368 char *
369 readtty(char *prefix, char *string)
371 char *ret = NULL;
372 struct termios ttybuf;
373 sighandler_type saveint = SIG_DFL;
374 #ifndef TIOCSTI
375 sighandler_type savequit;
376 #endif
377 sighandler_type savetstp;
378 sighandler_type savettou;
379 sighandler_type savettin;
381 (void) &saveint;
382 (void) &ret;
383 savetstp = safe_signal(SIGTSTP, SIG_DFL);
384 savettou = safe_signal(SIGTTOU, SIG_DFL);
385 savettin = safe_signal(SIGTTIN, SIG_DFL);
386 #ifndef TIOCSTI
387 ttyset = 0;
388 #endif
389 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
390 perror("tcgetattr");
391 return NULL;
393 c_erase = ttybuf.c_cc[VERASE];
394 c_kill = ttybuf.c_cc[VKILL];
395 #ifndef TIOCSTI
396 ttybuf.c_cc[VERASE] = 0;
397 ttybuf.c_cc[VKILL] = 0;
398 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
399 safe_signal(SIGINT, SIG_DFL);
400 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
401 safe_signal(SIGQUIT, SIG_DFL);
402 #else
403 if (sigsetjmp(intjmp, 1)) {
404 /* avoid garbled output with C-c */
405 printf("\n");
406 fflush(stdout);
407 goto out2;
409 saveint = safe_signal(SIGINT, ttyint);
410 #endif
411 TTYSET_CHECK(string)
412 ret = rtty_internal(prefix, string);
413 if (ret != NULL && *ret == '\0')
414 ret = NULL;
415 out2:
416 safe_signal(SIGTSTP, savetstp);
417 safe_signal(SIGTTOU, savettou);
418 safe_signal(SIGTTIN, savettin);
419 #ifndef TIOCSTI
420 ttybuf.c_cc[VERASE] = c_erase;
421 ttybuf.c_cc[VKILL] = c_kill;
422 if (ttyset)
423 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
424 safe_signal(SIGQUIT, savequit);
425 #endif
426 safe_signal(SIGINT, saveint);
427 return ret;
430 int
431 yorn(char *msg)
433 char *cp;
435 if (value("interactive") == NULL)
436 return 1;
438 cp = readtty(msg, NULL);
439 while (cp == NULL ||
440 (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N'));
441 return *cp == 'y' || *cp == 'Y';