Tweak previous, it added a bad memory access
[s-mailx.git] / auxlily.c
blob7e1feb65a47ecec69f69f21b07ad28745bb2b06f
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=".zst",5), !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=".gz",4), !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 char c;
708 ui32_t h;
709 NYD2_ENTER;
711 for(h = 0; (c = *name++) != '\0';)
712 h = (h * 33) + c;
713 NYD2_LEAVE;
714 return h;
717 FL ui32_t
718 n_torek_ihashn(char const *dat, size_t len){
719 /* See n_torek_hash() */
720 char c;
721 ui32_t h;
722 NYD2_ENTER;
724 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
725 h = (h * 33) + lowerconv(c);
726 NYD2_LEAVE;
727 return h;
730 FL ui32_t
731 n_prime_next(ui32_t n){
732 static ui32_t const primes[] = {
733 5, 11, 23, 47, 97, 157, 283,
734 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
735 131071, 262139, 524287, 1048573, 2097143, 4194301,
736 8388593, 16777213, 33554393, 67108859, 134217689,
737 268435399, 536870909, 1073741789, 2147483647
739 ui32_t i, mprime;
740 NYD2_ENTER;
742 i = (n < primes[n_NELEM(primes) / 4] ? 0
743 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
744 : n_NELEM(primes) / 2));
746 do if((mprime = primes[i]) > n)
747 break;
748 while(++i < n_NELEM(primes));
750 if(i == n_NELEM(primes) && mprime < n)
751 mprime = n;
752 NYD2_LEAVE;
753 return mprime;
756 FL char const *
757 n_getdeadletter(void){
758 char const *cp_base, *cp;
759 NYD_ENTER;
761 cp_base = NULL;
762 jredo:
763 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
764 if(cp == NULL || strlen(cp) >= PATH_MAX){
765 if(cp_base == NULL){
766 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
767 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
768 ok_vclear(DEAD);
769 goto jredo;
770 }else{
771 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
772 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
775 NYD_LEAVE;
776 return cp;
779 FL char *
780 n_nodename(bool_t mayoverride){
781 static char *sys_hostname, *hostname; /* XXX free-at-exit */
783 struct utsname ut;
784 char *hn;
785 #ifdef HAVE_SOCKETS
786 # ifdef HAVE_GETADDRINFO
787 struct addrinfo hints, *res;
788 # else
789 struct hostent *hent;
790 # endif
791 #endif
792 NYD2_ENTER;
794 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
796 }else if((hn = sys_hostname) == NULL){
797 uname(&ut);
798 hn = ut.nodename;
799 #ifdef HAVE_SOCKETS
800 # ifdef HAVE_GETADDRINFO
801 memset(&hints, 0, sizeof hints);
802 hints.ai_family = AF_UNSPEC;
803 hints.ai_flags = AI_CANONNAME;
804 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
805 if(res->ai_canonname != NULL){
806 size_t l;
808 l = strlen(res->ai_canonname) +1;
809 hn = n_lofi_alloc(l);
810 memcpy(hn, res->ai_canonname, l);
812 freeaddrinfo(res);
814 # else
815 hent = gethostbyname(hn);
816 if(hent != NULL)
817 hn = hent->h_name;
818 # endif
819 #endif
820 sys_hostname = sstrdup(hn);
821 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
822 if(hn != ut.nodename)
823 n_lofi_free(hn);
824 #endif
825 hn = sys_hostname;
828 if(hostname != NULL && hostname != sys_hostname)
829 n_free(hostname);
830 hostname = sstrdup(hn);
831 NYD2_LEAVE;
832 return hostname;
835 FL char *
836 n_random_create_cp(size_t length){
837 struct str b64;
838 char *data;
839 size_t i;
840 NYD_ENTER;
842 #ifndef HAVE_POSIX_RANDOM
843 if(a_aux_rand == NULL)
844 a_aux_rand_init();
845 #endif
847 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
848 * with PAD stripped is still longer than what the user requests, easy way */
849 data = n_lofi_alloc(i = length + 3);
851 #ifndef HAVE_POSIX_RANDOM
852 while(i-- > 0)
853 data[i] = (char)a_aux_rand_get8();
854 #else
855 { char *cp;
857 for(cp = data; i > 0;){
858 union {ui32_t i4; char c[4];} r;
859 size_t j;
861 r.i4 = (ui32_t)arc4random();
862 switch((j = i & 3)){
863 case 0: cp[3] = r.c[3]; j = 4;
864 case 3: cp[2] = r.c[2];
865 case 2: cp[1] = r.c[1];
866 default: cp[0] = r.c[0]; break;
868 cp += j;
869 i -= j;
872 #endif
874 assert(length + 3 < UIZ_MAX / 4);
875 b64_encode_buf(&b64, data, length + 3,
876 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
877 ac_free(data);
879 assert(b64.l >= length);
880 b64.s[length] = '\0';
881 NYD_LEAVE;
882 return b64.s;
885 FL si8_t
886 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
888 si8_t rv;
889 NYD_ENTER;
891 assert(inlen == 0 || inbuf != NULL);
893 if (inlen == UIZ_MAX)
894 inlen = strlen(inbuf);
896 if (inlen == 0)
897 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
898 else {
899 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
900 !ascncasecmp(inbuf, "true", inlen) ||
901 !ascncasecmp(inbuf, "yes", inlen) ||
902 !ascncasecmp(inbuf, "on", inlen))
903 rv = 1;
904 else if ((inlen == 1 &&
905 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
906 !ascncasecmp(inbuf, "false", inlen) ||
907 !ascncasecmp(inbuf, "no", inlen) ||
908 !ascncasecmp(inbuf, "off", inlen))
909 rv = 0;
910 else {
911 ui64_t ib;
913 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
914 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
915 ) != n_IDEC_STATE_CONSUMED)
916 rv = -1;
917 else
918 rv = (ib != 0);
921 NYD_LEAVE;
922 return rv;
925 FL si8_t
926 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
928 si8_t rv;
929 NYD_ENTER;
931 assert(inlen == 0 || inbuf != NULL);
933 if (inlen == UIZ_MAX)
934 inlen = strlen(inbuf);
936 if (inlen == 0)
937 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
938 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
939 !ascncasecmp(inbuf, "ask-", 4) &&
940 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
941 (n_psonce & n_PSO_INTERACTIVE))
942 rv = getapproval(prompt, rv);
943 NYD_LEAVE;
944 return rv;
947 FL bool_t
948 n_is_all_or_aster(char const *name){
949 bool_t rv;
950 NYD_ENTER;
952 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
953 NYD_LEAVE;
954 return rv;
957 FL time_t
958 n_time_epoch(void)
960 #ifdef HAVE_CLOCK_GETTIME
961 struct timespec ts;
962 #elif defined HAVE_GETTIMEOFDAY
963 struct timeval ts;
964 #endif
965 time_t rv;
966 char const *cp;
967 NYD2_ENTER;
969 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){
970 ui64_t tib;
972 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib, cp, 0, NULL);
973 rv = (time_t)tib;
974 goto jleave;
977 #ifdef HAVE_CLOCK_GETTIME
978 clock_gettime(CLOCK_REALTIME, &ts);
979 rv = (time_t)ts.tv_sec;
980 #elif defined HAVE_GETTIMEOFDAY
981 gettimeofday(&ts, NULL);
982 rv = (time_t)ts.tv_sec;
983 #else
984 rv = time(NULL);
985 #endif
986 jleave:
987 NYD2_LEAVE;
988 return rv;
991 FL void
992 time_current_update(struct time_current *tc, bool_t full_update)
994 NYD_ENTER;
995 tc->tc_time = n_time_epoch();
996 if (full_update) {
997 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
998 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
999 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1001 NYD_LEAVE;
1004 FL uiz_t
1005 n_msleep(uiz_t millis, bool_t ignint){
1006 uiz_t rv;
1007 NYD2_ENTER;
1009 #ifdef HAVE_NANOSLEEP
1010 /* C99 */{
1011 struct timespec ts, trem;
1012 int i;
1014 ts.tv_sec = millis / 1000;
1015 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1017 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1018 ts = trem;
1019 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1022 #elif defined HAVE_SLEEP
1023 if((millis /= 1000) == 0)
1024 millis = 1;
1025 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1026 millis = rv;
1027 #else
1028 # error Configuration should have detected a function for sleeping.
1029 #endif
1031 NYD2_LEAVE;
1032 return rv;
1035 FL void
1036 n_err(char const *format, ...){
1037 va_list ap;
1038 NYD2_ENTER;
1040 va_start(ap, format);
1041 #ifdef HAVE_ERRORS
1042 if(n_psonce & n_PSO_INTERACTIVE)
1043 n_verr(format, ap);
1044 else
1045 #endif
1047 size_t len;
1048 bool_t doname, doflush;
1050 doflush = FAL0;
1051 while(*format == '\n'){
1052 doflush = TRU1;
1053 putc('\n', n_stderr);
1054 ++format;
1057 if((doname = doflush))
1058 a_aux_err_linelen = 0;
1060 if((len = strlen(format)) > 0){
1061 if(doname || a_aux_err_linelen == 0){
1062 char const *cp;
1064 if(*(cp = ok_vlook(log_prefix)) != '\0')
1065 fputs(cp, n_stderr);
1067 vfprintf(n_stderr, format, ap);
1069 /* C99 */{
1070 size_t i = len;
1072 if(format[--len] == '\n'){
1073 a_aux_err_linelen = (i -= ++len);
1074 break;
1076 ++a_aux_err_linelen;
1077 }while(len > 0);
1081 if(doflush)
1082 fflush(n_stderr);
1084 va_end(ap);
1085 NYD2_LEAVE;
1088 FL void
1089 n_verr(char const *format, va_list ap){
1090 #ifdef HAVE_ERRORS
1091 struct a_aux_err_node *enp;
1092 #endif
1093 bool_t doname;
1094 size_t len;
1095 NYD2_ENTER;
1097 doname = FAL0;
1099 while(*format == '\n'){
1100 putc('\n', n_stderr);
1101 doname = TRU1;
1102 ++format;
1105 if(doname){
1106 a_aux_err_linelen = 0;
1107 #ifdef HAVE_ERRORS
1108 if(n_psonce & n_PSO_INTERACTIVE){
1109 if((enp = a_aux_err_tail) != NULL &&
1110 (enp->ae_str.s_len > 0 &&
1111 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1112 n_string_push_c(&enp->ae_str, '\n');
1114 #endif
1117 if((len = strlen(format)) == 0)
1118 goto jleave;
1119 #ifdef HAVE_ERRORS
1120 n_pstate |= n_PS_ERRORS_PROMPT;
1121 #endif
1123 if(doname || a_aux_err_linelen == 0){
1124 char const *cp;
1126 if(*(cp = ok_vlook(log_prefix)) != '\0')
1127 fputs(cp, n_stderr);
1130 /* C99 */{
1131 size_t i = len;
1133 if(format[--len] == '\n'){
1134 a_aux_err_linelen = (i -= ++len);
1135 break;
1137 ++a_aux_err_linelen;
1138 }while(len > 0);
1141 #ifdef HAVE_ERRORS
1142 if(!(n_psonce & n_PSO_INTERACTIVE))
1143 #endif
1144 vfprintf(n_stderr, format, ap);
1145 #ifdef HAVE_ERRORS
1146 else{
1147 int imax, i;
1148 n_LCTAV(ERRORS_MAX > 3);
1150 /* Link it into the `errors' message ring */
1151 if((enp = a_aux_err_tail) == NULL){
1152 jcreat:
1153 enp = smalloc(sizeof *enp);
1154 enp->ae_next = NULL;
1155 n_string_creat(&enp->ae_str);
1156 if(a_aux_err_tail != NULL)
1157 a_aux_err_tail->ae_next = enp;
1158 else
1159 a_aux_err_head = enp;
1160 a_aux_err_tail = enp;
1161 ++a_aux_err_cnt;
1162 }else if(doname ||
1163 (enp->ae_str.s_len > 0 &&
1164 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1165 if(a_aux_err_cnt < ERRORS_MAX)
1166 goto jcreat;
1168 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1169 a_aux_err_tail->ae_next = enp;
1170 a_aux_err_tail = enp;
1171 enp->ae_next = NULL;
1172 n_string_trunc(&enp->ae_str, 0);
1175 # ifdef HAVE_N_VA_COPY
1176 imax = 64;
1177 # else
1178 imax = n_MIN(LINESIZE, 1024);
1179 # endif
1180 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1181 # ifdef HAVE_N_VA_COPY
1182 va_list vac;
1184 n_va_copy(vac, ap);
1185 # else
1186 # define vac ap
1187 # endif
1189 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1190 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1191 # ifdef HAVE_N_VA_COPY
1192 va_end(vac);
1193 # else
1194 # undef vac
1195 # endif
1196 if(i <= 0)
1197 goto jleave;
1198 if(UICMP(z, i, >=, imax)){
1199 # ifdef HAVE_N_VA_COPY
1200 /* XXX Check overflow for upcoming LEN+++i! */
1201 n_string_trunc(&enp->ae_str, len);
1202 continue;
1203 # else
1204 i = (int)strlen(&enp->ae_str.s_dat[len]);
1205 # endif
1207 break;
1209 n_string_trunc(&enp->ae_str, len + (size_t)i);
1211 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1213 #endif /* HAVE_ERRORS */
1215 jleave:
1216 fflush(n_stderr);
1217 NYD2_LEAVE;
1220 FL void
1221 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1222 va_list ap;
1223 NYD_X;
1225 va_start(ap, format);
1226 vfprintf(n_stderr, format, ap);
1227 va_end(ap);
1228 fflush(n_stderr);
1231 FL void
1232 n_perr(char const *msg, int errval){
1233 int e;
1234 char const *fmt;
1235 NYD2_ENTER;
1237 if(msg == NULL){
1238 fmt = "%s%s\n";
1239 msg = n_empty;
1240 }else
1241 fmt = "%s: %s\n";
1243 e = (errval == 0) ? n_err_no : errval;
1244 n_err(fmt, msg, n_err_to_doc(e));
1245 if(errval == 0)
1246 n_err_no = e;
1247 NYD2_LEAVE;
1250 FL void
1251 n_alert(char const *format, ...){
1252 va_list ap;
1253 NYD2_ENTER;
1255 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1257 va_start(ap, format);
1258 n_verr(format, ap);
1259 va_end(ap);
1261 n_err("\n");
1262 NYD2_LEAVE;
1265 FL void
1266 n_panic(char const *format, ...){
1267 va_list ap;
1268 NYD2_ENTER;
1270 if(a_aux_err_linelen > 0){
1271 putc('\n', n_stderr);
1272 a_aux_err_linelen = 0;
1274 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1276 va_start(ap, format);
1277 vfprintf(n_stderr, format, ap);
1278 va_end(ap);
1280 putc('\n', n_stderr);
1281 fflush(n_stderr);
1282 NYD2_LEAVE;
1283 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1286 #ifdef HAVE_ERRORS
1287 FL int
1288 c_errors(void *v){
1289 char **argv = v;
1290 struct a_aux_err_node *enp;
1291 NYD_ENTER;
1293 if(*argv == NULL)
1294 goto jlist;
1295 if(argv[1] != NULL)
1296 goto jerr;
1297 if(!asccasecmp(*argv, "show"))
1298 goto jlist;
1299 if(!asccasecmp(*argv, "clear"))
1300 goto jclear;
1301 jerr:
1302 fprintf(n_stderr,
1303 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1304 v = NULL;
1305 jleave:
1306 NYD_LEAVE;
1307 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1309 jlist:{
1310 FILE *fp;
1311 size_t i;
1313 if(a_aux_err_head == NULL){
1314 fprintf(n_stderr, _("The error ring is empty\n"));
1315 goto jleave;
1318 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1319 NULL){
1320 fprintf(n_stderr, _("tmpfile"));
1321 v = NULL;
1322 goto jleave;
1325 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1326 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1327 /* We don't know whether last string ended with NL; be simple XXX */
1328 putc('\n', fp);
1330 page_or_print(fp, 0);
1331 Fclose(fp);
1333 /* FALLTHRU */
1335 jclear:
1336 a_aux_err_tail = NULL;
1337 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1338 a_aux_err_linelen = 0;
1339 while((enp = a_aux_err_head) != NULL){
1340 a_aux_err_head = enp->ae_next;
1341 n_string_gut(&enp->ae_str);
1342 free(enp);
1344 goto jleave;
1346 #endif /* HAVE_ERRORS */
1348 FL char const *
1349 n_err_to_doc(si32_t eno){
1350 char const *rv;
1351 struct a_aux_err_map const *aemp;
1352 NYD2_ENTER;
1354 aemp = a_aux_err_map_from_no(eno);
1355 rv = &a_aux_err_docs[aemp->aem_docoff];
1356 NYD2_LEAVE;
1357 return rv;
1360 FL char const *
1361 n_err_to_name(si32_t eno){
1362 char const *rv;
1363 struct a_aux_err_map const *aemp;
1364 NYD2_ENTER;
1366 aemp = a_aux_err_map_from_no(eno);
1367 rv = &a_aux_err_names[aemp->aem_nameoff];
1368 NYD2_LEAVE;
1369 return rv;
1372 FL si32_t
1373 n_err_from_name(char const *name){
1374 struct a_aux_err_map const *aemp;
1375 ui32_t hash, i, j, x;
1376 si32_t rv;
1377 NYD2_ENTER;
1379 hash = n_torek_hash(name);
1381 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1382 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1383 break;
1385 aemp = &a_aux_err_map[x];
1386 if(aemp->aem_hash == hash &&
1387 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1388 rv = aemp->aem_err_no;
1389 goto jleave;
1392 if(++i == a_AUX_ERR_REV_PRIME){
1393 #ifdef a_AUX_ERR_REV_WRAPAROUND
1394 i = 0;
1395 #else
1396 break;
1397 #endif
1401 /* Have not found it. But wait, it could be that the user did, e.g.,
1402 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1403 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1404 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1405 ) == n_IDEC_STATE_CONSUMED){
1406 aemp = a_aux_err_map_from_no(rv);
1407 rv = aemp->aem_err_no;
1408 goto jleave;
1411 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1412 jleave:
1413 NYD2_LEAVE;
1414 return rv;
1417 #ifdef HAVE_REGEX
1418 FL char const *
1419 n_regex_err_to_doc(const regex_t *rep, int e){
1420 char *cp;
1421 size_t i;
1422 NYD2_ENTER;
1424 i = regerror(e, rep, NULL, 0) +1;
1425 cp = salloc(i);
1426 regerror(e, rep, cp, i);
1427 NYD2_LEAVE;
1428 return cp;
1430 #endif
1432 /* s-it-mode */