make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / collect.c
blobeeac8e24b2de4adf98e708d11f4e63c68d4a55d8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Collect input from standard input, handling ~ escapes.
3 *@ TODO This needs a complete rewrite, with carriers, etc.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 * SPDX-License-Identifier: BSD-3-Clause
8 */
9 /*
10 * Copyright (c) 1980, 1993
11 * The Regents of the University of California. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
37 #undef n_FILE
38 #define n_FILE collect
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 struct a_coll_fmt_ctx{ /* xxx This is temporary until v15 has objects */
45 char const *cfc_fmt;
46 FILE *cfc_fp;
47 struct message *cfc_mp;
48 char *cfc_cumul;
49 char *cfc_addr;
50 char *cfc_real;
51 char *cfc_full;
52 char *cfc_date;
53 char const *cfc_msgid; /* Or NULL */
56 struct a_coll_ocs_arg{
57 sighandler_type coa_opipe;
58 sighandler_type coa_oint;
59 FILE *coa_stdin; /* The descriptor (pipe(2)+Fdopen()) we read from */
60 FILE *coa_stdout; /* The Popen()ed pipe through which we write to the hook */
61 int coa_pipe[2]; /* ..backing .coa_stdin */
62 si8_t *coa_senderr; /* Set to 1 on failure */
63 char coa_cmd[n_VFIELD_SIZE(0)];
66 /* The following hookiness with global variables is so that on receipt of an
67 * interrupt signal, the partial message can be salted away on *DEAD* */
69 static sighandler_type _coll_saveint; /* Previous SIGINT value */
70 static sighandler_type _coll_savehup; /* Previous SIGHUP value */
71 static FILE *_coll_fp; /* File for saving away */
72 static int volatile _coll_hadintr; /* Have seen one SIGINT so far */
73 static sigjmp_buf _coll_jmp; /* To get back to work */
74 static sigjmp_buf _coll_abort; /* To end collection with error */
75 static char const *a_coll_ocs__macname; /* *on-compose-splice* */
77 /* Handle `~:', `~_' and some hooks; hp may be NULL */
78 static void _execute_command(struct header *hp, char const *linebuf,
79 size_t linesize);
81 /* Return errno */
82 static si32_t a_coll_include_file(char const *name, bool_t indent,
83 bool_t writestat);
85 /* Execute cmd and insert its standard output into fp, return errno */
86 static si32_t a_coll_insert_cmd(FILE *fp, char const *cmd);
88 /* ~p command */
89 static void print_collf(FILE *collf, struct header *hp);
91 /* Write a file, ex-like if f set */
92 static si32_t a_coll_write(char const *name, FILE *fp, int f);
94 /* *message-inject-head* */
95 static bool_t a_coll_message_inject_head(FILE *fp);
97 /* With bells and whistles */
98 static bool_t a_coll_quote_message(FILE *fp, struct message *mp, bool_t isfwd);
100 /* *{forward,quote}-inject-{head,tail}*.
101 * fmt may be NULL or the empty string, in which case no output is produced */
102 static bool_t a_coll__fmt_inj(struct a_coll_fmt_ctx const *cfcp);
104 /* Parse off the message header from fp and store relevant fields in hp,
105 * replace _coll_fp with a shiny new version without any header.
106 * Takes care for closing of fp and _coll_fp as necessary */
107 static bool_t a_coll_makeheader(FILE *fp, struct header *hp,
108 si8_t *checkaddr_err, bool_t do_delayed_due_t);
110 /* Edit the message being collected on fp.
111 * If c=='|' pipecmd must be set and is passed through to n_run_editor().
112 * On successful return, make the edit file the new temp file; return errno */
113 static si32_t a_coll_edit(int c, struct header *hp, char const *pipecmd);
115 /* Pipe the message through the command. Old message is on stdin of command,
116 * new message collected from stdout. Shell must return 0 to accept new msg */
117 static si32_t a_coll_pipe(char const *cmd);
119 /* Interpolate the named messages into the current message, possibly doing
120 * indent stuff. The flag argument is one of the command escapes: [mMfFuU].
121 * Return errno */
122 static si32_t a_coll_forward(char const *ms, FILE *fp, int f);
124 /* On interrupt, come here to save the partial message in ~/dead.letter.
125 * Then jump out of the collection loop */
126 static void _collint(int s);
128 static void collhup(int s);
130 /* ~[AaIi], *message-inject-**: put value, expand \[nt] if *posix* */
131 static bool_t a_coll_putesc(char const *s, bool_t addnl, FILE *stream);
133 /* *on-compose-splice* driver and *on-compose-splice(-shell)?* finalizer */
134 static int a_coll_ocs__mac(void);
135 static void a_coll_ocs__finalize(void *vp);
137 static void
138 _execute_command(struct header *hp, char const *linebuf, size_t linesize){
139 /* The problem arises if there are rfc822 message attachments and the
140 * user uses `~:' to change the current file. TODO Unfortunately we
141 * TODO cannot simply keep a pointer to, or increment a reference count
142 * TODO of the current `file' (mailbox that is) object, because the
143 * TODO codebase doesn't deal with that at all; so, until some far
144 * TODO later time, copy the name of the path, and warn the user if it
145 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
146 * TODO copy the message attachments over to temporary files, but that
147 * TODO would require more changes so that the user still can recognize
148 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
149 struct n_sigman sm;
150 struct attachment *ap;
151 char * volatile mnbuf;
152 NYD_ENTER;
154 n_UNUSED(linesize);
155 mnbuf = NULL;
157 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_HUP | n_SIGMAN_INT | n_SIGMAN_QUIT){
158 case 0:
159 break;
160 default:
161 n_pstate_err_no = n_ERR_INTR;
162 n_pstate_ex_no = 1;
163 goto jleave;
166 /* If the above todo is worked, remove or outsource to attachment.c! */
167 if(hp != NULL && (ap = hp->h_attach) != NULL) do
168 if(ap->a_msgno){
169 mnbuf = sstrdup(mailname);
170 break;
172 while((ap = ap->a_flink) != NULL);
174 n_go_command(n_GO_INPUT_CTX_COMPOSE, linebuf);
176 n_sigman_cleanup_ping(&sm);
177 jleave:
178 if(mnbuf != NULL){
179 if(strcmp(mnbuf, mailname))
180 n_err(_("Mailbox changed: it is likely that existing "
181 "rfc822 attachments became invalid!\n"));
182 n_free(mnbuf);
184 NYD_LEAVE;
185 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
188 static si32_t
189 a_coll_include_file(char const *name, bool_t indent, bool_t writestat){
190 FILE *fbuf;
191 char const *heredb, *indb;
192 size_t linesize, heredl, indl, cnt, linelen;
193 char *linebuf;
194 si64_t lc, cc;
195 si32_t rv;
196 NYD_ENTER;
198 rv = n_ERR_NONE;
199 lc = cc = 0;
200 linebuf = NULL; /* TODO line pool */
201 linesize = 0;
202 heredb = NULL;
203 heredl = 0;
205 /* The -M case is special */
206 if(name == (char*)-1){
207 fbuf = n_stdin;
208 name = n_hy;
209 }else if(name[0] == '-' &&
210 (name[1] == '\0' || blankspacechar(name[1]))){
211 fbuf = n_stdin;
212 if(name[1] == '\0'){
213 if(!(n_psonce & n_PSO_INTERACTIVE)){
214 n_err(_("~< -: HERE-delimiter required in non-interactive mode\n"));
215 rv = n_ERR_INVAL;
216 goto jleave;
218 }else{
219 for(heredb = &name[2]; *heredb != '\0' && blankspacechar(*heredb);
220 ++heredb)
222 if((heredl = strlen(heredb)) == 0){
223 jdelim_empty:
224 n_err(_("~< - HERE-delimiter: delimiter must not be empty\n"));
225 rv = n_ERR_INVAL;
226 goto jleave;
229 if(*heredb == '\''){
230 for(indb = ++heredb; *indb != '\0' && *indb != '\''; ++indb)
232 if(*indb == '\0'){
233 n_err(_("~< - HERE-delimiter: missing trailing quote\n"));
234 rv = n_ERR_INVAL;
235 goto jleave;
236 }else if(indb[1] != '\0'){
237 n_err(_("~< - HERE-delimiter: trailing characters after "
238 "quote\n"));
239 rv = n_ERR_INVAL;
240 goto jleave;
242 if((heredl = PTR2SIZE(indb - heredb)) == 0)
243 goto jdelim_empty;
244 heredb = savestrbuf(heredb, heredl);
247 name = n_hy;
248 }else if((fbuf = Fopen(name, "r")) == NULL){
249 n_perr(name, rv = n_err_no);
250 goto jleave;
253 indl = indent ? strlen(indb = ok_vlook(indentprefix)) : 0;
255 if(fbuf != n_stdin)
256 cnt = fsize(fbuf);
257 while(fgetline(&linebuf, &linesize, (fbuf == n_stdin ? NULL : &cnt),
258 &linelen, fbuf, 0) != NULL){
259 if(heredl > 0 && heredl == linelen - 1 &&
260 !memcmp(heredb, linebuf, heredl)){
261 heredb = NULL;
262 break;
265 if(indl > 0){
266 if(fwrite(indb, sizeof *indb, indl, _coll_fp) != indl){
267 rv = n_err_no;
268 goto jleave;
270 cc += indl;
273 if(fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen){
274 rv = n_err_no;
275 goto jleave;
277 cc += linelen;
278 ++lc;
280 if(fflush(_coll_fp)){
281 rv = n_err_no;
282 goto jleave;
285 if(heredb != NULL)
286 rv = n_ERR_NOTOBACCO;
287 jleave:
288 if(linebuf != NULL)
289 n_free(linebuf);
290 if(fbuf != NULL){
291 if(fbuf != n_stdin)
292 Fclose(fbuf);
293 else if(heredl > 0)
294 clearerr(n_stdin);
297 if(writestat)
298 fprintf(n_stdout, "%s%s %" PRId64 "/%" PRId64 "\n",
299 n_shexp_quote_cp(name, FAL0), (rv ? " " n_ERROR : n_empty), lc, cc);
300 NYD_LEAVE;
301 return rv;
304 static si32_t
305 a_coll_insert_cmd(FILE *fp, char const *cmd){
306 FILE *ibuf;
307 si64_t lc, cc;
308 si32_t rv;
309 NYD_ENTER;
311 rv = n_ERR_NONE;
312 lc = cc = 0;
314 if((ibuf = Popen(cmd, "r", ok_vlook(SHELL), NULL, 0)) != NULL){
315 int c;
317 while((c = getc(ibuf)) != EOF){ /* XXX bytewise, yuck! */
318 if(putc(c, fp) == EOF){
319 rv = n_err_no;
320 break;
322 ++cc;
323 if(c == '\n')
324 ++lc;
326 if(!feof(ibuf) || ferror(ibuf)){
327 if(rv == n_ERR_NONE)
328 rv = n_ERR_IO;
330 if(!Pclose(ibuf, TRU1)){
331 if(rv == n_ERR_NONE)
332 rv = n_ERR_IO;
334 }else
335 n_perr(cmd, rv = n_err_no);
337 fprintf(n_stdout, "CMD%s %" PRId64 "/%" PRId64 "\n",
338 (rv == n_ERR_NONE ? n_empty : " " n_ERROR), lc, cc);
339 NYD_LEAVE;
340 return rv;
343 static void
344 print_collf(FILE *cf, struct header *hp)
346 char *lbuf;
347 FILE *obuf;
348 size_t cnt, linesize, linelen;
349 NYD_ENTER;
351 fflush_rewind(cf);
352 cnt = (size_t)fsize(cf);
354 if((obuf = Ftmp(NULL, "collfp", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
355 n_perr(_("Can't create temporary file for `~p' command"), 0);
356 goto jleave;
359 hold_all_sigs();
361 fprintf(obuf, _("-------\nMessage contains:\n")); /* XXX112 */
362 n_puthead(TRU1, hp, obuf,
363 (GIDENT | GTO | GSUBJECT | GCC | GBCC | GBCC_IS_FCC | GNL | GFILES |
364 GCOMMA), SEND_TODISP, CONV_NONE, NULL, NULL);
366 lbuf = NULL;
367 linesize = 0;
368 while(fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
369 prout(lbuf, linelen, obuf);
370 if(lbuf != NULL)
371 n_free(lbuf);
373 if(hp->h_attach != NULL){
374 fputs(_("-------\nAttachments:\n"), obuf);
375 n_attachment_list_print(hp->h_attach, obuf);
378 rele_all_sigs();
380 page_or_print(obuf, 0);
382 Fclose(obuf);
383 jleave:
384 NYD_LEAVE;
387 static si32_t
388 a_coll_write(char const *name, FILE *fp, int f)
390 FILE *of;
391 int c;
392 si64_t lc, cc;
393 si32_t rv;
394 NYD_ENTER;
396 rv = n_ERR_NONE;
398 if(f) {
399 fprintf(n_stdout, "%s ", n_shexp_quote_cp(name, FAL0));
400 fflush(n_stdout);
403 if ((of = Fopen(name, "a")) == NULL) {
404 n_perr(name, rv = n_err_no);
405 goto jerr;
408 lc = cc = 0;
409 while ((c = getc(fp)) != EOF) {
410 ++cc;
411 if (c == '\n')
412 ++lc;
413 if (putc(c, of) == EOF) {
414 n_perr(name, rv = n_err_no);
415 goto jerr;
418 fprintf(n_stdout, "%" PRId64 "/%" PRId64 "\n", lc, cc);
420 jleave:
421 if(of != NULL)
422 Fclose(of);
423 fflush(n_stdout);
424 NYD_LEAVE;
425 return rv;
426 jerr:
427 putc('-', n_stdout);
428 putc('\n', n_stdout);
429 goto jleave;
432 static bool_t
433 a_coll_message_inject_head(FILE *fp){
434 bool_t rv;
435 char const *cp, *cp_obsolete;
436 NYD2_ENTER;
438 cp_obsolete = ok_vlook(NAIL_HEAD);
439 if(cp_obsolete != NULL)
440 n_OBSOLETE(_("please use *message-inject-head*, not *NAIL_HEAD*"));
442 if(((cp = ok_vlook(message_inject_head)) != NULL ||
443 (cp = cp_obsolete) != NULL) && !a_coll_putesc(cp, TRU1, fp))
444 rv = FAL0;
445 else
446 rv = TRU1;
447 NYD2_LEAVE;
448 return rv;
451 static bool_t
452 a_coll_quote_message(FILE *fp, struct message *mp, bool_t isfwd){
453 struct a_coll_fmt_ctx cfc;
454 char const *cp;
455 struct n_ignore const *quoteitp;
456 enum sendaction action;
457 bool_t rv;
458 NYD_ENTER;
460 rv = FAL0;
462 if(isfwd || (cp = ok_vlook(quote)) != NULL){
463 quoteitp = n_IGNORE_ALL;
464 action = SEND_QUOTE;
466 if(isfwd){
467 char const *cp_v15compat;
469 if((cp_v15compat = ok_vlook(fwdheading)) != NULL)
470 n_OBSOLETE(_("please use *forward-inject-head* instead of "
471 "*fwdheading*"));
472 if((cp = ok_vlook(forward_inject_head)) == NULL &&
473 (cp = cp_v15compat) == NULL)
474 cp = n_FORWARD_INJECT_HEAD;
475 quoteitp = n_IGNORE_FWD;
476 }else{
477 if(!strcmp(cp, "noheading")){
478 cp = NULL;
479 }else if(!strcmp(cp, "headers")){
480 quoteitp = n_IGNORE_TYPE;
481 cp = NULL;
482 }else if(!strcmp(cp, "allheaders")){
483 quoteitp = NULL;
484 action = SEND_QUOTE_ALL;
485 cp = NULL;
486 }else if((cp = ok_vlook(quote_inject_head)) == NULL)
487 cp = n_QUOTE_INJECT_HEAD;
489 /* We we pass through our formatter? */
490 if((cfc.cfc_fmt = cp) != NULL){
491 /* TODO In v15 [-textual_-]sender_info() should only create a list
492 * TODO of matching header objects, and the formatter should simply
493 * TODO iterate over this list and call OBJ->to_ui_str(FLAGS) or so.
494 * TODO For now fully initialize this thing once (grrrr!!) */
495 cfc.cfc_fp = fp;
496 cfc.cfc_mp = mp;
497 n_header_textual_sender_info(cfc.cfc_mp = mp, &cfc.cfc_cumul,
498 &cfc.cfc_addr, &cfc.cfc_real, &cfc.cfc_full, NULL);
499 cfc.cfc_date = n_header_textual_date_info(mp, NULL);
500 /* C99 */{
501 struct name *np;
502 char const *msgid;
504 if((msgid = hfield1("message-id", mp)) != NULL &&
505 (np = lextract(msgid, GREF)) != NULL)
506 msgid = np->n_name;
507 else
508 msgid = NULL;
509 cfc.cfc_msgid = msgid;
512 if(!a_coll__fmt_inj(&cfc) || fflush(fp))
513 goto jleave;
516 if(sendmp(mp, fp, quoteitp, (isfwd ? NULL : ok_vlook(indentprefix)),
517 action, NULL) < 0)
518 goto jleave;
520 if(isfwd){
521 if((cp = ok_vlook(forward_inject_tail)) == NULL)
522 cp = n_FORWARD_INJECT_TAIL;
523 }else if(cp != NULL && (cp = ok_vlook(quote_inject_tail)) == NULL)
524 cp = n_QUOTE_INJECT_TAIL;
525 if((cfc.cfc_fmt = cp) != NULL && (!a_coll__fmt_inj(&cfc) || fflush(fp)))
526 goto jleave;
529 rv = TRU1;
530 jleave:
531 NYD_LEAVE;
532 return rv;
535 static bool_t
536 a_coll__fmt_inj(struct a_coll_fmt_ctx const *cfcp){
537 struct quoteflt qf;
538 struct n_string s_b, *sp;
539 char c;
540 char const *fmt;
541 NYD_ENTER;
543 if((fmt = cfcp->cfc_fmt) == NULL || *fmt == '\0')
544 goto jleave;
546 sp = n_string_book(n_string_creat_auto(&s_b), 127);
548 while((c = *fmt++) != '\0'){
549 if(c != '%' || (c = *fmt++) == '%'){
550 jwrite_char:
551 sp = n_string_push_c(sp, c);
552 }else switch(c){
553 case 'a':
554 sp = n_string_push_cp(sp, cfcp->cfc_addr);
555 break;
556 case 'd':
557 sp = n_string_push_cp(sp, cfcp->cfc_date);
558 break;
559 case 'f':
560 sp = n_string_push_cp(sp, cfcp->cfc_full);
561 break;
562 case 'i':
563 if(cfcp->cfc_msgid != NULL)
564 sp = n_string_push_cp(sp, cfcp->cfc_msgid);
565 break;
566 case 'n':
567 sp = n_string_push_cp(sp, cfcp->cfc_cumul);
568 break;
569 case 'r':
570 sp = n_string_push_cp(sp, cfcp->cfc_real);
571 break;
572 case '\0':
573 --fmt;
574 c = '%';
575 goto jwrite_char;
576 default:
577 n_err(_("*{forward,quote}-inject-{head,tail}*: "
578 "unknown format: %c (in: %s)\n"),
579 c, n_shexp_quote_cp(cfcp->cfc_fmt, FAL0));
580 goto jwrite_char;
584 quoteflt_init(&qf, NULL, FAL0);
585 quoteflt_reset(&qf, cfcp->cfc_fp);
586 if(quoteflt_push(&qf, sp->s_dat, sp->s_len) < 0 || quoteflt_flush(&qf) < 0)
587 cfcp = NULL;
588 quoteflt_destroy(&qf);
590 /*n_string_gut(sp);*/
591 jleave:
592 NYD_LEAVE;
593 return (cfcp != NULL);
596 static bool_t
597 a_coll_makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err,
598 bool_t do_delayed_due_t)
600 FILE *nf;
601 int c;
602 bool_t rv;
603 NYD_ENTER;
605 rv = FAL0;
607 if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
608 n_perr(_("temporary mail edit file"), 0);
609 goto jleave;
612 n_header_extract(((do_delayed_due_t
613 ? n_HEADER_EXTRACT_FULL | n_HEADER_EXTRACT_PREFILL_RECEIVERS
614 : n_HEADER_EXTRACT_EXTENDED) |
615 n_HEADER_EXTRACT_IGNORE_SHELL_COMMENTS), fp, hp, checkaddr_err);
616 if (checkaddr_err != NULL && *checkaddr_err != 0)
617 goto jleave;
619 /* In template mode some things have been delayed until the template has
620 * been read */
621 if(do_delayed_due_t){
622 char const *cp;
624 if((cp = ok_vlook(on_compose_enter)) != NULL){
625 setup_from_and_sender(hp);
626 temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
627 hp);
630 if(!a_coll_message_inject_head(nf))
631 goto jleave;
634 while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
635 putc(c, nf);
637 if (fp != _coll_fp)
638 Fclose(_coll_fp);
639 Fclose(fp);
640 _coll_fp = nf;
641 nf = NULL;
643 if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
644 goto jleave;
645 rv = TRU1;
646 jleave:
647 if(nf != NULL)
648 Fclose(nf);
649 NYD_LEAVE;
650 return rv;
653 static si32_t
654 a_coll_edit(int c, struct header *hp, char const *pipecmd) /* TODO errret */
656 struct n_sigman sm;
657 FILE *nf;
658 sighandler_type volatile sigint;
659 struct name *saved_in_reply_to;
660 bool_t saved_filrec;
661 si32_t volatile rv;
662 NYD_ENTER;
664 rv = n_ERR_NONE;
665 n_UNINIT(sigint, SIG_ERR);
666 saved_filrec = ok_blook(add_file_recipients);
668 n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL){
669 case 0:
670 sigint = safe_signal(SIGINT, SIG_IGN);
671 break;
672 default:
673 rv = n_ERR_INTR;
674 goto jleave;
677 if(!saved_filrec)
678 ok_bset(add_file_recipients);
680 saved_in_reply_to = NULL;
681 if(hp != NULL){
682 struct name *np;
684 if((np = hp->h_in_reply_to) == NULL)
685 hp->h_in_reply_to = np = n_header_setup_in_reply_to(hp);
686 if(np != NULL)
687 saved_in_reply_to = ndup(np, np->n_type);
690 rewind(_coll_fp);
691 nf = n_run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint,
692 pipecmd);
693 if(nf != NULL){
694 if(hp != NULL){
695 /* Overtaking of nf->_coll_fp is done by a_coll_makeheader()! */
696 if(!a_coll_makeheader(nf, hp, NULL, FAL0))
697 rv = n_ERR_INVAL;
698 /* Break the thread if In-Reply-To: has been modified */
699 if(hp->h_in_reply_to == NULL || (saved_in_reply_to != NULL &&
700 asccasecmp(hp->h_in_reply_to->n_fullname,
701 saved_in_reply_to->n_fullname)))
702 hp->h_ref = NULL;
703 }else{
704 fseek(nf, 0L, SEEK_END);
705 Fclose(_coll_fp);
706 _coll_fp = nf;
708 }else
709 rv = n_ERR_CHILD;
711 n_sigman_cleanup_ping(&sm);
712 jleave:
713 if(!saved_filrec)
714 ok_bclear(add_file_recipients);
715 safe_signal(SIGINT, sigint);
716 NYD_LEAVE;
717 n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
718 return rv;
721 static si32_t
722 a_coll_pipe(char const *cmd)
724 int ws;
725 FILE *nf;
726 sighandler_type sigint;
727 si32_t rv;
728 NYD_ENTER;
730 rv = n_ERR_NONE;
731 sigint = safe_signal(SIGINT, SIG_IGN);
733 if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
734 jperr:
735 n_perr(_("temporary mail edit file"), rv = n_err_no);
736 goto jout;
739 /* stdin = current message. stdout = new message */
740 if(fflush(_coll_fp) == EOF)
741 goto jperr;
742 rewind(_coll_fp);
743 if (n_child_run(ok_vlook(SHELL), 0, fileno(_coll_fp), fileno(nf), "-c",
744 cmd, NULL, NULL, &ws) < 0 || WEXITSTATUS(ws) != 0) {
745 Fclose(nf);
746 rv = n_ERR_CHILD;
747 goto jout;
750 if (fsize(nf) == 0) {
751 n_err(_("No bytes from %s !?\n"), n_shexp_quote_cp(cmd, FAL0));
752 Fclose(nf);
753 rv = n_ERR_NODATA;
754 goto jout;
757 /* Take new files */
758 fseek(nf, 0L, SEEK_END);
759 Fclose(_coll_fp);
760 _coll_fp = nf;
761 jout:
762 safe_signal(SIGINT, sigint);
763 NYD_LEAVE;
764 return rv;
767 static si32_t
768 a_coll_forward(char const *ms, FILE *fp, int f)
770 int *msgvec, rv = 0;
771 struct n_ignore const *itp;
772 char const *tabst;
773 enum sendaction action;
774 NYD_ENTER;
776 if ((rv = n_getmsglist(ms, n_msgvec, 0, NULL)) < 0) {
777 rv = n_ERR_NOENT; /* XXX not really, should be handled there! */
778 goto jleave;
780 if (rv == 0) {
781 *n_msgvec = first(0, MMNORM);
782 if (*n_msgvec == 0) {
783 n_err(_("No appropriate messages\n"));
784 rv = n_ERR_NOENT;
785 goto jleave;
787 rv = 1;
789 msgvec = n_autorec_calloc(rv +1, sizeof *msgvec);
790 while(rv-- > 0)
791 msgvec[rv] = n_msgvec[rv];
792 rv = 0;
794 if (f == 'f' || f == 'F' || f == 'u')
795 tabst = NULL;
796 else
797 tabst = ok_vlook(indentprefix);
798 if (f == 'u' || f == 'U')
799 itp = n_IGNORE_ALL;
800 else
801 itp = upperchar(f) ? NULL : n_IGNORE_TYPE;
802 action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
804 fprintf(n_stdout, A_("Interpolating:"));
805 srelax_hold();
806 for(; *msgvec != 0; ++msgvec){
807 struct message *mp;
809 mp = &message[*msgvec - 1];
810 touch(mp);
812 fprintf(n_stdout, " %d", *msgvec);
813 fflush(n_stdout);
814 if(f == 'Q'){
815 if(!a_coll_quote_message(fp, mp, FAL0)){
816 rv = n_ERR_IO;
817 break;
819 }else if(sendmp(mp, fp, itp, tabst, action, NULL) < 0){
820 n_perr(_("forward: temporary mail file"), 0);
821 rv = n_ERR_IO;
822 break;
824 srelax();
826 srelax_rele();
827 fprintf(n_stdout, "\n");
828 jleave:
829 NYD_LEAVE;
830 return rv;
833 static void
834 _collint(int s)
836 NYD_X; /* Signal handler */
838 /* the control flow is subtle, because we can be called from ~q */
839 if (_coll_hadintr == 0) {
840 if (ok_blook(ignore)) {
841 fputs("@\n", n_stdout);
842 fflush(n_stdout);
843 clearerr(n_stdin);
844 } else
845 _coll_hadintr = 1;
846 siglongjmp(_coll_jmp, 1);
848 n_exit_status |= n_EXIT_SEND_ERROR;
849 if (s != 0)
850 savedeadletter(_coll_fp, TRU1);
851 /* Aborting message, no need to fflush() .. */
852 siglongjmp(_coll_abort, 1);
855 static void
856 collhup(int s)
858 NYD_X; /* Signal handler */
859 n_UNUSED(s);
861 savedeadletter(_coll_fp, TRU1);
862 /* Let's pretend nobody else wants to clean up, a true statement at
863 * this time */
864 exit(n_EXIT_ERR);
867 static bool_t
868 a_coll_putesc(char const *s, bool_t addnl, FILE *stream){
869 char c1, c2;
870 bool_t isposix;
871 NYD2_ENTER;
873 isposix = ok_blook(posix);
875 while((c1 = *s++) != '\0'){
876 if(c1 == '\\' && ((c2 = *s) == 't' || c2 == 'n')){
877 if(!isposix){
878 isposix = TRU1; /* TODO v15 OBSOLETE! */
879 n_err(_("Compose mode warning: expanding \\t or \\n in variable "
880 "without *posix*!\n"
881 " Support remains only for ~A,~a,~I,~i in *posix* mode!\n"
882 " Please use \"wysh set X=y..\" instead\n"));
884 ++s;
885 c1 = (c2 == 't') ? '\t' : '\n';
888 if(putc(c1, stream) == EOF)
889 goto jleave;
892 if(addnl && putc('\n', stream) == EOF)
893 goto jleave;
895 jleave:
896 NYD2_LEAVE;
897 return (c1 == '\0');
900 static int
901 a_coll_ocs__mac(void){
902 /* Executes in a fork(2)ed child TODO if remains, global MASKs for those! */
903 setvbuf(n_stdin, NULL, _IOLBF, 0);
904 setvbuf(n_stdout, NULL, _IOLBF, 0);
905 n_psonce &= ~(n_PSO_INTERACTIVE | n_PSO_TTYIN | n_PSO_TTYOUT);
906 n_pstate |= n_PS_COMPOSE_FORKHOOK;
907 n_readctl_read_overlay = NULL; /* TODO need OnForkEvent! See c_readctl() */
908 n_digmsg_read_overlay = NULL; /* TODO need OnForkEvent! See c_digmsg() */
909 if(n_poption & n_PO_D_VV){
910 char buf[128];
912 snprintf(buf, sizeof buf, "[%d]%s", getpid(), ok_vlook(log_prefix));
913 ok_vset(log_prefix, buf);
915 /* TODO If that uses `!' it will effectively SIG_IGN SIGINT, ...and such */
916 temporary_compose_mode_hook_call(a_coll_ocs__macname, NULL, NULL);
917 return 0;
920 static void
921 a_coll_ocs__finalize(void *vp){
922 /* Note we use this for destruction upon setup errors, thus */
923 sighandler_type opipe;
924 sighandler_type oint;
925 struct a_coll_ocs_arg **coapp, *coap;
926 NYD2_ENTER;
928 temporary_compose_mode_hook_call((char*)-1, NULL, NULL);
930 coap = *(coapp = vp);
931 *coapp = (struct a_coll_ocs_arg*)-1;
933 if(coap->coa_stdin != NULL)
934 Fclose(coap->coa_stdin);
935 else if(coap->coa_pipe[0] != -1)
936 close(coap->coa_pipe[0]);
938 if(coap->coa_stdout != NULL && !Pclose(coap->coa_stdout, TRU1))
939 *coap->coa_senderr = 111;
940 if(coap->coa_pipe[1] != -1)
941 close(coap->coa_pipe[1]);
943 opipe = coap->coa_opipe;
944 oint = coap->coa_oint;
946 n_lofi_free(coap);
948 hold_all_sigs();
949 safe_signal(SIGPIPE, opipe);
950 safe_signal(SIGINT, oint);
951 rele_all_sigs();
952 NYD2_LEAVE;
955 FL void
956 n_temporary_compose_hook_varset(void *arg){ /* TODO v15: drop */
957 struct header *hp;
958 char const *val;
959 NYD2_ENTER;
961 hp = arg;
963 if((val = hp->h_subject) == NULL)
964 val = n_empty;
965 ok_vset(mailx_subject, val);
966 if((val = detract(hp->h_from, GNAMEONLY)) == NULL)
967 val = n_empty;
968 ok_vset(mailx_from, val);
969 if((val = detract(hp->h_sender, GNAMEONLY)) == NULL)
970 val = n_empty;
971 ok_vset(mailx_sender, val);
972 if((val = detract(hp->h_to, GNAMEONLY)) == NULL)
973 val = n_empty;
974 ok_vset(mailx_to, val);
975 if((val = detract(hp->h_cc, GNAMEONLY)) == NULL)
976 val = n_empty;
977 ok_vset(mailx_cc, val);
978 if((val = detract(hp->h_bcc, GNAMEONLY)) == NULL)
979 val = n_empty;
980 ok_vset(mailx_bcc, val);
982 if((val = hp->h_mailx_command) == NULL)
983 val = n_empty;
984 ok_vset(mailx_command, val);
986 if((val = detract(hp->h_mailx_raw_to, GNAMEONLY)) == NULL)
987 val = n_empty;
988 ok_vset(mailx_raw_to, val);
989 if((val = detract(hp->h_mailx_raw_cc, GNAMEONLY)) == NULL)
990 val = n_empty;
991 ok_vset(mailx_raw_cc, val);
992 if((val = detract(hp->h_mailx_raw_bcc, GNAMEONLY)) == NULL)
993 val = n_empty;
994 ok_vset(mailx_raw_bcc, val);
996 if((val = detract(hp->h_mailx_orig_from, GNAMEONLY)) == NULL)
997 val = n_empty;
998 ok_vset(mailx_orig_from, val);
999 if((val = detract(hp->h_mailx_orig_to, GNAMEONLY)) == NULL)
1000 val = n_empty;
1001 ok_vset(mailx_orig_to, val);
1002 if((val = detract(hp->h_mailx_orig_cc, GNAMEONLY)) == NULL)
1003 val = n_empty;
1004 ok_vset(mailx_orig_cc, val);
1005 if((val = detract(hp->h_mailx_orig_bcc, GNAMEONLY)) == NULL)
1006 val = n_empty;
1007 ok_vset(mailx_orig_bcc, val);
1008 NYD2_LEAVE;
1011 FL FILE *
1012 n_collect(enum n_mailsend_flags msf, struct header *hp, struct message *mp,
1013 char const *quotefile, si8_t *checkaddr_err)
1015 struct n_dig_msg_ctx dmc;
1016 struct n_string s, * volatile sp;
1017 struct a_coll_ocs_arg *coap;
1018 int c;
1019 int volatile t, eofcnt, getfields;
1020 char volatile escape;
1021 enum{
1022 a_NONE,
1023 a_ERREXIT = 1u<<0,
1024 a_IGNERR = 1u<<1,
1025 a_COAP_NOSIGTERM = 1u<<8
1026 #define a_HARDERR() ((flags & (a_ERREXIT | a_IGNERR)) == a_ERREXIT)
1027 } volatile flags;
1028 char *linebuf;
1029 char const *cp, *cp_base, * volatile coapm, * volatile ifs_saved;
1030 size_t i, linesize; /* TODO line pool */
1031 long cnt;
1032 sigset_t oset, nset;
1033 FILE * volatile sigfp;
1034 NYD_ENTER;
1036 n_DIG_MSG_COMPOSE_CREATE(&dmc, hp);
1037 _coll_fp = NULL;
1039 sigfp = NULL;
1040 linesize = 0;
1041 linebuf = NULL;
1042 flags = a_NONE;
1043 eofcnt = 0;
1044 ifs_saved = coapm = NULL;
1045 coap = NULL;
1046 sp = NULL;
1048 /* Start catching signals from here, but we still die on interrupts
1049 * until we're in the main loop */
1050 sigfillset(&nset);
1051 sigprocmask(SIG_BLOCK, &nset, &oset);
1052 if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1053 safe_signal(SIGINT, &_collint);
1054 if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
1055 safe_signal(SIGHUP, collhup);
1056 if (sigsetjmp(_coll_abort, 1))
1057 goto jerr;
1058 if (sigsetjmp(_coll_jmp, 1))
1059 goto jerr;
1060 n_pstate |= n_PS_COMPOSE_MODE;
1061 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
1063 if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1064 NULL) {
1065 n_perr(_("collect: temporary mail file"), 0);
1066 goto jerr;
1069 /* If we are going to prompt for a subject, refrain from printing a newline
1070 * after the headers (since some people mind) */
1071 getfields = 0;
1072 if(!(n_poption & n_PO_t_FLAG)){
1073 t = GTO | GSUBJECT | GCC | GNL;
1074 if(ok_blook(fullnames))
1075 t |= GCOMMA;
1077 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
1078 if(hp->h_subject == NULL && ok_blook(asksub)/* *ask* auto warped! */)
1079 t &= ~GNL, getfields |= GSUBJECT;
1081 if(hp->h_to == NULL)
1082 t &= ~GNL, getfields |= GTO;
1084 if(!ok_blook(bsdcompat) && !ok_blook(askatend)){
1085 if(ok_blook(askbcc))
1086 t &= ~GNL, getfields |= GBCC;
1087 if(ok_blook(askcc))
1088 t &= ~GNL, getfields |= GCC;
1091 }else{
1092 n_UNINIT(t, 0);
1095 _coll_hadintr = 0;
1097 if (!sigsetjmp(_coll_jmp, 1)) {
1098 /* Ask for some headers first, as necessary */
1099 if (getfields)
1100 grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, getfields, 1);
1102 /* Execute compose-enter; delayed for -t mode */
1103 if(!(n_poption & n_PO_t_FLAG) &&
1104 (cp = ok_vlook(on_compose_enter)) != NULL){
1105 setup_from_and_sender(hp);
1106 temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
1107 hp);
1110 /* TODO Mm: nope since it may require turning this into a multipart one */
1111 if(!(n_poption & (n_PO_Mm_FLAG | n_PO_t_FLAG))){
1112 if(!a_coll_message_inject_head(_coll_fp))
1113 goto jerr;
1115 /* Quote an original message */
1116 if(mp != NULL && !a_coll_quote_message(_coll_fp, mp,
1117 ((msf & n_MAILSEND_IS_FWD) != 0)))
1118 goto jerr;
1121 if (quotefile != NULL) {
1122 if((n_pstate_err_no = a_coll_include_file(quotefile, FAL0, FAL0)
1123 ) != n_ERR_NONE)
1124 goto jerr;
1127 if(n_psonce & n_PSO_INTERACTIVE){
1128 if(!(n_pstate & n_PS_SOURCING)){
1129 sp = n_string_creat_auto(&s);
1130 sp = n_string_reserve(sp, 80);
1133 if(!(n_poption & n_PO_Mm_FLAG) && !(n_pstate & n_PS_ROBOT)){
1134 /* Print what we have sofar also on the terminal (if useful) */
1135 if((cp = ok_vlook(editalong)) == NULL){
1136 if(msf & n_MAILSEND_HEADERS_PRINT)
1137 n_puthead(TRU1, hp, n_stdout, t, SEND_TODISP, CONV_NONE,
1138 NULL, NULL);
1140 rewind(_coll_fp);
1141 while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
1142 putc(c, n_stdout);
1143 if (fseek(_coll_fp, 0, SEEK_END))
1144 goto jerr;
1145 }else{
1146 if(a_coll_edit(((*cp == 'v') ? 'v' : 'e'), hp, NULL
1147 ) != n_ERR_NONE)
1148 goto jerr;
1149 /* Print msg mandated by the Mail Reference Manual */
1150 jcont:
1151 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1152 fputs(_("(continue)\n"), n_stdout);
1154 fflush(n_stdout);
1157 } else {
1158 /* Come here for printing the after-signal message. Duplicate messages
1159 * won't be printed because the write is aborted if we get a SIGTTOU */
1160 if(_coll_hadintr && (n_psonce & n_PSO_INTERACTIVE) &&
1161 !(n_pstate & n_PS_ROBOT))
1162 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
1165 /* If not under shell hook control */
1166 if(coap == NULL){
1167 /* We're done with -M or -m TODO because: we are too stupid yet, above */
1168 if(n_poption & n_PO_Mm_FLAG)
1169 goto jout;
1170 /* No command escapes, interrupts not expected. Simply copy STDIN */
1171 if(!(n_psonce & n_PSO_INTERACTIVE) &&
1172 !(n_poption & (n_PO_t_FLAG | n_PO_TILDE_FLAG))){
1173 linebuf = n_realloc(linebuf, linesize = LINESIZE);
1174 while ((i = fread(linebuf, sizeof *linebuf, linesize, n_stdin)) > 0) {
1175 if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
1176 goto jerr;
1178 goto jout;
1182 /* The interactive collect loop */
1183 if(coap == NULL)
1184 escape = *ok_vlook(escape);
1185 flags = ok_blook(errexit) ? a_ERREXIT : a_NONE;
1187 for(;;){
1188 enum {a_HIST_NONE, a_HIST_ADD = 1u<<0, a_HIST_GABBY = 1u<<1} hist;
1190 /* C99 */{
1191 enum n_go_input_flags gif;
1192 bool_t histadd;
1194 /* TODO optimize: no need to evaluate that anew for each loop tick! */
1195 gif = n_GO_INPUT_CTX_COMPOSE;
1196 histadd = (sp != NULL);
1197 if((n_psonce & n_PSO_INTERACTIVE) || (n_poption & n_PO_TILDE_FLAG)){
1198 if(!(n_poption & n_PO_t_FLAG) || (n_psonce & n_PSO_t_FLAG_DONE))
1199 gif |= n_GO_INPUT_NL_ESC;
1201 cnt = n_go_input(gif, n_empty, &linebuf, &linesize, NULL, &histadd);
1202 hist = histadd ? a_HIST_ADD | a_HIST_GABBY : a_HIST_NONE;
1205 if(cnt < 0){
1206 if(coap != NULL)
1207 break;
1208 if((n_poption & n_PO_t_FLAG) && !(n_psonce & n_PSO_t_FLAG_DONE)){
1209 fflush_rewind(_coll_fp);
1210 n_psonce |= n_PSO_t_FLAG_DONE;
1211 if(!a_coll_makeheader(_coll_fp, hp, checkaddr_err, TRU1))
1212 goto jerr;
1213 continue;
1214 }else if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
1215 ok_blook(ignoreeof) && ++eofcnt < 4){
1216 fprintf(n_stdout,
1217 _("*ignoreeof* set, use `~.' to terminate letter\n"));
1218 n_go_input_clearerr();
1219 continue;
1221 break;
1224 _coll_hadintr = 0;
1226 cp = linebuf;
1227 if(cnt == 0)
1228 goto jputnl;
1229 else if(coap == NULL){
1230 if(!(n_psonce & n_PSO_INTERACTIVE) && !(n_poption & n_PO_TILDE_FLAG))
1231 goto jputline;
1232 else if(cp[0] == '.'){
1233 if(cnt == 1 && (ok_blook(dot) ||
1234 (ok_blook(posix) && ok_blook(ignoreeof))))
1235 break;
1238 if(cp[0] != escape){
1239 jputline:
1240 if(fwrite(cp, sizeof *cp, cnt, _coll_fp) != (size_t)cnt)
1241 goto jerr;
1242 /* TODO n_PS_READLINE_NL is a terrible hack to ensure that _in_all_-
1243 * TODO _code_paths_ a file without trailing newline isn't modified
1244 * TODO to contain one; the "saw-newline" needs to be part of an
1245 * TODO I/O input machinery object */
1246 jputnl:
1247 if(n_pstate & n_PS_READLINE_NL){
1248 if(putc('\n', _coll_fp) == EOF)
1249 goto jerr;
1251 continue;
1254 c = *(cp_base = ++cp);
1255 if(--cnt == 0)
1256 goto jearg;
1258 /* Avoid history entry? */
1259 while(spacechar(c)){
1260 hist = a_HIST_NONE;
1261 c = *(cp_base = ++cp);
1262 if(--cnt == 0)
1263 goto jearg;
1266 /* It may just be an escaped escaped character, do that quick */
1267 if(c == escape)
1268 goto jputline;
1270 /* Avoid hard *errexit*? */
1271 flags &= ~a_IGNERR;
1272 if(c == '-'){
1273 flags ^= a_IGNERR;
1274 c = *++cp;
1275 if(--cnt == 0)
1276 goto jearg;
1279 /* Trim input, also to gain a somewhat normalized history entry */
1280 ++cp;
1281 if(--cnt > 0){
1282 struct str x;
1284 x.s = n_UNCONST(cp);
1285 x.l = (size_t)cnt;
1286 n_str_trim_ifs(&x, TRU1);
1287 x.s[x.l] = '\0';
1288 cp = x.s;
1289 cnt = (int)/*XXX*/x.l;
1292 if(hist != a_HIST_NONE){
1293 sp = n_string_assign_c(sp, escape);
1294 if(flags & a_IGNERR)
1295 sp = n_string_push_c(sp, '-');
1296 sp = n_string_push_c(sp, c);
1297 if(cnt > 0){
1298 sp = n_string_push_c(sp, ' ');
1299 sp = n_string_push_buf(sp, cp, cnt);
1303 /* Switch over all command escapes */
1304 switch(c){
1305 default:
1306 if(1){
1307 char buf[sizeof(n_UNIREPL)];
1309 if(asciichar(c))
1310 buf[0] = c, buf[1] = '\0';
1311 else if(n_psonce & n_PSO_UNICODE)
1312 memcpy(buf, n_unirepl, sizeof n_unirepl);
1313 else
1314 buf[0] = '?', buf[1] = '\0';
1315 n_err(_("Unknown command escape: `%c%s'\n"), escape, buf);
1316 }else
1317 jearg:
1318 n_err(_("Invalid command escape usage: %s\n"),
1319 n_shexp_quote_cp(linebuf, FAL0));
1320 if(a_HARDERR())
1321 goto jerr;
1322 n_pstate_err_no = n_ERR_INVAL;
1323 n_pstate_ex_no = 1;
1324 continue;
1325 case '!':
1326 /* Shell escape, send the balance of line to sh -c */
1327 if(cnt == 0 || coap != NULL)
1328 goto jearg;
1329 else{
1330 char const *argv[2];
1332 argv[0] = cp;
1333 argv[1] = NULL;
1334 n_pstate_ex_no = c_shell(argv); /* TODO history norm.; errexit? */
1336 goto jhistcont;
1337 case '.':
1338 /* Simulate end of file on input */
1339 if(cnt != 0 || coap != NULL)
1340 goto jearg;
1341 goto jout; /* TODO does not enter history, thus */
1342 case ':':
1343 case '_':
1344 /* Escape to command mode, but be nice! *//* TODO command expansion
1345 * TODO should be handled here so that we have unique history! */
1346 if(cnt == 0)
1347 goto jearg;
1348 _execute_command(hp, cp, cnt);
1349 if(ok_blook(errexit))
1350 flags |= a_ERREXIT;
1351 else
1352 flags &= ~a_ERREXIT;
1353 if(n_pstate_ex_no != 0 && a_HARDERR())
1354 goto jerr;
1355 if(coap == NULL)
1356 escape = *ok_vlook(escape);
1357 hist &= ~a_HIST_GABBY;
1358 break;
1359 /* case '<': <> 'd' */
1360 case '?':
1361 #ifdef HAVE_UISTRINGS
1362 fputs(_(
1363 "COMMAND ESCAPES (to be placed after a newline) excerpt:\n"
1364 "~. Commit and send message\n"
1365 "~: <command> Execute an internal command\n"
1366 "~< <file> Insert <file> (\"~<! <command>\": insert shell command)\n"
1367 "~@ [<files>] Edit [Add] attachments (file[=in-charset[#out-charset]])\n"
1368 "~c <users> Add users to Cc: list (`~b': to Bcc:)\n"
1369 "~e, ~v Edit message via $EDITOR / $VISUAL\n"
1370 ), n_stdout);
1371 fputs(_(
1372 "~F <msglist> Read in with headers, do not *indentprefix* lines\n"
1373 "~f <msglist> Like `~F', but honour `headerpick' configuration\n"
1374 "~H Edit From:, Reply-To: and Sender:\n"
1375 "~h Prompt for Subject:, To:, Cc: and Bcc:\n"
1376 "~i <variable> Insert a value and a newline (`~I': insert value)\n"
1377 "~M <msglist> Read in with headers, *indentprefix* (`~m': use `headerpick')\n"
1378 "~p Show current message compose buffer\n"
1379 "~Q <msglist> Read in using normal *quote* algorithm\n"
1380 ), n_stdout);
1381 fputs(_(
1382 "~r <file> Insert <file> (`~R': *indentprefix* lines)\n"
1383 " <file> may also be <- [HERE-DELIMITER]>\n"
1384 "~s <subject> Set Subject:\n"
1385 "~t <users> Add users to To: list\n"
1386 "~u <msglist> Read in without headers (`~U': *indentprefix* lines)\n"
1387 "~w <file> Write message onto file\n"
1388 "~x Abort composition, discard message (`~q': save in $DEAD)\n"
1389 "~| <command> Pipe message through shell filter (`~||': with headers)\n"
1390 ), n_stdout);
1391 #endif /* HAVE_UISTRINGS */
1392 if(cnt != 0)
1393 goto jearg;
1394 n_pstate_err_no = n_ERR_NONE;
1395 n_pstate_ex_no = 0;
1396 break;
1397 case '@':{
1398 struct attachment *aplist;
1400 /* Edit the attachment list */
1401 aplist = hp->h_attach;
1402 hp->h_attach = NULL;
1403 if(cnt != 0)
1404 hp->h_attach = n_attachment_append_list(aplist, cp);
1405 else
1406 hp->h_attach = n_attachment_list_edit(aplist,
1407 n_GO_INPUT_CTX_COMPOSE);
1408 n_pstate_err_no = n_ERR_NONE; /* XXX ~@ does NOT handle $!/$?! */
1409 n_pstate_ex_no = 0; /* XXX */
1410 }break;
1411 case '^':
1412 if(!n_dig_msg_circumflex(&dmc, n_stdout, cp)){
1413 if(ferror(_coll_fp))
1414 goto jerr;
1415 goto jearg;
1417 n_pstate_err_no = n_ERR_NONE; /* XXX */
1418 n_pstate_ex_no = 0; /* XXX */
1419 hist &= ~a_HIST_GABBY;
1420 break;
1421 /* case '_': <> ':' */
1422 case '|':
1423 /* Pipe message through command. Collect output as new message */
1424 if(cnt == 0)
1425 goto jearg;
1426 /* Is this request to do a "stream equivalent" to 'e' and 'v'? */
1427 if(*cp == '|'){
1428 ++cp;
1429 goto jev_go;
1431 if((n_pstate_err_no = a_coll_pipe(cp)) == n_ERR_NONE)
1432 n_pstate_ex_no = 0;
1433 else if(ferror(_coll_fp))
1434 goto jerr;
1435 else if(a_HARDERR())
1436 goto jerr;
1437 else
1438 n_pstate_ex_no = 1;
1439 hist &= ~a_HIST_GABBY;
1440 goto jhistcont;
1441 case 'A':
1442 case 'a':
1443 /* Insert the contents of a sign variable */
1444 if(cnt != 0)
1445 goto jearg;
1446 cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
1447 goto jIi_putesc;
1448 case 'b':
1449 /* Add stuff to blind carbon copies list TODO join 'c' */
1450 if(cnt == 0)
1451 goto jearg;
1452 else{
1453 struct name *np;
1454 si8_t soe;
1456 soe = 0;
1457 if((np = checkaddrs(lextract(cp, GBCC | GFULL), EACM_NORMAL, &soe)
1458 ) != NULL)
1459 hp->h_bcc = cat(hp->h_bcc, np);
1460 if(soe == 0){
1461 n_pstate_err_no = n_ERR_NONE;
1462 n_pstate_ex_no = 0;
1463 }else{
1464 n_pstate_ex_no = 1;
1465 n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
1468 hist &= ~a_HIST_GABBY;
1469 break;
1470 case 'c':
1471 /* Add to the CC list TODO join 'b' */
1472 if(cnt == 0)
1473 goto jearg;
1474 else{
1475 struct name *np;
1476 si8_t soe;
1478 soe = 0;
1479 if((np = checkaddrs(lextract(cp, GCC | GFULL), EACM_NORMAL, &soe)
1480 ) != NULL)
1481 hp->h_cc = cat(hp->h_cc, np);
1482 if(soe == 0){
1483 n_pstate_err_no = n_ERR_NONE;
1484 n_pstate_ex_no = 0;
1485 }else{
1486 n_pstate_ex_no = 1;
1487 n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
1490 hist &= ~a_HIST_GABBY;
1491 break;
1492 case 'd':
1493 if(cnt != 0)
1494 goto jearg;
1495 cp = n_getdeadletter();
1496 if(0){
1497 case '<':
1498 case 'R':
1499 case 'r':
1500 /* Invoke a file: Search for the file name, then open it and copy
1501 * the contents to _coll_fp */
1502 if(cnt == 0){
1503 n_err(_("Interpolate what file?\n"));
1504 if(a_HARDERR())
1505 goto jerr;
1506 n_pstate_err_no = n_ERR_NOENT;
1507 n_pstate_ex_no = 1;
1508 break;
1510 if(*cp == '!' && c == '<'){
1511 /* TODO hist. normalization */
1512 if((n_pstate_err_no = a_coll_insert_cmd(_coll_fp, ++cp)
1513 ) != n_ERR_NONE){
1514 if(ferror(_coll_fp))
1515 goto jerr;
1516 if(a_HARDERR())
1517 goto jerr;
1518 n_pstate_ex_no = 1;
1519 break;
1521 goto jhistcont;
1523 /* Note this also expands things like
1524 * !:vput vexpr delim random 0
1525 * !< - $delim */
1526 if((cp = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO | FEXP_NSHELL)
1527 ) == NULL){
1528 if(a_HARDERR())
1529 goto jerr;
1530 n_pstate_err_no = n_ERR_INVAL;
1531 n_pstate_ex_no = 1;
1532 break;
1535 /* XXX race, and why not test everywhere, then? */
1536 if(n_is_dir(cp, FAL0)){
1537 n_err(_("%s: is a directory\n"), n_shexp_quote_cp(cp, FAL0));
1538 if(a_HARDERR())
1539 goto jerr;
1540 n_pstate_err_no = n_ERR_ISDIR;
1541 n_pstate_ex_no = 1;
1542 break;
1544 if((n_pstate_err_no = a_coll_include_file(cp, (c == 'R'), TRU1)
1545 ) != n_ERR_NONE){
1546 if(ferror(_coll_fp))
1547 goto jerr;
1548 if(a_HARDERR())
1549 goto jerr;
1550 n_pstate_ex_no = 1;
1551 break;
1553 n_pstate_err_no = n_ERR_NONE; /* XXX */
1554 n_pstate_ex_no = 0; /* XXX */
1555 break;
1556 case 'e':
1557 case 'v':
1558 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1559 if(cnt != 0 || coap != NULL)
1560 goto jearg;
1561 jev_go:
1562 if((n_pstate_err_no = a_coll_edit(c,
1563 ((c == '|' || ok_blook(editheaders)) ? hp : NULL), cp)
1564 ) == n_ERR_NONE)
1565 n_pstate_ex_no = 0;
1566 else if(ferror(_coll_fp))
1567 goto jerr;
1568 else if(a_HARDERR())
1569 goto jerr;
1570 else
1571 n_pstate_ex_no = 1;
1572 goto jhistcont;
1573 case 'F':
1574 case 'f':
1575 case 'M':
1576 case 'm':
1577 case 'Q':
1578 case 'U':
1579 case 'u':
1580 /* Interpolate the named messages, if we are in receiving mail mode.
1581 * Does the standard list processing garbage. If ~f is given, we
1582 * don't shift over */
1583 if((n_pstate_err_no = a_coll_forward(cp, _coll_fp, c)) == n_ERR_NONE)
1584 n_pstate_ex_no = 0;
1585 else if(ferror(_coll_fp))
1586 goto jerr;
1587 else if(a_HARDERR())
1588 goto jerr;
1589 else
1590 n_pstate_ex_no = 1;
1591 break;
1592 case 'H':
1593 /* Grab extra headers */
1594 if(cnt != 0)
1595 goto jearg;
1597 grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, GEXTRA, 0);
1598 while(check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
1599 n_pstate_err_no = n_ERR_NONE; /* XXX */
1600 n_pstate_ex_no = 0; /* XXX */
1601 break;
1602 case 'h':
1603 /* Grab a bunch of headers */
1604 if(cnt != 0)
1605 goto jearg;
1607 grab_headers(n_GO_INPUT_CTX_COMPOSE, hp,
1608 (GTO | GSUBJECT | GCC | GBCC),
1609 (ok_blook(bsdcompat) && ok_blook(bsdorder)));
1610 while(hp->h_to == NULL);
1611 n_pstate_err_no = n_ERR_NONE; /* XXX */
1612 n_pstate_ex_no = 0; /* XXX */
1613 break;
1614 case 'I':
1615 case 'i':
1616 /* Insert a variable into the file */
1617 if(cnt == 0)
1618 goto jearg;
1619 cp = n_var_vlook(cp, TRU1);
1620 jIi_putesc:
1621 if(cp == NULL || *cp == '\0')
1622 break;
1623 if(!a_coll_putesc(cp, (c != 'I'), _coll_fp))
1624 goto jerr;
1625 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
1626 (!a_coll_putesc(cp, (c != 'I'), n_stdout) ||
1627 fflush(n_stdout) == EOF))
1628 goto jerr;
1629 n_pstate_err_no = n_ERR_NONE; /* XXX */
1630 n_pstate_ex_no = 0; /* XXX */
1631 break;
1632 /* case 'M': <> 'F' */
1633 /* case 'm': <> 'f' */
1634 case 'p':
1635 /* Print current state of the message without altering anything */
1636 if(cnt != 0)
1637 goto jearg;
1638 print_collf(_coll_fp, hp); /* XXX pstate_err_no ++ */
1639 if(ferror(_coll_fp))
1640 goto jerr;
1641 n_pstate_err_no = n_ERR_NONE; /* XXX */
1642 n_pstate_ex_no = 0; /* XXX */
1643 break;
1644 /* case 'Q': <> 'F' */
1645 case 'q':
1646 case 'x':
1647 /* Force a quit, act like an interrupt had happened */
1648 if(cnt != 0)
1649 goto jearg;
1650 /* If we are running a splice hook, assume it quits on its own now,
1651 * otherwise we (no true out-of-band IPC to signal this state, XXX sic)
1652 * have to SIGTERM it in order to stop this wild beast */
1653 flags |= a_COAP_NOSIGTERM;
1654 ++_coll_hadintr;
1655 _collint((c == 'x') ? 0 : SIGINT);
1656 exit(n_EXIT_ERR);
1657 /*NOTREACHED*/
1658 /* case 'R': <> 'd' */
1659 /* case 'r': <> 'd' */
1660 case 's':
1661 /* Set the Subject list */
1662 if(cnt == 0)
1663 goto jearg;
1664 /* Subject:; take care for Debian #419840 and strip any \r and \n */
1665 if(n_anyof_cp("\n\r", hp->h_subject = savestr(cp))){
1666 char *xp;
1668 n_err(_("-s: normalizing away invalid ASCII NL / CR bytes\n"));
1669 for(xp = hp->h_subject; *xp != '\0'; ++xp)
1670 if(*xp == '\n' || *xp == '\r')
1671 *xp = ' ';
1672 n_pstate_err_no = n_ERR_INVAL;
1673 n_pstate_ex_no = 1;
1674 }else{
1675 n_pstate_err_no = n_ERR_NONE;
1676 n_pstate_ex_no = 0;
1678 break;
1679 case 't':
1680 /* Add to the To: list TODO join 'b', 'c' */
1681 if(cnt == 0)
1682 goto jearg;
1683 else{
1684 struct name *np;
1685 si8_t soe;
1687 soe = 0;
1688 if((np = checkaddrs(lextract(cp, GTO | GFULL), EACM_NORMAL, &soe)
1689 ) != NULL)
1690 hp->h_to = cat(hp->h_to, np);
1691 if(soe == 0){
1692 n_pstate_err_no = n_ERR_NONE;
1693 n_pstate_ex_no = 0;
1694 }else{
1695 n_pstate_ex_no = 1;
1696 n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
1699 hist &= ~a_HIST_GABBY;
1700 break;
1701 /* case 'U': <> 'F' */
1702 /* case 'u': <> 'f' */
1703 /* case 'v': <> 'e' */
1704 case 'w':
1705 /* Write the message on a file */
1706 if(cnt == 0)
1707 goto jearg;
1708 if((cp = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) == NULL){
1709 n_err(_("Write what file!?\n"));
1710 if(a_HARDERR())
1711 goto jerr;
1712 n_pstate_err_no = n_ERR_INVAL;
1713 n_pstate_ex_no = 1;
1714 break;
1716 rewind(_coll_fp);
1717 if((n_pstate_err_no = a_coll_write(cp, _coll_fp, 1)) == n_ERR_NONE)
1718 n_pstate_ex_no = 0;
1719 else if(ferror(_coll_fp))
1720 goto jerr;
1721 else if(a_HARDERR())
1722 goto jerr;
1723 else
1724 n_pstate_ex_no = 1;
1725 break;
1726 /* case 'x': <> 'q' */
1729 /* Finally place an entry in history as applicable */
1730 if(0){
1731 jhistcont:
1732 c = '\1';
1733 }else
1734 c = '\0';
1735 if(hist & a_HIST_ADD){
1736 /* Do not add *escape* to the history in order to allow history search
1737 * to be handled generically in the MLE regardless of actual *escape*
1738 * settings etc. */
1739 n_tty_addhist(&n_string_cp(sp)[1], (n_GO_INPUT_CTX_COMPOSE |
1740 (hist & a_HIST_GABBY ? n_GO_INPUT_HIST_GABBY : n_GO_INPUT_NONE)));
1742 if(c != '\0')
1743 goto jcont;
1746 jout:
1747 /* Do we have *on-compose-splice-shell*, or *on-compose-splice*?
1748 * TODO Usual f...ed up state of signals and terminal etc. */
1749 if(coap == NULL && (cp = ok_vlook(on_compose_splice_shell)) != NULL) Jocs:{
1750 union {int (*ptf)(void); char const *sh;} u;
1751 char const *cmd;
1753 /* Reset *escape* and more to their defaults. On change update manual! */
1754 if(ifs_saved == NULL)
1755 ifs_saved = savestr(ok_vlook(ifs));
1756 escape = n_ESCAPE[0];
1757 ok_vclear(ifs);
1759 if(coapm != NULL){
1760 /* XXX Due Popen() fflush(NULL) in PTF mode, ensure nothing to flush */
1761 /*if(!n_real_seek(_coll_fp, 0, SEEK_END))
1762 * goto jerr;*/
1763 u.ptf = &a_coll_ocs__mac;
1764 cmd = (char*)-1;
1765 a_coll_ocs__macname = cp = coapm;
1766 }else{
1767 u.sh = ok_vlook(SHELL);
1768 cmd = cp;
1771 i = strlen(cp) +1;
1772 coap = n_lofi_alloc(n_VSTRUCT_SIZEOF(struct a_coll_ocs_arg, coa_cmd
1773 ) + i);
1774 coap->coa_pipe[0] = coap->coa_pipe[1] = -1;
1775 coap->coa_stdin = coap->coa_stdout = NULL;
1776 coap->coa_senderr = checkaddr_err;
1777 memcpy(coap->coa_cmd, cp, i);
1779 hold_all_sigs();
1780 coap->coa_opipe = safe_signal(SIGPIPE, SIG_IGN);
1781 coap->coa_oint = safe_signal(SIGINT, SIG_IGN);
1782 rele_all_sigs();
1784 if(pipe_cloexec(coap->coa_pipe) != -1 &&
1785 (coap->coa_stdin = Fdopen(coap->coa_pipe[0], "r", FAL0)) != NULL &&
1786 (coap->coa_stdout = Popen(cmd, "W", u.sh, NULL, coap->coa_pipe[1])
1787 ) != NULL){
1788 close(coap->coa_pipe[1]);
1789 coap->coa_pipe[1] = -1;
1791 temporary_compose_mode_hook_call(NULL, NULL, NULL);
1792 n_go_splice_hack(coap->coa_cmd, coap->coa_stdin, coap->coa_stdout,
1793 (n_psonce & ~(n_PSO_INTERACTIVE | n_PSO_TTYIN | n_PSO_TTYOUT)),
1794 &a_coll_ocs__finalize, &coap);
1795 /* Hook version protocol for ~^: update manual upon change! */
1796 fputs(n_DIG_MSG_PLUMBING_VERSION "\n", n_stdout/*coap->coa_stdout*/);
1797 goto jcont;
1800 c = n_err_no;
1801 a_coll_ocs__finalize(coap);
1802 n_perr(_("Cannot invoke *on-compose-splice(-shell)?*"), c);
1803 goto jerr;
1805 if(*checkaddr_err != 0){
1806 *checkaddr_err = 0;
1807 goto jerr;
1809 if(coapm == NULL && (coapm = ok_vlook(on_compose_splice)) != NULL)
1810 goto Jocs;
1811 if(coap != NULL){
1812 ok_vset(ifs, ifs_saved);
1813 ifs_saved = NULL;
1817 * Note: the order of the following steps is documented for `~.'.
1818 * Adjust the manual on change!!
1821 /* Final chance to edit headers, if not already above; and *asksend* */
1822 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
1823 if(ok_blook(bsdcompat) || ok_blook(askatend)){
1824 enum gfield gf;
1826 gf = GNONE;
1827 if(ok_blook(askcc))
1828 gf |= GCC;
1829 if(ok_blook(askbcc))
1830 gf |= GBCC;
1831 if(gf != 0)
1832 grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, gf, 1);
1835 if(ok_blook(askattach))
1836 hp->h_attach = n_attachment_list_edit(hp->h_attach,
1837 n_GO_INPUT_CTX_COMPOSE);
1839 if(ok_blook(asksend)){
1840 bool_t b;
1842 ifs_saved = coapm = NULL;
1843 coap = NULL;
1845 fprintf(n_stdout, _("-------\nEnvelope contains:\n")); /* XXX112 */
1846 if(!n_puthead(TRU1, hp, n_stdout,
1847 GIDENT | GREF_IRT | GSUBJECT | GTO | GCC | GBCC | GBCC_IS_FCC |
1848 GCOMMA, SEND_TODISP, CONV_NONE, NULL, NULL))
1849 goto jerr;
1851 jreasksend:
1852 if(n_go_input(n_GO_INPUT_CTX_COMPOSE | n_GO_INPUT_NL_ESC,
1853 _("Send this message [yes/no, empty: recompose]? "),
1854 &linebuf, &linesize, NULL, NULL) < 0){
1855 if(!n_go_input_is_eof())
1856 goto jerr;
1857 cp = n_1;
1860 if((b = n_boolify(linebuf, UIZ_MAX, TRUM1)) < FAL0)
1861 goto jreasksend;
1862 if(b == TRU2)
1863 goto jcont;
1864 if(!b)
1865 goto jerr;
1869 /* Execute compose-leave */
1870 if((cp = ok_vlook(on_compose_leave)) != NULL){
1871 setup_from_and_sender(hp);
1872 temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
1873 hp);
1876 /* Add automatic receivers */
1877 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1878 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC |
1879 (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN)),
1880 EACM_NORMAL, checkaddr_err));
1881 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1882 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC |
1883 (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN)),
1884 EACM_NORMAL, checkaddr_err));
1885 if (*checkaddr_err != 0)
1886 goto jerr;
1888 /* TODO Cannot do since it may require turning this into a multipart one */
1889 if(n_poption & n_PO_Mm_FLAG)
1890 goto jskiptails;
1892 /* Place signature? */
1893 if((cp = ok_vlook(signature)) != NULL && *cp != '\0'){ /* TODO OBSOLETE */
1894 char const *cpq;
1896 n_OBSOLETE(_("please use *on-compose-{leave,splice}* and/or "
1897 "*message-inject-tail*, not *signature*"));
1899 if((cpq = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) == NULL){
1900 n_err(_("*signature* expands to invalid file: %s\n"),
1901 n_shexp_quote_cp(cp, FAL0));
1902 goto jerr;
1904 cpq = n_shexp_quote_cp(cp = cpq, FAL0);
1906 if((sigfp = Fopen(cp, "r")) == NULL){
1907 n_err(_("Can't open *signature* %s: %s\n"),
1908 cpq, n_err_to_doc(n_err_no));
1909 goto jerr;
1912 if(linebuf == NULL)
1913 linebuf = n_alloc(linesize = LINESIZE);
1914 c = '\0';
1915 while((i = fread(linebuf, sizeof *linebuf, linesize, n_UNVOLATILE(sigfp)))
1916 > 0){
1917 c = linebuf[i - 1];
1918 if(i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
1919 goto jerr;
1922 /* C99 */{
1923 FILE *x = n_UNVOLATILE(sigfp);
1924 int e = n_err_no, ise = ferror(x);
1926 sigfp = NULL;
1927 Fclose(x);
1929 if(ise){
1930 n_err(_("Errors while reading *signature* %s: %s\n"),
1931 cpq, n_err_to_doc(e));
1932 goto jerr;
1936 if(c != '\0' && c != '\n')
1937 putc('\n', _coll_fp);
1940 { char const *cp_obsolete = ok_vlook(NAIL_TAIL);
1942 if(cp_obsolete != NULL)
1943 n_OBSOLETE(_("please use *message-inject-tail*, not *NAIL_TAIL*"));
1945 if((cp = ok_vlook(message_inject_tail)) != NULL ||
1946 (cp = cp_obsolete) != NULL){
1947 if(!a_coll_putesc(cp, TRU1, _coll_fp))
1948 goto jerr;
1949 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
1950 (!a_coll_putesc(cp, TRU1, n_stdout) || fflush(n_stdout) == EOF))
1951 goto jerr;
1955 jskiptails:
1956 if(fflush(_coll_fp))
1957 goto jerr;
1958 rewind(_coll_fp);
1960 if(mp != NULL && ok_blook(quote_as_attachment)){
1961 struct attachment *ap;
1963 ap = n_autorec_calloc(1, sizeof *ap);
1964 if((ap->a_flink = hp->h_attach) != NULL)
1965 hp->h_attach->a_blink = ap;
1966 hp->h_attach = ap;
1967 ap->a_msgno = (int)PTR2SIZE(mp - message + 1);
1968 ap->a_content_description = _("Original message content");
1971 jleave:
1972 if (linebuf != NULL)
1973 n_free(linebuf);
1974 sigprocmask(SIG_BLOCK, &nset, NULL);
1975 n_DIG_MSG_COMPOSE_GUT(&dmc);
1976 n_pstate &= ~n_PS_COMPOSE_MODE;
1977 safe_signal(SIGINT, _coll_saveint);
1978 safe_signal(SIGHUP, _coll_savehup);
1979 sigprocmask(SIG_SETMASK, &oset, NULL);
1980 NYD_LEAVE;
1981 return _coll_fp;
1983 jerr:
1984 hold_all_sigs();
1986 if(coap != NULL && coap != (struct a_coll_ocs_arg*)-1){
1987 if(!(flags & a_COAP_NOSIGTERM))
1988 n_psignal(coap->coa_stdout, SIGTERM);
1989 n_go_splice_hack_remove_after_jump();
1990 coap = NULL;
1992 if(ifs_saved != NULL){
1993 ok_vset(ifs, ifs_saved);
1994 ifs_saved = NULL;
1996 if(sigfp != NULL){
1997 Fclose(n_UNVOLATILE(sigfp));
1998 sigfp = NULL;
2000 if(_coll_fp != NULL){
2001 Fclose(_coll_fp);
2002 _coll_fp = NULL;
2005 rele_all_sigs();
2007 assert(checkaddr_err != NULL);
2008 /* TODO We don't save in $DEAD upon error because msg not readily composed?
2009 * TODO But this no good, it should go ZOMBIE / DRAFT / POSTPONED or what! */
2010 if(*checkaddr_err != 0){
2011 if(*checkaddr_err == 111)
2012 n_err(_("Compose mode splice hook failure\n"));
2013 else
2014 n_err(_("Some addressees were classified as \"hard error\"\n"));
2015 }else if(_coll_hadintr == 0){
2016 *checkaddr_err = TRU1; /* TODO ugly: "sendout_error" now.. */
2017 n_err(_("Failed to prepare composed message\n"));
2019 goto jleave;
2021 #undef a_HARDERR
2024 /* s-it-mode */