"Fossies" - the Fresh Open Source Software Archive

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