Implement `xcall' stack-avoidance optimization
[s-mailx.git] / auxlily.c
blobb302a15da24d86286d71137612ca4d1299c3471a
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_errno; /* The OS errno 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 for errno, 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 ^= nextprime(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_source_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 run_command(pager, NULL, fileno(fp), COMMAND_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 torek_hash(char const *name)
707 /* Chris Torek's hash.
708 * NOTE: need to change *at least* mk-okey-map.pl when changing the
709 * algorithm!! */
710 ui32_t h = 0;
711 NYD_ENTER;
713 while (*name != '\0') {
714 h *= 33;
715 h += *name++;
717 NYD_LEAVE;
718 return h;
721 FL ui32_t
722 torek_ihashn(char const *dat, size_t len){
723 /* See torek_hash() */
724 char c;
725 ui32_t h;
726 NYD_ENTER;
728 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
729 h = (h * 33) + lowerconv(c);
730 NYD_LEAVE;
731 return h;
734 FL ui32_t
735 nextprime(ui32_t n)
737 static ui32_t const primes[] = {
738 5, 11, 23, 47, 97, 157, 283,
739 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
740 131071, 262139, 524287, 1048573, 2097143, 4194301,
741 8388593, 16777213, 33554393, 67108859, 134217689,
742 268435399, 536870909, 1073741789, 2147483647
745 ui32_t i, mprime;
746 NYD_ENTER;
748 i = (n < primes[n_NELEM(primes) / 4] ? 0
749 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
750 : n_NELEM(primes) / 2));
752 if ((mprime = primes[i]) > n)
753 break;
754 while (++i < n_NELEM(primes));
755 if (i == n_NELEM(primes) && mprime < n)
756 mprime = n;
757 NYD_LEAVE;
758 return mprime;
761 FL char const *
762 n_getdeadletter(void){
763 char const *cp_base, *cp;
764 NYD_ENTER;
766 cp_base = NULL;
767 jredo:
768 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
769 if(cp == NULL || strlen(cp) >= PATH_MAX){
770 if(cp_base == NULL){
771 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
772 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
773 ok_vclear(DEAD);
774 goto jredo;
775 }else{
776 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
777 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
780 NYD_LEAVE;
781 return cp;
784 FL char *
785 nodename(int mayoverride)
787 static char *sys_hostname, *hostname; /* XXX free-at-exit */
789 struct utsname ut;
790 char *hn;
791 #ifdef HAVE_SOCKETS
792 # ifdef HAVE_GETADDRINFO
793 struct addrinfo hints, *res;
794 # else
795 struct hostent *hent;
796 # endif
797 #endif
798 NYD_ENTER;
800 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
802 } else if ((hn = sys_hostname) == NULL) {
803 uname(&ut);
804 hn = ut.nodename;
805 #ifdef HAVE_SOCKETS
806 # ifdef HAVE_GETADDRINFO
807 memset(&hints, 0, sizeof hints);
808 hints.ai_family = AF_UNSPEC;
809 hints.ai_flags = AI_CANONNAME;
810 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
811 if (res->ai_canonname != NULL) {
812 size_t l = strlen(res->ai_canonname) +1;
814 hn = ac_alloc(l);
815 memcpy(hn, res->ai_canonname, l);
817 freeaddrinfo(res);
819 # else
820 hent = gethostbyname(hn);
821 if (hent != NULL)
822 hn = hent->h_name;
823 # endif
824 #endif
825 sys_hostname = sstrdup(hn);
826 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
827 if (hn != ut.nodename)
828 ac_free(hn);
829 #endif
830 hn = sys_hostname;
833 if (hostname != NULL && hostname != sys_hostname)
834 free(hostname);
835 hostname = sstrdup(hn);
836 NYD_LEAVE;
837 return hostname;
840 FL char *
841 getrandstring(size_t length){
842 struct str b64;
843 char *data;
844 size_t i;
845 NYD_ENTER;
847 #ifndef HAVE_POSIX_RANDOM
848 if(a_aux_rand == NULL)
849 a_aux_rand_init();
850 #endif
852 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
853 * with PAD stripped is still longer than what the user requests, easy way */
854 data = n_lofi_alloc(i = length + 3);
856 #ifndef HAVE_POSIX_RANDOM
857 while(i-- > 0)
858 data[i] = (char)a_aux_rand_get8();
859 #else
860 { char *cp;
862 for(cp = data; i > 0;){
863 union {ui32_t i4; char c[4];} r;
864 size_t j;
866 r.i4 = (ui32_t)arc4random();
867 switch((j = i & 3)){
868 case 0: cp[3] = r.c[3]; j = 4;
869 case 3: cp[2] = r.c[2];
870 case 2: cp[1] = r.c[1];
871 default: cp[0] = r.c[0]; break;
873 cp += j;
874 i -= j;
877 #endif
879 assert(length + 3 < UIZ_MAX / 4);
880 b64_encode_buf(&b64, data, length + 3,
881 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
882 ac_free(data);
884 assert(b64.l >= length);
885 b64.s[length] = '\0';
886 NYD_LEAVE;
887 return b64.s;
890 FL si8_t
891 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
893 si8_t rv;
894 NYD_ENTER;
896 assert(inlen == 0 || inbuf != NULL);
898 if (inlen == UIZ_MAX)
899 inlen = strlen(inbuf);
901 if (inlen == 0)
902 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
903 else {
904 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
905 !ascncasecmp(inbuf, "true", inlen) ||
906 !ascncasecmp(inbuf, "yes", inlen) ||
907 !ascncasecmp(inbuf, "on", inlen))
908 rv = 1;
909 else if ((inlen == 1 &&
910 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
911 !ascncasecmp(inbuf, "false", inlen) ||
912 !ascncasecmp(inbuf, "no", inlen) ||
913 !ascncasecmp(inbuf, "off", inlen))
914 rv = 0;
915 else {
916 ui64_t ib;
918 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
919 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
920 ) != n_IDEC_STATE_CONSUMED)
921 rv = -1;
922 else
923 rv = (ib != 0);
926 NYD_LEAVE;
927 return rv;
930 FL si8_t
931 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
933 si8_t rv;
934 NYD_ENTER;
936 assert(inlen == 0 || inbuf != NULL);
938 if (inlen == UIZ_MAX)
939 inlen = strlen(inbuf);
941 if (inlen == 0)
942 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
943 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
944 !ascncasecmp(inbuf, "ask-", 4) &&
945 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
946 (n_psonce & n_PSO_INTERACTIVE))
947 rv = getapproval(prompt, rv);
948 NYD_LEAVE;
949 return rv;
952 FL bool_t
953 n_is_all_or_aster(char const *name){
954 bool_t rv;
955 NYD_ENTER;
957 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
958 NYD_LEAVE;
959 return rv;
962 FL time_t
963 n_time_epoch(void)
965 #ifdef HAVE_CLOCK_GETTIME
966 struct timespec ts;
967 #elif defined HAVE_GETTIMEOFDAY
968 struct timeval ts;
969 #endif
970 time_t rv;
971 char const *cp;
972 NYD2_ENTER;
974 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){
975 ui64_t tib;
977 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib, cp, 0, NULL);
978 rv = (time_t)tib;
979 goto jleave;
982 #ifdef HAVE_CLOCK_GETTIME
983 clock_gettime(CLOCK_REALTIME, &ts);
984 rv = (time_t)ts.tv_sec;
985 #elif defined HAVE_GETTIMEOFDAY
986 gettimeofday(&ts, NULL);
987 rv = (time_t)ts.tv_sec;
988 #else
989 rv = time(NULL);
990 #endif
991 jleave:
992 NYD2_LEAVE;
993 return rv;
996 FL void
997 time_current_update(struct time_current *tc, bool_t full_update)
999 NYD_ENTER;
1000 tc->tc_time = n_time_epoch();
1001 if (full_update) {
1002 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1003 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1004 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1006 NYD_LEAVE;
1009 FL uiz_t
1010 n_msleep(uiz_t millis, bool_t ignint){
1011 uiz_t rv;
1012 NYD2_ENTER;
1014 #ifdef HAVE_NANOSLEEP
1015 /* C99 */{
1016 struct timespec ts, trem;
1017 int i;
1019 ts.tv_sec = millis / 1000;
1020 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1022 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1023 ts = trem;
1024 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1027 #elif defined HAVE_SLEEP
1028 if((millis /= 1000) == 0)
1029 millis = 1;
1030 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1031 millis = rv;
1032 #else
1033 # error Configuration should have detected a function for sleeping.
1034 #endif
1036 NYD2_LEAVE;
1037 return rv;
1040 FL void
1041 n_err(char const *format, ...){
1042 va_list ap;
1043 NYD2_ENTER;
1045 va_start(ap, format);
1046 #ifdef HAVE_ERRORS
1047 if(n_psonce & n_PSO_INTERACTIVE)
1048 n_verr(format, ap);
1049 else
1050 #endif
1052 size_t len;
1053 bool_t doname, doflush;
1055 doflush = FAL0;
1056 while(*format == '\n'){
1057 doflush = TRU1;
1058 putc('\n', n_stderr);
1059 ++format;
1062 if((doname = doflush))
1063 a_aux_err_linelen = 0;
1065 if((len = strlen(format)) > 0){
1066 if(doname || a_aux_err_linelen == 0)
1067 fputs(VAL_UAGENT ": ", n_stderr);
1068 vfprintf(n_stderr, format, ap);
1070 /* C99 */{
1071 size_t i = len;
1073 if(format[--len] == '\n'){
1074 a_aux_err_linelen = (i -= ++len);
1075 break;
1077 ++a_aux_err_linelen;
1078 }while(len > 0);
1082 if(doflush)
1083 fflush(n_stderr);
1085 va_end(ap);
1086 NYD2_LEAVE;
1089 FL void
1090 n_verr(char const *format, va_list ap){
1091 #ifdef HAVE_ERRORS
1092 struct a_aux_err_node *enp;
1093 #endif
1094 bool_t doname;
1095 size_t len;
1096 NYD2_ENTER;
1098 doname = FAL0;
1100 while(*format == '\n'){
1101 putc('\n', n_stderr);
1102 doname = TRU1;
1103 ++format;
1106 if(doname){
1107 a_aux_err_linelen = 0;
1108 #ifdef HAVE_ERRORS
1109 if(n_psonce & n_PSO_INTERACTIVE){
1110 if((enp = a_aux_err_tail) != NULL &&
1111 (enp->ae_str.s_len > 0 &&
1112 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1113 n_string_push_c(&enp->ae_str, '\n');
1115 #endif
1118 if((len = strlen(format)) == 0)
1119 goto jleave;
1120 #ifdef HAVE_ERRORS
1121 n_pstate |= n_PS_ERRORS_PROMPT;
1122 #endif
1124 if(doname || a_aux_err_linelen == 0)
1125 fputs(VAL_UAGENT ": ", n_stderr);
1127 /* C99 */{
1128 size_t i = len;
1130 if(format[--len] == '\n'){
1131 a_aux_err_linelen = (i -= ++len);
1132 break;
1134 ++a_aux_err_linelen;
1135 }while(len > 0);
1138 #ifdef HAVE_ERRORS
1139 if(!(n_psonce & n_PSO_INTERACTIVE))
1140 #endif
1141 vfprintf(n_stderr, format, ap);
1142 #ifdef HAVE_ERRORS
1143 else{
1144 int imax, i;
1145 n_LCTAV(ERRORS_MAX > 3);
1147 /* Link it into the `errors' message ring */
1148 if((enp = a_aux_err_tail) == NULL){
1149 jcreat:
1150 enp = smalloc(sizeof *enp);
1151 enp->ae_next = NULL;
1152 n_string_creat(&enp->ae_str);
1153 if(a_aux_err_tail != NULL)
1154 a_aux_err_tail->ae_next = enp;
1155 else
1156 a_aux_err_head = enp;
1157 a_aux_err_tail = enp;
1158 ++a_aux_err_cnt;
1159 }else if(doname ||
1160 (enp->ae_str.s_len > 0 &&
1161 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1162 if(a_aux_err_cnt < ERRORS_MAX)
1163 goto jcreat;
1165 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1166 a_aux_err_tail->ae_next = enp;
1167 a_aux_err_tail = enp;
1168 enp->ae_next = NULL;
1169 n_string_trunc(&enp->ae_str, 0);
1172 # ifdef HAVE_N_VA_COPY
1173 imax = 64;
1174 # else
1175 imax = n_MIN(LINESIZE, 1024);
1176 # endif
1177 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1178 # ifdef HAVE_N_VA_COPY
1179 va_list vac;
1181 n_va_copy(vac, ap);
1182 # else
1183 # define vac ap
1184 # endif
1186 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1187 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1188 # ifdef HAVE_N_VA_COPY
1189 va_end(vac);
1190 # else
1191 # undef vac
1192 # endif
1193 if(i <= 0)
1194 goto jleave;
1195 if(UICMP(z, i, >=, imax)){
1196 # ifdef HAVE_N_VA_COPY
1197 /* XXX Check overflow for upcoming LEN+++i! */
1198 n_string_trunc(&enp->ae_str, len);
1199 continue;
1200 # else
1201 i = (int)strlen(&enp->ae_str.s_dat[len]);
1202 # endif
1204 break;
1206 n_string_trunc(&enp->ae_str, len + (size_t)i);
1208 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1210 #endif /* HAVE_ERRORS */
1212 jleave:
1213 fflush(n_stderr);
1214 NYD2_LEAVE;
1217 FL void
1218 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1219 va_list ap;
1220 NYD_X;
1222 va_start(ap, format);
1223 vfprintf(n_stderr, format, ap);
1224 va_end(ap);
1225 fflush(n_stderr);
1228 FL void
1229 n_perr(char const *msg, int errval){
1230 int e;
1231 char const *fmt;
1232 NYD2_ENTER;
1234 if(msg == NULL){
1235 fmt = "%s%s\n";
1236 msg = n_empty;
1237 }else
1238 fmt = "%s: %s\n";
1240 e = (errval == 0) ? n_err_no : errval;
1241 n_err(fmt, msg, n_err_to_doc(e));
1242 if(errval == 0)
1243 n_err_no = e;
1244 NYD2_LEAVE;
1247 FL void
1248 n_alert(char const *format, ...){
1249 va_list ap;
1250 NYD2_ENTER;
1252 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1254 va_start(ap, format);
1255 n_verr(format, ap);
1256 va_end(ap);
1258 n_err("\n");
1259 NYD2_LEAVE;
1262 FL void
1263 n_panic(char const *format, ...){
1264 va_list ap;
1265 NYD2_ENTER;
1267 if(a_aux_err_linelen > 0){
1268 putc('\n', n_stderr);
1269 a_aux_err_linelen = 0;
1271 fprintf(n_stderr, VAL_UAGENT ": Panic: ");
1273 va_start(ap, format);
1274 vfprintf(n_stderr, format, ap);
1275 va_end(ap);
1277 putc('\n', n_stderr);
1278 fflush(n_stderr);
1279 NYD2_LEAVE;
1280 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1283 #ifdef HAVE_ERRORS
1284 FL int
1285 c_errors(void *v){
1286 char **argv = v;
1287 struct a_aux_err_node *enp;
1288 NYD_ENTER;
1290 if(*argv == NULL)
1291 goto jlist;
1292 if(argv[1] != NULL)
1293 goto jerr;
1294 if(!asccasecmp(*argv, "show"))
1295 goto jlist;
1296 if(!asccasecmp(*argv, "clear"))
1297 goto jclear;
1298 jerr:
1299 fprintf(n_stderr,
1300 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1301 v = NULL;
1302 jleave:
1303 NYD_LEAVE;
1304 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1306 jlist:{
1307 FILE *fp;
1308 size_t i;
1310 if(a_aux_err_head == NULL){
1311 fprintf(n_stderr, _("The error ring is empty\n"));
1312 goto jleave;
1315 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1316 NULL){
1317 fprintf(n_stderr, _("tmpfile"));
1318 v = NULL;
1319 goto jleave;
1322 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1323 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1324 /* We don't know whether last string ended with NL; be simple XXX */
1325 putc('\n', fp);
1327 page_or_print(fp, 0);
1328 Fclose(fp);
1330 /* FALLTHRU */
1332 jclear:
1333 a_aux_err_tail = NULL;
1334 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1335 a_aux_err_linelen = 0;
1336 while((enp = a_aux_err_head) != NULL){
1337 a_aux_err_head = enp->ae_next;
1338 n_string_gut(&enp->ae_str);
1339 free(enp);
1341 goto jleave;
1343 #endif /* HAVE_ERRORS */
1345 FL char const *
1346 n_err_to_doc(si32_t eno){
1347 char const *rv;
1348 struct a_aux_err_map const *aemp;
1349 NYD2_ENTER;
1351 aemp = a_aux_err_map_from_no(eno);
1352 rv = &a_aux_err_docs[aemp->aem_docoff];
1353 NYD2_LEAVE;
1354 return rv;
1357 FL char const *
1358 n_err_to_name(si32_t eno){
1359 char const *rv;
1360 struct a_aux_err_map const *aemp;
1361 NYD2_ENTER;
1363 aemp = a_aux_err_map_from_no(eno);
1364 rv = &a_aux_err_names[aemp->aem_nameoff];
1365 NYD2_LEAVE;
1366 return rv;
1369 FL si32_t
1370 n_err_from_name(char const *name){
1371 struct a_aux_err_map const *aemp;
1372 ui32_t hash, i, j, x;
1373 si32_t rv;
1374 NYD2_ENTER;
1376 hash = torek_hash(name);
1378 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1379 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1380 break;
1382 aemp = &a_aux_err_map[x];
1383 if(aemp->aem_hash == hash &&
1384 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1385 rv = aemp->aem_errno;
1386 goto jleave;
1389 if(++i == a_AUX_ERR_REV_PRIME){
1390 #ifdef a_AUX_ERR_REV_WRAPAROUND
1391 i = 0;
1392 #else
1393 break;
1394 #endif
1398 /* Have not found it. But wait, it could be that the user did, e.g.,
1399 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1400 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1401 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1402 ) == n_IDEC_STATE_CONSUMED){
1403 aemp = a_aux_err_map_from_no(rv);
1404 rv = aemp->aem_errno;
1405 goto jleave;
1408 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_errno;
1409 jleave:
1410 NYD2_LEAVE;
1411 return rv;
1414 #ifdef HAVE_REGEX
1415 FL char const *
1416 n_regex_err_to_doc(const regex_t *rep, int e){
1417 char *cp;
1418 size_t i;
1419 NYD2_ENTER;
1421 i = regerror(e, rep, NULL, 0) +1;
1422 cp = salloc(i);
1423 regerror(e, rep, cp, i);
1424 NYD2_LEAVE;
1425 return cp;
1427 #endif
1429 /* s-it-mode */