collect(): CID 1377023 !
[s-mailx.git] / auxlily.c
blobfd7b996c4f0272d36ddcb23ce254e8130176d227
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 #ifdef HAVE_NL_LANGINFO
57 # include <langinfo.h>
58 #endif
59 #ifdef HAVE_SETLOCALE
60 # include <locale.h>
61 #endif
63 #ifndef HAVE_POSIX_RANDOM
64 union rand_state{
65 struct rand_arc4{
66 ui8_t _dat[256];
67 ui8_t _i;
68 ui8_t _j;
69 ui8_t __pad[6];
70 } a;
71 ui8_t b8[sizeof(struct rand_arc4)];
72 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
74 #endif
76 #ifdef HAVE_ERRORS
77 struct a_aux_err_node{
78 struct a_aux_err_node *ae_next;
79 struct n_string ae_str;
81 #endif
83 struct a_aux_err_map{
84 ui32_t aem_hash; /* Hash of name */
85 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
86 ui32_t aem_docoff; /* Into a_aux_err docs[] */
87 si32_t aem_err_no; /* The OS error value for this one */
90 static ui8_t a_aux_idec_atoi[256] = {
91 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
92 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
93 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
94 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
95 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
96 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
97 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
98 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
99 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
100 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
101 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
102 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
103 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
110 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
111 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
112 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
113 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
114 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
115 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
119 #define a_X(X) ((ui64_t)-1 / (X))
120 static ui64_t const a_aux_idec_cutlimit[35] = {
121 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
122 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
123 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
124 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
125 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
127 #undef a_X
129 /* Include the constant make-errors.sh output */
130 #include "gen-errors.h"
132 /* And these things come from mk-config.h (config-time make-errors.sh output) */
133 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
134 #undef a_X
135 #define a_X(N,I) {N,I},
136 n__ERR_NUMBER_TO_MAPOFF
137 #undef a_X
140 #ifndef HAVE_POSIX_RANDOM
141 static union rand_state *a_aux_rand;
142 #endif
144 /* Error ring, for `errors' */
145 #ifdef HAVE_ERRORS
146 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
147 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
148 #endif
149 static size_t a_aux_err_linelen;
151 /* Our ARC4 random generator with its completely unacademical pseudo
152 * initialization (shall /dev/urandom fail) */
153 #ifndef HAVE_POSIX_RANDOM
154 static void a_aux_rand_init(void);
155 SINLINE ui8_t a_aux_rand_get8(void);
156 # ifndef HAVE_GETRANDOM
157 static ui32_t a_aux_rand_weak(ui32_t seed);
158 # endif
159 #endif
161 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
162 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
164 #ifndef HAVE_POSIX_RANDOM
165 static void
166 a_aux_rand_init(void){
167 # ifndef HAVE_GETRANDOM
168 # ifdef HAVE_CLOCK_GETTIME
169 struct timespec ts;
170 # else
171 struct timeval ts;
172 # endif
173 union {int fd; size_t i;} u;
174 ui32_t seed, rnd;
175 # endif
176 NYD2_ENTER;
178 a_aux_rand = n_alloc(sizeof *a_aux_rand);
180 # ifdef HAVE_GETRANDOM
181 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
182 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
183 "Buffer too large to be served without n_ERR_INTR error");
184 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
185 "Buffer too small to serve used array indices");
186 for(;;){
187 ssize_t gr;
189 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
190 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
191 a_aux_rand->a._dat[84]];
192 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
193 a_aux_rand->a._dat[42]];
194 /* ..but be on the safe side */
195 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
196 break;
197 n_msleep(250, FAL0);
200 # else
201 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
202 bool_t ok;
204 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
205 sizeof(a_aux_rand->a._dat)));
206 close(u.fd);
208 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
209 a_aux_rand->a._dat[84]];
210 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
211 a_aux_rand->a._dat[42]];
212 if(ok)
213 goto jleave;
216 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
217 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
218 ui32_t t, k;
220 # ifdef HAVE_CLOCK_GETTIME
221 clock_gettime(CLOCK_REALTIME, &ts);
222 t = (ui32_t)ts.tv_nsec;
223 # else
224 gettimeofday(&ts, NULL);
225 t = (ui32_t)ts.tv_usec;
226 # endif
227 if(rnd & 1)
228 t = (t >> 16) | (t << 16);
229 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
230 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
231 if(rnd == 7 || rnd == 17)
232 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
233 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
234 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
235 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
236 if((rnd & 3) == 3)
237 seed ^= n_prime_next(seed);
241 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
242 a_aux_rand_get8();
243 jleave:
244 # endif /* !HAVE_GETRANDOM */
245 NYD2_LEAVE;
248 SINLINE ui8_t
249 a_aux_rand_get8(void){
250 ui8_t si, sj;
252 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
253 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
254 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
255 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
256 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
259 # ifndef HAVE_GETRANDOM
260 static ui32_t
261 a_aux_rand_weak(ui32_t seed){
262 /* From "Random number generators: good ones are hard to find",
263 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
264 * October 1988, p. 1195.
265 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
266 ui32_t hi;
268 if(seed == 0)
269 seed = 123459876;
270 hi = seed / 127773;
271 seed %= 127773;
272 seed = (seed * 16807) - (hi * 2836);
273 if((si32_t)seed < 0)
274 seed += SI32_MAX;
275 return seed;
277 # endif /* HAVE_GETRANDOM */
278 #endif /* !HAVE_POSIX_RANDOM */
280 static struct a_aux_err_map const *
281 a_aux_err_map_from_no(si32_t eno){
282 si32_t ecmp;
283 size_t asz;
284 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
285 struct a_aux_err_map const *aemp;
286 NYD2_ENTER;
288 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
290 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
291 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
292 asz != 0; asz >>= 1){
293 tmp = &adat[asz >> 1];
294 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
295 aemp = &a_aux_err_map[(*tmp)[1]];
296 break;
298 if(ecmp > 0){
299 adat = &tmp[1];
300 --asz;
304 NYD2_LEAVE;
305 return aemp;
308 FL void
309 n_locale_init(void){
310 NYD2_ENTER;
312 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
314 #ifndef HAVE_SETLOCALE
315 n_mb_cur_max = 1;
316 #else
317 setlocale(LC_ALL, n_empty);
318 n_mb_cur_max = MB_CUR_MAX;
319 # ifdef HAVE_NL_LANGINFO
320 /* C99 */{
321 char const *cp;
323 /* TODO *ttycharset* may be set several times during startup unless
324 * TODO we gain a mechanism that -S fixates a setting during startup,
325 * TODO effectively turning later adjustments (during startup) in noop */
326 if((cp = nl_langinfo(CODESET)) != NULL)
327 ok_vset(ttycharset, cp);
329 # endif
331 # ifdef HAVE_C90AMEND1
332 if(n_mb_cur_max > 1){
333 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
334 n_psonce |= n_PSO_UNICODE;
335 # else
336 wchar_t wc;
337 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
338 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
339 n_psonce |= n_PSO_UNICODE;
340 /* Reset possibly messed up state; luckily this also gives us an
341 * indication whether the encoding has locking shift state sequences */
342 if(mbtowc(&wc, NULL, n_mb_cur_max))
343 n_psonce |= n_PSO_ENC_MBSTATE;
344 # endif
346 # endif
347 #endif
348 NYD2_LEAVE;
351 FL size_t
352 n_screensize(void){
353 char const *cp;
354 uiz_t rv;
355 NYD2_ENTER;
357 if((cp = ok_vlook(screen)) != NULL){
358 n_idec_uiz_cp(&rv, cp, 0, NULL);
359 if(rv == 0)
360 rv = n_scrnheight;
361 }else
362 rv = n_scrnheight;
364 if(rv > 2)
365 rv -= 2;
366 NYD2_LEAVE;
367 return rv;
370 FL char const *
371 n_pager_get(char const **env_addon){
372 char const *rv;
373 NYD_ENTER;
375 rv = ok_vlook(PAGER);
377 if(env_addon != NULL){
378 *env_addon = NULL;
379 /* Update the manual upon any changes:
380 * *colour-pager*, $PAGER */
381 if(strstr(rv, "less") != NULL){
382 if(getenv("LESS") == NULL)
383 *env_addon =
384 #ifdef HAVE_TERMCAP
385 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
386 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
387 #endif
388 "LESS=FRXi";
389 }else if(strstr(rv, "lv") != NULL){
390 if(getenv("LV") == NULL)
391 *env_addon = "LV=-c";
394 NYD_LEAVE;
395 return rv;
398 FL void
399 page_or_print(FILE *fp, size_t lines)
401 int c;
402 char const *cp;
403 NYD_ENTER;
405 fflush_rewind(fp);
407 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
408 size_t rows;
410 if(*cp == '\0')
411 rows = (size_t)n_scrnheight;
412 else
413 n_idec_uiz_cp(&rows, cp, 0, NULL);
415 if (rows > 0 && lines == 0) {
416 while ((c = getc(fp)) != EOF)
417 if (c == '\n' && ++lines >= rows)
418 break;
419 really_rewind(fp);
422 if (lines >= rows) {
423 char const *env_add[2], *pager;
425 pager = n_pager_get(&env_add[0]);
426 env_add[1] = NULL;
427 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
428 env_add, NULL);
429 goto jleave;
433 while ((c = getc(fp)) != EOF)
434 putc(c, n_stdout);
435 jleave:
436 NYD_LEAVE;
439 FL enum protocol
440 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
441 char const **adjusted_or_null)
443 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
444 char const *cp, *orig_name;
445 enum protocol rv = PROTO_UNKNOWN;
446 NYD_ENTER;
448 if(name[0] == '%' && name[1] == ':')
449 name += 2;
450 orig_name = name;
452 for (cp = name; *cp && *cp != ':'; cp++)
453 if (!alnumchar(*cp))
454 goto jfile;
456 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
457 if(!strncmp(name, "file", sizeof("file") -1) ||
458 !strncmp(name, "mbox", sizeof("mbox") -1))
459 rv = PROTO_FILE;
460 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
461 rv = PROTO_MAILDIR;
462 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
463 #ifdef HAVE_POP3
464 rv = PROTO_POP3;
465 #else
466 n_err(_("No POP3 support compiled in\n"));
467 #endif
468 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
469 #if defined HAVE_POP3 && defined HAVE_SSL
470 rv = PROTO_POP3;
471 #else
472 n_err(_("No POP3S support compiled in\n"));
473 #endif
475 else if(!strncmp(name, "imap", sizeof("imap") -1)){
476 #ifdef HAVE_IMAP
477 rv = PROTO_IMAP;
478 #else
479 n_err(_("No IMAP support compiled in\n"));
480 #endif
481 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
482 #if defined HAVE_IMAP && defined HAVE_SSL
483 rv = PROTO_IMAP;
484 #else
485 n_err(_("No IMAPS support compiled in\n"));
486 #endif
488 orig_name = &cp[3];
489 goto jleave;
492 jfile:
493 rv = PROTO_FILE;
495 if(check_stat || try_hooks){
496 struct n_file_type ft;
497 struct stat stb;
498 char *np;
499 size_t sz;
501 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
502 memcpy(np, name, sz + 1);
504 if(!stat(name, &stb)){
505 if(S_ISDIR(stb.st_mode) &&
506 (memcpy(&np[sz], "/tmp", 5),
507 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
508 (memcpy(&np[sz], "/new", 5),
509 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
510 (memcpy(&np[sz], "/cur", 5),
511 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
512 rv = PROTO_MAILDIR;
513 }else if(try_hooks && n_filetype_trial(&ft, name))
514 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
515 else if((cp = ok_vlook(newfolders)) != NULL &&
516 !asccasecmp(cp, "maildir"))
517 rv = PROTO_MAILDIR;
519 n_lofi_free(np);
521 jleave:
522 if(adjusted_or_null != NULL)
523 *adjusted_or_null = orig_name;
524 NYD_LEAVE;
525 return rv;
528 FL char *
529 n_c_to_hex_base16(char store[3], char c){
530 static char const itoa16[] = "0123456789ABCDEF";
531 NYD2_ENTER;
533 store[2] = '\0';
534 store[1] = itoa16[(ui8_t)c & 0x0F];
535 c = ((ui8_t)c >> 4) & 0x0F;
536 store[0] = itoa16[(ui8_t)c];
537 NYD2_LEAVE;
538 return store;
541 FL si32_t
542 n_c_from_hex_base16(char const hex[2]){
543 static ui8_t const atoi16[] = {
544 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
545 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
546 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
547 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
548 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
549 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
550 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
552 ui8_t i1, i2;
553 si32_t rv;
554 NYD2_ENTER;
556 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
557 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
558 goto jerr;
559 i1 = atoi16[i1];
560 i2 = atoi16[i2];
561 if ((i1 | i2) & 0xF0u)
562 goto jerr;
563 rv = i1;
564 rv <<= 4;
565 rv += i2;
566 jleave:
567 NYD2_LEAVE;
568 return rv;
569 jerr:
570 rv = -1;
571 goto jleave;
574 FL enum n_idec_state
575 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
576 enum n_idec_mode idm, char const **endptr_or_null){
577 /* XXX Brute simple and */
578 ui8_t currc;
579 ui64_t res, cut;
580 enum n_idec_state rv;
581 NYD_ENTER;
583 idm &= n__IDEC_MODE_MASK;
584 rv = n_IDEC_STATE_NONE | idm;
585 res = 0;
587 if(clen == UIZ_MAX){
588 if(*cbuf == '\0')
589 goto jeinval;
590 }else if(clen == 0)
591 goto jeinval;
593 /* Leading WS */
594 while(spacechar(*cbuf))
595 if(*++cbuf == '\0' || --clen == 0)
596 goto jeinval;
598 /* Check sign */
599 switch(*cbuf){
600 case '-':
601 rv |= n_IDEC_STATE_SEEN_MINUS;
602 /* FALLTHROUGH */
603 case '+':
604 if(*++cbuf == '\0' || --clen == 0)
605 goto jeinval;
606 break;
609 /* Base detection/skip */
610 if(*cbuf != '0'){
611 if(base == 0)
612 base = 10;
613 /* Character must be valid for base */
614 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
615 if(currc >= base)
616 goto jeinval;
617 }else{
618 /* 0 always valid as is, fallback base 10 */
619 if(*++cbuf == '\0' || --clen == 0)
620 goto jleave;
622 /* Base "detection" */
623 if(base == 0 || base == 2 || base == 16){
624 switch(*cbuf){
625 case 'x':
626 case 'X':
627 if((base & 2) == 0){
628 base = 0x10;
629 goto jprefix_skip;
631 break;
632 case 'b':
633 case 'B':
634 if((base & 16) == 0){
635 base = 2; /* 0b10 */
636 /* Char after prefix must be valid */
637 jprefix_skip:
638 if(*++cbuf == '\0' || --clen == 0)
639 goto jeinval;
641 /* Character must be valid for base, invalid otherwise */
642 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
643 if(currc >= base)
644 goto jeinval;
646 break;
647 default:
648 if(base == 0)
649 base = 010;
650 break;
654 /* Character must be valid for base, _EBASE otherwise */
655 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
656 if(currc >= base)
657 goto jebase;
660 for(cut = a_aux_idec_cutlimit[base - 2];;){
661 if(res >= cut){
662 if(res == cut){
663 res *= base;
664 if(res > UI64_MAX - currc)
665 goto jeover;
666 res += currc;
667 }else
668 goto jeover;
669 }else{
670 res *= base;
671 res += currc;
674 if(*++cbuf == '\0' || --clen == 0)
675 break;
677 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
678 if(currc >= base)
679 goto jebase;
682 jleave:
684 ui64_t uimask;
686 switch(rv & n__IDEC_MODE_LIMIT_MASK){
687 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
688 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
689 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
690 default: uimask = UI64_MAX; break;
692 if(rv & n_IDEC_MODE_SIGNED_TYPE)
693 uimask >>= 1;
695 if(res & ~uimask){
696 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
697 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
698 if(res > uimask + 1){
699 res = uimask << 1;
700 res &= ~uimask;
701 }else{
702 res = -res;
703 break;
705 }else
706 res = uimask;
707 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
708 rv |= n_IDEC_STATE_EOVERFLOW;
709 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
710 res = -res;
711 }while(0);
713 switch(rv & n__IDEC_MODE_LIMIT_MASK){
714 case n_IDEC_MODE_LIMIT_8BIT:
715 if(rv & n_IDEC_MODE_SIGNED_TYPE)
716 *(si8_t*)resp = (si8_t)res;
717 else
718 *(ui8_t*)resp = (ui8_t)res;
719 break;
720 case n_IDEC_MODE_LIMIT_16BIT:
721 if(rv & n_IDEC_MODE_SIGNED_TYPE)
722 *(si16_t*)resp = (si16_t)res;
723 else
724 *(ui16_t*)resp = (ui16_t)res;
725 break;
726 case n_IDEC_MODE_LIMIT_32BIT:
727 if(rv & n_IDEC_MODE_SIGNED_TYPE)
728 *(si32_t*)resp = (si32_t)res;
729 else
730 *(ui32_t*)resp = (ui32_t)res;
731 break;
732 default:
733 if(rv & n_IDEC_MODE_SIGNED_TYPE)
734 *(si64_t*)resp = (si64_t)res;
735 else
736 *(ui64_t*)resp = (ui64_t)res;
737 break;
740 if(endptr_or_null != NULL)
741 *endptr_or_null = cbuf;
742 if(*cbuf == '\0' || clen == 0)
743 rv |= n_IDEC_STATE_CONSUMED;
744 NYD_LEAVE;
745 return rv;
747 jeinval:
748 rv |= n_IDEC_STATE_EINVAL;
749 goto j_maxval;
750 jebase:
751 /* Not a base error for terminator and whitespace! */
752 if(*cbuf != '\0' && !spacechar(*cbuf))
753 rv |= n_IDEC_STATE_EBASE;
754 goto jleave;
756 jeover:
757 /* Overflow error: consume input until bad character or length out */
758 for(;;){
759 if(*++cbuf == '\0' || --clen == 0)
760 break;
761 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
762 if(currc >= base)
763 break;
766 rv |= n_IDEC_STATE_EOVERFLOW;
767 j_maxval:
768 if(rv & n_IDEC_MODE_SIGNED_TYPE)
769 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
770 : (ui64_t)SI64_MAX;
771 else
772 res = UI64_MAX;
773 rv &= ~n_IDEC_STATE_SEEN_MINUS;
774 goto jleave;
777 FL ui32_t
778 n_torek_hash(char const *name){
779 /* Chris Torek's hash */
780 char c;
781 ui32_t h;
782 NYD2_ENTER;
784 for(h = 0; (c = *name++) != '\0';)
785 h = (h * 33) + c;
786 NYD2_LEAVE;
787 return h;
790 FL ui32_t
791 n_torek_ihashn(char const *dat, size_t len){
792 /* See n_torek_hash() */
793 char c;
794 ui32_t h;
795 NYD2_ENTER;
797 if(len == UIZ_MAX)
798 for(h = 0; (c = *dat++) != '\0';)
799 h = (h * 33) + lowerconv(c);
800 else
801 for(h = 0; len > 0; --len){
802 c = *dat++;
803 h = (h * 33) + lowerconv(c);
805 NYD2_LEAVE;
806 return h;
809 FL ui32_t
810 n_prime_next(ui32_t n){
811 static ui32_t const primes[] = {
812 5, 11, 23, 47, 97, 157, 283,
813 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
814 131071, 262139, 524287, 1048573, 2097143, 4194301,
815 8388593, 16777213, 33554393, 67108859, 134217689,
816 268435399, 536870909, 1073741789, 2147483647
818 ui32_t i, mprime;
819 NYD2_ENTER;
821 i = (n < primes[n_NELEM(primes) / 4] ? 0
822 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
823 : n_NELEM(primes) / 2));
825 do if((mprime = primes[i]) > n)
826 break;
827 while(++i < n_NELEM(primes));
829 if(i == n_NELEM(primes) && mprime < n)
830 mprime = n;
831 NYD2_LEAVE;
832 return mprime;
835 FL char const *
836 n_getdeadletter(void){
837 char const *cp_base, *cp;
838 NYD_ENTER;
840 cp_base = NULL;
841 jredo:
842 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
843 if(cp == NULL || strlen(cp) >= PATH_MAX){
844 if(cp_base == NULL){
845 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
846 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
847 ok_vclear(DEAD);
848 goto jredo;
849 }else{
850 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
851 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
854 NYD_LEAVE;
855 return cp;
858 FL char *
859 n_nodename(bool_t mayoverride){
860 static char *sys_hostname, *hostname; /* XXX free-at-exit */
862 struct utsname ut;
863 char *hn;
864 #ifdef HAVE_SOCKETS
865 # ifdef HAVE_GETADDRINFO
866 struct addrinfo hints, *res;
867 # else
868 struct hostent *hent;
869 # endif
870 #endif
871 NYD2_ENTER;
873 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
875 }else if((hn = sys_hostname) == NULL){
876 uname(&ut);
877 hn = ut.nodename;
878 #ifdef HAVE_SOCKETS
879 # ifdef HAVE_GETADDRINFO
880 memset(&hints, 0, sizeof hints);
881 hints.ai_family = AF_UNSPEC;
882 hints.ai_flags = AI_CANONNAME;
883 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
884 if(res->ai_canonname != NULL){
885 size_t l;
887 l = strlen(res->ai_canonname) +1;
888 hn = n_lofi_alloc(l);
889 memcpy(hn, res->ai_canonname, l);
891 freeaddrinfo(res);
893 # else
894 hent = gethostbyname(hn);
895 if(hent != NULL)
896 hn = hent->h_name;
897 # endif
898 #endif
899 sys_hostname = sstrdup(hn);
900 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
901 if(hn != ut.nodename)
902 n_lofi_free(hn);
903 #endif
904 hn = sys_hostname;
907 if(hostname != NULL && hostname != sys_hostname)
908 n_free(hostname);
909 hostname = sstrdup(hn);
910 NYD2_LEAVE;
911 return hostname;
914 FL char *
915 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
916 struct str b64;
917 char *data, *cp;
918 size_t i;
919 NYD_ENTER;
921 #ifndef HAVE_POSIX_RANDOM
922 if(a_aux_rand == NULL)
923 a_aux_rand_init();
924 #endif
926 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
927 * with PAD stripped is still longer than what the user requests, easy way */
928 data = n_lofi_alloc(i = length + 3);
930 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
931 #ifndef HAVE_POSIX_RANDOM
932 while(i-- > 0)
933 data[i] = (char)a_aux_rand_get8();
934 #else
935 for(cp = data; i > 0;){
936 union {ui32_t i4; char c[4];} r;
937 size_t j;
939 r.i4 = (ui32_t)arc4random();
940 switch((j = i & 3)){
941 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
942 case 3: cp[2] = r.c[2]; /* FALLTHRU */
943 case 2: cp[1] = r.c[1]; /* FALLTHRU */
944 default: cp[0] = r.c[0]; break;
946 cp += j;
947 i -= j;
949 #endif
950 }else{
951 for(cp = data; i > 0;){
952 union {ui32_t i4; char c[4];} r;
953 size_t j;
955 r.i4 = ++*reprocnt_or_null;
956 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
957 char x;
959 x = r.c[0];
960 r.c[0] = r.c[3];
961 r.c[3] = x;
962 x = r.c[1];
963 r.c[1] = r.c[2];
964 r.c[2] = x;
966 switch((j = i & 3)){
967 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
968 case 3: cp[2] = r.c[2]; /* FALLTHRU */
969 case 2: cp[1] = r.c[1]; /* FALLTHRU */
970 default: cp[0] = r.c[0]; break;
972 cp += j;
973 i -= j;
977 assert(length + 3 < UIZ_MAX / 4);
978 b64_encode_buf(&b64, data, length + 3,
979 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
980 n_lofi_free(data);
982 assert(b64.l >= length);
983 b64.s[length] = '\0';
984 NYD_LEAVE;
985 return b64.s;
988 FL si8_t
989 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
991 si8_t rv;
992 NYD_ENTER;
994 assert(inlen == 0 || inbuf != NULL);
996 if (inlen == UIZ_MAX)
997 inlen = strlen(inbuf);
999 if (inlen == 0)
1000 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1001 else {
1002 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1003 !ascncasecmp(inbuf, "true", inlen) ||
1004 !ascncasecmp(inbuf, "yes", inlen) ||
1005 !ascncasecmp(inbuf, "on", inlen))
1006 rv = 1;
1007 else if ((inlen == 1 &&
1008 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1009 !ascncasecmp(inbuf, "false", inlen) ||
1010 !ascncasecmp(inbuf, "no", inlen) ||
1011 !ascncasecmp(inbuf, "off", inlen))
1012 rv = 0;
1013 else {
1014 ui64_t ib;
1016 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1017 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1018 ) != n_IDEC_STATE_CONSUMED)
1019 rv = -1;
1020 else
1021 rv = (ib != 0);
1024 NYD_LEAVE;
1025 return rv;
1028 FL si8_t
1029 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1031 si8_t rv;
1032 NYD_ENTER;
1034 assert(inlen == 0 || inbuf != NULL);
1036 if (inlen == UIZ_MAX)
1037 inlen = strlen(inbuf);
1039 if (inlen == 0)
1040 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1041 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1042 !ascncasecmp(inbuf, "ask-", 4) &&
1043 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1044 (n_psonce & n_PSO_INTERACTIVE))
1045 rv = getapproval(prompt, rv);
1046 NYD_LEAVE;
1047 return rv;
1050 FL bool_t
1051 n_is_all_or_aster(char const *name){
1052 bool_t rv;
1053 NYD_ENTER;
1055 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1056 NYD_LEAVE;
1057 return rv;
1060 FL struct n_timespec const *
1061 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1062 static struct n_timespec ts_now;
1063 NYD2_ENTER;
1065 if(n_psonce & n_PSO_REPRODUCIBLE){
1066 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1067 ts_now.ts_nsec = 0;
1068 }else if(force_update || ts_now.ts_sec == 0){
1069 #ifdef HAVE_CLOCK_GETTIME
1070 struct timespec ts;
1072 clock_gettime(CLOCK_REALTIME, &ts);
1073 ts_now.ts_sec = (si64_t)ts.tv_sec;
1074 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1075 #elif defined HAVE_GETTIMEOFDAY
1076 struct timeval tv;
1078 gettimeofday(&tv, NULL);
1079 ts_now.ts_sec = (si64_t)tv.tv_sec;
1080 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1081 #else
1082 ts_now.ts_sec = (si64_t)time(NULL);
1083 ts_now.ts_nsec = 0;
1084 #endif
1086 NYD2_LEAVE;
1087 return &ts_now;
1090 FL void
1091 time_current_update(struct time_current *tc, bool_t full_update)
1093 NYD_ENTER;
1094 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1095 if (full_update) {
1096 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1097 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1098 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1100 NYD_LEAVE;
1103 FL uiz_t
1104 n_msleep(uiz_t millis, bool_t ignint){
1105 uiz_t rv;
1106 NYD2_ENTER;
1108 #ifdef HAVE_NANOSLEEP
1109 /* C99 */{
1110 struct timespec ts, trem;
1111 int i;
1113 ts.tv_sec = millis / 1000;
1114 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1116 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1117 ts = trem;
1118 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1121 #elif defined HAVE_SLEEP
1122 if((millis /= 1000) == 0)
1123 millis = 1;
1124 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1125 millis = rv;
1126 #else
1127 # error Configuration should have detected a function for sleeping.
1128 #endif
1130 NYD2_LEAVE;
1131 return rv;
1134 FL void
1135 n_err(char const *format, ...){
1136 va_list ap;
1137 NYD2_ENTER;
1139 va_start(ap, format);
1140 #ifdef HAVE_ERRORS
1141 if(n_psonce & n_PSO_INTERACTIVE)
1142 n_verr(format, ap);
1143 else
1144 #endif
1146 size_t len;
1147 bool_t doname;
1149 doname = FAL0;
1151 while(*format == '\n'){
1152 doname = TRU1;
1153 putc('\n', n_stderr);
1154 ++format;
1157 if(doname)
1158 a_aux_err_linelen = 0;
1160 if((len = strlen(format)) > 0){
1161 if(doname || a_aux_err_linelen == 0){
1162 char const *cp;
1164 if(*(cp = ok_vlook(log_prefix)) != '\0')
1165 fputs(cp, n_stderr);
1167 vfprintf(n_stderr, format, ap);
1169 /* C99 */{
1170 size_t i = len;
1172 if(format[--len] == '\n'){
1173 a_aux_err_linelen = (i -= ++len);
1174 break;
1176 ++a_aux_err_linelen;
1177 }while(len > 0);
1181 fflush(n_stderr);
1183 va_end(ap);
1184 NYD2_LEAVE;
1187 FL void
1188 n_verr(char const *format, va_list ap){
1189 #ifdef HAVE_ERRORS
1190 struct a_aux_err_node *enp;
1191 #endif
1192 bool_t doname;
1193 size_t len;
1194 NYD2_ENTER;
1196 doname = FAL0;
1198 while(*format == '\n'){
1199 putc('\n', n_stderr);
1200 doname = TRU1;
1201 ++format;
1204 if(doname){
1205 a_aux_err_linelen = 0;
1206 #ifdef HAVE_ERRORS
1207 if(n_psonce & n_PSO_INTERACTIVE){
1208 if((enp = a_aux_err_tail) != NULL &&
1209 (enp->ae_str.s_len > 0 &&
1210 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1211 n_string_push_c(&enp->ae_str, '\n');
1213 #endif
1216 if((len = strlen(format)) == 0)
1217 goto jleave;
1218 #ifdef HAVE_ERRORS
1219 n_pstate |= n_PS_ERRORS_PROMPT;
1220 #endif
1222 if(doname || a_aux_err_linelen == 0){
1223 char const *cp;
1225 if(*(cp = ok_vlook(log_prefix)) != '\0')
1226 fputs(cp, n_stderr);
1229 /* C99 */{
1230 size_t i = len;
1232 if(format[--len] == '\n'){
1233 a_aux_err_linelen = (i -= ++len);
1234 break;
1236 ++a_aux_err_linelen;
1237 }while(len > 0);
1240 #ifdef HAVE_ERRORS
1241 if(!(n_psonce & n_PSO_INTERACTIVE))
1242 #endif
1243 vfprintf(n_stderr, format, ap);
1244 #ifdef HAVE_ERRORS
1245 else{
1246 int imax, i;
1247 n_LCTAV(ERRORS_MAX > 3);
1249 /* Link it into the `errors' message ring */
1250 if((enp = a_aux_err_tail) == NULL){
1251 jcreat:
1252 enp = smalloc(sizeof *enp);
1253 enp->ae_next = NULL;
1254 n_string_creat(&enp->ae_str);
1255 if(a_aux_err_tail != NULL)
1256 a_aux_err_tail->ae_next = enp;
1257 else
1258 a_aux_err_head = enp;
1259 a_aux_err_tail = enp;
1260 ++a_aux_err_cnt;
1261 }else if(doname ||
1262 (enp->ae_str.s_len > 0 &&
1263 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1264 if(a_aux_err_cnt < ERRORS_MAX)
1265 goto jcreat;
1267 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1268 a_aux_err_tail->ae_next = enp;
1269 a_aux_err_tail = enp;
1270 enp->ae_next = NULL;
1271 n_string_trunc(&enp->ae_str, 0);
1274 # ifdef HAVE_N_VA_COPY
1275 imax = 64;
1276 # else
1277 imax = n_MIN(LINESIZE, 1024);
1278 # endif
1279 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1280 # ifdef HAVE_N_VA_COPY
1281 va_list vac;
1283 n_va_copy(vac, ap);
1284 # else
1285 # define vac ap
1286 # endif
1288 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1289 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1290 # ifdef HAVE_N_VA_COPY
1291 va_end(vac);
1292 # else
1293 # undef vac
1294 # endif
1295 if(i <= 0)
1296 goto jleave;
1297 if(UICMP(z, i, >=, imax)){
1298 # ifdef HAVE_N_VA_COPY
1299 /* XXX Check overflow for upcoming LEN+++i! */
1300 n_string_trunc(&enp->ae_str, len);
1301 continue;
1302 # else
1303 i = (int)strlen(&enp->ae_str.s_dat[len]);
1304 # endif
1306 break;
1308 n_string_trunc(&enp->ae_str, len + (size_t)i);
1310 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1312 #endif /* HAVE_ERRORS */
1314 jleave:
1315 fflush(n_stderr);
1316 NYD2_LEAVE;
1319 FL void
1320 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1321 va_list ap;
1322 NYD_X;
1324 va_start(ap, format);
1325 vfprintf(n_stderr, format, ap);
1326 va_end(ap);
1327 fflush(n_stderr);
1330 FL void
1331 n_perr(char const *msg, int errval){
1332 int e;
1333 char const *fmt;
1334 NYD2_ENTER;
1336 if(msg == NULL){
1337 fmt = "%s%s\n";
1338 msg = n_empty;
1339 }else
1340 fmt = "%s: %s\n";
1342 e = (errval == 0) ? n_err_no : errval;
1343 n_err(fmt, msg, n_err_to_doc(e));
1344 if(errval == 0)
1345 n_err_no = e;
1346 NYD2_LEAVE;
1349 FL void
1350 n_alert(char const *format, ...){
1351 va_list ap;
1352 NYD2_ENTER;
1354 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1356 va_start(ap, format);
1357 n_verr(format, ap);
1358 va_end(ap);
1360 n_err("\n");
1361 NYD2_LEAVE;
1364 FL void
1365 n_panic(char const *format, ...){
1366 va_list ap;
1367 NYD2_ENTER;
1369 if(a_aux_err_linelen > 0){
1370 putc('\n', n_stderr);
1371 a_aux_err_linelen = 0;
1373 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1375 va_start(ap, format);
1376 vfprintf(n_stderr, format, ap);
1377 va_end(ap);
1379 putc('\n', n_stderr);
1380 fflush(n_stderr);
1381 NYD2_LEAVE;
1382 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1385 #ifdef HAVE_ERRORS
1386 FL int
1387 c_errors(void *v){
1388 char **argv = v;
1389 struct a_aux_err_node *enp;
1390 NYD_ENTER;
1392 if(*argv == NULL)
1393 goto jlist;
1394 if(argv[1] != NULL)
1395 goto jerr;
1396 if(!asccasecmp(*argv, "show"))
1397 goto jlist;
1398 if(!asccasecmp(*argv, "clear"))
1399 goto jclear;
1400 jerr:
1401 fprintf(n_stderr,
1402 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1403 v = NULL;
1404 jleave:
1405 NYD_LEAVE;
1406 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1408 jlist:{
1409 FILE *fp;
1410 size_t i;
1412 if(a_aux_err_head == NULL){
1413 fprintf(n_stderr, _("The error ring is empty\n"));
1414 goto jleave;
1417 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1418 NULL){
1419 fprintf(n_stderr, _("tmpfile"));
1420 v = NULL;
1421 goto jleave;
1424 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1425 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1426 /* We don't know whether last string ended with NL; be simple XXX */
1427 putc('\n', fp);
1429 page_or_print(fp, 0);
1430 Fclose(fp);
1432 /* FALLTHRU */
1434 jclear:
1435 a_aux_err_tail = NULL;
1436 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1437 a_aux_err_linelen = 0;
1438 while((enp = a_aux_err_head) != NULL){
1439 a_aux_err_head = enp->ae_next;
1440 n_string_gut(&enp->ae_str);
1441 free(enp);
1443 goto jleave;
1445 #endif /* HAVE_ERRORS */
1447 FL char const *
1448 n_err_to_doc(si32_t eno){
1449 char const *rv;
1450 struct a_aux_err_map const *aemp;
1451 NYD2_ENTER;
1453 aemp = a_aux_err_map_from_no(eno);
1454 rv = &a_aux_err_docs[aemp->aem_docoff];
1455 NYD2_LEAVE;
1456 return rv;
1459 FL char const *
1460 n_err_to_name(si32_t eno){
1461 char const *rv;
1462 struct a_aux_err_map const *aemp;
1463 NYD2_ENTER;
1465 aemp = a_aux_err_map_from_no(eno);
1466 rv = &a_aux_err_names[aemp->aem_nameoff];
1467 NYD2_LEAVE;
1468 return rv;
1471 FL si32_t
1472 n_err_from_name(char const *name){
1473 struct a_aux_err_map const *aemp;
1474 ui32_t hash, i, j, x;
1475 si32_t rv;
1476 NYD2_ENTER;
1478 hash = n_torek_hash(name);
1480 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1481 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1482 break;
1484 aemp = &a_aux_err_map[x];
1485 if(aemp->aem_hash == hash &&
1486 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1487 rv = aemp->aem_err_no;
1488 goto jleave;
1491 if(++i == a_AUX_ERR_REV_PRIME){
1492 #ifdef a_AUX_ERR_REV_WRAPAROUND
1493 i = 0;
1494 #else
1495 break;
1496 #endif
1500 /* Have not found it. But wait, it could be that the user did, e.g.,
1501 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1502 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1503 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1504 ) == n_IDEC_STATE_CONSUMED){
1505 aemp = a_aux_err_map_from_no(rv);
1506 rv = aemp->aem_err_no;
1507 goto jleave;
1510 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1511 jleave:
1512 NYD2_LEAVE;
1513 return rv;
1516 #ifdef HAVE_REGEX
1517 FL char const *
1518 n_regex_err_to_doc(const regex_t *rep, int e){
1519 char *cp;
1520 size_t i;
1521 NYD2_ENTER;
1523 i = regerror(e, rep, NULL, 0) +1;
1524 cp = salloc(i);
1525 regerror(e, rep, cp, i);
1526 NYD2_LEAVE;
1527 return cp;
1529 #endif
1531 /* s-it-mode */