Move c_flag() etc. to cmd2.c: they belong
[s-mailx.git] / collect.c
bloba1c2b654e432327f895a2d7a2f0208b8a924a318
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); /* TODO wysh set! */
105 /* call_compose_mode_hook() setter hook */
106 static void a_coll__hook_setter(void *arg);
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 int c;
226 NYD_ENTER;
228 if ((ibuf = Popen(cmd, "r", ok_vlook(SHELL), NULL, 0)) != NULL) {
229 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
230 putc(c, fp);
231 Pclose(ibuf, TRU1);
232 } else
233 n_perr(cmd, 0);
234 NYD_LEAVE;
237 static void
238 print_collf(FILE *cf, struct header *hp)
240 char *lbuf = NULL; /* TODO line pool */
241 sighandler_type sigint;
242 FILE * volatile obuf = stdout;
243 struct attachment *ap;
244 char const *cp;
245 enum gfield gf;
246 size_t linesize = 0, linelen, cnt, cnt2;
247 NYD_ENTER;
249 fflush_rewind(cf);
250 cnt = cnt2 = fsize(cf);
252 sigint = safe_signal(SIGINT, SIG_IGN);
254 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
255 size_t l, m;
257 m = 4;
258 if (hp->h_to != NULL)
259 ++m;
260 if (hp->h_subject != NULL)
261 ++m;
262 if (hp->h_cc != NULL)
263 ++m;
264 if (hp->h_bcc != NULL)
265 ++m;
266 if (hp->h_attach != NULL)
267 ++m;
268 m += (hp->h_from != NULL || myaddrs(hp) != NULL);
269 m += (hp->h_sender != NULL || ok_vlook(sender) != NULL);
270 m += (hp->h_replyto != NULL || ok_vlook(replyto) != NULL);
272 l = (*cp == '\0') ? (size_t)screensize() : strtoul(cp, NULL, 0);
273 if (m > l)
274 goto jpager;
275 l -= m;
277 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
279 rewind(cf);
280 if (l < m) {
281 jpager:
282 if (sigsetjmp(_coll_pipejmp, 1))
283 goto jendpipe;
284 if ((obuf = n_pager_open()) == NULL)
285 obuf = stdout;
286 else
287 safe_signal(SIGPIPE, &_collect_onpipe);
291 fprintf(obuf, _("-------\nMessage contains:\n"));
292 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
293 puthead(TRU1, hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
294 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
295 prout(lbuf, linelen, obuf);
296 if (hp->h_attach != NULL) {
297 fputs(_("-------\nAttachments:\n"), obuf);
298 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
299 if (ap->a_msgno)
300 fprintf(obuf, " - message %u\n", ap->a_msgno);
301 else {
302 /* TODO after MIME/send layer rewrite we *know*
303 * TODO the details of the attachment here,
304 * TODO so adjust this again, then */
305 char const *cs, *csi = "-> ";
307 if ((cs = ap->a_charset) == NULL &&
308 (csi = "<- ", cs = ap->a_input_charset) == NULL)
309 cs = charset_get_lc();
310 if ((cp = ap->a_content_type) == NULL)
311 cp = "?";
312 else if (ascncasecmp(cp, "text/", 5))
313 csi = "";
314 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs,
315 n_shell_quote_cp(ap->a_name, FAL0));
320 jendpipe:
321 if (obuf != stdout)
322 n_pager_close(obuf);
323 if (lbuf != NULL)
324 free(lbuf);
325 safe_signal(SIGINT, sigint);
326 NYD_LEAVE;
329 static int
330 exwrite(char const *name, FILE *fp, int f)
332 FILE *of;
333 int c, lc, rv = -1;
334 long cc;
335 NYD_ENTER;
337 if (f) {
338 printf("\"%s\" ", name);
339 fflush(stdout);
341 if ((of = Fopen(name, "a")) == NULL) {
342 n_perr(NULL, 0);
343 goto jleave;
346 lc = 0;
347 cc = 0;
348 while ((c = getc(fp)) != EOF) {
349 ++cc;
350 if (c == '\n')
351 ++lc;
352 putc(c, of);
353 if (ferror(of)) {
354 n_perr(name, 0);
355 Fclose(of);
356 goto jleave;
359 Fclose(of);
360 printf(_("%d/%ld\n"), lc, cc);
361 fflush(stdout);
362 rv = 0;
363 jleave:
364 NYD_LEAVE;
365 return rv;
368 static enum okay
369 makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err)
371 FILE *nf;
372 int c;
373 enum okay rv = STOP;
374 NYD_ENTER;
376 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
377 n_perr(_("temporary mail edit file"), 0);
378 goto jleave;
381 extract_header(fp, hp, checkaddr_err);
383 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
384 putc(c, nf);
385 if (fp != _coll_fp)
386 Fclose(_coll_fp);
387 Fclose(fp);
388 _coll_fp = nf;
389 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
390 goto jleave;
391 rv = OKAY;
392 jleave:
393 NYD_LEAVE;
394 return rv;
397 static void
398 mesedit(int c, struct header *hp)
400 bool_t saved;
401 sighandler_type sigint;
402 FILE *nf;
403 NYD_ENTER;
405 saved = ok_blook(add_file_recipients);
406 ok_bset(add_file_recipients, TRU1);
408 sigint = safe_signal(SIGINT, SIG_IGN);
409 nf = run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint);
410 if (nf != NULL) {
411 if (hp) {
412 rewind(nf);
413 makeheader(nf, hp, NULL);
414 } else {
415 fseek(nf, 0L, SEEK_END);
416 Fclose(_coll_fp);
417 _coll_fp = nf;
420 safe_signal(SIGINT, sigint);
422 ok_bset(add_file_recipients, saved);
423 NYD_LEAVE;
426 static void
427 mespipe(char *cmd)
429 FILE *nf;
430 sighandler_type sigint;
431 NYD_ENTER;
433 sigint = safe_signal(SIGINT, SIG_IGN);
435 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
436 n_perr(_("temporary mail edit file"), 0);
437 goto jout;
440 /* stdin = current message. stdout = new message */
441 fflush(_coll_fp);
442 if (run_command(ok_vlook(SHELL), 0, fileno(_coll_fp), fileno(nf), "-c",
443 cmd, NULL, NULL) < 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 static void
607 a_coll__hook_setter(void *arg){ /* TODO v15: drop */
608 struct header *hp;
609 char const *val;
610 NYD2_ENTER;
612 hp = arg;
614 if((val = detract(hp->h_from, GNAMEONLY)) == NULL)
615 val = "";
616 ok_vset(compose_from, val);
617 if((val = detract(hp->h_sender, 0)) == NULL)
618 val = "";
619 ok_vset(compose_sender, val);
620 if((val = detract(hp->h_to, GNAMEONLY)) == NULL)
621 val = "";
622 ok_vset(compose_to, val);
623 if((val = detract(hp->h_cc, GNAMEONLY)) == NULL)
624 val = "";
625 ok_vset(compose_cc, val);
626 if((val = detract(hp->h_bcc, GNAMEONLY)) == NULL)
627 val = "";
628 ok_vset(compose_bcc, val);
629 if((val = hp->h_subject) == NULL)
630 val = "";
631 ok_vset(compose_subject, val);
632 NYD2_LEAVE;
635 FL FILE *
636 collect(struct header *hp, int printheaders, struct message *mp,
637 char *quotefile, int doprefix, si8_t *checkaddr_err)
639 struct ignoretab *quoteig;
640 int lc, cc, c, t;
641 int volatile escape, getfields;
642 char *linebuf, *quote;
643 char const *cp;
644 size_t i, linesize; /* TODO line pool */
645 long cnt;
646 enum sendaction action;
647 sigset_t oset, nset;
648 FILE * volatile sigfp;
649 NYD_ENTER;
651 _coll_fp = NULL;
652 sigfp = NULL;
653 linesize = 0;
654 linebuf = quote = NULL;
656 /* Start catching signals from here, but we're still die on interrupts
657 * until we're in the main loop */
658 sigfillset(&nset);
659 sigprocmask(SIG_BLOCK, &nset, &oset);
660 /* FIXME have dropped handlerpush() and localized onintr() in lex.c! */
661 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
662 safe_signal(SIGINT, &_collint);
663 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
664 safe_signal(SIGHUP, collhup);
665 /* TODO We do a lot of redundant signal handling, especially
666 * TODO with the command line editor(s); try to merge this */
667 _coll_savetstp = safe_signal(SIGTSTP, collstop);
668 _coll_savettou = safe_signal(SIGTTOU, collstop);
669 _coll_savettin = safe_signal(SIGTTIN, collstop);
670 if (sigsetjmp(_coll_abort, 1))
671 goto jerr;
672 if (sigsetjmp(_coll_jmp, 1))
673 goto jerr;
674 pstate |= PS_RECURSED;
675 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
677 ++noreset;
678 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
679 NULL) {
680 n_perr(_("temporary mail file"), 0);
681 goto jerr;
684 /* If we are going to prompt for a subject, refrain from printing a newline
685 * after the headers (since some people mind) */
686 getfields = 0;
687 if (!(options & OPT_t_FLAG)) {
688 t = GTO | GSUBJECT | GCC | GNL;
689 if (ok_blook(fullnames))
690 t |= GCOMMA;
692 if (options & OPT_INTERACTIVE) {
693 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
694 t &= ~GNL, getfields |= GSUBJECT;
696 if (hp->h_to == NULL)
697 t &= ~GNL, getfields |= GTO;
699 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
700 if (hp->h_bcc == NULL && ok_blook(askbcc))
701 t &= ~GNL, getfields |= GBCC;
702 if (hp->h_cc == NULL && ok_blook(askcc))
703 t &= ~GNL, getfields |= GCC;
706 } else {
707 UNINIT(t, 0);
710 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
711 _coll_hadintr = 0;
713 if (!sigsetjmp(_coll_jmp, 1)) {
714 /* Ask for some headers first, as necessary */
715 if (getfields)
716 grab_headers(hp, getfields, 1);
718 /* Execute compose-enter TODO completely v15-compat intermediate!! */
719 if((cp = ok_vlook(on_compose_enter)) != NULL){
720 setup_from_and_sender(hp);
721 call_compose_mode_hook(cp, &a_coll__hook_setter, hp);
724 if(!(options & OPT_Mm_FLAG)){
725 char const *cp_obsolete = ok_vlook(NAIL_HEAD);
726 if(cp_obsolete != NULL)
727 OBSOLETE(_("please use *message-inject-head* "
728 "instead of *NAIL_HEAD*"));
730 if(((cp = ok_vlook(message_inject_head)) != NULL ||
731 (cp = cp_obsolete) != NULL) && putesc(cp, _coll_fp) < 0)
732 goto jerr;
734 /* Quote an original message */
735 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
736 quoteig = allignore;
737 action = SEND_QUOTE;
738 if (doprefix) {
739 quoteig = fwdignore;
740 if ((cp = ok_vlook(fwdheading)) == NULL)
741 cp = "-------- Original Message --------";
742 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
743 goto jerr;
744 } else if (!strcmp(quote, "noheading")) {
745 /*EMPTY*/;
746 } else if (!strcmp(quote, "headers")) {
747 quoteig = ignore;
748 } else if (!strcmp(quote, "allheaders")) {
749 quoteig = NULL;
750 action = SEND_QUOTE_ALL;
751 } else {
752 cp = hfield1("from", mp);
753 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
754 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
755 goto jerr;
756 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
757 goto jerr;
760 if (fflush(_coll_fp))
761 goto jerr;
762 if (doprefix)
763 cp = NULL;
764 else if ((cp = ok_vlook(indentprefix)) == NULL)
765 cp = INDENT_DEFAULT;
766 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
767 goto jerr;
771 if (quotefile != NULL) {
772 if (_include_file(quotefile, &lc, &cc,
773 !(options & OPT_Mm_FLAG), FAL0) != 0)
774 goto jerr;
777 if ((options & (OPT_Mm_FLAG | OPT_INTERACTIVE)) == OPT_INTERACTIVE) {
778 /* Print what we have sofar also on the terminal (if useful) */
779 if (!ok_blook(editalong)) {
780 if (printheaders)
781 puthead(TRU1, hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
783 rewind(_coll_fp);
784 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
785 putc(c, stdout);
786 if (fseek(_coll_fp, 0, SEEK_END))
787 goto jerr;
789 /* Ensure this is clean xxx not really necessary? */
790 fflush(stdout);
791 } else {
792 rewind(_coll_fp);
793 mesedit('e', hp);
794 goto jcont;
797 } else {
798 /* Come here for printing the after-signal message. Duplicate messages
799 * won't be printed because the write is aborted if we get a SIGTTOU */
800 if (_coll_hadintr) {
801 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
802 } else {
803 jcont:
804 printf(_("(continue)\n"));
805 fflush(stdout);
809 /* We're done with -M or -m */
810 if(options & OPT_Mm_FLAG)
811 goto jout;
812 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
813 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
814 linebuf = srealloc(linebuf, linesize = LINESIZE);
815 while ((i = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
816 if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
817 goto jerr;
819 goto jout;
822 /* The interactive collect loop.
823 * All commands which come here are forbidden when sourcing! */
824 assert(_coll_hadintr || !(pstate & PS_SOURCING));
825 for (;;) {
826 _coll_jmp_p = 1;
827 cnt = n_lex_input("", FAL0, &linebuf, &linesize, NULL);
828 _coll_jmp_p = 0;
830 if (cnt < 0) {
831 assert(!(pstate & PS_SOURCING));
832 if (options & OPT_t_FLAG) {
833 fflush_rewind(_coll_fp);
834 /* It is important to set PS_t_FLAG before extract_header() *and*
835 * keep OPT_t_FLAG for the first parse of the message, too! */
836 pstate |= PS_t_FLAG;
837 if (makeheader(_coll_fp, hp, checkaddr_err) != OKAY)
838 goto jerr;
839 options &= ~OPT_t_FLAG;
840 continue;
841 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
842 printf(_("*ignoreeof* set, use \"~.\" to terminate letter\n"));
843 continue;
845 break;
848 _coll_hadintr = 0;
850 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
851 jputline:
852 /* TODO calls putline(), which *always* appends LF;
853 * TODO thus, STDIN with -t will ALWAYS end with LF,
854 * TODO even if no trailing LF and QP encoding.
855 * TODO when finally changed, update cc-test.sh */
856 if (putline(_coll_fp, linebuf, cnt) < 0)
857 goto jerr;
858 continue;
859 } else if (linebuf[0] == '.') {
860 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
861 break;
863 if (linebuf[0] != escape)
864 goto jputline;
866 if (!(options & OPT_t_FLAG))
867 n_tty_addhist(linebuf, TRU1);
869 c = linebuf[1];
870 switch (c) {
871 default:
872 /* On double escape, send a single one. Otherwise, it's an error */
873 if (c == escape) {
874 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
875 goto jerr;
876 else
877 break;
879 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
880 break;
881 case '!':
882 /* Shell escape, send the balance of line to sh -c */
883 c_shell(linebuf + 2);
884 break;
885 case ':':
886 /* FALLTHRU */
887 case '_':
888 /* Escape to command mode, but be nice! */
889 _execute_command(hp, linebuf + 2, cnt - 2);
890 goto jcont;
891 case '.':
892 /* Simulate end of file on input */
893 goto jout;
894 case 'x':
895 /* Same as 'q', but no *DEAD* saving */
896 /* FALLTHRU */
897 case 'q':
898 /* Force a quit, act like an interrupt had happened */
899 ++_coll_hadintr;
900 _collint((c == 'x') ? 0 : SIGINT);
901 exit(EXIT_ERR);
902 /*NOTREACHED*/
903 case 'h':
904 /* Grab a bunch of headers */
906 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
907 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
908 while (hp->h_to == NULL);
909 goto jcont;
910 case 'H':
911 /* Grab extra headers */
913 grab_headers(hp, GEXTRA, 0);
914 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
915 goto jcont;
916 case 't':
917 /* Add to the To list */
918 hp->h_to = cat(hp->h_to,
919 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
920 NULL));
921 break;
922 case 's':
923 /* Set the Subject list */
924 cp = linebuf + 2;
925 while (whitechar(*cp))
926 ++cp;
927 hp->h_subject = savestr(cp);
928 break;
929 #ifdef HAVE_MEMORY_DEBUG
930 case 'S':
931 c_sstats(NULL);
932 break;
933 #endif
934 case '@':
935 /* Edit the attachment list */
936 if (linebuf[2] != '\0')
937 append_attachments(&hp->h_attach, linebuf + 2);
938 else
939 edit_attachments(&hp->h_attach);
940 break;
941 case 'c':
942 /* Add to the CC list */
943 hp->h_cc = cat(hp->h_cc,
944 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
945 NULL));
946 break;
947 case 'b':
948 /* Add stuff to blind carbon copies list */
949 hp->h_bcc = cat(hp->h_bcc,
950 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
951 NULL));
952 break;
953 case 'd':
954 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
955 linebuf[linesize - 1] = '\0';
956 /*FALLTHRU*/
957 case 'R':
958 case 'r':
959 case '<':
960 /* Invoke a file: Search for the file name, then open it and copy the
961 * contents to _coll_fp */
962 cp = linebuf + 2;
963 while (whitechar(*cp))
964 ++cp;
965 if (*cp == '\0') {
966 n_err(_("Interpolate what file?\n"));
967 break;
969 if (*cp == '!') {
970 insertcommand(_coll_fp, cp + 1);
971 break;
973 if ((cp = file_expand(cp)) == NULL)
974 break;
975 if (is_dir(cp)) {
976 n_err(_("\"%s\": Directory\n"), cp);
977 break;
979 printf(_("\"%s\" "), cp);
980 fflush(stdout);
981 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
982 goto jerr;
983 printf(_("%d/%d\n"), lc, cc);
984 break;
985 case 'i':
986 /* Insert a variable into the file */
987 cp = linebuf + 2;
988 while (whitechar(*cp))
989 ++cp;
990 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
991 break;
992 if (putesc(cp, _coll_fp) < 0)
993 goto jerr;
994 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
995 goto jerr;
996 break;
997 case 'a':
998 case 'A':
999 /* Insert the contents of a signature variable */
1000 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
1001 if (cp != NULL && *cp != '\0') {
1002 if (putesc(cp, _coll_fp) < 0)
1003 goto jerr;
1004 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1005 goto jerr;
1007 break;
1008 case 'w':
1009 /* Write the message on a file */
1010 cp = linebuf + 2;
1011 while (blankchar(*cp))
1012 ++cp;
1013 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
1014 n_err(_("Write what file!?\n"));
1015 break;
1017 rewind(_coll_fp);
1018 if (exwrite(cp, _coll_fp, 1) < 0)
1019 goto jerr;
1020 break;
1021 case 'm':
1022 case 'M':
1023 case 'f':
1024 case 'F':
1025 case 'u':
1026 case 'U':
1027 /* Interpolate the named messages, if we are in receiving mail mode.
1028 * Does the standard list processing garbage. If ~f is given, we
1029 * don't shift over */
1030 if (forward(linebuf + 2, _coll_fp, c) < 0)
1031 goto jerr;
1032 goto jcont;
1033 case 'p':
1034 /* Print current state of the message without altering anything */
1035 print_collf(_coll_fp, hp);
1036 goto jcont;
1037 case '|':
1038 /* Pipe message through command. Collect output as new message */
1039 rewind(_coll_fp);
1040 mespipe(linebuf + 2);
1041 goto jcont;
1042 case 'v':
1043 case 'e':
1044 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1045 rewind(_coll_fp);
1046 mesedit(c, ok_blook(editheaders) ? hp : NULL);
1047 goto jcont;
1048 case '?':
1049 /* Last the lengthy help string. (Very ugly, but take care for
1050 * compiler supported string lengths :() */
1051 puts(_(
1052 "TILDE ESCAPES (to be placed after a newline) excerpt:\n"
1053 "~. Commit and send message\n"
1054 "~: <command> Execute a mail command\n"
1055 "~<! <command> Insert output of command\n"
1056 "~@ [<files>] Edit attachment list\n"
1057 "~A Insert *Sign* variable (`~a' inserts *sign*)\n"
1058 "~c <users> Add users to Cc: list (`~b' for Bcc:)\n"
1059 "~d Read in *DEAD* (dead.letter)\n"
1060 "~e Edit message via *EDITOR*"
1062 puts(_(
1063 "~F <msglist> Read in with headers, don't *indentprefix* lines\n"
1064 "~f <msglist> Like ~F, but honour `ignore' / `retain' configuration\n"
1065 "~H Edit From:, Reply-To: and Sender:\n"
1066 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"
1067 "~i <variable> Insert a value and a newline\n"
1068 "~M <msglist> Read in with headers, *indentprefix* (`~m': `retain' etc.)\n"
1069 "~p Print current message compose buffer\n"
1070 "~r <file> Read in a file (`~R' *indentprefix* lines)"
1072 puts(_(
1073 "~s <subject> Set Subject:\n"
1074 "~t <users> Add users to To: list\n"
1075 "~u <msglist> Read in message(s) without headers (`~U' indents lines)\n"
1076 "~v Edit message via *VISUAL*\n"
1077 "~w <file> Write message onto file\n"
1078 "~x Abort composition, discard message (`~q' saves in *DEAD*)\n"
1079 "~| <command> Pipe message through shell filter"
1081 break;
1085 jout:
1086 /* Execute compose-leave TODO completely v15-compat intermediate!! */
1087 if((cp = ok_vlook(on_compose_leave)) != NULL){
1088 setup_from_and_sender(hp);
1089 call_compose_mode_hook(cp, &a_coll__hook_setter, hp);
1092 /* Final change to edit headers, if not already above */
1093 if (ok_blook(bsdcompat) || ok_blook(askatend)) {
1094 if (hp->h_cc == NULL && ok_blook(askcc))
1095 grab_headers(hp, GCC, 1);
1096 if (hp->h_bcc == NULL && ok_blook(askbcc))
1097 grab_headers(hp, GBCC, 1);
1099 if (hp->h_attach == NULL && ok_blook(askattach))
1100 edit_attachments(&hp->h_attach);
1102 /* Add automatic receivers */
1103 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1104 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1105 EACM_NORMAL, checkaddr_err));
1106 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1107 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1108 EACM_NORMAL, checkaddr_err));
1109 if (*checkaddr_err != 0)
1110 goto jerr;
1112 if(options & OPT_Mm_FLAG)
1113 goto jskiptails;
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 jskiptails:
1169 if(fflush(_coll_fp))
1170 goto jerr;
1171 rewind(_coll_fp);
1173 jleave:
1174 if (linebuf != NULL)
1175 free(linebuf);
1176 --noreset;
1177 sigfillset(&nset);
1178 sigprocmask(SIG_BLOCK, &nset, NULL);
1179 pstate &= ~PS_RECURSED;
1180 safe_signal(SIGINT, _coll_saveint);
1181 safe_signal(SIGHUP, _coll_savehup);
1182 safe_signal(SIGTSTP, _coll_savetstp);
1183 safe_signal(SIGTTOU, _coll_savettou);
1184 safe_signal(SIGTTIN, _coll_savettin);
1185 sigprocmask(SIG_SETMASK, &oset, NULL);
1186 NYD_LEAVE;
1187 return _coll_fp;
1189 jerr:
1190 if(sigfp != NULL)
1191 Fclose(UNVOLATILE(sigfp));
1192 if (_coll_fp != NULL) {
1193 Fclose(_coll_fp);
1194 _coll_fp = NULL;
1196 goto jleave;
1199 FL void
1200 savedeadletter(FILE *fp, int fflush_rewind_first)
1202 char const *cp;
1203 int c;
1204 FILE *dbuf;
1205 ul_i lines, bytes;
1206 NYD_ENTER;
1208 if ((options & OPT_DEBUG) || !ok_blook(save))
1209 goto jleave;
1211 if (fflush_rewind_first) {
1212 fflush(fp);
1213 rewind(fp);
1215 if (fsize(fp) == 0)
1216 goto jleave;
1218 cp = getdeadletter();
1219 dbuf = Fopen(cp, "a");
1220 if (dbuf == NULL)
1221 goto jleave;
1223 really_rewind(fp);
1225 printf("\"%s\" ", cp);
1226 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1227 putc(c, dbuf);
1228 if (c == '\n')
1229 ++lines;
1231 printf("%lu/%lu\n", lines, bytes);
1233 Fclose(dbuf);
1234 rewind(fp);
1235 jleave:
1236 NYD_LEAVE;
1239 /* s-it-mode */