Review: quit.c
[s-mailx.git] / collect.c
blob50c771f57b90351b2b39e24b51160ea1a9ba2d45
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 return;
524 _coll_hadintr = 1;
525 siglongjmp(_coll_jmp, 1);
527 exit_status |= EXIT_SEND_ERROR;
528 if (ok_blook(save) && s != 0)
529 savedeadletter(_coll_fp, 1);
530 /* Aborting message, no need to fflush() .. */
531 siglongjmp(_coll_abort, 1);
534 static void
535 collhup(int s)
537 NYD_X; /* Signal handler */
538 UNUSED(s);
540 savedeadletter(_coll_fp, 1);
541 /* Let's pretend nobody else wants to clean up, a true statement at
542 * this time */
543 exit(1);
546 static int
547 putesc(char const *s, FILE *stream)
549 int n = 0, rv = -1;
550 NYD_ENTER;
552 while (s[0] != '\0') {
553 if (s[0] == '\\') {
554 if (s[1] == 't') {
555 if (putc('\t', stream) == EOF)
556 goto jleave;
557 ++n;
558 s += 2;
559 continue;
561 if (s[1] == 'n') {
562 if (putc('\n', stream) == EOF)
563 goto jleave;
564 ++n;
565 s += 2;
566 continue;
569 if (putc(s[0], stream) == EOF)
570 goto jleave;
571 ++n;
572 ++s;
574 if (putc('\n', stream) == EOF)
575 goto jleave;
576 rv = ++n;
577 jleave:
578 NYD_LEAVE;
579 return rv;
582 FL FILE *
583 collect(struct header *hp, int printheaders, struct message *mp,
584 char *quotefile, int doprefix)
586 FILE *fbuf;
587 struct ignoretab *quoteig;
588 int lc, cc, c, t;
589 int volatile escape, getfields;
590 char *linebuf = NULL, *quote = NULL;
591 char const *cp;
592 size_t linesize = 0; /* TODO line pool */
593 long cnt;
594 enum sendaction action;
595 sigset_t oset, nset;
596 sighandler_type savedtop;
597 NYD_ENTER;
599 _coll_fp = NULL;
600 /* Start catching signals from here, but we're still die on interrupts
601 * until we're in the main loop */
602 sigemptyset(&nset);
603 sigaddset(&nset, SIGINT);
604 sigaddset(&nset, SIGHUP);
605 sigprocmask(SIG_BLOCK, &nset, &oset);
606 handlerpush(collint);
607 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
608 safe_signal(SIGINT, collint);
609 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
610 safe_signal(SIGHUP, collhup);
611 /* TODO We do a lot of redundant signal handling, especially
612 * TODO with the command line editor(s); try to merge this */
613 _coll_savetstp = safe_signal(SIGTSTP, collstop);
614 _coll_savettou = safe_signal(SIGTTOU, collstop);
615 _coll_savettin = safe_signal(SIGTTIN, collstop);
616 if (sigsetjmp(_coll_abort, 1))
617 goto jerr;
618 if (sigsetjmp(_coll_jmp, 1))
619 goto jerr;
620 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
622 ++noreset;
623 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
624 0600)) == NULL) {
625 perror(tr(51, "temporary mail file"));
626 goto jerr;
629 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
630 goto jerr;
632 /* If we are going to prompt for a subject, refrain from printing a newline
633 * after the headers (since some people mind) */
634 getfields = 0;
635 if (!(options & OPT_t_FLAG)) {
636 t = GTO | GSUBJECT | GCC | GNL;
637 if (ok_blook(fullnames))
638 t |= GCOMMA;
639 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
640 (ok_blook(ask) || ok_blook(asksub)))
641 t &= ~GNL, getfields |= GSUBJECT;
642 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
643 t &= ~GNL, getfields |= GTO;
644 if (!ok_blook(bsdcompat) && !ok_blook(askatend) &&
645 (options & OPT_INTERACTIVE)) {
646 if (hp->h_bcc == NULL && ok_blook(askbcc))
647 t &= ~GNL, getfields |= GBCC;
648 if (hp->h_cc == NULL && ok_blook(askcc))
649 t &= ~GNL, getfields |= GCC;
651 if (printheaders) {
652 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
653 fflush(stdout);
657 /* Quote an original message */
658 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
659 quoteig = allignore;
660 action = SEND_QUOTE;
661 if (doprefix) {
662 quoteig = fwdignore;
663 if ((cp = ok_vlook(fwdheading)) == NULL)
664 cp = "-------- Original Message --------";
665 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
666 goto jerr;
667 } else if (!strcmp(quote, "noheading")) {
668 /*EMPTY*/;
669 } else if (!strcmp(quote, "headers")) {
670 quoteig = ignore;
671 } else if (!strcmp(quote, "allheaders")) {
672 quoteig = NULL;
673 action = SEND_QUOTE_ALL;
674 } else {
675 cp = hfield1("from", mp);
676 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
677 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE,NULL) < 0)
678 goto jerr;
679 if (fprintf(_coll_fp, tr(52, " wrote:\n\n")) < 0)
680 goto jerr;
683 if (fflush(_coll_fp))
684 goto jerr;
685 cp = ok_vlook(indentprefix);
686 if (cp != NULL && *cp == '\0')
687 cp = "\t";
688 if (sendmp(mp, _coll_fp, quoteig, (doprefix ? NULL : cp), action, NULL)
689 < 0)
690 goto jerr;
693 /* Print what we have sofar also on the terminal */
694 rewind(_coll_fp);
695 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
696 putc(c, stdout);
697 if (fseek(_coll_fp, 0, SEEK_END))
698 goto jerr;
700 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
701 _coll_hadintr = 0;
703 if (!sigsetjmp(_coll_jmp, 1)) {
704 if (getfields)
705 grab_headers(hp, getfields, 1);
706 if (quotefile != NULL) {
707 if (_include_file(NULL, quotefile, &lc, &cc, TRU1) != 0)
708 goto jerr;
710 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
711 rewind(_coll_fp);
712 mesedit('e', hp);
713 goto jcont;
715 } else {
716 /* Come here for printing the after-signal message. Duplicate messages
717 * won't be printed because the write is aborted if we get a SIGTTOU */
718 jcont:
719 if (_coll_hadintr) {
720 fprintf(stderr, tr(53, "\n(Interrupt -- one more to kill letter)\n"));
721 } else {
722 printf(tr(54, "(continue)\n"));
723 fflush(stdout);
727 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
728 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
729 linebuf = srealloc(linebuf, linesize = LINESIZE);
730 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
731 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
732 goto jerr;
734 if (fflush(_coll_fp))
735 goto jerr;
736 goto jout;
739 /* The interactive collect loop */
740 for (;;) {
741 _coll_jmp_p = 1;
742 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
743 _coll_jmp_p = 0;
745 if (cnt < 0) {
746 if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
747 printf(tr(55, "Use \".\" to terminate letter\n"));
748 continue;
750 break;
752 if ((options & OPT_t_FLAG) && cnt == 0) {
753 rewind(_coll_fp);
754 if (makeheader(_coll_fp, hp) != OKAY)
755 goto jerr;
756 rewind(_coll_fp);
757 options &= ~OPT_t_FLAG;
758 continue;
761 _coll_hadintr = 0;
762 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
763 (options & (OPT_INTERACTIVE | OPT_TILDE_FLAG)) &&
764 (ok_blook(dot) || ok_blook(ignoreeof)))
765 break;
766 if (cnt == 0 || linebuf[0] != escape ||
767 !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
768 /* TODO calls putline(), which *always* appends LF;
769 * TODO thus, STDIN with -t will ALWAYS end with LF,
770 * TODO even if no trailing LF and QP CTE */
771 if (putline(_coll_fp, linebuf, cnt) < 0)
772 goto jerr;
773 continue;
776 c = linebuf[1];
777 switch (c) {
778 default:
779 /* On double escape, send a single one. Otherwise, it's an error */
780 if (c == escape) {
781 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
782 goto jerr;
783 else
784 break;
786 fputs(tr(56, "Unknown tilde escape.\n"), stderr);
787 break;
788 case '!':
789 /* Shell escape, send the balance of line to sh -c */
790 c_shell(linebuf + 2);
791 break;
792 case ':':
793 /* FALLTHRU */
794 case '_':
795 /* Escape to command mode, but be nice! */
796 _execute_command(hp, linebuf + 2, cnt - 2);
797 goto jcont;
798 case '.':
799 /* Simulate end of file on input */
800 goto jout;
801 case 'x':
802 /* Same as 'q', but no *DEAD* saving */
803 /* FALLTHRU */
804 case 'q':
805 /* Force a quit, act like an interrupt had happened */
806 ++_coll_hadintr;
807 collint((c == 'x') ? 0 : SIGINT);
808 exit(1);
809 /*NOTREACHED*/
810 case 'h':
811 /* Grab a bunch of headers */
813 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
814 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
815 while (hp->h_to == NULL);
816 goto jcont;
817 case 'H':
818 /* Grab extra headers */
820 grab_headers(hp, GEXTRA, 0);
821 while (check_from_and_sender(hp->h_from, hp->h_sender));
822 goto jcont;
823 case 't':
824 /* Add to the To list */
825 while ((hp->h_to = cat(hp->h_to,
826 checkaddrs(lextract(linebuf + 2, GTO | GFULL)))) == NULL)
828 break;
829 case 's':
830 /* Set the Subject list */
831 cp = linebuf + 2;
832 while (whitechar(*cp))
833 ++cp;
834 hp->h_subject = savestr(cp);
835 break;
836 #ifdef HAVE_DEBUG
837 case 'S':
838 c_sstats(NULL);
839 break;
840 #endif
841 case '@':
842 /* Edit the attachment list */
843 if (linebuf[2] != '\0')
844 hp->h_attach = append_attachments(hp->h_attach, linebuf + 2);
845 else
846 hp->h_attach = edit_attachments(hp->h_attach);
847 break;
848 case 'c':
849 /* Add to the CC list */
850 hp->h_cc = cat(hp->h_cc,
851 checkaddrs(lextract(linebuf + 2, GCC | GFULL)));
852 break;
853 case 'b':
854 /* Add stuff to blind carbon copies list */
855 hp->h_bcc = cat(hp->h_bcc,
856 checkaddrs(lextract(linebuf + 2, GBCC | GFULL)));
857 break;
858 case 'd':
859 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
860 linebuf[linesize - 1] = '\0';
861 /*FALLTHRU*/
862 case 'r':
863 case '<':
864 /* Invoke a file: Search for the file name, then open it and copy the
865 * contents to _coll_fp */
866 cp = linebuf + 2;
867 while (whitechar(*cp))
868 ++cp;
869 if (*cp == '\0') {
870 fputs(tr(57, "Interpolate what file?\n"), stderr);
871 break;
873 if (*cp == '!') {
874 insertcommand(_coll_fp, cp + 1);
875 break;
877 if ((cp = file_expand(cp)) == NULL)
878 break;
879 if (is_dir(cp)) {
880 fprintf(stderr, tr(58, "%s: Directory\n"), cp);
881 break;
883 if ((fbuf = Fopen(cp, "r")) == NULL) {
884 perror(cp);
885 break;
887 printf(tr(59, "\"%s\" "), cp);
888 fflush(stdout);
889 if (_include_file(fbuf, cp, &lc, &cc, FAL0) != 0)
890 goto jerr;
891 printf(tr(60, "%d/%d\n"), lc, cc);
892 break;
893 case 'i':
894 /* Insert a variable into the file */
895 cp = linebuf + 2;
896 while (whitechar(*cp))
897 ++cp;
898 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
899 break;
900 if (putesc(cp, _coll_fp) < 0)
901 goto jerr;
902 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
903 goto jerr;
904 break;
905 case 'a':
906 case 'A':
907 /* Insert the contents of a signature variable */
908 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
909 if (cp != NULL && *cp != '\0') {
910 if (putesc(cp, _coll_fp) < 0)
911 goto jerr;
912 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
913 goto jerr;
915 break;
916 case 'w':
917 /* Write the message on a file */
918 cp = linebuf + 2;
919 while (blankchar(*cp))
920 ++cp;
921 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
922 fputs(tr(61, "Write what file!?\n"), stderr);
923 break;
925 rewind(_coll_fp);
926 if (exwrite(cp, _coll_fp, 1) < 0)
927 goto jerr;
928 break;
929 case 'm':
930 case 'M':
931 case 'f':
932 case 'F':
933 case 'u':
934 case 'U':
935 /* Interpolate the named messages, if we are in receiving mail mode.
936 * Does the standard list processing garbage. If ~f is given, we
937 * don't shift over */
938 if (forward(linebuf + 2, _coll_fp, c) < 0)
939 goto jerr;
940 goto jcont;
941 case 'p':
942 /* Print current state of the message without altering anything */
943 print_collf(_coll_fp, hp);
944 goto jcont;
945 case '|':
946 /* Pipe message through command. Collect output as new message */
947 rewind(_coll_fp);
948 mespipe(linebuf + 2);
949 goto jcont;
950 case 'v':
951 case 'e':
952 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
953 rewind(_coll_fp);
954 mesedit(c, ok_blook(editheaders) ? hp : NULL);
955 goto jcont;
956 case '?':
957 /* Last the lengthy help string. (Very ugly, but take care for
958 * compiler supported string lengths :() */
959 puts(tr(300,
960 "-------------------- ~ ESCAPES ----------------------------\n"
961 "~~ Quote a single tilde\n"
962 "~@ [file ...] Edit attachment list\n"
963 "~b users Add users to \"blind\" cc list\n"
964 "~c users Add users to cc list\n"
965 "~d Read in dead.letter\n"
966 "~e Edit the message buffer\n"
967 "~f messages Read in messages without indenting lines\n"
968 "~F messages Same as ~f, but keep all header lines\n"
969 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
970 puts(tr(301,
971 "~r file Read a file into the message buffer\n"
972 "~p Print the message buffer\n"
973 "~q Abort message composition and save text to dead.letter\n"
974 "~m messages Read in messages with each line indented\n"
975 "~M messages Same as ~m, but keep all header lines\n"
976 "~s subject Set subject\n"
977 "~t users Add users to to list\n"
978 "~u messages Same as ~f, but without any headers\n"
979 "~U messages Same as ~m, but without any headers\n"));
980 puts(tr(302,
981 "~v Invoke display editor on message\n"
982 "~w file Write message onto file\n"
983 "~x Abort message composition and discard text written so far\n"
984 "~!command Invoke the shell\n"
985 "~:command Execute a regular command\n"
986 "-----------------------------------------------------------\n"));
987 break;
991 jout:
992 if (_coll_fp != NULL) {
993 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
994 if (putesc(cp, _coll_fp) < 0)
995 goto jerr;
996 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
997 goto jerr;
999 rewind(_coll_fp);
1001 if (linebuf != NULL)
1002 free(linebuf);
1003 handlerpop();
1004 --noreset;
1005 sigemptyset(&nset);
1006 sigaddset(&nset, SIGINT);
1007 sigaddset(&nset, SIGHUP);
1008 sigprocmask(SIG_BLOCK, &nset, NULL);
1009 safe_signal(SIGINT, _coll_saveint);
1010 safe_signal(SIGHUP, _coll_savehup);
1011 safe_signal(SIGTSTP, _coll_savetstp);
1012 safe_signal(SIGTTOU, _coll_savettou);
1013 safe_signal(SIGTTIN, _coll_savettin);
1014 sigprocmask(SIG_SETMASK, &oset, NULL);
1015 NYD_LEAVE;
1016 return _coll_fp;
1018 jerr:
1019 if (_coll_fp != NULL) {
1020 Fclose(_coll_fp);
1021 _coll_fp = NULL;
1023 goto jout;
1026 FL void
1027 savedeadletter(FILE *fp, int fflush_rewind_first)
1029 char const *cp;
1030 int c;
1031 FILE *dbuf;
1032 ul_it lines, bytes;
1033 NYD_ENTER;
1035 if (fflush_rewind_first) {
1036 fflush(fp);
1037 rewind(fp);
1039 if (fsize(fp) == 0)
1040 goto jleave;
1042 cp = getdeadletter();
1043 c = umask(077);
1044 dbuf = Fopen(cp, "a");
1045 umask(c);
1046 if (dbuf == NULL)
1047 goto jleave;
1049 /* There are problems with dup()ing of file-descriptors for child
1050 * processes. As long as those are not fixed in equal spirit to
1051 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1052 * bugs like (If *record* is set, avoid writing dead content twice..,
1053 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1054 * makes itself comfortable with the *real* offset of the underlaying
1055 * file descriptor. Unfortunately Standard I/O and POSIX don't
1056 * describe a way for that -- fflush();rewind(); won't do it.
1057 * This fseek(END),rewind() pair works around the problem on *BSD */
1058 fseek(fp, 0, SEEK_END);
1059 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 */