"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/accmacvar.c" (16 Feb 2018, 121549 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 "accmacvar.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  *@ Account, macro and variable handling.
    3  *@ HOWTO add a new non-dynamic boolean or value option:
    4  *@ - add an entry to nail.h:enum okeys
    5  *@ - run make-okey-map.pl
    6  *@ - update the manual!
    7  *@ TODO . Improve support for chains so that we can apply the property checks
    8  *@ TODO   of the base variable to -HOST and -USER@HOST variants!  Add some!!
    9  *@ TODO   Now with `local' modifier we need to be able to catch that, as well
   10  *@ TODO   as linked environment variables: both of which are not catched and
   11  *@ TODO   can be shadowed, which is documented for `set' - change on change!!
   12  *@ TODO . `localopts' should act like an automatic permanent `scope' command
   13  *@ TODO    modifier!  We need an OnScopeLeaveEvent, then.
   14  *@ TODO   Also see the a_GO_SPLICE comment in go.c.
   15  *@ TODO . Optimize: with the dynamic hashmaps, and the object based approach
   16  *@ TODO   it should become possible to strip down the implementation again.
   17  *@ TODO   E.g., FREEZE is much too complicated: use an overlay object ptr,
   18  *@ TODO   UNLIKELY() it, and add a OnProgramStartupCompletedEvent to
   19  *@ TODO   incorporate what it tracks, then drop it.  Etc.
   20  *@ TODO   Global -> Scope -> Local, all "overlay" objects.
   21  *
   22  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
   23  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
   24  */
   25 /*
   26  * Copyright (c) 1980, 1993
   27  *      The Regents of the University of California.  All rights reserved.
   28  *
   29  * Redistribution and use in source and binary forms, with or without
   30  * modification, are permitted provided that the following conditions
   31  * are met:
   32  * 1. Redistributions of source code must retain the above copyright
   33  *    notice, this list of conditions and the following disclaimer.
   34  * 2. Redistributions in binary form must reproduce the above copyright
   35  *    notice, this list of conditions and the following disclaimer in the
   36  *    documentation and/or other materials provided with the distribution.
   37  * 3. Neither the name of the University nor the names of its contributors
   38  *    may be used to endorse or promote products derived from this software
   39  *    without specific prior written permission.
   40  *
   41  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   51  * SUCH DAMAGE.
   52  */
   53 #undef n_FILE
   54 #define n_FILE accmacvar
   55 
   56 #ifndef HAVE_AMALGAMATION
   57 # include "nail.h"
   58 #endif
   59 
   60 #if !defined HAVE_SETENV && !defined HAVE_PUTENV
   61 # error Exactly one of HAVE_SETENV and HAVE_PUTENV
   62 #endif
   63 
   64 /* Positional parameter maximum (macro arguments, `vexpr' "splitifs") */
   65 #define a_AMV_POSPAR_MAX SI16_MAX
   66 
   67 /* Special "pseudo macro" that stabs you from the back */
   68 #define a_AMV_MACKY_MACK ((struct a_amv_mac*)-1)
   69 
   70 /* Note: changing the hash function must be reflected in `vexpr' "hash",
   71  * because that is used by the hashtable creator scripts! */
   72 #define a_AMV_PRIME HSHSIZE
   73 #define a_AMV_NAME2HASH(N) n_torek_hash(N)
   74 #define a_AMV_HASH2PRIME(H) ((H) % a_AMV_PRIME)
   75 
   76 enum a_amv_mac_flags{
   77    a_AMV_MF_NONE = 0,
   78    a_AMV_MF_ACCOUNT = 1u<<0,  /* This macro is an `account' */
   79    a_AMV_MF_TYPE_MASK = a_AMV_MF_ACCOUNT,
   80    a_AMV_MF_UNDEF = 1u<<1,    /* Unlink after lookup */
   81    a_AMV_MF_DELETE = 1u<<7,   /* Delete in progress, free once refcnt==0 */
   82    a_AMV_MF__MAX = 0xFFu
   83 };
   84 
   85 enum a_amv_loflags{
   86    a_AMV_LF_NONE = 0,
   87    a_AMV_LF_SCOPE = 1u<<0,          /* Current scope `localopts' on */
   88    a_AMV_LF_SCOPE_FIXATE = 1u<<1,   /* Ditto, but fixated */
   89    a_AMV_LF_SCOPE_MASK = a_AMV_LF_SCOPE | a_AMV_LF_SCOPE_FIXATE,
   90    a_AMV_LF_CALL = 1u<<2,           /* `localopts' on for `call'ed scopes */
   91    a_AMV_LF_CALL_FIXATE = 1u<<3,    /* Ditto, but fixated */
   92    a_AMV_LF_CALL_MASK = a_AMV_LF_CALL | a_AMV_LF_CALL_FIXATE,
   93    a_AMV_LF_CALL_TO_SCOPE_SHIFT = 2
   94 };
   95 
   96 /* make-okey-map.pl ensures that _VIRT implies _RDONLY and _NODEL, and that
   97  * _IMPORT implies _ENV; it doesn't verify anything...
   98  * More description at nail.h:enum okeys */
   99 enum a_amv_var_flags{
  100    a_AMV_VF_NONE = 0,
  101 
  102    /* The basic set of flags, also present in struct a_amv_var_map.avm_flags */
  103    a_AMV_VF_BOOL = 1u<<0,     /* ok_b_* */
  104    a_AMV_VF_CHAIN = 1u<<1,    /* Is variable chain (-USER{,@HOST} variants) */
  105    a_AMV_VF_VIRT = 1u<<2,     /* "Stateless" automatic variable */
  106    a_AMV_VF_VIP = 1u<<3,      /* Wants _var_check_vips() evaluation */
  107    a_AMV_VF_RDONLY = 1u<<4,   /* May not be set by user */
  108    a_AMV_VF_NODEL = 1u<<5,    /* May not be deleted */
  109    a_AMV_VF_I3VAL = 1u<<6,    /* Has an initial value */
  110    a_AMV_VF_DEFVAL = 1u<<7,   /* Has a default value */
  111    a_AMV_VF_IMPORT = 1u<<8,   /* Import ONLY from env (pre n_PSO_STARTED) */
  112    a_AMV_VF_ENV = 1u<<9,      /* Update environment on change */
  113    a_AMV_VF_NOLOPTS = 1u<<10, /* May not be tracked by `localopts' */
  114    a_AMV_VF_NOTEMPTY = 1u<<11, /* May not be assigned an empty value */
  115    /* TODO _VF_NUM, _VF_POSNUM: we also need 64-bit limit numbers! */
  116    a_AMV_VF_NUM = 1u<<12,     /* Value must be a 32-bit number */
  117    a_AMV_VF_POSNUM = 1u<<13,  /* Value must be positive 32-bit number */
  118    a_AMV_VF_LOWER = 1u<<14,   /* Values will be stored in lowercase version */
  119    a_AMV_VF_OBSOLETE = 1u<<15, /* Is obsolete? */
  120    a_AMV_VF__MASK = (1u<<(15+1)) - 1,
  121 
  122    /* Extended flags, not part of struct a_amv_var_map.avm_flags */
  123    a_AMV_VF_EXT_LOCAL = 1u<<23,        /* `local' */
  124    a_AMV_VF_EXT_LINKED = 1u<<24,       /* `environ' link'ed */
  125    a_AMV_VF_EXT_FROZEN = 1u<<25,       /* Has been set by -S,.. */
  126    a_AMV_VF_EXT_FROZEN_UNSET = 1u<<26, /* ..and was used to unset a variable */
  127    a_AMV_VF_EXT__FROZEN_MASK = a_AMV_VF_EXT_FROZEN | a_AMV_VF_EXT_FROZEN_UNSET,
  128    a_AMV_VF_EXT__MASK = (1u<<(26+1)) - 1
  129 };
  130 
  131 enum a_amv_var_lookup_flags{
  132    a_AMV_VLOOK_NONE = 0,
  133    a_AMV_VLOOK_LOCAL = 1u<<0,       /* Query `local' layer first */
  134    a_AMV_VLOOK_LOCAL_ONLY = 1u<<1,  /* MUST be a `local' variable */
  135    /* Do not allocate new var for _I3VAL, see _var_lookup() for more */
  136    a_AMV_VLOOK_I3VAL_NONEW = 1u<<2,
  137    a_AMV_VLOOK_I3VAL_NONEW_REPORT = 1u<<3
  138 };
  139 
  140 enum a_amv_var_setclr_flags{
  141    a_AMV_VSETCLR_NONE = 0,
  142    a_AMV_VSETCLR_LOCAL = 1u<<0,     /* Use `local' variables only */
  143    a_AMV_VSETCLR_ENV = 1u<<1        /* `environ' or otherwise environ */
  144 };
  145 
  146 /* We support some special parameter names for one(+)-letter variable names;
  147  * note these have counterparts in the code that manages shell expansion!
  148  * All these special variables are solely backed by n_var_vlook(), and besides
  149  * there is only a_amv_var_revlookup() which knows about them */
  150 enum a_amv_var_special_category{
  151    a_AMV_VSC_NONE,      /* Normal variable, no special treatment */
  152    a_AMV_VSC_GLOBAL,    /* ${[?!]} are specially mapped, but global */
  153    a_AMV_VSC_MULTIPLEX, /* ${^.*} circumflex accent multiplexer */
  154    a_AMV_VSC_POSPAR,    /* ${[1-9][0-9]*} positional parameters */
  155    a_AMV_VSC_POSPAR_ENV /* ${[*@#]} positional parameter support variables */
  156 };
  157 
  158 enum a_amv_var_special_type{
  159    /* _VSC_GLOBAL */
  160    a_AMV_VST_QM,     /* ? */
  161    a_AMV_VST_EM,     /* ! */
  162    /* _VSC_MULTIPLEX */
  163    /* This is special in that it is a multiplex indicator, the ^ is followed by
  164     * a normal variable */
  165    a_AMV_VST_CACC,   /* ^ (circumflex accent) */
  166    /* _VSC_POSPAR_ENV */
  167    a_AMV_VST_STAR,   /* * */
  168    a_AMV_VST_AT,     /* @ */
  169    a_AMV_VST_NOSIGN  /* # */
  170 };
  171 
  172 enum a_amv_var_vip_mode{
  173    a_AMV_VIP_SET_PRE,
  174    a_AMV_VIP_SET_POST,
  175    a_AMV_VIP_CLEAR
  176 };
  177 
  178 struct a_amv_pospar{
  179    ui16_t app_maxcount;    /* == slots in .app_dat */
  180    ui16_t app_count;       /* Maximum a_AMV_POSPAR_MAX */
  181    ui16_t app_idx;         /* `shift' moves this one, decs .app_count */
  182    bool_t app_not_heap;    /* .app_dat stuff not dynamically allocated */
  183    ui8_t app__dummy[1];
  184    char const **app_dat;   /* NULL terminated (for "$@" explosion support) */
  185 };
  186 n_CTA(a_AMV_POSPAR_MAX <= SI16_MAX, "Limit exceeds datatype capabilities");
  187 
  188 struct a_amv_mac{
  189    struct a_amv_mac *am_next;
  190    ui32_t am_maxlen;             /* of any line in .am_line_dat */
  191    ui32_t am_line_cnt;           /* of *.am_line_dat (but NULL terminated) */
  192    struct a_amv_mac_line **am_line_dat; /* TODO use deque? */
  193    struct a_amv_var *am_lopts;   /* `localopts' unroll list */
  194    ui32_t am_refcnt;             /* 0-based for `un{account,define}' purposes */
  195    ui8_t am_flags;               /* enum a_amv_mac_flags */
  196    char am_name[n_VFIELD_SIZE(3)]; /* of this macro */
  197 };
  198 n_CTA(a_AMV_MF__MAX <= UI8_MAX, "Enumeration excesses storage datatype");
  199 
  200 struct a_amv_mac_line{
  201    ui32_t aml_len;
  202    ui32_t aml_prespc;   /* Number of leading SPACEs, for display purposes */
  203    char aml_dat[n_VFIELD_SIZE(0)];
  204 };
  205 
  206 struct a_amv_mac_call_args{
  207    char const *amca_name;        /* For MACKY_MACK, this is *0*! */
  208    struct a_amv_mac *amca_amp;   /* "const", but for am_refcnt */
  209    struct a_amv_var **amca_unroller;
  210    void (*amca_hook_pre)(void *);
  211    void *amca_hook_arg;
  212    ui8_t amca_loflags;
  213    bool_t amca_ps_hook_mask;
  214    bool_t amca_no_xcall;         /* We want n_GO_INPUT_NO_XCALL for this */
  215    ui8_t amca__pad[5];
  216    struct a_amv_var *(*amca_local_vars)[a_AMV_PRIME]; /* `local's, or NULL */
  217    struct a_amv_pospar amca_pospar;
  218 };
  219 
  220 struct a_amv_lostack{
  221    struct a_amv_lostack *as_global_saved; /* Saved global XXX due to jump */
  222    struct a_amv_mac_call_args *as_amcap;
  223    struct a_amv_lostack *as_up;  /* Outer context */
  224    struct a_amv_var *as_lopts;
  225    ui8_t as_loflags;             /* enum a_amv_mac_loflags */
  226    ui8_t avs__pad[7];
  227 };
  228 
  229 struct a_amv_var{
  230    struct a_amv_var *av_link;
  231    char *av_value;
  232 #ifdef HAVE_PUTENV
  233    char *av_env;              /* Actively managed putenv(3) memory, or NULL */
  234 #endif
  235    ui32_t av_flags;           /* enum a_amv_var_flags inclusive extended bits */
  236    char av_name[n_VFIELD_SIZE(4)];
  237 };
  238 n_CTA(a_AMV_VF_EXT__MASK <= UI32_MAX, "Enumeration excesses storage datatype");
  239 
  240 struct a_amv_var_map{
  241    ui32_t avm_hash;
  242    ui16_t avm_keyoff;
  243    ui16_t avm_flags;    /* enum a_amv_var_flags without extended bits */
  244 };
  245 n_CTA(a_AMV_VF__MASK <= UI16_MAX, "Enumeration excesses storage datatype");
  246 
  247 struct a_amv_var_virt{
  248    ui32_t avv_okey;
  249    ui8_t avv__dummy[4];
  250    struct a_amv_var const *avv_var;
  251 };
  252 
  253 struct a_amv_var_defval{
  254    ui32_t avdv_okey;
  255    ui8_t avdv__pad[4];
  256    char const *avdv_value; /* Only for !BOOL (otherwise plain existence) */
  257 };
  258 
  259 struct a_amv_var_carrier{
  260    char const *avc_name;
  261    ui32_t avc_hash;
  262    ui32_t avc_prime;
  263    struct a_amv_var *avc_var;
  264    struct a_amv_var_map const *avc_map;
  265    enum okeys avc_okey;
  266    ui8_t avc__pad[1];
  267    ui8_t avc_special_cat;
  268    /* Numerical parameter name if .avc_special_cat=a_AMV_VSC_POSPAR,
  269     * otherwise a enum a_amv_var_special_type */
  270    ui16_t avc_special_prop;
  271 };
  272 
  273 /* Include constant make-okey-map.pl output, and the generated version data */
  274 #include <gen-version.h>
  275 #include <gen-okeys.h>
  276 
  277 /* The currently active account */
  278 static struct a_amv_mac *a_amv_acc_curr;
  279 
  280 static struct a_amv_mac *a_amv_macs[a_AMV_PRIME]; /* TODO dynamically spaced */
  281 
  282 /* Unroll list of currently running macro stack */
  283 static struct a_amv_lostack *a_amv_lopts;
  284 
  285 static struct a_amv_var *a_amv_vars[a_AMV_PRIME]; /* TODO dynamically spaced */
  286 
  287 /* Global (i.e., non-local) a_AMV_VSC_POSPAR stack */
  288 static struct a_amv_pospar a_amv_pospar;
  289 
  290 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
  291  * TODO today via a static lostack, it should be a field in mailbox, once that
  292  * TODO is a real multi-instance object */
  293 static struct a_amv_var *a_amv_folder_hook_lopts;
  294 
  295 /* TODO Rather ditto (except for storage -> cmd_ctx), compose hooks */
  296 static struct a_amv_var *a_amv_compose_lopts;
  297 
  298 /* Lookup for macros/accounts: if newamp is not NULL it will be linked in the
  299  * map, if _MF_UNDEF is set a possibly existing entry will be removed (first).
  300  * Returns NULL if a lookup failed, or if newamp was set, the found entry in
  301  * plain lookup cases or when _UNDEF was performed on a currently active entry
  302  * (the entry will have been unlinked, and the _MF_DELETE will be honoured once
  303  * the reference count reaches 0), and (*)-1 if an _UNDEF was performed */
  304 static struct a_amv_mac *a_amv_mac_lookup(char const *name,
  305                            struct a_amv_mac *newamp, enum a_amv_mac_flags amf);
  306 
  307 /* `call', `call_if' (and `xcall' via go.c -> c_call()) */
  308 static int a_amv_mac_call(void *v, bool_t silent_nexist);
  309 
  310 /* Execute a macro; amcap must reside in LOFI memory */
  311 static bool_t a_amv_mac_exec(struct a_amv_mac_call_args *amcap);
  312 
  313 static void a_amv_mac__finalize(void *vp);
  314 
  315 /* User display helpers */
  316 static bool_t a_amv_mac_show(enum a_amv_mac_flags amf);
  317 
  318 /* _def() returns error for faulty definitions and already existing * names,
  319  * _undef() returns error if a named thing doesn't exist */
  320 static bool_t a_amv_mac_def(char const *name, enum a_amv_mac_flags amf);
  321 static bool_t a_amv_mac_undef(char const *name, enum a_amv_mac_flags amf);
  322 
  323 /* */
  324 static void a_amv_mac_free(struct a_amv_mac *amp);
  325 
  326 /* Update replay-log */
  327 static void a_amv_lopts_add(struct a_amv_lostack *alp, char const *name,
  328                struct a_amv_var *oavp);
  329 static void a_amv_lopts_unroll(struct a_amv_var **avpp);
  330 
  331 /* Special cased value string allocation */
  332 static char *a_amv_var_copy(char const *str);
  333 static void a_amv_var_free(char *cp);
  334 
  335 /* Check for special housekeeping.  _VIP_SET_POST and _VIP_CLEAR do not fail
  336  * (or propagate errors), _VIP_SET_PRE may and should case abortion */
  337 static bool_t a_amv_var_check_vips(enum a_amv_var_vip_mode avvm,
  338                enum okeys okey, char const **val);
  339 
  340 /* _VF_NUM / _VF_POSNUM */
  341 static bool_t a_amv_var_check_num(char const *val, bool_t posnum);
  342 
  343 /* If a variable name begins with a lowercase-character and contains at
  344  * least one '@', it is converted to all-lowercase. This is necessary
  345  * for lookups of names based on email addresses.
  346  * Following the standard, only the part following the last '@' should
  347  * be lower-cased, but practice has established otherwise here */
  348 static char const *a_amv_var_canonify(char const *vn);
  349 
  350 /* Try to reverse lookup an option name to an enum okeys mapping.
  351  * Updates .avc_name and .avc_hash; .avc_map is NULL if none found */
  352 static bool_t a_amv_var_revlookup(struct a_amv_var_carrier *avcp,
  353                char const *name);
  354 
  355 /* Lookup a variable from .avc_(map|name|hash), return whether it was found.
  356  * Sets .avc_prime; .avc_var is NULL if not found.
  357  * Here it is where we care for _I3VAL and _DEFVAL.
  358  * An _I3VAL will be "consumed" as necessary anyway, but it won't be used to
  359  * create a new variable if _VLOOK_I3VAL_NONEW is set; if
  360  * _VLOOK_I3VAL_NONEW_REPORT is set then we set .avc_var to -1 and return true
  361  * if that was the case, otherwise we'll return FAL0, then! */
  362 static bool_t a_amv_var_lookup(struct a_amv_var_carrier *avcp,
  363                enum a_amv_var_lookup_flags avlf);
  364 
  365 /* Lookup functions for special category variables, _pospar drives all
  366  * positional parameter etc. special categories */
  367 static char const *a_amv_var_vsc_global(struct a_amv_var_carrier *avcp);
  368 static char const *a_amv_var_vsc_multiplex(struct a_amv_var_carrier *avcp);
  369 static char const *a_amv_var_vsc_pospar(struct a_amv_var_carrier *avcp);
  370 
  371 /* Set var from .avc_(map|name|hash), return success */
  372 static bool_t a_amv_var_set(struct a_amv_var_carrier *avcp, char const *value,
  373                enum a_amv_var_setclr_flags avscf);
  374 
  375 static bool_t a_amv_var__putenv(struct a_amv_var_carrier *avcp,
  376                struct a_amv_var *avp);
  377 
  378 /* Clear var from .avc_(map|name|hash); sets .avc_var=NULL, return success */
  379 static bool_t a_amv_var_clear(struct a_amv_var_carrier *avcp,
  380                enum a_amv_var_setclr_flags avscf);
  381 
  382 static bool_t a_amv_var__clearenv(char const *name, struct a_amv_var *avp);
  383 
  384 /* List all variables */
  385 static void a_amv_var_show_all(void);
  386 
  387 static int a_amv_var__show_cmp(void const *s1, void const *s2);
  388 
  389 /* Actually do print one, return number of lines written */
  390 static size_t a_amv_var_show(char const *name, FILE *fp, struct n_string *msgp);
  391 
  392 /* Shared c_set() and c_environ():set impl, return success */
  393 static bool_t a_amv_var_c_set(char **ap, enum a_amv_var_setclr_flags avscf);
  394 
  395 static struct a_amv_mac *
  396 a_amv_mac_lookup(char const *name, struct a_amv_mac *newamp,
  397       enum a_amv_mac_flags amf){
  398    struct a_amv_mac *amp, **ampp;
  399    ui32_t h;
  400    enum a_amv_mac_flags save_amf;
  401    NYD2_ENTER;
  402 
  403    save_amf = amf;
  404    amf &= a_AMV_MF_TYPE_MASK;
  405    h = a_AMV_NAME2HASH(name);
  406    h = a_AMV_HASH2PRIME(h);
  407    ampp = &a_amv_macs[h];
  408 
  409    for(amp = *ampp; amp != NULL; ampp = &(*ampp)->am_next, amp = amp->am_next){
  410       if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf &&
  411             !strcmp(amp->am_name, name)){
  412          if(n_LIKELY((save_amf & a_AMV_MF_UNDEF) == 0))
  413             goto jleave;
  414 
  415          *ampp = amp->am_next;
  416 
  417          if(amp->am_refcnt > 0){
  418             amp->am_flags |= a_AMV_MF_DELETE;
  419             if(n_poption & n_PO_D_V)
  420                n_err(_("Delayed deletion of currently active %s: %s\n"),
  421                   (amp->am_flags & a_AMV_MF_ACCOUNT ? "account" : "define"),
  422                   name);
  423          }else{
  424             a_amv_mac_free(amp);
  425             amp = (struct a_amv_mac*)-1;
  426          }
  427          break;
  428       }
  429    }
  430 
  431    if(newamp != NULL){
  432       ampp = &a_amv_macs[h];
  433       newamp->am_next = *ampp;
  434       *ampp = newamp;
  435       amp = NULL;
  436    }
  437 jleave:
  438    NYD2_LEAVE;
  439    return amp;
  440 }
  441 
  442 static int
  443 a_amv_mac_call(void *v, bool_t silent_nexist){
  444    struct a_amv_mac *amp;
  445    int rv;
  446    char const *name;
  447    struct n_cmd_arg_ctx *cacp;
  448    NYD_ENTER;
  449 
  450    cacp = v;
  451 
  452    if(cacp->cac_no == 0){
  453       n_err(_("Synopsis: call(_if)?: name [:<arguments>:]\n"));
  454       n_pstate_err_no = n_ERR_INVAL;
  455       rv = 1;
  456       goto jleave;
  457    }
  458 
  459    name = cacp->cac_arg->ca_arg.ca_str.s;
  460 
  461    if(n_UNLIKELY(cacp->cac_no > a_AMV_POSPAR_MAX)){
  462       n_err(_("Too many arguments to macro `call': %s\n"), name);
  463       n_pstate_err_no = n_ERR_OVERFLOW;
  464       rv = 1;
  465    }else if(n_UNLIKELY((amp = a_amv_mac_lookup(name, NULL, a_AMV_MF_NONE)
  466          ) == NULL)){
  467       if(!silent_nexist)
  468          n_err(_("Undefined macro called: %s\n"), n_shexp_quote_cp(name, FAL0));
  469       n_pstate_err_no = n_ERR_NOENT;
  470       rv = 1;
  471    }else{
  472       char const **argv;
  473       struct a_amv_mac_call_args *amcap;
  474       size_t argc;
  475 
  476       argc = cacp->cac_no + 1;
  477       amcap = n_lofi_alloc(sizeof *amcap + (argc * sizeof *argv));
  478       argv = (void*)&amcap[1];
  479 
  480       for(argc = 0; (cacp->cac_arg = cacp->cac_arg->ca_next) != NULL; ++argc)
  481          argv[argc] = cacp->cac_arg->ca_arg.ca_str.s;
  482       argv[argc] = NULL;
  483 
  484       memset(amcap, 0, sizeof *amcap);
  485       amcap->amca_name = name;
  486       amcap->amca_amp = amp;
  487       if(a_amv_lopts != NULL)
  488          amcap->amca_loflags = (a_amv_lopts->as_loflags & a_AMV_LF_CALL_MASK
  489                ) >> a_AMV_LF_CALL_TO_SCOPE_SHIFT;
  490       if(argc > 0){
  491          amcap->amca_pospar.app_count = (ui16_t)argc;
  492          amcap->amca_pospar.app_not_heap = TRU1;
  493          amcap->amca_pospar.app_dat = argv;
  494       }
  495 
  496       (void)a_amv_mac_exec(amcap);
  497       rv = n_pstate_ex_no;
  498    }
  499 jleave:
  500    NYD_LEAVE;
  501    return rv;
  502 }
  503 
  504 static bool_t
  505 a_amv_mac_exec(struct a_amv_mac_call_args *amcap){
  506    struct a_amv_lostack *losp;
  507    struct a_amv_mac_line **amlp;
  508    char **args_base, **args;
  509    struct a_amv_mac *amp;
  510    bool_t rv;
  511    NYD2_ENTER;
  512 
  513    amp = amcap->amca_amp;
  514    assert(amp != NULL && amp != a_AMV_MACKY_MACK);
  515    ++amp->am_refcnt;
  516    /* XXX Unfortunately we yet need to dup the macro lines! :( */
  517    args_base = args = n_alloc(sizeof(*args) * (amp->am_line_cnt +1));
  518    for(amlp = amp->am_line_dat; *amlp != NULL; ++amlp)
  519       *(args++) = sbufdup((*amlp)->aml_dat, (*amlp)->aml_len);
  520    *args = NULL;
  521 
  522    losp = n_lofi_alloc(sizeof *losp);
  523    losp->as_global_saved = a_amv_lopts;
  524    if((losp->as_amcap = amcap)->amca_unroller == NULL){
  525       losp->as_up = losp->as_global_saved;
  526       losp->as_lopts = NULL;
  527    }else{
  528       losp->as_up = NULL;
  529       losp->as_lopts = *amcap->amca_unroller;
  530    }
  531    losp->as_loflags = amcap->amca_loflags;
  532 
  533    a_amv_lopts = losp;
  534    if(amcap->amca_hook_pre != NULL)
  535       n_PS_ROOT_BLOCK((*amcap->amca_hook_pre)(amcap->amca_hook_arg));
  536    rv = n_go_macro((n_GO_INPUT_NONE |
  537             (amcap->amca_no_xcall ? n_GO_INPUT_NO_XCALL : 0)),
  538          amp->am_name, args_base, &a_amv_mac__finalize, losp);
  539    NYD2_LEAVE;
  540    return rv;
  541 }
  542 
  543 static void
  544 a_amv_mac__finalize(void *vp){
  545    struct a_amv_mac *amp;
  546    struct a_amv_mac_call_args *amcap;
  547    struct a_amv_lostack *losp;
  548    NYD2_ENTER;
  549 
  550    losp = vp;
  551    a_amv_lopts = losp->as_global_saved;
  552 
  553    amcap = losp->as_amcap;
  554 
  555    /* Delete positional parameter stack */
  556    if(!amcap->amca_pospar.app_not_heap && amcap->amca_pospar.app_maxcount > 0){
  557       ui16_t i;
  558 
  559       for(i = amcap->amca_pospar.app_maxcount; i-- != 0;)
  560          n_free(n_UNCONST(amcap->amca_pospar.app_dat[i]));
  561       n_free(amcap->amca_pospar.app_dat);
  562    }
  563 
  564    /* `local' variable hashmap.  These have no environment map, never */
  565    if(amcap->amca_local_vars != NULL){
  566       struct a_amv_var **avpp_base, **avpp, *avp;
  567 
  568       for(avpp_base = *amcap->amca_local_vars, avpp = &avpp_base[a_AMV_PRIME];
  569             avpp-- != avpp_base;)
  570          while((avp = *avpp)){
  571             assert((avp->av_flags & (a_AMV_VF_NOLOPTS | a_AMV_VF_EXT_LOCAL)) ==
  572                (a_AMV_VF_NOLOPTS | a_AMV_VF_EXT_LOCAL));
  573             assert(!(avp->av_flags &
  574                   ((a_AMV_VF__MASK | a_AMV_VF_EXT__MASK) &
  575                      ~(a_AMV_VF_NOLOPTS | a_AMV_VF_EXT_LOCAL))));
  576             *avpp = avp->av_link;
  577             a_amv_var_free(avp->av_value);
  578             n_free(avp);
  579          }
  580       n_free(avpp_base);
  581    }
  582 
  583    /* Unroll `localopts', if applicable */
  584    if(amcap->amca_unroller == NULL){
  585       if(losp->as_lopts != NULL)
  586          a_amv_lopts_unroll(&losp->as_lopts);
  587    }else
  588       *amcap->amca_unroller = losp->as_lopts;
  589 
  590    if(amcap->amca_ps_hook_mask)
  591       n_pstate &= ~n_PS_HOOK_MASK;
  592 
  593    if((amp = amcap->amca_amp) != a_AMV_MACKY_MACK && amp != NULL &&
  594          --amp->am_refcnt == 0 && (amp->am_flags & a_AMV_MF_DELETE))
  595       a_amv_mac_free(amp);
  596 
  597    n_lofi_free(losp);
  598    n_lofi_free(amcap);
  599    NYD2_LEAVE;
  600 }
  601 
  602 static bool_t
  603 a_amv_mac_show(enum a_amv_mac_flags amf){
  604    size_t lc, mc, ti, i;
  605    char const *typestr;
  606    FILE *fp;
  607    bool_t rv;
  608    NYD2_ENTER;
  609 
  610    rv = FAL0;
  611 
  612    if((fp = Ftmp(NULL, "deflist", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
  613          NULL){
  614       n_perr(_("`define' or `account' list: cannot create temporary file"), 0);
  615       goto jleave;
  616    }
  617 
  618    amf &= a_AMV_MF_TYPE_MASK;
  619    typestr = (amf & a_AMV_MF_ACCOUNT) ? "account" : "define";
  620 
  621    for(lc = mc = ti = 0; ti < a_AMV_PRIME; ++ti){
  622       struct a_amv_mac *amp;
  623 
  624       for(amp = a_amv_macs[ti]; amp != NULL; amp = amp->am_next){
  625          if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf){
  626             struct a_amv_mac_line **amlpp;
  627 
  628             if(++mc > 1){
  629                putc('\n', fp);
  630                ++lc;
  631             }
  632             ++lc;
  633             fprintf(fp, "%s %s {\n", typestr, amp->am_name);
  634             for(amlpp = amp->am_line_dat; *amlpp != NULL; ++lc, ++amlpp){
  635                for(i = (*amlpp)->aml_prespc; i > 0; --i)
  636                   putc(' ', fp);
  637                fputs((*amlpp)->aml_dat, fp);
  638                putc('\n', fp);
  639             }
  640             fputs("}\n", fp);
  641             ++lc;
  642          }
  643       }
  644    }
  645    if(mc > 0)
  646       page_or_print(fp, lc);
  647 
  648    rv = (ferror(fp) == 0);
  649    Fclose(fp);
  650 jleave:
  651    NYD2_LEAVE;
  652    return rv;
  653 }
  654 
  655 static bool_t
  656 a_amv_mac_def(char const *name, enum a_amv_mac_flags amf){
  657    struct str line;
  658    ui32_t line_cnt, maxlen;
  659    struct linelist{
  660       struct linelist *ll_next;
  661       struct a_amv_mac_line *ll_amlp;
  662    } *llp, *ll_head, *ll_tail;
  663    union {size_t s; int i; ui32_t ui; size_t l;} n;
  664    struct a_amv_mac *amp;
  665    bool_t rv;
  666    NYD2_ENTER;
  667 
  668    memset(&line, 0, sizeof line);
  669    rv = FAL0;
  670    amp = NULL;
  671 
  672    /* TODO We should have our input state machine which emits Line events,
  673     * TODO and hook different consumers dependent on our content, as stated
  674     * TODO in i think lex_input: like this local macros etc. would become
  675     * TODO possible from the input side */
  676    /* Read in the lines which form the macro content */
  677    for(ll_tail = ll_head = NULL, line_cnt = maxlen = 0;;){
  678       ui32_t leaspc;
  679       char *cp;
  680 
  681       n.i = n_go_input(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_NL_ESC, n_empty,
  682             &line.s, &line.l, NULL, NULL);
  683       if(n.ui == 0)
  684          continue;
  685       if(n.i < 0){
  686          n_err(_("Unterminated %s definition: %s\n"),
  687             (amf & a_AMV_MF_ACCOUNT ? "account" : "macro"), name);
  688          goto jerr;
  689       }
  690 
  691       /* Trim WS, remember amount of leading spaces for display purposes */
  692       for(cp = line.s, leaspc = 0; n.ui > 0; ++cp, --n.ui)
  693          if(*cp == '\t')
  694             leaspc = (leaspc + 8u) & ~7u;
  695          else if(*cp == ' ')
  696             ++leaspc;
  697          else
  698             break;
  699       for(; n.ui > 0 && spacechar(cp[n.ui - 1]); --n.ui)
  700          ;
  701       if(n.ui == 0)
  702          continue;
  703 
  704       maxlen = n_MAX(maxlen, n.ui);
  705       cp[n.ui++] = '\0';
  706 
  707       /* Is is the closing brace? */
  708       if(*cp == '}')
  709          break;
  710 
  711       if(n_LIKELY(++line_cnt < UI32_MAX)){
  712          struct a_amv_mac_line *amlp;
  713 
  714          llp = n_autorec_alloc(sizeof *llp);
  715          if(ll_head == NULL)
  716             ll_head = llp;
  717          else
  718             ll_tail->ll_next = llp;
  719          ll_tail = llp;
  720          llp->ll_next = NULL;
  721          llp->ll_amlp = amlp = n_alloc(n_VSTRUCT_SIZEOF(struct a_amv_mac_line,
  722                aml_dat) + n.ui);
  723          amlp->aml_len = n.ui -1;
  724          amlp->aml_prespc = leaspc;
  725          memcpy(amlp->aml_dat, cp, n.ui);
  726       }else{
  727          n_err(_("Too much content in %s definition: %s\n"),
  728             (amf & a_AMV_MF_ACCOUNT ? "account" : "macro"), name);
  729          goto jerr;
  730       }
  731    }
  732 
  733    /* Create the new macro */
  734    n.s = strlen(name) +1;
  735    amp = n_alloc(n_VSTRUCT_SIZEOF(struct a_amv_mac, am_name) + n.s);
  736    memset(amp, 0, n_VSTRUCT_SIZEOF(struct a_amv_mac, am_name));
  737    amp->am_maxlen = maxlen;
  738    amp->am_line_cnt = line_cnt;
  739    amp->am_flags = amf;
  740    memcpy(amp->am_name, name, n.s);
  741    /* C99 */{
  742       struct a_amv_mac_line **amlpp;
  743 
  744       amp->am_line_dat = amlpp = n_alloc(sizeof(*amlpp) * ++line_cnt);
  745       for(llp = ll_head; llp != NULL; llp = llp->ll_next)
  746          *amlpp++ = llp->ll_amlp;
  747       *amlpp = NULL;
  748    }
  749 
  750    /* Create entry, replace a yet existing one as necessary */
  751    a_amv_mac_lookup(name, amp, amf | a_AMV_MF_UNDEF);
  752    rv = TRU1;
  753 jleave:
  754    if(line.s != NULL)
  755       n_free(line.s);
  756    NYD2_LEAVE;
  757    return rv;
  758 
  759 jerr:
  760    for(llp = ll_head; llp != NULL; llp = llp->ll_next)
  761       n_free(llp->ll_amlp);
  762    /*
  763     * if(amp != NULL){
  764     *   n_free(amp->am_line_dat);
  765     *   n_free(amp);
  766     *}*/
  767    goto jleave;
  768 }
  769 
  770 static bool_t
  771 a_amv_mac_undef(char const *name, enum a_amv_mac_flags amf){
  772    struct a_amv_mac *amp;
  773    bool_t rv;
  774    NYD2_ENTER;
  775 
  776    rv = TRU1;
  777 
  778    if(n_LIKELY(name[0] != '*' || name[1] != '\0')){
  779       if((amp = a_amv_mac_lookup(name, NULL, amf | a_AMV_MF_UNDEF)) == NULL){
  780          n_err(_("%s not defined: %s\n"),
  781             (amf & a_AMV_MF_ACCOUNT ? "Account" : "Macro"), name);
  782          rv = FAL0;
  783       }
  784    }else{
  785       struct a_amv_mac **ampp, *lamp;
  786 
  787       for(ampp = a_amv_macs; PTRCMP(ampp, <, &a_amv_macs[n_NELEM(a_amv_macs)]);
  788             ++ampp)
  789          for(lamp = NULL, amp = *ampp; amp != NULL;){
  790             if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf){
  791                /* xxx Expensive but rare: be simple */
  792                a_amv_mac_lookup(amp->am_name, NULL, amf | a_AMV_MF_UNDEF);
  793                amp = (lamp == NULL) ? *ampp : lamp->am_next;
  794             }else{
  795                lamp = amp;
  796                amp = amp->am_next;
  797             }
  798          }
  799    }
  800    NYD2_LEAVE;
  801    return rv;
  802 }
  803 
  804 static void
  805 a_amv_mac_free(struct a_amv_mac *amp){
  806    struct a_amv_mac_line **amlpp;
  807    NYD2_ENTER;
  808 
  809    for(amlpp = amp->am_line_dat; *amlpp != NULL; ++amlpp)
  810       n_free(*amlpp);
  811    n_free(amp->am_line_dat);
  812    n_free(amp);
  813    NYD2_LEAVE;
  814 }
  815 
  816 static void
  817 a_amv_lopts_add(struct a_amv_lostack *alp, char const *name,
  818       struct a_amv_var *oavp){
  819    struct a_amv_var *avp;
  820    size_t nl, vl;
  821    NYD2_ENTER;
  822 
  823    /* Propagate unrolling up the stack, as necessary */
  824    assert(alp != NULL);
  825    for(;;){
  826       if(alp->as_loflags & a_AMV_LF_SCOPE_MASK)
  827          break;
  828       if((alp = alp->as_up) == NULL)
  829          goto jleave;
  830    }
  831 
  832    /* Check whether this variable is handled yet XXX Boost: ID check etc.!! */
  833    for(avp = alp->as_lopts; avp != NULL; avp = avp->av_link)
  834       if(!strcmp(avp->av_name, name))
  835          goto jleave;
  836 
  837    nl = strlen(name) +1;
  838    vl = (oavp != NULL) ? strlen(oavp->av_value) +1 : 0;
  839    avp = n_calloc(1, n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + nl + vl);
  840    avp->av_link = alp->as_lopts;
  841    alp->as_lopts = avp;
  842    if(vl != 0){
  843       avp->av_value = &avp->av_name[nl];
  844       avp->av_flags = oavp->av_flags;
  845       memcpy(avp->av_value, oavp->av_value, vl);
  846 #ifdef HAVE_PUTENV
  847       if(oavp->av_env != NULL)
  848          avp->av_env = sstrdup(oavp->av_env);
  849       assert(avp->av_env == NULL ||
  850          (avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED)));
  851 #endif
  852    }
  853    memcpy(avp->av_name, name, nl);
  854 jleave:
  855    NYD2_LEAVE;
  856 }
  857 
  858 static void
  859 a_amv_lopts_unroll(struct a_amv_var **avpp){
  860    struct a_amv_lostack *save_alp;
  861    struct a_amv_var *x, *avp;
  862    NYD2_ENTER;
  863 
  864    avp = *avpp;
  865    *avpp = NULL;
  866 
  867    save_alp = a_amv_lopts;
  868    a_amv_lopts = NULL;
  869    while(avp != NULL){
  870       x = avp;
  871       avp = avp->av_link;
  872       n_PS_ROOT_BLOCK(n_var_vset(x->av_name, (uintptr_t)x->av_value));
  873       n_free(x);
  874    }
  875    a_amv_lopts = save_alp;
  876    NYD2_LEAVE;
  877 }
  878 
  879 static char *
  880 a_amv_var_copy(char const *str){
  881    char *news;
  882    size_t len;
  883    NYD2_ENTER;
  884 
  885    if(*str == '\0')
  886       news = n_UNCONST(n_empty);
  887    else if(str[1] == '\0'){
  888       if(str[0] == '1')
  889          news = n_UNCONST(n_1);
  890       else if(str[0] == '0')
  891          news = n_UNCONST(n_0);
  892       else
  893          goto jheap;
  894    }else if(str[2] == '\0' && str[0] == '-' && str[1] == '1')
  895       news = n_UNCONST(n_m1);
  896    else{
  897 jheap:
  898       len = strlen(str) +1;
  899       news = n_alloc(len);
  900       memcpy(news, str, len);
  901    }
  902    NYD2_LEAVE;
  903    return news;
  904 }
  905 
  906 static void
  907 a_amv_var_free(char *cp){
  908    NYD2_ENTER;
  909    if(cp[0] != '\0' && cp != n_0 && cp != n_1 && cp != n_m1)
  910       n_free(cp);
  911    NYD2_LEAVE;
  912 }
  913 
  914 static bool_t
  915 a_amv_var_check_vips(enum a_amv_var_vip_mode avvm, enum okeys okey,
  916       char const **val){
  917    char const *emsg;
  918    bool_t ok;
  919    NYD2_ENTER;
  920 
  921    ok = TRU1;
  922 
  923    if(avvm == a_AMV_VIP_SET_PRE){
  924       switch(okey){
  925       default:
  926          break;
  927       case ok_v_customhdr:{
  928          char const *vp;
  929          char *buf;
  930          struct n_header_field *hflp, **hflpp, *hfp;
  931          NYD_ENTER;
  932 
  933          buf = savestr(*val);
  934          hflp = NULL;
  935          hflpp = &hflp;
  936 
  937          while((vp = n_strsep_esc(&buf, ',', TRU1)) != NULL){
  938             if(!n_header_add_custom(hflpp, vp, TRU1)){
  939                emsg = N_("Invalid *customhdr* entry: %s\n");
  940                ok = FAL0;
  941                break;
  942             }
  943             hflpp = &(*hflpp)->hf_next;
  944          }
  945 
  946          hflpp = ok ? &n_customhdr_list : &hflp;
  947          while((hfp = *hflpp) != NULL){
  948             *hflpp = hfp->hf_next;
  949             n_free(hfp);
  950          }
  951          if(!ok)
  952             goto jerr;
  953          n_customhdr_list = hflp;
  954       }  break;
  955       case ok_v_from:
  956       case ok_v_sender:{
  957          struct name *np;
  958 
  959          if((np = lextract(*val, GEXTRA | GFULL)) == NULL){
  960 jefrom:
  961             emsg = N_("*from* / *sender*: invalid  address(es): %s\n");
  962             goto jerr;
  963          }else if(okey == ok_v_sender && np->n_flink != NULL){
  964             emsg = N_("*sender*: may not contain multiple addresses: %s\n");
  965             goto jerr;
  966          }else for(; np != NULL; np = np->n_flink)
  967             if(is_addr_invalid(np, EACM_STRICT | EACM_NOLOG | EACM_NONAME))
  968                goto jefrom;
  969       }  break;
  970       case ok_v_HOME:
  971          /* Note this gets called from main.c during initialization, and they
  972           * simply set this to pw_dir as a fallback: don't verify _that_ call.
  973           * See main.c! */
  974          if(!(n_pstate & n_PS_ROOT) && !n_is_dir(*val, TRUM1)){
  975             emsg = N_("$HOME is not a directory or not accessible: %s\n");
  976             goto jerr;
  977          }
  978          break;
  979       case ok_v_hostname:
  980       case ok_v_smtp_hostname:
  981 #ifdef HAVE_IDNA
  982          if(*val != '\0'){
  983             struct n_string cnv;
  984 
  985             n_string_creat_auto(&cnv);
  986             if(!n_idna_to_ascii(&cnv, *val, UIZ_MAX)){
  987                /*n_string_gut(&res);*/
  988                emsg = N_("*hostname*/*smtp_hostname*: "
  989                      "IDNA encoding failed: %s\n");
  990                goto jerr;
  991             }
  992             *val = n_string_cp(&cnv);
  993             /*n_string_drop_ownership(&cnv);*/
  994          }
  995 #endif
  996          break;
  997       case ok_v_quote_chars:{
  998          char c;
  999          char const *cp;
 1000 
 1001          for(cp = *val; (c = *cp++) != '\0';)
 1002             if(!asciichar(c) || blankspacechar(c)){
 1003                ok = FAL0;
 1004                break;
 1005             }
 1006       }  break;
 1007       case ok_v_TMPDIR:
 1008          if(!n_is_dir(*val, TRU1)){
 1009             emsg = N_("$TMPDIR is not a directory or not accessible: %s\n");
 1010             goto jerr;
 1011          }
 1012          break;
 1013       case ok_v_umask:
 1014          if(**val != '\0'){
 1015             ui64_t uib;
 1016 
 1017             n_idec_ui64_cp(&uib, *val, 0, NULL);
 1018             if(uib & ~0777u){ /* (is valid _VF_POSNUM) */
 1019                emsg = N_("Invalid *umask* setting: %s\n");
 1020                goto jerr;
 1021             }
 1022          }
 1023          break;
 1024       }
 1025    }else if(avvm == a_AMV_VIP_SET_POST){
 1026       switch(okey){
 1027       default:
 1028          break;
 1029       case ok_b_ask:
 1030          ok_bset(asksub);
 1031          break;
 1032       case ok_b_debug:
 1033          n_poption |= n_PO_DEBUG;
 1034          break;
 1035       case ok_v_HOME:
 1036          /* Invalidate any resolved folder then, too
 1037           * FALLTHRU */
 1038       case ok_v_folder:
 1039          n_PS_ROOT_BLOCK(ok_vclear(folder_resolved));
 1040          break;
 1041       case ok_v_ifs:{
 1042          char *x_b, *x, c;
 1043          char const *cp;
 1044 
 1045          cp = *val;
 1046          x_b = x = n_autorec_alloc(strlen(cp) +1);
 1047          while((c = *cp++) != '\0')
 1048             if(spacechar(c))
 1049                *x++ = c;
 1050          *x = '\0';
 1051          n_PS_ROOT_BLOCK(ok_vset(ifs_ws, x_b));
 1052       }  break;
 1053 #ifdef HAVE_SETLOCALE
 1054       case ok_v_LANG:
 1055       case ok_v_LC_ALL:
 1056       case ok_v_LC_CTYPE:
 1057          n_locale_init();
 1058          break;
 1059 #endif
 1060       case ok_b_memdebug:
 1061          n_poption |= n_PO_MEMDEBUG;
 1062          break;
 1063       case ok_b_POSIXLY_CORRECT: /* <-> *posix* */
 1064          if(!(n_pstate & n_PS_ROOT))
 1065             n_PS_ROOT_BLOCK(ok_bset(posix));
 1066          break;
 1067       case ok_b_posix: /* <-> $POSIXLY_CORRECT */
 1068          if(!(n_pstate & n_PS_ROOT))
 1069             n_PS_ROOT_BLOCK(ok_bset(POSIXLY_CORRECT));
 1070          break;
 1071       case ok_b_skipemptybody:
 1072          n_poption |= n_PO_E_FLAG;
 1073          break;
 1074       case ok_b_typescript_mode:
 1075          ok_bset(colour_disable);
 1076          ok_bset(line_editor_disable);
 1077          if(!(n_psonce & n_PSO_STARTED))
 1078             ok_bset(termcap_disable);
 1079          break;
 1080       case ok_v_umask:
 1081          if(**val != '\0'){
 1082             ui64_t uib;
 1083 
 1084             n_idec_ui64_cp(&uib, *val, 0, NULL);
 1085             umask((mode_t)uib);
 1086          }
 1087          break;
 1088       case ok_b_verbose:
 1089          n_poption |= (n_poption & n_PO_VERB) ? n_PO_VERBVERB : n_PO_VERB;
 1090          break;
 1091       }
 1092    }else{
 1093       switch(okey){
 1094       default:
 1095          break;
 1096       case ok_b_ask:
 1097          ok_bclear(asksub);
 1098          break;
 1099       case ok_b_debug:
 1100          n_poption &= ~n_PO_DEBUG;
 1101          break;
 1102       case ok_v_customhdr:{
 1103          struct n_header_field *hfp;
 1104 
 1105          while((hfp = n_customhdr_list) != NULL){
 1106             n_customhdr_list = hfp->hf_next;
 1107             n_free(hfp);
 1108          }
 1109       }  break;
 1110       case ok_v_HOME:
 1111          /* Invalidate any resolved folder then, too
 1112           * FALLTHRU */
 1113       case ok_v_folder:
 1114          n_PS_ROOT_BLOCK(ok_vclear(folder_resolved));
 1115          break;
 1116       case ok_b_memdebug:
 1117          n_poption &= ~n_PO_MEMDEBUG;
 1118          break;
 1119       case ok_b_POSIXLY_CORRECT: /* <-> *posix* */
 1120          if(!(n_pstate & n_PS_ROOT))
 1121             n_PS_ROOT_BLOCK(ok_bclear(posix));
 1122          break;
 1123       case ok_b_posix: /* <-> $POSIXLY_CORRECT */
 1124          if(!(n_pstate & n_PS_ROOT))
 1125             n_PS_ROOT_BLOCK(ok_bclear(POSIXLY_CORRECT));
 1126          break;
 1127       case ok_b_skipemptybody:
 1128          n_poption &= ~n_PO_E_FLAG;
 1129          break;
 1130       case ok_b_verbose:
 1131          n_poption &= ~(n_PO_VERB | n_PO_VERBVERB);
 1132          break;
 1133       }
 1134    }
 1135 
 1136 jleave:
 1137    NYD2_LEAVE;
 1138    return ok;
 1139 jerr:
 1140    n_err(V_(emsg), n_shexp_quote_cp(*val, FAL0));
 1141    ok = FAL0;
 1142    goto jleave;
 1143 }
 1144 
 1145 static bool_t
 1146 a_amv_var_check_num(char const *val, bool_t posnum){
 1147    /* TODO The internal/environment  variables which are num= or posnum= should
 1148     * TODO gain special lookup functions, or the return should be void* and
 1149     * TODO castable to integer; i.e. no more strtoX() should be needed.
 1150     * TODO I.e., the result of this function should instead be stored */
 1151    bool_t rv;
 1152    NYD2_ENTER;
 1153 
 1154    rv = TRU1;
 1155 
 1156    if(*val != '\0'){ /* Would be _VF_NOTEMPTY if not allowed */
 1157       ui64_t uib;
 1158       enum n_idec_state ids;
 1159 
 1160       ids = n_idec_cp(&uib, val, 0,
 1161             (n_IDEC_MODE_LIMIT_32BIT |
 1162              (posnum ?  n_IDEC_MODE_SIGNED_TYPE : n_IDEC_MODE_NONE)), NULL);
 1163       if((ids & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1164             ) != n_IDEC_STATE_CONSUMED)
 1165          rv = FAL0;
 1166       /* TODO Unless we store integers we need to look and forbid, because
 1167        * TODO callee may not be able to swallow, e.g., "-1" */
 1168       if(posnum && (ids & n_IDEC_STATE_SEEN_MINUS))
 1169          rv = FAL0;
 1170    }
 1171    NYD2_LEAVE;
 1172    return rv;
 1173 }
 1174 
 1175 static char const *
 1176 a_amv_var_canonify(char const *vn){
 1177    NYD2_ENTER;
 1178    if(!upperchar(*vn)){
 1179       char const *vp;
 1180 
 1181       for(vp = vn; *vp != '\0' && *vp != '@'; ++vp)
 1182          ;
 1183       vn = (*vp == '@') ? i_strdup(vn) : vn;
 1184    }
 1185    NYD2_LEAVE;
 1186    return vn;
 1187 }
 1188 
 1189 static bool_t
 1190 a_amv_var_revlookup(struct a_amv_var_carrier *avcp, char const *name){
 1191    ui32_t hash, i, j;
 1192    struct a_amv_var_map const *avmp;
 1193    char c;
 1194    NYD2_ENTER;
 1195 
 1196    /* It may be a special a.k.a. macro-local or one-letter parameter */
 1197    c = name[0];
 1198    if(n_UNLIKELY(digitchar(c))){
 1199       /* (Inline dec. atoi, ugh) */
 1200       for(j = (ui8_t)c - '0', i = 1;; ++i){
 1201          c = name[i];
 1202          if(c == '\0')
 1203             break;
 1204          if(!digitchar(c))
 1205             goto jno_special_param;
 1206          j = j * 10 + (ui8_t)c - '0';
 1207       }
 1208       if(j <= a_AMV_POSPAR_MAX){
 1209          avcp->avc_special_cat = a_AMV_VSC_POSPAR;
 1210          goto jspecial_param;
 1211       }
 1212    }else if(n_UNLIKELY(name[1] == '\0')){
 1213       switch(c){
 1214       case '?':
 1215       case '!':
 1216          avcp->avc_special_cat = a_AMV_VSC_GLOBAL;
 1217          j = (c == '?') ? a_AMV_VST_QM : a_AMV_VST_EM;
 1218          goto jspecial_param;
 1219       case '^':
 1220          goto jmultiplex;
 1221       case '*':
 1222          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1223          j = a_AMV_VST_STAR;
 1224          goto jspecial_param;
 1225       case '@':
 1226          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1227          j = a_AMV_VST_AT;
 1228          goto jspecial_param;
 1229       case '#':
 1230          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1231          j = a_AMV_VST_NOSIGN;
 1232          goto jspecial_param;
 1233       default:
 1234          break;
 1235       }
 1236    }else if(c == '^'){
 1237 jmultiplex:
 1238       avcp->avc_special_cat = a_AMV_VSC_MULTIPLEX;
 1239       j = a_AMV_VST_CACC;
 1240       goto jspecial_param;
 1241    }
 1242 
 1243    /* Normal reverse lookup, walk over the hashtable */
 1244 jno_special_param:
 1245    avcp->avc_special_cat = a_AMV_VSC_NONE;
 1246    avcp->avc_name = name = a_amv_var_canonify(name);
 1247    avcp->avc_hash = hash = a_AMV_NAME2HASH(name);
 1248 
 1249    for(i = hash % a_AMV_VAR_REV_PRIME, j = 0; j <= a_AMV_VAR_REV_LONGEST; ++j){
 1250       ui32_t x;
 1251 
 1252       if((x = a_amv_var_revmap[i]) == a_AMV_VAR_REV_ILL)
 1253          break;
 1254 
 1255       avmp = &a_amv_var_map[x];
 1256       if(avmp->avm_hash == hash &&
 1257             !strcmp(&a_amv_var_names[avmp->avm_keyoff], name)){
 1258          avcp->avc_map = avmp;
 1259          avcp->avc_okey = (enum okeys)x;
 1260          goto jleave;
 1261       }
 1262 
 1263       if(++i == a_AMV_VAR_REV_PRIME){
 1264 #ifdef a_AMV_VAR_REV_WRAPAROUND
 1265          i = 0;
 1266 #else
 1267          break;
 1268 #endif
 1269       }
 1270    }
 1271 
 1272    avcp->avc_map = NULL;
 1273    avcp = NULL;
 1274 jleave:
 1275    assert(avcp == NULL || avcp->avc_map != NULL ||
 1276       avcp->avc_special_cat == a_AMV_VSC_NONE);
 1277    NYD2_LEAVE;
 1278    return (avcp != NULL);
 1279 
 1280    /* All these are mapped to *--special-param* */
 1281 jspecial_param:
 1282    avcp->avc_name = name;
 1283    avcp->avc_special_prop = (ui16_t)j;
 1284    avmp = &a_amv_var_map[a_AMV_VAR__SPECIAL_PARAM_MAP_IDX];
 1285    avcp->avc_hash = avmp->avm_hash;
 1286    avcp->avc_map = avmp;
 1287    avcp->avc_okey = ok_v___special_param;
 1288    goto jleave;
 1289 }
 1290 
 1291 static bool_t
 1292 a_amv_var_lookup(struct a_amv_var_carrier *avcp,
 1293       enum a_amv_var_lookup_flags avlf){
 1294    size_t i;
 1295    char const *cp;
 1296    struct a_amv_var_map const *avmp;
 1297    struct a_amv_var *avp;
 1298    NYD2_ENTER;
 1299 
 1300    assert(!(avlf & a_AMV_VLOOK_LOCAL_ONLY) || (avlf & a_AMV_VLOOK_LOCAL));
 1301    assert(!(avlf & a_AMV_VLOOK_I3VAL_NONEW_REPORT) ||
 1302       (avlf & a_AMV_VLOOK_I3VAL_NONEW));
 1303 
 1304    /* C99 */{
 1305       struct a_amv_var **avpp, *lavp;
 1306 
 1307       avcp->avc_prime = a_AMV_HASH2PRIME(avcp->avc_hash);
 1308 
 1309       /* Optionally macro-`local' variables first */
 1310       if(avlf & a_AMV_VLOOK_LOCAL){
 1311          if(a_amv_lopts != NULL &&
 1312                (avpp = *a_amv_lopts->as_amcap->amca_local_vars) != NULL){
 1313             avpp += avcp->avc_prime;
 1314 
 1315             for(lavp = NULL, avp = *avpp; avp != NULL;
 1316                   lavp = avp, avp = avp->av_link)
 1317                if(!strcmp(avp->av_name, avcp->avc_name)){
 1318                   /* Relink as head, hope it "sorts on usage" over time.
 1319                    * The code relies on this behaviour! */
 1320                   if(lavp != NULL){
 1321                      lavp->av_link = avp->av_link;
 1322                      avp->av_link = *avpp;
 1323                      *avpp = avp;
 1324                   }
 1325                   goto jleave;
 1326                }
 1327          }
 1328 
 1329          if(avlf & a_AMV_VLOOK_LOCAL_ONLY)
 1330             goto jerr;
 1331       }
 1332 
 1333       /* Global variable map */
 1334       avpp = &a_amv_vars[avcp->avc_prime];
 1335 
 1336       for(lavp = NULL, avp = *avpp; avp != NULL; lavp = avp, avp = avp->av_link)
 1337          if(!strcmp(avp->av_name, avcp->avc_name)){
 1338             /* Relink as head, hope it "sorts on usage" over time.
 1339              * The code relies on this behaviour! */
 1340             if(lavp != NULL){
 1341                lavp->av_link = avp->av_link;
 1342                avp->av_link = *avpp;
 1343                *avpp = avp;
 1344             }
 1345 
 1346             /* If this setting has been established via -S and we still have
 1347              * not reached the _STARTED_CONFIG program state, it may have been
 1348              * an explicit "clearance" that is to be treated as unset.
 1349              * Because that is a special condition that (has been hacked in
 1350              * later and) needs to be encapsulated in lower levels, but not
 1351              * of interest if _set() or _clear() called us */
 1352             switch(avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK){
 1353             case a_AMV_VF_EXT_FROZEN | a_AMV_VF_EXT_FROZEN_UNSET:
 1354                if(!(avlf & a_AMV_VLOOK_I3VAL_NONEW)){
 1355                   avcp->avc_var = avp;
 1356                   avp = NULL;
 1357                   goto j_leave;
 1358                }
 1359                /* FALLTHRU */
 1360             default:
 1361                break;
 1362             }
 1363             goto jleave;
 1364          }
 1365    }
 1366 
 1367    /* If this is not an assembled variable we need to consider some special
 1368     * initialization cases and eventually create the variable anew */
 1369    if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
 1370       /* Does it have an import-from-environment flag? */
 1371       if(n_UNLIKELY((avmp->avm_flags & (a_AMV_VF_IMPORT | a_AMV_VF_ENV)) != 0)){
 1372          if(n_LIKELY((cp = getenv(avcp->avc_name)) != NULL)){
 1373             /* May be better not to use that one, though? */
 1374             /* TODO Outsource the tests into a _shared_ test function! */
 1375             bool_t isempty, isbltin;
 1376 
 1377             isempty = (*cp == '\0' &&
 1378                   (avmp->avm_flags & a_AMV_VF_NOTEMPTY) != 0);
 1379             isbltin = ((avmp->avm_flags & (a_AMV_VF_I3VAL | a_AMV_VF_DEFVAL)
 1380                   ) != 0);
 1381 
 1382             if(n_UNLIKELY(isempty)){
 1383                n_err(_("Environment variable must not be empty: %s\n"),
 1384                   avcp->avc_name);
 1385                if(!isbltin)
 1386                   goto jerr;
 1387             }else if(n_LIKELY(*cp != '\0')){
 1388                if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
 1389                      !a_amv_var_check_num(cp, FAL0))){
 1390                   n_err(_("Environment variable value not a number "
 1391                      "or out of range: %s\n"), avcp->avc_name);
 1392                   goto jerr;
 1393                }
 1394                if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
 1395                      !a_amv_var_check_num(cp, TRU1))){
 1396                   n_err(_("Environment variable value not a number, "
 1397                      "negative or out of range: %s\n"), avcp->avc_name);
 1398                   goto jerr;
 1399                }
 1400                goto jnewval;
 1401             }else
 1402                goto jnewval;
 1403          }
 1404       }
 1405 
 1406       /* A first-time init switch is to be handled now and here */
 1407       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_I3VAL) != 0)){
 1408          static struct a_amv_var_defval const **arr,
 1409             *arr_base[a_AMV_VAR_I3VALS_CNT +1];
 1410 
 1411          if(arr == NULL){
 1412             arr = &arr_base[0];
 1413             arr[i = a_AMV_VAR_I3VALS_CNT] = NULL;
 1414             while(i-- > 0)
 1415                arr[i] = &a_amv_var_i3vals[i];
 1416          }
 1417 
 1418          for(i = 0; arr[i] != NULL; ++i)
 1419             if(arr[i]->avdv_okey == avcp->avc_okey){
 1420                cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_1
 1421                      : arr[i]->avdv_value;
 1422                /* Remove this entry, hope entire block becomes no-op asap */
 1423                do
 1424                   arr[i] = arr[i + 1];
 1425                while(arr[i++] != NULL);
 1426 
 1427                if(!(avlf & a_AMV_VLOOK_I3VAL_NONEW))
 1428                   goto jnewval;
 1429                if(avlf & a_AMV_VLOOK_I3VAL_NONEW_REPORT)
 1430                   avp = (struct a_amv_var*)-1;
 1431                goto jleave;
 1432             }
 1433       }
 1434 
 1435       /* */
 1436 jdefval:
 1437       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_DEFVAL) != 0){
 1438          for(i = 0; i < a_AMV_VAR_DEFVALS_CNT; ++i)
 1439             if(a_amv_var_defvals[i].avdv_okey == avcp->avc_okey){
 1440                cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_1
 1441                      : a_amv_var_defvals[i].avdv_value;
 1442                goto jnewval;
 1443             }
 1444       }
 1445 
 1446       /* The virtual variables */
 1447       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIRT) != 0)){
 1448          for(i = 0; i < a_AMV_VAR_VIRTS_CNT; ++i)
 1449             if(a_amv_var_virts[i].avv_okey == avcp->avc_okey){
 1450                avp = n_UNCONST(a_amv_var_virts[i].avv_var);
 1451                goto jleave;
 1452             }
 1453          /* Not reached */
 1454       }
 1455    }
 1456 
 1457 jerr:
 1458    avp = NULL;
 1459 jleave:
 1460    avcp->avc_var = avp;
 1461 j_leave:
 1462    NYD2_LEAVE;
 1463    return (avp != NULL);
 1464 
 1465 jnewval:
 1466    /* E.g., $TMPDIR may be set to non-existent, so we need to be able to catch
 1467     * that and redirect to a possible default value */
 1468    if((avmp->avm_flags & a_AMV_VF_VIP) &&
 1469          !a_amv_var_check_vips(a_AMV_VIP_SET_PRE, avcp->avc_okey, &cp)){
 1470 #ifdef HAVE_SETENV
 1471       if(avmp->avm_flags & (a_AMV_VF_IMPORT | a_AMV_VF_ENV))
 1472          unsetenv(avcp->avc_name);
 1473 #endif
 1474       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_DEFVAL) != 0)
 1475          goto jdefval;
 1476       goto jerr;
 1477    }else{
 1478       struct a_amv_var **avpp;
 1479       size_t l;
 1480 
 1481       l = strlen(avcp->avc_name) +1;
 1482       avcp->avc_var =
 1483       avp = n_calloc(1, n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1484       avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
 1485       *avpp = avp;
 1486       avp->av_flags = avmp->avm_flags;
 1487       avp->av_value = a_amv_var_copy(cp);
 1488       memcpy(avp->av_name, avcp->avc_name, l);
 1489 
 1490       if(avp->av_flags & a_AMV_VF_ENV)
 1491          a_amv_var__putenv(avcp, avp);
 1492       if(avmp->avm_flags & a_AMV_VF_VIP)
 1493          a_amv_var_check_vips(a_AMV_VIP_SET_POST, avcp->avc_okey, &cp);
 1494       goto jleave;
 1495    }
 1496 }
 1497 
 1498 static char const *
 1499 a_amv_var_vsc_global(struct a_amv_var_carrier *avcp){
 1500    char itoabuf[32];
 1501    char const *rv;
 1502    si32_t *ep;
 1503    struct a_amv_var_map const *avmp;
 1504    NYD2_ENTER;
 1505 
 1506    /* Not function local, TODO but lazy evaluted for now */
 1507    if(avcp->avc_special_prop == a_AMV_VST_QM){
 1508       avmp = &a_amv_var_map[a_AMV_VAR__QM_MAP_IDX];
 1509       avcp->avc_okey = ok_v___qm;
 1510       ep = &n_pstate_ex_no;
 1511    }else{
 1512       avmp = &a_amv_var_map[a_AMV_VAR__EM_MAP_IDX];
 1513       avcp->avc_okey = ok_v___em;
 1514       ep = &n_pstate_err_no;
 1515    }
 1516 
 1517    /* XXX Oh heaven, we are responsible to ensure that $?/! is up-to-date
 1518     * TODO we could num=1 ok_v___[qe]m, but the thing is still a string
 1519     * TODO and thus conversion takes places over and over again; also
 1520     * TODO for now that would have to occur before we set _that_ value
 1521     * TODO so let's special treat it until we store ints as such */
 1522    switch(*ep){
 1523    case 0: rv = n_0; break;
 1524    case 1: rv = n_1; break;
 1525    default:
 1526       snprintf(itoabuf, sizeof itoabuf, "%d", *ep);
 1527       rv = itoabuf;
 1528       break;
 1529    }
 1530    n_PS_ROOT_BLOCK(n_var_okset(avcp->avc_okey, (uintptr_t)rv));
 1531 
 1532    avcp->avc_hash = avmp->avm_hash;
 1533    avcp->avc_map = avmp;
 1534    rv = a_amv_var_lookup(avcp, a_AMV_VLOOK_NONE)
 1535          ? avcp->avc_var->av_value : NULL;
 1536    NYD2_LEAVE;
 1537    return rv;
 1538 }
 1539 
 1540 static char const *
 1541 a_amv_var_vsc_multiplex(struct a_amv_var_carrier *avcp){
 1542    char itoabuf[32];
 1543    si32_t e;
 1544    size_t i;
 1545    char const *rv;
 1546    NYD2_ENTER;
 1547 
 1548    i = strlen(rv = &avcp->avc_name[1]);
 1549 
 1550    /* ERR, ERRDOC, ERRNAME, plus *-NAME variants */
 1551    if(rv[0] == 'E' && i >= 3 && rv[1] == 'R' && rv[2] == 'R'){
 1552       if(i == 3){
 1553          e = n_pstate_err_no;
 1554          goto jeno;
 1555       }else if(rv[3] == '-'){
 1556          e = n_err_from_name(&rv[4]);
 1557 jeno:
 1558          switch(e){
 1559          case 0: rv = n_0; break;
 1560          case 1: rv = n_1; break;
 1561          default:
 1562             snprintf(itoabuf, sizeof itoabuf, "%d", e);
 1563             rv = savestr(itoabuf); /* XXX yet, cannot do numbers */
 1564             break;
 1565          }
 1566          goto jleave;
 1567       }else if(i >= 6){
 1568          if(!memcmp(&rv[3], "DOC", 3)){
 1569             rv += 6;
 1570             switch(*rv){
 1571             case '\0': e = n_pstate_err_no; break;
 1572             case '-': e = n_err_from_name(&rv[1]); break;
 1573             default: goto jerr;
 1574             }
 1575             rv = n_err_to_doc(e);
 1576             goto jleave;
 1577          }else if(i >= 7 && !memcmp(&rv[3], "NAME", 4)){
 1578             rv += 7;
 1579             switch(*rv){
 1580             case '\0': e = n_pstate_err_no; break;
 1581             case '-': e = n_err_from_name(&rv[1]); break;
 1582             default: goto jerr;
 1583             }
 1584             rv = n_err_to_name(e);
 1585             goto jleave;
 1586          }
 1587       }
 1588    }
 1589 
 1590 jerr:
 1591    rv = NULL;
 1592 jleave:
 1593    NYD2_LEAVE;
 1594    return rv;
 1595 }
 1596 
 1597 static char const *
 1598 a_amv_var_vsc_pospar(struct a_amv_var_carrier *avcp){
 1599    size_t i, j;
 1600    ui16_t argc;
 1601    char const *rv, **argv;
 1602    NYD2_ENTER;
 1603 
 1604    rv = NULL;
 1605 
 1606    /* If in a macro/xy.. */
 1607    if(a_amv_lopts != NULL){
 1608       bool_t ismacky;
 1609       struct a_amv_mac_call_args *amcap;
 1610 
 1611       amcap = a_amv_lopts->as_amcap;
 1612       argc = amcap->amca_pospar.app_count;
 1613       argv = amcap->amca_pospar.app_dat;
 1614       argv += amcap->amca_pospar.app_idx;
 1615 
 1616       /* ..in a `call'ed macro only, to be exact.  Or in a_AMV_MACKY_MACK */
 1617       if(!(ismacky = (amcap->amca_amp == a_AMV_MACKY_MACK)) &&
 1618             (amcap->amca_ps_hook_mask ||
 1619              (assert(amcap->amca_amp != NULL),
 1620               (amcap->amca_amp->am_flags & a_AMV_MF_TYPE_MASK
 1621                ) == a_AMV_MF_ACCOUNT)))
 1622          goto jleave;
 1623 
 1624       if(avcp->avc_special_cat == a_AMV_VSC_POSPAR){
 1625          if(avcp->avc_special_prop > 0){
 1626             if(argc >= avcp->avc_special_prop)
 1627                rv = argv[avcp->avc_special_prop - 1];
 1628          }else if(ismacky)
 1629             rv = amcap->amca_name;
 1630          else
 1631             rv = (a_amv_lopts->as_up != NULL
 1632                   ? a_amv_lopts->as_up->as_amcap->amca_name : n_empty);
 1633          goto jleave;
 1634       }
 1635       /* MACKY_MACK doesn't know about [*@#] */
 1636       /*else*/ if(ismacky){
 1637          if(n_poption & n_PO_D_V)
 1638             n_err(_("Cannot use $*/$@/$# in this context: %s\n"),
 1639                n_shexp_quote_cp(avcp->avc_name, FAL0));
 1640          goto jleave;
 1641       }
 1642    }else{
 1643       argc = a_amv_pospar.app_count;
 1644       argv = a_amv_pospar.app_dat;
 1645       argv += a_amv_pospar.app_idx;
 1646 
 1647       if(avcp->avc_special_cat == a_AMV_VSC_POSPAR){
 1648          if(avcp->avc_special_prop > 0){
 1649             if(argc >= avcp->avc_special_prop)
 1650                rv = argv[avcp->avc_special_prop - 1];
 1651          }else
 1652             rv = n_progname;
 1653          goto jleave;
 1654       }
 1655    }
 1656 
 1657    switch(avcp->avc_special_prop){ /* XXX OPTIMIZE */
 1658    case a_AMV_VST_STAR:{
 1659       char sep;
 1660 
 1661       sep = *ok_vlook(ifs);
 1662       if(0){
 1663    case a_AMV_VST_AT:
 1664          sep = ' ';
 1665       }
 1666       for(i = j = 0; i < argc; ++i)
 1667          j += strlen(argv[i]) + 1;
 1668       if(j == 0)
 1669          rv = n_empty;
 1670       else{
 1671          char *cp;
 1672 
 1673          rv = cp = n_autorec_alloc(j);
 1674          for(i = j = 0; i < argc; ++i){
 1675             j = strlen(argv[i]);
 1676             memcpy(cp, argv[i], j);
 1677             cp += j;
 1678             if(sep != '\0')
 1679                *cp++ = sep;
 1680          }
 1681          if(sep != '\0')
 1682             --cp;
 1683          *cp = '\0';
 1684       }
 1685       }break;
 1686    case a_AMV_VST_NOSIGN:{
 1687       char *cp;
 1688 
 1689       rv = cp = n_autorec_alloc(sizeof("65535"));
 1690       snprintf(cp, sizeof("65535"), "%hu", argc);
 1691       }break;
 1692    default:
 1693       rv = n_empty;
 1694       break;
 1695    }
 1696 jleave:
 1697    NYD2_LEAVE;
 1698    return rv;
 1699 }
 1700 
 1701 static bool_t
 1702 a_amv_var_set(struct a_amv_var_carrier *avcp, char const *value,
 1703       enum a_amv_var_setclr_flags avscf){
 1704    struct a_amv_var *avp;
 1705    char *oval;
 1706    struct a_amv_var_map const *avmp;
 1707    bool_t rv;
 1708    NYD2_ENTER;
 1709 
 1710    if(value == NULL){
 1711       rv = a_amv_var_clear(avcp, avscf);
 1712       goto jleave;
 1713    }
 1714 
 1715    if((avmp = avcp->avc_map) != NULL){
 1716       rv = FAL0;
 1717 
 1718       /* Validity checks */
 1719       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_RDONLY) != 0 &&
 1720             !(n_pstate & n_PS_ROOT))){
 1721          value = N_("Variable is read-only: %s\n");
 1722          goto jeavmp;
 1723       }
 1724       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NOTEMPTY) && *value == '\0')){
 1725          value = N_("Variable must not be empty: %s\n");
 1726          goto jeavmp;
 1727       }
 1728       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
 1729             !a_amv_var_check_num(value, FAL0))){
 1730          value = N_("Variable value not a number or out of range: %s\n");
 1731          goto jeavmp;
 1732       }
 1733       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
 1734             !a_amv_var_check_num(value, TRU1))){
 1735          value = _("Variable value not a number, negative, "
 1736                "or out of range: %s\n");
 1737          goto jeavmp;
 1738       }
 1739 
 1740       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_IMPORT) != 0 &&
 1741             !(n_psonce & n_PSO_STARTED) && !(n_pstate & n_PS_ROOT))){
 1742          value = N_("Variable cannot be set in a resource file: %s\n");
 1743          goto jeavmp;
 1744       }
 1745 
 1746       /* Any more complicated inter-dependency? */
 1747       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIP) != 0 &&
 1748             !a_amv_var_check_vips(a_AMV_VIP_SET_PRE, avcp->avc_okey, &value))){
 1749          value = N_("Assignment of variable aborted: %s\n");
 1750 jeavmp:
 1751          n_err(V_(value), avcp->avc_name);
 1752          goto jleave;
 1753       }
 1754 
 1755       /* Transformations */
 1756       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_LOWER)){
 1757          char c;
 1758 
 1759          oval = savestr(value);
 1760          value = oval;
 1761          for(; (c = *oval) != '\0'; ++oval)
 1762             *oval = lowerconv(c);
 1763       }
 1764 
 1765       /* Obsoletion warning */
 1766       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_OBSOLETE) != 0))
 1767          n_OBSOLETE2(_("obsoleted variable"), avcp->avc_name);
 1768    }
 1769 
 1770    /* Lookup possibly existing var.  For */
 1771 
 1772    rv = TRU1;
 1773    a_amv_var_lookup(avcp, (a_AMV_VLOOK_I3VAL_NONEW |
 1774          ((avscf & a_AMV_VSETCLR_LOCAL)
 1775           ? (a_AMV_VLOOK_LOCAL | a_AMV_VLOOK_LOCAL_ONLY) : 0)));
 1776    avp = avcp->avc_var;
 1777 
 1778    /* A `local' setting is never covered by `localopts' nor frozen */
 1779    if(avscf & a_AMV_VSETCLR_LOCAL)
 1780       goto jislocal;
 1781 
 1782    /* If this setting had been established via -S and we still have not reached
 1783     * the _STARTED_CONFIG program state, silently ignore request! */
 1784    if(n_UNLIKELY(avp != NULL) &&
 1785          n_UNLIKELY((avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK) != 0)){
 1786       if(!(n_psonce & n_PSO_STARTED_CONFIG)){
 1787          if((n_pstate & n_PS_ROOT) ||
 1788                (!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1789                 (n_poption & n_PO_S_FLAG_TEMPORARY)))
 1790             goto joval_and_go;
 1791          if(n_poption & n_PO_D_VV)
 1792             n_err(_("Temporarily frozen by -S, not `set'ing: %s\n"),
 1793                avcp->avc_name);
 1794          goto jleave;
 1795       }
 1796 
 1797       /* Otherwise, if -S freezing was an `unset' request, be very simple and
 1798        * avoid tampering with that very special case we are not really prepared
 1799        * for just one more line of code: throw the old thing away! */
 1800       if(!(avp->av_flags & a_AMV_VF_EXT_FROZEN_UNSET))
 1801          avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 1802       else{
 1803          assert(avp->av_value == n_empty);
 1804          a_amv_vars[avcp->avc_prime] = avp->av_link;
 1805          n_free(avp);
 1806          avcp->avc_var = avp = NULL;
 1807       }
 1808    }
 1809 
 1810    /* Optionally cover by `localopts' */
 1811    if(n_UNLIKELY(a_amv_lopts != NULL) &&
 1812          (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
 1813       a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
 1814 
 1815 jislocal:
 1816    if(avp != NULL)
 1817 joval_and_go:
 1818       oval = avp->av_value;
 1819    else{
 1820       size_t l;
 1821       struct a_amv_var **avpp;
 1822 
 1823       if(avscf & a_AMV_VSETCLR_LOCAL){
 1824          if((avpp = *a_amv_lopts->as_amcap->amca_local_vars) == NULL)
 1825             avpp = *(a_amv_lopts->as_amcap->amca_local_vars =
 1826                   n_calloc(1, sizeof(*a_amv_lopts->as_amcap->amca_local_vars)));
 1827          avpp += avcp->avc_prime;
 1828       }else
 1829          avpp = &a_amv_vars[avcp->avc_prime];
 1830 
 1831       l = strlen(avcp->avc_name) +1;
 1832       avcp->avc_var = avp = n_calloc(1,
 1833             n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1834       avp->av_link = *avpp;
 1835       *avpp = avp;
 1836       avp->av_flags = (avscf & a_AMV_VSETCLR_LOCAL)
 1837             ? a_AMV_VF_NOLOPTS | a_AMV_VF_EXT_LOCAL
 1838             : (avmp != NULL) ? avmp->avm_flags : 0;
 1839       memcpy(avp->av_name, avcp->avc_name, l);
 1840       oval = n_UNCONST(n_empty);
 1841    }
 1842 
 1843    if(avmp == NULL)
 1844       avp->av_value = a_amv_var_copy(value);
 1845    else{
 1846       assert(!(avscf & a_AMV_VSETCLR_LOCAL));
 1847       /* Via `set' etc. the user may give even boolean options non-boolean
 1848        * values, ignore that and force boolean */
 1849       if(!(avp->av_flags & a_AMV_VF_BOOL))
 1850          avp->av_value = a_amv_var_copy(value);
 1851       else{
 1852          if(!(n_pstate & n_PS_ROOT) && (n_poption & n_PO_D_V) &&
 1853                *value != '\0')
 1854             n_err(_("Ignoring value of boolean variable: %s: %s\n"),
 1855                avcp->avc_name, value);
 1856          avp->av_value = n_UNCONST(n_1);
 1857       }
 1858    }
 1859 
 1860    /* A `local' setting can skip all the crude special things */
 1861    if(!(avscf & a_AMV_VSETCLR_LOCAL)){
 1862       if((avscf & a_AMV_VSETCLR_ENV) && !(avp->av_flags & a_AMV_VF_ENV))
 1863          avp->av_flags |= a_AMV_VF_EXT_LINKED;
 1864       if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 1865          rv = a_amv_var__putenv(avcp, avp);
 1866       if(avp->av_flags & a_AMV_VF_VIP)
 1867          a_amv_var_check_vips(a_AMV_VIP_SET_POST, avcp->avc_okey, &value);
 1868 
 1869       avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 1870       if(!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1871             (n_poption & n_PO_S_FLAG_TEMPORARY) != 0)
 1872          avp->av_flags |= a_AMV_VF_EXT_FROZEN;
 1873    }
 1874 
 1875    a_amv_var_free(oval);
 1876 jleave:
 1877    NYD2_LEAVE;
 1878    return rv;
 1879 }
 1880 
 1881 static bool_t
 1882 a_amv_var__putenv(struct a_amv_var_carrier *avcp, struct a_amv_var *avp){
 1883 #ifndef HAVE_SETENV
 1884    char *cp;
 1885 #endif
 1886    bool_t rv;
 1887    NYD2_ENTER;
 1888 
 1889 #ifdef HAVE_SETENV
 1890    rv = (setenv(avcp->avc_name, avp->av_value, 1) == 0);
 1891 #else
 1892    cp = sstrdup(savecatsep(avcp->avc_name, '=', avp->av_value));
 1893 
 1894    if((rv = (putenv(cp) == 0))){
 1895       char *ocp;
 1896 
 1897       ocp = avp->av_env;
 1898       avp->av_env = cp;
 1899       cp = ocp;
 1900    }
 1901 
 1902    if(cp != NULL)
 1903       n_free(cp);
 1904 #endif
 1905    NYD2_LEAVE;
 1906    return rv;
 1907 }
 1908 
 1909 static bool_t
 1910 a_amv_var_clear(struct a_amv_var_carrier *avcp,
 1911       enum a_amv_var_setclr_flags avscf){
 1912    struct a_amv_var **avpp, *avp;
 1913    struct a_amv_var_map const *avmp;
 1914    bool_t rv;
 1915    NYD2_ENTER;
 1916 
 1917    rv = FAL0;
 1918 
 1919    if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
 1920       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NODEL) != 0 &&
 1921             !(n_pstate & n_PS_ROOT))){
 1922          n_err(_("Variable may not be unset: %s\n"), avcp->avc_name);
 1923          goto jleave;
 1924       }
 1925       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIP) != 0 &&
 1926             !a_amv_var_check_vips(a_AMV_VIP_CLEAR, avcp->avc_okey, NULL))){
 1927          n_err(_("Clearance of variable aborted: %s\n"), avcp->avc_name);
 1928          goto jleave;
 1929       }
 1930    }
 1931 
 1932    rv = TRU1;
 1933 
 1934    if(n_UNLIKELY(!a_amv_var_lookup(avcp,
 1935          (((avscf & a_AMV_VSETCLR_LOCAL)
 1936             ? (a_AMV_VLOOK_LOCAL | a_AMV_VLOOK_LOCAL_ONLY) : 0) |
 1937           a_AMV_VLOOK_I3VAL_NONEW | a_AMV_VLOOK_I3VAL_NONEW_REPORT)))){
 1938       assert(avcp->avc_var == NULL);
 1939       /* This may be a clearance request from the command line, via -S, and we
 1940        * need to keep track of that!  Unfortunately we are not prepared for
 1941        * this, really, so we need to create a fake entry that is known and
 1942        * handled correctly by the lowermost variable layer!
 1943        * However, all this cannot happen for plain unset of `local' variables */
 1944       if(avscf & a_AMV_VSETCLR_LOCAL)
 1945          goto jleave;
 1946       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT)) &&
 1947             (n_poption & n_PO_S_FLAG_TEMPORARY)) Jfreeze:{
 1948          size_t l;
 1949 
 1950          l = strlen(avcp->avc_name) +1;
 1951          avp = n_calloc(1, n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1952          avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
 1953          *avpp = avp;
 1954          avp->av_value = n_UNCONST(n_empty); /* Sth. covered by _var_free()! */
 1955          avp->av_flags = (avmp != NULL ? avmp->avm_flags : 0) |
 1956                a_AMV_VF_EXT_FROZEN | a_AMV_VF_EXT_FROZEN_UNSET;
 1957          memcpy(avp->av_name, avcp->avc_name, l);
 1958 
 1959          if((avscf & a_AMV_VSETCLR_ENV) ||
 1960                (avmp != NULL && (avmp->avm_flags & a_AMV_VF_ENV)))
 1961             a_amv_var__clearenv(avcp->avc_name, NULL);
 1962       }else if(avscf & a_AMV_VSETCLR_ENV){
 1963 jforce_env:
 1964          if(!(rv = a_amv_var__clearenv(avcp->avc_name, NULL)))
 1965             goto jerr_env_unset;
 1966       }else{
 1967          /* TODO "cannot unset undefined variable" not echoed in "ROBOT" state,
 1968           * TODO should only be like that with "ignerr"! */
 1969 jerr_env_unset:
 1970          if(!(n_pstate & (n_PS_ROOT | n_PS_ROBOT)) && (n_poption & n_PO_D_V))
 1971             n_err(_("Cannot unset undefined variable: %s\n"), avcp->avc_name);
 1972       }
 1973       goto jleave;
 1974    }else if((avp = avcp->avc_var) == (struct a_amv_var*)-1){
 1975       /* Clearance request from command line, via -S?  As above.. */
 1976       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1977             (n_poption & n_PO_S_FLAG_TEMPORARY) != 0))
 1978          goto Jfreeze;
 1979       avcp->avc_var = NULL;
 1980       if(avscf & a_AMV_VSETCLR_ENV)
 1981          goto jforce_env;
 1982       goto jleave;
 1983    }
 1984    assert(avcp->avc_var != NULL);
 1985 
 1986    /* `local' variables bypass "frozen" checks and `localopts' coverage etc. */
 1987    if(avp->av_flags & a_AMV_VF_EXT_LOCAL)
 1988       goto jdefault_path;
 1989 
 1990    /* If this setting has been established via -S and we still have not reached
 1991     * the _STARTED_CONFIG program state, silently ignore request!
 1992     * XXX All this is very complicated for the tenth of a second */
 1993    /*else*/ if(n_UNLIKELY((avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK) != 0)){
 1994       if(!(n_psonce & n_PSO_STARTED_CONFIG)){
 1995          if((n_pstate & n_PS_ROOT) ||
 1996                (!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1997                 (n_poption & n_PO_S_FLAG_TEMPORARY))){
 1998             /* Be aware this may turn a set into an unset! */
 1999             if(!(avp->av_flags & a_AMV_VF_EXT_FROZEN_UNSET)){
 2000                if(avp->av_flags & a_AMV_VF_DEFVAL)
 2001                   goto jdefault_path;
 2002                a_amv_var_free(avp->av_value);
 2003                avp->av_flags |= a_AMV_VF_EXT_FROZEN_UNSET;
 2004                avp->av_value = n_UNCONST(n_empty); /* _var_free() covered */
 2005                if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 2006                   goto jforce_env;
 2007             }
 2008             goto jleave;
 2009          }
 2010          if(n_poption & n_PO_D_VV)
 2011             n_err(_("Temporarily frozen by -S, not `unset'ting: %s\n"),
 2012                avcp->avc_name);
 2013          goto jleave;
 2014       }
 2015       avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 2016    }
 2017 
 2018    if(n_UNLIKELY(a_amv_lopts != NULL) &&
 2019          (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
 2020       a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
 2021 
 2022 jdefault_path:
 2023    assert(avp == avcp->avc_var);
 2024    avcp->avc_var = NULL;
 2025    avpp = &((avp->av_flags & a_AMV_VF_EXT_LOCAL)
 2026          ? *a_amv_lopts->as_amcap->amca_local_vars : a_amv_vars
 2027          )[avcp->avc_prime];
 2028    assert(*avpp == avp); /* (always listhead after lookup()) */
 2029    *avpp = (*avpp)->av_link;
 2030 
 2031    if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 2032       rv = a_amv_var__clearenv(avp->av_name, avp);
 2033    a_amv_var_free(avp->av_value);
 2034    n_free(avp);
 2035 
 2036    /* XXX Fun part, extremely simple-minded for now: if this variable has
 2037     * XXX a default value, immediately reinstantiate it!  TODO Heh? */
 2038    if(n_UNLIKELY(avmp != NULL && (avmp->avm_flags & a_AMV_VF_DEFVAL) != 0)){
 2039       a_amv_var_lookup(avcp, a_AMV_VLOOK_I3VAL_NONEW);
 2040       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT)) &&
 2041             (n_poption & n_PO_S_FLAG_TEMPORARY))
 2042          avp->av_flags |= a_AMV_VF_EXT_FROZEN;
 2043    }
 2044 jleave:
 2045    NYD2_LEAVE;
 2046    return rv;
 2047 }
 2048 
 2049 static bool_t
 2050 a_amv_var__clearenv(char const *name, struct a_amv_var *avp){
 2051    extern char **environ;
 2052    char **ecpp;
 2053    bool_t rv;
 2054    NYD2_ENTER;
 2055    n_UNUSED(avp);
 2056 
 2057    rv = FAL0;
 2058    ecpp = environ;
 2059 
 2060 #ifndef HAVE_SETENV
 2061    if(avp != NULL && avp->av_env != NULL){
 2062       for(; *ecpp != NULL; ++ecpp)
 2063          if(*ecpp == avp->av_env){
 2064             do
 2065                ecpp[0] = ecpp[1];
 2066             while(*ecpp++ != NULL);
 2067             n_free(avp->av_env);
 2068             avp->av_env = NULL;
 2069             rv = TRU1;
 2070             break;
 2071          }
 2072    }else
 2073 #endif
 2074    {
 2075       size_t l;
 2076 
 2077       if((l = strlen(name)) > 0){
 2078          for(; *ecpp != NULL; ++ecpp)
 2079             if(!strncmp(*ecpp, name, l) && (*ecpp)[l] == '='){
 2080 #ifdef HAVE_SETENV
 2081                unsetenv(name);
 2082 #else
 2083                do
 2084                   ecpp[0] = ecpp[1];
 2085                while(*ecpp++ != NULL);
 2086 #endif
 2087                rv = TRU1;
 2088                break;
 2089             }
 2090       }
 2091    }
 2092    NYD2_LEAVE;
 2093    return rv;
 2094 }
 2095 
 2096 static void
 2097 a_amv_var_show_all(void){
 2098    struct n_string msg, *msgp;
 2099    FILE *fp;
 2100    size_t no, i;
 2101    struct a_amv_var *avp;
 2102    char const **vacp, **cap;
 2103    NYD2_ENTER;
 2104 
 2105    if((fp = Ftmp(NULL, "setlist", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
 2106       n_perr(_("`set' list: cannot create temporary file"), 0);
 2107       goto jleave;
 2108    }
 2109 
 2110    /* We need to instantiate first-time-inits and default values here, so that
 2111     * they will be regular members of our _vars[] table */
 2112    for(i = a_AMV_VAR_I3VALS_CNT; i-- > 0;)
 2113       n_var_oklook(a_amv_var_i3vals[i].avdv_okey);
 2114    for(i = a_AMV_VAR_DEFVALS_CNT; i-- > 0;)
 2115       n_var_oklook(a_amv_var_defvals[i].avdv_okey);
 2116 
 2117    for(no = i = 0; i < a_AMV_PRIME; ++i)
 2118       for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
 2119          ++no;
 2120    no += a_AMV_VAR_VIRTS_CNT;
 2121 
 2122    vacp = n_autorec_alloc(no * sizeof(*vacp));
 2123 
 2124    for(cap = vacp, i = 0; i < a_AMV_PRIME; ++i)
 2125       for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
 2126          *cap++ = avp->av_name;
 2127    for(i = a_AMV_VAR_VIRTS_CNT; i-- > 0;)
 2128       *cap++ = a_amv_var_virts[i].avv_var->av_name;
 2129 
 2130    if(no > 1)
 2131       qsort(vacp, no, sizeof *vacp, &a_amv_var__show_cmp);
 2132 
 2133    msgp = &msg;
 2134    msgp = n_string_reserve(n_string_creat(msgp), 80);
 2135    for(i = 0, cap = vacp; no != 0; ++cap, --no)
 2136       i += a_amv_var_show(*cap, fp, msgp);
 2137    n_string_gut(&msg);
 2138 
 2139    page_or_print(fp, i);
 2140    Fclose(fp);
 2141 jleave:
 2142    NYD2_LEAVE;
 2143 }
 2144 
 2145 static int
 2146 a_amv_var__show_cmp(void const *s1, void const *s2){
 2147    int rv;
 2148    NYD2_ENTER;
 2149 
 2150    rv = strcmp(*(char**)n_UNCONST(s1), *(char**)n_UNCONST(s2));
 2151    NYD2_LEAVE;
 2152    return rv;
 2153 }
 2154 
 2155 static size_t
 2156 a_amv_var_show(char const *name, FILE *fp, struct n_string *msgp){
 2157    struct a_amv_var_carrier avc;
 2158    char const *quote;
 2159    struct a_amv_var *avp;
 2160    bool_t isset;
 2161    size_t i;
 2162    NYD2_ENTER;
 2163 
 2164    msgp = n_string_trunc(msgp, 0);
 2165    i = 0;
 2166 
 2167    a_amv_var_revlookup(&avc, name);
 2168    isset = a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE);
 2169    avp = avc.avc_var;
 2170 
 2171    if(n_poption & n_PO_D_V){
 2172       if(avc.avc_map == NULL){
 2173          msgp = n_string_push_cp(msgp, "#assembled variable with value");
 2174          i = 1;
 2175       }else{
 2176          struct{
 2177             ui16_t flag;
 2178             char msg[22];
 2179          } const tbase[] = {
 2180             {a_AMV_VF_CHAIN, "variable chain"},
 2181             {a_AMV_VF_VIRT, "virtual"},
 2182             {a_AMV_VF_RDONLY, "read-only"},
 2183             {a_AMV_VF_NODEL, "nodelete"},
 2184             {a_AMV_VF_I3VAL, "initial-value"},
 2185             {a_AMV_VF_DEFVAL, "default-value"},
 2186             {a_AMV_VF_IMPORT, "import-environ-first\0"}, /* assert NUL in max */
 2187             {a_AMV_VF_ENV, "sync-environ"},
 2188             {a_AMV_VF_NOLOPTS, "no-localopts"},
 2189             {a_AMV_VF_NOTEMPTY, "notempty"},
 2190             {a_AMV_VF_NUM, "number"},
 2191             {a_AMV_VF_POSNUM, "positive-number"},
 2192             {a_AMV_VF_OBSOLETE, "obsoleted"},
 2193          }, *tp;
 2194 
 2195          for(tp = tbase; PTRCMP(tp, <, &tbase[n_NELEM(tbase)]); ++tp)
 2196             if(isset ? (avp->av_flags & tp->flag)
 2197                   : (avc.avc_map->avm_flags & tp->flag)){
 2198                msgp = n_string_push_c(msgp, (i++ == 0 ? '#' : ','));
 2199                msgp = n_string_push_cp(msgp, tp->msg);
 2200             }
 2201       }
 2202 
 2203       if(avp != NULL && (avp->av_flags & a_AMV_VF_EXT_FROZEN)){
 2204          msgp = n_string_push_c(msgp, (i++ == 0 ? '#' : ','));
 2205          msgp = n_string_push_cp(msgp, "(un)?set via -S");
 2206       }
 2207 
 2208       if(i > 0)
 2209          msgp = n_string_push_cp(msgp, "\n  ");
 2210    }
 2211 
 2212    /* (Read-only variables are generally shown via comments..) */
 2213    if(!isset || (avp->av_flags & a_AMV_VF_RDONLY)){
 2214       msgp = n_string_push_c(msgp, n_ns[0]);
 2215       if(!isset){
 2216          if(avc.avc_map != NULL && (avc.avc_map->avm_flags & a_AMV_VF_BOOL))
 2217             msgp = n_string_push_cp(msgp, "boolean; ");
 2218          msgp = n_string_push_cp(msgp, "variable not set: ");
 2219          msgp = n_string_push_cp(msgp, n_shexp_quote_cp(name, FAL0));
 2220          goto jleave;
 2221       }
 2222    }
 2223 
 2224    n_UNINIT(quote, NULL);
 2225    if(!(avp->av_flags & a_AMV_VF_BOOL)){
 2226       quote = n_shexp_quote_cp(avp->av_value, TRU1);
 2227       if(strcmp(quote, avp->av_value))
 2228          msgp = n_string_push_cp(msgp, "wysh ");
 2229    }else if(n_poption & n_PO_D_V)
 2230       msgp = n_string_push_cp(msgp, "wysh "); /* (for shell-style comment) */
 2231 
 2232    if(avp->av_flags & a_AMV_VF_EXT_LINKED)
 2233       msgp = n_string_push_cp(msgp, "environ ");
 2234    msgp = n_string_push_cp(msgp, "set ");
 2235    msgp = n_string_push_cp(msgp, name);
 2236 
 2237    if(!(avp->av_flags & a_AMV_VF_BOOL)){
 2238       msgp = n_string_push_c(msgp, '=');
 2239       msgp = n_string_push_cp(msgp, quote);
 2240    }else if(n_poption & n_PO_D_V)
 2241       msgp = n_string_push_cp(msgp, " #boolean");
 2242 
 2243 jleave:
 2244    msgp = n_string_push_c(msgp, '\n');
 2245    fputs(n_string_cp(msgp), fp);
 2246    NYD2_ENTER;
 2247    return (i > 0 ? 2 : 1);
 2248 }
 2249 
 2250 static bool_t
 2251 a_amv_var_c_set(char **ap, enum a_amv_var_setclr_flags avscf){
 2252    char *cp, *cp2, *varbuf, c;
 2253    size_t errs;
 2254    NYD2_ENTER;
 2255 
 2256    errs = 0;
 2257 jouter:
 2258    while((cp = *ap++) != NULL){
 2259       /* Isolate key */
 2260       cp2 = varbuf = n_autorec_alloc(strlen(cp) +1);
 2261 
 2262       for(; (c = *cp) != '=' && c != '\0'; ++cp){
 2263          if(cntrlchar(c) || spacechar(c)){
 2264             n_err(_("Variable name with control or space character ignored: "
 2265                "%s\n"), ap[-1]);
 2266             ++errs;
 2267             goto jouter;
 2268          }
 2269          *cp2++ = c;
 2270       }
 2271       *cp2 = '\0';
 2272       if(c == '\0')
 2273          cp = n_UNCONST(n_empty);
 2274       else
 2275          ++cp;
 2276 
 2277       if(varbuf == cp2){
 2278          n_err(_("Empty variable name ignored\n"));
 2279          ++errs;
 2280       }else{
 2281          struct a_amv_var_carrier avc;
 2282          bool_t isunset;
 2283 
 2284          if((isunset = (varbuf[0] == 'n' && varbuf[1] == 'o')))
 2285             varbuf = &varbuf[2];
 2286 
 2287          a_amv_var_revlookup(&avc, varbuf);
 2288 
 2289          if((avscf & a_AMV_VSETCLR_LOCAL) && avc.avc_map != NULL){
 2290             if(n_poption & n_PO_D_V)
 2291                n_err(_("Builtin variable not overwritten by `local': %s\n"),
 2292                   varbuf);
 2293             ++errs;
 2294          }else if(isunset)
 2295             errs += !a_amv_var_clear(&avc, avscf);
 2296          else
 2297             errs += !a_amv_var_set(&avc, cp, avscf);
 2298       }
 2299    }
 2300    NYD2_LEAVE;
 2301    return (errs == 0);
 2302 }
 2303 
 2304 FL int
 2305 c_define(void *v){
 2306    int rv;
 2307    char **args;
 2308    NYD_ENTER;
 2309 
 2310    rv = 1;
 2311 
 2312    if((args = v)[0] == NULL){
 2313       rv = (a_amv_mac_show(a_AMV_MF_NONE) == FAL0);
 2314       goto jleave;
 2315    }
 2316 
 2317    if(args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
 2318          args[2] != NULL){
 2319       n_err(_("Synopsis: define: <name> {\n"));
 2320       goto jleave;
 2321    }
 2322 
 2323    rv = (a_amv_mac_def(args[0], a_AMV_MF_NONE) == FAL0);
 2324 jleave:
 2325    NYD_LEAVE;
 2326    return rv;
 2327 }
 2328 
 2329 FL int
 2330 c_undefine(void *v){
 2331    int rv;
 2332    char **args;
 2333    NYD_ENTER;
 2334 
 2335    rv = 0;
 2336    args = v;
 2337    do
 2338       rv |= !a_amv_mac_undef(*args, a_AMV_MF_NONE);
 2339    while(*++args != NULL);
 2340    NYD_LEAVE;
 2341    return rv;
 2342 }
 2343 
 2344 FL int
 2345 c_call(void *vp){
 2346    int rv;
 2347    NYD_ENTER;
 2348 
 2349    rv = a_amv_mac_call(vp, FAL0);
 2350    NYD_LEAVE;
 2351    return rv;
 2352 }
 2353 
 2354 FL int
 2355 c_call_if(void *vp){
 2356    int rv;
 2357    NYD_ENTER;
 2358 
 2359    rv = a_amv_mac_call(vp, TRU1);
 2360    NYD_LEAVE;
 2361    return rv;
 2362 }
 2363 
 2364 FL int
 2365 c_account(void *v){
 2366    struct a_amv_mac_call_args *amcap;
 2367    struct a_amv_mac *amp;
 2368    char **args;
 2369    int rv, i, oqf, nqf;
 2370    NYD_ENTER;
 2371 
 2372    rv = 1;
 2373 
 2374    if((args = v)[0] == NULL){
 2375       rv = (a_amv_mac_show(a_AMV_MF_ACCOUNT) == FAL0);
 2376       goto jleave;
 2377    }
 2378 
 2379    if(args[1] && args[1][0] == '{' && args[1][1] == '\0'){
 2380       if(args[2] != NULL){
 2381          n_err(_("Synopsis: account: <name> {\n"));
 2382          goto jleave;
 2383       }
 2384       if(!asccasecmp(args[0], ACCOUNT_NULL)){
 2385          n_err(_("`account': cannot use reserved name: %s\n"),
 2386             ACCOUNT_NULL);
 2387          goto jleave;
 2388       }
 2389       rv = (a_amv_mac_def(args[0], a_AMV_MF_ACCOUNT) == FAL0);
 2390       goto jleave;
 2391    }
 2392 
 2393    if(n_pstate & n_PS_HOOK_MASK){
 2394       n_err(_("`account': cannot change account from within a hook\n"));
 2395       goto jleave;
 2396    }
 2397 
 2398    save_mbox_for_possible_quitstuff();
 2399 
 2400    amp = NULL;
 2401    if(asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
 2402          (amp = a_amv_mac_lookup(args[0], NULL, a_AMV_MF_ACCOUNT)) == NULL){
 2403       n_err(_("`account': account does not exist: %s\n"), args[0]);
 2404       goto jleave;
 2405    }
 2406 
 2407    oqf = savequitflags();
 2408 
 2409    /* Shutdown the active account */
 2410    if(a_amv_acc_curr != NULL){
 2411       char const *cp;
 2412       char *var;
 2413 
 2414       /* Is there a cleanup hook? */
 2415       var = savecat("on-account-cleanup-", a_amv_acc_curr->am_name);
 2416       if((cp = n_var_vlook(var, FAL0)) != NULL ||
 2417             (cp = ok_vlook(on_account_cleanup)) != NULL){
 2418          struct a_amv_mac *amphook;
 2419 
 2420          if((amphook = a_amv_mac_lookup(cp, NULL, a_AMV_MF_NONE)) != NULL){
 2421             amcap = n_lofi_alloc(sizeof *amcap);
 2422             memset(amcap, 0, sizeof *amcap);
 2423             amcap->amca_name = cp;
 2424             amcap->amca_amp = amphook;
 2425             amcap->amca_unroller = &a_amv_acc_curr->am_lopts;
 2426             amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2427             amcap->amca_no_xcall = TRU1;
 2428             n_pstate |= n_PS_HOOK;
 2429             rv = a_amv_mac_exec(amcap);
 2430             n_pstate &= ~n_PS_HOOK_MASK;
 2431          }else
 2432             n_err(_("*on-account-leave* hook %s does not exist\n"),
 2433                n_shexp_quote_cp(cp, FAL0));
 2434       }
 2435 
 2436       /* `localopts'? */
 2437       if(a_amv_acc_curr->am_lopts != NULL)
 2438          a_amv_lopts_unroll(&a_amv_acc_curr->am_lopts);
 2439 
 2440       /* For accounts this lingers */
 2441       --a_amv_acc_curr->am_refcnt;
 2442       if(a_amv_acc_curr->am_flags & a_AMV_MF_DELETE)
 2443          a_amv_mac_free(a_amv_acc_curr);
 2444    }
 2445 
 2446    a_amv_acc_curr = amp;
 2447 
 2448    /* And switch to any non-"null" account */
 2449    if(amp != NULL){
 2450       assert(amp->am_lopts == NULL);
 2451       amcap = n_lofi_alloc(sizeof *amcap);
 2452       memset(amcap, 0, sizeof *amcap);
 2453       amcap->amca_name = amp->am_name;
 2454       amcap->amca_amp = amp;
 2455       amcap->amca_unroller = &amp->am_lopts;
 2456       amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2457       amcap->amca_no_xcall = TRU1;
 2458       ++amp->am_refcnt; /* We may not run 0 to avoid being deleted! */
 2459       if(!a_amv_mac_exec(amcap)){
 2460          /* XXX account switch incomplete, unroll? */
 2461          n_err(_("`account': failed to switch to account: %s\n"), amp->am_name);
 2462          goto jleave;
 2463       }
 2464    }
 2465 
 2466    n_PS_ROOT_BLOCK((amp != NULL ? ok_vset(account, amp->am_name)
 2467       : ok_vclear(account)));
 2468 
 2469    if(n_psonce & n_PSO_STARTED){
 2470       assert(!(n_pstate & n_PS_HOOK_MASK));
 2471       nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
 2472       restorequitflags(oqf);
 2473       if((i = setfile("%", 0)) < 0)
 2474          goto jleave;
 2475       temporary_folder_hook_check(FAL0);
 2476       if(i > 0 && !ok_blook(emptystart))
 2477          goto jleave;
 2478       n_folder_announce(n_ANNOUNCE_CHANGE);
 2479       restorequitflags(nqf);
 2480    }
 2481    rv = 0;
 2482 jleave:
 2483    NYD_LEAVE;
 2484    return rv;
 2485 }
 2486 
 2487 FL int
 2488 c_unaccount(void *v){
 2489    int rv;
 2490    char **args;
 2491    NYD_ENTER;
 2492 
 2493    rv = 0;
 2494    args = v;
 2495    do
 2496       rv |= !a_amv_mac_undef(*args, a_AMV_MF_ACCOUNT);
 2497    while(*++args != NULL);
 2498    NYD_LEAVE;
 2499    return rv;
 2500 }
 2501 
 2502 FL int
 2503 c_localopts(void *vp){
 2504    enum a_amv_loflags alf, alm;
 2505    char const **argv;
 2506    int rv;
 2507    NYD_ENTER;
 2508 
 2509    rv = 1;
 2510 
 2511    if(a_amv_lopts == NULL){
 2512       n_err(_("Cannot use `localopts' in this context\n"));
 2513       goto jleave;
 2514    }
 2515 
 2516    if((argv = vp)[1] == NULL || is_asccaseprefix((++argv)[-1], "scope"))
 2517       alf = alm = a_AMV_LF_SCOPE;
 2518    else if(is_asccaseprefix(argv[-1], "call"))
 2519       alf = a_AMV_LF_CALL, alm = a_AMV_LF_CALL_MASK;
 2520    else if(is_asccaseprefix(argv[-1], "call-fixate"))
 2521       alf = a_AMV_LF_CALL_FIXATE, alm = a_AMV_LF_CALL_MASK;
 2522    else{
 2523       n_err(_("Synopsis: localopts: [<scope|call|call-fixate>] <boolean>\n"));
 2524       goto jleave;
 2525    }
 2526 
 2527    if(alf == a_AMV_LF_SCOPE &&
 2528          (a_amv_lopts->as_loflags & a_AMV_LF_SCOPE_FIXATE)){
 2529       if(n_poption & n_PO_D_V)
 2530          n_err(_("Cannot turn off `localopts', setting is fixated\n"));
 2531       goto jleave;
 2532    }
 2533 
 2534    a_amv_lopts->as_loflags &= ~alm;
 2535    if(boolify(*argv, UIZ_MAX, FAL0) > 0)
 2536       a_amv_lopts->as_loflags |= alf;
 2537    rv = 0;
 2538 jleave:
 2539    NYD_LEAVE;
 2540    return rv;
 2541 }
 2542 
 2543 FL int
 2544 c_shift(void *vp){ /* xxx move to bottom, not in macro part! */
 2545    struct a_amv_pospar *appp;
 2546    ui16_t i;
 2547    int rv;
 2548    NYD_ENTER;
 2549 
 2550    rv = 1;
 2551 
 2552    if((vp = *(char**)vp) == NULL)
 2553       i = 1;
 2554    else{
 2555       si16_t sib;
 2556 
 2557       if((n_idec_si16_cp(&sib, vp, 10, NULL
 2558                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2559             ) != n_IDEC_STATE_CONSUMED || sib < 0){
 2560          n_err(_("`shift': invalid argument: %s\n"), vp);
 2561          goto jleave;
 2562       }
 2563       i = (ui16_t)sib;
 2564    }
 2565 
 2566    /* If in in a macro/xy */
 2567    if(a_amv_lopts != NULL){
 2568       struct a_amv_mac const *amp;
 2569       struct a_amv_mac_call_args *amcap;
 2570 
 2571       /* Explicitly do allow `vpospar' created things! */
 2572       amp = (amcap = a_amv_lopts->as_amcap)->amca_amp;
 2573       if((amp == NULL || amcap->amca_ps_hook_mask ||
 2574                (amp->am_flags & a_AMV_MF_TYPE_MASK) == a_AMV_MF_ACCOUNT) &&
 2575             amcap->amca_pospar.app_not_heap){
 2576          n_err(_("Cannot use `shift' in `account's or hook macros etc.\n"));
 2577          goto jleave;
 2578       }
 2579       appp = &amcap->amca_pospar;
 2580    }else
 2581       appp = &a_amv_pospar;
 2582 
 2583    if(i > appp->app_count){
 2584       n_err(_("`shift': cannot shift %hu of %hu parameters\n"),
 2585          i, appp->app_count);
 2586       goto jleave;
 2587    }else{
 2588       appp->app_idx += i;
 2589       appp->app_count -= i;
 2590       rv = 0;
 2591    }
 2592 jleave:
 2593    NYD_LEAVE;
 2594    return rv;
 2595 }
 2596 
 2597 FL int
 2598 c_return(void *vp){ /* TODO the exit status should be m_si64! */
 2599    int rv;
 2600    NYD_ENTER;
 2601 
 2602    if(a_amv_lopts != NULL){
 2603       char const **argv;
 2604 
 2605       n_go_input_force_eof();
 2606       n_pstate_err_no = n_ERR_NONE;
 2607       rv = 0;
 2608 
 2609       if((argv = vp)[0] != NULL){
 2610          si32_t i;
 2611 
 2612          if((n_idec_si32_cp(&i, argv[0], 10, NULL
 2613                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2614                ) == n_IDEC_STATE_CONSUMED && i >= 0)
 2615             rv = (int)i;
 2616          else{
 2617             n_err(_("`return': return value argument is invalid: %s\n"),
 2618                argv[0]);
 2619             n_pstate_err_no = n_ERR_INVAL;
 2620             rv = 1;
 2621          }
 2622 
 2623          if(argv[1] != NULL){
 2624             if((n_idec_si32_cp(&i, argv[1], 10, NULL
 2625                      ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2626                   ) == n_IDEC_STATE_CONSUMED && i >= 0)
 2627                n_pstate_err_no = i;
 2628             else{
 2629                n_err(_("`return': error number argument is invalid: %s\n"),
 2630                   argv[1]);
 2631                n_pstate_err_no = n_ERR_INVAL;
 2632                rv = 1;
 2633             }
 2634          }
 2635       }
 2636    }else{
 2637       n_err(_("Can only use `return' in a macro\n"));
 2638       n_pstate_err_no = n_ERR_OPNOTSUPP;
 2639       rv = 1;
 2640    }
 2641    NYD_LEAVE;
 2642    return rv;
 2643 }
 2644 
 2645 FL bool_t
 2646 temporary_folder_hook_check(bool_t nmail){ /* TODO temporary, v15: drop */
 2647    struct a_amv_mac_call_args *amcap;
 2648    struct a_amv_mac *amp;
 2649    size_t len;
 2650    char const *cp;
 2651    char *var;
 2652    bool_t rv;
 2653    NYD_ENTER;
 2654 
 2655    rv = TRU1;
 2656    var = n_autorec_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
 2657 
 2658    /* First try the fully resolved path */
 2659    snprintf(var, len, "folder-hook-%s", mailname);
 2660    if((cp = n_var_vlook(var, FAL0)) != NULL)
 2661       goto jmac;
 2662 
 2663    /* If we are under *folder*, try the usual +NAME syntax, too */
 2664    if(displayname[0] == '+'){
 2665       char *x;
 2666 
 2667       for(x = &mailname[len]; x != mailname; --x)
 2668          if(x[-1] == '/'){
 2669             snprintf(var, len, "folder-hook-+%s", x);
 2670             if((cp = n_var_vlook(var, FAL0)) != NULL)
 2671                goto jmac;
 2672             break;
 2673          }
 2674    }
 2675 
 2676    /* Plain *folder-hook* is our last try */
 2677    if((cp = ok_vlook(folder_hook)) == NULL)
 2678       goto jleave;
 2679 
 2680 jmac:
 2681    if((amp = a_amv_mac_lookup(cp, NULL, a_AMV_MF_NONE)) == NULL){
 2682       n_err(_("Cannot call *folder-hook* for %s: macro does not exist: %s\n"),
 2683          n_shexp_quote_cp(displayname, FAL0), cp);
 2684       rv = FAL0;
 2685       goto jleave;
 2686    }
 2687 
 2688    amcap = n_lofi_alloc(sizeof *amcap);
 2689    memset(amcap, 0, sizeof *amcap);
 2690    amcap->amca_name = cp;
 2691    amcap->amca_amp = amp;
 2692    n_pstate &= ~n_PS_HOOK_MASK;
 2693    if(nmail){
 2694       amcap->amca_unroller = NULL;
 2695       n_pstate |= n_PS_HOOK_NEWMAIL;
 2696    }else{
 2697       amcap->amca_unroller = &a_amv_folder_hook_lopts;
 2698       n_pstate |= n_PS_HOOK;
 2699    }
 2700    amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2701    amcap->amca_ps_hook_mask = TRU1;
 2702    amcap->amca_no_xcall = TRU1;
 2703    rv = a_amv_mac_exec(amcap);
 2704    n_pstate &= ~n_PS_HOOK_MASK;
 2705 
 2706 jleave:
 2707    NYD_LEAVE;
 2708    return rv;
 2709 }
 2710 
 2711 FL void
 2712 temporary_folder_hook_unroll(void){ /* XXX intermediate hack */
 2713    NYD_ENTER;
 2714    if(a_amv_folder_hook_lopts != NULL){
 2715       void *save = a_amv_lopts;
 2716 
 2717       a_amv_lopts = NULL;
 2718       a_amv_lopts_unroll(&a_amv_folder_hook_lopts);
 2719       assert(a_amv_folder_hook_lopts == NULL);
 2720       a_amv_lopts = save;
 2721    }
 2722    NYD_LEAVE;
 2723 }
 2724 
 2725 FL void
 2726 temporary_compose_mode_hook_call(char const *macname,
 2727       void (*hook_pre)(void *), void *hook_arg){
 2728    /* TODO compose_mode_hook_call() temporary, v15: generalize; see a_GO_SPLICE
 2729     * TODO comment in go.c for the right way of doing things! */
 2730    static struct a_amv_lostack *cmh_losp;
 2731    struct a_amv_mac_call_args *amcap;
 2732    struct a_amv_mac *amp;
 2733    NYD_ENTER;
 2734 
 2735    amp = NULL;
 2736 
 2737    if(macname == (char*)-1){
 2738       a_amv_mac__finalize(cmh_losp);
 2739       cmh_losp = NULL;
 2740    }else if(macname != NULL &&
 2741          (amp = a_amv_mac_lookup(macname, NULL, a_AMV_MF_NONE)) == NULL)
 2742       n_err(_("Cannot call *on-compose-**: macro does not exist: %s\n"),
 2743          macname);
 2744    else{
 2745       amcap = n_lofi_alloc(sizeof *amcap);
 2746       memset(amcap, 0, sizeof *amcap);
 2747       amcap->amca_name = (macname != NULL) ? macname
 2748             : "*on-compose-splice(-shell)?*";
 2749       amcap->amca_amp = amp;
 2750       amcap->amca_unroller = &a_amv_compose_lopts;
 2751       amcap->amca_hook_pre = hook_pre;
 2752       amcap->amca_hook_arg = hook_arg;
 2753       amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2754       amcap->amca_ps_hook_mask = TRU1;
 2755       amcap->amca_no_xcall = TRU1;
 2756       n_pstate &= ~n_PS_HOOK_MASK;
 2757       n_pstate |= n_PS_HOOK;
 2758       if(macname != NULL)
 2759          a_amv_mac_exec(amcap);
 2760       else{
 2761          cmh_losp = n_lofi_alloc(sizeof *cmh_losp);
 2762          memset(cmh_losp, 0, sizeof *cmh_losp);
 2763          cmh_losp->as_global_saved = a_amv_lopts;
 2764          cmh_losp->as_lopts = *(cmh_losp->as_amcap = amcap)->amca_unroller;
 2765          cmh_losp->as_loflags = a_AMV_LF_SCOPE_FIXATE;
 2766          a_amv_lopts = cmh_losp;
 2767       }
 2768    }
 2769    NYD_LEAVE;
 2770 }
 2771 
 2772 FL void
 2773 temporary_compose_mode_hook_unroll(void){ /* XXX intermediate hack */
 2774    NYD_ENTER;
 2775    if(a_amv_compose_lopts != NULL){
 2776       void *save = a_amv_lopts;
 2777 
 2778       a_amv_lopts = NULL;
 2779       a_amv_lopts_unroll(&a_amv_compose_lopts);
 2780       assert(a_amv_compose_lopts == NULL);
 2781       a_amv_lopts = save;
 2782    }
 2783    NYD_LEAVE;
 2784 }
 2785 
 2786 FL bool_t
 2787 n_var_is_user_writable(char const *name){
 2788    struct a_amv_var_carrier avc;
 2789    struct a_amv_var_map const *avmp;
 2790    bool_t rv;
 2791    NYD_ENTER;
 2792 
 2793    a_amv_var_revlookup(&avc, name);
 2794    if((avmp = avc.avc_map) == NULL)
 2795       rv = TRU1;
 2796    else
 2797       rv = ((avmp->avm_flags & (a_AMV_VF_BOOL | a_AMV_VF_RDONLY)) == 0);
 2798    NYD_LEAVE;
 2799    return rv;
 2800 }
 2801 
 2802 FL char *
 2803 n_var_oklook(enum okeys okey){
 2804    struct a_amv_var_carrier avc;
 2805    char *rv;
 2806    struct a_amv_var_map const *avmp;
 2807    NYD_ENTER;
 2808 
 2809    avc.avc_map = avmp = &a_amv_var_map[okey];
 2810    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2811    avc.avc_hash = avmp->avm_hash;
 2812    avc.avc_okey = okey;
 2813 
 2814    if(a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE))
 2815       rv = avc.avc_var->av_value;
 2816    else
 2817       rv = NULL;
 2818    NYD_LEAVE;
 2819    return rv;
 2820 }
 2821 
 2822 FL bool_t
 2823 n_var_okset(enum okeys okey, uintptr_t val){
 2824    struct a_amv_var_carrier avc;
 2825    bool_t ok;
 2826    struct a_amv_var_map const *avmp;
 2827    NYD_ENTER;
 2828 
 2829    avc.avc_map = avmp = &a_amv_var_map[okey];
 2830    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2831    avc.avc_hash = avmp->avm_hash;
 2832    avc.avc_okey = okey;
 2833 
 2834    ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val),
 2835          a_AMV_VSETCLR_NONE);
 2836    NYD_LEAVE;
 2837    return ok;
 2838 }
 2839 
 2840 FL bool_t
 2841 n_var_okclear(enum okeys okey){
 2842    struct a_amv_var_carrier avc;
 2843    bool_t rv;
 2844    struct a_amv_var_map const *avmp;
 2845    NYD_ENTER;
 2846 
 2847    avc.avc_map = avmp = &a_amv_var_map[okey];
 2848    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2849    avc.avc_hash = avmp->avm_hash;
 2850    avc.avc_okey = okey;
 2851 
 2852    rv = a_amv_var_clear(&avc, a_AMV_VSETCLR_NONE);
 2853    NYD_LEAVE;
 2854    return rv;
 2855 }
 2856 
 2857 FL char const *
 2858 n_var_vlook(char const *vokey, bool_t try_getenv){
 2859    struct a_amv_var_carrier avc;
 2860    char const *rv;
 2861    NYD_ENTER;
 2862 
 2863    a_amv_var_revlookup(&avc, vokey);
 2864 
 2865    switch((enum a_amv_var_special_category)avc.avc_special_cat){
 2866    default: /* silence CC */
 2867    case a_AMV_VSC_NONE:
 2868       if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL))
 2869          rv = avc.avc_var->av_value;
 2870       /* Only check the environment for something that is otherwise unknown */
 2871       else if(try_getenv && avc.avc_map == NULL)
 2872          rv = getenv(vokey);
 2873       else
 2874          rv = NULL;
 2875       break;
 2876    case a_AMV_VSC_GLOBAL:
 2877       rv = a_amv_var_vsc_global(&avc);
 2878       break;
 2879    case a_AMV_VSC_MULTIPLEX:
 2880       rv = a_amv_var_vsc_multiplex(&avc);
 2881       break;
 2882    case a_AMV_VSC_POSPAR:
 2883    case a_AMV_VSC_POSPAR_ENV:
 2884       rv = a_amv_var_vsc_pospar(&avc);
 2885       break;
 2886    }
 2887    NYD_LEAVE;
 2888    return rv;
 2889 }
 2890 
 2891 FL bool_t
 2892 n_var_vexplode(void const **cookie){
 2893    struct a_amv_pospar *appp;
 2894    NYD_ENTER;
 2895 
 2896    appp = (a_amv_lopts != NULL) ? &a_amv_lopts->as_amcap->amca_pospar
 2897          : &a_amv_pospar;
 2898    *cookie = (appp->app_count > 0) ? &appp->app_dat[appp->app_idx] : NULL;
 2899    NYD_LEAVE;
 2900    return (*cookie != NULL);
 2901 }
 2902 
 2903 FL bool_t
 2904 n_var_vset(char const *vokey, uintptr_t val){
 2905    struct a_amv_var_carrier avc;
 2906    bool_t ok;
 2907    NYD_ENTER;
 2908 
 2909    a_amv_var_revlookup(&avc, vokey);
 2910 
 2911    ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val),
 2912          a_AMV_VSETCLR_NONE);
 2913    NYD_LEAVE;
 2914    return ok;
 2915 }
 2916 
 2917 FL bool_t
 2918 n_var_vclear(char const *vokey){
 2919    struct a_amv_var_carrier avc;
 2920    bool_t ok;
 2921    NYD_ENTER;
 2922 
 2923    a_amv_var_revlookup(&avc, vokey);
 2924 
 2925    ok = a_amv_var_clear(&avc, a_AMV_VSETCLR_NONE);
 2926    NYD_LEAVE;
 2927    return ok;
 2928 }
 2929 
 2930 #ifdef HAVE_SOCKETS
 2931 FL char *
 2932 n_var_xoklook(enum okeys okey, struct url const *urlp,
 2933       enum okey_xlook_mode oxm){
 2934    struct a_amv_var_carrier avc;
 2935    struct str const *us;
 2936    size_t nlen;
 2937    char *nbuf, *rv;
 2938    struct a_amv_var_map const *avmp;
 2939    NYD_ENTER;
 2940 
 2941    assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
 2942 
 2943    /* For simplicity: allow this case too */
 2944    if(!(oxm & (OXM_H_P | OXM_U_H_P))){
 2945       nbuf = NULL;
 2946       goto jplain;
 2947    }
 2948 
 2949    avc.avc_map = avmp = &a_amv_var_map[okey];
 2950    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2951    avc.avc_okey = okey;
 2952 
 2953    us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
 2954    nlen = strlen(avc.avc_name);
 2955    nbuf = n_lofi_alloc(nlen + 1 + us->l +1);
 2956    memcpy(nbuf, avc.avc_name, nlen);
 2957    nbuf[nlen++] = '-';
 2958 
 2959    /* One of .url_u_h_p and .url_h_p we test in here */
 2960    memcpy(nbuf + nlen, us->s, us->l +1);
 2961    avc.avc_name = a_amv_var_canonify(nbuf);
 2962    avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
 2963    if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL))
 2964       goto jvar;
 2965 
 2966    /* The second */
 2967    if(oxm & OXM_H_P){
 2968       us = &urlp->url_h_p;
 2969       memcpy(nbuf + nlen, us->s, us->l +1);
 2970       avc.avc_name = a_amv_var_canonify(nbuf);
 2971       avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
 2972       if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL)){
 2973 jvar:
 2974          rv = avc.avc_var->av_value;
 2975          goto jleave;
 2976       }
 2977    }
 2978 
 2979 jplain:
 2980    rv = (oxm & OXM_PLAIN) ? n_var_oklook(okey) : NULL;
 2981 jleave:
 2982    if(nbuf != NULL)
 2983       n_lofi_free(nbuf);
 2984    NYD_LEAVE;
 2985    return rv;
 2986 }
 2987 #endif /* HAVE_SOCKETS */
 2988 
 2989 FL int
 2990 c_set(void *vp){
 2991    int err;
 2992    char **ap;
 2993    NYD_ENTER;
 2994 
 2995    if(*(ap = vp) == NULL){
 2996       a_amv_var_show_all();
 2997       err = 0;
 2998    }else{
 2999       enum a_amv_var_setclr_flags avscf;
 3000 
 3001       if(!(n_pstate & n_PS_ARGMOD_LOCAL))
 3002          avscf = a_AMV_VSETCLR_NONE;
 3003       else{
 3004          if(a_amv_lopts == NULL){
 3005             n_err(_("`set': cannot use `local' in this context\n"));
 3006             err = 1;
 3007             goto jleave;
 3008          }
 3009          avscf = a_AMV_VSETCLR_LOCAL;
 3010       }
 3011       err = !a_amv_var_c_set(ap, avscf);
 3012    }
 3013 jleave:
 3014    NYD_LEAVE;
 3015    return err;
 3016 }
 3017 
 3018 FL int
 3019 c_unset(void *vp){
 3020    struct a_amv_var_carrier avc;
 3021    char **ap;
 3022    int err;
 3023    enum a_amv_var_setclr_flags avscf;
 3024    NYD_ENTER;
 3025 
 3026    if(!(n_pstate & n_PS_ARGMOD_LOCAL))
 3027       avscf = a_AMV_VSETCLR_NONE;
 3028    else{
 3029       if(a_amv_lopts == NULL){
 3030          n_err(_("`unset': cannot use `local' in this context\n"));
 3031          err = 1;
 3032          goto jleave;
 3033       }
 3034       avscf = a_AMV_VSETCLR_LOCAL;
 3035    }
 3036 
 3037    for(err = 0, ap = vp; *ap != NULL; ++ap){
 3038       a_amv_var_revlookup(&avc, *ap);
 3039 
 3040       err |= !a_amv_var_clear(&avc, avscf);
 3041    }
 3042 jleave:
 3043    NYD_LEAVE;
 3044    return err;
 3045 }
 3046 
 3047 FL int
 3048 c_varshow(void *v){
 3049    char **ap;
 3050    NYD_ENTER;
 3051 
 3052    if(*(ap = v) == NULL)
 3053       v = NULL;
 3054    else{
 3055       struct n_string msg, *msgp = &msg;
 3056 
 3057       msgp = n_string_creat(msgp);
 3058       for(; *ap != NULL; ++ap)
 3059          a_amv_var_show(*ap, n_stdout, msgp);
 3060       n_string_gut(msgp);
 3061    }
 3062    NYD_LEAVE;
 3063    return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
 3064 }
 3065 
 3066 FL int
 3067 c_varedit(void *v){
 3068    struct a_amv_var_carrier avc;
 3069    FILE *of, *nf;
 3070    char *val, **argv;
 3071    int err;
 3072    sighandler_type sigint;
 3073    NYD_ENTER;
 3074 
 3075    sigint = safe_signal(SIGINT, SIG_IGN);
 3076 
 3077    for(err = 0, argv = v; *argv != NULL; ++argv){
 3078       memset(&avc, 0, sizeof avc);
 3079 
 3080       a_amv_var_revlookup(&avc, *argv);
 3081 
 3082       if(avc.avc_map != NULL){
 3083          if(avc.avc_map->avm_flags & a_AMV_VF_BOOL){
 3084             n_err(_("`varedit': cannot edit boolean variable: %s\n"),
 3085                avc.avc_name);
 3086             continue;
 3087          }
 3088          if(avc.avc_map->avm_flags & a_AMV_VF_RDONLY){
 3089             n_err(_("`varedit': cannot edit read-only variable: %s\n"),
 3090                avc.avc_name);
 3091             continue;
 3092          }
 3093       }
 3094 
 3095       a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE);
 3096 
 3097       if((of = Ftmp(NULL, "varedit", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
 3098             NULL){
 3099          n_perr(_("`varedit': cannot create temporary file"), 0);
 3100          err = 1;
 3101          break;
 3102       }else if(avc.avc_var != NULL && *(val = avc.avc_var->av_value) != '\0' &&
 3103             sizeof *val != fwrite(val, strlen(val), sizeof *val, of)){
 3104          n_perr(_("`varedit' failed to write old value to temporary file"), 0);
 3105          Fclose(of);
 3106          err = 1;
 3107          continue;
 3108       }
 3109 
 3110       fflush_rewind(of);
 3111       nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
 3112       Fclose(of);
 3113 
 3114       if(nf != NULL){
 3115          int c;
 3116          char const *varres;
 3117          off_t l;
 3118 
 3119          l = fsize(nf);
 3120          if(UICMP(64, l, >=, UIZ_MAX -42)){
 3121             n_err(_("`varedit': not enough memory to store variable: %s\n"),
 3122                avc.avc_name);
 3123             varres = n_empty;
 3124             err = 1;
 3125          }else{
 3126             varres = val = n_autorec_alloc(l +1);
 3127             for(; l > 0 && (c = getc(nf)) != EOF; --l)
 3128                *val++ = c;
 3129             *val++ = '\0';
 3130             if(l != 0){
 3131                n_err(_("`varedit': I/O while reading new value of: %s\n"),
 3132                   avc.avc_name);
 3133                err = 1;
 3134             }
 3135          }
 3136 
 3137          if(!a_amv_var_set(&avc, varres, a_AMV_VSETCLR_NONE))
 3138             err = 1;
 3139 
 3140          Fclose(nf);
 3141       }else{
 3142          n_err(_("`varedit': cannot start $EDITOR\n"));
 3143          err = 1;
 3144          break;
 3145       }
 3146    }
 3147 
 3148    safe_signal(SIGINT, sigint);
 3149    NYD_LEAVE;
 3150    return err;
 3151 }
 3152 
 3153 FL int
 3154 c_environ(void *v){
 3155    struct a_amv_var_carrier avc;
 3156    int err;
 3157    char **ap;
 3158    bool_t islnk;
 3159    NYD_ENTER;
 3160 
 3161    if((islnk = is_asccaseprefix(*(ap = v), "link")) ||
 3162          is_asccaseprefix(*ap, "unlink")){
 3163       for(err = 0; *++ap != NULL;){
 3164          a_amv_var_revlookup(&avc, *ap);
 3165 
 3166          if(a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE) && (islnk ||
 3167                (avc.avc_var->av_flags & a_AMV_VF_EXT_LINKED))){
 3168             if(!islnk){
 3169                avc.avc_var->av_flags &= ~a_AMV_VF_EXT_LINKED;
 3170                continue;
 3171             }else if(avc.avc_var->av_flags &
 3172                   (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED)){
 3173                if(n_poption & n_PO_D_V)
 3174                   n_err(_("`environ': link: already established: %s\n"), *ap);
 3175                continue;
 3176             }
 3177             avc.avc_var->av_flags |= a_AMV_VF_EXT_LINKED;
 3178             if(!(avc.avc_var->av_flags & a_AMV_VF_ENV))
 3179                a_amv_var__putenv(&avc, avc.avc_var);
 3180          }else if(!islnk){
 3181             n_err(_("`environ': unlink: no link established: %s\n"), *ap);
 3182             err = 1;
 3183          }else{
 3184             char const *evp = getenv(*ap);
 3185 
 3186             if(evp != NULL)
 3187                err |= !a_amv_var_set(&avc, evp, a_AMV_VSETCLR_ENV);
 3188             else{
 3189                n_err(_("`environ': link: cannot link to non-existent: %s\n"),
 3190                   *ap);
 3191                err = 1;
 3192             }
 3193          }
 3194       }
 3195    }else if(is_asccaseprefix(*ap, "set"))
 3196       err = !a_amv_var_c_set(++ap, a_AMV_VSETCLR_ENV);
 3197    else if(is_asccaseprefix(*ap, "unset")){
 3198       for(err = 0; *++ap != NULL;){
 3199          a_amv_var_revlookup(&avc, *ap);
 3200 
 3201          if(!a_amv_var_clear(&avc, a_AMV_VSETCLR_ENV))
 3202             err = 1;
 3203       }
 3204    }else{
 3205       n_err(_("Synopsis: environ: <link|set|unset> <variable>...\n"));
 3206       err = 1;
 3207    }
 3208    NYD_LEAVE;
 3209    return err;
 3210 }
 3211 
 3212 FL int
 3213 c_vexpr(void *v){ /* TODO POSIX expr(1) comp. exit status; overly complicat. */
 3214    char pbase, op, iencbuf[2+1/* BASE# prefix*/ + n_IENC_BUFFER_SIZE + 1];
 3215    size_t i;
 3216    enum n_idec_state ids;
 3217    enum n_idec_mode idm;
 3218    si64_t lhv, rhv;
 3219    char const **argv, *varname, *varres, *cp;
 3220    enum{
 3221       a_ERR = 1u<<0,
 3222       a_SOFTOVERFLOW = 1u<<1,
 3223       a_ISNUM = 1u<<2,
 3224       a_ISDECIMAL = 1u<<3,    /* Print only decimal result */
 3225       a_SATURATED = 1u<<4,
 3226       a_ICASE = 1u<<5,
 3227       a_UNSIGNED_OP = 1u<<6,  /* Unsigned right shift (share bit ok) */
 3228       a_PBASE = 1u<<7,        /* Print additional number base */
 3229       a_TMP = 1u<<30
 3230    } f;
 3231    NYD_ENTER;
 3232 
 3233    f = a_ERR;
 3234    argv = v;
 3235    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
 3236    n_UNINIT(varres, n_empty);
 3237    n_UNINIT(pbase, '\0');
 3238 
 3239    if((cp = argv[0])[0] == '\0')
 3240       goto jesubcmd;
 3241 
 3242    if(cp[1] == '\0'){
 3243       op = cp[0];
 3244 jnumop:
 3245       f |= a_ISNUM;
 3246       switch(op){
 3247       case '=':
 3248       case '~':
 3249          if(argv[1] == NULL || argv[2] != NULL)
 3250             goto jesynopsis;
 3251 
 3252          if(*(cp = *++argv) == '\0')
 3253             lhv = 0;
 3254          else{
 3255             idm = ((*cp == 'u' || *cp == 'U')
 3256                   ? (++cp, n_IDEC_MODE_NONE)
 3257                   : ((*cp == 's' || *cp == 'S')
 3258                      ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3259                      : n_IDEC_MODE_SIGNED_TYPE |
 3260                         n_IDEC_MODE_POW2BASE_UNSIGNED));
 3261             if(((ids = n_idec_cp(&lhv, cp, 0, idm, NULL)
 3262                      ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3263                   ) != n_IDEC_STATE_CONSUMED){
 3264                if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3265                   goto jenum_range;
 3266                f |= a_SOFTOVERFLOW;
 3267                break;
 3268             }
 3269          }
 3270          if(op == '~')
 3271             lhv = ~lhv;
 3272          break;
 3273 
 3274       case '+':
 3275       case '-':
 3276       case '*':
 3277       case '/':
 3278       case '%':
 3279       case '|':
 3280       case '&':
 3281       case '^':
 3282       case '<':
 3283       case '>':
 3284          if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3285             goto jesynopsis;
 3286          else{
 3287             char xop;
 3288 
 3289             if(*(cp = *++argv) == '\0')
 3290                lhv = 0;
 3291             else{
 3292                idm = ((*cp == 'u' || *cp == 'U')
 3293                      ? (++cp, n_IDEC_MODE_NONE)
 3294                      : ((*cp == 's' || *cp == 'S')
 3295                         ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3296                         : n_IDEC_MODE_SIGNED_TYPE |
 3297                            n_IDEC_MODE_POW2BASE_UNSIGNED));
 3298                if(((ids = n_idec_cp(&lhv, cp, 0, idm, NULL)
 3299                         ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3300                      ) != n_IDEC_STATE_CONSUMED){
 3301                   if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3302                      goto jenum_range;
 3303                   f |= a_SOFTOVERFLOW;
 3304                   break;
 3305                }
 3306             }
 3307 
 3308             if(*(cp = *++argv) == '\0')
 3309                rhv = 0;
 3310             else{
 3311                idm = ((*cp == 'u' || *cp == 'U')
 3312                      ? (++cp, n_IDEC_MODE_NONE)
 3313                      : ((*cp == 's' || *cp == 'S')
 3314                         ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3315                         : n_IDEC_MODE_SIGNED_TYPE |
 3316                            n_IDEC_MODE_POW2BASE_UNSIGNED));
 3317                if(((ids = n_idec_cp(&rhv, cp, 0, idm, NULL)
 3318                         ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3319                      ) != n_IDEC_STATE_CONSUMED){
 3320                   if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3321                      goto jenum_range;
 3322                   f |= a_SOFTOVERFLOW;
 3323                   lhv = rhv;
 3324                   break;
 3325                }
 3326             }
 3327 
 3328             xop = op;
 3329 jnumop_again:
 3330             switch(xop){
 3331             case '+':
 3332                if(rhv < 0){
 3333                   if(rhv != SI64_MIN){
 3334                      rhv = -rhv;
 3335                      xop = '-';
 3336                      goto jnumop_again;
 3337                   }else if(lhv < 0)
 3338                      goto jenum_plusminus;
 3339                   else if(lhv == 0){
 3340                      lhv = rhv;
 3341                      break;
 3342                   }
 3343                }else if(SI64_MAX - rhv < lhv)
 3344                   goto jenum_plusminus;
 3345                lhv += rhv;
 3346                break;
 3347             case '-':
 3348                if(rhv < 0){
 3349                   if(rhv != SI64_MIN){
 3350                      rhv = -rhv;
 3351                      xop = '+';
 3352                      goto jnumop_again;
 3353                   }else if(lhv > 0)
 3354                      goto jenum_plusminus;
 3355                   else if(lhv == 0){
 3356                      lhv = rhv;
 3357                      break;
 3358                   }
 3359                }else if(SI64_MIN + rhv > lhv){
 3360 jenum_plusminus:
 3361                   if(!(f & a_SATURATED))
 3362                      goto jenum_overflow;
 3363                   f |= a_SOFTOVERFLOW;
 3364                   lhv = (lhv < 0 || xop == '-') ? SI64_MIN : SI64_MAX;
 3365                   break;
 3366                }
 3367                lhv -= rhv;
 3368                break;
 3369             case '*':
 3370                /* Will the result be positive? */
 3371                if((lhv < 0) == (rhv < 0)){
 3372                   if(lhv > 0){
 3373                      lhv = -lhv;
 3374                      rhv = -rhv;
 3375                   }
 3376                   if(rhv != 0 && lhv != 0 && SI64_MAX / rhv > lhv){
 3377                      if(!(f & a_SATURATED))
 3378                         goto jenum_overflow;
 3379                      f |= a_SOFTOVERFLOW;
 3380                      lhv = SI64_MAX;
 3381                   }else
 3382                      lhv *= rhv;
 3383                }else{
 3384                   if(rhv > 0){
 3385                      if(lhv != 0 && SI64_MIN / lhv < rhv){
 3386                         if(!(f & a_SATURATED))
 3387                            goto jenum_overflow;
 3388                         f |= a_SOFTOVERFLOW;
 3389                         lhv = SI64_MIN;
 3390                      }else
 3391                         lhv *= rhv;
 3392                   }else{
 3393                      if(rhv != 0 && lhv != 0 && SI64_MIN / rhv < lhv){
 3394                         if(!(f & a_SATURATED))
 3395                            goto jenum_overflow;
 3396                         f |= a_SOFTOVERFLOW;
 3397                         lhv = SI64_MIN;
 3398                      }else
 3399                         lhv *= rhv;
 3400                   }
 3401                }
 3402                break;
 3403             case '/':
 3404                if(rhv == 0){
 3405                   if(!(f & a_SATURATED))
 3406                      goto jenum_range;
 3407                   f |= a_SOFTOVERFLOW;
 3408                   lhv = SI64_MAX;
 3409                }else
 3410                   lhv /= rhv;
 3411                break;
 3412             case '%':
 3413                if(rhv == 0){
 3414                   if(!(f & a_SATURATED))
 3415                      goto jenum_range;
 3416                   f |= a_SOFTOVERFLOW;
 3417                   lhv = SI64_MAX;
 3418                }else
 3419                   lhv %= rhv;
 3420                break;
 3421             case '|':
 3422                lhv |= rhv;
 3423                break;
 3424             case '&':
 3425                lhv &= rhv;
 3426                break;
 3427             case '^':
 3428                lhv ^= rhv;
 3429                break;
 3430             case '<':
 3431             case '>':
 3432                if(!(f & a_TMP)){
 3433                   argv -= 2;
 3434                   goto jesubcmd;
 3435                }
 3436                if(rhv > 63){ /* xxx 63? */
 3437                   if(!(f & a_SATURATED))
 3438                      goto jenum_overflow;
 3439                   rhv = 63;
 3440                }
 3441                if(op == '<')
 3442                   lhv <<= (ui8_t)rhv;
 3443                else if(f & a_UNSIGNED_OP)
 3444                   lhv = (ui64_t)lhv >> (ui8_t)rhv;
 3445                else
 3446                   lhv >>= (ui8_t)rhv;
 3447                break;
 3448             }
 3449          }
 3450          break;
 3451       default:
 3452          goto jesubcmd;
 3453       }
 3454    }else if(cp[0] == '<'){
 3455       if(*++cp != '<')
 3456          goto jesubcmd;
 3457       if(*++cp == '@'){
 3458          n_OBSOLETE(_("`vexpr': please use @ modifier as prefix not suffix"));
 3459          f |= a_SATURATED;
 3460          ++cp;
 3461       }
 3462       if(*cp != '\0')
 3463          goto jesubcmd;
 3464       f |= a_TMP;
 3465       op = '<';
 3466       goto jnumop;
 3467    }else if(cp[0] == '>'){
 3468       if(*++cp != '>')
 3469          goto jesubcmd;
 3470       if(*++cp == '>'){
 3471          f |= a_UNSIGNED_OP;
 3472          ++cp;
 3473       }
 3474       if(*cp == '@'){
 3475          n_OBSOLETE(_("`vexpr': please use @ modifier as prefix not suffix"));
 3476          f |= a_SATURATED;
 3477          ++cp;
 3478       }
 3479       if(*cp != '\0')
 3480          goto jesubcmd;
 3481       f |= a_TMP;
 3482       op = '>';
 3483       goto jnumop;
 3484    }else if(cp[2] == '\0' && cp[1] == '@'){
 3485       n_OBSOLETE(_("`vexpr': please use @ modifier as prefix, not suffix"));
 3486       f |= a_SATURATED;
 3487       op = cp[0];
 3488       goto jnumop;
 3489    }else if(cp[0] == '@'){
 3490       f |= a_SATURATED;
 3491       op = *++cp;
 3492       if(*++cp != '\0'){
 3493          if(op != *cp)
 3494             goto jesubcmd;
 3495          switch(op){
 3496          case '<':
 3497             if(*++cp != '\0')
 3498                goto jesubcmd;
 3499             f |= a_TMP;
 3500             break;
 3501          case '>':
 3502             if(*++cp != '\0'){
 3503                if(*cp != '>' || *++cp != '\0')
 3504                   goto jesubcmd;
 3505                f |= a_UNSIGNED_OP;
 3506             }
 3507             f |= a_TMP;
 3508             break;
 3509          default:
 3510             goto jesubcmd;
 3511          }
 3512       }
 3513       goto jnumop;
 3514    }else if(is_asccaseprefix(cp, "pbase")){
 3515       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3516          goto jesynopsis;
 3517 
 3518       if(((ids = n_idec_si8_cp(&pbase, argv[1], 0, NULL)
 3519                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3520             ) != n_IDEC_STATE_CONSUMED || pbase < 2 || pbase > 36)
 3521          goto jenum_range;
 3522 
 3523       f |= a_PBASE;
 3524       op = *(argv[0] = cp = "=");
 3525       argv[1] = argv[2];
 3526       argv[2] = NULL;
 3527       goto jnumop;
 3528    }else if(is_asccaseprefix(cp, "length")){
 3529       f |= a_ISNUM | a_ISDECIMAL;
 3530       if(argv[1] == NULL || argv[2] != NULL)
 3531          goto jesynopsis;
 3532 
 3533       i = strlen(*++argv);
 3534       if(UICMP(64, i, >, SI64_MAX))
 3535          goto jestr_overflow;
 3536       lhv = (si64_t)i;
 3537    }else if(is_asccaseprefix(cp, "hash")){
 3538       f |= a_ISNUM | a_ISDECIMAL;
 3539       if(argv[1] == NULL || argv[2] != NULL)
 3540          goto jesynopsis;
 3541 
 3542       i = n_torek_hash(*++argv);
 3543       lhv = (si64_t)i;
 3544    }else if(is_asccaseprefix(cp, "find")){
 3545       f |= a_ISNUM | a_ISDECIMAL;
 3546       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3547          goto jesynopsis;
 3548 
 3549       if((cp = strstr(argv[1], argv[2])) == NULL)
 3550          goto jestr_nodata;
 3551       i = PTR2SIZE(cp - argv[1]);
 3552       if(UICMP(64, i, >, SI64_MAX))
 3553          goto jestr_overflow;
 3554       lhv = (si64_t)i;
 3555    }else if(is_asccaseprefix(cp, "ifind")){
 3556       f |= a_ISNUM | a_ISDECIMAL;
 3557       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3558          goto jesynopsis;
 3559 
 3560       if((cp = asccasestr(argv[1], argv[2])) == NULL)
 3561          goto jestr_nodata;
 3562       i = PTR2SIZE(cp - argv[1]);
 3563       if(UICMP(64, i, >, SI64_MAX))
 3564          goto jestr_overflow;
 3565       lhv = (si64_t)i;
 3566    }else if(is_asccaseprefix(cp, "substring")){
 3567       if(argv[1] == NULL || argv[2] == NULL)
 3568          goto jesynopsis;
 3569       if(argv[3] != NULL && argv[4] != NULL)
 3570          goto jesynopsis;
 3571 
 3572       i = strlen(varres = *++argv);
 3573 
 3574       if(*(cp = *++argv) == '\0')
 3575          lhv = 0;
 3576       else if((n_idec_si64_cp(&lhv, cp, 0, NULL
 3577                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3578             ) != n_IDEC_STATE_CONSUMED)
 3579          goto jestr_numrange;
 3580       if(lhv < 0){
 3581          if(UICMP(64, i, <, -lhv))
 3582             goto jesubstring_off;
 3583          lhv = i + lhv;
 3584       }
 3585       if(UICMP(64, i, >=, lhv)){
 3586          i -= lhv;
 3587          varres += lhv;
 3588       }else{
 3589 jesubstring_off:
 3590          if(n_poption & n_PO_D_V)
 3591             n_err(_("`vexpr': substring: offset argument too large: %s\n"),
 3592                n_shexp_quote_cp(argv[-1], FAL0));
 3593          f |= a_SOFTOVERFLOW;
 3594       }
 3595 
 3596       if(argv[1] != NULL){
 3597          if(*(cp = *++argv) == '\0')
 3598             lhv = 0;
 3599          else if((n_idec_si64_cp(&lhv, cp, 0, NULL
 3600                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3601                ) != n_IDEC_STATE_CONSUMED)
 3602             goto jestr_numrange;
 3603          if(lhv < 0){
 3604             if(UICMP(64, i, <, -lhv))
 3605                goto jesubstring_len;
 3606             lhv = i + lhv;
 3607          }
 3608          if(UICMP(64, i, >=, lhv)){
 3609             if(UICMP(64, i, !=, lhv))
 3610                varres = savestrbuf(varres, (size_t)lhv);
 3611          }else{
 3612 jesubstring_len:
 3613             if(n_poption & n_PO_D_V)
 3614                n_err(_("`vexpr': substring: length argument too large: %s\n"),
 3615                   n_shexp_quote_cp(argv[-2], FAL0));
 3616             f |= a_SOFTOVERFLOW;
 3617          }
 3618       }
 3619    }else if(is_asccaseprefix(cp, "trim")) Jtrim: {
 3620       struct str trim;
 3621       enum n_str_trim_flags stf;
 3622 
 3623       if(argv[1] == NULL || argv[2] != NULL)
 3624          goto jesynopsis;
 3625 
 3626       if(is_asccaseprefix(cp, "trim-front"))
 3627          stf = n_STR_TRIM_FRONT;
 3628       else if(is_asccaseprefix(cp, "trim-end"))
 3629          stf = n_STR_TRIM_END;
 3630       else
 3631          stf = n_STR_TRIM_BOTH;
 3632 
 3633       trim.l = strlen(trim.s = n_UNCONST(argv[1]));
 3634       (void)n_str_trim(&trim, stf);
 3635       varres = savestrbuf(trim.s, trim.l);
 3636    }else if(is_asccaseprefix(cp, "trim-front"))
 3637       goto Jtrim;
 3638    else if(is_asccaseprefix(cp, "trim-end"))
 3639       goto Jtrim;
 3640    else if(is_asccaseprefix(cp, "random")){
 3641       if(argv[1] == NULL || argv[2] != NULL)
 3642          goto jesynopsis;
 3643 
 3644       if((n_idec_si64_cp(&lhv, argv[1], 0, NULL
 3645                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3646             ) != n_IDEC_STATE_CONSUMED || lhv < 0 || lhv > PATH_MAX)
 3647          goto jestr_numrange;
 3648       if(lhv == 0)
 3649          lhv = NAME_MAX;
 3650       varres = n_random_create_cp((size_t)lhv, NULL);
 3651    }else if(is_asccaseprefix(cp, "file-expand")){
 3652       if(argv[1] == NULL || argv[2] != NULL)
 3653          goto jesynopsis;
 3654 
 3655       if((varres = fexpand(argv[1], FEXP_NVAR | FEXP_NOPROTO)) == NULL)
 3656          goto jestr_nodata;
 3657    }else if(is_asccaseprefix(cp, "makeprint")){
 3658       struct str sin, sout;
 3659 
 3660       if(argv[1] == NULL || argv[2] != NULL)
 3661          goto jesynopsis;
 3662 
 3663       /* XXX using strlen for `vexpr makeprint' is wrong for UTF-16 */
 3664       sin.l = strlen(sin.s = n_UNCONST(argv[1]));
 3665       makeprint(&sin, &sout);
 3666       varres = savestrbuf(sout.s, sout.l);
 3667       n_free(sout.s);
 3668    /* TODO `vexpr': (wide) string length, find, etc!! */
 3669 #ifdef HAVE_REGEX
 3670    }else if(is_asccaseprefix(cp, "regex")) Jregex:{
 3671       regmatch_t rema[1 + n_VEXPR_REGEX_MAX];
 3672       regex_t re;
 3673       int reflrv;
 3674 
 3675       f |= a_ISNUM | a_ISDECIMAL;
 3676       if(argv[1] == NULL || argv[2] == NULL ||
 3677             (argv[3] != NULL && argv[4] != NULL))
 3678          goto jesynopsis;
 3679 
 3680       reflrv = REG_EXTENDED;
 3681       if(f & a_ICASE)
 3682          reflrv |= REG_ICASE;
 3683       if((reflrv = regcomp(&re, argv[2], reflrv))){
 3684          n_err(_("`vexpr': invalid regular expression: %s: %s\n"),
 3685             n_shexp_quote_cp(argv[2], FAL0), n_regex_err_to_doc(NULL, reflrv));
 3686          assert(f & a_ERR);
 3687          n_pstate_err_no = n_ERR_INVAL;
 3688          goto jestr;
 3689       }
 3690       reflrv = regexec(&re, argv[1], n_NELEM(rema), rema, 0);
 3691       regfree(&re);
 3692       if(reflrv == REG_NOMATCH)
 3693          goto jestr_nodata;
 3694 
 3695       /* Search only?  Else replace, which is a bit */
 3696       if(argv[3] == NULL){
 3697          if(UICMP(64, rema[0].rm_so, >, SI64_MAX))
 3698             goto jestr_overflow;
 3699          lhv = (si64_t)rema[0].rm_so;
 3700       }else{
 3701          /* We need to setup some kind of pseudo macro environment for this */
 3702          struct a_amv_lostack los;
 3703          struct a_amv_mac_call_args amca;
 3704          char const **reargv;
 3705 
 3706          memset(&amca, 0, sizeof amca);
 3707          amca.amca_name = savestrbuf(&argv[1][rema[0].rm_so],
 3708                rema[0].rm_eo - rema[0].rm_so);
 3709          amca.amca_amp = a_AMV_MACKY_MACK;
 3710          for(i = 1; i < n_NELEM(rema) && rema[i].rm_so != -1; ++i)
 3711             ;
 3712          amca.amca_pospar.app_count = (ui32_t)i - 1;
 3713          amca.amca_pospar.app_not_heap = TRU1;
 3714          amca.amca_pospar.app_dat =
 3715          reargv = n_autorec_alloc(sizeof(char*) * i);
 3716          for(i = 1; i < n_NELEM(rema) && rema[i].rm_so != -1; ++i)
 3717             *reargv++ = savestrbuf(&argv[1][rema[i].rm_so],
 3718                   rema[i].rm_eo - rema[i].rm_so);
 3719          *reargv = NULL;
 3720 
 3721          memset(&los, 0, sizeof los);
 3722          hold_all_sigs(); /* TODO DISLIKE! */
 3723          los.as_global_saved = a_amv_lopts;
 3724          los.as_amcap = &amca;
 3725          los.as_up = los.as_global_saved;
 3726          a_amv_lopts = &los;
 3727 
 3728          /* C99 */{
 3729             struct str templ;
 3730             struct n_string s_b;
 3731             enum n_shexp_state shs;
 3732 
 3733             templ.s = n_UNCONST(argv[3]);
 3734             templ.l = UIZ_MAX;
 3735             shs = n_shexp_parse_token((n_SHEXP_PARSE_LOG |
 3736                   n_SHEXP_PARSE_IGNORE_EMPTY | n_SHEXP_PARSE_QUOTE_AUTO_FIXED |
 3737                   n_SHEXP_PARSE_QUOTE_AUTO_DSQ),
 3738                   n_string_creat_auto(&s_b), &templ, NULL);
 3739             if((shs & (n_SHEXP_STATE_ERR_MASK | n_SHEXP_STATE_STOP)
 3740                   ) == n_SHEXP_STATE_STOP){
 3741                varres = n_string_cp(&s_b);
 3742                n_string_drop_ownership(&s_b);
 3743             }else
 3744                varres = NULL;
 3745          }
 3746 
 3747          a_amv_lopts = los.as_global_saved;
 3748          rele_all_sigs(); /* TODO DISLIKE! */
 3749 
 3750          if(varres == NULL)
 3751             goto jestr_nodata;
 3752          f &= ~(a_ISNUM | a_ISDECIMAL);
 3753       }
 3754    }else if(is_asccaseprefix(argv[0], "iregex")){
 3755       f |= a_ICASE;
 3756       goto Jregex;
 3757 #endif /* HAVE_REGEX */
 3758    }else
 3759       goto jesubcmd;
 3760 
 3761    n_pstate_err_no = (f & a_SOFTOVERFLOW) ? n_ERR_OVERFLOW : n_ERR_NONE;
 3762    f &= ~a_ERR;
 3763 
 3764    /* Generate the variable value content for numerics.
 3765     * Anticipate in our handling below!  (Don't do needless work) */
 3766 jleave:
 3767    if((f & a_ISNUM) && ((f & (a_ISDECIMAL | a_PBASE)) || varname != NULL)){
 3768       cp = n_ienc_buf(iencbuf, lhv, (f & a_PBASE ? pbase : 10),
 3769             n_IENC_MODE_SIGNED_TYPE);
 3770       if(cp != NULL)
 3771          varres = cp;
 3772       else{
 3773          f |= a_ERR;
 3774          varres = n_empty;
 3775       }
 3776    }
 3777 
 3778    if(varname == NULL){
 3779       /* If there was no error and we are printing a numeric result, print some
 3780        * more bases for the fun of it */
 3781       if((f & (a_ERR | a_ISNUM | a_ISDECIMAL)) == a_ISNUM){
 3782          char binabuf[64 + 64 / 8 +1];
 3783          size_t j;
 3784 
 3785          for(j = 1, i = 0; i < 64; ++i){
 3786             binabuf[63 + 64 / 8 -j - i] = (lhv & ((ui64_t)1 << i)) ? '1' : '0';
 3787             if((i & 7) == 7 && i != 63){
 3788                ++j;
 3789                binabuf[63 + 64 / 8 -j - i] = ' ';
 3790             }
 3791          }
 3792          binabuf[64 + 64 / 8 -1] = '\0';
 3793 
 3794          if(fprintf(n_stdout,
 3795                   "0b %s\n0%" PRIo64 " | 0x%" PRIX64 " | %" PRId64 "\n",
 3796                   binabuf, lhv, lhv, lhv) < 0 ||
 3797                ((f & a_PBASE) && (assert(varres != NULL),
 3798                 fprintf(n_stdout, "%s\n", varres) < 0))){
 3799             n_pstate_err_no = n_err_no;
 3800             f |= a_ERR;
 3801          }
 3802       }else if(varres != NULL && fprintf(n_stdout, "%s\n", varres) < 0){
 3803          n_pstate_err_no = n_err_no;
 3804          f |= a_ERR;
 3805       }
 3806    }else if(!n_var_vset(varname, (uintptr_t)varres)){
 3807       n_pstate_err_no = n_ERR_NOTSUP;
 3808       f |= a_ERR;
 3809    }
 3810    NYD_LEAVE;
 3811    return (f & a_ERR) ? 1 : 0;
 3812 
 3813 jerr:
 3814    f = a_ERR | a_ISNUM;
 3815    lhv = -1;
 3816    goto jleave;
 3817 jesubcmd:
 3818    n_err(_("`vexpr': invalid subcommand: %s\n"),
 3819       n_shexp_quote_cp(*argv, FAL0));
 3820    n_pstate_err_no = n_ERR_INVAL;
 3821    goto jerr;
 3822 jesynopsis:
 3823    n_err(_("Synopsis: vexpr: <operator> <:argument:>\n"));
 3824    n_pstate_err_no = n_ERR_INVAL;
 3825    goto jerr;
 3826 
 3827 jenum_range:
 3828    n_err(_("`vexpr': numeric argument invalid or out of range: %s\n"),
 3829       n_shexp_quote_cp(*argv, FAL0));
 3830    n_pstate_err_no = n_ERR_RANGE;
 3831    goto jerr;
 3832 jenum_overflow:
 3833    n_err(_("`vexpr': expression overflows datatype: %" PRId64 " %c %" PRId64
 3834       "\n"), lhv, op, rhv);
 3835    n_pstate_err_no = n_ERR_OVERFLOW;
 3836    goto jerr;
 3837 
 3838 jestr_numrange:
 3839    n_err(_("`vexpr': numeric argument invalid or out of range: %s\n"),
 3840       n_shexp_quote_cp(*argv, FAL0));
 3841    n_pstate_err_no = n_ERR_RANGE;
 3842    goto jestr;
 3843 jestr_overflow:
 3844    n_err(_("`vexpr': string length or offset overflows datatype\n"));
 3845    n_pstate_err_no = n_ERR_OVERFLOW;
 3846    goto jestr;
 3847 jestr_nodata:
 3848    n_pstate_err_no = n_ERR_NODATA;
 3849    /* FALLTHRU */
 3850 jestr:
 3851    varres = n_empty;
 3852    f &= ~a_ISNUM;
 3853    f |= a_ERR;
 3854    goto jleave;
 3855 }
 3856 
 3857 FL int
 3858 c_vpospar(void *v){
 3859    struct n_cmd_arg *cap;
 3860    size_t i;
 3861    struct a_amv_pospar *appp;
 3862    enum{
 3863       a_NONE = 0,
 3864       a_ERR = 1u<<0,
 3865       a_SET = 1u<<1,
 3866       a_CLEAR = 1u<<2,
 3867       a_QUOTE = 1u<<3
 3868    } f;
 3869    char const *varres;
 3870    struct n_cmd_arg_ctx *cacp;
 3871    NYD_ENTER;
 3872 
 3873    n_pstate_err_no = n_ERR_NONE;
 3874    n_UNINIT(varres, n_empty);
 3875    cacp = v;
 3876    cap = cacp->cac_arg;
 3877 
 3878    if(is_asccaseprefix(cap->ca_arg.ca_str.s, "set"))
 3879       f = a_SET;
 3880    else if(is_asccaseprefix(cap->ca_arg.ca_str.s, "clear"))
 3881       f = a_CLEAR;
 3882    else if(is_asccaseprefix(cap->ca_arg.ca_str.s, "quote"))
 3883       f = a_QUOTE;
 3884    else{
 3885       n_err(_("`vpospar': invalid subcommand: %s\n"),
 3886          n_shexp_quote_cp(cap->ca_arg.ca_str.s, FAL0));
 3887       n_pstate_err_no = n_ERR_INVAL;
 3888       f = a_ERR;
 3889       goto jleave;
 3890    }
 3891    --cacp->cac_no;
 3892 
 3893    if((f & (a_CLEAR | a_QUOTE)) && cap->ca_next != NULL){
 3894       n_err(_("`vpospar': `%s': takes no argument\n"), cap->ca_arg.ca_str.s);
 3895       n_pstate_err_no = n_ERR_INVAL;
 3896       f = a_ERR;
 3897       goto jleave;
 3898    }
 3899 
 3900    cap = cap->ca_next;
 3901 
 3902    /* If in a macro, we need to overwrite the local instead of global argv */
 3903    appp = (a_amv_lopts != NULL) ? &a_amv_lopts->as_amcap->amca_pospar
 3904          : &a_amv_pospar;
 3905 
 3906    if(f & (a_SET | a_CLEAR)){
 3907       if(cacp->cac_vput != NULL)
 3908          n_err(_("`vpospar': `vput' only supported for `quote' subcommand\n"));
 3909       if(!appp->app_not_heap && appp->app_maxcount > 0){
 3910          for(i = appp->app_maxcount; i-- != 0;)
 3911             n_free(n_UNCONST(appp->app_dat[i]));
 3912          n_free(appp->app_dat);
 3913       }
 3914       memset(appp, 0, sizeof *appp);
 3915 
 3916       if(f & a_SET){
 3917          if((i = cacp->cac_no) > a_AMV_POSPAR_MAX){
 3918             n_err(_("`vpospar': overflow: %" PRIuZ " arguments!\n"), i);
 3919             n_pstate_err_no = n_ERR_OVERFLOW;
 3920             f = a_ERR;
 3921             goto jleave;
 3922          }
 3923 
 3924          memset(appp, 0, sizeof *appp);
 3925          if(i > 0){
 3926             appp->app_maxcount = appp->app_count = (ui16_t)i;
 3927             /* XXX Optimize: store it all in one chunk! */
 3928             ++i;
 3929             i *= sizeof *appp->app_dat;
 3930             appp->app_dat = n_alloc(i);
 3931 
 3932             for(i = 0; cap != NULL; ++i, cap = cap->ca_next){
 3933                appp->app_dat[i] = n_alloc(cap->ca_arg.ca_str.l +1);
 3934                memcpy(n_UNCONST(appp->app_dat[i]), cap->ca_arg.ca_str.s,
 3935                   cap->ca_arg.ca_str.l +1);
 3936             }
 3937 
 3938             appp->app_dat[i] = NULL;
 3939          }
 3940       }
 3941    }else{
 3942       if(appp->app_count == 0)
 3943          varres = n_empty;
 3944       else{
 3945          struct str in;
 3946          struct n_string s, *sp;
 3947          char sep1, sep2;
 3948 
 3949          sp = n_string_creat_auto(&s);
 3950 
 3951          sep1 = *ok_vlook(ifs);
 3952          sep2 = *ok_vlook(ifs_ws);
 3953          if(sep1 == sep2)
 3954             sep2 = '\0';
 3955          if(sep1 == '\0')
 3956             sep1 = ' ';
 3957 
 3958          for(i = 0; i < appp->app_count; ++i){
 3959             if(sp->s_len){
 3960                if(!n_string_can_swallow(sp, 2))
 3961                   goto jeover;
 3962                sp = n_string_push_c(sp, sep1);
 3963                if(sep2 != '\0')
 3964                   sp = n_string_push_c(sp, sep2);
 3965             }
 3966             in.l = strlen(in.s = n_UNCONST(appp->app_dat[i + appp->app_idx]));
 3967 
 3968             if(!n_string_can_swallow(sp, in.l)){
 3969 jeover:
 3970                n_err(_("`vpospar': overflow: string too long!\n"));
 3971                n_pstate_err_no = n_ERR_OVERFLOW;
 3972                f = a_ERR;
 3973                goto jleave;
 3974             }
 3975             sp = n_shexp_quote(sp, &in, TRU1);
 3976          }
 3977 
 3978          varres = n_string_cp(sp);
 3979       }
 3980 
 3981       if(cacp->cac_vput == NULL){
 3982          if(fprintf(n_stdout, "%s\n", varres) < 0){
 3983             n_pstate_err_no = n_err_no;
 3984             f |= a_ERR;
 3985          }
 3986       }else if(!n_var_vset(cacp->cac_vput, (uintptr_t)varres)){
 3987          n_pstate_err_no = n_ERR_NOTSUP;
 3988          f |= a_ERR;
 3989       }
 3990    }
 3991 jleave:
 3992    NYD_LEAVE;
 3993    return (f & a_ERR) ? 1 : 0;
 3994 }
 3995 
 3996 /* s-it-mode */