Add `readctl' command..
[s-mailx.git] / auxlily.c
blob4f908320976511456241c0c2a36fada7540422fa
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 make-errors.sh output */
123 #include "gen-errors.h"
125 /* And these things come from mk-config.h (config-time make-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 = n_alloc(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 too large to be served without n_ERR_INTR error");
177 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
178 "Buffer too small to serve used array indices");
179 for(;;){
180 ssize_t gr;
182 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
183 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
184 a_aux_rand->a._dat[84]];
185 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
186 a_aux_rand->a._dat[42]];
187 /* ..but be on the safe side */
188 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
189 break;
190 n_msleep(250, FAL0);
193 # else
194 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
195 bool_t ok;
197 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
198 sizeof(a_aux_rand->a._dat)));
199 close(u.fd);
201 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
202 a_aux_rand->a._dat[84]];
203 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
204 a_aux_rand->a._dat[42]];
205 if(ok)
206 goto jleave;
209 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
210 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
211 ui32_t t, k;
213 # ifdef HAVE_CLOCK_GETTIME
214 clock_gettime(CLOCK_REALTIME, &ts);
215 t = (ui32_t)ts.tv_nsec;
216 # else
217 gettimeofday(&ts, NULL);
218 t = (ui32_t)ts.tv_usec;
219 # endif
220 if(rnd & 1)
221 t = (t >> 16) | (t << 16);
222 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
223 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
224 if(rnd == 7 || rnd == 17)
225 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
226 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
227 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
228 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
229 if((rnd & 3) == 3)
230 seed ^= n_prime_next(seed);
234 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
235 a_aux_rand_get8();
236 jleave:
237 # endif /* !HAVE_GETRANDOM */
238 NYD2_LEAVE;
241 SINLINE ui8_t
242 a_aux_rand_get8(void){
243 ui8_t si, sj;
245 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
246 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
247 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
248 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
249 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
252 # ifndef HAVE_GETRANDOM
253 static ui32_t
254 a_aux_rand_weak(ui32_t seed){
255 /* From "Random number generators: good ones are hard to find",
256 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
257 * October 1988, p. 1195.
258 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
259 ui32_t hi;
261 if(seed == 0)
262 seed = 123459876;
263 hi = seed / 127773;
264 seed %= 127773;
265 seed = (seed * 16807) - (hi * 2836);
266 if((si32_t)seed < 0)
267 seed += SI32_MAX;
268 return seed;
270 # endif /* HAVE_GETRANDOM */
271 #endif /* !HAVE_POSIX_RANDOM */
273 static struct a_aux_err_map const *
274 a_aux_err_map_from_no(si32_t eno){
275 si32_t ecmp;
276 size_t asz;
277 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
278 struct a_aux_err_map const *aemp;
279 NYD2_ENTER;
281 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
283 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
284 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
285 asz != 0; asz >>= 1){
286 tmp = &adat[asz >> 1];
287 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
288 aemp = &a_aux_err_map[(*tmp)[1]];
289 break;
291 if(ecmp > 0){
292 adat = &tmp[1];
293 --asz;
297 NYD2_LEAVE;
298 return aemp;
301 FL size_t
302 n_screensize(void){
303 char const *cp;
304 uiz_t rv;
305 NYD2_ENTER;
307 if((cp = ok_vlook(screen)) != NULL){
308 n_idec_uiz_cp(&rv, cp, 0, NULL);
309 if(rv == 0)
310 rv = n_scrnheight;
311 }else
312 rv = n_scrnheight;
314 if(rv > 2)
315 rv -= 2;
316 NYD2_LEAVE;
317 return rv;
320 FL char const *
321 n_pager_get(char const **env_addon){
322 char const *rv;
323 NYD_ENTER;
325 rv = ok_vlook(PAGER);
327 if(env_addon != NULL){
328 *env_addon = NULL;
329 /* Update the manual upon any changes:
330 * *colour-pager*, $PAGER */
331 if(strstr(rv, "less") != NULL){
332 if(getenv("LESS") == NULL)
333 *env_addon =
334 #ifdef HAVE_TERMCAP
335 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
336 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
337 #endif
338 "LESS=FRXi";
339 }else if(strstr(rv, "lv") != NULL){
340 if(getenv("LV") == NULL)
341 *env_addon = "LV=-c";
344 NYD_LEAVE;
345 return rv;
348 FL void
349 page_or_print(FILE *fp, size_t lines)
351 int c;
352 char const *cp;
353 NYD_ENTER;
355 fflush_rewind(fp);
357 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
358 size_t rows;
360 if(*cp == '\0')
361 rows = (size_t)n_scrnheight;
362 else
363 n_idec_uiz_cp(&rows, cp, 0, NULL);
365 if (rows > 0 && lines == 0) {
366 while ((c = getc(fp)) != EOF)
367 if (c == '\n' && ++lines >= rows)
368 break;
369 really_rewind(fp);
372 if (lines >= rows) {
373 char const *env_add[2], *pager;
375 pager = n_pager_get(&env_add[0]);
376 env_add[1] = NULL;
377 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
378 env_add);
379 goto jleave;
383 while ((c = getc(fp)) != EOF)
384 putc(c, n_stdout);
385 jleave:
386 NYD_LEAVE;
389 FL enum protocol
390 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
391 char const **adjusted_or_null)
393 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
394 char const *cp, *orig_name;
395 enum protocol rv = PROTO_UNKNOWN;
396 NYD_ENTER;
398 if(name[0] == '%' && name[1] == ':')
399 name += 2;
400 orig_name = name;
402 for (cp = name; *cp && *cp != ':'; cp++)
403 if (!alnumchar(*cp))
404 goto jfile;
406 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
407 if(!strncmp(name, "file", sizeof("file") -1) ||
408 !strncmp(name, "mbox", sizeof("mbox") -1))
409 rv = PROTO_FILE;
410 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
411 rv = PROTO_MAILDIR;
412 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
413 #ifdef HAVE_POP3
414 rv = PROTO_POP3;
415 #else
416 n_err(_("No POP3 support compiled in\n"));
417 #endif
418 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
419 #if defined HAVE_POP3 && defined HAVE_SSL
420 rv = PROTO_POP3;
421 #else
422 n_err(_("No POP3S support compiled in\n"));
423 #endif
425 orig_name = &cp[3];
426 goto jleave;
429 jfile:
430 rv = PROTO_FILE;
432 if(check_stat || try_hooks){
433 struct n_file_type ft;
434 struct stat stb;
435 char *np;
436 size_t sz;
438 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
439 memcpy(np, name, sz + 1);
441 if(!stat(name, &stb)){
442 if(S_ISDIR(stb.st_mode) &&
443 (memcpy(&np[sz], "/tmp", 5),
444 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
445 (memcpy(&np[sz], "/new", 5),
446 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
447 (memcpy(&np[sz], "/cur", 5),
448 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
449 rv = PROTO_MAILDIR;
450 }else if(try_hooks && n_filetype_trial(&ft, name))
451 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
452 else if((cp = ok_vlook(newfolders)) != NULL &&
453 !asccasecmp(cp, "maildir"))
454 rv = PROTO_MAILDIR;
456 n_lofi_free(np);
458 jleave:
459 if(adjusted_or_null != NULL)
460 *adjusted_or_null = orig_name;
461 NYD_LEAVE;
462 return rv;
465 FL char *
466 n_c_to_hex_base16(char store[3], char c){
467 static char const itoa16[] = "0123456789ABCDEF";
468 NYD2_ENTER;
470 store[2] = '\0';
471 store[1] = itoa16[(ui8_t)c & 0x0F];
472 c = ((ui8_t)c >> 4) & 0x0F;
473 store[0] = itoa16[(ui8_t)c];
474 NYD2_LEAVE;
475 return store;
478 FL si32_t
479 n_c_from_hex_base16(char const hex[2]){
480 static ui8_t const atoi16[] = {
481 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
482 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
483 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
484 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
485 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
486 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
487 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
489 ui8_t i1, i2;
490 si32_t rv;
491 NYD2_ENTER;
493 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
494 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
495 goto jerr;
496 i1 = atoi16[i1];
497 i2 = atoi16[i2];
498 if ((i1 | i2) & 0xF0u)
499 goto jerr;
500 rv = i1;
501 rv <<= 4;
502 rv += i2;
503 jleave:
504 NYD2_LEAVE;
505 return rv;
506 jerr:
507 rv = -1;
508 goto jleave;
511 FL enum n_idec_state
512 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
513 enum n_idec_mode idm, char const **endptr_or_null){
514 /* XXX Brute simple and */
515 ui8_t currc;
516 ui64_t res, cut;
517 enum n_idec_state rv;
518 NYD_ENTER;
520 idm &= n__IDEC_MODE_MASK;
521 rv = n_IDEC_STATE_NONE | idm;
522 res = 0;
524 if(clen == UIZ_MAX){
525 if(*cbuf == '\0')
526 goto jeinval;
527 }else if(clen == 0)
528 goto jeinval;
530 /* Leading WS */
531 while(spacechar(*cbuf))
532 if(*++cbuf == '\0' || --clen == 0)
533 goto jeinval;
535 /* Check sign */
536 switch(*cbuf){
537 case '-':
538 rv |= n_IDEC_STATE_SEEN_MINUS;
539 /* FALLTHROUGH */
540 case '+':
541 if(*++cbuf == '\0' || --clen == 0)
542 goto jeinval;
543 break;
546 /* Base detection/skip */
547 if(*cbuf != '0'){
548 if(base == 0)
549 base = 10;
550 /* Character must be valid for base */
551 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
552 if(currc >= base)
553 goto jeinval;
554 }else{
555 /* 0 always valid as is, fallback base 10 */
556 if(*++cbuf == '\0' || --clen == 0)
557 goto jleave;
559 /* Base "detection" */
560 if(base == 0 || base == 2 || base == 16){
561 switch(*cbuf){
562 case 'x':
563 case 'X':
564 if((base & 2) == 0){
565 base = 0x10;
566 goto jprefix_skip;
568 break;
569 case 'b':
570 case 'B':
571 if((base & 16) == 0){
572 base = 2; /* 0b10 */
573 /* Char after prefix must be valid */
574 jprefix_skip:
575 if(*++cbuf == '\0' || --clen == 0)
576 goto jeinval;
578 /* Character must be valid for base, invalid otherwise */
579 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
580 if(currc >= base)
581 goto jeinval;
583 break;
584 default:
585 if(base == 0)
586 base = 010;
587 break;
591 /* Character must be valid for base, _EBASE otherwise */
592 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
593 if(currc >= base)
594 goto jebase;
597 for(cut = a_aux_idec_cutlimit[base - 2];;){
598 if(res >= cut){
599 if(res == cut){
600 res *= base;
601 if(res > UI64_MAX - currc)
602 goto jeover;
603 res += currc;
604 }else
605 goto jeover;
606 }else{
607 res *= base;
608 res += currc;
611 if(*++cbuf == '\0' || --clen == 0)
612 break;
614 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
615 if(currc >= base)
616 goto jebase;
619 jleave:
621 ui64_t uimask;
623 switch(rv & n__IDEC_MODE_LIMIT_MASK){
624 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
625 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
626 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
627 default: uimask = UI64_MAX; break;
629 if(rv & n_IDEC_MODE_SIGNED_TYPE)
630 uimask >>= 1;
632 if(res & ~uimask){
633 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
634 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
635 if(res > uimask + 1){
636 res = uimask << 1;
637 res &= ~uimask;
638 }else{
639 res = -res;
640 break;
642 }else
643 res = uimask;
644 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
645 rv |= n_IDEC_STATE_EOVERFLOW;
646 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
647 res = -res;
648 }while(0);
650 switch(rv & n__IDEC_MODE_LIMIT_MASK){
651 case n_IDEC_MODE_LIMIT_8BIT:
652 if(rv & n_IDEC_MODE_SIGNED_TYPE)
653 *(si8_t*)resp = (si8_t)res;
654 else
655 *(ui8_t*)resp = (ui8_t)res;
656 break;
657 case n_IDEC_MODE_LIMIT_16BIT:
658 if(rv & n_IDEC_MODE_SIGNED_TYPE)
659 *(si16_t*)resp = (si16_t)res;
660 else
661 *(ui16_t*)resp = (ui16_t)res;
662 break;
663 case n_IDEC_MODE_LIMIT_32BIT:
664 if(rv & n_IDEC_MODE_SIGNED_TYPE)
665 *(si32_t*)resp = (si32_t)res;
666 else
667 *(ui32_t*)resp = (ui32_t)res;
668 break;
669 default:
670 if(rv & n_IDEC_MODE_SIGNED_TYPE)
671 *(si64_t*)resp = (si64_t)res;
672 else
673 *(ui64_t*)resp = (ui64_t)res;
674 break;
677 if(endptr_or_null != NULL)
678 *endptr_or_null = cbuf;
679 if(*cbuf == '\0' || clen == 0)
680 rv |= n_IDEC_STATE_CONSUMED;
681 NYD_LEAVE;
682 return rv;
684 jeinval:
685 rv |= n_IDEC_STATE_EINVAL;
686 goto j_maxval;
687 jebase:
688 /* Not a base error for terminator and whitespace! */
689 if(*cbuf != '\0' && !spacechar(*cbuf))
690 rv |= n_IDEC_STATE_EBASE;
691 goto jleave;
693 jeover:
694 /* Overflow error: consume input until bad character or length out */
695 for(;;){
696 if(*++cbuf == '\0' || --clen == 0)
697 break;
698 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
699 if(currc >= base)
700 break;
703 rv |= n_IDEC_STATE_EOVERFLOW;
704 j_maxval:
705 if(rv & n_IDEC_MODE_SIGNED_TYPE)
706 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
707 : (ui64_t)SI64_MAX;
708 else
709 res = UI64_MAX;
710 rv &= ~n_IDEC_STATE_SEEN_MINUS;
711 goto jleave;
714 FL ui32_t
715 n_torek_hash(char const *name){
716 /* Chris Torek's hash */
717 char c;
718 ui32_t h;
719 NYD2_ENTER;
721 for(h = 0; (c = *name++) != '\0';)
722 h = (h * 33) + c;
723 NYD2_LEAVE;
724 return h;
727 FL ui32_t
728 n_torek_ihashn(char const *dat, size_t len){
729 /* See n_torek_hash() */
730 char c;
731 ui32_t h;
732 NYD2_ENTER;
734 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
735 h = (h * 33) + lowerconv(c);
736 NYD2_LEAVE;
737 return h;
740 FL ui32_t
741 n_prime_next(ui32_t n){
742 static ui32_t const primes[] = {
743 5, 11, 23, 47, 97, 157, 283,
744 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
745 131071, 262139, 524287, 1048573, 2097143, 4194301,
746 8388593, 16777213, 33554393, 67108859, 134217689,
747 268435399, 536870909, 1073741789, 2147483647
749 ui32_t i, mprime;
750 NYD2_ENTER;
752 i = (n < primes[n_NELEM(primes) / 4] ? 0
753 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
754 : n_NELEM(primes) / 2));
756 do if((mprime = primes[i]) > n)
757 break;
758 while(++i < n_NELEM(primes));
760 if(i == n_NELEM(primes) && mprime < n)
761 mprime = n;
762 NYD2_LEAVE;
763 return mprime;
766 FL char const *
767 n_getdeadletter(void){
768 char const *cp_base, *cp;
769 NYD_ENTER;
771 cp_base = NULL;
772 jredo:
773 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
774 if(cp == NULL || strlen(cp) >= PATH_MAX){
775 if(cp_base == NULL){
776 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
777 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
778 ok_vclear(DEAD);
779 goto jredo;
780 }else{
781 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
782 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
785 NYD_LEAVE;
786 return cp;
789 FL char *
790 n_nodename(bool_t mayoverride){
791 static char *sys_hostname, *hostname; /* XXX free-at-exit */
793 struct utsname ut;
794 char *hn;
795 #ifdef HAVE_SOCKETS
796 # ifdef HAVE_GETADDRINFO
797 struct addrinfo hints, *res;
798 # else
799 struct hostent *hent;
800 # endif
801 #endif
802 NYD2_ENTER;
804 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
806 }else if((hn = sys_hostname) == NULL){
807 uname(&ut);
808 hn = ut.nodename;
809 #ifdef HAVE_SOCKETS
810 # ifdef HAVE_GETADDRINFO
811 memset(&hints, 0, sizeof hints);
812 hints.ai_family = AF_UNSPEC;
813 hints.ai_flags = AI_CANONNAME;
814 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
815 if(res->ai_canonname != NULL){
816 size_t l;
818 l = strlen(res->ai_canonname) +1;
819 hn = n_lofi_alloc(l);
820 memcpy(hn, res->ai_canonname, l);
822 freeaddrinfo(res);
824 # else
825 hent = gethostbyname(hn);
826 if(hent != NULL)
827 hn = hent->h_name;
828 # endif
829 #endif
830 sys_hostname = sstrdup(hn);
831 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
832 if(hn != ut.nodename)
833 n_lofi_free(hn);
834 #endif
835 hn = sys_hostname;
838 if(hostname != NULL && hostname != sys_hostname)
839 n_free(hostname);
840 hostname = sstrdup(hn);
841 NYD2_LEAVE;
842 return hostname;
845 FL char *
846 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
847 struct str b64;
848 char *data, *cp;
849 size_t i;
850 NYD_ENTER;
852 #ifndef HAVE_POSIX_RANDOM
853 if(a_aux_rand == NULL)
854 a_aux_rand_init();
855 #endif
857 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
858 * with PAD stripped is still longer than what the user requests, easy way */
859 data = n_lofi_alloc(i = length + 3);
861 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
862 #ifndef HAVE_POSIX_RANDOM
863 while(i-- > 0)
864 data[i] = (char)a_aux_rand_get8();
865 #else
866 for(cp = data; i > 0;){
867 union {ui32_t i4; char c[4];} r;
868 size_t j;
870 r.i4 = (ui32_t)arc4random();
871 switch((j = i & 3)){
872 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
873 case 3: cp[2] = r.c[2]; /* FALLTHRU */
874 case 2: cp[1] = r.c[1]; /* FALLTHRU */
875 default: cp[0] = r.c[0]; break;
877 cp += j;
878 i -= j;
880 #endif
881 }else{
882 for(cp = data; i > 0;){
883 union {ui32_t i4; char c[4];} r;
884 size_t j;
886 r.i4 = ++*reprocnt_or_null;
887 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
888 char x;
890 x = r.c[0];
891 r.c[0] = r.c[3];
892 r.c[3] = x;
893 x = r.c[1];
894 r.c[1] = r.c[2];
895 r.c[2] = x;
897 switch((j = i & 3)){
898 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
899 case 3: cp[2] = r.c[2]; /* FALLTHRU */
900 case 2: cp[1] = r.c[1]; /* FALLTHRU */
901 default: cp[0] = r.c[0]; break;
903 cp += j;
904 i -= j;
908 assert(length + 3 < UIZ_MAX / 4);
909 b64_encode_buf(&b64, data, length + 3,
910 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
911 n_lofi_free(data);
913 assert(b64.l >= length);
914 b64.s[length] = '\0';
915 NYD_LEAVE;
916 return b64.s;
919 FL si8_t
920 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
922 si8_t rv;
923 NYD_ENTER;
925 assert(inlen == 0 || inbuf != NULL);
927 if (inlen == UIZ_MAX)
928 inlen = strlen(inbuf);
930 if (inlen == 0)
931 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
932 else {
933 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
934 !ascncasecmp(inbuf, "true", inlen) ||
935 !ascncasecmp(inbuf, "yes", inlen) ||
936 !ascncasecmp(inbuf, "on", inlen))
937 rv = 1;
938 else if ((inlen == 1 &&
939 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
940 !ascncasecmp(inbuf, "false", inlen) ||
941 !ascncasecmp(inbuf, "no", inlen) ||
942 !ascncasecmp(inbuf, "off", inlen))
943 rv = 0;
944 else {
945 ui64_t ib;
947 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
948 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
949 ) != n_IDEC_STATE_CONSUMED)
950 rv = -1;
951 else
952 rv = (ib != 0);
955 NYD_LEAVE;
956 return rv;
959 FL si8_t
960 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
962 si8_t rv;
963 NYD_ENTER;
965 assert(inlen == 0 || inbuf != NULL);
967 if (inlen == UIZ_MAX)
968 inlen = strlen(inbuf);
970 if (inlen == 0)
971 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
972 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
973 !ascncasecmp(inbuf, "ask-", 4) &&
974 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
975 (n_psonce & n_PSO_INTERACTIVE))
976 rv = getapproval(prompt, rv);
977 NYD_LEAVE;
978 return rv;
981 FL bool_t
982 n_is_all_or_aster(char const *name){
983 bool_t rv;
984 NYD_ENTER;
986 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
987 NYD_LEAVE;
988 return rv;
991 FL struct n_timespec const *
992 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
993 static struct n_timespec ts_now;
994 NYD2_ENTER;
996 if(n_psonce & n_PSO_REPRODUCIBLE){
997 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
998 ts_now.ts_nsec = 0;
999 }else if(force_update || ts_now.ts_sec == 0){
1000 #ifdef HAVE_CLOCK_GETTIME
1001 struct timespec ts;
1003 clock_gettime(CLOCK_REALTIME, &ts);
1004 ts_now.ts_sec = (si64_t)ts.tv_sec;
1005 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1006 #elif defined HAVE_GETTIMEOFDAY
1007 struct timeval tv;
1009 gettimeofday(&tv, NULL);
1010 ts_now.ts_sec = (si64_t)tv.tv_sec;
1011 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1012 #else
1013 ts_now.ts_sec = (si64_t)time(NULL);
1014 ts_now.ts_nsec = 0;
1015 #endif
1017 NYD2_LEAVE;
1018 return &ts_now;
1021 FL void
1022 time_current_update(struct time_current *tc, bool_t full_update)
1024 NYD_ENTER;
1025 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1026 if (full_update) {
1027 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1028 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1029 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1031 NYD_LEAVE;
1034 FL uiz_t
1035 n_msleep(uiz_t millis, bool_t ignint){
1036 uiz_t rv;
1037 NYD2_ENTER;
1039 #ifdef HAVE_NANOSLEEP
1040 /* C99 */{
1041 struct timespec ts, trem;
1042 int i;
1044 ts.tv_sec = millis / 1000;
1045 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1047 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1048 ts = trem;
1049 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1052 #elif defined HAVE_SLEEP
1053 if((millis /= 1000) == 0)
1054 millis = 1;
1055 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1056 millis = rv;
1057 #else
1058 # error Configuration should have detected a function for sleeping.
1059 #endif
1061 NYD2_LEAVE;
1062 return rv;
1065 FL void
1066 n_err(char const *format, ...){
1067 va_list ap;
1068 NYD2_ENTER;
1070 va_start(ap, format);
1071 #ifdef HAVE_ERRORS
1072 if(n_psonce & n_PSO_INTERACTIVE)
1073 n_verr(format, ap);
1074 else
1075 #endif
1077 size_t len;
1078 bool_t doname, doflush;
1080 doflush = FAL0;
1081 while(*format == '\n'){
1082 doflush = TRU1;
1083 putc('\n', n_stderr);
1084 ++format;
1087 if((doname = doflush))
1088 a_aux_err_linelen = 0;
1090 if((len = strlen(format)) > 0){
1091 if(doname || a_aux_err_linelen == 0){
1092 char const *cp;
1094 if(*(cp = ok_vlook(log_prefix)) != '\0')
1095 fputs(cp, n_stderr);
1097 vfprintf(n_stderr, format, ap);
1099 /* C99 */{
1100 size_t i = len;
1102 if(format[--len] == '\n'){
1103 a_aux_err_linelen = (i -= ++len);
1104 break;
1106 ++a_aux_err_linelen;
1107 }while(len > 0);
1111 if(doflush)
1112 fflush(n_stderr);
1114 va_end(ap);
1115 NYD2_LEAVE;
1118 FL void
1119 n_verr(char const *format, va_list ap){
1120 #ifdef HAVE_ERRORS
1121 struct a_aux_err_node *enp;
1122 #endif
1123 bool_t doname;
1124 size_t len;
1125 NYD2_ENTER;
1127 doname = FAL0;
1129 while(*format == '\n'){
1130 putc('\n', n_stderr);
1131 doname = TRU1;
1132 ++format;
1135 if(doname){
1136 a_aux_err_linelen = 0;
1137 #ifdef HAVE_ERRORS
1138 if(n_psonce & n_PSO_INTERACTIVE){
1139 if((enp = a_aux_err_tail) != NULL &&
1140 (enp->ae_str.s_len > 0 &&
1141 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1142 n_string_push_c(&enp->ae_str, '\n');
1144 #endif
1147 if((len = strlen(format)) == 0)
1148 goto jleave;
1149 #ifdef HAVE_ERRORS
1150 n_pstate |= n_PS_ERRORS_PROMPT;
1151 #endif
1153 if(doname || a_aux_err_linelen == 0){
1154 char const *cp;
1156 if(*(cp = ok_vlook(log_prefix)) != '\0')
1157 fputs(cp, n_stderr);
1160 /* C99 */{
1161 size_t i = len;
1163 if(format[--len] == '\n'){
1164 a_aux_err_linelen = (i -= ++len);
1165 break;
1167 ++a_aux_err_linelen;
1168 }while(len > 0);
1171 #ifdef HAVE_ERRORS
1172 if(!(n_psonce & n_PSO_INTERACTIVE))
1173 #endif
1174 vfprintf(n_stderr, format, ap);
1175 #ifdef HAVE_ERRORS
1176 else{
1177 int imax, i;
1178 n_LCTAV(ERRORS_MAX > 3);
1180 /* Link it into the `errors' message ring */
1181 if((enp = a_aux_err_tail) == NULL){
1182 jcreat:
1183 enp = smalloc(sizeof *enp);
1184 enp->ae_next = NULL;
1185 n_string_creat(&enp->ae_str);
1186 if(a_aux_err_tail != NULL)
1187 a_aux_err_tail->ae_next = enp;
1188 else
1189 a_aux_err_head = enp;
1190 a_aux_err_tail = enp;
1191 ++a_aux_err_cnt;
1192 }else if(doname ||
1193 (enp->ae_str.s_len > 0 &&
1194 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1195 if(a_aux_err_cnt < ERRORS_MAX)
1196 goto jcreat;
1198 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1199 a_aux_err_tail->ae_next = enp;
1200 a_aux_err_tail = enp;
1201 enp->ae_next = NULL;
1202 n_string_trunc(&enp->ae_str, 0);
1205 # ifdef HAVE_N_VA_COPY
1206 imax = 64;
1207 # else
1208 imax = n_MIN(LINESIZE, 1024);
1209 # endif
1210 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1211 # ifdef HAVE_N_VA_COPY
1212 va_list vac;
1214 n_va_copy(vac, ap);
1215 # else
1216 # define vac ap
1217 # endif
1219 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1220 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1221 # ifdef HAVE_N_VA_COPY
1222 va_end(vac);
1223 # else
1224 # undef vac
1225 # endif
1226 if(i <= 0)
1227 goto jleave;
1228 if(UICMP(z, i, >=, imax)){
1229 # ifdef HAVE_N_VA_COPY
1230 /* XXX Check overflow for upcoming LEN+++i! */
1231 n_string_trunc(&enp->ae_str, len);
1232 continue;
1233 # else
1234 i = (int)strlen(&enp->ae_str.s_dat[len]);
1235 # endif
1237 break;
1239 n_string_trunc(&enp->ae_str, len + (size_t)i);
1241 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1243 #endif /* HAVE_ERRORS */
1245 jleave:
1246 fflush(n_stderr);
1247 NYD2_LEAVE;
1250 FL void
1251 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1252 va_list ap;
1253 NYD_X;
1255 va_start(ap, format);
1256 vfprintf(n_stderr, format, ap);
1257 va_end(ap);
1258 fflush(n_stderr);
1261 FL void
1262 n_perr(char const *msg, int errval){
1263 int e;
1264 char const *fmt;
1265 NYD2_ENTER;
1267 if(msg == NULL){
1268 fmt = "%s%s\n";
1269 msg = n_empty;
1270 }else
1271 fmt = "%s: %s\n";
1273 e = (errval == 0) ? n_err_no : errval;
1274 n_err(fmt, msg, n_err_to_doc(e));
1275 if(errval == 0)
1276 n_err_no = e;
1277 NYD2_LEAVE;
1280 FL void
1281 n_alert(char const *format, ...){
1282 va_list ap;
1283 NYD2_ENTER;
1285 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1287 va_start(ap, format);
1288 n_verr(format, ap);
1289 va_end(ap);
1291 n_err("\n");
1292 NYD2_LEAVE;
1295 FL void
1296 n_panic(char const *format, ...){
1297 va_list ap;
1298 NYD2_ENTER;
1300 if(a_aux_err_linelen > 0){
1301 putc('\n', n_stderr);
1302 a_aux_err_linelen = 0;
1304 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1306 va_start(ap, format);
1307 vfprintf(n_stderr, format, ap);
1308 va_end(ap);
1310 putc('\n', n_stderr);
1311 fflush(n_stderr);
1312 NYD2_LEAVE;
1313 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1316 #ifdef HAVE_ERRORS
1317 FL int
1318 c_errors(void *v){
1319 char **argv = v;
1320 struct a_aux_err_node *enp;
1321 NYD_ENTER;
1323 if(*argv == NULL)
1324 goto jlist;
1325 if(argv[1] != NULL)
1326 goto jerr;
1327 if(!asccasecmp(*argv, "show"))
1328 goto jlist;
1329 if(!asccasecmp(*argv, "clear"))
1330 goto jclear;
1331 jerr:
1332 fprintf(n_stderr,
1333 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1334 v = NULL;
1335 jleave:
1336 NYD_LEAVE;
1337 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1339 jlist:{
1340 FILE *fp;
1341 size_t i;
1343 if(a_aux_err_head == NULL){
1344 fprintf(n_stderr, _("The error ring is empty\n"));
1345 goto jleave;
1348 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1349 NULL){
1350 fprintf(n_stderr, _("tmpfile"));
1351 v = NULL;
1352 goto jleave;
1355 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1356 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1357 /* We don't know whether last string ended with NL; be simple XXX */
1358 putc('\n', fp);
1360 page_or_print(fp, 0);
1361 Fclose(fp);
1363 /* FALLTHRU */
1365 jclear:
1366 a_aux_err_tail = NULL;
1367 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1368 a_aux_err_linelen = 0;
1369 while((enp = a_aux_err_head) != NULL){
1370 a_aux_err_head = enp->ae_next;
1371 n_string_gut(&enp->ae_str);
1372 free(enp);
1374 goto jleave;
1376 #endif /* HAVE_ERRORS */
1378 FL char const *
1379 n_err_to_doc(si32_t eno){
1380 char const *rv;
1381 struct a_aux_err_map const *aemp;
1382 NYD2_ENTER;
1384 aemp = a_aux_err_map_from_no(eno);
1385 rv = &a_aux_err_docs[aemp->aem_docoff];
1386 NYD2_LEAVE;
1387 return rv;
1390 FL char const *
1391 n_err_to_name(si32_t eno){
1392 char const *rv;
1393 struct a_aux_err_map const *aemp;
1394 NYD2_ENTER;
1396 aemp = a_aux_err_map_from_no(eno);
1397 rv = &a_aux_err_names[aemp->aem_nameoff];
1398 NYD2_LEAVE;
1399 return rv;
1402 FL si32_t
1403 n_err_from_name(char const *name){
1404 struct a_aux_err_map const *aemp;
1405 ui32_t hash, i, j, x;
1406 si32_t rv;
1407 NYD2_ENTER;
1409 hash = n_torek_hash(name);
1411 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1412 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1413 break;
1415 aemp = &a_aux_err_map[x];
1416 if(aemp->aem_hash == hash &&
1417 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1418 rv = aemp->aem_err_no;
1419 goto jleave;
1422 if(++i == a_AUX_ERR_REV_PRIME){
1423 #ifdef a_AUX_ERR_REV_WRAPAROUND
1424 i = 0;
1425 #else
1426 break;
1427 #endif
1431 /* Have not found it. But wait, it could be that the user did, e.g.,
1432 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1433 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1434 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1435 ) == n_IDEC_STATE_CONSUMED){
1436 aemp = a_aux_err_map_from_no(rv);
1437 rv = aemp->aem_err_no;
1438 goto jleave;
1441 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1442 jleave:
1443 NYD2_LEAVE;
1444 return rv;
1447 #ifdef HAVE_REGEX
1448 FL char const *
1449 n_regex_err_to_doc(const regex_t *rep, int e){
1450 char *cp;
1451 size_t i;
1452 NYD2_ENTER;
1454 i = regerror(e, rep, NULL, 0) +1;
1455 cp = salloc(i);
1456 regerror(e, rep, cp, i);
1457 NYD2_LEAVE;
1458 return cp;
1460 #endif
1462 /* s-it-mode */