Merge branch 'topic/unibidi'
[s-mailx.git] / collect.c
blob8952072c1307a1a3404c0ee00f4bce35bbe3dfd0
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(FILE *fbuf, char const *name, int *linecount,
65 int *charcount, bool_t doecho);
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(tr(237, "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(FILE *fbuf, char const *name, int *linecount, int *charcount,
147 bool_t doecho)
149 int ret = -1;
150 char *linebuf = NULL; /* TODO line pool */
151 size_t linesize = 0, linelen, cnt;
152 NYD_ENTER;
154 if (fbuf == NULL) {
155 if ((fbuf = Fopen(name, "r")) == NULL) {
156 perror(name);
157 goto jleave;
159 } else
160 fflush_rewind(fbuf);
162 *linecount = *charcount = 0;
163 cnt = fsize(fbuf);
164 while (fgetline(&linebuf, &linesize, &cnt, &linelen, fbuf, 0) != NULL) {
165 if (fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen)
166 goto jleave;
167 if ((options & OPT_INTERACTIVE) && doecho)
168 fwrite(linebuf, sizeof *linebuf, linelen, stdout);
169 ++(*linecount);
170 (*charcount) += linelen;
172 if (fflush(_coll_fp))
173 goto jleave;
175 ret = 0;
176 jleave:
177 if (linebuf != NULL)
178 free(linebuf);
179 if (fbuf != NULL)
180 Fclose(fbuf);
181 NYD_LEAVE;
182 return ret;
185 static void
186 _collect_onpipe(int signo)
188 NYD_X; /* Signal handler */
189 UNUSED(signo);
190 siglongjmp(_coll_pipejmp, 1);
193 static void
194 insertcommand(FILE *fp, char const *cmd)
196 FILE *ibuf = NULL;
197 char const *cp;
198 int c;
199 NYD_ENTER;
201 cp = ok_vlook(SHELL);
202 if (cp == NULL)
203 cp = XSHELL;
205 if ((ibuf = Popen(cmd, "r", cp, 0)) != NULL) {
206 while ((c = getc(ibuf)) != EOF) /* XXX bytewise, yuck! */
207 putc(c, fp);
208 Pclose(ibuf, TRU1);
209 } else
210 perror(cmd);
211 NYD_LEAVE;
214 static void
215 print_collf(FILE *cf, struct header *hp)
217 char *lbuf = NULL; /* TODO line pool */
218 FILE *volatile obuf = stdout;
219 struct attachment *ap;
220 char const *cp;
221 enum gfield gf;
222 size_t linecnt, maxlines, linesize = 0, linelen, cnt, cnt2;
223 NYD_ENTER;
225 fflush(cf);
226 rewind(cf);
227 cnt = cnt2 = fsize(cf);
229 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
230 for (linecnt = 0; fgetline(&lbuf, &linesize, &cnt2, NULL, cf, 0);
231 ++linecnt);
232 rewind(cf);
233 maxlines = (*cp == '\0' ? screensize() : atoi(cp));
234 maxlines -= 4;
235 if (hp->h_to)
236 --maxlines;
237 if (hp->h_subject)
238 --maxlines;
239 if (hp->h_cc)
240 --maxlines;
241 if (hp->h_bcc)
242 --maxlines;
243 if (hp->h_attach)
244 --maxlines;
245 maxlines -= (myaddrs(hp) != NULL || hp->h_from != NULL);
246 maxlines -= (ok_vlook(ORGANIZATION) !=NULL || hp->h_organization !=NULL);
247 maxlines -= (ok_vlook(replyto) != NULL || hp->h_replyto != NULL);
248 maxlines -= (ok_vlook(sender) != NULL || hp->h_sender != NULL);
249 if ((ssize_t)maxlines < 0 || linecnt > maxlines) {
250 cp = get_pager();
251 if (sigsetjmp(_coll_pipejmp, 1))
252 goto jendpipe;
253 obuf = Popen(cp, "w", NULL, 1);
254 if (obuf == NULL) {
255 perror(cp);
256 obuf = stdout;
257 } else
258 safe_signal(SIGPIPE, &_collect_onpipe);
262 fprintf(obuf, tr(62, "-------\nMessage contains:\n"));
263 gf = GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES;
264 if (ok_blook(fullnames))
265 gf |= GCOMMA;
266 puthead(hp, obuf, gf, SEND_TODISP, CONV_NONE, NULL, NULL);
267 while (fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
268 prout(lbuf, linelen, obuf);
269 if (hp->h_attach != NULL) {
270 fputs(tr(63, "-------\nAttachments:\n"), obuf);
271 for (ap = hp->h_attach; ap != NULL; ap = ap->a_flink) {
272 if (ap->a_msgno)
273 fprintf(obuf, " - message %u\n", ap->a_msgno);
274 else {
275 /* TODO after MIME/send layer rewrite we *know*
276 * TODO the details of the attachment here,
277 * TODO so adjust this again, then */
278 char const *cs, *csi = "-> ";
280 if ((cs = ap->a_charset) == NULL &&
281 (csi = "<- ", cs = ap->a_input_charset) == NULL)
282 cs = charset_get_lc();
283 if ((cp = ap->a_content_type) == NULL)
284 cp = "?";
285 else if (ascncasecmp(cp, "text/", 5))
286 csi = "";
287 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
291 jendpipe:
292 if (obuf != stdout) {
293 safe_signal(SIGPIPE, SIG_IGN);
294 Pclose(obuf, TRU1);
295 safe_signal(SIGPIPE, dflpipe);
297 if (lbuf != NULL)
298 free(lbuf);
299 NYD_LEAVE;
302 static int
303 exwrite(char const *name, FILE *fp, int f)
305 FILE *of;
306 int c, lc, rv = -1;
307 long cc;
308 NYD_ENTER;
310 if (f) {
311 printf("\"%s\" ", name);
312 fflush(stdout);
314 if ((of = Fopen(name, "a")) == NULL) {
315 perror(NULL);
316 goto jleave;
319 lc = 0;
320 cc = 0;
321 while ((c = getc(fp)) != EOF) {
322 ++cc;
323 if (c == '\n')
324 ++lc;
325 putc(c, of);
326 if (ferror(of)) {
327 perror(name);
328 Fclose(of);
329 goto jleave;
332 Fclose(of);
333 printf(tr(65, "%d/%ld\n"), lc, cc);
334 fflush(stdout);
335 rv = 0;
336 jleave:
337 NYD_LEAVE;
338 return rv;
341 static enum okay
342 makeheader(FILE *fp, struct header *hp)
344 FILE *nf;
345 int c;
346 enum okay rv = STOP;
347 NYD_ENTER;
349 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
350 NULL) {
351 perror(tr(66, "temporary mail edit file"));
352 goto jleave;
355 extract_header(fp, hp);
356 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
357 putc(c, nf);
358 if (fp != _coll_fp)
359 Fclose(_coll_fp);
360 Fclose(fp);
361 _coll_fp = nf;
362 if (check_from_and_sender(hp->h_from, hp->h_sender))
363 goto jleave;
364 rv = OKAY;
365 jleave:
366 NYD_LEAVE;
367 return rv;
370 static void
371 mesedit(int c, struct header *hp)
373 bool_t saved;
374 sighandler_type sigint;
375 FILE *nf;
376 NYD_ENTER;
378 saved = ok_blook(add_file_recipients);
379 ok_bset(add_file_recipients, TRU1);
381 sigint = safe_signal(SIGINT, SIG_IGN);
382 nf = run_editor(_coll_fp, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
383 if (nf != NULL) {
384 if (hp) {
385 rewind(nf);
386 makeheader(nf, hp);
387 } else {
388 fseek(nf, 0L, SEEK_END);
389 Fclose(_coll_fp);
390 _coll_fp = nf;
393 safe_signal(SIGINT, sigint);
395 ok_bset(add_file_recipients, saved);
396 NYD_LEAVE;
399 static void
400 mespipe(char *cmd)
402 FILE *nf;
403 sighandler_type sigint;
404 char const *sh;
405 NYD_ENTER;
407 sigint = safe_signal(SIGINT, SIG_IGN);
409 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
410 NULL) {
411 perror(tr(66, "temporary mail edit file"));
412 goto jout;
415 /* stdin = current message. stdout = new message */
416 if ((sh = ok_vlook(SHELL)) == NULL)
417 sh = XSHELL;
418 fflush(_coll_fp);
419 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL) < 0) {
420 Fclose(nf);
421 goto jout;
424 if (fsize(nf) == 0) {
425 fprintf(stderr, tr(67, "No bytes from \"%s\" !?\n"), cmd);
426 Fclose(nf);
427 goto jout;
430 /* Take new files */
431 fseek(nf, 0L, SEEK_END);
432 Fclose(_coll_fp);
433 _coll_fp = nf;
434 jout:
435 safe_signal(SIGINT, sigint);
436 NYD_LEAVE;
439 static int
440 forward(char *ms, FILE *fp, int f)
442 int *msgvec, rv = 0;
443 struct ignoretab *ig;
444 char const *tabst;
445 enum sendaction action;
446 NYD_ENTER;
448 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
449 if (getmsglist(ms, msgvec, 0) < 0)
450 goto jleave;
451 if (*msgvec == 0) {
452 *msgvec = first(0, MMNORM);
453 if (*msgvec == 0) {
454 fputs(tr(68, "No appropriate messages\n"), stderr);
455 goto jleave;
457 msgvec[1] = 0;
460 if (f == 'f' || f == 'F' || f == 'u')
461 tabst = NULL;
462 else if ((tabst = ok_vlook(indentprefix)) == NULL)
463 tabst = "\t";
464 if (f == 'u' || f == 'U')
465 ig = allignore;
466 else
467 ig = upperchar(f) ? NULL : ignore;
468 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
470 printf(tr(69, "Interpolating:"));
471 for (; *msgvec != 0; ++msgvec) {
472 struct message *mp = message + *msgvec - 1;
474 touch(mp);
475 printf(" %d", *msgvec);
476 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
477 perror(tr(70, "temporary mail file"));
478 rv = -1;
479 break;
482 printf("\n");
483 jleave:
484 NYD_LEAVE;
485 return rv;
488 static void
489 collstop(int s)
491 sighandler_type old_action;
492 sigset_t nset;
493 NYD_X; /* Signal handler */
495 old_action = safe_signal(s, SIG_DFL);
497 sigemptyset(&nset);
498 sigaddset(&nset, s);
499 sigprocmask(SIG_UNBLOCK, &nset, NULL);
500 kill(0, s);
501 sigprocmask(SIG_BLOCK, &nset, NULL);
503 safe_signal(s, old_action);
504 if (_coll_jmp_p) {
505 _coll_jmp_p = 0;
506 _coll_hadintr = 0;
507 siglongjmp(_coll_jmp, 1);
511 static void
512 _collint(int s)
514 NYD_X; /* Signal handler */
516 /* the control flow is subtle, because we can be called from ~q */
517 if (_coll_hadintr == 0) {
518 if (ok_blook(ignore)) {
519 puts("@");
520 fflush(stdout);
521 clearerr(stdin);
522 } else
523 _coll_hadintr = 1;
524 siglongjmp(_coll_jmp, 1);
526 exit_status |= EXIT_SEND_ERROR;
527 if (ok_blook(save) && s != 0)
528 savedeadletter(_coll_fp, 1);
529 /* Aborting message, no need to fflush() .. */
530 siglongjmp(_coll_abort, 1);
533 static void
534 collhup(int s)
536 NYD_X; /* Signal handler */
537 UNUSED(s);
539 savedeadletter(_coll_fp, 1);
540 /* Let's pretend nobody else wants to clean up, a true statement at
541 * this time */
542 exit(1);
545 static int
546 putesc(char const *s, FILE *stream)
548 int n = 0, rv = -1;
549 NYD_ENTER;
551 while (s[0] != '\0') {
552 if (s[0] == '\\') {
553 if (s[1] == 't') {
554 if (putc('\t', stream) == EOF)
555 goto jleave;
556 ++n;
557 s += 2;
558 continue;
560 if (s[1] == 'n') {
561 if (putc('\n', stream) == EOF)
562 goto jleave;
563 ++n;
564 s += 2;
565 continue;
568 if (putc(s[0], stream) == EOF)
569 goto jleave;
570 ++n;
571 ++s;
573 if (putc('\n', stream) == EOF)
574 goto jleave;
575 rv = ++n;
576 jleave:
577 NYD_LEAVE;
578 return rv;
581 FL FILE *
582 collect(struct header *hp, int printheaders, struct message *mp,
583 char *quotefile, int doprefix)
585 FILE *fbuf;
586 struct ignoretab *quoteig;
587 int lc, cc, c, t;
588 int volatile escape, getfields;
589 char *linebuf = NULL, *quote = NULL;
590 char const *cp;
591 size_t linesize = 0; /* TODO line pool */
592 long cnt;
593 enum sendaction action;
594 sigset_t oset, nset;
595 sighandler_type savedtop;
596 NYD_ENTER;
598 _coll_fp = NULL;
599 /* Start catching signals from here, but we're still die on interrupts
600 * until we're in the main loop */
601 sigemptyset(&nset);
602 sigaddset(&nset, SIGINT);
603 sigaddset(&nset, SIGHUP);
604 sigprocmask(SIG_BLOCK, &nset, &oset);
605 handlerpush(&_collint);
606 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
607 safe_signal(SIGINT, &_collint);
608 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
609 safe_signal(SIGHUP, collhup);
610 /* TODO We do a lot of redundant signal handling, especially
611 * TODO with the command line editor(s); try to merge this */
612 _coll_savetstp = safe_signal(SIGTSTP, collstop);
613 _coll_savettou = safe_signal(SIGTTOU, collstop);
614 _coll_savettin = safe_signal(SIGTTIN, collstop);
615 if (sigsetjmp(_coll_abort, 1))
616 goto jerr;
617 if (sigsetjmp(_coll_jmp, 1))
618 goto jerr;
619 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
621 ++noreset;
622 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
623 0600)) == NULL) {
624 perror(tr(51, "temporary mail file"));
625 goto jerr;
628 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
629 goto jerr;
631 /* If we are going to prompt for a subject, refrain from printing a newline
632 * after the headers (since some people mind) */
633 getfields = 0;
634 if (!(options & OPT_t_FLAG)) {
635 t = GTO | GSUBJECT | GCC | GNL;
636 if (ok_blook(fullnames))
637 t |= GCOMMA;
638 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
639 (ok_blook(ask) || ok_blook(asksub)))
640 t &= ~GNL, getfields |= GSUBJECT;
641 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
642 t &= ~GNL, getfields |= GTO;
643 if (!ok_blook(bsdcompat) && !ok_blook(askatend) &&
644 (options & OPT_INTERACTIVE)) {
645 if (hp->h_bcc == NULL && ok_blook(askbcc))
646 t &= ~GNL, getfields |= GBCC;
647 if (hp->h_cc == NULL && ok_blook(askcc))
648 t &= ~GNL, getfields |= GCC;
650 if (printheaders) {
651 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
652 fflush(stdout);
656 /* Quote an original message */
657 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
658 quoteig = allignore;
659 action = SEND_QUOTE;
660 if (doprefix) {
661 quoteig = fwdignore;
662 if ((cp = ok_vlook(fwdheading)) == NULL)
663 cp = "-------- Original Message --------";
664 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
665 goto jerr;
666 } else if (!strcmp(quote, "noheading")) {
667 /*EMPTY*/;
668 } else if (!strcmp(quote, "headers")) {
669 quoteig = ignore;
670 } else if (!strcmp(quote, "allheaders")) {
671 quoteig = NULL;
672 action = SEND_QUOTE_ALL;
673 } else {
674 cp = hfield1("from", mp);
675 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
676 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE,NULL) < 0)
677 goto jerr;
678 if (fprintf(_coll_fp, tr(52, " wrote:\n\n")) < 0)
679 goto jerr;
682 if (fflush(_coll_fp))
683 goto jerr;
684 cp = ok_vlook(indentprefix);
685 if (cp != NULL && *cp == '\0')
686 cp = "\t";
687 if (sendmp(mp, _coll_fp, quoteig, (doprefix ? NULL : cp), action, NULL)
688 < 0)
689 goto jerr;
692 /* Print what we have sofar also on the terminal */
693 rewind(_coll_fp);
694 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
695 putc(c, stdout);
696 if (fseek(_coll_fp, 0, SEEK_END))
697 goto jerr;
699 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
700 _coll_hadintr = 0;
702 if (!sigsetjmp(_coll_jmp, 1)) {
703 if (getfields)
704 grab_headers(hp, getfields, 1);
705 if (quotefile != NULL) {
706 if (_include_file(NULL, quotefile, &lc, &cc, TRU1) != 0)
707 goto jerr;
709 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
710 rewind(_coll_fp);
711 mesedit('e', hp);
712 goto jcont;
714 } else {
715 /* Come here for printing the after-signal message. Duplicate messages
716 * won't be printed because the write is aborted if we get a SIGTTOU */
717 jcont:
718 if (_coll_hadintr) {
719 fprintf(stderr, tr(53, "\n(Interrupt -- one more to kill letter)\n"));
720 } else {
721 printf(tr(54, "(continue)\n"));
722 fflush(stdout);
726 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
727 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
728 linebuf = srealloc(linebuf, linesize = LINESIZE);
729 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
730 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
731 goto jerr;
733 if (fflush(_coll_fp))
734 goto jerr;
735 goto jout;
738 /* The interactive collect loop */
739 for (;;) {
740 _coll_jmp_p = 1;
741 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
742 _coll_jmp_p = 0;
744 if (cnt < 0) {
745 if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
746 printf(tr(55, "Use \".\" to terminate letter\n"));
747 continue;
749 break;
751 if ((options & OPT_t_FLAG) && cnt == 0) {
752 rewind(_coll_fp);
753 if (makeheader(_coll_fp, hp) != OKAY)
754 goto jerr;
755 rewind(_coll_fp);
756 options &= ~OPT_t_FLAG;
757 continue;
760 _coll_hadintr = 0;
761 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
762 (options & (OPT_INTERACTIVE | OPT_TILDE_FLAG)) &&
763 (ok_blook(dot) || ok_blook(ignoreeof)))
764 break;
765 if (cnt == 0 || linebuf[0] != escape ||
766 !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
767 /* TODO calls putline(), which *always* appends LF;
768 * TODO thus, STDIN with -t will ALWAYS end with LF,
769 * TODO even if no trailing LF and QP CTE */
770 if (putline(_coll_fp, linebuf, cnt) < 0)
771 goto jerr;
772 continue;
775 c = linebuf[1];
776 switch (c) {
777 default:
778 /* On double escape, send a single one. Otherwise, it's an error */
779 if (c == escape) {
780 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
781 goto jerr;
782 else
783 break;
785 fputs(tr(56, "Unknown tilde escape.\n"), stderr);
786 break;
787 case '!':
788 /* Shell escape, send the balance of line to sh -c */
789 c_shell(linebuf + 2);
790 break;
791 case ':':
792 /* FALLTHRU */
793 case '_':
794 /* Escape to command mode, but be nice! */
795 _execute_command(hp, linebuf + 2, cnt - 2);
796 goto jcont;
797 case '.':
798 /* Simulate end of file on input */
799 goto jout;
800 case 'x':
801 /* Same as 'q', but no *DEAD* saving */
802 /* FALLTHRU */
803 case 'q':
804 /* Force a quit, act like an interrupt had happened */
805 ++_coll_hadintr;
806 _collint((c == 'x') ? 0 : SIGINT);
807 exit(1);
808 /*NOTREACHED*/
809 case 'h':
810 /* Grab a bunch of headers */
812 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
813 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
814 while (hp->h_to == NULL);
815 goto jcont;
816 case 'H':
817 /* Grab extra headers */
819 grab_headers(hp, GEXTRA, 0);
820 while (check_from_and_sender(hp->h_from, hp->h_sender));
821 goto jcont;
822 case 't':
823 /* Add to the To list */
824 while ((hp->h_to = cat(hp->h_to,
825 checkaddrs(lextract(linebuf + 2, GTO | GFULL)))) == NULL)
827 break;
828 case 's':
829 /* Set the Subject list */
830 cp = linebuf + 2;
831 while (whitechar(*cp))
832 ++cp;
833 hp->h_subject = savestr(cp);
834 break;
835 #ifdef HAVE_DEBUG
836 case 'S':
837 c_sstats(NULL);
838 break;
839 #endif
840 case '@':
841 /* Edit the attachment list */
842 if (linebuf[2] != '\0')
843 append_attachments(&hp->h_attach, linebuf + 2);
844 else
845 edit_attachments(&hp->h_attach);
846 break;
847 case 'c':
848 /* Add to the CC list */
849 hp->h_cc = cat(hp->h_cc,
850 checkaddrs(lextract(linebuf + 2, GCC | GFULL)));
851 break;
852 case 'b':
853 /* Add stuff to blind carbon copies list */
854 hp->h_bcc = cat(hp->h_bcc,
855 checkaddrs(lextract(linebuf + 2, GBCC | GFULL)));
856 break;
857 case 'd':
858 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
859 linebuf[linesize - 1] = '\0';
860 /*FALLTHRU*/
861 case 'r':
862 case '<':
863 /* Invoke a file: Search for the file name, then open it and copy the
864 * contents to _coll_fp */
865 cp = linebuf + 2;
866 while (whitechar(*cp))
867 ++cp;
868 if (*cp == '\0') {
869 fputs(tr(57, "Interpolate what file?\n"), stderr);
870 break;
872 if (*cp == '!') {
873 insertcommand(_coll_fp, cp + 1);
874 break;
876 if ((cp = file_expand(cp)) == NULL)
877 break;
878 if (is_dir(cp)) {
879 fprintf(stderr, tr(58, "%s: Directory\n"), cp);
880 break;
882 if ((fbuf = Fopen(cp, "r")) == NULL) {
883 perror(cp);
884 break;
886 printf(tr(59, "\"%s\" "), cp);
887 fflush(stdout);
888 if (_include_file(fbuf, cp, &lc, &cc, FAL0) != 0)
889 goto jerr;
890 printf(tr(60, "%d/%d\n"), lc, cc);
891 break;
892 case 'i':
893 /* Insert a variable into the file */
894 cp = linebuf + 2;
895 while (whitechar(*cp))
896 ++cp;
897 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
898 break;
899 if (putesc(cp, _coll_fp) < 0)
900 goto jerr;
901 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
902 goto jerr;
903 break;
904 case 'a':
905 case 'A':
906 /* Insert the contents of a signature variable */
907 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
908 if (cp != NULL && *cp != '\0') {
909 if (putesc(cp, _coll_fp) < 0)
910 goto jerr;
911 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
912 goto jerr;
914 break;
915 case 'w':
916 /* Write the message on a file */
917 cp = linebuf + 2;
918 while (blankchar(*cp))
919 ++cp;
920 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
921 fputs(tr(61, "Write what file!?\n"), stderr);
922 break;
924 rewind(_coll_fp);
925 if (exwrite(cp, _coll_fp, 1) < 0)
926 goto jerr;
927 break;
928 case 'm':
929 case 'M':
930 case 'f':
931 case 'F':
932 case 'u':
933 case 'U':
934 /* Interpolate the named messages, if we are in receiving mail mode.
935 * Does the standard list processing garbage. If ~f is given, we
936 * don't shift over */
937 if (forward(linebuf + 2, _coll_fp, c) < 0)
938 goto jerr;
939 goto jcont;
940 case 'p':
941 /* Print current state of the message without altering anything */
942 print_collf(_coll_fp, hp);
943 goto jcont;
944 case '|':
945 /* Pipe message through command. Collect output as new message */
946 rewind(_coll_fp);
947 mespipe(linebuf + 2);
948 goto jcont;
949 case 'v':
950 case 'e':
951 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
952 rewind(_coll_fp);
953 mesedit(c, ok_blook(editheaders) ? hp : NULL);
954 goto jcont;
955 case '?':
956 /* Last the lengthy help string. (Very ugly, but take care for
957 * compiler supported string lengths :() */
958 puts(tr(300,
959 "-------------------- ~ ESCAPES ----------------------------\n"
960 "~~ Quote a single tilde\n"
961 "~@ [file ...] Edit attachment list\n"
962 "~b users Add users to \"blind\" cc list\n"
963 "~c users Add users to cc list\n"
964 "~d Read in dead.letter\n"
965 "~e Edit the message buffer\n"
966 "~f messages Read in messages without indenting lines\n"
967 "~F messages Same as ~f, but keep all header lines\n"
968 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
969 puts(tr(301,
970 "~r file Read a file into the message buffer\n"
971 "~p Print the message buffer\n"
972 "~q Abort message composition and save text to dead.letter\n"
973 "~m messages Read in messages with each line indented\n"
974 "~M messages Same as ~m, but keep all header lines\n"
975 "~s subject Set subject\n"
976 "~t users Add users to to list\n"
977 "~u messages Same as ~f, but without any headers\n"
978 "~U messages Same as ~m, but without any headers\n"));
979 puts(tr(302,
980 "~v Invoke display editor on message\n"
981 "~w file Write message onto file\n"
982 "~x Abort message composition and discard text written so far\n"
983 "~!command Invoke the shell\n"
984 "~:command Execute a regular command\n"
985 "-----------------------------------------------------------\n"));
986 break;
990 jout:
991 if (_coll_fp != NULL) {
992 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
993 if (putesc(cp, _coll_fp) < 0)
994 goto jerr;
995 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
996 goto jerr;
998 rewind(_coll_fp);
1000 if (linebuf != NULL)
1001 free(linebuf);
1002 handlerpop();
1003 --noreset;
1004 sigemptyset(&nset);
1005 sigaddset(&nset, SIGINT);
1006 sigaddset(&nset, SIGHUP);
1007 sigprocmask(SIG_BLOCK, &nset, NULL);
1008 safe_signal(SIGINT, _coll_saveint);
1009 safe_signal(SIGHUP, _coll_savehup);
1010 safe_signal(SIGTSTP, _coll_savetstp);
1011 safe_signal(SIGTTOU, _coll_savettou);
1012 safe_signal(SIGTTIN, _coll_savettin);
1013 sigprocmask(SIG_SETMASK, &oset, NULL);
1014 NYD_LEAVE;
1015 return _coll_fp;
1017 jerr:
1018 if (_coll_fp != NULL) {
1019 Fclose(_coll_fp);
1020 _coll_fp = NULL;
1022 goto jout;
1025 FL void
1026 savedeadletter(FILE *fp, int fflush_rewind_first)
1028 char const *cp;
1029 int c;
1030 FILE *dbuf;
1031 ul_it lines, bytes;
1032 NYD_ENTER;
1034 if (fflush_rewind_first) {
1035 fflush(fp);
1036 rewind(fp);
1038 if (fsize(fp) == 0)
1039 goto jleave;
1041 cp = getdeadletter();
1042 c = umask(077);
1043 dbuf = Fopen(cp, "a");
1044 umask(c);
1045 if (dbuf == NULL)
1046 goto jleave;
1048 really_rewind(fp);
1050 printf("\"%s\" ", cp);
1051 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1052 putc(c, dbuf);
1053 if (c == '\n')
1054 ++lines;
1056 printf("%lu/%lu\n", lines, bytes);
1058 Fclose(dbuf);
1059 rewind(fp);
1060 jleave:
1061 NYD_LEAVE;
1064 /* vim:set fenc=utf-8:s-it-mode */