Do cat(checkaddrs()) not checkaddrs(cat()) .. please
[s-mailx.git] / collect.c
blobcc23b82dbd66a39d697b7d5c960f792251206da5
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
41 * Mail -- a mail program
43 * Collect input from standard input, handling
44 * ~ escapes.
47 #include "rcv.h"
48 #include "extern.h"
49 #include <unistd.h>
50 #include <sys/stat.h>
52 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
53 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
54 * TODO may leave child running if fdopen() fails! */
57 * Read a message from standard output and return a read file to it
58 * or NULL on error.
62 * The following hokiness with global variables is so that on
63 * receipt of an interrupt signal, the partial message can be salted
64 * away on dead.letter.
67 static sighandler_type saveint; /* Previous SIGINT value */
68 static sighandler_type savehup; /* Previous SIGHUP value */
69 static sighandler_type savetstp; /* Previous SIGTSTP value */
70 static sighandler_type savettou; /* Previous SIGTTOU value */
71 static sighandler_type savettin; /* Previous SIGTTIN value */
72 static FILE *collf; /* File for saving away */
73 static int hadintr; /* Have seen one SIGINT so far */
75 static sigjmp_buf colljmp; /* To get back to work */
76 static int colljmp_p; /* whether to long jump */
77 static sigjmp_buf collabort; /* To end collection with error */
79 static sigjmp_buf pipejmp; /* On broken pipe */
81 static void onpipe(int signo);
82 static void insertcommand(FILE *fp, char *cmd);
83 static void print_collf(FILE *collf, struct header *hp);
84 static int include_file(FILE *fbuf, char *name, int *linecount,
85 int *charcount, int echo);
86 static struct attachment *read_attachment_data(struct attachment *ap,
87 unsigned number);
88 static struct attachment *append_attachments(struct attachment *attach,
89 char *names);
90 static int exwrite(char *name, FILE *fp, int f);
91 static enum okay makeheader(FILE *fp, struct header *hp);
92 static void mesedit(int c, struct header *hp);
93 static void mespipe(char *cmd);
94 static int forward(char *ms, FILE *fp, int f);
95 static void collstop(int s);
96 static void collint(int s);
97 static void collhup(int s);
98 static int putesc(const char *s, FILE *stream);
100 /*ARGSUSED*/
101 static void
102 onpipe(int signo)
104 (void)signo;
105 siglongjmp(pipejmp, 1);
109 * Execute cmd and insert its standard output into fp.
111 static void
112 insertcommand(FILE *fp, char *cmd)
114 FILE *obuf = NULL;
115 char *cp;
116 int c;
118 cp = value("SHELL");
119 if (sigsetjmp(pipejmp, 1))
120 goto endpipe;
121 if (cp == NULL)
122 cp = SHELL;
123 if ((obuf = Popen(cmd, "r", cp, 0)) == NULL) {
124 perror(cmd);
125 return;
127 safe_signal(SIGPIPE, onpipe);
128 while ((c = getc(obuf)) != EOF)
129 putc(c, fp);
130 endpipe:
131 safe_signal(SIGPIPE, SIG_IGN);
132 Pclose(obuf);
133 safe_signal(SIGPIPE, dflpipe);
137 * ~p command.
139 static void
140 print_collf(FILE *collf, struct header *hp)
142 char *lbuf = NULL;
143 FILE *obuf = stdout;
144 struct attachment *ap;
145 char *cp;
146 enum gfield gf;
147 size_t linecnt, maxlines, linesize = 0, linelen, count, count2;
149 (void)&obuf;
150 (void)&cp;
151 fflush(collf);
152 rewind(collf);
153 count = count2 = fsize(collf);
154 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
155 for (linecnt = 0;
156 fgetline(&lbuf, &linesize, &count2, NULL, collf, 0);
157 linecnt++);
158 rewind(collf);
159 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
160 maxlines -= 4;
161 if (hp->h_to)
162 maxlines--;
163 if (hp->h_subject)
164 maxlines--;
165 if (hp->h_cc)
166 maxlines--;
167 if (hp->h_bcc)
168 maxlines--;
169 if (hp->h_attach)
170 maxlines--;
171 maxlines -= myaddrs(hp) != NULL || hp->h_from != NULL;
172 maxlines -= value("ORGANIZATION") != NULL ||
173 hp->h_organization != NULL;
174 maxlines -= value("replyto") != NULL || hp->h_replyto != NULL;
175 maxlines -= value("sender") != NULL || hp->h_sender != NULL;
176 if ((long)maxlines < 0 || linecnt > maxlines) {
177 cp = get_pager();
178 if (sigsetjmp(pipejmp, 1))
179 goto endpipe;
180 obuf = Popen(cp, "w", NULL, 1);
181 if (obuf == NULL) {
182 perror(cp);
183 obuf = stdout;
184 } else
185 safe_signal(SIGPIPE, onpipe);
188 fprintf(obuf, catgets(catd, CATSET, 62,
189 "-------\nMessage contains:\n"));
190 gf = GIDENT|GTO|GSUBJECT|GCC|GBCC|GNL|GFILES;
191 if (value("fullnames"))
192 gf |= GCOMMA;
193 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
194 while (fgetline(&lbuf, &linesize, &count, &linelen, collf, 1))
195 prout(lbuf, linelen, obuf);
196 if (hp->h_attach != NULL) {
197 fputs(catgets(catd, CATSET, 63, "Attachments:"), obuf);
198 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
199 if (ap->a_msgno)
200 fprintf(obuf, " message %u", ap->a_msgno);
201 else
202 fprintf(obuf, " %s", ap->a_name);
203 if (ap->a_flink)
204 putc(',', obuf);
206 putc('\n', obuf);
208 endpipe:
209 if (obuf != stdout) {
210 safe_signal(SIGPIPE, SIG_IGN);
211 Pclose(obuf);
212 safe_signal(SIGPIPE, dflpipe);
214 if (lbuf)
215 free(lbuf);
218 static int
219 include_file(FILE *fbuf, char *name, int *linecount, int *charcount, int echo)
221 char *interactive, *linebuf = NULL;
222 size_t linesize = 0, linelen, count;
224 if (fbuf == NULL)
225 fbuf = Fopen(name, "r");
226 if (fbuf == NULL) {
227 perror(name);
228 return -1;
230 interactive = value("interactive");
231 *linecount = 0;
232 *charcount = 0;
233 fflush(fbuf);
234 rewind(fbuf);
235 count = fsize(fbuf);
236 while (fgetline(&linebuf, &linesize, &count, &linelen, fbuf, 0)
237 != NULL) {
238 if (fwrite(linebuf, sizeof *linebuf, linelen, collf)
239 != linelen) {
240 Fclose(fbuf);
241 return -1;
243 if (interactive != NULL && echo)
244 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
245 (*linecount)++;
246 (*charcount) += linelen;
248 if (linebuf)
249 free(linebuf);
250 Fclose(fbuf);
251 return 0;
255 * Ask the user to edit file names and other data for the given
256 * attachment. NULL is returned if no file name is given.
258 static struct attachment *
259 read_attachment_data(struct attachment *ap, unsigned number)
261 char prefix[80], *cp;
263 if (ap == NULL)
264 ap = csalloc(1, sizeof *ap);
265 if (ap->a_msgno) {
266 printf("#%u\tmessage %u\n", number, ap->a_msgno);
267 return ap;
269 snprintf(prefix, sizeof prefix, catgets(catd, CATSET, 50,
270 "#%u\tfilename: "), number);
271 for (;;) {
272 char *exf;
273 if ((ap->a_name = readtty(prefix, ap->a_name)) == NULL)
274 break;
275 if ((exf = expand(ap->a_name)) != NULL)
276 ap->a_name = exf;
277 if (access(ap->a_name, R_OK) == 0)
278 break;
279 perror(ap->a_name);
281 if ((ap->a_name && (value("attachment-ask-charset"))) ||
282 ((cp = value("sendcharsets")) != NULL &&
283 strchr(cp, ',') != NULL)) {
284 snprintf(prefix, sizeof prefix, "#%u\tcharset: ", number);
285 ap->a_charset = readtty(prefix, ap->a_charset);
288 * The "attachment-ask-content-*" variables are left undocumented
289 * since they are for RFC connoisseurs only.
291 if (ap->a_name && value("attachment-ask-content-type")) {
292 if (ap->a_content_type == NULL)
293 ap->a_content_type = mime_filecontent(ap->a_name);
294 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
295 ap->a_content_type = readtty(prefix, ap->a_content_type);
297 if (ap->a_name && value("attachment-ask-content-disposition")) {
298 snprintf(prefix, sizeof prefix,
299 "#%u\tContent-Disposition: ", number);
300 ap->a_content_disposition = readtty(prefix,
301 ap->a_content_disposition);
303 if (ap->a_name && value("attachment-ask-content-id")) {
304 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
305 ap->a_content_id = readtty(prefix, ap->a_content_id);
307 if (ap->a_name && value("attachment-ask-content-description")) {
308 snprintf(prefix, sizeof prefix,
309 "#%u\tContent-Description: ", number);
310 ap->a_content_description = readtty(prefix,
311 ap->a_content_description);
313 return ap->a_name ? ap : NULL;
317 * Interactively edit the attachment list.
319 struct attachment *
320 edit_attachments(struct attachment *attach)
322 struct attachment *ap, *nap;
323 unsigned attno = 1;
325 for (ap = attach; ap; ap = ap->a_flink) {
326 if ((nap = read_attachment_data(ap, attno)) == NULL) {
327 if (ap->a_blink)
328 ap->a_blink->a_flink = ap->a_flink;
329 else
330 attach = ap->a_flink;
331 if (ap->a_flink)
332 ap->a_flink->a_blink = ap->a_blink;
333 else
334 return attach;
335 } else
336 attno++;
338 while ((nap = read_attachment_data(NULL, attno)) != NULL) {
339 for (ap = attach; ap && ap->a_flink; ap = ap->a_flink);
340 if (ap)
341 ap->a_flink = nap;
342 nap->a_blink = ap;
343 nap->a_flink = NULL;
344 if (attach == NULL)
345 attach = nap;
346 attno++;
348 return attach;
352 * Put the given file to the end of the attachment list.
354 struct attachment *
355 add_attachment(struct attachment *attach, char *file, int expand_file)
357 struct attachment *ap, *nap;
359 if (expand_file) {
360 file = expand(file);
361 if (file == NULL)
362 return NULL;
363 } else
364 file = savestr(file);
365 if (access(file, R_OK) != 0)
366 return NULL;
367 /*LINTED*/
368 nap = csalloc(1, sizeof *nap);
369 nap->a_name = file;
370 if (attach != NULL) {
371 for (ap = attach; ap->a_flink != NULL; ap = ap->a_flink);
372 ap->a_flink = nap;
373 nap->a_blink = ap;
374 } else {
375 nap->a_blink = NULL;
376 attach = nap;
378 return attach;
382 * Append the whitespace-separated file names to the end of
383 * the attachment list.
385 static struct attachment *
386 append_attachments(struct attachment *attach, char *names)
388 char *cp;
389 int c;
390 struct attachment *ap;
392 cp = names;
393 while (*cp != '\0' && blankchar(*cp & 0377))
394 cp++;
395 while (*cp != '\0') {
396 names = cp;
397 while (*cp != '\0' && !blankchar(*cp & 0377))
398 cp++;
399 c = *cp;
400 *cp++ = '\0';
401 if (*names != '\0') {
402 if ((ap = add_attachment(attach, names, 1)) == NULL)
403 perror(names);
404 else
405 attach = ap;
407 if (c == '\0')
408 break;
409 while (*cp != '\0' && blankchar(*cp & 0377))
410 cp++;
412 return attach;
415 FILE *
416 collect(struct header *hp, int printheaders, struct message *mp,
417 char *quotefile, int doprefix, int tflag)
419 enum {
420 val_INTERACT = 1
423 FILE *fbuf;
424 struct ignoretab *quoteig;
425 int lc, cc, escape, eofcount;
426 int val, 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;
436 val = 0;
437 if (value("interactive") != NULL)
438 val |= val_INTERACT;
440 collf = NULL;
442 * Start catching signals from here, but we're still die on interrupts
443 * until we're in the main loop.
445 sigemptyset(&nset);
446 sigaddset(&nset, SIGINT);
447 sigaddset(&nset, SIGHUP);
448 sigprocmask(SIG_BLOCK, &nset, &oset);
449 handlerpush(collint);
450 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
451 safe_signal(SIGINT, collint);
452 if ((savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
453 safe_signal(SIGHUP, collhup);
454 savetstp = safe_signal(SIGTSTP, collstop);
455 savettou = safe_signal(SIGTTOU, collstop);
456 savettin = safe_signal(SIGTTIN, collstop);
457 if (sigsetjmp(collabort, 1)) {
458 if (tempMail != NULL) {
459 rm(tempMail);
460 Ftfree(&tempMail);
462 goto err;
464 if (sigsetjmp(colljmp, 1)) {
465 if (tempMail != NULL) {
466 rm(tempMail);
467 Ftfree(&tempMail);
469 goto err;
471 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
473 noreset++;
474 if ((collf = Ftemp(&tempMail, "Rs", "w+", 0600, 1)) == NULL) {
475 perror(catgets(catd, CATSET, 51, "temporary mail file"));
476 goto err;
478 unlink(tempMail);
479 Ftfree(&tempMail);
481 if ((cp = value("NAIL_HEAD")) != NULL) {
482 if (is_a_tty[0])
483 putesc(cp, stdout);
484 putesc(cp, collf);
488 * If we are going to prompt for a subject,
489 * refrain from printing a newline after
490 * the headers (since some people mind).
492 getfields = 0;
493 if (!tflag) {
494 t = GTO|GSUBJECT|GCC|GNL;
495 if (value("fullnames"))
496 t |= GCOMMA;
497 if (hp->h_subject == NULL && (val & val_INTERACT) &&
498 (value("ask") != NULL || value("asksub") != NULL))
499 t &= ~GNL, getfields |= GSUBJECT;
500 if (hp->h_to == NULL && (val & val_INTERACT))
501 t &= ~GNL, getfields |= GTO;
502 if (value("bsdcompat") == NULL && value("askatend") == NULL &&
503 (val & val_INTERACT)) {
504 if (hp->h_bcc == NULL && value("askbcc"))
505 t &= ~GNL, getfields |= GBCC;
506 if (hp->h_cc == NULL && value("askcc"))
507 t &= ~GNL, getfields |= GCC;
509 if (printheaders) {
510 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE,
511 NULL, NULL);
512 fflush(stdout);
517 * Quote an original message
519 if (mp != NULL && (doprefix || (quote = value("quote")) != NULL)) {
520 quoteig = allignore;
521 action = SEND_QUOTE;
522 if (doprefix) {
523 quoteig = fwdignore;
524 if ((cp = value("fwdheading")) == NULL)
525 cp = "-------- Original Message --------";
526 if (*cp) {
527 fprintf(collf, "%s\n", cp);
528 fprintf(stdout, "%s\n", cp);
530 } else if (strcmp(quote, "noheading") == 0) {
531 /*EMPTY*/;
532 } else if (strcmp(quote, "headers") == 0) {
533 quoteig = ignore;
534 } else if (strcmp(quote, "allheaders") == 0) {
535 quoteig = NULL;
536 action = SEND_QUOTE_ALL;
537 } else {
538 cp = hfield1("from", mp);
539 if (cp != NULL) {
540 mime_write(cp, strlen(cp),
541 collf, CONV_FROMHDR, TD_NONE,
542 NULL, (size_t) 0,
543 NULL, NULL);
544 mime_write(cp, strlen(cp),
545 stdout, CONV_FROMHDR, TD_NONE,
546 NULL, (size_t) 0,
547 NULL, NULL);
548 fwrite(catgets(catd, CATSET, 52,
549 " wrote:\n\n"), sizeof(char), 9, collf);
550 fwrite(catgets(catd, CATSET, 52,
551 " wrote:\n\n"), sizeof(char), 9, stdout);
554 cp = value("indentprefix");
555 if (cp != NULL && *cp == '\0')
556 cp = "\t";
557 send(mp, collf, quoteig, doprefix ? NULL : cp, action, NULL);
558 send(mp, stdout, quoteig, doprefix ? NULL : cp, action, NULL);
561 if ((cp = value("escape")) != NULL)
562 escape = *cp;
563 else
564 escape = ESCAPE;
565 eofcount = 0;
566 hadintr = 0;
568 if (!sigsetjmp(colljmp, 1)) {
569 if (getfields)
570 grabh(hp, getfields, 1);
571 if (quotefile != NULL) {
572 if (include_file(NULL, quotefile, &lc, &cc, 1) != 0)
573 goto err;
575 } else {
577 * Come here for printing the after-signal message.
578 * Duplicate messages won't be printed because
579 * the write is aborted if we get a SIGTTOU.
581 cont:
582 if (hadintr) {
583 fflush(stdout);
584 fprintf(stderr, catgets(catd, CATSET, 53,
585 "\n(Interrupt -- one more to kill letter)\n"));
586 } else {
587 printf(catgets(catd, CATSET, 54, "(continue)\n"));
588 fflush(stdout);
591 if ((val & val_INTERACT) == 0 && tildeflag <= 0 &&
592 !is_a_tty[0] && !tflag) {
594 * No tilde escapes, interrupts not expected. Copy
595 * standard input the simple way.
597 linebuf = srealloc(linebuf, linesize = LINESIZE);
598 while ((count = fread(linebuf, sizeof *linebuf,
599 linesize, stdin)) > 0) {
600 if ((size_t)count != fwrite(linebuf, sizeof *linebuf,
601 count, collf))
602 goto err;
604 goto out;
606 for (;;) {
607 colljmp_p = 1;
608 count = readline(stdin, &linebuf, &linesize);
609 colljmp_p = 0;
610 if (count < 0) {
611 if ((val & val_INTERACT) &&
612 value("ignoreeof") != NULL && ++eofcount < 25) {
613 printf(catgets(catd, CATSET, 55,
614 "Use \".\" to terminate letter\n"));
615 continue;
617 break;
619 if (tflag && count == 0) {
620 rewind(collf);
621 if (makeheader(collf, hp) != OKAY)
622 goto err;
623 rewind(collf);
624 tflag = 0;
625 continue;
627 eofcount = 0;
628 hadintr = 0;
629 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
630 (val & val_INTERACT) &&
631 (value("dot") != NULL || value("ignoreeof") != NULL))
632 break;
633 if (linebuf[0] != escape ||
634 ((val & val_INTERACT) == 0 && tildeflag == 0) ||
635 tildeflag < 0) {
636 if (putline(collf, linebuf, count) < 0)
637 goto err;
638 continue;
640 c = linebuf[1];
641 switch (c) {
642 default:
644 * On double escape, just send the single one.
645 * Otherwise, it's an error.
647 if (c == escape) {
648 if (putline(collf, &linebuf[1], count - 1) < 0)
649 goto err;
650 else
651 break;
653 printf(catgets(catd, CATSET, 56,
654 "Unknown tilde escape.\n"));
655 break;
656 #ifdef DEBUG_COMMANDS
657 case 'C':
659 * Dump core.
661 core(NULL);
662 break;
663 #endif /* DEBUG_COMMANDS */
664 case '!':
666 * Shell escape, send the balance of the
667 * line to sh -c.
669 shell(&linebuf[2]);
670 break;
671 case ':':
672 case '_':
674 * Escape to command mode, but be nice!
676 inhook = 0;
677 execute(&linebuf[2], 1, count - 2);
678 goto cont;
679 case '.':
681 * Simulate end of file on input.
683 goto out;
684 case 'x':
686 * Same as 'q', but no dead.letter saving.
688 hadintr++;
689 collint(0);
690 exit(1);
691 /*NOTREACHED*/
692 case 'q':
694 * Force a quit of sending mail.
695 * Act like an interrupt happened.
697 hadintr++;
698 collint(SIGINT);
699 exit(1);
700 /*NOTREACHED*/
701 case 'h':
703 * Grab a bunch of headers.
706 grabh(hp, GTO|GSUBJECT|GCC|GBCC,
707 value("bsdcompat") != NULL &&
708 value("bsdorder") != NULL);
709 while (hp->h_to == NULL);
710 goto cont;
711 case 'H':
713 * Grab extra headers.
716 grabh(hp, GEXTRA, 0);
717 while (check_from_and_sender(hp->h_from, hp->h_sender));
718 goto cont;
719 case 't':
721 * Add to the To list.
723 while ((hp->h_to = cat(hp->h_to, checkaddrs(
724 lextract(&linebuf[2], GTO|GFULL))))
725 == NULL);
726 break;
727 case 's':
729 * Set the Subject list.
731 cp = &linebuf[2];
732 while (whitechar(*cp & 0377))
733 cp++;
734 hp->h_subject = savestr(cp);
735 break;
736 case '@':
738 * Edit the attachment list.
740 if (linebuf[2] != '\0')
741 hp->h_attach = append_attachments(hp->h_attach,
742 &linebuf[2]);
743 else
744 hp->h_attach = edit_attachments(hp->h_attach);
745 break;
746 case 'c':
748 * Add to the CC list.
750 hp->h_cc = cat(hp->h_cc, checkaddrs(
751 lextract(&linebuf[2], GCC|GFULL)));
752 break;
753 case 'b':
755 * Add stuff to blind carbon copies list.
757 hp->h_bcc = cat(hp->h_bcc, checkaddrs(
758 lextract(&linebuf[2], GBCC|GFULL)));
759 break;
760 case 'd':
761 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
762 linebuf[linesize-1]='\0';
763 /*FALLTHRU*/
764 case 'r':
765 case '<':
767 * Invoke a file:
768 * Search for the file name,
769 * then open it and copy the contents to collf.
771 cp = &linebuf[2];
772 while (whitechar(*cp & 0377))
773 cp++;
774 if (*cp == '\0') {
775 printf(catgets(catd, CATSET, 57,
776 "Interpolate what file?\n"));
777 break;
779 if (*cp == '!') {
780 insertcommand(collf, cp + 1);
781 break;
783 cp = expand(cp);
784 if (cp == NULL)
785 break;
786 if (is_dir(cp)) {
787 printf(catgets(catd, CATSET, 58,
788 "%s: Directory\n"), cp);
789 break;
791 if ((fbuf = Fopen(cp, "r")) == NULL) {
792 perror(cp);
793 break;
795 printf(catgets(catd, CATSET, 59, "\"%s\" "), cp);
796 fflush(stdout);
797 if (include_file(fbuf, cp, &lc, &cc, 0) != 0)
798 goto err;
799 printf(catgets(catd, CATSET, 60, "%d/%d\n"), lc, cc);
800 break;
801 case 'i':
803 * Insert an environment variable into the file.
805 cp = &linebuf[2];
806 while (whitechar(*cp & 0377))
807 cp++;
808 if ((cp = value(cp)) == NULL || *cp == '\0')
809 break;
810 if (is_a_tty[0])
811 putesc(cp, stdout);
812 putesc(cp, collf);
813 break;
814 case 'a':
815 case 'A':
817 * Insert the contents of a signature variable.
819 if ((cp = value(c == 'a' ? "sign" : "Sign")) != NULL &&
820 *cp != '\0') {
821 if (is_a_tty[0])
822 putesc(cp, stdout);
823 putesc(cp, collf);
825 break;
826 case 'w':
828 * Write the message on a file.
830 cp = &linebuf[2];
831 while (blankchar(*cp & 0377))
832 cp++;
833 if (*cp == '\0') {
834 fprintf(stderr, catgets(catd, CATSET, 61,
835 "Write what file!?\n"));
836 break;
838 if ((cp = expand(cp)) == NULL)
839 break;
840 rewind(collf);
841 exwrite(cp, collf, 1);
842 break;
843 case 'm':
844 case 'M':
845 case 'f':
846 case 'F':
848 * Interpolate the named messages, if we
849 * are in receiving mail mode. Does the
850 * standard list processing garbage.
851 * If ~f is given, we don't shift over.
853 if (forward(linebuf + 2, collf, c) < 0)
854 goto err;
855 goto cont;
856 case 'p':
858 * Print out the current state of the
859 * message without altering anything.
861 print_collf(collf, hp);
862 goto cont;
863 case '|':
865 * Pipe message through command.
866 * Collect output as new message.
868 rewind(collf);
869 mespipe(&linebuf[2]);
870 goto cont;
871 case 'v':
872 case 'e':
874 * Edit the current message.
875 * 'e' means to use EDITOR
876 * 'v' means to use VISUAL
878 rewind(collf);
879 mesedit(c, value("editheaders") ? hp : NULL);
880 goto cont;
881 case '?':
883 * Last the lengthy help string.
884 * (Very ugly, but take care for compiler supported
885 * string lengths :()
887 puts(
888 "-------------------- ~ ESCAPES ----------------------------\n"
889 "~~ Quote a single tilde\n"
890 "~@ [file ...] Edit attachment list\n"
891 "~b users Add users to \"blind\" cc list\n"
892 "~c users Add users to cc list\n"
893 "~d Read in dead.letter\n"
894 "~e Edit the message buffer\n"
895 "~f messages Read in messages without indenting lines\n"
896 "~F messages Same as ~f, but keep all header lines\n"
897 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n");
898 puts(
899 "~r file Read a file into the message buffer\n"
900 "~p Print the message buffer\n"
901 "~q Abort message composition and save text to dead.letter\n"
902 "~m messages Read in messages with each line indented\n"
903 "~M messages Same as ~m, but keep all header lines\n"
904 "~s subject Set subject\n"
905 "~t users Add users to to list\n"
906 "~v Invoke display editor on message\n"
907 "~w file Write message onto file\n"
908 "~x Abort message composition and discard text written so far\n");
909 puts(
910 "~!command Invoke the shell\n"
911 "~:command Execute a regular command\n"
912 "-----------------------------------------------------------\n");
913 break;
917 goto out;
918 err:
919 if (collf != NULL) {
920 Fclose(collf);
921 collf = NULL;
923 out:
924 if (collf != NULL) {
925 if ((cp = value("NAIL_TAIL")) != NULL) {
926 if (is_a_tty[0])
927 putesc(cp, stdout);
928 fflush(collf);
929 putesc(cp, collf);
931 rewind(collf);
933 handlerpop();
934 noreset--;
935 sigemptyset(&nset);
936 sigaddset(&nset, SIGINT);
937 sigaddset(&nset, SIGHUP);
938 #ifndef OLDBUG
939 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
940 #else
941 sigprocmask(SIG_BLOCK, &nset, &oset);
942 #endif
943 safe_signal(SIGINT, saveint);
944 safe_signal(SIGHUP, savehup);
945 safe_signal(SIGTSTP, savetstp);
946 safe_signal(SIGTTOU, savettou);
947 safe_signal(SIGTTIN, savettin);
948 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
949 return collf;
953 * Write a file, ex-like if f set.
955 static int
956 exwrite(char *name, FILE *fp, int f)
958 FILE *of;
959 int c;
960 long cc;
961 int lc;
963 if (f) {
964 printf("\"%s\" ", name);
965 fflush(stdout);
967 if ((of = Fopen(name, "a")) == NULL) {
968 perror(NULL);
969 return(-1);
971 lc = 0;
972 cc = 0;
973 while ((c = getc(fp)) != EOF) {
974 cc++;
975 if (c == '\n')
976 lc++;
977 putc(c, of);
978 if (ferror(of)) {
979 perror(name);
980 Fclose(of);
981 return(-1);
984 Fclose(of);
985 printf(catgets(catd, CATSET, 65, "%d/%ld\n"), lc, cc);
986 fflush(stdout);
987 return(0);
990 static enum okay
991 makeheader(FILE *fp, struct header *hp)
993 char *tempEdit;
994 FILE *nf;
995 int c;
997 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
998 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
999 Fclose(nf);
1000 unlink(tempEdit);
1001 Ftfree(&tempEdit);
1002 return STOP;
1004 unlink(tempEdit);
1005 Ftfree(&tempEdit);
1006 extract_header(fp, hp);
1007 while ((c = getc(fp)) != EOF)
1008 putc(c, nf);
1009 if (fp != collf)
1010 Fclose(collf);
1011 Fclose(fp);
1012 collf = nf;
1013 if (check_from_and_sender(hp->h_from, hp->h_sender))
1014 return STOP;
1015 return OKAY;
1019 * Edit the message being collected on fp.
1020 * On return, make the edit file the new temp file.
1022 static void
1023 mesedit(int c, struct header *hp)
1025 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1026 FILE *nf = run_editor(collf, (off_t)-1, c, 0, hp, NULL, SEND_MBOX,
1027 sigint);
1029 if (nf != NULL) {
1030 if (hp) {
1031 rewind(nf);
1032 makeheader(nf, hp);
1033 } else {
1034 fseek(nf, 0L, SEEK_END);
1035 Fclose(collf);
1036 collf = nf;
1039 safe_signal(SIGINT, sigint);
1043 * Pipe the message through the command.
1044 * Old message is on stdin of command;
1045 * New message collected from stdout.
1046 * Sh -c must return 0 to accept the new message.
1048 static void
1049 mespipe(char *cmd)
1051 FILE *nf;
1052 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
1053 char *tempEdit;
1054 char *shell;
1056 if ((nf = Ftemp(&tempEdit, "Re", "w+", 0600, 1)) == NULL) {
1057 perror(catgets(catd, CATSET, 66, "temporary mail edit file"));
1058 goto out;
1060 fflush(collf);
1061 unlink(tempEdit);
1062 Ftfree(&tempEdit);
1064 * stdin = current message.
1065 * stdout = new message.
1067 if ((shell = value("SHELL")) == NULL)
1068 shell = SHELL;
1069 if (run_command(shell,
1070 0, fileno(collf), fileno(nf), "-c", cmd, NULL) < 0) {
1071 Fclose(nf);
1072 goto out;
1074 if (fsize(nf) == 0) {
1075 fprintf(stderr, catgets(catd, CATSET, 67,
1076 "No bytes from \"%s\" !?\n"), cmd);
1077 Fclose(nf);
1078 goto out;
1081 * Take new files.
1083 fseek(nf, 0L, SEEK_END);
1084 Fclose(collf);
1085 collf = nf;
1086 out:
1087 safe_signal(SIGINT, sigint);
1091 * Interpolate the named messages into the current
1092 * message, preceding each line with a tab.
1093 * Return a count of the number of characters now in
1094 * the message, or -1 if an error is encountered writing
1095 * the message temporary. The flag argument is 'm' if we
1096 * should shift over and 'f' if not.
1098 static int
1099 forward(char *ms, FILE *fp, int f)
1101 int *msgvec;
1102 struct ignoretab *ig;
1103 char *tabst;
1104 enum sendaction action;
1106 /*LINTED*/
1107 msgvec = (int *)salloc((msgCount+1) * sizeof *msgvec);
1108 if (msgvec == NULL)
1109 return(0);
1110 if (getmsglist(ms, msgvec, 0) < 0)
1111 return(0);
1112 if (*msgvec == 0) {
1113 *msgvec = first(0, MMNORM);
1114 if (*msgvec == 0) {
1115 printf(catgets(catd, CATSET, 68,
1116 "No appropriate messages\n"));
1117 return(0);
1119 msgvec[1] = 0;
1121 if (f == 'f' || f == 'F')
1122 tabst = NULL;
1123 else if ((tabst = value("indentprefix")) == NULL)
1124 tabst = "\t";
1125 ig = upperchar(f) ? (struct ignoretab *)NULL : ignore;
1126 action = upperchar(f) ? SEND_QUOTE_ALL : SEND_QUOTE;
1127 printf(catgets(catd, CATSET, 69, "Interpolating:"));
1128 for (; *msgvec != 0; msgvec++) {
1129 struct message *mp = message + *msgvec - 1;
1131 touch(mp);
1132 printf(" %d", *msgvec);
1133 if (send(mp, fp, ig, tabst, action, NULL) < 0) {
1134 perror(catgets(catd, CATSET, 70,
1135 "temporary mail file"));
1136 return(-1);
1139 printf("\n");
1140 return(0);
1144 * Print (continue) when continued after ^Z.
1146 /*ARGSUSED*/
1147 static void
1148 collstop(int s)
1150 sighandler_type old_action = safe_signal(s, SIG_DFL);
1151 sigset_t nset;
1153 sigemptyset(&nset);
1154 sigaddset(&nset, s);
1155 sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
1156 kill(0, s);
1157 sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
1158 safe_signal(s, old_action);
1159 if (colljmp_p) {
1160 colljmp_p = 0;
1161 hadintr = 0;
1162 siglongjmp(colljmp, 1);
1167 * On interrupt, come here to save the partial message in ~/dead.letter.
1168 * Then jump out of the collection loop.
1170 /*ARGSUSED*/
1171 static void
1172 collint(int s)
1175 * the control flow is subtle, because we can be called from ~q.
1177 if (!hadintr) {
1178 if (value("ignore") != NULL) {
1179 puts("@");
1180 fflush(stdout);
1181 clearerr(stdin);
1182 return;
1184 hadintr = 1;
1185 siglongjmp(colljmp, 1);
1187 exit_status |= 04;
1188 rewind(collf);
1189 if (value("save") != NULL && s != 0)
1190 savedeadletter(collf);
1191 siglongjmp(collabort, 1);
1194 /*ARGSUSED*/
1195 static void
1196 collhup(int s)
1198 (void)s;
1199 rewind(collf);
1200 savedeadletter(collf);
1202 * Let's pretend nobody else wants to clean up,
1203 * a true statement at this time.
1205 exit(1);
1208 void
1209 savedeadletter(FILE *fp)
1211 FILE *dbuf;
1212 int c, lines = 0, bytes = 0;
1213 char *cp;
1215 if (fsize(fp) == 0)
1216 return;
1217 cp = getdeadletter();
1218 c = umask(077);
1219 dbuf = Fopen(cp, "a");
1220 umask(c);
1221 if (dbuf == NULL)
1222 return;
1223 printf("\"%s\" ", cp);
1224 while ((c = getc(fp)) != EOF) {
1225 putc(c, dbuf);
1226 bytes++;
1227 if (c == '\n')
1228 lines++;
1230 Fclose(dbuf);
1231 printf("%d/%d\n", lines, bytes);
1232 rewind(fp);
1235 static int
1236 putesc(const char *s, FILE *stream)
1238 int n = 0;
1240 while (s[0]) {
1241 if (s[0] == '\\') {
1242 if (s[1] == 't') {
1243 putc('\t', stream);
1244 n++;
1245 s += 2;
1246 continue;
1248 if (s[1] == 'n') {
1249 putc('\n', stream);
1250 n++;
1251 s += 2;
1252 continue;
1255 putc(s[0]&0377, stream);
1256 n++;
1257 s++;
1259 putc('\n', stream);
1260 return ++n;