More `alternates' processing: a 2nd "pure", non-extended run
[s-mailx.git] / auxlily.c
blob675c82ca929504505ed827ad0e17ac5da4a73c65
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, NULL);
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 if(len == UIZ_MAX)
735 for(h = 0; (c = *dat++) != '\0';)
736 h = (h * 33) + lowerconv(c);
737 else
738 for(h = 0; len > 0; --len){
739 c = *dat++;
740 h = (h * 33) + lowerconv(c);
742 NYD2_LEAVE;
743 return h;
746 FL ui32_t
747 n_prime_next(ui32_t n){
748 static ui32_t const primes[] = {
749 5, 11, 23, 47, 97, 157, 283,
750 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
751 131071, 262139, 524287, 1048573, 2097143, 4194301,
752 8388593, 16777213, 33554393, 67108859, 134217689,
753 268435399, 536870909, 1073741789, 2147483647
755 ui32_t i, mprime;
756 NYD2_ENTER;
758 i = (n < primes[n_NELEM(primes) / 4] ? 0
759 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
760 : n_NELEM(primes) / 2));
762 do if((mprime = primes[i]) > n)
763 break;
764 while(++i < n_NELEM(primes));
766 if(i == n_NELEM(primes) && mprime < n)
767 mprime = n;
768 NYD2_LEAVE;
769 return mprime;
772 FL char const *
773 n_getdeadletter(void){
774 char const *cp_base, *cp;
775 NYD_ENTER;
777 cp_base = NULL;
778 jredo:
779 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
780 if(cp == NULL || strlen(cp) >= PATH_MAX){
781 if(cp_base == NULL){
782 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
783 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
784 ok_vclear(DEAD);
785 goto jredo;
786 }else{
787 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
788 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
791 NYD_LEAVE;
792 return cp;
795 FL char *
796 n_nodename(bool_t mayoverride){
797 static char *sys_hostname, *hostname; /* XXX free-at-exit */
799 struct utsname ut;
800 char *hn;
801 #ifdef HAVE_SOCKETS
802 # ifdef HAVE_GETADDRINFO
803 struct addrinfo hints, *res;
804 # else
805 struct hostent *hent;
806 # endif
807 #endif
808 NYD2_ENTER;
810 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
812 }else if((hn = sys_hostname) == NULL){
813 uname(&ut);
814 hn = ut.nodename;
815 #ifdef HAVE_SOCKETS
816 # ifdef HAVE_GETADDRINFO
817 memset(&hints, 0, sizeof hints);
818 hints.ai_family = AF_UNSPEC;
819 hints.ai_flags = AI_CANONNAME;
820 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
821 if(res->ai_canonname != NULL){
822 size_t l;
824 l = strlen(res->ai_canonname) +1;
825 hn = n_lofi_alloc(l);
826 memcpy(hn, res->ai_canonname, l);
828 freeaddrinfo(res);
830 # else
831 hent = gethostbyname(hn);
832 if(hent != NULL)
833 hn = hent->h_name;
834 # endif
835 #endif
836 sys_hostname = sstrdup(hn);
837 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
838 if(hn != ut.nodename)
839 n_lofi_free(hn);
840 #endif
841 hn = sys_hostname;
844 if(hostname != NULL && hostname != sys_hostname)
845 n_free(hostname);
846 hostname = sstrdup(hn);
847 NYD2_LEAVE;
848 return hostname;
851 FL char *
852 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
853 struct str b64;
854 char *data, *cp;
855 size_t i;
856 NYD_ENTER;
858 #ifndef HAVE_POSIX_RANDOM
859 if(a_aux_rand == NULL)
860 a_aux_rand_init();
861 #endif
863 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
864 * with PAD stripped is still longer than what the user requests, easy way */
865 data = n_lofi_alloc(i = length + 3);
867 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
868 #ifndef HAVE_POSIX_RANDOM
869 while(i-- > 0)
870 data[i] = (char)a_aux_rand_get8();
871 #else
872 for(cp = data; i > 0;){
873 union {ui32_t i4; char c[4];} r;
874 size_t j;
876 r.i4 = (ui32_t)arc4random();
877 switch((j = i & 3)){
878 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
879 case 3: cp[2] = r.c[2]; /* FALLTHRU */
880 case 2: cp[1] = r.c[1]; /* FALLTHRU */
881 default: cp[0] = r.c[0]; break;
883 cp += j;
884 i -= j;
886 #endif
887 }else{
888 for(cp = data; i > 0;){
889 union {ui32_t i4; char c[4];} r;
890 size_t j;
892 r.i4 = ++*reprocnt_or_null;
893 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
894 char x;
896 x = r.c[0];
897 r.c[0] = r.c[3];
898 r.c[3] = x;
899 x = r.c[1];
900 r.c[1] = r.c[2];
901 r.c[2] = x;
903 switch((j = i & 3)){
904 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
905 case 3: cp[2] = r.c[2]; /* FALLTHRU */
906 case 2: cp[1] = r.c[1]; /* FALLTHRU */
907 default: cp[0] = r.c[0]; break;
909 cp += j;
910 i -= j;
914 assert(length + 3 < UIZ_MAX / 4);
915 b64_encode_buf(&b64, data, length + 3,
916 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
917 n_lofi_free(data);
919 assert(b64.l >= length);
920 b64.s[length] = '\0';
921 NYD_LEAVE;
922 return b64.s;
925 FL si8_t
926 boolify(char const *inbuf, uiz_t inlen, 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 {
939 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
940 !ascncasecmp(inbuf, "true", inlen) ||
941 !ascncasecmp(inbuf, "yes", inlen) ||
942 !ascncasecmp(inbuf, "on", inlen))
943 rv = 1;
944 else if ((inlen == 1 &&
945 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
946 !ascncasecmp(inbuf, "false", inlen) ||
947 !ascncasecmp(inbuf, "no", inlen) ||
948 !ascncasecmp(inbuf, "off", inlen))
949 rv = 0;
950 else {
951 ui64_t ib;
953 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
954 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
955 ) != n_IDEC_STATE_CONSUMED)
956 rv = -1;
957 else
958 rv = (ib != 0);
961 NYD_LEAVE;
962 return rv;
965 FL si8_t
966 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
968 si8_t rv;
969 NYD_ENTER;
971 assert(inlen == 0 || inbuf != NULL);
973 if (inlen == UIZ_MAX)
974 inlen = strlen(inbuf);
976 if (inlen == 0)
977 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
978 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
979 !ascncasecmp(inbuf, "ask-", 4) &&
980 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
981 (n_psonce & n_PSO_INTERACTIVE))
982 rv = getapproval(prompt, rv);
983 NYD_LEAVE;
984 return rv;
987 FL bool_t
988 n_is_all_or_aster(char const *name){
989 bool_t rv;
990 NYD_ENTER;
992 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
993 NYD_LEAVE;
994 return rv;
997 FL struct n_timespec const *
998 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
999 static struct n_timespec ts_now;
1000 NYD2_ENTER;
1002 if(n_psonce & n_PSO_REPRODUCIBLE){
1003 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1004 ts_now.ts_nsec = 0;
1005 }else if(force_update || ts_now.ts_sec == 0){
1006 #ifdef HAVE_CLOCK_GETTIME
1007 struct timespec ts;
1009 clock_gettime(CLOCK_REALTIME, &ts);
1010 ts_now.ts_sec = (si64_t)ts.tv_sec;
1011 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1012 #elif defined HAVE_GETTIMEOFDAY
1013 struct timeval tv;
1015 gettimeofday(&tv, NULL);
1016 ts_now.ts_sec = (si64_t)tv.tv_sec;
1017 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1018 #else
1019 ts_now.ts_sec = (si64_t)time(NULL);
1020 ts_now.ts_nsec = 0;
1021 #endif
1023 NYD2_LEAVE;
1024 return &ts_now;
1027 FL void
1028 time_current_update(struct time_current *tc, bool_t full_update)
1030 NYD_ENTER;
1031 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1032 if (full_update) {
1033 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1034 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1035 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1037 NYD_LEAVE;
1040 FL uiz_t
1041 n_msleep(uiz_t millis, bool_t ignint){
1042 uiz_t rv;
1043 NYD2_ENTER;
1045 #ifdef HAVE_NANOSLEEP
1046 /* C99 */{
1047 struct timespec ts, trem;
1048 int i;
1050 ts.tv_sec = millis / 1000;
1051 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1053 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1054 ts = trem;
1055 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1058 #elif defined HAVE_SLEEP
1059 if((millis /= 1000) == 0)
1060 millis = 1;
1061 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1062 millis = rv;
1063 #else
1064 # error Configuration should have detected a function for sleeping.
1065 #endif
1067 NYD2_LEAVE;
1068 return rv;
1071 FL void
1072 n_err(char const *format, ...){
1073 va_list ap;
1074 NYD2_ENTER;
1076 va_start(ap, format);
1077 #ifdef HAVE_ERRORS
1078 if(n_psonce & n_PSO_INTERACTIVE)
1079 n_verr(format, ap);
1080 else
1081 #endif
1083 size_t len;
1084 bool_t doname, doflush;
1086 doflush = FAL0;
1087 while(*format == '\n'){
1088 doflush = TRU1;
1089 putc('\n', n_stderr);
1090 ++format;
1093 if((doname = doflush))
1094 a_aux_err_linelen = 0;
1096 if((len = strlen(format)) > 0){
1097 if(doname || a_aux_err_linelen == 0){
1098 char const *cp;
1100 if(*(cp = ok_vlook(log_prefix)) != '\0')
1101 fputs(cp, n_stderr);
1103 vfprintf(n_stderr, format, ap);
1105 /* C99 */{
1106 size_t i = len;
1108 if(format[--len] == '\n'){
1109 a_aux_err_linelen = (i -= ++len);
1110 break;
1112 ++a_aux_err_linelen;
1113 }while(len > 0);
1117 if(doflush)
1118 fflush(n_stderr);
1120 va_end(ap);
1121 NYD2_LEAVE;
1124 FL void
1125 n_verr(char const *format, va_list ap){
1126 #ifdef HAVE_ERRORS
1127 struct a_aux_err_node *enp;
1128 #endif
1129 bool_t doname;
1130 size_t len;
1131 NYD2_ENTER;
1133 doname = FAL0;
1135 while(*format == '\n'){
1136 putc('\n', n_stderr);
1137 doname = TRU1;
1138 ++format;
1141 if(doname){
1142 a_aux_err_linelen = 0;
1143 #ifdef HAVE_ERRORS
1144 if(n_psonce & n_PSO_INTERACTIVE){
1145 if((enp = a_aux_err_tail) != NULL &&
1146 (enp->ae_str.s_len > 0 &&
1147 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1148 n_string_push_c(&enp->ae_str, '\n');
1150 #endif
1153 if((len = strlen(format)) == 0)
1154 goto jleave;
1155 #ifdef HAVE_ERRORS
1156 n_pstate |= n_PS_ERRORS_PROMPT;
1157 #endif
1159 if(doname || a_aux_err_linelen == 0){
1160 char const *cp;
1162 if(*(cp = ok_vlook(log_prefix)) != '\0')
1163 fputs(cp, n_stderr);
1166 /* C99 */{
1167 size_t i = len;
1169 if(format[--len] == '\n'){
1170 a_aux_err_linelen = (i -= ++len);
1171 break;
1173 ++a_aux_err_linelen;
1174 }while(len > 0);
1177 #ifdef HAVE_ERRORS
1178 if(!(n_psonce & n_PSO_INTERACTIVE))
1179 #endif
1180 vfprintf(n_stderr, format, ap);
1181 #ifdef HAVE_ERRORS
1182 else{
1183 int imax, i;
1184 n_LCTAV(ERRORS_MAX > 3);
1186 /* Link it into the `errors' message ring */
1187 if((enp = a_aux_err_tail) == NULL){
1188 jcreat:
1189 enp = smalloc(sizeof *enp);
1190 enp->ae_next = NULL;
1191 n_string_creat(&enp->ae_str);
1192 if(a_aux_err_tail != NULL)
1193 a_aux_err_tail->ae_next = enp;
1194 else
1195 a_aux_err_head = enp;
1196 a_aux_err_tail = enp;
1197 ++a_aux_err_cnt;
1198 }else if(doname ||
1199 (enp->ae_str.s_len > 0 &&
1200 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1201 if(a_aux_err_cnt < ERRORS_MAX)
1202 goto jcreat;
1204 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1205 a_aux_err_tail->ae_next = enp;
1206 a_aux_err_tail = enp;
1207 enp->ae_next = NULL;
1208 n_string_trunc(&enp->ae_str, 0);
1211 # ifdef HAVE_N_VA_COPY
1212 imax = 64;
1213 # else
1214 imax = n_MIN(LINESIZE, 1024);
1215 # endif
1216 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1217 # ifdef HAVE_N_VA_COPY
1218 va_list vac;
1220 n_va_copy(vac, ap);
1221 # else
1222 # define vac ap
1223 # endif
1225 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1226 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1227 # ifdef HAVE_N_VA_COPY
1228 va_end(vac);
1229 # else
1230 # undef vac
1231 # endif
1232 if(i <= 0)
1233 goto jleave;
1234 if(UICMP(z, i, >=, imax)){
1235 # ifdef HAVE_N_VA_COPY
1236 /* XXX Check overflow for upcoming LEN+++i! */
1237 n_string_trunc(&enp->ae_str, len);
1238 continue;
1239 # else
1240 i = (int)strlen(&enp->ae_str.s_dat[len]);
1241 # endif
1243 break;
1245 n_string_trunc(&enp->ae_str, len + (size_t)i);
1247 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1249 #endif /* HAVE_ERRORS */
1251 jleave:
1252 fflush(n_stderr);
1253 NYD2_LEAVE;
1256 FL void
1257 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1258 va_list ap;
1259 NYD_X;
1261 va_start(ap, format);
1262 vfprintf(n_stderr, format, ap);
1263 va_end(ap);
1264 fflush(n_stderr);
1267 FL void
1268 n_perr(char const *msg, int errval){
1269 int e;
1270 char const *fmt;
1271 NYD2_ENTER;
1273 if(msg == NULL){
1274 fmt = "%s%s\n";
1275 msg = n_empty;
1276 }else
1277 fmt = "%s: %s\n";
1279 e = (errval == 0) ? n_err_no : errval;
1280 n_err(fmt, msg, n_err_to_doc(e));
1281 if(errval == 0)
1282 n_err_no = e;
1283 NYD2_LEAVE;
1286 FL void
1287 n_alert(char const *format, ...){
1288 va_list ap;
1289 NYD2_ENTER;
1291 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1293 va_start(ap, format);
1294 n_verr(format, ap);
1295 va_end(ap);
1297 n_err("\n");
1298 NYD2_LEAVE;
1301 FL void
1302 n_panic(char const *format, ...){
1303 va_list ap;
1304 NYD2_ENTER;
1306 if(a_aux_err_linelen > 0){
1307 putc('\n', n_stderr);
1308 a_aux_err_linelen = 0;
1310 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1312 va_start(ap, format);
1313 vfprintf(n_stderr, format, ap);
1314 va_end(ap);
1316 putc('\n', n_stderr);
1317 fflush(n_stderr);
1318 NYD2_LEAVE;
1319 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1322 #ifdef HAVE_ERRORS
1323 FL int
1324 c_errors(void *v){
1325 char **argv = v;
1326 struct a_aux_err_node *enp;
1327 NYD_ENTER;
1329 if(*argv == NULL)
1330 goto jlist;
1331 if(argv[1] != NULL)
1332 goto jerr;
1333 if(!asccasecmp(*argv, "show"))
1334 goto jlist;
1335 if(!asccasecmp(*argv, "clear"))
1336 goto jclear;
1337 jerr:
1338 fprintf(n_stderr,
1339 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1340 v = NULL;
1341 jleave:
1342 NYD_LEAVE;
1343 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1345 jlist:{
1346 FILE *fp;
1347 size_t i;
1349 if(a_aux_err_head == NULL){
1350 fprintf(n_stderr, _("The error ring is empty\n"));
1351 goto jleave;
1354 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1355 NULL){
1356 fprintf(n_stderr, _("tmpfile"));
1357 v = NULL;
1358 goto jleave;
1361 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1362 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1363 /* We don't know whether last string ended with NL; be simple XXX */
1364 putc('\n', fp);
1366 page_or_print(fp, 0);
1367 Fclose(fp);
1369 /* FALLTHRU */
1371 jclear:
1372 a_aux_err_tail = NULL;
1373 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1374 a_aux_err_linelen = 0;
1375 while((enp = a_aux_err_head) != NULL){
1376 a_aux_err_head = enp->ae_next;
1377 n_string_gut(&enp->ae_str);
1378 free(enp);
1380 goto jleave;
1382 #endif /* HAVE_ERRORS */
1384 FL char const *
1385 n_err_to_doc(si32_t eno){
1386 char const *rv;
1387 struct a_aux_err_map const *aemp;
1388 NYD2_ENTER;
1390 aemp = a_aux_err_map_from_no(eno);
1391 rv = &a_aux_err_docs[aemp->aem_docoff];
1392 NYD2_LEAVE;
1393 return rv;
1396 FL char const *
1397 n_err_to_name(si32_t eno){
1398 char const *rv;
1399 struct a_aux_err_map const *aemp;
1400 NYD2_ENTER;
1402 aemp = a_aux_err_map_from_no(eno);
1403 rv = &a_aux_err_names[aemp->aem_nameoff];
1404 NYD2_LEAVE;
1405 return rv;
1408 FL si32_t
1409 n_err_from_name(char const *name){
1410 struct a_aux_err_map const *aemp;
1411 ui32_t hash, i, j, x;
1412 si32_t rv;
1413 NYD2_ENTER;
1415 hash = n_torek_hash(name);
1417 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1418 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1419 break;
1421 aemp = &a_aux_err_map[x];
1422 if(aemp->aem_hash == hash &&
1423 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1424 rv = aemp->aem_err_no;
1425 goto jleave;
1428 if(++i == a_AUX_ERR_REV_PRIME){
1429 #ifdef a_AUX_ERR_REV_WRAPAROUND
1430 i = 0;
1431 #else
1432 break;
1433 #endif
1437 /* Have not found it. But wait, it could be that the user did, e.g.,
1438 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1439 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1440 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1441 ) == n_IDEC_STATE_CONSUMED){
1442 aemp = a_aux_err_map_from_no(rv);
1443 rv = aemp->aem_err_no;
1444 goto jleave;
1447 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1448 jleave:
1449 NYD2_LEAVE;
1450 return rv;
1453 #ifdef HAVE_REGEX
1454 FL char const *
1455 n_regex_err_to_doc(const regex_t *rep, int e){
1456 char *cp;
1457 size_t i;
1458 NYD2_ENTER;
1460 i = regerror(e, rep, NULL, 0) +1;
1461 cp = salloc(i);
1462 regerror(e, rep, cp, i);
1463 NYD2_LEAVE;
1464 return cp;
1466 #endif
1468 /* s-it-mode */