Many: use srelax() is some places
[s-mailx.git] / collect.c
blobeddf5fed6d4deb643e9633875d0b5ef240618d33
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Collect input from standard input, handling ~ escapes.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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 #include "nail.h"
43 * The following hokiness with global variables is so that on
44 * receipt of an interrupt signal, the partial message can be salted
45 * away on dead.letter.
48 static sighandler_type saveint; /* Previous SIGINT value */
49 static sighandler_type savehup; /* Previous SIGHUP value */
50 static sighandler_type savetstp; /* Previous SIGTSTP value */
51 static sighandler_type savettou; /* Previous SIGTTOU value */
52 static sighandler_type savettin; /* Previous SIGTTIN value */
53 static FILE *collf; /* File for saving away */
54 static int hadintr; /* Have seen one SIGINT so far */
56 static sigjmp_buf colljmp; /* To get back to work */
57 static int colljmp_p; /* whether to long jump */
58 static sigjmp_buf collabort; /* To end collection with error */
60 static sigjmp_buf pipejmp; /* On broken pipe */
62 /* Handle `~:', `~_' */
63 static void _execute_command(struct header *hp, char *linebuf,
64 size_t linesize);
66 /* If *interactive* is set and *doecho* is, too, also dump to *stdout* */
67 static int _include_file(FILE *fbuf, char const *name, int *linecount,
68 int *charcount, bool_t doecho);
70 static void onpipe(int signo);
71 static void insertcommand(FILE *fp, char const *cmd);
72 static void print_collf(FILE *collf, struct header *hp);
73 static int exwrite(char const *name, FILE *fp, int f);
74 static enum okay makeheader(FILE *fp, struct header *hp);
75 static void mesedit(int c, struct header *hp);
76 static void mespipe(char *cmd);
77 static int forward(char *ms, FILE *fp, int f);
78 static void collstop(int s);
79 static void collint(int s);
80 static void collhup(int s);
81 static int putesc(const char *s, FILE *stream);
83 static void
84 _execute_command(struct header *hp, char *linebuf, size_t linesize)
86 /* The problem arises if there are rfc822 message attachments and the
87 * user uses `~:' to change the current file. TODO Unfortunately we
88 * TODO cannot simply keep a pointer to, or increment a reference count
89 * TODO of the current `file' (mailbox that is) object, because the
90 * TODO codebase doesn't deal with that at all; so, until some far
91 * TODO later time, copy the name of the path, and warn the user if it
92 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
93 * TODO copy the message attachments over to temporary files, but that
94 * TODO would require more changes so that the user still can recognize
95 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
96 char *mnbuf = NULL;
97 struct attachment *ap;
98 size_t mnlen;
100 /* If the above todo is worked, remove or outsource to attachments.c! */
101 if ((ap = hp->h_attach) != NULL) do
102 if (ap->a_msgno) {
103 mnlen = strlen(mailname) + 1;
104 mnbuf = ac_alloc(mnlen);
105 memcpy(mnbuf, mailname, mnlen);
107 while ((ap = ap->a_flink) != NULL);
109 inhook = 0;
110 execute(linebuf, TRU1, linesize);
112 if (mnbuf != NULL && memcmp(mnbuf, mailname, mnlen) != 0)
113 fputs(tr(237, "The mail file has changed, existing rfc822 "
114 "attachments became invalid!\n"), stderr);
117 static int
118 _include_file(FILE *fbuf, char const *name, int *linecount, int *charcount,
119 bool_t doecho)
121 int ret = -1;
122 char *linebuf = NULL;
123 size_t linesize = 0, linelen, cnt;
125 if (fbuf == NULL) {
126 if ((fbuf = Fopen(name, "r")) == NULL) {
127 perror(name);
128 goto jleave;
130 } else
131 fflush_rewind(fbuf);
133 *linecount = *charcount = 0;
134 cnt = fsize(fbuf);
135 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0)
136 != NULL) {
137 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
138 != linelen)
139 goto jleave;
140 if ((options & OPT_INTERACTIVE) && doecho)
141 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
142 ++(*linecount);
143 (*charcount) += linelen;
145 if (fflush(collf))
146 goto jleave;
148 ret = 0;
149 jleave:
150 if (linebuf != NULL)
151 free(linebuf);
152 if (fbuf != NULL)
153 Fclose(fbuf);
154 return (ret);
157 /*ARGSUSED*/
158 static void
159 onpipe(int signo)
161 (void)signo;
162 siglongjmp(pipejmp, 1);
166 * Execute cmd and insert its standard output into fp.
168 static void
169 insertcommand(FILE *fp, char const *cmd)
171 FILE *ibuf = NULL;
172 char const *cp;
173 int c;
175 cp = value("SHELL");
176 if (cp == NULL)
177 cp = SHELL;
178 if ((ibuf = Popen(cmd, "r", cp, 0)) != NULL) {
179 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
180 putc(c, fp);
181 Pclose(ibuf, TRU1);
182 } else
183 perror(cmd);
187 * ~p command.
189 static void
190 print_collf(FILE *cf, struct header *hp)
192 char *lbuf = NULL;
193 FILE *volatile obuf = stdout;
194 struct attachment *ap;
195 char const *cp;
196 enum gfield gf;
197 size_t linecnt, maxlines, linesize = 0, linelen, cnt, cnt2;
199 fflush(cf);
200 rewind(cf);
201 cnt = cnt2 = fsize(cf);
203 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL) {
204 for (linecnt = 0;
205 fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0);
206 linecnt++);
207 rewind(cf);
208 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
209 maxlines -= 4;
210 if (hp->h_to)
211 maxlines--;
212 if (hp->h_subject)
213 maxlines--;
214 if (hp->h_cc)
215 maxlines--;
216 if (hp->h_bcc)
217 maxlines--;
218 if (hp->h_attach)
219 maxlines--;
220 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
221 maxlines -= value("ORGANIZATION") != NULL ||
222 hp->h_organization != NULL;
223 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
224 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
225 if ((long)maxlines < 0 || linecnt > maxlines) {
226 cp = get_pager();
227 if (sigsetjmp(pipejmp, 1))
228 goto endpipe;
229 obuf = Popen(cp, "w", NULL, 1);
230 if (obuf == NULL) {
231 perror(cp);
232 obuf = stdout;
233 } else
234 safe_signal(SIGPIPE, onpipe);
238 fprintf(obuf, tr(62, "-------\nMessage contains:\n"));
239 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
240 if (value("fullnames"))
241 gf |= GCOMMA;
242 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
243 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
244 prout(lbuf, linelen, obuf);
245 if (hp->h_attach != NULL) {
246 fputs(tr(63, "-------\nAttachments:\n"), obuf);
247 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
248 if (ap->a_msgno)
249 fprintf(obuf, " - message %u\n", ap->a_msgno);
250 else {
251 /* TODO after MIME/send layer rewrite we *know*
252 * TODO the details of the attachment here,
253 * TODO so adjust this again, then */
254 char const *cs, *csi = "-> ";
256 if ((cs = ap->a_charset) == NULL &&
257 (csi = "<- ",
258 cs = ap->a_input_charset)
259 == NULL)
260 cs = charset_get_lc();
261 if ((cp = ap->a_content_type) == NULL)
262 cp = "?";
263 else if (ascncasecmp(cp, "text/", 5) != 0)
264 csi = "";
266 fprintf(obuf, " - [%s, %s%s] %s\n",
267 cp, csi, cs, ap->a_name);
271 endpipe:
272 if (obuf != stdout) {
273 safe_signal(SIGPIPE, SIG_IGN);
274 Pclose(obuf, TRU1);
275 safe_signal(SIGPIPE, dflpipe);
277 if (lbuf)
278 free(lbuf);
281 FILE *
282 collect(struct header *hp, int printheaders, struct message *mp,
283 char *quotefile, int doprefix)
285 FILE *fbuf;
286 struct ignoretab *quoteig;
287 int lc, cc, eofcount, c, t;
288 int volatile escape, getfields;
289 char *linebuf = NULL, *quote = NULL, *tempMail = NULL;
290 char const *cp;
291 size_t linesize = 0;
292 long cnt;
293 enum sendaction action;
294 sigset_t oset, nset;
295 sighandler_type savedtop;
297 collf = NULL;
299 * Start catching signals from here, but we're still die on interrupts
300 * until we're in the main loop.
302 sigemptyset(&nset);
303 sigaddset(&nset, SIGINT);
304 sigaddset(&nset, SIGHUP);
305 sigprocmask(SIG_BLOCK, &nset, &oset);
306 handlerpush(collint);
307 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
308 safe_signal(SIGINT, collint);
309 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
310 safe_signal(SIGHUP, collhup);
311 /* TODO We do a lot of redundant signal handling, especially
312 * TODO with the line editor(s); try to merge this */
313 savetstp = safe_signal(SIGTSTP, collstop);
314 savettou = safe_signal(SIGTTOU, collstop);
315 savettin = safe_signal(SIGTTIN, collstop);
316 if (sigsetjmp(collabort, 1))
317 goto jerr;
318 if (sigsetjmp(colljmp, 1))
319 goto jerr;
320 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
322 noreset++;
323 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
324 perror(tr(51, "temporary mail file"));
325 goto jerr;
327 unlink(tempMail);
328 Ftfree(&tempMail);
330 if ((cp = value("NAIL_HEAD")) != NULL && putesc(cp, collf) < 0)
331 goto jerr;
334 * If we are going to prompt for a subject,
335 * refrain from printing a newline after
336 * the headers (since some people mind).
338 getfields = 0;
339 if (! (options & OPT_t_FLAG)) {
340 t = GTO|GSUBJECT|GCC|GNL;
341 if (value("fullnames"))
342 t |= GCOMMA;
343 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
344 (value("ask") != NULL || value("asksub") != NULL))
345 t &= ~GNL, getfields |= GSUBJECT;
346 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
347 t &= ~GNL, getfields |= GTO;
348 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
349 (options & OPT_INTERACTIVE)) {
350 if (hp->h_bcc == NULL && value("askbcc"))
351 t &= ~GNL, getfields |= GBCC;
352 if (hp->h_cc == NULL && value("askcc"))
353 t &= ~GNL, getfields |= GCC;
355 if (printheaders) {
356 (void)puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
357 NULL, NULL);
358 (void)fflush(stdout);
363 * Quote an original message
365 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
366 quoteig = allignore;
367 action = SEND_QUOTE;
368 if (doprefix) {
369 quoteig = fwdignore;
370 if ((cp = value("fwdheading")) == NULL)
371 cp = "-------- Original Message --------";
372 if (*cp && fprintf(collf, "%s\n", cp) < 0)
373 goto jerr;
374 } else if (strcmp(quote, "noheading") == 0) {
375 /*EMPTY*/;
376 } else if (strcmp(quote, "headers") == 0) {
377 quoteig = ignore;
378 } else if (strcmp(quote, "allheaders") == 0) {
379 quoteig = NULL;
380 action = SEND_QUOTE_ALL;
381 } else {
382 cp = hfield1("from", mp);
383 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
384 if (xmime_write(cp, cnt,
385 collf, CONV_FROMHDR, TD_NONE,
386 NULL) < 0)
387 goto jerr;
388 if (fprintf(collf, tr(52, " wrote:\n\n")) < 0)
389 goto jerr;
392 if (fflush(collf))
393 goto jerr;
394 cp = value("indentprefix");
395 if (cp != NULL && *cp == '\0')
396 cp = "\t";
397 if (sendmp(mp, collf, quoteig, (doprefix ? NULL : cp), action,
398 NULL) < 0)
399 goto jerr;
402 /* Print what we have sofar also on the terminal */
403 (void)rewind(collf);
404 while ((c = getc(collf)) != EOF) /* XXX bytewise, yuck! */
405 (void)putc(c, stdout);
406 if (fseek(collf, 0, SEEK_END))
407 goto jerr;
409 escape = ((cp = value("escape")) != NULL) ? *cp : ESCAPE;
410 eofcount = 0;
411 hadintr = 0;
413 if (! sigsetjmp(colljmp, 1)) {
414 if (getfields)
415 grab_headers(hp, getfields, 1);
416 if (quotefile != NULL) {
417 if (_include_file(NULL, quotefile, &lc, &cc, TRU1) != 0)
418 goto jerr;
420 if ((options & OPT_INTERACTIVE) && value("editalong")) {
421 rewind(collf);
422 mesedit('e', hp);
423 goto jcont;
425 } else {
427 * Come here for printing the after-signal message.
428 * Duplicate messages won't be printed because
429 * the write is aborted if we get a SIGTTOU.
431 jcont:
432 if (hadintr) {
433 (void)fprintf(stderr, tr(53,
434 "\n(Interrupt -- one more to kill letter)\n"));
435 } else {
436 printf(tr(54, "(continue)\n"));
437 (void)fflush(stdout);
442 * No tilde escapes, interrupts not expected. Simply copy STDIN
444 if (! (options & (OPT_INTERACTIVE | OPT_t_FLAG|OPT_TILDE_FLAG))) {
445 linebuf = srealloc(linebuf, linesize = LINESIZE);
446 while ((cnt = fread(linebuf, sizeof *linebuf,
447 linesize, stdin)) > 0) {
448 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf,
449 cnt, collf))
450 goto jerr;
452 if (fflush(collf))
453 goto jerr;
454 goto jout;
458 * The interactive collect loop
460 for (;;) {
461 colljmp_p = 1;
462 cnt = readline_input(LNED_NONE, "", &linebuf, &linesize);
463 colljmp_p = 0;
465 if (cnt < 0) {
466 if ((options & OPT_INTERACTIVE) &&
467 value("ignoreeof") != NULL && ++eofcount < 25) {
468 printf(tr(55,
469 "Use \".\" to terminate letter\n"));
470 continue;
472 break;
474 if ((options & OPT_t_FLAG) && cnt == 0) {
475 rewind(collf);
476 if (makeheader(collf, hp) != OKAY)
477 goto jerr;
478 rewind(collf);
479 options &= ~OPT_t_FLAG;
480 continue;
483 eofcount = 0;
484 hadintr = 0;
485 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
486 (options & (OPT_INTERACTIVE|OPT_TILDE_FLAG)) &&
487 (boption("dot") || boption("ignoreeof")))
488 break;
489 if (cnt == 0 || linebuf[0] != escape || ! (options &
490 (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
491 /* TODO calls putline(), which *always* appends LF;
492 * TODO thus, STDIN with -t will ALWAYS end with LF,
493 * TODO even if no trailing LF and QP CTE */
494 if (putline(collf, linebuf, cnt) < 0)
495 goto jerr;
496 continue;
499 c = linebuf[1];
500 switch (c) {
501 default:
503 * On double escape, just send the single one.
504 * Otherwise, it's an error.
506 if (c == escape) {
507 if (putline(collf, &linebuf[1], cnt - 1) < 0)
508 goto jerr;
509 else
510 break;
512 fputs(tr(56, "Unknown tilde escape.\n"), stderr);
513 break;
514 #ifdef HAVE_ASSERTS
515 case 'C':
516 /* Dump core */
517 core(NULL);
518 break;
519 #endif
520 case '!':
521 /* Shell escape, send the balance of line to sh -c */
522 shell(&linebuf[2]);
523 break;
524 case ':':
525 /* FALLTHRU */
526 case '_':
527 /* Escape to command mode, but be nice! */
528 _execute_command(hp, linebuf + 2, cnt - 2);
529 goto jcont;
530 case '.':
531 /* Simulate end of file on input */
532 goto jout;
533 case 'x':
534 /* Same as 'q', but no dead.letter saving */
535 /* FALLTHRU */
536 case 'q':
537 /* Force a quit, act like an interrupt had happened */
538 ++hadintr;
539 collint((c == 'x') ? 0 : SIGINT);
540 exit(1);
541 /*NOTREACHED*/
542 case 'h':
543 /* Grab a bunch of headers */
545 grab_headers(hp, GTO|GSUBJECT|GCC|GBCC,
546 (value("bsdcompat") != NULL &&
547 value("bsdorder") != NULL));
548 while (hp->h_to == NULL);
549 goto jcont;
550 case 'H':
551 /* Grab extra headers */
553 grab_headers(hp, GEXTRA, 0);
554 while (check_from_and_sender(hp->h_from, hp->h_sender));
555 goto jcont;
556 case 't':
557 /* Add to the To list */
558 while ((hp->h_to = cat(hp->h_to, checkaddrs(
559 lextract(&linebuf[2], GTO|GFULL))))
560 == NULL)
562 break;
563 case 's':
564 /* Set the Subject list */
565 cp = &linebuf[2];
566 while (whitechar(*cp))
567 ++cp;
568 hp->h_subject = savestr(cp);
569 break;
570 #ifdef HAVE_ASSERTS
571 case 'S':
572 c_sstats(NULL);
573 break;
574 #endif
575 case '@':
576 /* Edit the attachment list */
577 if (linebuf[2] != '\0')
578 hp->h_attach = append_attachments(hp->h_attach,
579 &linebuf[2]);
580 else
581 hp->h_attach = edit_attachments(hp->h_attach);
582 break;
583 case 'c':
584 /* Add to the CC list */
585 hp->h_cc = cat(hp->h_cc, checkaddrs(
586 lextract(&linebuf[2], GCC|GFULL)));
587 break;
588 case 'b':
589 /* Add stuff to blind carbon copies list */
590 hp->h_bcc = cat(hp->h_bcc, checkaddrs(
591 lextract(&linebuf[2], GBCC|GFULL)));
592 break;
593 case 'd':
594 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
595 linebuf[linesize-1]='\0';
596 /*FALLTHRU*/
597 case 'r':
598 case '<':
600 * Invoke a file:
601 * Search for the file name,
602 * then open it and copy the contents to collf.
604 cp = &linebuf[2];
605 while (whitechar(*cp))
606 cp++;
607 if (*cp == '\0') {
608 fputs(tr(57, "Interpolate what file?\n"),
609 stderr);
610 break;
612 if (*cp == '!') {
613 insertcommand(collf, cp + 1);
614 break;
616 if ((cp = file_expand(cp)) == NULL)
617 break;
618 if (is_dir(cp)) {
619 fprintf(stderr, tr(58, "%s: Directory\n"), cp);
620 break;
622 if ((fbuf = Fopen(cp, "r")) == NULL) {
623 perror(cp);
624 break;
626 printf(tr(59, "\"%s\" "), cp);
627 fflush(stdout);
628 if (_include_file(fbuf, cp, &lc, &cc, FAL0) != 0)
629 goto jerr;
630 printf(tr(60, "%d/%d\n"), lc, cc);
631 break;
632 case 'i':
633 /* Insert an environment variable into the file */
634 cp = &linebuf[2];
635 while (whitechar(*cp))
636 ++cp;
637 if ((cp = value(cp)) == NULL || *cp == '\0')
638 break;
639 if (putesc(cp, collf) < 0)
640 goto jerr;
641 if ((options & OPT_INTERACTIVE) &&
642 putesc(cp, stdout) < 0)
643 goto jerr;
644 break;
645 case 'a':
646 case 'A':
647 /* Insert the contents of a signature variable */
648 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
649 *cp != '\0') {
650 if (putesc(cp, collf) < 0)
651 goto jerr;
652 if ((options & OPT_INTERACTIVE) &&
653 putesc(cp, stdout) < 0)
654 goto jerr;
656 break;
657 case 'w':
658 /* Write the message on a file */
659 cp = &linebuf[2];
660 while (blankchar(*cp))
661 ++cp;
662 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
663 fputs(tr(61, "Write what file!?\n"), stderr);
664 break;
666 rewind(collf);
667 if (exwrite(cp, collf, 1) < 0)
668 goto jerr;
669 break;
670 case 'm':
671 case 'M':
672 case 'f':
673 case 'F':
675 * Interpolate the named messages, if we
676 * are in receiving mail mode. Does the
677 * standard list processing garbage.
678 * If ~f is given, we don't shift over.
680 if (forward(linebuf + 2, collf, c) < 0)
681 goto jerr;
682 goto jcont;
683 case 'p':
685 * Print out the current state of the
686 * message without altering anything.
688 print_collf(collf, hp);
689 goto jcont;
690 case '|':
692 * Pipe message through command.
693 * Collect output as new message.
695 rewind(collf);
696 mespipe(&linebuf[2]);
697 goto jcont;
698 case 'v':
699 case 'e':
701 * Edit the current message.
702 * 'e' means to use EDITOR
703 * 'v' means to use VISUAL
705 rewind(collf);
706 mesedit(c, value("editheaders") ? hp : NULL);
707 goto jcont;
708 case '?':
710 * Last the lengthy help string.
711 * (Very ugly, but take care for compiler supported
712 * string lengths :()
714 (void)puts(tr(300,
715 "-------------------- ~ ESCAPES ----------------------------\n"
716 "~~ Quote a single tilde\n"
717 "~@ [file ...] Edit attachment list\n"
718 "~b users Add users to \"blind\" cc list\n"
719 "~c users Add users to cc list\n"
720 "~d Read in dead.letter\n"
721 "~e Edit the message buffer\n"
722 "~f messages Read in messages without indenting lines\n"
723 "~F messages Same as ~f, but keep all header lines\n"
724 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
725 (void)puts(tr(301,
726 "~r file Read a file into the message buffer\n"
727 "~p Print the message buffer\n"
728 "~q Abort message composition and save text to dead.letter\n"
729 "~m messages Read in messages with each line indented\n"
730 "~M messages Same as ~m, but keep all header lines\n"
731 "~s subject Set subject\n"
732 "~t users Add users to to list\n"
733 "~v Invoke display editor on message\n"
734 "~w file Write message onto file\n"
735 "~x Abort message composition and discard text written so far\n"));
736 (void)puts(tr(302,
737 "~!command Invoke the shell\n"
738 "~:command Execute a regular command\n"
739 "-----------------------------------------------------------\n"));
740 break;
744 jout:
745 if (collf != NULL) {
746 if ((cp = value("NAIL_TAIL")) != NULL) {
747 if (putesc(cp, collf) < 0)
748 goto jerr;
749 if ((options & OPT_INTERACTIVE) &&
750 putesc(cp, stdout) < 0)
751 goto jerr;
753 rewind(collf);
755 if (linebuf != NULL)
756 free(linebuf);
757 handlerpop();
758 noreset--;
759 sigemptyset(&nset);
760 sigaddset(&nset, SIGINT);
761 sigaddset(&nset, SIGHUP);
762 sigprocmask(SIG_BLOCK, &nset, (sigset_t*)NULL);
763 safe_signal(SIGINT, saveint);
764 safe_signal(SIGHUP, savehup);
765 safe_signal(SIGTSTP, savetstp);
766 safe_signal(SIGTTOU, savettou);
767 safe_signal(SIGTTIN, savettin);
768 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
769 return (collf);
771 jerr:
772 if (tempMail != NULL) {
773 rm(tempMail);
774 Ftfree(&tempMail);
776 if (collf != NULL) {
777 Fclose(collf);
778 collf = NULL;
780 goto jout;
784 * Write a file, ex-like if f set.
786 static int
787 exwrite(char const *name, FILE *fp, int f)
789 FILE *of;
790 int c;
791 long cc;
792 int lc;
794 if (f) {
795 printf("\"%s\" ", name);
796 fflush(stdout);
798 if ((of = Fopen(name, "a")) == NULL) {
799 perror(NULL);
800 return(-1);
802 lc = 0;
803 cc = 0;
804 while ((c = getc(fp)) != EOF) {
805 cc++;
806 if (c == '\n')
807 lc++;
808 putc(c, of);
809 if (ferror(of)) {
810 perror(name);
811 Fclose(of);
812 return(-1);
815 Fclose(of);
816 printf(tr(65, "%d/%ld\n"), lc, cc);
817 fflush(stdout);
818 return(0);
821 static enum okay
822 makeheader(FILE *fp, struct header *hp)
824 char *tempEdit;
825 FILE *nf;
826 int c;
828 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
829 perror(tr(66, "temporary mail edit file"));
830 return STOP;
832 unlink(tempEdit);
833 Ftfree(&tempEdit);
835 extract_header(fp, hp);
836 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
837 putc(c, nf);
838 if (fp != collf)
839 Fclose(collf);
840 Fclose(fp);
841 collf = nf;
842 if (check_from_and_sender(hp->h_from, hp->h_sender))
843 return STOP;
844 return OKAY;
848 * Edit the message being collected on fp.
849 * On return, make the edit file the new temp file.
851 static void
852 mesedit(int c, struct header *hp)
854 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
855 char *saved = value("add-file-recipients");
856 FILE *nf;
858 assign("add-file-recipients", "");
859 nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
861 if (nf != NULL) {
862 if (hp) {
863 rewind(nf);
864 makeheader(nf, hp);
865 } else {
866 fseek(nf, 0L, SEEK_END);
867 Fclose(collf);
868 collf = nf;
872 assign("add-file-recipients", saved);
873 safe_signal(SIGINT, sigint);
877 * Pipe the message through the command.
878 * Old message is on stdin of command;
879 * New message collected from stdout.
880 * Sh -c must return 0 to accept the new message.
882 static void
883 mespipe(char *cmd)
885 FILE *nf;
886 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
887 char *tempEdit;
888 char const *sh;
890 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
891 perror(tr(66, "temporary mail edit file"));
892 goto out;
894 fflush(collf);
895 unlink(tempEdit);
896 Ftfree(&tempEdit);
898 * stdin = current message.
899 * stdout = new message.
901 if ((sh = value("SHELL")) == NULL)
902 sh = SHELL;
903 if (run_command(sh,
904 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
905 Fclose(nf);
906 goto out;
908 if (fsize(nf) == 0) {
909 fprintf(stderr, tr(67, "No bytes from \"%s\" !?\n"), cmd);
910 Fclose(nf);
911 goto out;
914 * Take new files.
916 fseek(nf, 0L, SEEK_END);
917 Fclose(collf);
918 collf = nf;
919 out:
920 safe_signal(SIGINT, sigint);
924 * Interpolate the named messages into the current
925 * message, preceding each line with a tab.
926 * Return a count of the number of characters now in
927 * the message, or -1 if an error is encountered writing
928 * the message temporary. The flag argument is 'm' if we
929 * should shift over and 'f' if not.
931 static int
932 forward(char *ms, FILE *fp, int f)
934 int *msgvec;
935 struct ignoretab *ig;
936 char const *tabst;
937 enum sendaction action;
939 /*LINTED*/
940 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
941 if (msgvec == NULL)
942 return(0);
943 if (getmsglist(ms, msgvec, 0) < 0)
944 return(0);
945 if (*msgvec == 0) {
946 *msgvec = first(0, MMNORM);
947 if (*msgvec == 0) {
948 fputs(tr(68, "No appropriate messages\n"), stderr);
949 return 0;
951 msgvec[1] = 0;
953 if (f == 'f' || f == 'F')
954 tabst = NULL;
955 else if ((tabst = value("indentprefix")) == NULL)
956 tabst = "\t";
957 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
958 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
959 printf(tr(69, "Interpolating:"));
960 for (; *msgvec != 0; msgvec++) {
961 struct message *mp = message + *msgvec - 1;
963 touch(mp);
964 printf(" %d", *msgvec);
965 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
966 perror(tr(70, "temporary mail file"));
967 return(-1);
970 printf("\n");
971 return(0);
975 * Print (continue) when continued after ^Z.
977 /*ARGSUSED*/
978 static void
979 collstop(int s)
981 sighandler_type old_action = safe_signal(s, SIG_DFL);
982 sigset_t nset;
984 sigemptyset(&nset);
985 sigaddset(&nset, s);
986 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
987 kill(0, s);
988 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
989 safe_signal(s, old_action);
990 if (colljmp_p) {
991 colljmp_p = 0;
992 hadintr = 0;
993 siglongjmp(colljmp, 1);
998 * On interrupt, come here to save the partial message in ~/dead.letter.
999 * Then jump out of the collection loop.
1001 /*ARGSUSED*/
1002 static void
1003 collint(int s)
1006 * the control flow is subtle, because we can be called from ~q.
1008 if (!hadintr) {
1009 if (value("ignore") != NULL) {
1010 puts("@");
1011 fflush(stdout);
1012 clearerr(stdin);
1013 return;
1015 hadintr = 1;
1016 siglongjmp(colljmp, 1);
1018 exit_status |= 04;
1019 if (value("save") != NULL && s != 0)
1020 savedeadletter(collf, 1);
1021 /* Aborting message, no need to fflush() .. */
1022 siglongjmp(collabort, 1);
1025 /*ARGSUSED*/
1026 static void
1027 collhup(int s)
1029 (void)s;
1030 savedeadletter(collf, 1);
1032 * Let's pretend nobody else wants to clean up,
1033 * a true statement at this time.
1035 exit(1);
1038 void
1039 savedeadletter(FILE *fp, int fflush_rewind_first)
1041 char const *cp;
1042 int c;
1043 FILE *dbuf;
1044 ul_it lines, bytes;
1046 if (fflush_rewind_first) {
1047 (void)fflush(fp);
1048 rewind(fp);
1050 if (fsize(fp) == 0)
1051 goto jleave;
1053 cp = getdeadletter();
1054 c = umask(077);
1055 dbuf = Fopen(cp, "a");
1056 umask(c);
1057 if (dbuf == NULL)
1058 goto jleave;
1061 * There are problems with dup()ing of file-descriptors for child
1062 * processes. As long as those are not fixed in equal spirit to
1063 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1064 * bugs like (If *record* is set, avoid writing dead content twice..,
1065 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1066 * makes itself comfortable with the *real* offset of the underlaying
1067 * file descriptor. Unfortunately Standard I/O and POSIX don't
1068 * describe a way for that -- fflush();rewind(); won't do it.
1069 * This fseek(END),rewind() pair works around the problem on *BSD.
1071 (void)fseek(fp, 0, SEEK_END);
1072 rewind(fp);
1074 printf("\"%s\" ", cp);
1075 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1076 putc(c, dbuf);
1077 if (c == '\n')
1078 ++lines;
1080 printf("%lu/%lu\n", lines, bytes);
1082 Fclose(dbuf);
1083 rewind(fp);
1084 jleave: ;
1087 static int
1088 putesc(const char *s, FILE *stream)
1090 int n = 0;
1092 while (s[0]) {
1093 if (s[0] == '\\') {
1094 if (s[1] == 't') {
1095 if (putc('\t', stream) == EOF)
1096 return (-1);
1097 n++;
1098 s += 2;
1099 continue;
1101 if (s[1] == 'n') {
1102 if (putc('\n', stream) == EOF)
1103 return (-1);
1104 n++;
1105 s += 2;
1106 continue;
1109 if (putc(s[0], stream) == EOF)
1110 return (-1);
1111 n++;
1112 s++;
1114 if (putc('\n', stream) == EOF)
1115 return (-1);
1116 return (++n);