makeconfig: add WANT_IDNA -> USE_IDNA test
[s-mailx.git] / collect.c
bloba84bd71aeea18ccccd8f0dff6b3eead07f6671c6
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>
53 * Read a message from standard output and return a read file to it
54 * or NULL on error.
58 * The following hokiness with global variables is so that on
59 * receipt of an interrupt signal, the partial message can be salted
60 * away on dead.letter.
63 static sighandler_type saveint; /* Previous SIGINT value */
64 static sighandler_type savehup; /* Previous SIGHUP value */
65 static sighandler_type savetstp; /* Previous SIGTSTP value */
66 static sighandler_type savettou; /* Previous SIGTTOU value */
67 static sighandler_type savettin; /* Previous SIGTTIN value */
68 static FILE *collf; /* File for saving away */
69 static int hadintr; /* Have seen one SIGINT so far */
71 static sigjmp_buf colljmp; /* To get back to work */
72 static int colljmp_p; /* whether to long jump */
73 static sigjmp_buf collabort; /* To end collection with error */
75 static sigjmp_buf pipejmp; /* On broken pipe */
77 static void onpipe(int signo);
78 static void insertcommand(FILE *fp, char *cmd);
79 static void print_collf(FILE *collf, struct header *hp);
80 static int include_file(FILE *fbuf, char *name, int *linecount,
81 int *charcount, int echo);
82 static struct attachment *read_attachment_data(struct attachment *ap,
83 unsigned number);
84 static struct attachment *append_attachments(struct attachment *attach,
85 char *names);
86 static int exwrite(char *name, FILE *fp, int f);
87 static enum okay makeheader(FILE *fp, struct header *hp);
88 static void mesedit(int c, struct header *hp);
89 static void mespipe(char *cmd);
90 static int forward(char *ms, FILE *fp, int f);
91 static void collstop(int s);
92 static void collint(int s);
93 static void collhup(int s);
94 static int putesc(const char *s, FILE *stream);
96 /*ARGSUSED*/
97 static void
98 onpipe(int signo)
100 (void)signo;
101 siglongjmp(pipejmp, 1);
105 * Execute cmd and insert its standard output into fp.
107 static void
108 insertcommand(FILE *fp, char *cmd)
110 FILE *obuf = NULL;
111 char *volatile cp;
112 int c;
114 cp = value("SHELL");
115 if (sigsetjmp(pipejmp, 1))
116 goto endpipe;
117 if (cp == NULL)
118 cp = SHELL;
119 if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) {
120 perror(cmd);
121 return;
123 safe_signal(SIGPIPE, onpipe);
124 while ((c = getc(obuf)) != EOF)
125 putc(c, fp);
126 endpipe:
127 safe_signal(SIGPIPE, SIG_IGN);
128 Pclose(obuf);
129 safe_signal(SIGPIPE, dflpipe);
133 * ~p command.
135 static void
136 print_collf(FILE *collf, struct header *hp)
138 char *lbuf = NULL;
139 FILE *volatile obuf = stdout;
140 struct attachment *ap;
141 char *cp;
142 enum gfield gf;
143 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
145 (void)&obuf;
146 (void)&cp;
147 fflush(collf);
148 rewind(collf);
149 count = count2 = fsize(collf);
150 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
151 for (linecnt = 0;
152 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
153 linecnt++);
154 rewind(collf);
155 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
156 maxlines -= 4;
157 if (hp->h_to)
158 maxlines--;
159 if (hp->h_subject)
160 maxlines--;
161 if (hp->h_cc)
162 maxlines--;
163 if (hp->h_bcc)
164 maxlines--;
165 if (hp->h_attach)
166 maxlines--;
167 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
168 maxlines -= value("ORGANIZATION") != NULL ||
169 hp->h_organization != NULL;
170 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
171 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
172 if ((long)maxlines < 0 || linecnt > maxlines) {
173 cp = get_pager();
174 if (sigsetjmp(pipejmp, 1))
175 goto endpipe;
176 obuf = Popen(cp, "w", NULL, 1);
177 if (obuf == NULL) {
178 perror(cp);
179 obuf = stdout;
180 } else
181 safe_signal(SIGPIPE, onpipe);
184 fprintf(obuf, catgets(catd, CATSET, 62,
185 "-------\nMessage contains:\n"));
186 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
187 if (value("fullnames"))
188 gf |= GCOMMA;
189 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
190 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
191 prout(lbuf, linelen, obuf);
192 if (hp->h_attach != NULL) {
193 fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf);
194 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
195 if (ap->a_msgno)
196 fprintf(obuf, " message %u", ap->a_msgno);
197 else
198 fprintf(obuf, " %s", ap->a_name);
199 if (ap->a_flink)
200 putc(',', obuf);
202 putc('\n', obuf);
204 endpipe:
205 if (obuf != stdout) {
206 safe_signal(SIGPIPE, SIG_IGN);
207 Pclose(obuf);
208 safe_signal(SIGPIPE, dflpipe);
210 if (lbuf)
211 free(lbuf);
214 static int
215 include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo)
217 char *interactive, *linebuf = NULL;
218 size_t linesize = 0, linelen, count;
220 if (fbuf == NULL)
221 fbuf = Fopen(name, "r");
222 if (fbuf == NULL) {
223 perror(name);
224 return (-1);
226 interactive = value("interactive");
227 *linecount = 0;
228 *charcount = 0;
229 fflush(fbuf);
230 rewind(fbuf);
231 count = fsize(fbuf);
232 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
233 != NULL) {
234 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
235 != linelen) {
236 Fclose(fbuf);
237 return (-1);
239 if (interactive != NULL && echo)
240 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
241 (*linecount)++;
242 (*charcount) += linelen;
244 if (linebuf)
245 free(linebuf);
246 Fclose(fbuf);
247 if (fflush(collf))
248 return (-1);
249 return (0);
253 * Ask the user to edit file names and other data for the given
254 * attachment. NULL is returned if no file name is given.
256 static struct attachment *
257 read_attachment_data(struct attachment *ap, unsigned number)
259 char prefix[80], *cp;
261 if (ap == NULL)
262 ap = csalloc(1, sizeof *ap);
263 if (ap->a_msgno) {
264 printf("#%u\tmessage %u\n", number, ap->a_msgno);
265 return ap;
267 snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50,
268 "#%u\tfilename: "), number);
269 for (;;) {
270 char *exf;
271 if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL)
272 break;
273 if ((exf = file_expand(ap->a_name)) == NULL)
274 continue;
275 ap->a_name = exf;
276 if (access(ap->a_name, R_OK) == 0)
277 break;
278 perror(ap->a_name);
280 if ((ap->a_name && (value("attachment-ask-charset"))) ||
281 ((cp = value("sendcharsets")) != NULL &&
282 strchr(cp, ',') != NULL)) {
283 snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number);
284 ap->a_charset = readtty(prefix, ap->a_charset);
287 * The "attachment-ask-content-*" variables are left undocumented
288 * since they are for RFC connoisseurs only.
290 if (ap->a_name && value("attachment-ask-content-type")) {
291 if (ap->a_content_type == NULL)
292 ap->a_content_type = mime_filecontent(ap->a_name);
293 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
294 ap->a_content_type = readtty(prefix, ap->a_content_type);
296 if (ap->a_name && value("attachment-ask-content-disposition")) {
297 snprintf(prefix, sizeof prefix,
298 "#%u\tContent-Disposition: ", number);
299 ap->a_content_disposition = readtty(prefix,
300 ap->a_content_disposition);
302 if (ap->a_name && value("attachment-ask-content-id")) {
303 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
304 ap->a_content_id = readtty(prefix, ap->a_content_id);
306 if (ap->a_name && value("attachment-ask-content-description")) {
307 snprintf(prefix, sizeof prefix,
308 "#%u\tContent-Description: ", number);
309 ap->a_content_description = readtty(prefix,
310 ap->a_content_description);
312 return ap->a_name ? ap : NULL;
316 * Interactively edit the attachment list.
318 struct attachment *
319 edit_attachments(struct attachment *attach)
321 struct attachment *ap, *nap;
322 unsigned attno = 1;
324 for (ap = attach; ap; ap = ap->a_flink) {
325 if ((nap = read_attachment_data(ap, attno)) == NULL) {
326 if (ap->a_blink)
327 ap->a_blink->a_flink = ap->a_flink;
328 else
329 attach = ap->a_flink;
330 if (ap->a_flink)
331 ap->a_flink->a_blink = ap->a_blink;
332 else
333 return attach;
334 } else
335 attno++;
337 while ((nap = read_attachment_data(NULL, attno)) != NULL) {
338 for (ap = attach; ap && ap->a_flink; ap = ap->a_flink);
339 if (ap)
340 ap->a_flink = nap;
341 nap->a_blink = ap;
342 nap->a_flink = NULL;
343 if (attach == NULL)
344 attach = nap;
345 attno++;
347 return attach;
351 * Put the given file to the end of the attachment list.
353 struct attachment *
354 add_attachment(struct attachment *attach, char *file, int expand_file)
356 struct attachment *ap, *nap;
358 if (expand_file) {
359 file = file_expand(file);
360 if (file == NULL)
361 return NULL;
362 } else
363 file = savestr(file);
364 if (access(file, R_OK) != 0)
365 return NULL;
366 /*LINTED*/
367 nap = csalloc(1, sizeof *nap);
368 nap->a_name = file;
369 if (attach != NULL) {
370 for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink);
371 ap->a_flink = nap;
372 nap->a_blink = ap;
373 } else {
374 nap->a_blink = NULL;
375 attach = nap;
377 return attach;
381 * Append the whitespace-separated file names to the end of
382 * the attachment list.
384 static struct attachment *
385 append_attachments(struct attachment *attach, char *names)
387 char *cp;
388 int c;
389 struct attachment *ap;
391 cp = names;
392 while (*cp != '\0' && blankchar(*cp & 0377))
393 cp++;
394 while (*cp != '\0') {
395 names = cp;
396 while (*cp != '\0' && !blankchar(*cp & 0377))
397 cp++;
398 c = *cp;
399 *cp++ = '\0';
400 if (*names != '\0') {
401 if ((ap = add_attachment(attach, names, 1)) == NULL)
402 perror(names);
403 else
404 attach = ap;
406 if (c == '\0')
407 break;
408 while (*cp != '\0' && blankchar(*cp & 0377))
409 cp++;
411 return attach;
414 FILE *
415 collect(struct header *hp, int printheaders, struct message *mp,
416 char *quotefile, int doprefix, int volatile tflag)
418 enum {
419 val_INTERACT = 1
422 FILE *fbuf;
423 struct ignoretab *quoteig;
424 int lc, cc, eofcount, val, c, t;
425 int volatile escape, getfields;
426 char *linebuf = NULL, *cp, *quote = NULL, *tempMail = NULL;
427 size_t linesize;
428 long count;
429 enum sendaction action;
430 sigset_t oset, nset;
431 sighandler_type savedtop;
433 val = 0;
434 if (value("interactive") != NULL)
435 val |= val_INTERACT;
437 collf = NULL;
439 * Start catching signals from here, but we're still die on interrupts
440 * until we're in the main loop.
442 sigemptyset(&nset);
443 sigaddset(&nset, SIGINT);
444 sigaddset(&nset, SIGHUP);
445 sigprocmask(SIG_BLOCK, &nset, &oset);
446 handlerpush(collint);
447 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
448 safe_signal(SIGINT, collint);
449 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
450 safe_signal(SIGHUP, collhup);
451 savetstp = safe_signal(SIGTSTP, collstop);
452 savettou = safe_signal(SIGTTOU, collstop);
453 savettin = safe_signal(SIGTTIN, collstop);
454 if (sigsetjmp(collabort, 1))
455 goto jerr;
456 if (sigsetjmp(colljmp, 1))
457 goto jerr;
458 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
460 noreset++;
461 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
462 perror(tr(51, "temporary mail file"));
463 goto jerr;
465 unlink(tempMail);
466 Ftfree(&tempMail);
468 if ((cp = value("NAIL_HEAD")) != NULL && putesc(cp, collf) < 0)
469 goto jerr;
472 * If we are going to prompt for a subject,
473 * refrain from printing a newline after
474 * the headers (since some people mind).
476 getfields = 0;
477 if (! tflag) {
478 t = GTO|GSUBJECT|GCC|GNL;
479 if (value("fullnames"))
480 t |= GCOMMA;
481 if (hp->h_subject == NULL && (val & val_INTERACT) &&
482 (value("ask") != NULL || value("asksub") != NULL))
483 t &= ~GNL, getfields |= GSUBJECT;
484 if (hp->h_to == NULL && (val & val_INTERACT))
485 t &= ~GNL, getfields |= GTO;
486 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
487 (val & val_INTERACT)) {
488 if (hp->h_bcc == NULL && value("askbcc"))
489 t &= ~GNL, getfields |= GBCC;
490 if (hp->h_cc == NULL && value("askcc"))
491 t &= ~GNL, getfields |= GCC;
493 if (printheaders) {
494 (void)puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
495 NULL, NULL);
496 (void)fflush(stdout);
501 * Quote an original message
503 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
504 quoteig = allignore;
505 action = SEND_QUOTE;
506 if (doprefix) {
507 quoteig = fwdignore;
508 if ((cp = value("fwdheading")) == NULL)
509 cp = "-------- Original Message --------";
510 if (*cp && fprintf(collf, "%s\n", cp) < 0)
511 goto jerr;
512 } else if (strcmp(quote, "noheading") == 0) {
513 /*EMPTY*/;
514 } else if (strcmp(quote, "headers") == 0) {
515 quoteig = ignore;
516 } else if (strcmp(quote, "allheaders") == 0) {
517 quoteig = NULL;
518 action = SEND_QUOTE_ALL;
519 } else {
520 cp = hfield1("from", mp);
521 if (cp != NULL && (count = (long)strlen(cp)) > 0) {
522 if (mime_write(cp, count,
523 collf, CONV_FROMHDR, TD_NONE,
524 NULL, (size_t) 0,
525 NULL, NULL) == 0)
526 goto jerr;
527 if (fprintf(collf, tr(52, " wrote:\n\n")) < 0)
528 goto jerr;
531 if (fflush(collf))
532 goto jerr;
533 cp = value("indentprefix");
534 if (cp != NULL && *cp == '\0')
535 cp = "\t";
536 if (send(mp, collf, quoteig, (doprefix ? NULL : cp), action,
537 NULL) < 0)
538 goto jerr;
541 /* Print what we have sofar also on the terminal */
542 (void)rewind(collf);
543 while ((c = getc(collf)) != EOF)
544 (void)putc(c, stdout);
545 if (fseek(collf, 0, SEEK_END))
546 goto jerr;
548 escape = ((cp = value("escape")) != NULL) ? *cp : ESCAPE;
549 eofcount = 0;
550 hadintr = 0;
552 if (! sigsetjmp(colljmp, 1)) {
553 if (getfields)
554 grabh(hp, getfields, 1);
555 if (quotefile != NULL) {
556 if (include_file(NULL, quotefile, &lc, &cc, 1) != 0)
557 goto jerr;
559 } else {
561 * Come here for printing the after-signal message.
562 * Duplicate messages won't be printed because
563 * the write is aborted if we get a SIGTTOU.
565 jcont:
566 if (hadintr) {
567 (void)fprintf(stderr, tr(53,
568 "\n(Interrupt -- one more to kill letter)\n"));
569 } else {
570 printf(tr(54, "(continue)\n"));
571 (void)fflush(stdout);
576 * No tilde escapes, interrupts not expected. Simply copy STDIN
578 if ((val & val_INTERACT) == 0 && tildeflag <= 0 && ! tflag) {
579 linebuf = srealloc(linebuf, linesize = LINESIZE);
580 while ((count = fread(linebuf, sizeof *linebuf,
581 linesize, stdin)) > 0) {
582 if ((size_t)count != fwrite(linebuf, sizeof *linebuf,
583 count, collf))
584 goto jerr;
586 if (fflush(collf))
587 goto jerr;
588 goto jout;
592 * The interactive collect loop
594 for (;;) {
595 colljmp_p = 1;
596 count = readline(stdin, &linebuf, &linesize);
597 colljmp_p = 0;
598 if (count < 0) {
599 if ((val & val_INTERACT) &&
600 value("ignoreeof") != NULL && ++eofcount < 25) {
601 printf(tr(55,
602 "Use \".\" to terminate letter\n"));
603 continue;
605 break;
607 if (tflag && count == 0) {
608 rewind(collf);
609 if (makeheader(collf, hp) != OKAY)
610 goto jerr;
611 rewind(collf);
612 tflag = 0;
613 continue;
615 eofcount = 0;
616 hadintr = 0;
617 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
618 (val & val_INTERACT) &&
619 (value("dot") != NULL ||
620 value("ignoreeof") != NULL))
621 break;
622 if (linebuf[0] != escape ||
623 ((val & val_INTERACT) == 0 && tildeflag == 0) ||
624 tildeflag < 0) {
625 if (putline(collf, linebuf, count) < 0)
626 goto jerr;
627 continue;
630 c = linebuf[1];
631 switch (c) {
632 default:
634 * On double escape, just send the single one.
635 * Otherwise, it's an error.
637 if (c == escape) {
638 if (putline(collf, &linebuf[1], count - 1) < 0)
639 goto jerr;
640 else
641 break;
643 printf(tr(56, "Unknown tilde escape.\n"));
644 break;
645 #ifdef HAVE_ASSERTS
646 case 'C':
647 /* Dump core */
648 core(NULL);
649 break;
650 #endif
651 case '!':
652 /* Shell escape, send the balance of line to sh -c */
653 shell(&linebuf[2]);
654 break;
655 case ':':
656 case '_':
657 /* Escape to command mode, but be nice! */
658 inhook = 0;
659 execute(&linebuf[2], 1, count - 2);
660 goto jcont;
661 case '.':
662 /* Simulate end of file on input */
663 goto jout;
664 case 'x':
665 /* Same as 'q', but no dead.letter saving */
666 hadintr++;
667 collint(0);
668 exit(1);
669 /*NOTREACHED*/
670 case 'q':
672 * Force a quit of sending mail.
673 * Act like an interrupt happened.
675 hadintr++;
676 collint(SIGINT);
677 exit(1);
678 /*NOTREACHED*/
679 case 'h':
680 /* Grab a bunch of headers */
682 grabh(hp, GTO|GSUBJECT|GCC|GBCC,
683 value("bsdcompat") != NULL &&
684 value("bsdorder") != NULL);
685 while (hp->h_to == NULL);
686 goto jcont;
687 case 'H':
688 /* Grab extra headers */
690 grabh(hp, GEXTRA, 0);
691 while (check_from_and_sender(hp->h_from, hp->h_sender));
692 goto jcont;
693 case 't':
694 /* Add to the To list */
695 while ((hp->h_to = cat(hp->h_to, checkaddrs(
696 lextract(&linebuf[2], GTO|GFULL))))
697 == NULL)
699 break;
700 case 's':
701 /* Set the Subject list */
702 cp = &linebuf[2];
703 while (whitechar(*cp))
704 ++cp;
705 hp->h_subject = savestr(cp);
706 break;
707 case '@':
708 /* Edit the attachment list */
709 if (linebuf[2] != '\0')
710 hp->h_attach = append_attachments(hp->h_attach,
711 &linebuf[2]);
712 else
713 hp->h_attach = edit_attachments(hp->h_attach);
714 break;
715 case 'c':
716 /* Add to the CC list */
717 hp->h_cc = cat(hp->h_cc, checkaddrs(
718 lextract(&linebuf[2], GCC|GFULL)));
719 break;
720 case 'b':
721 /* Add stuff to blind carbon copies list */
722 hp->h_bcc = cat(hp->h_bcc, checkaddrs(
723 lextract(&linebuf[2], GBCC|GFULL)));
724 break;
725 case 'd':
726 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
727 linebuf[linesize-1]='\0';
728 /*FALLTHRU*/
729 case 'r':
730 case '<':
732 * Invoke a file:
733 * Search for the file name,
734 * then open it and copy the contents to collf.
736 cp = &linebuf[2];
737 while (whitechar(*cp))
738 cp++;
739 if (*cp == '\0') {
740 printf(tr(57, "Interpolate what file?\n"));
741 break;
743 if (*cp == '!') {
744 insertcommand(collf, cp + 1);
745 break;
747 cp = file_expand(cp);
748 if (cp == NULL)
749 break;
750 if (is_dir(cp)) {
751 printf(tr(58, "%s: Directory\n"), cp);
752 break;
754 if ((fbuf = Fopen(cp, "r")) == NULL) {
755 perror(cp);
756 break;
758 printf(tr(59, "\"%s\" "), cp);
759 fflush(stdout);
760 if (include_file(fbuf, cp, &lc, &cc, 0) != 0)
761 goto jerr;
762 printf(tr(60, "%d/%d\n"), lc, cc);
763 break;
764 case 'i':
765 /* Insert an environment variable into the file */
766 cp = &linebuf[2];
767 while (whitechar(*cp))
768 ++cp;
769 if ((cp = value(cp)) == NULL || *cp == '\0')
770 break;
771 if (putesc(cp, collf) < 0)
772 goto jerr;
773 if ((val & val_INTERACT) && putesc(cp, stdout) < 0)
774 goto jerr;
775 break;
776 case 'a':
777 case 'A':
778 /* Insert the contents of a signature variable */
779 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
780 *cp != '\0') {
781 if (putesc(cp, collf) < 0)
782 goto jerr;
783 if ((val & val_INTERACT) &&
784 putesc(cp, stdout) < 0)
785 goto jerr;
787 break;
788 case 'w':
789 /* Write the message on a file */
790 cp = &linebuf[2];
791 while (blankchar(*cp))
792 ++cp;
793 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
794 fprintf(stderr, tr(61, "Write what file!?\n"));
795 break;
797 rewind(collf);
798 if (exwrite(cp, collf, 1) < 0)
799 goto jerr;
800 break;
801 case 'm':
802 case 'M':
803 case 'f':
804 case 'F':
806 * Interpolate the named messages, if we
807 * are in receiving mail mode. Does the
808 * standard list processing garbage.
809 * If ~f is given, we don't shift over.
811 if (forward(linebuf + 2, collf, c) < 0)
812 goto jerr;
813 goto jcont;
814 case 'p':
816 * Print out the current state of the
817 * message without altering anything.
819 print_collf(collf, hp);
820 goto jcont;
821 case '|':
823 * Pipe message through command.
824 * Collect output as new message.
826 rewind(collf);
827 mespipe(&linebuf[2]);
828 goto jcont;
829 case 'v':
830 case 'e':
832 * Edit the current message.
833 * 'e' means to use EDITOR
834 * 'v' means to use VISUAL
836 rewind(collf);
837 mesedit(c, value("editheaders") ? hp : NULL);
838 goto jcont;
839 case '?':
841 * Last the lengthy help string.
842 * (Very ugly, but take care for compiler supported
843 * string lengths :()
845 (void)puts(
846 "-------------------- ~ ESCAPES ----------------------------\n"
847 "~~ Quote a single tilde\n"
848 "~@ [file ...] Edit attachment list\n"
849 "~b users Add users to \"blind\" cc list\n"
850 "~c users Add users to cc list\n"
851 "~d Read in dead.letter\n"
852 "~e Edit the message buffer\n"
853 "~f messages Read in messages without indenting lines\n"
854 "~F messages Same as ~f, but keep all header lines\n"
855 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n");
856 (void)puts(
857 "~r file Read a file into the message buffer\n"
858 "~p Print the message buffer\n"
859 "~q Abort message composition and save text to dead.letter\n"
860 "~m messages Read in messages with each line indented\n"
861 "~M messages Same as ~m, but keep all header lines\n"
862 "~s subject Set subject\n"
863 "~t users Add users to to list\n"
864 "~v Invoke display editor on message\n"
865 "~w file Write message onto file\n"
866 "~x Abort message composition and discard text written so far\n");
867 (void)puts(
868 "~!command Invoke the shell\n"
869 "~:command Execute a regular command\n"
870 "-----------------------------------------------------------\n");
871 break;
875 jout:
876 if (collf != NULL) {
877 if ((cp = value("NAIL_TAIL")) != NULL) {
878 if (putesc(cp, collf) < 0)
879 goto jerr;
880 if ((val & val_INTERACT) && putesc(cp, stdout) < 0)
881 goto jerr;
883 rewind(collf);
885 handlerpop();
886 noreset--;
887 sigemptyset(&nset);
888 sigaddset(&nset, SIGINT);
889 sigaddset(&nset, SIGHUP);
890 sigprocmask(SIG_BLOCK, &nset, (sigset_t*)NULL);
891 safe_signal(SIGINT, saveint);
892 safe_signal(SIGHUP, savehup);
893 safe_signal(SIGTSTP, savetstp);
894 safe_signal(SIGTTOU, savettou);
895 safe_signal(SIGTTIN, savettin);
896 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
897 return (collf);
898 jerr:
899 if (tempMail != NULL) {
900 rm(tempMail);
901 Ftfree(&tempMail);
903 if (collf != NULL) {
904 Fclose(collf);
905 collf = NULL;
907 goto jout;
911 * Write a file, ex-like if f set.
913 static int
914 exwrite(char *name, FILE *fp, int f)
916 FILE *of;
917 int c;
918 long cc;
919 int lc;
921 if (f) {
922 printf("\"%s\" ", name);
923 fflush(stdout);
925 if ((of = Fopen(name, "a")) == NULL) {
926 perror(NULL);
927 return(-1);
929 lc = 0;
930 cc = 0;
931 while ((c = getc(fp)) != EOF) {
932 cc++;
933 if (c == '\n')
934 lc++;
935 putc(c, of);
936 if (ferror(of)) {
937 perror(name);
938 Fclose(of);
939 return(-1);
942 Fclose(of);
943 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
944 fflush(stdout);
945 return(0);
948 static enum okay
949 makeheader(FILE *fp, struct header *hp)
951 char *tempEdit;
952 FILE *nf;
953 int c;
955 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
956 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
957 Fclose(nf);
958 unlink(tempEdit);
959 Ftfree(&tempEdit);
960 return STOP;
962 unlink(tempEdit);
963 Ftfree(&tempEdit);
964 extract_header(fp, hp);
965 while ((c = getc(fp)) != EOF)
966 putc(c, nf);
967 if (fp != collf)
968 Fclose(collf);
969 Fclose(fp);
970 collf = nf;
971 if (check_from_and_sender(hp->h_from, hp->h_sender))
972 return STOP;
973 return OKAY;
977 * Edit the message being collected on fp.
978 * On return, make the edit file the new temp file.
980 static void
981 mesedit(int c, struct header *hp)
983 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
984 FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX,
985 sigint);
987 if (nf != NULL) {
988 if (hp) {
989 rewind(nf);
990 makeheader(nf, hp);
991 } else {
992 fseek(nf, 0L, SEEK_END);
993 Fclose(collf);
994 collf = nf;
997 safe_signal(SIGINT, sigint);
1001 * Pipe the message through the command.
1002 * Old message is on stdin of command;
1003 * New message collected from stdout.
1004 * Sh -c must return 0 to accept the new message.
1006 static void
1007 mespipe(char *cmd)
1009 FILE *nf;
1010 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1011 char *tempEdit;
1012 char *shell;
1014 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1015 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1016 goto out;
1018 fflush(collf);
1019 unlink(tempEdit);
1020 Ftfree(&tempEdit);
1022 * stdin = current message.
1023 * stdout = new message.
1025 if ((shell = value("SHELL")) == NULL)
1026 shell = SHELL;
1027 if (run_command(shell,
1028 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
1029 Fclose(nf);
1030 goto out;
1032 if (fsize(nf) == 0) {
1033 fprintf(stderr, catgets(catd, CATSET, 67,
1034 "No bytes from \"%s\" !?\n"), cmd);
1035 Fclose(nf);
1036 goto out;
1039 * Take new files.
1041 fseek(nf, 0L, SEEK_END);
1042 Fclose(collf);
1043 collf = nf;
1044 out:
1045 safe_signal(SIGINT, sigint);
1049 * Interpolate the named messages into the current
1050 * message, preceding each line with a tab.
1051 * Return a count of the number of characters now in
1052 * the message, or -1 if an error is encountered writing
1053 * the message temporary. The flag argument is 'm' if we
1054 * should shift over and 'f' if not.
1056 static int
1057 forward(char *ms, FILE *fp, int f)
1059 int *msgvec;
1060 struct ignoretab *ig;
1061 char *tabst;
1062 enum sendaction action;
1064 /*LINTED*/
1065 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
1066 if (msgvec == NULL)
1067 return(0);
1068 if (getmsglist(ms, msgvec, 0) < 0)
1069 return(0);
1070 if (*msgvec == 0) {
1071 *msgvec = first(0, MMNORM);
1072 if (*msgvec == 0) {
1073 printf(catgets(catd, CATSET, 68,
1074 "No appropriate messages\n"));
1075 return(0);
1077 msgvec[1] = 0;
1079 if (f == 'f' || f == 'F')
1080 tabst = NULL;
1081 else if ((tabst = value("indentprefix")) == NULL)
1082 tabst = "\t";
1083 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
1084 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
1085 printf(catgets(catd, CATSET, 69, "Interpolating:"));
1086 for (; *msgvec != 0; msgvec++) {
1087 struct message *mp = message + *msgvec - 1;
1089 touch(mp);
1090 printf(" %d", *msgvec);
1091 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
1092 perror(catgets(catd, CATSET, 70,
1093 "temporary mail file"));
1094 return(-1);
1097 printf("\n");
1098 return(0);
1102 * Print (continue) when continued after ^Z.
1104 /*ARGSUSED*/
1105 static void
1106 collstop(int s)
1108 sighandler_type old_action = safe_signal(s, SIG_DFL);
1109 sigset_t nset;
1111 sigemptyset(&nset);
1112 sigaddset(&nset, s);
1113 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
1114 kill(0, s);
1115 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
1116 safe_signal(s, old_action);
1117 if (colljmp_p) {
1118 colljmp_p = 0;
1119 hadintr = 0;
1120 siglongjmp(colljmp, 1);
1125 * On interrupt, come here to save the partial message in ~/dead.letter.
1126 * Then jump out of the collection loop.
1128 /*ARGSUSED*/
1129 static void
1130 collint(int s)
1133 * the control flow is subtle, because we can be called from ~q.
1135 if (!hadintr) {
1136 if (value("ignore") != NULL) {
1137 puts("@");
1138 fflush(stdout);
1139 clearerr(stdin);
1140 return;
1142 hadintr = 1;
1143 siglongjmp(colljmp, 1);
1145 exit_status |= 04;
1146 rewind(collf);
1147 if (value("save") != NULL && s != 0)
1148 savedeadletter(collf);
1149 siglongjmp(collabort, 1);
1152 /*ARGSUSED*/
1153 static void
1154 collhup(int s)
1156 (void)s;
1157 rewind(collf);
1158 savedeadletter(collf);
1160 * Let's pretend nobody else wants to clean up,
1161 * a true statement at this time.
1163 exit(1);
1166 void
1167 savedeadletter(FILE *fp)
1169 FILE *dbuf;
1170 int c, lines = 0, bytes = 0;
1171 char *cp;
1173 if (fsize(fp) == 0)
1174 return;
1175 cp = getdeadletter();
1176 c = umask(077);
1177 dbuf = Fopen(cp, "a");
1178 umask(c);
1179 if (dbuf == NULL)
1180 return;
1181 printf("\"%s\" ", cp);
1182 while ((c = getc(fp)) != EOF) {
1183 putc(c, dbuf);
1184 bytes++;
1185 if (c == '\n')
1186 lines++;
1188 Fclose(dbuf);
1189 printf("%d/%d\n", lines, bytes);
1190 rewind(fp);
1193 static int
1194 putesc(const char *s, FILE *stream)
1196 int n = 0;
1198 while (s[0]) {
1199 if (s[0] == '\\') {
1200 if (s[1] == 't') {
1201 if (putc('\t', stream) == EOF)
1202 return (-1);
1203 n++;
1204 s += 2;
1205 continue;
1207 if (s[1] == 'n') {
1208 if (putc('\n', stream) == EOF)
1209 return (-1);
1210 n++;
1211 s += 2;
1212 continue;
1215 if (putc(s[0], stream) == EOF)
1216 return (-1);
1217 n++;
1218 s++;
1220 if (putc('\n', stream) == EOF)
1221 return (-1);
1222 return (++n);