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
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
,
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
);
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 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
, collf
)
140 if ((options
& OPT_INTERACTIVE
) && doecho
)
141 fwrite(linebuf
, sizeof *linebuf
, linelen
, stdout
);
143 (*charcount
) += linelen
;
162 siglongjmp(pipejmp
, 1);
166 * Execute cmd and insert its standard output into fp.
169 insertcommand(FILE *fp
, char const *cmd
)
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
= voption("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
-= 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
) {
227 if (sigsetjmp(pipejmp
, 1))
229 obuf
= Popen(cp
, "w", NULL
, 1);
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"))
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 ((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))
318 if (sigsetjmp(colljmp
, 1))
320 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
323 if ((collf
= Ftemp(&tempMail
, "Rs", "w+", 0600, 1)) == NULL
) {
324 perror(tr(51, "temporary mail file"));
330 if ((cp
= value("NAIL_HEAD")) != NULL
&& putesc(cp
, collf
) < 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 (value("fullnames"))
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
;
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
= value("quote")) != NULL
)) {
370 if ((cp
= value("fwdheading")) == NULL
)
371 cp
= "-------- Original Message --------";
372 if (*cp
&& fprintf(collf
, "%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
,
385 collf
, CONV_FROMHDR
, TD_NONE
,
388 if (fprintf(collf
, tr(52, " wrote:\n\n")) < 0)
394 cp
= value("indentprefix");
395 if (cp
!= NULL
&& *cp
== '\0')
397 if (sendmp(mp
, collf
, quoteig
, (doprefix
? NULL
: cp
), action
,
402 /* Print what we have sofar also on the terminal */
404 while ((c
= getc(collf
)) != EOF
) /* XXX bytewise, yuck! */
405 (void)putc(c
, stdout
);
406 if (fseek(collf
, 0, SEEK_END
))
409 escape
= ((cp
= value("escape")) != NULL
) ? *cp
: ESCAPE
;
413 if (! sigsetjmp(colljmp
, 1)) {
415 grab_headers(hp
, getfields
, 1);
416 if (quotefile
!= NULL
) {
417 if (_include_file(NULL
, quotefile
, &lc
, &cc
, TRU1
) != 0)
420 if ((options
& OPT_INTERACTIVE
) && value("editalong")) {
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.
433 (void)fprintf(stderr
, tr(53,
434 "\n(Interrupt -- one more to kill letter)\n"));
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
,
458 * The interactive collect loop
462 cnt
= readline_input(LNED_NONE
, "", &linebuf
, &linesize
);
466 if ((options
& OPT_INTERACTIVE
) &&
467 value("ignoreeof") != NULL
&& ++eofcount
< 25) {
469 "Use \".\" to terminate letter\n"));
474 if ((options
& OPT_t_FLAG
) && cnt
== 0) {
476 if (makeheader(collf
, hp
) != OKAY
)
479 options
&= ~OPT_t_FLAG
;
485 if (linebuf
[0] == '.' && linebuf
[1] == '\0' &&
486 (options
& (OPT_INTERACTIVE
|OPT_TILDE_FLAG
)) &&
487 (boption("dot") || boption("ignoreeof")))
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)
503 * On double escape, just send the single one.
504 * Otherwise, it's an error.
507 if (putline(collf
, &linebuf
[1], cnt
- 1) < 0)
512 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 (value("bsdcompat") != NULL
&&
547 value("bsdorder") != NULL
));
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 collf.
605 while (whitechar(*cp
))
608 fputs(tr(57, "Interpolate what file?\n"),
613 insertcommand(collf
, 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 an environment variable into the file */
635 while (whitechar(*cp
))
637 if ((cp
= value(cp
)) == NULL
|| *cp
== '\0')
639 if (putesc(cp
, collf
) < 0)
641 if ((options
& OPT_INTERACTIVE
) &&
642 putesc(cp
, stdout
) < 0)
647 /* Insert the contents of a signature variable */
648 if ((cp
= value(c
== 'a' ? "sign" : "Sign")) != NULL
&&
650 if (putesc(cp
, collf
) < 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
, collf
, 1) < 0)
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)
685 * Print out the current state of the
686 * message without altering anything.
688 print_collf(collf
, hp
);
692 * Pipe message through command.
693 * Collect output as new message.
696 mespipe(&linebuf
[2]);
701 * Edit the current message.
702 * 'e' means to use EDITOR
703 * 'v' means to use VISUAL
706 mesedit(c
, value("editheaders") ? hp
: NULL
);
710 * Last the lengthy help string.
711 * (Very ugly, but take care for compiler supported
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"));
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"));
737 "~!command Invoke the shell\n"
738 "~:command Execute a regular command\n"
739 "-----------------------------------------------------------\n"));
746 if ((cp
= value("NAIL_TAIL")) != NULL
) {
747 if (putesc(cp
, collf
) < 0)
749 if ((options
& OPT_INTERACTIVE
) &&
750 putesc(cp
, stdout
) < 0)
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
);
772 if (tempMail
!= NULL
) {
784 * Write a file, ex-like if f set.
787 exwrite(char const *name
, FILE *fp
, int f
)
795 printf("\"%s\" ", name
);
798 if ((of
= Fopen(name
, "a")) == NULL
) {
804 while ((c
= getc(fp
)) != EOF
) {
816 printf(tr(65, "%d/%ld\n"), lc
, cc
);
822 makeheader(FILE *fp
, struct header
*hp
)
828 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
829 perror(tr(66, "temporary mail edit file"));
835 extract_header(fp
, hp
);
836 while ((c
= getc(fp
)) != EOF
) /* XXX bytewise, yuck! */
842 if (check_from_and_sender(hp
->h_from
, hp
->h_sender
))
848 * Edit the message being collected on fp.
849 * On return, make the edit file the new temp file.
852 mesedit(int c
, struct header
*hp
)
854 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
855 char *saved
= value("add-file-recipients");
858 assign("add-file-recipients", "");
859 nf
= run_editor(collf
, (off_t
)-1, c
, 0, hp
, NULL
, SEND_MBOX
, sigint
);
866 fseek(nf
, 0L, SEEK_END
);
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.
886 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
890 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
891 perror(tr(66, "temporary mail edit file"));
898 * stdin = current message.
899 * stdout = new message.
901 if ((sh
= value("SHELL")) == NULL
)
904 0, fileno(collf
), fileno(nf
), "-c", cmd
, NULL
) < 0) {
908 if (fsize(nf
) == 0) {
909 fprintf(stderr
, tr(67, "No bytes from \"%s\" !?\n"), cmd
);
916 fseek(nf
, 0L, SEEK_END
);
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.
932 forward(char *ms
, FILE *fp
, int f
)
935 struct ignoretab
*ig
;
937 enum sendaction action
;
940 msgvec
= (int *)salloc((msgCount
+1) * sizeof *msgvec
);
943 if (getmsglist(ms
, msgvec
, 0) < 0)
946 *msgvec
= first(0, MMNORM
);
948 fputs(tr(68, "No appropriate messages\n"), stderr
);
953 if (f
== 'f' || f
== 'F')
955 else if ((tabst
= value("indentprefix")) == NULL
)
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;
964 printf(" %d", *msgvec
);
965 if (sendmp(mp
, fp
, ig
, tabst
, action
, NULL
) < 0) {
966 perror(tr(70, "temporary mail file"));
975 * Print (continue) when continued after ^Z.
981 sighandler_type old_action
= safe_signal(s
, SIG_DFL
);
986 sigprocmask(SIG_UNBLOCK
, &nset
, (sigset_t
*)NULL
);
988 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
989 safe_signal(s
, old_action
);
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.
1006 * the control flow is subtle, because we can be called from ~q.
1009 if (value("ignore") != NULL
) {
1016 siglongjmp(colljmp
, 1);
1019 if (value("save") != NULL
&& s
!= 0)
1020 savedeadletter(collf
, 1);
1021 /* Aborting message, no need to fflush() .. */
1022 siglongjmp(collabort
, 1);
1030 savedeadletter(collf
, 1);
1032 * Let's pretend nobody else wants to clean up,
1033 * a true statement at this time.
1039 savedeadletter(FILE *fp
, int fflush_rewind_first
)
1046 if (fflush_rewind_first
) {
1053 cp
= getdeadletter();
1055 dbuf
= Fopen(cp
, "a");
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
);
1074 printf("\"%s\" ", cp
);
1075 for (lines
= bytes
= 0; (c
= getc(fp
)) != EOF
; ++bytes
) {
1080 printf("%lu/%lu\n", lines
, bytes
);
1088 putesc(const char *s
, FILE *stream
)
1095 if (putc('\t', stream
) == EOF
)
1102 if (putc('\n', stream
) == EOF
)
1109 if (putc(s
[0], stream
) == EOF
)
1114 if (putc('\n', stream
) == EOF
)