"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/accmacvar.c" (25 Mar 2018, 122404 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

    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_charset_7bit:
  928       case ok_v_charset_8bit:
  929       case ok_v_charset_unknown_8bit:
  930       case ok_v_ttycharset:
  931          if((*val = n_iconv_normalize_name(*val)) == NULL)
  932             ok = FAL0;
  933          break;
  934       case ok_v_customhdr:{
  935          char const *vp;
  936          char *buf;
  937          struct n_header_field *hflp, **hflpp, *hfp;
  938          NYD_ENTER;
  939 
  940          buf = savestr(*val);
  941          hflp = NULL;
  942          hflpp = &hflp;
  943 
  944          while((vp = n_strsep_esc(&buf, ',', TRU1)) != NULL){
  945             if(!n_header_add_custom(hflpp, vp, TRU1)){
  946                emsg = N_("Invalid *customhdr* entry: %s\n");
  947                ok = FAL0;
  948                break;
  949             }
  950             hflpp = &(*hflpp)->hf_next;
  951          }
  952 
  953          hflpp = ok ? &n_customhdr_list : &hflp;
  954          while((hfp = *hflpp) != NULL){
  955             *hflpp = hfp->hf_next;
  956             n_free(hfp);
  957          }
  958          if(!ok)
  959             goto jerr;
  960          n_customhdr_list = hflp;
  961       }  break;
  962       case ok_v_from:
  963       case ok_v_sender:{
  964          struct name *np;
  965 
  966          if((np = lextract(*val, GEXTRA | GFULL)) == NULL){
  967 jefrom:
  968             emsg = N_("*from* / *sender*: invalid  address(es): %s\n");
  969             goto jerr;
  970          }else if(okey == ok_v_sender && np->n_flink != NULL){
  971             emsg = N_("*sender*: may not contain multiple addresses: %s\n");
  972             goto jerr;
  973          }else for(; np != NULL; np = np->n_flink)
  974             if(is_addr_invalid(np, EACM_STRICT | EACM_NOLOG | EACM_NONAME))
  975                goto jefrom;
  976       }  break;
  977       case ok_v_HOME:
  978          /* Note this gets called from main.c during initialization, and they
  979           * simply set this to pw_dir as a fallback: don't verify _that_ call.
  980           * See main.c! */
  981          if(!(n_pstate & n_PS_ROOT) && !n_is_dir(*val, TRUM1)){
  982             emsg = N_("$HOME is not a directory or not accessible: %s\n");
  983             goto jerr;
  984          }
  985          break;
  986       case ok_v_hostname:
  987       case ok_v_smtp_hostname:
  988 #ifdef HAVE_IDNA
  989          if(**val != '\0'){
  990             struct n_string cnv;
  991 
  992             n_string_creat_auto(&cnv);
  993             if(!n_idna_to_ascii(&cnv, *val, UIZ_MAX)){
  994                /*n_string_gut(&res);*/
  995                emsg = N_("*hostname*/*smtp_hostname*: "
  996                      "IDNA encoding failed: %s\n");
  997                goto jerr;
  998             }
  999             *val = n_string_cp(&cnv);
 1000             /*n_string_drop_ownership(&cnv);*/
 1001          }
 1002 #endif
 1003          break;
 1004       case ok_v_quote_chars:{
 1005          char c;
 1006          char const *cp;
 1007 
 1008          for(cp = *val; (c = *cp++) != '\0';)
 1009             if(!asciichar(c) || blankspacechar(c)){
 1010                ok = FAL0;
 1011                break;
 1012             }
 1013       }  break;
 1014       case ok_v_sendcharsets:{
 1015          struct n_string s, *sp = &s;
 1016          char *csv, *cp;
 1017 
 1018          sp = n_string_creat_auto(sp);
 1019          csv = savestr(*val);
 1020 
 1021          while((cp = n_strsep(&csv, ',', TRU1)) != NULL){
 1022             if((cp = n_iconv_normalize_name(cp)) == NULL){
 1023                ok = FAL0;
 1024                break;
 1025             }
 1026             if(sp->s_len > 0)
 1027                sp = n_string_push_c(sp, ',');
 1028             sp = n_string_push_cp(sp, cp);
 1029          }
 1030 
 1031          *val = n_string_cp(sp);
 1032          /* n_string_drop_ownership(sp); */
 1033       }  break;
 1034       case ok_v_TMPDIR:
 1035          if(!n_is_dir(*val, TRU1)){
 1036             emsg = N_("$TMPDIR is not a directory or not accessible: %s\n");
 1037             goto jerr;
 1038          }
 1039          break;
 1040       case ok_v_umask:
 1041          if(**val != '\0'){
 1042             ui64_t uib;
 1043 
 1044             n_idec_ui64_cp(&uib, *val, 0, NULL);
 1045             if(uib & ~0777u){ /* (is valid _VF_POSNUM) */
 1046                emsg = N_("Invalid *umask* setting: %s\n");
 1047                goto jerr;
 1048             }
 1049          }
 1050          break;
 1051       }
 1052    }else if(avvm == a_AMV_VIP_SET_POST){
 1053       switch(okey){
 1054       default:
 1055          break;
 1056       case ok_b_ask:
 1057          ok_bset(asksub);
 1058          break;
 1059       case ok_b_debug:
 1060          n_poption |= n_PO_DEBUG;
 1061          break;
 1062       case ok_v_HOME:
 1063          /* Invalidate any resolved folder then, too
 1064           * FALLTHRU */
 1065       case ok_v_folder:
 1066          n_PS_ROOT_BLOCK(ok_vclear(folder_resolved));
 1067          break;
 1068       case ok_v_ifs:{
 1069          char *x_b, *x, c;
 1070          char const *cp;
 1071 
 1072          cp = *val;
 1073          x_b = x = n_autorec_alloc(strlen(cp) +1);
 1074          while((c = *cp++) != '\0')
 1075             if(spacechar(c))
 1076                *x++ = c;
 1077          *x = '\0';
 1078          n_PS_ROOT_BLOCK(ok_vset(ifs_ws, x_b));
 1079       }  break;
 1080 #ifdef HAVE_SETLOCALE
 1081       case ok_v_LANG:
 1082       case ok_v_LC_ALL:
 1083       case ok_v_LC_CTYPE:
 1084          n_locale_init();
 1085          break;
 1086 #endif
 1087       case ok_b_memdebug:
 1088          n_poption |= n_PO_MEMDEBUG;
 1089          break;
 1090       case ok_b_POSIXLY_CORRECT: /* <-> *posix* */
 1091          if(!(n_pstate & n_PS_ROOT))
 1092             n_PS_ROOT_BLOCK(ok_bset(posix));
 1093          break;
 1094       case ok_b_posix: /* <-> $POSIXLY_CORRECT */
 1095          if(!(n_pstate & n_PS_ROOT))
 1096             n_PS_ROOT_BLOCK(ok_bset(POSIXLY_CORRECT));
 1097          break;
 1098       case ok_b_skipemptybody:
 1099          n_poption |= n_PO_E_FLAG;
 1100          break;
 1101       case ok_b_typescript_mode:
 1102          ok_bset(colour_disable);
 1103          ok_bset(line_editor_disable);
 1104          if(!(n_psonce & n_PSO_STARTED))
 1105             ok_bset(termcap_disable);
 1106          break;
 1107       case ok_v_umask:
 1108          if(**val != '\0'){
 1109             ui64_t uib;
 1110 
 1111             n_idec_ui64_cp(&uib, *val, 0, NULL);
 1112             umask((mode_t)uib);
 1113          }
 1114          break;
 1115       case ok_b_verbose:
 1116          n_poption |= (n_poption & n_PO_VERB) ? n_PO_VERBVERB : n_PO_VERB;
 1117          break;
 1118       }
 1119    }else{
 1120       switch(okey){
 1121       default:
 1122          break;
 1123       case ok_b_ask:
 1124          ok_bclear(asksub);
 1125          break;
 1126       case ok_b_debug:
 1127          n_poption &= ~n_PO_DEBUG;
 1128          break;
 1129       case ok_v_customhdr:{
 1130          struct n_header_field *hfp;
 1131 
 1132          while((hfp = n_customhdr_list) != NULL){
 1133             n_customhdr_list = hfp->hf_next;
 1134             n_free(hfp);
 1135          }
 1136       }  break;
 1137       case ok_v_HOME:
 1138          /* Invalidate any resolved folder then, too
 1139           * FALLTHRU */
 1140       case ok_v_folder:
 1141          n_PS_ROOT_BLOCK(ok_vclear(folder_resolved));
 1142          break;
 1143       case ok_b_memdebug:
 1144          n_poption &= ~n_PO_MEMDEBUG;
 1145          break;
 1146       case ok_b_POSIXLY_CORRECT: /* <-> *posix* */
 1147          if(!(n_pstate & n_PS_ROOT))
 1148             n_PS_ROOT_BLOCK(ok_bclear(posix));
 1149          break;
 1150       case ok_b_posix: /* <-> $POSIXLY_CORRECT */
 1151          if(!(n_pstate & n_PS_ROOT))
 1152             n_PS_ROOT_BLOCK(ok_bclear(POSIXLY_CORRECT));
 1153          break;
 1154       case ok_b_skipemptybody:
 1155          n_poption &= ~n_PO_E_FLAG;
 1156          break;
 1157       case ok_b_verbose:
 1158          n_poption &= ~(n_PO_VERB | n_PO_VERBVERB);
 1159          break;
 1160       }
 1161    }
 1162 
 1163 jleave:
 1164    NYD2_LEAVE;
 1165    return ok;
 1166 jerr:
 1167    n_err(V_(emsg), n_shexp_quote_cp(*val, FAL0));
 1168    ok = FAL0;
 1169    goto jleave;
 1170 }
 1171 
 1172 static bool_t
 1173 a_amv_var_check_num(char const *val, bool_t posnum){
 1174    /* TODO The internal/environment  variables which are num= or posnum= should
 1175     * TODO gain special lookup functions, or the return should be void* and
 1176     * TODO castable to integer; i.e. no more strtoX() should be needed.
 1177     * TODO I.e., the result of this function should instead be stored */
 1178    bool_t rv;
 1179    NYD2_ENTER;
 1180 
 1181    rv = TRU1;
 1182 
 1183    if(*val != '\0'){ /* Would be _VF_NOTEMPTY if not allowed */
 1184       ui64_t uib;
 1185       enum n_idec_state ids;
 1186 
 1187       ids = n_idec_cp(&uib, val, 0,
 1188             (n_IDEC_MODE_LIMIT_32BIT |
 1189              (posnum ?  n_IDEC_MODE_SIGNED_TYPE : n_IDEC_MODE_NONE)), NULL);
 1190       if((ids & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1191             ) != n_IDEC_STATE_CONSUMED)
 1192          rv = FAL0;
 1193       /* TODO Unless we store integers we need to look and forbid, because
 1194        * TODO callee may not be able to swallow, e.g., "-1" */
 1195       if(posnum && (ids & n_IDEC_STATE_SEEN_MINUS))
 1196          rv = FAL0;
 1197    }
 1198    NYD2_LEAVE;
 1199    return rv;
 1200 }
 1201 
 1202 static char const *
 1203 a_amv_var_canonify(char const *vn){
 1204    NYD2_ENTER;
 1205    if(!upperchar(*vn)){
 1206       char const *vp;
 1207 
 1208       for(vp = vn; *vp != '\0' && *vp != '@'; ++vp)
 1209          ;
 1210       vn = (*vp == '@') ? i_strdup(vn) : vn;
 1211    }
 1212    NYD2_LEAVE;
 1213    return vn;
 1214 }
 1215 
 1216 static bool_t
 1217 a_amv_var_revlookup(struct a_amv_var_carrier *avcp, char const *name){
 1218    ui32_t hash, i, j;
 1219    struct a_amv_var_map const *avmp;
 1220    char c;
 1221    NYD2_ENTER;
 1222 
 1223    /* It may be a special a.k.a. macro-local or one-letter parameter */
 1224    c = name[0];
 1225    if(n_UNLIKELY(digitchar(c))){
 1226       /* (Inline dec. atoi, ugh) */
 1227       for(j = (ui8_t)c - '0', i = 1;; ++i){
 1228          c = name[i];
 1229          if(c == '\0')
 1230             break;
 1231          if(!digitchar(c))
 1232             goto jno_special_param;
 1233          j = j * 10 + (ui8_t)c - '0';
 1234       }
 1235       if(j <= a_AMV_POSPAR_MAX){
 1236          avcp->avc_special_cat = a_AMV_VSC_POSPAR;
 1237          goto jspecial_param;
 1238       }
 1239    }else if(n_UNLIKELY(name[1] == '\0')){
 1240       switch(c){
 1241       case '?':
 1242       case '!':
 1243          avcp->avc_special_cat = a_AMV_VSC_GLOBAL;
 1244          j = (c == '?') ? a_AMV_VST_QM : a_AMV_VST_EM;
 1245          goto jspecial_param;
 1246       case '^':
 1247          goto jmultiplex;
 1248       case '*':
 1249          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1250          j = a_AMV_VST_STAR;
 1251          goto jspecial_param;
 1252       case '@':
 1253          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1254          j = a_AMV_VST_AT;
 1255          goto jspecial_param;
 1256       case '#':
 1257          avcp->avc_special_cat = a_AMV_VSC_POSPAR_ENV;
 1258          j = a_AMV_VST_NOSIGN;
 1259          goto jspecial_param;
 1260       default:
 1261          break;
 1262       }
 1263    }else if(c == '^'){
 1264 jmultiplex:
 1265       avcp->avc_special_cat = a_AMV_VSC_MULTIPLEX;
 1266       j = a_AMV_VST_CACC;
 1267       goto jspecial_param;
 1268    }
 1269 
 1270    /* Normal reverse lookup, walk over the hashtable */
 1271 jno_special_param:
 1272    avcp->avc_special_cat = a_AMV_VSC_NONE;
 1273    avcp->avc_name = name = a_amv_var_canonify(name);
 1274    avcp->avc_hash = hash = a_AMV_NAME2HASH(name);
 1275 
 1276    for(i = hash % a_AMV_VAR_REV_PRIME, j = 0; j <= a_AMV_VAR_REV_LONGEST; ++j){
 1277       ui32_t x;
 1278 
 1279       if((x = a_amv_var_revmap[i]) == a_AMV_VAR_REV_ILL)
 1280          break;
 1281 
 1282       avmp = &a_amv_var_map[x];
 1283       if(avmp->avm_hash == hash &&
 1284             !strcmp(&a_amv_var_names[avmp->avm_keyoff], name)){
 1285          avcp->avc_map = avmp;
 1286          avcp->avc_okey = (enum okeys)x;
 1287          goto jleave;
 1288       }
 1289 
 1290       if(++i == a_AMV_VAR_REV_PRIME){
 1291 #ifdef a_AMV_VAR_REV_WRAPAROUND
 1292          i = 0;
 1293 #else
 1294          break;
 1295 #endif
 1296       }
 1297    }
 1298 
 1299    avcp->avc_map = NULL;
 1300    avcp = NULL;
 1301 jleave:
 1302    assert(avcp == NULL || avcp->avc_map != NULL ||
 1303       avcp->avc_special_cat == a_AMV_VSC_NONE);
 1304    NYD2_LEAVE;
 1305    return (avcp != NULL);
 1306 
 1307    /* All these are mapped to *--special-param* */
 1308 jspecial_param:
 1309    avcp->avc_name = name;
 1310    avcp->avc_special_prop = (ui16_t)j;
 1311    avmp = &a_amv_var_map[a_AMV_VAR__SPECIAL_PARAM_MAP_IDX];
 1312    avcp->avc_hash = avmp->avm_hash;
 1313    avcp->avc_map = avmp;
 1314    avcp->avc_okey = ok_v___special_param;
 1315    goto jleave;
 1316 }
 1317 
 1318 static bool_t
 1319 a_amv_var_lookup(struct a_amv_var_carrier *avcp,
 1320       enum a_amv_var_lookup_flags avlf){
 1321    size_t i;
 1322    char const *cp;
 1323    struct a_amv_var_map const *avmp;
 1324    struct a_amv_var *avp;
 1325    NYD2_ENTER;
 1326 
 1327    assert(!(avlf & a_AMV_VLOOK_LOCAL_ONLY) || (avlf & a_AMV_VLOOK_LOCAL));
 1328    assert(!(avlf & a_AMV_VLOOK_I3VAL_NONEW_REPORT) ||
 1329       (avlf & a_AMV_VLOOK_I3VAL_NONEW));
 1330 
 1331    /* C99 */{
 1332       struct a_amv_var **avpp, *lavp;
 1333 
 1334       avcp->avc_prime = a_AMV_HASH2PRIME(avcp->avc_hash);
 1335 
 1336       /* Optionally macro-`local' variables first */
 1337       if(avlf & a_AMV_VLOOK_LOCAL){
 1338          if(a_amv_lopts != NULL &&
 1339                (avpp = *a_amv_lopts->as_amcap->amca_local_vars) != NULL){
 1340             avpp += avcp->avc_prime;
 1341 
 1342             for(lavp = NULL, avp = *avpp; avp != NULL;
 1343                   lavp = avp, avp = avp->av_link)
 1344                if(!strcmp(avp->av_name, avcp->avc_name)){
 1345                   /* Relink as head, hope it "sorts on usage" over time.
 1346                    * The code relies on this behaviour! */
 1347                   if(lavp != NULL){
 1348                      lavp->av_link = avp->av_link;
 1349                      avp->av_link = *avpp;
 1350                      *avpp = avp;
 1351                   }
 1352                   goto jleave;
 1353                }
 1354          }
 1355 
 1356          if(avlf & a_AMV_VLOOK_LOCAL_ONLY)
 1357             goto jerr;
 1358       }
 1359 
 1360       /* Global variable map */
 1361       avpp = &a_amv_vars[avcp->avc_prime];
 1362 
 1363       for(lavp = NULL, avp = *avpp; avp != NULL; lavp = avp, avp = avp->av_link)
 1364          if(!strcmp(avp->av_name, avcp->avc_name)){
 1365             /* Relink as head, hope it "sorts on usage" over time.
 1366              * The code relies on this behaviour! */
 1367             if(lavp != NULL){
 1368                lavp->av_link = avp->av_link;
 1369                avp->av_link = *avpp;
 1370                *avpp = avp;
 1371             }
 1372 
 1373             /* If this setting has been established via -S and we still have
 1374              * not reached the _STARTED_CONFIG program state, it may have been
 1375              * an explicit "clearance" that is to be treated as unset.
 1376              * Because that is a special condition that (has been hacked in
 1377              * later and) needs to be encapsulated in lower levels, but not
 1378              * of interest if _set() or _clear() called us */
 1379             switch(avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK){
 1380             case a_AMV_VF_EXT_FROZEN | a_AMV_VF_EXT_FROZEN_UNSET:
 1381                if(!(avlf & a_AMV_VLOOK_I3VAL_NONEW)){
 1382                   avcp->avc_var = avp;
 1383                   avp = NULL;
 1384                   goto j_leave;
 1385                }
 1386                /* FALLTHRU */
 1387             default:
 1388                break;
 1389             }
 1390             goto jleave;
 1391          }
 1392    }
 1393 
 1394    /* If this is not an assembled variable we need to consider some special
 1395     * initialization cases and eventually create the variable anew */
 1396    if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
 1397       /* Does it have an import-from-environment flag? */
 1398       if(n_UNLIKELY((avmp->avm_flags & (a_AMV_VF_IMPORT | a_AMV_VF_ENV)) != 0)){
 1399          if(n_LIKELY((cp = getenv(avcp->avc_name)) != NULL)){
 1400             /* May be better not to use that one, though? */
 1401             /* TODO Outsource the tests into a _shared_ test function! */
 1402             bool_t isempty, isbltin;
 1403 
 1404             isempty = (*cp == '\0' &&
 1405                   (avmp->avm_flags & a_AMV_VF_NOTEMPTY) != 0);
 1406             isbltin = ((avmp->avm_flags & (a_AMV_VF_I3VAL | a_AMV_VF_DEFVAL)
 1407                   ) != 0);
 1408 
 1409             if(n_UNLIKELY(isempty)){
 1410                n_err(_("Environment variable must not be empty: %s\n"),
 1411                   avcp->avc_name);
 1412                if(!isbltin)
 1413                   goto jerr;
 1414             }else if(n_LIKELY(*cp != '\0')){
 1415                if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
 1416                      !a_amv_var_check_num(cp, FAL0))){
 1417                   n_err(_("Environment variable value not a number "
 1418                      "or out of range: %s\n"), avcp->avc_name);
 1419                   goto jerr;
 1420                }
 1421                if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
 1422                      !a_amv_var_check_num(cp, TRU1))){
 1423                   n_err(_("Environment variable value not a number, "
 1424                      "negative or out of range: %s\n"), avcp->avc_name);
 1425                   goto jerr;
 1426                }
 1427                goto jnewval;
 1428             }else
 1429                goto jnewval;
 1430          }
 1431       }
 1432 
 1433       /* A first-time init switch is to be handled now and here */
 1434       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_I3VAL) != 0)){
 1435          static struct a_amv_var_defval const **arr,
 1436             *arr_base[a_AMV_VAR_I3VALS_CNT +1];
 1437 
 1438          if(arr == NULL){
 1439             arr = &arr_base[0];
 1440             arr[i = a_AMV_VAR_I3VALS_CNT] = NULL;
 1441             while(i-- > 0)
 1442                arr[i] = &a_amv_var_i3vals[i];
 1443          }
 1444 
 1445          for(i = 0; arr[i] != NULL; ++i)
 1446             if(arr[i]->avdv_okey == avcp->avc_okey){
 1447                cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_1
 1448                      : arr[i]->avdv_value;
 1449                /* Remove this entry, hope entire block becomes no-op asap */
 1450                do
 1451                   arr[i] = arr[i + 1];
 1452                while(arr[i++] != NULL);
 1453 
 1454                if(!(avlf & a_AMV_VLOOK_I3VAL_NONEW))
 1455                   goto jnewval;
 1456                if(avlf & a_AMV_VLOOK_I3VAL_NONEW_REPORT)
 1457                   avp = (struct a_amv_var*)-1;
 1458                goto jleave;
 1459             }
 1460       }
 1461 
 1462       /* */
 1463 jdefval:
 1464       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_DEFVAL) != 0){
 1465          for(i = 0; i < a_AMV_VAR_DEFVALS_CNT; ++i)
 1466             if(a_amv_var_defvals[i].avdv_okey == avcp->avc_okey){
 1467                cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_1
 1468                      : a_amv_var_defvals[i].avdv_value;
 1469                goto jnewval;
 1470             }
 1471       }
 1472 
 1473       /* The virtual variables */
 1474       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIRT) != 0)){
 1475          for(i = 0; i < a_AMV_VAR_VIRTS_CNT; ++i)
 1476             if(a_amv_var_virts[i].avv_okey == avcp->avc_okey){
 1477                avp = n_UNCONST(a_amv_var_virts[i].avv_var);
 1478                goto jleave;
 1479             }
 1480          /* Not reached */
 1481       }
 1482    }
 1483 
 1484 jerr:
 1485    avp = NULL;
 1486 jleave:
 1487    avcp->avc_var = avp;
 1488 j_leave:
 1489    NYD2_LEAVE;
 1490    return (avp != NULL);
 1491 
 1492 jnewval:
 1493    /* E.g., $TMPDIR may be set to non-existent, so we need to be able to catch
 1494     * that and redirect to a possible default value */
 1495    if((avmp->avm_flags & a_AMV_VF_VIP) &&
 1496          !a_amv_var_check_vips(a_AMV_VIP_SET_PRE, avcp->avc_okey, &cp)){
 1497 #ifdef HAVE_SETENV
 1498       if(avmp->avm_flags & (a_AMV_VF_IMPORT | a_AMV_VF_ENV))
 1499          unsetenv(avcp->avc_name);
 1500 #endif
 1501       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_DEFVAL) != 0)
 1502          goto jdefval;
 1503       goto jerr;
 1504    }else{
 1505       struct a_amv_var **avpp;
 1506       size_t l;
 1507 
 1508       l = strlen(avcp->avc_name) +1;
 1509       avcp->avc_var =
 1510       avp = n_calloc(1, n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1511       avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
 1512       *avpp = avp;
 1513       avp->av_flags = avmp->avm_flags;
 1514       avp->av_value = a_amv_var_copy(cp);
 1515       memcpy(avp->av_name, avcp->avc_name, l);
 1516 
 1517       if(avp->av_flags & a_AMV_VF_ENV)
 1518          a_amv_var__putenv(avcp, avp);
 1519       if(avmp->avm_flags & a_AMV_VF_VIP)
 1520          a_amv_var_check_vips(a_AMV_VIP_SET_POST, avcp->avc_okey, &cp);
 1521       goto jleave;
 1522    }
 1523 }
 1524 
 1525 static char const *
 1526 a_amv_var_vsc_global(struct a_amv_var_carrier *avcp){
 1527    char itoabuf[32];
 1528    char const *rv;
 1529    si32_t *ep;
 1530    struct a_amv_var_map const *avmp;
 1531    NYD2_ENTER;
 1532 
 1533    /* Not function local, TODO but lazy evaluted for now */
 1534    if(avcp->avc_special_prop == a_AMV_VST_QM){
 1535       avmp = &a_amv_var_map[a_AMV_VAR__QM_MAP_IDX];
 1536       avcp->avc_okey = ok_v___qm;
 1537       ep = &n_pstate_ex_no;
 1538    }else{
 1539       avmp = &a_amv_var_map[a_AMV_VAR__EM_MAP_IDX];
 1540       avcp->avc_okey = ok_v___em;
 1541       ep = &n_pstate_err_no;
 1542    }
 1543 
 1544    /* XXX Oh heaven, we are responsible to ensure that $?/! is up-to-date
 1545     * TODO we could num=1 ok_v___[qe]m, but the thing is still a string
 1546     * TODO and thus conversion takes places over and over again; also
 1547     * TODO for now that would have to occur before we set _that_ value
 1548     * TODO so let's special treat it until we store ints as such */
 1549    switch(*ep){
 1550    case 0: rv = n_0; break;
 1551    case 1: rv = n_1; break;
 1552    default:
 1553       snprintf(itoabuf, sizeof itoabuf, "%d", *ep);
 1554       rv = itoabuf;
 1555       break;
 1556    }
 1557    n_PS_ROOT_BLOCK(n_var_okset(avcp->avc_okey, (uintptr_t)rv));
 1558 
 1559    avcp->avc_hash = avmp->avm_hash;
 1560    avcp->avc_map = avmp;
 1561    rv = a_amv_var_lookup(avcp, a_AMV_VLOOK_NONE)
 1562          ? avcp->avc_var->av_value : NULL;
 1563    NYD2_LEAVE;
 1564    return rv;
 1565 }
 1566 
 1567 static char const *
 1568 a_amv_var_vsc_multiplex(struct a_amv_var_carrier *avcp){
 1569    char itoabuf[32];
 1570    si32_t e;
 1571    size_t i;
 1572    char const *rv;
 1573    NYD2_ENTER;
 1574 
 1575    i = strlen(rv = &avcp->avc_name[1]);
 1576 
 1577    /* ERR, ERRDOC, ERRNAME, plus *-NAME variants */
 1578    if(rv[0] == 'E' && i >= 3 && rv[1] == 'R' && rv[2] == 'R'){
 1579       if(i == 3){
 1580          e = n_pstate_err_no;
 1581          goto jeno;
 1582       }else if(rv[3] == '-'){
 1583          e = n_err_from_name(&rv[4]);
 1584 jeno:
 1585          switch(e){
 1586          case 0: rv = n_0; break;
 1587          case 1: rv = n_1; break;
 1588          default:
 1589             snprintf(itoabuf, sizeof itoabuf, "%d", e);
 1590             rv = savestr(itoabuf); /* XXX yet, cannot do numbers */
 1591             break;
 1592          }
 1593          goto jleave;
 1594       }else if(i >= 6){
 1595          if(!memcmp(&rv[3], "DOC", 3)){
 1596             rv += 6;
 1597             switch(*rv){
 1598             case '\0': e = n_pstate_err_no; break;
 1599             case '-': e = n_err_from_name(&rv[1]); break;
 1600             default: goto jerr;
 1601             }
 1602             rv = n_err_to_doc(e);
 1603             goto jleave;
 1604          }else if(i >= 7 && !memcmp(&rv[3], "NAME", 4)){
 1605             rv += 7;
 1606             switch(*rv){
 1607             case '\0': e = n_pstate_err_no; break;
 1608             case '-': e = n_err_from_name(&rv[1]); break;
 1609             default: goto jerr;
 1610             }
 1611             rv = n_err_to_name(e);
 1612             goto jleave;
 1613          }
 1614       }
 1615    }
 1616 
 1617 jerr:
 1618    rv = NULL;
 1619 jleave:
 1620    NYD2_LEAVE;
 1621    return rv;
 1622 }
 1623 
 1624 static char const *
 1625 a_amv_var_vsc_pospar(struct a_amv_var_carrier *avcp){
 1626    size_t i, j;
 1627    ui16_t argc;
 1628    char const *rv, **argv;
 1629    NYD2_ENTER;
 1630 
 1631    rv = NULL;
 1632 
 1633    /* If in a macro/xy.. */
 1634    if(a_amv_lopts != NULL){
 1635       bool_t ismacky;
 1636       struct a_amv_mac_call_args *amcap;
 1637 
 1638       amcap = a_amv_lopts->as_amcap;
 1639       argc = amcap->amca_pospar.app_count;
 1640       argv = amcap->amca_pospar.app_dat;
 1641       argv += amcap->amca_pospar.app_idx;
 1642 
 1643       /* ..in a `call'ed macro only, to be exact.  Or in a_AMV_MACKY_MACK */
 1644       if(!(ismacky = (amcap->amca_amp == a_AMV_MACKY_MACK)) &&
 1645             (amcap->amca_ps_hook_mask ||
 1646              (assert(amcap->amca_amp != NULL),
 1647               (amcap->amca_amp->am_flags & a_AMV_MF_TYPE_MASK
 1648                ) == a_AMV_MF_ACCOUNT)))
 1649          goto jleave;
 1650 
 1651       if(avcp->avc_special_cat == a_AMV_VSC_POSPAR){
 1652          if(avcp->avc_special_prop > 0){
 1653             if(argc >= avcp->avc_special_prop)
 1654                rv = argv[avcp->avc_special_prop - 1];
 1655          }else if(ismacky)
 1656             rv = amcap->amca_name;
 1657          else
 1658             rv = (a_amv_lopts->as_up != NULL
 1659                   ? a_amv_lopts->as_up->as_amcap->amca_name : n_empty);
 1660          goto jleave;
 1661       }
 1662       /* MACKY_MACK doesn't know about [*@#] */
 1663       /*else*/ if(ismacky){
 1664          if(n_poption & n_PO_D_V)
 1665             n_err(_("Cannot use $*/$@/$# in this context: %s\n"),
 1666                n_shexp_quote_cp(avcp->avc_name, FAL0));
 1667          goto jleave;
 1668       }
 1669    }else{
 1670       argc = a_amv_pospar.app_count;
 1671       argv = a_amv_pospar.app_dat;
 1672       argv += a_amv_pospar.app_idx;
 1673 
 1674       if(avcp->avc_special_cat == a_AMV_VSC_POSPAR){
 1675          if(avcp->avc_special_prop > 0){
 1676             if(argc >= avcp->avc_special_prop)
 1677                rv = argv[avcp->avc_special_prop - 1];
 1678          }else
 1679             rv = n_progname;
 1680          goto jleave;
 1681       }
 1682    }
 1683 
 1684    switch(avcp->avc_special_prop){ /* XXX OPTIMIZE */
 1685    case a_AMV_VST_STAR:{
 1686       char sep;
 1687 
 1688       sep = *ok_vlook(ifs);
 1689       if(0){
 1690    case a_AMV_VST_AT:
 1691          sep = ' ';
 1692       }
 1693       for(i = j = 0; i < argc; ++i)
 1694          j += strlen(argv[i]) + 1;
 1695       if(j == 0)
 1696          rv = n_empty;
 1697       else{
 1698          char *cp;
 1699 
 1700          rv = cp = n_autorec_alloc(j);
 1701          for(i = j = 0; i < argc; ++i){
 1702             j = strlen(argv[i]);
 1703             memcpy(cp, argv[i], j);
 1704             cp += j;
 1705             if(sep != '\0')
 1706                *cp++ = sep;
 1707          }
 1708          if(sep != '\0')
 1709             --cp;
 1710          *cp = '\0';
 1711       }
 1712       }break;
 1713    case a_AMV_VST_NOSIGN:{
 1714       char *cp;
 1715 
 1716       rv = cp = n_autorec_alloc(sizeof("65535"));
 1717       snprintf(cp, sizeof("65535"), "%hu", argc);
 1718       }break;
 1719    default:
 1720       rv = n_empty;
 1721       break;
 1722    }
 1723 jleave:
 1724    NYD2_LEAVE;
 1725    return rv;
 1726 }
 1727 
 1728 static bool_t
 1729 a_amv_var_set(struct a_amv_var_carrier *avcp, char const *value,
 1730       enum a_amv_var_setclr_flags avscf){
 1731    struct a_amv_var *avp;
 1732    char *oval;
 1733    struct a_amv_var_map const *avmp;
 1734    bool_t rv;
 1735    NYD2_ENTER;
 1736 
 1737    if(value == NULL){
 1738       rv = a_amv_var_clear(avcp, avscf);
 1739       goto jleave;
 1740    }
 1741 
 1742    if((avmp = avcp->avc_map) != NULL){
 1743       rv = FAL0;
 1744 
 1745       /* Validity checks */
 1746       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_RDONLY) != 0 &&
 1747             !(n_pstate & n_PS_ROOT))){
 1748          value = N_("Variable is read-only: %s\n");
 1749          goto jeavmp;
 1750       }
 1751       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NOTEMPTY) && *value == '\0')){
 1752          value = N_("Variable must not be empty: %s\n");
 1753          goto jeavmp;
 1754       }
 1755       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
 1756             !a_amv_var_check_num(value, FAL0))){
 1757          value = N_("Variable value not a number or out of range: %s\n");
 1758          goto jeavmp;
 1759       }
 1760       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
 1761             !a_amv_var_check_num(value, TRU1))){
 1762          value = _("Variable value not a number, negative, "
 1763                "or out of range: %s\n");
 1764          goto jeavmp;
 1765       }
 1766 
 1767       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_IMPORT) != 0 &&
 1768             !(n_psonce & n_PSO_STARTED) && !(n_pstate & n_PS_ROOT))){
 1769          value = N_("Variable cannot be set in a resource file: %s\n");
 1770          goto jeavmp;
 1771       }
 1772 
 1773       /* Any more complicated inter-dependency? */
 1774       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIP) != 0 &&
 1775             !a_amv_var_check_vips(a_AMV_VIP_SET_PRE, avcp->avc_okey, &value))){
 1776          value = N_("Assignment of variable aborted: %s\n");
 1777 jeavmp:
 1778          n_err(V_(value), avcp->avc_name);
 1779          goto jleave;
 1780       }
 1781 
 1782       /* Transformations */
 1783       if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_LOWER)){
 1784          char c;
 1785 
 1786          oval = savestr(value);
 1787          value = oval;
 1788          for(; (c = *oval) != '\0'; ++oval)
 1789             *oval = lowerconv(c);
 1790       }
 1791 
 1792       /* Obsoletion warning */
 1793       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_OBSOLETE) != 0))
 1794          n_OBSOLETE2(_("obsoleted variable"), avcp->avc_name);
 1795    }
 1796 
 1797    /* Lookup possibly existing var.  For */
 1798 
 1799    rv = TRU1;
 1800    a_amv_var_lookup(avcp, (a_AMV_VLOOK_I3VAL_NONEW |
 1801          ((avscf & a_AMV_VSETCLR_LOCAL)
 1802           ? (a_AMV_VLOOK_LOCAL | a_AMV_VLOOK_LOCAL_ONLY) : 0)));
 1803    avp = avcp->avc_var;
 1804 
 1805    /* A `local' setting is never covered by `localopts' nor frozen */
 1806    if(avscf & a_AMV_VSETCLR_LOCAL)
 1807       goto jislocal;
 1808 
 1809    /* If this setting had been established via -S and we still have not reached
 1810     * the _STARTED_CONFIG program state, silently ignore request! */
 1811    if(n_UNLIKELY(avp != NULL) &&
 1812          n_UNLIKELY((avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK) != 0)){
 1813       if(!(n_psonce & n_PSO_STARTED_CONFIG)){
 1814          if((n_pstate & n_PS_ROOT) ||
 1815                (!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1816                 (n_poption & n_PO_S_FLAG_TEMPORARY)))
 1817             goto joval_and_go;
 1818          if(n_poption & n_PO_D_VV)
 1819             n_err(_("Temporarily frozen by -S, not `set'ing: %s\n"),
 1820                avcp->avc_name);
 1821          goto jleave;
 1822       }
 1823 
 1824       /* Otherwise, if -S freezing was an `unset' request, be very simple and
 1825        * avoid tampering with that very special case we are not really prepared
 1826        * for just one more line of code: throw the old thing away! */
 1827       if(!(avp->av_flags & a_AMV_VF_EXT_FROZEN_UNSET))
 1828          avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 1829       else{
 1830          assert(avp->av_value == n_empty);
 1831          a_amv_vars[avcp->avc_prime] = avp->av_link;
 1832          n_free(avp);
 1833          avcp->avc_var = avp = NULL;
 1834       }
 1835    }
 1836 
 1837    /* Optionally cover by `localopts' */
 1838    if(n_UNLIKELY(a_amv_lopts != NULL) &&
 1839          (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
 1840       a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
 1841 
 1842 jislocal:
 1843    if(avp != NULL)
 1844 joval_and_go:
 1845       oval = avp->av_value;
 1846    else{
 1847       size_t l;
 1848       struct a_amv_var **avpp;
 1849 
 1850       if(avscf & a_AMV_VSETCLR_LOCAL){
 1851          if((avpp = *a_amv_lopts->as_amcap->amca_local_vars) == NULL)
 1852             avpp = *(a_amv_lopts->as_amcap->amca_local_vars =
 1853                   n_calloc(1, sizeof(*a_amv_lopts->as_amcap->amca_local_vars)));
 1854          avpp += avcp->avc_prime;
 1855       }else
 1856          avpp = &a_amv_vars[avcp->avc_prime];
 1857 
 1858       l = strlen(avcp->avc_name) +1;
 1859       avcp->avc_var = avp = n_calloc(1,
 1860             n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1861       avp->av_link = *avpp;
 1862       *avpp = avp;
 1863       avp->av_flags = (avscf & a_AMV_VSETCLR_LOCAL)
 1864             ? a_AMV_VF_NOLOPTS | a_AMV_VF_EXT_LOCAL
 1865             : (avmp != NULL) ? avmp->avm_flags : 0;
 1866       memcpy(avp->av_name, avcp->avc_name, l);
 1867       oval = n_UNCONST(n_empty);
 1868    }
 1869 
 1870    if(avmp == NULL)
 1871       avp->av_value = a_amv_var_copy(value);
 1872    else{
 1873       assert(!(avscf & a_AMV_VSETCLR_LOCAL));
 1874       /* Via `set' etc. the user may give even boolean options non-boolean
 1875        * values, ignore that and force boolean */
 1876       if(!(avp->av_flags & a_AMV_VF_BOOL))
 1877          avp->av_value = a_amv_var_copy(value);
 1878       else{
 1879          if(!(n_pstate & n_PS_ROOT) && (n_poption & n_PO_D_V) &&
 1880                *value != '\0')
 1881             n_err(_("Ignoring value of boolean variable: %s: %s\n"),
 1882                avcp->avc_name, value);
 1883          avp->av_value = n_UNCONST(n_1);
 1884       }
 1885    }
 1886 
 1887    /* A `local' setting can skip all the crude special things */
 1888    if(!(avscf & a_AMV_VSETCLR_LOCAL)){
 1889       if((avscf & a_AMV_VSETCLR_ENV) && !(avp->av_flags & a_AMV_VF_ENV))
 1890          avp->av_flags |= a_AMV_VF_EXT_LINKED;
 1891       if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 1892          rv = a_amv_var__putenv(avcp, avp);
 1893       if(avp->av_flags & a_AMV_VF_VIP)
 1894          a_amv_var_check_vips(a_AMV_VIP_SET_POST, avcp->avc_okey, &value);
 1895 
 1896       avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 1897       if(!(n_psonce & n_PSO_STARTED_GETOPT) &&
 1898             (n_poption & n_PO_S_FLAG_TEMPORARY) != 0)
 1899          avp->av_flags |= a_AMV_VF_EXT_FROZEN;
 1900    }
 1901 
 1902    a_amv_var_free(oval);
 1903 jleave:
 1904    NYD2_LEAVE;
 1905    return rv;
 1906 }
 1907 
 1908 static bool_t
 1909 a_amv_var__putenv(struct a_amv_var_carrier *avcp, struct a_amv_var *avp){
 1910 #ifndef HAVE_SETENV
 1911    char *cp;
 1912 #endif
 1913    bool_t rv;
 1914    NYD2_ENTER;
 1915 
 1916 #ifdef HAVE_SETENV
 1917    rv = (setenv(avcp->avc_name, avp->av_value, 1) == 0);
 1918 #else
 1919    cp = sstrdup(savecatsep(avcp->avc_name, '=', avp->av_value));
 1920 
 1921    if((rv = (putenv(cp) == 0))){
 1922       char *ocp;
 1923 
 1924       ocp = avp->av_env;
 1925       avp->av_env = cp;
 1926       cp = ocp;
 1927    }
 1928 
 1929    if(cp != NULL)
 1930       n_free(cp);
 1931 #endif
 1932    NYD2_LEAVE;
 1933    return rv;
 1934 }
 1935 
 1936 static bool_t
 1937 a_amv_var_clear(struct a_amv_var_carrier *avcp,
 1938       enum a_amv_var_setclr_flags avscf){
 1939    struct a_amv_var **avpp, *avp;
 1940    struct a_amv_var_map const *avmp;
 1941    bool_t rv;
 1942    NYD2_ENTER;
 1943 
 1944    rv = FAL0;
 1945 
 1946    if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
 1947       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NODEL) != 0 &&
 1948             !(n_pstate & n_PS_ROOT))){
 1949          n_err(_("Variable may not be unset: %s\n"), avcp->avc_name);
 1950          goto jleave;
 1951       }
 1952       if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIP) != 0 &&
 1953             !a_amv_var_check_vips(a_AMV_VIP_CLEAR, avcp->avc_okey, NULL))){
 1954          n_err(_("Clearance of variable aborted: %s\n"), avcp->avc_name);
 1955          goto jleave;
 1956       }
 1957    }
 1958 
 1959    rv = TRU1;
 1960 
 1961    if(n_UNLIKELY(!a_amv_var_lookup(avcp,
 1962          (((avscf & a_AMV_VSETCLR_LOCAL)
 1963             ? (a_AMV_VLOOK_LOCAL | a_AMV_VLOOK_LOCAL_ONLY) : 0) |
 1964           a_AMV_VLOOK_I3VAL_NONEW | a_AMV_VLOOK_I3VAL_NONEW_REPORT)))){
 1965       assert(avcp->avc_var == NULL);
 1966       /* This may be a clearance request from the command line, via -S, and we
 1967        * need to keep track of that!  Unfortunately we are not prepared for
 1968        * this, really, so we need to create a fake entry that is known and
 1969        * handled correctly by the lowermost variable layer!
 1970        * However, all this cannot happen for plain unset of `local' variables */
 1971       if(avscf & a_AMV_VSETCLR_LOCAL)
 1972          goto jleave;
 1973       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT)) &&
 1974             (n_poption & n_PO_S_FLAG_TEMPORARY)) Jfreeze:{
 1975          size_t l;
 1976 
 1977          l = strlen(avcp->avc_name) +1;
 1978          avp = n_calloc(1, n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + l);
 1979          avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
 1980          *avpp = avp;
 1981          avp->av_value = n_UNCONST(n_empty); /* Sth. covered by _var_free()! */
 1982          avp->av_flags = (avmp != NULL ? avmp->avm_flags : 0) |
 1983                a_AMV_VF_EXT_FROZEN | a_AMV_VF_EXT_FROZEN_UNSET;
 1984          memcpy(avp->av_name, avcp->avc_name, l);
 1985 
 1986          if((avscf & a_AMV_VSETCLR_ENV) ||
 1987                (avmp != NULL && (avmp->avm_flags & a_AMV_VF_ENV)))
 1988             a_amv_var__clearenv(avcp->avc_name, NULL);
 1989       }else if(avscf & a_AMV_VSETCLR_ENV){
 1990 jforce_env:
 1991          if(!(rv = a_amv_var__clearenv(avcp->avc_name, NULL)))
 1992             goto jerr_env_unset;
 1993       }else{
 1994          /* TODO "cannot unset undefined variable" not echoed in "ROBOT" state,
 1995           * TODO should only be like that with "ignerr"! */
 1996 jerr_env_unset:
 1997          if(!(n_pstate & (n_PS_ROOT | n_PS_ROBOT)) && (n_poption & n_PO_D_V))
 1998             n_err(_("Cannot unset undefined variable: %s\n"), avcp->avc_name);
 1999       }
 2000       goto jleave;
 2001    }else if((avp = avcp->avc_var) == (struct a_amv_var*)-1){
 2002       /* Clearance request from command line, via -S?  As above.. */
 2003       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT) &&
 2004             (n_poption & n_PO_S_FLAG_TEMPORARY) != 0))
 2005          goto Jfreeze;
 2006       avcp->avc_var = NULL;
 2007       if(avscf & a_AMV_VSETCLR_ENV)
 2008          goto jforce_env;
 2009       goto jleave;
 2010    }
 2011    assert(avcp->avc_var != NULL);
 2012 
 2013    /* `local' variables bypass "frozen" checks and `localopts' coverage etc. */
 2014    if(avp->av_flags & a_AMV_VF_EXT_LOCAL)
 2015       goto jdefault_path;
 2016 
 2017    /* If this setting has been established via -S and we still have not reached
 2018     * the _STARTED_CONFIG program state, silently ignore request!
 2019     * XXX All this is very complicated for the tenth of a second */
 2020    /*else*/ if(n_UNLIKELY((avp->av_flags & a_AMV_VF_EXT__FROZEN_MASK) != 0)){
 2021       if(!(n_psonce & n_PSO_STARTED_CONFIG)){
 2022          if((n_pstate & n_PS_ROOT) ||
 2023                (!(n_psonce & n_PSO_STARTED_GETOPT) &&
 2024                 (n_poption & n_PO_S_FLAG_TEMPORARY))){
 2025             /* Be aware this may turn a set into an unset! */
 2026             if(!(avp->av_flags & a_AMV_VF_EXT_FROZEN_UNSET)){
 2027                if(avp->av_flags & a_AMV_VF_DEFVAL)
 2028                   goto jdefault_path;
 2029                a_amv_var_free(avp->av_value);
 2030                avp->av_flags |= a_AMV_VF_EXT_FROZEN_UNSET;
 2031                avp->av_value = n_UNCONST(n_empty); /* _var_free() covered */
 2032                if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 2033                   goto jforce_env;
 2034             }
 2035             goto jleave;
 2036          }
 2037          if(n_poption & n_PO_D_VV)
 2038             n_err(_("Temporarily frozen by -S, not `unset'ting: %s\n"),
 2039                avcp->avc_name);
 2040          goto jleave;
 2041       }
 2042       avp->av_flags &= ~a_AMV_VF_EXT__FROZEN_MASK;
 2043    }
 2044 
 2045    if(n_UNLIKELY(a_amv_lopts != NULL) &&
 2046          (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
 2047       a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
 2048 
 2049 jdefault_path:
 2050    assert(avp == avcp->avc_var);
 2051    avcp->avc_var = NULL;
 2052    avpp = &((avp->av_flags & a_AMV_VF_EXT_LOCAL)
 2053          ? *a_amv_lopts->as_amcap->amca_local_vars : a_amv_vars
 2054          )[avcp->avc_prime];
 2055    assert(*avpp == avp); /* (always listhead after lookup()) */
 2056    *avpp = (*avpp)->av_link;
 2057 
 2058    if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED))
 2059       rv = a_amv_var__clearenv(avp->av_name, avp);
 2060    a_amv_var_free(avp->av_value);
 2061    n_free(avp);
 2062 
 2063    /* XXX Fun part, extremely simple-minded for now: if this variable has
 2064     * XXX a default value, immediately reinstantiate it!  TODO Heh? */
 2065    if(n_UNLIKELY(avmp != NULL && (avmp->avm_flags & a_AMV_VF_DEFVAL) != 0)){
 2066       a_amv_var_lookup(avcp, a_AMV_VLOOK_I3VAL_NONEW);
 2067       if(n_UNLIKELY(!(n_psonce & n_PSO_STARTED_GETOPT)) &&
 2068             (n_poption & n_PO_S_FLAG_TEMPORARY))
 2069          avcp->avc_var->av_flags |= a_AMV_VF_EXT_FROZEN;
 2070    }
 2071 jleave:
 2072    NYD2_LEAVE;
 2073    return rv;
 2074 }
 2075 
 2076 static bool_t
 2077 a_amv_var__clearenv(char const *name, struct a_amv_var *avp){
 2078    extern char **environ;
 2079    char **ecpp;
 2080    bool_t rv;
 2081    NYD2_ENTER;
 2082    n_UNUSED(avp);
 2083 
 2084    rv = FAL0;
 2085    ecpp = environ;
 2086 
 2087 #ifndef HAVE_SETENV
 2088    if(avp != NULL && avp->av_env != NULL){
 2089       for(; *ecpp != NULL; ++ecpp)
 2090          if(*ecpp == avp->av_env){
 2091             do
 2092                ecpp[0] = ecpp[1];
 2093             while(*ecpp++ != NULL);
 2094             n_free(avp->av_env);
 2095             avp->av_env = NULL;
 2096             rv = TRU1;
 2097             break;
 2098          }
 2099    }else
 2100 #endif
 2101    {
 2102       size_t l;
 2103 
 2104       if((l = strlen(name)) > 0){
 2105          for(; *ecpp != NULL; ++ecpp)
 2106             if(!strncmp(*ecpp, name, l) && (*ecpp)[l] == '='){
 2107 #ifdef HAVE_SETENV
 2108                unsetenv(name);
 2109 #else
 2110                do
 2111                   ecpp[0] = ecpp[1];
 2112                while(*ecpp++ != NULL);
 2113 #endif
 2114                rv = TRU1;
 2115                break;
 2116             }
 2117       }
 2118    }
 2119    NYD2_LEAVE;
 2120    return rv;
 2121 }
 2122 
 2123 static void
 2124 a_amv_var_show_all(void){
 2125    struct n_string msg, *msgp;
 2126    FILE *fp;
 2127    size_t no, i;
 2128    struct a_amv_var *avp;
 2129    char const **vacp, **cap;
 2130    NYD2_ENTER;
 2131 
 2132    if((fp = Ftmp(NULL, "setlist", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
 2133       n_perr(_("`set' list: cannot create temporary file"), 0);
 2134       goto jleave;
 2135    }
 2136 
 2137    /* We need to instantiate first-time-inits and default values here, so that
 2138     * they will be regular members of our _vars[] table */
 2139    for(i = a_AMV_VAR_I3VALS_CNT; i-- > 0;)
 2140       n_var_oklook(a_amv_var_i3vals[i].avdv_okey);
 2141    for(i = a_AMV_VAR_DEFVALS_CNT; i-- > 0;)
 2142       n_var_oklook(a_amv_var_defvals[i].avdv_okey);
 2143 
 2144    for(no = i = 0; i < a_AMV_PRIME; ++i)
 2145       for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
 2146          ++no;
 2147    no += a_AMV_VAR_VIRTS_CNT;
 2148 
 2149    vacp = n_autorec_alloc(no * sizeof(*vacp));
 2150 
 2151    for(cap = vacp, i = 0; i < a_AMV_PRIME; ++i)
 2152       for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
 2153          *cap++ = avp->av_name;
 2154    for(i = a_AMV_VAR_VIRTS_CNT; i-- > 0;)
 2155       *cap++ = a_amv_var_virts[i].avv_var->av_name;
 2156 
 2157    if(no > 1)
 2158       qsort(vacp, no, sizeof *vacp, &a_amv_var__show_cmp);
 2159 
 2160    msgp = &msg;
 2161    msgp = n_string_reserve(n_string_creat(msgp), 80);
 2162    for(i = 0, cap = vacp; no != 0; ++cap, --no)
 2163       i += a_amv_var_show(*cap, fp, msgp);
 2164    n_string_gut(&msg);
 2165 
 2166    page_or_print(fp, i);
 2167    Fclose(fp);
 2168 jleave:
 2169    NYD2_LEAVE;
 2170 }
 2171 
 2172 static int
 2173 a_amv_var__show_cmp(void const *s1, void const *s2){
 2174    int rv;
 2175    NYD2_ENTER;
 2176 
 2177    rv = strcmp(*(char**)n_UNCONST(s1), *(char**)n_UNCONST(s2));
 2178    NYD2_LEAVE;
 2179    return rv;
 2180 }
 2181 
 2182 static size_t
 2183 a_amv_var_show(char const *name, FILE *fp, struct n_string *msgp){
 2184    struct a_amv_var_carrier avc;
 2185    char const *quote;
 2186    struct a_amv_var *avp;
 2187    bool_t isset;
 2188    size_t i;
 2189    NYD2_ENTER;
 2190 
 2191    msgp = n_string_trunc(msgp, 0);
 2192    i = 0;
 2193 
 2194    a_amv_var_revlookup(&avc, name);
 2195    isset = a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE);
 2196    avp = avc.avc_var;
 2197 
 2198    if(n_poption & n_PO_D_V){
 2199       if(avc.avc_map == NULL){
 2200          msgp = n_string_push_cp(msgp, "#assembled variable with value");
 2201          i = 1;
 2202       }else{
 2203          struct{
 2204             ui16_t flag;
 2205             char msg[22];
 2206          } const tbase[] = {
 2207             {a_AMV_VF_CHAIN, "variable chain"},
 2208             {a_AMV_VF_VIRT, "virtual"},
 2209             {a_AMV_VF_RDONLY, "read-only"},
 2210             {a_AMV_VF_NODEL, "nodelete"},
 2211             {a_AMV_VF_I3VAL, "initial-value"},
 2212             {a_AMV_VF_DEFVAL, "default-value"},
 2213             {a_AMV_VF_IMPORT, "import-environ-first\0"}, /* assert NUL in max */
 2214             {a_AMV_VF_ENV, "sync-environ"},
 2215             {a_AMV_VF_NOLOPTS, "no-localopts"},
 2216             {a_AMV_VF_NOTEMPTY, "notempty"},
 2217             {a_AMV_VF_NUM, "number"},
 2218             {a_AMV_VF_POSNUM, "positive-number"},
 2219             {a_AMV_VF_OBSOLETE, "obsoleted"},
 2220          }, *tp;
 2221 
 2222          for(tp = tbase; PTRCMP(tp, <, &tbase[n_NELEM(tbase)]); ++tp)
 2223             if(isset ? (avp->av_flags & tp->flag)
 2224                   : (avc.avc_map->avm_flags & tp->flag)){
 2225                msgp = n_string_push_c(msgp, (i++ == 0 ? '#' : ','));
 2226                msgp = n_string_push_cp(msgp, tp->msg);
 2227             }
 2228       }
 2229 
 2230       if(isset && (avp->av_flags & a_AMV_VF_EXT_FROZEN)){
 2231          msgp = n_string_push_c(msgp, (i++ == 0 ? '#' : ','));
 2232          msgp = n_string_push_cp(msgp, "(un)?set via -S");
 2233       }
 2234 
 2235       if(i > 0)
 2236          msgp = n_string_push_cp(msgp, "\n  ");
 2237    }
 2238 
 2239    /* (Read-only variables are generally shown via comments..) */
 2240    if(!isset || (avp->av_flags & a_AMV_VF_RDONLY)){
 2241       msgp = n_string_push_c(msgp, n_ns[0]);
 2242       if(!isset){
 2243          if(avc.avc_map != NULL && (avc.avc_map->avm_flags & a_AMV_VF_BOOL))
 2244             msgp = n_string_push_cp(msgp, "boolean; ");
 2245          msgp = n_string_push_cp(msgp, "variable not set: ");
 2246          msgp = n_string_push_cp(msgp, n_shexp_quote_cp(name, FAL0));
 2247          goto jleave;
 2248       }
 2249    }
 2250 
 2251    n_UNINIT(quote, NULL);
 2252    if(!(avp->av_flags & a_AMV_VF_BOOL)){
 2253       quote = n_shexp_quote_cp(avp->av_value, TRU1);
 2254       if(strcmp(quote, avp->av_value))
 2255          msgp = n_string_push_cp(msgp, "wysh ");
 2256    }else if(n_poption & n_PO_D_V)
 2257       msgp = n_string_push_cp(msgp, "wysh "); /* (for shell-style comment) */
 2258 
 2259    if(avp->av_flags & a_AMV_VF_EXT_LINKED)
 2260       msgp = n_string_push_cp(msgp, "environ ");
 2261    msgp = n_string_push_cp(msgp, "set ");
 2262    msgp = n_string_push_cp(msgp, name);
 2263 
 2264    if(!(avp->av_flags & a_AMV_VF_BOOL)){
 2265       msgp = n_string_push_c(msgp, '=');
 2266       msgp = n_string_push_cp(msgp, quote);
 2267    }else if(n_poption & n_PO_D_V)
 2268       msgp = n_string_push_cp(msgp, " #boolean");
 2269 
 2270 jleave:
 2271    msgp = n_string_push_c(msgp, '\n');
 2272    fputs(n_string_cp(msgp), fp);
 2273    NYD2_ENTER;
 2274    return (i > 0 ? 2 : 1);
 2275 }
 2276 
 2277 static bool_t
 2278 a_amv_var_c_set(char **ap, enum a_amv_var_setclr_flags avscf){
 2279    char *cp, *cp2, *varbuf, c;
 2280    size_t errs;
 2281    NYD2_ENTER;
 2282 
 2283    errs = 0;
 2284 jouter:
 2285    while((cp = *ap++) != NULL){
 2286       /* Isolate key */
 2287       cp2 = varbuf = n_autorec_alloc(strlen(cp) +1);
 2288 
 2289       for(; (c = *cp) != '=' && c != '\0'; ++cp){
 2290          if(cntrlchar(c) || spacechar(c)){
 2291             n_err(_("Variable name with control or space character ignored: "
 2292                "%s\n"), ap[-1]);
 2293             ++errs;
 2294             goto jouter;
 2295          }
 2296          *cp2++ = c;
 2297       }
 2298       *cp2 = '\0';
 2299       if(c == '\0')
 2300          cp = n_UNCONST(n_empty);
 2301       else
 2302          ++cp;
 2303 
 2304       if(varbuf == cp2){
 2305          n_err(_("Empty variable name ignored\n"));
 2306          ++errs;
 2307       }else{
 2308          struct a_amv_var_carrier avc;
 2309          bool_t isunset;
 2310 
 2311          if((isunset = (varbuf[0] == 'n' && varbuf[1] == 'o')))
 2312             varbuf = &varbuf[2];
 2313 
 2314          a_amv_var_revlookup(&avc, varbuf);
 2315 
 2316          if((avscf & a_AMV_VSETCLR_LOCAL) && avc.avc_map != NULL){
 2317             if(n_poption & n_PO_D_V)
 2318                n_err(_("Builtin variable not overwritten by `local': %s\n"),
 2319                   varbuf);
 2320             ++errs;
 2321          }else if(isunset)
 2322             errs += !a_amv_var_clear(&avc, avscf);
 2323          else
 2324             errs += !a_amv_var_set(&avc, cp, avscf);
 2325       }
 2326    }
 2327    NYD2_LEAVE;
 2328    return (errs == 0);
 2329 }
 2330 
 2331 FL int
 2332 c_define(void *v){
 2333    int rv;
 2334    char **args;
 2335    NYD_ENTER;
 2336 
 2337    rv = 1;
 2338 
 2339    if((args = v)[0] == NULL){
 2340       rv = (a_amv_mac_show(a_AMV_MF_NONE) == FAL0);
 2341       goto jleave;
 2342    }
 2343 
 2344    if(args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
 2345          args[2] != NULL){
 2346       n_err(_("Synopsis: define: <name> {\n"));
 2347       goto jleave;
 2348    }
 2349 
 2350    rv = (a_amv_mac_def(args[0], a_AMV_MF_NONE) == FAL0);
 2351 jleave:
 2352    NYD_LEAVE;
 2353    return rv;
 2354 }
 2355 
 2356 FL int
 2357 c_undefine(void *v){
 2358    int rv;
 2359    char **args;
 2360    NYD_ENTER;
 2361 
 2362    rv = 0;
 2363    args = v;
 2364    do
 2365       rv |= !a_amv_mac_undef(*args, a_AMV_MF_NONE);
 2366    while(*++args != NULL);
 2367    NYD_LEAVE;
 2368    return rv;
 2369 }
 2370 
 2371 FL int
 2372 c_call(void *vp){
 2373    int rv;
 2374    NYD_ENTER;
 2375 
 2376    rv = a_amv_mac_call(vp, FAL0);
 2377    NYD_LEAVE;
 2378    return rv;
 2379 }
 2380 
 2381 FL int
 2382 c_call_if(void *vp){
 2383    int rv;
 2384    NYD_ENTER;
 2385 
 2386    rv = a_amv_mac_call(vp, TRU1);
 2387    NYD_LEAVE;
 2388    return rv;
 2389 }
 2390 
 2391 FL int
 2392 c_account(void *v){
 2393    struct a_amv_mac_call_args *amcap;
 2394    struct a_amv_mac *amp;
 2395    char **args;
 2396    int rv, i, oqf, nqf;
 2397    NYD_ENTER;
 2398 
 2399    rv = 1;
 2400 
 2401    if((args = v)[0] == NULL){
 2402       rv = (a_amv_mac_show(a_AMV_MF_ACCOUNT) == FAL0);
 2403       goto jleave;
 2404    }
 2405 
 2406    if(args[1] && args[1][0] == '{' && args[1][1] == '\0'){
 2407       if(args[2] != NULL){
 2408          n_err(_("Synopsis: account: <name> {\n"));
 2409          goto jleave;
 2410       }
 2411       if(!asccasecmp(args[0], ACCOUNT_NULL)){
 2412          n_err(_("`account': cannot use reserved name: %s\n"),
 2413             ACCOUNT_NULL);
 2414          goto jleave;
 2415       }
 2416       rv = (a_amv_mac_def(args[0], a_AMV_MF_ACCOUNT) == FAL0);
 2417       goto jleave;
 2418    }
 2419 
 2420    if(n_pstate & n_PS_HOOK_MASK){
 2421       n_err(_("`account': cannot change account from within a hook\n"));
 2422       goto jleave;
 2423    }
 2424 
 2425    save_mbox_for_possible_quitstuff();
 2426 
 2427    amp = NULL;
 2428    if(asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
 2429          (amp = a_amv_mac_lookup(args[0], NULL, a_AMV_MF_ACCOUNT)) == NULL){
 2430       n_err(_("`account': account does not exist: %s\n"), args[0]);
 2431       goto jleave;
 2432    }
 2433 
 2434    oqf = savequitflags();
 2435 
 2436    /* Shutdown the active account */
 2437    if(a_amv_acc_curr != NULL){
 2438       char const *cp;
 2439       char *var;
 2440 
 2441       /* Is there a cleanup hook? */
 2442       var = savecat("on-account-cleanup-", a_amv_acc_curr->am_name);
 2443       if((cp = n_var_vlook(var, FAL0)) != NULL ||
 2444             (cp = ok_vlook(on_account_cleanup)) != NULL){
 2445          struct a_amv_mac *amphook;
 2446 
 2447          if((amphook = a_amv_mac_lookup(cp, NULL, a_AMV_MF_NONE)) != NULL){
 2448             amcap = n_lofi_alloc(sizeof *amcap);
 2449             memset(amcap, 0, sizeof *amcap);
 2450             amcap->amca_name = cp;
 2451             amcap->amca_amp = amphook;
 2452             amcap->amca_unroller = &a_amv_acc_curr->am_lopts;
 2453             amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2454             amcap->amca_no_xcall = TRU1;
 2455             n_pstate |= n_PS_HOOK;
 2456             rv = a_amv_mac_exec(amcap);
 2457             n_pstate &= ~n_PS_HOOK_MASK;
 2458          }else
 2459             n_err(_("*on-account-leave* hook %s does not exist\n"),
 2460                n_shexp_quote_cp(cp, FAL0));
 2461       }
 2462 
 2463       /* `localopts'? */
 2464       if(a_amv_acc_curr->am_lopts != NULL)
 2465          a_amv_lopts_unroll(&a_amv_acc_curr->am_lopts);
 2466 
 2467       /* For accounts this lingers */
 2468       --a_amv_acc_curr->am_refcnt;
 2469       if(a_amv_acc_curr->am_flags & a_AMV_MF_DELETE)
 2470          a_amv_mac_free(a_amv_acc_curr);
 2471    }
 2472 
 2473    a_amv_acc_curr = amp;
 2474 
 2475    /* And switch to any non-"null" account */
 2476    if(amp != NULL){
 2477       assert(amp->am_lopts == NULL);
 2478       amcap = n_lofi_alloc(sizeof *amcap);
 2479       memset(amcap, 0, sizeof *amcap);
 2480       amcap->amca_name = amp->am_name;
 2481       amcap->amca_amp = amp;
 2482       amcap->amca_unroller = &amp->am_lopts;
 2483       amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2484       amcap->amca_no_xcall = TRU1;
 2485       ++amp->am_refcnt; /* We may not run 0 to avoid being deleted! */
 2486       if(!a_amv_mac_exec(amcap)){
 2487          /* XXX account switch incomplete, unroll? */
 2488          n_err(_("`account': failed to switch to account: %s\n"), amp->am_name);
 2489          goto jleave;
 2490       }
 2491    }
 2492 
 2493    n_PS_ROOT_BLOCK((amp != NULL ? ok_vset(account, amp->am_name)
 2494       : ok_vclear(account)));
 2495 
 2496    if(n_psonce & n_PSO_STARTED){
 2497       assert(!(n_pstate & n_PS_HOOK_MASK));
 2498       nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
 2499       restorequitflags(oqf);
 2500       if((i = setfile("%", 0)) < 0)
 2501          goto jleave;
 2502       temporary_folder_hook_check(FAL0);
 2503       if(i > 0 && !ok_blook(emptystart))
 2504          goto jleave;
 2505       n_folder_announce(n_ANNOUNCE_CHANGE);
 2506       restorequitflags(nqf);
 2507    }
 2508    rv = 0;
 2509 jleave:
 2510    NYD_LEAVE;
 2511    return rv;
 2512 }
 2513 
 2514 FL int
 2515 c_unaccount(void *v){
 2516    int rv;
 2517    char **args;
 2518    NYD_ENTER;
 2519 
 2520    rv = 0;
 2521    args = v;
 2522    do
 2523       rv |= !a_amv_mac_undef(*args, a_AMV_MF_ACCOUNT);
 2524    while(*++args != NULL);
 2525    NYD_LEAVE;
 2526    return rv;
 2527 }
 2528 
 2529 FL int
 2530 c_localopts(void *vp){
 2531    enum a_amv_loflags alf, alm;
 2532    char const **argv;
 2533    int rv;
 2534    NYD_ENTER;
 2535 
 2536    rv = 1;
 2537 
 2538    if(a_amv_lopts == NULL){
 2539       n_err(_("Cannot use `localopts' in this context\n"));
 2540       goto jleave;
 2541    }
 2542 
 2543    if((argv = vp)[1] == NULL || is_asccaseprefix((++argv)[-1], "scope"))
 2544       alf = alm = a_AMV_LF_SCOPE;
 2545    else if(is_asccaseprefix(argv[-1], "call"))
 2546       alf = a_AMV_LF_CALL, alm = a_AMV_LF_CALL_MASK;
 2547    else if(is_asccaseprefix(argv[-1], "call-fixate"))
 2548       alf = a_AMV_LF_CALL_FIXATE, alm = a_AMV_LF_CALL_MASK;
 2549    else{
 2550 jesynopsis:
 2551       n_err(_("Synopsis: localopts: [<scope|call|call-fixate>] <boolean>\n"));
 2552       goto jleave;
 2553    }
 2554 
 2555    if(alf == a_AMV_LF_SCOPE &&
 2556          (a_amv_lopts->as_loflags & a_AMV_LF_SCOPE_FIXATE)){
 2557       if(n_poption & n_PO_D_V)
 2558          n_err(_("Cannot turn off `localopts', setting is fixated\n"));
 2559       goto jleave;
 2560    }
 2561 
 2562    if((rv = n_boolify(*argv, UIZ_MAX, FAL0)) < FAL0)
 2563       goto jesynopsis;
 2564    a_amv_lopts->as_loflags &= ~alm;
 2565    if(rv > FAL0)
 2566       a_amv_lopts->as_loflags |= alf;
 2567    rv = 0;
 2568 jleave:
 2569    NYD_LEAVE;
 2570    return rv;
 2571 }
 2572 
 2573 FL int
 2574 c_shift(void *vp){ /* xxx move to bottom, not in macro part! */
 2575    struct a_amv_pospar *appp;
 2576    ui16_t i;
 2577    int rv;
 2578    NYD_ENTER;
 2579 
 2580    rv = 1;
 2581 
 2582    if((vp = *(char**)vp) == NULL)
 2583       i = 1;
 2584    else{
 2585       si16_t sib;
 2586 
 2587       if((n_idec_si16_cp(&sib, vp, 10, NULL
 2588                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2589             ) != n_IDEC_STATE_CONSUMED || sib < 0){
 2590          n_err(_("`shift': invalid argument: %s\n"), vp);
 2591          goto jleave;
 2592       }
 2593       i = (ui16_t)sib;
 2594    }
 2595 
 2596    /* If in in a macro/xy */
 2597    if(a_amv_lopts != NULL){
 2598       struct a_amv_mac const *amp;
 2599       struct a_amv_mac_call_args *amcap;
 2600 
 2601       /* Explicitly do allow `vpospar' created things! */
 2602       amp = (amcap = a_amv_lopts->as_amcap)->amca_amp;
 2603       if((amp == NULL || amcap->amca_ps_hook_mask ||
 2604                (amp->am_flags & a_AMV_MF_TYPE_MASK) == a_AMV_MF_ACCOUNT) &&
 2605             amcap->amca_pospar.app_not_heap){
 2606          n_err(_("Cannot use `shift' in `account's or hook macros etc.\n"));
 2607          goto jleave;
 2608       }
 2609       appp = &amcap->amca_pospar;
 2610    }else
 2611       appp = &a_amv_pospar;
 2612 
 2613    if(i > appp->app_count){
 2614       n_err(_("`shift': cannot shift %hu of %hu parameters\n"),
 2615          i, appp->app_count);
 2616       goto jleave;
 2617    }else{
 2618       appp->app_idx += i;
 2619       appp->app_count -= i;
 2620       rv = 0;
 2621    }
 2622 jleave:
 2623    NYD_LEAVE;
 2624    return rv;
 2625 }
 2626 
 2627 FL int
 2628 c_return(void *vp){ /* TODO the exit status should be m_si64! */
 2629    int rv;
 2630    NYD_ENTER;
 2631 
 2632    if(a_amv_lopts != NULL){
 2633       char const **argv;
 2634 
 2635       n_go_input_force_eof();
 2636       n_pstate_err_no = n_ERR_NONE;
 2637       rv = 0;
 2638 
 2639       if((argv = vp)[0] != NULL){
 2640          si32_t i;
 2641 
 2642          if((n_idec_si32_cp(&i, argv[0], 10, NULL
 2643                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2644                ) == n_IDEC_STATE_CONSUMED && i >= 0)
 2645             rv = (int)i;
 2646          else{
 2647             n_err(_("`return': return value argument is invalid: %s\n"),
 2648                argv[0]);
 2649             n_pstate_err_no = n_ERR_INVAL;
 2650             rv = 1;
 2651          }
 2652 
 2653          if(argv[1] != NULL){
 2654             if((n_idec_si32_cp(&i, argv[1], 10, NULL
 2655                      ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 2656                   ) == n_IDEC_STATE_CONSUMED && i >= 0)
 2657                n_pstate_err_no = i;
 2658             else{
 2659                n_err(_("`return': error number argument is invalid: %s\n"),
 2660                   argv[1]);
 2661                n_pstate_err_no = n_ERR_INVAL;
 2662                rv = 1;
 2663             }
 2664          }
 2665       }
 2666    }else{
 2667       n_err(_("Can only use `return' in a macro\n"));
 2668       n_pstate_err_no = n_ERR_OPNOTSUPP;
 2669       rv = 1;
 2670    }
 2671    NYD_LEAVE;
 2672    return rv;
 2673 }
 2674 
 2675 FL bool_t
 2676 temporary_folder_hook_check(bool_t nmail){ /* TODO temporary, v15: drop */
 2677    struct a_amv_mac_call_args *amcap;
 2678    struct a_amv_mac *amp;
 2679    size_t len;
 2680    char const *cp;
 2681    char *var;
 2682    bool_t rv;
 2683    NYD_ENTER;
 2684 
 2685    rv = TRU1;
 2686    var = n_autorec_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
 2687 
 2688    /* First try the fully resolved path */
 2689    snprintf(var, len, "folder-hook-%s", mailname);
 2690    if((cp = n_var_vlook(var, FAL0)) != NULL)
 2691       goto jmac;
 2692 
 2693    /* If we are under *folder*, try the usual +NAME syntax, too */
 2694    if(displayname[0] == '+'){
 2695       char *x;
 2696 
 2697       for(x = &mailname[len]; x != mailname; --x)
 2698          if(x[-1] == '/'){
 2699             snprintf(var, len, "folder-hook-+%s", x);
 2700             if((cp = n_var_vlook(var, FAL0)) != NULL)
 2701                goto jmac;
 2702             break;
 2703          }
 2704    }
 2705 
 2706    /* Plain *folder-hook* is our last try */
 2707    if((cp = ok_vlook(folder_hook)) == NULL)
 2708       goto jleave;
 2709 
 2710 jmac:
 2711    if((amp = a_amv_mac_lookup(cp, NULL, a_AMV_MF_NONE)) == NULL){
 2712       n_err(_("Cannot call *folder-hook* for %s: macro does not exist: %s\n"),
 2713          n_shexp_quote_cp(displayname, FAL0), cp);
 2714       rv = FAL0;
 2715       goto jleave;
 2716    }
 2717 
 2718    amcap = n_lofi_alloc(sizeof *amcap);
 2719    memset(amcap, 0, sizeof *amcap);
 2720    amcap->amca_name = cp;
 2721    amcap->amca_amp = amp;
 2722    n_pstate &= ~n_PS_HOOK_MASK;
 2723    if(nmail){
 2724       amcap->amca_unroller = NULL;
 2725       n_pstate |= n_PS_HOOK_NEWMAIL;
 2726    }else{
 2727       amcap->amca_unroller = &a_amv_folder_hook_lopts;
 2728       n_pstate |= n_PS_HOOK;
 2729    }
 2730    amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2731    amcap->amca_ps_hook_mask = TRU1;
 2732    amcap->amca_no_xcall = TRU1;
 2733    rv = a_amv_mac_exec(amcap);
 2734    n_pstate &= ~n_PS_HOOK_MASK;
 2735 
 2736 jleave:
 2737    NYD_LEAVE;
 2738    return rv;
 2739 }
 2740 
 2741 FL void
 2742 temporary_folder_hook_unroll(void){ /* XXX intermediate hack */
 2743    NYD_ENTER;
 2744    if(a_amv_folder_hook_lopts != NULL){
 2745       void *save = a_amv_lopts;
 2746 
 2747       a_amv_lopts = NULL;
 2748       a_amv_lopts_unroll(&a_amv_folder_hook_lopts);
 2749       assert(a_amv_folder_hook_lopts == NULL);
 2750       a_amv_lopts = save;
 2751    }
 2752    NYD_LEAVE;
 2753 }
 2754 
 2755 FL void
 2756 temporary_compose_mode_hook_call(char const *macname,
 2757       void (*hook_pre)(void *), void *hook_arg){
 2758    /* TODO compose_mode_hook_call() temporary, v15: generalize; see a_GO_SPLICE
 2759     * TODO comment in go.c for the right way of doing things! */
 2760    static struct a_amv_lostack *cmh_losp;
 2761    struct a_amv_mac_call_args *amcap;
 2762    struct a_amv_mac *amp;
 2763    NYD_ENTER;
 2764 
 2765    amp = NULL;
 2766 
 2767    if(macname == (char*)-1){
 2768       a_amv_mac__finalize(cmh_losp);
 2769       cmh_losp = NULL;
 2770    }else if(macname != NULL &&
 2771          (amp = a_amv_mac_lookup(macname, NULL, a_AMV_MF_NONE)) == NULL)
 2772       n_err(_("Cannot call *on-compose-**: macro does not exist: %s\n"),
 2773          macname);
 2774    else{
 2775       amcap = n_lofi_alloc(sizeof *amcap);
 2776       memset(amcap, 0, sizeof *amcap);
 2777       amcap->amca_name = (macname != NULL) ? macname
 2778             : "*on-compose-splice(-shell)?*";
 2779       amcap->amca_amp = amp;
 2780       amcap->amca_unroller = &a_amv_compose_lopts;
 2781       amcap->amca_hook_pre = hook_pre;
 2782       amcap->amca_hook_arg = hook_arg;
 2783       amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE;
 2784       amcap->amca_ps_hook_mask = TRU1;
 2785       amcap->amca_no_xcall = TRU1;
 2786       n_pstate &= ~n_PS_HOOK_MASK;
 2787       n_pstate |= n_PS_HOOK;
 2788       if(macname != NULL)
 2789          a_amv_mac_exec(amcap);
 2790       else{
 2791          cmh_losp = n_lofi_alloc(sizeof *cmh_losp);
 2792          memset(cmh_losp, 0, sizeof *cmh_losp);
 2793          cmh_losp->as_global_saved = a_amv_lopts;
 2794          cmh_losp->as_lopts = *(cmh_losp->as_amcap = amcap)->amca_unroller;
 2795          cmh_losp->as_loflags = a_AMV_LF_SCOPE_FIXATE;
 2796          a_amv_lopts = cmh_losp;
 2797       }
 2798    }
 2799    NYD_LEAVE;
 2800 }
 2801 
 2802 FL void
 2803 temporary_compose_mode_hook_unroll(void){ /* XXX intermediate hack */
 2804    NYD_ENTER;
 2805    if(a_amv_compose_lopts != NULL){
 2806       void *save = a_amv_lopts;
 2807 
 2808       a_amv_lopts = NULL;
 2809       a_amv_lopts_unroll(&a_amv_compose_lopts);
 2810       assert(a_amv_compose_lopts == NULL);
 2811       a_amv_lopts = save;
 2812    }
 2813    NYD_LEAVE;
 2814 }
 2815 
 2816 FL bool_t
 2817 n_var_is_user_writable(char const *name){
 2818    struct a_amv_var_carrier avc;
 2819    struct a_amv_var_map const *avmp;
 2820    bool_t rv;
 2821    NYD_ENTER;
 2822 
 2823    a_amv_var_revlookup(&avc, name);
 2824    if((avmp = avc.avc_map) == NULL)
 2825       rv = TRU1;
 2826    else
 2827       rv = ((avmp->avm_flags & (a_AMV_VF_BOOL | a_AMV_VF_RDONLY)) == 0);
 2828    NYD_LEAVE;
 2829    return rv;
 2830 }
 2831 
 2832 FL char *
 2833 n_var_oklook(enum okeys okey){
 2834    struct a_amv_var_carrier avc;
 2835    char *rv;
 2836    struct a_amv_var_map const *avmp;
 2837    NYD_ENTER;
 2838 
 2839    avc.avc_map = avmp = &a_amv_var_map[okey];
 2840    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2841    avc.avc_hash = avmp->avm_hash;
 2842    avc.avc_okey = okey;
 2843 
 2844    if(a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE))
 2845       rv = avc.avc_var->av_value;
 2846    else
 2847       rv = NULL;
 2848    NYD_LEAVE;
 2849    return rv;
 2850 }
 2851 
 2852 FL bool_t
 2853 n_var_okset(enum okeys okey, uintptr_t val){
 2854    struct a_amv_var_carrier avc;
 2855    bool_t ok;
 2856    struct a_amv_var_map const *avmp;
 2857    NYD_ENTER;
 2858 
 2859    avc.avc_map = avmp = &a_amv_var_map[okey];
 2860    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2861    avc.avc_hash = avmp->avm_hash;
 2862    avc.avc_okey = okey;
 2863 
 2864    ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val),
 2865          a_AMV_VSETCLR_NONE);
 2866    NYD_LEAVE;
 2867    return ok;
 2868 }
 2869 
 2870 FL bool_t
 2871 n_var_okclear(enum okeys okey){
 2872    struct a_amv_var_carrier avc;
 2873    bool_t rv;
 2874    struct a_amv_var_map const *avmp;
 2875    NYD_ENTER;
 2876 
 2877    avc.avc_map = avmp = &a_amv_var_map[okey];
 2878    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2879    avc.avc_hash = avmp->avm_hash;
 2880    avc.avc_okey = okey;
 2881 
 2882    rv = a_amv_var_clear(&avc, a_AMV_VSETCLR_NONE);
 2883    NYD_LEAVE;
 2884    return rv;
 2885 }
 2886 
 2887 FL char const *
 2888 n_var_vlook(char const *vokey, bool_t try_getenv){
 2889    struct a_amv_var_carrier avc;
 2890    char const *rv;
 2891    NYD_ENTER;
 2892 
 2893    a_amv_var_revlookup(&avc, vokey);
 2894 
 2895    switch((enum a_amv_var_special_category)avc.avc_special_cat){
 2896    default: /* silence CC */
 2897    case a_AMV_VSC_NONE:
 2898       if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL))
 2899          rv = avc.avc_var->av_value;
 2900       /* Only check the environment for something that is otherwise unknown */
 2901       else if(try_getenv && avc.avc_map == NULL)
 2902          rv = getenv(vokey);
 2903       else
 2904          rv = NULL;
 2905       break;
 2906    case a_AMV_VSC_GLOBAL:
 2907       rv = a_amv_var_vsc_global(&avc);
 2908       break;
 2909    case a_AMV_VSC_MULTIPLEX:
 2910       rv = a_amv_var_vsc_multiplex(&avc);
 2911       break;
 2912    case a_AMV_VSC_POSPAR:
 2913    case a_AMV_VSC_POSPAR_ENV:
 2914       rv = a_amv_var_vsc_pospar(&avc);
 2915       break;
 2916    }
 2917    NYD_LEAVE;
 2918    return rv;
 2919 }
 2920 
 2921 FL bool_t
 2922 n_var_vexplode(void const **cookie){
 2923    struct a_amv_pospar *appp;
 2924    NYD_ENTER;
 2925 
 2926    appp = (a_amv_lopts != NULL) ? &a_amv_lopts->as_amcap->amca_pospar
 2927          : &a_amv_pospar;
 2928    *cookie = (appp->app_count > 0) ? &appp->app_dat[appp->app_idx] : NULL;
 2929    NYD_LEAVE;
 2930    return (*cookie != NULL);
 2931 }
 2932 
 2933 FL bool_t
 2934 n_var_vset(char const *vokey, uintptr_t val){
 2935    struct a_amv_var_carrier avc;
 2936    bool_t ok;
 2937    NYD_ENTER;
 2938 
 2939    a_amv_var_revlookup(&avc, vokey);
 2940 
 2941    ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val),
 2942          a_AMV_VSETCLR_NONE);
 2943    NYD_LEAVE;
 2944    return ok;
 2945 }
 2946 
 2947 FL bool_t
 2948 n_var_vclear(char const *vokey){
 2949    struct a_amv_var_carrier avc;
 2950    bool_t ok;
 2951    NYD_ENTER;
 2952 
 2953    a_amv_var_revlookup(&avc, vokey);
 2954 
 2955    ok = a_amv_var_clear(&avc, a_AMV_VSETCLR_NONE);
 2956    NYD_LEAVE;
 2957    return ok;
 2958 }
 2959 
 2960 #ifdef HAVE_SOCKETS
 2961 FL char *
 2962 n_var_xoklook(enum okeys okey, struct url const *urlp,
 2963       enum okey_xlook_mode oxm){
 2964    struct a_amv_var_carrier avc;
 2965    struct str const *us;
 2966    size_t nlen;
 2967    char *nbuf, *rv;
 2968    struct a_amv_var_map const *avmp;
 2969    NYD_ENTER;
 2970 
 2971    assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
 2972 
 2973    /* For simplicity: allow this case too */
 2974    if(!(oxm & (OXM_H_P | OXM_U_H_P))){
 2975       nbuf = NULL;
 2976       goto jplain;
 2977    }
 2978 
 2979    avc.avc_map = avmp = &a_amv_var_map[okey];
 2980    avc.avc_name = &a_amv_var_names[avmp->avm_keyoff];
 2981    avc.avc_okey = okey;
 2982 
 2983    us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
 2984    nlen = strlen(avc.avc_name);
 2985    nbuf = n_lofi_alloc(nlen + 1 + us->l +1);
 2986    memcpy(nbuf, avc.avc_name, nlen);
 2987    nbuf[nlen++] = '-';
 2988 
 2989    /* One of .url_u_h_p and .url_h_p we test in here */
 2990    memcpy(nbuf + nlen, us->s, us->l +1);
 2991    avc.avc_name = a_amv_var_canonify(nbuf);
 2992    avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
 2993    if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL))
 2994       goto jvar;
 2995 
 2996    /* The second */
 2997    if(oxm & OXM_H_P){
 2998       us = &urlp->url_h_p;
 2999       memcpy(nbuf + nlen, us->s, us->l +1);
 3000       avc.avc_name = a_amv_var_canonify(nbuf);
 3001       avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
 3002       if(a_amv_var_lookup(&avc, a_AMV_VLOOK_LOCAL)){
 3003 jvar:
 3004          rv = avc.avc_var->av_value;
 3005          goto jleave;
 3006       }
 3007    }
 3008 
 3009 jplain:
 3010    rv = (oxm & OXM_PLAIN) ? n_var_oklook(okey) : NULL;
 3011 jleave:
 3012    if(nbuf != NULL)
 3013       n_lofi_free(nbuf);
 3014    NYD_LEAVE;
 3015    return rv;
 3016 }
 3017 #endif /* HAVE_SOCKETS */
 3018 
 3019 FL int
 3020 c_set(void *vp){
 3021    int err;
 3022    char **ap;
 3023    NYD_ENTER;
 3024 
 3025    if(*(ap = vp) == NULL){
 3026       a_amv_var_show_all();
 3027       err = 0;
 3028    }else{
 3029       enum a_amv_var_setclr_flags avscf;
 3030 
 3031       if(!(n_pstate & n_PS_ARGMOD_LOCAL))
 3032          avscf = a_AMV_VSETCLR_NONE;
 3033       else{
 3034          if(a_amv_lopts == NULL){
 3035             n_err(_("`set': cannot use `local' in this context\n"));
 3036             err = 1;
 3037             goto jleave;
 3038          }
 3039          avscf = a_AMV_VSETCLR_LOCAL;
 3040       }
 3041       err = !a_amv_var_c_set(ap, avscf);
 3042    }
 3043 jleave:
 3044    NYD_LEAVE;
 3045    return err;
 3046 }
 3047 
 3048 FL int
 3049 c_unset(void *vp){
 3050    struct a_amv_var_carrier avc;
 3051    char **ap;
 3052    int err;
 3053    enum a_amv_var_setclr_flags avscf;
 3054    NYD_ENTER;
 3055 
 3056    if(!(n_pstate & n_PS_ARGMOD_LOCAL))
 3057       avscf = a_AMV_VSETCLR_NONE;
 3058    else{
 3059       if(a_amv_lopts == NULL){
 3060          n_err(_("`unset': cannot use `local' in this context\n"));
 3061          err = 1;
 3062          goto jleave;
 3063       }
 3064       avscf = a_AMV_VSETCLR_LOCAL;
 3065    }
 3066 
 3067    for(err = 0, ap = vp; *ap != NULL; ++ap){
 3068       a_amv_var_revlookup(&avc, *ap);
 3069 
 3070       err |= !a_amv_var_clear(&avc, avscf);
 3071    }
 3072 jleave:
 3073    NYD_LEAVE;
 3074    return err;
 3075 }
 3076 
 3077 FL int
 3078 c_varshow(void *v){
 3079    char **ap;
 3080    NYD_ENTER;
 3081 
 3082    if(*(ap = v) == NULL)
 3083       v = NULL;
 3084    else{
 3085       struct n_string msg, *msgp = &msg;
 3086 
 3087       msgp = n_string_creat(msgp);
 3088       for(; *ap != NULL; ++ap)
 3089          a_amv_var_show(*ap, n_stdout, msgp);
 3090       n_string_gut(msgp);
 3091    }
 3092    NYD_LEAVE;
 3093    return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
 3094 }
 3095 
 3096 FL int
 3097 c_varedit(void *v){
 3098    struct a_amv_var_carrier avc;
 3099    FILE *of, *nf;
 3100    char *val, **argv;
 3101    int err;
 3102    sighandler_type sigint;
 3103    NYD_ENTER;
 3104 
 3105    sigint = safe_signal(SIGINT, SIG_IGN);
 3106 
 3107    for(err = 0, argv = v; *argv != NULL; ++argv){
 3108       memset(&avc, 0, sizeof avc);
 3109 
 3110       a_amv_var_revlookup(&avc, *argv);
 3111 
 3112       if(avc.avc_map != NULL){
 3113          if(avc.avc_map->avm_flags & a_AMV_VF_BOOL){
 3114             n_err(_("`varedit': cannot edit boolean variable: %s\n"),
 3115                avc.avc_name);
 3116             continue;
 3117          }
 3118          if(avc.avc_map->avm_flags & a_AMV_VF_RDONLY){
 3119             n_err(_("`varedit': cannot edit read-only variable: %s\n"),
 3120                avc.avc_name);
 3121             continue;
 3122          }
 3123       }
 3124 
 3125       a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE);
 3126 
 3127       if((of = Ftmp(NULL, "varedit", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
 3128             NULL){
 3129          n_perr(_("`varedit': cannot create temporary file"), 0);
 3130          err = 1;
 3131          break;
 3132       }else if(avc.avc_var != NULL && *(val = avc.avc_var->av_value) != '\0' &&
 3133             sizeof *val != fwrite(val, strlen(val), sizeof *val, of)){
 3134          n_perr(_("`varedit' failed to write old value to temporary file"), 0);
 3135          Fclose(of);
 3136          err = 1;
 3137          continue;
 3138       }
 3139 
 3140       fflush_rewind(of);
 3141       nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
 3142       Fclose(of);
 3143 
 3144       if(nf != NULL){
 3145          int c;
 3146          char const *varres;
 3147          off_t l;
 3148 
 3149          l = fsize(nf);
 3150          if(UICMP(64, l, >=, UIZ_MAX -42)){
 3151             n_err(_("`varedit': not enough memory to store variable: %s\n"),
 3152                avc.avc_name);
 3153             varres = n_empty;
 3154             err = 1;
 3155          }else{
 3156             varres = val = n_autorec_alloc(l +1);
 3157             for(; l > 0 && (c = getc(nf)) != EOF; --l)
 3158                *val++ = c;
 3159             *val++ = '\0';
 3160             if(l != 0){
 3161                n_err(_("`varedit': I/O while reading new value of: %s\n"),
 3162                   avc.avc_name);
 3163                err = 1;
 3164             }
 3165          }
 3166 
 3167          if(!a_amv_var_set(&avc, varres, a_AMV_VSETCLR_NONE))
 3168             err = 1;
 3169 
 3170          Fclose(nf);
 3171       }else{
 3172          n_err(_("`varedit': cannot start $EDITOR\n"));
 3173          err = 1;
 3174          break;
 3175       }
 3176    }
 3177 
 3178    safe_signal(SIGINT, sigint);
 3179    NYD_LEAVE;
 3180    return err;
 3181 }
 3182 
 3183 FL int
 3184 c_environ(void *v){
 3185    struct a_amv_var_carrier avc;
 3186    int err;
 3187    char **ap;
 3188    bool_t islnk;
 3189    NYD_ENTER;
 3190 
 3191    if((islnk = is_asccaseprefix(*(ap = v), "link")) ||
 3192          is_asccaseprefix(*ap, "unlink")){
 3193       for(err = 0; *++ap != NULL;){
 3194          a_amv_var_revlookup(&avc, *ap);
 3195 
 3196          if(a_amv_var_lookup(&avc, a_AMV_VLOOK_NONE) && (islnk ||
 3197                (avc.avc_var->av_flags & a_AMV_VF_EXT_LINKED))){
 3198             if(!islnk){
 3199                avc.avc_var->av_flags &= ~a_AMV_VF_EXT_LINKED;
 3200                continue;
 3201             }else if(avc.avc_var->av_flags &
 3202                   (a_AMV_VF_ENV | a_AMV_VF_EXT_LINKED)){
 3203                if(n_poption & n_PO_D_V)
 3204                   n_err(_("`environ': link: already established: %s\n"), *ap);
 3205                continue;
 3206             }
 3207             avc.avc_var->av_flags |= a_AMV_VF_EXT_LINKED;
 3208             if(!(avc.avc_var->av_flags & a_AMV_VF_ENV))
 3209                a_amv_var__putenv(&avc, avc.avc_var);
 3210          }else if(!islnk){
 3211             n_err(_("`environ': unlink: no link established: %s\n"), *ap);
 3212             err = 1;
 3213          }else{
 3214             char const *evp = getenv(*ap);
 3215 
 3216             if(evp != NULL)
 3217                err |= !a_amv_var_set(&avc, evp, a_AMV_VSETCLR_ENV);
 3218             else{
 3219                n_err(_("`environ': link: cannot link to non-existent: %s\n"),
 3220                   *ap);
 3221                err = 1;
 3222             }
 3223          }
 3224       }
 3225    }else if(is_asccaseprefix(*ap, "set"))
 3226       err = !a_amv_var_c_set(++ap, a_AMV_VSETCLR_ENV);
 3227    else if(is_asccaseprefix(*ap, "unset")){
 3228       for(err = 0; *++ap != NULL;){
 3229          a_amv_var_revlookup(&avc, *ap);
 3230 
 3231          if(!a_amv_var_clear(&avc, a_AMV_VSETCLR_ENV))
 3232             err = 1;
 3233       }
 3234    }else{
 3235       n_err(_("Synopsis: environ: <link|set|unset> <variable>...\n"));
 3236       err = 1;
 3237    }
 3238    NYD_LEAVE;
 3239    return err;
 3240 }
 3241 
 3242 FL int
 3243 c_vexpr(void *v){ /* TODO POSIX expr(1) comp. exit status; overly complicat. */
 3244    char pbase, op, iencbuf[2+1/* BASE# prefix*/ + n_IENC_BUFFER_SIZE + 1];
 3245    size_t i;
 3246    enum n_idec_state ids;
 3247    enum n_idec_mode idm;
 3248    si64_t lhv, rhv;
 3249    char const **argv, *varname, *varres, *cp;
 3250    enum{
 3251       a_ERR = 1u<<0,
 3252       a_SOFTOVERFLOW = 1u<<1,
 3253       a_ISNUM = 1u<<2,
 3254       a_ISDECIMAL = 1u<<3,    /* Print only decimal result */
 3255       a_SATURATED = 1u<<4,
 3256       a_ICASE = 1u<<5,
 3257       a_UNSIGNED_OP = 1u<<6,  /* Unsigned right shift (share bit ok) */
 3258       a_PBASE = 1u<<7,        /* Print additional number base */
 3259       a_TMP = 1u<<30
 3260    } f;
 3261    NYD_ENTER;
 3262 
 3263    f = a_ERR;
 3264    argv = v;
 3265    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
 3266    n_UNINIT(varres, n_empty);
 3267    n_UNINIT(pbase, '\0');
 3268 
 3269    if((cp = argv[0])[0] == '\0')
 3270       goto jesubcmd;
 3271 
 3272    if(cp[1] == '\0'){
 3273       op = cp[0];
 3274 jnumop:
 3275       f |= a_ISNUM;
 3276       switch(op){
 3277       case '=':
 3278       case '~':
 3279          if(argv[1] == NULL || argv[2] != NULL)
 3280             goto jesynopsis;
 3281 
 3282          if(*(cp = *++argv) == '\0')
 3283             lhv = 0;
 3284          else{
 3285             idm = ((*cp == 'u' || *cp == 'U')
 3286                   ? (++cp, n_IDEC_MODE_NONE)
 3287                   : ((*cp == 's' || *cp == 'S')
 3288                      ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3289                      : n_IDEC_MODE_SIGNED_TYPE |
 3290                         n_IDEC_MODE_POW2BASE_UNSIGNED));
 3291             if(((ids = n_idec_cp(&lhv, cp, 0, idm, NULL)
 3292                      ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3293                   ) != n_IDEC_STATE_CONSUMED){
 3294                if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3295                   goto jenum_range;
 3296                f |= a_SOFTOVERFLOW;
 3297                break;
 3298             }
 3299          }
 3300          if(op == '~')
 3301             lhv = ~lhv;
 3302          break;
 3303 
 3304       case '+':
 3305       case '-':
 3306       case '*':
 3307       case '/':
 3308       case '%':
 3309       case '|':
 3310       case '&':
 3311       case '^':
 3312       case '<':
 3313       case '>':
 3314          if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3315             goto jesynopsis;
 3316          else{
 3317             char xop;
 3318 
 3319             if(*(cp = *++argv) == '\0')
 3320                lhv = 0;
 3321             else{
 3322                idm = ((*cp == 'u' || *cp == 'U')
 3323                      ? (++cp, n_IDEC_MODE_NONE)
 3324                      : ((*cp == 's' || *cp == 'S')
 3325                         ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3326                         : n_IDEC_MODE_SIGNED_TYPE |
 3327                            n_IDEC_MODE_POW2BASE_UNSIGNED));
 3328                if(((ids = n_idec_cp(&lhv, cp, 0, idm, NULL)
 3329                         ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3330                      ) != n_IDEC_STATE_CONSUMED){
 3331                   if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3332                      goto jenum_range;
 3333                   f |= a_SOFTOVERFLOW;
 3334                   break;
 3335                }
 3336             }
 3337 
 3338             if(*(cp = *++argv) == '\0')
 3339                rhv = 0;
 3340             else{
 3341                idm = ((*cp == 'u' || *cp == 'U')
 3342                      ? (++cp, n_IDEC_MODE_NONE)
 3343                      : ((*cp == 's' || *cp == 'S')
 3344                         ? (++cp, n_IDEC_MODE_SIGNED_TYPE)
 3345                         : n_IDEC_MODE_SIGNED_TYPE |
 3346                            n_IDEC_MODE_POW2BASE_UNSIGNED));
 3347                if(((ids = n_idec_cp(&rhv, cp, 0, idm, NULL)
 3348                         ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3349                      ) != n_IDEC_STATE_CONSUMED){
 3350                   if(!(ids & n_IDEC_STATE_EOVERFLOW) || !(f & a_SATURATED))
 3351                      goto jenum_range;
 3352                   f |= a_SOFTOVERFLOW;
 3353                   lhv = rhv;
 3354                   break;
 3355                }
 3356             }
 3357 
 3358             xop = op;
 3359 jnumop_again:
 3360             switch(xop){
 3361             case '+':
 3362                if(rhv < 0){
 3363                   if(rhv != SI64_MIN){
 3364                      rhv = -rhv;
 3365                      xop = '-';
 3366                      goto jnumop_again;
 3367                   }else if(lhv < 0)
 3368                      goto jenum_plusminus;
 3369                   else if(lhv == 0){
 3370                      lhv = rhv;
 3371                      break;
 3372                   }
 3373                }else if(SI64_MAX - rhv < lhv)
 3374                   goto jenum_plusminus;
 3375                lhv += rhv;
 3376                break;
 3377             case '-':
 3378                if(rhv < 0){
 3379                   if(rhv != SI64_MIN){
 3380                      rhv = -rhv;
 3381                      xop = '+';
 3382                      goto jnumop_again;
 3383                   }else if(lhv > 0)
 3384                      goto jenum_plusminus;
 3385                   else if(lhv == 0){
 3386                      lhv = rhv;
 3387                      break;
 3388                   }
 3389                }else if(SI64_MIN + rhv > lhv){
 3390 jenum_plusminus:
 3391                   if(!(f & a_SATURATED))
 3392                      goto jenum_overflow;
 3393                   f |= a_SOFTOVERFLOW;
 3394                   lhv = (lhv < 0 || xop == '-') ? SI64_MIN : SI64_MAX;
 3395                   break;
 3396                }
 3397                lhv -= rhv;
 3398                break;
 3399             case '*':
 3400                /* Will the result be positive? */
 3401                if((lhv < 0) == (rhv < 0)){
 3402                   if(lhv > 0){
 3403                      lhv = -lhv;
 3404                      rhv = -rhv;
 3405                   }
 3406                   if(rhv != 0 && lhv != 0 && SI64_MAX / rhv > lhv){
 3407                      if(!(f & a_SATURATED))
 3408                         goto jenum_overflow;
 3409                      f |= a_SOFTOVERFLOW;
 3410                      lhv = SI64_MAX;
 3411                   }else
 3412                      lhv *= rhv;
 3413                }else{
 3414                   if(rhv > 0){
 3415                      if(lhv != 0 && SI64_MIN / lhv < rhv){
 3416                         if(!(f & a_SATURATED))
 3417                            goto jenum_overflow;
 3418                         f |= a_SOFTOVERFLOW;
 3419                         lhv = SI64_MIN;
 3420                      }else
 3421                         lhv *= rhv;
 3422                   }else{
 3423                      if(rhv != 0 && lhv != 0 && SI64_MIN / rhv < lhv){
 3424                         if(!(f & a_SATURATED))
 3425                            goto jenum_overflow;
 3426                         f |= a_SOFTOVERFLOW;
 3427                         lhv = SI64_MIN;
 3428                      }else
 3429                         lhv *= rhv;
 3430                   }
 3431                }
 3432                break;
 3433             case '/':
 3434                if(rhv == 0){
 3435                   if(!(f & a_SATURATED))
 3436                      goto jenum_range;
 3437                   f |= a_SOFTOVERFLOW;
 3438                   lhv = SI64_MAX;
 3439                }else
 3440                   lhv /= rhv;
 3441                break;
 3442             case '%':
 3443                if(rhv == 0){
 3444                   if(!(f & a_SATURATED))
 3445                      goto jenum_range;
 3446                   f |= a_SOFTOVERFLOW;
 3447                   lhv = SI64_MAX;
 3448                }else
 3449                   lhv %= rhv;
 3450                break;
 3451             case '|':
 3452                lhv |= rhv;
 3453                break;
 3454             case '&':
 3455                lhv &= rhv;
 3456                break;
 3457             case '^':
 3458                lhv ^= rhv;
 3459                break;
 3460             case '<':
 3461             case '>':
 3462                if(!(f & a_TMP)){
 3463                   argv -= 2;
 3464                   goto jesubcmd;
 3465                }
 3466                if(rhv > 63){ /* xxx 63? */
 3467                   if(!(f & a_SATURATED))
 3468                      goto jenum_overflow;
 3469                   rhv = 63;
 3470                }
 3471                if(op == '<')
 3472                   lhv <<= (ui8_t)rhv;
 3473                else if(f & a_UNSIGNED_OP)
 3474                   lhv = (ui64_t)lhv >> (ui8_t)rhv;
 3475                else
 3476                   lhv >>= (ui8_t)rhv;
 3477                break;
 3478             }
 3479          }
 3480          break;
 3481       default:
 3482          goto jesubcmd;
 3483       }
 3484    }else if(cp[0] == '<'){
 3485       if(*++cp != '<')
 3486          goto jesubcmd;
 3487       if(*++cp == '@'){
 3488          n_OBSOLETE(_("`vexpr': please use @ modifier as prefix not suffix"));
 3489          f |= a_SATURATED;
 3490          ++cp;
 3491       }
 3492       if(*cp != '\0')
 3493          goto jesubcmd;
 3494       f |= a_TMP;
 3495       op = '<';
 3496       goto jnumop;
 3497    }else if(cp[0] == '>'){
 3498       if(*++cp != '>')
 3499          goto jesubcmd;
 3500       if(*++cp == '>'){
 3501          f |= a_UNSIGNED_OP;
 3502          ++cp;
 3503       }
 3504       if(*cp == '@'){
 3505          n_OBSOLETE(_("`vexpr': please use @ modifier as prefix not suffix"));
 3506          f |= a_SATURATED;
 3507          ++cp;
 3508       }
 3509       if(*cp != '\0')
 3510          goto jesubcmd;
 3511       f |= a_TMP;
 3512       op = '>';
 3513       goto jnumop;
 3514    }else if(cp[2] == '\0' && cp[1] == '@'){
 3515       n_OBSOLETE(_("`vexpr': please use @ modifier as prefix, not suffix"));
 3516       f |= a_SATURATED;
 3517       op = cp[0];
 3518       goto jnumop;
 3519    }else if(cp[0] == '@'){
 3520       f |= a_SATURATED;
 3521       op = *++cp;
 3522       if(*++cp != '\0'){
 3523          if(op != *cp)
 3524             goto jesubcmd;
 3525          switch(op){
 3526          case '<':
 3527             if(*++cp != '\0')
 3528                goto jesubcmd;
 3529             f |= a_TMP;
 3530             break;
 3531          case '>':
 3532             if(*++cp != '\0'){
 3533                if(*cp != '>' || *++cp != '\0')
 3534                   goto jesubcmd;
 3535                f |= a_UNSIGNED_OP;
 3536             }
 3537             f |= a_TMP;
 3538             break;
 3539          default:
 3540             goto jesubcmd;
 3541          }
 3542       }
 3543       goto jnumop;
 3544    }else if(is_asccaseprefix(cp, "pbase")){
 3545       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3546          goto jesynopsis;
 3547 
 3548       if(((ids = n_idec_si8_cp(&pbase, argv[1], 0, NULL)
 3549                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3550             ) != n_IDEC_STATE_CONSUMED || pbase < 2 || pbase > 36)
 3551          goto jenum_range;
 3552 
 3553       f |= a_PBASE;
 3554       op = *(argv[0] = cp = "=");
 3555       argv[1] = argv[2];
 3556       argv[2] = NULL;
 3557       goto jnumop;
 3558    }else if(is_asccaseprefix(cp, "length")){
 3559       f |= a_ISNUM | a_ISDECIMAL;
 3560       if(argv[1] == NULL || argv[2] != NULL)
 3561          goto jesynopsis;
 3562 
 3563       i = strlen(*++argv);
 3564       if(UICMP(64, i, >, SI64_MAX))
 3565          goto jestr_overflow;
 3566       lhv = (si64_t)i;
 3567    }else if(is_asccaseprefix(cp, "hash")){
 3568       f |= a_ISNUM | a_ISDECIMAL;
 3569       if(argv[1] == NULL || argv[2] != NULL)
 3570          goto jesynopsis;
 3571 
 3572       i = n_torek_hash(*++argv);
 3573       lhv = (si64_t)i;
 3574    }else if(is_asccaseprefix(cp, "find")){
 3575       f |= a_ISNUM | a_ISDECIMAL;
 3576       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3577          goto jesynopsis;
 3578 
 3579       if((cp = strstr(argv[1], argv[2])) == NULL)
 3580          goto jestr_nodata;
 3581       i = PTR2SIZE(cp - argv[1]);
 3582       if(UICMP(64, i, >, SI64_MAX))
 3583          goto jestr_overflow;
 3584       lhv = (si64_t)i;
 3585    }else if(is_asccaseprefix(cp, "ifind")){
 3586       f |= a_ISNUM | a_ISDECIMAL;
 3587       if(argv[1] == NULL || argv[2] == NULL || argv[3] != NULL)
 3588          goto jesynopsis;
 3589 
 3590       if((cp = asccasestr(argv[1], argv[2])) == NULL)
 3591          goto jestr_nodata;
 3592       i = PTR2SIZE(cp - argv[1]);
 3593       if(UICMP(64, i, >, SI64_MAX))
 3594          goto jestr_overflow;
 3595       lhv = (si64_t)i;
 3596    }else if(is_asccaseprefix(cp, "substring")){
 3597       if(argv[1] == NULL || argv[2] == NULL)
 3598          goto jesynopsis;
 3599       if(argv[3] != NULL && argv[4] != NULL)
 3600          goto jesynopsis;
 3601 
 3602       i = strlen(varres = *++argv);
 3603 
 3604       if(*(cp = *++argv) == '\0')
 3605          lhv = 0;
 3606       else if((n_idec_si64_cp(&lhv, cp, 0, NULL
 3607                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3608             ) != n_IDEC_STATE_CONSUMED)
 3609          goto jestr_numrange;
 3610       if(lhv < 0){
 3611          if(UICMP(64, i, <, -lhv))
 3612             goto jesubstring_off;
 3613          lhv = i + lhv;
 3614       }
 3615       if(UICMP(64, i, >=, lhv)){
 3616          i -= lhv;
 3617          varres += lhv;
 3618       }else{
 3619 jesubstring_off:
 3620          if(n_poption & n_PO_D_V)
 3621             n_err(_("`vexpr': substring: offset argument too large: %s\n"),
 3622                n_shexp_quote_cp(argv[-1], FAL0));
 3623          f |= a_SOFTOVERFLOW;
 3624       }
 3625 
 3626       if(argv[1] != NULL){
 3627          if(*(cp = *++argv) == '\0')
 3628             lhv = 0;
 3629          else if((n_idec_si64_cp(&lhv, cp, 0, NULL
 3630                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3631                ) != n_IDEC_STATE_CONSUMED)
 3632             goto jestr_numrange;
 3633          if(lhv < 0){
 3634             if(UICMP(64, i, <, -lhv))
 3635                goto jesubstring_len;
 3636             lhv = i + lhv;
 3637          }
 3638          if(UICMP(64, i, >=, lhv)){
 3639             if(UICMP(64, i, !=, lhv))
 3640                varres = savestrbuf(varres, (size_t)lhv);
 3641          }else{
 3642 jesubstring_len:
 3643             if(n_poption & n_PO_D_V)
 3644                n_err(_("`vexpr': substring: length argument too large: %s\n"),
 3645                   n_shexp_quote_cp(argv[-2], FAL0));
 3646             f |= a_SOFTOVERFLOW;
 3647          }
 3648       }
 3649    }else if(is_asccaseprefix(cp, "trim")) Jtrim: {
 3650       struct str trim;
 3651       enum n_str_trim_flags stf;
 3652 
 3653       if(argv[1] == NULL || argv[2] != NULL)
 3654          goto jesynopsis;
 3655 
 3656       if(is_asccaseprefix(cp, "trim-front"))
 3657          stf = n_STR_TRIM_FRONT;
 3658       else if(is_asccaseprefix(cp, "trim-end"))
 3659          stf = n_STR_TRIM_END;
 3660       else
 3661          stf = n_STR_TRIM_BOTH;
 3662 
 3663       trim.l = strlen(trim.s = n_UNCONST(argv[1]));
 3664       (void)n_str_trim(&trim, stf);
 3665       varres = savestrbuf(trim.s, trim.l);
 3666    }else if(is_asccaseprefix(cp, "trim-front"))
 3667       goto Jtrim;
 3668    else if(is_asccaseprefix(cp, "trim-end"))
 3669       goto Jtrim;
 3670    else if(is_asccaseprefix(cp, "random")){
 3671       if(argv[1] == NULL || argv[2] != NULL)
 3672          goto jesynopsis;
 3673 
 3674       if((n_idec_si64_cp(&lhv, argv[1], 0, NULL
 3675                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 3676             ) != n_IDEC_STATE_CONSUMED || lhv < 0 || lhv > PATH_MAX)
 3677          goto jestr_numrange;
 3678       if(lhv == 0)
 3679          lhv = NAME_MAX;
 3680       varres = n_random_create_cp((size_t)lhv, NULL);
 3681    }else if(is_asccaseprefix(cp, "file-expand")){
 3682       if(argv[1] == NULL || argv[2] != NULL)
 3683          goto jesynopsis;
 3684 
 3685       if((varres = fexpand(argv[1], FEXP_NVAR | FEXP_NOPROTO)) == NULL)
 3686          goto jestr_nodata;
 3687    }else if(is_asccaseprefix(cp, "makeprint")){
 3688       struct str sin, sout;
 3689 
 3690       if(argv[1] == NULL || argv[2] != NULL)
 3691          goto jesynopsis;
 3692 
 3693       /* XXX using strlen for `vexpr makeprint' is wrong for UTF-16 */
 3694       sin.l = strlen(sin.s = n_UNCONST(argv[1]));
 3695       makeprint(&sin, &sout);
 3696       varres = savestrbuf(sout.s, sout.l);
 3697       n_free(sout.s);
 3698    /* TODO `vexpr': (wide) string length, find, etc!! */
 3699 #ifdef HAVE_REGEX
 3700    }else if(is_asccaseprefix(cp, "regex")) Jregex:{
 3701       regmatch_t rema[1 + n_VEXPR_REGEX_MAX];
 3702       regex_t re;
 3703       int reflrv;
 3704 
 3705       f |= a_ISNUM | a_ISDECIMAL;
 3706       if(argv[1] == NULL || argv[2] == NULL ||
 3707             (argv[3] != NULL && argv[4] != NULL))
 3708          goto jesynopsis;
 3709 
 3710       reflrv = REG_EXTENDED;
 3711       if(f & a_ICASE)
 3712          reflrv |= REG_ICASE;
 3713       if((reflrv = regcomp(&re, argv[2], reflrv))){
 3714          n_err(_("`vexpr': invalid regular expression: %s: %s\n"),
 3715             n_shexp_quote_cp(argv[2], FAL0), n_regex_err_to_doc(NULL, reflrv));
 3716          assert(f & a_ERR);
 3717          n_pstate_err_no = n_ERR_INVAL;
 3718          goto jestr;
 3719       }
 3720       reflrv = regexec(&re, argv[1], n_NELEM(rema), rema, 0);
 3721       regfree(&re);
 3722       if(reflrv == REG_NOMATCH)
 3723          goto jestr_nodata;
 3724 
 3725       /* Search only?  Else replace, which is a bit */
 3726       if(argv[3] == NULL){
 3727          if(UICMP(64, rema[0].rm_so, >, SI64_MAX))
 3728             goto jestr_overflow;
 3729          lhv = (si64_t)rema[0].rm_so;
 3730       }else{
 3731          /* We need to setup some kind of pseudo macro environment for this */
 3732          struct a_amv_lostack los;
 3733          struct a_amv_mac_call_args amca;
 3734          char const **reargv;
 3735 
 3736          memset(&amca, 0, sizeof amca);
 3737          amca.amca_name = savestrbuf(&argv[1][rema[0].rm_so],
 3738                rema[0].rm_eo - rema[0].rm_so);
 3739          amca.amca_amp = a_AMV_MACKY_MACK;
 3740          for(i = 1; i < n_NELEM(rema) && rema[i].rm_so != -1; ++i)
 3741             ;
 3742          amca.amca_pospar.app_count = (ui32_t)i - 1;
 3743          amca.amca_pospar.app_not_heap = TRU1;
 3744          amca.amca_pospar.app_dat =
 3745          reargv = n_autorec_alloc(sizeof(char*) * i);
 3746          for(i = 1; i < n_NELEM(rema) && rema[i].rm_so != -1; ++i)
 3747             *reargv++ = savestrbuf(&argv[1][rema[i].rm_so],
 3748                   rema[i].rm_eo - rema[i].rm_so);
 3749          *reargv = NULL;
 3750 
 3751          memset(&los, 0, sizeof los);
 3752          hold_all_sigs(); /* TODO DISLIKE! */
 3753          los.as_global_saved = a_amv_lopts;
 3754          los.as_amcap = &amca;
 3755          los.as_up = los.as_global_saved;
 3756          a_amv_lopts = &los;
 3757 
 3758          /* C99 */{
 3759             struct str templ;
 3760             struct n_string s_b;
 3761             enum n_shexp_state shs;
 3762 
 3763             templ.s = n_UNCONST(argv[3]);
 3764             templ.l = UIZ_MAX;
 3765             shs = n_shexp_parse_token((n_SHEXP_PARSE_LOG |
 3766                   n_SHEXP_PARSE_IGNORE_EMPTY | n_SHEXP_PARSE_QUOTE_AUTO_FIXED |
 3767                   n_SHEXP_PARSE_QUOTE_AUTO_DSQ),
 3768                   n_string_creat_auto(&s_b), &templ, NULL);
 3769             if((shs & (n_SHEXP_STATE_ERR_MASK | n_SHEXP_STATE_STOP)
 3770                   ) == n_SHEXP_STATE_STOP){
 3771                varres = n_string_cp(&s_b);
 3772                n_string_drop_ownership(&s_b);
 3773             }else
 3774                varres = NULL;
 3775          }
 3776 
 3777          a_amv_lopts = los.as_global_saved;
 3778          rele_all_sigs(); /* TODO DISLIKE! */
 3779 
 3780          if(varres == NULL)
 3781             goto jestr_nodata;
 3782          f &= ~(a_ISNUM | a_ISDECIMAL);
 3783       }
 3784    }else if(is_asccaseprefix(argv[0], "iregex")){
 3785       f |= a_ICASE;
 3786       goto Jregex;
 3787 #endif /* HAVE_REGEX */
 3788    }else
 3789       goto jesubcmd;
 3790 
 3791    n_pstate_err_no = (f & a_SOFTOVERFLOW) ? n_ERR_OVERFLOW : n_ERR_NONE;
 3792    f &= ~a_ERR;
 3793 
 3794    /* Generate the variable value content for numerics.
 3795     * Anticipate in our handling below!  (Don't do needless work) */
 3796 jleave:
 3797    if((f & a_ISNUM) && ((f & (a_ISDECIMAL | a_PBASE)) || varname != NULL)){
 3798       cp = n_ienc_buf(iencbuf, lhv, (f & a_PBASE ? pbase : 10),
 3799             n_IENC_MODE_SIGNED_TYPE);
 3800       if(cp != NULL)
 3801          varres = cp;
 3802       else{
 3803          f |= a_ERR;
 3804          varres = n_empty;
 3805       }
 3806    }
 3807 
 3808    if(varname == NULL){
 3809       /* If there was no error and we are printing a numeric result, print some
 3810        * more bases for the fun of it */
 3811       if((f & (a_ERR | a_ISNUM | a_ISDECIMAL)) == a_ISNUM){
 3812          char binabuf[64 + 64 / 8 +1];
 3813          size_t j;
 3814 
 3815          for(j = 1, i = 0; i < 64; ++i){
 3816             binabuf[63 + 64 / 8 -j - i] = (lhv & ((ui64_t)1 << i)) ? '1' : '0';
 3817             if((i & 7) == 7 && i != 63){
 3818                ++j;
 3819                binabuf[63 + 64 / 8 -j - i] = ' ';
 3820             }
 3821          }
 3822          binabuf[64 + 64 / 8 -1] = '\0';
 3823 
 3824          if(fprintf(n_stdout,
 3825                   "0b %s\n0%" PRIo64 " | 0x%" PRIX64 " | %" PRId64 "\n",
 3826                   binabuf, lhv, lhv, lhv) < 0 ||
 3827                ((f & a_PBASE) && (assert(varres != NULL),
 3828                 fprintf(n_stdout, "%s\n", varres) < 0))){
 3829             n_pstate_err_no = n_err_no;
 3830             f |= a_ERR;
 3831          }
 3832       }else if(varres != NULL && fprintf(n_stdout, "%s\n", varres) < 0){
 3833          n_pstate_err_no = n_err_no;
 3834          f |= a_ERR;
 3835       }
 3836    }else if(!n_var_vset(varname, (uintptr_t)varres)){
 3837       n_pstate_err_no = n_ERR_NOTSUP;
 3838       f |= a_ERR;
 3839    }
 3840    NYD_LEAVE;
 3841    return (f & a_ERR) ? 1 : 0;
 3842 
 3843 jerr:
 3844    f = a_ERR | a_ISNUM;
 3845    lhv = -1;
 3846    goto jleave;
 3847 jesubcmd:
 3848    n_err(_("`vexpr': invalid subcommand: %s\n"),
 3849       n_shexp_quote_cp(*argv, FAL0));
 3850    n_pstate_err_no = n_ERR_INVAL;
 3851    goto jerr;
 3852 jesynopsis:
 3853    n_err(_("Synopsis: vexpr: <operator> <:argument:>\n"));
 3854    n_pstate_err_no = n_ERR_INVAL;
 3855    goto jerr;
 3856 
 3857 jenum_range:
 3858    n_err(_("`vexpr': numeric argument invalid or out of range: %s\n"),
 3859       n_shexp_quote_cp(*argv, FAL0));
 3860    n_pstate_err_no = n_ERR_RANGE;
 3861    goto jerr;
 3862 jenum_overflow:
 3863    n_err(_("`vexpr': expression overflows datatype: %" PRId64 " %c %" PRId64
 3864       "\n"), lhv, op, rhv);
 3865    n_pstate_err_no = n_ERR_OVERFLOW;
 3866    goto jerr;
 3867 
 3868 jestr_numrange:
 3869    n_err(_("`vexpr': numeric argument invalid or out of range: %s\n"),
 3870       n_shexp_quote_cp(*argv, FAL0));
 3871    n_pstate_err_no = n_ERR_RANGE;
 3872    goto jestr;
 3873 jestr_overflow:
 3874    n_err(_("`vexpr': string length or offset overflows datatype\n"));
 3875    n_pstate_err_no = n_ERR_OVERFLOW;
 3876    goto jestr;
 3877 jestr_nodata:
 3878    n_pstate_err_no = n_ERR_NODATA;
 3879    /* FALLTHRU */
 3880 jestr:
 3881    varres = n_empty;
 3882    f &= ~a_ISNUM;
 3883    f |= a_ERR;
 3884    goto jleave;
 3885 }
 3886 
 3887 FL int
 3888 c_vpospar(void *v){
 3889    struct n_cmd_arg *cap;
 3890    size_t i;
 3891    struct a_amv_pospar *appp;
 3892    enum{
 3893       a_NONE = 0,
 3894       a_ERR = 1u<<0,
 3895       a_SET = 1u<<1,
 3896       a_CLEAR = 1u<<2,
 3897       a_QUOTE = 1u<<3
 3898    } f;
 3899    char const *varres;
 3900    struct n_cmd_arg_ctx *cacp;
 3901    NYD_ENTER;
 3902 
 3903    n_pstate_err_no = n_ERR_NONE;
 3904    n_UNINIT(varres, n_empty);
 3905    cacp = v;
 3906    cap = cacp->cac_arg;
 3907 
 3908    if(is_asccaseprefix(cap->ca_arg.ca_str.s, "set"))
 3909       f = a_SET;
 3910    else if(is_asccaseprefix(cap->ca_arg.ca_str.s, "clear"))
 3911       f = a_CLEAR;
 3912    else if(is_asccaseprefix(cap->ca_arg.ca_str.s, "quote"))
 3913       f = a_QUOTE;
 3914    else{
 3915       n_err(_("`vpospar': invalid subcommand: %s\n"),
 3916          n_shexp_quote_cp(cap->ca_arg.ca_str.s, FAL0));
 3917       n_pstate_err_no = n_ERR_INVAL;
 3918       f = a_ERR;
 3919       goto jleave;
 3920    }
 3921    --cacp->cac_no;
 3922 
 3923    if((f & (a_CLEAR | a_QUOTE)) && cap->ca_next != NULL){
 3924       n_err(_("`vpospar': `%s': takes no argument\n"), cap->ca_arg.ca_str.s);
 3925       n_pstate_err_no = n_ERR_INVAL;
 3926       f = a_ERR;
 3927       goto jleave;
 3928    }
 3929 
 3930    cap = cap->ca_next;
 3931 
 3932    /* If in a macro, we need to overwrite the local instead of global argv */
 3933    appp = (a_amv_lopts != NULL) ? &a_amv_lopts->as_amcap->amca_pospar
 3934          : &a_amv_pospar;
 3935 
 3936    if(f & (a_SET | a_CLEAR)){
 3937       if(cacp->cac_vput != NULL)
 3938          n_err(_("`vpospar': `vput' only supported for `quote' subcommand\n"));
 3939       if(!appp->app_not_heap && appp->app_maxcount > 0){
 3940          for(i = appp->app_maxcount; i-- != 0;)
 3941             n_free(n_UNCONST(appp->app_dat[i]));
 3942          n_free(appp->app_dat);
 3943       }
 3944       memset(appp, 0, sizeof *appp);
 3945 
 3946       if(f & a_SET){
 3947          if((i = cacp->cac_no) > a_AMV_POSPAR_MAX){
 3948             n_err(_("`vpospar': overflow: %" PRIuZ " arguments!\n"), i);
 3949             n_pstate_err_no = n_ERR_OVERFLOW;
 3950             f = a_ERR;
 3951             goto jleave;
 3952          }
 3953 
 3954          memset(appp, 0, sizeof *appp);
 3955          if(i > 0){
 3956             appp->app_maxcount = appp->app_count = (ui16_t)i;
 3957             /* XXX Optimize: store it all in one chunk! */
 3958             ++i;
 3959             i *= sizeof *appp->app_dat;
 3960             appp->app_dat = n_alloc(i);
 3961 
 3962             for(i = 0; cap != NULL; ++i, cap = cap->ca_next){
 3963                appp->app_dat[i] = n_alloc(cap->ca_arg.ca_str.l +1);
 3964                memcpy(n_UNCONST(appp->app_dat[i]), cap->ca_arg.ca_str.s,
 3965                   cap->ca_arg.ca_str.l +1);
 3966             }
 3967 
 3968             appp->app_dat[i] = NULL;
 3969          }
 3970       }
 3971    }else{
 3972       if(appp->app_count == 0)
 3973          varres = n_empty;
 3974       else{
 3975          struct str in;
 3976          struct n_string s, *sp;
 3977          char sep1, sep2;
 3978 
 3979          sp = n_string_creat_auto(&s);
 3980 
 3981          sep1 = *ok_vlook(ifs);
 3982          sep2 = *ok_vlook(ifs_ws);
 3983          if(sep1 == sep2)
 3984             sep2 = '\0';
 3985          if(sep1 == '\0')
 3986             sep1 = ' ';
 3987 
 3988          for(i = 0; i < appp->app_count; ++i){
 3989             if(sp->s_len){
 3990                if(!n_string_can_book(sp, 2))
 3991                   goto jeover;
 3992                sp = n_string_push_c(sp, sep1);
 3993                if(sep2 != '\0')
 3994                   sp = n_string_push_c(sp, sep2);
 3995             }
 3996             in.l = strlen(in.s = n_UNCONST(appp->app_dat[i + appp->app_idx]));
 3997 
 3998             if(!n_string_can_book(sp, in.l)){
 3999 jeover:
 4000                n_err(_("`vpospar': overflow: string too long!\n"));
 4001                n_pstate_err_no = n_ERR_OVERFLOW;
 4002                f = a_ERR;
 4003                goto jleave;
 4004             }
 4005             sp = n_shexp_quote(sp, &in, TRU1);
 4006          }
 4007 
 4008          varres = n_string_cp(sp);
 4009       }
 4010 
 4011       if(cacp->cac_vput == NULL){
 4012          if(fprintf(n_stdout, "%s\n", varres) < 0){
 4013             n_pstate_err_no = n_err_no;
 4014             f |= a_ERR;
 4015          }
 4016       }else if(!n_var_vset(cacp->cac_vput, (uintptr_t)varres)){
 4017          n_pstate_err_no = n_ERR_NOTSUP;
 4018          f |= a_ERR;
 4019       }
 4020    }
 4021 jleave:
 4022    NYD_LEAVE;
 4023    return (f & a_ERR) ? 1 : 0;
 4024 }
 4025 
 4026 /* s-it-mode */