nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / collect.c
blob1669f0b9b1d2e9f238ede37e2c1cfeeaf07965aa
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 hokiness with global variables is so that on receipt of an
45 * interrupt signal, the partial message can be salted away on dead.letter */
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;
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;
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 ((long)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) != 0)
286 csi = "";
288 fprintf(obuf, " - [%s, %s%s] %s\n", cp, csi, cs, ap->a_name);
292 jendpipe:
293 if (obuf != stdout) {
294 safe_signal(SIGPIPE, SIG_IGN);
295 Pclose(obuf, TRU1);
296 safe_signal(SIGPIPE, dflpipe);
298 if (lbuf != NULL)
299 free(lbuf);
300 NYD_LEAVE;
303 static int
304 exwrite(char const *name, FILE *fp, int f)
306 FILE *of;
307 int c, lc, rv = -1;
308 long cc;
309 NYD_ENTER;
311 if (f) {
312 printf("\"%s\" ", name);
313 fflush(stdout);
315 if ((of = Fopen(name, "a")) == NULL) {
316 perror(NULL);
317 goto jleave;
320 lc = 0;
321 cc = 0;
322 while ((c = getc(fp)) != EOF) {
323 ++cc;
324 if (c == '\n')
325 ++lc;
326 putc(c, of);
327 if (ferror(of)) {
328 perror(name);
329 Fclose(of);
330 goto jleave;
333 Fclose(of);
334 printf(tr(65, "%d/%ld\n"), lc, cc);
335 fflush(stdout);
336 rv = 0;
337 jleave:
338 NYD_LEAVE;
339 return rv;
342 static enum okay
343 makeheader(FILE *fp, struct header *hp)
345 FILE *nf;
346 int c;
347 enum okay rv = STOP;
348 NYD_ENTER;
350 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
351 NULL) {
352 perror(tr(66, "temporary mail edit file"));
353 goto jleave;
356 extract_header(fp, hp);
357 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
358 putc(c, nf);
359 if (fp != _coll_fp)
360 Fclose(_coll_fp);
361 Fclose(fp);
362 _coll_fp = nf;
363 if (check_from_and_sender(hp->h_from, hp->h_sender))
364 goto jleave;
365 rv = OKAY;
366 jleave:
367 NYD_LEAVE;
368 return rv;
371 static void
372 mesedit(int c, struct header *hp)
374 bool_t saved;
375 sighandler_type sigint;
376 FILE *nf;
377 NYD_ENTER;
379 saved = ok_blook(add_file_recipients);
380 ok_bset(add_file_recipients, TRU1);
382 sigint = safe_signal(SIGINT, SIG_IGN);
383 nf = run_editor(_coll_fp, (off_t)-1, c, 0, hp, NULL, SEND_MBOX, sigint);
384 if (nf != NULL) {
385 if (hp) {
386 rewind(nf);
387 makeheader(nf, hp);
388 } else {
389 fseek(nf, 0L, SEEK_END);
390 Fclose(_coll_fp);
391 _coll_fp = nf;
394 safe_signal(SIGINT, sigint);
396 ok_bset(add_file_recipients, saved);
397 NYD_LEAVE;
400 static void
401 mespipe(char *cmd)
403 FILE *nf;
404 sighandler_type sigint;
405 char const *sh;
406 NYD_ENTER;
408 sigint = safe_signal(SIGINT, SIG_IGN);
410 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
411 NULL) {
412 perror(tr(66, "temporary mail edit file"));
413 goto jout;
416 /* stdin = current message. stdout = new message */
417 if ((sh = ok_vlook(SHELL)) == NULL)
418 sh = XSHELL;
419 fflush(_coll_fp);
420 if (run_command(sh, 0, fileno(_coll_fp), fileno(nf), "-c", cmd, NULL) < 0) {
421 Fclose(nf);
422 goto jout;
425 if (fsize(nf) == 0) {
426 fprintf(stderr, tr(67, "No bytes from \"%s\" !?\n"), cmd);
427 Fclose(nf);
428 goto jout;
431 /* Take new files */
432 fseek(nf, 0L, SEEK_END);
433 Fclose(_coll_fp);
434 _coll_fp = nf;
435 jout:
436 safe_signal(SIGINT, sigint);
437 NYD_LEAVE;
440 static int
441 forward(char *ms, FILE *fp, int f)
443 int *msgvec, rv = 0;
444 struct ignoretab *ig;
445 char const *tabst;
446 enum sendaction action;
447 NYD_ENTER;
449 msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
450 if (msgvec == NULL)
451 goto jleave;
452 if (getmsglist(ms, msgvec, 0) < 0)
453 goto jleave;
454 if (*msgvec == 0) {
455 *msgvec = first(0, MMNORM);
456 if (*msgvec == 0) {
457 fputs(tr(68, "No appropriate messages\n"), stderr);
458 goto jleave;
460 msgvec[1] = 0;
463 if (f == 'f' || f == 'F' || f == 'u')
464 tabst = NULL;
465 else if ((tabst = ok_vlook(indentprefix)) == NULL)
466 tabst = "\t";
467 if (f == 'u' || f == 'U')
468 ig = allignore;
469 else
470 ig = upperchar(f) ? NULL : ignore;
471 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
473 printf(tr(69, "Interpolating:"));
474 for (; *msgvec != 0; ++msgvec) {
475 struct message *mp = message + *msgvec - 1;
477 touch(mp);
478 printf(" %d", *msgvec);
479 if (sendmp(mp, fp, ig, tabst, action, NULL) < 0) {
480 perror(tr(70, "temporary mail file"));
481 rv = -1;
482 break;
485 printf("\n");
486 jleave:
487 NYD_LEAVE;
488 return rv;
491 static void
492 collstop(int s)
494 sighandler_type old_action;
495 sigset_t nset;
496 NYD_X; /* Signal handler */
498 old_action = safe_signal(s, SIG_DFL);
500 sigemptyset(&nset);
501 sigaddset(&nset, s);
502 sigprocmask(SIG_UNBLOCK, &nset, NULL);
503 kill(0, s);
504 sigprocmask(SIG_BLOCK, &nset, NULL);
506 safe_signal(s, old_action);
507 if (_coll_jmp_p) {
508 _coll_jmp_p = 0;
509 _coll_hadintr = 0;
510 siglongjmp(_coll_jmp, 1);
514 static void
515 collint(int s)
517 NYD_X; /* Signal handler */
519 /* the control flow is subtle, because we can be called from ~q */
520 if (_coll_hadintr == 0) {
521 if (ok_blook(ignore)) {
522 puts("@");
523 fflush(stdout);
524 clearerr(stdin);
525 return;
527 _coll_hadintr = 1;
528 siglongjmp(_coll_jmp, 1);
530 exit_status |= 04;
531 if (ok_blook(save) && s != 0)
532 savedeadletter(_coll_fp, 1);
533 /* Aborting message, no need to fflush() .. */
534 siglongjmp(_coll_abort, 1);
537 static void
538 collhup(int s)
540 NYD_X; /* Signal handler */
541 UNUSED(s);
543 savedeadletter(_coll_fp, 1);
544 /* Let's pretend nobody else wants to clean up, a true statement at
545 * this time */
546 exit(1);
549 static int
550 putesc(char const *s, FILE *stream)
552 int n = 0, rv = -1;
553 NYD_ENTER;
555 while (s[0] != '\0') {
556 if (s[0] == '\\') {
557 if (s[1] == 't') {
558 if (putc('\t', stream) == EOF)
559 goto jleave;
560 ++n;
561 s += 2;
562 continue;
564 if (s[1] == 'n') {
565 if (putc('\n', stream) == EOF)
566 goto jleave;
567 ++n;
568 s += 2;
569 continue;
572 if (putc(s[0], stream) == EOF)
573 goto jleave;
574 ++n;
575 ++s;
577 if (putc('\n', stream) == EOF)
578 goto jleave;
579 rv = ++n;
580 jleave:
581 NYD_LEAVE;
582 return rv;
585 FL FILE *
586 collect(struct header *hp, int printheaders, struct message *mp,
587 char *quotefile, int doprefix)
589 FILE *fbuf;
590 struct ignoretab *quoteig;
591 int lc, cc, c, t;
592 int volatile escape, getfields;
593 char *linebuf = NULL, *quote = NULL;
594 char const *cp;
595 size_t linesize = 0;
596 long cnt;
597 enum sendaction action;
598 sigset_t oset, nset;
599 sighandler_type savedtop;
600 NYD_ENTER;
602 _coll_fp = NULL;
603 /* Start catching signals from here, but we're still die on interrupts
604 * until we're in the main loop */
605 sigemptyset(&nset);
606 sigaddset(&nset, SIGINT);
607 sigaddset(&nset, SIGHUP);
608 sigprocmask(SIG_BLOCK, &nset, &oset);
609 handlerpush(collint);
610 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
611 safe_signal(SIGINT, collint);
612 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
613 safe_signal(SIGHUP, collhup);
614 /* TODO We do a lot of redundant signal handling, especially
615 * TODO with the command line editor(s); try to merge this */
616 _coll_savetstp = safe_signal(SIGTSTP, collstop);
617 _coll_savettou = safe_signal(SIGTTOU, collstop);
618 _coll_savettin = safe_signal(SIGTTIN, collstop);
619 if (sigsetjmp(_coll_abort, 1))
620 goto jerr;
621 if (sigsetjmp(_coll_jmp, 1))
622 goto jerr;
623 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
625 ++noreset;
626 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER,
627 0600)) == NULL) {
628 perror(tr(51, "temporary mail file"));
629 goto jerr;
632 if ((cp = ok_vlook(NAIL_HEAD)) != NULL && putesc(cp, _coll_fp) < 0)
633 goto jerr;
635 /* If we are going to prompt for a subject, refrain from printing a newline
636 * after the headers (since some people mind) */
637 getfields = 0;
638 if (!(options & OPT_t_FLAG)) {
639 t = GTO | GSUBJECT | GCC | GNL;
640 if (ok_blook(fullnames))
641 t |= GCOMMA;
642 if (hp->h_subject == NULL && (options & OPT_INTERACTIVE) &&
643 (ok_blook(ask) || ok_blook(asksub)))
644 t &= ~GNL, getfields |= GSUBJECT;
645 if (hp->h_to == NULL && (options & OPT_INTERACTIVE))
646 t &= ~GNL, getfields |= GTO;
647 if (!ok_blook(bsdcompat) && !ok_blook(askatend) &&
648 (options & OPT_INTERACTIVE)) {
649 if (hp->h_bcc == NULL && ok_blook(askbcc))
650 t &= ~GNL, getfields |= GBCC;
651 if (hp->h_cc == NULL && ok_blook(askcc))
652 t &= ~GNL, getfields |= GCC;
654 if (printheaders) {
655 puthead(hp, stdout, t, SEND_TODISP, CONV_NONE, NULL, NULL);
656 fflush(stdout);
660 /* Quote an original message */
661 if (mp != NULL && (doprefix || (quote = ok_vlook(quote)) != NULL)) {
662 quoteig = allignore;
663 action = SEND_QUOTE;
664 if (doprefix) {
665 quoteig = fwdignore;
666 if ((cp = ok_vlook(fwdheading)) == NULL)
667 cp = "-------- Original Message --------";
668 if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
669 goto jerr;
670 } else if (!strcmp(quote, "noheading")) {
671 /*EMPTY*/;
672 } else if (!strcmp(quote, "headers")) {
673 quoteig = ignore;
674 } else if (!strcmp(quote, "allheaders")) {
675 quoteig = NULL;
676 action = SEND_QUOTE_ALL;
677 } else {
678 cp = hfield1("from", mp);
679 if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
680 if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE,NULL) < 0)
681 goto jerr;
682 if (fprintf(_coll_fp, tr(52, " wrote:\n\n")) < 0)
683 goto jerr;
686 if (fflush(_coll_fp))
687 goto jerr;
688 cp = ok_vlook(indentprefix);
689 if (cp != NULL && *cp == '\0')
690 cp = "\t";
691 if (sendmp(mp, _coll_fp, quoteig, (doprefix ? NULL : cp), action, NULL)
692 < 0)
693 goto jerr;
696 /* Print what we have sofar also on the terminal */
697 rewind(_coll_fp);
698 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
699 putc(c, stdout);
700 if (fseek(_coll_fp, 0, SEEK_END))
701 goto jerr;
703 escape = ((cp = ok_vlook(escape)) != NULL) ? *cp : ESCAPE;
704 _coll_hadintr = 0;
706 if (!sigsetjmp(_coll_jmp, 1)) {
707 if (getfields)
708 grab_headers(hp, getfields, 1);
709 if (quotefile != NULL) {
710 if (_include_file(NULL, quotefile, &lc, &cc, TRU1) != 0)
711 goto jerr;
713 if ((options & OPT_INTERACTIVE) && ok_blook(editalong)) {
714 rewind(_coll_fp);
715 mesedit('e', hp);
716 goto jcont;
718 } else {
719 /* Come here for printing the after-signal message. Duplicate messages
720 * won't be printed because the write is aborted if we get a SIGTTOU */
721 jcont:
722 if (_coll_hadintr) {
723 fprintf(stderr, tr(53, "\n(Interrupt -- one more to kill letter)\n"));
724 } else {
725 printf(tr(54, "(continue)\n"));
726 fflush(stdout);
730 /* No tilde escapes, interrupts not expected. Simply copy STDIN */
731 if (!(options & (OPT_INTERACTIVE | OPT_t_FLAG | OPT_TILDE_FLAG))) {
732 linebuf = srealloc(linebuf, linesize = LINESIZE);
733 while ((cnt = fread(linebuf, sizeof *linebuf, linesize, stdin)) > 0) {
734 if ((size_t)cnt != fwrite(linebuf, sizeof *linebuf, cnt, _coll_fp))
735 goto jerr;
737 if (fflush(_coll_fp))
738 goto jerr;
739 goto jout;
742 /* The interactive collect loop */
743 for (;;) {
744 _coll_jmp_p = 1;
745 cnt = readline_input("", FAL0, &linebuf, &linesize, NULL);
746 _coll_jmp_p = 0;
748 if (cnt < 0) {
749 if ((options & OPT_INTERACTIVE) && ok_blook(ignoreeof)) {
750 printf(tr(55, "Use \".\" to terminate letter\n"));
751 continue;
753 break;
755 if ((options & OPT_t_FLAG) && cnt == 0) {
756 rewind(_coll_fp);
757 if (makeheader(_coll_fp, hp) != OKAY)
758 goto jerr;
759 rewind(_coll_fp);
760 options &= ~OPT_t_FLAG;
761 continue;
764 _coll_hadintr = 0;
765 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
766 (options & (OPT_INTERACTIVE | OPT_TILDE_FLAG)) &&
767 (ok_blook(dot) || ok_blook(ignoreeof)))
768 break;
769 if (cnt == 0 || linebuf[0] != escape ||
770 !(options & (OPT_INTERACTIVE | OPT_TILDE_FLAG))) {
771 /* TODO calls putline(), which *always* appends LF;
772 * TODO thus, STDIN with -t will ALWAYS end with LF,
773 * TODO even if no trailing LF and QP CTE */
774 if (putline(_coll_fp, linebuf, cnt) < 0)
775 goto jerr;
776 continue;
779 c = linebuf[1];
780 switch (c) {
781 default:
782 /* On double escape, send a single one. Otherwise, it's an error */
783 if (c == escape) {
784 if (putline(_coll_fp, linebuf + 1, cnt - 1) < 0)
785 goto jerr;
786 else
787 break;
789 fputs(tr(56, "Unknown tilde escape.\n"), stderr);
790 break;
791 case '!':
792 /* Shell escape, send the balance of line to sh -c */
793 shell(linebuf + 2);
794 break;
795 case ':':
796 /* FALLTHRU */
797 case '_':
798 /* Escape to command mode, but be nice! */
799 _execute_command(hp, linebuf + 2, cnt - 2);
800 goto jcont;
801 case '.':
802 /* Simulate end of file on input */
803 goto jout;
804 case 'x':
805 /* Same as 'q', but no dead.letter saving */
806 /* FALLTHRU */
807 case 'q':
808 /* Force a quit, act like an interrupt had happened */
809 ++_coll_hadintr;
810 collint((c == 'x') ? 0 : SIGINT);
811 exit(1);
812 /*NOTREACHED*/
813 case 'h':
814 /* Grab a bunch of headers */
816 grab_headers(hp, GTO | GSUBJECT | GCC | GBCC,
817 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
818 while (hp->h_to == NULL);
819 goto jcont;
820 case 'H':
821 /* Grab extra headers */
823 grab_headers(hp, GEXTRA, 0);
824 while (check_from_and_sender(hp->h_from, hp->h_sender));
825 goto jcont;
826 case 't':
827 /* Add to the To list */
828 while ((hp->h_to = cat(hp->h_to,
829 checkaddrs(lextract(linebuf + 2, GTO | GFULL)))) == NULL)
831 break;
832 case 's':
833 /* Set the Subject list */
834 cp = linebuf + 2;
835 while (whitechar(*cp))
836 ++cp;
837 hp->h_subject = savestr(cp);
838 break;
839 #ifdef HAVE_DEBUG
840 case 'S':
841 c_sstats(NULL);
842 break;
843 #endif
844 case '@':
845 /* Edit the attachment list */
846 if (linebuf[2] != '\0')
847 hp->h_attach = append_attachments(hp->h_attach, linebuf + 2);
848 else
849 hp->h_attach = edit_attachments(hp->h_attach);
850 break;
851 case 'c':
852 /* Add to the CC list */
853 hp->h_cc = cat(hp->h_cc,
854 checkaddrs(lextract(linebuf + 2, GCC | GFULL)));
855 break;
856 case 'b':
857 /* Add stuff to blind carbon copies list */
858 hp->h_bcc = cat(hp->h_bcc,
859 checkaddrs(lextract(linebuf + 2, GBCC | GFULL)));
860 break;
861 case 'd':
862 strncpy(linebuf + 2, getdeadletter(), linesize - 2);
863 linebuf[linesize - 1]='\0';
864 /*FALLTHRU*/
865 case 'r':
866 case '<':
867 /* Invoke a file: Search for the file name, then open it and copy the
868 * contents to _coll_fp */
869 cp = linebuf + 2;
870 while (whitechar(*cp))
871 cp++;
872 if (*cp == '\0') {
873 fputs(tr(57, "Interpolate what file?\n"), stderr);
874 break;
876 if (*cp == '!') {
877 insertcommand(_coll_fp, cp + 1);
878 break;
880 if ((cp = file_expand(cp)) == NULL)
881 break;
882 if (is_dir(cp)) {
883 fprintf(stderr, tr(58, "%s: Directory\n"), cp);
884 break;
886 if ((fbuf = Fopen(cp, "r")) == NULL) {
887 perror(cp);
888 break;
890 printf(tr(59, "\"%s\" "), cp);
891 fflush(stdout);
892 if (_include_file(fbuf, cp, &lc, &cc, FAL0) != 0)
893 goto jerr;
894 printf(tr(60, "%d/%d\n"), lc, cc);
895 break;
896 case 'i':
897 /* Insert a variable into the file */
898 cp = linebuf + 2;
899 while (whitechar(*cp))
900 ++cp;
901 if ((cp = vok_vlook(cp)) == NULL || *cp == '\0')
902 break;
903 if (putesc(cp, _coll_fp) < 0)
904 goto jerr;
905 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
906 goto jerr;
907 break;
908 case 'a':
909 case 'A':
910 /* Insert the contents of a signature variable */
911 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
912 if (cp != NULL && *cp != '\0') {
913 if (putesc(cp, _coll_fp) < 0)
914 goto jerr;
915 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
916 goto jerr;
918 break;
919 case 'w':
920 /* Write the message on a file */
921 cp = linebuf + 2;
922 while (blankchar(*cp))
923 ++cp;
924 if (*cp == '\0' || (cp = file_expand(cp)) == NULL) {
925 fputs(tr(61, "Write what file!?\n"), stderr);
926 break;
928 rewind(_coll_fp);
929 if (exwrite(cp, _coll_fp, 1) < 0)
930 goto jerr;
931 break;
932 case 'm':
933 case 'M':
934 case 'f':
935 case 'F':
936 case 'u':
937 case 'U':
938 /* Interpolate the named messages, if we are in receiving mail mode.
939 * Does the standard list processing garbage. If ~f is given, we
940 * don't shift over */
941 if (forward(linebuf + 2, _coll_fp, c) < 0)
942 goto jerr;
943 goto jcont;
944 case 'p':
945 /* Print current state of the message without altering anything */
946 print_collf(_coll_fp, hp);
947 goto jcont;
948 case '|':
949 /* Pipe message through command. Collect output as new message */
950 rewind(_coll_fp);
951 mespipe(linebuf + 2);
952 goto jcont;
953 case 'v':
954 case 'e':
955 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
956 rewind(_coll_fp);
957 mesedit(c, ok_blook(editheaders) ? hp : NULL);
958 goto jcont;
959 case '?':
960 /* Last the lengthy help string. (Very ugly, but take care for
961 * compiler supported string lengths :() */
962 puts(tr(300,
963 "-------------------- ~ ESCAPES ----------------------------\n"
964 "~~ Quote a single tilde\n"
965 "~@ [file ...] Edit attachment list\n"
966 "~b users Add users to \"blind\" cc list\n"
967 "~c users Add users to cc list\n"
968 "~d Read in dead.letter\n"
969 "~e Edit the message buffer\n"
970 "~f messages Read in messages without indenting lines\n"
971 "~F messages Same as ~f, but keep all header lines\n"
972 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n"));
973 puts(tr(301,
974 "~r file Read a file into the message buffer\n"
975 "~p Print the message buffer\n"
976 "~q Abort message composition and save text to dead.letter\n"
977 "~m messages Read in messages with each line indented\n"
978 "~M messages Same as ~m, but keep all header lines\n"
979 "~s subject Set subject\n"
980 "~t users Add users to to list\n"
981 "~u messages Same as ~f, but without any headers\n"
982 "~U messages Same as ~m, but without any headers\n"));
983 puts(tr(302,
984 "~v Invoke display editor on message\n"
985 "~w file Write message onto file\n"
986 "~x Abort message composition and discard text written so far\n"
987 "~!command Invoke the shell\n"
988 "~:command Execute a regular command\n"
989 "-----------------------------------------------------------\n"));
990 break;
994 jout:
995 if (_coll_fp != NULL) {
996 if ((cp = ok_vlook(NAIL_TAIL)) != NULL) {
997 if (putesc(cp, _coll_fp) < 0)
998 goto jerr;
999 if ((options & OPT_INTERACTIVE) && putesc(cp, stdout) < 0)
1000 goto jerr;
1002 rewind(_coll_fp);
1004 if (linebuf != NULL)
1005 free(linebuf);
1006 handlerpop();
1007 --noreset;
1008 sigemptyset(&nset);
1009 sigaddset(&nset, SIGINT);
1010 sigaddset(&nset, SIGHUP);
1011 sigprocmask(SIG_BLOCK, &nset, NULL);
1012 safe_signal(SIGINT, _coll_saveint);
1013 safe_signal(SIGHUP, _coll_savehup);
1014 safe_signal(SIGTSTP, _coll_savetstp);
1015 safe_signal(SIGTTOU, _coll_savettou);
1016 safe_signal(SIGTTIN, _coll_savettin);
1017 sigprocmask(SIG_SETMASK, &oset, NULL);
1018 NYD_LEAVE;
1019 return _coll_fp;
1021 jerr:
1022 if (_coll_fp != NULL) {
1023 Fclose(_coll_fp);
1024 _coll_fp = NULL;
1026 goto jout;
1029 FL void
1030 savedeadletter(FILE *fp, int fflush_rewind_first)
1032 char const *cp;
1033 int c;
1034 FILE *dbuf;
1035 ul_it lines, bytes;
1036 NYD_ENTER;
1038 if (fflush_rewind_first) {
1039 fflush(fp);
1040 rewind(fp);
1042 if (fsize(fp) == 0)
1043 goto jleave;
1045 cp = getdeadletter();
1046 c = umask(077);
1047 dbuf = Fopen(cp, "a");
1048 umask(c);
1049 if (dbuf == NULL)
1050 goto jleave;
1052 /* There are problems with dup()ing of file-descriptors for child
1053 * processes. As long as those are not fixed in equal spirit to
1054 * (outof(): FIX and recode.., 2012-10-04), and to avoid reviving of
1055 * bugs like (If *record* is set, avoid writing dead content twice..,
1056 * 2012-09-14), we have to somehow accomplish that the FILE* fp
1057 * makes itself comfortable with the *real* offset of the underlaying
1058 * file descriptor. Unfortunately Standard I/O and POSIX don't
1059 * describe a way for that -- fflush();rewind(); won't do it.
1060 * This fseek(END),rewind() pair works around the problem on *BSD */
1061 fseek(fp, 0, SEEK_END);
1062 rewind(fp);
1064 printf("\"%s\" ", cp);
1065 for (lines = bytes = 0; (c = getc(fp)) != EOF; ++bytes) {
1066 putc(c, dbuf);
1067 if (c == '\n')
1068 ++lines;
1070 printf("%lu/%lu\n", lines, bytes);
1072 Fclose(dbuf);
1073 rewind(fp);
1074 jleave:
1075 NYD_LEAVE;
1078 /* vim:set fenc=utf-8:s-it-mode */