run_editor(): also compare size when deciding "has changed"
[s-mailx.git] / collect.c
blob0f1551779dcaba582c167693d6acd413f4b151cd
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 - 2014 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.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 /* The following hookiness with global variables is so that on receipt of an
45 * interrupt signal, the partial message can be salted away on *DEAD* */
47 static sighandler_type _coll_saveint; /* Previous SIGINT value */
48 static sighandler_type _coll_savehup; /* Previous SIGHUP value */
49 static sighandler_type _coll_savetstp; /* Previous SIGTSTP value */
50 static sighandler_type _coll_savettou; /* Previous SIGTTOU value */
51 static sighandler_type _coll_savettin; /* Previous SIGTTIN value */
52 static FILE *_coll_fp; /* File for saving away */
53 static int volatile _coll_hadintr; /* Have seen one SIGINT so far */
54 static sigjmp_buf _coll_jmp; /* To get back to work */
55 static int _coll_jmp_p; /* whether to long jump */
56 static sigjmp_buf _coll_abort; /* To end collection with error */
57 static sigjmp_buf _coll_pipejmp; /* On broken pipe */
59 /* Handle `~:', `~_' */
60 static void _execute_command(struct header *hp, char *linebuf,
61 size_t linesize);
63 /* If *interactive* is set and *doecho* is, too, also dump to *stdout* */
64 static int _include_file(char const *name, int *linecount,
65 int *charcount, bool_t doecho, bool_t indent);
67 static void _collect_onpipe(int signo);
69 /* Execute cmd and insert its standard output into fp */
70 static void insertcommand(FILE *fp, char const *cmd);
72 /* ~p command */
73 static void print_collf(FILE *collf, struct header *hp);
75 /* Write a file, ex-like if f set */
76 static int exwrite(char const *name, FILE *fp, int f);
78 static enum okay makeheader(FILE *fp, struct header *hp);
80 /* Edit the message being collected on fp. On return, make the edit file the
81 * new temp file */
82 static void mesedit(int c, struct header *hp);
84 /* Pipe the message through the command. Old message is on stdin of command,
85 * new message collected from stdout. Shell must return 0 to accept new msg */
86 static void mespipe(char *cmd);
88 /* Interpolate the named messages into the current message, possibly doing
89 * indent stuff. The flag argument is one of the tilde escapes: [mMfFuU].
90 * Return a count of the number of characters now in the message, or -1 if an
91 * error is encountered writing the message temporary */
92 static int forward(char *ms, FILE *fp, int f);
94 /* Print (continue) when continued after ^Z */
95 static void collstop(int s);
97 /* On interrupt, come here to save the partial message in ~/dead.letter.
98 * Then jump out of the collection loop */
99 static void _collint(int s);
101 static void collhup(int s);
103 static int putesc(char const *s, FILE *stream);
105 static void
106 _execute_command(struct header *hp, char *linebuf, size_t linesize)
108 /* The problem arises if there are rfc822 message attachments and the
109 * user uses `~:' to change the current file. TODO Unfortunately we
110 * TODO cannot simply keep a pointer to, or increment a reference count
111 * TODO of the current `file' (mailbox that is) object, because the
112 * TODO codebase doesn't deal with that at all; so, until some far
113 * TODO later time, copy the name of the path, and warn the user if it
114 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
115 * TODO copy the message attachments over to temporary files, but that
116 * TODO would require more changes so that the user still can recognize
117 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
118 char *mnbuf = NULL;
119 size_t mnlen = 0 /* silence CC */;
120 struct attachment *ap;
121 NYD_ENTER;
123 /* If the above todo is worked, remove or outsource to attachments.c! */
124 if ((ap = hp->h_attach) != NULL) do
125 if (ap->a_msgno) {
126 mnlen = strlen(mailname) +1;
127 mnbuf = ac_alloc(mnlen);
128 memcpy(mnbuf, mailname, mnlen);
129 break;
131 while ((ap = ap->a_flink) != NULL);
133 inhook = 0;
134 execute(linebuf, TRU1, linesize);
136 if (mnbuf != NULL) {
137 if (strncmp(mnbuf, mailname, mnlen))
138 fputs(_("Mailbox changed: it seems existing rfc822 attachments "
139 "became invalid!\n"), stderr);
140 ac_free(mnbuf);
142 NYD_LEAVE;
145 static int
146 _include_file(char const *name, int *linecount, int *charcount,
147 bool_t doecho, bool_t indent)
149 FILE *fbuf;
150 char const *indb;
151 int ret = -1;
152 char *linebuf = NULL; /* TODO line pool */
153 size_t linesize = 0, indl, linelen, cnt;
154 NYD_ENTER;
156 if ((fbuf = Fopen(name, "r")) == NULL) {
157 perror(name);
158 goto jleave;
161 if (!indent)
162 indb = NULL, indl = 0;
163 else {
164 if ((indb = ok_vlook(indentprefix)) == NULL)
165 indb = INDENT_DEFAULT;
166 indl = strlen(indb);
169 *linecount = *charcount = 0;
170 cnt = fsize(fbuf);
171 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
172 if (indl > 0 && fwrite(indb, sizeof *indb, indl, _coll_fp) != indl)
173 goto jleave;
174 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
175 goto jleave;
176 ++(*linecount);
177 (*charcount) += linelen + indl;
178 if ((options & OPT_INTERACTIVE) && doecho) {
179 if (indl > 0)
180 fwrite(indb, sizeof *indb, indl, stdout);
181 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
184 if (fflush(_coll_fp))
185 goto jleave;
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 perror(cmd);
223 NYD_LEAVE;
226 static void
227 print_collf(FILE *cf, struct header *hp)
229 char *lbuf = NULL; /* TODO line pool */
230 FILE *volatile obuf = stdout;
231 struct attachment *ap;
232 char const *cp;
233 enum gfield gf;
234 size_t linecnt, maxlines, linesize = 0, linelen, cnt, cnt2;
235 NYD_ENTER;
237 fflush(cf);
238 rewind(cf);
239 cnt = cnt2 = fsize(cf);
241 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
242 for (linecnt = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0);
243 ++linecnt);
244 rewind(cf);
245 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
246 maxlines -= 4;
247 if (hp->h_to)
248 --maxlines;
249 if (hp->h_subject)
250 --maxlines;
251 if (hp->h_cc)
252 --maxlines;
253 if (hp->h_bcc)
254 --maxlines;
255 if (hp->h_attach)
256 --maxlines;
257 maxlines -= (myaddrs(hp) != NULL || hp->h_from != NULL);
258 maxlines -= (ok_vlook(ORGANIZATION) !=NULL || hp->h_organization !=NULL);
259 maxlines -= (ok_vlook(replyto) != NULL || hp->h_replyto != NULL);
260 maxlines -= (ok_vlook(sender) != NULL || hp->h_sender != NULL);
261 if ((ssize_t)maxlines < 0 || linecnt > maxlines) {
262 cp = get_pager(NULL);
263 if (sigsetjmp(_coll_pipejmp, 1))
264 goto jendpipe;
265 obuf = Popen(cp, "w", NULL, NULL, 1);
266 if (obuf == NULL) {
267 perror(cp);
268 obuf = stdout;
269 } else
270 safe_signal(SIGPIPE, &_collect_onpipe);
274 fprintf(obuf, _("-------\nMessage contains:\n"));
275 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES;
276 if (ok_blook(fullnames))
277 gf |= GCOMMA;
278 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
279 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
280 prout(lbuf, linelen, obuf);
281 if (hp->h_attach != NULL) {
282 fputs(_("-------\nAttachments:\n"), obuf);
283 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
284 if (ap->a_msgno)
285 fprintf(obuf, " - message %u\n", ap->a_msgno);
286 else {
287 /* TODO after MIME/send layer rewrite we *know*
288 * TODO the details of the attachment here,
289 * TODO so adjust this again, then */
290 char const *cs, *csi = "-> ";
292 if ((cs = ap->a_charset) == NULL &&
293 (csi = "<- ", cs = ap->a_input_charset) == NULL)
294 cs = charset_get_lc();
295 if ((cp = ap->a_content_type) == NULL)
296 cp = "?";
297 else if (ascncasecmp(cp, "text/", 5))
298 csi = "";
299 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
303 jendpipe:
304 if (obuf != stdout) {
305 safe_signal(SIGPIPE, SIG_IGN);
306 Pclose(obuf, TRU1);
307 safe_signal(SIGPIPE, dflpipe);
309 if (lbuf != NULL)
310 free(lbuf);
311 NYD_LEAVE;
314 static int
315 exwrite(char const *name, FILE *fp, int f)
317 FILE *of;
318 int c, lc, rv = -1;
319 long cc;
320 NYD_ENTER;
322 if (f) {
323 printf("\"%s\" ", name);
324 fflush(stdout);
326 if ((of = Fopen(name, "a")) == NULL) {
327 perror(NULL);
328 goto jleave;
331 lc = 0;
332 cc = 0;
333 while ((c = getc(fp)) != EOF) {
334 ++cc;
335 if (c == '\n')
336 ++lc;
337 putc(c, of);
338 if (ferror(of)) {
339 perror(name);
340 Fclose(of);
341 goto jleave;
344 Fclose(of);
345 printf(_("%d/%ld\n"), lc, cc);
346 fflush(stdout);
347 rv = 0;
348 jleave:
349 NYD_LEAVE;
350 return rv;
353 static enum okay
354 makeheader(FILE *fp, struct header *hp)
356 FILE *nf;
357 int c;
358 enum okay rv = STOP;
359 NYD_ENTER;
361 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
362 NULL) {
363 perror(_("temporary mail edit file"));
364 goto jleave;
367 extract_header(fp, hp);
368 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
369 putc(c, nf);
370 if (fp != _coll_fp)
371 Fclose(_coll_fp);
372 Fclose(fp);
373 _coll_fp = nf;
374 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
375 goto jleave;
376 rv = OKAY;
377 jleave:
378 NYD_LEAVE;
379 return rv;
382 static void
383 mesedit(int c, struct header *hp)
385 bool_t saved;
386 sighandler_type sigint;
387 FILE *nf;
388 NYD_ENTER;
390 saved = ok_blook(add_file_recipients);
391 ok_bset(add_file_recipients, TRU1);
393 sigint = safe_signal(SIGINT, SIG_IGN);
394 nf = run_editor(_coll_fp, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
395 if (nf != NULL) {
396 if (hp) {
397 rewind(nf);
398 makeheader(nf, hp);
399 } else {
400 fseek(nf, 0L, SEEK_END);
401 Fclose(_coll_fp);
402 _coll_fp = nf;
405 safe_signal(SIGINT, sigint);
407 ok_bset(add_file_recipients, saved);
408 NYD_LEAVE;
411 static void
412 mespipe(char *cmd)
414 FILE *nf;
415 sighandler_type sigint;
416 char const *sh;
417 NYD_ENTER;
419 sigint = safe_signal(SIGINT, SIG_IGN);
421 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
422 NULL) {
423 perror(_("temporary mail edit file"));
424 goto jout;
427 /* stdin = current message. stdout = new message */
428 if ((sh = ok_vlook(SHELL)) == NULL)
429 sh = XSHELL;
430 fflush(_coll_fp);
431 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL) < 0) {
432 Fclose(nf);
433 goto jout;
436 if (fsize(nf) == 0) {
437 fprintf(stderr, _("No bytes from \"%s\" !?\n"), cmd);
438 Fclose(nf);
439 goto jout;
442 /* Take new files */
443 fseek(nf, 0L, SEEK_END);
444 Fclose(_coll_fp);
445 _coll_fp = nf;
446 jout:
447 safe_signal(SIGINT, sigint);
448 NYD_LEAVE;
451 static int
452 forward(char *ms, FILE *fp, int f)
454 int *msgvec, rv = 0;
455 struct ignoretab *ig;
456 char const *tabst;
457 enum sendaction action;
458 NYD_ENTER;
460 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
461 if (getmsglist(ms, msgvec, 0) < 0)
462 goto jleave;
463 if (*msgvec == 0) {
464 *msgvec = first(0, MMNORM);
465 if (*msgvec == 0) {
466 fputs(_("No appropriate messages\n"), stderr);
467 goto jleave;
469 msgvec[1] = 0;
472 if (f == 'f' || f == 'F' || f == 'u')
473 tabst = NULL;
474 else if ((tabst = ok_vlook(indentprefix)) == NULL)
475 tabst = INDENT_DEFAULT;
476 if (f == 'u' || f == 'U')
477 ig = allignore;
478 else
479 ig = upperchar(f) ? NULL : ignore;
480 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
482 printf(_("Interpolating:"));
483 for (; *msgvec != 0; ++msgvec) {
484 struct message *mp = message + *msgvec - 1;
486 touch(mp);
487 printf(" %d", *msgvec);
488 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
489 perror(_("temporary mail file"));
490 rv = -1;
491 break;
494 printf("\n");
495 jleave:
496 NYD_LEAVE;
497 return rv;
500 static void
501 collstop(int s)
503 sighandler_type old_action;
504 sigset_t nset;
505 NYD_X; /* Signal handler */
507 old_action = safe_signal(s, SIG_DFL);
509 sigemptyset(&nset);
510 sigaddset(&nset, s);
511 sigprocmask(SIG_UNBLOCK, &nset, NULL);
512 kill(0, s);
513 sigprocmask(SIG_BLOCK, &nset, NULL);
515 safe_signal(s, old_action);
516 if (_coll_jmp_p) {
517 _coll_jmp_p = 0;
518 _coll_hadintr = 0;
519 siglongjmp(_coll_jmp, 1);
523 static void
524 _collint(int s)
526 NYD_X; /* Signal handler */
528 /* the control flow is subtle, because we can be called from ~q */
529 if (_coll_hadintr == 0) {
530 if (ok_blook(ignore)) {
531 puts("@");
532 fflush(stdout);
533 clearerr(stdin);
534 } else
535 _coll_hadintr = 1;
536 siglongjmp(_coll_jmp, 1);
538 exit_status |= EXIT_SEND_ERROR;
539 if (ok_blook(save) && s != 0)
540 savedeadletter(_coll_fp, 1);
541 /* Aborting message, no need to fflush() .. */
542 siglongjmp(_coll_abort, 1);
545 static void
546 collhup(int s)
548 NYD_X; /* Signal handler */
549 UNUSED(s);
551 savedeadletter(_coll_fp, 1);
552 /* Let's pretend nobody else wants to clean up, a true statement at
553 * this time */
554 exit(1);
557 static int
558 putesc(char const *s, FILE *stream)
560 int n = 0, rv = -1;
561 NYD_ENTER;
563 while (s[0] != '\0') {
564 if (s[0] == '\\') {
565 if (s[1] == 't') {
566 if (putc('\t', stream) == EOF)
567 goto jleave;
568 ++n;
569 s += 2;
570 continue;
572 if (s[1] == 'n') {
573 if (putc('\n', stream) == EOF)
574 goto jleave;
575 ++n;
576 s += 2;
577 continue;
580 if (putc(s[0], stream) == EOF)
581 goto jleave;
582 ++n;
583 ++s;
585 if (putc('\n', stream) == EOF)
586 goto jleave;
587 rv = ++n;
588 jleave:
589 NYD_LEAVE;
590 return rv;
593 FL FILE *
594 collect(struct header *hp, int printheaders, struct message *mp,
595 char *quotefile, int doprefix)
597 struct ignoretab *quoteig;
598 int lc, cc, c, t;
599 int volatile escape, getfields;
600 char *linebuf = NULL, *quote = NULL;
601 char const *cp;
602 size_t linesize = 0; /* TODO line pool */
603 long cnt;
604 enum sendaction action;
605 sigset_t oset, nset;
606 sighandler_type savedtop;
607 NYD_ENTER;
609 _coll_fp = NULL;
610 /* Start catching signals from here, but we're still die on interrupts
611 * until we're in the main loop */
612 sigemptyset(&nset);
613 sigaddset(&nset, SIGINT);
614 sigaddset(&nset, SIGHUP);
615 sigprocmask(SIG_BLOCK, &nset, &oset);
616 handlerpush(&_collint);
617 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
618 safe_signal(SIGINT, &_collint);
619 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
620 safe_signal(SIGHUP, collhup);
621 /* TODO We do a lot of redundant signal handling, especially
622 * TODO with the command line editor(s); try to merge this */
623 _coll_savetstp = safe_signal(SIGTSTP, collstop);
624 _coll_savettou = safe_signal(SIGTTOU, collstop);
625 _coll_savettin = safe_signal(SIGTTIN, collstop);
626 if (sigsetjmp(_coll_abort, 1))
627 goto jerr;
628 if (sigsetjmp(_coll_jmp, 1))
629 goto jerr;
630 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
632 ++noreset;
633 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
634 0600)) == NULL) {
635 perror(_("temporary mail file"));
636 goto jerr;
639 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
640 goto jerr;
642 /* If we are going to prompt for a subject, refrain from printing a newline
643 * after the headers (since some people mind) */
644 getfields = 0;
645 if (!(options & OPT_t_FLAG)) {
646 t = GTO | GSUBJECT | GCC | GNL;
647 if (ok_blook(fullnames))
648 t |= GCOMMA;
649 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
650 (ok_blook(ask) || ok_blook(asksub)))
651 t &= ~GNL, getfields |= GSUBJECT;
652 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
653 t &= ~GNL, getfields |= GTO;
654 if (!ok_blook(bsdcompat) && !ok_blook(askatend) &&
655 (options & OPT_INTERACTIVE)) {
656 if (hp->h_bcc == NULL && ok_blook(askbcc))
657 t &= ~GNL, getfields |= GBCC;
658 if (hp->h_cc == NULL && ok_blook(askcc))
659 t &= ~GNL, getfields |= GCC;
661 if (printheaders) {
662 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
663 fflush(stdout);
667 /* Quote an original message */
668 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
669 quoteig = allignore;
670 action = SEND_QUOTE;
671 if (doprefix) {
672 quoteig = fwdignore;
673 if ((cp = ok_vlook(fwdheading)) == NULL)
674 cp = "-------- Original Message --------";
675 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
676 goto jerr;
677 } else if (!strcmp(quote, "noheading")) {
678 /*EMPTY*/;
679 } else if (!strcmp(quote, "headers")) {
680 quoteig = ignore;
681 } else if (!strcmp(quote, "allheaders")) {
682 quoteig = NULL;
683 action = SEND_QUOTE_ALL;
684 } else {
685 cp = hfield1("from", mp);
686 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
687 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE,NULL) < 0)
688 goto jerr;
689 if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
690 goto jerr;
693 if (fflush(_coll_fp))
694 goto jerr;
695 if (doprefix)
696 cp = NULL;
697 else if ((cp = ok_vlook(indentprefix)) == NULL)
698 cp = INDENT_DEFAULT;
699 if (sendmp(mp, _coll_fp, quoteig, cp, action, NULL) < 0)
700 goto jerr;
703 /* Print what we have sofar also on the terminal */
704 rewind(_coll_fp);
705 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
706 putc(c, stdout);
707 if (fseek(_coll_fp, 0, SEEK_END))
708 goto jerr;
710 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
711 _coll_hadintr = 0;
713 if (!sigsetjmp(_coll_jmp, 1)) {
714 if (getfields)
715 grab_headers(hp, getfields, 1);
716 if (quotefile != NULL) {
717 if (_include_file(quotefile, &lc, &cc, TRU1, FAL0) != 0)
718 goto jerr;
720 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
721 rewind(_coll_fp);
722 mesedit('e', hp);
723 goto jcont;
725 } else {
726 /* Come here for printing the after-signal message. Duplicate messages
727 * won't be printed because the write is aborted if we get a SIGTTOU */
728 jcont:
729 if (_coll_hadintr) {
730 fprintf(stderr, _("\n(Interrupt -- one more to kill letter)\n"));
731 } else {
732 printf(_("(continue)\n"));
733 fflush(stdout);
737 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
738 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
739 linebuf = srealloc(linebuf, linesize = LINESIZE);
740 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
741 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
742 goto jerr;
744 if (fflush(_coll_fp))
745 goto jerr;
746 goto jout;
749 /* The interactive collect loop */
750 for (;;) {
751 _coll_jmp_p = 1;
752 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
753 _coll_jmp_p = 0;
755 if (cnt < 0) {
756 if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
757 printf(_("Use \".\" to terminate letter\n"));
758 continue;
760 break;
762 if ((options & OPT_t_FLAG) && cnt == 0) {
763 rewind(_coll_fp);
764 if (makeheader(_coll_fp, hp) != OKAY)
765 goto jerr;
766 rewind(_coll_fp);
767 options &= ~OPT_t_FLAG;
768 continue;
771 _coll_hadintr = 0;
772 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
773 (options & (OPT_INTERACTIVE | OPT_TILDE_FLAG)) &&
774 (ok_blook(dot) || ok_blook(ignoreeof)))
775 break;
776 if (cnt == 0 || linebuf[0] != escape ||
777 !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
778 /* TODO calls putline(), which *always* appends LF;
779 * TODO thus, STDIN with -t will ALWAYS end with LF,
780 * TODO even if no trailing LF and QP CTE */
781 if (putline(_coll_fp, linebuf, cnt) < 0)
782 goto jerr;
783 continue;
786 tty_addhist(linebuf, TRU1);
788 c = linebuf[1];
789 switch (c) {
790 default:
791 /* On double escape, send a single one. Otherwise, it's an error */
792 if (c == escape) {
793 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
794 goto jerr;
795 else
796 break;
798 fputs(_("Unknown tilde escape.\n"), stderr);
799 break;
800 case '!':
801 /* Shell escape, send the balance of line to sh -c */
802 c_shell(linebuf + 2);
803 break;
804 case ':':
805 /* FALLTHRU */
806 case '_':
807 /* Escape to command mode, but be nice! */
808 _execute_command(hp, linebuf + 2, cnt - 2);
809 goto jcont;
810 case '.':
811 /* Simulate end of file on input */
812 goto jout;
813 case 'x':
814 /* Same as 'q', but no *DEAD* saving */
815 /* FALLTHRU */
816 case 'q':
817 /* Force a quit, act like an interrupt had happened */
818 ++_coll_hadintr;
819 _collint((c == 'x') ? 0 : SIGINT);
820 exit(1);
821 /*NOTREACHED*/
822 case 'h':
823 /* Grab a bunch of headers */
825 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
826 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
827 while (hp->h_to == NULL);
828 goto jcont;
829 case 'H':
830 /* Grab extra headers */
832 grab_headers(hp, GEXTRA, 0);
833 while (check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
834 goto jcont;
835 case 't':
836 /* Add to the To list */
837 while ((hp->h_to = cat(hp->h_to,
838 checkaddrs(lextract(linebuf + 2, GTO | GFULL)))) == NULL)
840 break;
841 case 's':
842 /* Set the Subject list */
843 cp = linebuf + 2;
844 while (whitechar(*cp))
845 ++cp;
846 hp->h_subject = savestr(cp);
847 break;
848 #ifdef HAVE_DEBUG
849 case 'S':
850 c_sstats(NULL);
851 break;
852 #endif
853 case '@':
854 /* Edit the attachment list */
855 if (linebuf[2] != '\0')
856 append_attachments(&hp->h_attach, linebuf + 2);
857 else
858 edit_attachments(&hp->h_attach);
859 break;
860 case 'c':
861 /* Add to the CC list */
862 hp->h_cc = cat(hp->h_cc,
863 checkaddrs(lextract(linebuf + 2, GCC | GFULL)));
864 break;
865 case 'b':
866 /* Add stuff to blind carbon copies list */
867 hp->h_bcc = cat(hp->h_bcc,
868 checkaddrs(lextract(linebuf + 2, GBCC | GFULL)));
869 break;
870 case 'd':
871 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
872 linebuf[linesize - 1] = '\0';
873 /*FALLTHRU*/
874 case 'R':
875 case 'r':
876 case '<':
877 /* Invoke a file: Search for the file name, then open it and copy the
878 * contents to _coll_fp */
879 cp = linebuf + 2;
880 while (whitechar(*cp))
881 ++cp;
882 if (*cp == '\0') {
883 fputs(_("Interpolate what file?\n"), stderr);
884 break;
886 if (*cp == '!') {
887 insertcommand(_coll_fp, cp + 1);
888 break;
890 if ((cp = file_expand(cp)) == NULL)
891 break;
892 if (is_dir(cp)) {
893 fprintf(stderr, _("%s: Directory\n"), cp);
894 break;
896 printf(_("\"%s\" "), cp);
897 fflush(stdout);
898 if (_include_file(cp, &lc, &cc, FAL0, (c == 'R')) != 0)
899 goto jerr;
900 printf(_("%d/%d\n"), lc, cc);
901 break;
902 case 'i':
903 /* Insert a variable into the file */
904 cp = linebuf + 2;
905 while (whitechar(*cp))
906 ++cp;
907 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
908 break;
909 if (putesc(cp, _coll_fp) < 0)
910 goto jerr;
911 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
912 goto jerr;
913 break;
914 case 'a':
915 case 'A':
916 /* Insert the contents of a signature variable */
917 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
918 if (cp != NULL && *cp != '\0') {
919 if (putesc(cp, _coll_fp) < 0)
920 goto jerr;
921 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
922 goto jerr;
924 break;
925 case 'w':
926 /* Write the message on a file */
927 cp = linebuf + 2;
928 while (blankchar(*cp))
929 ++cp;
930 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
931 fputs(_("Write what file!?\n"), stderr);
932 break;
934 rewind(_coll_fp);
935 if (exwrite(cp, _coll_fp, 1) < 0)
936 goto jerr;
937 break;
938 case 'm':
939 case 'M':
940 case 'f':
941 case 'F':
942 case 'u':
943 case 'U':
944 /* Interpolate the named messages, if we are in receiving mail mode.
945 * Does the standard list processing garbage. If ~f is given, we
946 * don't shift over */
947 if (forward(linebuf + 2, _coll_fp, c) < 0)
948 goto jerr;
949 goto jcont;
950 case 'p':
951 /* Print current state of the message without altering anything */
952 print_collf(_coll_fp, hp);
953 goto jcont;
954 case '|':
955 /* Pipe message through command. Collect output as new message */
956 rewind(_coll_fp);
957 mespipe(linebuf + 2);
958 goto jcont;
959 case 'v':
960 case 'e':
961 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
962 rewind(_coll_fp);
963 mesedit(c, ok_blook(editheaders) ? hp : NULL);
964 goto jcont;
965 case '?':
966 /* Last the lengthy help string. (Very ugly, but take care for
967 * compiler supported string lengths :() */
968 puts(_(
969 "-------------------- ~ ESCAPES ----------------------------\n"
970 "~~ Quote a single tilde\n"
971 "~@ [file ...] Edit attachment list\n"
972 "~b users Add users to \"blind\" cc list\n"
973 "~c users Add users to cc list\n"
974 "~d Read in dead.letter\n"
975 "~e Edit the message buffer\n"
976 "~F messages Read in messages, keep all header lines, don't indent lines\n"
977 "~f messages Like ~F, but keep only selected header lines\n"
978 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
979 puts(_(
980 "~R file Read in a file, indent lines\n"
981 "~r file Read in a file, don't indent lines\n"
982 "~p Print the message buffer\n"
983 "~q Abort message composition and save text to dead.letter\n"
984 "~M messages Read in messages, keep all header lines, indent lines\n"
985 "~m messages Like ~F, but keep only selected header lines\n"
986 "~s subject Set subject\n"
987 "~t users Add users to to list\n"));
988 puts(_(
989 "~U messages Same as ~m, but without any headers\n"
990 "~u messages Same as ~f, but without any headers\n"
991 "~v Invoke display editor on message\n"
992 "~w file Write message onto file\n"
993 "~x Abort message composition and discard text written so far\n"
994 "~!command Invoke the shell\n"
995 "~:command Execute a regular command\n"
996 "-----------------------------------------------------------\n"));
997 break;
1001 jout:
1002 if (_coll_fp != NULL) {
1003 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
1004 if (putesc(cp, _coll_fp) < 0)
1005 goto jerr;
1006 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1007 goto jerr;
1009 rewind(_coll_fp);
1011 if (linebuf != NULL)
1012 free(linebuf);
1013 handlerpop();
1014 --noreset;
1015 sigemptyset(&nset);
1016 sigaddset(&nset, SIGINT);
1017 sigaddset(&nset, SIGHUP);
1018 sigprocmask(SIG_BLOCK, &nset, NULL);
1019 safe_signal(SIGINT, _coll_saveint);
1020 safe_signal(SIGHUP, _coll_savehup);
1021 safe_signal(SIGTSTP, _coll_savetstp);
1022 safe_signal(SIGTTOU, _coll_savettou);
1023 safe_signal(SIGTTIN, _coll_savettin);
1024 sigprocmask(SIG_SETMASK, &oset, NULL);
1025 NYD_LEAVE;
1026 return _coll_fp;
1028 jerr:
1029 if (_coll_fp != NULL) {
1030 Fclose(_coll_fp);
1031 _coll_fp = NULL;
1033 goto jout;
1036 FL void
1037 savedeadletter(FILE *fp, int fflush_rewind_first)
1039 char const *cp;
1040 int c;
1041 FILE *dbuf;
1042 ul_it lines, bytes;
1043 NYD_ENTER;
1045 if (fflush_rewind_first) {
1046 fflush(fp);
1047 rewind(fp);
1049 if (fsize(fp) == 0)
1050 goto jleave;
1052 cp = getdeadletter();
1053 c = umask(077);
1054 dbuf = Fopen(cp, "a");
1055 umask(c);
1056 if (dbuf == NULL)
1057 goto jleave;
1059 really_rewind(fp);
1061 printf("\"%s\" ", cp);
1062 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1063 putc(c, dbuf);
1064 if (c == '\n')
1065 ++lines;
1067 printf("%lu/%lu\n", lines, bytes);
1069 Fclose(dbuf);
1070 rewind(fp);
1071 jleave:
1072 NYD_LEAVE;
1075 /* vim:set fenc=utf-8:s-it-mode */