Merge branch 'topic/colour.2'
[s-mailx.git] / collect.c
blob2913e348d483d2e83abaebfcafd96d8d97ccfff5
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Collect input from standard input, handling ~ escapes.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE collect
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* The following hookiness with global variables is so that on receipt of an
43 * interrupt signal, the partial message can be salted away on *DEAD* */
45 static sighandler_type _coll_saveint; /* Previous SIGINT value */
46 static sighandler_type _coll_savehup; /* Previous SIGHUP value */
47 static sighandler_type _coll_savetstp; /* Previous SIGTSTP value */
48 static sighandler_type _coll_savettou; /* Previous SIGTTOU value */
49 static sighandler_type _coll_savettin; /* Previous SIGTTIN value */
50 static FILE *_coll_fp; /* File for saving away */
51 static int volatile _coll_hadintr; /* Have seen one SIGINT so far */
52 static sigjmp_buf _coll_jmp; /* To get back to work */
53 static int _coll_jmp_p; /* whether to long jump */
54 static sigjmp_buf _coll_abort; /* To end collection with error */
55 static sigjmp_buf _coll_pipejmp; /* On broken pipe */
57 /* Handle `~:', `~_' */
58 static void _execute_command(struct header *hp, char *linebuf,
59 size_t linesize);
61 /* If *interactive* is set and *doecho* is, too, also dump to *stdout* */
62 static int _include_file(char const *name, int *linecount,
63 int *charcount, bool_t doecho, bool_t indent);
65 static void _collect_onpipe(int signo);
67 /* Execute cmd and insert its standard output into fp */
68 static void insertcommand(FILE *fp, char const *cmd);
70 /* ~p command */
71 static void print_collf(FILE *collf, struct header *hp);
73 /* Write a file, ex-like if f set */
74 static int exwrite(char const *name, FILE *fp, int f);
76 /* Parse off the message header from fp and store relevant fields in hp,
77 * replace _coll_fp with a shiny new version without any header */
78 static enum okay makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err);
80 /* Edit the message being collected on fp. On return, make the edit file the
81 * new temp file */
82 static void mesedit(int c, struct header *hp);
84 /* Pipe the message through the command. Old message is on stdin of command,
85 * new message collected from stdout. Shell must return 0 to accept new msg */
86 static void mespipe(char *cmd);
88 /* Interpolate the named messages into the current message, possibly doing
89 * indent stuff. The flag argument is one of the tilde escapes: [mMfFuU].
90 * Return a count of the number of characters now in the message, or -1 if an
91 * error is encountered writing the message temporary */
92 static int forward(char *ms, FILE *fp, int f);
94 /* Print (continue) when continued after ^Z */
95 static void collstop(int s);
97 /* On interrupt, come here to save the partial message in ~/dead.letter.
98 * Then jump out of the collection loop */
99 static void _collint(int s);
101 static void collhup(int s);
103 static int putesc(char const *s, FILE *stream);
105 static void
106 _execute_command(struct header *hp, char *linebuf, size_t linesize)
108 /* The problem arises if there are rfc822 message attachments and the
109 * user uses `~:' to change the current file. TODO Unfortunately we
110 * TODO cannot simply keep a pointer to, or increment a reference count
111 * TODO of the current `file' (mailbox that is) object, because the
112 * TODO codebase doesn't deal with that at all; so, until some far
113 * TODO later time, copy the name of the path, and warn the user if it
114 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
115 * TODO copy the message attachments over to temporary files, but that
116 * TODO would require more changes so that the user still can recognize
117 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
118 char *mnbuf = NULL;
119 size_t mnlen = 0 /* silence CC */;
120 struct attachment *ap;
121 NYD_ENTER;
123 /* If the above todo is worked, remove or outsource to attachments.c! */
124 if ((ap = hp->h_attach) != NULL) do
125 if (ap->a_msgno) {
126 mnlen = strlen(mailname) +1;
127 mnbuf = ac_alloc(mnlen);
128 memcpy(mnbuf, mailname, mnlen);
129 break;
131 while ((ap = ap->a_flink) != NULL);
133 pstate &= ~PS_HOOK_MASK;
134 execute(linebuf, linesize);
136 if (mnbuf != NULL) {
137 if (strncmp(mnbuf, mailname, mnlen))
138 n_err(_("Mailbox changed: it is likely that existing "
139 "rfc822 attachments became invalid!\n"));
140 ac_free(mnbuf);
142 NYD_LEAVE;
145 static int
146 _include_file(char const *name, int *linecount, int *charcount,
147 bool_t doecho, bool_t indent)
149 FILE *fbuf;
150 char const *indb;
151 int ret = -1;
152 char *linebuf = NULL; /* TODO line pool */
153 size_t linesize = 0, indl, linelen, cnt;
154 NYD_ENTER;
156 if ((fbuf = Fopen(name, "r")) == NULL) {
157 n_perr(name, 0);
158 goto jleave;
161 if (!indent)
162 indb = NULL, indl = 0;
163 else {
164 if ((indb = ok_vlook(indentprefix)) == NULL)
165 indb = INDENT_DEFAULT;
166 indl = strlen(indb);
169 *linecount = *charcount = 0;
170 cnt = fsize(fbuf);
171 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
172 if (indl > 0 && fwrite(indb, sizeof *indb, indl, _coll_fp) != indl)
173 goto jleave;
174 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
175 goto jleave;
176 ++(*linecount);
177 (*charcount) += linelen + indl;
178 if ((options & OPT_INTERACTIVE) && doecho) {
179 if (indl > 0)
180 fwrite(indb, sizeof *indb, indl, stdout);
181 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
184 if (fflush(_coll_fp))
185 goto jleave;
186 if ((options & OPT_INTERACTIVE) && doecho)
187 fflush(stdout);
189 ret = 0;
190 jleave:
191 if (linebuf != NULL)
192 free(linebuf);
193 if (fbuf != NULL)
194 Fclose(fbuf);
195 NYD_LEAVE;
196 return ret;
199 static void
200 _collect_onpipe(int signo)
202 NYD_X; /* Signal handler */
203 UNUSED(signo);
204 siglongjmp(_coll_pipejmp, 1);
207 static void
208 insertcommand(FILE *fp, char const *cmd)
210 FILE *ibuf = NULL;
211 char const *cp;
212 int c;
213 NYD_ENTER;
215 cp = ok_vlook(SHELL);
216 if (cp == NULL)
217 cp = XSHELL;
219 if ((ibuf = Popen(cmd, "r", cp, NULL, 0)) != NULL) {
220 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
221 putc(c, fp);
222 Pclose(ibuf, TRU1);
223 } else
224 n_perr(cmd, 0);
225 NYD_LEAVE;
228 static void
229 print_collf(FILE *cf, struct header *hp)
231 char *lbuf = NULL; /* TODO line pool */
232 sighandler_type sigint;
233 FILE * volatile obuf = stdout;
234 struct attachment *ap;
235 char const *cp;
236 enum gfield gf;
237 size_t linesize = 0, linelen, cnt, cnt2;
238 NYD_ENTER;
240 fflush_rewind(cf);
241 cnt = cnt2 = fsize(cf);
243 sigint = safe_signal(SIGINT, SIG_IGN);
245 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
246 size_t l, m;
248 m = 4;
249 if (hp->h_to != NULL)
250 ++m;
251 if (hp->h_subject != NULL)
252 ++m;
253 if (hp->h_cc != NULL)
254 ++m;
255 if (hp->h_bcc != NULL)
256 ++m;
257 if (hp->h_attach != NULL)
258 ++m;
259 m += (hp->h_from != NULL || myaddrs(hp) != NULL);
260 m += (hp->h_sender != NULL || ok_vlook(sender) != NULL);
261 m += (hp->h_replyto != NULL || ok_vlook(replyto) != NULL);
262 m += (hp->h_organization != NULL || ok_vlook(ORGANIZATION) != NULL);
264 l = (*cp == '\0') ? screensize() : atoi(cp);
265 if (m > l)
266 goto jpager;
267 l -= m;
269 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
271 rewind(cf);
272 if (l < m) {
273 jpager:
274 cp = get_pager(NULL);
275 if (sigsetjmp(_coll_pipejmp, 1))
276 goto jendpipe;
277 obuf = Popen(cp, "w", NULL, NULL, 1);
278 if (obuf == NULL) {
279 n_perr(cp, 0);
280 obuf = stdout;
281 } else
282 safe_signal(SIGPIPE, &_collect_onpipe);
286 fprintf(obuf, _("-------\nMessage contains:\n"));
287 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
288 puthead(TRU1, hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
289 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
290 prout(lbuf, linelen, obuf);
291 if (hp->h_attach != NULL) {
292 fputs(_("-------\nAttachments:\n"), obuf);
293 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
294 if (ap->a_msgno)
295 fprintf(obuf, " - message %u\n", ap->a_msgno);
296 else {
297 /* TODO after MIME/send layer rewrite we *know*
298 * TODO the details of the attachment here,
299 * TODO so adjust this again, then */
300 char const *cs, *csi = "-> ";
302 if ((cs = ap->a_charset) == NULL &&
303 (csi = "<- ", cs = ap->a_input_charset) == NULL)
304 cs = charset_get_lc();
305 if ((cp = ap->a_content_type) == NULL)
306 cp = "?";
307 else if (ascncasecmp(cp, "text/", 5))
308 csi = "";
309 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
314 jendpipe:
315 if (obuf != stdout) {
316 safe_signal(SIGPIPE, SIG_IGN);
317 Pclose(obuf, TRU1);
318 safe_signal(SIGPIPE, dflpipe);
320 if (lbuf != NULL)
321 free(lbuf);
322 safe_signal(SIGINT, sigint);
323 NYD_LEAVE;
326 static int
327 exwrite(char const *name, FILE *fp, int f)
329 FILE *of;
330 int c, lc, rv = -1;
331 long cc;
332 NYD_ENTER;
334 if (f) {
335 printf("\"%s\" ", name);
336 fflush(stdout);
338 if ((of = Fopen(name, "a")) == NULL) {
339 n_perr(NULL, 0);
340 goto jleave;
343 lc = 0;
344 cc = 0;
345 while ((c = getc(fp)) != EOF) {
346 ++cc;
347 if (c == '\n')
348 ++lc;
349 putc(c, of);
350 if (ferror(of)) {
351 n_perr(name, 0);
352 Fclose(of);
353 goto jleave;
356 Fclose(of);
357 printf(_("%d/%ld\n"), lc, cc);
358 fflush(stdout);
359 rv = 0;
360 jleave:
361 NYD_LEAVE;
362 return rv;
365 static enum okay
366 makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err)
368 FILE *nf;
369 int c;
370 enum okay rv = STOP;
371 NYD_ENTER;
373 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
374 n_perr(_("temporary mail edit file"), 0);
375 goto jleave;
378 extract_header(fp, hp, checkaddr_err);
380 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
381 putc(c, nf);
382 if (fp != _coll_fp)
383 Fclose(_coll_fp);
384 Fclose(fp);
385 _coll_fp = nf;
386 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
387 goto jleave;
388 rv = OKAY;
389 jleave:
390 NYD_LEAVE;
391 return rv;
394 static void
395 mesedit(int c, struct header *hp)
397 bool_t saved;
398 sighandler_type sigint;
399 FILE *nf;
400 NYD_ENTER;
402 saved = ok_blook(add_file_recipients);
403 ok_bset(add_file_recipients, TRU1);
405 sigint = safe_signal(SIGINT, SIG_IGN);
406 nf = run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint);
407 if (nf != NULL) {
408 if (hp) {
409 rewind(nf);
410 makeheader(nf, hp, NULL);
411 } else {
412 fseek(nf, 0L, SEEK_END);
413 Fclose(_coll_fp);
414 _coll_fp = nf;
417 safe_signal(SIGINT, sigint);
419 ok_bset(add_file_recipients, saved);
420 NYD_LEAVE;
423 static void
424 mespipe(char *cmd)
426 FILE *nf;
427 sighandler_type sigint;
428 char const *sh;
429 NYD_ENTER;
431 sigint = safe_signal(SIGINT, SIG_IGN);
433 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
434 n_perr(_("temporary mail edit file"), 0);
435 goto jout;
438 /* stdin = current message. stdout = new message */
439 if ((sh = ok_vlook(SHELL)) == NULL)
440 sh = XSHELL;
441 fflush(_coll_fp);
442 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL, NULL)
443 < 0) {
444 Fclose(nf);
445 goto jout;
448 if (fsize(nf) == 0) {
449 n_err(_("No bytes from \"%s\" !?\n"), cmd);
450 Fclose(nf);
451 goto jout;
454 /* Take new files */
455 fseek(nf, 0L, SEEK_END);
456 Fclose(_coll_fp);
457 _coll_fp = nf;
458 jout:
459 safe_signal(SIGINT, sigint);
460 NYD_LEAVE;
463 static int
464 forward(char *ms, FILE *fp, int f)
466 int *msgvec, rv = 0;
467 struct ignoretab *ig;
468 char const *tabst;
469 enum sendaction action;
470 NYD_ENTER;
472 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
473 if (getmsglist(ms, msgvec, 0) < 0)
474 goto jleave;
475 if (*msgvec == 0) {
476 *msgvec = first(0, MMNORM);
477 if (*msgvec == 0) {
478 n_err(_("No appropriate messages\n"));
479 goto jleave;
481 msgvec[1] = 0;
484 if (f == 'f' || f == 'F' || f == 'u')
485 tabst = NULL;
486 else if ((tabst = ok_vlook(indentprefix)) == NULL)
487 tabst = INDENT_DEFAULT;
488 if (f == 'u' || f == 'U')
489 ig = allignore;
490 else
491 ig = upperchar(f) ? NULL : ignore;
492 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
494 printf(_("Interpolating:"));
495 for (; *msgvec != 0; ++msgvec) {
496 struct message *mp = message + *msgvec - 1;
498 touch(mp);
499 printf(" %d", *msgvec);
500 fflush(stdout);
501 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
502 n_perr(_("temporary mail file"), 0);
503 rv = -1;
504 break;
507 printf("\n");
508 jleave:
509 NYD_LEAVE;
510 return rv;
513 static void
514 collstop(int s)
516 sighandler_type old_action;
517 sigset_t nset;
518 NYD_X; /* Signal handler */
520 old_action = safe_signal(s, SIG_DFL);
522 sigemptyset(&nset);
523 sigaddset(&nset, s);
524 sigprocmask(SIG_UNBLOCK, &nset, NULL);
525 n_raise(s);
526 sigprocmask(SIG_BLOCK, &nset, NULL);
528 safe_signal(s, old_action);
529 if (_coll_jmp_p) {
530 _coll_jmp_p = 0;
531 _coll_hadintr = 0;
532 siglongjmp(_coll_jmp, 1);
536 static void
537 _collint(int s)
539 NYD_X; /* Signal handler */
541 /* the control flow is subtle, because we can be called from ~q */
542 if (_coll_hadintr == 0) {
543 if (ok_blook(ignore)) {
544 puts("@");
545 fflush(stdout);
546 clearerr(stdin);
547 } else
548 _coll_hadintr = 1;
549 siglongjmp(_coll_jmp, 1);
551 exit_status |= EXIT_SEND_ERROR;
552 if (s != 0)
553 savedeadletter(_coll_fp, 1);
554 /* Aborting message, no need to fflush() .. */
555 siglongjmp(_coll_abort, 1);
558 static void
559 collhup(int s)
561 NYD_X; /* Signal handler */
562 UNUSED(s);
564 savedeadletter(_coll_fp, 1);
565 /* Let's pretend nobody else wants to clean up, a true statement at
566 * this time */
567 exit(EXIT_ERR);
570 static int
571 putesc(char const *s, FILE *stream)
573 int n = 0, rv = -1;
574 NYD_ENTER;
576 while (s[0] != '\0') {
577 if (s[0] == '\\') {
578 if (s[1] == 't') {
579 if (putc('\t', stream) == EOF)
580 goto jleave;
581 ++n;
582 s += 2;
583 continue;
585 if (s[1] == 'n') {
586 if (putc('\n', stream) == EOF)
587 goto jleave;
588 ++n;
589 s += 2;
590 continue;
593 if (putc(s[0], stream) == EOF)
594 goto jleave;
595 ++n;
596 ++s;
598 if (putc('\n', stream) == EOF)
599 goto jleave;
600 rv = ++n;
601 jleave:
602 NYD_LEAVE;
603 return rv;
606 FL FILE *
607 collect(struct header *hp, int printheaders, struct message *mp,
608 char *quotefile, int doprefix, si8_t *checkaddr_err)
610 struct ignoretab *quoteig;
611 int lc, cc, c, t;
612 int volatile escape, getfields;
613 char *linebuf, *quote;
614 char const *cp;
615 size_t i, linesize; /* TODO line pool */
616 long cnt;
617 enum sendaction action;
618 sigset_t oset, nset;
619 sighandler_type savedtop;
620 FILE * volatile sigfp;
621 NYD_ENTER;
623 _coll_fp = NULL;
624 sigfp = NULL;
625 linesize = 0;
626 linebuf = quote = NULL;
628 /* Start catching signals from here, but we're still die on interrupts
629 * until we're in the main loop */
630 sigemptyset(&nset);
631 sigaddset(&nset, SIGINT);
632 sigaddset(&nset, SIGHUP);
633 sigprocmask(SIG_BLOCK, &nset, &oset);
634 handlerpush(&_collint);
635 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
636 safe_signal(SIGINT, &_collint);
637 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
638 safe_signal(SIGHUP, collhup);
639 /* TODO We do a lot of redundant signal handling, especially
640 * TODO with the command line editor(s); try to merge this */
641 _coll_savetstp = safe_signal(SIGTSTP, collstop);
642 _coll_savettou = safe_signal(SIGTTOU, collstop);
643 _coll_savettin = safe_signal(SIGTTIN, collstop);
644 if (sigsetjmp(_coll_abort, 1))
645 goto jerr;
646 if (sigsetjmp(_coll_jmp, 1))
647 goto jerr;
648 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
650 ++noreset;
651 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
652 NULL) {
653 n_perr(_("temporary mail file"), 0);
654 goto jerr;
658 char const *cp_obsolete = ok_vlook(NAIL_HEAD);
659 if(cp_obsolete != NULL)
660 OBSOLETE(_("please use *message-inject-head* instead of *NAIL_HEAD*"));
662 if(((cp = ok_vlook(message_inject_head)) != NULL ||
663 (cp = cp_obsolete) != NULL) && putesc(cp, _coll_fp) < 0)
664 goto jerr;
667 /* If we are going to prompt for a subject, refrain from printing a newline
668 * after the headers (since some people mind) */
669 getfields = 0;
670 if (!(options & OPT_t_FLAG)) {
671 t = GTO | GSUBJECT | GCC | GNL;
672 if (ok_blook(fullnames))
673 t |= GCOMMA;
675 if (options & OPT_INTERACTIVE) {
676 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
677 t &= ~GNL, getfields |= GSUBJECT;
679 if (hp->h_to == NULL)
680 t &= ~GNL, getfields |= GTO;
682 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
683 if (hp->h_bcc == NULL && ok_blook(askbcc))
684 t &= ~GNL, getfields |= GBCC;
685 if (hp->h_cc == NULL && ok_blook(askcc))
686 t &= ~GNL, getfields |= GCC;
690 if (printheaders && !ok_blook(editalong)) {
691 puthead(TRU1, hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
692 fflush(stdout);
696 /* Quote an original message */
697 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
698 quoteig = allignore;
699 action = SEND_QUOTE;
700 if (doprefix) {
701 quoteig = fwdignore;
702 if ((cp = ok_vlook(fwdheading)) == NULL)
703 cp = "-------- Original Message --------";
704 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
705 goto jerr;
706 } else if (!strcmp(quote, "noheading")) {
707 /*EMPTY*/;
708 } else if (!strcmp(quote, "headers")) {
709 quoteig = ignore;
710 } else if (!strcmp(quote, "allheaders")) {
711 quoteig = NULL;
712 action = SEND_QUOTE_ALL;
713 } else {
714 cp = hfield1("from", mp);
715 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
716 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
717 goto jerr;
718 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
719 goto jerr;
722 if (fflush(_coll_fp))
723 goto jerr;
724 if (doprefix)
725 cp = NULL;
726 else if ((cp = ok_vlook(indentprefix)) == NULL)
727 cp = INDENT_DEFAULT;
728 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
729 goto jerr;
732 /* Print what we have sofar also on the terminal (if useful) */
733 if ((options & OPT_INTERACTIVE) && !ok_blook(editalong)) {
734 rewind(_coll_fp);
735 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
736 putc(c, stdout);
737 if (fseek(_coll_fp, 0, SEEK_END))
738 goto jerr;
739 /* Ensure this is clean xxx not really necessary? */
740 fflush(stdout);
743 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
744 _coll_hadintr = 0;
746 if (!sigsetjmp(_coll_jmp, 1)) {
747 if (getfields)
748 grab_headers(hp, getfields, 1);
749 if (quotefile != NULL) {
750 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
751 goto jerr;
753 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
754 rewind(_coll_fp);
755 mesedit('e', hp);
756 goto jcont;
758 } else {
759 /* Come here for printing the after-signal message. Duplicate messages
760 * won't be printed because the write is aborted if we get a SIGTTOU */
761 if (_coll_hadintr) {
762 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
763 } else {
764 jcont:
765 printf(_("(continue)\n"));
766 fflush(stdout);
770 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
771 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
772 linebuf = srealloc(linebuf, linesize = LINESIZE);
773 while ((i = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
774 if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
775 goto jerr;
777 goto jout;
780 /* The interactive collect loop.
781 * All commands which come here are forbidden when sourcing! */
782 assert(_coll_hadintr || !(pstate & PS_SOURCING));
783 for (;;) {
784 _coll_jmp_p = 1;
785 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
786 _coll_jmp_p = 0;
788 if (cnt < 0) {
789 assert(!(pstate & PS_SOURCING));
790 if (options & OPT_t_FLAG) {
791 fflush_rewind(_coll_fp);
792 /* It is important to set PS_t_FLAG before extract_header() *and*
793 * keep OPT_t_FLAG for the first parse of the message, too! */
794 pstate |= PS_t_FLAG;
795 if (makeheader(_coll_fp, hp, checkaddr_err) != OKAY)
796 goto jerr;
797 options &= ~OPT_t_FLAG;
798 continue;
799 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
800 printf(_("*ignoreeof* set, use \"~.\" to terminate letter\n"));
801 continue;
803 break;
806 _coll_hadintr = 0;
808 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
809 jputline:
810 /* TODO calls putline(), which *always* appends LF;
811 * TODO thus, STDIN with -t will ALWAYS end with LF,
812 * TODO even if no trailing LF and QP encoding.
813 * TODO when finally changed, update cc-test.sh */
814 if (putline(_coll_fp, linebuf, cnt) < 0)
815 goto jerr;
816 continue;
817 } else if (linebuf[0] == '.') {
818 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
819 break;
821 if (linebuf[0] != escape)
822 goto jputline;
824 if (!(options & OPT_t_FLAG))
825 n_tty_addhist(linebuf, TRU1);
827 c = linebuf[1];
828 switch (c) {
829 default:
830 /* On double escape, send a single one. Otherwise, it's an error */
831 if (c == escape) {
832 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
833 goto jerr;
834 else
835 break;
837 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
838 break;
839 case '!':
840 /* Shell escape, send the balance of line to sh -c */
841 c_shell(linebuf + 2);
842 break;
843 case ':':
844 /* FALLTHRU */
845 case '_':
846 /* Escape to command mode, but be nice! */
847 _execute_command(hp, linebuf + 2, cnt - 2);
848 goto jcont;
849 case '.':
850 /* Simulate end of file on input */
851 goto jout;
852 case 'x':
853 /* Same as 'q', but no *DEAD* saving */
854 /* FALLTHRU */
855 case 'q':
856 /* Force a quit, act like an interrupt had happened */
857 ++_coll_hadintr;
858 _collint((c == 'x') ? 0 : SIGINT);
859 exit(EXIT_ERR);
860 /*NOTREACHED*/
861 case 'h':
862 /* Grab a bunch of headers */
864 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
865 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
866 while (hp->h_to == NULL);
867 goto jcont;
868 case 'H':
869 /* Grab extra headers */
871 grab_headers(hp, GEXTRA, 0);
872 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
873 goto jcont;
874 case 't':
875 /* Add to the To list */
876 hp->h_to = cat(hp->h_to,
877 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
878 NULL));
879 break;
880 case 's':
881 /* Set the Subject list */
882 cp = linebuf + 2;
883 while (whitechar(*cp))
884 ++cp;
885 hp->h_subject = savestr(cp);
886 break;
887 #ifdef HAVE_DEBUG
888 case 'S':
889 c_sstats(NULL);
890 break;
891 #endif
892 case '@':
893 /* Edit the attachment list */
894 if (linebuf[2] != '\0')
895 append_attachments(&hp->h_attach, linebuf + 2);
896 else
897 edit_attachments(&hp->h_attach);
898 break;
899 case 'c':
900 /* Add to the CC list */
901 hp->h_cc = cat(hp->h_cc,
902 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
903 NULL));
904 break;
905 case 'b':
906 /* Add stuff to blind carbon copies list */
907 hp->h_bcc = cat(hp->h_bcc,
908 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
909 NULL));
910 break;
911 case 'd':
912 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
913 linebuf[linesize - 1] = '\0';
914 /*FALLTHRU*/
915 case 'R':
916 case 'r':
917 case '<':
918 /* Invoke a file: Search for the file name, then open it and copy the
919 * contents to _coll_fp */
920 cp = linebuf + 2;
921 while (whitechar(*cp))
922 ++cp;
923 if (*cp == '\0') {
924 n_err(_("Interpolate what file?\n"));
925 break;
927 if (*cp == '!') {
928 insertcommand(_coll_fp, cp + 1);
929 break;
931 if ((cp = file_expand(cp)) == NULL)
932 break;
933 if (is_dir(cp)) {
934 n_err(_("\"%s\": Directory\n"), cp);
935 break;
937 printf(_("\"%s\" "), cp);
938 fflush(stdout);
939 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
940 goto jerr;
941 printf(_("%d/%d\n"), lc, cc);
942 break;
943 case 'i':
944 /* Insert a variable into the file */
945 cp = linebuf + 2;
946 while (whitechar(*cp))
947 ++cp;
948 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
949 break;
950 if (putesc(cp, _coll_fp) < 0)
951 goto jerr;
952 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
953 goto jerr;
954 break;
955 case 'a':
956 case 'A':
957 /* Insert the contents of a signature variable */
958 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
959 if (cp != NULL && *cp != '\0') {
960 if (putesc(cp, _coll_fp) < 0)
961 goto jerr;
962 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
963 goto jerr;
965 break;
966 case 'w':
967 /* Write the message on a file */
968 cp = linebuf + 2;
969 while (blankchar(*cp))
970 ++cp;
971 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
972 n_err(_("Write what file!?\n"));
973 break;
975 rewind(_coll_fp);
976 if (exwrite(cp, _coll_fp, 1) < 0)
977 goto jerr;
978 break;
979 case 'm':
980 case 'M':
981 case 'f':
982 case 'F':
983 case 'u':
984 case 'U':
985 /* Interpolate the named messages, if we are in receiving mail mode.
986 * Does the standard list processing garbage. If ~f is given, we
987 * don't shift over */
988 if (forward(linebuf + 2, _coll_fp, c) < 0)
989 goto jerr;
990 goto jcont;
991 case 'p':
992 /* Print current state of the message without altering anything */
993 print_collf(_coll_fp, hp);
994 goto jcont;
995 case '|':
996 /* Pipe message through command. Collect output as new message */
997 rewind(_coll_fp);
998 mespipe(linebuf + 2);
999 goto jcont;
1000 case 'v':
1001 case 'e':
1002 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1003 rewind(_coll_fp);
1004 mesedit(c, ok_blook(editheaders) ? hp : NULL);
1005 goto jcont;
1006 case '?':
1007 /* Last the lengthy help string. (Very ugly, but take care for
1008 * compiler supported string lengths :() */
1009 puts(_(
1010 "TILDE ESCAPES (to be placed after a newline) excerpt:\n"
1011 "~. Commit and send message\n"
1012 "~: <command> Execute a mail command\n"
1013 "~<! <command> Insert output of command\n"
1014 "~@ [<files>] Edit attachment list\n"
1015 "~A Insert *Sign* variable (`~a' inserts *sign*)\n"
1016 "~c <users> Add users to Cc: list (`~b' for Bcc:)\n"
1017 "~d Read in *DEAD* (dead.letter)\n"
1018 "~e Edit message via *EDITOR*"
1020 puts(_(
1021 "~F <msglist> Read in with headers, don't *indentprefix* lines\n"
1022 "~f <msglist> Like ~F, but honour `ignore' / `retain' configuration\n"
1023 "~H Edit From:, Reply-To: and Sender:\n"
1024 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"
1025 "~i <variable> Insert a value and a newline\n"
1026 "~M <msglist> Read in with headers, *indentprefix* (`~m': `retain' etc.)\n"
1027 "~p Print current message compose buffer\n"
1028 "~r <file> Read in a file (`~R' *indentprefix* lines)"
1030 puts(_(
1031 "~s <subject> Set Subject:\n"
1032 "~t <users> Add users to To: list\n"
1033 "~u <msglist> Read in message(s) without headers (`~U' indents lines)\n"
1034 "~v Edit message via *VISUAL*\n"
1035 "~w <file> Write message onto file\n"
1036 "~x Abort composition, discard message (`~q' saves in *DEAD*)\n"
1037 "~| <command> Pipe message through shell filter"
1039 break;
1043 jout:
1044 /* Place signature? */
1045 if((cp = ok_vlook(signature)) != NULL && *cp != '\0'){
1046 if((quote = file_expand(cp)) == NULL){
1047 n_err(_("*signature* expands to invalid file: \"%s\"\n"), cp);
1048 goto jerr;
1050 if((sigfp = Fopen(cp = quote, "r")) == NULL){
1051 n_err(_("Can't open *signature* \"%s\": %s\n"), cp, strerror(errno));
1052 goto jerr;
1055 if(linebuf == NULL)
1056 linebuf = smalloc(linesize = LINESIZE);
1057 c = '\0';
1058 while((i = fread(linebuf, sizeof *linebuf, linesize, UNVOLATILE(sigfp)))
1059 > 0){
1060 c = linebuf[i - 1];
1061 if(i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
1062 goto jerr;
1065 /* C99 */{
1066 FILE *x = UNVOLATILE(sigfp);
1067 int e = errno, ise = ferror(x);
1069 sigfp = NULL;
1070 Fclose(x);
1072 if(ise){
1073 n_err(_("Errors while reading *signature* \"%s\": %s\n"),
1074 cp, strerror(e));
1075 goto jerr;
1079 if(c != '\0' && c != '\n')
1080 putc('\n', _coll_fp);
1083 { char const *cp_obsolete = ok_vlook(NAIL_TAIL);
1085 if(cp_obsolete != NULL)
1086 OBSOLETE(_("please use *message-inject-tail* instead of *NAIL_TAIL*"));
1088 if((cp = ok_vlook(message_inject_tail)) != NULL ||
1089 (cp = cp_obsolete) != NULL){
1090 if(putesc(cp, _coll_fp) < 0)
1091 goto jerr;
1092 if((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1093 goto jerr;
1097 if(fflush(_coll_fp))
1098 goto jerr;
1099 rewind(_coll_fp);
1101 jleave:
1102 if (linebuf != NULL)
1103 free(linebuf);
1104 handlerpop();
1105 --noreset;
1106 sigemptyset(&nset);
1107 sigaddset(&nset, SIGINT);
1108 sigaddset(&nset, SIGHUP);
1109 sigprocmask(SIG_BLOCK, &nset, NULL);
1110 safe_signal(SIGINT, _coll_saveint);
1111 safe_signal(SIGHUP, _coll_savehup);
1112 safe_signal(SIGTSTP, _coll_savetstp);
1113 safe_signal(SIGTTOU, _coll_savettou);
1114 safe_signal(SIGTTIN, _coll_savettin);
1115 sigprocmask(SIG_SETMASK, &oset, NULL);
1116 NYD_LEAVE;
1117 return _coll_fp;
1119 jerr:
1120 if(sigfp != NULL)
1121 Fclose(UNVOLATILE(sigfp));
1122 if (_coll_fp != NULL) {
1123 Fclose(_coll_fp);
1124 _coll_fp = NULL;
1126 goto jleave;
1129 FL void
1130 savedeadletter(FILE *fp, int fflush_rewind_first)
1132 char const *cp;
1133 int c;
1134 FILE *dbuf;
1135 ul_i lines, bytes;
1136 NYD_ENTER;
1138 if ((options & OPT_DEBUG) || !ok_blook(save))
1139 goto jleave;
1141 if (fflush_rewind_first) {
1142 fflush(fp);
1143 rewind(fp);
1145 if (fsize(fp) == 0)
1146 goto jleave;
1148 cp = getdeadletter();
1149 c = umask(077);
1150 dbuf = Fopen(cp, "a");
1151 umask(c);
1152 if (dbuf == NULL)
1153 goto jleave;
1155 really_rewind(fp);
1157 printf("\"%s\" ", cp);
1158 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1159 putc(c, dbuf);
1160 if (c == '\n')
1161 ++lines;
1163 printf("%lu/%lu\n", lines, bytes);
1165 Fclose(dbuf);
1166 rewind(fp);
1167 jleave:
1168 NYD_LEAVE;
1171 /* s-it-mode */