collect(): flags not value(), keep interactivity..
[s-mailx.git] / collect.c
blobf2c52b17b16a430b4d0328207d799c930e5a2f29
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 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)collect.c 2.54 (gritter) 6/16/07";
43 #endif
44 #endif /* not lint */
47 * Mail -- a mail program
49 * Collect input from standard input, handling
50 * ~ escapes.
53 #include "rcv.h"
54 #include "extern.h"
55 #include <unistd.h>
56 #include <sys/stat.h>
58 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
59 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
60 * TODO may leave child running if fdopen() fails! */
63 * Read a message from standard output and return a read file to it
64 * or NULL on error.
68 * The following hokiness with global variables is so that on
69 * receipt of an interrupt signal, the partial message can be salted
70 * away on dead.letter.
73 static sighandler_type saveint; /* Previous SIGINT value */
74 static sighandler_type savehup; /* Previous SIGHUP value */
75 static sighandler_type savetstp; /* Previous SIGTSTP value */
76 static sighandler_type savettou; /* Previous SIGTTOU value */
77 static sighandler_type savettin; /* Previous SIGTTIN value */
78 static FILE *collf; /* File for saving away */
79 static int hadintr; /* Have seen one SIGINT so far */
81 static sigjmp_buf colljmp; /* To get back to work */
82 static int colljmp_p; /* whether to long jump */
83 static sigjmp_buf collabort; /* To end collection with error */
85 static sigjmp_buf pipejmp; /* On broken pipe */
87 static void onpipe(int signo);
88 static void insertcommand(FILE *fp, char *cmd);
89 static void print_collf(FILE *collf, struct header *hp);
90 static int include_file(FILE *fbuf, char *name, int *linecount,
91 int *charcount, int echo);
92 static struct attachment *read_attachment_data(struct attachment *ap,
93 unsigned number);
94 static struct attachment *append_attachments(struct attachment *attach,
95 char *names);
96 static int exwrite(char *name, FILE *fp, int f);
97 static enum okay makeheader(FILE *fp, struct header *hp);
98 static void mesedit(int c, struct header *hp);
99 static void mespipe(char *cmd);
100 static int forward(char *ms, FILE *fp, int f);
101 static void collstop(int s);
102 static void collint(int s);
103 static void collhup(int s);
104 static int putesc(const char *s, FILE *stream);
106 /*ARGSUSED*/
107 static void
108 onpipe(int signo)
110 (void)signo;
111 siglongjmp(pipejmp, 1);
115 * Execute cmd and insert its standard output into fp.
117 static void
118 insertcommand(FILE *fp, char *cmd)
120 FILE *obuf = NULL;
121 char *cp;
122 int c;
124 cp = value("SHELL");
125 if (sigsetjmp(pipejmp, 1))
126 goto endpipe;
127 if (cp == NULL)
128 cp = SHELL;
129 if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) {
130 perror(cmd);
131 return;
133 safe_signal(SIGPIPE, onpipe);
134 while ((c = getc(obuf)) != EOF)
135 putc(c, fp);
136 endpipe:
137 safe_signal(SIGPIPE, SIG_IGN);
138 Pclose(obuf);
139 safe_signal(SIGPIPE, dflpipe);
143 * ~p command.
145 static void
146 print_collf(FILE *collf, struct header *hp)
148 char *lbuf = NULL;
149 FILE *obuf = stdout;
150 struct attachment *ap;
151 char *cp;
152 enum gfield gf;
153 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
155 (void)&obuf;
156 (void)&cp;
157 fflush(collf);
158 rewind(collf);
159 count = count2 = fsize(collf);
160 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
161 for (linecnt = 0;
162 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
163 linecnt++);
164 rewind(collf);
165 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
166 maxlines -= 4;
167 if (hp->h_to)
168 maxlines--;
169 if (hp->h_subject)
170 maxlines--;
171 if (hp->h_cc)
172 maxlines--;
173 if (hp->h_bcc)
174 maxlines--;
175 if (hp->h_attach)
176 maxlines--;
177 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
178 maxlines -= value("ORGANIZATION") != NULL ||
179 hp->h_organization != NULL;
180 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
181 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
182 if ((long)maxlines < 0 || linecnt > maxlines) {
183 cp = get_pager();
184 if (sigsetjmp(pipejmp, 1))
185 goto endpipe;
186 obuf = Popen(cp, "w", NULL, 1);
187 if (obuf == NULL) {
188 perror(cp);
189 obuf = stdout;
190 } else
191 safe_signal(SIGPIPE, onpipe);
194 fprintf(obuf, catgets(catd, CATSET, 62,
195 "-------\nMessage contains:\n"));
196 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
197 if (value("fullnames"))
198 gf |= GCOMMA;
199 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
200 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
201 prout(lbuf, linelen, obuf);
202 if (hp->h_attach != NULL) {
203 fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf);
204 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
205 if (ap->a_msgno)
206 fprintf(obuf, " message %u", ap->a_msgno);
207 else
208 fprintf(obuf, " %s", ap->a_name);
209 if (ap->a_flink)
210 putc(',', obuf);
212 putc('\n', obuf);
214 endpipe:
215 if (obuf != stdout) {
216 safe_signal(SIGPIPE, SIG_IGN);
217 Pclose(obuf);
218 safe_signal(SIGPIPE, dflpipe);
220 if (lbuf)
221 free(lbuf);
224 static int
225 include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo)
227 char *interactive, *linebuf = NULL;
228 size_t linesize = 0, linelen, count;
230 if (fbuf == NULL)
231 fbuf = Fopen(name, "r");
232 if (fbuf == NULL) {
233 perror(name);
234 return -1;
236 interactive = value("interactive");
237 *linecount = 0;
238 *charcount = 0;
239 fflush(fbuf);
240 rewind(fbuf);
241 count = fsize(fbuf);
242 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
243 != NULL) {
244 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
245 != linelen) {
246 Fclose(fbuf);
247 return -1;
249 if (interactive != NULL && echo)
250 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
251 (*linecount)++;
252 (*charcount) += linelen;
254 if (linebuf)
255 free(linebuf);
256 Fclose(fbuf);
257 return 0;
261 * Ask the user to edit file names and other data for the given
262 * attachment. NULL is returned if no file name is given.
264 static struct attachment *
265 read_attachment_data(struct attachment *ap, unsigned number)
267 char prefix[80], *cp;
269 if (ap == NULL)
270 ap = csalloc(1, sizeof *ap);
271 if (ap->a_msgno) {
272 printf("#%u\tmessage %u\n", number, ap->a_msgno);
273 return ap;
275 snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50,
276 "#%u\tfilename: "), number);
277 for (;;) {
278 char *exf;
279 if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL)
280 break;
281 if ((exf = expand(ap->a_name)) != NULL)
282 ap->a_name = exf;
283 if (access(ap->a_name, R_OK) == 0)
284 break;
285 perror(ap->a_name);
287 if ((ap->a_name && (value("attachment-ask-charset"))) ||
288 ((cp = value("sendcharsets")) != NULL &&
289 strchr(cp, ',') != NULL)) {
290 snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number);
291 ap->a_charset = readtty(prefix, ap->a_charset);
294 * The "attachment-ask-content-*" variables are left undocumented
295 * since they are for RFC connoisseurs only.
297 if (ap->a_name && value("attachment-ask-content-type")) {
298 if (ap->a_content_type == NULL)
299 ap->a_content_type = mime_filecontent(ap->a_name);
300 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
301 ap->a_content_type = readtty(prefix, ap->a_content_type);
303 if (ap->a_name && value("attachment-ask-content-disposition")) {
304 snprintf(prefix, sizeof prefix,
305 "#%u\tContent-Disposition: ", number);
306 ap->a_content_disposition = readtty(prefix,
307 ap->a_content_disposition);
309 if (ap->a_name && value("attachment-ask-content-id")) {
310 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
311 ap->a_content_id = readtty(prefix, ap->a_content_id);
313 if (ap->a_name && value("attachment-ask-content-description")) {
314 snprintf(prefix, sizeof prefix,
315 "#%u\tContent-Description: ", number);
316 ap->a_content_description = readtty(prefix,
317 ap->a_content_description);
319 return ap->a_name ? ap : NULL;
323 * Interactively edit the attachment list.
325 struct attachment *
326 edit_attachments(struct attachment *attach)
328 struct attachment *ap, *nap;
329 unsigned attno = 1;
331 for (ap = attach; ap; ap = ap->a_flink) {
332 if ((nap = read_attachment_data(ap, attno)) == NULL) {
333 if (ap->a_blink)
334 ap->a_blink->a_flink = ap->a_flink;
335 else
336 attach = ap->a_flink;
337 if (ap->a_flink)
338 ap->a_flink->a_blink = ap->a_blink;
339 else
340 return attach;
341 } else
342 attno++;
344 while ((nap = read_attachment_data(NULL, attno)) != NULL) {
345 for (ap = attach; ap && ap->a_flink; ap = ap->a_flink);
346 if (ap)
347 ap->a_flink = nap;
348 nap->a_blink = ap;
349 nap->a_flink = NULL;
350 if (attach == NULL)
351 attach = nap;
352 attno++;
354 return attach;
358 * Put the given file to the end of the attachment list.
360 struct attachment *
361 add_attachment(struct attachment *attach, char *file, int expand_file)
363 struct attachment *ap, *nap;
365 if (expand_file) {
366 file = expand(file);
367 if (file == NULL)
368 return NULL;
369 } else
370 file = savestr(file);
371 if (access(file, R_OK) != 0)
372 return NULL;
373 /*LINTED*/
374 nap = csalloc(1, sizeof *nap);
375 nap->a_name = file;
376 if (attach != NULL) {
377 for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink);
378 ap->a_flink = nap;
379 nap->a_blink = ap;
380 } else {
381 nap->a_blink = NULL;
382 attach = nap;
384 return attach;
388 * Append the whitespace-separated file names to the end of
389 * the attachment list.
391 static struct attachment *
392 append_attachments(struct attachment *attach, char *names)
394 char *cp;
395 int c;
396 struct attachment *ap;
398 cp = names;
399 while (*cp != '\0' && blankchar(*cp & 0377))
400 cp++;
401 while (*cp != '\0') {
402 names = cp;
403 while (*cp != '\0' && !blankchar(*cp & 0377))
404 cp++;
405 c = *cp;
406 *cp++ = '\0';
407 if (*names != '\0') {
408 if ((ap = add_attachment(attach, names, 1)) == NULL)
409 perror(names);
410 else
411 attach = ap;
413 if (c == '\0')
414 break;
415 while (*cp != '\0' && blankchar(*cp & 0377))
416 cp++;
418 return attach;
421 FILE *
422 collect(struct header *hp, int printheaders, struct message *mp,
423 char *quotefile, int doprefix, int tflag)
425 enum {
426 val_INTERACT = 1
429 FILE *fbuf;
430 struct ignoretab *quoteig;
431 int lc, cc, escape, eofcount;
432 int val, c, t;
433 char *linebuf = NULL, *cp, *quote = NULL;
434 size_t linesize;
435 char *tempMail = NULL;
436 int getfields;
437 sigset_t oset, nset;
438 long count;
439 enum sendaction action;
440 sighandler_type savedtop;
442 val = 0;
443 if (value("interactive") != NULL)
444 val |= val_INTERACT;
446 collf = NULL;
448 * Start catching signals from here, but we're still die on interrupts
449 * until we're in the main loop.
451 sigemptyset(&nset);
452 sigaddset(&nset, SIGINT);
453 sigaddset(&nset, SIGHUP);
454 sigprocmask(SIG_BLOCK, &nset, &oset);
455 handlerpush(collint);
456 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
457 safe_signal(SIGINT, collint);
458 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
459 safe_signal(SIGHUP, collhup);
460 savetstp = safe_signal(SIGTSTP, collstop);
461 savettou = safe_signal(SIGTTOU, collstop);
462 savettin = safe_signal(SIGTTIN, collstop);
463 if (sigsetjmp(collabort, 1)) {
464 if (tempMail != NULL) {
465 rm(tempMail);
466 Ftfree(&tempMail);
468 goto err;
470 if (sigsetjmp(colljmp, 1)) {
471 if (tempMail != NULL) {
472 rm(tempMail);
473 Ftfree(&tempMail);
475 goto err;
477 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
479 noreset++;
480 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
481 perror(catgets(catd, CATSET, 51, "temporary mail file"));
482 goto err;
484 unlink(tempMail);
485 Ftfree(&tempMail);
487 if ((cp = value("SNAIL_HEAD")) != NULL) {
488 if (is_a_tty[0])
489 putesc(cp, stdout);
490 putesc(cp, collf);
494 * If we are going to prompt for a subject,
495 * refrain from printing a newline after
496 * the headers (since some people mind).
498 getfields = 0;
499 if (!tflag) {
500 t = GTO|GSUBJECT|GCC|GNL;
501 if (value("fullnames"))
502 t |= GCOMMA;
503 if (hp->h_subject == NULL && (val & val_INTERACT) &&
504 (value("ask") != NULL || value("asksub") != NULL))
505 t &= ~GNL, getfields |= GSUBJECT;
506 if (hp->h_to == NULL && (val & val_INTERACT))
507 t &= ~GNL, getfields |= GTO;
508 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
509 (val & val_INTERACT)) {
510 if (hp->h_bcc == NULL && value("askbcc"))
511 t &= ~GNL, getfields |= GBCC;
512 if (hp->h_cc == NULL && value("askcc"))
513 t &= ~GNL, getfields |= GCC;
515 if (printheaders) {
516 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
517 NULL, NULL);
518 fflush(stdout);
523 * Quote an original message
525 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
526 quoteig = allignore;
527 action = SEND_QUOTE;
528 if (doprefix) {
529 quoteig = fwdignore;
530 if ((cp = value("fwdheading")) == NULL)
531 cp = "-------- Original Message --------";
532 if (*cp) {
533 fprintf(collf, "%s\n", cp);
534 fprintf(stdout, "%s\n", cp);
536 } else if (strcmp(quote, "noheading") == 0) {
537 /*EMPTY*/;
538 } else if (strcmp(quote, "headers") == 0) {
539 quoteig = ignore;
540 } else if (strcmp(quote, "allheaders") == 0) {
541 quoteig = NULL;
542 action = SEND_QUOTE_ALL;
543 } else {
544 cp = hfield("from", mp);
545 if (cp != NULL) {
546 mime_write(cp, strlen(cp),
547 collf, CONV_FROMHDR, TD_NONE,
548 NULL, (size_t) 0,
549 NULL, NULL);
550 mime_write(cp, strlen(cp),
551 stdout, CONV_FROMHDR, TD_NONE,
552 NULL, (size_t) 0,
553 NULL, NULL);
554 fwrite(catgets(catd, CATSET, 52,
555 " wrote:\n\n"), sizeof(char), 9, collf);
556 fwrite(catgets(catd, CATSET, 52,
557 " wrote:\n\n"), sizeof(char), 9, stdout);
560 cp = value("indentprefix");
561 if (cp != NULL && *cp == '\0')
562 cp = "\t";
563 send(mp, collf, quoteig, doprefix ? NULL : cp, action, NULL);
564 send(mp, stdout, quoteig, doprefix ? NULL : cp, action, NULL);
567 if ((cp = value("escape")) != NULL)
568 escape = *cp;
569 else
570 escape = ESCAPE;
571 eofcount = 0;
572 hadintr = 0;
574 if (!sigsetjmp(colljmp, 1)) {
575 if (getfields)
576 grabh(hp, getfields, 1);
577 if (quotefile != NULL) {
578 if (include_file(NULL, quotefile, &lc, &cc, 1) != 0)
579 goto err;
581 } else {
583 * Come here for printing the after-signal message.
584 * Duplicate messages won't be printed because
585 * the write is aborted if we get a SIGTTOU.
587 cont:
588 if (hadintr) {
589 fflush(stdout);
590 fprintf(stderr, catgets(catd, CATSET, 53,
591 "\n(Interrupt -- one more to kill letter)\n"));
592 } else {
593 printf(catgets(catd, CATSET, 54, "(continue)\n"));
594 fflush(stdout);
597 if ((val & val_INTERACT) == 0 && tildeflag <= 0 &&
598 !is_a_tty[0] && !tflag) {
600 * No tilde escapes, interrupts not expected. Copy
601 * standard input the simple way.
603 linebuf = srealloc(linebuf, linesize = BUFSIZ);
604 while ((count = fread(linebuf, sizeof *linebuf,
605 linesize, stdin)) > 0) {
606 if ((size_t)count != fwrite(linebuf, sizeof *linebuf,
607 count, collf))
608 goto err;
610 goto out;
612 for (;;) {
613 colljmp_p = 1;
614 count = readline(stdin, &linebuf, &linesize);
615 colljmp_p = 0;
616 if (count < 0) {
617 if ((val & val_INTERACT) &&
618 value("ignoreeof") != NULL && ++eofcount < 25) {
619 printf(catgets(catd, CATSET, 55,
620 "Use \".\" to terminate letter\n"));
621 continue;
623 break;
625 if (tflag && count == 0) {
626 rewind(collf);
627 if (makeheader(collf, hp) != OKAY)
628 goto err;
629 rewind(collf);
630 tflag = 0;
631 continue;
633 eofcount = 0;
634 hadintr = 0;
635 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
636 (val & val_INTERACT) &&
637 (value("dot") != NULL || value("ignoreeof") != NULL))
638 break;
639 if (linebuf[0] != escape ||
640 ((val & val_INTERACT) == 0 && tildeflag == 0) ||
641 tildeflag < 0) {
642 if (putline(collf, linebuf, count) < 0)
643 goto err;
644 continue;
646 c = linebuf[1];
647 switch (c) {
648 default:
650 * On double escape, just send the single one.
651 * Otherwise, it's an error.
653 if (c == escape) {
654 if (putline(collf, &linebuf[1], count - 1) < 0)
655 goto err;
656 else
657 break;
659 printf(catgets(catd, CATSET, 56,
660 "Unknown tilde escape.\n"));
661 break;
662 #ifdef DEBUG_COMMANDS
663 case 'C':
665 * Dump core.
667 core(NULL);
668 break;
669 #endif /* DEBUG_COMMANDS */
670 case '!':
672 * Shell escape, send the balance of the
673 * line to sh -c.
675 shell(&linebuf[2]);
676 break;
677 case ':':
678 case '_':
680 * Escape to command mode, but be nice!
682 inhook = 0;
683 execute(&linebuf[2], 1, count - 2);
684 goto cont;
685 case '.':
687 * Simulate end of file on input.
689 goto out;
690 case 'x':
692 * Same as 'q', but no dead.letter saving.
694 hadintr++;
695 collint(0);
696 exit(1);
697 /*NOTREACHED*/
698 case 'q':
700 * Force a quit of sending mail.
701 * Act like an interrupt happened.
703 hadintr++;
704 collint(SIGINT);
705 exit(1);
706 /*NOTREACHED*/
707 case 'h':
709 * Grab a bunch of headers.
712 grabh(hp, GTO|GSUBJECT|GCC|GBCC,
713 value("bsdcompat") != NULL &&
714 value("bsdorder") != NULL);
715 while (hp->h_to == NULL);
716 goto cont;
717 case 'H':
719 * Grab extra headers.
722 grabh(hp, GEXTRA, 0);
723 while (check_from_and_sender(hp->h_from, hp->h_sender));
724 goto cont;
725 case 't':
727 * Add to the To list.
729 while ((hp->h_to = checkaddrs(cat(hp->h_to,
730 sextract(&linebuf[2], GTO|GFULL))))
731 == NULL);
732 break;
733 case 's':
735 * Set the Subject list.
737 cp = &linebuf[2];
738 while (whitechar(*cp & 0377))
739 cp++;
740 hp->h_subject = savestr(cp);
741 break;
742 case '@':
744 * Edit the attachment list.
746 if (linebuf[2] != '\0')
747 hp->h_attach = append_attachments(hp->h_attach,
748 &linebuf[2]);
749 else
750 hp->h_attach = edit_attachments(hp->h_attach);
751 break;
752 case 'c':
754 * Add to the CC list.
756 hp->h_cc = checkaddrs(cat(hp->h_cc,
757 sextract(&linebuf[2], GCC|GFULL)));
758 break;
759 case 'b':
761 * Add stuff to blind carbon copies list.
763 hp->h_bcc = checkaddrs(cat(hp->h_bcc,
764 sextract(&linebuf[2], GBCC|GFULL)));
765 break;
766 case 'd':
767 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
768 linebuf[linesize-1]='\0';
769 /*FALLTHRU*/
770 case 'r':
771 case '<':
773 * Invoke a file:
774 * Search for the file name,
775 * then open it and copy the contents to collf.
777 cp = &linebuf[2];
778 while (whitechar(*cp & 0377))
779 cp++;
780 if (*cp == '\0') {
781 printf(catgets(catd, CATSET, 57,
782 "Interpolate what file?\n"));
783 break;
785 if (*cp == '!') {
786 insertcommand(collf, cp + 1);
787 break;
789 cp = expand(cp);
790 if (cp == NULL)
791 break;
792 if (is_dir(cp)) {
793 printf(catgets(catd, CATSET, 58,
794 "%s: Directory\n"), cp);
795 break;
797 if ((fbuf = Fopen(cp, "r")) == NULL) {
798 perror(cp);
799 break;
801 printf(catgets(catd, CATSET, 59, "\"%s\" "), cp);
802 fflush(stdout);
803 if (include_file(fbuf, cp, &lc, &cc, 0) != 0)
804 goto err;
805 printf(catgets(catd, CATSET, 60, "%d/%d\n"), lc, cc);
806 break;
807 case 'i':
809 * Insert an environment variable into the file.
811 cp = &linebuf[2];
812 while (whitechar(*cp & 0377))
813 cp++;
814 if ((cp = value(cp)) == NULL || *cp == '\0')
815 break;
816 if (is_a_tty[0])
817 putesc(cp, stdout);
818 putesc(cp, collf);
819 break;
820 case 'a':
821 case 'A':
823 * Insert the contents of a signature variable.
825 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
826 *cp != '\0') {
827 if (is_a_tty[0])
828 putesc(cp, stdout);
829 putesc(cp, collf);
831 break;
832 case 'w':
834 * Write the message on a file.
836 cp = &linebuf[2];
837 while (blankchar(*cp & 0377))
838 cp++;
839 if (*cp == '\0') {
840 fprintf(stderr, catgets(catd, CATSET, 61,
841 "Write what file!?\n"));
842 break;
844 if ((cp = expand(cp)) == NULL)
845 break;
846 rewind(collf);
847 exwrite(cp, collf, 1);
848 break;
849 case 'm':
850 case 'M':
851 case 'f':
852 case 'F':
854 * Interpolate the named messages, if we
855 * are in receiving mail mode. Does the
856 * standard list processing garbage.
857 * If ~f is given, we don't shift over.
859 if (forward(linebuf + 2, collf, c) < 0)
860 goto err;
861 goto cont;
862 case 'p':
864 * Print out the current state of the
865 * message without altering anything.
867 print_collf(collf, hp);
868 goto cont;
869 case '|':
871 * Pipe message through command.
872 * Collect output as new message.
874 rewind(collf);
875 mespipe(&linebuf[2]);
876 goto cont;
877 case 'v':
878 case 'e':
880 * Edit the current message.
881 * 'e' means to use EDITOR
882 * 'v' means to use VISUAL
884 rewind(collf);
885 mesedit(c, value("editheaders") ? hp : NULL);
886 goto cont;
887 case '?':
889 * Last the lengthy help string.
890 * (Very ugly, but take care for compiler supported
891 * string lengths :()
893 puts(
894 "-------------------- ~ ESCAPES ----------------------------\n"
895 "~~ Quote a single tilde\n"
896 "~@ [file ...] Edit attachment list\n"
897 "~b users Add users to \"blind\" cc list\n"
898 "~c users Add users to cc list\n"
899 "~d Read in dead.letter\n"
900 "~e Edit the message buffer\n"
901 "~f messages Read in messages without indenting lines\n"
902 "~F messages Same as ~f, but keep all header lines\n"
903 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n");
904 puts(
905 "~r file Read a file into the message buffer\n"
906 "~p Print the message buffer\n"
907 "~q Abort message composition and save text to dead.letter\n"
908 "~m messages Read in messages with each line indented\n"
909 "~M messages Same as ~m, but keep all header lines\n"
910 "~s subject Set subject\n"
911 "~t users Add users to to list\n"
912 "~v Invoke display editor on message\n"
913 "~w file Write message onto file\n"
914 "~x Abort message composition and discard text written so far\n");
915 puts(
916 "~!command Invoke the shell\n"
917 "~:command Execute a regular command\n"
918 "-----------------------------------------------------------\n");
919 break;
923 goto out;
924 err:
925 if (collf != NULL) {
926 Fclose(collf);
927 collf = NULL;
929 out:
930 if (collf != NULL) {
931 if ((cp = value("SNAIL_TAIL")) != NULL) {
932 if (is_a_tty[0])
933 putesc(cp, stdout);
934 fflush(collf);
935 putesc(cp, collf);
937 rewind(collf);
939 handlerpop();
940 noreset--;
941 sigemptyset(&nset);
942 sigaddset(&nset, SIGINT);
943 sigaddset(&nset, SIGHUP);
944 #ifndef OLDBUG
945 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
946 #else
947 sigprocmask(SIG_BLOCK, &nset, &oset);
948 #endif
949 safe_signal(SIGINT, saveint);
950 safe_signal(SIGHUP, savehup);
951 safe_signal(SIGTSTP, savetstp);
952 safe_signal(SIGTTOU, savettou);
953 safe_signal(SIGTTIN, savettin);
954 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
955 return collf;
959 * Write a file, ex-like if f set.
961 static int
962 exwrite(char *name, FILE *fp, int f)
964 FILE *of;
965 int c;
966 long cc;
967 int lc;
969 if (f) {
970 printf("\"%s\" ", name);
971 fflush(stdout);
973 if ((of = Fopen(name, "a")) == NULL) {
974 perror(NULL);
975 return(-1);
977 lc = 0;
978 cc = 0;
979 while ((c = getc(fp)) != EOF) {
980 cc++;
981 if (c == '\n')
982 lc++;
983 putc(c, of);
984 if (ferror(of)) {
985 perror(name);
986 Fclose(of);
987 return(-1);
990 Fclose(of);
991 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
992 fflush(stdout);
993 return(0);
996 static enum okay
997 makeheader(FILE *fp, struct header *hp)
999 char *tempEdit;
1000 FILE *nf;
1001 int c;
1003 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1004 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1005 Fclose(nf);
1006 unlink(tempEdit);
1007 Ftfree(&tempEdit);
1008 return STOP;
1010 unlink(tempEdit);
1011 Ftfree(&tempEdit);
1012 extract_header(fp, hp);
1013 while ((c = getc(fp)) != EOF)
1014 putc(c, nf);
1015 if (fp != collf)
1016 Fclose(collf);
1017 Fclose(fp);
1018 collf = nf;
1019 if (check_from_and_sender(hp->h_from, hp->h_sender))
1020 return STOP;
1021 return OKAY;
1025 * Edit the message being collected on fp.
1026 * On return, make the edit file the new temp file.
1028 static void
1029 mesedit(int c, struct header *hp)
1031 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1032 FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX,
1033 sigint);
1035 if (nf != NULL) {
1036 if (hp) {
1037 rewind(nf);
1038 makeheader(nf, hp);
1039 } else {
1040 fseek(nf, 0L, SEEK_END);
1041 Fclose(collf);
1042 collf = nf;
1045 safe_signal(SIGINT, sigint);
1049 * Pipe the message through the command.
1050 * Old message is on stdin of command;
1051 * New message collected from stdout.
1052 * Sh -c must return 0 to accept the new message.
1054 static void
1055 mespipe(char *cmd)
1057 FILE *nf;
1058 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1059 char *tempEdit;
1060 char *shell;
1062 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1063 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1064 goto out;
1066 fflush(collf);
1067 unlink(tempEdit);
1068 Ftfree(&tempEdit);
1070 * stdin = current message.
1071 * stdout = new message.
1073 if ((shell = value("SHELL")) == NULL)
1074 shell = SHELL;
1075 if (run_command(shell,
1076 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
1077 Fclose(nf);
1078 goto out;
1080 if (fsize(nf) == 0) {
1081 fprintf(stderr, catgets(catd, CATSET, 67,
1082 "No bytes from \"%s\" !?\n"), cmd);
1083 Fclose(nf);
1084 goto out;
1087 * Take new files.
1089 fseek(nf, 0L, SEEK_END);
1090 Fclose(collf);
1091 collf = nf;
1092 out:
1093 safe_signal(SIGINT, sigint);
1097 * Interpolate the named messages into the current
1098 * message, preceding each line with a tab.
1099 * Return a count of the number of characters now in
1100 * the message, or -1 if an error is encountered writing
1101 * the message temporary. The flag argument is 'm' if we
1102 * should shift over and 'f' if not.
1104 static int
1105 forward(char *ms, FILE *fp, int f)
1107 int *msgvec;
1108 struct ignoretab *ig;
1109 char *tabst;
1110 enum sendaction action;
1112 /*LINTED*/
1113 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
1114 if (msgvec == NULL)
1115 return(0);
1116 if (getmsglist(ms, msgvec, 0) < 0)
1117 return(0);
1118 if (*msgvec == 0) {
1119 *msgvec = first(0, MMNORM);
1120 if (*msgvec == 0) {
1121 printf(catgets(catd, CATSET, 68,
1122 "No appropriate messages\n"));
1123 return(0);
1125 msgvec[1] = 0;
1127 if (f == 'f' || f == 'F')
1128 tabst = NULL;
1129 else if ((tabst = value("indentprefix")) == NULL)
1130 tabst = "\t";
1131 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
1132 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
1133 printf(catgets(catd, CATSET, 69, "Interpolating:"));
1134 for (; *msgvec != 0; msgvec++) {
1135 struct message *mp = message + *msgvec - 1;
1137 touch(mp);
1138 printf(" %d", *msgvec);
1139 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
1140 perror(catgets(catd, CATSET, 70,
1141 "temporary mail file"));
1142 return(-1);
1145 printf("\n");
1146 return(0);
1150 * Print (continue) when continued after ^Z.
1152 /*ARGSUSED*/
1153 static void
1154 collstop(int s)
1156 sighandler_type old_action = safe_signal(s, SIG_DFL);
1157 sigset_t nset;
1159 sigemptyset(&nset);
1160 sigaddset(&nset, s);
1161 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
1162 kill(0, s);
1163 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
1164 safe_signal(s, old_action);
1165 if (colljmp_p) {
1166 colljmp_p = 0;
1167 hadintr = 0;
1168 siglongjmp(colljmp, 1);
1173 * On interrupt, come here to save the partial message in ~/dead.letter.
1174 * Then jump out of the collection loop.
1176 /*ARGSUSED*/
1177 static void
1178 collint(int s)
1181 * the control flow is subtle, because we can be called from ~q.
1183 if (!hadintr) {
1184 if (value("ignore") != NULL) {
1185 puts("@");
1186 fflush(stdout);
1187 clearerr(stdin);
1188 return;
1190 hadintr = 1;
1191 siglongjmp(colljmp, 1);
1193 exit_status |= 04;
1194 rewind(collf);
1195 if (value("save") != NULL && s != 0)
1196 savedeadletter(collf);
1197 siglongjmp(collabort, 1);
1200 /*ARGSUSED*/
1201 static void
1202 collhup(int s)
1204 (void)s;
1205 rewind(collf);
1206 savedeadletter(collf);
1208 * Let's pretend nobody else wants to clean up,
1209 * a true statement at this time.
1211 exit(1);
1214 void
1215 savedeadletter(FILE *fp)
1217 FILE *dbuf;
1218 int c, lines = 0, bytes = 0;
1219 char *cp;
1221 if (fsize(fp) == 0)
1222 return;
1223 cp = getdeadletter();
1224 c = umask(077);
1225 dbuf = Fopen(cp, "a");
1226 umask(c);
1227 if (dbuf == NULL)
1228 return;
1229 printf("\"%s\" ", cp);
1230 while ((c = getc(fp)) != EOF) {
1231 putc(c, dbuf);
1232 bytes++;
1233 if (c == '\n')
1234 lines++;
1236 Fclose(dbuf);
1237 printf("%d/%d\n", lines, bytes);
1238 rewind(fp);
1241 static int
1242 putesc(const char *s, FILE *stream)
1244 int n = 0;
1246 while (s[0]) {
1247 if (s[0] == '\\') {
1248 if (s[1] == 't') {
1249 putc('\t', stream);
1250 n++;
1251 s += 2;
1252 continue;
1254 if (s[1] == 'n') {
1255 putc('\n', stream);
1256 n++;
1257 s += 2;
1258 continue;
1261 putc(s[0]&0377, stream);
1262 n++;
1263 s++;
1265 putc('\n', stream);
1266 return ++n;