n_getdeadletter(): CID 1376996 !
[s-mailx.git] / auxlily.c
blob0005f8f8ee7097009a86fc33321845386d6d3ab8
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;
838 bool_t bla;
839 NYD_ENTER;
841 bla = FAL0;
842 jredo:
843 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
844 if(cp == NULL || strlen(cp) >= PATH_MAX){
845 if(!bla){
846 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
847 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
848 ok_vclear(DEAD);
849 bla = TRU1;
850 goto jredo;
851 }else{
852 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
853 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
856 NYD_LEAVE;
857 return cp;
860 FL char *
861 n_nodename(bool_t mayoverride){
862 static char *sys_hostname, *hostname; /* XXX free-at-exit */
864 struct utsname ut;
865 char *hn;
866 #ifdef HAVE_SOCKETS
867 # ifdef HAVE_GETADDRINFO
868 struct addrinfo hints, *res;
869 # else
870 struct hostent *hent;
871 # endif
872 #endif
873 NYD2_ENTER;
875 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
877 }else if((hn = sys_hostname) == NULL){
878 uname(&ut);
879 hn = ut.nodename;
880 #ifdef HAVE_SOCKETS
881 # ifdef HAVE_GETADDRINFO
882 memset(&hints, 0, sizeof hints);
883 hints.ai_family = AF_UNSPEC;
884 hints.ai_flags = AI_CANONNAME;
885 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
886 if(res->ai_canonname != NULL){
887 size_t l;
889 l = strlen(res->ai_canonname) +1;
890 hn = n_lofi_alloc(l);
891 memcpy(hn, res->ai_canonname, l);
893 freeaddrinfo(res);
895 # else
896 hent = gethostbyname(hn);
897 if(hent != NULL)
898 hn = hent->h_name;
899 # endif
900 #endif
901 sys_hostname = sstrdup(hn);
902 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
903 if(hn != ut.nodename)
904 n_lofi_free(hn);
905 #endif
906 hn = sys_hostname;
909 if(hostname != NULL && hostname != sys_hostname)
910 n_free(hostname);
911 hostname = sstrdup(hn);
912 NYD2_LEAVE;
913 return hostname;
916 FL char *
917 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
918 struct str b64;
919 char *data, *cp;
920 size_t i;
921 NYD_ENTER;
923 #ifndef HAVE_POSIX_RANDOM
924 if(a_aux_rand == NULL)
925 a_aux_rand_init();
926 #endif
928 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
929 * with PAD stripped is still longer than what the user requests, easy way */
930 data = n_lofi_alloc(i = length + 3);
932 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
933 #ifndef HAVE_POSIX_RANDOM
934 while(i-- > 0)
935 data[i] = (char)a_aux_rand_get8();
936 #else
937 for(cp = data; i > 0;){
938 union {ui32_t i4; char c[4];} r;
939 size_t j;
941 r.i4 = (ui32_t)arc4random();
942 switch((j = i & 3)){
943 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
944 case 3: cp[2] = r.c[2]; /* FALLTHRU */
945 case 2: cp[1] = r.c[1]; /* FALLTHRU */
946 default: cp[0] = r.c[0]; break;
948 cp += j;
949 i -= j;
951 #endif
952 }else{
953 for(cp = data; i > 0;){
954 union {ui32_t i4; char c[4];} r;
955 size_t j;
957 r.i4 = ++*reprocnt_or_null;
958 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
959 char x;
961 x = r.c[0];
962 r.c[0] = r.c[3];
963 r.c[3] = x;
964 x = r.c[1];
965 r.c[1] = r.c[2];
966 r.c[2] = x;
968 switch((j = i & 3)){
969 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
970 case 3: cp[2] = r.c[2]; /* FALLTHRU */
971 case 2: cp[1] = r.c[1]; /* FALLTHRU */
972 default: cp[0] = r.c[0]; break;
974 cp += j;
975 i -= j;
979 assert(length + 3 < UIZ_MAX / 4);
980 b64_encode_buf(&b64, data, length + 3,
981 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
982 n_lofi_free(data);
984 assert(b64.l >= length);
985 b64.s[length] = '\0';
986 NYD_LEAVE;
987 return b64.s;
990 FL si8_t
991 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
993 si8_t rv;
994 NYD_ENTER;
996 assert(inlen == 0 || inbuf != NULL);
998 if (inlen == UIZ_MAX)
999 inlen = strlen(inbuf);
1001 if (inlen == 0)
1002 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1003 else {
1004 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1005 !ascncasecmp(inbuf, "true", inlen) ||
1006 !ascncasecmp(inbuf, "yes", inlen) ||
1007 !ascncasecmp(inbuf, "on", inlen))
1008 rv = 1;
1009 else if ((inlen == 1 &&
1010 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1011 !ascncasecmp(inbuf, "false", inlen) ||
1012 !ascncasecmp(inbuf, "no", inlen) ||
1013 !ascncasecmp(inbuf, "off", inlen))
1014 rv = 0;
1015 else {
1016 ui64_t ib;
1018 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1019 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1020 ) != n_IDEC_STATE_CONSUMED)
1021 rv = -1;
1022 else
1023 rv = (ib != 0);
1026 NYD_LEAVE;
1027 return rv;
1030 FL si8_t
1031 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1033 si8_t rv;
1034 NYD_ENTER;
1036 assert(inlen == 0 || inbuf != NULL);
1038 if (inlen == UIZ_MAX)
1039 inlen = strlen(inbuf);
1041 if (inlen == 0)
1042 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1043 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1044 !ascncasecmp(inbuf, "ask-", 4) &&
1045 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1046 (n_psonce & n_PSO_INTERACTIVE))
1047 rv = getapproval(prompt, rv);
1048 NYD_LEAVE;
1049 return rv;
1052 FL bool_t
1053 n_is_all_or_aster(char const *name){
1054 bool_t rv;
1055 NYD_ENTER;
1057 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1058 NYD_LEAVE;
1059 return rv;
1062 FL struct n_timespec const *
1063 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1064 static struct n_timespec ts_now;
1065 NYD2_ENTER;
1067 if(n_psonce & n_PSO_REPRODUCIBLE){
1068 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1069 ts_now.ts_nsec = 0;
1070 }else if(force_update || ts_now.ts_sec == 0){
1071 #ifdef HAVE_CLOCK_GETTIME
1072 struct timespec ts;
1074 clock_gettime(CLOCK_REALTIME, &ts);
1075 ts_now.ts_sec = (si64_t)ts.tv_sec;
1076 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1077 #elif defined HAVE_GETTIMEOFDAY
1078 struct timeval tv;
1080 gettimeofday(&tv, NULL);
1081 ts_now.ts_sec = (si64_t)tv.tv_sec;
1082 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1083 #else
1084 ts_now.ts_sec = (si64_t)time(NULL);
1085 ts_now.ts_nsec = 0;
1086 #endif
1088 NYD2_LEAVE;
1089 return &ts_now;
1092 FL void
1093 time_current_update(struct time_current *tc, bool_t full_update)
1095 NYD_ENTER;
1096 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1097 if (full_update) {
1098 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1099 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1100 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1102 NYD_LEAVE;
1105 FL uiz_t
1106 n_msleep(uiz_t millis, bool_t ignint){
1107 uiz_t rv;
1108 NYD2_ENTER;
1110 #ifdef HAVE_NANOSLEEP
1111 /* C99 */{
1112 struct timespec ts, trem;
1113 int i;
1115 ts.tv_sec = millis / 1000;
1116 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1118 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1119 ts = trem;
1120 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1123 #elif defined HAVE_SLEEP
1124 if((millis /= 1000) == 0)
1125 millis = 1;
1126 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1127 millis = rv;
1128 #else
1129 # error Configuration should have detected a function for sleeping.
1130 #endif
1132 NYD2_LEAVE;
1133 return rv;
1136 FL void
1137 n_err(char const *format, ...){
1138 va_list ap;
1139 NYD2_ENTER;
1141 va_start(ap, format);
1142 #ifdef HAVE_ERRORS
1143 if(n_psonce & n_PSO_INTERACTIVE)
1144 n_verr(format, ap);
1145 else
1146 #endif
1148 size_t len;
1149 bool_t doname;
1151 doname = FAL0;
1153 while(*format == '\n'){
1154 doname = TRU1;
1155 putc('\n', n_stderr);
1156 ++format;
1159 if(doname)
1160 a_aux_err_linelen = 0;
1162 if((len = strlen(format)) > 0){
1163 if(doname || a_aux_err_linelen == 0){
1164 char const *cp;
1166 if(*(cp = ok_vlook(log_prefix)) != '\0')
1167 fputs(cp, n_stderr);
1169 vfprintf(n_stderr, format, ap);
1171 /* C99 */{
1172 size_t i = len;
1174 if(format[--len] == '\n'){
1175 a_aux_err_linelen = (i -= ++len);
1176 break;
1178 ++a_aux_err_linelen;
1179 }while(len > 0);
1183 fflush(n_stderr);
1185 va_end(ap);
1186 NYD2_LEAVE;
1189 FL void
1190 n_verr(char const *format, va_list ap){
1191 #ifdef HAVE_ERRORS
1192 struct a_aux_err_node *enp;
1193 #endif
1194 bool_t doname;
1195 size_t len;
1196 NYD2_ENTER;
1198 doname = FAL0;
1200 while(*format == '\n'){
1201 putc('\n', n_stderr);
1202 doname = TRU1;
1203 ++format;
1206 if(doname){
1207 a_aux_err_linelen = 0;
1208 #ifdef HAVE_ERRORS
1209 if(n_psonce & n_PSO_INTERACTIVE){
1210 if((enp = a_aux_err_tail) != NULL &&
1211 (enp->ae_str.s_len > 0 &&
1212 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1213 n_string_push_c(&enp->ae_str, '\n');
1215 #endif
1218 if((len = strlen(format)) == 0)
1219 goto jleave;
1220 #ifdef HAVE_ERRORS
1221 n_pstate |= n_PS_ERRORS_PROMPT;
1222 #endif
1224 if(doname || a_aux_err_linelen == 0){
1225 char const *cp;
1227 if(*(cp = ok_vlook(log_prefix)) != '\0')
1228 fputs(cp, n_stderr);
1231 /* C99 */{
1232 size_t i = len;
1234 if(format[--len] == '\n'){
1235 a_aux_err_linelen = (i -= ++len);
1236 break;
1238 ++a_aux_err_linelen;
1239 }while(len > 0);
1242 #ifdef HAVE_ERRORS
1243 if(!(n_psonce & n_PSO_INTERACTIVE))
1244 #endif
1245 vfprintf(n_stderr, format, ap);
1246 #ifdef HAVE_ERRORS
1247 else{
1248 int imax, i;
1249 n_LCTAV(ERRORS_MAX > 3);
1251 /* Link it into the `errors' message ring */
1252 if((enp = a_aux_err_tail) == NULL){
1253 jcreat:
1254 enp = smalloc(sizeof *enp);
1255 enp->ae_next = NULL;
1256 n_string_creat(&enp->ae_str);
1257 if(a_aux_err_tail != NULL)
1258 a_aux_err_tail->ae_next = enp;
1259 else
1260 a_aux_err_head = enp;
1261 a_aux_err_tail = enp;
1262 ++a_aux_err_cnt;
1263 }else if(doname ||
1264 (enp->ae_str.s_len > 0 &&
1265 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1266 if(a_aux_err_cnt < ERRORS_MAX)
1267 goto jcreat;
1269 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1270 a_aux_err_tail->ae_next = enp;
1271 a_aux_err_tail = enp;
1272 enp->ae_next = NULL;
1273 n_string_trunc(&enp->ae_str, 0);
1276 # ifdef HAVE_N_VA_COPY
1277 imax = 64;
1278 # else
1279 imax = n_MIN(LINESIZE, 1024);
1280 # endif
1281 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1282 # ifdef HAVE_N_VA_COPY
1283 va_list vac;
1285 n_va_copy(vac, ap);
1286 # else
1287 # define vac ap
1288 # endif
1290 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1291 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1292 # ifdef HAVE_N_VA_COPY
1293 va_end(vac);
1294 # else
1295 # undef vac
1296 # endif
1297 if(i <= 0)
1298 goto jleave;
1299 if(UICMP(z, i, >=, imax)){
1300 # ifdef HAVE_N_VA_COPY
1301 /* XXX Check overflow for upcoming LEN+++i! */
1302 n_string_trunc(&enp->ae_str, len);
1303 continue;
1304 # else
1305 i = (int)strlen(&enp->ae_str.s_dat[len]);
1306 # endif
1308 break;
1310 n_string_trunc(&enp->ae_str, len + (size_t)i);
1312 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1314 #endif /* HAVE_ERRORS */
1316 jleave:
1317 fflush(n_stderr);
1318 NYD2_LEAVE;
1321 FL void
1322 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1323 va_list ap;
1324 NYD_X;
1326 va_start(ap, format);
1327 vfprintf(n_stderr, format, ap);
1328 va_end(ap);
1329 fflush(n_stderr);
1332 FL void
1333 n_perr(char const *msg, int errval){
1334 int e;
1335 char const *fmt;
1336 NYD2_ENTER;
1338 if(msg == NULL){
1339 fmt = "%s%s\n";
1340 msg = n_empty;
1341 }else
1342 fmt = "%s: %s\n";
1344 e = (errval == 0) ? n_err_no : errval;
1345 n_err(fmt, msg, n_err_to_doc(e));
1346 if(errval == 0)
1347 n_err_no = e;
1348 NYD2_LEAVE;
1351 FL void
1352 n_alert(char const *format, ...){
1353 va_list ap;
1354 NYD2_ENTER;
1356 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1358 va_start(ap, format);
1359 n_verr(format, ap);
1360 va_end(ap);
1362 n_err("\n");
1363 NYD2_LEAVE;
1366 FL void
1367 n_panic(char const *format, ...){
1368 va_list ap;
1369 NYD2_ENTER;
1371 if(a_aux_err_linelen > 0){
1372 putc('\n', n_stderr);
1373 a_aux_err_linelen = 0;
1375 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1377 va_start(ap, format);
1378 vfprintf(n_stderr, format, ap);
1379 va_end(ap);
1381 putc('\n', n_stderr);
1382 fflush(n_stderr);
1383 NYD2_LEAVE;
1384 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1387 #ifdef HAVE_ERRORS
1388 FL int
1389 c_errors(void *v){
1390 char **argv = v;
1391 struct a_aux_err_node *enp;
1392 NYD_ENTER;
1394 if(*argv == NULL)
1395 goto jlist;
1396 if(argv[1] != NULL)
1397 goto jerr;
1398 if(!asccasecmp(*argv, "show"))
1399 goto jlist;
1400 if(!asccasecmp(*argv, "clear"))
1401 goto jclear;
1402 jerr:
1403 fprintf(n_stderr,
1404 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1405 v = NULL;
1406 jleave:
1407 NYD_LEAVE;
1408 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1410 jlist:{
1411 FILE *fp;
1412 size_t i;
1414 if(a_aux_err_head == NULL){
1415 fprintf(n_stderr, _("The error ring is empty\n"));
1416 goto jleave;
1419 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1420 NULL){
1421 fprintf(n_stderr, _("tmpfile"));
1422 v = NULL;
1423 goto jleave;
1426 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1427 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1428 /* We don't know whether last string ended with NL; be simple XXX */
1429 putc('\n', fp);
1431 page_or_print(fp, 0);
1432 Fclose(fp);
1434 /* FALLTHRU */
1436 jclear:
1437 a_aux_err_tail = NULL;
1438 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1439 a_aux_err_linelen = 0;
1440 while((enp = a_aux_err_head) != NULL){
1441 a_aux_err_head = enp->ae_next;
1442 n_string_gut(&enp->ae_str);
1443 free(enp);
1445 goto jleave;
1447 #endif /* HAVE_ERRORS */
1449 FL char const *
1450 n_err_to_doc(si32_t eno){
1451 char const *rv;
1452 struct a_aux_err_map const *aemp;
1453 NYD2_ENTER;
1455 aemp = a_aux_err_map_from_no(eno);
1456 rv = &a_aux_err_docs[aemp->aem_docoff];
1457 NYD2_LEAVE;
1458 return rv;
1461 FL char const *
1462 n_err_to_name(si32_t eno){
1463 char const *rv;
1464 struct a_aux_err_map const *aemp;
1465 NYD2_ENTER;
1467 aemp = a_aux_err_map_from_no(eno);
1468 rv = &a_aux_err_names[aemp->aem_nameoff];
1469 NYD2_LEAVE;
1470 return rv;
1473 FL si32_t
1474 n_err_from_name(char const *name){
1475 struct a_aux_err_map const *aemp;
1476 ui32_t hash, i, j, x;
1477 si32_t rv;
1478 NYD2_ENTER;
1480 hash = n_torek_hash(name);
1482 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1483 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1484 break;
1486 aemp = &a_aux_err_map[x];
1487 if(aemp->aem_hash == hash &&
1488 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1489 rv = aemp->aem_err_no;
1490 goto jleave;
1493 if(++i == a_AUX_ERR_REV_PRIME){
1494 #ifdef a_AUX_ERR_REV_WRAPAROUND
1495 i = 0;
1496 #else
1497 break;
1498 #endif
1502 /* Have not found it. But wait, it could be that the user did, e.g.,
1503 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1504 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1505 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1506 ) == n_IDEC_STATE_CONSUMED){
1507 aemp = a_aux_err_map_from_no(rv);
1508 rv = aemp->aem_err_no;
1509 goto jleave;
1512 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1513 jleave:
1514 NYD2_LEAVE;
1515 return rv;
1518 #ifdef HAVE_REGEX
1519 FL char const *
1520 n_regex_err_to_doc(const regex_t *rep, int e){
1521 char *cp;
1522 size_t i;
1523 NYD2_ENTER;
1525 i = regerror(e, rep, NULL, 0) +1;
1526 cp = salloc(i);
1527 regerror(e, rep, cp, i);
1528 NYD2_LEAVE;
1529 return cp;
1531 #endif
1533 /* s-it-mode */