mk-conf.sh: test for fpathconf(2) when going pathconf(2)
[s-mailx.git] / collect.c
blob8e6ffc40222ff50cb2e2b9add7b52037cd108b59
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 char *mnbuf = NULL;
121 size_t mnlen = 0 /* silence CC */;
122 struct attachment *ap;
123 NYD_ENTER;
125 /* If the above todo is worked, remove or outsource to attachments.c! */
126 if(hp != NULL && (ap = hp->h_attach) != NULL) do
127 if(ap->a_msgno){
128 mnbuf = sstrdup(mailname);
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 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 (name == (char*)-1)
157 fbuf = stdin;
158 else if ((fbuf = Fopen(name, "r")) == NULL) {
159 n_perr(name, 0);
160 goto jleave;
163 if (!indent)
164 indb = NULL, indl = 0;
165 else {
166 if ((indb = ok_vlook(indentprefix)) == NULL)
167 indb = INDENT_DEFAULT;
168 indl = strlen(indb);
171 *linecount = *charcount = 0;
172 cnt = fsize(fbuf);
173 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
174 if (indl > 0 && fwrite(indb, sizeof *indb, indl, _coll_fp) != indl)
175 goto jleave;
176 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
177 goto jleave;
178 ++(*linecount);
179 (*charcount) += linelen + indl;
180 if ((options & OPT_INTERACTIVE) && doecho) {
181 if (indl > 0)
182 fwrite(indb, sizeof *indb, indl, stdout);
183 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
186 if (fflush(_coll_fp))
187 goto jleave;
188 if ((options & OPT_INTERACTIVE) && doecho)
189 fflush(stdout);
191 ret = 0;
192 jleave:
193 if (linebuf != NULL)
194 free(linebuf);
195 if (fbuf != NULL && fbuf != stdin)
196 Fclose(fbuf);
197 NYD_LEAVE;
198 return ret;
201 static void
202 _collect_onpipe(int signo)
204 NYD_X; /* Signal handler */
205 UNUSED(signo);
206 siglongjmp(_coll_pipejmp, 1);
209 static void
210 insertcommand(FILE *fp, char const *cmd)
212 FILE *ibuf = NULL;
213 char const *cp;
214 int c;
215 NYD_ENTER;
217 cp = ok_vlook(SHELL);
218 if (cp == NULL)
219 cp = XSHELL;
221 if ((ibuf = Popen(cmd, "r", cp, NULL, 0)) != NULL) {
222 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
223 putc(c, fp);
224 Pclose(ibuf, TRU1);
225 } else
226 n_perr(cmd, 0);
227 NYD_LEAVE;
230 static void
231 print_collf(FILE *cf, struct header *hp)
233 char *lbuf = NULL; /* TODO line pool */
234 sighandler_type sigint;
235 FILE * volatile obuf = stdout;
236 struct attachment *ap;
237 char const *cp;
238 enum gfield gf;
239 size_t linesize = 0, linelen, cnt, cnt2;
240 NYD_ENTER;
242 fflush_rewind(cf);
243 cnt = cnt2 = fsize(cf);
245 sigint = safe_signal(SIGINT, SIG_IGN);
247 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
248 size_t l, m;
250 m = 4;
251 if (hp->h_to != NULL)
252 ++m;
253 if (hp->h_subject != NULL)
254 ++m;
255 if (hp->h_cc != NULL)
256 ++m;
257 if (hp->h_bcc != NULL)
258 ++m;
259 if (hp->h_attach != NULL)
260 ++m;
261 m += (hp->h_from != NULL || myaddrs(hp) != NULL);
262 m += (hp->h_sender != NULL || ok_vlook(sender) != NULL);
263 m += (hp->h_replyto != NULL || ok_vlook(replyto) != NULL);
265 l = (*cp == '\0') ? screensize() : atoi(cp);
266 if (m > l)
267 goto jpager;
268 l -= m;
270 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
272 rewind(cf);
273 if (l < m) {
274 jpager:
275 cp = get_pager(NULL);
276 if (sigsetjmp(_coll_pipejmp, 1))
277 goto jendpipe;
278 obuf = Popen(cp, "w", NULL, NULL, 1);
279 if (obuf == NULL) {
280 n_perr(cp, 0);
281 obuf = stdout;
282 } else
283 safe_signal(SIGPIPE, &_collect_onpipe);
287 fprintf(obuf, _("-------\nMessage contains:\n"));
288 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
289 puthead(TRU1, hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
290 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
291 prout(lbuf, linelen, obuf);
292 if (hp->h_attach != NULL) {
293 fputs(_("-------\nAttachments:\n"), obuf);
294 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
295 if (ap->a_msgno)
296 fprintf(obuf, " - message %u\n", ap->a_msgno);
297 else {
298 /* TODO after MIME/send layer rewrite we *know*
299 * TODO the details of the attachment here,
300 * TODO so adjust this again, then */
301 char const *cs, *csi = "-> ";
303 if ((cs = ap->a_charset) == NULL &&
304 (csi = "<- ", cs = ap->a_input_charset) == NULL)
305 cs = charset_get_lc();
306 if ((cp = ap->a_content_type) == NULL)
307 cp = "?";
308 else if (ascncasecmp(cp, "text/", 5))
309 csi = "";
310 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
315 jendpipe:
316 if (obuf != stdout) {
317 safe_signal(SIGPIPE, SIG_IGN);
318 Pclose(obuf, TRU1);
319 safe_signal(SIGPIPE, dflpipe);
321 if (lbuf != NULL)
322 free(lbuf);
323 safe_signal(SIGINT, sigint);
324 NYD_LEAVE;
327 static int
328 exwrite(char const *name, FILE *fp, int f)
330 FILE *of;
331 int c, lc, rv = -1;
332 long cc;
333 NYD_ENTER;
335 if (f) {
336 printf("\"%s\" ", name);
337 fflush(stdout);
339 if ((of = Fopen(name, "a")) == NULL) {
340 n_perr(NULL, 0);
341 goto jleave;
344 lc = 0;
345 cc = 0;
346 while ((c = getc(fp)) != EOF) {
347 ++cc;
348 if (c == '\n')
349 ++lc;
350 putc(c, of);
351 if (ferror(of)) {
352 n_perr(name, 0);
353 Fclose(of);
354 goto jleave;
357 Fclose(of);
358 printf(_("%d/%ld\n"), lc, cc);
359 fflush(stdout);
360 rv = 0;
361 jleave:
362 NYD_LEAVE;
363 return rv;
366 static enum okay
367 makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err)
369 FILE *nf;
370 int c;
371 enum okay rv = STOP;
372 NYD_ENTER;
374 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
375 n_perr(_("temporary mail edit file"), 0);
376 goto jleave;
379 extract_header(fp, hp, checkaddr_err);
381 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
382 putc(c, nf);
383 if (fp != _coll_fp)
384 Fclose(_coll_fp);
385 Fclose(fp);
386 _coll_fp = nf;
387 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
388 goto jleave;
389 rv = OKAY;
390 jleave:
391 NYD_LEAVE;
392 return rv;
395 static void
396 mesedit(int c, struct header *hp)
398 bool_t saved;
399 sighandler_type sigint;
400 FILE *nf;
401 NYD_ENTER;
403 saved = ok_blook(add_file_recipients);
404 ok_bset(add_file_recipients, TRU1);
406 sigint = safe_signal(SIGINT, SIG_IGN);
407 nf = run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint);
408 if (nf != NULL) {
409 if (hp) {
410 rewind(nf);
411 makeheader(nf, hp, NULL);
412 } else {
413 fseek(nf, 0L, SEEK_END);
414 Fclose(_coll_fp);
415 _coll_fp = nf;
418 safe_signal(SIGINT, sigint);
420 ok_bset(add_file_recipients, saved);
421 NYD_LEAVE;
424 static void
425 mespipe(char *cmd)
427 FILE *nf;
428 sighandler_type sigint;
429 char const *sh;
430 NYD_ENTER;
432 sigint = safe_signal(SIGINT, SIG_IGN);
434 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
435 n_perr(_("temporary mail edit file"), 0);
436 goto jout;
439 /* stdin = current message. stdout = new message */
440 if ((sh = ok_vlook(SHELL)) == NULL)
441 sh = XSHELL;
442 fflush(_coll_fp);
443 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL, NULL)
444 < 0) {
445 Fclose(nf);
446 goto jout;
449 if (fsize(nf) == 0) {
450 n_err(_("No bytes from \"%s\" !?\n"), cmd);
451 Fclose(nf);
452 goto jout;
455 /* Take new files */
456 fseek(nf, 0L, SEEK_END);
457 Fclose(_coll_fp);
458 _coll_fp = nf;
459 jout:
460 safe_signal(SIGINT, sigint);
461 NYD_LEAVE;
464 static int
465 forward(char *ms, FILE *fp, int f)
467 int *msgvec, rv = 0;
468 struct ignoretab *ig;
469 char const *tabst;
470 enum sendaction action;
471 NYD_ENTER;
473 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
474 if (getmsglist(ms, msgvec, 0) < 0)
475 goto jleave;
476 if (*msgvec == 0) {
477 *msgvec = first(0, MMNORM);
478 if (*msgvec == 0) {
479 n_err(_("No appropriate messages\n"));
480 goto jleave;
482 msgvec[1] = 0;
485 if (f == 'f' || f == 'F' || f == 'u')
486 tabst = NULL;
487 else if ((tabst = ok_vlook(indentprefix)) == NULL)
488 tabst = INDENT_DEFAULT;
489 if (f == 'u' || f == 'U')
490 ig = allignore;
491 else
492 ig = upperchar(f) ? NULL : ignore;
493 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
495 printf(_("Interpolating:"));
496 for (; *msgvec != 0; ++msgvec) {
497 struct message *mp = message + *msgvec - 1;
499 touch(mp);
500 printf(" %d", *msgvec);
501 fflush(stdout);
502 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
503 n_perr(_("temporary mail file"), 0);
504 rv = -1;
505 break;
508 printf("\n");
509 jleave:
510 NYD_LEAVE;
511 return rv;
514 static void
515 collstop(int s)
517 sighandler_type old_action;
518 sigset_t nset;
519 NYD_X; /* Signal handler */
521 old_action = safe_signal(s, SIG_DFL);
523 sigemptyset(&nset);
524 sigaddset(&nset, s);
525 sigprocmask(SIG_UNBLOCK, &nset, NULL);
526 n_raise(s);
527 sigprocmask(SIG_BLOCK, &nset, NULL);
529 safe_signal(s, old_action);
530 if (_coll_jmp_p) {
531 _coll_jmp_p = 0;
532 _coll_hadintr = 0;
533 siglongjmp(_coll_jmp, 1);
537 static void
538 _collint(int s)
540 NYD_X; /* Signal handler */
542 /* the control flow is subtle, because we can be called from ~q */
543 if (_coll_hadintr == 0) {
544 if (ok_blook(ignore)) {
545 puts("@");
546 fflush(stdout);
547 clearerr(stdin);
548 } else
549 _coll_hadintr = 1;
550 siglongjmp(_coll_jmp, 1);
552 exit_status |= EXIT_SEND_ERROR;
553 if (s != 0)
554 savedeadletter(_coll_fp, 1);
555 /* Aborting message, no need to fflush() .. */
556 siglongjmp(_coll_abort, 1);
559 static void
560 collhup(int s)
562 NYD_X; /* Signal handler */
563 UNUSED(s);
565 savedeadletter(_coll_fp, 1);
566 /* Let's pretend nobody else wants to clean up, a true statement at
567 * this time */
568 exit(EXIT_ERR);
571 static int
572 putesc(char const *s, FILE *stream)
574 int n = 0, rv = -1;
575 NYD_ENTER;
577 while (s[0] != '\0') {
578 if (s[0] == '\\') {
579 if (s[1] == 't') {
580 if (putc('\t', stream) == EOF)
581 goto jleave;
582 ++n;
583 s += 2;
584 continue;
586 if (s[1] == 'n') {
587 if (putc('\n', stream) == EOF)
588 goto jleave;
589 ++n;
590 s += 2;
591 continue;
594 if (putc(s[0], stream) == EOF)
595 goto jleave;
596 ++n;
597 ++s;
599 if (putc('\n', stream) == EOF)
600 goto jleave;
601 rv = ++n;
602 jleave:
603 NYD_LEAVE;
604 return rv;
607 static void
608 a_coll_call_hook(struct header *hp, char const *macname){ /* TODO v15: drop */
609 char const *val;
610 NYD2_ENTER;
612 if((val = detract(hp->h_from, GNAMEONLY)) == NULL)
613 val = "";
614 ok_vset(compose_from, val);
615 if((val = detract(hp->h_sender, 0)) == NULL)
616 val = "";
617 ok_vset(compose_sender, val);
618 if((val = detract(hp->h_to, GNAMEONLY)) == NULL)
619 val = "";
620 ok_vset(compose_to, val);
621 if((val = detract(hp->h_cc, GNAMEONLY)) == NULL)
622 val = "";
623 ok_vset(compose_cc, val);
624 if((val = detract(hp->h_bcc, GNAMEONLY)) == NULL)
625 val = "";
626 ok_vset(compose_bcc, val);
627 if((val = hp->h_subject) == NULL)
628 val = "";
629 ok_vset(compose_subject, val);
631 call_compose_mode_hook(macname);
633 ok_vclear(compose_subject);
634 ok_vclear(compose_bcc);
635 ok_vclear(compose_cc);
636 ok_vclear(compose_to);
637 ok_vclear(compose_sender);
638 ok_vclear(compose_from);
639 NYD2_LEAVE;
642 FL FILE *
643 collect(struct header *hp, int printheaders, struct message *mp,
644 char *quotefile, int doprefix, si8_t *checkaddr_err)
646 struct ignoretab *quoteig;
647 int lc, cc, c, t;
648 int volatile escape, getfields;
649 char *linebuf, *quote;
650 char const *cp;
651 size_t i, linesize; /* TODO line pool */
652 long cnt;
653 enum sendaction action;
654 sigset_t oset, nset;
655 sighandler_type savedtop;
656 FILE * volatile sigfp;
657 NYD_ENTER;
659 _coll_fp = NULL;
660 sigfp = NULL;
661 linesize = 0;
662 linebuf = quote = NULL;
664 /* Start catching signals from here, but we're still die on interrupts
665 * until we're in the main loop */
666 sigemptyset(&nset);
667 sigaddset(&nset, SIGINT);
668 sigaddset(&nset, SIGHUP);
669 sigprocmask(SIG_BLOCK, &nset, &oset);
670 handlerpush(&_collint);
671 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
672 safe_signal(SIGINT, &_collint);
673 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
674 safe_signal(SIGHUP, collhup);
675 /* TODO We do a lot of redundant signal handling, especially
676 * TODO with the command line editor(s); try to merge this */
677 _coll_savetstp = safe_signal(SIGTSTP, collstop);
678 _coll_savettou = safe_signal(SIGTTOU, collstop);
679 _coll_savettin = safe_signal(SIGTTIN, collstop);
680 if (sigsetjmp(_coll_abort, 1))
681 goto jerr;
682 if (sigsetjmp(_coll_jmp, 1))
683 goto jerr;
684 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
686 ++noreset;
687 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
688 NULL) {
689 n_perr(_("temporary mail file"), 0);
690 goto jerr;
693 /* If we are going to prompt for a subject, refrain from printing a newline
694 * after the headers (since some people mind) */
695 getfields = 0;
696 if (!(options & OPT_t_FLAG)) {
697 t = GTO | GSUBJECT | GCC | GNL;
698 if (ok_blook(fullnames))
699 t |= GCOMMA;
701 if (options & OPT_INTERACTIVE) {
702 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
703 t &= ~GNL, getfields |= GSUBJECT;
705 if (hp->h_to == NULL)
706 t &= ~GNL, getfields |= GTO;
708 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
709 if (hp->h_bcc == NULL && ok_blook(askbcc))
710 t &= ~GNL, getfields |= GBCC;
711 if (hp->h_cc == NULL && ok_blook(askcc))
712 t &= ~GNL, getfields |= GCC;
717 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
718 _coll_hadintr = 0;
720 if (!sigsetjmp(_coll_jmp, 1)) {
721 /* Ask for some headers first, as necessary */
722 if (getfields)
723 grab_headers(hp, getfields, 1);
725 /* Execute compose-post-hook TODO completely v15-compat intermediate!! */
726 if((cp = ok_vlook(on_compose_enter)) != NULL){
727 setup_from_and_sender(hp);
728 a_coll_call_hook(hp, cp);
731 /* C99 */{
732 char const *cp_obsolete = ok_vlook(NAIL_HEAD);
733 if(cp_obsolete != NULL)
734 OBSOLETE(_("please use *message-inject-head* "
735 "instead of *NAIL_HEAD*"));
737 if(((cp = ok_vlook(message_inject_head)) != NULL ||
738 (cp = cp_obsolete) != NULL) && putesc(cp, _coll_fp) < 0)
739 goto jerr;
742 /* Quote an original message */
743 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
744 quoteig = allignore;
745 action = SEND_QUOTE;
746 if (doprefix) {
747 quoteig = fwdignore;
748 if ((cp = ok_vlook(fwdheading)) == NULL)
749 cp = "-------- Original Message --------";
750 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
751 goto jerr;
752 } else if (!strcmp(quote, "noheading")) {
753 /*EMPTY*/;
754 } else if (!strcmp(quote, "headers")) {
755 quoteig = ignore;
756 } else if (!strcmp(quote, "allheaders")) {
757 quoteig = NULL;
758 action = SEND_QUOTE_ALL;
759 } else {
760 cp = hfield1("from", mp);
761 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
762 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
763 goto jerr;
764 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
765 goto jerr;
768 if (fflush(_coll_fp))
769 goto jerr;
770 if (doprefix)
771 cp = NULL;
772 else if ((cp = ok_vlook(indentprefix)) == NULL)
773 cp = INDENT_DEFAULT;
774 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
775 goto jerr;
778 if (quotefile != NULL) {
779 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
780 goto jerr;
783 if (options & OPT_INTERACTIVE) {
784 /* Print what we have sofar also on the terminal (if useful) */
785 if (!ok_blook(editalong)) {
786 if (printheaders)
787 puthead(TRU1, hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
789 rewind(_coll_fp);
790 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
791 putc(c, stdout);
792 if (fseek(_coll_fp, 0, SEEK_END))
793 goto jerr;
795 /* Ensure this is clean xxx not really necessary? */
796 fflush(stdout);
797 } else {
798 rewind(_coll_fp);
799 mesedit('e', hp);
800 goto jcont;
803 } else {
804 /* Come here for printing the after-signal message. Duplicate messages
805 * won't be printed because the write is aborted if we get a SIGTTOU */
806 if (_coll_hadintr) {
807 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
808 } else {
809 jcont:
810 printf(_("(continue)\n"));
811 fflush(stdout);
815 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
816 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
817 linebuf = srealloc(linebuf, linesize = LINESIZE);
818 while ((i = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
819 if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
820 goto jerr;
822 goto jout;
825 /* The interactive collect loop.
826 * All commands which come here are forbidden when sourcing! */
827 assert(_coll_hadintr || !(pstate & PS_SOURCING));
828 for (;;) {
829 _coll_jmp_p = 1;
830 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
831 _coll_jmp_p = 0;
833 if (cnt < 0) {
834 assert(!(pstate & PS_SOURCING));
835 if (options & OPT_t_FLAG) {
836 fflush_rewind(_coll_fp);
837 /* It is important to set PS_t_FLAG before extract_header() *and*
838 * keep OPT_t_FLAG for the first parse of the message, too! */
839 pstate |= PS_t_FLAG;
840 if (makeheader(_coll_fp, hp, checkaddr_err) != OKAY)
841 goto jerr;
842 options &= ~OPT_t_FLAG;
843 continue;
844 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
845 printf(_("*ignoreeof* set, use \"~.\" to terminate letter\n"));
846 continue;
848 break;
851 _coll_hadintr = 0;
853 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
854 jputline:
855 /* TODO calls putline(), which *always* appends LF;
856 * TODO thus, STDIN with -t will ALWAYS end with LF,
857 * TODO even if no trailing LF and QP encoding.
858 * TODO when finally changed, update cc-test.sh */
859 if (putline(_coll_fp, linebuf, cnt) < 0)
860 goto jerr;
861 continue;
862 } else if (linebuf[0] == '.') {
863 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
864 break;
866 if (linebuf[0] != escape)
867 goto jputline;
869 if (!(options & OPT_t_FLAG))
870 n_tty_addhist(linebuf, TRU1);
872 c = linebuf[1];
873 switch (c) {
874 default:
875 /* On double escape, send a single one. Otherwise, it's an error */
876 if (c == escape) {
877 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
878 goto jerr;
879 else
880 break;
882 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
883 break;
884 case '!':
885 /* Shell escape, send the balance of line to sh -c */
886 c_shell(linebuf + 2);
887 break;
888 case ':':
889 /* FALLTHRU */
890 case '_':
891 /* Escape to command mode, but be nice! */
892 _execute_command(hp, linebuf + 2, cnt - 2);
893 goto jcont;
894 case '.':
895 /* Simulate end of file on input */
896 goto jout;
897 case 'x':
898 /* Same as 'q', but no *DEAD* saving */
899 /* FALLTHRU */
900 case 'q':
901 /* Force a quit, act like an interrupt had happened */
902 ++_coll_hadintr;
903 _collint((c == 'x') ? 0 : SIGINT);
904 exit(EXIT_ERR);
905 /*NOTREACHED*/
906 case 'h':
907 /* Grab a bunch of headers */
909 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
910 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
911 while (hp->h_to == NULL);
912 goto jcont;
913 case 'H':
914 /* Grab extra headers */
916 grab_headers(hp, GEXTRA, 0);
917 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
918 goto jcont;
919 case 't':
920 /* Add to the To list */
921 hp->h_to = cat(hp->h_to,
922 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
923 NULL));
924 break;
925 case 's':
926 /* Set the Subject list */
927 cp = linebuf + 2;
928 while (whitechar(*cp))
929 ++cp;
930 hp->h_subject = savestr(cp);
931 break;
932 #ifdef HAVE_DEBUG
933 case 'S':
934 c_sstats(NULL);
935 break;
936 #endif
937 case '@':
938 /* Edit the attachment list */
939 if (linebuf[2] != '\0')
940 append_attachments(&hp->h_attach, linebuf + 2);
941 else
942 edit_attachments(&hp->h_attach);
943 break;
944 case 'c':
945 /* Add to the CC list */
946 hp->h_cc = cat(hp->h_cc,
947 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
948 NULL));
949 break;
950 case 'b':
951 /* Add stuff to blind carbon copies list */
952 hp->h_bcc = cat(hp->h_bcc,
953 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
954 NULL));
955 break;
956 case 'd':
957 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
958 linebuf[linesize - 1] = '\0';
959 /*FALLTHRU*/
960 case 'R':
961 case 'r':
962 case '<':
963 /* Invoke a file: Search for the file name, then open it and copy the
964 * contents to _coll_fp */
965 cp = linebuf + 2;
966 while (whitechar(*cp))
967 ++cp;
968 if (*cp == '\0') {
969 n_err(_("Interpolate what file?\n"));
970 break;
972 if (*cp == '!') {
973 insertcommand(_coll_fp, cp + 1);
974 break;
976 if ((cp = file_expand(cp)) == NULL)
977 break;
978 if (is_dir(cp)) {
979 n_err(_("\"%s\": Directory\n"), cp);
980 break;
982 printf(_("\"%s\" "), cp);
983 fflush(stdout);
984 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
985 goto jerr;
986 printf(_("%d/%d\n"), lc, cc);
987 break;
988 case 'i':
989 /* Insert a variable into the file */
990 cp = linebuf + 2;
991 while (whitechar(*cp))
992 ++cp;
993 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
994 break;
995 if (putesc(cp, _coll_fp) < 0)
996 goto jerr;
997 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
998 goto jerr;
999 break;
1000 case 'a':
1001 case 'A':
1002 /* Insert the contents of a signature variable */
1003 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
1004 if (cp != NULL && *cp != '\0') {
1005 if (putesc(cp, _coll_fp) < 0)
1006 goto jerr;
1007 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1008 goto jerr;
1010 break;
1011 case 'w':
1012 /* Write the message on a file */
1013 cp = linebuf + 2;
1014 while (blankchar(*cp))
1015 ++cp;
1016 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
1017 n_err(_("Write what file!?\n"));
1018 break;
1020 rewind(_coll_fp);
1021 if (exwrite(cp, _coll_fp, 1) < 0)
1022 goto jerr;
1023 break;
1024 case 'm':
1025 case 'M':
1026 case 'f':
1027 case 'F':
1028 case 'u':
1029 case 'U':
1030 /* Interpolate the named messages, if we are in receiving mail mode.
1031 * Does the standard list processing garbage. If ~f is given, we
1032 * don't shift over */
1033 if (forward(linebuf + 2, _coll_fp, c) < 0)
1034 goto jerr;
1035 goto jcont;
1036 case 'p':
1037 /* Print current state of the message without altering anything */
1038 print_collf(_coll_fp, hp);
1039 goto jcont;
1040 case '|':
1041 /* Pipe message through command. Collect output as new message */
1042 rewind(_coll_fp);
1043 mespipe(linebuf + 2);
1044 goto jcont;
1045 case 'v':
1046 case 'e':
1047 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1048 rewind(_coll_fp);
1049 mesedit(c, ok_blook(editheaders) ? hp : NULL);
1050 goto jcont;
1051 case '?':
1052 /* Last the lengthy help string. (Very ugly, but take care for
1053 * compiler supported string lengths :() */
1054 puts(_(
1055 "TILDE ESCAPES (to be placed after a newline) excerpt:\n"
1056 "~. Commit and send message\n"
1057 "~: <command> Execute a mail command\n"
1058 "~<! <command> Insert output of command\n"
1059 "~@ [<files>] Edit attachment list\n"
1060 "~A Insert *Sign* variable (`~a' inserts *sign*)\n"
1061 "~c <users> Add users to Cc: list (`~b' for Bcc:)\n"
1062 "~d Read in *DEAD* (dead.letter)\n"
1063 "~e Edit message via *EDITOR*"
1065 puts(_(
1066 "~F <msglist> Read in with headers, don't *indentprefix* lines\n"
1067 "~f <msglist> Like ~F, but honour `ignore' / `retain' configuration\n"
1068 "~H Edit From:, Reply-To: and Sender:\n"
1069 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"
1070 "~i <variable> Insert a value and a newline\n"
1071 "~M <msglist> Read in with headers, *indentprefix* (`~m': `retain' etc.)\n"
1072 "~p Print current message compose buffer\n"
1073 "~r <file> Read in a file (`~R' *indentprefix* lines)"
1075 puts(_(
1076 "~s <subject> Set Subject:\n"
1077 "~t <users> Add users to To: list\n"
1078 "~u <msglist> Read in message(s) without headers (`~U' indents lines)\n"
1079 "~v Edit message via *VISUAL*\n"
1080 "~w <file> Write message onto file\n"
1081 "~x Abort composition, discard message (`~q' saves in *DEAD*)\n"
1082 "~| <command> Pipe message through shell filter"
1084 break;
1088 jout:
1089 /* Execute compose-post-hook TODO completely v15-compat intermediate!! */
1090 if((cp = ok_vlook(on_compose_leave)) != NULL){
1091 setup_from_and_sender(hp);
1092 a_coll_call_hook(hp, cp);
1095 /* Final change to edit headers, if not already above */
1096 if (ok_blook(bsdcompat) || ok_blook(askatend)) {
1097 if (hp->h_cc == NULL && ok_blook(askcc))
1098 grab_headers(hp, GCC, 1);
1099 if (hp->h_bcc == NULL && ok_blook(askbcc))
1100 grab_headers(hp, GBCC, 1);
1102 if (hp->h_attach == NULL && ok_blook(askattach))
1103 edit_attachments(&hp->h_attach);
1105 /* Add automatic receivers */
1106 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1107 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1108 EACM_NORMAL, checkaddr_err));
1109 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1110 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1111 EACM_NORMAL, checkaddr_err));
1112 if (*checkaddr_err != 0)
1113 goto jerr;
1115 /* Place signature? */
1116 if((cp = ok_vlook(signature)) != NULL && *cp != '\0'){
1117 if((quote = file_expand(cp)) == NULL){
1118 n_err(_("*signature* expands to invalid file: \"%s\"\n"), cp);
1119 goto jerr;
1121 if((sigfp = Fopen(cp = quote, "r")) == NULL){
1122 n_err(_("Can't open *signature* \"%s\": %s\n"), cp, strerror(errno));
1123 goto jerr;
1126 if(linebuf == NULL)
1127 linebuf = smalloc(linesize = LINESIZE);
1128 c = '\0';
1129 while((i = fread(linebuf, sizeof *linebuf, linesize, UNVOLATILE(sigfp)))
1130 > 0){
1131 c = linebuf[i - 1];
1132 if(i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
1133 goto jerr;
1136 /* C99 */{
1137 FILE *x = UNVOLATILE(sigfp);
1138 int e = errno, ise = ferror(x);
1140 sigfp = NULL;
1141 Fclose(x);
1143 if(ise){
1144 n_err(_("Errors while reading *signature* \"%s\": %s\n"),
1145 cp, strerror(e));
1146 goto jerr;
1150 if(c != '\0' && c != '\n')
1151 putc('\n', _coll_fp);
1154 { char const *cp_obsolete = ok_vlook(NAIL_TAIL);
1156 if(cp_obsolete != NULL)
1157 OBSOLETE(_("please use *message-inject-tail* instead of *NAIL_TAIL*"));
1159 if((cp = ok_vlook(message_inject_tail)) != NULL ||
1160 (cp = cp_obsolete) != NULL){
1161 if(putesc(cp, _coll_fp) < 0)
1162 goto jerr;
1163 if((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1164 goto jerr;
1168 if(fflush(_coll_fp))
1169 goto jerr;
1170 rewind(_coll_fp);
1172 jleave:
1173 if (linebuf != NULL)
1174 free(linebuf);
1175 handlerpop();
1176 --noreset;
1177 sigemptyset(&nset);
1178 sigaddset(&nset, SIGINT);
1179 sigaddset(&nset, SIGHUP);
1180 sigprocmask(SIG_BLOCK, &nset, NULL);
1181 safe_signal(SIGINT, _coll_saveint);
1182 safe_signal(SIGHUP, _coll_savehup);
1183 safe_signal(SIGTSTP, _coll_savetstp);
1184 safe_signal(SIGTTOU, _coll_savettou);
1185 safe_signal(SIGTTIN, _coll_savettin);
1186 sigprocmask(SIG_SETMASK, &oset, NULL);
1187 NYD_LEAVE;
1188 return _coll_fp;
1190 jerr:
1191 if(sigfp != NULL)
1192 Fclose(UNVOLATILE(sigfp));
1193 if (_coll_fp != NULL) {
1194 Fclose(_coll_fp);
1195 _coll_fp = NULL;
1197 goto jleave;
1200 FL void
1201 savedeadletter(FILE *fp, int fflush_rewind_first)
1203 char const *cp;
1204 int c;
1205 FILE *dbuf;
1206 ul_i lines, bytes;
1207 NYD_ENTER;
1209 if ((options & OPT_DEBUG) || !ok_blook(save))
1210 goto jleave;
1212 if (fflush_rewind_first) {
1213 fflush(fp);
1214 rewind(fp);
1216 if (fsize(fp) == 0)
1217 goto jleave;
1219 cp = getdeadletter();
1220 c = umask(077);
1221 dbuf = Fopen(cp, "a");
1222 umask(c);
1223 if (dbuf == NULL)
1224 goto jleave;
1226 really_rewind(fp);
1228 printf("\"%s\" ", cp);
1229 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1230 putc(c, dbuf);
1231 if (c == '\n')
1232 ++lines;
1234 printf("%lu/%lu\n", lines, bytes);
1236 Fclose(dbuf);
1237 rewind(fp);
1238 jleave:
1239 NYD_LEAVE;
1242 /* s-it-mode */