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>.
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
);
107 while ((ap
= ap
->a_flink
) != NULL
);
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
);
118 _include_file(FILE *fbuf
, char const *name
, int *linecount
, int *charcount
,
122 char *linebuf
= NULL
;
123 size_t linesize
= 0, linelen
, cnt
;
126 if ((fbuf
= Fopen(name
, "r")) == NULL
) {
133 *linecount
= *charcount
= 0;
135 while (fgetline(&linebuf
, &linesize
, &cnt
, &linelen
, fbuf
, 0)
137 if (fwrite(linebuf
, sizeof *linebuf
, linelen
, _coll_fp
)
140 if ((options
& OPT_INTERACTIVE
) && doecho
)
141 fwrite(linebuf
, sizeof *linebuf
, linelen
, stdout
);
143 (*charcount
) += linelen
;
145 if (fflush(_coll_fp
))
159 _collect_onpipe(int signo
)
162 siglongjmp(_coll_pipejmp
, 1);
166 * Execute cmd and insert its standard output into fp.
169 insertcommand(FILE *fp
, char const *cmd
)
175 cp
= ok_vlook(SHELL
);
178 if ((ibuf
= Popen(cmd
, "r", cp
, 0)) != NULL
) {
179 while ((c
= getc(ibuf
)) != EOF
) /* XXX bytewise, yuck! */
190 print_collf(FILE *cf
, struct header
*hp
)
193 FILE *volatile obuf
= stdout
;
194 struct attachment
*ap
;
197 size_t linecnt
, maxlines
, linesize
= 0, linelen
, cnt
, cnt2
;
201 cnt
= cnt2
= fsize(cf
);
203 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
) {
205 fgetline(&lbuf
, &linesize
, &cnt2
, NULL
, cf
, 0);
208 maxlines
= (*cp
== '\0' ? screensize() : atoi(cp
));
220 maxlines
-= myaddrs(hp
) != NULL
|| hp
->h_from
!= NULL
;
221 maxlines
-= ok_vlook(ORGANIZATION
) != NULL
||
222 hp
->h_organization
!= NULL
;
223 maxlines
-= ok_vlook(replyto
) != NULL
|| hp
->h_replyto
!= NULL
;
224 maxlines
-= ok_vlook(sender
) != NULL
|| hp
->h_sender
!= NULL
;
225 if ((long)maxlines
< 0 || linecnt
> maxlines
) {
227 if (sigsetjmp(_coll_pipejmp
, 1))
229 obuf
= Popen(cp
, "w", NULL
, 1);
234 safe_signal(SIGPIPE
, &_collect_onpipe
);
238 fprintf(obuf
, tr(62, "-------\nMessage contains:\n"));
239 gf
= GIDENT
|GTO
|GSUBJECT
|GCC
|GBCC
|GNL
|GFILES
;
240 if (ok_blook(fullnames
))
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
) {
249 fprintf(obuf
, " - message %u\n", ap
->a_msgno
);
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
&&
258 cs
= ap
->a_input_charset
)
260 cs
= charset_get_lc();
261 if ((cp
= ap
->a_content_type
) == NULL
)
263 else if (ascncasecmp(cp
, "text/", 5) != 0)
266 fprintf(obuf
, " - [%s, %s%s] %s\n",
267 cp
, csi
, cs
, ap
->a_name
);
272 if (obuf
!= stdout
) {
273 safe_signal(SIGPIPE
, SIG_IGN
);
275 safe_signal(SIGPIPE
, dflpipe
);
282 collect(struct header
*hp
, int printheaders
, struct message
*mp
,
283 char *quotefile
, int doprefix
)
286 struct ignoretab
*quoteig
;
287 int lc
, cc
, eofcount
, c
, t
;
288 int volatile escape
, getfields
;
289 char *linebuf
= NULL
, *quote
= NULL
, *tempMail
= NULL
;
293 enum sendaction action
;
295 sighandler_type savedtop
;
299 * Start catching signals from here, but we're still die on interrupts
300 * until we're in the main loop.
303 sigaddset(&nset
, SIGINT
);
304 sigaddset(&nset
, SIGHUP
);
305 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
306 handlerpush(collint
);
307 if ((_coll_saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
308 safe_signal(SIGINT
, collint
);
309 if ((_coll_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 command line editor(s); try to merge this */
313 _coll_savetstp
= safe_signal(SIGTSTP
, collstop
);
314 _coll_savettou
= safe_signal(SIGTTOU
, collstop
);
315 _coll_savettin
= safe_signal(SIGTTIN
, collstop
);
316 if (sigsetjmp(_coll_abort
, 1))
318 if (sigsetjmp(_coll_jmp
, 1))
320 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
323 if ((_coll_fp
= Ftemp(&tempMail
, "Rs", "w+", 0600, 1)) == NULL
) {
324 perror(tr(51, "temporary mail file"));
330 if ((cp
= ok_vlook(NAIL_HEAD
)) != NULL
&& putesc(cp
, _coll_fp
) < 0)
334 * If we are going to prompt for a subject,
335 * refrain from printing a newline after
336 * the headers (since some people mind).
339 if (! (options
& OPT_t_FLAG
)) {
340 t
= GTO
|GSUBJECT
|GCC
|GNL
;
341 if (ok_blook(fullnames
))
343 if (hp
->h_subject
== NULL
&& (options
& OPT_INTERACTIVE
) &&
344 (ok_blook(ask
) || ok_blook(asksub
)))
345 t
&= ~GNL
, getfields
|= GSUBJECT
;
346 if (hp
->h_to
== NULL
&& (options
& OPT_INTERACTIVE
))
347 t
&= ~GNL
, getfields
|= GTO
;
348 if (!ok_blook(bsdcompat
) && !ok_blook(askatend
) &&
349 (options
& OPT_INTERACTIVE
)) {
350 if (hp
->h_bcc
== NULL
&& ok_blook(askbcc
))
351 t
&= ~GNL
, getfields
|= GBCC
;
352 if (hp
->h_cc
== NULL
&& ok_blook(askcc
))
353 t
&= ~GNL
, getfields
|= GCC
;
356 (void)puthead(hp
, stdout
, t
, SEND_TODISP
, CONV_NONE
,
358 (void)fflush(stdout
);
363 * Quote an original message
365 if (mp
!= NULL
&& (doprefix
|| (quote
= ok_vlook(quote
)) != NULL
)) {
370 if ((cp
= ok_vlook(fwdheading
)) == NULL
)
371 cp
= "-------- Original Message --------";
372 if (*cp
&& fprintf(_coll_fp
, "%s\n", cp
) < 0)
374 } else if (strcmp(quote
, "noheading") == 0) {
376 } else if (strcmp(quote
, "headers") == 0) {
378 } else if (strcmp(quote
, "allheaders") == 0) {
380 action
= SEND_QUOTE_ALL
;
382 cp
= hfield1("from", mp
);
383 if (cp
!= NULL
&& (cnt
= (long)strlen(cp
)) > 0) {
384 if (xmime_write(cp
, cnt
, _coll_fp
,
385 CONV_FROMHDR
, TD_NONE
,
388 if (fprintf(_coll_fp
,
389 tr(52, " wrote:\n\n")) < 0)
393 if (fflush(_coll_fp
))
395 cp
= ok_vlook(indentprefix
);
396 if (cp
!= NULL
&& *cp
== '\0')
398 if (sendmp(mp
, _coll_fp
, quoteig
, (doprefix
? NULL
: cp
),
403 /* Print what we have sofar also on the terminal */
405 while ((c
= getc(_coll_fp
)) != EOF
) /* XXX bytewise, yuck! */
406 (void)putc(c
, stdout
);
407 if (fseek(_coll_fp
, 0, SEEK_END
))
410 escape
= ((cp
= ok_vlook(escape
)) != NULL
) ? *cp
: ESCAPE
;
414 if (!sigsetjmp(_coll_jmp
, 1)) {
416 grab_headers(hp
, getfields
, 1);
417 if (quotefile
!= NULL
) {
418 if (_include_file(NULL
, quotefile
, &lc
, &cc
, TRU1
) != 0)
421 if ((options
& OPT_INTERACTIVE
) && ok_blook(editalong
)) {
428 * Come here for printing the after-signal message.
429 * Duplicate messages won't be printed because
430 * the write is aborted if we get a SIGTTOU.
434 (void)fprintf(stderr
, tr(53,
435 "\n(Interrupt -- one more to kill letter)\n"));
437 printf(tr(54, "(continue)\n"));
438 (void)fflush(stdout
);
443 * No tilde escapes, interrupts not expected. Simply copy STDIN
445 if (! (options
& (OPT_INTERACTIVE
| OPT_t_FLAG
|OPT_TILDE_FLAG
))) {
446 linebuf
= srealloc(linebuf
, linesize
= LINESIZE
);
447 while ((cnt
= fread(linebuf
, sizeof *linebuf
,
448 linesize
, stdin
)) > 0) {
449 if ((size_t)cnt
!= fwrite(linebuf
, sizeof *linebuf
,
453 if (fflush(_coll_fp
))
459 * The interactive collect loop
463 cnt
= readline_input(LNED_NONE
, "", &linebuf
, &linesize
);
467 if ((options
& OPT_INTERACTIVE
) &&
468 ok_blook(ignoreeof
) && ++eofcount
< 25) {
470 "Use \".\" to terminate letter\n"));
475 if ((options
& OPT_t_FLAG
) && cnt
== 0) {
477 if (makeheader(_coll_fp
, hp
) != OKAY
)
480 options
&= ~OPT_t_FLAG
;
486 if (linebuf
[0] == '.' && linebuf
[1] == '\0' &&
487 (options
& (OPT_INTERACTIVE
|OPT_TILDE_FLAG
)) &&
488 (ok_blook(dot
) || ok_blook(ignoreeof
)))
490 if (cnt
== 0 || linebuf
[0] != escape
|| ! (options
&
491 (OPT_INTERACTIVE
| OPT_TILDE_FLAG
))) {
492 /* TODO calls putline(), which *always* appends LF;
493 * TODO thus, STDIN with -t will ALWAYS end with LF,
494 * TODO even if no trailing LF and QP CTE */
495 if (putline(_coll_fp
, linebuf
, cnt
) < 0)
504 * On double escape, just send the single one.
505 * Otherwise, it's an error.
508 if (putline(_coll_fp
, &linebuf
[1], cnt
- 1) < 0)
513 fputs(tr(56, "Unknown tilde escape.\n"), stderr
);
522 /* Shell escape, send the balance of line to sh -c */
528 /* Escape to command mode, but be nice! */
529 _execute_command(hp
, linebuf
+ 2, cnt
- 2);
532 /* Simulate end of file on input */
535 /* Same as 'q', but no dead.letter saving */
538 /* Force a quit, act like an interrupt had happened */
540 collint((c
== 'x') ? 0 : SIGINT
);
544 /* Grab a bunch of headers */
546 grab_headers(hp
, GTO
|GSUBJECT
|GCC
|GBCC
,
547 (ok_blook(bsdcompat
) &&
548 ok_blook(bsdorder
)));
549 while (hp
->h_to
== NULL
);
552 /* Grab extra headers */
554 grab_headers(hp
, GEXTRA
, 0);
555 while (check_from_and_sender(hp
->h_from
, hp
->h_sender
));
558 /* Add to the To list */
559 while ((hp
->h_to
= cat(hp
->h_to
, checkaddrs(
560 lextract(&linebuf
[2], GTO
|GFULL
))))
565 /* Set the Subject list */
567 while (whitechar(*cp
))
569 hp
->h_subject
= savestr(cp
);
577 /* Edit the attachment list */
578 if (linebuf
[2] != '\0')
579 hp
->h_attach
= append_attachments(hp
->h_attach
,
582 hp
->h_attach
= edit_attachments(hp
->h_attach
);
585 /* Add to the CC list */
586 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(
587 lextract(&linebuf
[2], GCC
|GFULL
)));
590 /* Add stuff to blind carbon copies list */
591 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(
592 lextract(&linebuf
[2], GBCC
|GFULL
)));
595 strncpy(linebuf
+ 2, getdeadletter(), linesize
- 2);
596 linebuf
[linesize
-1]='\0';
602 * Search for the file name,
603 * then open it and copy the contents to _coll_fp.
606 while (whitechar(*cp
))
609 fputs(tr(57, "Interpolate what file?\n"),
614 insertcommand(_coll_fp
, cp
+ 1);
617 if ((cp
= file_expand(cp
)) == NULL
)
620 fprintf(stderr
, tr(58, "%s: Directory\n"), cp
);
623 if ((fbuf
= Fopen(cp
, "r")) == NULL
) {
627 printf(tr(59, "\"%s\" "), cp
);
629 if (_include_file(fbuf
, cp
, &lc
, &cc
, FAL0
) != 0)
631 printf(tr(60, "%d/%d\n"), lc
, cc
);
634 /* Insert a variable into the file */
636 while (whitechar(*cp
))
638 if ((cp
= vok_vlook(cp
)) == NULL
|| *cp
== '\0')
640 if (putesc(cp
, _coll_fp
) < 0)
642 if ((options
& OPT_INTERACTIVE
) &&
643 putesc(cp
, stdout
) < 0)
648 /* Insert the contents of a signature variable */
649 cp
= (c
== 'a') ? ok_vlook(sign
) : ok_vlook(Sign
);
650 if (cp
!= NULL
&& *cp
!= '\0') {
651 if (putesc(cp
, _coll_fp
) < 0)
653 if ((options
& OPT_INTERACTIVE
) &&
654 putesc(cp
, stdout
) < 0)
659 /* Write the message on a file */
661 while (blankchar(*cp
))
663 if (*cp
== '\0' || (cp
= file_expand(cp
)) == NULL
) {
664 fputs(tr(61, "Write what file!?\n"), stderr
);
668 if (exwrite(cp
, _coll_fp
, 1) < 0)
676 * Interpolate the named messages, if we
677 * are in receiving mail mode. Does the
678 * standard list processing garbage.
679 * If ~f is given, we don't shift over.
681 if (forward(linebuf
+ 2, _coll_fp
, c
) < 0)
686 * Print out the current state of the
687 * message without altering anything.
689 print_collf(_coll_fp
, hp
);
693 * Pipe message through command.
694 * Collect output as new message.
697 mespipe(&linebuf
[2]);
702 * Edit the current message.
703 * 'e' means to use EDITOR
704 * 'v' means to use VISUAL
707 mesedit(c
, ok_blook(editheaders
) ? hp
: NULL
);
711 * Last the lengthy help string.
712 * (Very ugly, but take care for compiler supported
716 "-------------------- ~ ESCAPES ----------------------------\n"
717 "~~ Quote a single tilde\n"
718 "~@ [file ...] Edit attachment list\n"
719 "~b users Add users to \"blind\" cc list\n"
720 "~c users Add users to cc list\n"
721 "~d Read in dead.letter\n"
722 "~e Edit the message buffer\n"
723 "~f messages Read in messages without indenting lines\n"
724 "~F messages Same as ~f, but keep all header lines\n"
725 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
727 "~r file Read a file into the message buffer\n"
728 "~p Print the message buffer\n"
729 "~q Abort message composition and save text to dead.letter\n"
730 "~m messages Read in messages with each line indented\n"
731 "~M messages Same as ~m, but keep all header lines\n"
732 "~s subject Set subject\n"
733 "~t users Add users to to list\n"
734 "~v Invoke display editor on message\n"
735 "~w file Write message onto file\n"
736 "~x Abort message composition and discard text written so far\n"));
738 "~!command Invoke the shell\n"
739 "~:command Execute a regular command\n"
740 "-----------------------------------------------------------\n"));
746 if (_coll_fp
!= NULL
) {
747 if ((cp
= ok_vlook(NAIL_TAIL
)) != NULL
) {
748 if (putesc(cp
, _coll_fp
) < 0)
750 if ((options
& OPT_INTERACTIVE
) &&
751 putesc(cp
, stdout
) < 0)
761 sigaddset(&nset
, SIGINT
);
762 sigaddset(&nset
, SIGHUP
);
763 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
764 safe_signal(SIGINT
, _coll_saveint
);
765 safe_signal(SIGHUP
, _coll_savehup
);
766 safe_signal(SIGTSTP
, _coll_savetstp
);
767 safe_signal(SIGTTOU
, _coll_savettou
);
768 safe_signal(SIGTTIN
, _coll_savettin
);
769 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
773 if (tempMail
!= NULL
) {
777 if (_coll_fp
!= NULL
) {
785 * Write a file, ex-like if f set.
788 exwrite(char const *name
, FILE *fp
, int f
)
796 printf("\"%s\" ", name
);
799 if ((of
= Fopen(name
, "a")) == NULL
) {
805 while ((c
= getc(fp
)) != EOF
) {
817 printf(tr(65, "%d/%ld\n"), lc
, cc
);
823 makeheader(FILE *fp
, struct header
*hp
)
829 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
830 perror(tr(66, "temporary mail edit file"));
836 extract_header(fp
, hp
);
837 while ((c
= getc(fp
)) != EOF
) /* XXX bytewise, yuck! */
843 if (check_from_and_sender(hp
->h_from
, hp
->h_sender
))
849 * Edit the message being collected on fp.
850 * On return, make the edit file the new temp file.
853 mesedit(int c
, struct header
*hp
)
855 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
856 bool_t saved
= ok_blook(add_file_recipients
);
859 ok_bset(add_file_recipients
, FAL0
);
860 nf
= run_editor(_coll_fp
, (off_t
)-1, c
, 0, hp
, NULL
, SEND_MBOX
, sigint
);
867 fseek(nf
, 0L, SEEK_END
);
873 ok_bset(add_file_recipients
, saved
);
874 safe_signal(SIGINT
, sigint
);
878 * Pipe the message through the command.
879 * Old message is on stdin of command;
880 * New message collected from stdout.
881 * Sh -c must return 0 to accept the new message.
887 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
891 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
892 perror(tr(66, "temporary mail edit file"));
899 * stdin = current message.
900 * stdout = new message.
902 if ((sh
= ok_vlook(SHELL
)) == NULL
)
904 if (run_command(sh
, 0, fileno(_coll_fp
), fileno(nf
), "-c", cmd
, NULL
)
909 if (fsize(nf
) == 0) {
910 fprintf(stderr
, tr(67, "No bytes from \"%s\" !?\n"), cmd
);
917 fseek(nf
, 0L, SEEK_END
);
921 safe_signal(SIGINT
, sigint
);
925 * Interpolate the named messages into the current
926 * message, preceding each line with a tab.
927 * Return a count of the number of characters now in
928 * the message, or -1 if an error is encountered writing
929 * the message temporary. The flag argument is 'm' if we
930 * should shift over and 'f' if not.
933 forward(char *ms
, FILE *fp
, int f
)
936 struct ignoretab
*ig
;
938 enum sendaction action
;
941 msgvec
= (int *)salloc((msgCount
+1) * sizeof *msgvec
);
944 if (getmsglist(ms
, msgvec
, 0) < 0)
947 *msgvec
= first(0, MMNORM
);
949 fputs(tr(68, "No appropriate messages\n"), stderr
);
954 if (f
== 'f' || f
== 'F')
956 else if ((tabst
= ok_vlook(indentprefix
)) == NULL
)
958 ig
= upperchar(f
) ? (struct ignoretab
*)NULL
: ignore
;
959 action
= upperchar(f
) ? SEND_QUOTE_ALL
: SEND_QUOTE
;
960 printf(tr(69, "Interpolating:"));
961 for (; *msgvec
!= 0; msgvec
++) {
962 struct message
*mp
= message
+ *msgvec
- 1;
965 printf(" %d", *msgvec
);
966 if (sendmp(mp
, fp
, ig
, tabst
, action
, NULL
) < 0) {
967 perror(tr(70, "temporary mail file"));
976 * Print (continue) when continued after ^Z.
982 sighandler_type old_action
= safe_signal(s
, SIG_DFL
);
987 sigprocmask(SIG_UNBLOCK
, &nset
, (sigset_t
*)NULL
);
989 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
990 safe_signal(s
, old_action
);
994 siglongjmp(_coll_jmp
, 1);
999 * On interrupt, come here to save the partial message in ~/dead.letter.
1000 * Then jump out of the collection loop.
1006 /* the control flow is subtle, because we can be called from ~q */
1007 if (_coll_hadintr
== 0) {
1008 if (ok_blook(ignore
)) {
1015 siglongjmp(_coll_jmp
, 1);
1018 if (ok_blook(save
) && s
!= 0)
1019 savedeadletter(_coll_fp
, 1);
1020 /* Aborting message, no need to fflush() .. */
1021 siglongjmp(_coll_abort
, 1);
1029 savedeadletter(_coll_fp
, 1);
1031 * Let's pretend nobody else wants to clean up,
1032 * a true statement at this time.
1038 savedeadletter(FILE *fp
, int fflush_rewind_first
)
1045 if (fflush_rewind_first
) {
1052 cp
= getdeadletter();
1054 dbuf
= Fopen(cp
, "a");
1060 * There are problems with dup()ing of file-descriptors for child
1061 * processes. As long as those are not fixed in equal spirit to
1062 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1063 * bugs like (If *record* is set, avoid writing dead content twice..,
1064 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1065 * makes itself comfortable with the *real* offset of the underlaying
1066 * file descriptor. Unfortunately Standard I/O and POSIX don't
1067 * describe a way for that -- fflush();rewind(); won't do it.
1068 * This fseek(END),rewind() pair works around the problem on *BSD.
1070 (void)fseek(fp
, 0, SEEK_END
);
1073 printf("\"%s\" ", cp
);
1074 for (lines
= bytes
= 0; (c
= getc(fp
)) != EOF
; ++bytes
) {
1079 printf("%lu/%lu\n", lines
, bytes
);
1087 putesc(const char *s
, FILE *stream
)
1094 if (putc('\t', stream
) == EOF
)
1101 if (putc('\n', stream
) == EOF
)
1108 if (putc(s
[0], stream
) == EOF
)
1113 if (putc('\n', stream
) == EOF
)