uagent,version are "char const" not "const char"
[s-mailx.git] / tty.c
blob1e309fc48bb0fa3e66de86854eb45e65d76ac3a4
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 #include "rcv.h"
47 #include "extern.h"
48 #include <errno.h>
49 #include <termios.h>
50 #include <unistd.h>
51 #include <sys/ioctl.h>
53 static cc_t c_erase; /* Current erase char */
54 static cc_t c_kill; /* Current kill char */
55 static sigjmp_buf rewrite; /* Place to go when continued */
56 static sigjmp_buf intjmp; /* Place to go when interrupted */
57 #ifndef TIOCSTI
58 static int ttyset; /* We must now do erase/kill */
59 #endif
60 static struct termios ttybuf;
61 static long vdis; /* _POSIX_VDISABLE char */
63 static void ttystop(int s);
64 static void ttyint(int s);
65 static int safe_getc(FILE *ibuf);
66 static char *rtty_internal(const char *pr, char *src);
69 * Receipt continuation.
71 static void
72 ttystop(int s)
74 sighandler_type old_action = safe_signal(s, SIG_DFL);
75 sigset_t nset;
77 sigemptyset(&nset);
78 sigaddset(&nset, s);
79 sigprocmask(SIG_BLOCK, &nset, NULL);
80 kill(0, s);
81 sigprocmask(SIG_UNBLOCK, &nset, NULL);
82 safe_signal(s, old_action);
83 siglongjmp(rewrite, 1);
86 /*ARGSUSED*/
87 static void
88 ttyint(int s)
90 (void)s;
91 siglongjmp(intjmp, 1);
95 * Interrupts will cause trouble if we are inside a stdio call. As
96 * this is only relevant if input comes from a terminal, we can simply
97 * bypass it by read() then.
99 static int
100 safe_getc(FILE *ibuf)
102 if (fileno(ibuf) == 0 && is_a_tty[0]) {
103 char c;
104 int sz;
106 again:
107 if ((sz = read(0, &c, 1)) != 1) {
108 if (sz < 0 && errno == EINTR)
109 goto again;
110 return EOF;
112 return c & 0377;
113 } else
114 return getc(ibuf);
118 * Read up a header from standard input.
119 * The source string has the preliminary contents to
120 * be read.
122 static char *
123 rtty_internal(const char *pr, char *src)
125 char ch, canonb[LINESIZE];
126 int c;
127 char *cp, *cp2;
129 (void) &c;
130 (void) &cp2;
131 fputs(pr, stdout);
132 fflush(stdout);
133 if (src != NULL && strlen(src) > sizeof canonb - 2) {
134 printf(catgets(catd, CATSET, 200, "too long to edit\n"));
135 return(src);
137 #ifndef TIOCSTI
138 if (src != NULL)
139 cp = sstpcpy(canonb, src);
140 else
141 cp = sstpcpy(canonb, "");
142 fputs(canonb, stdout);
143 fflush(stdout);
144 #else
145 cp = src == NULL ? "" : src;
146 while ((c = *cp++) != '\0') {
147 if ((c_erase != vdis && c == c_erase) ||
148 (c_kill != vdis && c == c_kill)) {
149 ch = '\\';
150 ioctl(0, TIOCSTI, &ch);
152 ch = c;
153 ioctl(0, TIOCSTI, &ch);
155 cp = canonb;
156 *cp = 0;
157 #endif
158 cp2 = cp;
159 while (cp2 < canonb + sizeof canonb)
160 *cp2++ = 0;
161 cp2 = cp;
162 if (sigsetjmp(rewrite, 1))
163 goto redo;
164 safe_signal(SIGTSTP, ttystop);
165 safe_signal(SIGTTOU, ttystop);
166 safe_signal(SIGTTIN, ttystop);
167 clearerr(stdin);
168 while (cp2 < canonb + sizeof canonb - 1) {
169 c = safe_getc(stdin);
170 if (c == EOF || c == '\n')
171 break;
172 *cp2++ = c;
174 *cp2 = 0;
175 safe_signal(SIGTSTP, SIG_DFL);
176 safe_signal(SIGTTOU, SIG_DFL);
177 safe_signal(SIGTTIN, SIG_DFL);
178 if (c == EOF && ferror(stdin)) {
179 redo:
180 cp = strlen(canonb) > 0 ? canonb : NULL;
181 clearerr(stdin);
182 return(rtty_internal(pr, cp));
184 #ifndef TIOCSTI
185 if (cp == NULL || *cp == '\0')
186 return(src);
187 cp2 = cp;
188 if (!ttyset)
189 return(strlen(canonb) > 0 ? savestr(canonb) : NULL);
190 while (*cp != '\0') {
191 c = *cp++;
192 if (c_erase != vdis && c == c_erase) {
193 if (cp2 == canonb)
194 continue;
195 if (cp2[-1] == '\\') {
196 cp2[-1] = c;
197 continue;
199 cp2--;
200 continue;
202 if (c_kill != vdis && c == c_kill) {
203 if (cp2 == canonb)
204 continue;
205 if (cp2[-1] == '\\') {
206 cp2[-1] = c;
207 continue;
209 cp2 = canonb;
210 continue;
212 *cp2++ = c;
214 *cp2 = '\0';
215 #endif
216 return ((*canonb == '\0') ? NULL : savestr(canonb));
220 * Read all relevant header fields.
223 #ifndef TIOCSTI
224 #define TTYSET_CHECK(h) if (!ttyset && (h) != NULL) \
225 ttyset++, tcsetattr(0, TCSADRAIN, \
226 &ttybuf);
227 #else
228 #define TTYSET_CHECK(h)
229 #endif
231 #define GRAB_SUBJECT if (gflags & GSUBJECT) { \
232 TTYSET_CHECK(hp->h_subject) \
233 hp->h_subject = rtty_internal("Subject: ", \
234 hp->h_subject); \
237 static struct name *
238 grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags)
240 struct name *nq;
242 TTYSET_CHECK(np);
243 loop:
244 np = lextract(rtty_internal(field, detract(np, comma)), gflags);
245 for (nq = np; nq != NULL; nq = nq->n_flink)
246 if (is_addr_invalid(nq, 1))
247 goto loop;
248 return np;
251 int
252 grabh(struct header *hp, enum gfield gflags, int subjfirst)
254 sighandler_type saveint, savetstp, savettou, savettin;
255 #ifndef TIOCSTI
256 sighandler_type savequit;
257 #endif
258 int errs;
259 int volatile comma;
261 savetstp = safe_signal(SIGTSTP, SIG_DFL);
262 savettou = safe_signal(SIGTTOU, SIG_DFL);
263 savettin = safe_signal(SIGTTIN, SIG_DFL);
264 errs = 0;
265 comma = value("bsdcompat") || value("bsdmsgs") ? 0 : GCOMMA;
266 #ifndef TIOCSTI
267 ttyset = 0;
268 #endif
269 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
270 perror("tcgetattr");
271 return(-1);
273 c_erase = ttybuf.c_cc[VERASE];
274 c_kill = ttybuf.c_cc[VKILL];
275 #if defined (_PC_VDISABLE) && defined (HAVE_FPATHCONF)
276 if ((vdis = fpathconf(0, _PC_VDISABLE)) < 0)
277 vdis = '\377';
278 #elif defined (_POSIX_VDISABLE)
279 vdis = _POSIX_VDISABLE;
280 #else
281 vdis = '\377';
282 #endif
283 #ifndef TIOCSTI
284 ttybuf.c_cc[VERASE] = 0;
285 ttybuf.c_cc[VKILL] = 0;
286 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
287 safe_signal(SIGINT, SIG_DFL);
288 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
289 safe_signal(SIGQUIT, SIG_DFL);
290 #else /* TIOCSTI */
291 saveint = safe_signal(SIGINT, SIG_IGN);
292 if (sigsetjmp(intjmp, 1)) {
293 /* avoid garbled output with C-c */
294 printf("\n");
295 fflush(stdout);
296 goto out;
298 if (saveint != SIG_IGN)
299 safe_signal(SIGINT, ttyint);
300 #endif /* TIOCSTI */
301 if (gflags & GTO)
302 hp->h_to = grabaddrs("To: ", hp->h_to, comma, GTO|GFULL);
303 if (subjfirst)
304 GRAB_SUBJECT
305 if (gflags & GCC)
306 hp->h_cc = grabaddrs("Cc: ", hp->h_cc, comma, GCC|GFULL);
307 if (gflags & GBCC)
308 hp->h_bcc = grabaddrs("Bcc: ", hp->h_bcc, comma, GBCC|GFULL);
309 if (gflags & GEXTRA) {
310 if (hp->h_from == NULL)
311 hp->h_from = sextract(myaddrs(hp), GEXTRA|GFULL);
312 hp->h_from = grabaddrs("From: ", hp->h_from, comma,
313 GEXTRA|GFULL);
314 if (hp->h_replyto == NULL)
315 hp->h_replyto = sextract(value("replyto"),
316 GEXTRA|GFULL);
317 hp->h_replyto = grabaddrs("Reply-To: ", hp->h_replyto, comma,
318 GEXTRA|GFULL);
319 if (hp->h_sender == NULL)
320 hp->h_sender = sextract(value("sender"),
321 GEXTRA|GFULL);
322 hp->h_sender = grabaddrs("Sender: ", hp->h_sender, comma,
323 GEXTRA|GFULL);
324 if (hp->h_organization == NULL)
325 hp->h_organization = value("ORGANIZATION");
326 TTYSET_CHECK(hp->h_organization);
327 hp->h_organization = rtty_internal("Organization: ",
328 hp->h_organization);
330 if (!subjfirst)
331 GRAB_SUBJECT
332 out:
333 safe_signal(SIGTSTP, savetstp);
334 safe_signal(SIGTTOU, savettou);
335 safe_signal(SIGTTIN, savettin);
336 #ifndef TIOCSTI
337 ttybuf.c_cc[VERASE] = c_erase;
338 ttybuf.c_cc[VKILL] = c_kill;
339 if (ttyset)
340 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
341 safe_signal(SIGQUIT, savequit);
342 #endif
343 safe_signal(SIGINT, saveint);
344 return(errs);
348 * Read a line from tty; to be called from elsewhere
351 char *
352 readtty(char *prefix, char *string)
354 char *ret = NULL;
355 struct termios ttybuf;
356 sighandler_type saveint = SIG_DFL;
357 #ifndef TIOCSTI
358 sighandler_type savequit;
359 #endif
360 sighandler_type savetstp;
361 sighandler_type savettou;
362 sighandler_type savettin;
364 (void) &saveint;
365 (void) &ret;
366 savetstp = safe_signal(SIGTSTP, SIG_DFL);
367 savettou = safe_signal(SIGTTOU, SIG_DFL);
368 savettin = safe_signal(SIGTTIN, SIG_DFL);
369 #ifndef TIOCSTI
370 ttyset = 0;
371 #endif
372 if (tcgetattr(fileno(stdin), &ttybuf) < 0) {
373 perror("tcgetattr");
374 return NULL;
376 c_erase = ttybuf.c_cc[VERASE];
377 c_kill = ttybuf.c_cc[VKILL];
378 #ifndef TIOCSTI
379 ttybuf.c_cc[VERASE] = 0;
380 ttybuf.c_cc[VKILL] = 0;
381 if ((saveint = safe_signal(SIGINT, SIG_IGN)) == SIG_DFL)
382 safe_signal(SIGINT, SIG_DFL);
383 if ((savequit = safe_signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
384 safe_signal(SIGQUIT, SIG_DFL);
385 #else
386 if (sigsetjmp(intjmp, 1)) {
387 /* avoid garbled output with C-c */
388 printf("\n");
389 fflush(stdout);
390 goto out2;
392 saveint = safe_signal(SIGINT, ttyint);
393 #endif
394 TTYSET_CHECK(string)
395 ret = rtty_internal(prefix, string);
396 if (ret != NULL && *ret == '\0')
397 ret = NULL;
398 out2:
399 safe_signal(SIGTSTP, savetstp);
400 safe_signal(SIGTTOU, savettou);
401 safe_signal(SIGTTIN, savettin);
402 #ifndef TIOCSTI
403 ttybuf.c_cc[VERASE] = c_erase;
404 ttybuf.c_cc[VKILL] = c_kill;
405 if (ttyset)
406 tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf);
407 safe_signal(SIGQUIT, savequit);
408 #endif
409 safe_signal(SIGINT, saveint);
410 return ret;
413 int
414 yorn(char *msg)
416 char *cp;
418 if (value("interactive") == NULL)
419 return (1);
420 do if ((cp = readtty(msg, NULL)) == NULL)
421 return (0);
422 while (*cp != 'y' && *cp != 'Y' && *cp != 'n' && *cp != 'N');
423 return (*cp == 'y' || *cp == 'Y');