NEWS: v14.8.6 (Hen Harrier II)
[s-mailx.git] / collect.c
bloba7862cd83ebade9e7e0c6255f1bb486191692d21
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Collect input from standard input, handling ~ escapes.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE collect
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* The following hookiness with global variables is so that on receipt of an
43 * interrupt signal, the partial message can be salted away on *DEAD* */
45 static sighandler_type _coll_saveint; /* Previous SIGINT value */
46 static sighandler_type _coll_savehup; /* Previous SIGHUP value */
47 static sighandler_type _coll_savetstp; /* Previous SIGTSTP value */
48 static sighandler_type _coll_savettou; /* Previous SIGTTOU value */
49 static sighandler_type _coll_savettin; /* Previous SIGTTIN value */
50 static FILE *_coll_fp; /* File for saving away */
51 static int volatile _coll_hadintr; /* Have seen one SIGINT so far */
52 static sigjmp_buf _coll_jmp; /* To get back to work */
53 static int _coll_jmp_p; /* whether to long jump */
54 static sigjmp_buf _coll_abort; /* To end collection with error */
55 static sigjmp_buf _coll_pipejmp; /* On broken pipe */
57 /* Handle `~:', `~_' */
58 static void _execute_command(struct header *hp, char *linebuf,
59 size_t linesize);
61 /* If *interactive* is set and *doecho* is, too, also dump to *stdout* */
62 static int _include_file(char const *name, int *linecount,
63 int *charcount, bool_t doecho, bool_t indent);
65 static void _collect_onpipe(int signo);
67 /* Execute cmd and insert its standard output into fp */
68 static void insertcommand(FILE *fp, char const *cmd);
70 /* ~p command */
71 static void print_collf(FILE *collf, struct header *hp);
73 /* Write a file, ex-like if f set */
74 static int exwrite(char const *name, FILE *fp, int f);
76 static enum okay makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err);
78 /* Edit the message being collected on fp. On return, make the edit file the
79 * new temp file */
80 static void mesedit(int c, struct header *hp);
82 /* Pipe the message through the command. Old message is on stdin of command,
83 * new message collected from stdout. Shell must return 0 to accept new msg */
84 static void mespipe(char *cmd);
86 /* Interpolate the named messages into the current message, possibly doing
87 * indent stuff. The flag argument is one of the tilde escapes: [mMfFuU].
88 * Return a count of the number of characters now in the message, or -1 if an
89 * error is encountered writing the message temporary */
90 static int forward(char *ms, FILE *fp, int f);
92 /* Print (continue) when continued after ^Z */
93 static void collstop(int s);
95 /* On interrupt, come here to save the partial message in ~/dead.letter.
96 * Then jump out of the collection loop */
97 static void _collint(int s);
99 static void collhup(int s);
101 static int putesc(char const *s, FILE *stream);
103 static void
104 _execute_command(struct header *hp, char *linebuf, size_t linesize)
106 /* The problem arises if there are rfc822 message attachments and the
107 * user uses `~:' to change the current file. TODO Unfortunately we
108 * TODO cannot simply keep a pointer to, or increment a reference count
109 * TODO of the current `file' (mailbox that is) object, because the
110 * TODO codebase doesn't deal with that at all; so, until some far
111 * TODO later time, copy the name of the path, and warn the user if it
112 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
113 * TODO copy the message attachments over to temporary files, but that
114 * TODO would require more changes so that the user still can recognize
115 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
116 char *mnbuf = NULL;
117 size_t mnlen = 0 /* silence CC */;
118 struct attachment *ap;
119 NYD_ENTER;
121 /* If the above todo is worked, remove or outsource to attachments.c! */
122 if ((ap = hp->h_attach) != NULL) do
123 if (ap->a_msgno) {
124 mnlen = strlen(mailname) +1;
125 mnbuf = ac_alloc(mnlen);
126 memcpy(mnbuf, mailname, mnlen);
127 break;
129 while ((ap = ap->a_flink) != NULL);
131 pstate &= ~PS_HOOK_MASK;
132 execute(linebuf, linesize);
134 if (mnbuf != NULL) {
135 if (strncmp(mnbuf, mailname, mnlen))
136 n_err(_("Mailbox changed: it is likely that existing "
137 "rfc822 attachments became invalid!\n"));
138 ac_free(mnbuf);
140 NYD_LEAVE;
143 static int
144 _include_file(char const *name, int *linecount, int *charcount,
145 bool_t doecho, bool_t indent)
147 FILE *fbuf;
148 char const *indb;
149 int ret = -1;
150 char *linebuf = NULL; /* TODO line pool */
151 size_t linesize = 0, indl, linelen, cnt;
152 NYD_ENTER;
154 if ((fbuf = Fopen(name, "r")) == NULL) {
155 n_perr(name, 0);
156 goto jleave;
159 if (!indent)
160 indb = NULL, indl = 0;
161 else {
162 if ((indb = ok_vlook(indentprefix)) == NULL)
163 indb = INDENT_DEFAULT;
164 indl = strlen(indb);
167 *linecount = *charcount = 0;
168 cnt = fsize(fbuf);
169 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
170 if (indl > 0 && fwrite(indb, sizeof *indb, indl, _coll_fp) != indl)
171 goto jleave;
172 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
173 goto jleave;
174 ++(*linecount);
175 (*charcount) += linelen + indl;
176 if ((options & OPT_INTERACTIVE) && doecho) {
177 if (indl > 0)
178 fwrite(indb, sizeof *indb, indl, stdout);
179 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
182 if (fflush(_coll_fp))
183 goto jleave;
184 if ((options & OPT_INTERACTIVE) && doecho)
185 fflush(stdout);
187 ret = 0;
188 jleave:
189 if (linebuf != NULL)
190 free(linebuf);
191 if (fbuf != NULL)
192 Fclose(fbuf);
193 NYD_LEAVE;
194 return ret;
197 static void
198 _collect_onpipe(int signo)
200 NYD_X; /* Signal handler */
201 UNUSED(signo);
202 siglongjmp(_coll_pipejmp, 1);
205 static void
206 insertcommand(FILE *fp, char const *cmd)
208 FILE *ibuf = NULL;
209 char const *cp;
210 int c;
211 NYD_ENTER;
213 cp = ok_vlook(SHELL);
214 if (cp == NULL)
215 cp = XSHELL;
217 if ((ibuf = Popen(cmd, "r", cp, NULL, 0)) != NULL) {
218 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
219 putc(c, fp);
220 Pclose(ibuf, TRU1);
221 } else
222 n_perr(cmd, 0);
223 NYD_LEAVE;
226 static void
227 print_collf(FILE *cf, struct header *hp)
229 char *lbuf = NULL; /* TODO line pool */
230 sighandler_type sigint;
231 FILE *volatile obuf = stdout;
232 struct attachment *ap;
233 char const *cp;
234 enum gfield gf;
235 size_t linesize = 0, linelen, cnt, cnt2;
236 NYD_ENTER;
238 fflush_rewind(cf);
239 cnt = cnt2 = fsize(cf);
241 sigint = safe_signal(SIGINT, SIG_IGN);
243 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
244 size_t l, m;
246 m = 4;
247 if (hp->h_to != NULL)
248 ++m;
249 if (hp->h_subject != NULL)
250 ++m;
251 if (hp->h_cc != NULL)
252 ++m;
253 if (hp->h_bcc != NULL)
254 ++m;
255 if (hp->h_attach != NULL)
256 ++m;
257 m += (hp->h_from != NULL || myaddrs(hp) != NULL);
258 m += (hp->h_sender != NULL || ok_vlook(sender) != NULL);
259 m += (hp->h_replyto != NULL || ok_vlook(replyto) != NULL);
260 m += (hp->h_organization != NULL || ok_vlook(ORGANIZATION) != NULL);
262 l = (*cp == '\0') ? screensize() : atoi(cp);
263 if (m > l)
264 goto jpager;
265 l -= m;
267 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
269 rewind(cf);
270 if (l < m) {
271 jpager:
272 cp = get_pager(NULL);
273 if (sigsetjmp(_coll_pipejmp, 1))
274 goto jendpipe;
275 obuf = Popen(cp, "w", NULL, NULL, 1);
276 if (obuf == NULL) {
277 n_perr(cp, 0);
278 obuf = stdout;
279 } else
280 safe_signal(SIGPIPE, &_collect_onpipe);
284 fprintf(obuf, _("-------\nMessage contains:\n"));
285 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
286 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
287 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
288 prout(lbuf, linelen, obuf);
289 if (hp->h_attach != NULL) {
290 fputs(_("-------\nAttachments:\n"), obuf);
291 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
292 if (ap->a_msgno)
293 fprintf(obuf, " - message %u\n", ap->a_msgno);
294 else {
295 /* TODO after MIME/send layer rewrite we *know*
296 * TODO the details of the attachment here,
297 * TODO so adjust this again, then */
298 char const *cs, *csi = "-> ";
300 if ((cs = ap->a_charset) == NULL &&
301 (csi = "<- ", cs = ap->a_input_charset) == NULL)
302 cs = charset_get_lc();
303 if ((cp = ap->a_content_type) == NULL)
304 cp = "?";
305 else if (ascncasecmp(cp, "text/", 5))
306 csi = "";
307 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
312 jendpipe:
313 if (obuf != stdout) {
314 safe_signal(SIGPIPE, SIG_IGN);
315 Pclose(obuf, TRU1);
316 safe_signal(SIGPIPE, dflpipe);
318 if (lbuf != NULL)
319 free(lbuf);
320 safe_signal(SIGINT, sigint);
321 NYD_LEAVE;
324 static int
325 exwrite(char const *name, FILE *fp, int f)
327 FILE *of;
328 int c, lc, rv = -1;
329 long cc;
330 NYD_ENTER;
332 if (f) {
333 printf("\"%s\" ", name);
334 fflush(stdout);
336 if ((of = Fopen(name, "a")) == NULL) {
337 n_perr(NULL, 0);
338 goto jleave;
341 lc = 0;
342 cc = 0;
343 while ((c = getc(fp)) != EOF) {
344 ++cc;
345 if (c == '\n')
346 ++lc;
347 putc(c, of);
348 if (ferror(of)) {
349 n_perr(name, 0);
350 Fclose(of);
351 goto jleave;
354 Fclose(of);
355 printf(_("%d/%ld\n"), lc, cc);
356 fflush(stdout);
357 rv = 0;
358 jleave:
359 NYD_LEAVE;
360 return rv;
363 static enum okay
364 makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err)
366 FILE *nf;
367 int c;
368 enum okay rv = STOP;
369 NYD_ENTER;
371 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
372 NULL) {
373 n_perr(_("temporary mail edit file"), 0);
374 goto jleave;
377 extract_header(fp, hp, checkaddr_err);
379 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
380 putc(c, nf);
381 if (fp != _coll_fp)
382 Fclose(_coll_fp);
383 Fclose(fp);
384 _coll_fp = nf;
385 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
386 goto jleave;
387 rv = OKAY;
388 jleave:
389 NYD_LEAVE;
390 return rv;
393 static void
394 mesedit(int c, struct header *hp)
396 bool_t saved;
397 sighandler_type sigint;
398 FILE *nf;
399 NYD_ENTER;
401 saved = ok_blook(add_file_recipients);
402 ok_bset(add_file_recipients, TRU1);
404 sigint = safe_signal(SIGINT, SIG_IGN);
405 nf = run_editor(_coll_fp, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
406 if (nf != NULL) {
407 if (hp) {
408 rewind(nf);
409 makeheader(nf, hp, NULL);
410 } else {
411 fseek(nf, 0L, SEEK_END);
412 Fclose(_coll_fp);
413 _coll_fp = nf;
416 safe_signal(SIGINT, sigint);
418 ok_bset(add_file_recipients, saved);
419 NYD_LEAVE;
422 static void
423 mespipe(char *cmd)
425 FILE *nf;
426 sighandler_type sigint;
427 char const *sh;
428 NYD_ENTER;
430 sigint = safe_signal(SIGINT, SIG_IGN);
432 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
433 NULL) {
434 n_perr(_("temporary mail edit file"), 0);
435 goto jout;
438 /* stdin = current message. stdout = new message */
439 if ((sh = ok_vlook(SHELL)) == NULL)
440 sh = XSHELL;
441 fflush(_coll_fp);
442 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL) < 0) {
443 Fclose(nf);
444 goto jout;
447 if (fsize(nf) == 0) {
448 n_err(_("No bytes from \"%s\" !?\n"), cmd);
449 Fclose(nf);
450 goto jout;
453 /* Take new files */
454 fseek(nf, 0L, SEEK_END);
455 Fclose(_coll_fp);
456 _coll_fp = nf;
457 jout:
458 safe_signal(SIGINT, sigint);
459 NYD_LEAVE;
462 static int
463 forward(char *ms, FILE *fp, int f)
465 int *msgvec, rv = 0;
466 struct ignoretab *ig;
467 char const *tabst;
468 enum sendaction action;
469 NYD_ENTER;
471 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
472 if (getmsglist(ms, msgvec, 0) < 0)
473 goto jleave;
474 if (*msgvec == 0) {
475 *msgvec = first(0, MMNORM);
476 if (*msgvec == 0) {
477 n_err(_("No appropriate messages\n"));
478 goto jleave;
480 msgvec[1] = 0;
483 if (f == 'f' || f == 'F' || f == 'u')
484 tabst = NULL;
485 else if ((tabst = ok_vlook(indentprefix)) == NULL)
486 tabst = INDENT_DEFAULT;
487 if (f == 'u' || f == 'U')
488 ig = allignore;
489 else
490 ig = upperchar(f) ? NULL : ignore;
491 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
493 printf(_("Interpolating:"));
494 for (; *msgvec != 0; ++msgvec) {
495 struct message *mp = message + *msgvec - 1;
497 touch(mp);
498 printf(" %d", *msgvec);
499 fflush(stdout);
500 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
501 n_perr(_("temporary mail file"), 0);
502 rv = -1;
503 break;
506 printf("\n");
507 jleave:
508 NYD_LEAVE;
509 return rv;
512 static void
513 collstop(int s)
515 sighandler_type old_action;
516 sigset_t nset;
517 NYD_X; /* Signal handler */
519 old_action = safe_signal(s, SIG_DFL);
521 sigemptyset(&nset);
522 sigaddset(&nset, s);
523 sigprocmask(SIG_UNBLOCK, &nset, NULL);
524 n_raise(s);
525 sigprocmask(SIG_BLOCK, &nset, NULL);
527 safe_signal(s, old_action);
528 if (_coll_jmp_p) {
529 _coll_jmp_p = 0;
530 _coll_hadintr = 0;
531 siglongjmp(_coll_jmp, 1);
535 static void
536 _collint(int s)
538 NYD_X; /* Signal handler */
540 /* the control flow is subtle, because we can be called from ~q */
541 if (_coll_hadintr == 0) {
542 if (ok_blook(ignore)) {
543 puts("@");
544 fflush(stdout);
545 clearerr(stdin);
546 } else
547 _coll_hadintr = 1;
548 siglongjmp(_coll_jmp, 1);
550 exit_status |= EXIT_SEND_ERROR;
551 if (s != 0)
552 savedeadletter(_coll_fp, 1);
553 /* Aborting message, no need to fflush() .. */
554 siglongjmp(_coll_abort, 1);
557 static void
558 collhup(int s)
560 NYD_X; /* Signal handler */
561 UNUSED(s);
563 savedeadletter(_coll_fp, 1);
564 /* Let's pretend nobody else wants to clean up, a true statement at
565 * this time */
566 exit(EXIT_ERR);
569 static int
570 putesc(char const *s, FILE *stream)
572 int n = 0, rv = -1;
573 NYD_ENTER;
575 while (s[0] != '\0') {
576 if (s[0] == '\\') {
577 if (s[1] == 't') {
578 if (putc('\t', stream) == EOF)
579 goto jleave;
580 ++n;
581 s += 2;
582 continue;
584 if (s[1] == 'n') {
585 if (putc('\n', stream) == EOF)
586 goto jleave;
587 ++n;
588 s += 2;
589 continue;
592 if (putc(s[0], stream) == EOF)
593 goto jleave;
594 ++n;
595 ++s;
597 if (putc('\n', stream) == EOF)
598 goto jleave;
599 rv = ++n;
600 jleave:
601 NYD_LEAVE;
602 return rv;
605 FL FILE *
606 collect(struct header *hp, int printheaders, struct message *mp,
607 char *quotefile, int doprefix, si8_t *checkaddr_err)
609 struct ignoretab *quoteig;
610 int lc, cc, c, t;
611 int volatile escape, getfields;
612 char *linebuf = NULL, *quote = NULL;
613 char const *cp;
614 size_t linesize = 0; /* TODO line pool */
615 long cnt;
616 enum sendaction action;
617 sigset_t oset, nset;
618 sighandler_type savedtop;
619 NYD_ENTER;
621 _coll_fp = NULL;
622 /* Start catching signals from here, but we're still die on interrupts
623 * until we're in the main loop */
624 sigemptyset(&nset);
625 sigaddset(&nset, SIGINT);
626 sigaddset(&nset, SIGHUP);
627 sigprocmask(SIG_BLOCK, &nset, &oset);
628 handlerpush(&_collint);
629 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
630 safe_signal(SIGINT, &_collint);
631 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
632 safe_signal(SIGHUP, collhup);
633 /* TODO We do a lot of redundant signal handling, especially
634 * TODO with the command line editor(s); try to merge this */
635 _coll_savetstp = safe_signal(SIGTSTP, collstop);
636 _coll_savettou = safe_signal(SIGTTOU, collstop);
637 _coll_savettin = safe_signal(SIGTTIN, collstop);
638 if (sigsetjmp(_coll_abort, 1))
639 goto jerr;
640 if (sigsetjmp(_coll_jmp, 1))
641 goto jerr;
642 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
644 ++noreset;
645 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
646 0600)) == NULL) {
647 n_perr(_("temporary mail file"), 0);
648 goto jerr;
651 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
652 goto jerr;
654 /* If we are going to prompt for a subject, refrain from printing a newline
655 * after the headers (since some people mind) */
656 getfields = 0;
657 if (!(options & OPT_t_FLAG)) {
658 t = GTO | GSUBJECT | GCC | GNL;
659 if (ok_blook(fullnames))
660 t |= GCOMMA;
662 if (options & OPT_INTERACTIVE) {
663 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
664 t &= ~GNL, getfields |= GSUBJECT;
666 if (hp->h_to == NULL)
667 t &= ~GNL, getfields |= GTO;
669 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
670 if (hp->h_bcc == NULL && ok_blook(askbcc))
671 t &= ~GNL, getfields |= GBCC;
672 if (hp->h_cc == NULL && ok_blook(askcc))
673 t &= ~GNL, getfields |= GCC;
677 if (printheaders && !ok_blook(editalong)) {
678 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
679 fflush(stdout);
683 /* Quote an original message */
684 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
685 quoteig = allignore;
686 action = SEND_QUOTE;
687 if (doprefix) {
688 quoteig = fwdignore;
689 if ((cp = ok_vlook(fwdheading)) == NULL)
690 cp = "-------- Original Message --------";
691 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
692 goto jerr;
693 } else if (!strcmp(quote, "noheading")) {
694 /*EMPTY*/;
695 } else if (!strcmp(quote, "headers")) {
696 quoteig = ignore;
697 } else if (!strcmp(quote, "allheaders")) {
698 quoteig = NULL;
699 action = SEND_QUOTE_ALL;
700 } else {
701 cp = hfield1("from", mp);
702 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
703 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
704 goto jerr;
705 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
706 goto jerr;
709 if (fflush(_coll_fp))
710 goto jerr;
711 if (doprefix)
712 cp = NULL;
713 else if ((cp = ok_vlook(indentprefix)) == NULL)
714 cp = INDENT_DEFAULT;
715 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
716 goto jerr;
719 /* Print what we have sofar also on the terminal (if useful) */
720 if ((options & OPT_INTERACTIVE) && !ok_blook(editalong)) {
721 rewind(_coll_fp);
722 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
723 putc(c, stdout);
724 if (fseek(_coll_fp, 0, SEEK_END))
725 goto jerr;
726 /* Ensure this is clean xxx not really necessary? */
727 fflush(stdout);
730 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
731 _coll_hadintr = 0;
733 if (!sigsetjmp(_coll_jmp, 1)) {
734 if (getfields)
735 grab_headers(hp, getfields, 1);
736 if (quotefile != NULL) {
737 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
738 goto jerr;
740 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
741 rewind(_coll_fp);
742 mesedit('e', hp);
743 goto jcont;
745 } else {
746 /* Come here for printing the after-signal message. Duplicate messages
747 * won't be printed because the write is aborted if we get a SIGTTOU */
748 if (_coll_hadintr) {
749 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
750 } else {
751 jcont:
752 printf(_("(continue)\n"));
753 fflush(stdout);
757 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
758 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
759 linebuf = srealloc(linebuf, linesize = LINESIZE);
760 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
761 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
762 goto jerr;
764 if (fflush(_coll_fp))
765 goto jerr;
766 goto jout;
769 /* The interactive collect loop.
770 * All commands which come here are forbidden when sourcing! */
771 assert(_coll_hadintr || !(pstate & PS_SOURCING));
772 for (;;) {
773 _coll_jmp_p = 1;
774 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
775 _coll_jmp_p = 0;
777 if (cnt < 0) {
778 assert(!(pstate & PS_SOURCING));
779 if (options & OPT_t_FLAG) {
780 fflush_rewind(_coll_fp);
781 /* It is important to set PS_t_FLAG before extract_header() *and*
782 * keep OPT_t_FLAG for the first parse of the message, too! */
783 pstate |= PS_t_FLAG;
784 if (makeheader(_coll_fp, hp, checkaddr_err) != OKAY)
785 goto jerr;
786 rewind(_coll_fp);
787 options &= ~OPT_t_FLAG;
788 continue;
789 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
790 printf(_("*ignoreeof* set, use \".\" to terminate letter\n"));
791 continue;
793 break;
796 _coll_hadintr = 0;
798 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
799 jputline:
800 /* TODO calls putline(), which *always* appends LF;
801 * TODO thus, STDIN with -t will ALWAYS end with LF,
802 * TODO even if no trailing LF and QP encoding.
803 * TODO when finally changed, update cc-test.sh */
804 if (putline(_coll_fp, linebuf, cnt) < 0)
805 goto jerr;
806 continue;
807 } else if (linebuf[0] == '.') {
808 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
809 break;
811 if (linebuf[0] != escape)
812 goto jputline;
814 tty_addhist(linebuf, TRU1);
816 c = linebuf[1];
817 switch (c) {
818 default:
819 /* On double escape, send a single one. Otherwise, it's an error */
820 if (c == escape) {
821 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
822 goto jerr;
823 else
824 break;
826 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
827 break;
828 case '!':
829 /* Shell escape, send the balance of line to sh -c */
830 c_shell(linebuf + 2);
831 break;
832 case ':':
833 /* FALLTHRU */
834 case '_':
835 /* Escape to command mode, but be nice! */
836 _execute_command(hp, linebuf + 2, cnt - 2);
837 goto jcont;
838 case '.':
839 /* Simulate end of file on input */
840 goto jout;
841 case 'x':
842 /* Same as 'q', but no *DEAD* saving */
843 /* FALLTHRU */
844 case 'q':
845 /* Force a quit, act like an interrupt had happened */
846 ++_coll_hadintr;
847 _collint((c == 'x') ? 0 : SIGINT);
848 exit(EXIT_ERR);
849 /*NOTREACHED*/
850 case 'h':
851 /* Grab a bunch of headers */
853 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
854 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
855 while (hp->h_to == NULL);
856 goto jcont;
857 case 'H':
858 /* Grab extra headers */
860 grab_headers(hp, GEXTRA, 0);
861 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
862 goto jcont;
863 case 't':
864 /* Add to the To list */
865 hp->h_to = cat(hp->h_to,
866 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
867 NULL));
868 break;
869 case 's':
870 /* Set the Subject list */
871 cp = linebuf + 2;
872 while (whitechar(*cp))
873 ++cp;
874 hp->h_subject = savestr(cp);
875 break;
876 #ifdef HAVE_DEBUG
877 case 'S':
878 c_sstats(NULL);
879 break;
880 #endif
881 case '@':
882 /* Edit the attachment list */
883 if (linebuf[2] != '\0')
884 append_attachments(&hp->h_attach, linebuf + 2);
885 else
886 edit_attachments(&hp->h_attach);
887 break;
888 case 'c':
889 /* Add to the CC list */
890 hp->h_cc = cat(hp->h_cc,
891 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
892 NULL));
893 break;
894 case 'b':
895 /* Add stuff to blind carbon copies list */
896 hp->h_bcc = cat(hp->h_bcc,
897 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
898 NULL));
899 break;
900 case 'd':
901 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
902 linebuf[linesize - 1] = '\0';
903 /*FALLTHRU*/
904 case 'R':
905 case 'r':
906 case '<':
907 /* Invoke a file: Search for the file name, then open it and copy the
908 * contents to _coll_fp */
909 cp = linebuf + 2;
910 while (whitechar(*cp))
911 ++cp;
912 if (*cp == '\0') {
913 n_err(_("Interpolate what file?\n"));
914 break;
916 if (*cp == '!') {
917 insertcommand(_coll_fp, cp + 1);
918 break;
920 if ((cp = file_expand(cp)) == NULL)
921 break;
922 if (is_dir(cp)) {
923 n_err(_("\"%s\": Directory\n"), cp);
924 break;
926 printf(_("\"%s\" "), cp);
927 fflush(stdout);
928 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
929 goto jerr;
930 printf(_("%d/%d\n"), lc, cc);
931 break;
932 case 'i':
933 /* Insert a variable into the file */
934 cp = linebuf + 2;
935 while (whitechar(*cp))
936 ++cp;
937 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
938 break;
939 if (putesc(cp, _coll_fp) < 0)
940 goto jerr;
941 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
942 goto jerr;
943 break;
944 case 'a':
945 case 'A':
946 /* Insert the contents of a signature variable */
947 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
948 if (cp != NULL && *cp != '\0') {
949 if (putesc(cp, _coll_fp) < 0)
950 goto jerr;
951 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
952 goto jerr;
954 break;
955 case 'w':
956 /* Write the message on a file */
957 cp = linebuf + 2;
958 while (blankchar(*cp))
959 ++cp;
960 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
961 n_err(_("Write what file!?\n"));
962 break;
964 rewind(_coll_fp);
965 if (exwrite(cp, _coll_fp, 1) < 0)
966 goto jerr;
967 break;
968 case 'm':
969 case 'M':
970 case 'f':
971 case 'F':
972 case 'u':
973 case 'U':
974 /* Interpolate the named messages, if we are in receiving mail mode.
975 * Does the standard list processing garbage. If ~f is given, we
976 * don't shift over */
977 if (forward(linebuf + 2, _coll_fp, c) < 0)
978 goto jerr;
979 goto jcont;
980 case 'p':
981 /* Print current state of the message without altering anything */
982 print_collf(_coll_fp, hp);
983 goto jcont;
984 case '|':
985 /* Pipe message through command. Collect output as new message */
986 rewind(_coll_fp);
987 mespipe(linebuf + 2);
988 goto jcont;
989 case 'v':
990 case 'e':
991 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
992 rewind(_coll_fp);
993 mesedit(c, ok_blook(editheaders) ? hp : NULL);
994 goto jcont;
995 case '?':
996 /* Last the lengthy help string. (Very ugly, but take care for
997 * compiler supported string lengths :() */
998 puts(_(
999 "-------------------- ~ ESCAPES ----------------------------\n"
1000 "~~ Quote a single tilde\n"
1001 "~@ [file ...] Edit attachment list\n"
1002 "~b users Add users to \"blind\" Bcc: list\n"
1003 "~c users Add users to Cc: list\n"
1004 "~d Read in dead.letter\n"
1005 "~e Edit the message buffer\n"
1006 "~F messages Read in messages including all headers, don't indent lines\n"
1007 "~f messages Like ~F, but honour the `ignore' / `retain' configuration\n"
1008 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"));
1009 puts(_(
1010 "~R file Read in a file, indent lines\n"
1011 "~r file Read in a file\n"
1012 "~p Print the message buffer\n"
1013 "~q Abort message composition and save text to DEAD\n"
1014 "~M messages Read in messages, keep all header lines, indent lines\n"
1015 "~m messages Like ~M, but honour the `ignore' / `retain' configuration\n"
1016 "~s subject Set Subject:\n"
1017 "~t users Add users to To: list\n"));
1018 puts(_(
1019 "~U messages Read in message(s) without any headers, indent lines\n"
1020 "~u messages Read in message(s) without any headers\n"
1021 "~v Invoke alternate editor ($VISUAL) on message\n"
1022 "~w file Write message onto file\n"
1023 "~x Abort message composition and discard message\n"
1024 "~!command Invoke the shell\n"
1025 "~:command Execute a regular command\n"
1026 "-----------------------------------------------------------\n"));
1027 break;
1031 jout:
1032 if (_coll_fp != NULL) {
1033 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
1034 if (putesc(cp, _coll_fp) < 0)
1035 goto jerr;
1036 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1037 goto jerr;
1039 rewind(_coll_fp);
1041 if (linebuf != NULL)
1042 free(linebuf);
1043 handlerpop();
1044 --noreset;
1045 sigemptyset(&nset);
1046 sigaddset(&nset, SIGINT);
1047 sigaddset(&nset, SIGHUP);
1048 sigprocmask(SIG_BLOCK, &nset, NULL);
1049 safe_signal(SIGINT, _coll_saveint);
1050 safe_signal(SIGHUP, _coll_savehup);
1051 safe_signal(SIGTSTP, _coll_savetstp);
1052 safe_signal(SIGTTOU, _coll_savettou);
1053 safe_signal(SIGTTIN, _coll_savettin);
1054 sigprocmask(SIG_SETMASK, &oset, NULL);
1055 NYD_LEAVE;
1056 return _coll_fp;
1058 jerr:
1059 if (_coll_fp != NULL) {
1060 Fclose(_coll_fp);
1061 _coll_fp = NULL;
1063 goto jout;
1066 FL void
1067 savedeadletter(FILE *fp, int fflush_rewind_first)
1069 char const *cp;
1070 int c;
1071 FILE *dbuf;
1072 ul_i lines, bytes;
1073 NYD_ENTER;
1075 if ((options & OPT_DEBUG) || !ok_blook(save))
1076 goto jleave;
1078 if (fflush_rewind_first) {
1079 fflush(fp);
1080 rewind(fp);
1082 if (fsize(fp) == 0)
1083 goto jleave;
1085 cp = getdeadletter();
1086 c = umask(077);
1087 dbuf = Fopen(cp, "a");
1088 umask(c);
1089 if (dbuf == NULL)
1090 goto jleave;
1092 really_rewind(fp);
1094 printf("\"%s\" ", cp);
1095 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1096 putc(c, dbuf);
1097 if (c == '\n')
1098 ++lines;
1100 printf("%lu/%lu\n", lines, bytes);
1102 Fclose(dbuf);
1103 rewind(fp);
1104 jleave:
1105 NYD_LEAVE;
1108 /* s-it-mode */