"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/collect.c" (16 Feb 2018, 83261 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "collect.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

    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.
    4  *
    5  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    6  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE collect
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 struct a_coll_ocs_arg{
   44    sighandler_type coa_opipe;
   45    sighandler_type coa_oint;
   46    FILE *coa_stdin;  /* The descriptor (pipe(2)+Fdopen()) we read from */
   47    FILE *coa_stdout; /* The Popen()ed pipe through which we write to the hook */
   48    int coa_pipe[2];  /* ..backing .coa_stdin */
   49    si8_t *coa_senderr; /* Set to 1 on failure */
   50    char coa_cmd[n_VFIELD_SIZE(0)];
   51 };
   52 
   53 /* The following hookiness with global variables is so that on receipt of an
   54  * interrupt signal, the partial message can be salted away on *DEAD* */
   55 
   56 static sighandler_type  _coll_saveint;    /* Previous SIGINT value */
   57 static sighandler_type  _coll_savehup;    /* Previous SIGHUP value */
   58 static FILE             *_coll_fp;        /* File for saving away */
   59 static int volatile     _coll_hadintr;    /* Have seen one SIGINT so far */
   60 static sigjmp_buf       _coll_jmp;        /* To get back to work */
   61 static sigjmp_buf       _coll_abort;      /* To end collection with error */
   62 static char const *a_coll_ocs__macname;   /* *on-compose-splice* */
   63 
   64 /* Handle `~:', `~_' and some hooks; hp may be NULL */
   65 static void       _execute_command(struct header *hp, char const *linebuf,
   66                      size_t linesize);
   67 
   68 /* Return errno */
   69 static si32_t a_coll_include_file(char const *name, bool_t indent,
   70                bool_t writestat);
   71 
   72 /* Execute cmd and insert its standard output into fp, return errno */
   73 static si32_t a_coll_insert_cmd(FILE *fp, char const *cmd);
   74 
   75 /* ~p command */
   76 static void       print_collf(FILE *collf, struct header *hp);
   77 
   78 /* Write a file, ex-like if f set */
   79 static si32_t a_coll_write(char const *name, FILE *fp, int f);
   80 
   81 /* *message-inject-head* */
   82 static bool_t a_coll_message_inject_head(FILE *fp);
   83 
   84 /* Parse off the message header from fp and store relevant fields in hp,
   85  * replace _coll_fp with a shiny new version without any header */
   86 static bool_t a_coll_makeheader(FILE *fp, struct header *hp,
   87                si8_t *checkaddr_err, bool_t do_delayed_due_t);
   88 
   89 /* Edit the message being collected on fp.  On return, make the edit file the
   90  * new temp file.  Return errno */
   91 static si32_t a_coll_edit(int c, struct header *hp);
   92 
   93 /* Pipe the message through the command.  Old message is on stdin of command,
   94  * new message collected from stdout.  Shell must return 0 to accept new msg */
   95 static si32_t a_coll_pipe(char const *cmd);
   96 
   97 /* Interpolate the named messages into the current message, possibly doing
   98  * indent stuff.  The flag argument is one of the command escapes: [mMfFuU].
   99  * Return errno */
  100 static si32_t a_coll_forward(char const *ms, FILE *fp, int f);
  101 
  102 /* ~^ mode */
  103 static bool_t a_collect_plumbing(char const *ms, struct header *p);
  104 
  105 static bool_t a_collect__plumb_header(char const *cp, struct header *p,
  106                char const *cmd[4]);
  107 static bool_t a_collect__plumb_attach(char const *cp, struct header *p,
  108                char const *cmd[4]);
  109 
  110 /* On interrupt, come here to save the partial message in ~/dead.letter.
  111  * Then jump out of the collection loop */
  112 static void       _collint(int s);
  113 
  114 static void       collhup(int s);
  115 
  116 /* ~[AaIi], *message-inject-**: put value, expand \[nt] if *posix* */
  117 static bool_t a_coll_putesc(char const *s, bool_t addnl, FILE *stream);
  118 
  119 /* *on-compose-splice* driver and *on-compose-splice(-shell)?* finalizer */
  120 static int a_coll_ocs__mac(void);
  121 static void a_coll_ocs__finalize(void *vp);
  122 
  123 static void
  124 _execute_command(struct header *hp, char const *linebuf, size_t linesize){
  125    /* The problem arises if there are rfc822 message attachments and the
  126     * user uses `~:' to change the current file.  TODO Unfortunately we
  127     * TODO cannot simply keep a pointer to, or increment a reference count
  128     * TODO of the current `file' (mailbox that is) object, because the
  129     * TODO codebase doesn't deal with that at all; so, until some far
  130     * TODO later time, copy the name of the path, and warn the user if it
  131     * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
  132     * TODO copy the message attachments over to temporary files, but that
  133     * TODO would require more changes so that the user still can recognize
  134     * TODO in `~@' etc. that its a rfc822 message attachment; see below */
  135    struct n_sigman sm;
  136    struct attachment *ap;
  137    char * volatile mnbuf;
  138    NYD_ENTER;
  139 
  140    n_UNUSED(linesize);
  141    mnbuf = NULL;
  142 
  143    n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_HUP | n_SIGMAN_INT | n_SIGMAN_QUIT){
  144    case 0:
  145       break;
  146    default:
  147       n_pstate_err_no = n_ERR_INTR;
  148       n_pstate_ex_no = 1;
  149       goto jleave;
  150    }
  151 
  152    /* If the above todo is worked, remove or outsource to attachment.c! */
  153    if(hp != NULL && (ap = hp->h_attach) != NULL) do
  154       if(ap->a_msgno){
  155          mnbuf = sstrdup(mailname);
  156          break;
  157       }
  158    while((ap = ap->a_flink) != NULL);
  159 
  160    n_go_command(n_GO_INPUT_CTX_COMPOSE, linebuf);
  161 
  162    n_sigman_cleanup_ping(&sm);
  163 jleave:
  164    if(mnbuf != NULL){
  165       if(strcmp(mnbuf, mailname))
  166          n_err(_("Mailbox changed: it is likely that existing "
  167             "rfc822 attachments became invalid!\n"));
  168       free(mnbuf);
  169    }
  170    NYD_LEAVE;
  171    n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
  172 }
  173 
  174 static si32_t
  175 a_coll_include_file(char const *name, bool_t indent, bool_t writestat){
  176    FILE *fbuf;
  177    char const *heredb, *indb;
  178    size_t linesize, heredl, indl, cnt, linelen;
  179    char *linebuf;
  180    si64_t lc, cc;
  181    si32_t rv;
  182    NYD_ENTER;
  183 
  184    rv = n_ERR_NONE;
  185    lc = cc = 0;
  186    linebuf = NULL; /* TODO line pool */
  187    linesize = 0;
  188    heredb = NULL;
  189    heredl = 0;
  190 
  191    /* The -M case is special */
  192    if(name == (char*)-1){
  193       fbuf = n_stdin;
  194       name = "-";
  195    }else if(name[0] == '-' &&
  196          (name[1] == '\0' || blankspacechar(name[1]))){
  197       fbuf = n_stdin;
  198       if(name[1] == '\0'){
  199          if(!(n_psonce & n_PSO_INTERACTIVE)){
  200             n_err(_("~< -: HERE-delimiter required in non-interactive mode\n"));
  201             rv = n_ERR_INVAL;
  202             goto jleave;
  203          }
  204       }else{
  205          for(heredb = &name[2]; *heredb != '\0' && blankspacechar(*heredb);
  206                ++heredb)
  207             ;
  208          if((heredl = strlen(heredb)) == 0){
  209 jdelim_empty:
  210             n_err(_("~< - HERE-delimiter: delimiter must not be empty\n"));
  211             rv = n_ERR_INVAL;
  212             goto jleave;
  213          }
  214 
  215          if(*heredb == '\''){
  216             for(indb = ++heredb; *indb != '\0' && *indb != '\''; ++indb)
  217                ;
  218             if(*indb == '\0'){
  219                n_err(_("~< - HERE-delimiter: missing trailing quote\n"));
  220                rv = n_ERR_INVAL;
  221                goto jleave;
  222             }else if(indb[1] != '\0'){
  223                n_err(_("~< - HERE-delimiter: trailing characters after "
  224                   "quote\n"));
  225                rv = n_ERR_INVAL;
  226                goto jleave;
  227             }
  228             if((heredl = PTR2SIZE(indb - heredb)) == 0)
  229                goto jdelim_empty;
  230             heredb = savestrbuf(heredb, heredl);
  231          }
  232       }
  233       name = "-";
  234    }else if((fbuf = Fopen(name, "r")) == NULL){
  235       n_perr(name, rv = n_err_no);
  236       goto jleave;
  237    }
  238 
  239    indl = indent ? strlen(indb = ok_vlook(indentprefix)) : 0;
  240 
  241    if(fbuf != n_stdin)
  242       cnt = fsize(fbuf);
  243    while(fgetline(&linebuf, &linesize, (fbuf == n_stdin ? NULL : &cnt),
  244          &linelen, fbuf, 0) != NULL){
  245       if(heredl > 0 && heredl == linelen - 1 &&
  246             !memcmp(heredb, linebuf, heredl)){
  247          heredb = NULL;
  248          break;
  249       }
  250 
  251       if(indl > 0){
  252          if(fwrite(indb, sizeof *indb, indl, _coll_fp) != indl){
  253             rv = n_err_no;
  254             goto jleave;
  255          }
  256          cc += indl;
  257       }
  258 
  259       if(fwrite(linebuf, sizeof *linebuf, linelen, _coll_fp) != linelen){
  260          rv = n_err_no;
  261          goto jleave;
  262       }
  263       cc += linelen;
  264       ++lc;
  265    }
  266    if(fflush(_coll_fp)){
  267       rv = n_err_no;
  268       goto jleave;
  269    }
  270 
  271    if(heredb != NULL)
  272       rv = n_ERR_NOTOBACCO;
  273 jleave:
  274    if(linebuf != NULL)
  275       free(linebuf);
  276    if(fbuf != NULL){
  277       if(fbuf != n_stdin)
  278          Fclose(fbuf);
  279       else if(heredl > 0)
  280          clearerr(n_stdin);
  281    }
  282 
  283    if(writestat)
  284       fprintf(n_stdout, "%s%s %" PRId64 "/%" PRId64 "\n",
  285          n_shexp_quote_cp(name, FAL0), (rv ? " " n_ERROR : n_empty), lc, cc);
  286    NYD_LEAVE;
  287    return rv;
  288 }
  289 
  290 static si32_t
  291 a_coll_insert_cmd(FILE *fp, char const *cmd){
  292    FILE *ibuf;
  293    si64_t lc, cc;
  294    si32_t rv;
  295    NYD_ENTER;
  296 
  297    rv = n_ERR_NONE;
  298    lc = cc = 0;
  299 
  300    if((ibuf = Popen(cmd, "r", ok_vlook(SHELL), NULL, 0)) != NULL){
  301       int c;
  302 
  303       while((c = getc(ibuf)) != EOF){ /* XXX bytewise, yuck! */
  304          if(putc(c, fp) == EOF){
  305             rv = n_err_no;
  306             break;
  307          }
  308          ++cc;
  309          if(c == '\n')
  310             ++lc;
  311       }
  312       if(!feof(ibuf) || ferror(ibuf)){
  313          if(rv == n_ERR_NONE)
  314             rv = n_ERR_IO;
  315       }
  316       if(!Pclose(ibuf, TRU1)){
  317          if(rv == n_ERR_NONE)
  318             rv = n_ERR_IO;
  319       }
  320    }else
  321       n_perr(cmd, rv = n_err_no);
  322 
  323    fprintf(n_stdout, "CMD%s %" PRId64 "/%" PRId64 "\n",
  324       (rv == n_ERR_NONE ? n_empty : " " n_ERROR), lc, cc);
  325    NYD_LEAVE;
  326    return rv;
  327 }
  328 
  329 static void
  330 print_collf(FILE *cf, struct header *hp)
  331 {
  332    char *lbuf;
  333    FILE *obuf;
  334    size_t cnt, linesize, linelen;
  335    NYD_ENTER;
  336 
  337    fflush_rewind(cf);
  338    cnt = (size_t)fsize(cf);
  339 
  340    if((obuf = Ftmp(NULL, "collfp", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
  341       n_perr(_("Can't create temporary file for `~p' command"), 0);
  342       goto jleave;
  343    }
  344 
  345    hold_all_sigs();
  346 
  347    fprintf(obuf, _("-------\nMessage contains:\n")); /* xxx SEARCH112 */
  348    puthead(TRU1, hp, obuf,
  349       (GIDENT | GTO | GSUBJECT | GCC | GBCC | GNL | GFILES | GCOMMA),
  350       SEND_TODISP, CONV_NONE, NULL, NULL);
  351 
  352    lbuf = NULL;
  353    linesize = 0;
  354    while(fgetline(&lbuf, &linesize, &cnt, &linelen, cf, 1))
  355       prout(lbuf, linelen, obuf);
  356    if(lbuf != NULL)
  357       free(lbuf);
  358 
  359    if(hp->h_attach != NULL){
  360       fputs(_("-------\nAttachments:\n"), obuf);
  361       n_attachment_list_print(hp->h_attach, obuf);
  362    }
  363 
  364    rele_all_sigs();
  365 
  366    page_or_print(obuf, 0);
  367 
  368    Fclose(obuf);
  369 jleave:
  370    NYD_LEAVE;
  371 }
  372 
  373 static si32_t
  374 a_coll_write(char const *name, FILE *fp, int f)
  375 {
  376    FILE *of;
  377    int c;
  378    si64_t lc, cc;
  379    si32_t rv;
  380    NYD_ENTER;
  381 
  382    rv = n_ERR_NONE;
  383 
  384    if(f) {
  385       fprintf(n_stdout, "%s ", n_shexp_quote_cp(name, FAL0));
  386       fflush(n_stdout);
  387    }
  388 
  389    if ((of = Fopen(name, "a")) == NULL) {
  390       n_perr(name, rv = n_err_no);
  391       goto jerr;
  392    }
  393 
  394    lc = cc = 0;
  395    while ((c = getc(fp)) != EOF) {
  396       ++cc;
  397       if (c == '\n')
  398          ++lc;
  399       if (putc(c, of) == EOF) {
  400          n_perr(name, rv = n_err_no);
  401          goto jerr;
  402       }
  403    }
  404    fprintf(n_stdout, _("%" PRId64 "/%" PRId64 "\n"), lc, cc);
  405 
  406 jleave:
  407    if(of != NULL)
  408       Fclose(of);
  409    fflush(n_stdout);
  410    NYD_LEAVE;
  411    return rv;
  412 jerr:
  413    putc('-', n_stdout);
  414    putc('\n', n_stdout);
  415    goto jleave;
  416 }
  417 
  418 static bool_t
  419 a_coll_message_inject_head(FILE *fp){
  420    bool_t rv;
  421    char const *cp, *cp_obsolete;
  422    NYD2_ENTER;
  423 
  424    cp_obsolete = ok_vlook(NAIL_HEAD);
  425    if(cp_obsolete != NULL)
  426       n_OBSOLETE(_("please use *message-inject-head*, not *NAIL_HEAD*"));
  427 
  428    if(((cp = ok_vlook(message_inject_head)) != NULL ||
  429          (cp = cp_obsolete) != NULL) && !a_coll_putesc(cp, TRU1, fp))
  430       rv = FAL0;
  431    else
  432       rv = TRU1;
  433    NYD2_LEAVE;
  434    return rv;
  435 }
  436 
  437 static bool_t
  438 a_coll_makeheader(FILE *fp, struct header *hp, si8_t *checkaddr_err,
  439    bool_t do_delayed_due_t)
  440 {
  441    FILE *nf;
  442    int c;
  443    bool_t rv;
  444    NYD_ENTER;
  445 
  446    rv = FAL0;
  447 
  448    if ((nf = Ftmp(NULL, "colhead", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
  449       n_perr(_("temporary mail edit file"), 0);
  450       goto jleave;
  451    }
  452 
  453    extract_header(fp, hp, checkaddr_err);
  454    if (checkaddr_err != NULL && *checkaddr_err != 0)
  455       goto jleave;
  456 
  457    /* In template mode some things have been delayed until the template has
  458     * been read */
  459    if(do_delayed_due_t){
  460       char const *cp;
  461 
  462       if((cp = ok_vlook(on_compose_enter)) != NULL){
  463          setup_from_and_sender(hp);
  464          temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
  465             hp);
  466       }
  467 
  468       if(!a_coll_message_inject_head(nf))
  469          goto jleave;
  470    }
  471 
  472    while ((c = getc(fp)) != EOF) /* XXX bytewise, yuck! */
  473       putc(c, nf);
  474 
  475    if (fp != _coll_fp)
  476       Fclose(_coll_fp);
  477    Fclose(fp);
  478    _coll_fp = nf;
  479    nf = NULL;
  480 
  481    if (check_from_and_sender(hp->h_from, hp->h_sender) == NULL)
  482       goto jleave;
  483    rv = TRU1;
  484 jleave:
  485    if(nf != NULL)
  486       Fclose(nf);
  487    NYD_LEAVE;
  488    return rv;
  489 }
  490 
  491 static si32_t
  492 a_coll_edit(int c, struct header *hp) /* TODO error(return) weird */
  493 {
  494    struct n_sigman sm;
  495    FILE *nf;
  496    sighandler_type volatile sigint;
  497    bool_t saved;
  498    si32_t volatile rv;
  499    NYD_ENTER;
  500 
  501    n_UNINIT(sigint, SIG_ERR);
  502    rv = n_ERR_NONE;
  503 
  504    if(!(saved = ok_blook(add_file_recipients)))
  505       ok_bset(add_file_recipients);
  506 
  507    n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL){
  508    case 0:
  509       sigint = safe_signal(SIGINT, SIG_IGN);
  510       break;
  511    default:
  512       rv = n_ERR_INTR;
  513       goto jleave;
  514    }
  515 
  516    nf = run_editor(_coll_fp, (off_t)-1, c, FAL0, hp, NULL, SEND_MBOX, sigint);
  517    if (nf != NULL) {
  518       if (hp) {
  519          if(!a_coll_makeheader(nf, hp, NULL, FAL0))
  520             rv = n_ERR_INVAL;
  521       } else {
  522          fseek(nf, 0L, SEEK_END);
  523          Fclose(_coll_fp);
  524          _coll_fp = nf;
  525       }
  526    } else
  527       rv = n_ERR_CHILD;
  528 
  529    n_sigman_cleanup_ping(&sm);
  530 jleave:
  531    if(!saved)
  532       ok_bclear(add_file_recipients);
  533    safe_signal(SIGINT, sigint);
  534    NYD_LEAVE;
  535    n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
  536    return rv;
  537 }
  538 
  539 static si32_t
  540 a_coll_pipe(char const *cmd)
  541 {
  542    int ws;
  543    FILE *nf;
  544    sighandler_type sigint;
  545    si32_t rv;
  546    NYD_ENTER;
  547 
  548    rv = n_ERR_NONE;
  549    sigint = safe_signal(SIGINT, SIG_IGN);
  550 
  551    if ((nf = Ftmp(NULL, "colpipe", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==NULL) {
  552       n_perr(_("temporary mail edit file"), rv = n_err_no);
  553       goto jout;
  554    }
  555 
  556    /* stdin = current message.  stdout = new message */
  557    fflush(_coll_fp);
  558    if (n_child_run(ok_vlook(SHELL), 0, fileno(_coll_fp), fileno(nf), "-c",
  559          cmd, NULL, NULL, &ws) < 0 || WEXITSTATUS(ws) != 0) {
  560       Fclose(nf);
  561       rv = n_ERR_CHILD;
  562       goto jout;
  563    }
  564 
  565    if (fsize(nf) == 0) {
  566       n_err(_("No bytes from %s !?\n"), n_shexp_quote_cp(cmd, FAL0));
  567       Fclose(nf);
  568       rv = n_ERR_NODATA;
  569       goto jout;
  570    }
  571 
  572    /* Take new files */
  573    fseek(nf, 0L, SEEK_END);
  574    Fclose(_coll_fp);
  575    _coll_fp = nf;
  576 jout:
  577    safe_signal(SIGINT, sigint);
  578    NYD_LEAVE;
  579    return rv;
  580 }
  581 
  582 static si32_t
  583 a_coll_forward(char const *ms, FILE *fp, int f)
  584 {
  585    int *msgvec, rv = 0;
  586    struct n_ignore const *itp;
  587    char const *tabst;
  588    enum sendaction action;
  589    NYD_ENTER;
  590 
  591    msgvec = salloc((size_t)(msgCount + 1) * sizeof *msgvec);
  592    if (getmsglist(ms, msgvec, 0) < 0) {
  593       rv = n_ERR_NOENT; /* XXX not really, should be handled there! */
  594       goto jleave;
  595    }
  596    if (*msgvec == 0) {
  597       *msgvec = first(0, MMNORM);
  598       if (*msgvec == 0) {
  599          n_err(_("No appropriate messages\n"));
  600          rv = n_ERR_NOENT;
  601          goto jleave;
  602       }
  603       msgvec[1] = 0;
  604    }
  605 
  606    if (f == 'f' || f == 'F' || f == 'u')
  607       tabst = NULL;
  608    else
  609       tabst = ok_vlook(indentprefix);
  610    if (f == 'u' || f == 'U')
  611       itp = n_IGNORE_ALL;
  612    else
  613       itp = upperchar(f) ? NULL : n_IGNORE_TYPE;
  614    action = (upperchar(f) && f != 'U') ? SEND_QUOTE_ALL : SEND_QUOTE;
  615 
  616    fprintf(n_stdout, _("Interpolating:"));
  617    srelax_hold();
  618    for (; *msgvec != 0; ++msgvec) {
  619       struct message *mp = message + *msgvec - 1;
  620 
  621       touch(mp);
  622       fprintf(n_stdout, " %d", *msgvec);
  623       fflush(n_stdout);
  624       if (sendmp(mp, fp, itp, tabst, action, NULL) < 0) {
  625          n_perr(_("temporary mail file"), 0);
  626          rv = n_ERR_IO;
  627          break;
  628       }
  629       srelax();
  630    }
  631    srelax_rele();
  632    fprintf(n_stdout, "\n");
  633 jleave:
  634    NYD_LEAVE;
  635    return rv;
  636 }
  637 
  638 static bool_t
  639 a_collect_plumbing(char const *ms, struct header *hp){
  640    /* TODO _collect_plumbing: instead of fields the basic headers should
  641     * TODO be in an array and have IDs, like in termcap etc., so then this
  642     * TODO could be simplified as table-walks.  Also true for arg-checks! */
  643    bool_t rv;
  644    char const *cp, *cmd[4];
  645    NYD2_ENTER;
  646 
  647    /* Protcol version for *on-compose-splice** -- update manual on change! */
  648 #define a_COLL_PLUMBING_VERSION "0 0 1"
  649    cp = ms;
  650 
  651    /* C99 */{
  652       size_t i;
  653 
  654       for(i = 0; i < n_NELEM(cmd); ++i){ /* TODO trim+strlist_split(_ifs?)() */
  655          while(blankchar(*cp))
  656             ++cp;
  657          if(*cp == '\0')
  658             cmd[i] = NULL;
  659          else{
  660             if(i < n_NELEM(cmd) - 1)
  661                for(cmd[i] = cp++; *cp != '\0' && !blankchar(*cp); ++cp)
  662                   ;
  663             else{
  664                /* Last slot takes all the rest of the line, less trailing WS */
  665                for(cmd[i] = cp++; *cp != '\0'; ++cp)
  666                   ;
  667                while(blankchar(cp[-1]))
  668                   --cp;
  669             }
  670             cmd[i] = savestrbuf(cmd[i], PTR2SIZE(cp - cmd[i]));
  671          }
  672       }
  673    }
  674 
  675    if(n_UNLIKELY(cmd[0] == NULL))
  676       goto jecmd;
  677    if(is_asccaseprefix(cmd[0], "header"))
  678       rv = a_collect__plumb_header(cp, hp, cmd);
  679    else if(is_asccaseprefix(cmd[0], "attachment"))
  680       rv = a_collect__plumb_attach(cp, hp, cmd);
  681    else{
  682 jecmd:
  683       fputs("500\n", n_stdout);
  684       rv = FAL0;
  685    }
  686    fflush(n_stdout);
  687 
  688    NYD2_LEAVE;
  689    return rv;
  690 }
  691 
  692 static bool_t
  693 a_collect__plumb_header(char const *cp, struct header *hp,
  694       char const *cmd[4]){
  695    uiz_t i;
  696    struct n_header_field *hfp;
  697    struct name *np, **npp;
  698    NYD2_ENTER;
  699 
  700    if(cmd[1] == NULL)
  701       goto jdefault;
  702 
  703    if(is_asccaseprefix(cmd[1], "insert")){ /* TODO LOGIC BELONGS head.c
  704        * TODO That is: Header::factory(string) -> object (blahblah).
  705        * TODO I.e., as long as we don't have regular RFC compliant parsers
  706        * TODO which differentiate in between structured and unstructured
  707        * TODO header fields etc., a little workaround */
  708       struct name *xnp;
  709       si8_t aerr;
  710       enum expand_addr_check_mode eacm;
  711       enum gfield ntype;
  712       bool_t mult_ok;
  713 
  714       if(cmd[2] == NULL || cmd[3] == NULL)
  715          goto jecmd;
  716 
  717       /* Strip [\r\n] which would render a body invalid XXX all controls? */
  718       /* C99 */{
  719          char *xp, c;
  720 
  721          cmd[3] = xp = savestr(cmd[3]);
  722          for(; (c = *xp) != '\0'; ++xp)
  723             if(c == '\n' || c == '\r')
  724                *xp = ' ';
  725       }
  726 
  727       if(!asccasecmp(cmd[2], cp = "Subject")){
  728          if(cmd[3][0] != '\0'){
  729             if(hp->h_subject != NULL)
  730                hp->h_subject = savecatsep(hp->h_subject, ' ', cmd[3]);
  731             else
  732                hp->h_subject = n_UNCONST(cmd[3]);
  733             fprintf(n_stdout, "210 %s 1\n", cp);
  734             goto jleave;
  735          }else
  736             goto j501cp;
  737       }
  738 
  739       mult_ok = TRU1;
  740       ntype = GEXTRA | GFULL | GFULLEXTRA;
  741       eacm = EACM_STRICT;
  742 
  743       if(!asccasecmp(cmd[2], cp = "From")){
  744          npp = &hp->h_from;
  745 jins:
  746          aerr = 0;
  747          if((np = lextract(cmd[3], ntype)) == NULL)
  748             goto j501cp;
  749 
  750          if((np = checkaddrs(np, eacm, &aerr), aerr != 0)){
  751             fprintf(n_stdout, "505 %s\n", cp);
  752             goto jleave;
  753          }
  754 
  755          /* Go to the end of the list, track whether it contains any
  756           * non-deleted entries */
  757          i = 0;
  758          if((xnp = *npp) != NULL)
  759             for(;; xnp = xnp->n_flink){
  760                if(!(xnp->n_type & GDEL))
  761                   ++i;
  762                if(xnp->n_flink == NULL)
  763                   break;
  764             }
  765 
  766          if(!mult_ok && (i != 0 || np->n_flink != NULL))
  767             fprintf(n_stdout, "506 %s\n", cp);
  768          else{
  769             if(xnp == NULL)
  770                *npp = np;
  771             else
  772                xnp->n_flink = np;
  773             np->n_blink = xnp;
  774             fprintf(n_stdout, "210 %s %" PRIuZ "\n", cp, ++i);
  775          }
  776          goto jleave;
  777       }
  778       if(!asccasecmp(cmd[2], cp = "Sender")){
  779          mult_ok = FAL0;
  780          npp = &hp->h_sender;
  781          goto jins;
  782       }
  783       if(!asccasecmp(cmd[2], cp = "To")){
  784          npp = &hp->h_to;
  785          ntype = GTO | GFULL;
  786          eacm = EACM_NORMAL | EAF_NAME;
  787          goto jins;
  788       }
  789       if(!asccasecmp(cmd[2], cp = "Cc")){
  790          npp = &hp->h_cc;
  791          ntype = GCC | GFULL;
  792          eacm = EACM_NORMAL | EAF_NAME;
  793          goto jins;
  794       }
  795       if(!asccasecmp(cmd[2], cp = "Bcc")){
  796          npp = &hp->h_bcc;
  797          ntype = GBCC | GFULL;
  798          eacm = EACM_NORMAL | EAF_NAME;
  799          goto jins;
  800       }
  801       if(!asccasecmp(cmd[2], cp = "Reply-To")){
  802          npp = &hp->h_reply_to;
  803          eacm = EACM_NONAME;
  804          goto jins;
  805       }
  806       if(!asccasecmp(cmd[2], cp = "Mail-Followup-To")){
  807          npp = &hp->h_mft;
  808          eacm = EACM_NONAME;
  809          goto jins;
  810       }
  811       if(!asccasecmp(cmd[2], cp = "Message-ID")){
  812          mult_ok = FAL0;
  813          npp = &hp->h_message_id;
  814          ntype = GREF;
  815          eacm = EACM_NONAME;
  816          goto jins;
  817       }
  818       if(!asccasecmp(cmd[2], cp = "References")){
  819          npp = &hp->h_ref;
  820          ntype = GREF;
  821          eacm = EACM_NONAME;
  822          goto jins;
  823       }
  824       if(!asccasecmp(cmd[2], cp = "In-Reply-To")){
  825          npp = &hp->h_in_reply_to;
  826          ntype = GREF;
  827          eacm = EACM_NONAME;
  828          goto jins;
  829       }
  830 
  831       if((cp = n_header_is_standard(cmd[2], UIZ_MAX)) != NULL){
  832          fprintf(n_stdout, "505 %s\n", cp);
  833          goto jleave;
  834       }
  835 
  836       /* Free-form header fields */
  837       /* C99 */{
  838          size_t nl, bl;
  839          struct n_header_field **hfpp;
  840 
  841          for(cp = cmd[2]; *cp != '\0'; ++cp)
  842             if(!fieldnamechar(*cp)){
  843                cp = cmd[2];
  844                goto j501cp;
  845             }
  846 
  847          for(i = 0, hfpp = &hp->h_user_headers; *hfpp != NULL; ++i)
  848             hfpp = &(*hfpp)->hf_next;
  849 
  850          nl = strlen(cp = cmd[2]) +1;
  851          bl = strlen(cmd[3]) +1;
  852          *hfpp = hfp = salloc(n_VSTRUCT_SIZEOF(struct n_header_field, hf_dat
  853                ) + nl + bl);
  854          hfp->hf_next = NULL;
  855          hfp->hf_nl = nl - 1;
  856          hfp->hf_bl = bl - 1;
  857          memcpy(&hfp->hf_dat[0], cp, nl);
  858          memcpy(&hfp->hf_dat[nl], cmd[3], bl);
  859          fprintf(n_stdout, "210 %s %" PRIuZ "\n", &hfp->hf_dat[0], ++i);
  860       }
  861       goto jleave;
  862    }
  863 
  864    if(is_asccaseprefix(cmd[1], "list")){
  865 jdefault:
  866       if(cmd[2] == NULL){
  867          fputs("210", n_stdout);
  868          if(hp->h_subject != NULL) fputs(" Subject", n_stdout);
  869          if(hp->h_from != NULL) fputs(" From", n_stdout);
  870          if(hp->h_sender != NULL) fputs(" Sender", n_stdout);
  871          if(hp->h_to != NULL) fputs(" To", n_stdout);
  872          if(hp->h_cc != NULL) fputs(" Cc", n_stdout);
  873          if(hp->h_bcc != NULL) fputs(" Bcc", n_stdout);
  874          if(hp->h_reply_to != NULL) fputs(" Reply-To", n_stdout);
  875          if(hp->h_mft != NULL) fputs(" Mail-Followup-To", n_stdout);
  876          if(hp->h_message_id != NULL) fputs(" Message-ID", n_stdout);
  877          if(hp->h_ref != NULL) fputs(" References", n_stdout);
  878          if(hp->h_in_reply_to != NULL) fputs(" In-Reply-To", n_stdout);
  879          if(hp->h_mailx_command != NULL) fputs(" Mailx-Command", n_stdout);
  880          if(hp->h_mailx_raw_to != NULL) fputs(" Mailx-Raw-To", n_stdout);
  881          if(hp->h_mailx_raw_cc != NULL) fputs(" Mailx-Raw-Cc", n_stdout);
  882          if(hp->h_mailx_raw_bcc != NULL) fputs(" Mailx-Raw-Bcc", n_stdout);
  883          if(hp->h_mailx_orig_from != NULL) fputs(" Mailx-Orig-From", n_stdout);
  884          if(hp->h_mailx_orig_to != NULL) fputs(" Mailx-Orig-To", n_stdout);
  885          if(hp->h_mailx_orig_cc != NULL) fputs(" Mailx-Orig-Cc", n_stdout);
  886          if(hp->h_mailx_orig_bcc != NULL) fputs(" Mailx-Orig-Bcc", n_stdout);
  887 
  888          /* Print only one instance of each free-form header */
  889          for(hfp = hp->h_user_headers; hfp != NULL; hfp = hfp->hf_next){
  890             struct n_header_field *hfpx;
  891 
  892             for(hfpx = hp->h_user_headers;; hfpx = hfpx->hf_next)
  893                if(hfpx == hfp){
  894                   putc(' ', n_stdout);
  895                   fputs(&hfp->hf_dat[0], n_stdout);
  896                   break;
  897                }else if(!asccasecmp(&hfpx->hf_dat[0], &hfp->hf_dat[0]))
  898                   break;
  899          }
  900          putc('\n', n_stdout);
  901          goto jleave;
  902       }
  903 
  904       if(cmd[3] != NULL)
  905          goto jecmd;
  906 
  907       if(!asccasecmp(cmd[2], cp = "Subject")){
  908          np = (hp->h_subject != NULL) ? (struct name*)-1 : NULL;
  909          goto jlist;
  910       }
  911       if(!asccasecmp(cmd[2], cp = "From")){
  912          np = hp->h_from;
  913 jlist:
  914          fprintf(n_stdout, "%s %s\n", (np == NULL ? "501" : "210"), cp);
  915          goto jleave;
  916       }
  917       if(!asccasecmp(cmd[2], cp = "Sender")){
  918          np = hp->h_sender;
  919          goto jlist;
  920       }
  921       if(!asccasecmp(cmd[2], cp = "To")){
  922          np = hp->h_to;
  923          goto jlist;
  924       }
  925       if(!asccasecmp(cmd[2], cp = "Cc")){
  926          np = hp->h_cc;
  927          goto jlist;
  928       }
  929       if(!asccasecmp(cmd[2], cp = "Bcc")){
  930          np = hp->h_bcc;
  931          goto jlist;
  932       }
  933       if(!asccasecmp(cmd[2], cp = "Reply-To")){
  934          np = hp->h_reply_to;
  935          goto jlist;
  936       }
  937       if(!asccasecmp(cmd[2], cp = "Mail-Followup-To")){
  938          np = hp->h_mft;
  939          goto jlist;
  940       }
  941       if(!asccasecmp(cmd[2], cp = "Message-ID")){
  942          np = hp->h_message_id;
  943          goto jlist;
  944       }
  945       if(!asccasecmp(cmd[2], cp = "References")){
  946          np = hp->h_ref;
  947          goto jlist;
  948       }
  949       if(!asccasecmp(cmd[2], cp = "In-Reply-To")){
  950          np = hp->h_in_reply_to;
  951          goto jlist;
  952       }
  953 
  954       if(!asccasecmp(cmd[2], cp = "Mailx-Command")){
  955          np = (hp->h_mailx_command != NULL) ? (struct name*)-1 : NULL;
  956          goto jlist;
  957       }
  958       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-To")){
  959          np = hp->h_mailx_raw_to;
  960          goto jlist;
  961       }
  962       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-Cc")){
  963          np = hp->h_mailx_raw_cc;
  964          goto jlist;
  965       }
  966       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-Bcc")){
  967          np = hp->h_mailx_raw_bcc;
  968          goto jlist;
  969       }
  970       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-From")){
  971          np = hp->h_mailx_orig_from;
  972          goto jlist;
  973       }
  974       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-To")){
  975          np = hp->h_mailx_orig_to;
  976          goto jlist;
  977       }
  978       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-Cc")){
  979          np = hp->h_mailx_orig_cc;
  980          goto jlist;
  981       }
  982       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-Bcc")){
  983          np = hp->h_mailx_orig_bcc;
  984          goto jlist;
  985       }
  986 
  987       /* Free-form header fields */
  988       for(cp = cmd[2]; *cp != '\0'; ++cp)
  989          if(!fieldnamechar(*cp)){
  990             cp = cmd[2];
  991             goto j501cp;
  992          }
  993       cp = cmd[2];
  994       for(hfp = hp->h_user_headers;; hfp = hfp->hf_next){
  995          if(hfp == NULL)
  996             goto j501cp;
  997          else if(!asccasecmp(cp, &hfp->hf_dat[0])){
  998             fprintf(n_stdout, "210 %s\n", &hfp->hf_dat[0]);
  999             break;
 1000          }
 1001       }
 1002       goto jleave;
 1003    }
 1004 
 1005    if(is_asccaseprefix(cmd[1], "remove")){
 1006       if(cmd[2] == NULL || cmd[3] != NULL)
 1007          goto jecmd;
 1008 
 1009       if(!asccasecmp(cmd[2], cp = "Subject")){
 1010          if(hp->h_subject != NULL){
 1011             hp->h_subject = NULL;
 1012             fprintf(n_stdout, "210 %s\n", cp);
 1013             goto jleave;
 1014          }else
 1015             goto j501cp;
 1016       }
 1017 
 1018       if(!asccasecmp(cmd[2], cp = "From")){
 1019          npp = &hp->h_from;
 1020 jrem:
 1021          if(*npp != NULL){
 1022             *npp = NULL;
 1023             fprintf(n_stdout, "210 %s\n", cp);
 1024             goto jleave;
 1025          }else
 1026             goto j501cp;
 1027       }
 1028       if(!asccasecmp(cmd[2], cp = "Sender")){
 1029          npp = &hp->h_sender;
 1030          goto jrem;
 1031       }
 1032       if(!asccasecmp(cmd[2], cp = "To")){
 1033          npp = &hp->h_to;
 1034          goto jrem;
 1035       }
 1036       if(!asccasecmp(cmd[2], cp = "Cc")){
 1037          npp = &hp->h_cc;
 1038          goto jrem;
 1039       }
 1040       if(!asccasecmp(cmd[2], cp = "Bcc")){
 1041          npp = &hp->h_bcc;
 1042          goto jrem;
 1043       }
 1044       if(!asccasecmp(cmd[2], cp = "Reply-To")){
 1045          npp = &hp->h_reply_to;
 1046          goto jrem;
 1047       }
 1048       if(!asccasecmp(cmd[2], cp = "Mail-Followup-To")){
 1049          npp = &hp->h_mft;
 1050          goto jrem;
 1051       }
 1052       if(!asccasecmp(cmd[2], cp = "Message-ID")){
 1053          npp = &hp->h_message_id;
 1054          goto jrem;
 1055       }
 1056       if(!asccasecmp(cmd[2], cp = "References")){
 1057          npp = &hp->h_ref;
 1058          goto jrem;
 1059       }
 1060       if(!asccasecmp(cmd[2], cp = "In-Reply-To")){
 1061          npp = &hp->h_in_reply_to;
 1062          goto jrem;
 1063       }
 1064 
 1065       if((cp = n_header_is_standard(cmd[2], UIZ_MAX)) != NULL){
 1066          fprintf(n_stdout, "505 %s\n", cp);
 1067          goto jleave;
 1068       }
 1069 
 1070       /* Free-form header fields (note j501cp may print non-normalized name) */
 1071       /* C99 */{
 1072          struct n_header_field **hfpp;
 1073          bool_t any;
 1074 
 1075          for(cp = cmd[2]; *cp != '\0'; ++cp)
 1076             if(!fieldnamechar(*cp)){
 1077                cp = cmd[2];
 1078                goto j501cp;
 1079             }
 1080          cp = cmd[2];
 1081 
 1082          for(any = FAL0, hfpp = &hp->h_user_headers; (hfp = *hfpp) != NULL;){
 1083             if(!asccasecmp(cp, &hfp->hf_dat[0])){
 1084                *hfpp = hfp->hf_next;
 1085                if(!any)
 1086                   fprintf(n_stdout, "210 %s\n", &hfp->hf_dat[0]);
 1087                any = TRU1;
 1088             }else
 1089                hfpp = &hfp->hf_next;
 1090          }
 1091          if(!any)
 1092             goto j501cp;
 1093       }
 1094       goto jleave;
 1095    }
 1096 
 1097    if(is_asccaseprefix(cmd[1], "remove-at")){
 1098       if(cmd[2] == NULL || cmd[3] == NULL)
 1099          goto jecmd;
 1100 
 1101       if((n_idec_uiz_cp(&i, cmd[3], 0, NULL
 1102                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1103             ) != n_IDEC_STATE_CONSUMED || i == 0){
 1104          fputs("505\n", n_stdout);
 1105          goto jleave;
 1106       }
 1107 
 1108       if(!asccasecmp(cmd[2], cp = "Subject")){
 1109          if(hp->h_subject != NULL && i == 1){
 1110             hp->h_subject = NULL;
 1111             fprintf(n_stdout, "210 %s 1\n", cp);
 1112             goto jleave;
 1113          }else
 1114             goto j501cp;
 1115       }
 1116 
 1117       if(!asccasecmp(cmd[2], cp = "From")){
 1118          npp = &hp->h_from;
 1119 jremat:
 1120          if((np = *npp) == NULL)
 1121             goto j501cp;
 1122          while(--i != 0 && np != NULL)
 1123             np = np->n_flink;
 1124          if(np == NULL)
 1125             goto j501cp;
 1126 
 1127          if(np->n_blink != NULL)
 1128             np->n_blink->n_flink = np->n_flink;
 1129          else
 1130             *npp = np->n_flink;
 1131          if(np->n_flink != NULL)
 1132             np->n_flink->n_blink = np->n_blink;
 1133 
 1134          fprintf(n_stdout, "210 %s\n", cp);
 1135          goto jleave;
 1136       }
 1137       if(!asccasecmp(cmd[2], cp = "Sender")){
 1138          npp = &hp->h_sender;
 1139          goto jremat;
 1140       }
 1141       if(!asccasecmp(cmd[2], cp = "To")){
 1142          npp = &hp->h_to;
 1143          goto jremat;
 1144       }
 1145       if(!asccasecmp(cmd[2], cp = "Cc")){
 1146          npp = &hp->h_cc;
 1147          goto jremat;
 1148       }
 1149       if(!asccasecmp(cmd[2], cp = "Bcc")){
 1150          npp = &hp->h_bcc;
 1151          goto jremat;
 1152       }
 1153       if(!asccasecmp(cmd[2], cp = "Reply-To")){
 1154          npp = &hp->h_reply_to;
 1155          goto jremat;
 1156       }
 1157       if(!asccasecmp(cmd[2], cp = "Mail-Followup-To")){
 1158          npp = &hp->h_mft;
 1159          goto jremat;
 1160       }
 1161       if(!asccasecmp(cmd[2], cp = "Message-ID")){
 1162          npp = &hp->h_message_id;
 1163          goto jremat;
 1164       }
 1165       if(!asccasecmp(cmd[2], cp = "References")){
 1166          npp = &hp->h_ref;
 1167          goto jremat;
 1168       }
 1169       if(!asccasecmp(cmd[2], cp = "In-Reply-To")){
 1170          npp = &hp->h_in_reply_to;
 1171          goto jremat;
 1172       }
 1173 
 1174       if((cp = n_header_is_standard(cmd[2], UIZ_MAX)) != NULL){
 1175          fprintf(n_stdout, "505 %s\n", cp);
 1176          goto jleave;
 1177       }
 1178 
 1179       /* Free-form header fields */
 1180       /* C99 */{
 1181          struct n_header_field **hfpp;
 1182 
 1183          for(cp = cmd[2]; *cp != '\0'; ++cp)
 1184             if(!fieldnamechar(*cp)){
 1185                cp = cmd[2];
 1186                goto j501cp;
 1187             }
 1188          cp = cmd[2];
 1189 
 1190          for(hfpp = &hp->h_user_headers; (hfp = *hfpp) != NULL;){
 1191             if(--i == 0){
 1192                *hfpp = hfp->hf_next;
 1193                fprintf(n_stdout, "210 %s %" PRIuZ "\n", &hfp->hf_dat[0], i);
 1194                break;
 1195             }else
 1196                hfpp = &hfp->hf_next;
 1197          }
 1198          if(hfp == NULL)
 1199             goto j501cp;
 1200       }
 1201       goto jleave;
 1202    }
 1203 
 1204    if(is_asccaseprefix(cmd[1], "show")){
 1205       if(cmd[2] == NULL || cmd[3] != NULL)
 1206          goto jecmd;
 1207 
 1208       if(!asccasecmp(cmd[2], cp = "Subject")){
 1209          if(hp->h_subject == NULL)
 1210             goto j501cp;
 1211          fprintf(n_stdout, "212 %s\n%s\n\n", cp, hp->h_subject);
 1212          goto jleave;
 1213       }
 1214 
 1215       if(!asccasecmp(cmd[2], cp = "From")){
 1216          np = hp->h_from;
 1217 jshow:
 1218          if(np != NULL){
 1219             fprintf(n_stdout, "211 %s\n", cp);
 1220             do if(!(np->n_type & GDEL))
 1221                fprintf(n_stdout, "%s %s\n", np->n_name, np->n_fullname);
 1222             while((np = np->n_flink) != NULL);
 1223             putc('\n', n_stdout);
 1224             goto jleave;
 1225          }else
 1226             goto j501cp;
 1227       }
 1228       if(!asccasecmp(cmd[2], cp = "Sender")){
 1229          np = hp->h_sender;
 1230          goto jshow;
 1231       }
 1232       if(!asccasecmp(cmd[2], cp = "To")){
 1233          np = hp->h_to;
 1234          goto jshow;
 1235       }
 1236       if(!asccasecmp(cmd[2], cp = "Cc")){
 1237          np = hp->h_cc;
 1238          goto jshow;
 1239       }
 1240       if(!asccasecmp(cmd[2], cp = "Bcc")){
 1241          np = hp->h_bcc;
 1242          goto jshow;
 1243       }
 1244       if(!asccasecmp(cmd[2], cp = "Reply-To")){
 1245          np = hp->h_reply_to;
 1246          goto jshow;
 1247       }
 1248       if(!asccasecmp(cmd[2], cp = "Mail-Followup-To")){
 1249          np = hp->h_mft;
 1250          goto jshow;
 1251       }
 1252       if(!asccasecmp(cmd[2], cp = "Message-ID")){
 1253          np = hp->h_message_id;
 1254          goto jshow;
 1255       }
 1256       if(!asccasecmp(cmd[2], cp = "References")){
 1257          np = hp->h_ref;
 1258          goto jshow;
 1259       }
 1260       if(!asccasecmp(cmd[2], cp = "In-Reply-To")){
 1261          np = hp->h_in_reply_to;
 1262          goto jshow;
 1263       }
 1264 
 1265       if(!asccasecmp(cmd[2], cp = "Mailx-Command")){
 1266          if(hp->h_mailx_command == NULL)
 1267             goto j501cp;
 1268          fprintf(n_stdout, "212 %s\n%s\n\n", cp, hp->h_mailx_command);
 1269          goto jleave;
 1270       }
 1271       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-To")){
 1272          np = hp->h_mailx_raw_to;
 1273          goto jshow;
 1274       }
 1275       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-Cc")){
 1276          np = hp->h_mailx_raw_cc;
 1277          goto jshow;
 1278       }
 1279       if(!asccasecmp(cmd[2], cp = "Mailx-Raw-Bcc")){
 1280          np = hp->h_mailx_raw_bcc;
 1281          goto jshow;
 1282       }
 1283       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-From")){
 1284          np = hp->h_mailx_orig_from;
 1285          goto jshow;
 1286       }
 1287       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-To")){
 1288          np = hp->h_mailx_orig_to;
 1289          goto jshow;
 1290       }
 1291       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-Cc")){
 1292          np = hp->h_mailx_orig_cc;
 1293          goto jshow;
 1294       }
 1295       if(!asccasecmp(cmd[2], cp = "Mailx-Orig-Bcc")){
 1296          np = hp->h_mailx_orig_bcc;
 1297          goto jshow;
 1298       }
 1299 
 1300       /* Free-form header fields */
 1301       /* C99 */{
 1302          bool_t any;
 1303 
 1304          for(cp = cmd[2]; *cp != '\0'; ++cp)
 1305             if(!fieldnamechar(*cp)){
 1306                cp = cmd[2];
 1307                goto j501cp;
 1308             }
 1309          cp = cmd[2];
 1310 
 1311          for(any = FAL0, hfp = hp->h_user_headers; hfp != NULL;
 1312                hfp = hfp->hf_next){
 1313             if(!asccasecmp(cp, &hfp->hf_dat[0])){
 1314                if(!any)
 1315                   fprintf(n_stdout, "212 %s\n", &hfp->hf_dat[0]);
 1316                any = TRU1;
 1317                fprintf(n_stdout, "%s\n", &hfp->hf_dat[hfp->hf_nl +1]);
 1318             }
 1319          }
 1320          if(any)
 1321             putc('\n', n_stdout);
 1322          else
 1323             goto j501cp;
 1324       }
 1325       goto jleave;
 1326    }
 1327 
 1328 jecmd:
 1329    fputs("500\n", n_stdout);
 1330    cp = NULL;
 1331 jleave:
 1332    NYD2_LEAVE;
 1333    return (cp != NULL);
 1334 
 1335 j501cp:
 1336    fputs("501 ", n_stdout);
 1337    fputs(cp, n_stdout);
 1338    putc('\n', n_stdout);
 1339    goto jleave;
 1340 }
 1341 
 1342 static bool_t
 1343 a_collect__plumb_attach(char const *cp, struct header *hp,
 1344       char const *cmd[4]){
 1345    bool_t status;
 1346    struct attachment *ap;
 1347    NYD2_ENTER;
 1348 
 1349    if(cmd[1] == NULL)
 1350       goto jdefault;
 1351 
 1352    if(is_asccaseprefix(cmd[1], "attribute")){
 1353       if(cmd[2] == NULL || cmd[3] != NULL)
 1354          goto jecmd;
 1355 
 1356       if((ap = n_attachment_find(hp->h_attach, cmd[2], NULL)) != NULL){
 1357 jatt_att:
 1358          fprintf(n_stdout, "212 %s\n", cmd[2]);
 1359          if(ap->a_msgno > 0)
 1360             fprintf(n_stdout, "message-number %d\n\n", ap->a_msgno);
 1361          else{
 1362             fprintf(n_stdout,
 1363                "creation-name %s\nopen-path %s\nfilename %s\n",
 1364                ap->a_path_user, ap->a_path, ap->a_name);
 1365             if(ap->a_content_description != NULL)
 1366                fprintf(n_stdout, "content-description %s\n",
 1367                   ap->a_content_description);
 1368             if(ap->a_content_id != NULL)
 1369                fprintf(n_stdout, "content-id %s\n",
 1370                   ap->a_content_id->n_name);
 1371             if(ap->a_content_type != NULL)
 1372                fprintf(n_stdout, "content-type %s\n", ap->a_content_type);
 1373             if(ap->a_content_disposition != NULL)
 1374                fprintf(n_stdout, "content-disposition %s\n",
 1375                   ap->a_content_disposition);
 1376             putc('\n', n_stdout);
 1377          }
 1378       }else
 1379          fputs("501\n", n_stdout);
 1380       goto jleave;
 1381    }
 1382 
 1383    if(is_asccaseprefix(cmd[1], "attribute-at")){
 1384       uiz_t i;
 1385 
 1386       if(cmd[2] == NULL || cmd[3] != NULL)
 1387          goto jecmd;
 1388 
 1389       if((n_idec_uiz_cp(&i, cmd[2], 0, NULL
 1390                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1391             ) != n_IDEC_STATE_CONSUMED || i == 0)
 1392          fputs("505\n", n_stdout);
 1393       else{
 1394          for(ap = hp->h_attach; ap != NULL && --i != 0; ap = ap->a_flink)
 1395             ;
 1396          if(ap != NULL)
 1397             goto jatt_att;
 1398          else
 1399             fputs("501\n", n_stdout);
 1400       }
 1401       goto jleave;
 1402    }
 1403 
 1404    if(is_asccaseprefix(cmd[1], "attribute-set")){
 1405       if(cmd[2] == NULL || cmd[3] == NULL)
 1406          goto jecmd;
 1407 
 1408       if((ap = n_attachment_find(hp->h_attach, cmd[2], NULL)) != NULL){
 1409 jatt_attset:
 1410          if(ap->a_msgno > 0)
 1411             fputs("505\n", n_stdout);
 1412          else{
 1413             char c, *keyw;
 1414 
 1415             cp = cmd[3];
 1416             while((c = *cp) != '\0' && !blankchar(c))
 1417                ++cp;
 1418             keyw = savestrbuf(cmd[3], PTR2SIZE(cp - cmd[3]));
 1419             if(c != '\0'){
 1420                for(; (c = *++cp) != '\0' && blankchar(c);)
 1421                   ;
 1422                if(c != '\0'){
 1423                   char *xp;
 1424 
 1425                   /* Strip [\r\n] which would render a parameter invalid XXX
 1426                    * XXX all controls? */
 1427                   cp = xp = savestr(cp);
 1428                   for(; (c = *xp) != '\0'; ++xp)
 1429                      if(c == '\n' || c == '\r')
 1430                         *xp = ' ';
 1431                   c = *cp;
 1432                }
 1433             }
 1434 
 1435             if(!asccasecmp(keyw, "filename"))
 1436                ap->a_name = (c == '\0') ? ap->a_path_bname : cp;
 1437             else if(!asccasecmp(keyw, "content-description"))
 1438                ap->a_content_description = (c == '\0') ? NULL : cp;
 1439             else if(!asccasecmp(keyw, "content-id")){
 1440                ap->a_content_id = NULL;
 1441 
 1442                if(c != '\0'){
 1443                   struct name *np;
 1444 
 1445                   np = checkaddrs(lextract(cp, GREF),
 1446                         /*EACM_STRICT | TODO '/' valid!! */ EACM_NOLOG |
 1447                         EACM_NONAME, NULL);
 1448                   if(np != NULL && np->n_flink == NULL)
 1449                      ap->a_content_id = np;
 1450                   else
 1451                      cp = NULL;
 1452                }
 1453             }else if(!asccasecmp(keyw, "content-type"))
 1454                ap->a_content_type = (c == '\0') ? NULL : cp;
 1455             else if(!asccasecmp(keyw, "content-disposition"))
 1456                ap->a_content_disposition = (c == '\0') ? NULL : cp;
 1457             else
 1458                cp = NULL;
 1459 
 1460             if(cp != NULL){
 1461                size_t i;
 1462 
 1463                for(i = 0; ap != NULL; ++i, ap = ap->a_blink)
 1464                   ;
 1465                fprintf(n_stdout, "210 %" PRIuZ "\n", i);
 1466             }else
 1467                fputs("505\n", n_stdout);
 1468          }
 1469       }else
 1470          fputs("501\n", n_stdout);
 1471       goto jleave;
 1472    }
 1473 
 1474    if(is_asccaseprefix(cmd[1], "attribute-set-at")){
 1475       uiz_t i;
 1476 
 1477       if(cmd[2] == NULL || cmd[3] == NULL)
 1478          goto jecmd;
 1479 
 1480       if((n_idec_uiz_cp(&i, cmd[2], 0, NULL
 1481                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1482             ) != n_IDEC_STATE_CONSUMED || i == 0)
 1483          fputs("505\n", n_stdout);
 1484       else{
 1485          for(ap = hp->h_attach; ap != NULL && --i != 0; ap = ap->a_flink)
 1486             ;
 1487          if(ap != NULL)
 1488             goto jatt_attset;
 1489          else
 1490             fputs("501\n", n_stdout);
 1491       }
 1492       goto jleave;
 1493    }
 1494 
 1495    if(is_asccaseprefix(cmd[1], "insert")){
 1496       enum n_attach_error aerr;
 1497 
 1498       if(cmd[2] == NULL || cmd[3] != NULL)
 1499          goto jecmd;
 1500 
 1501       hp->h_attach = n_attachment_append(hp->h_attach, cmd[2], &aerr, &ap);
 1502       switch(aerr){
 1503       case n_ATTACH_ERR_FILE_OPEN: cp = "505\n"; goto jatt_ins;
 1504       case n_ATTACH_ERR_ICONV_FAILED: cp = "506\n"; goto jatt_ins;
 1505       case n_ATTACH_ERR_ICONV_NAVAIL:
 1506       case n_ATTACH_ERR_OTHER:
 1507       default:
 1508          cp = "501\n";
 1509 jatt_ins:
 1510          fputs(cp, n_stdout);
 1511          break;
 1512       case n_ATTACH_ERR_NONE:{
 1513          size_t i;
 1514 
 1515          for(i = 0; ap != NULL; ++i, ap = ap->a_blink)
 1516             ;
 1517          fprintf(n_stdout, "210 %" PRIuZ "\n", i);
 1518          }break;
 1519       }
 1520       goto jleave;
 1521    }
 1522 
 1523    if(is_asccaseprefix(cmd[1], "list")){
 1524 jdefault:
 1525       if(cmd[2] != NULL)
 1526          goto jecmd;
 1527 
 1528       if((ap = hp->h_attach) != NULL){
 1529          fputs("212\n", n_stdout);
 1530          do
 1531             fprintf(n_stdout, "%s\n", ap->a_path_user);
 1532          while((ap = ap->a_flink) != NULL);
 1533          putc('\n', n_stdout);
 1534       }else
 1535          fputs("501\n", n_stdout);
 1536       goto jleave;
 1537    }
 1538 
 1539    if(is_asccaseprefix(cmd[1], "remove")){
 1540       if(cmd[2] == NULL || cmd[3] != NULL)
 1541          goto jecmd;
 1542 
 1543       if((ap = n_attachment_find(hp->h_attach, cmd[2], &status)) != NULL){
 1544          if(status == TRUM1)
 1545             fputs("506\n", n_stdout);
 1546          else{
 1547             hp->h_attach = n_attachment_remove(hp->h_attach, ap);
 1548             fprintf(n_stdout, "210 %s\n", cmd[2]);
 1549          }
 1550       }else
 1551          fputs("501\n", n_stdout);
 1552       goto jleave;
 1553    }
 1554 
 1555    if(is_asccaseprefix(cmd[1], "remove-at")){
 1556       uiz_t i;
 1557 
 1558       if(cmd[2] == NULL || cmd[3] != NULL)
 1559          goto jecmd;
 1560 
 1561       if((n_idec_uiz_cp(&i, cmd[2], 0, NULL
 1562                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1563             ) != n_IDEC_STATE_CONSUMED || i == 0)
 1564          fputs("505\n", n_stdout);
 1565       else{
 1566          for(ap = hp->h_attach; ap != NULL && --i != 0; ap = ap->a_flink)
 1567             ;
 1568          if(ap != NULL){
 1569             hp->h_attach = n_attachment_remove(hp->h_attach, ap);
 1570             fprintf(n_stdout, "210 %s\n", cmd[2]);
 1571          }else
 1572             fputs("501\n", n_stdout);
 1573       }
 1574       goto jleave;
 1575    }
 1576 
 1577 jecmd:
 1578    fputs("500\n", n_stdout);
 1579    cp = NULL;
 1580 jleave:
 1581    NYD2_LEAVE;
 1582    return (cp != NULL);
 1583 }
 1584 
 1585 static void
 1586 _collint(int s)
 1587 {
 1588    NYD_X; /* Signal handler */
 1589 
 1590    /* the control flow is subtle, because we can be called from ~q */
 1591    if (_coll_hadintr == 0) {
 1592       if (ok_blook(ignore)) {
 1593          fputs("@\n", n_stdout);
 1594          fflush(n_stdout);
 1595          clearerr(n_stdin);
 1596       } else
 1597          _coll_hadintr = 1;
 1598       siglongjmp(_coll_jmp, 1);
 1599    }
 1600    n_exit_status |= n_EXIT_SEND_ERROR;
 1601    if (s != 0)
 1602       savedeadletter(_coll_fp, TRU1);
 1603    /* Aborting message, no need to fflush() .. */
 1604    siglongjmp(_coll_abort, 1);
 1605 }
 1606 
 1607 static void
 1608 collhup(int s)
 1609 {
 1610    NYD_X; /* Signal handler */
 1611    n_UNUSED(s);
 1612 
 1613    savedeadletter(_coll_fp, TRU1);
 1614    /* Let's pretend nobody else wants to clean up, a true statement at
 1615     * this time */
 1616    exit(n_EXIT_ERR);
 1617 }
 1618 
 1619 static bool_t
 1620 a_coll_putesc(char const *s, bool_t addnl, FILE *stream){
 1621    char c1, c2;
 1622    bool_t isposix;
 1623    NYD2_ENTER;
 1624 
 1625    isposix = ok_blook(posix);
 1626 
 1627    while((c1 = *s++) != '\0'){
 1628       if(c1 == '\\' && ((c2 = *s) == 't' || c2 == 'n')){
 1629          if(!isposix){
 1630             isposix = TRU1; /* TODO v15 OBSOLETE! */
 1631             n_err(_("Compose mode warning: expanding \\t or \\n in variable "
 1632                   "without *posix*!"
 1633                "\n  Support remains only for ~A,~a,~I,~i in *posix* mode!\n"));
 1634          }
 1635          ++s;
 1636          c1 = (c2 == 't') ? '\t' : '\n';
 1637       }
 1638 
 1639       if(putc(c1, stream) == EOF)
 1640          goto jleave;
 1641    }
 1642 
 1643    if(addnl && putc('\n', stream) == EOF)
 1644       goto jleave;
 1645 
 1646 jleave:
 1647    NYD2_LEAVE;
 1648    return (c1 == '\0');
 1649 }
 1650 
 1651 static int
 1652 a_coll_ocs__mac(void){
 1653    /* Executes in a fork(2)ed child  TODO if remains, global MASKs for those! */
 1654    setvbuf(n_stdin, NULL, _IOLBF, 0);
 1655    setvbuf(n_stdout, NULL, _IOLBF, 0);
 1656    n_psonce &= ~(n_PSO_INTERACTIVE | n_PSO_TTYIN | n_PSO_TTYOUT);
 1657    n_pstate |= n_PS_COMPOSE_FORKHOOK;
 1658    n_readctl_overlay = NULL; /* TODO we need OnForkEvent! See c_readctl() */
 1659    if(n_poption & n_PO_D_VV){
 1660       char buf[128];
 1661 
 1662       snprintf(buf, sizeof buf, "[%d]%s", getpid(), ok_vlook(log_prefix));
 1663       ok_vset(log_prefix, buf);
 1664    }
 1665    /* TODO If that uses `!' it will effectively SIG_IGN SIGINT, ...and such */
 1666    temporary_compose_mode_hook_call(a_coll_ocs__macname, NULL, NULL);
 1667    return 0;
 1668 }
 1669 
 1670 static void
 1671 a_coll_ocs__finalize(void *vp){
 1672    /* Note we use this for destruction upon setup errors, thus */
 1673    sighandler_type opipe;
 1674    sighandler_type oint;
 1675    struct a_coll_ocs_arg **coapp, *coap;
 1676    NYD2_ENTER;
 1677 
 1678    temporary_compose_mode_hook_call((char*)-1, NULL, NULL);
 1679 
 1680    coap = *(coapp = vp);
 1681    *coapp = (struct a_coll_ocs_arg*)-1;
 1682 
 1683    if(coap->coa_stdin != NULL)
 1684       Fclose(coap->coa_stdin);
 1685    else if(coap->coa_pipe[0] != -1)
 1686       close(coap->coa_pipe[0]);
 1687 
 1688    if(coap->coa_stdout != NULL && !Pclose(coap->coa_stdout, TRU1))
 1689       *coap->coa_senderr = 111;
 1690    if(coap->coa_pipe[1] != -1)
 1691       close(coap->coa_pipe[1]);
 1692 
 1693    opipe = coap->coa_opipe;
 1694    oint = coap->coa_oint;
 1695 
 1696    n_lofi_free(coap);
 1697 
 1698    hold_all_sigs();
 1699    safe_signal(SIGPIPE, opipe);
 1700    safe_signal(SIGINT, oint);
 1701    rele_all_sigs();
 1702    NYD2_LEAVE;
 1703 }
 1704 
 1705 FL void
 1706 n_temporary_compose_hook_varset(void *arg){ /* TODO v15: drop */
 1707    struct header *hp;
 1708    char const *val;
 1709    NYD2_ENTER;
 1710 
 1711    hp = arg;
 1712 
 1713    if((val = hp->h_subject) == NULL)
 1714       val = n_empty;
 1715    ok_vset(mailx_subject, val);
 1716    if((val = detract(hp->h_from, GNAMEONLY)) == NULL)
 1717       val = n_empty;
 1718    ok_vset(mailx_from, val);
 1719    if((val = detract(hp->h_sender, GNAMEONLY)) == NULL)
 1720       val = n_empty;
 1721    ok_vset(mailx_sender, val);
 1722    if((val = detract(hp->h_to, GNAMEONLY)) == NULL)
 1723       val = n_empty;
 1724    ok_vset(mailx_to, val);
 1725    if((val = detract(hp->h_cc, GNAMEONLY)) == NULL)
 1726       val = n_empty;
 1727    ok_vset(mailx_cc, val);
 1728    if((val = detract(hp->h_bcc, GNAMEONLY)) == NULL)
 1729       val = n_empty;
 1730    ok_vset(mailx_bcc, val);
 1731 
 1732    if((val = hp->h_mailx_command) == NULL)
 1733       val = n_empty;
 1734    ok_vset(mailx_command, val);
 1735 
 1736    if((val = detract(hp->h_mailx_raw_to, GNAMEONLY)) == NULL)
 1737       val = n_empty;
 1738    ok_vset(mailx_raw_to, val);
 1739    if((val = detract(hp->h_mailx_raw_cc, GNAMEONLY)) == NULL)
 1740       val = n_empty;
 1741    ok_vset(mailx_raw_cc, val);
 1742    if((val = detract(hp->h_mailx_raw_bcc, GNAMEONLY)) == NULL)
 1743       val = n_empty;
 1744    ok_vset(mailx_raw_bcc, val);
 1745 
 1746    if((val = detract(hp->h_mailx_orig_from, GNAMEONLY)) == NULL)
 1747       val = n_empty;
 1748    ok_vset(mailx_orig_from, val);
 1749    if((val = detract(hp->h_mailx_orig_to, GNAMEONLY)) == NULL)
 1750       val = n_empty;
 1751    ok_vset(mailx_orig_to, val);
 1752    if((val = detract(hp->h_mailx_orig_cc, GNAMEONLY)) == NULL)
 1753       val = n_empty;
 1754    ok_vset(mailx_orig_cc, val);
 1755    if((val = detract(hp->h_mailx_orig_bcc, GNAMEONLY)) == NULL)
 1756       val = n_empty;
 1757    ok_vset(mailx_orig_bcc, val);
 1758    NYD2_LEAVE;
 1759 }
 1760 
 1761 FL FILE *
 1762 collect(struct header *hp, int printheaders, struct message *mp,
 1763    char const *quotefile, int doprefix, si8_t *checkaddr_err)
 1764 {
 1765    struct n_string s, * volatile sp;
 1766    struct n_ignore const *quoteitp;
 1767    struct a_coll_ocs_arg *coap;
 1768    int c;
 1769    int volatile t, eofcnt, getfields;
 1770    char volatile escape;
 1771    enum{
 1772       a_NONE,
 1773       a_ERREXIT = 1u<<0,
 1774       a_IGNERR = 1u<<1,
 1775       a_COAP_NOSIGTERM = 1u<<8
 1776 #define a_HARDERR() ((flags & (a_ERREXIT | a_IGNERR)) == a_ERREXIT)
 1777    } volatile flags;
 1778    char *linebuf;
 1779    char const *cp, *cp_base, * volatile coapm, * volatile ifs_saved;
 1780    size_t i, linesize; /* TODO line pool */
 1781    long cnt;
 1782    enum sendaction action;
 1783    sigset_t oset, nset;
 1784    FILE * volatile sigfp;
 1785    NYD_ENTER;
 1786 
 1787    _coll_fp = NULL;
 1788    sigfp = NULL;
 1789    linesize = 0;
 1790    linebuf = NULL;
 1791    eofcnt = 0;
 1792    ifs_saved = coapm = NULL;
 1793    coap = NULL;
 1794    sp = NULL;
 1795 
 1796    /* Start catching signals from here, but we're still die on interrupts
 1797     * until we're in the main loop */
 1798    sigfillset(&nset);
 1799    sigprocmask(SIG_BLOCK, &nset, &oset);
 1800    if ((_coll_saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
 1801       safe_signal(SIGINT, &_collint);
 1802    if ((_coll_savehup = safe_signal(SIGHUP, SIG_IGN)) != SIG_IGN)
 1803       safe_signal(SIGHUP, collhup);
 1804    if (sigsetjmp(_coll_abort, 1))
 1805       goto jerr;
 1806    if (sigsetjmp(_coll_jmp, 1))
 1807       goto jerr;
 1808    n_pstate |= n_PS_COMPOSE_MODE;
 1809    sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
 1810 
 1811    if ((_coll_fp = Ftmp(NULL, "collect", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
 1812          NULL) {
 1813       n_perr(_("temporary mail file"), 0);
 1814       goto jerr;
 1815    }
 1816 
 1817    /* If we are going to prompt for a subject, refrain from printing a newline
 1818     * after the headers (since some people mind) */
 1819    getfields = 0;
 1820    if(!(n_poption & n_PO_t_FLAG)){
 1821       t = GTO | GSUBJECT | GCC | GNL;
 1822       if(ok_blook(fullnames))
 1823          t |= GCOMMA;
 1824 
 1825       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
 1826          if(hp->h_subject == NULL && ok_blook(asksub)/* *ask* auto warped! */)
 1827             t &= ~GNL, getfields |= GSUBJECT;
 1828 
 1829          if(hp->h_to == NULL)
 1830             t &= ~GNL, getfields |= GTO;
 1831 
 1832          if(!ok_blook(bsdcompat) && !ok_blook(askatend)){
 1833             if(ok_blook(askbcc))
 1834                t &= ~GNL, getfields |= GBCC;
 1835             if(ok_blook(askcc))
 1836                t &= ~GNL, getfields |= GCC;
 1837          }
 1838       }
 1839    }else{
 1840       n_UNINIT(t, 0);
 1841    }
 1842 
 1843    _coll_hadintr = 0;
 1844 
 1845    if (!sigsetjmp(_coll_jmp, 1)) {
 1846       /* Ask for some headers first, as necessary */
 1847       if (getfields)
 1848          grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, getfields, 1);
 1849 
 1850       /* Execute compose-enter; delayed for -t mode */
 1851       if(!(n_poption & n_PO_t_FLAG) &&
 1852             (cp = ok_vlook(on_compose_enter)) != NULL){
 1853          setup_from_and_sender(hp);
 1854          temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
 1855             hp);
 1856       }
 1857 
 1858       /* TODO Mm: nope since it may require turning this into a multipart one */
 1859       if(!(n_poption & (n_PO_Mm_FLAG | n_PO_t_FLAG))){
 1860          if(!a_coll_message_inject_head(_coll_fp))
 1861             goto jerr;
 1862 
 1863          /* Quote an original message */
 1864          if (mp != NULL && (doprefix || (cp = ok_vlook(quote)) != NULL)) {
 1865             quoteitp = n_IGNORE_ALL;
 1866             action = SEND_QUOTE;
 1867             if (doprefix) {
 1868                char const *cp_v15compat;
 1869 
 1870                quoteitp = n_IGNORE_FWD;
 1871 
 1872                if((cp_v15compat = ok_vlook(fwdheading)) != NULL)
 1873                   n_OBSOLETE(_("please use *forward-inject-head*, "
 1874                      "not *fwdheading*"));
 1875                cp = ok_vlook(forward_inject_head);
 1876                if(cp == NULL && (cp = cp_v15compat) == NULL)
 1877                   cp = "-------- Original Message --------";
 1878                if (*cp != '\0' && fprintf(_coll_fp, "%s\n", cp) < 0)
 1879                   goto jerr;
 1880             } else if (!strcmp(cp, "noheading")) {
 1881                /*EMPTY*/;
 1882             } else if (!strcmp(cp, "headers")) {
 1883                quoteitp = n_IGNORE_TYPE;
 1884             } else if (!strcmp(cp, "allheaders")) {
 1885                quoteitp = NULL;
 1886                action = SEND_QUOTE_ALL;
 1887             } else {
 1888                cp = hfield1("from", mp);
 1889                if (cp != NULL && (cnt = (long)strlen(cp)) > 0) {
 1890                   if (xmime_write(cp, cnt, _coll_fp, CONV_FROMHDR, TD_NONE,
 1891                         NULL, NULL) < 0)
 1892                      goto jerr;
 1893                   if (fprintf(_coll_fp, _(" wrote:\n\n")) < 0)
 1894                      goto jerr;
 1895                }
 1896             }
 1897             if (fflush(_coll_fp))
 1898                goto jerr;
 1899             if (sendmp(mp, _coll_fp, quoteitp,
 1900                   (doprefix ? NULL : ok_vlook(indentprefix)),
 1901                   action, NULL) < 0)
 1902                goto jerr;
 1903          }
 1904       }
 1905 
 1906       if (quotefile != NULL) {
 1907          if((n_pstate_err_no = a_coll_include_file(quotefile, FAL0, FAL0)
 1908                ) != n_ERR_NONE)
 1909             goto jerr;
 1910       }
 1911 
 1912       if(n_psonce & n_PSO_INTERACTIVE){
 1913          if(!(n_pstate & n_PS_SOURCING)){
 1914             sp = n_string_creat_auto(&s);
 1915             sp = n_string_reserve(sp, 80);
 1916          }
 1917 
 1918          if(!(n_poption & n_PO_Mm_FLAG) && !(n_pstate & n_PS_ROBOT)){
 1919             /* Print what we have sofar also on the terminal (if useful) */
 1920             if (!ok_blook(editalong)) {
 1921                if (printheaders)
 1922                   puthead(TRU1, hp, n_stdout, t,
 1923                      SEND_TODISP, CONV_NONE, NULL, NULL);
 1924 
 1925                rewind(_coll_fp);
 1926                while ((c = getc(_coll_fp)) != EOF) /* XXX bytewise, yuck! */
 1927                   putc(c, n_stdout);
 1928                if (fseek(_coll_fp, 0, SEEK_END))
 1929                   goto jerr;
 1930             } else {
 1931                rewind(_coll_fp);
 1932                if(a_coll_edit('e', hp) != n_ERR_NONE)
 1933                   goto jerr;
 1934                /* Print msg mandated by the Mail Reference Manual */
 1935 jcont:
 1936                if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
 1937                   fputs(_("(continue)\n"), n_stdout);
 1938             }
 1939             fflush(n_stdout);
 1940          }
 1941       }
 1942    } else {
 1943       /* Come here for printing the after-signal message.  Duplicate messages
 1944        * won't be printed because the write is aborted if we get a SIGTTOU */
 1945       if(_coll_hadintr && (n_psonce & n_PSO_INTERACTIVE) &&
 1946             !(n_pstate & n_PS_ROBOT))
 1947          n_err(_("\n(Interrupt -- one more to kill letter)\n"));
 1948    }
 1949 
 1950    /* If not under shell hook control */
 1951    if(coap == NULL){
 1952       /* We're done with -M or -m TODO because: we are too stupid yet, above */
 1953       if(n_poption & n_PO_Mm_FLAG)
 1954          goto jout;
 1955       /* No command escapes, interrupts not expected.  Simply copy STDIN */
 1956       if(!(n_psonce & n_PSO_INTERACTIVE) &&
 1957             !(n_poption & (n_PO_t_FLAG | n_PO_TILDE_FLAG))){
 1958          linebuf = srealloc(linebuf, linesize = LINESIZE);
 1959          while ((i = fread(linebuf, sizeof *linebuf, linesize, n_stdin)) > 0) {
 1960             if (i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
 1961                goto jerr;
 1962          }
 1963          goto jout;
 1964       }
 1965    }
 1966 
 1967    /* The interactive collect loop */
 1968    if(coap == NULL)
 1969       escape = *ok_vlook(escape);
 1970    flags = ok_blook(errexit) ? a_ERREXIT : a_NONE;
 1971 
 1972    for(;;){
 1973       enum {a_HIST_NONE, a_HIST_ADD = 1u<<0, a_HIST_GABBY = 1u<<1} hist;
 1974 
 1975       /* C99 */{
 1976          enum n_go_input_flags gif;
 1977          bool_t histadd;
 1978 
 1979          gif = n_GO_INPUT_CTX_COMPOSE;
 1980          histadd = (sp != NULL);
 1981          if((n_psonce & n_PSO_INTERACTIVE) || (n_poption & n_PO_TILDE_FLAG)){
 1982             if(!(n_poption & n_PO_t_FLAG))
 1983                gif |= n_GO_INPUT_NL_ESC;
 1984          }
 1985          cnt = n_go_input(gif, n_empty, &linebuf, &linesize, NULL, &histadd);
 1986          hist = histadd ? a_HIST_ADD | a_HIST_GABBY : a_HIST_NONE;
 1987       }
 1988 
 1989       if(cnt < 0){
 1990          if(coap != NULL)
 1991             break;
 1992          if(n_poption & n_PO_t_FLAG){
 1993             fflush_rewind(_coll_fp);
 1994             /* It is important to set n_PSO_t_FLAG before extract_header()
 1995              * *and* keep n_PO_t_FLAG for the first parse of the message!
 1996              * TODO This is a hack, we need a clean approach for this */
 1997             n_psonce |= n_PSO_t_FLAG;
 1998             if(!a_coll_makeheader(_coll_fp, hp, checkaddr_err, TRU1))
 1999                goto jerr;
 2000             n_poption &= ~n_PO_t_FLAG;
 2001             continue;
 2002          }else if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
 2003                ok_blook(ignoreeof) && ++eofcnt < 4){
 2004             fprintf(n_stdout,
 2005                _("*ignoreeof* set, use `~.' to terminate letter\n"));
 2006             n_go_input_clearerr();
 2007             continue;
 2008          }
 2009          break;
 2010       }
 2011 
 2012       _coll_hadintr = 0;
 2013 
 2014       cp = linebuf;
 2015       if(cnt == 0)
 2016          goto jputnl;
 2017       else if(coap == NULL){
 2018          if(!(n_psonce & n_PSO_INTERACTIVE) && !(n_poption & n_PO_TILDE_FLAG))
 2019             goto jputline;
 2020          else if(cp[0] == '.'){
 2021             if(cnt == 1 && (ok_blook(dot) ||
 2022                   (ok_blook(posix) && ok_blook(ignoreeof))))
 2023                break;
 2024          }
 2025       }
 2026       if(cp[0] != escape){
 2027 jputline:
 2028          if(fwrite(cp, sizeof *cp, cnt, _coll_fp) != (size_t)cnt)
 2029             goto jerr;
 2030          /* TODO n_PS_READLINE_NL is a terrible hack to ensure that _in_all_-
 2031           * TODO _code_paths_ a file without trailing newline isn't modified
 2032           * TODO to contain one; the "saw-newline" needs to be part of an
 2033           * TODO I/O input machinery object */
 2034 jputnl:
 2035          if(n_pstate & n_PS_READLINE_NL){
 2036             if(putc('\n', _coll_fp) == EOF)
 2037                goto jerr;
 2038          }
 2039          continue;
 2040       }
 2041 
 2042       c = *(cp_base = ++cp);
 2043       if(--cnt == 0)
 2044          goto jearg;
 2045 
 2046       /* Avoid history entry? */
 2047       while(spacechar(c)){
 2048          hist = a_HIST_NONE;
 2049          c = *(cp_base = ++cp);
 2050          if(--cnt == 0)
 2051             goto jearg;
 2052       }
 2053 
 2054       /* It may just be an escaped escaped character, do that quick */
 2055       if(c == escape)
 2056          goto jputline;
 2057 
 2058       /* Avoid hard *errexit*? */
 2059       flags &= ~a_IGNERR;
 2060       if(c == '-'){
 2061          flags ^= a_IGNERR;
 2062          c = *++cp;
 2063          if(--cnt == 0)
 2064             goto jearg;
 2065       }
 2066 
 2067       /* Trim input, also to gain a somewhat normalized history entry */
 2068       ++cp;
 2069       if(--cnt > 0){
 2070          struct str x;
 2071 
 2072          x.s = n_UNCONST(cp);
 2073          x.l = (size_t)cnt;
 2074          n_str_trim_ifs(&x, TRU1);
 2075          x.s[x.l] = '\0';
 2076          cp = x.s;
 2077          cnt = (int)/*XXX*/x.l;
 2078       }
 2079 
 2080       if(hist != a_HIST_NONE){
 2081          sp = n_string_assign_c(sp, escape);
 2082          if(flags & a_IGNERR)
 2083             sp = n_string_push_c(sp, '-');
 2084          sp = n_string_push_c(sp, c);
 2085          if(cnt > 0){
 2086             sp = n_string_push_c(sp, ' ');
 2087             sp = n_string_push_buf(sp, cp, cnt);
 2088          }
 2089       }
 2090 
 2091       /* Switch over all command escapes */
 2092       switch(c){
 2093       default:
 2094          if(1){
 2095             char buf[sizeof(n_UNIREPL)];
 2096 
 2097             if(asciichar(c))
 2098                buf[0] = c, buf[1] = '\0';
 2099             else if(n_psonce & n_PSO_UNICODE)
 2100                memcpy(buf, n_unirepl, sizeof n_unirepl);
 2101             else
 2102                buf[0] = '?', buf[1] = '\0';
 2103             n_err(_("Unknown command escape: `%c%s'\n"), escape, buf);
 2104          }else
 2105 jearg:
 2106             n_err(_("Invalid command escape usage: %s\n"),
 2107                n_shexp_quote_cp(linebuf, FAL0));
 2108          if(a_HARDERR())
 2109             goto jerr;
 2110          n_pstate_err_no = n_ERR_INVAL;
 2111          n_pstate_ex_no = 1;
 2112          continue;
 2113       case '!':
 2114          /* Shell escape, send the balance of line to sh -c */
 2115          if(cnt == 0 || coap != NULL)
 2116             goto jearg;
 2117          else{
 2118             char const *argv[2];
 2119 
 2120             argv[0] = cp;
 2121             argv[1] = NULL;
 2122             n_pstate_ex_no = c_shell(argv); /* TODO history norm.; errexit? */
 2123          }
 2124          goto jhistcont;
 2125       case '.':
 2126          /* Simulate end of file on input */
 2127          if(cnt != 0 || coap != NULL)
 2128             goto jearg;
 2129          goto jout; /* TODO does not enter history, thus */
 2130       case ':':
 2131       case '_':
 2132          /* Escape to command mode, but be nice! *//* TODO command expansion
 2133           * TODO should be handled here so that we have unique history! */
 2134          if(cnt == 0)
 2135             goto jearg;
 2136          _execute_command(hp, cp, cnt);
 2137          if(ok_blook(errexit))
 2138             flags |= a_ERREXIT;
 2139          else
 2140             flags &= ~a_ERREXIT;
 2141          if(n_pstate_ex_no != 0 && a_HARDERR())
 2142             goto jerr;
 2143          if(coap == NULL)
 2144             escape = *ok_vlook(escape);
 2145          break;
 2146       /* case '<': <> 'd' */
 2147       case '?':
 2148          fputs(_(
 2149 "COMMAND ESCAPES (to be placed after a newline) excerpt:\n"
 2150 "~.            Commit and send message\n"
 2151 "~: <command>  Execute an internal command\n"
 2152 "~< <file>     Insert <file> (\"~<! <command>\" inserts shell command)\n"
 2153 "~@ [<files>]  Edit[/Add] attachments (file[=input-charset[#output-charset]])\n"
 2154 "~c <users>    Add users to Cc: list (`~b': to Bcc:)\n"
 2155 "~d            Read in $DEAD (dead.letter)\n"
 2156 "~e            Edit message via $EDITOR\n"
 2157 "~F <msglist>  Read in with headers, do not *indentprefix* lines\n"
 2158             ), n_stdout);
 2159          fputs(_(
 2160 "~f <msglist>  Like ~F, but honour `ignore' / `retain' configuration\n"
 2161 "~H            Edit From:, Reply-To: and Sender:\n"
 2162 "~h            Prompt for Subject:, To:, Cc: and \"blind\" Bcc:\n"
 2163 "~i <variable> Insert a value and a newline\n"
 2164 "~M <msglist>  Read in with headers, *indentprefix* (`~m': `retain' etc.)\n"
 2165 "~p            Show current message compose buffer\n"
 2166 "~r <file>     Insert <file> (`~R': likewise, but *indentprefix* lines)\n"
 2167 "              <file> may also be <- [HERE-DELIMITER]>\n"
 2168             ), n_stdout);
 2169          fputs(_(
 2170 "~s <subject>  Set Subject:\n"
 2171 "~t <users>    Add users to To: list\n"
 2172 "~u <msglist>  Read in message(s) without headers (`~U': indent lines)\n"
 2173 "~v            Edit message via $VISUAL\n"
 2174 "~w <file>     Write message onto file\n"
 2175 "~x            Abort composition, discard message (`~q': save in $DEAD)\n"
 2176 "~| <command>  Pipe message through shell filter\n"
 2177             ), n_stdout);
 2178          if(cnt != 0)
 2179             goto jearg;
 2180          n_pstate_err_no = n_ERR_NONE;
 2181          n_pstate_ex_no = 0;
 2182          break;
 2183       case '@':{
 2184          struct attachment *aplist;
 2185 
 2186          /* Edit the attachment list */
 2187          aplist = hp->h_attach;
 2188          hp->h_attach = NULL;
 2189          if(cnt != 0)
 2190             hp->h_attach = n_attachment_append_list(aplist, cp);
 2191          else
 2192             hp->h_attach = n_attachment_list_edit(aplist,
 2193                   n_GO_INPUT_CTX_COMPOSE);
 2194          n_pstate_err_no = n_ERR_NONE; /* XXX ~@ does NOT handle $!/$?! */
 2195          n_pstate_ex_no = 0; /* XXX */
 2196          }break;
 2197       case '^':
 2198          if(!a_collect_plumbing(cp, hp)){
 2199             if(ferror(_coll_fp))
 2200                goto jerr;
 2201             goto jearg;
 2202          }
 2203          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2204          n_pstate_ex_no = 0; /* XXX */
 2205          hist &= ~a_HIST_GABBY;
 2206          break;
 2207       /* case '_': <> ':' */
 2208       case '|':
 2209          /* Pipe message through command. Collect output as new message */
 2210          if(cnt == 0)
 2211             goto jearg;
 2212          rewind(_coll_fp);
 2213          if((n_pstate_err_no = a_coll_pipe(cp)) == n_ERR_NONE)
 2214             n_pstate_ex_no = 0;
 2215          else if(a_HARDERR())
 2216             goto jerr;
 2217          else
 2218             n_pstate_ex_no = 1;
 2219          hist &= ~a_HIST_GABBY;
 2220          goto jhistcont;
 2221       case 'A':
 2222       case 'a':
 2223          /* Insert the contents of a sign variable */
 2224          if(cnt != 0)
 2225             goto jearg;
 2226          cp = (c == 'a') ? ok_vlook(sign) : ok_vlook(Sign);
 2227          goto jIi_putesc;
 2228       case 'b':
 2229          /* Add stuff to blind carbon copies list TODO join 'c' */
 2230          if(cnt == 0)
 2231             goto jearg;
 2232          else{
 2233             struct name *np;
 2234             si8_t soe;
 2235 
 2236             soe = 0;
 2237             if((np = checkaddrs(lextract(cp, GBCC | GFULL), EACM_NORMAL, &soe)
 2238                   ) != NULL)
 2239                hp->h_bcc = cat(hp->h_bcc, np);
 2240             if(soe == 0){
 2241                n_pstate_err_no = n_ERR_NONE;
 2242                n_pstate_ex_no = 0;
 2243             }else{
 2244                n_pstate_ex_no = 1;
 2245                n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
 2246             }
 2247          }
 2248          hist &= ~a_HIST_GABBY;
 2249          break;
 2250       case 'c':
 2251          /* Add to the CC list TODO join 'b' */
 2252          if(cnt == 0)
 2253             goto jearg;
 2254          else{
 2255             struct name *np;
 2256             si8_t soe;
 2257 
 2258             soe = 0;
 2259             if((np = checkaddrs(lextract(cp, GCC | GFULL), EACM_NORMAL, &soe)
 2260                   ) != NULL)
 2261                hp->h_cc = cat(hp->h_cc, np);
 2262             if(soe == 0){
 2263                n_pstate_err_no = n_ERR_NONE;
 2264                n_pstate_ex_no = 0;
 2265             }else{
 2266                n_pstate_ex_no = 1;
 2267                n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
 2268             }
 2269          }
 2270          hist &= ~a_HIST_GABBY;
 2271          break;
 2272       case 'd':
 2273          if(cnt != 0)
 2274             goto jearg;
 2275          cp = n_getdeadletter();
 2276          if(0){
 2277       case '<':
 2278       case 'R':
 2279       case 'r':
 2280             /* Invoke a file: Search for the file name, then open it and copy
 2281              * the contents to _coll_fp */
 2282             if(cnt == 0){
 2283                n_err(_("Interpolate what file?\n"));
 2284                if(a_HARDERR())
 2285                   goto jerr;
 2286                n_pstate_err_no = n_ERR_NOENT;
 2287                n_pstate_ex_no = 1;
 2288                break;
 2289             }
 2290             if(*cp == '!' && c == '<'){
 2291                /* TODO hist. normalization */
 2292                if((n_pstate_err_no = a_coll_insert_cmd(_coll_fp, ++cp)
 2293                      ) != n_ERR_NONE){
 2294                   if(ferror(_coll_fp))
 2295                      goto jerr;
 2296                   if(a_HARDERR())
 2297                      goto jerr;
 2298                   n_pstate_ex_no = 1;
 2299                   break;
 2300                }
 2301                goto jhistcont;
 2302             }
 2303             /* Note this also expands things like
 2304              *    !:vput vexpr delim random 0
 2305              *    !< - $delim */
 2306             if((cp = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO | FEXP_NSHELL)
 2307                   ) == NULL){
 2308                if(a_HARDERR())
 2309                   goto jerr;
 2310                n_pstate_err_no = n_ERR_INVAL;
 2311                n_pstate_ex_no = 1;
 2312                break;
 2313             }
 2314          }
 2315          if(n_is_dir(cp, FAL0)){
 2316             n_err(_("%s: is a directory\n"), n_shexp_quote_cp(cp, FAL0));
 2317             if(a_HARDERR())
 2318                goto jerr;
 2319             n_pstate_err_no = n_ERR_ISDIR;
 2320             n_pstate_ex_no = 1;
 2321             break;
 2322          }
 2323          if((n_pstate_err_no = a_coll_include_file(cp, (c == 'R'), TRU1)
 2324                ) != n_ERR_NONE){
 2325             if(ferror(_coll_fp))
 2326                goto jerr;
 2327             if(a_HARDERR())
 2328                goto jerr;
 2329             n_pstate_ex_no = 1;
 2330             break;
 2331          }
 2332          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2333          n_pstate_ex_no = 0; /* XXX */
 2334          break;
 2335       case 'e':
 2336       case 'v':
 2337          /* Edit the current message.  'e' -> use EDITOR, 'v' -> use VISUAL */
 2338          if(cnt != 0 || coap != NULL)
 2339             goto jearg;
 2340          rewind(_coll_fp);
 2341          if((n_pstate_err_no = a_coll_edit(c, ok_blook(editheaders) ? hp : NULL)
 2342                ) == n_ERR_NONE)
 2343             n_pstate_ex_no = 0;
 2344          else if(ferror(_coll_fp))
 2345             goto jerr;
 2346          else if(a_HARDERR())
 2347             goto jerr;
 2348          else
 2349             n_pstate_ex_no = 1;
 2350          goto jhistcont;
 2351       case 'F':
 2352       case 'f':
 2353       case 'M':
 2354       case 'm':
 2355       case 'U':
 2356       case 'u':
 2357          /* Interpolate the named messages, if we are in receiving mail mode.
 2358           * Does the standard list processing garbage.  If ~f is given, we
 2359           * don't shift over */
 2360          if(cnt == 0)
 2361             goto jearg;
 2362          if((n_pstate_err_no = a_coll_forward(cp, _coll_fp, c)) == n_ERR_NONE)
 2363             n_pstate_ex_no = 0;
 2364          else if(ferror(_coll_fp))
 2365             goto jerr;
 2366          else if(a_HARDERR())
 2367             goto jerr;
 2368          else
 2369             n_pstate_ex_no = 1;
 2370          break;
 2371       case 'H':
 2372          /* Grab extra headers */
 2373          if(cnt != 0)
 2374             goto jearg;
 2375          do
 2376             grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, GEXTRA, 0);
 2377          while(check_from_and_sender(hp->h_from, hp->h_sender) == NULL);
 2378          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2379          n_pstate_ex_no = 0; /* XXX */
 2380          break;
 2381       case 'h':
 2382          /* Grab a bunch of headers */
 2383          if(cnt != 0)
 2384             goto jearg;
 2385          do
 2386             grab_headers(n_GO_INPUT_CTX_COMPOSE, hp,
 2387               (GTO | GSUBJECT | GCC | GBCC),
 2388               (ok_blook(bsdcompat) && ok_blook(bsdorder)));
 2389          while(hp->h_to == NULL);
 2390          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2391          n_pstate_ex_no = 0; /* XXX */
 2392          break;
 2393       case 'I':
 2394       case 'i':
 2395          /* Insert a variable into the file */
 2396          if(cnt == 0)
 2397             goto jearg;
 2398          cp = n_var_vlook(cp, TRU1);
 2399 jIi_putesc:
 2400          if(cp == NULL || *cp == '\0')
 2401             break;
 2402          if(!a_coll_putesc(cp, (c != 'I'), _coll_fp))
 2403             goto jerr;
 2404          if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
 2405                !a_coll_putesc(cp, (c != 'I'), n_stdout))
 2406             goto jerr;
 2407          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2408          n_pstate_ex_no = 0; /* XXX */
 2409          break;
 2410       /* case 'M': <> 'F' */
 2411       /* case 'm': <> 'f' */
 2412       case 'p':
 2413          /* Print current state of the message without altering anything */
 2414          if(cnt != 0)
 2415             goto jearg;
 2416          print_collf(_coll_fp, hp); /* XXX pstate_err_no ++ */
 2417          if(ferror(_coll_fp))
 2418             goto jerr;
 2419          n_pstate_err_no = n_ERR_NONE; /* XXX */
 2420          n_pstate_ex_no = 0; /* XXX */
 2421          break;
 2422       case 'q':
 2423       case 'x':
 2424          /* Force a quit, act like an interrupt had happened */
 2425          if(cnt != 0)
 2426             goto jearg;
 2427          /* If we are running a splice hook, assume it quits on its own now,
 2428           * otherwise we (no true out-of-band IPC to signal this state, XXX sic)
 2429           * have to SIGTERM it in order to stop this wild beast */
 2430          flags |= a_COAP_NOSIGTERM;
 2431          ++_coll_hadintr;
 2432          _collint((c == 'x') ? 0 : SIGINT);
 2433          exit(n_EXIT_ERR);
 2434          /*NOTREACHED*/
 2435       /* case 'R': <> 'd' */
 2436       /* case 'r': <> 'd' */
 2437       case 's':
 2438          /* Set the Subject list */
 2439          if(cnt == 0)
 2440             goto jearg;
 2441          /* Subject:; take care for Debian #419840 and strip any \r and \n */
 2442          if(n_anyof_cp("\n\r", hp->h_subject = savestr(cp))){
 2443             char *xp;
 2444 
 2445             n_err(_("-s: normalizing away invalid ASCII NL / CR bytes\n"));
 2446             for(xp = hp->h_subject; *xp != '\0'; ++xp)
 2447                if(*xp == '\n' || *xp == '\r')
 2448                   *xp = ' ';
 2449             n_pstate_err_no = n_ERR_INVAL;
 2450             n_pstate_ex_no = 1;
 2451          }else{
 2452             n_pstate_err_no = n_ERR_NONE;
 2453             n_pstate_ex_no = 0;
 2454          }
 2455          break;
 2456       case 't':
 2457          /* Add to the To: list TODO join 'b', 'c' */
 2458          if(cnt == 0)
 2459             goto jearg;
 2460          else{
 2461             struct name *np;
 2462             si8_t soe;
 2463 
 2464             soe = 0;
 2465             if((np = checkaddrs(lextract(cp, GTO | GFULL), EACM_NORMAL, &soe)
 2466                   ) != NULL)
 2467                hp->h_to = cat(hp->h_to, np);
 2468             if(soe == 0){
 2469                n_pstate_err_no = n_ERR_NONE;
 2470                n_pstate_ex_no = 0;
 2471             }else{
 2472                n_pstate_ex_no = 1;
 2473                n_pstate_err_no = (soe < 0) ? n_ERR_PERM : n_ERR_INVAL;
 2474             }
 2475          }
 2476          hist &= ~a_HIST_GABBY;
 2477          break;
 2478       /* case 'U': <> 'F' */
 2479       /* case 'u': <> 'f' */
 2480       /* case 'v': <> 'e' */
 2481       case 'w':
 2482          /* Write the message on a file */
 2483          if(cnt == 0)
 2484             goto jearg;
 2485          if((cp = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) == NULL){
 2486             n_err(_("Write what file!?\n"));
 2487             if(a_HARDERR())
 2488                goto jerr;
 2489             n_pstate_err_no = n_ERR_INVAL;
 2490             n_pstate_ex_no = 1;
 2491             break;
 2492          }
 2493          rewind(_coll_fp);
 2494          if((n_pstate_err_no = a_coll_write(cp, _coll_fp, 1)) == n_ERR_NONE)
 2495             n_pstate_ex_no = 0;
 2496          else if(ferror(_coll_fp))
 2497             goto jerr;
 2498          else if(a_HARDERR())
 2499             goto jerr;
 2500          else
 2501             n_pstate_ex_no = 1;
 2502          break;
 2503       /* case 'x': <> 'q' */
 2504       }
 2505 
 2506       /* Finally place an entry in history as applicable */
 2507       if(0){
 2508 jhistcont:
 2509          c = '\1';
 2510       }else
 2511          c = '\0';
 2512       if(hist & a_HIST_ADD){
 2513          /* Do not add *escape* to the history in order to allow history search
 2514           * to be handled generically in the MLE regardless of actual *escape*
 2515           * settings etc. */
 2516          n_tty_addhist(&n_string_cp(sp)[1], (n_GO_INPUT_CTX_COMPOSE |
 2517             (hist & a_HIST_GABBY ? n_GO_INPUT_HIST_GABBY : n_GO_INPUT_NONE)));
 2518       }
 2519       if(c != '\0')
 2520          goto jcont;
 2521    }
 2522 
 2523 jout:
 2524    /* Do we have *on-compose-splice-shell*, or *on-compose-splice*?
 2525     * TODO Usual f...ed up state of signals and terminal etc. */
 2526    if(coap == NULL && (cp = ok_vlook(on_compose_splice_shell)) != NULL) Jocs:{
 2527       union {int (*ptf)(void); char const *sh;} u;
 2528       char const *cmd;
 2529 
 2530       /* Reset *escape* and more to their defaults.  On change update manual! */
 2531       if(ifs_saved == NULL)
 2532          ifs_saved = savestr(ok_vlook(ifs));
 2533       escape = n_ESCAPE[0];
 2534       ok_vclear(ifs);
 2535 
 2536       if(coapm != NULL){
 2537          /* XXX Due Popen() fflush(NULL) in PTF mode, ensure nothing to flush */
 2538          /*if(!n_real_seek(_coll_fp, 0, SEEK_END))
 2539           *  goto jerr;*/
 2540          u.ptf = &a_coll_ocs__mac;
 2541          cmd = (char*)-1;
 2542          a_coll_ocs__macname = cp = coapm;
 2543       }else{
 2544          u.sh = ok_vlook(SHELL);
 2545          cmd = cp;
 2546       }
 2547 
 2548       i = strlen(cp) +1;
 2549       coap = n_lofi_alloc(n_VSTRUCT_SIZEOF(struct a_coll_ocs_arg, coa_cmd
 2550             ) + i);
 2551       coap->coa_pipe[0] = coap->coa_pipe[1] = -1;
 2552       coap->coa_stdin = coap->coa_stdout = NULL;
 2553       coap->coa_senderr = checkaddr_err;
 2554       memcpy(coap->coa_cmd, cp, i);
 2555 
 2556       hold_all_sigs();
 2557       coap->coa_opipe = safe_signal(SIGPIPE, SIG_IGN);
 2558       coap->coa_oint = safe_signal(SIGINT, SIG_IGN);
 2559       rele_all_sigs();
 2560 
 2561       if(pipe_cloexec(coap->coa_pipe) != -1 &&
 2562             (coap->coa_stdin = Fdopen(coap->coa_pipe[0], "r", FAL0)) != NULL &&
 2563             (coap->coa_stdout = Popen(cmd, "W", u.sh, NULL, coap->coa_pipe[1])
 2564              ) != NULL){
 2565          close(coap->coa_pipe[1]);
 2566          coap->coa_pipe[1] = -1;
 2567 
 2568          temporary_compose_mode_hook_call(NULL, NULL, NULL);
 2569          n_go_splice_hack(coap->coa_cmd, coap->coa_stdin, coap->coa_stdout,
 2570             (n_psonce & ~(n_PSO_INTERACTIVE | n_PSO_TTYIN | n_PSO_TTYOUT)),
 2571             &a_coll_ocs__finalize, &coap);
 2572          /* Hook version protocol for ~^: update manual upon change! */
 2573          fputs(a_COLL_PLUMBING_VERSION "\n", coap->coa_stdout);
 2574 #undef a_COLL_PLUMBING_VERSION
 2575          goto jcont;
 2576       }
 2577 
 2578       c = n_err_no;
 2579       a_coll_ocs__finalize(coap);
 2580       n_perr(_("Cannot invoke *on-compose-splice(-shell)?*"), c);
 2581       goto jerr;
 2582    }
 2583    if(*checkaddr_err != 0){
 2584       *checkaddr_err = 0;
 2585       goto jerr;
 2586    }
 2587    if(coapm == NULL && (coapm = ok_vlook(on_compose_splice)) != NULL)
 2588       goto Jocs;
 2589    if(coap != NULL){
 2590       ok_vset(ifs, ifs_saved);
 2591       ifs_saved = NULL;
 2592    }
 2593 
 2594    /* Final chance to edit headers, if not already above */
 2595    if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
 2596       if(ok_blook(bsdcompat) || ok_blook(askatend)){
 2597          enum gfield gf;
 2598 
 2599          gf = GNONE;
 2600          if(ok_blook(askcc))
 2601             gf |= GCC;
 2602          if(ok_blook(askbcc))
 2603             gf |= GBCC;
 2604          if(gf != 0)
 2605             grab_headers(n_GO_INPUT_CTX_COMPOSE, hp, gf, 1);
 2606       }
 2607       if(ok_blook(askattach))
 2608          hp->h_attach = n_attachment_list_edit(hp->h_attach,
 2609                n_GO_INPUT_CTX_COMPOSE);
 2610    }
 2611 
 2612    /* Execute compose-leave */
 2613    if((cp = ok_vlook(on_compose_leave)) != NULL){
 2614       setup_from_and_sender(hp);
 2615       temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
 2616          hp);
 2617    }
 2618 
 2619    /* Add automatic receivers */
 2620    if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
 2621       hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC |
 2622             (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN)),
 2623             EACM_NORMAL, checkaddr_err));
 2624    if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
 2625       hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC |
 2626             (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN)),
 2627             EACM_NORMAL, checkaddr_err));
 2628    if (*checkaddr_err != 0)
 2629       goto jerr;
 2630 
 2631    if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
 2632          ok_blook(asksend)){
 2633       bool_t b;
 2634 
 2635       ifs_saved = coapm = NULL;
 2636       coap = NULL;
 2637 
 2638       fprintf(n_stdout, _("-------\nEnvelope contains:\n")); /* xxx SEARCH112 */
 2639       puthead(TRU1, hp, n_stdout, GIDENT | GSUBJECT | GTO | GCC | GBCC | GCOMMA,
 2640          SEND_TODISP, CONV_NONE, NULL, NULL);
 2641 
 2642       while((cp = n_go_input_cp(n_GO_INPUT_CTX_COMPOSE | n_GO_INPUT_NL_ESC,
 2643             _("Send this message [yes/no, or recompose]? "), NULL)) == NULL)
 2644          if(n_go_input_is_eof()){
 2645             cp = n_1;
 2646             break;
 2647          }
 2648 
 2649       b = boolify(cp, UIZ_MAX, TRU1);
 2650       if(b < FAL0)
 2651          goto jcont;
 2652       else if(!b)
 2653          goto jerr;
 2654    }
 2655 
 2656    /* TODO Cannot do since it may require turning this into a multipart one */
 2657    if(n_poption & n_PO_Mm_FLAG)
 2658       goto jskiptails;
 2659 
 2660    /* Place signature? */
 2661    if((cp = ok_vlook(signature)) != NULL && *cp != '\0'){ /* TODO OBSOLETE */
 2662       char const *cpq;
 2663 
 2664       n_OBSOLETE(_("please use *on-compose-{leave,splice}* and/or "
 2665          "*message-inject-tail*, not *signature*"));
 2666 
 2667       if((cpq = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) == NULL){
 2668          n_err(_("*signature* expands to invalid file: %s\n"),
 2669             n_shexp_quote_cp(cp, FAL0));
 2670          goto jerr;
 2671       }
 2672       cpq = n_shexp_quote_cp(cp = cpq, FAL0);
 2673 
 2674       if((sigfp = Fopen(cp, "r")) == NULL){
 2675          n_err(_("Can't open *signature* %s: %s\n"),
 2676             cpq, n_err_to_doc(n_err_no));
 2677          goto jerr;
 2678       }
 2679 
 2680       if(linebuf == NULL)
 2681          linebuf = smalloc(linesize = LINESIZE);
 2682       c = '\0';
 2683       while((i = fread(linebuf, sizeof *linebuf, linesize, n_UNVOLATILE(sigfp)))
 2684             > 0){
 2685          c = linebuf[i - 1];
 2686          if(i != fwrite(linebuf, sizeof *linebuf, i, _coll_fp))
 2687             goto jerr;
 2688       }
 2689 
 2690       /* C99 */{
 2691          FILE *x = n_UNVOLATILE(sigfp);
 2692          int e = n_err_no, ise = ferror(x);
 2693 
 2694          sigfp = NULL;
 2695          Fclose(x);
 2696 
 2697          if(ise){
 2698             n_err(_("Errors while reading *signature* %s: %s\n"),
 2699                cpq, n_err_to_doc(e));
 2700             goto jerr;
 2701          }
 2702       }
 2703 
 2704       if(c != '\0' && c != '\n')
 2705          putc('\n', _coll_fp);
 2706    }
 2707 
 2708    {  char const *cp_obsolete = ok_vlook(NAIL_TAIL);
 2709 
 2710       if(cp_obsolete != NULL)
 2711          n_OBSOLETE(_("please use *message-inject-tail*, not *NAIL_TAIL*"));
 2712 
 2713    if((cp = ok_vlook(message_inject_tail)) != NULL ||
 2714          (cp = cp_obsolete) != NULL){
 2715       if(!a_coll_putesc(cp, TRU1, _coll_fp))
 2716          goto jerr;
 2717       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT) &&
 2718             !a_coll_putesc(cp, TRU1, n_stdout))
 2719          goto jerr;
 2720    }
 2721    }
 2722 
 2723 jskiptails:
 2724    if(fflush(_coll_fp))
 2725       goto jerr;
 2726    rewind(_coll_fp);
 2727 
 2728 jleave:
 2729    if (linebuf != NULL)
 2730       free(linebuf);
 2731    sigprocmask(SIG_BLOCK, &nset, NULL);
 2732    n_pstate &= ~n_PS_COMPOSE_MODE;
 2733    safe_signal(SIGINT, _coll_saveint);
 2734    safe_signal(SIGHUP, _coll_savehup);
 2735    sigprocmask(SIG_SETMASK, &oset, NULL);
 2736    NYD_LEAVE;
 2737    return _coll_fp;
 2738 
 2739 jerr:
 2740    hold_all_sigs();
 2741 
 2742    if(coap != NULL && coap != (struct a_coll_ocs_arg*)-1){
 2743       if(!(flags & a_COAP_NOSIGTERM))
 2744          n_psignal(coap->coa_stdout, SIGTERM);
 2745       n_go_splice_hack_remove_after_jump();
 2746       coap = NULL;
 2747    }
 2748    if(ifs_saved != NULL){
 2749       ok_vset(ifs, ifs_saved);
 2750       ifs_saved = NULL;
 2751    }
 2752    if(sigfp != NULL){
 2753       Fclose(n_UNVOLATILE(sigfp));
 2754       sigfp = NULL;
 2755    }
 2756    if(_coll_fp != NULL){
 2757       Fclose(_coll_fp);
 2758       _coll_fp = NULL;
 2759    }
 2760 
 2761    rele_all_sigs();
 2762 
 2763    assert(checkaddr_err != NULL);
 2764    /* TODO We don't save in $DEAD upon error because msg not readily composed?
 2765     * TODO But this no good, it should go ZOMBIE / DRAFT / POSTPONED or what! */
 2766    if(*checkaddr_err != 0){
 2767       if(*checkaddr_err == 111)
 2768          n_err(_("Compose mode splice hook failure\n"));
 2769       else
 2770          n_err(_("Some addressees were classified as \"hard error\"\n"));
 2771    }else if(_coll_hadintr == 0){
 2772       *checkaddr_err = TRU1; /* TODO ugly: "sendout_error" now.. */
 2773       n_err(_("Failed to prepare composed message\n"));
 2774    }
 2775    goto jleave;
 2776 
 2777 #undef a_HARDERR
 2778 }
 2779 
 2780 /* s-it-mode */