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 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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
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
40 #ifndef HAVE_AMALGAMATION
45 * The following hokiness with global variables is so that on
46 * receipt of an interrupt signal, the partial message can be salted
47 * away on dead.letter.
50 static sighandler_type _coll_saveint
; /* Previous SIGINT value */
51 static sighandler_type _coll_savehup
; /* Previous SIGHUP value */
52 static sighandler_type _coll_savetstp
; /* Previous SIGTSTP value */
53 static sighandler_type _coll_savettou
; /* Previous SIGTTOU value */
54 static sighandler_type _coll_savettin
; /* Previous SIGTTIN value */
55 static FILE *_coll_fp
; /* File for saving away */
56 static int volatile _coll_hadintr
; /* Have seen one SIGINT so far */
57 static sigjmp_buf _coll_jmp
; /* To get back to work */
58 static int _coll_jmp_p
; /* whether to long jump */
59 static sigjmp_buf _coll_abort
; /* To end collection with error */
60 static sigjmp_buf _coll_pipejmp
; /* On broken pipe */
62 /* Handle `~:', `~_' */
63 static void _execute_command(struct header
*hp
, char *linebuf
,
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 _collect_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
);
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 */
97 size_t mnlen
= 0 /* silence CC */;
98 struct attachment
*ap
;
100 /* If the above todo is worked, remove or outsource to attachments.c! */
101 if ((ap
= hp
->h_attach
) != NULL
) do
103 mnlen
= strlen(mailname
) + 1;
104 mnbuf
= ac_alloc(mnlen
);
105 memcpy(mnbuf
, mailname
, mnlen
);
108 while ((ap
= ap
->a_flink
) != NULL
);
111 execute(linebuf
, TRU1
, linesize
);
114 if (strncmp(mnbuf
, mailname
, mnlen
))
115 fputs(tr(237, "Mailbox changed: it seems existing "
116 "rfc822 attachments became invalid!\n"),
123 _include_file(FILE *fbuf
, char const *name
, int *linecount
, int *charcount
,
127 char *linebuf
= NULL
;
128 size_t linesize
= 0, linelen
, cnt
;
131 if ((fbuf
= Fopen(name
, "r")) == NULL
) {
138 *linecount
= *charcount
= 0;
140 while (fgetline(&linebuf
, &linesize
, &cnt
, &linelen
, fbuf
, 0)
142 if (fwrite(linebuf
, sizeof *linebuf
, linelen
, _coll_fp
)
145 if ((options
& OPT_INTERACTIVE
) && doecho
)
146 fwrite(linebuf
, sizeof *linebuf
, linelen
, stdout
);
148 (*charcount
) += linelen
;
150 if (fflush(_coll_fp
))
164 _collect_onpipe(int signo
)
167 siglongjmp(_coll_pipejmp
, 1);
171 * Execute cmd and insert its standard output into fp.
174 insertcommand(FILE *fp
, char const *cmd
)
180 cp
= ok_vlook(SHELL
);
183 if ((ibuf
= Popen(cmd
, "r", cp
, 0)) != NULL
) {
184 while ((c
= getc(ibuf
)) != EOF
) /* XXX bytewise, yuck! */
195 print_collf(FILE *cf
, struct header
*hp
)
198 FILE *volatile obuf
= stdout
;
199 struct attachment
*ap
;
202 size_t linecnt
, maxlines
, linesize
= 0, linelen
, cnt
, cnt2
;
206 cnt
= cnt2
= fsize(cf
);
208 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
) {
210 fgetline(&lbuf
, &linesize
, &cnt2
, NULL
, cf
, 0);
213 maxlines
= (*cp
== '\0' ? screensize() : atoi(cp
));
225 maxlines
-= myaddrs(hp
) != NULL
|| hp
->h_from
!= NULL
;
226 maxlines
-= ok_vlook(ORGANIZATION
) != NULL
||
227 hp
->h_organization
!= NULL
;
228 maxlines
-= ok_vlook(replyto
) != NULL
|| hp
->h_replyto
!= NULL
;
229 maxlines
-= ok_vlook(sender
) != NULL
|| hp
->h_sender
!= NULL
;
230 if ((long)maxlines
< 0 || linecnt
> maxlines
) {
232 if (sigsetjmp(_coll_pipejmp
, 1))
234 obuf
= Popen(cp
, "w", NULL
, 1);
239 safe_signal(SIGPIPE
, &_collect_onpipe
);
243 fprintf(obuf
, tr(62, "-------\nMessage contains:\n"));
244 gf
= GIDENT
|GTO
|GSUBJECT
|GCC
|GBCC
|GNL
|GFILES
;
245 if (ok_blook(fullnames
))
247 puthead(hp
, obuf
, gf
, SEND_TODISP
, CONV_NONE
, NULL
, NULL
);
248 while (fgetline(&lbuf
, &linesize
, &cnt
, &linelen
, cf
, 1))
249 prout(lbuf
, linelen
, obuf
);
250 if (hp
->h_attach
!= NULL
) {
251 fputs(tr(63, "-------\nAttachments:\n"), obuf
);
252 for (ap
= hp
->h_attach
; ap
!= NULL
; ap
= ap
->a_flink
) {
254 fprintf(obuf
, " - message %u\n", ap
->a_msgno
);
256 /* TODO after MIME/send layer rewrite we *know*
257 * TODO the details of the attachment here,
258 * TODO so adjust this again, then */
259 char const *cs
, *csi
= "-> ";
261 if ((cs
= ap
->a_charset
) == NULL
&&
263 cs
= ap
->a_input_charset
)
265 cs
= charset_get_lc();
266 if ((cp
= ap
->a_content_type
) == NULL
)
268 else if (ascncasecmp(cp
, "text/", 5) != 0)
271 fprintf(obuf
, " - [%s, %s%s] %s\n",
272 cp
, csi
, cs
, ap
->a_name
);
277 if (obuf
!= stdout
) {
278 safe_signal(SIGPIPE
, SIG_IGN
);
280 safe_signal(SIGPIPE
, dflpipe
);
287 collect(struct header
*hp
, int printheaders
, struct message
*mp
,
288 char *quotefile
, int doprefix
)
291 struct ignoretab
*quoteig
;
292 int lc
, cc
, eofcount
, c
, t
;
293 int volatile escape
, getfields
;
294 char *linebuf
= NULL
, *quote
= NULL
, *tempMail
= NULL
;
298 enum sendaction action
;
300 sighandler_type savedtop
;
304 * Start catching signals from here, but we're still die on interrupts
305 * until we're in the main loop.
308 sigaddset(&nset
, SIGINT
);
309 sigaddset(&nset
, SIGHUP
);
310 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
311 handlerpush(collint
);
312 if ((_coll_saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
313 safe_signal(SIGINT
, collint
);
314 if ((_coll_savehup
= safe_signal(SIGHUP
, SIG_IGN
)) != SIG_IGN
)
315 safe_signal(SIGHUP
, collhup
);
316 /* TODO We do a lot of redundant signal handling, especially
317 * TODO with the command line editor(s); try to merge this */
318 _coll_savetstp
= safe_signal(SIGTSTP
, collstop
);
319 _coll_savettou
= safe_signal(SIGTTOU
, collstop
);
320 _coll_savettin
= safe_signal(SIGTTIN
, collstop
);
321 if (sigsetjmp(_coll_abort
, 1))
323 if (sigsetjmp(_coll_jmp
, 1))
325 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
328 if ((_coll_fp
= Ftemp(&tempMail
, "Rs", "w+", 0600, 1)) == NULL
) {
329 perror(tr(51, "temporary mail file"));
335 if ((cp
= ok_vlook(NAIL_HEAD
)) != NULL
&& putesc(cp
, _coll_fp
) < 0)
339 * If we are going to prompt for a subject,
340 * refrain from printing a newline after
341 * the headers (since some people mind).
344 if (! (options
& OPT_t_FLAG
)) {
345 t
= GTO
|GSUBJECT
|GCC
|GNL
;
346 if (ok_blook(fullnames
))
348 if (hp
->h_subject
== NULL
&& (options
& OPT_INTERACTIVE
) &&
349 (ok_blook(ask
) || ok_blook(asksub
)))
350 t
&= ~GNL
, getfields
|= GSUBJECT
;
351 if (hp
->h_to
== NULL
&& (options
& OPT_INTERACTIVE
))
352 t
&= ~GNL
, getfields
|= GTO
;
353 if (!ok_blook(bsdcompat
) && !ok_blook(askatend
) &&
354 (options
& OPT_INTERACTIVE
)) {
355 if (hp
->h_bcc
== NULL
&& ok_blook(askbcc
))
356 t
&= ~GNL
, getfields
|= GBCC
;
357 if (hp
->h_cc
== NULL
&& ok_blook(askcc
))
358 t
&= ~GNL
, getfields
|= GCC
;
361 (void)puthead(hp
, stdout
, t
, SEND_TODISP
, CONV_NONE
,
363 (void)fflush(stdout
);
368 * Quote an original message
370 if (mp
!= NULL
&& (doprefix
|| (quote
= ok_vlook(quote
)) != NULL
)) {
375 if ((cp
= ok_vlook(fwdheading
)) == NULL
)
376 cp
= "-------- Original Message --------";
377 if (*cp
&& fprintf(_coll_fp
, "%s\n", cp
) < 0)
379 } else if (strcmp(quote
, "noheading") == 0) {
381 } else if (strcmp(quote
, "headers") == 0) {
383 } else if (strcmp(quote
, "allheaders") == 0) {
385 action
= SEND_QUOTE_ALL
;
387 cp
= hfield1("from", mp
);
388 if (cp
!= NULL
&& (cnt
= (long)strlen(cp
)) > 0) {
389 if (xmime_write(cp
, cnt
, _coll_fp
,
390 CONV_FROMHDR
, TD_NONE
,
393 if (fprintf(_coll_fp
,
394 tr(52, " wrote:\n\n")) < 0)
398 if (fflush(_coll_fp
))
400 cp
= ok_vlook(indentprefix
);
401 if (cp
!= NULL
&& *cp
== '\0')
403 if (sendmp(mp
, _coll_fp
, quoteig
, (doprefix
? NULL
: cp
),
408 /* Print what we have sofar also on the terminal */
410 while ((c
= getc(_coll_fp
)) != EOF
) /* XXX bytewise, yuck! */
411 (void)putc(c
, stdout
);
412 if (fseek(_coll_fp
, 0, SEEK_END
))
415 escape
= ((cp
= ok_vlook(escape
)) != NULL
) ? *cp
: ESCAPE
;
419 if (!sigsetjmp(_coll_jmp
, 1)) {
421 grab_headers(hp
, getfields
, 1);
422 if (quotefile
!= NULL
) {
423 if (_include_file(NULL
, quotefile
, &lc
, &cc
, TRU1
) != 0)
426 if ((options
& OPT_INTERACTIVE
) && ok_blook(editalong
)) {
433 * Come here for printing the after-signal message.
434 * Duplicate messages won't be printed because
435 * the write is aborted if we get a SIGTTOU.
439 (void)fprintf(stderr
, tr(53,
440 "\n(Interrupt -- one more to kill letter)\n"));
442 printf(tr(54, "(continue)\n"));
443 (void)fflush(stdout
);
448 * No tilde escapes, interrupts not expected. Simply copy STDIN
450 if (! (options
& (OPT_INTERACTIVE
| OPT_t_FLAG
|OPT_TILDE_FLAG
))) {
451 linebuf
= srealloc(linebuf
, linesize
= LINESIZE
);
452 while ((cnt
= fread(linebuf
, sizeof *linebuf
,
453 linesize
, stdin
)) > 0) {
454 if ((size_t)cnt
!= fwrite(linebuf
, sizeof *linebuf
,
458 if (fflush(_coll_fp
))
464 * The interactive collect loop
468 cnt
= readline_input(LNED_NONE
, "", &linebuf
, &linesize
);
472 if ((options
& OPT_INTERACTIVE
) &&
473 ok_blook(ignoreeof
) && ++eofcount
< 25) {
475 "Use \".\" to terminate letter\n"));
480 if ((options
& OPT_t_FLAG
) && cnt
== 0) {
482 if (makeheader(_coll_fp
, hp
) != OKAY
)
485 options
&= ~OPT_t_FLAG
;
491 if (linebuf
[0] == '.' && linebuf
[1] == '\0' &&
492 (options
& (OPT_INTERACTIVE
|OPT_TILDE_FLAG
)) &&
493 (ok_blook(dot
) || ok_blook(ignoreeof
)))
495 if (cnt
== 0 || linebuf
[0] != escape
|| ! (options
&
496 (OPT_INTERACTIVE
| OPT_TILDE_FLAG
))) {
497 /* TODO calls putline(), which *always* appends LF;
498 * TODO thus, STDIN with -t will ALWAYS end with LF,
499 * TODO even if no trailing LF and QP CTE */
500 if (putline(_coll_fp
, linebuf
, cnt
) < 0)
509 * On double escape, just send the single one.
510 * Otherwise, it's an error.
513 if (putline(_coll_fp
, &linebuf
[1], cnt
- 1) < 0)
518 fputs(tr(56, "Unknown tilde escape.\n"), stderr
);
521 /* Shell escape, send the balance of line to sh -c */
527 /* Escape to command mode, but be nice! */
528 _execute_command(hp
, linebuf
+ 2, cnt
- 2);
531 /* Simulate end of file on input */
534 /* Same as 'q', but no dead.letter saving */
537 /* Force a quit, act like an interrupt had happened */
539 collint((c
== 'x') ? 0 : SIGINT
);
543 /* Grab a bunch of headers */
545 grab_headers(hp
, GTO
|GSUBJECT
|GCC
|GBCC
,
546 (ok_blook(bsdcompat
) &&
547 ok_blook(bsdorder
)));
548 while (hp
->h_to
== NULL
);
551 /* Grab extra headers */
553 grab_headers(hp
, GEXTRA
, 0);
554 while (check_from_and_sender(hp
->h_from
, hp
->h_sender
));
557 /* Add to the To list */
558 while ((hp
->h_to
= cat(hp
->h_to
, checkaddrs(
559 lextract(&linebuf
[2], GTO
|GFULL
))))
564 /* Set the Subject list */
566 while (whitechar(*cp
))
568 hp
->h_subject
= savestr(cp
);
576 /* Edit the attachment list */
577 if (linebuf
[2] != '\0')
578 hp
->h_attach
= append_attachments(hp
->h_attach
,
581 hp
->h_attach
= edit_attachments(hp
->h_attach
);
584 /* Add to the CC list */
585 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(
586 lextract(&linebuf
[2], GCC
|GFULL
)));
589 /* Add stuff to blind carbon copies list */
590 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(
591 lextract(&linebuf
[2], GBCC
|GFULL
)));
594 strncpy(linebuf
+ 2, getdeadletter(), linesize
- 2);
595 linebuf
[linesize
-1]='\0';
601 * Search for the file name,
602 * then open it and copy the contents to _coll_fp.
605 while (whitechar(*cp
))
608 fputs(tr(57, "Interpolate what file?\n"),
613 insertcommand(_coll_fp
, cp
+ 1);
616 if ((cp
= file_expand(cp
)) == NULL
)
619 fprintf(stderr
, tr(58, "%s: Directory\n"), cp
);
622 if ((fbuf
= Fopen(cp
, "r")) == NULL
) {
626 printf(tr(59, "\"%s\" "), cp
);
628 if (_include_file(fbuf
, cp
, &lc
, &cc
, FAL0
) != 0)
630 printf(tr(60, "%d/%d\n"), lc
, cc
);
633 /* Insert a variable into the file */
635 while (whitechar(*cp
))
637 if ((cp
= vok_vlook(cp
)) == NULL
|| *cp
== '\0')
639 if (putesc(cp
, _coll_fp
) < 0)
641 if ((options
& OPT_INTERACTIVE
) &&
642 putesc(cp
, stdout
) < 0)
647 /* Insert the contents of a signature variable */
648 cp
= (c
== 'a') ? ok_vlook(sign
) : ok_vlook(Sign
);
649 if (cp
!= NULL
&& *cp
!= '\0') {
650 if (putesc(cp
, _coll_fp
) < 0)
652 if ((options
& OPT_INTERACTIVE
) &&
653 putesc(cp
, stdout
) < 0)
658 /* Write the message on a file */
660 while (blankchar(*cp
))
662 if (*cp
== '\0' || (cp
= file_expand(cp
)) == NULL
) {
663 fputs(tr(61, "Write what file!?\n"), stderr
);
667 if (exwrite(cp
, _coll_fp
, 1) < 0)
677 * Interpolate the named messages, if we
678 * are in receiving mail mode. Does the
679 * standard list processing garbage.
680 * If ~f is given, we don't shift over.
682 if (forward(linebuf
+ 2, _coll_fp
, c
) < 0)
687 * Print out the current state of the
688 * message without altering anything.
690 print_collf(_coll_fp
, hp
);
694 * Pipe message through command.
695 * Collect output as new message.
698 mespipe(&linebuf
[2]);
703 * Edit the current message.
704 * 'e' means to use EDITOR
705 * 'v' means to use VISUAL
708 mesedit(c
, ok_blook(editheaders
) ? hp
: NULL
);
712 * Last the lengthy help string.
713 * (Very ugly, but take care for compiler supported
717 "-------------------- ~ ESCAPES ----------------------------\n"
718 "~~ Quote a single tilde\n"
719 "~@ [file ...] Edit attachment list\n"
720 "~b users Add users to \"blind\" cc list\n"
721 "~c users Add users to cc list\n"
722 "~d Read in dead.letter\n"
723 "~e Edit the message buffer\n"
724 "~f messages Read in messages without indenting lines\n"
725 "~F messages Same as ~f, but keep all header lines\n"
726 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
728 "~r file Read a file into the message buffer\n"
729 "~p Print the message buffer\n"
730 "~q Abort message composition and save text to dead.letter\n"
731 "~m messages Read in messages with each line indented\n"
732 "~M messages Same as ~m, but keep all header lines\n"
733 "~s subject Set subject\n"
734 "~t users Add users to to list\n"
735 "~u messages Same as ~f, but without any headers\n"
736 "~U messages Same as ~m, but without any headers\n"));
738 "~v Invoke display editor on message\n"
739 "~w file Write message onto file\n"
740 "~x Abort message composition and discard text written so far\n"
741 "~!command Invoke the shell\n"
742 "~:command Execute a regular command\n"
743 "-----------------------------------------------------------\n"));
749 if (_coll_fp
!= NULL
) {
750 if ((cp
= ok_vlook(NAIL_TAIL
)) != NULL
) {
751 if (putesc(cp
, _coll_fp
) < 0)
753 if ((options
& OPT_INTERACTIVE
) &&
754 putesc(cp
, stdout
) < 0)
764 sigaddset(&nset
, SIGINT
);
765 sigaddset(&nset
, SIGHUP
);
766 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
767 safe_signal(SIGINT
, _coll_saveint
);
768 safe_signal(SIGHUP
, _coll_savehup
);
769 safe_signal(SIGTSTP
, _coll_savetstp
);
770 safe_signal(SIGTTOU
, _coll_savettou
);
771 safe_signal(SIGTTIN
, _coll_savettin
);
772 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
776 if (tempMail
!= NULL
) {
780 if (_coll_fp
!= NULL
) {
788 * Write a file, ex-like if f set.
791 exwrite(char const *name
, FILE *fp
, int f
)
799 printf("\"%s\" ", name
);
802 if ((of
= Fopen(name
, "a")) == NULL
) {
808 while ((c
= getc(fp
)) != EOF
) {
820 printf(tr(65, "%d/%ld\n"), lc
, cc
);
826 makeheader(FILE *fp
, struct header
*hp
)
832 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
833 perror(tr(66, "temporary mail edit file"));
839 extract_header(fp
, hp
);
840 while ((c
= getc(fp
)) != EOF
) /* XXX bytewise, yuck! */
846 if (check_from_and_sender(hp
->h_from
, hp
->h_sender
))
852 * Edit the message being collected on fp.
853 * On return, make the edit file the new temp file.
856 mesedit(int c
, struct header
*hp
)
858 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
859 bool_t saved
= ok_blook(add_file_recipients
);
862 ok_bset(add_file_recipients
, TRU1
);
863 nf
= run_editor(_coll_fp
, (off_t
)-1, c
, 0, hp
, NULL
, SEND_MBOX
, sigint
);
870 fseek(nf
, 0L, SEEK_END
);
876 ok_bset(add_file_recipients
, saved
);
877 safe_signal(SIGINT
, sigint
);
881 * Pipe the message through the command.
882 * Old message is on stdin of command;
883 * New message collected from stdout.
884 * Sh -c must return 0 to accept the new message.
890 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
894 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
895 perror(tr(66, "temporary mail edit file"));
902 * stdin = current message.
903 * stdout = new message.
905 if ((sh
= ok_vlook(SHELL
)) == NULL
)
907 if (run_command(sh
, 0, fileno(_coll_fp
), fileno(nf
), "-c", cmd
, NULL
)
912 if (fsize(nf
) == 0) {
913 fprintf(stderr
, tr(67, "No bytes from \"%s\" !?\n"), cmd
);
920 fseek(nf
, 0L, SEEK_END
);
924 safe_signal(SIGINT
, sigint
);
928 * Interpolate the named messages into the current
929 * message, preceding each line with a tab.
930 * Return a count of the number of characters now in
931 * the message, or -1 if an error is encountered writing
932 * the message temporary. The flag argument is 'm' if we
933 * should shift over and 'f' if not.
936 forward(char *ms
, FILE *fp
, int f
)
939 struct ignoretab
*ig
;
941 enum sendaction action
;
944 msgvec
= (int *)salloc((msgCount
+1) * sizeof *msgvec
);
947 if (getmsglist(ms
, msgvec
, 0) < 0)
950 *msgvec
= first(0, MMNORM
);
952 fputs(tr(68, "No appropriate messages\n"), stderr
);
958 if (f
== 'f' || f
== 'F' || f
== 'u')
960 else if ((tabst
= ok_vlook(indentprefix
)) == NULL
)
962 if (f
== 'u' || f
== 'U')
965 ig
= upperchar(f
) ? (struct ignoretab
*)NULL
: ignore
;
966 action
= (upperchar(f
) && f
!= 'U') ? SEND_QUOTE_ALL
: SEND_QUOTE
;
968 printf(tr(69, "Interpolating:"));
969 for (; *msgvec
!= 0; msgvec
++) {
970 struct message
*mp
= message
+ *msgvec
- 1;
973 printf(" %d", *msgvec
);
974 if (sendmp(mp
, fp
, ig
, tabst
, action
, NULL
) < 0) {
975 perror(tr(70, "temporary mail file"));
984 * Print (continue) when continued after ^Z.
990 sighandler_type old_action
= safe_signal(s
, SIG_DFL
);
995 sigprocmask(SIG_UNBLOCK
, &nset
, (sigset_t
*)NULL
);
997 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
998 safe_signal(s
, old_action
);
1002 siglongjmp(_coll_jmp
, 1);
1007 * On interrupt, come here to save the partial message in ~/dead.letter.
1008 * Then jump out of the collection loop.
1014 /* the control flow is subtle, because we can be called from ~q */
1015 if (_coll_hadintr
== 0) {
1016 if (ok_blook(ignore
)) {
1023 siglongjmp(_coll_jmp
, 1);
1026 if (ok_blook(save
) && s
!= 0)
1027 savedeadletter(_coll_fp
, 1);
1028 /* Aborting message, no need to fflush() .. */
1029 siglongjmp(_coll_abort
, 1);
1037 savedeadletter(_coll_fp
, 1);
1039 * Let's pretend nobody else wants to clean up,
1040 * a true statement at this time.
1046 savedeadletter(FILE *fp
, int fflush_rewind_first
)
1053 if (fflush_rewind_first
) {
1060 cp
= getdeadletter();
1062 dbuf
= Fopen(cp
, "a");
1068 * There are problems with dup()ing of file-descriptors for child
1069 * processes. As long as those are not fixed in equal spirit to
1070 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1071 * bugs like (If *record* is set, avoid writing dead content twice..,
1072 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1073 * makes itself comfortable with the *real* offset of the underlaying
1074 * file descriptor. Unfortunately Standard I/O and POSIX don't
1075 * describe a way for that -- fflush();rewind(); won't do it.
1076 * This fseek(END),rewind() pair works around the problem on *BSD.
1078 (void)fseek(fp
, 0, SEEK_END
);
1081 printf("\"%s\" ", cp
);
1082 for (lines
= bytes
= 0; (c
= getc(fp
)) != EOF
; ++bytes
) {
1087 printf("%lu/%lu\n", lines
, bytes
);
1095 putesc(const char *s
, FILE *stream
)
1102 if (putc('\t', stream
) == EOF
)
1109 if (putc('\n', stream
) == EOF
)
1116 if (putc(s
[0], stream
) == EOF
)
1121 if (putc('\n', stream
) == EOF
)