enum okeys: new flags, additions and removals
[s-mailx.git] / collect.c
blobc1c457af6cab20e3b7e8133c5d8abd35fb1a4db7
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 `~:', `~_' and some hooks; hp may be NULL */
58 static void _execute_command(struct header *hp, char const *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 /* Call the hook macname */
106 static void a_coll_call_hook(struct header *hp, char const *macname);
108 static void
109 _execute_command(struct header *hp, char const *linebuf, size_t linesize){
110 /* The problem arises if there are rfc822 message attachments and the
111 * user uses `~:' to change the current file. TODO Unfortunately we
112 * TODO cannot simply keep a pointer to, or increment a reference count
113 * TODO of the current `file' (mailbox that is) object, because the
114 * TODO codebase doesn't deal with that at all; so, until some far
115 * TODO later time, copy the name of the path, and warn the user if it
116 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
117 * TODO copy the message attachments over to temporary files, but that
118 * TODO would require more changes so that the user still can recognize
119 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
120 struct n_sigman sm;
121 struct attachment *ap;
122 char *mnbuf;
123 NYD_ENTER;
125 UNUSED(linesize);
126 mnbuf = NULL;
128 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL){
129 case 0:
130 break;
131 default:
132 goto jleave;
135 /* If the above todo is worked, remove or outsource to attachments.c! */
136 if(hp != NULL && (ap = hp->h_attach) != NULL) do
137 if(ap->a_msgno){
138 mnbuf = sstrdup(mailname);
139 break;
141 while((ap = ap->a_flink) != NULL);
143 n_source_command(linebuf);
145 n_sigman_cleanup_ping(&sm);
146 jleave:
147 if(mnbuf != NULL){
148 if(strcmp(mnbuf, mailname))
149 n_err(_("Mailbox changed: it is likely that existing "
150 "rfc822 attachments became invalid!\n"));
151 free(mnbuf);
153 NYD_LEAVE;
154 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
157 static int
158 _include_file(char const *name, int *linecount, int *charcount,
159 bool_t doecho, bool_t indent)
161 FILE *fbuf;
162 char const *indb;
163 int ret = -1;
164 char *linebuf = NULL; /* TODO line pool */
165 size_t linesize = 0, indl, linelen, cnt;
166 NYD_ENTER;
168 if (name == (char*)-1)
169 fbuf = stdin;
170 else if ((fbuf = Fopen(name, "r")) == NULL) {
171 n_perr(name, 0);
172 goto jleave;
175 if (!indent)
176 indb = NULL, indl = 0;
177 else {
178 if ((indb = ok_vlook(indentprefix)) == NULL)
179 indb = INDENT_DEFAULT;
180 indl = strlen(indb);
183 *linecount = *charcount = 0;
184 cnt = fsize(fbuf);
185 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
186 if (indl > 0 && fwrite(indb, sizeof *indb, indl, _coll_fp) != indl)
187 goto jleave;
188 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
189 goto jleave;
190 ++(*linecount);
191 (*charcount) += linelen + indl;
192 if ((options & OPT_INTERACTIVE) && doecho) {
193 if (indl > 0)
194 fwrite(indb, sizeof *indb, indl, stdout);
195 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
198 if (fflush(_coll_fp))
199 goto jleave;
200 if ((options & OPT_INTERACTIVE) && doecho)
201 fflush(stdout);
203 ret = 0;
204 jleave:
205 if (linebuf != NULL)
206 free(linebuf);
207 if (fbuf != NULL && fbuf != stdin)
208 Fclose(fbuf);
209 NYD_LEAVE;
210 return ret;
213 static void
214 _collect_onpipe(int signo)
216 NYD_X; /* Signal handler */
217 UNUSED(signo);
218 siglongjmp(_coll_pipejmp, 1);
221 static void
222 insertcommand(FILE *fp, char const *cmd)
224 FILE *ibuf = NULL;
225 char const *cp;
226 int c;
227 NYD_ENTER;
229 cp = ok_vlook(SHELL);
230 if (cp == NULL)
231 cp = XSHELL;
233 if ((ibuf = Popen(cmd, "r", cp, NULL, 0)) != NULL) {
234 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
235 putc(c, fp);
236 Pclose(ibuf, TRU1);
237 } else
238 n_perr(cmd, 0);
239 NYD_LEAVE;
242 static void
243 print_collf(FILE *cf, struct header *hp)
245 char *lbuf = NULL; /* TODO line pool */
246 sighandler_type sigint;
247 FILE * volatile obuf = stdout;
248 struct attachment *ap;
249 char const *cp;
250 enum gfield gf;
251 size_t linesize = 0, linelen, cnt, cnt2;
252 NYD_ENTER;
254 fflush_rewind(cf);
255 cnt = cnt2 = fsize(cf);
257 sigint = safe_signal(SIGINT, SIG_IGN);
259 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
260 size_t l, m;
262 m = 4;
263 if (hp->h_to != NULL)
264 ++m;
265 if (hp->h_subject != NULL)
266 ++m;
267 if (hp->h_cc != NULL)
268 ++m;
269 if (hp->h_bcc != NULL)
270 ++m;
271 if (hp->h_attach != NULL)
272 ++m;
273 m += (hp->h_from != NULL || myaddrs(hp) != NULL);
274 m += (hp->h_sender != NULL || ok_vlook(sender) != NULL);
275 m += (hp->h_replyto != NULL || ok_vlook(replyto) != NULL);
277 l = (*cp == '\0') ? screensize() : atoi(cp);
278 if (m > l)
279 goto jpager;
280 l -= m;
282 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
284 rewind(cf);
285 if (l < m) {
286 jpager:
287 if (sigsetjmp(_coll_pipejmp, 1))
288 goto jendpipe;
289 if ((obuf = n_pager_open()) == NULL)
290 obuf = stdout;
291 else
292 safe_signal(SIGPIPE, &_collect_onpipe);
296 fprintf(obuf, _("-------\nMessage contains:\n"));
297 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
298 puthead(TRU1, hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
299 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
300 prout(lbuf, linelen, obuf);
301 if (hp->h_attach != NULL) {
302 fputs(_("-------\nAttachments:\n"), obuf);
303 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
304 if (ap->a_msgno)
305 fprintf(obuf, " - message %u\n", ap->a_msgno);
306 else {
307 /* TODO after MIME/send layer rewrite we *know*
308 * TODO the details of the attachment here,
309 * TODO so adjust this again, then */
310 char const *cs, *csi = "-> ";
312 if ((cs = ap->a_charset) == NULL &&
313 (csi = "<- ", cs = ap->a_input_charset) == NULL)
314 cs = charset_get_lc();
315 if ((cp = ap->a_content_type) == NULL)
316 cp = "?";
317 else if (ascncasecmp(cp, "text/", 5))
318 csi = "";
319 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
324 jendpipe:
325 if (obuf != stdout)
326 n_pager_close(obuf);
327 if (lbuf != NULL)
328 free(lbuf);
329 safe_signal(SIGINT, sigint);
330 NYD_LEAVE;
333 static int
334 exwrite(char const *name, FILE *fp, int f)
336 FILE *of;
337 int c, lc, rv = -1;
338 long cc;
339 NYD_ENTER;
341 if (f) {
342 printf("\"%s\" ", name);
343 fflush(stdout);
345 if ((of = Fopen(name, "a")) == NULL) {
346 n_perr(NULL, 0);
347 goto jleave;
350 lc = 0;
351 cc = 0;
352 while ((c = getc(fp)) != EOF) {
353 ++cc;
354 if (c == '\n')
355 ++lc;
356 putc(c, of);
357 if (ferror(of)) {
358 n_perr(name, 0);
359 Fclose(of);
360 goto jleave;
363 Fclose(of);
364 printf(_("%d/%ld\n"), lc, cc);
365 fflush(stdout);
366 rv = 0;
367 jleave:
368 NYD_LEAVE;
369 return rv;
372 static enum okay
373 makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err)
375 FILE *nf;
376 int c;
377 enum okay rv = STOP;
378 NYD_ENTER;
380 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
381 n_perr(_("temporary mail edit file"), 0);
382 goto jleave;
385 extract_header(fp, hp, checkaddr_err);
387 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
388 putc(c, nf);
389 if (fp != _coll_fp)
390 Fclose(_coll_fp);
391 Fclose(fp);
392 _coll_fp = nf;
393 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
394 goto jleave;
395 rv = OKAY;
396 jleave:
397 NYD_LEAVE;
398 return rv;
401 static void
402 mesedit(int c, struct header *hp)
404 bool_t saved;
405 sighandler_type sigint;
406 FILE *nf;
407 NYD_ENTER;
409 saved = ok_blook(add_file_recipients);
410 ok_bset(add_file_recipients, TRU1);
412 sigint = safe_signal(SIGINT, SIG_IGN);
413 nf = run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint);
414 if (nf != NULL) {
415 if (hp) {
416 rewind(nf);
417 makeheader(nf, hp, NULL);
418 } else {
419 fseek(nf, 0L, SEEK_END);
420 Fclose(_coll_fp);
421 _coll_fp = nf;
424 safe_signal(SIGINT, sigint);
426 ok_bset(add_file_recipients, saved);
427 NYD_LEAVE;
430 static void
431 mespipe(char *cmd)
433 FILE *nf;
434 sighandler_type sigint;
435 char const *sh;
436 NYD_ENTER;
438 sigint = safe_signal(SIGINT, SIG_IGN);
440 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
441 n_perr(_("temporary mail edit file"), 0);
442 goto jout;
445 /* stdin = current message. stdout = new message */
446 if ((sh = ok_vlook(SHELL)) == NULL)
447 sh = XSHELL;
448 fflush(_coll_fp);
449 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL, NULL)
450 < 0) {
451 Fclose(nf);
452 goto jout;
455 if (fsize(nf) == 0) {
456 n_err(_("No bytes from \"%s\" !?\n"), cmd);
457 Fclose(nf);
458 goto jout;
461 /* Take new files */
462 fseek(nf, 0L, SEEK_END);
463 Fclose(_coll_fp);
464 _coll_fp = nf;
465 jout:
466 safe_signal(SIGINT, sigint);
467 NYD_LEAVE;
470 static int
471 forward(char *ms, FILE *fp, int f)
473 int *msgvec, rv = 0;
474 struct ignoretab *ig;
475 char const *tabst;
476 enum sendaction action;
477 NYD_ENTER;
479 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
480 if (getmsglist(ms, msgvec, 0) < 0)
481 goto jleave;
482 if (*msgvec == 0) {
483 *msgvec = first(0, MMNORM);
484 if (*msgvec == 0) {
485 n_err(_("No appropriate messages\n"));
486 goto jleave;
488 msgvec[1] = 0;
491 if (f == 'f' || f == 'F' || f == 'u')
492 tabst = NULL;
493 else if ((tabst = ok_vlook(indentprefix)) == NULL)
494 tabst = INDENT_DEFAULT;
495 if (f == 'u' || f == 'U')
496 ig = allignore;
497 else
498 ig = upperchar(f) ? NULL : ignore;
499 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
501 printf(_("Interpolating:"));
502 for (; *msgvec != 0; ++msgvec) {
503 struct message *mp = message + *msgvec - 1;
505 touch(mp);
506 printf(" %d", *msgvec);
507 fflush(stdout);
508 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
509 n_perr(_("temporary mail file"), 0);
510 rv = -1;
511 break;
514 printf("\n");
515 jleave:
516 NYD_LEAVE;
517 return rv;
520 static void
521 collstop(int s)
523 sighandler_type old_action;
524 sigset_t nset;
525 NYD_X; /* Signal handler */
527 old_action = safe_signal(s, SIG_DFL);
529 sigemptyset(&nset);
530 sigaddset(&nset, s);
531 sigprocmask(SIG_UNBLOCK, &nset, NULL);
532 n_raise(s);
533 sigprocmask(SIG_BLOCK, &nset, NULL);
535 safe_signal(s, old_action);
536 if (_coll_jmp_p) {
537 _coll_jmp_p = 0;
538 _coll_hadintr = 0;
539 siglongjmp(_coll_jmp, 1);
543 static void
544 _collint(int s)
546 NYD_X; /* Signal handler */
548 /* the control flow is subtle, because we can be called from ~q */
549 if (_coll_hadintr == 0) {
550 if (ok_blook(ignore)) {
551 puts("@");
552 fflush(stdout);
553 clearerr(stdin);
554 } else
555 _coll_hadintr = 1;
556 siglongjmp(_coll_jmp, 1);
558 exit_status |= EXIT_SEND_ERROR;
559 if (s != 0)
560 savedeadletter(_coll_fp, 1);
561 /* Aborting message, no need to fflush() .. */
562 siglongjmp(_coll_abort, 1);
565 static void
566 collhup(int s)
568 NYD_X; /* Signal handler */
569 UNUSED(s);
571 savedeadletter(_coll_fp, 1);
572 /* Let's pretend nobody else wants to clean up, a true statement at
573 * this time */
574 exit(EXIT_ERR);
577 static int
578 putesc(char const *s, FILE *stream)
580 int n = 0, rv = -1;
581 NYD_ENTER;
583 while (s[0] != '\0') {
584 if (s[0] == '\\') {
585 if (s[1] == 't') {
586 if (putc('\t', stream) == EOF)
587 goto jleave;
588 ++n;
589 s += 2;
590 continue;
592 if (s[1] == 'n') {
593 if (putc('\n', stream) == EOF)
594 goto jleave;
595 ++n;
596 s += 2;
597 continue;
600 if (putc(s[0], stream) == EOF)
601 goto jleave;
602 ++n;
603 ++s;
605 if (putc('\n', stream) == EOF)
606 goto jleave;
607 rv = ++n;
608 jleave:
609 NYD_LEAVE;
610 return rv;
613 static void
614 a_coll_call_hook(struct header *hp, char const *macname){ /* TODO v15: drop */
615 char const *val;
616 NYD2_ENTER;
618 if((val = detract(hp->h_from, GNAMEONLY)) == NULL)
619 val = "";
620 ok_vset(compose_from, val);
621 if((val = detract(hp->h_sender, 0)) == NULL)
622 val = "";
623 ok_vset(compose_sender, val);
624 if((val = detract(hp->h_to, GNAMEONLY)) == NULL)
625 val = "";
626 ok_vset(compose_to, val);
627 if((val = detract(hp->h_cc, GNAMEONLY)) == NULL)
628 val = "";
629 ok_vset(compose_cc, val);
630 if((val = detract(hp->h_bcc, GNAMEONLY)) == NULL)
631 val = "";
632 ok_vset(compose_bcc, val);
633 if((val = hp->h_subject) == NULL)
634 val = "";
635 ok_vset(compose_subject, val);
637 call_compose_mode_hook(macname);
639 ok_vclear(compose_subject);
640 ok_vclear(compose_bcc);
641 ok_vclear(compose_cc);
642 ok_vclear(compose_to);
643 ok_vclear(compose_sender);
644 ok_vclear(compose_from);
645 NYD2_LEAVE;
648 FL FILE *
649 collect(struct header *hp, int printheaders, struct message *mp,
650 char *quotefile, int doprefix, si8_t *checkaddr_err)
652 struct ignoretab *quoteig;
653 int lc, cc, c, t;
654 int volatile escape, getfields;
655 char *linebuf, *quote;
656 char const *cp;
657 size_t i, linesize; /* TODO line pool */
658 long cnt;
659 enum sendaction action;
660 sigset_t oset, nset;
661 FILE * volatile sigfp;
662 NYD_ENTER;
664 _coll_fp = NULL;
665 sigfp = NULL;
666 linesize = 0;
667 linebuf = quote = NULL;
669 /* Start catching signals from here, but we're still die on interrupts
670 * until we're in the main loop */
671 sigfillset(&nset);
672 sigprocmask(SIG_BLOCK, &nset, &oset);
673 /* FIXME have dropped handlerpush() and localized onintr() in lex.c! */
674 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
675 safe_signal(SIGINT, &_collint);
676 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
677 safe_signal(SIGHUP, collhup);
678 /* TODO We do a lot of redundant signal handling, especially
679 * TODO with the command line editor(s); try to merge this */
680 _coll_savetstp = safe_signal(SIGTSTP, collstop);
681 _coll_savettou = safe_signal(SIGTTOU, collstop);
682 _coll_savettin = safe_signal(SIGTTIN, collstop);
683 if (sigsetjmp(_coll_abort, 1))
684 goto jerr;
685 if (sigsetjmp(_coll_jmp, 1))
686 goto jerr;
687 pstate |= PS_RECURSED;
688 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
690 ++noreset;
691 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
692 NULL) {
693 n_perr(_("temporary mail file"), 0);
694 goto jerr;
697 /* If we are going to prompt for a subject, refrain from printing a newline
698 * after the headers (since some people mind) */
699 getfields = 0;
700 if (!(options & OPT_t_FLAG)) {
701 t = GTO | GSUBJECT | GCC | GNL;
702 if (ok_blook(fullnames))
703 t |= GCOMMA;
705 if (options & OPT_INTERACTIVE) {
706 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
707 t &= ~GNL, getfields |= GSUBJECT;
709 if (hp->h_to == NULL)
710 t &= ~GNL, getfields |= GTO;
712 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
713 if (hp->h_bcc == NULL && ok_blook(askbcc))
714 t &= ~GNL, getfields |= GBCC;
715 if (hp->h_cc == NULL && ok_blook(askcc))
716 t &= ~GNL, getfields |= GCC;
721 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
722 _coll_hadintr = 0;
724 if (!sigsetjmp(_coll_jmp, 1)) {
725 /* Ask for some headers first, as necessary */
726 if (getfields)
727 grab_headers(hp, getfields, 1);
729 /* Execute compose-post-hook TODO completely v15-compat intermediate!! */
730 if((cp = ok_vlook(on_compose_enter)) != NULL){
731 setup_from_and_sender(hp);
732 a_coll_call_hook(hp, cp);
735 /* C99 */{
736 char const *cp_obsolete = ok_vlook(NAIL_HEAD);
737 if(cp_obsolete != NULL)
738 OBSOLETE(_("please use *message-inject-head* "
739 "instead of *NAIL_HEAD*"));
741 if(((cp = ok_vlook(message_inject_head)) != NULL ||
742 (cp = cp_obsolete) != NULL) && putesc(cp, _coll_fp) < 0)
743 goto jerr;
746 /* Quote an original message */
747 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
748 quoteig = allignore;
749 action = SEND_QUOTE;
750 if (doprefix) {
751 quoteig = fwdignore;
752 if ((cp = ok_vlook(fwdheading)) == NULL)
753 cp = "-------- Original Message --------";
754 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
755 goto jerr;
756 } else if (!strcmp(quote, "noheading")) {
757 /*EMPTY*/;
758 } else if (!strcmp(quote, "headers")) {
759 quoteig = ignore;
760 } else if (!strcmp(quote, "allheaders")) {
761 quoteig = NULL;
762 action = SEND_QUOTE_ALL;
763 } else {
764 cp = hfield1("from", mp);
765 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
766 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
767 goto jerr;
768 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
769 goto jerr;
772 if (fflush(_coll_fp))
773 goto jerr;
774 if (doprefix)
775 cp = NULL;
776 else if ((cp = ok_vlook(indentprefix)) == NULL)
777 cp = INDENT_DEFAULT;
778 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
779 goto jerr;
782 if (quotefile != NULL) {
783 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
784 goto jerr;
787 if (options & OPT_INTERACTIVE) {
788 /* Print what we have sofar also on the terminal (if useful) */
789 if (!ok_blook(editalong)) {
790 if (printheaders)
791 puthead(TRU1, hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
793 rewind(_coll_fp);
794 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
795 putc(c, stdout);
796 if (fseek(_coll_fp, 0, SEEK_END))
797 goto jerr;
799 /* Ensure this is clean xxx not really necessary? */
800 fflush(stdout);
801 } else {
802 rewind(_coll_fp);
803 mesedit('e', hp);
804 goto jcont;
807 } else {
808 /* Come here for printing the after-signal message. Duplicate messages
809 * won't be printed because the write is aborted if we get a SIGTTOU */
810 if (_coll_hadintr) {
811 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
812 } else {
813 jcont:
814 printf(_("(continue)\n"));
815 fflush(stdout);
819 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
820 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
821 linebuf = srealloc(linebuf, linesize = LINESIZE);
822 while ((i = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
823 if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
824 goto jerr;
826 goto jout;
829 /* The interactive collect loop.
830 * All commands which come here are forbidden when sourcing! */
831 assert(_coll_hadintr || !(pstate & PS_SOURCING));
832 for (;;) {
833 _coll_jmp_p = 1;
834 cnt = n_lex_input("", FAL0, &linebuf, &linesize, NULL);
835 _coll_jmp_p = 0;
837 if (cnt < 0) {
838 assert(!(pstate & PS_SOURCING));
839 if (options & OPT_t_FLAG) {
840 fflush_rewind(_coll_fp);
841 /* It is important to set PS_t_FLAG before extract_header() *and*
842 * keep OPT_t_FLAG for the first parse of the message, too! */
843 pstate |= PS_t_FLAG;
844 if (makeheader(_coll_fp, hp, checkaddr_err) != OKAY)
845 goto jerr;
846 options &= ~OPT_t_FLAG;
847 continue;
848 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
849 printf(_("*ignoreeof* set, use \"~.\" to terminate letter\n"));
850 continue;
852 break;
855 _coll_hadintr = 0;
857 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
858 jputline:
859 /* TODO calls putline(), which *always* appends LF;
860 * TODO thus, STDIN with -t will ALWAYS end with LF,
861 * TODO even if no trailing LF and QP encoding.
862 * TODO when finally changed, update cc-test.sh */
863 if (putline(_coll_fp, linebuf, cnt) < 0)
864 goto jerr;
865 continue;
866 } else if (linebuf[0] == '.') {
867 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
868 break;
870 if (linebuf[0] != escape)
871 goto jputline;
873 if (!(options & OPT_t_FLAG))
874 n_tty_addhist(linebuf, TRU1);
876 c = linebuf[1];
877 switch (c) {
878 default:
879 /* On double escape, send a single one. Otherwise, it's an error */
880 if (c == escape) {
881 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
882 goto jerr;
883 else
884 break;
886 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
887 break;
888 case '!':
889 /* Shell escape, send the balance of line to sh -c */
890 c_shell(linebuf + 2);
891 break;
892 case ':':
893 /* FALLTHRU */
894 case '_':
895 /* Escape to command mode, but be nice! */
896 _execute_command(hp, linebuf + 2, cnt - 2);
897 goto jcont;
898 case '.':
899 /* Simulate end of file on input */
900 goto jout;
901 case 'x':
902 /* Same as 'q', but no *DEAD* saving */
903 /* FALLTHRU */
904 case 'q':
905 /* Force a quit, act like an interrupt had happened */
906 ++_coll_hadintr;
907 _collint((c == 'x') ? 0 : SIGINT);
908 exit(EXIT_ERR);
909 /*NOTREACHED*/
910 case 'h':
911 /* Grab a bunch of headers */
913 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
914 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
915 while (hp->h_to == NULL);
916 goto jcont;
917 case 'H':
918 /* Grab extra headers */
920 grab_headers(hp, GEXTRA, 0);
921 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
922 goto jcont;
923 case 't':
924 /* Add to the To list */
925 hp->h_to = cat(hp->h_to,
926 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
927 NULL));
928 break;
929 case 's':
930 /* Set the Subject list */
931 cp = linebuf + 2;
932 while (whitechar(*cp))
933 ++cp;
934 hp->h_subject = savestr(cp);
935 break;
936 #ifdef HAVE_MEMORY_DEBUG
937 case 'S':
938 c_sstats(NULL);
939 break;
940 #endif
941 case '@':
942 /* Edit the attachment list */
943 if (linebuf[2] != '\0')
944 append_attachments(&hp->h_attach, linebuf + 2);
945 else
946 edit_attachments(&hp->h_attach);
947 break;
948 case 'c':
949 /* Add to the CC list */
950 hp->h_cc = cat(hp->h_cc,
951 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
952 NULL));
953 break;
954 case 'b':
955 /* Add stuff to blind carbon copies list */
956 hp->h_bcc = cat(hp->h_bcc,
957 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
958 NULL));
959 break;
960 case 'd':
961 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
962 linebuf[linesize - 1] = '\0';
963 /*FALLTHRU*/
964 case 'R':
965 case 'r':
966 case '<':
967 /* Invoke a file: Search for the file name, then open it and copy the
968 * contents to _coll_fp */
969 cp = linebuf + 2;
970 while (whitechar(*cp))
971 ++cp;
972 if (*cp == '\0') {
973 n_err(_("Interpolate what file?\n"));
974 break;
976 if (*cp == '!') {
977 insertcommand(_coll_fp, cp + 1);
978 break;
980 if ((cp = file_expand(cp)) == NULL)
981 break;
982 if (is_dir(cp)) {
983 n_err(_("\"%s\": Directory\n"), cp);
984 break;
986 printf(_("\"%s\" "), cp);
987 fflush(stdout);
988 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
989 goto jerr;
990 printf(_("%d/%d\n"), lc, cc);
991 break;
992 case 'i':
993 /* Insert a variable into the file */
994 cp = linebuf + 2;
995 while (whitechar(*cp))
996 ++cp;
997 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
998 break;
999 if (putesc(cp, _coll_fp) < 0)
1000 goto jerr;
1001 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1002 goto jerr;
1003 break;
1004 case 'a':
1005 case 'A':
1006 /* Insert the contents of a signature variable */
1007 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
1008 if (cp != NULL && *cp != '\0') {
1009 if (putesc(cp, _coll_fp) < 0)
1010 goto jerr;
1011 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1012 goto jerr;
1014 break;
1015 case 'w':
1016 /* Write the message on a file */
1017 cp = linebuf + 2;
1018 while (blankchar(*cp))
1019 ++cp;
1020 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
1021 n_err(_("Write what file!?\n"));
1022 break;
1024 rewind(_coll_fp);
1025 if (exwrite(cp, _coll_fp, 1) < 0)
1026 goto jerr;
1027 break;
1028 case 'm':
1029 case 'M':
1030 case 'f':
1031 case 'F':
1032 case 'u':
1033 case 'U':
1034 /* Interpolate the named messages, if we are in receiving mail mode.
1035 * Does the standard list processing garbage. If ~f is given, we
1036 * don't shift over */
1037 if (forward(linebuf + 2, _coll_fp, c) < 0)
1038 goto jerr;
1039 goto jcont;
1040 case 'p':
1041 /* Print current state of the message without altering anything */
1042 print_collf(_coll_fp, hp);
1043 goto jcont;
1044 case '|':
1045 /* Pipe message through command. Collect output as new message */
1046 rewind(_coll_fp);
1047 mespipe(linebuf + 2);
1048 goto jcont;
1049 case 'v':
1050 case 'e':
1051 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1052 rewind(_coll_fp);
1053 mesedit(c, ok_blook(editheaders) ? hp : NULL);
1054 goto jcont;
1055 case '?':
1056 /* Last the lengthy help string. (Very ugly, but take care for
1057 * compiler supported string lengths :() */
1058 puts(_(
1059 "TILDE ESCAPES (to be placed after a newline) excerpt:\n"
1060 "~. Commit and send message\n"
1061 "~: <command> Execute a mail command\n"
1062 "~<! <command> Insert output of command\n"
1063 "~@ [<files>] Edit attachment list\n"
1064 "~A Insert *Sign* variable (`~a' inserts *sign*)\n"
1065 "~c <users> Add users to Cc: list (`~b' for Bcc:)\n"
1066 "~d Read in *DEAD* (dead.letter)\n"
1067 "~e Edit message via *EDITOR*"
1069 puts(_(
1070 "~F <msglist> Read in with headers, don't *indentprefix* lines\n"
1071 "~f <msglist> Like ~F, but honour `ignore' / `retain' configuration\n"
1072 "~H Edit From:, Reply-To: and Sender:\n"
1073 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"
1074 "~i <variable> Insert a value and a newline\n"
1075 "~M <msglist> Read in with headers, *indentprefix* (`~m': `retain' etc.)\n"
1076 "~p Print current message compose buffer\n"
1077 "~r <file> Read in a file (`~R' *indentprefix* lines)"
1079 puts(_(
1080 "~s <subject> Set Subject:\n"
1081 "~t <users> Add users to To: list\n"
1082 "~u <msglist> Read in message(s) without headers (`~U' indents lines)\n"
1083 "~v Edit message via *VISUAL*\n"
1084 "~w <file> Write message onto file\n"
1085 "~x Abort composition, discard message (`~q' saves in *DEAD*)\n"
1086 "~| <command> Pipe message through shell filter"
1088 break;
1092 jout:
1093 /* Execute compose-post-hook TODO completely v15-compat intermediate!! */
1094 if((cp = ok_vlook(on_compose_leave)) != NULL){
1095 setup_from_and_sender(hp);
1096 a_coll_call_hook(hp, cp);
1099 /* Final change to edit headers, if not already above */
1100 if (ok_blook(bsdcompat) || ok_blook(askatend)) {
1101 if (hp->h_cc == NULL && ok_blook(askcc))
1102 grab_headers(hp, GCC, 1);
1103 if (hp->h_bcc == NULL && ok_blook(askbcc))
1104 grab_headers(hp, GBCC, 1);
1106 if (hp->h_attach == NULL && ok_blook(askattach))
1107 edit_attachments(&hp->h_attach);
1109 /* Add automatic receivers */
1110 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1111 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1112 EACM_NORMAL, checkaddr_err));
1113 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1114 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1115 EACM_NORMAL, checkaddr_err));
1116 if (*checkaddr_err != 0)
1117 goto jerr;
1119 /* Place signature? */
1120 if((cp = ok_vlook(signature)) != NULL && *cp != '\0'){
1121 if((quote = file_expand(cp)) == NULL){
1122 n_err(_("*signature* expands to invalid file: \"%s\"\n"), cp);
1123 goto jerr;
1125 if((sigfp = Fopen(cp = quote, "r")) == NULL){
1126 n_err(_("Can't open *signature* \"%s\": %s\n"), cp, strerror(errno));
1127 goto jerr;
1130 if(linebuf == NULL)
1131 linebuf = smalloc(linesize = LINESIZE);
1132 c = '\0';
1133 while((i = fread(linebuf, sizeof *linebuf, linesize, UNVOLATILE(sigfp)))
1134 > 0){
1135 c = linebuf[i - 1];
1136 if(i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
1137 goto jerr;
1140 /* C99 */{
1141 FILE *x = UNVOLATILE(sigfp);
1142 int e = errno, ise = ferror(x);
1144 sigfp = NULL;
1145 Fclose(x);
1147 if(ise){
1148 n_err(_("Errors while reading *signature* \"%s\": %s\n"),
1149 cp, strerror(e));
1150 goto jerr;
1154 if(c != '\0' && c != '\n')
1155 putc('\n', _coll_fp);
1158 { char const *cp_obsolete = ok_vlook(NAIL_TAIL);
1160 if(cp_obsolete != NULL)
1161 OBSOLETE(_("please use *message-inject-tail* instead of *NAIL_TAIL*"));
1163 if((cp = ok_vlook(message_inject_tail)) != NULL ||
1164 (cp = cp_obsolete) != NULL){
1165 if(putesc(cp, _coll_fp) < 0)
1166 goto jerr;
1167 if((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1168 goto jerr;
1172 if(fflush(_coll_fp))
1173 goto jerr;
1174 rewind(_coll_fp);
1176 jleave:
1177 if (linebuf != NULL)
1178 free(linebuf);
1179 --noreset;
1180 sigfillset(&nset);
1181 sigprocmask(SIG_BLOCK, &nset, NULL);
1182 pstate &= ~PS_RECURSED;
1183 safe_signal(SIGINT, _coll_saveint);
1184 safe_signal(SIGHUP, _coll_savehup);
1185 safe_signal(SIGTSTP, _coll_savetstp);
1186 safe_signal(SIGTTOU, _coll_savettou);
1187 safe_signal(SIGTTIN, _coll_savettin);
1188 sigprocmask(SIG_SETMASK, &oset, NULL);
1189 NYD_LEAVE;
1190 return _coll_fp;
1192 jerr:
1193 if(sigfp != NULL)
1194 Fclose(UNVOLATILE(sigfp));
1195 if (_coll_fp != NULL) {
1196 Fclose(_coll_fp);
1197 _coll_fp = NULL;
1199 goto jleave;
1202 FL void
1203 savedeadletter(FILE *fp, int fflush_rewind_first)
1205 char const *cp;
1206 int c;
1207 FILE *dbuf;
1208 ul_i lines, bytes;
1209 NYD_ENTER;
1211 if ((options & OPT_DEBUG) || !ok_blook(save))
1212 goto jleave;
1214 if (fflush_rewind_first) {
1215 fflush(fp);
1216 rewind(fp);
1218 if (fsize(fp) == 0)
1219 goto jleave;
1221 cp = getdeadletter();
1222 c = umask(077);
1223 dbuf = Fopen(cp, "a");
1224 umask(c);
1225 if (dbuf == NULL)
1226 goto jleave;
1228 really_rewind(fp);
1230 printf("\"%s\" ", cp);
1231 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1232 putc(c, dbuf);
1233 if (c == '\n')
1234 ++lines;
1236 printf("%lu/%lu\n", lines, bytes);
1238 Fclose(dbuf);
1239 rewind(fp);
1240 jleave:
1241 NYD_LEAVE;
1244 /* s-it-mode */