"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/auxlily.c" (8 Aug 2018, 52166 Bytes) of package /linux/misc/s-nail-14.9.11.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 "auxlily.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Auxiliary functions that don't fit anywhere else.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  * SPDX-License-Identifier: BSD-3-Clause
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE auxlily
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 #include <sys/utsname.h>
   44 
   45 #ifdef HAVE_SOCKETS
   46 # ifdef HAVE_GETADDRINFO
   47 #  include <sys/socket.h>
   48 # endif
   49 
   50 # include <netdb.h>
   51 #endif
   52 
   53 #ifdef HAVE_NL_LANGINFO
   54 # include <langinfo.h>
   55 #endif
   56 #ifdef HAVE_SETLOCALE
   57 # include <locale.h>
   58 #endif
   59 
   60 #if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
   61 # include n_RANDOM_GETRANDOM_H
   62 #endif
   63 
   64 #ifdef HAVE_IDNA
   65 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
   66 #  include <idn2.h>
   67 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
   68 #  include <idna.h>
   69 #  include <idn-free.h>
   70 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
   71 #  include <idn/api.h>
   72 # endif
   73 #endif
   74 
   75 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
   76 union rand_state{
   77    struct rand_arc4{
   78       ui8_t _dat[256];
   79       ui8_t _i;
   80       ui8_t _j;
   81       ui8_t __pad[6];
   82    } a;
   83    ui8_t b8[sizeof(struct rand_arc4)];
   84    ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
   85 };
   86 #endif
   87 
   88 #ifdef HAVE_ERRORS
   89 struct a_aux_err_node{
   90    struct a_aux_err_node *ae_next;
   91    struct n_string ae_str;
   92 };
   93 #endif
   94 
   95 struct a_aux_err_map{
   96    ui32_t aem_hash;     /* Hash of name */
   97    ui32_t aem_nameoff;  /* Into a_aux_err_names[] */
   98    ui32_t aem_docoff;   /* Into a_aux_err docs[] (if HAVE_DOCSTRINGS) */
   99    si32_t aem_err_no;   /* The OS error value for this one */
  100 };
  101 
  102 /* IDEC: byte to integer value lookup table */
  103 static ui8_t const a_aux_idec_atoi[256] = {
  104    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  105    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  106    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  107    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  108    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
  109    0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
  110    0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
  111    0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
  112    0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
  113    0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
  114    0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
  115    0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
  116    0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  117    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  118    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  119    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  120    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  121    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  122    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  123    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  124    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  125    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  126    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  127    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  128    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  129    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
  130 };
  131 
  132 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
  133 #define a_X(X) (UI64_MAX / (X))
  134 static ui64_t const a_aux_idec_cutlimit[35] = {
  135    a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
  136    a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
  137    a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
  138    a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
  139    a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
  140 };
  141 #undef a_X
  142 
  143 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
  144 static ui8_t const a_aux_ienc_shifts[35] = {
  145          1, 0, 2, 0, 0, 0, 3, 0,   /*  2 ..  9 */
  146    0, 0, 0, 0, 0, 0, 4, 0, 0, 0,   /* 10 .. 19 */
  147    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 20 .. 29 */
  148    0, 0, 5, 0, 0, 0, 0             /* 30 .. 36 */
  149 };
  150 
  151 /* IENC: integer to byte lookup tables */
  152 static char const a_aux_ienc_itoa_upper[36] =
  153       "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  154 static char const a_aux_ienc_itoa_lower[36] =
  155       "0123456789abcdefghijklmnopqrstuvwxyz";
  156 
  157 /* Include the constant make-errors.sh output */
  158 #include <gen-errors.h>
  159 
  160 /* And these things come from mk-config.h (config-time make-errors.sh output) */
  161 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
  162 #undef a_X
  163 #define a_X(N,I) {N,I},
  164 n__ERR_NUMBER_TO_MAPOFF
  165 #undef a_X
  166 };
  167 
  168 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
  169 static union rand_state *a_aux_rand;
  170 #endif
  171 
  172 /* Error ring, for `errors' */
  173 #ifdef HAVE_ERRORS
  174 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
  175 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
  176 #endif
  177 static size_t a_aux_err_linelen;
  178 
  179 /* Our ARC4 random generator with its completely unacademical pseudo
  180  * initialization (shall /dev/urandom fail) */
  181 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
  182 static void a_aux_rand_init(void);
  183 n_INLINE ui8_t a_aux_rand_get8(void);
  184 static ui32_t a_aux_rand_weak(ui32_t seed);
  185 #endif
  186 
  187 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
  188 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
  189 
  190 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
  191 static void
  192 a_aux_rand_init(void){
  193 # ifdef HAVE_CLOCK_GETTIME
  194    struct timespec ts;
  195 # else
  196    struct timeval ts;
  197 # endif
  198    union {int fd; size_t i;} u;
  199    ui32_t seed, rnd;
  200    NYD2_ENTER;
  201 
  202    a_aux_rand = n_alloc(sizeof *a_aux_rand);
  203 
  204 # if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
  205    /* getrandom(2) guarantees 256 without n_ERR_INTR..
  206     * However, support sequential reading to avoid possible hangs that have
  207     * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
  208     * HAVE_GETRANDOM is #defined) */
  209    n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
  210       "Buffer too large to be served without n_ERR_INTR error");
  211    n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
  212       "Buffer too small to serve used array indices");
  213    /* C99 */{
  214       size_t o, i;
  215 
  216       for(o = 0, i = sizeof a_aux_rand->a._dat;;){
  217          ssize_t gr;
  218 
  219          gr = n_RANDOM_GETRANDOM_FUN(&a_aux_rand->a._dat[o], i);
  220          if(gr == -1 && n_err_no == n_ERR_NOSYS)
  221             break;
  222          a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
  223                a_aux_rand->a._dat[84]];
  224          a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
  225                a_aux_rand->a._dat[42]];
  226          /* ..but be on the safe side */
  227          if(gr > 0){
  228             i -= (size_t)gr;
  229             if(i == 0)
  230                goto jleave;
  231             o += (size_t)gr;
  232          }
  233          n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
  234             "waiting a bit\n"));
  235          n_msleep(250, FAL0);
  236       }
  237    }
  238 
  239 # elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
  240    if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
  241       bool_t ok;
  242 
  243       ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
  244             sizeof(a_aux_rand->a._dat)));
  245       close(u.fd);
  246 
  247       a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
  248             a_aux_rand->a._dat[84]];
  249       a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
  250             a_aux_rand->a._dat[42]];
  251       if(ok)
  252          goto jleave;
  253    }
  254 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
  255 #  error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
  256 # endif
  257 
  258    /* As a fallback, a homebrew seed */
  259    if(n_poption & n_PO_D_V)
  260       n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
  261    for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
  262       for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
  263          ui32_t t, k;
  264 
  265 # ifdef HAVE_CLOCK_GETTIME
  266          clock_gettime(CLOCK_REALTIME, &ts);
  267          t = (ui32_t)ts.tv_nsec;
  268 # else
  269          gettimeofday(&ts, NULL);
  270          t = (ui32_t)ts.tv_usec;
  271 # endif
  272          if(rnd & 1)
  273             t = (t >> 16) | (t << 16);
  274          a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
  275          a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
  276          if(rnd == 7 || rnd == 17)
  277             a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
  278          k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
  279          a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
  280          seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
  281          if((rnd & 3) == 3)
  282             seed ^= n_prime_next(seed);
  283       }
  284    }
  285 
  286    for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
  287       a_aux_rand_get8();
  288 jleave:
  289    NYD2_LEAVE;
  290 }
  291 
  292 n_INLINE ui8_t
  293 a_aux_rand_get8(void){
  294    ui8_t si, sj;
  295 
  296    si = a_aux_rand->a._dat[++a_aux_rand->a._i];
  297    sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
  298    a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
  299    a_aux_rand->a._dat[a_aux_rand->a._j] = si;
  300    return a_aux_rand->a._dat[(ui8_t)(si + sj)];
  301 }
  302 
  303 static ui32_t
  304 a_aux_rand_weak(ui32_t seed){
  305    /* From "Random number generators: good ones are hard to find",
  306     * Park and Miller, Communications of the ACM, vol. 31, no. 10,
  307     * October 1988, p. 1195.
  308     * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
  309    ui32_t hi;
  310 
  311    if(seed == 0)
  312       seed = 123459876;
  313    hi =  seed /  127773;
  314          seed %= 127773;
  315    seed = (seed * 16807) - (hi * 2836);
  316    if((si32_t)seed < 0)
  317       seed += SI32_MAX;
  318    return seed;
  319 }
  320 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_TLS */
  321 
  322 static struct a_aux_err_map const *
  323 a_aux_err_map_from_no(si32_t eno){
  324    si32_t ecmp;
  325    size_t asz;
  326    n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
  327    struct a_aux_err_map const *aemp;
  328    NYD2_ENTER;
  329 
  330    aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
  331 
  332    if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
  333       for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
  334             asz != 0; asz >>= 1){
  335          tmp = &adat[asz >> 1];
  336          if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
  337             aemp = &a_aux_err_map[(*tmp)[1]];
  338             break;
  339          }
  340          if(ecmp > 0){
  341             adat = &tmp[1];
  342             --asz;
  343          }
  344       }
  345    }
  346    NYD2_LEAVE;
  347    return aemp;
  348 }
  349 
  350 FL void
  351 n_locale_init(void){
  352    NYD2_ENTER;
  353 
  354    n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
  355 
  356 #ifndef HAVE_SETLOCALE
  357    n_mb_cur_max = 1;
  358 #else
  359    setlocale(LC_ALL, n_empty);
  360    n_mb_cur_max = MB_CUR_MAX;
  361 # ifdef HAVE_NL_LANGINFO
  362    /* C99 */{
  363       char const *cp;
  364 
  365       if((cp = nl_langinfo(CODESET)) != NULL)
  366          /* (Will log during startup if user set that via -S) */
  367          ok_vset(ttycharset, cp);
  368    }
  369 # endif /* HAVE_SETLOCALE */
  370 
  371 # ifdef HAVE_C90AMEND1
  372    if(n_mb_cur_max > 1){
  373 #  ifdef HAVE_ALWAYS_UNICODE_LOCALE
  374       n_psonce |= n_PSO_UNICODE;
  375 #  else
  376       wchar_t wc;
  377       if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
  378             mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
  379          n_psonce |= n_PSO_UNICODE;
  380       /* Reset possibly messed up state; luckily this also gives us an
  381        * indication whether the encoding has locking shift state sequences */
  382       if(mbtowc(&wc, NULL, n_mb_cur_max))
  383          n_psonce |= n_PSO_ENC_MBSTATE;
  384 #  endif
  385    }
  386 # endif
  387 #endif /* HAVE_C90AMEND1 */
  388    NYD2_LEAVE;
  389 }
  390 
  391 FL size_t
  392 n_screensize(void){
  393    char const *cp;
  394    uiz_t rv;
  395    NYD2_ENTER;
  396 
  397    if((cp = ok_vlook(screen)) != NULL){
  398       n_idec_uiz_cp(&rv, cp, 0, NULL);
  399       if(rv == 0)
  400          rv = n_scrnheight;
  401    }else
  402       rv = n_scrnheight;
  403 
  404    if(rv > 2)
  405       rv -= 2;
  406    NYD2_LEAVE;
  407    return rv;
  408 }
  409 
  410 FL char const *
  411 n_pager_get(char const **env_addon){
  412    char const *rv;
  413    NYD_ENTER;
  414 
  415    rv = ok_vlook(PAGER);
  416 
  417    if(env_addon != NULL){
  418       *env_addon = NULL;
  419       /* Update the manual upon any changes:
  420        *    *colour-pager*, $PAGER */
  421       if(strstr(rv, "less") != NULL){
  422          if(getenv("LESS") == NULL)
  423             *env_addon = "LESS=RXi";
  424       }else if(strstr(rv, "lv") != NULL){
  425          if(getenv("LV") == NULL)
  426             *env_addon = "LV=-c";
  427       }
  428    }
  429    NYD_LEAVE;
  430    return rv;
  431 }
  432 
  433 FL void
  434 page_or_print(FILE *fp, size_t lines)
  435 {
  436    int c;
  437    char const *cp;
  438    NYD_ENTER;
  439 
  440    fflush_rewind(fp);
  441 
  442    if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
  443       size_t rows;
  444 
  445       if(*cp == '\0')
  446          rows = (size_t)n_scrnheight;
  447       else
  448          n_idec_uiz_cp(&rows, cp, 0, NULL);
  449 
  450       if (rows > 0 && lines == 0) {
  451          while ((c = getc(fp)) != EOF)
  452             if (c == '\n' && ++lines >= rows)
  453                break;
  454          really_rewind(fp);
  455       }
  456 
  457       if (lines >= rows) {
  458          char const *env_add[2], *pager;
  459 
  460          pager = n_pager_get(&env_add[0]);
  461          env_add[1] = NULL;
  462          n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
  463             env_add, NULL);
  464          goto jleave;
  465       }
  466    }
  467 
  468    while ((c = getc(fp)) != EOF)
  469       putc(c, n_stdout);
  470 jleave:
  471    NYD_LEAVE;
  472 }
  473 
  474 FL enum protocol
  475 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
  476    char const **adjusted_or_null)
  477 {
  478    /* TODO This which_protocol() sickness should be URL::new()->protocol() */
  479    char const *cp, *orig_name;
  480    enum protocol rv = PROTO_UNKNOWN;
  481    NYD_ENTER;
  482 
  483    if(name[0] == '%' && name[1] == ':')
  484       name += 2;
  485    orig_name = name;
  486 
  487    for (cp = name; *cp && *cp != ':'; cp++)
  488       if (!alnumchar(*cp))
  489          goto jfile;
  490 
  491    if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
  492       if(!strncmp(name, "file", sizeof("file") -1) ||
  493             !strncmp(name, "mbox", sizeof("mbox") -1))
  494          rv = PROTO_FILE;
  495       else if(!strncmp(name, "maildir", sizeof("maildir") -1)){
  496 #ifdef HAVE_MAILDIR
  497          rv = PROTO_MAILDIR;
  498 #else
  499          n_err(_("No Maildir directory support compiled in\n"));
  500 #endif
  501       }else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
  502 #ifdef HAVE_POP3
  503          rv = PROTO_POP3;
  504 #else
  505          n_err(_("No POP3 support compiled in\n"));
  506 #endif
  507       }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
  508 #if defined HAVE_POP3 && defined HAVE_TLS
  509          rv = PROTO_POP3;
  510 #else
  511          n_err(_("No POP3S support compiled in\n"));
  512 #endif
  513       }else if(!strncmp(name, "imap", sizeof("imap") -1)){
  514 #ifdef HAVE_IMAP
  515          rv = PROTO_IMAP;
  516 #else
  517          n_err(_("No IMAP support compiled in\n"));
  518 #endif
  519       }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
  520 #if defined HAVE_IMAP && defined HAVE_TLS
  521          rv = PROTO_IMAP;
  522 #else
  523          n_err(_("No IMAPS support compiled in\n"));
  524 #endif
  525       }
  526       orig_name = &cp[3];
  527       goto jleave;
  528    }
  529 
  530 jfile:
  531    rv = PROTO_FILE;
  532 
  533    if(check_stat || try_hooks){
  534       struct n_file_type ft;
  535       struct stat stb;
  536       char *np;
  537       size_t sz;
  538 
  539       np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
  540       memcpy(np, name, sz + 1);
  541 
  542       if(!stat(name, &stb)){
  543          if(S_ISDIR(stb.st_mode)
  544 #ifdef HAVE_MAILDIR
  545                && (memcpy(&np[sz], "/tmp", 5),
  546                   !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
  547                (memcpy(&np[sz], "/new", 5),
  548                   !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
  549                (memcpy(&np[sz], "/cur", 5),
  550                   !stat(np, &stb) && S_ISDIR(stb.st_mode))
  551 #endif
  552                ){
  553 #ifdef HAVE_MAILDIR
  554             rv = PROTO_MAILDIR;
  555 #else
  556             rv = PROTO_UNKNOWN;
  557 #endif
  558          }
  559       }else if(try_hooks && n_filetype_trial(&ft, name))
  560          orig_name = savecatsep(name, '.', ft.ft_ext_dat);
  561       else if((cp = ok_vlook(newfolders)) != NULL &&
  562             !asccasecmp(cp, "maildir")){
  563 #ifdef HAVE_MAILDIR
  564          rv = PROTO_MAILDIR;
  565 #else
  566          n_err(_("*newfolders*: no Maildir directory support compiled in\n"));
  567 #endif
  568       }
  569 
  570       n_lofi_free(np);
  571    }
  572 jleave:
  573    if(adjusted_or_null != NULL)
  574       *adjusted_or_null = orig_name;
  575    NYD_LEAVE;
  576    return rv;
  577 }
  578 
  579 FL char *
  580 n_c_to_hex_base16(char store[3], char c){
  581    static char const itoa16[] = "0123456789ABCDEF";
  582    NYD2_ENTER;
  583 
  584    store[2] = '\0';
  585    store[1] = itoa16[(ui8_t)c & 0x0F];
  586    c = ((ui8_t)c >> 4) & 0x0F;
  587    store[0] = itoa16[(ui8_t)c];
  588    NYD2_LEAVE;
  589    return store;
  590 }
  591 
  592 FL si32_t
  593 n_c_from_hex_base16(char const hex[2]){
  594    static ui8_t const atoi16[] = {
  595       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
  596       0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
  597       0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
  598       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
  599       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
  600       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
  601       0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF  /* 0x60-0x67 */
  602    };
  603    ui8_t i1, i2;
  604    si32_t rv;
  605    NYD2_ENTER;
  606 
  607    if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
  608          (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
  609       goto jerr;
  610    i1 = atoi16[i1];
  611    i2 = atoi16[i2];
  612    if ((i1 | i2) & 0xF0u)
  613       goto jerr;
  614    rv = i1;
  615    rv <<= 4;
  616    rv += i2;
  617 jleave:
  618    NYD2_LEAVE;
  619    return rv;
  620 jerr:
  621    rv = -1;
  622    goto jleave;
  623 }
  624 
  625 FL enum n_idec_state
  626 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
  627       enum n_idec_mode idm, char const **endptr_or_null){
  628    ui8_t currc;
  629    ui64_t res, cut;
  630    enum n_idec_state rv;
  631    NYD_ENTER;
  632 
  633    idm &= n__IDEC_MODE_MASK;
  634    rv = n_IDEC_STATE_NONE | idm;
  635    res = 0;
  636 
  637    if(clen == UIZ_MAX){
  638       if(*cbuf == '\0')
  639          goto jeinval;
  640    }else if(clen == 0)
  641       goto jeinval;
  642 
  643    assert(base != 1 && base <= 36);
  644    /*if(base == 1 || base > 36)
  645     *   goto jeinval;*/
  646 
  647    /* Leading WS */
  648    while(spacechar(*cbuf))
  649       if(*++cbuf == '\0' || --clen == 0)
  650          goto jeinval;
  651 
  652    /* Check sign */
  653    switch(*cbuf){
  654    case '-':
  655       rv |= n_IDEC_STATE_SEEN_MINUS;
  656       /* FALLTHROUGH */
  657    case '+':
  658       if(*++cbuf == '\0' || --clen == 0)
  659          goto jeinval;
  660       break;
  661    }
  662 
  663    /* Base detection/skip */
  664    if(*cbuf != '0'){
  665       if(base == 0){
  666          base = 10;
  667 
  668          /* Support BASE#number prefix, where BASE is decimal 2-36 */
  669          if(clen > 2){
  670             char c1, c2, c3;
  671 
  672             if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
  673                   (((c2 = cbuf[1]) == '#') ||
  674                    (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
  675                base = a_aux_idec_atoi[(ui8_t)c1];
  676                if(c2 == '#')
  677                   c3 = cbuf[2];
  678                else{
  679                   c3 = cbuf[3];
  680                   base *= 10; /* xxx Inline atoi decimal base */
  681                   base += a_aux_idec_atoi[(ui8_t)c2];
  682                }
  683 
  684                /* We do not interpret this as BASE#number at all if either we
  685                 * did not get a valid base or if the first char is not valid
  686                 * according to base, to comply to the latest interpretion of
  687                 * "prefix", see comment for standard prefixes below */
  688                if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
  689                   base = 10;
  690                else if(c2 == '#')
  691                   clen -= 2, cbuf += 2;
  692                else
  693                   clen -= 3, cbuf += 3;
  694             }
  695          }
  696       }
  697 
  698       /* Character must be valid for base */
  699       currc = a_aux_idec_atoi[(ui8_t)*cbuf];
  700       if(currc >= base)
  701          goto jeinval;
  702    }else{
  703       /* 0 always valid as is, fallback base 10 */
  704       if(*++cbuf == '\0' || --clen == 0)
  705          goto jleave;
  706 
  707       /* Base "detection" */
  708       if(base == 0 || base == 2 || base == 16){
  709          switch(*cbuf){
  710          case 'x':
  711          case 'X':
  712             if((base & 2) == 0){
  713                base = 0x10;
  714                goto jprefix_skip;
  715             }
  716             break;
  717          case 'b':
  718          case 'B':
  719             if((base & 16) == 0){
  720                base = 2; /* 0b10 */
  721                /* Char after prefix must be valid.  However, after some error
  722                 * in the tor software all libraries (which had to) turned to
  723                 * an interpretation of the C standard which says that the
  724                 * prefix may optionally precede an otherwise valid sequence,
  725                 * which means that "0x" is not a STATE_INVAL error but gives
  726                 * a "0" result with a "STATE_BASE" error and a rest of "x" */
  727 jprefix_skip:
  728 #if 1
  729                if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
  730                   --clen, ++cbuf;
  731 #else
  732                if(*++cbuf == '\0' || --clen == 0)
  733                   goto jeinval;
  734 
  735                /* Character must be valid for base, invalid otherwise */
  736                currc = a_aux_idec_atoi[(ui8_t)*cbuf];
  737                if(currc >= base)
  738                   goto jeinval;
  739 #endif
  740             }
  741             break;
  742          default:
  743             if(base == 0)
  744                base = 010;
  745             break;
  746          }
  747       }
  748 
  749       /* Character must be valid for base, _EBASE otherwise */
  750       currc = a_aux_idec_atoi[(ui8_t)*cbuf];
  751       if(currc >= base)
  752          goto jebase;
  753    }
  754 
  755    for(cut = a_aux_idec_cutlimit[base - 2];;){
  756       if(res >= cut){
  757          if(res == cut){
  758             res *= base;
  759             if(res > UI64_MAX - currc)
  760                goto jeover;
  761             res += currc;
  762          }else
  763             goto jeover;
  764       }else{
  765          res *= base;
  766          res += currc;
  767       }
  768 
  769       if(*++cbuf == '\0' || --clen == 0)
  770          break;
  771 
  772       currc = a_aux_idec_atoi[(ui8_t)*cbuf];
  773       if(currc >= base)
  774          goto jebase;
  775    }
  776 
  777 jleave:
  778    do{
  779       ui64_t uimask;
  780 
  781       switch(rv & n__IDEC_MODE_LIMIT_MASK){
  782       case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
  783       case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
  784       case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
  785       default: uimask = UI64_MAX; break;
  786       }
  787       if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
  788             (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
  789          uimask >>= 1;
  790 
  791       if(res & ~uimask){
  792          /* XXX never entered unless _SIGNED_TYPE! */
  793          if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
  794                ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
  795             if(res > uimask + 1){
  796                res = uimask << 1;
  797                res &= ~uimask;
  798             }else{
  799                res = -res;
  800                break;
  801             }
  802          }else
  803             res = uimask;
  804          if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
  805             rv |= n_IDEC_STATE_EOVERFLOW;
  806       }else if(rv & n_IDEC_STATE_SEEN_MINUS)
  807          res = -res;
  808    }while(0);
  809 
  810    switch(rv & n__IDEC_MODE_LIMIT_MASK){
  811    case n_IDEC_MODE_LIMIT_8BIT:
  812       if(rv & n_IDEC_MODE_SIGNED_TYPE)
  813          *(si8_t*)resp = (si8_t)res;
  814       else
  815          *(ui8_t*)resp = (ui8_t)res;
  816       break;
  817    case n_IDEC_MODE_LIMIT_16BIT:
  818       if(rv & n_IDEC_MODE_SIGNED_TYPE)
  819          *(si16_t*)resp = (si16_t)res;
  820       else
  821          *(ui16_t*)resp = (ui16_t)res;
  822       break;
  823    case n_IDEC_MODE_LIMIT_32BIT:
  824       if(rv & n_IDEC_MODE_SIGNED_TYPE)
  825          *(si32_t*)resp = (si32_t)res;
  826       else
  827          *(ui32_t*)resp = (ui32_t)res;
  828       break;
  829    default:
  830       if(rv & n_IDEC_MODE_SIGNED_TYPE)
  831          *(si64_t*)resp = (si64_t)res;
  832       else
  833          *(ui64_t*)resp = (ui64_t)res;
  834       break;
  835    }
  836 
  837    if(endptr_or_null != NULL)
  838       *endptr_or_null = cbuf;
  839    if(*cbuf == '\0' || clen == 0)
  840       rv |= n_IDEC_STATE_CONSUMED;
  841    NYD_LEAVE;
  842    return rv;
  843 
  844 jeinval:
  845    rv |= n_IDEC_STATE_EINVAL;
  846    goto j_maxval;
  847 jebase:
  848    /* Not a base error for terminator and whitespace! */
  849    if(*cbuf != '\0' && !spacechar(*cbuf))
  850       rv |= n_IDEC_STATE_EBASE;
  851    goto jleave;
  852 
  853 jeover:
  854    /* Overflow error: consume input until bad character or length out */
  855    for(;;){
  856       if(*++cbuf == '\0' || --clen == 0)
  857          break;
  858       currc = a_aux_idec_atoi[(ui8_t)*cbuf];
  859       if(currc >= base)
  860          break;
  861    }
  862 
  863    rv |= n_IDEC_STATE_EOVERFLOW;
  864 j_maxval:
  865    if(rv & n_IDEC_MODE_SIGNED_TYPE)
  866       res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
  867             : (ui64_t)SI64_MAX;
  868    else
  869       res = UI64_MAX;
  870    rv &= ~n_IDEC_STATE_SEEN_MINUS;
  871    goto jleave;
  872 }
  873 
  874 FL char *
  875 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
  876       enum n_ienc_mode iem){
  877    enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
  878 
  879    ui8_t shiftmodu;
  880    char const *itoa;
  881    char *rv;
  882    NYD_ENTER;
  883 
  884    iem &= n__IENC_MODE_MASK;
  885 
  886    assert(base != 1 && base <= 36);
  887    /*if(base == 1 || base > 36){
  888     *   rv = NULL;
  889     *   goto jleave;
  890     *}*/
  891 
  892    *(rv = &cbuf[n_IENC_BUFFER_SIZE -1]) = '\0';
  893    itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
  894          : a_aux_ienc_itoa_upper;
  895 
  896    if((si64_t)value < 0){
  897       iem |= a_ISNEG;
  898       if(iem & n_IENC_MODE_SIGNED_TYPE){
  899          /* self->is_negative = TRU1; */
  900          value = -value;
  901       }
  902    }
  903 
  904    if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
  905       --base; /* convert to mask */
  906       do{
  907          *--rv = itoa[value & base];
  908          value >>= shiftmodu;
  909       }while(value != 0);
  910 
  911       if(!(iem & n_IENC_MODE_NO_PREFIX)){
  912          /* self->before_prefix = cp; */
  913          if(shiftmodu == 4)
  914             *--rv = 'x';
  915          else if(shiftmodu == 1)
  916             *--rv = 'b';
  917          else if(shiftmodu != 3){
  918             ++base; /* Reconvert from mask */
  919             goto jnumber_sign_prefix;
  920          }
  921          *--rv = '0';
  922       }
  923    }else{
  924       do{
  925          shiftmodu = value % base;
  926          value /= base;
  927          *--rv = itoa[shiftmodu];
  928       }while(value != 0);
  929 
  930       if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
  931 jnumber_sign_prefix:
  932          value = base;
  933          base = 10;
  934          *--rv = '#';
  935          do{
  936             shiftmodu = value % base;
  937             value /= base;
  938             *--rv = itoa[shiftmodu];
  939          }while(value != 0);
  940       }
  941 
  942       if(iem & n_IENC_MODE_SIGNED_TYPE){
  943          char c;
  944 
  945          if(iem & a_ISNEG)
  946             c = '-';
  947          else if(iem & n_IENC_MODE_SIGNED_PLUS)
  948             c = '+';
  949          else if(iem & n_IENC_MODE_SIGNED_SPACE)
  950             c = ' ';
  951          else
  952             c = '\0';
  953 
  954          if(c != '\0')
  955             *--rv = c;
  956       }
  957    }
  958    NYD_LEAVE;
  959    return rv;
  960 }
  961 
  962 FL ui32_t
  963 n_torek_hash(char const *name){
  964    /* Chris Torek's hash */
  965    char c;
  966    ui32_t h;
  967    NYD2_ENTER;
  968 
  969    for(h = 0; (c = *name++) != '\0';)
  970       h = (h * 33) + c;
  971    NYD2_LEAVE;
  972    return h;
  973 }
  974 
  975 FL ui32_t
  976 n_torek_ihashn(char const *dat, size_t len){
  977    /* See n_torek_hash() */
  978    char c;
  979    ui32_t h;
  980    NYD2_ENTER;
  981 
  982    if(len == UIZ_MAX)
  983       for(h = 0; (c = *dat++) != '\0';)
  984          h = (h * 33) + lowerconv(c);
  985    else
  986       for(h = 0; len > 0; --len){
  987          c = *dat++;
  988          h = (h * 33) + lowerconv(c);
  989       }
  990    NYD2_LEAVE;
  991    return h;
  992 }
  993 
  994 FL ui32_t
  995 n_prime_next(ui32_t n){
  996    static ui32_t const primes[] = {
  997       5, 11, 23, 47, 97, 157, 283,
  998       509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
  999       131071, 262139, 524287, 1048573, 2097143, 4194301,
 1000       8388593, 16777213, 33554393, 67108859, 134217689,
 1001       268435399, 536870909, 1073741789, 2147483647
 1002    };
 1003    ui32_t i, mprime;
 1004    NYD2_ENTER;
 1005 
 1006    i = (n < primes[n_NELEM(primes) / 4] ? 0
 1007          : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
 1008          : n_NELEM(primes) / 2));
 1009 
 1010    do if((mprime = primes[i]) > n)
 1011       break;
 1012    while(++i < n_NELEM(primes));
 1013 
 1014    if(i == n_NELEM(primes) && mprime < n)
 1015       mprime = n;
 1016    NYD2_LEAVE;
 1017    return mprime;
 1018 }
 1019 
 1020 FL char const *
 1021 n_getdeadletter(void){
 1022    char const *cp;
 1023    bool_t bla;
 1024    NYD_ENTER;
 1025 
 1026    bla = FAL0;
 1027 jredo:
 1028    cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
 1029    if(cp == NULL || strlen(cp) >= PATH_MAX){
 1030       if(!bla){
 1031          n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
 1032             VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
 1033          ok_vclear(DEAD);
 1034          bla = TRU1;
 1035          goto jredo;
 1036       }else{
 1037          cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
 1038          n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
 1039       }
 1040    }
 1041    NYD_LEAVE;
 1042    return cp;
 1043 }
 1044 
 1045 FL char *
 1046 n_nodename(bool_t mayoverride){
 1047    static char *sys_hostname, *hostname; /* XXX free-at-exit */
 1048 
 1049    struct utsname ut;
 1050    char *hn;
 1051 #ifdef HAVE_SOCKETS
 1052 # ifdef HAVE_GETADDRINFO
 1053    struct addrinfo hints, *res;
 1054 # else
 1055    struct hostent *hent;
 1056 # endif
 1057 #endif
 1058    NYD2_ENTER;
 1059 
 1060    if(n_psonce & n_PSO_REPRODUCIBLE)
 1061       hn = n_UNCONST(n_reproducible_name);
 1062    else if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
 1063       ;
 1064    }else if((hn = sys_hostname) == NULL){
 1065       bool_t lofi;
 1066 
 1067       lofi = FAL0;
 1068       uname(&ut);
 1069       hn = ut.nodename;
 1070 
 1071 #ifdef HAVE_SOCKETS
 1072 # ifdef HAVE_GETADDRINFO
 1073       memset(&hints, 0, sizeof hints);
 1074       hints.ai_family = AF_UNSPEC;
 1075       hints.ai_flags = AI_CANONNAME;
 1076       if(getaddrinfo(hn, NULL, &hints, &res) == 0){
 1077          if(res->ai_canonname != NULL){
 1078             size_t l;
 1079 
 1080             l = strlen(res->ai_canonname) +1;
 1081             hn = n_lofi_alloc(l);
 1082             lofi = TRU1;
 1083             memcpy(hn, res->ai_canonname, l);
 1084          }
 1085          freeaddrinfo(res);
 1086       }
 1087 # else
 1088       hent = gethostbyname(hn);
 1089       if(hent != NULL)
 1090          hn = hent->h_name;
 1091 # endif
 1092 #endif /* HAVE_SOCKETS */
 1093 
 1094 #ifdef HAVE_IDNA
 1095       /* C99 */{
 1096          struct n_string cnv;
 1097 
 1098          n_string_creat(&cnv);
 1099          if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
 1100             n_panic(_("The system hostname is invalid, "
 1101                   "IDNA conversion failed: %s\n"),
 1102                n_shexp_quote_cp(hn, FAL0));
 1103          sys_hostname = n_string_cp(&cnv);
 1104          n_string_drop_ownership(&cnv);
 1105          /*n_string_gut(&cnv);*/
 1106       }
 1107 #else
 1108       sys_hostname = sstrdup(hn);
 1109 #endif
 1110 
 1111       if(lofi)
 1112          n_lofi_free(hn);
 1113       hn = sys_hostname;
 1114    }
 1115 
 1116    if(hostname != NULL && hostname != sys_hostname)
 1117       n_free(hostname);
 1118    hostname = sstrdup(hn);
 1119    NYD2_LEAVE;
 1120    return hostname;
 1121 }
 1122 
 1123 #ifdef HAVE_IDNA
 1124 FL bool_t
 1125 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
 1126    char *idna_utf8;
 1127    bool_t lofi, rv;
 1128    NYD_ENTER;
 1129 
 1130    if(ilen == UIZ_MAX)
 1131       ilen = strlen(ibuf);
 1132 
 1133    lofi = FAL0;
 1134 
 1135    if((rv = (ilen == 0)))
 1136       goto jleave;
 1137    if(ibuf[ilen] != '\0'){
 1138       lofi = TRU1;
 1139       idna_utf8 = n_lofi_alloc(ilen +1);
 1140       memcpy(idna_utf8, ibuf, ilen);
 1141       idna_utf8[ilen] = '\0';
 1142       ibuf = idna_utf8;
 1143    }
 1144    ilen = 0;
 1145 
 1146 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
 1147    if(n_psonce & n_PSO_UNICODE)
 1148 # endif
 1149       idna_utf8 = n_UNCONST(ibuf);
 1150 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
 1151    else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
 1152          ok_vlook(ttycharset), ibuf)) == NULL)
 1153       goto jleave;
 1154 # endif
 1155 
 1156 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
 1157    /* C99 */{
 1158       char *idna_ascii;
 1159       int f, rc;
 1160 
 1161       f = IDN2_NONTRANSITIONAL;
 1162 jidn2_redo:
 1163       if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
 1164          out = n_string_assign_cp(out, idna_ascii);
 1165          idn2_free(idna_ascii);
 1166          rv = TRU1;
 1167          ilen = out->s_len;
 1168       }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
 1169          f = IDN2_TRANSITIONAL;
 1170          goto jidn2_redo;
 1171       }
 1172    }
 1173 
 1174 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
 1175    /* C99 */{
 1176       char *idna_ascii;
 1177 
 1178       if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
 1179          out = n_string_assign_cp(out, idna_ascii);
 1180          idn_free(idna_ascii);
 1181          rv = TRU1;
 1182          ilen = out->s_len;
 1183       }
 1184    }
 1185 
 1186 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
 1187    ilen = strlen(idna_utf8);
 1188 jredo:
 1189    switch(idn_encodename(
 1190       /* LOCALCONV changed meaning in v2 and is no longer available for
 1191        * encoding.  This makes sense, bu */
 1192          (
 1193 #  ifdef IDN_UNICODECONV /* v2 */
 1194          IDN_ENCODE_APP & ~IDN_UNICODECONV
 1195 #  else
 1196          IDN_DELIMMAP | IDN_LOCALMAP | IDN_NAMEPREP | IDN_IDNCONV |
 1197          IDN_LENCHECK | IDN_ASCCHECK
 1198 #  endif
 1199          ), idna_utf8,
 1200          n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
 1201    case idn_buffer_overflow:
 1202       ilen += HOST_NAME_MAX +1;
 1203       goto jredo;
 1204    case idn_success:
 1205       rv = TRU1;
 1206       ilen = strlen(out->s_dat);
 1207       break;
 1208    default:
 1209       ilen = 0;
 1210       break;
 1211    }
 1212 
 1213 # else
 1214 #  error Unknown HAVE_IDNA
 1215 # endif
 1216 jleave:
 1217    if(lofi)
 1218       n_lofi_free(n_UNCONST(ibuf));
 1219    out = n_string_trunc(out, ilen);
 1220    NYD_LEAVE;
 1221    return rv;
 1222 }
 1223 #endif /* HAVE_IDNA */
 1224 
 1225 FL char *
 1226 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
 1227    struct str b64;
 1228    char *indat, *cp, *oudat;
 1229    size_t i, inlen, oulen;
 1230    NYD_ENTER;
 1231 
 1232    if(!(n_psonce & n_PSO_RANDOM_INIT)){
 1233       n_psonce |= n_PSO_RANDOM_INIT;
 1234 
 1235       if(n_poption & n_PO_D_V){
 1236          char const *prngn;
 1237 
 1238 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
 1239          prngn = "arc4random";
 1240 #elif HAVE_RANDOM == n_RANDOM_IMPL_TLS
 1241          prngn = "*TLS RAND_*";
 1242 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
 1243          prngn = "getrandom(2/3) + builtin ARC4";
 1244 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
 1245          prngn = "/dev/urandom + builtin ARC4";
 1246 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
 1247          prngn = "builtin ARC4";
 1248 #else
 1249 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
 1250 #endif
 1251          n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn);
 1252       }
 1253 
 1254 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
 1255       a_aux_rand_init();
 1256 #endif
 1257    }
 1258 
 1259    /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
 1260     * with PAD stripped is still longer than what the user requests, easy way.
 1261     * The relation of base64 is fixed 3 in = 4 out, and we do not want to
 1262     * include the base64 PAD characters in our random string: give some pad */
 1263    i = len;
 1264    if((inlen = i % 3) != 0)
 1265       i += 3 - inlen;
 1266 jinc1:
 1267    inlen = i >> 2;
 1268    oulen = inlen << 2;
 1269    if(oulen < len){
 1270       i += 3;
 1271       goto jinc1;
 1272    }
 1273    inlen = inlen + (inlen << 1);
 1274 
 1275    indat = n_lofi_alloc(inlen +1);
 1276 
 1277    if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
 1278 #if HAVE_RANDOM == n_RANDOM_IMPL_TLS
 1279       n_tls_rand_bytes(indat, inlen);
 1280 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
 1281       for(i = inlen; i-- > 0;)
 1282          indat[i] = (char)a_aux_rand_get8();
 1283 #else
 1284       for(cp = indat, i = inlen; i > 0;){
 1285          union {ui32_t i4; char c[4];} r;
 1286          size_t j;
 1287 
 1288          r.i4 = (ui32_t)arc4random();
 1289          switch((j = i & 3)){
 1290          case 0:  cp[3] = r.c[3]; j = 4; /* FALLTHRU */
 1291          case 3:  cp[2] = r.c[2]; /* FALLTHRU */
 1292          case 2:  cp[1] = r.c[1]; /* FALLTHRU */
 1293          default: cp[0] = r.c[0]; break;
 1294          }
 1295          cp += j;
 1296          i -= j;
 1297       }
 1298 #endif
 1299    }else{
 1300       for(cp = indat, i = inlen; i > 0;){
 1301          union {ui32_t i4; char c[4];} r;
 1302          size_t j;
 1303 
 1304          r.i4 = ++*reprocnt_or_null;
 1305          if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
 1306             char x;
 1307 
 1308             x = r.c[0];
 1309             r.c[0] = r.c[3];
 1310             r.c[3] = x;
 1311             x = r.c[1];
 1312             r.c[1] = r.c[2];
 1313             r.c[2] = x;
 1314          }
 1315          switch((j = i & 3)){
 1316          case 0:  cp[3] = r.c[3]; j = 4; /* FALLTHRU */
 1317          case 3:  cp[2] = r.c[2]; /* FALLTHRU */
 1318          case 2:  cp[1] = r.c[1]; /* FALLTHRU */
 1319          default: cp[0] = r.c[0]; break;
 1320          }
 1321          cp += j;
 1322          i -= j;
 1323       }
 1324    }
 1325 
 1326    oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
 1327    b64.s = oudat;
 1328    b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
 1329    assert(b64.l >= len);
 1330    memcpy(dat, b64.s, len);
 1331    dat[len] = '\0';
 1332    if(oudat != dat)
 1333       n_lofi_free(oudat);
 1334 
 1335    n_lofi_free(indat);
 1336 
 1337    NYD_LEAVE;
 1338    return dat;
 1339 }
 1340 
 1341 FL char *
 1342 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
 1343    char *dat;
 1344    NYD_ENTER;
 1345 
 1346    dat = n_autorec_alloc(len +1);
 1347    dat = n_random_create_buf(dat, len, reprocnt_or_null);
 1348    NYD_LEAVE;
 1349    return dat;
 1350 }
 1351 
 1352 FL bool_t
 1353 n_boolify(char const *inbuf, uiz_t inlen, bool_t emptyrv){
 1354    bool_t rv;
 1355    NYD2_ENTER;
 1356    assert(inlen == 0 || inbuf != NULL);
 1357 
 1358    if(inlen == UIZ_MAX)
 1359       inlen = strlen(inbuf);
 1360 
 1361    if(inlen == 0)
 1362       rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
 1363    else{
 1364       if((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
 1365             !ascncasecmp(inbuf, "true", inlen) ||
 1366             !ascncasecmp(inbuf, "yes", inlen) ||
 1367             !ascncasecmp(inbuf, "on", inlen))
 1368          rv = TRU1;
 1369       else if((inlen == 1 &&
 1370                (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
 1371             !ascncasecmp(inbuf, "false", inlen) ||
 1372             !ascncasecmp(inbuf, "no", inlen) ||
 1373             !ascncasecmp(inbuf, "off", inlen))
 1374          rv = FAL0;
 1375       else{
 1376          ui64_t ib;
 1377 
 1378          if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
 1379                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1380                ) != n_IDEC_STATE_CONSUMED)
 1381             rv = TRUM1;
 1382          else
 1383             rv = (ib != 0);
 1384       }
 1385    }
 1386    NYD2_LEAVE;
 1387    return rv;
 1388 }
 1389 
 1390 FL bool_t
 1391 n_quadify(char const *inbuf, uiz_t inlen, char const *prompt, bool_t emptyrv){
 1392    bool_t rv;
 1393    NYD2_ENTER;
 1394    assert(inlen == 0 || inbuf != NULL);
 1395 
 1396    if(inlen == UIZ_MAX)
 1397       inlen = strlen(inbuf);
 1398 
 1399    if(inlen == 0)
 1400       rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
 1401    else if((rv = n_boolify(inbuf, inlen, emptyrv)) < FAL0 &&
 1402          !ascncasecmp(inbuf, "ask-", 4) &&
 1403          (rv = n_boolify(&inbuf[4], inlen - 4, emptyrv)) >= FAL0 &&
 1404          (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
 1405       rv = getapproval(prompt, rv);
 1406    NYD2_LEAVE;
 1407    return rv;
 1408 }
 1409 
 1410 FL bool_t
 1411 n_is_all_or_aster(char const *name){
 1412    bool_t rv;
 1413    NYD2_ENTER;
 1414 
 1415    rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
 1416    NYD2_LEAVE;
 1417    return rv;
 1418 }
 1419 
 1420 FL struct n_timespec const *
 1421 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
 1422    static struct n_timespec ts_now;
 1423    NYD2_ENTER;
 1424 
 1425    if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
 1426       /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
 1427       (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
 1428       ts_now.ts_nsec = 0;
 1429    }else if(force_update || ts_now.ts_sec == 0){
 1430 #ifdef HAVE_CLOCK_GETTIME
 1431       struct timespec ts;
 1432 
 1433       clock_gettime(CLOCK_REALTIME, &ts);
 1434       ts_now.ts_sec = (si64_t)ts.tv_sec;
 1435       ts_now.ts_nsec = (siz_t)ts.tv_nsec;
 1436 #elif defined HAVE_GETTIMEOFDAY
 1437       struct timeval tv;
 1438 
 1439       gettimeofday(&tv, NULL);
 1440       ts_now.ts_sec = (si64_t)tv.tv_sec;
 1441       ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
 1442 #else
 1443       ts_now.ts_sec = (si64_t)time(NULL);
 1444       ts_now.ts_nsec = 0;
 1445 #endif
 1446    }
 1447 
 1448    /* Just in case.. */
 1449    if(n_UNLIKELY(ts_now.ts_sec < 0))
 1450       ts_now.ts_sec = 0;
 1451    NYD2_LEAVE;
 1452    return &ts_now;
 1453 }
 1454 
 1455 FL void
 1456 time_current_update(struct time_current *tc, bool_t full_update){
 1457    NYD_ENTER;
 1458    tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
 1459 
 1460    if(full_update){
 1461       char *cp;
 1462       struct tm *tmp;
 1463       time_t t;
 1464 
 1465       t = tc->tc_time;
 1466 jredo:
 1467       if((tmp = gmtime(&t)) == NULL){
 1468          t = 0;
 1469          goto jredo;
 1470       }
 1471       memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
 1472       if((tmp = localtime(&t)) == NULL){
 1473          t = 0;
 1474          goto jredo;
 1475       }
 1476       memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
 1477       cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
 1478       *cp++ = '\n';
 1479       *cp = '\0';
 1480       assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
 1481    }
 1482    NYD_LEAVE;
 1483 }
 1484 
 1485 FL char *
 1486 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
 1487    /* Problem is that secsepoch may be invalid for representation of ctime(3),
 1488     * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
 1489     *    ISO C requires us to use the above format string,
 1490     *    even if it will not fit in the buffer. Thus asctime_r
 1491     *    is _supposed_ to crash if the fields in tm are too large.
 1492     *    We follow this behavior and crash "gracefully" to warn
 1493     *    application developers that they may not be so lucky
 1494     *    on other implementations (e.g. stack smashing..).
 1495     * So we need to do it on our own or the libc may kill us */
 1496    static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
 1497 
 1498    si32_t y, md, th, tm, ts;
 1499    char const *wdn, *mn;
 1500    struct tm const *tmp;
 1501    NYD_ENTER;
 1502 
 1503    if((tmp = localtime_or_nil) == NULL){
 1504       time_t t;
 1505 
 1506       t = (time_t)secsepoch;
 1507 jredo:
 1508       if((tmp = localtime(&t)) == NULL){
 1509          /* TODO error log */
 1510          t = 0;
 1511          goto jredo;
 1512       }
 1513    }
 1514 
 1515    if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
 1516       y = 1970;
 1517       wdn = n_weekday_names[4];
 1518       mn = n_month_names[0];
 1519       md = 1;
 1520       th = tm = ts = 0;
 1521    }else{
 1522       y += 1900;
 1523       wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
 1524             ? n_weekday_names[tmp->tm_wday] : n_qm;
 1525       mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
 1526             ? n_month_names[tmp->tm_mon] : n_qm;
 1527 
 1528       if((md = tmp->tm_mday) < 1 || md > 31)
 1529          md = 1;
 1530 
 1531       if((th = tmp->tm_hour) < 0 || th > 23)
 1532          th = 0;
 1533       if((tm = tmp->tm_min) < 0 || tm > 59)
 1534          tm = 0;
 1535       if((ts = tmp->tm_sec) < 0 || ts > 60)
 1536          ts = 0;
 1537    }
 1538 
 1539    (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
 1540          wdn, mn, md, th, tm, ts, y);
 1541    NYD_LEAVE;
 1542    return buf;
 1543 }
 1544 
 1545 FL uiz_t
 1546 n_msleep(uiz_t millis, bool_t ignint){
 1547    uiz_t rv;
 1548    NYD2_ENTER;
 1549 
 1550 #ifdef HAVE_NANOSLEEP
 1551    /* C99 */{
 1552       struct timespec ts, trem;
 1553       int i;
 1554 
 1555       ts.tv_sec = millis / 1000;
 1556       ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
 1557 
 1558       while((i = nanosleep(&ts, &trem)) != 0 && ignint)
 1559          ts = trem;
 1560       rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
 1561    }
 1562 
 1563 #elif defined HAVE_SLEEP
 1564    if((millis /= 1000) == 0)
 1565       millis = 1;
 1566    while((rv = sleep((unsigned int)millis)) != 0 && ignint)
 1567       millis = rv;
 1568 #else
 1569 # error Configuration should have detected a function for sleeping.
 1570 #endif
 1571 
 1572    NYD2_LEAVE;
 1573    return rv;
 1574 }
 1575 
 1576 FL void
 1577 n_err(char const *format, ...){
 1578    va_list ap;
 1579    NYD2_ENTER;
 1580 
 1581    va_start(ap, format);
 1582 #ifdef HAVE_ERRORS
 1583    if(n_psonce & n_PSO_INTERACTIVE)
 1584       n_verr(format, ap);
 1585    else
 1586 #endif
 1587    {
 1588       size_t len;
 1589       bool_t doname;
 1590 
 1591       doname = FAL0;
 1592 
 1593       while(*format == '\n'){
 1594          doname = TRU1;
 1595          putc('\n', n_stderr);
 1596          ++format;
 1597       }
 1598 
 1599       if(doname)
 1600          a_aux_err_linelen = 0;
 1601 
 1602       if((len = strlen(format)) > 0){
 1603          if(doname || a_aux_err_linelen == 0){
 1604             char const *cp;
 1605 
 1606             if(*(cp = ok_vlook(log_prefix)) != '\0')
 1607                fputs(cp, n_stderr);
 1608          }
 1609          vfprintf(n_stderr, format, ap);
 1610 
 1611          /* C99 */{
 1612             size_t i = len;
 1613             do{
 1614                if(format[--len] == '\n'){
 1615                   a_aux_err_linelen = (i -= ++len);
 1616                   break;
 1617                }
 1618                ++a_aux_err_linelen;
 1619             }while(len > 0);
 1620          }
 1621       }
 1622 
 1623       fflush(n_stderr);
 1624    }
 1625    va_end(ap);
 1626    NYD2_LEAVE;
 1627 }
 1628 
 1629 FL void
 1630 n_verr(char const *format, va_list ap){
 1631 #ifdef HAVE_ERRORS
 1632    struct a_aux_err_node *enp;
 1633 #endif
 1634    bool_t doname;
 1635    size_t len;
 1636    NYD2_ENTER;
 1637 
 1638    doname = FAL0;
 1639 
 1640    while(*format == '\n'){
 1641       putc('\n', n_stderr);
 1642       doname = TRU1;
 1643       ++format;
 1644    }
 1645 
 1646    if(doname){
 1647       a_aux_err_linelen = 0;
 1648 #ifdef HAVE_ERRORS
 1649       if(n_psonce & n_PSO_INTERACTIVE){
 1650          if((enp = a_aux_err_tail) != NULL &&
 1651                (enp->ae_str.s_len > 0 &&
 1652                 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
 1653             n_string_push_c(&enp->ae_str, '\n');
 1654       }
 1655 #endif
 1656    }
 1657 
 1658    if((len = strlen(format)) == 0)
 1659       goto jleave;
 1660 #ifdef HAVE_ERRORS
 1661    n_pstate |= n_PS_ERRORS_PROMPT;
 1662 #endif
 1663 
 1664    if(doname || a_aux_err_linelen == 0){
 1665       char const *cp;
 1666 
 1667       if(*(cp = ok_vlook(log_prefix)) != '\0')
 1668          fputs(cp, n_stderr);
 1669    }
 1670 
 1671    /* C99 */{
 1672       size_t i = len;
 1673       do{
 1674          if(format[--len] == '\n'){
 1675             a_aux_err_linelen = (i -= ++len);
 1676             break;
 1677          }
 1678          ++a_aux_err_linelen;
 1679       }while(len > 0);
 1680    }
 1681 
 1682 #ifdef HAVE_ERRORS
 1683    if(!(n_psonce & n_PSO_INTERACTIVE))
 1684 #endif
 1685       vfprintf(n_stderr, format, ap);
 1686 #ifdef HAVE_ERRORS
 1687    else{
 1688       int imax, i;
 1689       n_LCTAV(ERRORS_MAX > 3);
 1690 
 1691       /* Link it into the `errors' message ring */
 1692       if((enp = a_aux_err_tail) == NULL){
 1693 jcreat:
 1694          enp = n_alloc(sizeof *enp);
 1695          enp->ae_next = NULL;
 1696          n_string_creat(&enp->ae_str);
 1697          if(a_aux_err_tail != NULL)
 1698             a_aux_err_tail->ae_next = enp;
 1699          else
 1700             a_aux_err_head = enp;
 1701          a_aux_err_tail = enp;
 1702          ++a_aux_err_cnt;
 1703       }else if(doname ||
 1704             (enp->ae_str.s_len > 0 &&
 1705              enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
 1706          if(a_aux_err_cnt < ERRORS_MAX)
 1707             goto jcreat;
 1708 
 1709          a_aux_err_head = (enp = a_aux_err_head)->ae_next;
 1710          a_aux_err_tail->ae_next = enp;
 1711          a_aux_err_tail = enp;
 1712          enp->ae_next = NULL;
 1713          n_string_trunc(&enp->ae_str, 0);
 1714       }
 1715 
 1716 # ifdef HAVE_N_VA_COPY
 1717       imax = 64;
 1718 # else
 1719       imax = n_MIN(LINESIZE, 1024);
 1720 # endif
 1721       for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
 1722 # ifdef HAVE_N_VA_COPY
 1723          va_list vac;
 1724 
 1725          n_va_copy(vac, ap);
 1726 # else
 1727 #  define vac ap
 1728 # endif
 1729 
 1730          n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
 1731          i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
 1732 # ifdef HAVE_N_VA_COPY
 1733          va_end(vac);
 1734 # else
 1735 #  undef vac
 1736 # endif
 1737          if(i <= 0)
 1738             goto jleave;
 1739          if(UICMP(z, i, >=, imax)){
 1740 # ifdef HAVE_N_VA_COPY
 1741             /* XXX Check overflow for upcoming LEN+++i! */
 1742             n_string_trunc(&enp->ae_str, len);
 1743             continue;
 1744 # else
 1745             i = (int)strlen(&enp->ae_str.s_dat[len]);
 1746 # endif
 1747          }
 1748          break;
 1749       }
 1750       n_string_trunc(&enp->ae_str, len + (size_t)i);
 1751 
 1752       fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
 1753    }
 1754 #endif /* HAVE_ERRORS */
 1755 
 1756 jleave:
 1757    fflush(n_stderr);
 1758    NYD2_LEAVE;
 1759 }
 1760 
 1761 FL void
 1762 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
 1763    va_list ap;
 1764    NYD_X;
 1765 
 1766    va_start(ap, format);
 1767    vfprintf(n_stderr, format, ap);
 1768    va_end(ap);
 1769    fflush(n_stderr);
 1770 }
 1771 
 1772 FL void
 1773 n_perr(char const *msg, int errval){
 1774    int e;
 1775    char const *fmt;
 1776    NYD2_ENTER;
 1777 
 1778    if(msg == NULL){
 1779       fmt = "%s%s\n";
 1780       msg = n_empty;
 1781    }else
 1782       fmt = "%s: %s\n";
 1783 
 1784    e = (errval == 0) ? n_err_no : errval;
 1785    n_err(fmt, msg, n_err_to_doc(e));
 1786    if(errval == 0)
 1787       n_err_no = e;
 1788    NYD2_LEAVE;
 1789 }
 1790 
 1791 FL void
 1792 n_alert(char const *format, ...){
 1793    va_list ap;
 1794    NYD2_ENTER;
 1795 
 1796    n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
 1797 
 1798    va_start(ap, format);
 1799    n_verr(format, ap);
 1800    va_end(ap);
 1801 
 1802    n_err("\n");
 1803    NYD2_LEAVE;
 1804 }
 1805 
 1806 FL void
 1807 n_panic(char const *format, ...){
 1808    va_list ap;
 1809    NYD2_ENTER;
 1810 
 1811    if(a_aux_err_linelen > 0){
 1812       putc('\n', n_stderr);
 1813       a_aux_err_linelen = 0;
 1814    }
 1815    fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
 1816 
 1817    va_start(ap, format);
 1818    vfprintf(n_stderr, format, ap);
 1819    va_end(ap);
 1820 
 1821    putc('\n', n_stderr);
 1822    fflush(n_stderr);
 1823    NYD2_LEAVE;
 1824    abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
 1825 }
 1826 
 1827 #ifdef HAVE_ERRORS
 1828 FL int
 1829 c_errors(void *v){
 1830    char **argv = v;
 1831    struct a_aux_err_node *enp;
 1832    NYD_ENTER;
 1833 
 1834    if(*argv == NULL)
 1835       goto jlist;
 1836    if(argv[1] != NULL)
 1837       goto jerr;
 1838    if(!asccasecmp(*argv, "show"))
 1839       goto jlist;
 1840    if(!asccasecmp(*argv, "clear"))
 1841       goto jclear;
 1842 jerr:
 1843    fprintf(n_stderr,
 1844       _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
 1845    v = NULL;
 1846 jleave:
 1847    NYD_LEAVE;
 1848    return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
 1849 
 1850 jlist:{
 1851       FILE *fp;
 1852       size_t i;
 1853 
 1854       if(a_aux_err_head == NULL){
 1855          fprintf(n_stderr, _("The error ring is empty\n"));
 1856          goto jleave;
 1857       }
 1858 
 1859       if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
 1860             NULL){
 1861          fprintf(n_stderr, _("tmpfile"));
 1862          v = NULL;
 1863          goto jleave;
 1864       }
 1865 
 1866       for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
 1867          fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
 1868       /* We don't know whether last string ended with NL; be simple XXX */
 1869       putc('\n', fp);
 1870 
 1871       page_or_print(fp, 0);
 1872       Fclose(fp);
 1873    }
 1874    /* FALLTHRU */
 1875 
 1876 jclear:
 1877    a_aux_err_tail = NULL;
 1878    a_aux_err_cnt = a_aux_err_cnt_noted = 0;
 1879    a_aux_err_linelen = 0;
 1880    while((enp = a_aux_err_head) != NULL){
 1881       a_aux_err_head = enp->ae_next;
 1882       n_string_gut(&enp->ae_str);
 1883       n_free(enp);
 1884    }
 1885    goto jleave;
 1886 }
 1887 #endif /* HAVE_ERRORS */
 1888 
 1889 FL char const *
 1890 n_err_to_doc(si32_t eno){
 1891    char const *rv;
 1892    struct a_aux_err_map const *aemp;
 1893    NYD2_ENTER;
 1894 
 1895    aemp = a_aux_err_map_from_no(eno);
 1896 #ifdef HAVE_DOCSTRINGS
 1897    rv = &a_aux_err_docs[aemp->aem_docoff];
 1898 #else
 1899    rv = &a_aux_err_names[aemp->aem_nameoff];
 1900 #endif
 1901    NYD2_LEAVE;
 1902    return rv;
 1903 }
 1904 
 1905 FL char const *
 1906 n_err_to_name(si32_t eno){
 1907    char const *rv;
 1908    struct a_aux_err_map const *aemp;
 1909    NYD2_ENTER;
 1910 
 1911    aemp = a_aux_err_map_from_no(eno);
 1912    rv = &a_aux_err_names[aemp->aem_nameoff];
 1913    NYD2_LEAVE;
 1914    return rv;
 1915 }
 1916 
 1917 FL si32_t
 1918 n_err_from_name(char const *name){
 1919    struct a_aux_err_map const *aemp;
 1920    ui32_t hash, i, j, x;
 1921    si32_t rv;
 1922    NYD2_ENTER;
 1923 
 1924    hash = n_torek_hash(name);
 1925 
 1926    for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
 1927       if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
 1928          break;
 1929 
 1930       aemp = &a_aux_err_map[x];
 1931       if(aemp->aem_hash == hash &&
 1932             !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
 1933          rv = aemp->aem_err_no;
 1934          goto jleave;
 1935       }
 1936 
 1937       if(++i == a_AUX_ERR_REV_PRIME){
 1938 #ifdef a_AUX_ERR_REV_WRAPAROUND
 1939          i = 0;
 1940 #else
 1941          break;
 1942 #endif
 1943       }
 1944    }
 1945 
 1946    /* Have not found it.  But wait, it could be that the user did, e.g.,
 1947     *    eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
 1948    if((n_idec_si32_cp(&rv, name, 0, NULL) &
 1949          (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
 1950             ) == n_IDEC_STATE_CONSUMED){
 1951       aemp = a_aux_err_map_from_no(rv);
 1952       rv = aemp->aem_err_no;
 1953       goto jleave;
 1954    }
 1955 
 1956    rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
 1957 jleave:
 1958    NYD2_LEAVE;
 1959    return rv;
 1960 }
 1961 
 1962 #ifdef HAVE_REGEX
 1963 FL char const *
 1964 n_regex_err_to_doc(const regex_t *rep, int e){
 1965    char *cp;
 1966    size_t i;
 1967    NYD2_ENTER;
 1968 
 1969    i = regerror(e, rep, NULL, 0) +1;
 1970    cp = n_autorec_alloc(i);
 1971    regerror(e, rep, cp, i);
 1972    NYD2_LEAVE;
 1973    return cp;
 1974 }
 1975 #endif
 1976 
 1977 /* s-it-mode */