Tweak *rfc822-show-all* manual text
[s-mailx.git] / collect.c
blob7c054bc8f5977e1f061aa2a70c2393ce63beda05
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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.
41 * Mail -- a mail program
43 * Collect input from standard input, handling
44 * ~ escapes.
47 #include "rcv.h"
48 #include "extern.h"
49 #include <unistd.h>
50 #include <sys/stat.h>
52 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
53 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
54 * TODO may leave child running if fdopen() fails! */
57 * Read a message from standard output and return a read file to it
58 * or NULL on error.
62 * The following hokiness with global variables is so that on
63 * receipt of an interrupt signal, the partial message can be salted
64 * away on dead.letter.
67 static sighandler_type saveint; /* Previous SIGINT value */
68 static sighandler_type savehup; /* Previous SIGHUP value */
69 static sighandler_type savetstp; /* Previous SIGTSTP value */
70 static sighandler_type savettou; /* Previous SIGTTOU value */
71 static sighandler_type savettin; /* Previous SIGTTIN value */
72 static FILE *collf; /* File for saving away */
73 static int hadintr; /* Have seen one SIGINT so far */
75 static sigjmp_buf colljmp; /* To get back to work */
76 static int colljmp_p; /* whether to long jump */
77 static sigjmp_buf collabort; /* To end collection with error */
79 static sigjmp_buf pipejmp; /* On broken pipe */
81 static void onpipe(int signo);
82 static void insertcommand(FILE *fp, char *cmd);
83 static void print_collf(FILE *collf, struct header *hp);
84 static int include_file(FILE *fbuf, char *name, int *linecount,
85 int *charcount, int echo);
86 static struct attachment *read_attachment_data(struct attachment *ap,
87 unsigned number);
88 static struct attachment *append_attachments(struct attachment *attach,
89 char *names);
90 static int exwrite(char *name, FILE *fp, int f);
91 static enum okay makeheader(FILE *fp, struct header *hp);
92 static void mesedit(int c, struct header *hp);
93 static void mespipe(char *cmd);
94 static int forward(char *ms, FILE *fp, int f);
95 static void collstop(int s);
96 static void collint(int s);
97 static void collhup(int s);
98 static int putesc(const char *s, FILE *stream);
100 /*ARGSUSED*/
101 static void
102 onpipe(int signo)
104 (void)signo;
105 siglongjmp(pipejmp, 1);
109 * Execute cmd and insert its standard output into fp.
111 static void
112 insertcommand(FILE *fp, char *cmd)
114 FILE *obuf = NULL;
115 char *volatile cp;
116 int c;
118 cp = value("SHELL");
119 if (sigsetjmp(pipejmp, 1))
120 goto endpipe;
121 if (cp == NULL)
122 cp = SHELL;
123 if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) {
124 perror(cmd);
125 return;
127 safe_signal(SIGPIPE, onpipe);
128 while ((c = getc(obuf)) != EOF)
129 putc(c, fp);
130 endpipe:
131 safe_signal(SIGPIPE, SIG_IGN);
132 Pclose(obuf);
133 safe_signal(SIGPIPE, dflpipe);
137 * ~p command.
139 static void
140 print_collf(FILE *collf, struct header *hp)
142 char *lbuf = NULL;
143 FILE *volatile obuf = stdout;
144 struct attachment *ap;
145 char *cp;
146 enum gfield gf;
147 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
149 (void)&obuf;
150 (void)&cp;
151 fflush(collf);
152 rewind(collf);
153 count = count2 = fsize(collf);
154 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
155 for (linecnt = 0;
156 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
157 linecnt++);
158 rewind(collf);
159 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
160 maxlines -= 4;
161 if (hp->h_to)
162 maxlines--;
163 if (hp->h_subject)
164 maxlines--;
165 if (hp->h_cc)
166 maxlines--;
167 if (hp->h_bcc)
168 maxlines--;
169 if (hp->h_attach)
170 maxlines--;
171 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
172 maxlines -= value("ORGANIZATION") != NULL ||
173 hp->h_organization != NULL;
174 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
175 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
176 if ((long)maxlines < 0 || linecnt > maxlines) {
177 cp = get_pager();
178 if (sigsetjmp(pipejmp, 1))
179 goto endpipe;
180 obuf = Popen(cp, "w", NULL, 1);
181 if (obuf == NULL) {
182 perror(cp);
183 obuf = stdout;
184 } else
185 safe_signal(SIGPIPE, onpipe);
188 fprintf(obuf, catgets(catd, CATSET, 62,
189 "-------\nMessage contains:\n"));
190 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
191 if (value("fullnames"))
192 gf |= GCOMMA;
193 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
194 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
195 prout(lbuf, linelen, obuf);
196 if (hp->h_attach != NULL) {
197 fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf);
198 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
199 if (ap->a_msgno)
200 fprintf(obuf, " message %u", ap->a_msgno);
201 else
202 fprintf(obuf, " %s", ap->a_name);
203 if (ap->a_flink)
204 putc(',', obuf);
206 putc('\n', obuf);
208 endpipe:
209 if (obuf != stdout) {
210 safe_signal(SIGPIPE, SIG_IGN);
211 Pclose(obuf);
212 safe_signal(SIGPIPE, dflpipe);
214 if (lbuf)
215 free(lbuf);
218 static int
219 include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo)
221 char *interactive, *linebuf = NULL;
222 size_t linesize = 0, linelen, count;
224 if (fbuf == NULL)
225 fbuf = Fopen(name, "r");
226 if (fbuf == NULL) {
227 perror(name);
228 return (-1);
230 interactive = value("interactive");
231 *linecount = 0;
232 *charcount = 0;
233 fflush(fbuf);
234 rewind(fbuf);
235 count = fsize(fbuf);
236 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
237 != NULL) {
238 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
239 != linelen) {
240 Fclose(fbuf);
241 return (-1);
243 if (interactive != NULL && echo)
244 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
245 (*linecount)++;
246 (*charcount) += linelen;
248 if (linebuf)
249 free(linebuf);
250 Fclose(fbuf);
251 if (fflush(collf))
252 return (-1);
253 return (0);
257 * Ask the user to edit file names and other data for the given
258 * attachment. NULL is returned if no file name is given.
260 static struct attachment *
261 read_attachment_data(struct attachment *ap, unsigned number)
263 char prefix[80], *cp;
265 if (ap == NULL)
266 ap = csalloc(1, sizeof *ap);
267 if (ap->a_msgno) {
268 printf("#%u\tmessage %u\n", number, ap->a_msgno);
269 return ap;
271 snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50,
272 "#%u\tfilename: "), number);
273 for (;;) {
274 char *exf;
275 if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL)
276 break;
277 if ((exf = file_expand(ap->a_name)) == NULL)
278 continue;
279 ap->a_name = exf;
280 if (access(ap->a_name, R_OK) == 0)
281 break;
282 perror(ap->a_name);
284 if ((ap->a_name && (value("attachment-ask-charset"))) ||
285 ((cp = value("sendcharsets")) != NULL &&
286 strchr(cp, ',') != NULL)) {
287 snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number);
288 ap->a_charset = readtty(prefix, ap->a_charset);
291 * The "attachment-ask-content-*" variables are left undocumented
292 * since they are for RFC connoisseurs only.
294 if (ap->a_name && value("attachment-ask-content-type")) {
295 if (ap->a_content_type == NULL)
296 ap->a_content_type = mime_filecontent(ap->a_name);
297 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
298 ap->a_content_type = readtty(prefix, ap->a_content_type);
300 if (ap->a_name && value("attachment-ask-content-disposition")) {
301 snprintf(prefix, sizeof prefix,
302 "#%u\tContent-Disposition: ", number);
303 ap->a_content_disposition = readtty(prefix,
304 ap->a_content_disposition);
306 if (ap->a_name && value("attachment-ask-content-id")) {
307 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
308 ap->a_content_id = readtty(prefix, ap->a_content_id);
310 if (ap->a_name && value("attachment-ask-content-description")) {
311 snprintf(prefix, sizeof prefix,
312 "#%u\tContent-Description: ", number);
313 ap->a_content_description = readtty(prefix,
314 ap->a_content_description);
316 return ap->a_name ? ap : NULL;
320 * Interactively edit the attachment list.
322 struct attachment *
323 edit_attachments(struct attachment *attach)
325 struct attachment *ap, *nap;
326 unsigned attno = 1;
328 for (ap = attach; ap; ap = ap->a_flink) {
329 if ((nap = read_attachment_data(ap, attno)) == NULL) {
330 if (ap->a_blink)
331 ap->a_blink->a_flink = ap->a_flink;
332 else
333 attach = ap->a_flink;
334 if (ap->a_flink)
335 ap->a_flink->a_blink = ap->a_blink;
336 else
337 return attach;
338 } else
339 attno++;
341 while ((nap = read_attachment_data(NULL, attno)) != NULL) {
342 for (ap = attach; ap && ap->a_flink; ap = ap->a_flink);
343 if (ap)
344 ap->a_flink = nap;
345 nap->a_blink = ap;
346 nap->a_flink = NULL;
347 if (attach == NULL)
348 attach = nap;
349 attno++;
351 return attach;
355 * Put the given file to the end of the attachment list.
357 struct attachment *
358 add_attachment(struct attachment *attach, char *file, int expand_file)
360 struct attachment *ap, *nap;
362 if (expand_file) {
363 file = file_expand(file);
364 if (file == NULL)
365 return NULL;
366 } else
367 file = savestr(file);
368 if (access(file, R_OK) != 0)
369 return NULL;
370 /*LINTED*/
371 nap = csalloc(1, sizeof *nap);
372 nap->a_name = file;
373 if (attach != NULL) {
374 for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink);
375 ap->a_flink = nap;
376 nap->a_blink = ap;
377 } else {
378 nap->a_blink = NULL;
379 attach = nap;
381 return attach;
385 * Append the whitespace-separated file names to the end of
386 * the attachment list.
388 static struct attachment *
389 append_attachments(struct attachment *attach, char *names)
391 char *cp;
392 int c;
393 struct attachment *ap;
395 cp = names;
396 while (*cp != '\0' && blankchar(*cp & 0377))
397 cp++;
398 while (*cp != '\0') {
399 names = cp;
400 while (*cp != '\0' && !blankchar(*cp & 0377))
401 cp++;
402 c = *cp;
403 *cp++ = '\0';
404 if (*names != '\0') {
405 if ((ap = add_attachment(attach, names, 1)) == NULL)
406 perror(names);
407 else
408 attach = ap;
410 if (c == '\0')
411 break;
412 while (*cp != '\0' && blankchar(*cp & 0377))
413 cp++;
415 return attach;
418 FILE *
419 collect(struct header *hp, int printheaders, struct message *mp,
420 char *quotefile, int doprefix, int volatile tflag)
422 enum {
423 val_INTERACT = 1
426 FILE *fbuf;
427 struct ignoretab *quoteig;
428 int lc, cc, eofcount, val, c, t;
429 int volatile escape, getfields;
430 char *linebuf = NULL, *cp, *quote = NULL, *tempMail = NULL;
431 size_t linesize;
432 long count;
433 enum sendaction action;
434 sigset_t oset, nset;
435 sighandler_type savedtop;
437 val = 0;
438 if (value("interactive") != NULL)
439 val |= val_INTERACT;
441 collf = NULL;
443 * Start catching signals from here, but we're still die on interrupts
444 * until we're in the main loop.
446 sigemptyset(&nset);
447 sigaddset(&nset, SIGINT);
448 sigaddset(&nset, SIGHUP);
449 sigprocmask(SIG_BLOCK, &nset, &oset);
450 handlerpush(collint);
451 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
452 safe_signal(SIGINT, collint);
453 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
454 safe_signal(SIGHUP, collhup);
455 savetstp = safe_signal(SIGTSTP, collstop);
456 savettou = safe_signal(SIGTTOU, collstop);
457 savettin = safe_signal(SIGTTIN, collstop);
458 if (sigsetjmp(collabort, 1))
459 goto jerr;
460 if (sigsetjmp(colljmp, 1))
461 goto jerr;
462 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
464 noreset++;
465 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
466 perror(tr(51, "temporary mail file"));
467 goto jerr;
469 unlink(tempMail);
470 Ftfree(&tempMail);
472 if ((cp = value("NAIL_HEAD")) != NULL && putesc(cp, collf) < 0)
473 goto jerr;
476 * If we are going to prompt for a subject,
477 * refrain from printing a newline after
478 * the headers (since some people mind).
480 getfields = 0;
481 if (! tflag) {
482 t = GTO|GSUBJECT|GCC|GNL;
483 if (value("fullnames"))
484 t |= GCOMMA;
485 if (hp->h_subject == NULL && (val & val_INTERACT) &&
486 (value("ask") != NULL || value("asksub") != NULL))
487 t &= ~GNL, getfields |= GSUBJECT;
488 if (hp->h_to == NULL && (val & val_INTERACT))
489 t &= ~GNL, getfields |= GTO;
490 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
491 (val & val_INTERACT)) {
492 if (hp->h_bcc == NULL && value("askbcc"))
493 t &= ~GNL, getfields |= GBCC;
494 if (hp->h_cc == NULL && value("askcc"))
495 t &= ~GNL, getfields |= GCC;
497 if (printheaders) {
498 (void)puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
499 NULL, NULL);
500 (void)fflush(stdout);
505 * Quote an original message
507 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
508 quoteig = allignore;
509 action = SEND_QUOTE;
510 if (doprefix) {
511 quoteig = fwdignore;
512 if ((cp = value("fwdheading")) == NULL)
513 cp = "-------- Original Message --------";
514 if (*cp && fprintf(collf, "%s\n", cp) < 0)
515 goto jerr;
516 } else if (strcmp(quote, "noheading") == 0) {
517 /*EMPTY*/;
518 } else if (strcmp(quote, "headers") == 0) {
519 quoteig = ignore;
520 } else if (strcmp(quote, "allheaders") == 0) {
521 quoteig = NULL;
522 action = SEND_QUOTE_ALL;
523 } else {
524 cp = hfield1("from", mp);
525 if (cp != NULL && (count = (long)strlen(cp)) > 0) {
526 if (mime_write(cp, count,
527 collf, CONV_FROMHDR, TD_NONE,
528 NULL, (size_t) 0,
529 NULL, NULL) == 0)
530 goto jerr;
531 if (fprintf(collf, tr(52, " wrote:\n\n")) < 0)
532 goto jerr;
535 if (fflush(collf))
536 goto jerr;
537 cp = value("indentprefix");
538 if (cp != NULL && *cp == '\0')
539 cp = "\t";
540 if (send(mp, collf, quoteig, (doprefix ? NULL : cp), action,
541 NULL) < 0)
542 goto jerr;
545 /* Print what we have sofar also on the terminal */
546 (void)rewind(collf);
547 while ((c = getc(collf)) != EOF)
548 (void)putc(c, stdout);
549 if (fseek(collf, 0, SEEK_END))
550 goto jerr;
552 escape = ((cp = value("escape")) != NULL) ? *cp : ESCAPE;
553 eofcount = 0;
554 hadintr = 0;
556 if (! sigsetjmp(colljmp, 1)) {
557 if (getfields)
558 grabh(hp, getfields, 1);
559 if (quotefile != NULL) {
560 if (include_file(NULL, quotefile, &lc, &cc, 1) != 0)
561 goto jerr;
563 } else {
565 * Come here for printing the after-signal message.
566 * Duplicate messages won't be printed because
567 * the write is aborted if we get a SIGTTOU.
569 jcont:
570 if (hadintr) {
571 (void)fprintf(stderr, tr(53,
572 "\n(Interrupt -- one more to kill letter)\n"));
573 } else {
574 printf(tr(54, "(continue)\n"));
575 (void)fflush(stdout);
580 * No tilde escapes, interrupts not expected. Simply copy STDIN
582 if ((val & val_INTERACT) == 0 && tildeflag <= 0 && ! tflag) {
583 linebuf = srealloc(linebuf, linesize = LINESIZE);
584 while ((count = fread(linebuf, sizeof *linebuf,
585 linesize, stdin)) > 0) {
586 if ((size_t)count != fwrite(linebuf, sizeof *linebuf,
587 count, collf))
588 goto jerr;
590 if (fflush(collf))
591 goto jerr;
592 goto jout;
596 * The interactive collect loop
598 for (;;) {
599 colljmp_p = 1;
600 count = readline(stdin, &linebuf, &linesize);
601 colljmp_p = 0;
602 if (count < 0) {
603 if ((val & val_INTERACT) &&
604 value("ignoreeof") != NULL && ++eofcount < 25) {
605 printf(tr(55,
606 "Use \".\" to terminate letter\n"));
607 continue;
609 break;
611 if (tflag && count == 0) {
612 rewind(collf);
613 if (makeheader(collf, hp) != OKAY)
614 goto jerr;
615 rewind(collf);
616 tflag = 0;
617 continue;
619 eofcount = 0;
620 hadintr = 0;
621 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
622 (val & val_INTERACT) &&
623 (value("dot") != NULL ||
624 value("ignoreeof") != NULL))
625 break;
626 if (linebuf[0] != escape ||
627 ((val & val_INTERACT) == 0 && tildeflag == 0) ||
628 tildeflag < 0) {
629 if (putline(collf, linebuf, count) < 0)
630 goto jerr;
631 continue;
634 c = linebuf[1];
635 switch (c) {
636 default:
638 * On double escape, just send the single one.
639 * Otherwise, it's an error.
641 if (c == escape) {
642 if (putline(collf, &linebuf[1], count - 1) < 0)
643 goto jerr;
644 else
645 break;
647 printf(tr(56, "Unknown tilde escape.\n"));
648 break;
649 #ifdef HAVE_ASSERTS
650 case 'C':
651 /* Dump core */
652 core(NULL);
653 break;
654 #endif
655 case '!':
656 /* Shell escape, send the balance of line to sh -c */
657 shell(&linebuf[2]);
658 break;
659 case ':':
660 case '_':
661 /* Escape to command mode, but be nice! */
662 inhook = 0;
663 execute(&linebuf[2], 1, count - 2);
664 goto jcont;
665 case '.':
666 /* Simulate end of file on input */
667 goto jout;
668 case 'x':
669 /* Same as 'q', but no dead.letter saving */
670 hadintr++;
671 collint(0);
672 exit(1);
673 /*NOTREACHED*/
674 case 'q':
676 * Force a quit of sending mail.
677 * Act like an interrupt happened.
679 hadintr++;
680 collint(SIGINT);
681 exit(1);
682 /*NOTREACHED*/
683 case 'h':
684 /* Grab a bunch of headers */
686 grabh(hp, GTO|GSUBJECT|GCC|GBCC,
687 value("bsdcompat") != NULL &&
688 value("bsdorder") != NULL);
689 while (hp->h_to == NULL);
690 goto jcont;
691 case 'H':
692 /* Grab extra headers */
694 grabh(hp, GEXTRA, 0);
695 while (check_from_and_sender(hp->h_from, hp->h_sender));
696 goto jcont;
697 case 't':
698 /* Add to the To list */
699 while ((hp->h_to = cat(hp->h_to, checkaddrs(
700 lextract(&linebuf[2], GTO|GFULL))))
701 == NULL)
703 break;
704 case 's':
705 /* Set the Subject list */
706 cp = &linebuf[2];
707 while (whitechar(*cp))
708 ++cp;
709 hp->h_subject = savestr(cp);
710 break;
711 case '@':
712 /* Edit the attachment list */
713 if (linebuf[2] != '\0')
714 hp->h_attach = append_attachments(hp->h_attach,
715 &linebuf[2]);
716 else
717 hp->h_attach = edit_attachments(hp->h_attach);
718 break;
719 case 'c':
720 /* Add to the CC list */
721 hp->h_cc = cat(hp->h_cc, checkaddrs(
722 lextract(&linebuf[2], GCC|GFULL)));
723 break;
724 case 'b':
725 /* Add stuff to blind carbon copies list */
726 hp->h_bcc = cat(hp->h_bcc, checkaddrs(
727 lextract(&linebuf[2], GBCC|GFULL)));
728 break;
729 case 'd':
730 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
731 linebuf[linesize-1]='\0';
732 /*FALLTHRU*/
733 case 'r':
734 case '<':
736 * Invoke a file:
737 * Search for the file name,
738 * then open it and copy the contents to collf.
740 cp = &linebuf[2];
741 while (whitechar(*cp))
742 cp++;
743 if (*cp == '\0') {
744 printf(tr(57, "Interpolate what file?\n"));
745 break;
747 if (*cp == '!') {
748 insertcommand(collf, cp + 1);
749 break;
751 cp = file_expand(cp);
752 if (cp == NULL)
753 break;
754 if (is_dir(cp)) {
755 printf(tr(58, "%s: Directory\n"), cp);
756 break;
758 if ((fbuf = Fopen(cp, "r")) == NULL) {
759 perror(cp);
760 break;
762 printf(tr(59, "\"%s\" "), cp);
763 fflush(stdout);
764 if (include_file(fbuf, cp, &lc, &cc, 0) != 0)
765 goto jerr;
766 printf(tr(60, "%d/%d\n"), lc, cc);
767 break;
768 case 'i':
769 /* Insert an environment variable into the file */
770 cp = &linebuf[2];
771 while (whitechar(*cp))
772 ++cp;
773 if ((cp = value(cp)) == NULL || *cp == '\0')
774 break;
775 if (putesc(cp, collf) < 0)
776 goto jerr;
777 if ((val & val_INTERACT) && putesc(cp, stdout) < 0)
778 goto jerr;
779 break;
780 case 'a':
781 case 'A':
782 /* Insert the contents of a signature variable */
783 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
784 *cp != '\0') {
785 if (putesc(cp, collf) < 0)
786 goto jerr;
787 if ((val & val_INTERACT) &&
788 putesc(cp, stdout) < 0)
789 goto jerr;
791 break;
792 case 'w':
793 /* Write the message on a file */
794 cp = &linebuf[2];
795 while (blankchar(*cp))
796 ++cp;
797 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
798 fprintf(stderr, tr(61, "Write what file!?\n"));
799 break;
801 rewind(collf);
802 if (exwrite(cp, collf, 1) < 0)
803 goto jerr;
804 break;
805 case 'm':
806 case 'M':
807 case 'f':
808 case 'F':
810 * Interpolate the named messages, if we
811 * are in receiving mail mode. Does the
812 * standard list processing garbage.
813 * If ~f is given, we don't shift over.
815 if (forward(linebuf + 2, collf, c) < 0)
816 goto jerr;
817 goto jcont;
818 case 'p':
820 * Print out the current state of the
821 * message without altering anything.
823 print_collf(collf, hp);
824 goto jcont;
825 case '|':
827 * Pipe message through command.
828 * Collect output as new message.
830 rewind(collf);
831 mespipe(&linebuf[2]);
832 goto jcont;
833 case 'v':
834 case 'e':
836 * Edit the current message.
837 * 'e' means to use EDITOR
838 * 'v' means to use VISUAL
840 rewind(collf);
841 mesedit(c, value("editheaders") ? hp : NULL);
842 goto jcont;
843 case '?':
845 * Last the lengthy help string.
846 * (Very ugly, but take care for compiler supported
847 * string lengths :()
849 (void)puts(
850 "-------------------- ~ ESCAPES ----------------------------\n"
851 "~~ Quote a single tilde\n"
852 "~@ [file ...] Edit attachment list\n"
853 "~b users Add users to \"blind\" cc list\n"
854 "~c users Add users to cc list\n"
855 "~d Read in dead.letter\n"
856 "~e Edit the message buffer\n"
857 "~f messages Read in messages without indenting lines\n"
858 "~F messages Same as ~f, but keep all header lines\n"
859 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n");
860 (void)puts(
861 "~r file Read a file into the message buffer\n"
862 "~p Print the message buffer\n"
863 "~q Abort message composition and save text to dead.letter\n"
864 "~m messages Read in messages with each line indented\n"
865 "~M messages Same as ~m, but keep all header lines\n"
866 "~s subject Set subject\n"
867 "~t users Add users to to list\n"
868 "~v Invoke display editor on message\n"
869 "~w file Write message onto file\n"
870 "~x Abort message composition and discard text written so far\n");
871 (void)puts(
872 "~!command Invoke the shell\n"
873 "~:command Execute a regular command\n"
874 "-----------------------------------------------------------\n");
875 break;
879 jout:
880 if (collf != NULL) {
881 if ((cp = value("NAIL_TAIL")) != NULL) {
882 if (putesc(cp, collf) < 0)
883 goto jerr;
884 if ((val & val_INTERACT) && putesc(cp, stdout) < 0)
885 goto jerr;
887 rewind(collf);
889 handlerpop();
890 noreset--;
891 sigemptyset(&nset);
892 sigaddset(&nset, SIGINT);
893 sigaddset(&nset, SIGHUP);
894 sigprocmask(SIG_BLOCK, &nset, (sigset_t*)NULL);
895 safe_signal(SIGINT, saveint);
896 safe_signal(SIGHUP, savehup);
897 safe_signal(SIGTSTP, savetstp);
898 safe_signal(SIGTTOU, savettou);
899 safe_signal(SIGTTIN, savettin);
900 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
901 return (collf);
902 jerr:
903 if (tempMail != NULL) {
904 rm(tempMail);
905 Ftfree(&tempMail);
907 if (collf != NULL) {
908 Fclose(collf);
909 collf = NULL;
911 goto jout;
915 * Write a file, ex-like if f set.
917 static int
918 exwrite(char *name, FILE *fp, int f)
920 FILE *of;
921 int c;
922 long cc;
923 int lc;
925 if (f) {
926 printf("\"%s\" ", name);
927 fflush(stdout);
929 if ((of = Fopen(name, "a")) == NULL) {
930 perror(NULL);
931 return(-1);
933 lc = 0;
934 cc = 0;
935 while ((c = getc(fp)) != EOF) {
936 cc++;
937 if (c == '\n')
938 lc++;
939 putc(c, of);
940 if (ferror(of)) {
941 perror(name);
942 Fclose(of);
943 return(-1);
946 Fclose(of);
947 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
948 fflush(stdout);
949 return(0);
952 static enum okay
953 makeheader(FILE *fp, struct header *hp)
955 char *tempEdit;
956 FILE *nf;
957 int c;
959 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
960 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
961 Fclose(nf);
962 unlink(tempEdit);
963 Ftfree(&tempEdit);
964 return STOP;
966 unlink(tempEdit);
967 Ftfree(&tempEdit);
968 extract_header(fp, hp);
969 while ((c = getc(fp)) != EOF)
970 putc(c, nf);
971 if (fp != collf)
972 Fclose(collf);
973 Fclose(fp);
974 collf = nf;
975 if (check_from_and_sender(hp->h_from, hp->h_sender))
976 return STOP;
977 return OKAY;
981 * Edit the message being collected on fp.
982 * On return, make the edit file the new temp file.
984 static void
985 mesedit(int c, struct header *hp)
987 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
988 FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX,
989 sigint);
991 if (nf != NULL) {
992 if (hp) {
993 rewind(nf);
994 makeheader(nf, hp);
995 } else {
996 fseek(nf, 0L, SEEK_END);
997 Fclose(collf);
998 collf = nf;
1001 safe_signal(SIGINT, sigint);
1005 * Pipe the message through the command.
1006 * Old message is on stdin of command;
1007 * New message collected from stdout.
1008 * Sh -c must return 0 to accept the new message.
1010 static void
1011 mespipe(char *cmd)
1013 FILE *nf;
1014 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1015 char *tempEdit;
1016 char *shell;
1018 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1019 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1020 goto out;
1022 fflush(collf);
1023 unlink(tempEdit);
1024 Ftfree(&tempEdit);
1026 * stdin = current message.
1027 * stdout = new message.
1029 if ((shell = value("SHELL")) == NULL)
1030 shell = SHELL;
1031 if (run_command(shell,
1032 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
1033 Fclose(nf);
1034 goto out;
1036 if (fsize(nf) == 0) {
1037 fprintf(stderr, catgets(catd, CATSET, 67,
1038 "No bytes from \"%s\" !?\n"), cmd);
1039 Fclose(nf);
1040 goto out;
1043 * Take new files.
1045 fseek(nf, 0L, SEEK_END);
1046 Fclose(collf);
1047 collf = nf;
1048 out:
1049 safe_signal(SIGINT, sigint);
1053 * Interpolate the named messages into the current
1054 * message, preceding each line with a tab.
1055 * Return a count of the number of characters now in
1056 * the message, or -1 if an error is encountered writing
1057 * the message temporary. The flag argument is 'm' if we
1058 * should shift over and 'f' if not.
1060 static int
1061 forward(char *ms, FILE *fp, int f)
1063 int *msgvec;
1064 struct ignoretab *ig;
1065 char *tabst;
1066 enum sendaction action;
1068 /*LINTED*/
1069 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
1070 if (msgvec == NULL)
1071 return(0);
1072 if (getmsglist(ms, msgvec, 0) < 0)
1073 return(0);
1074 if (*msgvec == 0) {
1075 *msgvec = first(0, MMNORM);
1076 if (*msgvec == 0) {
1077 printf(catgets(catd, CATSET, 68,
1078 "No appropriate messages\n"));
1079 return(0);
1081 msgvec[1] = 0;
1083 if (f == 'f' || f == 'F')
1084 tabst = NULL;
1085 else if ((tabst = value("indentprefix")) == NULL)
1086 tabst = "\t";
1087 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
1088 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
1089 printf(catgets(catd, CATSET, 69, "Interpolating:"));
1090 for (; *msgvec != 0; msgvec++) {
1091 struct message *mp = message + *msgvec - 1;
1093 touch(mp);
1094 printf(" %d", *msgvec);
1095 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
1096 perror(catgets(catd, CATSET, 70,
1097 "temporary mail file"));
1098 return(-1);
1101 printf("\n");
1102 return(0);
1106 * Print (continue) when continued after ^Z.
1108 /*ARGSUSED*/
1109 static void
1110 collstop(int s)
1112 sighandler_type old_action = safe_signal(s, SIG_DFL);
1113 sigset_t nset;
1115 sigemptyset(&nset);
1116 sigaddset(&nset, s);
1117 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
1118 kill(0, s);
1119 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
1120 safe_signal(s, old_action);
1121 if (colljmp_p) {
1122 colljmp_p = 0;
1123 hadintr = 0;
1124 siglongjmp(colljmp, 1);
1129 * On interrupt, come here to save the partial message in ~/dead.letter.
1130 * Then jump out of the collection loop.
1132 /*ARGSUSED*/
1133 static void
1134 collint(int s)
1137 * the control flow is subtle, because we can be called from ~q.
1139 if (!hadintr) {
1140 if (value("ignore") != NULL) {
1141 puts("@");
1142 fflush(stdout);
1143 clearerr(stdin);
1144 return;
1146 hadintr = 1;
1147 siglongjmp(colljmp, 1);
1149 exit_status |= 04;
1150 rewind(collf);
1151 if (value("save") != NULL && s != 0)
1152 savedeadletter(collf);
1153 siglongjmp(collabort, 1);
1156 /*ARGSUSED*/
1157 static void
1158 collhup(int s)
1160 (void)s;
1161 rewind(collf);
1162 savedeadletter(collf);
1164 * Let's pretend nobody else wants to clean up,
1165 * a true statement at this time.
1167 exit(1);
1170 void
1171 savedeadletter(FILE *fp)
1173 FILE *dbuf;
1174 int c, lines = 0, bytes = 0;
1175 char *cp;
1177 if (fsize(fp) == 0)
1178 return;
1179 cp = getdeadletter();
1180 c = umask(077);
1181 dbuf = Fopen(cp, "a");
1182 umask(c);
1183 if (dbuf == NULL)
1184 return;
1185 printf("\"%s\" ", cp);
1186 while ((c = getc(fp)) != EOF) {
1187 putc(c, dbuf);
1188 bytes++;
1189 if (c == '\n')
1190 lines++;
1192 Fclose(dbuf);
1193 printf("%d/%d\n", lines, bytes);
1194 rewind(fp);
1197 static int
1198 putesc(const char *s, FILE *stream)
1200 int n = 0;
1202 while (s[0]) {
1203 if (s[0] == '\\') {
1204 if (s[1] == 't') {
1205 if (putc('\t', stream) == EOF)
1206 return (-1);
1207 n++;
1208 s += 2;
1209 continue;
1211 if (s[1] == 'n') {
1212 if (putc('\n', stream) == EOF)
1213 return (-1);
1214 n++;
1215 s += 2;
1216 continue;
1219 if (putc(s[0], stream) == EOF)
1220 return (-1);
1221 n++;
1222 s++;
1224 if (putc('\n', stream) == EOF)
1225 return (-1);
1226 return (++n);