MLE: jump-away safety also for colours (SIGH!!!)
[s-mailx.git] / auxlily.c
blob13a9948ad91460186293331be29b20d769608737
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 orig_name = &cp[3];
476 goto jleave;
479 jfile:
480 rv = PROTO_FILE;
482 if(check_stat || try_hooks){
483 struct n_file_type ft;
484 struct stat stb;
485 char *np;
486 size_t sz;
488 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
489 memcpy(np, name, sz + 1);
491 if(!stat(name, &stb)){
492 if(S_ISDIR(stb.st_mode) &&
493 (memcpy(&np[sz], "/tmp", 5),
494 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
495 (memcpy(&np[sz], "/new", 5),
496 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
497 (memcpy(&np[sz], "/cur", 5),
498 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
499 rv = PROTO_MAILDIR;
500 }else if(try_hooks && n_filetype_trial(&ft, name))
501 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
502 else if((cp = ok_vlook(newfolders)) != NULL &&
503 !asccasecmp(cp, "maildir"))
504 rv = PROTO_MAILDIR;
506 n_lofi_free(np);
508 jleave:
509 if(adjusted_or_null != NULL)
510 *adjusted_or_null = orig_name;
511 NYD_LEAVE;
512 return rv;
515 FL char *
516 n_c_to_hex_base16(char store[3], char c){
517 static char const itoa16[] = "0123456789ABCDEF";
518 NYD2_ENTER;
520 store[2] = '\0';
521 store[1] = itoa16[(ui8_t)c & 0x0F];
522 c = ((ui8_t)c >> 4) & 0x0F;
523 store[0] = itoa16[(ui8_t)c];
524 NYD2_LEAVE;
525 return store;
528 FL si32_t
529 n_c_from_hex_base16(char const hex[2]){
530 static ui8_t const atoi16[] = {
531 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
532 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
533 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
534 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
535 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
536 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
537 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
539 ui8_t i1, i2;
540 si32_t rv;
541 NYD2_ENTER;
543 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
544 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
545 goto jerr;
546 i1 = atoi16[i1];
547 i2 = atoi16[i2];
548 if ((i1 | i2) & 0xF0u)
549 goto jerr;
550 rv = i1;
551 rv <<= 4;
552 rv += i2;
553 jleave:
554 NYD2_LEAVE;
555 return rv;
556 jerr:
557 rv = -1;
558 goto jleave;
561 FL enum n_idec_state
562 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
563 enum n_idec_mode idm, char const **endptr_or_null){
564 /* XXX Brute simple and */
565 ui8_t currc;
566 ui64_t res, cut;
567 enum n_idec_state rv;
568 NYD_ENTER;
570 idm &= n__IDEC_MODE_MASK;
571 rv = n_IDEC_STATE_NONE | idm;
572 res = 0;
574 if(clen == UIZ_MAX){
575 if(*cbuf == '\0')
576 goto jeinval;
577 }else if(clen == 0)
578 goto jeinval;
580 /* Leading WS */
581 while(spacechar(*cbuf))
582 if(*++cbuf == '\0' || --clen == 0)
583 goto jeinval;
585 /* Check sign */
586 switch(*cbuf){
587 case '-':
588 rv |= n_IDEC_STATE_SEEN_MINUS;
589 /* FALLTHROUGH */
590 case '+':
591 if(*++cbuf == '\0' || --clen == 0)
592 goto jeinval;
593 break;
596 /* Base detection/skip */
597 if(*cbuf != '0'){
598 if(base == 0)
599 base = 10;
600 /* Character must be valid for base */
601 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
602 if(currc >= base)
603 goto jeinval;
604 }else{
605 /* 0 always valid as is, fallback base 10 */
606 if(*++cbuf == '\0' || --clen == 0)
607 goto jleave;
609 /* Base "detection" */
610 if(base == 0 || base == 2 || base == 16){
611 switch(*cbuf){
612 case 'x':
613 case 'X':
614 if((base & 2) == 0){
615 base = 0x10;
616 goto jprefix_skip;
618 break;
619 case 'b':
620 case 'B':
621 if((base & 16) == 0){
622 base = 2; /* 0b10 */
623 /* Char after prefix must be valid */
624 jprefix_skip:
625 if(*++cbuf == '\0' || --clen == 0)
626 goto jeinval;
628 /* Character must be valid for base, invalid otherwise */
629 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
630 if(currc >= base)
631 goto jeinval;
633 break;
634 default:
635 if(base == 0)
636 base = 010;
637 break;
641 /* Character must be valid for base, _EBASE otherwise */
642 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
643 if(currc >= base)
644 goto jebase;
647 for(cut = a_aux_idec_cutlimit[base - 2];;){
648 if(res >= cut){
649 if(res == cut){
650 res *= base;
651 if(res > UI64_MAX - currc)
652 goto jeover;
653 res += currc;
654 }else
655 goto jeover;
656 }else{
657 res *= base;
658 res += currc;
661 if(*++cbuf == '\0' || --clen == 0)
662 break;
664 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
665 if(currc >= base)
666 goto jebase;
669 jleave:
671 ui64_t uimask;
673 switch(rv & n__IDEC_MODE_LIMIT_MASK){
674 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
675 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
676 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
677 default: uimask = UI64_MAX; break;
679 if(rv & n_IDEC_MODE_SIGNED_TYPE)
680 uimask >>= 1;
682 if(res & ~uimask){
683 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
684 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
685 if(res > uimask + 1){
686 res = uimask << 1;
687 res &= ~uimask;
688 }else{
689 res = -res;
690 break;
692 }else
693 res = uimask;
694 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
695 rv |= n_IDEC_STATE_EOVERFLOW;
696 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
697 res = -res;
698 }while(0);
700 switch(rv & n__IDEC_MODE_LIMIT_MASK){
701 case n_IDEC_MODE_LIMIT_8BIT:
702 if(rv & n_IDEC_MODE_SIGNED_TYPE)
703 *(si8_t*)resp = (si8_t)res;
704 else
705 *(ui8_t*)resp = (ui8_t)res;
706 break;
707 case n_IDEC_MODE_LIMIT_16BIT:
708 if(rv & n_IDEC_MODE_SIGNED_TYPE)
709 *(si16_t*)resp = (si16_t)res;
710 else
711 *(ui16_t*)resp = (ui16_t)res;
712 break;
713 case n_IDEC_MODE_LIMIT_32BIT:
714 if(rv & n_IDEC_MODE_SIGNED_TYPE)
715 *(si32_t*)resp = (si32_t)res;
716 else
717 *(ui32_t*)resp = (ui32_t)res;
718 break;
719 default:
720 if(rv & n_IDEC_MODE_SIGNED_TYPE)
721 *(si64_t*)resp = (si64_t)res;
722 else
723 *(ui64_t*)resp = (ui64_t)res;
724 break;
727 if(endptr_or_null != NULL)
728 *endptr_or_null = cbuf;
729 if(*cbuf == '\0' || clen == 0)
730 rv |= n_IDEC_STATE_CONSUMED;
731 NYD_LEAVE;
732 return rv;
734 jeinval:
735 rv |= n_IDEC_STATE_EINVAL;
736 goto j_maxval;
737 jebase:
738 /* Not a base error for terminator and whitespace! */
739 if(*cbuf != '\0' && !spacechar(*cbuf))
740 rv |= n_IDEC_STATE_EBASE;
741 goto jleave;
743 jeover:
744 /* Overflow error: consume input until bad character or length out */
745 for(;;){
746 if(*++cbuf == '\0' || --clen == 0)
747 break;
748 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
749 if(currc >= base)
750 break;
753 rv |= n_IDEC_STATE_EOVERFLOW;
754 j_maxval:
755 if(rv & n_IDEC_MODE_SIGNED_TYPE)
756 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
757 : (ui64_t)SI64_MAX;
758 else
759 res = UI64_MAX;
760 rv &= ~n_IDEC_STATE_SEEN_MINUS;
761 goto jleave;
764 FL ui32_t
765 n_torek_hash(char const *name){
766 /* Chris Torek's hash */
767 char c;
768 ui32_t h;
769 NYD2_ENTER;
771 for(h = 0; (c = *name++) != '\0';)
772 h = (h * 33) + c;
773 NYD2_LEAVE;
774 return h;
777 FL ui32_t
778 n_torek_ihashn(char const *dat, size_t len){
779 /* See n_torek_hash() */
780 char c;
781 ui32_t h;
782 NYD2_ENTER;
784 if(len == UIZ_MAX)
785 for(h = 0; (c = *dat++) != '\0';)
786 h = (h * 33) + lowerconv(c);
787 else
788 for(h = 0; len > 0; --len){
789 c = *dat++;
790 h = (h * 33) + lowerconv(c);
792 NYD2_LEAVE;
793 return h;
796 FL ui32_t
797 n_prime_next(ui32_t n){
798 static ui32_t const primes[] = {
799 5, 11, 23, 47, 97, 157, 283,
800 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
801 131071, 262139, 524287, 1048573, 2097143, 4194301,
802 8388593, 16777213, 33554393, 67108859, 134217689,
803 268435399, 536870909, 1073741789, 2147483647
805 ui32_t i, mprime;
806 NYD2_ENTER;
808 i = (n < primes[n_NELEM(primes) / 4] ? 0
809 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
810 : n_NELEM(primes) / 2));
812 do if((mprime = primes[i]) > n)
813 break;
814 while(++i < n_NELEM(primes));
816 if(i == n_NELEM(primes) && mprime < n)
817 mprime = n;
818 NYD2_LEAVE;
819 return mprime;
822 FL char const *
823 n_getdeadletter(void){
824 char const *cp_base, *cp;
825 NYD_ENTER;
827 cp_base = NULL;
828 jredo:
829 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
830 if(cp == NULL || strlen(cp) >= PATH_MAX){
831 if(cp_base == NULL){
832 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
833 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
834 ok_vclear(DEAD);
835 goto jredo;
836 }else{
837 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
838 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
841 NYD_LEAVE;
842 return cp;
845 FL char *
846 n_nodename(bool_t mayoverride){
847 static char *sys_hostname, *hostname; /* XXX free-at-exit */
849 struct utsname ut;
850 char *hn;
851 #ifdef HAVE_SOCKETS
852 # ifdef HAVE_GETADDRINFO
853 struct addrinfo hints, *res;
854 # else
855 struct hostent *hent;
856 # endif
857 #endif
858 NYD2_ENTER;
860 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
862 }else if((hn = sys_hostname) == NULL){
863 uname(&ut);
864 hn = ut.nodename;
865 #ifdef HAVE_SOCKETS
866 # ifdef HAVE_GETADDRINFO
867 memset(&hints, 0, sizeof hints);
868 hints.ai_family = AF_UNSPEC;
869 hints.ai_flags = AI_CANONNAME;
870 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
871 if(res->ai_canonname != NULL){
872 size_t l;
874 l = strlen(res->ai_canonname) +1;
875 hn = n_lofi_alloc(l);
876 memcpy(hn, res->ai_canonname, l);
878 freeaddrinfo(res);
880 # else
881 hent = gethostbyname(hn);
882 if(hent != NULL)
883 hn = hent->h_name;
884 # endif
885 #endif
886 sys_hostname = sstrdup(hn);
887 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
888 if(hn != ut.nodename)
889 n_lofi_free(hn);
890 #endif
891 hn = sys_hostname;
894 if(hostname != NULL && hostname != sys_hostname)
895 n_free(hostname);
896 hostname = sstrdup(hn);
897 NYD2_LEAVE;
898 return hostname;
901 FL char *
902 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
903 struct str b64;
904 char *data, *cp;
905 size_t i;
906 NYD_ENTER;
908 #ifndef HAVE_POSIX_RANDOM
909 if(a_aux_rand == NULL)
910 a_aux_rand_init();
911 #endif
913 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
914 * with PAD stripped is still longer than what the user requests, easy way */
915 data = n_lofi_alloc(i = length + 3);
917 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
918 #ifndef HAVE_POSIX_RANDOM
919 while(i-- > 0)
920 data[i] = (char)a_aux_rand_get8();
921 #else
922 for(cp = data; i > 0;){
923 union {ui32_t i4; char c[4];} r;
924 size_t j;
926 r.i4 = (ui32_t)arc4random();
927 switch((j = i & 3)){
928 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
929 case 3: cp[2] = r.c[2]; /* FALLTHRU */
930 case 2: cp[1] = r.c[1]; /* FALLTHRU */
931 default: cp[0] = r.c[0]; break;
933 cp += j;
934 i -= j;
936 #endif
937 }else{
938 for(cp = data; i > 0;){
939 union {ui32_t i4; char c[4];} r;
940 size_t j;
942 r.i4 = ++*reprocnt_or_null;
943 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
944 char x;
946 x = r.c[0];
947 r.c[0] = r.c[3];
948 r.c[3] = x;
949 x = r.c[1];
950 r.c[1] = r.c[2];
951 r.c[2] = x;
953 switch((j = i & 3)){
954 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
955 case 3: cp[2] = r.c[2]; /* FALLTHRU */
956 case 2: cp[1] = r.c[1]; /* FALLTHRU */
957 default: cp[0] = r.c[0]; break;
959 cp += j;
960 i -= j;
964 assert(length + 3 < UIZ_MAX / 4);
965 b64_encode_buf(&b64, data, length + 3,
966 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
967 n_lofi_free(data);
969 assert(b64.l >= length);
970 b64.s[length] = '\0';
971 NYD_LEAVE;
972 return b64.s;
975 FL si8_t
976 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
978 si8_t rv;
979 NYD_ENTER;
981 assert(inlen == 0 || inbuf != NULL);
983 if (inlen == UIZ_MAX)
984 inlen = strlen(inbuf);
986 if (inlen == 0)
987 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
988 else {
989 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
990 !ascncasecmp(inbuf, "true", inlen) ||
991 !ascncasecmp(inbuf, "yes", inlen) ||
992 !ascncasecmp(inbuf, "on", inlen))
993 rv = 1;
994 else if ((inlen == 1 &&
995 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
996 !ascncasecmp(inbuf, "false", inlen) ||
997 !ascncasecmp(inbuf, "no", inlen) ||
998 !ascncasecmp(inbuf, "off", inlen))
999 rv = 0;
1000 else {
1001 ui64_t ib;
1003 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1004 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1005 ) != n_IDEC_STATE_CONSUMED)
1006 rv = -1;
1007 else
1008 rv = (ib != 0);
1011 NYD_LEAVE;
1012 return rv;
1015 FL si8_t
1016 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1018 si8_t rv;
1019 NYD_ENTER;
1021 assert(inlen == 0 || inbuf != NULL);
1023 if (inlen == UIZ_MAX)
1024 inlen = strlen(inbuf);
1026 if (inlen == 0)
1027 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1028 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1029 !ascncasecmp(inbuf, "ask-", 4) &&
1030 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1031 (n_psonce & n_PSO_INTERACTIVE))
1032 rv = getapproval(prompt, rv);
1033 NYD_LEAVE;
1034 return rv;
1037 FL bool_t
1038 n_is_all_or_aster(char const *name){
1039 bool_t rv;
1040 NYD_ENTER;
1042 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1043 NYD_LEAVE;
1044 return rv;
1047 FL struct n_timespec const *
1048 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1049 static struct n_timespec ts_now;
1050 NYD2_ENTER;
1052 if(n_psonce & n_PSO_REPRODUCIBLE){
1053 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1054 ts_now.ts_nsec = 0;
1055 }else if(force_update || ts_now.ts_sec == 0){
1056 #ifdef HAVE_CLOCK_GETTIME
1057 struct timespec ts;
1059 clock_gettime(CLOCK_REALTIME, &ts);
1060 ts_now.ts_sec = (si64_t)ts.tv_sec;
1061 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1062 #elif defined HAVE_GETTIMEOFDAY
1063 struct timeval tv;
1065 gettimeofday(&tv, NULL);
1066 ts_now.ts_sec = (si64_t)tv.tv_sec;
1067 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1068 #else
1069 ts_now.ts_sec = (si64_t)time(NULL);
1070 ts_now.ts_nsec = 0;
1071 #endif
1073 NYD2_LEAVE;
1074 return &ts_now;
1077 FL void
1078 time_current_update(struct time_current *tc, bool_t full_update)
1080 NYD_ENTER;
1081 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1082 if (full_update) {
1083 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1084 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1085 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1087 NYD_LEAVE;
1090 FL uiz_t
1091 n_msleep(uiz_t millis, bool_t ignint){
1092 uiz_t rv;
1093 NYD2_ENTER;
1095 #ifdef HAVE_NANOSLEEP
1096 /* C99 */{
1097 struct timespec ts, trem;
1098 int i;
1100 ts.tv_sec = millis / 1000;
1101 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1103 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1104 ts = trem;
1105 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1108 #elif defined HAVE_SLEEP
1109 if((millis /= 1000) == 0)
1110 millis = 1;
1111 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1112 millis = rv;
1113 #else
1114 # error Configuration should have detected a function for sleeping.
1115 #endif
1117 NYD2_LEAVE;
1118 return rv;
1121 FL void
1122 n_err(char const *format, ...){
1123 va_list ap;
1124 NYD2_ENTER;
1126 va_start(ap, format);
1127 #ifdef HAVE_ERRORS
1128 if(n_psonce & n_PSO_INTERACTIVE)
1129 n_verr(format, ap);
1130 else
1131 #endif
1133 size_t len;
1134 bool_t doname;
1136 doname = FAL0;
1138 while(*format == '\n'){
1139 doname = TRU1;
1140 putc('\n', n_stderr);
1141 ++format;
1144 if(doname)
1145 a_aux_err_linelen = 0;
1147 if((len = strlen(format)) > 0){
1148 if(doname || a_aux_err_linelen == 0){
1149 char const *cp;
1151 if(*(cp = ok_vlook(log_prefix)) != '\0')
1152 fputs(cp, n_stderr);
1154 vfprintf(n_stderr, format, ap);
1156 /* C99 */{
1157 size_t i = len;
1159 if(format[--len] == '\n'){
1160 a_aux_err_linelen = (i -= ++len);
1161 break;
1163 ++a_aux_err_linelen;
1164 }while(len > 0);
1168 fflush(n_stderr);
1170 va_end(ap);
1171 NYD2_LEAVE;
1174 FL void
1175 n_verr(char const *format, va_list ap){
1176 #ifdef HAVE_ERRORS
1177 struct a_aux_err_node *enp;
1178 #endif
1179 bool_t doname;
1180 size_t len;
1181 NYD2_ENTER;
1183 doname = FAL0;
1185 while(*format == '\n'){
1186 putc('\n', n_stderr);
1187 doname = TRU1;
1188 ++format;
1191 if(doname){
1192 a_aux_err_linelen = 0;
1193 #ifdef HAVE_ERRORS
1194 if(n_psonce & n_PSO_INTERACTIVE){
1195 if((enp = a_aux_err_tail) != NULL &&
1196 (enp->ae_str.s_len > 0 &&
1197 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1198 n_string_push_c(&enp->ae_str, '\n');
1200 #endif
1203 if((len = strlen(format)) == 0)
1204 goto jleave;
1205 #ifdef HAVE_ERRORS
1206 n_pstate |= n_PS_ERRORS_PROMPT;
1207 #endif
1209 if(doname || a_aux_err_linelen == 0){
1210 char const *cp;
1212 if(*(cp = ok_vlook(log_prefix)) != '\0')
1213 fputs(cp, n_stderr);
1216 /* C99 */{
1217 size_t i = len;
1219 if(format[--len] == '\n'){
1220 a_aux_err_linelen = (i -= ++len);
1221 break;
1223 ++a_aux_err_linelen;
1224 }while(len > 0);
1227 #ifdef HAVE_ERRORS
1228 if(!(n_psonce & n_PSO_INTERACTIVE))
1229 #endif
1230 vfprintf(n_stderr, format, ap);
1231 #ifdef HAVE_ERRORS
1232 else{
1233 int imax, i;
1234 n_LCTAV(ERRORS_MAX > 3);
1236 /* Link it into the `errors' message ring */
1237 if((enp = a_aux_err_tail) == NULL){
1238 jcreat:
1239 enp = smalloc(sizeof *enp);
1240 enp->ae_next = NULL;
1241 n_string_creat(&enp->ae_str);
1242 if(a_aux_err_tail != NULL)
1243 a_aux_err_tail->ae_next = enp;
1244 else
1245 a_aux_err_head = enp;
1246 a_aux_err_tail = enp;
1247 ++a_aux_err_cnt;
1248 }else if(doname ||
1249 (enp->ae_str.s_len > 0 &&
1250 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1251 if(a_aux_err_cnt < ERRORS_MAX)
1252 goto jcreat;
1254 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1255 a_aux_err_tail->ae_next = enp;
1256 a_aux_err_tail = enp;
1257 enp->ae_next = NULL;
1258 n_string_trunc(&enp->ae_str, 0);
1261 # ifdef HAVE_N_VA_COPY
1262 imax = 64;
1263 # else
1264 imax = n_MIN(LINESIZE, 1024);
1265 # endif
1266 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1267 # ifdef HAVE_N_VA_COPY
1268 va_list vac;
1270 n_va_copy(vac, ap);
1271 # else
1272 # define vac ap
1273 # endif
1275 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1276 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1277 # ifdef HAVE_N_VA_COPY
1278 va_end(vac);
1279 # else
1280 # undef vac
1281 # endif
1282 if(i <= 0)
1283 goto jleave;
1284 if(UICMP(z, i, >=, imax)){
1285 # ifdef HAVE_N_VA_COPY
1286 /* XXX Check overflow for upcoming LEN+++i! */
1287 n_string_trunc(&enp->ae_str, len);
1288 continue;
1289 # else
1290 i = (int)strlen(&enp->ae_str.s_dat[len]);
1291 # endif
1293 break;
1295 n_string_trunc(&enp->ae_str, len + (size_t)i);
1297 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1299 #endif /* HAVE_ERRORS */
1301 jleave:
1302 fflush(n_stderr);
1303 NYD2_LEAVE;
1306 FL void
1307 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1308 va_list ap;
1309 NYD_X;
1311 va_start(ap, format);
1312 vfprintf(n_stderr, format, ap);
1313 va_end(ap);
1314 fflush(n_stderr);
1317 FL void
1318 n_perr(char const *msg, int errval){
1319 int e;
1320 char const *fmt;
1321 NYD2_ENTER;
1323 if(msg == NULL){
1324 fmt = "%s%s\n";
1325 msg = n_empty;
1326 }else
1327 fmt = "%s: %s\n";
1329 e = (errval == 0) ? n_err_no : errval;
1330 n_err(fmt, msg, n_err_to_doc(e));
1331 if(errval == 0)
1332 n_err_no = e;
1333 NYD2_LEAVE;
1336 FL void
1337 n_alert(char const *format, ...){
1338 va_list ap;
1339 NYD2_ENTER;
1341 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1343 va_start(ap, format);
1344 n_verr(format, ap);
1345 va_end(ap);
1347 n_err("\n");
1348 NYD2_LEAVE;
1351 FL void
1352 n_panic(char const *format, ...){
1353 va_list ap;
1354 NYD2_ENTER;
1356 if(a_aux_err_linelen > 0){
1357 putc('\n', n_stderr);
1358 a_aux_err_linelen = 0;
1360 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1362 va_start(ap, format);
1363 vfprintf(n_stderr, format, ap);
1364 va_end(ap);
1366 putc('\n', n_stderr);
1367 fflush(n_stderr);
1368 NYD2_LEAVE;
1369 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1372 #ifdef HAVE_ERRORS
1373 FL int
1374 c_errors(void *v){
1375 char **argv = v;
1376 struct a_aux_err_node *enp;
1377 NYD_ENTER;
1379 if(*argv == NULL)
1380 goto jlist;
1381 if(argv[1] != NULL)
1382 goto jerr;
1383 if(!asccasecmp(*argv, "show"))
1384 goto jlist;
1385 if(!asccasecmp(*argv, "clear"))
1386 goto jclear;
1387 jerr:
1388 fprintf(n_stderr,
1389 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1390 v = NULL;
1391 jleave:
1392 NYD_LEAVE;
1393 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1395 jlist:{
1396 FILE *fp;
1397 size_t i;
1399 if(a_aux_err_head == NULL){
1400 fprintf(n_stderr, _("The error ring is empty\n"));
1401 goto jleave;
1404 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1405 NULL){
1406 fprintf(n_stderr, _("tmpfile"));
1407 v = NULL;
1408 goto jleave;
1411 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1412 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1413 /* We don't know whether last string ended with NL; be simple XXX */
1414 putc('\n', fp);
1416 page_or_print(fp, 0);
1417 Fclose(fp);
1419 /* FALLTHRU */
1421 jclear:
1422 a_aux_err_tail = NULL;
1423 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1424 a_aux_err_linelen = 0;
1425 while((enp = a_aux_err_head) != NULL){
1426 a_aux_err_head = enp->ae_next;
1427 n_string_gut(&enp->ae_str);
1428 free(enp);
1430 goto jleave;
1432 #endif /* HAVE_ERRORS */
1434 FL char const *
1435 n_err_to_doc(si32_t eno){
1436 char const *rv;
1437 struct a_aux_err_map const *aemp;
1438 NYD2_ENTER;
1440 aemp = a_aux_err_map_from_no(eno);
1441 rv = &a_aux_err_docs[aemp->aem_docoff];
1442 NYD2_LEAVE;
1443 return rv;
1446 FL char const *
1447 n_err_to_name(si32_t eno){
1448 char const *rv;
1449 struct a_aux_err_map const *aemp;
1450 NYD2_ENTER;
1452 aemp = a_aux_err_map_from_no(eno);
1453 rv = &a_aux_err_names[aemp->aem_nameoff];
1454 NYD2_LEAVE;
1455 return rv;
1458 FL si32_t
1459 n_err_from_name(char const *name){
1460 struct a_aux_err_map const *aemp;
1461 ui32_t hash, i, j, x;
1462 si32_t rv;
1463 NYD2_ENTER;
1465 hash = n_torek_hash(name);
1467 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1468 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1469 break;
1471 aemp = &a_aux_err_map[x];
1472 if(aemp->aem_hash == hash &&
1473 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1474 rv = aemp->aem_err_no;
1475 goto jleave;
1478 if(++i == a_AUX_ERR_REV_PRIME){
1479 #ifdef a_AUX_ERR_REV_WRAPAROUND
1480 i = 0;
1481 #else
1482 break;
1483 #endif
1487 /* Have not found it. But wait, it could be that the user did, e.g.,
1488 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1489 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1490 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1491 ) == n_IDEC_STATE_CONSUMED){
1492 aemp = a_aux_err_map_from_no(rv);
1493 rv = aemp->aem_err_no;
1494 goto jleave;
1497 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1498 jleave:
1499 NYD2_LEAVE;
1500 return rv;
1503 #ifdef HAVE_REGEX
1504 FL char const *
1505 n_regex_err_to_doc(const regex_t *rep, int e){
1506 char *cp;
1507 size_t i;
1508 NYD2_ENTER;
1510 i = regerror(e, rep, NULL, 0) +1;
1511 cp = salloc(i);
1512 regerror(e, rep, cp, i);
1513 NYD2_LEAVE;
1514 return cp;
1516 #endif
1518 /* s-it-mode */