(BWDIC!) No! Let $! be errno, and $? the sole exit status..
[s-mailx.git] / auxlily.c
blob06bdaf8240edf598d463326f37533d4f16855067
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions that don't fit anywhere else.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
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.
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.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #ifdef HAVE_GETRANDOM
45 # include HAVE_GETRANDOM_HEADER
46 #endif
48 #ifdef HAVE_SOCKETS
49 # ifdef HAVE_GETADDRINFO
50 # include <sys/socket.h>
51 # endif
53 # include <netdb.h>
54 #endif
56 #ifndef HAVE_POSIX_RANDOM
57 union rand_state{
58 struct rand_arc4{
59 ui8_t _dat[256];
60 ui8_t _i;
61 ui8_t _j;
62 ui8_t __pad[6];
63 } a;
64 ui8_t b8[sizeof(struct rand_arc4)];
65 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
67 #endif
69 #ifdef HAVE_ERRORS
70 struct a_aux_err_node{
71 struct a_aux_err_node *ae_next;
72 struct n_string ae_str;
74 #endif
76 struct a_aux_err_map{
77 ui32_t aem_hash; /* Hash of name */
78 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
79 ui32_t aem_docoff; /* Into a_aux_err docs[] */
80 si32_t aem_err_no; /* The OS error value for this one */
83 static ui8_t a_aux_idec_atoi[256] = {
84 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
85 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
86 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
87 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
88 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
89 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
90 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
91 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
92 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
93 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
94 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
95 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
96 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
97 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
98 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
99 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
100 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
101 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
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,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
109 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
112 #define a_X(X) ((ui64_t)-1 / (X))
113 static ui64_t const a_aux_idec_cutlimit[35] = {
114 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
115 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
116 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
117 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
118 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
120 #undef a_X
122 /* Include the constant mk-errors.sh output */
123 #include "gen-errors.h"
125 /* And these things come from config.h (config-time mk-errors.sh output) */
126 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
127 #undef a_X
128 #define a_X(N,I) {N,I},
129 n__ERR_NUMBER_TO_MAPOFF
130 #undef a_X
133 #ifndef HAVE_POSIX_RANDOM
134 static union rand_state *a_aux_rand;
135 #endif
137 /* Error ring, for `errors' */
138 #ifdef HAVE_ERRORS
139 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
140 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
141 #endif
142 static size_t a_aux_err_linelen;
144 /* Our ARC4 random generator with its completely unacademical pseudo
145 * initialization (shall /dev/urandom fail) */
146 #ifndef HAVE_POSIX_RANDOM
147 static void a_aux_rand_init(void);
148 SINLINE ui8_t a_aux_rand_get8(void);
149 # ifndef HAVE_GETRANDOM
150 static ui32_t a_aux_rand_weak(ui32_t seed);
151 # endif
152 #endif
154 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
155 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
157 #ifndef HAVE_POSIX_RANDOM
158 static void
159 a_aux_rand_init(void){
160 # ifndef HAVE_GETRANDOM
161 # ifdef HAVE_CLOCK_GETTIME
162 struct timespec ts;
163 # else
164 struct timeval ts;
165 # endif
166 union {int fd; size_t i;} u;
167 ui32_t seed, rnd;
168 # endif
169 NYD2_ENTER;
171 a_aux_rand = smalloc(sizeof *a_aux_rand);
173 # ifdef HAVE_GETRANDOM
174 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
175 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
176 "Buffer to large to be served without n_ERR_INTR error");
177 for(;;){
178 ssize_t gr;
180 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
181 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
182 a_aux_rand->a._dat[84]];
183 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
184 a_aux_rand->a._dat[42]];
185 /* ..but be on the safe side */
186 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
187 break;
188 n_msleep(250, FAL0);
191 # else
192 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
193 bool_t ok;
195 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
196 sizeof(a_aux_rand->a._dat)));
197 close(u.fd);
199 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
200 a_aux_rand->a._dat[84]];
201 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
202 a_aux_rand->a._dat[42]];
203 if(ok)
204 goto jleave;
207 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
208 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
209 ui32_t t, k;
211 # ifdef HAVE_CLOCK_GETTIME
212 clock_gettime(CLOCK_REALTIME, &ts);
213 t = (ui32_t)ts.tv_nsec;
214 # else
215 gettimeofday(&ts, NULL);
216 t = (ui32_t)ts.tv_usec;
217 # endif
218 if(rnd & 1)
219 t = (t >> 16) | (t << 16);
220 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
221 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
222 if(rnd == 7 || rnd == 17)
223 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
224 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
225 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
226 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
227 if((rnd & 3) == 3)
228 seed ^= n_prime_next(seed);
232 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
233 a_aux_rand_get8();
234 jleave:
235 # endif /* !HAVE_GETRANDOM */
236 NYD2_LEAVE;
239 SINLINE ui8_t
240 a_aux_rand_get8(void){
241 ui8_t si, sj;
243 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
244 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
245 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
246 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
247 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
250 # ifndef HAVE_GETRANDOM
251 static ui32_t
252 a_aux_rand_weak(ui32_t seed){
253 /* From "Random number generators: good ones are hard to find",
254 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
255 * October 1988, p. 1195.
256 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
257 ui32_t hi;
259 if(seed == 0)
260 seed = 123459876;
261 hi = seed / 127773;
262 seed %= 127773;
263 seed = (seed * 16807) - (hi * 2836);
264 if((si32_t)seed < 0)
265 seed += SI32_MAX;
266 return seed;
268 # endif /* HAVE_GETRANDOM */
269 #endif /* !HAVE_POSIX_RANDOM */
271 static struct a_aux_err_map const *
272 a_aux_err_map_from_no(si32_t eno){
273 si32_t ecmp;
274 size_t asz;
275 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
276 struct a_aux_err_map const *aemp;
277 NYD2_ENTER;
279 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
281 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
282 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
283 asz != 0; asz >>= 1){
284 tmp = &adat[asz >> 1];
285 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
286 aemp = &a_aux_err_map[(*tmp)[1]];
287 break;
289 if(ecmp > 0){
290 adat = &tmp[1];
291 --asz;
295 NYD2_LEAVE;
296 return aemp;
299 FL size_t
300 n_screensize(void){
301 char const *cp;
302 uiz_t rv;
303 NYD2_ENTER;
305 if((cp = ok_vlook(screen)) != NULL){
306 n_idec_uiz_cp(&rv, cp, 0, NULL);
307 if(rv == 0)
308 rv = n_scrnheight;
309 }else
310 rv = n_scrnheight;
312 if(rv > 2)
313 rv -= 2;
314 NYD2_LEAVE;
315 return rv;
318 FL char const *
319 n_pager_get(char const **env_addon){
320 char const *rv;
321 NYD_ENTER;
323 rv = ok_vlook(PAGER);
325 if(env_addon != NULL){
326 *env_addon = NULL;
327 /* Update the manual upon any changes:
328 * *colour-pager*, $PAGER */
329 if(strstr(rv, "less") != NULL){
330 if(getenv("LESS") == NULL)
331 *env_addon =
332 #ifdef HAVE_TERMCAP
333 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
334 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
335 #endif
336 "LESS=FRXi";
337 }else if(strstr(rv, "lv") != NULL){
338 if(getenv("LV") == NULL)
339 *env_addon = "LV=-c";
342 NYD_LEAVE;
343 return rv;
346 FL void
347 page_or_print(FILE *fp, size_t lines)
349 int c;
350 char const *cp;
351 NYD_ENTER;
353 fflush_rewind(fp);
355 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
356 size_t rows;
358 if(*cp == '\0')
359 rows = (size_t)n_scrnheight;
360 else
361 n_idec_uiz_cp(&rows, cp, 0, NULL);
363 if (rows > 0 && lines == 0) {
364 while ((c = getc(fp)) != EOF)
365 if (c == '\n' && ++lines >= rows)
366 break;
367 really_rewind(fp);
370 if (lines >= rows) {
371 char const *env_add[2], *pager;
373 pager = n_pager_get(&env_add[0]);
374 env_add[1] = NULL;
375 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
376 env_add);
377 goto jleave;
381 while ((c = getc(fp)) != EOF)
382 putc(c, n_stdout);
383 jleave:
384 NYD_LEAVE;
387 FL enum protocol
388 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
390 struct stat st;
391 char const *cp;
392 char *np;
393 size_t sz;
394 enum protocol rv = PROTO_UNKNOWN;
395 NYD_ENTER;
397 temporary_protocol_ext = NULL;
399 if (name[0] == '%' && name[1] == ':')
400 name += 2;
401 for (cp = name; *cp && *cp != ':'; cp++)
402 if (!alnumchar(*cp))
403 goto jfile;
405 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
406 if (!strncmp(name, "pop3://", 7)) {
407 #ifdef HAVE_POP3
408 rv = PROTO_POP3;
409 #else
410 n_err(_("No POP3 support compiled in\n"));
411 #endif
412 } else if (!strncmp(name, "pop3s://", 8)) {
413 #if defined HAVE_POP3 && defined HAVE_SSL
414 rv = PROTO_POP3;
415 #else
416 # ifndef HAVE_POP3
417 n_err(_("No POP3 support compiled in\n"));
418 # endif
419 # ifndef HAVE_SSL
420 n_err(_("No SSL support compiled in\n"));
421 # endif
422 #endif
424 goto jleave;
427 /* TODO This is the de facto maildir code and thus belongs into there!
428 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
429 * TODO or (more likely) in addition to *newfolders*) */
430 jfile:
431 rv = PROTO_FILE;
432 np = ac_alloc((sz = strlen(name)) + 4 +1);
433 memcpy(np, name, sz + 1);
434 if (!stat(name, &st)) {
435 if (S_ISDIR(st.st_mode) &&
436 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
437 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
438 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
439 rv = PROTO_MAILDIR;
440 } else {
441 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
442 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
443 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
444 temporary_protocol_ext = cp;
445 else if ((cp = ok_vlook(newfolders)) != NULL &&
446 !asccasecmp(cp, "maildir"))
447 rv = PROTO_MAILDIR;
449 ac_free(np);
450 jleave:
451 NYD_LEAVE;
452 return rv;
455 FL char *
456 n_c_to_hex_base16(char store[3], char c){
457 static char const itoa16[] = "0123456789ABCDEF";
458 NYD2_ENTER;
460 store[2] = '\0';
461 store[1] = itoa16[(ui8_t)c & 0x0F];
462 c = ((ui8_t)c >> 4) & 0x0F;
463 store[0] = itoa16[(ui8_t)c];
464 NYD2_LEAVE;
465 return store;
468 FL si32_t
469 n_c_from_hex_base16(char const hex[2]){
470 static ui8_t const atoi16[] = {
471 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
472 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
473 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
474 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
475 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
476 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
477 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
479 ui8_t i1, i2;
480 si32_t rv;
481 NYD2_ENTER;
483 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
484 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
485 goto jerr;
486 i1 = atoi16[i1];
487 i2 = atoi16[i2];
488 if ((i1 | i2) & 0xF0u)
489 goto jerr;
490 rv = i1;
491 rv <<= 4;
492 rv += i2;
493 jleave:
494 NYD2_LEAVE;
495 return rv;
496 jerr:
497 rv = -1;
498 goto jleave;
501 FL enum n_idec_state
502 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
503 enum n_idec_mode idm, char const **endptr_or_null){
504 /* XXX Brute simple and */
505 ui8_t currc;
506 ui64_t res, cut;
507 enum n_idec_state rv;
508 NYD_ENTER;
510 idm &= n__IDEC_MODE_MASK;
511 rv = n_IDEC_STATE_NONE | idm;
512 res = 0;
514 if(clen == UIZ_MAX){
515 if(*cbuf == '\0')
516 goto jeinval;
517 }else if(clen == 0)
518 goto jeinval;
520 /* Leading WS */
521 while(spacechar(*cbuf))
522 if(*++cbuf == '\0' || --clen == 0)
523 goto jeinval;
525 /* Check sign */
526 switch(*cbuf){
527 case '-':
528 rv |= n_IDEC_STATE_SEEN_MINUS;
529 /* FALLTHROUGH */
530 case '+':
531 if(*++cbuf == '\0' || --clen == 0)
532 goto jeinval;
533 break;
536 /* Base detection/skip */
537 if(*cbuf != '0'){
538 if(base == 0)
539 base = 10;
540 /* Character must be valid for base */
541 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
542 if(currc >= base)
543 goto jeinval;
544 }else{
545 /* 0 always valid as is, fallback base 10 */
546 if(*++cbuf == '\0' || --clen == 0)
547 goto jleave;
549 /* Base "detection" */
550 if(base == 0 || base == 2 || base == 16){
551 switch(*cbuf){
552 case 'x':
553 case 'X':
554 if((base & 2) == 0){
555 base = 0x10;
556 goto jprefix_skip;
558 break;
559 case 'b':
560 case 'B':
561 if((base & 16) == 0){
562 base = 2; /* 0b10 */
563 /* Char after prefix must be valid */
564 jprefix_skip:
565 if(*++cbuf == '\0' || --clen == 0)
566 goto jeinval;
568 /* Character must be valid for base, invalid otherwise */
569 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
570 if(currc >= base)
571 goto jeinval;
573 break;
574 default:
575 if(base == 0)
576 base = 010;
577 break;
581 /* Character must be valid for base, _EBASE otherwise */
582 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
583 if(currc >= base)
584 goto jebase;
587 for(cut = a_aux_idec_cutlimit[base - 2];;){
588 if(res >= cut){
589 if(res == cut){
590 res *= base;
591 if(res > UI64_MAX - currc)
592 goto jeover;
593 res += currc;
594 }else
595 goto jeover;
596 }else{
597 res *= base;
598 res += currc;
601 if(*++cbuf == '\0' || --clen == 0)
602 break;
604 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
605 if(currc >= base)
606 goto jebase;
609 jleave:
611 ui64_t uimask;
613 switch(rv & n__IDEC_MODE_LIMIT_MASK){
614 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
615 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
616 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
617 default: uimask = UI64_MAX; break;
619 if(rv & n_IDEC_MODE_SIGNED_TYPE)
620 uimask >>= 1;
622 if(res & ~uimask){
623 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
624 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
625 if(res > uimask + 1){
626 res = uimask << 1;
627 res &= ~uimask;
628 }else{
629 res = -res;
630 break;
632 }else
633 res = uimask;
634 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
635 rv |= n_IDEC_STATE_EOVERFLOW;
636 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
637 res = -res;
638 }while(0);
640 switch(rv & n__IDEC_MODE_LIMIT_MASK){
641 case n_IDEC_MODE_LIMIT_8BIT:
642 if(rv & n_IDEC_MODE_SIGNED_TYPE)
643 *(si8_t*)resp = (si8_t)res;
644 else
645 *(ui8_t*)resp = (ui8_t)res;
646 break;
647 case n_IDEC_MODE_LIMIT_16BIT:
648 if(rv & n_IDEC_MODE_SIGNED_TYPE)
649 *(si16_t*)resp = (si16_t)res;
650 else
651 *(ui16_t*)resp = (ui16_t)res;
652 break;
653 case n_IDEC_MODE_LIMIT_32BIT:
654 if(rv & n_IDEC_MODE_SIGNED_TYPE)
655 *(si32_t*)resp = (si32_t)res;
656 else
657 *(ui32_t*)resp = (ui32_t)res;
658 break;
659 default:
660 if(rv & n_IDEC_MODE_SIGNED_TYPE)
661 *(si64_t*)resp = (si64_t)res;
662 else
663 *(ui64_t*)resp = (ui64_t)res;
664 break;
667 if(endptr_or_null != NULL)
668 *endptr_or_null = cbuf;
669 if(*cbuf == '\0' || clen == 0)
670 rv |= n_IDEC_STATE_CONSUMED;
671 NYD_LEAVE;
672 return rv;
674 jeinval:
675 rv |= n_IDEC_STATE_EINVAL;
676 goto j_maxval;
677 jebase:
678 /* Not a base error for terminator and whitespace! */
679 if(*cbuf != '\0' && !spacechar(*cbuf))
680 rv |= n_IDEC_STATE_EBASE;
681 goto jleave;
683 jeover:
684 /* Overflow error: consume input until bad character or length out */
685 for(;;){
686 if(*++cbuf == '\0' || --clen == 0)
687 break;
688 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
689 if(currc >= base)
690 break;
693 rv |= n_IDEC_STATE_EOVERFLOW;
694 j_maxval:
695 if(rv & n_IDEC_MODE_SIGNED_TYPE)
696 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
697 : (ui64_t)SI64_MAX;
698 else
699 res = UI64_MAX;
700 rv &= ~n_IDEC_STATE_SEEN_MINUS;
701 goto jleave;
704 FL ui32_t
705 n_torek_hash(char const *name){
706 /* Chris Torek's hash.
707 * NOTE: need to change *at least* mk-okey-map.pl when changing the
708 * algorithm!! */
709 char c;
710 ui32_t h;
711 NYD2_ENTER;
713 for(h = 0; (c = *name++) != '\0';)
714 h = (h * 33) + c;
715 NYD2_LEAVE;
716 return h;
719 FL ui32_t
720 n_torek_ihashn(char const *dat, size_t len){
721 /* See n_torek_hash() */
722 char c;
723 ui32_t h;
724 NYD2_ENTER;
726 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
727 h = (h * 33) + lowerconv(c);
728 NYD2_LEAVE;
729 return h;
732 FL ui32_t
733 n_prime_next(ui32_t n){
734 static ui32_t const primes[] = {
735 5, 11, 23, 47, 97, 157, 283,
736 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
737 131071, 262139, 524287, 1048573, 2097143, 4194301,
738 8388593, 16777213, 33554393, 67108859, 134217689,
739 268435399, 536870909, 1073741789, 2147483647
741 ui32_t i, mprime;
742 NYD2_ENTER;
744 i = (n < primes[n_NELEM(primes) / 4] ? 0
745 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
746 : n_NELEM(primes) / 2));
748 do if((mprime = primes[i]) > n)
749 break;
750 while(++i < n_NELEM(primes));
752 if(i == n_NELEM(primes) && mprime < n)
753 mprime = n;
754 NYD2_LEAVE;
755 return mprime;
758 FL char const *
759 n_getdeadletter(void){
760 char const *cp_base, *cp;
761 NYD_ENTER;
763 cp_base = NULL;
764 jredo:
765 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
766 if(cp == NULL || strlen(cp) >= PATH_MAX){
767 if(cp_base == NULL){
768 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
769 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
770 ok_vclear(DEAD);
771 goto jredo;
772 }else{
773 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
774 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
777 NYD_LEAVE;
778 return cp;
781 FL char *
782 n_nodename(bool_t mayoverride){
783 static char *sys_hostname, *hostname; /* XXX free-at-exit */
785 struct utsname ut;
786 char *hn;
787 #ifdef HAVE_SOCKETS
788 # ifdef HAVE_GETADDRINFO
789 struct addrinfo hints, *res;
790 # else
791 struct hostent *hent;
792 # endif
793 #endif
794 NYD2_ENTER;
796 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
798 }else if((hn = sys_hostname) == NULL){
799 uname(&ut);
800 hn = ut.nodename;
801 #ifdef HAVE_SOCKETS
802 # ifdef HAVE_GETADDRINFO
803 memset(&hints, 0, sizeof hints);
804 hints.ai_family = AF_UNSPEC;
805 hints.ai_flags = AI_CANONNAME;
806 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
807 if(res->ai_canonname != NULL){
808 size_t l;
810 l = strlen(res->ai_canonname) +1;
811 hn = n_lofi_alloc(l);
812 memcpy(hn, res->ai_canonname, l);
814 freeaddrinfo(res);
816 # else
817 hent = gethostbyname(hn);
818 if(hent != NULL)
819 hn = hent->h_name;
820 # endif
821 #endif
822 sys_hostname = sstrdup(hn);
823 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
824 if(hn != ut.nodename)
825 n_lofi_free(hn);
826 #endif
827 hn = sys_hostname;
830 if(hostname != NULL && hostname != sys_hostname)
831 n_free(hostname);
832 hostname = sstrdup(hn);
833 NYD2_LEAVE;
834 return hostname;
837 FL char *
838 n_random_create_cp(size_t length){
839 struct str b64;
840 char *data;
841 size_t i;
842 NYD_ENTER;
844 #ifndef HAVE_POSIX_RANDOM
845 if(a_aux_rand == NULL)
846 a_aux_rand_init();
847 #endif
849 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
850 * with PAD stripped is still longer than what the user requests, easy way */
851 data = n_lofi_alloc(i = length + 3);
853 #ifndef HAVE_POSIX_RANDOM
854 while(i-- > 0)
855 data[i] = (char)a_aux_rand_get8();
856 #else
857 { char *cp;
859 for(cp = data; i > 0;){
860 union {ui32_t i4; char c[4];} r;
861 size_t j;
863 r.i4 = (ui32_t)arc4random();
864 switch((j = i & 3)){
865 case 0: cp[3] = r.c[3]; j = 4;
866 case 3: cp[2] = r.c[2];
867 case 2: cp[1] = r.c[1];
868 default: cp[0] = r.c[0]; break;
870 cp += j;
871 i -= j;
874 #endif
876 assert(length + 3 < UIZ_MAX / 4);
877 b64_encode_buf(&b64, data, length + 3,
878 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
879 ac_free(data);
881 assert(b64.l >= length);
882 b64.s[length] = '\0';
883 NYD_LEAVE;
884 return b64.s;
887 FL si8_t
888 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
890 si8_t rv;
891 NYD_ENTER;
893 assert(inlen == 0 || inbuf != NULL);
895 if (inlen == UIZ_MAX)
896 inlen = strlen(inbuf);
898 if (inlen == 0)
899 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
900 else {
901 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
902 !ascncasecmp(inbuf, "true", inlen) ||
903 !ascncasecmp(inbuf, "yes", inlen) ||
904 !ascncasecmp(inbuf, "on", inlen))
905 rv = 1;
906 else if ((inlen == 1 &&
907 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
908 !ascncasecmp(inbuf, "false", inlen) ||
909 !ascncasecmp(inbuf, "no", inlen) ||
910 !ascncasecmp(inbuf, "off", inlen))
911 rv = 0;
912 else {
913 ui64_t ib;
915 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
916 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
917 ) != n_IDEC_STATE_CONSUMED)
918 rv = -1;
919 else
920 rv = (ib != 0);
923 NYD_LEAVE;
924 return rv;
927 FL si8_t
928 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
930 si8_t rv;
931 NYD_ENTER;
933 assert(inlen == 0 || inbuf != NULL);
935 if (inlen == UIZ_MAX)
936 inlen = strlen(inbuf);
938 if (inlen == 0)
939 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
940 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
941 !ascncasecmp(inbuf, "ask-", 4) &&
942 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
943 (n_psonce & n_PSO_INTERACTIVE))
944 rv = getapproval(prompt, rv);
945 NYD_LEAVE;
946 return rv;
949 FL bool_t
950 n_is_all_or_aster(char const *name){
951 bool_t rv;
952 NYD_ENTER;
954 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
955 NYD_LEAVE;
956 return rv;
959 FL time_t
960 n_time_epoch(void)
962 #ifdef HAVE_CLOCK_GETTIME
963 struct timespec ts;
964 #elif defined HAVE_GETTIMEOFDAY
965 struct timeval ts;
966 #endif
967 time_t rv;
968 char const *cp;
969 NYD2_ENTER;
971 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){
972 ui64_t tib;
974 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib, cp, 0, NULL);
975 rv = (time_t)tib;
976 goto jleave;
979 #ifdef HAVE_CLOCK_GETTIME
980 clock_gettime(CLOCK_REALTIME, &ts);
981 rv = (time_t)ts.tv_sec;
982 #elif defined HAVE_GETTIMEOFDAY
983 gettimeofday(&ts, NULL);
984 rv = (time_t)ts.tv_sec;
985 #else
986 rv = time(NULL);
987 #endif
988 jleave:
989 NYD2_LEAVE;
990 return rv;
993 FL void
994 time_current_update(struct time_current *tc, bool_t full_update)
996 NYD_ENTER;
997 tc->tc_time = n_time_epoch();
998 if (full_update) {
999 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1000 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1001 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1003 NYD_LEAVE;
1006 FL uiz_t
1007 n_msleep(uiz_t millis, bool_t ignint){
1008 uiz_t rv;
1009 NYD2_ENTER;
1011 #ifdef HAVE_NANOSLEEP
1012 /* C99 */{
1013 struct timespec ts, trem;
1014 int i;
1016 ts.tv_sec = millis / 1000;
1017 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1019 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1020 ts = trem;
1021 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1024 #elif defined HAVE_SLEEP
1025 if((millis /= 1000) == 0)
1026 millis = 1;
1027 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1028 millis = rv;
1029 #else
1030 # error Configuration should have detected a function for sleeping.
1031 #endif
1033 NYD2_LEAVE;
1034 return rv;
1037 FL void
1038 n_err(char const *format, ...){
1039 va_list ap;
1040 NYD2_ENTER;
1042 va_start(ap, format);
1043 #ifdef HAVE_ERRORS
1044 if(n_psonce & n_PSO_INTERACTIVE)
1045 n_verr(format, ap);
1046 else
1047 #endif
1049 size_t len;
1050 bool_t doname, doflush;
1052 doflush = FAL0;
1053 while(*format == '\n'){
1054 doflush = TRU1;
1055 putc('\n', n_stderr);
1056 ++format;
1059 if((doname = doflush))
1060 a_aux_err_linelen = 0;
1062 if((len = strlen(format)) > 0){
1063 if(doname || a_aux_err_linelen == 0)
1064 fputs(VAL_UAGENT ": ", n_stderr);
1065 vfprintf(n_stderr, format, ap);
1067 /* C99 */{
1068 size_t i = len;
1070 if(format[--len] == '\n'){
1071 a_aux_err_linelen = (i -= ++len);
1072 break;
1074 ++a_aux_err_linelen;
1075 }while(len > 0);
1079 if(doflush)
1080 fflush(n_stderr);
1082 va_end(ap);
1083 NYD2_LEAVE;
1086 FL void
1087 n_verr(char const *format, va_list ap){
1088 #ifdef HAVE_ERRORS
1089 struct a_aux_err_node *enp;
1090 #endif
1091 bool_t doname;
1092 size_t len;
1093 NYD2_ENTER;
1095 doname = FAL0;
1097 while(*format == '\n'){
1098 putc('\n', n_stderr);
1099 doname = TRU1;
1100 ++format;
1103 if(doname){
1104 a_aux_err_linelen = 0;
1105 #ifdef HAVE_ERRORS
1106 if(n_psonce & n_PSO_INTERACTIVE){
1107 if((enp = a_aux_err_tail) != NULL &&
1108 (enp->ae_str.s_len > 0 &&
1109 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1110 n_string_push_c(&enp->ae_str, '\n');
1112 #endif
1115 if((len = strlen(format)) == 0)
1116 goto jleave;
1117 #ifdef HAVE_ERRORS
1118 n_pstate |= n_PS_ERRORS_PROMPT;
1119 #endif
1121 if(doname || a_aux_err_linelen == 0)
1122 fputs(VAL_UAGENT ": ", n_stderr);
1124 /* C99 */{
1125 size_t i = len;
1127 if(format[--len] == '\n'){
1128 a_aux_err_linelen = (i -= ++len);
1129 break;
1131 ++a_aux_err_linelen;
1132 }while(len > 0);
1135 #ifdef HAVE_ERRORS
1136 if(!(n_psonce & n_PSO_INTERACTIVE))
1137 #endif
1138 vfprintf(n_stderr, format, ap);
1139 #ifdef HAVE_ERRORS
1140 else{
1141 int imax, i;
1142 n_LCTAV(ERRORS_MAX > 3);
1144 /* Link it into the `errors' message ring */
1145 if((enp = a_aux_err_tail) == NULL){
1146 jcreat:
1147 enp = smalloc(sizeof *enp);
1148 enp->ae_next = NULL;
1149 n_string_creat(&enp->ae_str);
1150 if(a_aux_err_tail != NULL)
1151 a_aux_err_tail->ae_next = enp;
1152 else
1153 a_aux_err_head = enp;
1154 a_aux_err_tail = enp;
1155 ++a_aux_err_cnt;
1156 }else if(doname ||
1157 (enp->ae_str.s_len > 0 &&
1158 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1159 if(a_aux_err_cnt < ERRORS_MAX)
1160 goto jcreat;
1162 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1163 a_aux_err_tail->ae_next = enp;
1164 a_aux_err_tail = enp;
1165 enp->ae_next = NULL;
1166 n_string_trunc(&enp->ae_str, 0);
1169 # ifdef HAVE_N_VA_COPY
1170 imax = 64;
1171 # else
1172 imax = n_MIN(LINESIZE, 1024);
1173 # endif
1174 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1175 # ifdef HAVE_N_VA_COPY
1176 va_list vac;
1178 n_va_copy(vac, ap);
1179 # else
1180 # define vac ap
1181 # endif
1183 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1184 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1185 # ifdef HAVE_N_VA_COPY
1186 va_end(vac);
1187 # else
1188 # undef vac
1189 # endif
1190 if(i <= 0)
1191 goto jleave;
1192 if(UICMP(z, i, >=, imax)){
1193 # ifdef HAVE_N_VA_COPY
1194 /* XXX Check overflow for upcoming LEN+++i! */
1195 n_string_trunc(&enp->ae_str, len);
1196 continue;
1197 # else
1198 i = (int)strlen(&enp->ae_str.s_dat[len]);
1199 # endif
1201 break;
1203 n_string_trunc(&enp->ae_str, len + (size_t)i);
1205 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1207 #endif /* HAVE_ERRORS */
1209 jleave:
1210 fflush(n_stderr);
1211 NYD2_LEAVE;
1214 FL void
1215 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1216 va_list ap;
1217 NYD_X;
1219 va_start(ap, format);
1220 vfprintf(n_stderr, format, ap);
1221 va_end(ap);
1222 fflush(n_stderr);
1225 FL void
1226 n_perr(char const *msg, int errval){
1227 int e;
1228 char const *fmt;
1229 NYD2_ENTER;
1231 if(msg == NULL){
1232 fmt = "%s%s\n";
1233 msg = n_empty;
1234 }else
1235 fmt = "%s: %s\n";
1237 e = (errval == 0) ? n_err_no : errval;
1238 n_err(fmt, msg, n_err_to_doc(e));
1239 if(errval == 0)
1240 n_err_no = e;
1241 NYD2_LEAVE;
1244 FL void
1245 n_alert(char const *format, ...){
1246 va_list ap;
1247 NYD2_ENTER;
1249 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1251 va_start(ap, format);
1252 n_verr(format, ap);
1253 va_end(ap);
1255 n_err("\n");
1256 NYD2_LEAVE;
1259 FL void
1260 n_panic(char const *format, ...){
1261 va_list ap;
1262 NYD2_ENTER;
1264 if(a_aux_err_linelen > 0){
1265 putc('\n', n_stderr);
1266 a_aux_err_linelen = 0;
1268 fprintf(n_stderr, VAL_UAGENT ": Panic: ");
1270 va_start(ap, format);
1271 vfprintf(n_stderr, format, ap);
1272 va_end(ap);
1274 putc('\n', n_stderr);
1275 fflush(n_stderr);
1276 NYD2_LEAVE;
1277 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1280 #ifdef HAVE_ERRORS
1281 FL int
1282 c_errors(void *v){
1283 char **argv = v;
1284 struct a_aux_err_node *enp;
1285 NYD_ENTER;
1287 if(*argv == NULL)
1288 goto jlist;
1289 if(argv[1] != NULL)
1290 goto jerr;
1291 if(!asccasecmp(*argv, "show"))
1292 goto jlist;
1293 if(!asccasecmp(*argv, "clear"))
1294 goto jclear;
1295 jerr:
1296 fprintf(n_stderr,
1297 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1298 v = NULL;
1299 jleave:
1300 NYD_LEAVE;
1301 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1303 jlist:{
1304 FILE *fp;
1305 size_t i;
1307 if(a_aux_err_head == NULL){
1308 fprintf(n_stderr, _("The error ring is empty\n"));
1309 goto jleave;
1312 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1313 NULL){
1314 fprintf(n_stderr, _("tmpfile"));
1315 v = NULL;
1316 goto jleave;
1319 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1320 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1321 /* We don't know whether last string ended with NL; be simple XXX */
1322 putc('\n', fp);
1324 page_or_print(fp, 0);
1325 Fclose(fp);
1327 /* FALLTHRU */
1329 jclear:
1330 a_aux_err_tail = NULL;
1331 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1332 a_aux_err_linelen = 0;
1333 while((enp = a_aux_err_head) != NULL){
1334 a_aux_err_head = enp->ae_next;
1335 n_string_gut(&enp->ae_str);
1336 free(enp);
1338 goto jleave;
1340 #endif /* HAVE_ERRORS */
1342 FL char const *
1343 n_err_to_doc(si32_t eno){
1344 char const *rv;
1345 struct a_aux_err_map const *aemp;
1346 NYD2_ENTER;
1348 aemp = a_aux_err_map_from_no(eno);
1349 rv = &a_aux_err_docs[aemp->aem_docoff];
1350 NYD2_LEAVE;
1351 return rv;
1354 FL char const *
1355 n_err_to_name(si32_t eno){
1356 char const *rv;
1357 struct a_aux_err_map const *aemp;
1358 NYD2_ENTER;
1360 aemp = a_aux_err_map_from_no(eno);
1361 rv = &a_aux_err_names[aemp->aem_nameoff];
1362 NYD2_LEAVE;
1363 return rv;
1366 FL si32_t
1367 n_err_from_name(char const *name){
1368 struct a_aux_err_map const *aemp;
1369 ui32_t hash, i, j, x;
1370 si32_t rv;
1371 NYD2_ENTER;
1373 hash = n_torek_hash(name);
1375 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1376 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1377 break;
1379 aemp = &a_aux_err_map[x];
1380 if(aemp->aem_hash == hash &&
1381 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1382 rv = aemp->aem_err_no;
1383 goto jleave;
1386 if(++i == a_AUX_ERR_REV_PRIME){
1387 #ifdef a_AUX_ERR_REV_WRAPAROUND
1388 i = 0;
1389 #else
1390 break;
1391 #endif
1395 /* Have not found it. But wait, it could be that the user did, e.g.,
1396 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1397 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1398 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1399 ) == n_IDEC_STATE_CONSUMED){
1400 aemp = a_aux_err_map_from_no(rv);
1401 rv = aemp->aem_err_no;
1402 goto jleave;
1405 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1406 jleave:
1407 NYD2_LEAVE;
1408 return rv;
1411 #ifdef HAVE_REGEX
1412 FL char const *
1413 n_regex_err_to_doc(const regex_t *rep, int e){
1414 char *cp;
1415 size_t i;
1416 NYD2_ENTER;
1418 i = regerror(e, rep, NULL, 0) +1;
1419 cp = salloc(i);
1420 regerror(e, rep, cp, i);
1421 NYD2_LEAVE;
1422 return cp;
1424 #endif
1426 /* s-it-mode */