"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/auxlily.c" (16 Feb 2018, 49896 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "auxlily.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

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