base64.c: fix compiler warnings
[s-mailx.git] / collect.c
blob235d668a38246ca0af5a4193932a118a77cbab68
1 /*
2 * Heirloom mailx - 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 * All rights reserved.
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 #ifndef lint
42 #ifdef DOSCCS
43 static char sccsid[] = "@(#)collect.c 2.54 (gritter) 6/16/07";
44 #endif
45 #endif /* not lint */
48 * Mail -- a mail program
50 * Collect input from standard input, handling
51 * ~ escapes.
54 #include "rcv.h"
55 #include "extern.h"
56 #include <unistd.h>
57 #include <sys/stat.h>
60 * Read a message from standard output and return a read file to it
61 * or NULL on error.
65 * The following hokiness with global variables is so that on
66 * receipt of an interrupt signal, the partial message can be salted
67 * away on dead.letter.
70 static sighandler_type saveint; /* Previous SIGINT value */
71 static sighandler_type savehup; /* Previous SIGHUP value */
72 static sighandler_type savetstp; /* Previous SIGTSTP value */
73 static sighandler_type savettou; /* Previous SIGTTOU value */
74 static sighandler_type savettin; /* Previous SIGTTIN value */
75 static FILE *collf; /* File for saving away */
76 static int hadintr; /* Have seen one SIGINT so far */
78 static sigjmp_buf colljmp; /* To get back to work */
79 static int colljmp_p; /* whether to long jump */
80 static sigjmp_buf collabort; /* To end collection with error */
82 static sigjmp_buf pipejmp; /* On broken pipe */
84 static void onpipe(int signo);
85 static void insertcommand(FILE *fp, char *cmd);
86 static void print_collf(FILE *collf, struct header *hp);
87 static int include_file(FILE *fbuf, char *name, int *linecount,
88 int *charcount, int echo);
89 static struct attachment *read_attachment_data(struct attachment *ap,
90 unsigned number);
91 static struct attachment *append_attachments(struct attachment *attach,
92 char *names);
93 static int exwrite(char *name, FILE *fp, int f);
94 static enum okay makeheader(FILE *fp, struct header *hp);
95 static void mesedit(int c, struct header *hp);
96 static void mespipe(char *cmd);
97 static int forward(char *ms, FILE *fp, int f);
98 static void collstop(int s);
99 static void collint(int s);
100 static void collhup(int s);
101 static int putesc(const char *s, FILE *stream);
103 /*ARGSUSED*/
104 static void
105 onpipe(int signo)
107 siglongjmp(pipejmp, 1);
111 * Execute cmd and insert its standard output into fp.
113 static void
114 insertcommand(FILE *fp, char *cmd)
116 FILE *obuf = NULL;
117 char *cp;
118 int c;
120 (void)&obuf;
121 (void)&cp;
122 cp = value("SHELL");
123 if (sigsetjmp(pipejmp, 1))
124 goto endpipe;
125 if (cp == NULL)
126 cp = SHELL;
127 if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) {
128 perror(cmd);
129 return;
131 safe_signal(SIGPIPE, onpipe);
132 while ((c = getc(obuf)) != EOF)
133 putc(c, fp);
134 endpipe:
135 safe_signal(SIGPIPE, SIG_IGN);
136 Pclose(obuf);
137 safe_signal(SIGPIPE, dflpipe);
141 * ~p command.
143 static void
144 print_collf(FILE *collf, struct header *hp)
146 char *lbuf = NULL;
147 FILE *obuf = stdout;
148 struct attachment *ap;
149 char *cp;
150 enum gfield gf;
151 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
153 (void)&obuf;
154 (void)&cp;
155 fflush(collf);
156 rewind(collf);
157 count = count2 = fsize(collf);
158 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
159 for (linecnt = 0;
160 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
161 linecnt++);
162 rewind(collf);
163 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
164 maxlines -= 4;
165 if (hp->h_to)
166 maxlines--;
167 if (hp->h_subject)
168 maxlines--;
169 if (hp->h_cc)
170 maxlines--;
171 if (hp->h_bcc)
172 maxlines--;
173 if (hp->h_attach)
174 maxlines--;
175 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
176 maxlines -= value("ORGANIZATION") != NULL ||
177 hp->h_organization != NULL;
178 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
179 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
180 if ((long)maxlines < 0 || linecnt > maxlines) {
181 cp = get_pager();
182 if (sigsetjmp(pipejmp, 1))
183 goto endpipe;
184 obuf = Popen(cp, "w", NULL, 1);
185 if (obuf == NULL) {
186 perror(cp);
187 obuf = stdout;
188 } else
189 safe_signal(SIGPIPE, onpipe);
192 fprintf(obuf, catgets(catd, CATSET, 62,
193 "-------\nMessage contains:\n"));
194 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
195 if (value("fullnames"))
196 gf |= GCOMMA;
197 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
198 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
199 prout(lbuf, linelen, obuf);
200 if (hp->h_attach != NULL) {
201 fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf);
202 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
203 if (ap->a_msgno)
204 fprintf(obuf, " message %u", ap->a_msgno);
205 else
206 fprintf(obuf, " %s", ap->a_name);
207 if (ap->a_flink)
208 putc(',', obuf);
210 putc('\n', obuf);
212 endpipe:
213 if (obuf != stdout) {
214 safe_signal(SIGPIPE, SIG_IGN);
215 Pclose(obuf);
216 safe_signal(SIGPIPE, dflpipe);
218 if (lbuf)
219 free(lbuf);
222 static int
223 include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo)
225 char *interactive, *linebuf = NULL;
226 size_t linesize = 0, linelen, count;
228 if (fbuf == NULL)
229 fbuf = Fopen(name, "r");
230 if (fbuf == NULL) {
231 perror(name);
232 return -1;
234 interactive = value("interactive");
235 *linecount = 0;
236 *charcount = 0;
237 fflush(fbuf);
238 rewind(fbuf);
239 count = fsize(fbuf);
240 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
241 != NULL) {
242 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
243 != linelen) {
244 Fclose(fbuf);
245 return -1;
247 if (interactive != NULL && echo)
248 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
249 (*linecount)++;
250 (*charcount) += linelen;
252 if (linebuf)
253 free(linebuf);
254 Fclose(fbuf);
255 return 0;
259 * Ask the user to edit file names and other data for the given
260 * attachment. NULL is returned if no file name is given.
262 static struct attachment *
263 read_attachment_data(struct attachment *ap, unsigned number)
265 char prefix[80], *cp;
267 if (ap == NULL)
268 ap = csalloc(1, sizeof *ap);
269 if (ap->a_msgno) {
270 printf("#%u\tmessage %u\n", number, ap->a_msgno);
271 return ap;
273 snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50,
274 "#%u\tfilename: "), number);
275 for (;;) {
276 char *exf;
277 if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL)
278 break;
279 if ((exf = expand(ap->a_name)) != NULL)
280 ap->a_name = exf;
281 if (access(ap->a_name, R_OK) == 0)
282 break;
283 perror(ap->a_name);
285 if (ap->a_name && (value("attachment-ask-charset") ||
286 (cp = value("sendcharsets")) != NULL &&
287 strchr(cp, ',') != NULL)) {
288 snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number);
289 ap->a_charset = readtty(prefix, ap->a_charset);
292 * The "attachment-ask-content-*" variables are left undocumented
293 * since they are for RFC connoisseurs only.
295 if (ap->a_name && value("attachment-ask-content-type")) {
296 if (ap->a_content_type == NULL)
297 ap->a_content_type = mime_filecontent(ap->a_name);
298 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
299 ap->a_content_type = readtty(prefix, ap->a_content_type);
301 if (ap->a_name && value("attachment-ask-content-disposition")) {
302 snprintf(prefix, sizeof prefix,
303 "#%u\tContent-Disposition: ", number);
304 ap->a_content_disposition = readtty(prefix,
305 ap->a_content_disposition);
307 if (ap->a_name && value("attachment-ask-content-id")) {
308 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
309 ap->a_content_id = readtty(prefix, ap->a_content_id);
311 if (ap->a_name && value("attachment-ask-content-description")) {
312 snprintf(prefix, sizeof prefix,
313 "#%u\tContent-Description: ", number);
314 ap->a_content_description = readtty(prefix,
315 ap->a_content_description);
317 return ap->a_name ? ap : NULL;
321 * Interactively edit the attachment list.
323 struct attachment *
324 edit_attachments(struct attachment *attach)
326 struct attachment *ap, *nap;
327 unsigned attno = 1;
329 for (ap = attach; ap; ap = ap->a_flink) {
330 if ((nap = read_attachment_data(ap, attno)) == NULL) {
331 if (ap->a_blink)
332 ap->a_blink->a_flink = ap->a_flink;
333 else
334 attach = ap->a_flink;
335 if (ap->a_flink)
336 ap->a_flink->a_blink = ap->a_blink;
337 else
338 return attach;
339 } else
340 attno++;
342 while ((nap = read_attachment_data(NULL, attno)) != NULL) {
343 for (ap = attach; ap && ap->a_flink; ap = ap->a_flink);
344 if (ap)
345 ap->a_flink = nap;
346 nap->a_blink = ap;
347 nap->a_flink = NULL;
348 if (attach == NULL)
349 attach = nap;
350 attno++;
352 return attach;
356 * Put the given file to the end of the attachment list.
358 struct attachment *
359 add_attachment(struct attachment *attach, char *file, int expand_file)
361 struct attachment *ap, *nap;
363 if (expand_file) {
364 file = expand(file);
365 if (file == NULL)
366 return NULL;
367 } else
368 file = savestr(file);
369 if (access(file, R_OK) != 0)
370 return NULL;
371 /*LINTED*/
372 nap = csalloc(1, sizeof *nap);
373 nap->a_name = file;
374 if (attach != NULL) {
375 for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink);
376 ap->a_flink = nap;
377 nap->a_blink = ap;
378 } else {
379 nap->a_blink = NULL;
380 attach = nap;
382 return attach;
386 * Append the whitespace-separated file names to the end of
387 * the attachment list.
389 static struct attachment *
390 append_attachments(struct attachment *attach, char *names)
392 char *cp;
393 int c;
394 struct attachment *ap;
396 cp = names;
397 while (*cp != '\0' && blankchar(*cp & 0377))
398 cp++;
399 while (*cp != '\0') {
400 names = cp;
401 while (*cp != '\0' && !blankchar(*cp & 0377))
402 cp++;
403 c = *cp;
404 *cp++ = '\0';
405 if (*names != '\0') {
406 if ((ap = add_attachment(attach, names, 1)) == NULL)
407 perror(names);
408 else
409 attach = ap;
411 if (c == '\0')
412 break;
413 while (*cp != '\0' && blankchar(*cp & 0377))
414 cp++;
416 return attach;
419 FILE *
420 collect(struct header *hp, int printheaders, struct message *mp,
421 char *quotefile, int doprefix, int tflag)
423 FILE *fbuf;
424 struct ignoretab *quoteig;
425 int lc, cc, escape, eofcount;
426 int c, t;
427 char *linebuf = NULL, *cp, *quote = NULL;
428 size_t linesize;
429 char *tempMail = NULL;
430 int getfields;
431 sigset_t oset, nset;
432 long count;
433 enum sendaction action;
434 sighandler_type savedtop;
435 const char tildehelp[] =
436 "-------------------- ~ ESCAPES ----------------------------\n\
437 ~~ Quote a single tilde\n\
438 ~@ [file ...] Edit attachment list\n\
439 ~b users Add users to \"blind\" cc list\n\
440 ~c users Add users to cc list\n\
441 ~d Read in dead.letter\n\
442 ~e Edit the message buffer\n\
443 ~f messages Read in messages without indenting lines\n\
444 ~F messages Same as ~f, but keep all header lines\n\
445 ~h Prompt for to list, subject, cc, and \"blind\" cc list\n\
446 ~r file Read a file into the message buffer\n\
447 ~p Print the message buffer\n\
448 ~q Abort message composition and save text to dead.letter\n\
449 ~m messages Read in messages with each line indented\n\
450 ~M messages Same as ~m, but keep all header lines\n\
451 ~s subject Set subject\n\
452 ~t users Add users to to list\n\
453 ~v Invoke display editor on message\n\
454 ~w file Write message onto file\n\
455 ~x Abort message composition and discard text written so far\n\
456 ~!command Invoke the shell\n\
457 ~:command Execute a regular command\n\
458 -----------------------------------------------------------\n";
460 (void) &escape;
461 (void) &eofcount;
462 (void) &getfields;
463 (void) &tempMail;
464 (void) &tflag;
465 (void) &quote;
467 collf = NULL;
469 * Start catching signals from here, but we're still die on interrupts
470 * until we're in the main loop.
472 sigemptyset(&nset);
473 sigaddset(&nset, SIGINT);
474 sigaddset(&nset, SIGHUP);
475 sigprocmask(SIG_BLOCK, &nset, &oset);
476 handlerpush(collint);
477 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
478 safe_signal(SIGINT, collint);
479 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
480 safe_signal(SIGHUP, collhup);
481 savetstp = safe_signal(SIGTSTP, collstop);
482 savettou = safe_signal(SIGTTOU, collstop);
483 savettin = safe_signal(SIGTTIN, collstop);
484 if (sigsetjmp(collabort, 1)) {
485 if (tempMail != NULL) {
486 rm(tempMail);
487 Ftfree(&tempMail);
489 goto err;
491 if (sigsetjmp(colljmp, 1)) {
492 if (tempMail != NULL) {
493 rm(tempMail);
494 Ftfree(&tempMail);
496 goto err;
498 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
500 noreset++;
501 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
502 perror(catgets(catd, CATSET, 51, "temporary mail file"));
503 goto err;
505 unlink(tempMail);
506 Ftfree(&tempMail);
508 if ((cp = value("SNAIL_HEAD")) != NULL) {
509 if (is_a_tty[0])
510 putesc(cp, stdout);
511 putesc(cp, collf);
515 * If we are going to prompt for a subject,
516 * refrain from printing a newline after
517 * the headers (since some people mind).
519 getfields = 0;
520 if (!tflag) {
521 t = GTO|GSUBJECT|GCC|GNL;
522 if (value("fullnames"))
523 t |= GCOMMA;
524 if (hp->h_subject == NULL && value("interactive") != NULL &&
525 (value("ask") != NULL || value("asksub") != NULL))
526 t &= ~GNL, getfields |= GSUBJECT;
527 if (hp->h_to == NULL && value("interactive") != NULL)
528 t &= ~GNL, getfields |= GTO;
529 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
530 value("interactive")) {
531 if (hp->h_bcc == NULL && value("askbcc"))
532 t &= ~GNL, getfields |= GBCC;
533 if (hp->h_cc == NULL && value("askcc"))
534 t &= ~GNL, getfields |= GCC;
536 if (printheaders) {
537 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
538 NULL, NULL);
539 fflush(stdout);
544 * Quote an original message
546 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
547 quoteig = allignore;
548 action = SEND_QUOTE;
549 if (doprefix) {
550 quoteig = fwdignore;
551 if ((cp = value("fwdheading")) == NULL)
552 cp = "-------- Original Message --------";
553 if (*cp) {
554 fprintf(collf, "%s\n", cp);
555 fprintf(stdout, "%s\n", cp);
557 } else if (strcmp(quote, "noheading") == 0) {
558 /*EMPTY*/;
559 } else if (strcmp(quote, "headers") == 0) {
560 quoteig = ignore;
561 } else if (strcmp(quote, "allheaders") == 0) {
562 quoteig = NULL;
563 action = SEND_QUOTE_ALL;
564 } else {
565 cp = hfield("from", mp);
566 if (cp != NULL) {
567 mime_write(cp, strlen(cp),
568 collf, CONV_FROMHDR, TD_NONE,
569 NULL, (size_t) 0,
570 NULL, NULL);
571 mime_write(cp, strlen(cp),
572 stdout, CONV_FROMHDR, TD_NONE,
573 NULL, (size_t) 0,
574 NULL, NULL);
575 fwrite(catgets(catd, CATSET, 52,
576 " wrote:\n\n"), sizeof(char), 9, collf);
577 fwrite(catgets(catd, CATSET, 52,
578 " wrote:\n\n"), sizeof(char), 9, stdout);
581 cp = value("indentprefix");
582 if (cp != NULL && *cp == '\0')
583 cp = "\t";
584 send(mp, collf, quoteig, doprefix ? NULL : cp, action, NULL);
585 send(mp, stdout, quoteig, doprefix ? NULL : cp, action, NULL);
588 if ((cp = value("escape")) != NULL)
589 escape = *cp;
590 else
591 escape = ESCAPE;
592 eofcount = 0;
593 hadintr = 0;
595 if (!sigsetjmp(colljmp, 1)) {
596 if (getfields)
597 grabh(hp, getfields, 1);
598 if (quotefile != NULL) {
599 if (include_file(NULL, quotefile, &lc, &cc, 1) != 0)
600 goto err;
602 } else {
604 * Come here for printing the after-signal message.
605 * Duplicate messages won't be printed because
606 * the write is aborted if we get a SIGTTOU.
608 cont:
609 if (hadintr) {
610 fflush(stdout);
611 fprintf(stderr, catgets(catd, CATSET, 53,
612 "\n(Interrupt -- one more to kill letter)\n"));
613 } else {
614 printf(catgets(catd, CATSET, 54, "(continue)\n"));
615 fflush(stdout);
618 if (value("interactive") == NULL && tildeflag <= 0 && !is_a_tty[0] &&
619 !tflag) {
621 * No tilde escapes, interrupts not expected. Copy
622 * standard input the simple way.
624 linebuf = srealloc(linebuf, linesize = BUFSIZ);
625 while ((count = fread(linebuf, sizeof *linebuf,
626 linesize, stdin)) > 0) {
627 if (fwrite(linebuf, sizeof *linebuf,
628 count, collf) != count)
629 goto err;
631 goto out;
633 for (;;) {
634 colljmp_p = 1;
635 count = readline(stdin, &linebuf, &linesize);
636 colljmp_p = 0;
637 if (count < 0) {
638 if (value("interactive") != NULL &&
639 value("ignoreeof") != NULL && ++eofcount < 25) {
640 printf(catgets(catd, CATSET, 55,
641 "Use \".\" to terminate letter\n"));
642 continue;
644 break;
646 if (tflag && count == 0) {
647 rewind(collf);
648 if (makeheader(collf, hp) != OKAY)
649 goto err;
650 rewind(collf);
651 tflag = 0;
652 continue;
654 eofcount = 0;
655 hadintr = 0;
656 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
657 value("interactive") != NULL &&
658 (value("dot") != NULL || value("ignoreeof") != NULL))
659 break;
660 if (linebuf[0] != escape || (value("interactive") == NULL &&
661 tildeflag == 0 ||
662 tildeflag < 0)) {
663 if (putline(collf, linebuf, count) < 0)
664 goto err;
665 continue;
667 c = linebuf[1];
668 switch (c) {
669 default:
671 * On double escape, just send the single one.
672 * Otherwise, it's an error.
674 if (c == escape) {
675 if (putline(collf, &linebuf[1], count - 1) < 0)
676 goto err;
677 else
678 break;
680 printf(catgets(catd, CATSET, 56,
681 "Unknown tilde escape.\n"));
682 break;
683 #ifdef DEBUG_COMMANDS
684 case 'C':
686 * Dump core.
688 core(NULL);
689 break;
690 #endif /* DEBUG_COMMANDS */
691 case '!':
693 * Shell escape, send the balance of the
694 * line to sh -c.
696 shell(&linebuf[2]);
697 break;
698 case ':':
699 case '_':
701 * Escape to command mode, but be nice!
703 inhook = 0;
704 execute(&linebuf[2], 1, count - 2);
705 goto cont;
706 case '.':
708 * Simulate end of file on input.
710 goto out;
711 case 'x':
713 * Same as 'q', but no dead.letter saving.
715 hadintr++;
716 collint(0);
717 exit(1);
718 /*NOTREACHED*/
719 case 'q':
721 * Force a quit of sending mail.
722 * Act like an interrupt happened.
724 hadintr++;
725 collint(SIGINT);
726 exit(1);
727 /*NOTREACHED*/
728 case 'h':
730 * Grab a bunch of headers.
733 grabh(hp, GTO|GSUBJECT|GCC|GBCC,
734 value("bsdcompat") != NULL &&
735 value("bsdorder") != NULL);
736 while (hp->h_to == NULL);
737 goto cont;
738 case 'H':
740 * Grab extra headers.
743 grabh(hp, GEXTRA, 0);
744 while (check_from_and_sender(hp->h_from, hp->h_sender));
745 goto cont;
746 case 't':
748 * Add to the To list.
750 while ((hp->h_to = checkaddrs(cat(hp->h_to,
751 sextract(&linebuf[2], GTO|GFULL))))
752 == NULL);
753 break;
754 case 's':
756 * Set the Subject list.
758 cp = &linebuf[2];
759 while (whitechar(*cp & 0377))
760 cp++;
761 hp->h_subject = savestr(cp);
762 break;
763 case '@':
765 * Edit the attachment list.
767 if (linebuf[2] != '\0')
768 hp->h_attach = append_attachments(hp->h_attach,
769 &linebuf[2]);
770 else
771 hp->h_attach = edit_attachments(hp->h_attach);
772 break;
773 case 'c':
775 * Add to the CC list.
777 hp->h_cc = checkaddrs(cat(hp->h_cc,
778 sextract(&linebuf[2], GCC|GFULL)));
779 break;
780 case 'b':
782 * Add stuff to blind carbon copies list.
784 hp->h_bcc = checkaddrs(cat(hp->h_bcc,
785 sextract(&linebuf[2], GBCC|GFULL)));
786 break;
787 case 'd':
788 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
789 linebuf[linesize-1]='\0';
790 /*FALLTHRU*/
791 case 'r':
792 case '<':
794 * Invoke a file:
795 * Search for the file name,
796 * then open it and copy the contents to collf.
798 cp = &linebuf[2];
799 while (whitechar(*cp & 0377))
800 cp++;
801 if (*cp == '\0') {
802 printf(catgets(catd, CATSET, 57,
803 "Interpolate what file?\n"));
804 break;
806 if (*cp == '!') {
807 insertcommand(collf, cp + 1);
808 break;
810 cp = expand(cp);
811 if (cp == NULL)
812 break;
813 if (is_dir(cp)) {
814 printf(catgets(catd, CATSET, 58,
815 "%s: Directory\n"), cp);
816 break;
818 if ((fbuf = Fopen(cp, "r")) == NULL) {
819 perror(cp);
820 break;
822 printf(catgets(catd, CATSET, 59, "\"%s\" "), cp);
823 fflush(stdout);
824 if (include_file(fbuf, cp, &lc, &cc, 0) != 0)
825 goto err;
826 printf(catgets(catd, CATSET, 60, "%d/%d\n"), lc, cc);
827 break;
828 case 'i':
830 * Insert an environment variable into the file.
832 cp = &linebuf[2];
833 while (whitechar(*cp & 0377))
834 cp++;
835 if ((cp = value(cp)) == NULL || *cp == '\0')
836 break;
837 if (is_a_tty[0])
838 putesc(cp, stdout);
839 putesc(cp, collf);
840 break;
841 case 'a':
842 case 'A':
844 * Insert the contents of a signature variable.
846 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
847 *cp != '\0') {
848 if (is_a_tty[0])
849 putesc(cp, stdout);
850 putesc(cp, collf);
852 break;
853 case 'w':
855 * Write the message on a file.
857 cp = &linebuf[2];
858 while (blankchar(*cp & 0377))
859 cp++;
860 if (*cp == '\0') {
861 fprintf(stderr, catgets(catd, CATSET, 61,
862 "Write what file!?\n"));
863 break;
865 if ((cp = expand(cp)) == NULL)
866 break;
867 rewind(collf);
868 exwrite(cp, collf, 1);
869 break;
870 case 'm':
871 case 'M':
872 case 'f':
873 case 'F':
875 * Interpolate the named messages, if we
876 * are in receiving mail mode. Does the
877 * standard list processing garbage.
878 * If ~f is given, we don't shift over.
880 if (forward(linebuf + 2, collf, c) < 0)
881 goto err;
882 goto cont;
883 case '?':
884 fputs(tildehelp, stdout);
885 break;
886 case 'p':
888 * Print out the current state of the
889 * message without altering anything.
891 print_collf(collf, hp);
892 goto cont;
893 case '|':
895 * Pipe message through command.
896 * Collect output as new message.
898 rewind(collf);
899 mespipe(&linebuf[2]);
900 goto cont;
901 case 'v':
902 case 'e':
904 * Edit the current message.
905 * 'e' means to use EDITOR
906 * 'v' means to use VISUAL
908 rewind(collf);
909 mesedit(c, value("editheaders") ? hp : NULL);
910 goto cont;
913 goto out;
914 err:
915 if (collf != NULL) {
916 Fclose(collf);
917 collf = NULL;
919 out:
920 if (collf != NULL) {
921 if ((cp = value("SNAIL_TAIL")) != NULL) {
922 if (is_a_tty[0])
923 putesc(cp, stdout);
924 fflush(collf);
925 putesc(cp, collf);
927 rewind(collf);
929 handlerpop();
930 noreset--;
931 sigemptyset(&nset);
932 sigaddset(&nset, SIGINT);
933 sigaddset(&nset, SIGHUP);
934 #ifndef OLDBUG
935 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
936 #else
937 sigprocmask(SIG_BLOCK, &nset, &oset);
938 #endif
939 safe_signal(SIGINT, saveint);
940 safe_signal(SIGHUP, savehup);
941 safe_signal(SIGTSTP, savetstp);
942 safe_signal(SIGTTOU, savettou);
943 safe_signal(SIGTTIN, savettin);
944 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
945 return collf;
949 * Write a file, ex-like if f set.
951 static int
952 exwrite(char *name, FILE *fp, int f)
954 FILE *of;
955 int c;
956 long cc;
957 int lc;
959 if (f) {
960 printf("\"%s\" ", name);
961 fflush(stdout);
963 if ((of = Fopen(name, "a")) == NULL) {
964 perror(NULL);
965 return(-1);
967 lc = 0;
968 cc = 0;
969 while ((c = getc(fp)) != EOF) {
970 cc++;
971 if (c == '\n')
972 lc++;
973 putc(c, of);
974 if (ferror(of)) {
975 perror(name);
976 Fclose(of);
977 return(-1);
980 Fclose(of);
981 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
982 fflush(stdout);
983 return(0);
986 static enum okay
987 makeheader(FILE *fp, struct header *hp)
989 char *tempEdit;
990 FILE *nf;
991 int c;
993 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
994 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
995 Fclose(nf);
996 unlink(tempEdit);
997 Ftfree(&tempEdit);
998 return STOP;
1000 unlink(tempEdit);
1001 Ftfree(&tempEdit);
1002 extract_header(fp, hp);
1003 while ((c = getc(fp)) != EOF)
1004 putc(c, nf);
1005 if (fp != collf)
1006 Fclose(collf);
1007 Fclose(fp);
1008 collf = nf;
1009 if (check_from_and_sender(hp->h_from, hp->h_sender))
1010 return STOP;
1011 return OKAY;
1015 * Edit the message being collected on fp.
1016 * On return, make the edit file the new temp file.
1018 static void
1019 mesedit(int c, struct header *hp)
1021 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1022 FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX,
1023 sigint);
1025 if (nf != NULL) {
1026 if (hp) {
1027 rewind(nf);
1028 makeheader(nf, hp);
1029 } else {
1030 fseek(nf, 0L, SEEK_END);
1031 Fclose(collf);
1032 collf = nf;
1035 safe_signal(SIGINT, sigint);
1039 * Pipe the message through the command.
1040 * Old message is on stdin of command;
1041 * New message collected from stdout.
1042 * Sh -c must return 0 to accept the new message.
1044 static void
1045 mespipe(char *cmd)
1047 FILE *nf;
1048 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1049 char *tempEdit;
1050 char *shell;
1052 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1053 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1054 goto out;
1056 fflush(collf);
1057 unlink(tempEdit);
1058 Ftfree(&tempEdit);
1060 * stdin = current message.
1061 * stdout = new message.
1063 if ((shell = value("SHELL")) == NULL)
1064 shell = SHELL;
1065 if (run_command(shell,
1066 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
1067 Fclose(nf);
1068 goto out;
1070 if (fsize(nf) == 0) {
1071 fprintf(stderr, catgets(catd, CATSET, 67,
1072 "No bytes from \"%s\" !?\n"), cmd);
1073 Fclose(nf);
1074 goto out;
1077 * Take new files.
1079 fseek(nf, 0L, SEEK_END);
1080 Fclose(collf);
1081 collf = nf;
1082 out:
1083 safe_signal(SIGINT, sigint);
1087 * Interpolate the named messages into the current
1088 * message, preceding each line with a tab.
1089 * Return a count of the number of characters now in
1090 * the message, or -1 if an error is encountered writing
1091 * the message temporary. The flag argument is 'm' if we
1092 * should shift over and 'f' if not.
1094 static int
1095 forward(char *ms, FILE *fp, int f)
1097 int *msgvec;
1098 struct ignoretab *ig;
1099 char *tabst;
1100 enum sendaction action;
1102 /*LINTED*/
1103 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
1104 if (msgvec == NULL)
1105 return(0);
1106 if (getmsglist(ms, msgvec, 0) < 0)
1107 return(0);
1108 if (*msgvec == 0) {
1109 *msgvec = first(0, MMNORM);
1110 if (*msgvec == 0) {
1111 printf(catgets(catd, CATSET, 68,
1112 "No appropriate messages\n"));
1113 return(0);
1115 msgvec[1] = 0;
1117 if (f == 'f' || f == 'F')
1118 tabst = NULL;
1119 else if ((tabst = value("indentprefix")) == NULL)
1120 tabst = "\t";
1121 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
1122 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
1123 printf(catgets(catd, CATSET, 69, "Interpolating:"));
1124 for (; *msgvec != 0; msgvec++) {
1125 struct message *mp = message + *msgvec - 1;
1127 touch(mp);
1128 printf(" %d", *msgvec);
1129 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
1130 perror(catgets(catd, CATSET, 70,
1131 "temporary mail file"));
1132 return(-1);
1135 printf("\n");
1136 return(0);
1140 * Print (continue) when continued after ^Z.
1142 /*ARGSUSED*/
1143 static void
1144 collstop(int s)
1146 sighandler_type old_action = safe_signal(s, SIG_DFL);
1147 sigset_t nset;
1149 sigemptyset(&nset);
1150 sigaddset(&nset, s);
1151 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
1152 kill(0, s);
1153 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
1154 safe_signal(s, old_action);
1155 if (colljmp_p) {
1156 colljmp_p = 0;
1157 hadintr = 0;
1158 siglongjmp(colljmp, 1);
1163 * On interrupt, come here to save the partial message in ~/dead.letter.
1164 * Then jump out of the collection loop.
1166 /*ARGSUSED*/
1167 static void
1168 collint(int s)
1171 * the control flow is subtle, because we can be called from ~q.
1173 if (!hadintr) {
1174 if (value("ignore") != NULL) {
1175 puts("@");
1176 fflush(stdout);
1177 clearerr(stdin);
1178 return;
1180 hadintr = 1;
1181 siglongjmp(colljmp, 1);
1183 exit_status |= 04;
1184 rewind(collf);
1185 if (value("save") != NULL && s != 0)
1186 savedeadletter(collf);
1187 siglongjmp(collabort, 1);
1190 /*ARGSUSED*/
1191 static void
1192 collhup(int s)
1194 rewind(collf);
1195 savedeadletter(collf);
1197 * Let's pretend nobody else wants to clean up,
1198 * a true statement at this time.
1200 exit(1);
1203 void
1204 savedeadletter(FILE *fp)
1206 FILE *dbuf;
1207 int c, lines = 0, bytes = 0;
1208 char *cp;
1210 if (fsize(fp) == 0)
1211 return;
1212 cp = getdeadletter();
1213 c = umask(077);
1214 dbuf = Fopen(cp, "a");
1215 umask(c);
1216 if (dbuf == NULL)
1217 return;
1218 printf("\"%s\" ", cp);
1219 while ((c = getc(fp)) != EOF) {
1220 putc(c, dbuf);
1221 bytes++;
1222 if (c == '\n')
1223 lines++;
1225 Fclose(dbuf);
1226 printf("%d/%d\n", lines, bytes);
1227 rewind(fp);
1230 static int
1231 putesc(const char *s, FILE *stream)
1233 int n = 0;
1235 while (s[0]) {
1236 if (s[0] == '\\') {
1237 if (s[1] == 't') {
1238 putc('\t', stream);
1239 n++;
1240 s += 2;
1241 continue;
1243 if (s[1] == 'n') {
1244 putc('\n', stream);
1245 n++;
1246 s += 2;
1247 continue;
1250 putc(s[0]&0377, stream);
1251 n++;
1252 s++;
1254 putc('\n', stream);
1255 return ++n;