WANT_AMALGAMATION will henceforth work through main.c
[s-mailx.git] / collect.c
blob218f2fba6477c1597ea281b6f59b9f8dfbd7363f
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE collect
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 /* The following hookiness with global variables is so that on receipt of an
47 * interrupt signal, the partial message can be salted away on *DEAD* */
49 static sighandler_type _coll_saveint; /* Previous SIGINT value */
50 static sighandler_type _coll_savehup; /* Previous SIGHUP value */
51 static sighandler_type _coll_savetstp; /* Previous SIGTSTP value */
52 static sighandler_type _coll_savettou; /* Previous SIGTTOU value */
53 static sighandler_type _coll_savettin; /* Previous SIGTTIN value */
54 static FILE *_coll_fp; /* File for saving away */
55 static int volatile _coll_hadintr; /* Have seen one SIGINT so far */
56 static sigjmp_buf _coll_jmp; /* To get back to work */
57 static int _coll_jmp_p; /* whether to long jump */
58 static sigjmp_buf _coll_abort; /* To end collection with error */
59 static sigjmp_buf _coll_pipejmp; /* On broken pipe */
61 /* Handle `~:', `~_' */
62 static void _execute_command(struct header *hp, char *linebuf,
63 size_t linesize);
65 /* If *interactive* is set and *doecho* is, too, also dump to *stdout* */
66 static int _include_file(char const *name, int *linecount,
67 int *charcount, bool_t doecho, bool_t indent);
69 static void _collect_onpipe(int signo);
71 /* Execute cmd and insert its standard output into fp */
72 static void insertcommand(FILE *fp, char const *cmd);
74 /* ~p command */
75 static void print_collf(FILE *collf, struct header *hp);
77 /* Write a file, ex-like if f set */
78 static int exwrite(char const *name, FILE *fp, int f);
80 static enum okay makeheader(FILE *fp, struct header *hp);
82 /* Edit the message being collected on fp. On return, make the edit file the
83 * new temp file */
84 static void mesedit(int c, struct header *hp);
86 /* Pipe the message through the command. Old message is on stdin of command,
87 * new message collected from stdout. Shell must return 0 to accept new msg */
88 static void mespipe(char *cmd);
90 /* Interpolate the named messages into the current message, possibly doing
91 * indent stuff. The flag argument is one of the tilde escapes: [mMfFuU].
92 * Return a count of the number of characters now in the message, or -1 if an
93 * error is encountered writing the message temporary */
94 static int forward(char *ms, FILE *fp, int f);
96 /* Print (continue) when continued after ^Z */
97 static void collstop(int s);
99 /* On interrupt, come here to save the partial message in ~/dead.letter.
100 * Then jump out of the collection loop */
101 static void _collint(int s);
103 static void collhup(int s);
105 static int putesc(char const *s, FILE *stream);
107 static void
108 _execute_command(struct header *hp, char *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 ((ap = hp->h_attach) != NULL) do
127 if (ap->a_msgno) {
128 mnlen = strlen(mailname) +1;
129 mnbuf = ac_alloc(mnlen);
130 memcpy(mnbuf, mailname, mnlen);
131 break;
133 while ((ap = ap->a_flink) != NULL);
135 pstate &= ~PS_HOOK_MASK;
136 execute(linebuf, TRU1, linesize);
138 if (mnbuf != NULL) {
139 if (strncmp(mnbuf, mailname, mnlen))
140 n_err(_("Mailbox changed: it is likely that existing "
141 "rfc822 attachments became invalid!\n"));
142 ac_free(mnbuf);
144 NYD_LEAVE;
147 static int
148 _include_file(char const *name, int *linecount, int *charcount,
149 bool_t doecho, bool_t indent)
151 FILE *fbuf;
152 char const *indb;
153 int ret = -1;
154 char *linebuf = NULL; /* TODO line pool */
155 size_t linesize = 0, indl, linelen, cnt;
156 NYD_ENTER;
158 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)
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 (IS_TTY_SESSION() && (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);
264 m += (hp->h_organization != NULL || ok_vlook(ORGANIZATION) != NULL);
266 l = (*cp == '\0') ? screensize() : atoi(cp);
267 if (m > l)
268 goto jpager;
269 l -= m;
271 for (m = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0); ++m)
273 rewind(cf);
274 if (l < m) {
275 jpager:
276 cp = get_pager(NULL);
277 if (sigsetjmp(_coll_pipejmp, 1))
278 goto jendpipe;
279 obuf = Popen(cp, "w", NULL, NULL, 1);
280 if (obuf == NULL) {
281 n_perr(cp, 0);
282 obuf = stdout;
283 } else
284 safe_signal(SIGPIPE, &_collect_onpipe);
288 fprintf(obuf, _("-------\nMessage contains:\n"));
289 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA;
290 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
291 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
292 prout(lbuf, linelen, obuf);
293 if (hp->h_attach != NULL) {
294 fputs(_("-------\nAttachments:\n"), obuf);
295 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
296 if (ap->a_msgno)
297 fprintf(obuf, " - message %u\n", ap->a_msgno);
298 else {
299 /* TODO after MIME/send layer rewrite we *know*
300 * TODO the details of the attachment here,
301 * TODO so adjust this again, then */
302 char const *cs, *csi = "-> ";
304 if ((cs = ap->a_charset) == NULL &&
305 (csi = "<- ", cs = ap->a_input_charset) == NULL)
306 cs = charset_get_lc();
307 if ((cp = ap->a_content_type) == NULL)
308 cp = "?";
309 else if (ascncasecmp(cp, "text/", 5))
310 csi = "";
311 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
316 jendpipe:
317 if (obuf != stdout) {
318 safe_signal(SIGPIPE, SIG_IGN);
319 Pclose(obuf, TRU1);
320 safe_signal(SIGPIPE, dflpipe);
322 if (lbuf != NULL)
323 free(lbuf);
324 safe_signal(SIGINT, sigint);
325 NYD_LEAVE;
328 static int
329 exwrite(char const *name, FILE *fp, int f)
331 FILE *of;
332 int c, lc, rv = -1;
333 long cc;
334 NYD_ENTER;
336 if (f) {
337 printf("\"%s\" ", name);
338 fflush(stdout);
340 if ((of = Fopen(name, "a")) == NULL) {
341 n_perr(NULL, 0);
342 goto jleave;
345 lc = 0;
346 cc = 0;
347 while ((c = getc(fp)) != EOF) {
348 ++cc;
349 if (c == '\n')
350 ++lc;
351 putc(c, of);
352 if (ferror(of)) {
353 n_perr(name, 0);
354 Fclose(of);
355 goto jleave;
358 Fclose(of);
359 printf(_("%d/%ld\n"), lc, cc);
360 fflush(stdout);
361 rv = 0;
362 jleave:
363 NYD_LEAVE;
364 return rv;
367 static enum okay
368 makeheader(FILE *fp, struct header *hp)
370 FILE *nf;
371 int c;
372 enum okay rv = STOP;
373 NYD_ENTER;
375 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
376 NULL) {
377 n_perr(_("temporary mail edit file"), 0);
378 goto jleave;
381 extract_header(fp, hp);
382 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
383 putc(c, nf);
384 if (fp != _coll_fp)
385 Fclose(_coll_fp);
386 Fclose(fp);
387 _coll_fp = nf;
388 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
389 goto jleave;
390 rv = OKAY;
391 jleave:
392 NYD_LEAVE;
393 return rv;
396 static void
397 mesedit(int c, struct header *hp)
399 bool_t saved;
400 sighandler_type sigint;
401 FILE *nf;
402 NYD_ENTER;
404 saved = ok_blook(add_file_recipients);
405 ok_bset(add_file_recipients, TRU1);
407 sigint = safe_signal(SIGINT, SIG_IGN);
408 nf = run_editor(_coll_fp, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
409 if (nf != NULL) {
410 if (hp) {
411 rewind(nf);
412 makeheader(nf, hp);
413 } else {
414 fseek(nf, 0L, SEEK_END);
415 Fclose(_coll_fp);
416 _coll_fp = nf;
419 safe_signal(SIGINT, sigint);
421 ok_bset(add_file_recipients, saved);
422 NYD_LEAVE;
425 static void
426 mespipe(char *cmd)
428 FILE *nf;
429 sighandler_type sigint;
430 char const *sh;
431 NYD_ENTER;
433 sigint = safe_signal(SIGINT, SIG_IGN);
435 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
436 NULL) {
437 n_perr(_("temporary mail edit file"), 0);
438 goto jout;
441 /* stdin = current message. stdout = new message */
442 if ((sh = ok_vlook(SHELL)) == NULL)
443 sh = XSHELL;
444 fflush(_coll_fp);
445 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL) < 0) {
446 Fclose(nf);
447 goto jout;
450 if (fsize(nf) == 0) {
451 n_err(_("No bytes from \"%s\" !?\n"), cmd);
452 Fclose(nf);
453 goto jout;
456 /* Take new files */
457 fseek(nf, 0L, SEEK_END);
458 Fclose(_coll_fp);
459 _coll_fp = nf;
460 jout:
461 safe_signal(SIGINT, sigint);
462 NYD_LEAVE;
465 static int
466 forward(char *ms, FILE *fp, int f)
468 int *msgvec, rv = 0;
469 struct ignoretab *ig;
470 char const *tabst;
471 enum sendaction action;
472 NYD_ENTER;
474 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
475 if (getmsglist(ms, msgvec, 0) < 0)
476 goto jleave;
477 if (*msgvec == 0) {
478 *msgvec = first(0, MMNORM);
479 if (*msgvec == 0) {
480 n_err(_("No appropriate messages\n"));
481 goto jleave;
483 msgvec[1] = 0;
486 if (f == 'f' || f == 'F' || f == 'u')
487 tabst = NULL;
488 else if ((tabst = ok_vlook(indentprefix)) == NULL)
489 tabst = INDENT_DEFAULT;
490 if (f == 'u' || f == 'U')
491 ig = allignore;
492 else
493 ig = upperchar(f) ? NULL : ignore;
494 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
496 printf(_("Interpolating:"));
497 for (; *msgvec != 0; ++msgvec) {
498 struct message *mp = message + *msgvec - 1;
500 touch(mp);
501 printf(" %d", *msgvec);
502 fflush(stdout);
503 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
504 n_perr(_("temporary mail file"), 0);
505 rv = -1;
506 break;
509 printf("\n");
510 jleave:
511 NYD_LEAVE;
512 return rv;
515 static void
516 collstop(int s)
518 sighandler_type old_action;
519 sigset_t nset;
520 NYD_X; /* Signal handler */
522 old_action = safe_signal(s, SIG_DFL);
524 sigemptyset(&nset);
525 sigaddset(&nset, s);
526 sigprocmask(SIG_UNBLOCK, &nset, NULL);
527 n_raise(s);
528 sigprocmask(SIG_BLOCK, &nset, NULL);
530 safe_signal(s, old_action);
531 if (_coll_jmp_p) {
532 _coll_jmp_p = 0;
533 _coll_hadintr = 0;
534 siglongjmp(_coll_jmp, 1);
538 static void
539 _collint(int s)
541 NYD_X; /* Signal handler */
543 /* the control flow is subtle, because we can be called from ~q */
544 if (_coll_hadintr == 0) {
545 if (ok_blook(ignore)) {
546 puts("@");
547 fflush(stdout);
548 clearerr(stdin);
549 } else
550 _coll_hadintr = 1;
551 siglongjmp(_coll_jmp, 1);
553 exit_status |= EXIT_SEND_ERROR;
554 if (ok_blook(save) && s != 0)
555 savedeadletter(_coll_fp, 1);
556 /* Aborting message, no need to fflush() .. */
557 siglongjmp(_coll_abort, 1);
560 static void
561 collhup(int s)
563 NYD_X; /* Signal handler */
564 UNUSED(s);
566 savedeadletter(_coll_fp, 1);
567 /* Let's pretend nobody else wants to clean up, a true statement at
568 * this time */
569 exit(EXIT_ERR);
572 static int
573 putesc(char const *s, FILE *stream)
575 int n = 0, rv = -1;
576 NYD_ENTER;
578 while (s[0] != '\0') {
579 if (s[0] == '\\') {
580 if (s[1] == 't') {
581 if (putc('\t', stream) == EOF)
582 goto jleave;
583 ++n;
584 s += 2;
585 continue;
587 if (s[1] == 'n') {
588 if (putc('\n', stream) == EOF)
589 goto jleave;
590 ++n;
591 s += 2;
592 continue;
595 if (putc(s[0], stream) == EOF)
596 goto jleave;
597 ++n;
598 ++s;
600 if (putc('\n', stream) == EOF)
601 goto jleave;
602 rv = ++n;
603 jleave:
604 NYD_LEAVE;
605 return rv;
608 FL FILE *
609 collect(struct header *hp, int printheaders, struct message *mp,
610 char *quotefile, int doprefix)
612 struct ignoretab *quoteig;
613 int lc, cc, c, t;
614 int volatile escape, getfields;
615 char *linebuf = NULL, *quote = NULL;
616 char const *cp;
617 size_t linesize = 0; /* TODO line pool */
618 long cnt;
619 enum sendaction action;
620 sigset_t oset, nset;
621 sighandler_type savedtop;
622 NYD_ENTER;
624 _coll_fp = NULL;
625 /* Start catching signals from here, but we're still die on interrupts
626 * until we're in the main loop */
627 sigemptyset(&nset);
628 sigaddset(&nset, SIGINT);
629 sigaddset(&nset, SIGHUP);
630 sigprocmask(SIG_BLOCK, &nset, &oset);
631 handlerpush(&_collint);
632 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
633 safe_signal(SIGINT, &_collint);
634 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
635 safe_signal(SIGHUP, collhup);
636 /* TODO We do a lot of redundant signal handling, especially
637 * TODO with the command line editor(s); try to merge this */
638 _coll_savetstp = safe_signal(SIGTSTP, collstop);
639 _coll_savettou = safe_signal(SIGTTOU, collstop);
640 _coll_savettin = safe_signal(SIGTTIN, collstop);
641 if (sigsetjmp(_coll_abort, 1))
642 goto jerr;
643 if (sigsetjmp(_coll_jmp, 1))
644 goto jerr;
645 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
647 ++noreset;
648 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
649 0600)) == NULL) {
650 n_perr(_("temporary mail file"), 0);
651 goto jerr;
654 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
655 goto jerr;
657 /* If we are going to prompt for a subject, refrain from printing a newline
658 * after the headers (since some people mind) */
659 getfields = 0;
660 if (!(options & OPT_t_FLAG)) {
661 t = GTO | GSUBJECT | GCC | GNL;
662 if (ok_blook(fullnames))
663 t |= GCOMMA;
665 if (options & OPT_INTERACTIVE) {
666 if (hp->h_subject == NULL && (ok_blook(ask) || ok_blook(asksub)))
667 t &= ~GNL, getfields |= GSUBJECT;
669 if (hp->h_to == NULL)
670 t &= ~GNL, getfields |= GTO;
672 if (!ok_blook(bsdcompat) && !ok_blook(askatend)) {
673 if (hp->h_bcc == NULL && ok_blook(askbcc))
674 t &= ~GNL, getfields |= GBCC;
675 if (hp->h_cc == NULL && ok_blook(askcc))
676 t &= ~GNL, getfields |= GCC;
680 if (printheaders && !ok_blook(editalong)) {
681 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
682 fflush(stdout);
686 /* Quote an original message */
687 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
688 quoteig = allignore;
689 action = SEND_QUOTE;
690 if (doprefix) {
691 quoteig = fwdignore;
692 if ((cp = ok_vlook(fwdheading)) == NULL)
693 cp = "-------- Original Message --------";
694 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
695 goto jerr;
696 } else if (!strcmp(quote, "noheading")) {
697 /*EMPTY*/;
698 } else if (!strcmp(quote, "headers")) {
699 quoteig = ignore;
700 } else if (!strcmp(quote, "allheaders")) {
701 quoteig = NULL;
702 action = SEND_QUOTE_ALL;
703 } else {
704 cp = hfield1("from", mp);
705 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
706 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE) < 0)
707 goto jerr;
708 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
709 goto jerr;
712 if (fflush(_coll_fp))
713 goto jerr;
714 if (doprefix)
715 cp = NULL;
716 else if ((cp = ok_vlook(indentprefix)) == NULL)
717 cp = INDENT_DEFAULT;
718 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
719 goto jerr;
722 /* Print what we have sofar also on the terminal (if useful) */
723 if ((options & OPT_INTERACTIVE) && !ok_blook(editalong)) {
724 rewind(_coll_fp);
725 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
726 putc(c, stdout);
727 if (fseek(_coll_fp, 0, SEEK_END))
728 goto jerr;
729 /* Ensure this is clean xxx not really necessary? */
730 fflush(stdout);
733 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
734 _coll_hadintr = 0;
736 if (!sigsetjmp(_coll_jmp, 1)) {
737 if (getfields)
738 grab_headers(hp, getfields, 1);
739 if (quotefile != NULL) {
740 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
741 goto jerr;
743 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
744 rewind(_coll_fp);
745 mesedit('e', hp);
746 goto jcont;
748 } else {
749 /* Come here for printing the after-signal message. Duplicate messages
750 * won't be printed because the write is aborted if we get a SIGTTOU */
751 if (_coll_hadintr) {
752 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
753 } else {
754 jcont:
755 printf(_("(continue)\n"));
756 fflush(stdout);
760 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
761 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
762 linebuf = srealloc(linebuf, linesize = LINESIZE);
763 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
764 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
765 goto jerr;
767 if (fflush(_coll_fp))
768 goto jerr;
769 goto jout;
772 /* The interactive collect loop.
773 * All commands which come here are forbidden when sourcing! */
774 assert(_coll_hadintr || !(pstate & PS_SOURCING));
775 for (;;) {
776 _coll_jmp_p = 1;
777 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
778 _coll_jmp_p = 0;
780 if (cnt < 0) {
781 assert(!(pstate & PS_SOURCING));
782 if (options & OPT_t_FLAG) {
783 fflush_rewind(_coll_fp);
784 pstate |= PS_t_FLAG;
785 if (makeheader(_coll_fp, hp) != OKAY)
786 goto jerr;
787 rewind(_coll_fp);
788 options &= ~OPT_t_FLAG;
789 continue;
790 } else if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
791 printf(_("*ignoreeof* set, use \".\" to terminate letter\n"));
792 continue;
794 break;
797 _coll_hadintr = 0;
799 if (cnt == 0 || !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
800 jputline:
801 /* TODO calls putline(), which *always* appends LF;
802 * TODO thus, STDIN with -t will ALWAYS end with LF,
803 * TODO even if no trailing LF and QP encoding.
804 * TODO when finally changed, update cc-test.sh */
805 if (putline(_coll_fp, linebuf, cnt) < 0)
806 goto jerr;
807 continue;
808 } else if (linebuf[0] == '.') {
809 if (linebuf[1] == '\0' && (ok_blook(dot) || ok_blook(ignoreeof)))
810 break;
812 if (linebuf[0] != escape)
813 goto jputline;
815 tty_addhist(linebuf, TRU1);
817 c = linebuf[1];
818 switch (c) {
819 default:
820 /* On double escape, send a single one. Otherwise, it's an error */
821 if (c == escape) {
822 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
823 goto jerr;
824 else
825 break;
827 n_err(_("Unknown tilde escape: ~%c\n"), asciichar(c) ? c : '?');
828 break;
829 case '!':
830 /* Shell escape, send the balance of line to sh -c */
831 c_shell(linebuf + 2);
832 break;
833 case ':':
834 /* FALLTHRU */
835 case '_':
836 /* Escape to command mode, but be nice! */
837 _execute_command(hp, linebuf + 2, cnt - 2);
838 goto jcont;
839 case '.':
840 /* Simulate end of file on input */
841 goto jout;
842 case 'x':
843 /* Same as 'q', but no *DEAD* saving */
844 /* FALLTHRU */
845 case 'q':
846 /* Force a quit, act like an interrupt had happened */
847 ++_coll_hadintr;
848 _collint((c == 'x') ? 0 : SIGINT);
849 exit(EXIT_ERR);
850 /*NOTREACHED*/
851 case 'h':
852 /* Grab a bunch of headers */
854 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
855 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
856 while (hp->h_to == NULL);
857 goto jcont;
858 case 'H':
859 /* Grab extra headers */
861 grab_headers(hp, GEXTRA, 0);
862 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
863 goto jcont;
864 case 't':
865 /* Add to the To list */
866 hp->h_to = cat(hp->h_to,
867 checkaddrs(lextract(linebuf + 2, GTO | GFULL), EACM_NORMAL,
868 NULL));
869 break;
870 case 's':
871 /* Set the Subject list */
872 cp = linebuf + 2;
873 while (whitechar(*cp))
874 ++cp;
875 hp->h_subject = savestr(cp);
876 break;
877 #ifdef HAVE_DEBUG
878 case 'S':
879 c_sstats(NULL);
880 break;
881 #endif
882 case '@':
883 /* Edit the attachment list */
884 if (linebuf[2] != '\0')
885 append_attachments(&hp->h_attach, linebuf + 2);
886 else
887 edit_attachments(&hp->h_attach);
888 break;
889 case 'c':
890 /* Add to the CC list */
891 hp->h_cc = cat(hp->h_cc,
892 checkaddrs(lextract(linebuf + 2, GCC | GFULL), EACM_NORMAL,
893 NULL));
894 break;
895 case 'b':
896 /* Add stuff to blind carbon copies list */
897 hp->h_bcc = cat(hp->h_bcc,
898 checkaddrs(lextract(linebuf + 2, GBCC | GFULL), EACM_NORMAL,
899 NULL));
900 break;
901 case 'd':
902 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
903 linebuf[linesize - 1] = '\0';
904 /*FALLTHRU*/
905 case 'R':
906 case 'r':
907 case '<':
908 /* Invoke a file: Search for the file name, then open it and copy the
909 * contents to _coll_fp */
910 cp = linebuf + 2;
911 while (whitechar(*cp))
912 ++cp;
913 if (*cp == '\0') {
914 n_err(_("Interpolate what file?\n"));
915 break;
917 if (*cp == '!') {
918 insertcommand(_coll_fp, cp + 1);
919 break;
921 if ((cp = file_expand(cp)) == NULL)
922 break;
923 if (is_dir(cp)) {
924 n_err(_("\"%s\": Directory\n"), cp);
925 break;
927 printf(_("\"%s\" "), cp);
928 fflush(stdout);
929 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
930 goto jerr;
931 printf(_("%d/%d\n"), lc, cc);
932 break;
933 case 'i':
934 /* Insert a variable into the file */
935 cp = linebuf + 2;
936 while (whitechar(*cp))
937 ++cp;
938 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
939 break;
940 if (putesc(cp, _coll_fp) < 0)
941 goto jerr;
942 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
943 goto jerr;
944 break;
945 case 'a':
946 case 'A':
947 /* Insert the contents of a signature variable */
948 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
949 if (cp != NULL && *cp != '\0') {
950 if (putesc(cp, _coll_fp) < 0)
951 goto jerr;
952 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
953 goto jerr;
955 break;
956 case 'w':
957 /* Write the message on a file */
958 cp = linebuf + 2;
959 while (blankchar(*cp))
960 ++cp;
961 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
962 n_err(_("Write what file!?\n"));
963 break;
965 rewind(_coll_fp);
966 if (exwrite(cp, _coll_fp, 1) < 0)
967 goto jerr;
968 break;
969 case 'm':
970 case 'M':
971 case 'f':
972 case 'F':
973 case 'u':
974 case 'U':
975 /* Interpolate the named messages, if we are in receiving mail mode.
976 * Does the standard list processing garbage. If ~f is given, we
977 * don't shift over */
978 if (forward(linebuf + 2, _coll_fp, c) < 0)
979 goto jerr;
980 goto jcont;
981 case 'p':
982 /* Print current state of the message without altering anything */
983 print_collf(_coll_fp, hp);
984 goto jcont;
985 case '|':
986 /* Pipe message through command. Collect output as new message */
987 rewind(_coll_fp);
988 mespipe(linebuf + 2);
989 goto jcont;
990 case 'v':
991 case 'e':
992 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
993 rewind(_coll_fp);
994 mesedit(c, ok_blook(editheaders) ? hp : NULL);
995 goto jcont;
996 case '?':
997 /* Last the lengthy help string. (Very ugly, but take care for
998 * compiler supported string lengths :() */
999 puts(_(
1000 "-------------------- ~ ESCAPES ----------------------------\n"
1001 "~~ Quote a single tilde\n"
1002 "~@ [file ...] Edit attachment list\n"
1003 "~b users Add users to \"blind\" Bcc: list\n"
1004 "~c users Add users to Cc: list\n"
1005 "~d Read in dead.letter\n"
1006 "~e Edit the message buffer\n"
1007 "~F messages Read in messages including all headers, don't indent lines\n"
1008 "~f messages Like ~F, but honour the `ignore' / `retain' configuration\n"
1009 "~h Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"));
1010 puts(_(
1011 "~R file Read in a file, indent lines\n"
1012 "~r file Read in a file\n"
1013 "~p Print the message buffer\n"
1014 "~q Abort message composition and save text to DEAD\n"
1015 "~M messages Read in messages, keep all header lines, indent lines\n"
1016 "~m messages Like ~M, but honour the `ignore' / `retain' configuration\n"
1017 "~s subject Set Subject:\n"
1018 "~t users Add users to To: list\n"));
1019 puts(_(
1020 "~U messages Read in message(s) without any headers, indent lines\n"
1021 "~u messages Read in message(s) without any headers\n"
1022 "~v Invoke alternate editor ($VISUAL) on message\n"
1023 "~w file Write message onto file\n"
1024 "~x Abort message composition and discard message\n"
1025 "~!command Invoke the shell\n"
1026 "~:command Execute a regular command\n"
1027 "-----------------------------------------------------------\n"));
1028 break;
1032 jout:
1033 if (_coll_fp != NULL) {
1034 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
1035 if (putesc(cp, _coll_fp) < 0)
1036 goto jerr;
1037 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1038 goto jerr;
1040 rewind(_coll_fp);
1042 if (linebuf != NULL)
1043 free(linebuf);
1044 handlerpop();
1045 --noreset;
1046 sigemptyset(&nset);
1047 sigaddset(&nset, SIGINT);
1048 sigaddset(&nset, SIGHUP);
1049 sigprocmask(SIG_BLOCK, &nset, NULL);
1050 safe_signal(SIGINT, _coll_saveint);
1051 safe_signal(SIGHUP, _coll_savehup);
1052 safe_signal(SIGTSTP, _coll_savetstp);
1053 safe_signal(SIGTTOU, _coll_savettou);
1054 safe_signal(SIGTTIN, _coll_savettin);
1055 sigprocmask(SIG_SETMASK, &oset, NULL);
1056 NYD_LEAVE;
1057 return _coll_fp;
1059 jerr:
1060 if (_coll_fp != NULL) {
1061 Fclose(_coll_fp);
1062 _coll_fp = NULL;
1064 goto jout;
1067 FL void
1068 savedeadletter(FILE *fp, int fflush_rewind_first)
1070 char const *cp;
1071 int c;
1072 FILE *dbuf;
1073 ul_i lines, bytes;
1074 NYD_ENTER;
1076 if (fflush_rewind_first) {
1077 fflush(fp);
1078 rewind(fp);
1080 if (fsize(fp) == 0)
1081 goto jleave;
1083 cp = getdeadletter();
1084 c = umask(077);
1085 dbuf = Fopen(cp, "a");
1086 umask(c);
1087 if (dbuf == NULL)
1088 goto jleave;
1090 really_rewind(fp);
1092 printf("\"%s\" ", cp);
1093 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1094 putc(c, dbuf);
1095 if (c == '\n')
1096 ++lines;
1098 printf("%lu/%lu\n", lines, bytes);
1100 Fclose(dbuf);
1101 rewind(fp);
1102 jleave:
1103 NYD_LEAVE;
1106 /* s-it-mode */