"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.22/src/mx/main.c" (24 Feb 2021, 43186 Bytes) of package /linux/misc/s-nail-14.9.22.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 "main.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.21_vs_14.9.22.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Startup and initialization.
    3  *@ This file is also used to materialize externals.
    4  *@ TODO we need a program wide global ctx; furtherly split main();
    5  *@ TODO when arguments are parsed the a_main_ctx instance should be dropped
    6  *
    7  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    8  * SPDX-License-Identifier: ISC
    9  *
   10  * Permission to use, copy, modify, and/or distribute this software for any
   11  * purpose with or without fee is hereby granted, provided that the above
   12  * copyright notice and this permission notice appear in all copies.
   13  *
   14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   21  */
   22 #undef su_FILE
   23 #define su_FILE main
   24 #define mx_SOURCE
   25 #define mx_SOURCE_MASTER
   26 
   27 #include "mx/nail.h"
   28 
   29 #include <pwd.h>
   30 
   31 #include <su/avopt.h>
   32 #include <su/cs.h>
   33 #include <su/mem.h>
   34 
   35 #include "mx/attachments.h"
   36 #include "mx/iconv.h"
   37 #include "mx/mime-type.h"
   38 #include "mx/names.h"
   39 #include "mx/sigs.h"
   40 #include "mx/termcap.h"
   41 #include "mx/termios.h"
   42 #include "mx/tty.h"
   43 #include "mx/ui-str.h"
   44 
   45 /* TODO fake */
   46 #include "su/code-in.h"
   47 
   48 struct a_main_ctx{
   49    uz mc_smopts_size; /* To manage n_smopts_cnt and n_smopts */
   50    char const *mc_A;
   51    struct a_main_aarg *mc_a_head;
   52    struct a_main_aarg *mc_a_curr;
   53    struct mx_attachment *mc_attach;
   54    struct mx_name *mc_bcc;
   55    struct mx_name *mc_cc;
   56    char const *mc_folder;
   57    char const *mc_L;
   58    char const *mc_quote;
   59    char const *mc_subject;
   60    struct mx_name *mc_to;
   61    char const *mc_u;
   62    char const **mc_X;
   63    uz mc_X_size;
   64    uz mc_X_cnt;
   65    char const **mc_Y;
   66    uz mc_Y_size;
   67    uz mc_Y_cnt;
   68 };
   69 
   70 struct a_main_aarg{
   71    struct a_main_aarg *maa_next;
   72    char const *maa_file;
   73 };
   74 
   75 /* (extern, but not with amalgamation, so define here) */
   76 VL char const n_weekday_names[7 + 1][4] = {
   77    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", ""
   78 };
   79 VL char const n_month_names[12 + 1][4] = {
   80    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
   81    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""
   82 };
   83 VL char const n_uagent[sizeof VAL_UAGENT] = VAL_UAGENT;
   84 #ifdef mx_HAVE_UISTRINGS
   85 VL char const n_error[sizeof n_ERROR] = N_(n_ERROR);
   86 #endif
   87 VL char const n_path_devnull[sizeof n_PATH_DEVNULL] = n_PATH_DEVNULL;
   88 VL char const n_0[2] = "0";
   89 VL char const n_1[2] = "1";
   90 VL char const n_m1[3] = "-1";
   91 VL char const n_em[2] = "!";
   92 VL char const n_ns[2] = "#";
   93 VL char const n_star[2] = "*";
   94 VL char const n_hy[2] = "-";
   95 VL char const n_qm[2] = "?";
   96 VL char const n_at[2] = "@";
   97 
   98 /* Perform basic startup initialization */
   99 static void a_main_startup(void);
  100 
  101 /* Grow a char** */
  102 static uz a_main_grow_cpp(char const ***cpp, uz newsize, uz oldcnt);
  103 
  104 /* Setup some variables which we require to be valid / verified */
  105 static void a_main_setup_vars(void);
  106 
  107 /* Ok, we are reading mail.  Decide whether we are editing a mailbox or reading
  108  * the system mailbox, and open up the right stuff */
  109 static int a_main_rcv_mode(struct a_main_ctx *mcp);
  110 
  111 /* Interrupt printing of the headers */
  112 static void a_main_hdrstop(int signo);
  113 
  114 /* SIGUSR1, then */
  115 #if DVLOR(1, 0) && defined mx_HAVE_DEVEL && defined su_MEM_ALLOC_DEBUG
  116 static void a_main_memtrace(int signo);
  117 #endif
  118 
  119 /* The _opt_ series returns an error message or NIL */
  120 static char const *a_main_o_r(struct a_main_ctx *mcp, struct su_avopt *avop);
  121 static char const *a_main_o_S(struct a_main_ctx *mcp, struct su_avopt *avop);
  122 static void a_main_o_s(struct a_main_ctx *mcp, struct su_avopt *avop);
  123 
  124 /* */
  125 static void a_main_usage(FILE *fp);
  126 static boole a_main_dump_doc(up cookie, boole has_arg, char const *sopt,
  127       char const *lopt, char const *doc);
  128 
  129 static void
  130 a_main_startup(void){
  131    struct passwd *pwuid;
  132    char *cp;
  133    NYD2_IN;
  134 
  135    n_stdin = stdin;
  136    n_stdout = stdout;
  137    n_stderr = stderr;
  138 
  139    if((cp = su_cs_rfind_c(su_program, '/')) != NIL)
  140       su_program = ++cp;
  141    /* XXX Due to n_err() mess the su_log config only applies to EMERG yet! */
  142    su_state_set(su_STATE_LOG_SHOW_LEVEL | su_STATE_LOG_SHOW_PID
  143          /* XXX | su_STATE_ERR_NOMEM | su_STATE_ERR_OVERFLOW */
  144    );
  145    su_log_set_level(n_LOG_LEVEL); /* XXX _EMERG is 0.. */
  146 
  147    /* TODO This is wrong: interactive is STDIN/STDERR for a POSIX sh(1).
  148     * TODO For now we get this wrong, all over the place, as this software
  149     * TODO has always been developed with stdout as an output channel.
  150     * TODO Start doing it right for at least explicit terminal-related things,
  151     * TODO but v15 should use ONLY this, also for terminal input! */
  152    if(isatty(STDIN_FILENO)){
  153       n_psonce |= n_PSO_TTYIN;
  154       /* We need a writable terminal descriptor then, anyway */
  155       if((mx_tty_fp = fdopen(fileno(n_stdin), "w+")) != NIL)
  156          setvbuf(mx_tty_fp, NIL, _IOLBF, 0);
  157    }
  158 
  159    if(isatty(STDOUT_FILENO))
  160       n_psonce |= n_PSO_TTYOUT;
  161    /* STDOUT is always line buffered from our point of view */
  162    setvbuf(n_stdout, NIL, _IOLBF, 0);
  163    if(mx_tty_fp == NIL)
  164       mx_tty_fp = n_stdout;
  165 
  166    /* Assume we are interactive, then.
  167     * This state will become unset later for n_PO_QUICKRUN_MASK! */
  168    if((n_psonce & n_PSO_TTYANY) == n_PSO_TTYANY)
  169       n_psonce |= n_PSO_INTERACTIVE;
  170 
  171    if(isatty(STDERR_FILENO))
  172       n_psonce |= n_PSO_TTYERR;
  173 
  174    /* Now that the basic I/O is accessible, initialize our main machinery,
  175     * input, loop, child, termios, whatever */
  176    n_go_init();
  177 
  178    if(n_psonce & n_PSO_INTERACTIVE)
  179       safe_signal(SIGPIPE, SIG_IGN);
  180 
  181 #if DVLOR(1, 0)
  182 # if defined mx_HAVE_DEVEL && defined su_MEM_ALLOC_DEBUG
  183    safe_signal(SIGUSR1, &a_main_memtrace);
  184 # endif
  185    safe_signal(SIGUSR2, &mx__nyd_oncrash);
  186    safe_signal(SIGABRT, &mx__nyd_oncrash);
  187 # ifdef SIGBUS
  188    safe_signal(SIGBUS, &mx__nyd_oncrash);
  189 # endif
  190    safe_signal(SIGFPE, &mx__nyd_oncrash);
  191    safe_signal(SIGILL, &mx__nyd_oncrash);
  192    safe_signal(SIGSEGV, &mx__nyd_oncrash);
  193 #endif
  194 
  195    /*  --  >8  --  8<  --  */
  196 
  197    n_locale_init();
  198 
  199 #ifdef mx_HAVE_ICONV
  200    iconvd = R(iconv_t,-1);
  201 #endif
  202 
  203    /*
  204     * Ensure some variables get loaded and/or verified, I. (pre-getopt)
  205     */
  206 
  207    /* Detect, verify and fixate our invoking user (environment) */
  208    n_group_id = getgid();
  209    if((pwuid = getpwuid(n_user_id = getuid())) == NIL)
  210       n_panic(_("Cannot associate a name with uid %lu"), S(ul,n_user_id));
  211    else{
  212       char const *ep;
  213       boole doenv;
  214 
  215       if(!(doenv = (ep = ok_vlook(LOGNAME)) == NIL) &&
  216             (doenv = (su_cs_cmp(pwuid->pw_name, ep) != 0)))
  217          n_err(_("Warning: $LOGNAME (%s) not identical to user (%s)!\n"),
  218             ep, pwuid->pw_name);
  219       if(doenv){
  220          n_pstate |= n_PS_ROOT;
  221          ok_vset(LOGNAME, pwuid->pw_name);
  222          n_pstate &= ~n_PS_ROOT;
  223       }
  224 
  225       /* BSD compat */
  226       if((ep = ok_vlook(USER)) != NIL && su_cs_cmp(pwuid->pw_name, ep)){
  227          n_err(_("Warning: $USER (%s) not identical to user (%s)!\n"),
  228             ep, pwuid->pw_name);
  229          n_pstate |= n_PS_ROOT;
  230          ok_vset(USER, pwuid->pw_name);
  231          n_pstate &= ~n_PS_ROOT;
  232       }
  233 
  234       /* XXX myfullname = pw->pw_gecos[OPTIONAL!] -> GUT THAT; TODO pw_shell */
  235    }
  236 
  237    /* This is not automated just as $TMPDIR is for the initial setting, since
  238     * we have the pwuid at hand and can simply use it!  See accmacvar.c! */
  239    if(n_user_id == 0 || (cp = ok_vlook(HOME)) == NIL){
  240       cp = pwuid->pw_dir;
  241       n_pstate |= n_PS_ROOT;
  242       ok_vset(HOME, cp);
  243       n_pstate &= ~n_PS_ROOT;
  244    }
  245 
  246    /* XXX Perform lookup of environmental VIP variables which have a mapping
  247     * XXX This should be local to the variable handling stuff or so! */
  248    (void)ok_blook(POSIXLY_CORRECT);
  249 #ifdef mx_HAVE_NET
  250    (void)ok_vlook(SOCKS5_PROXY);
  251 #endif
  252 
  253    NYD2_OU;
  254 }
  255 
  256 static uz
  257 a_main_grow_cpp(char const ***cpp, uz newsize, uz oldcnt){
  258    /* Just use auto-reclaimed storage, it will be preserved */
  259    char const **newcpp;
  260    NYD2_IN;
  261 
  262    newcpp = n_autorec_alloc(sizeof(char*) * (newsize + 1));
  263 
  264    if(oldcnt > 0)
  265       su_mem_copy(newcpp, *cpp, oldcnt * sizeof(char*));
  266    *cpp = newcpp;
  267 
  268    NYD2_OU;
  269    return newsize;
  270 }
  271 
  272 static void
  273 a_main_setup_vars(void){
  274    char const *cp;
  275    NYD2_IN;
  276 
  277    /*
  278     * Ensure some variables get loaded and/or verified, II. (post getopt).
  279     */
  280 
  281    /* Do not honour TMPDIR if root */
  282    if(n_user_id == 0)
  283       ok_vset(TMPDIR, NIL);
  284    else
  285       (void)ok_vlook(TMPDIR);
  286 
  287    /* Are we in a reproducible-builds.org environment?
  288     * That special mode bends some settings (again) */
  289    if(ok_vlook(SOURCE_DATE_EPOCH) != NIL){
  290       su_state_set(su_STATE_REPRODUCIBLE);
  291       su_program = su_reproducible_build;
  292       n_pstate |= n_PS_ROOT;
  293       ok_vset(LOGNAME, su_reproducible_build);
  294       /* Do not care about USER at all in this special mode! */
  295       n_pstate &= ~n_PS_ROOT;
  296       cp = savecat(su_reproducible_build, ": ");
  297       ok_vset(log_prefix, cp);
  298    }
  299 
  300    /* Finally set our terminal dimension */
  301    mx_termios_controller_setup(mx_TERMIOS_SETUP_TERMSIZE);
  302 
  303    NYD2_OU;
  304 }
  305 
  306 static sigjmp_buf a_main__hdrjmp; /* XXX */
  307 
  308 static int
  309 a_main_rcv_mode(struct a_main_ctx *mcp){
  310    n_sighdl_t prevint;
  311    int i;
  312    NYD_IN;
  313 
  314    i = (mcp->mc_A != NIL) ? FEDIT_ACCOUNT : FEDIT_NONE;
  315    if(n_poption & n_PO_QUICKRUN_MASK)
  316       i |= FEDIT_RDONLY;
  317 
  318    if(mcp->mc_folder == NIL){
  319       mcp->mc_folder = "%";
  320       if(i & FEDIT_ACCOUNT)
  321          i |= FEDIT_SYSBOX;
  322    }
  323 #ifdef mx_HAVE_IMAP
  324    else if(*mcp->mc_folder == '@'){
  325       /* This must be treated specially to make possible invocation like
  326        * -A imap -f @mailbox */
  327       char const *cp;
  328 
  329       cp = n_folder_query();
  330       if(which_protocol(cp, FAL0, FAL0, NIL) == PROTO_IMAP)
  331          su_cs_pcopy_n(mailname, cp, sizeof mailname);
  332    }
  333 #endif
  334 
  335    i = setfile(mcp->mc_folder, i);
  336    if(i < 0){
  337       n_exit_status = n_EXIT_ERR; /* error already reported */
  338       goto jquit;
  339    }
  340    temporary_folder_hook_check(FAL0);
  341    if(n_poption & n_PO_QUICKRUN_MASK){
  342       n_exit_status = i;
  343       if(i == n_EXIT_OK && (!(n_poption & n_PO_EXISTONLY) ||
  344             (n_poption & n_PO_HEADERLIST)))
  345          print_header_summary(mcp->mc_L);
  346       goto jquit;
  347    }
  348 
  349    if(i > 0 && !ok_blook(emptystart)){
  350       n_exit_status = n_EXIT_ERR;
  351       goto jleave;
  352    }
  353 
  354    if(sigsetjmp(a_main__hdrjmp, 1) == 0){
  355       if((prevint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
  356          safe_signal(SIGINT, &a_main_hdrstop);
  357       if(!ok_blook(quiet))
  358          fprintf(n_stdout, _("%s version %s.  Type `?' for help\n"),
  359             n_uagent,
  360             (su_state_has(su_STATE_REPRODUCIBLE)
  361                ? su_reproducible_build : ok_vlook(version)));
  362       n_folder_announce(n_ANNOUNCE_MAIN_CALL | n_ANNOUNCE_CHANGE);
  363       safe_signal(SIGINT, prevint);
  364    }
  365 
  366    /* Enter the command loop */
  367    /* "load()" more commands given on command line */
  368    if(mcp->mc_Y_cnt > 0 && !n_go_load_lines(TRU1, mcp->mc_Y, mcp->mc_Y_cnt))
  369       n_exit_status = n_EXIT_ERR;
  370    else
  371       n_go_main_loop();
  372 
  373    if(!(n_psonce & n_PSO_XIT)){
  374       if(mb.mb_type == MB_FILE || mb.mb_type == MB_MAILDIR){
  375          safe_signal(SIGHUP, SIG_IGN);
  376          safe_signal(SIGINT, SIG_IGN);
  377          safe_signal(SIGQUIT, SIG_IGN);
  378       }
  379 jquit:
  380       save_mbox_for_possible_quitstuff();
  381       quit(FAL0);
  382    }
  383 
  384 jleave:
  385    NYD_OU;
  386    return n_exit_status;
  387 }
  388 
  389 static void
  390 a_main_hdrstop(int signo){
  391    NYD; /* Signal handler */
  392    UNUSED(signo);
  393 
  394    fflush(n_stdout);
  395    n_err_sighdl(_("\nInterrupt\n"));
  396    siglongjmp(a_main__hdrjmp, 1);
  397 }
  398 
  399 #if DVLOR(1, 0) && defined mx_HAVE_DEVEL && defined su_MEM_ALLOC_DEBUG
  400 static void
  401 a_main_memtrace(int signo){
  402    enum su_log_level lvl;
  403    UNUSED(signo);
  404 
  405    lvl = su_log_get_level();
  406    su_log_set_level(su_LOG_INFO);
  407    su_mem_trace();
  408    su_log_set_level(lvl);
  409 }
  410 #endif
  411 
  412 static char const *
  413 a_main_o_r(struct a_main_ctx *mcp, struct su_avopt *avop){
  414    struct mx_name *fap;
  415    char const *rv;
  416    NYD2_IN;
  417 
  418    n_poption |= n_PO_r_FLAG;
  419 
  420    rv = NIL;
  421 
  422    if(avop->avo_current_arg[0] == '\0')
  423       goto jleave;
  424 
  425    fap = nalloc(avop->avo_current_arg, GSKIN | GFULL | GFULLEXTRA |
  426          GNOT_A_LIST | GNULL_OK | GSHEXP_PARSE_HACK);
  427    if(fap == NIL || is_addr_invalid(fap, EACM_STRICT | EACM_NOLOG)){
  428       rv = N_("Invalid address argument with -r");
  429       goto jleave;
  430    }
  431 
  432    n_poption_arg_r = fap;
  433 
  434    /* TODO -r options is set in n_smopts, but may
  435     * TODO be overwritten by setting from= in
  436     * TODO an interactive session!
  437     * TODO Maybe disable setting of from?
  438     * TODO Warn user?  Update manual!! */
  439    avop->avo_current_arg = savecat("from=", fap->n_fullname);
  440    rv = a_main_o_S(mcp, avop);
  441 
  442 jleave:
  443    NYD2_OU;
  444    return rv;
  445 }
  446 
  447 static char const *
  448 a_main_o_S(struct a_main_ctx *mcp, struct su_avopt *avop){
  449    struct str sin;
  450    struct n_string s_b, *s;
  451    boole b;
  452    char const *rv, *a[2];
  453    NYD2_IN;
  454    UNUSED(mcp);
  455 
  456    rv = NIL;
  457 
  458    /* May be called from _opt_r(), for example */
  459    if(avop->avo_current_opt != 'S' || ok_vlook(v15_compat) == NIL){
  460       a[0] = avop->avo_current_arg;
  461       s = NIL;
  462    }else{
  463       BITENUM_IS(u32,n_shexp_state) shs;
  464 
  465       n_autorec_relax_create();
  466       s = n_string_creat_auto(&s_b);
  467       sin.s = UNCONST(char*,avop->avo_current_arg);
  468       sin.l = UZ_MAX;
  469       shs = n_shexp_parse_token((n_SHEXP_PARSE_LOG |
  470             n_SHEXP_PARSE_IGNORE_EMPTY |
  471             n_SHEXP_PARSE_QUOTE_AUTO_FIXED |
  472             n_SHEXP_PARSE_QUOTE_AUTO_DSQ), s, &sin, NIL);
  473       if((shs & n_SHEXP_STATE_ERR_MASK) ||
  474             !(shs & n_SHEXP_STATE_STOP)){
  475          n_autorec_relax_gut();
  476          goto je_S;
  477       }
  478       a[0] = n_string_cp_const(s);
  479    }
  480 
  481    a[1] = NIL;
  482    n_poption |= n_PO_S_FLAG_TEMPORARY;
  483    n_pstate |= n_PS_ROBOT;
  484    b = (c_set(a) == n_EXIT_OK);
  485    n_pstate &= ~n_PS_ROBOT;
  486    n_poption &= ~n_PO_S_FLAG_TEMPORARY;
  487 
  488    if(s != NIL)
  489       n_autorec_relax_gut();
  490    if(!b && (ok_blook(errexit) || ok_blook(posix))){
  491 je_S:
  492       rv = N_("-S failed to set variable");
  493    }
  494 
  495    NYD2_OU;
  496    return rv;
  497 }
  498 
  499 static void
  500 a_main_o_s(struct a_main_ctx *mcp, struct su_avopt *avop){
  501    /* Take care for Debian #419840 and strip any \r and \n */
  502    uz i;
  503    char *cp;
  504    NYD2_IN;
  505 
  506    n_psonce |= n_PSO_SENDMODE;
  507 
  508    mcp->mc_subject = cp = UNCONST(char*,avop->avo_current_arg);
  509 
  510    if((i = su_cs_first_of(cp, "\n\r")) != UZ_MAX){
  511       n_err(_("-s: normalizing away invalid ASCII NL / CR bytes\n"));
  512 
  513       mcp->mc_subject = cp = savestr(cp);
  514 
  515       for(cp = &cp[i]; *cp != '\0'; ++cp)
  516          if(*cp == '\n' || *cp == '\r')
  517             *cp = ' ';
  518    }
  519 
  520    NYD2_OU;
  521 }
  522 
  523 static char const *
  524 a_main_o_T(struct a_main_ctx *mcp, struct su_avopt *avop){
  525    struct str suffix;
  526    struct mx_name **npp, *np;
  527    BITENUM_IS(u32,gfield) gf;
  528    char const *rv, *a;
  529    NYD2_IN;
  530 
  531    n_psonce |= n_PSO_SENDMODE;
  532 
  533    rv = NIL;
  534 
  535    if((a = n_header_get_field(avop->avo_current_arg, "to", &suffix)) != NIL){
  536       gf = GTO | GSHEXP_PARSE_HACK | GFULL | GNULL_OK;
  537       npp = &mcp->mc_to;
  538    }else if((a = n_header_get_field(avop->avo_current_arg, "cc", &suffix)
  539          ) != NIL){
  540       gf = GCC | GSHEXP_PARSE_HACK | GFULL | GNULL_OK;
  541       npp = &mcp->mc_cc;
  542    }else if((a = n_header_get_field(avop->avo_current_arg, "bcc", &suffix)
  543          ) != NIL){
  544       gf = GBCC | GSHEXP_PARSE_HACK | GFULL | GNULL_OK;
  545       npp = &mcp->mc_bcc;
  546    }else if((a = n_header_get_field(avop->avo_current_arg, "fcc", su_NIL)
  547          ) != NIL){
  548       gf = GBCC_IS_FCC;
  549       npp = &mcp->mc_bcc;
  550    }else{
  551       ASSERT(suffix.s == NIL);
  552 jeTuse:
  553       rv = N_("-T: only supports to,cc,bcc (with ?single modifier) and fcc");
  554       goto jleave;
  555    }
  556 
  557    if(suffix.s != NIL){
  558       if(suffix.l > 0 &&
  559             !su_cs_starts_with_case_n("single", suffix.s, suffix.l))
  560          goto jeTuse;
  561       gf |= GNOT_A_LIST;
  562    }
  563 
  564    if(!(gf & GBCC_IS_FCC))
  565       np = lextract(a, gf);
  566    else
  567       np = nalloc_fcc(a);
  568    if(np == NIL){
  569       rv = N_("-T: invalid receiver (address)");
  570       goto jleave;
  571    }
  572 
  573    *npp = cat(*npp, np);
  574 
  575 jleave:
  576    NYD2_OU;
  577    return rv;
  578 }
  579 
  580 static void
  581 a_main_usage(FILE *fp){
  582    /* Stay in VAL_HEIGHT lines; On buf length change: verify visual output! */
  583    char buf[7];
  584    uz i;
  585    NYD2_IN;
  586 
  587    i = su_cs_len(su_program);
  588    i = MIN(i, sizeof(buf) -1);
  589    if(i > 0)
  590       su_mem_set(buf, ' ', i);
  591    buf[i] = '\0';
  592 
  593    fprintf(fp, _("%s (%s %s): send and receive Internet mail\n"),
  594       su_program, n_uagent, ok_vlook(version));
  595    if(fp != n_stderr)
  596       putc('\n', fp);
  597 
  598    fprintf(fp, _(
  599       "Send-only mode: send mail \"to-addr\"(ess) receiver(s):\n"
  600       "  %s [-DdEFinv~#] [-: spec] [-A account] [:-C \"field: body\":]\n"
  601       "  %s [:-a attachment:] [:-b bcc-addr:] [:-c cc-addr:]\n"
  602       "  %s [-M type | -m file | -q file | -t] [-r from-addr] "
  603          "[:-S var[=value]:]\n"
  604       "  %s [-s subject] [-T \"arget: addr\"] [:-X/Y cmd:] [-.] :to-addr:\n"),
  605       su_program, buf, buf, buf);
  606    if(fp != n_stderr)
  607       putc('\n', fp);
  608 
  609    fprintf(fp, _(
  610       "\"Receive\" mode, starting on [-u user], primary *inbox* or [$MAIL]:\n"
  611       "  %s [-DdEeHiNnRv~#] [-: spec] [-A account] [:-C \"field: body\":]\n"
  612       "  %s [-L spec] [-r from-addr] [:-S var[=value]:] [-u user] "
  613          "[:-X/Y cmd:]\n"),
  614       su_program, buf);
  615    if(fp != n_stderr)
  616       putc('\n', fp);
  617 
  618    fprintf(fp, _(
  619       "\"Receive\" mode, starting on -f (secondary $MBOX or [file]):\n"
  620       "  %s [-DdEeHiNnRv~#] [-: spec] [-A account] [:-C \"field: body\":] -f\n"
  621       "  %s [-L spec] [-r from-addr] [:-S var[=value]:] [:-X/Y cmd:] "
  622          "[file]\n"),
  623       su_program, buf);
  624    if(fp != n_stderr)
  625       putc('\n', fp);
  626 
  627    /* (ISO C89 string length) */
  628    fprintf(fp, _(
  629          ". -d sandbox, -:/ no .rc files, -. end options and force send-mode\n"
  630          ". -a attachment[=input-charset[#output-charset]]\n"
  631          ". -b, -c, -r, -T, to-addr: ex@am.ple or '(Lovely) Ex <am@p.le>'\n"
  632          ". -M, -m, -q, -t: special input (-t: template message on stdin)\n"
  633          ". -e only mail check, -H header summary; "
  634             "both: message specification via -L\n"
  635          ". -S (un)sets variable, -X/-Y execute commands pre/post startup, "
  636             "-#: batch mode\n"));
  637    fprintf(fp, _(
  638          ". Features via \"$ %s -Xversion -Xx\"; there is --long-help\n"
  639          ". Bugs/Contact via "
  640             "\"$ %s -Sexpandaddr=shquote '\\$contact-mail'\"\n"),
  641          su_program, su_program);
  642    NYD2_OU;
  643 }
  644 
  645 static boole
  646 a_main_dump_doc(up cookie, boole has_arg, char const *sopt, char const *lopt,
  647       char const *doc){
  648    char const *x1, *x2;
  649    NYD2_IN;
  650    UNUSED(doc);
  651 
  652    if(has_arg)
  653       /* I18N: describing arguments to command line options */
  654       x1 = (sopt[0] != '\0' ? _(" ARG, ") : sopt), x2 = _("=ARG");
  655    else
  656       /* I18N: separating command line options */
  657       x1 = (sopt[0] != '\0' ? _(", ") : sopt), x2 = su_empty;
  658    /* I18N: short option, "[ ARG], " separator, long option [=ARG], doc */
  659    fprintf(S(FILE*,cookie), _("%s%s%s%s: %s\n"), sopt, x1, lopt, x2, V_(doc));
  660 
  661    NYD2_OU;
  662    return TRU1;
  663 }
  664 
  665 int
  666 main(int argc, char *argv[]){
  667    /* TODO Once v15 control flow/carrier rewrite took place main() should
  668     * TODO be rewritten and option parsing++ should be outsourced.
  669     * TODO Like so we can get rid of some stack locals etc.
  670     * TODO Furthermore: the locals should be in a carrier, and once there
  671     * TODO is the memory pool+page cache, that should go in LOFI memory,
  672     * TODO and there should be two pools: one which is fixated() and remains,
  673     * TODO and one with throw away data (-X, -Y args, temporary allocs, e.g.,
  674     * TODO redo -S like so, etc.) */
  675    enum a_rf_ids{
  676       a_RF_NONE,
  677       a_RF_SET = 1u<<0,
  678       a_RF_SYSTEM = 1u<<1,
  679       a_RF_USER = 1u<<2,
  680       a_RF_BLTIN = 1u<<3,
  681       a_RF_DEFAULT = a_RF_SYSTEM | a_RF_USER,
  682       a_RF_MASK = a_RF_SYSTEM | a_RF_USER | a_RF_BLTIN
  683    };
  684 
  685    /* Keep in SYNC: ./nail.1:"SYNOPSIS, main() */
  686    static char const a_sopts[] =
  687          "::A:a:Bb:C:c:DdEeFfHhiL:M:m:NnO:q:Rr:S:s:T:tu:VvX:Y:~#.";
  688    static char const * const a_lopts[] = {
  689       "resource-files:;:;" N_("control loading of resource files"),
  690       "account:;A;" N_("execute an `account' command"),
  691          "attach:;a;" N_("attach a file to message to be sent"),
  692       "bcc:;b;" N_("add blind carbon copy recipient"),
  693       "custom-header:;C;" N_("create custom header (\"header-field: body\")"),
  694          "cc:;c;" N_("add carbon copy recipient"),
  695       "disconnected;D;" N_("identical to -Sdisconnected"),
  696          "debug;d;" N_("identical to -Sdebug"),
  697       "discard-empty-messages;E;" N_("identical to -Sskipemptybody"),
  698          "check-and-exit;e;" N_("note mail presence (of -L) via exit status"),
  699       "file;f;" N_("open secondary mailbox, or \"file\" last on command line"),
  700       "header-summary;H;" N_("is to be displayed (for given file) only"),
  701          "help;h;" N_("short help"),
  702       "search:;L;" N_("like -H (or -e) for the given \"spec\" only"),
  703       "no-header-summary;N;" N_("identical to -Snoheader"),
  704       "quote-file:;q;" N_("initialize body of message to be sent with a file"),
  705       "read-only;R;" N_("any mailbox file will be opened read-only"),
  706          "from-address:;r;" N_("set source address used by MTAs (and -Sfrom)"),
  707       "set:;S;" N_("set one of the INTERNAL VARIABLES (unset via \"noARG\")"),
  708          "subject:;s;" N_("specify subject of message to be sent"),
  709       "target:;T;" N_("add receiver(s) \"header-field: address\" as via -t"),
  710       "template;t;" N_("message to be sent is read from standard input"),
  711       "inbox-of:;u;" N_("initially open primary mailbox of the given user"),
  712       "version;V;" N_("print version (more so with \"[-v] -Xversion -Xx\")"),
  713          "verbose;v;" N_("equals -Sverbose (multiply for more verbosity)"),
  714       "startup-cmd:;X;" N_("to be executed before normal operation"),
  715       "cmd:;Y;" N_("to be executed under normal operation (is \"input\")"),
  716       "enable-cmd-escapes;~;" N_("even in non-interactive compose mode"),
  717       "batch-mode;#;" N_("more confined non-interactive setup"),
  718       "end-options;.;" N_("force the end of options, and (enter) send mode"),
  719       "long-help;\201;" N_("this listing"),
  720       NIL
  721    };
  722 
  723    struct a_main_ctx mc;
  724    struct su_avopt avo;
  725    int i;
  726    char const *emsg;
  727    char *cp;
  728    BITENUM_IS(u32,a_rf_ids) resfiles;
  729    NYD_IN;
  730 
  731    su_mem_set(&mc, 0, sizeof mc);
  732    resfiles = a_RF_DEFAULT;
  733    UNINIT(emsg, NIL);
  734 
  735    /*
  736     * Start our lengthy setup, finalize by setting n_PSO_STARTED
  737     */
  738 
  739    su_program = argv[0];
  740    a_main_startup();
  741 
  742    /* Command line parsing.
  743     * XXX We could parse silently to grasp the actual mode (send, receive
  744     * XXX with/out -f, then use an according option array.  This would ease
  745     * XXX the interdependency checking necessities! */
  746    su_avopt_setup(&avo, --argc, C(char const*const*,++argv), a_sopts, a_lopts);
  747    while((i = su_avopt_parse(&avo)) != su_AVOPT_STATE_DONE){
  748       switch(i){
  749       case 'A':
  750          /* Execute an account command later on */
  751          mc.mc_A = avo.avo_current_arg;
  752          break;
  753       case 'a':{
  754          /* Add an attachment */
  755          struct a_main_aarg *nap;
  756 
  757          n_psonce |= n_PSO_SENDMODE;
  758          nap = n_autorec_alloc(sizeof(struct a_main_aarg));
  759          if(mc.mc_a_head == NIL)
  760             mc.mc_a_head = nap;
  761          else
  762             mc.mc_a_curr->maa_next = nap;
  763          nap->maa_next = NIL;
  764          nap->maa_file = avo.avo_current_arg;
  765          mc.mc_a_curr = nap;
  766          }break;
  767       case 'B':
  768          n_OBSOLETE(_("-B is obsolete, please use -# as necessary"));
  769          break;
  770       case 'b':
  771          /* Add (a) blind carbon copy recipient (list) */
  772          n_psonce |= n_PSO_SENDMODE;
  773          mc.mc_bcc = cat(mc.mc_bcc, lextract(avo.avo_current_arg,
  774                GBCC | GFULL | GNOT_A_LIST | GSHEXP_PARSE_HACK));
  775          break;
  776       case 'C':{
  777          /* Create custom header (at list tail) */
  778          struct n_header_field **hflpp;
  779 
  780          if(*(hflpp = &n_poption_arg_C) != NIL){
  781             while((*hflpp)->hf_next != NIL)
  782                hflpp = &(*hflpp)->hf_next;
  783             hflpp = &(*hflpp)->hf_next;
  784          }
  785          if(!n_header_add_custom(hflpp, avo.avo_current_arg, FAL0)){
  786             emsg = N_("Invalid custom header data with -C");
  787             goto jusage;
  788          }
  789          }break;
  790       case 'c':
  791          /* Add (a) carbon copy recipient (list) */
  792          n_psonce |= n_PSO_SENDMODE;
  793          mc.mc_cc = cat(mc.mc_cc, lextract(avo.avo_current_arg,
  794                GCC | GFULL | GNOT_A_LIST | GSHEXP_PARSE_HACK));
  795          break;
  796       case 'D':
  797 #ifdef mx_HAVE_IMAP
  798          ok_bset(disconnected);
  799 #endif
  800          break;
  801       case 'd':
  802          ok_bset(debug);
  803          break;
  804       case 'E':
  805          ok_bset(skipemptybody);
  806          break;
  807       case 'e':
  808          /* Check if mail (matching -L) exists in given box, exit status */
  809          n_poption |= n_PO_EXISTONLY;
  810          n_psonce &= ~n_PSO_INTERACTIVE;
  811          break;
  812       case 'F':
  813          /* Save msg in file named after local part of first recipient */
  814          n_poption |= n_PO_F_FLAG;
  815          n_psonce |= n_PSO_SENDMODE;
  816          break;
  817       case 'f':
  818          /* User is specifying file to "edit" with Mail, as opposed to reading
  819           * system mailbox.  If no argument is given, we read his mbox file.
  820           * Check for remaining arguments later */
  821          n_poption |= n_PO_f_FLAG;
  822          mc.mc_folder = "&";
  823          break;
  824       case 'H':
  825          /* Display summary of headers, exit */
  826          n_poption |= n_PO_HEADERSONLY;
  827          n_psonce &= ~n_PSO_INTERACTIVE;
  828          break;
  829       case 'h':
  830       case S(char,S(u8,'\201')):
  831          a_main_usage(n_stdout);
  832          if(i != 'h'){
  833             fprintf(n_stdout, "\nLong options:\n");
  834             (void)su_avopt_dump_doc(&avo, &a_main_dump_doc, S(up,n_stdout));
  835          }
  836          goto jleave;
  837       case 'i':
  838          /* Ignore interrupts */
  839          ok_bset(ignore);
  840          break;
  841       case 'L':
  842          /* Display summary of headers which match given spec, exit.
  843           * In conjunction with -e, only test the given spec for existence */
  844          n_poption |= n_PO_HEADERLIST;
  845          n_psonce &= ~n_PSO_INTERACTIVE;
  846          mc.mc_L = avo.avo_current_arg;
  847          /* TODO list.c:listspec_check() */
  848          if(*mc.mc_L == '"' || *mc.mc_L == '\''){
  849             uz j;
  850 
  851             j = su_cs_len(++mc.mc_L);
  852             if(j > 0){
  853                cp = savestrbuf(mc.mc_L, --j);
  854                mc.mc_L = cp;
  855             }
  856          }
  857          break;
  858       case 'M':
  859          /* Flag message body (standard input) with given MIME type */
  860          if(mc.mc_quote != NIL && (!(n_poption & n_PO_Mm_FLAG) ||
  861                mc.mc_quote != R(char*,-1)))
  862             goto jeMmq;
  863          n_poption_arg_Mm = avo.avo_current_arg;
  864          mc.mc_quote = R(char*,-1);
  865          if(0){
  866             /* FALLTHRU*/
  867       case 'm':
  868             /* Flag the given file with MIME type and use as message body */
  869             if(mc.mc_quote != NIL && (!(n_poption & n_PO_Mm_FLAG) ||
  870                   mc.mc_quote == R(char*,-1)))
  871                goto jeMmq;
  872             mc.mc_quote = avo.avo_current_arg;
  873          }
  874          n_poption |= n_PO_Mm_FLAG;
  875          n_psonce |= n_PSO_SENDMODE;
  876          break;
  877       case 'N':
  878          /* Avoid initial header printing */
  879          ok_bclear(header);
  880          break;
  881       case 'n':
  882          /* Don't source "unspecified system start-up file" */
  883          if(resfiles & a_RF_SET){
  884             emsg = N_("-n cannot be used in conjunction with -:");
  885             goto jusage;
  886          }
  887          resfiles = a_RF_USER;
  888          break;
  889       case 'O':
  890          /* Additional options to pass-through to MTA TODO v15-compat legacy */
  891          if(n_smopts_cnt == mc.mc_smopts_size)
  892             mc.mc_smopts_size = a_main_grow_cpp(&n_smopts,
  893                   mc.mc_smopts_size + 8, n_smopts_cnt);
  894          n_smopts[n_smopts_cnt++] = avo.avo_current_arg;
  895          break;
  896       case 'q':
  897          /* "Quote" file: use as message body (-t without headers etc.) */
  898          /* XXX Traditional.  Add -Q to initialize as *quote*d content? */
  899          if(mc.mc_quote != NIL && (n_poption & n_PO_Mm_FLAG)){
  900 jeMmq:
  901             emsg = N_("Only one of -M, -m or -q may be given");
  902             goto jusage;
  903          }
  904          n_psonce |= n_PSO_SENDMODE;
  905          /* Allow, we have to special check validity of -q- later on! */
  906          mc.mc_quote = ((avo.avo_current_arg[0] == '-' &&
  907                   avo.avo_current_arg[1] == '\0') ? R(char*,-1)
  908                : avo.avo_current_arg);
  909          break;
  910       case 'R':
  911          /* Open folders read-only */
  912          n_poption |= n_PO_R_FLAG;
  913          break;
  914       case 'r':
  915          /* Set From address. */
  916          if((emsg = a_main_o_r(&mc, &avo)) != NIL)
  917             goto jusage;
  918          break;
  919       case 'S':
  920          /* Set variable */
  921          if((emsg = a_main_o_S(&mc, &avo)) != NIL)
  922             goto jusage;
  923          break;
  924       case 's':
  925          /* Subject: */
  926          a_main_o_s(&mc, &avo);
  927          break;
  928       case 'T':
  929          /* Target mode: `digmsg header insert' from command line */
  930          if((emsg = a_main_o_T(&mc, &avo)) != NIL)
  931             goto jusage;
  932          break;
  933       case 't':
  934          /* Use the given message as send template */
  935          n_poption |= n_PO_t_FLAG;
  936          n_psonce |= n_PSO_SENDMODE;
  937          break;
  938       case 'u':
  939          /* Open primary mailbox of the given user */
  940          mc.mc_u = savecat("%", avo.avo_current_arg);
  941          break;
  942       case 'V':{
  943          struct n_string s;
  944 
  945          fputs(n_string_cp_const(n_version(
  946             n_string_book(n_string_creat_auto(&s), 120))), n_stdout);
  947          n_exit_status = n_EXIT_OK;
  948          }goto jleave;
  949       case 'v':
  950          /* Be verbose */
  951          ok_vset(verbose, su_empty);
  952          break;
  953       case 'X':
  954          /* Add to list of commands to exec before entering normal operation */
  955          if(mc.mc_X_cnt == mc.mc_X_size)
  956             mc.mc_X_size = a_main_grow_cpp(&mc.mc_X, mc.mc_X_size + 8,
  957                   mc.mc_X_cnt);
  958          mc.mc_X[mc.mc_X_cnt++] = avo.avo_current_arg;
  959          break;
  960       case 'Y':
  961          /* Add to list of commands to exec after entering normal operation */
  962          if(mc.mc_Y_cnt == mc.mc_Y_size)
  963             mc.mc_Y_size = a_main_grow_cpp(&mc.mc_Y, mc.mc_Y_size + 8,
  964                   mc.mc_Y_cnt);
  965          mc.mc_Y[mc.mc_Y_cnt++] = avo.avo_current_arg;
  966          break;
  967       case ':':
  968          /* Control which resource files shall be loaded */
  969          if(!(resfiles & (a_RF_SET | a_RF_SYSTEM))){
  970             emsg = N_("-n cannot be used in conjunction with -:");
  971             goto jusage;
  972          }
  973          resfiles = a_RF_SET;
  974          while((i = *avo.avo_current_arg++) != '\0')
  975             switch(i){
  976             case 'S': case 's': resfiles |= a_RF_SYSTEM; break;
  977             case 'U': case 'u': resfiles |= a_RF_USER; break;
  978             case 'X': case 'x': resfiles |= a_RF_BLTIN; break;
  979             case '-': case '/': resfiles &= ~a_RF_MASK; break;
  980             default:
  981                emsg = N_("Invalid argument of -:");
  982                goto jusage;
  983             }
  984          break;
  985       case '~':
  986          /* Enable command escapes even in non-interactive mode */
  987          n_poption |= n_PO_TILDE_FLAG;
  988          break;
  989       case '#':
  990          /* Work in batch mode, even if non-interactive */
  991          if(!(n_psonce & n_PSO_INTERACTIVE))
  992             setvbuf(n_stdin, NIL, _IOLBF, 0);
  993          n_poption |= n_PO_TILDE_FLAG | n_PO_BATCH_FLAG;
  994          mc.mc_folder = n_path_devnull;
  995          n_var_setup_batch_mode();
  996          break;
  997       case '.':
  998          /* Enforce send mode */
  999          n_psonce |= n_PSO_SENDMODE;
 1000          goto jgetopt_done;
 1001       case su_AVOPT_STATE_ERR_ARG:
 1002          emsg = su_avopt_fmt_err_arg;
 1003          if(0){
 1004             /* FALLTHRU */
 1005       case su_AVOPT_STATE_ERR_OPT:
 1006             emsg = su_avopt_fmt_err_opt;
 1007          }
 1008          n_err(emsg, avo.avo_current_err_opt);
 1009          if(0){
 1010 jusage:
 1011             if(emsg != NIL)
 1012                n_err("%s\n", V_(emsg));
 1013          }
 1014          a_main_usage(n_stderr);
 1015          n_exit_status = n_EXIT_USE;
 1016          goto jleave;
 1017       }
 1018    }
 1019 jgetopt_done:
 1020    ;
 1021 
 1022    /* The normal arguments may be followed by MTA arguments after a "--";
 1023     * however, -f may take off an argument, too, and before that.
 1024     * Since MTA arguments after "--" require *expandargv*, delay parsing off
 1025     * those options until after the resource files are loaded... */
 1026    argc = avo.avo_argc;
 1027    argv = C(char**,avo.avo_argv);
 1028    if((cp = argv[i = 0]) == NIL)
 1029       ;
 1030    else if(cp[0] == '-' && cp[1] == '-' && cp[2] == '\0')
 1031       ++i;
 1032    /* n_PO_BATCH_FLAG sets to /dev/null, but -f can still be used and sets & */
 1033    else if(n_poption & n_PO_f_FLAG){
 1034       mc.mc_folder = cp;
 1035       if((cp = argv[++i]) != NIL){
 1036          if(cp[0] != '-' || cp[1] != '-' || cp[2] != '\0'){
 1037             emsg = N_("More than one file given with -f");
 1038             goto jusage;
 1039          }
 1040          ++i;
 1041       }
 1042    }else{
 1043       n_psonce |= n_PSO_SENDMODE;
 1044       for(;;){
 1045          mc.mc_to = cat(mc.mc_to, lextract(cp, GTO | GFULL | GNOT_A_LIST |
 1046                GSHEXP_PARSE_HACK));
 1047          if((cp = argv[++i]) == NIL)
 1048             break;
 1049          if(cp[0] == '-' && cp[1] == '-' && cp[2] == '\0'){
 1050             ++i;
 1051             break;
 1052          }
 1053       }
 1054    }
 1055    argc = i;
 1056 
 1057    /* ...BUT, since we use n_autorec_alloc() for the MTA n_smopts storage we
 1058     * need to allocate the space for them before we fixate that storage! */
 1059    while(argv[i] != NIL)
 1060       ++i;
 1061    if(n_smopts_cnt + i > mc.mc_smopts_size)
 1062       DBG(mc.mc_smopts_size =)
 1063       a_main_grow_cpp(&n_smopts, n_smopts_cnt + i + 1, n_smopts_cnt);
 1064 
 1065    /* Check for inconsistent arguments, fix some temporaries */
 1066    if(n_psonce & n_PSO_SENDMODE){
 1067       /* XXX This is only because BATCH_FLAG sets *folder*=/dev/null
 1068        * XXX in order to function.  Ideally that would not be needed */
 1069       if(mc.mc_folder != NIL && !(n_poption & n_PO_BATCH_FLAG)){
 1070          emsg = N_("Cannot give -f and people to send to.");
 1071          goto jusage;
 1072       }
 1073       if(mc.mc_u != NIL){
 1074          emsg = N_("The -u option cannot be used in send mode");
 1075          goto jusage;
 1076       }
 1077       if(!(n_poption & n_PO_t_FLAG) && mc.mc_to == NIL){
 1078          emsg = N_("Send options without primary recipient specified.");
 1079          goto jusage;
 1080       }
 1081       if((n_poption & n_PO_t_FLAG) && mc.mc_quote != NIL){
 1082          emsg = N_("The -M, -m, -q and -t options are mutual exclusive.");
 1083          goto jusage;
 1084       }
 1085       if(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)){
 1086          emsg = N_("The -e, -H and -L options cannot be used in send mode.");
 1087          goto jusage;
 1088       }
 1089       if(n_poption & n_PO_R_FLAG){
 1090          emsg = N_("The -R option is meaningless in send mode.");
 1091          goto jusage;
 1092       }
 1093 
 1094       if(n_psonce & n_PSO_INTERACTIVE){
 1095          if(mc.mc_quote == R(char*,-1)){
 1096             if(!(n_poption & n_PO_Mm_FLAG))
 1097                emsg = N_("-q can't use standard input when interactive.\n");
 1098             goto jusage;
 1099          }
 1100       }
 1101    }else{
 1102       if(mc.mc_u != NIL && mc.mc_folder != NIL){
 1103          emsg = N_("The options -u and -f (and -#) are mutually exclusive");
 1104          goto jusage;
 1105       }
 1106       if((n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY)) ==
 1107             (n_PO_EXISTONLY | n_PO_HEADERSONLY)){
 1108          emsg = N_("The options -e and -H are mutual exclusive");
 1109          goto jusage;
 1110       }
 1111       if((n_poption & (n_PO_HEADERSONLY | n_PO_HEADERLIST) /* TODO OBSOLETE */
 1112             ) == (n_PO_HEADERSONLY | n_PO_HEADERLIST))
 1113          n_OBSOLETE(_("please use \"-e -L xy\" instead of \"-H -L xy\""));
 1114 
 1115       if(mc.mc_u != NIL)
 1116          mc.mc_folder = mc.mc_u;
 1117    }
 1118 
 1119    /*
 1120     * We have reached our second program state, the command line options have
 1121     * been worked and verified a bit, we are likely to go, perform more setup
 1122     */
 1123    n_psonce |= n_PSO_STARTED_GETOPT;
 1124    ASSERT(!(n_poption & n_PO_QUICKRUN_MASK) ||
 1125       !(n_psonce & n_PSO_INTERACTIVE));
 1126 
 1127    a_main_setup_vars();
 1128 
 1129    /* Create memory pool snapshot; Memory is auto-reclaimed from now on */
 1130    su_mem_bag_fixate(n_go_data->gdc_membag);
 1131 
 1132    /* load() any resource files */
 1133    if(resfiles & a_RF_MASK){
 1134       /* *expand() returns a savestr(), but load() only uses the file name
 1135        * for fopen(), so it is safe to do this */
 1136       if(resfiles & a_RF_SYSTEM){
 1137          boole nload;
 1138 
 1139          if((nload = ok_blook(NAIL_NO_SYSTEM_RC)))
 1140             n_OBSOLETE(_("Please use $MAILX_NO_SYSTEM_RC instead of "
 1141                "$NAIL_NO_SYSTEM_RC"));
 1142          if(!nload && !ok_blook(MAILX_NO_SYSTEM_RC) &&
 1143                !n_go_load_rc(ok_vlook(system_mailrc)))
 1144             goto jleave;
 1145       }
 1146 
 1147       if((resfiles & a_RF_USER) &&
 1148             (cp = fexpand(ok_vlook(MAILRC), (FEXP_NOPROTO | FEXP_LOCAL_FILE |
 1149                FEXP_NSHELL))) != NIL && !n_go_load_rc(cp))
 1150          goto jleave;
 1151 
 1152       if((resfiles & a_RF_BLTIN) && !n_go_load_lines(FAL0, NIL, 0))
 1153          goto jleave;
 1154    }
 1155 
 1156    if((cp = ok_vlook(NAIL_EXTRA_RC)) != NIL)
 1157       n_OBSOLETE(_("Please use *mailx-extra-rc*, not *NAIL_EXTRA_RC*"));
 1158    if((cp != NIL || (cp = ok_vlook(mailx_extra_rc)) != NIL) &&
 1159          (cp = fexpand(cp, (FEXP_NOPROTO | FEXP_LOCAL_FILE | FEXP_NSHELL))
 1160             ) != NIL && !n_go_load_rc(cp))
 1161       goto jleave;
 1162 
 1163    /* Cause possible umask(2) to be applied, now that any setting is
 1164     * established, and before we change accounts, evaluate commands etc. */
 1165    (void)ok_vlook(umask);
 1166 
 1167    /* Additional options to pass-through to MTA, and allowed to do so? */
 1168    i = argc;
 1169    if((cp = ok_vlook(expandargv)) != NIL){
 1170       boole isfail, isrestrict;
 1171 
 1172       isfail = !su_cs_cmp_case(cp, "fail");
 1173       isrestrict = (!isfail && !su_cs_cmp_case(cp, "restrict"));
 1174 
 1175       if((n_poption & n_PO_D_V) && !isfail && !isrestrict && *cp != '\0')
 1176          n_err(_("Unknown *expandargv* value: %s\n"), cp);
 1177 
 1178       if((cp = argv[i]) != NIL){
 1179          if(isfail || (isrestrict && (!(n_poption & n_PO_TILDE_FLAG) ||
 1180                   !(n_psonce & n_PSO_INTERACTIVE)))){
 1181 je_expandargv:
 1182             n_err(_("*expandargv* doesn't allow MTA arguments; consider "
 1183                "using *mta-arguments*\n"));
 1184             n_exit_status = n_EXIT_USE | n_EXIT_SEND_ERROR;
 1185             goto jleave;
 1186          }
 1187          do{
 1188             ASSERT(n_smopts_cnt + 1 <= mc.mc_smopts_size);
 1189             n_smopts[n_smopts_cnt++] = cp;
 1190          }while((cp = argv[++i]) != NIL);
 1191       }
 1192    }else if(argv[i] != NIL)
 1193       goto je_expandargv;
 1194 
 1195    /* We had to wait until the resource files are loaded and any command line
 1196     * setting has been restored, but get the termcap up and going before we
 1197     * switch account or running commands */
 1198    if(n_psonce & n_PSO_INTERACTIVE){
 1199 #ifdef mx_HAVE_TCAP
 1200       mx_termcap_init();
 1201 #endif
 1202       /* We have to fake some state of readiness in order to allow resolving of
 1203        * lazy `bind's (from config files); this is ok and allows one call to
 1204        * tty_init() (and one to tty_destroy()) instead of two according pairs
 1205        * for send and receive mode, which also had the ugly effect that -A
 1206        * account switch and -X commands ran without properly setup tty/MLE! */
 1207       n_psonce |= n_PSO_STARTED_CONFIG;
 1208       mx_tty_init();
 1209       n_psonce ^= n_PSO_STARTED_CONFIG;
 1210    }
 1211 
 1212    /* Now we can set the account */
 1213    if(mc.mc_A != NIL){
 1214       char const *a[2];
 1215 
 1216       a[0] = mc.mc_A;
 1217       a[1] = NIL;
 1218       if(c_account(a) && (!(n_psonce & n_PSO_INTERACTIVE) ||
 1219             ok_blook(errexit) || ok_blook(posix))){
 1220          n_exit_status = n_EXIT_USE | n_EXIT_SEND_ERROR;
 1221          goto jleave;
 1222       }
 1223    }
 1224 
 1225    /*
 1226     * Almost setup, only -X commands are missing!
 1227     */
 1228    n_psonce |= n_PSO_STARTED_CONFIG;
 1229 
 1230    /* "load()" commands given on command line */
 1231    if(mc.mc_X_cnt > 0 && !n_go_load_lines(FAL0, mc.mc_X, mc.mc_X_cnt))
 1232       goto jleave_full;
 1233 
 1234    /* Final tests */
 1235    if(n_poption & n_PO_Mm_FLAG){
 1236       if(mc.mc_quote == R(char*,-1)){
 1237          if(!mx_mimetype_is_known(n_poption_arg_Mm)){
 1238             n_err(_("Could not find `mimetype' for -M argument: %s\n"),
 1239                n_poption_arg_Mm);
 1240             n_exit_status = n_EXIT_ERR;
 1241             goto jleave_full;
 1242          }
 1243       }else if(/* XXX only to satisfy Coverity! */mc.mc_quote != NIL &&
 1244             (n_poption_arg_Mm = mx_mimetype_classify_filename(mc.mc_quote)
 1245                ) == NIL){
 1246          n_err(_("Could not `mimetype'-classify -m argument: %s\n"),
 1247             n_shexp_quote_cp(mc.mc_quote, FAL0));
 1248          n_exit_status = n_EXIT_ERR;
 1249          goto jleave_full;
 1250       }else if(!su_cs_cmp_case(n_poption_arg_Mm, "text/plain")) /* TODO magic*/
 1251          n_poption_arg_Mm = NULL;
 1252    }
 1253 
 1254    /*
 1255     * We are finally completely setup and ready to go!
 1256     */
 1257    n_psonce |= n_PSO_STARTED;
 1258 
 1259    /* TODO v15compat */
 1260    if((n_poption & n_PO_D_V) && ok_vlook(v15_compat) == NIL)
 1261       n_err("Warning -- v15-compat=yes will be default in v14.10.0!\n");
 1262 
 1263    if(!(n_psonce & n_PSO_SENDMODE))
 1264       n_exit_status = a_main_rcv_mode(&mc);
 1265    else{
 1266       /* XXX This may use savestr(), but since we will not enter the command
 1267        * XXX loop we do not need to care about that */
 1268       for(; mc.mc_a_head != NIL; mc.mc_a_head = mc.mc_a_head->maa_next){
 1269          BITENUM_IS(u32,mx_attachments_error) aerr;
 1270 
 1271          mc.mc_attach = mx_attachments_append(mc.mc_attach,
 1272                mc.mc_a_head->maa_file, &aerr, NIL);
 1273          if(aerr != mx_ATTACHMENTS_ERR_NONE){
 1274             n_exit_status = n_EXIT_ERR;
 1275             goto jleave_full;
 1276          }
 1277       }
 1278 
 1279       /* "load()" more commands given on command line */
 1280       if(mc.mc_Y_cnt > 0 && !n_go_load_lines(TRU1, mc.mc_Y, mc.mc_Y_cnt))
 1281          n_exit_status = n_EXIT_ERR;
 1282       else
 1283          n_mail((((n_psonce & n_PSO_INTERACTIVE
 1284                   ) ? n_MAILSEND_HEADERS_PRINT : 0) |
 1285                (n_poption & n_PO_F_FLAG ? n_MAILSEND_RECORD_RECIPIENT : 0)),
 1286             mc.mc_to, mc.mc_cc, mc.mc_bcc, mc.mc_subject,
 1287             mc.mc_attach, mc.mc_quote);
 1288    }
 1289 
 1290 jleave_full:/* C99 */{
 1291       char const *ccp;
 1292       boole was_xit;
 1293 
 1294       i = n_exit_status;
 1295       was_xit = ((n_psonce & n_PSO_XIT) != 0);
 1296 
 1297       n_psonce &= ~n_PSO_EXIT_MASK;
 1298       mx_account_leave();
 1299 
 1300       if(n_psonce & n_PSO_INTERACTIVE){
 1301          mx_tty_destroy(was_xit);
 1302 #ifdef mx_HAVE_TCAP
 1303          mx_termcap_destroy();
 1304 #endif
 1305       }
 1306 
 1307       n_psonce &= ~n_PSO_EXIT_MASK;
 1308       if((ccp = ok_vlook(on_program_exit)) != NIL)
 1309          temporary_on_xy_hook_caller("on-program-exit", ccp, FAL0);
 1310 
 1311       n_exit_status = i;
 1312    }
 1313 jleave:
 1314 #ifdef su_HAVE_DEBUG
 1315    su_mem_bag_gut(n_go_data->gdc_membag); /* Was init in go_init() */
 1316    su_mem_set_conf(su_MEM_CONF_LINGER_FREE_RELEASE, 0);
 1317 #endif
 1318 
 1319    NYD_OU;
 1320    return n_exit_status;
 1321 }
 1322 
 1323 #include "su/code-ou.h"
 1324 
 1325 /* Source the others in that case! */
 1326 #ifdef mx_HAVE_AMALGAMATION
 1327 # include <mx/gen-config.h>
 1328 #endif
 1329 
 1330 /* s-it-mode */