README: nits
[s-mailx.git] / collect.c
blobcb8aa24e6e0d269912e8aeda742f1c6d515fe738
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 "rcv.h"
42 #include <sys/stat.h>
43 #include <unistd.h>
45 #include "extern.h"
48 * The following hokiness with global variables is so that on
49 * receipt of an interrupt signal, the partial message can be salted
50 * away on dead.letter.
53 static sighandler_type saveint; /* Previous SIGINT value */
54 static sighandler_type savehup; /* Previous SIGHUP value */
55 static sighandler_type savetstp; /* Previous SIGTSTP value */
56 static sighandler_type savettou; /* Previous SIGTTOU value */
57 static sighandler_type savettin; /* Previous SIGTTIN value */
58 static FILE *collf; /* File for saving away */
59 static int hadintr; /* Have seen one SIGINT so far */
61 static sigjmp_buf colljmp; /* To get back to work */
62 static int colljmp_p; /* whether to long jump */
63 static sigjmp_buf collabort; /* To end collection with error */
65 static sigjmp_buf pipejmp; /* On broken pipe */
67 /* If *interactive* is set and *echo* is, too, also dump to *stdout* */
68 static int _include_file(FILE *fbuf, char const *name, int *linecount,
69 int *charcount, int echo);
71 static void onpipe(int signo);
72 static void insertcommand(FILE *fp, char const *cmd);
73 static void print_collf(FILE *collf, struct header *hp);
74 static int exwrite(char const *name, FILE *fp, int f);
75 static enum okay makeheader(FILE *fp, struct header *hp);
76 static void mesedit(int c, struct header *hp);
77 static void mespipe(char *cmd);
78 static int forward(char *ms, FILE *fp, int f);
79 static void collstop(int s);
80 static void collint(int s);
81 static void collhup(int s);
82 static int putesc(const char *s, FILE *stream);
84 static int
85 _include_file(FILE *fbuf, char const *name, int *linecount, int *charcount,
86 int echo)
88 int ret = -1;
89 char *linebuf = NULL;
90 size_t linesize = 0, linelen, count;
92 if (fbuf == NULL) {
93 if ((fbuf = Fopen(name, "r")) == NULL) {
94 perror(name);
95 goto jleave;
97 } else
98 fflush_rewind(fbuf);
100 *linecount = *charcount = 0;
101 count = fsize(fbuf);
102 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
103 != NULL) {
104 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
105 != linelen)
106 goto jleave;
107 if ((options & OPT_INTERACTIVE) && echo)
108 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
109 ++(*linecount);
110 (*charcount) += linelen;
112 if (fflush(collf))
113 goto jleave;
115 ret = 0;
116 jleave:
117 if (linebuf != NULL)
118 free(linebuf);
119 if (fbuf != NULL)
120 Fclose(fbuf);
121 return (ret);
124 /*ARGSUSED*/
125 static void
126 onpipe(int signo)
128 (void)signo;
129 siglongjmp(pipejmp, 1);
133 * Execute cmd and insert its standard output into fp.
135 static void
136 insertcommand(FILE *fp, char const *cmd)
138 FILE *ibuf = NULL;
139 char const *cp;
140 int c;
142 cp = value("SHELL");
143 if (cp == NULL)
144 cp = SHELL;
145 if ((ibuf = Popen(cmd, "r", cp, 0)) != NULL) {
146 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
147 putc(c, fp);
148 Pclose(ibuf, TRU1);
149 } else
150 perror(cmd);
154 * ~p command.
156 static void
157 print_collf(FILE *collf, struct header *hp)
159 char *lbuf = NULL;
160 FILE *volatile obuf = stdout;
161 struct attachment *ap;
162 char const *cp;
163 enum gfield gf;
164 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
166 fflush(collf);
167 rewind(collf);
168 count = count2 = fsize(collf);
170 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL) {
171 for (linecnt = 0;
172 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
173 linecnt++);
174 rewind(collf);
175 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
176 maxlines -= 4;
177 if (hp->h_to)
178 maxlines--;
179 if (hp->h_subject)
180 maxlines--;
181 if (hp->h_cc)
182 maxlines--;
183 if (hp->h_bcc)
184 maxlines--;
185 if (hp->h_attach)
186 maxlines--;
187 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
188 maxlines -= value("ORGANIZATION") != NULL ||
189 hp->h_organization != NULL;
190 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
191 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
192 if ((long)maxlines < 0 || linecnt > maxlines) {
193 cp = get_pager();
194 if (sigsetjmp(pipejmp, 1))
195 goto endpipe;
196 obuf = Popen(cp, "w", NULL, 1);
197 if (obuf == NULL) {
198 perror(cp);
199 obuf = stdout;
200 } else
201 safe_signal(SIGPIPE, onpipe);
205 fprintf(obuf, tr(62, "-------\nMessage contains:\n"));
206 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
207 if (value("fullnames"))
208 gf |= GCOMMA;
209 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
210 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
211 prout(lbuf, linelen, obuf);
212 if (hp->h_attach != NULL) {
213 fputs(tr(63, "-------\nAttachments:\n"), obuf);
214 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
215 if (ap->a_msgno)
216 fprintf(obuf, " - message %u\n", ap->a_msgno);
217 else {
218 /* TODO after MIME/send layer rewrite we *know*
219 * TODO the details of the attachment here,
220 * TODO so adjust this again, then */
221 char const *cs, *csi = "-> ";
223 if ((cs = ap->a_charset) == NULL &&
224 (csi = "<- ",
225 cs = ap->a_input_charset)
226 == NULL)
227 cs = charset_get_lc();
228 if ((cp = ap->a_content_type) == NULL)
229 cp = "?";
230 else if (ascncasecmp(cp, "text/", 5) != 0)
231 csi = "";
233 fprintf(obuf, " - [%s, %s%s] %s\n",
234 cp, csi, cs, ap->a_name);
238 endpipe:
239 if (obuf != stdout) {
240 safe_signal(SIGPIPE, SIG_IGN);
241 Pclose(obuf, TRU1);
242 safe_signal(SIGPIPE, dflpipe);
244 if (lbuf)
245 free(lbuf);
248 FILE *
249 collect(struct header *hp, int printheaders, struct message *mp,
250 char *quotefile, int doprefix)
252 FILE *fbuf;
253 struct ignoretab *quoteig;
254 int lc, cc, eofcount, c, t;
255 int volatile escape, getfields;
256 char *linebuf = NULL, *quote = NULL, *tempMail = NULL;
257 char const *cp;
258 size_t linesize = 0;
259 long count;
260 enum sendaction action;
261 sigset_t oset, nset;
262 sighandler_type savedtop;
264 collf = NULL;
266 * Start catching signals from here, but we're still die on interrupts
267 * until we're in the main loop.
269 sigemptyset(&nset);
270 sigaddset(&nset, SIGINT);
271 sigaddset(&nset, SIGHUP);
272 sigprocmask(SIG_BLOCK, &nset, &oset);
273 handlerpush(collint);
274 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
275 safe_signal(SIGINT, collint);
276 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
277 safe_signal(SIGHUP, collhup);
278 /* TODO We do a lot of redundant signal handling, especially
279 * TODO with the line editor(s); try to merge this */
280 savetstp = safe_signal(SIGTSTP, collstop);
281 savettou = safe_signal(SIGTTOU, collstop);
282 savettin = safe_signal(SIGTTIN, collstop);
283 if (sigsetjmp(collabort, 1))
284 goto jerr;
285 if (sigsetjmp(colljmp, 1))
286 goto jerr;
287 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
289 noreset++;
290 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
291 perror(tr(51, "temporary mail file"));
292 goto jerr;
294 unlink(tempMail);
295 Ftfree(&tempMail);
297 if ((cp = value("NAIL_HEAD")) != NULL && putesc(cp, collf) < 0)
298 goto jerr;
301 * If we are going to prompt for a subject,
302 * refrain from printing a newline after
303 * the headers (since some people mind).
305 getfields = 0;
306 if (! (options & OPT_t_FLAG)) {
307 t = GTO|GSUBJECT|GCC|GNL;
308 if (value("fullnames"))
309 t |= GCOMMA;
310 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
311 (value("ask") != NULL || value("asksub") != NULL))
312 t &= ~GNL, getfields |= GSUBJECT;
313 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
314 t &= ~GNL, getfields |= GTO;
315 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
316 (options & OPT_INTERACTIVE)) {
317 if (hp->h_bcc == NULL && value("askbcc"))
318 t &= ~GNL, getfields |= GBCC;
319 if (hp->h_cc == NULL && value("askcc"))
320 t &= ~GNL, getfields |= GCC;
322 if (printheaders) {
323 (void)puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
324 NULL, NULL);
325 (void)fflush(stdout);
330 * Quote an original message
332 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
333 quoteig = allignore;
334 action = SEND_QUOTE;
335 if (doprefix) {
336 quoteig = fwdignore;
337 if ((cp = value("fwdheading")) == NULL)
338 cp = "-------- Original Message --------";
339 if (*cp && fprintf(collf, "%s\n", cp) < 0)
340 goto jerr;
341 } else if (strcmp(quote, "noheading") == 0) {
342 /*EMPTY*/;
343 } else if (strcmp(quote, "headers") == 0) {
344 quoteig = ignore;
345 } else if (strcmp(quote, "allheaders") == 0) {
346 quoteig = NULL;
347 action = SEND_QUOTE_ALL;
348 } else {
349 cp = hfield1("from", mp);
350 if (cp != NULL && (count = (long)strlen(cp)) > 0) {
351 if (xmime_write(cp, count,
352 collf, CONV_FROMHDR, TD_NONE,
353 NULL) < 0)
354 goto jerr;
355 if (fprintf(collf, tr(52, " wrote:\n\n")) < 0)
356 goto jerr;
359 if (fflush(collf))
360 goto jerr;
361 cp = value("indentprefix");
362 if (cp != NULL && *cp == '\0')
363 cp = "\t";
364 if (send(mp, collf, quoteig, (doprefix ? NULL : cp), action,
365 NULL) < 0)
366 goto jerr;
369 /* Print what we have sofar also on the terminal */
370 (void)rewind(collf);
371 while ((c = getc(collf)) != EOF) /* XXX bytewise, yuck! */
372 (void)putc(c, stdout);
373 if (fseek(collf, 0, SEEK_END))
374 goto jerr;
376 escape = ((cp = value("escape")) != NULL) ? *cp : ESCAPE;
377 eofcount = 0;
378 hadintr = 0;
380 if (! sigsetjmp(colljmp, 1)) {
381 if (getfields)
382 grab_headers(hp, getfields, 1);
383 if (quotefile != NULL) {
384 if (_include_file(NULL, quotefile, &lc, &cc, 1) != 0)
385 goto jerr;
387 if ((options & OPT_INTERACTIVE) && value("editalong")) {
388 rewind(collf);
389 mesedit('e', hp);
390 goto jcont;
392 } else {
394 * Come here for printing the after-signal message.
395 * Duplicate messages won't be printed because
396 * the write is aborted if we get a SIGTTOU.
398 jcont:
399 if (hadintr) {
400 (void)fprintf(stderr, tr(53,
401 "\n(Interrupt -- one more to kill letter)\n"));
402 } else {
403 printf(tr(54, "(continue)\n"));
404 (void)fflush(stdout);
409 * No tilde escapes, interrupts not expected. Simply copy STDIN
411 if (! (options & (OPT_INTERACTIVE | OPT_t_FLAG|OPT_TILDE_FLAG))) {
412 linebuf = srealloc(linebuf, linesize = LINESIZE);
413 while ((count = fread(linebuf, sizeof *linebuf,
414 linesize, stdin)) > 0) {
415 if ((size_t)count != fwrite(linebuf, sizeof *linebuf,
416 count, collf))
417 goto jerr;
419 if (fflush(collf))
420 goto jerr;
421 goto jout;
425 * The interactive collect loop
427 for (;;) {
428 colljmp_p = 1;
429 count = readline_input(LNED_NONE, "", &linebuf, &linesize);
430 colljmp_p = 0;
432 if (count < 0) {
433 if ((options & OPT_INTERACTIVE) &&
434 value("ignoreeof") != NULL && ++eofcount < 25) {
435 printf(tr(55,
436 "Use \".\" to terminate letter\n"));
437 continue;
439 break;
441 if ((options & OPT_t_FLAG) && count == 0) {
442 rewind(collf);
443 if (makeheader(collf, hp) != OKAY)
444 goto jerr;
445 rewind(collf);
446 options &= ~OPT_t_FLAG;
447 continue;
450 eofcount = 0;
451 hadintr = 0;
452 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
453 (options & (OPT_INTERACTIVE|OPT_TILDE_FLAG)) &&
454 (boption("dot") || boption("ignoreeof")))
455 break;
456 if (count == 0 || linebuf[0] != escape || ! (options &
457 (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
458 /* TODO calls putline(), which *always* appends LF;
459 * TODO thus, STDIN with -t will ALWAYS end with LF,
460 * TODO even if no trailing LF and QP CTE */
461 if (putline(collf, linebuf, count) < 0)
462 goto jerr;
463 continue;
466 c = linebuf[1];
467 switch (c) {
468 default:
470 * On double escape, just send the single one.
471 * Otherwise, it's an error.
473 if (c == escape) {
474 if (putline(collf, &linebuf[1], count - 1) < 0)
475 goto jerr;
476 else
477 break;
479 printf(tr(56, "Unknown tilde escape.\n"));
480 break;
481 #ifdef HAVE_ASSERTS
482 case 'C':
483 /* Dump core */
484 core(NULL);
485 break;
486 #endif
487 case '!':
488 /* Shell escape, send the balance of line to sh -c */
489 shell(&linebuf[2]);
490 break;
491 case ':':
492 case '_':
493 /* Escape to command mode, but be nice! */
494 inhook = 0;
495 execute(&linebuf[2], 1, count - 2);
496 goto jcont;
497 case '.':
498 /* Simulate end of file on input */
499 goto jout;
500 case 'x':
501 /* Same as 'q', but no dead.letter saving */
502 hadintr++;
503 collint(0);
504 exit(1);
505 /*NOTREACHED*/
506 case 'q':
508 * Force a quit of sending mail.
509 * Act like an interrupt happened.
511 hadintr++;
512 collint(SIGINT);
513 exit(1);
514 /*NOTREACHED*/
515 case 'h':
516 /* Grab a bunch of headers */
518 grab_headers(hp, GTO|GSUBJECT|GCC|GBCC,
519 (value("bsdcompat") != NULL &&
520 value("bsdorder") != NULL));
521 while (hp->h_to == NULL);
522 goto jcont;
523 case 'H':
524 /* Grab extra headers */
526 grab_headers(hp, GEXTRA, 0);
527 while (check_from_and_sender(hp->h_from, hp->h_sender));
528 goto jcont;
529 case 't':
530 /* Add to the To list */
531 while ((hp->h_to = cat(hp->h_to, checkaddrs(
532 lextract(&linebuf[2], GTO|GFULL))))
533 == NULL)
535 break;
536 case 's':
537 /* Set the Subject list */
538 cp = &linebuf[2];
539 while (whitechar(*cp))
540 ++cp;
541 hp->h_subject = savestr(cp);
542 break;
543 #ifdef HAVE_ASSERTS
544 case 'S':
545 sstats(NULL);
546 break;
547 #endif
548 case '@':
549 /* Edit the attachment list */
550 if (linebuf[2] != '\0')
551 hp->h_attach = append_attachments(hp->h_attach,
552 &linebuf[2]);
553 else
554 hp->h_attach = edit_attachments(hp->h_attach);
555 break;
556 case 'c':
557 /* Add to the CC list */
558 hp->h_cc = cat(hp->h_cc, checkaddrs(
559 lextract(&linebuf[2], GCC|GFULL)));
560 break;
561 case 'b':
562 /* Add stuff to blind carbon copies list */
563 hp->h_bcc = cat(hp->h_bcc, checkaddrs(
564 lextract(&linebuf[2], GBCC|GFULL)));
565 break;
566 case 'd':
567 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
568 linebuf[linesize-1]='\0';
569 /*FALLTHRU*/
570 case 'r':
571 case '<':
573 * Invoke a file:
574 * Search for the file name,
575 * then open it and copy the contents to collf.
577 cp = &linebuf[2];
578 while (whitechar(*cp))
579 cp++;
580 if (*cp == '\0') {
581 fprintf(stderr, tr(57,
582 "Interpolate what file?\n"));
583 break;
585 if (*cp == '!') {
586 insertcommand(collf, cp + 1);
587 break;
589 if ((cp = file_expand(cp)) == NULL)
590 break;
591 if (is_dir(cp)) {
592 fprintf(stderr, tr(58, "%s: Directory\n"), cp);
593 break;
595 if ((fbuf = Fopen(cp, "r")) == NULL) {
596 perror(cp);
597 break;
599 printf(tr(59, "\"%s\" "), cp);
600 fflush(stdout);
601 if (_include_file(fbuf, cp, &lc, &cc, 0) != 0)
602 goto jerr;
603 printf(tr(60, "%d/%d\n"), lc, cc);
604 break;
605 case 'i':
606 /* Insert an environment variable into the file */
607 cp = &linebuf[2];
608 while (whitechar(*cp))
609 ++cp;
610 if ((cp = value(cp)) == NULL || *cp == '\0')
611 break;
612 if (putesc(cp, collf) < 0)
613 goto jerr;
614 if ((options & OPT_INTERACTIVE) &&
615 putesc(cp, stdout) < 0)
616 goto jerr;
617 break;
618 case 'a':
619 case 'A':
620 /* Insert the contents of a signature variable */
621 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
622 *cp != '\0') {
623 if (putesc(cp, collf) < 0)
624 goto jerr;
625 if ((options & OPT_INTERACTIVE) &&
626 putesc(cp, stdout) < 0)
627 goto jerr;
629 break;
630 case 'w':
631 /* Write the message on a file */
632 cp = &linebuf[2];
633 while (blankchar(*cp))
634 ++cp;
635 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
636 fprintf(stderr, tr(61, "Write what file!?\n"));
637 break;
639 rewind(collf);
640 if (exwrite(cp, collf, 1) < 0)
641 goto jerr;
642 break;
643 case 'm':
644 case 'M':
645 case 'f':
646 case 'F':
648 * Interpolate the named messages, if we
649 * are in receiving mail mode. Does the
650 * standard list processing garbage.
651 * If ~f is given, we don't shift over.
653 if (forward(linebuf + 2, collf, c) < 0)
654 goto jerr;
655 goto jcont;
656 case 'p':
658 * Print out the current state of the
659 * message without altering anything.
661 print_collf(collf, hp);
662 goto jcont;
663 case '|':
665 * Pipe message through command.
666 * Collect output as new message.
668 rewind(collf);
669 mespipe(&linebuf[2]);
670 goto jcont;
671 case 'v':
672 case 'e':
674 * Edit the current message.
675 * 'e' means to use EDITOR
676 * 'v' means to use VISUAL
678 rewind(collf);
679 mesedit(c, value("editheaders") ? hp : NULL);
680 goto jcont;
681 case '?':
683 * Last the lengthy help string.
684 * (Very ugly, but take care for compiler supported
685 * string lengths :()
687 (void)puts(tr(300,
688 "-------------------- ~ ESCAPES ----------------------------\n"
689 "~~ Quote a single tilde\n"
690 "~@ [file ...] Edit attachment list\n"
691 "~b users Add users to \"blind\" cc list\n"
692 "~c users Add users to cc list\n"
693 "~d Read in dead.letter\n"
694 "~e Edit the message buffer\n"
695 "~f messages Read in messages without indenting lines\n"
696 "~F messages Same as ~f, but keep all header lines\n"
697 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
698 (void)puts(tr(301,
699 "~r file Read a file into the message buffer\n"
700 "~p Print the message buffer\n"
701 "~q Abort message composition and save text to dead.letter\n"
702 "~m messages Read in messages with each line indented\n"
703 "~M messages Same as ~m, but keep all header lines\n"
704 "~s subject Set subject\n"
705 "~t users Add users to to list\n"
706 "~v Invoke display editor on message\n"
707 "~w file Write message onto file\n"
708 "~x Abort message composition and discard text written so far\n"));
709 (void)puts(tr(302,
710 "~!command Invoke the shell\n"
711 "~:command Execute a regular command\n"
712 "-----------------------------------------------------------\n"));
713 break;
717 jout:
718 if (collf != NULL) {
719 if ((cp = value("NAIL_TAIL")) != NULL) {
720 if (putesc(cp, collf) < 0)
721 goto jerr;
722 if ((options & OPT_INTERACTIVE) &&
723 putesc(cp, stdout) < 0)
724 goto jerr;
726 rewind(collf);
728 if (linebuf != NULL)
729 free(linebuf);
730 handlerpop();
731 noreset--;
732 sigemptyset(&nset);
733 sigaddset(&nset, SIGINT);
734 sigaddset(&nset, SIGHUP);
735 sigprocmask(SIG_BLOCK, &nset, (sigset_t*)NULL);
736 safe_signal(SIGINT, saveint);
737 safe_signal(SIGHUP, savehup);
738 safe_signal(SIGTSTP, savetstp);
739 safe_signal(SIGTTOU, savettou);
740 safe_signal(SIGTTIN, savettin);
741 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
742 return (collf);
744 jerr:
745 if (tempMail != NULL) {
746 rm(tempMail);
747 Ftfree(&tempMail);
749 if (collf != NULL) {
750 Fclose(collf);
751 collf = NULL;
753 goto jout;
757 * Write a file, ex-like if f set.
759 static int
760 exwrite(char const *name, FILE *fp, int f)
762 FILE *of;
763 int c;
764 long cc;
765 int lc;
767 if (f) {
768 printf("\"%s\" ", name);
769 fflush(stdout);
771 if ((of = Fopen(name, "a")) == NULL) {
772 perror(NULL);
773 return(-1);
775 lc = 0;
776 cc = 0;
777 while ((c = getc(fp)) != EOF) {
778 cc++;
779 if (c == '\n')
780 lc++;
781 putc(c, of);
782 if (ferror(of)) {
783 perror(name);
784 Fclose(of);
785 return(-1);
788 Fclose(of);
789 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
790 fflush(stdout);
791 return(0);
794 static enum okay
795 makeheader(FILE *fp, struct header *hp)
797 char *tempEdit;
798 FILE *nf;
799 int c;
801 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
802 perror(tr(66, "temporary mail edit file"));
803 return STOP;
805 unlink(tempEdit);
806 Ftfree(&tempEdit);
808 extract_header(fp, hp);
809 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
810 putc(c, nf);
811 if (fp != collf)
812 Fclose(collf);
813 Fclose(fp);
814 collf = nf;
815 if (check_from_and_sender(hp->h_from, hp->h_sender))
816 return STOP;
817 return OKAY;
821 * Edit the message being collected on fp.
822 * On return, make the edit file the new temp file.
824 static void
825 mesedit(int c, struct header *hp)
827 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
828 char *saved = value("add-file-recipients");
829 FILE *nf;
831 assign("add-file-recipients", "");
832 nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
834 if (nf != NULL) {
835 if (hp) {
836 rewind(nf);
837 makeheader(nf, hp);
838 } else {
839 fseek(nf, 0L, SEEK_END);
840 Fclose(collf);
841 collf = nf;
845 assign("add-file-recipients", saved);
846 safe_signal(SIGINT, sigint);
850 * Pipe the message through the command.
851 * Old message is on stdin of command;
852 * New message collected from stdout.
853 * Sh -c must return 0 to accept the new message.
855 static void
856 mespipe(char *cmd)
858 FILE *nf;
859 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
860 char *tempEdit;
861 char const *shell;
863 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
864 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
865 goto out;
867 fflush(collf);
868 unlink(tempEdit);
869 Ftfree(&tempEdit);
871 * stdin = current message.
872 * stdout = new message.
874 if ((shell = value("SHELL")) == NULL)
875 shell = SHELL;
876 if (run_command(shell,
877 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
878 Fclose(nf);
879 goto out;
881 if (fsize(nf) == 0) {
882 fprintf(stderr, catgets(catd, CATSET, 67,
883 "No bytes from \"%s\" !?\n"), cmd);
884 Fclose(nf);
885 goto out;
888 * Take new files.
890 fseek(nf, 0L, SEEK_END);
891 Fclose(collf);
892 collf = nf;
893 out:
894 safe_signal(SIGINT, sigint);
898 * Interpolate the named messages into the current
899 * message, preceding each line with a tab.
900 * Return a count of the number of characters now in
901 * the message, or -1 if an error is encountered writing
902 * the message temporary. The flag argument is 'm' if we
903 * should shift over and 'f' if not.
905 static int
906 forward(char *ms, FILE *fp, int f)
908 int *msgvec;
909 struct ignoretab *ig;
910 char const *tabst;
911 enum sendaction action;
913 /*LINTED*/
914 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
915 if (msgvec == NULL)
916 return(0);
917 if (getmsglist(ms, msgvec, 0) < 0)
918 return(0);
919 if (*msgvec == 0) {
920 *msgvec = first(0, MMNORM);
921 if (*msgvec == 0) {
922 printf(catgets(catd, CATSET, 68,
923 "No appropriate messages\n"));
924 return(0);
926 msgvec[1] = 0;
928 if (f == 'f' || f == 'F')
929 tabst = NULL;
930 else if ((tabst = value("indentprefix")) == NULL)
931 tabst = "\t";
932 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
933 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
934 printf(catgets(catd, CATSET, 69, "Interpolating:"));
935 for (; *msgvec != 0; msgvec++) {
936 struct message *mp = message + *msgvec - 1;
938 touch(mp);
939 printf(" %d", *msgvec);
940 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
941 perror(catgets(catd, CATSET, 70,
942 "temporary mail file"));
943 return(-1);
946 printf("\n");
947 return(0);
951 * Print (continue) when continued after ^Z.
953 /*ARGSUSED*/
954 static void
955 collstop(int s)
957 sighandler_type old_action = safe_signal(s, SIG_DFL);
958 sigset_t nset;
960 sigemptyset(&nset);
961 sigaddset(&nset, s);
962 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
963 kill(0, s);
964 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
965 safe_signal(s, old_action);
966 if (colljmp_p) {
967 colljmp_p = 0;
968 hadintr = 0;
969 siglongjmp(colljmp, 1);
974 * On interrupt, come here to save the partial message in ~/dead.letter.
975 * Then jump out of the collection loop.
977 /*ARGSUSED*/
978 static void
979 collint(int s)
982 * the control flow is subtle, because we can be called from ~q.
984 if (!hadintr) {
985 if (value("ignore") != NULL) {
986 puts("@");
987 fflush(stdout);
988 clearerr(stdin);
989 return;
991 hadintr = 1;
992 siglongjmp(colljmp, 1);
994 exit_status |= 04;
995 if (value("save") != NULL && s != 0)
996 savedeadletter(collf, 1);
997 /* Aborting message, no need to fflush() .. */
998 siglongjmp(collabort, 1);
1001 /*ARGSUSED*/
1002 static void
1003 collhup(int s)
1005 (void)s;
1006 savedeadletter(collf, 1);
1008 * Let's pretend nobody else wants to clean up,
1009 * a true statement at this time.
1011 exit(1);
1014 void
1015 savedeadletter(FILE *fp, int fflush_rewind_first)
1017 char const *cp;
1018 int c;
1019 FILE *dbuf;
1020 ul_it lines, bytes;
1022 if (fflush_rewind_first) {
1023 (void)fflush(fp);
1024 rewind(fp);
1026 if (fsize(fp) == 0)
1027 goto jleave;
1029 cp = getdeadletter();
1030 c = umask(077);
1031 dbuf = Fopen(cp, "a");
1032 umask(c);
1033 if (dbuf == NULL)
1034 goto jleave;
1037 * There are problems with dup()ing of file-descriptors for child
1038 * processes. As long as those are not fixed in equal spirit to
1039 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1040 * bugs like (If *record* is set, avoid writing dead content twice..,
1041 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1042 * makes itself comfortable with the *real* offset of the underlaying
1043 * file descriptor. Unfortunately Standard I/O and POSIX don't
1044 * describe a way for that -- fflush();rewind(); won't do it.
1045 * This fseek(END),rewind() pair works around the problem on *BSD.
1047 (void)fseek(fp, 0, SEEK_END);
1048 rewind(fp);
1050 printf("\"%s\" ", cp);
1051 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1052 putc(c, dbuf);
1053 if (c == '\n')
1054 ++lines;
1056 printf("%lu/%lu\n", lines, bytes);
1058 Fclose(dbuf);
1059 rewind(fp);
1060 jleave: ;
1063 static int
1064 putesc(const char *s, FILE *stream)
1066 int n = 0;
1068 while (s[0]) {
1069 if (s[0] == '\\') {
1070 if (s[1] == 't') {
1071 if (putc('\t', stream) == EOF)
1072 return (-1);
1073 n++;
1074 s += 2;
1075 continue;
1077 if (s[1] == 'n') {
1078 if (putc('\n', stream) == EOF)
1079 return (-1);
1080 n++;
1081 s += 2;
1082 continue;
1085 if (putc(s[0], stream) == EOF)
1086 return (-1);
1087 n++;
1088 s++;
1090 if (putc('\n', stream) == EOF)
1091 return (-1);
1092 return (++n);