make-release.txt: mention commits like that
[s-mailx.git] / auxlily.c
blob807f70e1ee6a15bd89b20d64c896baa049f87123
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_SOCKETS
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
47 # endif
49 # include <netdb.h>
50 #endif
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
54 #endif
55 #ifdef HAVE_SETLOCALE
56 # include <locale.h>
57 #endif
59 #ifdef HAVE_GETRANDOM
60 # include HAVE_GETRANDOM_HEADER
61 #endif
63 #ifdef HAVE_IDNA
64 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
65 # include <idna.h>
66 # include <idn-free.h>
67 # include <stringprep.h>
68 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
69 # include <idn/api.h>
70 # endif
71 #endif
73 #ifndef HAVE_POSIX_RANDOM
74 union rand_state{
75 struct rand_arc4{
76 ui8_t _dat[256];
77 ui8_t _i;
78 ui8_t _j;
79 ui8_t __pad[6];
80 } a;
81 ui8_t b8[sizeof(struct rand_arc4)];
82 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
84 #endif
86 #ifdef HAVE_ERRORS
87 struct a_aux_err_node{
88 struct a_aux_err_node *ae_next;
89 struct n_string ae_str;
91 #endif
93 struct a_aux_err_map{
94 ui32_t aem_hash; /* Hash of name */
95 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
96 ui32_t aem_docoff; /* Into a_aux_err docs[] */
97 si32_t aem_err_no; /* The OS error value for this one */
100 static ui8_t a_aux_idec_atoi[256] = {
101 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
102 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
103 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
104 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
105 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
106 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
108 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
109 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
110 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
111 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
112 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
113 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
117 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
121 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
122 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
123 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
124 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
125 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
126 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
129 #define a_X(X) ((ui64_t)-1 / (X))
130 static ui64_t const a_aux_idec_cutlimit[35] = {
131 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
132 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
133 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
134 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
135 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
137 #undef a_X
139 /* Include the constant make-errors.sh output */
140 #include "gen-errors.h"
142 /* And these things come from mk-config.h (config-time make-errors.sh output) */
143 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
144 #undef a_X
145 #define a_X(N,I) {N,I},
146 n__ERR_NUMBER_TO_MAPOFF
147 #undef a_X
150 #ifndef HAVE_POSIX_RANDOM
151 static union rand_state *a_aux_rand;
152 #endif
154 /* Error ring, for `errors' */
155 #ifdef HAVE_ERRORS
156 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
157 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
158 #endif
159 static size_t a_aux_err_linelen;
161 /* Our ARC4 random generator with its completely unacademical pseudo
162 * initialization (shall /dev/urandom fail) */
163 #ifndef HAVE_POSIX_RANDOM
164 static void a_aux_rand_init(void);
165 SINLINE ui8_t a_aux_rand_get8(void);
166 # ifndef HAVE_GETRANDOM
167 static ui32_t a_aux_rand_weak(ui32_t seed);
168 # endif
169 #endif
171 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
172 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
174 #ifndef HAVE_POSIX_RANDOM
175 static void
176 a_aux_rand_init(void){
177 # ifndef HAVE_GETRANDOM
178 # ifdef HAVE_CLOCK_GETTIME
179 struct timespec ts;
180 # else
181 struct timeval ts;
182 # endif
183 union {int fd; size_t i;} u;
184 ui32_t seed, rnd;
185 # endif
186 NYD2_ENTER;
188 a_aux_rand = n_alloc(sizeof *a_aux_rand);
190 # ifdef HAVE_GETRANDOM
191 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
192 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
193 "Buffer too large to be served without n_ERR_INTR error");
194 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
195 "Buffer too small to serve used array indices");
196 for(;;){
197 ssize_t gr;
199 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
200 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
201 a_aux_rand->a._dat[84]];
202 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
203 a_aux_rand->a._dat[42]];
204 /* ..but be on the safe side */
205 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
206 break;
207 n_msleep(250, FAL0);
210 # else
211 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
212 bool_t ok;
214 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
215 sizeof(a_aux_rand->a._dat)));
216 close(u.fd);
218 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
219 a_aux_rand->a._dat[84]];
220 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
221 a_aux_rand->a._dat[42]];
222 if(ok)
223 goto jleave;
226 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
227 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
228 ui32_t t, k;
230 # ifdef HAVE_CLOCK_GETTIME
231 clock_gettime(CLOCK_REALTIME, &ts);
232 t = (ui32_t)ts.tv_nsec;
233 # else
234 gettimeofday(&ts, NULL);
235 t = (ui32_t)ts.tv_usec;
236 # endif
237 if(rnd & 1)
238 t = (t >> 16) | (t << 16);
239 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
240 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
241 if(rnd == 7 || rnd == 17)
242 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
243 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
244 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
245 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
246 if((rnd & 3) == 3)
247 seed ^= n_prime_next(seed);
251 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
252 a_aux_rand_get8();
253 jleave:
254 # endif /* !HAVE_GETRANDOM */
255 NYD2_LEAVE;
258 SINLINE ui8_t
259 a_aux_rand_get8(void){
260 ui8_t si, sj;
262 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
263 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
264 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
265 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
266 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
269 # ifndef HAVE_GETRANDOM
270 static ui32_t
271 a_aux_rand_weak(ui32_t seed){
272 /* From "Random number generators: good ones are hard to find",
273 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
274 * October 1988, p. 1195.
275 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
276 ui32_t hi;
278 if(seed == 0)
279 seed = 123459876;
280 hi = seed / 127773;
281 seed %= 127773;
282 seed = (seed * 16807) - (hi * 2836);
283 if((si32_t)seed < 0)
284 seed += SI32_MAX;
285 return seed;
287 # endif /* HAVE_GETRANDOM */
288 #endif /* !HAVE_POSIX_RANDOM */
290 static struct a_aux_err_map const *
291 a_aux_err_map_from_no(si32_t eno){
292 si32_t ecmp;
293 size_t asz;
294 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
295 struct a_aux_err_map const *aemp;
296 NYD2_ENTER;
298 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
300 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
301 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
302 asz != 0; asz >>= 1){
303 tmp = &adat[asz >> 1];
304 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
305 aemp = &a_aux_err_map[(*tmp)[1]];
306 break;
308 if(ecmp > 0){
309 adat = &tmp[1];
310 --asz;
314 NYD2_LEAVE;
315 return aemp;
318 FL void
319 n_locale_init(void){
320 NYD2_ENTER;
322 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
324 #ifndef HAVE_SETLOCALE
325 n_mb_cur_max = 1;
326 #else
327 setlocale(LC_ALL, n_empty);
328 n_mb_cur_max = MB_CUR_MAX;
329 # ifdef HAVE_NL_LANGINFO
330 /* C99 */{
331 char const *cp;
333 /* TODO *ttycharset* may be set several times during startup unless
334 * TODO we gain a mechanism that -S fixates a setting during startup,
335 * TODO effectively turning later adjustments (during startup) in noop */
336 if((cp = nl_langinfo(CODESET)) != NULL)
337 ok_vset(ttycharset, cp);
339 # endif
341 # ifdef HAVE_C90AMEND1
342 if(n_mb_cur_max > 1){
343 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
344 n_psonce |= n_PSO_UNICODE;
345 # else
346 wchar_t wc;
347 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
348 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
349 n_psonce |= n_PSO_UNICODE;
350 /* Reset possibly messed up state; luckily this also gives us an
351 * indication whether the encoding has locking shift state sequences */
352 if(mbtowc(&wc, NULL, n_mb_cur_max))
353 n_psonce |= n_PSO_ENC_MBSTATE;
354 # endif
356 # endif
357 #endif
358 NYD2_LEAVE;
361 FL size_t
362 n_screensize(void){
363 char const *cp;
364 uiz_t rv;
365 NYD2_ENTER;
367 if((cp = ok_vlook(screen)) != NULL){
368 n_idec_uiz_cp(&rv, cp, 0, NULL);
369 if(rv == 0)
370 rv = n_scrnheight;
371 }else
372 rv = n_scrnheight;
374 if(rv > 2)
375 rv -= 2;
376 NYD2_LEAVE;
377 return rv;
380 FL char const *
381 n_pager_get(char const **env_addon){
382 char const *rv;
383 NYD_ENTER;
385 rv = ok_vlook(PAGER);
387 if(env_addon != NULL){
388 *env_addon = NULL;
389 /* Update the manual upon any changes:
390 * *colour-pager*, $PAGER */
391 if(strstr(rv, "less") != NULL){
392 if(getenv("LESS") == NULL)
393 *env_addon =
394 #ifdef HAVE_TERMCAP
395 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
396 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
397 #endif
398 "LESS=FRXi";
399 }else if(strstr(rv, "lv") != NULL){
400 if(getenv("LV") == NULL)
401 *env_addon = "LV=-c";
404 NYD_LEAVE;
405 return rv;
408 FL void
409 page_or_print(FILE *fp, size_t lines)
411 int c;
412 char const *cp;
413 NYD_ENTER;
415 fflush_rewind(fp);
417 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
418 size_t rows;
420 if(*cp == '\0')
421 rows = (size_t)n_scrnheight;
422 else
423 n_idec_uiz_cp(&rows, cp, 0, NULL);
425 if (rows > 0 && lines == 0) {
426 while ((c = getc(fp)) != EOF)
427 if (c == '\n' && ++lines >= rows)
428 break;
429 really_rewind(fp);
432 if (lines >= rows) {
433 char const *env_add[2], *pager;
435 pager = n_pager_get(&env_add[0]);
436 env_add[1] = NULL;
437 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
438 env_add, NULL);
439 goto jleave;
443 while ((c = getc(fp)) != EOF)
444 putc(c, n_stdout);
445 jleave:
446 NYD_LEAVE;
449 FL enum protocol
450 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
451 char const **adjusted_or_null)
453 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
454 char const *cp, *orig_name;
455 enum protocol rv = PROTO_UNKNOWN;
456 NYD_ENTER;
458 if(name[0] == '%' && name[1] == ':')
459 name += 2;
460 orig_name = name;
462 for (cp = name; *cp && *cp != ':'; cp++)
463 if (!alnumchar(*cp))
464 goto jfile;
466 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
467 if(!strncmp(name, "file", sizeof("file") -1) ||
468 !strncmp(name, "mbox", sizeof("mbox") -1))
469 rv = PROTO_FILE;
470 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
471 rv = PROTO_MAILDIR;
472 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
473 #ifdef HAVE_POP3
474 rv = PROTO_POP3;
475 #else
476 n_err(_("No POP3 support compiled in\n"));
477 #endif
478 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
479 #if defined HAVE_POP3 && defined HAVE_SSL
480 rv = PROTO_POP3;
481 #else
482 n_err(_("No POP3S support compiled in\n"));
483 #endif
485 else if(!strncmp(name, "imap", sizeof("imap") -1)){
486 #ifdef HAVE_IMAP
487 rv = PROTO_IMAP;
488 #else
489 n_err(_("No IMAP support compiled in\n"));
490 #endif
491 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
492 #if defined HAVE_IMAP && defined HAVE_SSL
493 rv = PROTO_IMAP;
494 #else
495 n_err(_("No IMAPS support compiled in\n"));
496 #endif
498 orig_name = &cp[3];
499 goto jleave;
502 jfile:
503 rv = PROTO_FILE;
505 if(check_stat || try_hooks){
506 struct n_file_type ft;
507 struct stat stb;
508 char *np;
509 size_t sz;
511 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
512 memcpy(np, name, sz + 1);
514 if(!stat(name, &stb)){
515 if(S_ISDIR(stb.st_mode) &&
516 (memcpy(&np[sz], "/tmp", 5),
517 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
518 (memcpy(&np[sz], "/new", 5),
519 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
520 (memcpy(&np[sz], "/cur", 5),
521 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
522 rv = PROTO_MAILDIR;
523 }else if(try_hooks && n_filetype_trial(&ft, name))
524 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
525 else if((cp = ok_vlook(newfolders)) != NULL &&
526 !asccasecmp(cp, "maildir"))
527 rv = PROTO_MAILDIR;
529 n_lofi_free(np);
531 jleave:
532 if(adjusted_or_null != NULL)
533 *adjusted_or_null = orig_name;
534 NYD_LEAVE;
535 return rv;
538 FL char *
539 n_c_to_hex_base16(char store[3], char c){
540 static char const itoa16[] = "0123456789ABCDEF";
541 NYD2_ENTER;
543 store[2] = '\0';
544 store[1] = itoa16[(ui8_t)c & 0x0F];
545 c = ((ui8_t)c >> 4) & 0x0F;
546 store[0] = itoa16[(ui8_t)c];
547 NYD2_LEAVE;
548 return store;
551 FL si32_t
552 n_c_from_hex_base16(char const hex[2]){
553 static ui8_t const atoi16[] = {
554 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
555 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
556 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
557 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
558 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
559 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
560 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
562 ui8_t i1, i2;
563 si32_t rv;
564 NYD2_ENTER;
566 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
567 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
568 goto jerr;
569 i1 = atoi16[i1];
570 i2 = atoi16[i2];
571 if ((i1 | i2) & 0xF0u)
572 goto jerr;
573 rv = i1;
574 rv <<= 4;
575 rv += i2;
576 jleave:
577 NYD2_LEAVE;
578 return rv;
579 jerr:
580 rv = -1;
581 goto jleave;
584 FL enum n_idec_state
585 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
586 enum n_idec_mode idm, char const **endptr_or_null){
587 /* XXX Brute simple and */
588 ui8_t currc;
589 ui64_t res, cut;
590 enum n_idec_state rv;
591 NYD_ENTER;
593 idm &= n__IDEC_MODE_MASK;
594 rv = n_IDEC_STATE_NONE | idm;
595 res = 0;
597 if(clen == UIZ_MAX){
598 if(*cbuf == '\0')
599 goto jeinval;
600 }else if(clen == 0)
601 goto jeinval;
603 /* Leading WS */
604 while(spacechar(*cbuf))
605 if(*++cbuf == '\0' || --clen == 0)
606 goto jeinval;
608 /* Check sign */
609 switch(*cbuf){
610 case '-':
611 rv |= n_IDEC_STATE_SEEN_MINUS;
612 /* FALLTHROUGH */
613 case '+':
614 if(*++cbuf == '\0' || --clen == 0)
615 goto jeinval;
616 break;
619 /* Base detection/skip */
620 if(*cbuf != '0'){
621 if(base == 0)
622 base = 10;
623 /* Character must be valid for base */
624 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
625 if(currc >= base)
626 goto jeinval;
627 }else{
628 /* 0 always valid as is, fallback base 10 */
629 if(*++cbuf == '\0' || --clen == 0)
630 goto jleave;
632 /* Base "detection" */
633 if(base == 0 || base == 2 || base == 16){
634 switch(*cbuf){
635 case 'x':
636 case 'X':
637 if((base & 2) == 0){
638 base = 0x10;
639 goto jprefix_skip;
641 break;
642 case 'b':
643 case 'B':
644 if((base & 16) == 0){
645 base = 2; /* 0b10 */
646 /* Char after prefix must be valid */
647 jprefix_skip:
648 if(*++cbuf == '\0' || --clen == 0)
649 goto jeinval;
651 /* Character must be valid for base, invalid otherwise */
652 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
653 if(currc >= base)
654 goto jeinval;
656 break;
657 default:
658 if(base == 0)
659 base = 010;
660 break;
664 /* Character must be valid for base, _EBASE otherwise */
665 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
666 if(currc >= base)
667 goto jebase;
670 for(cut = a_aux_idec_cutlimit[base - 2];;){
671 if(res >= cut){
672 if(res == cut){
673 res *= base;
674 if(res > UI64_MAX - currc)
675 goto jeover;
676 res += currc;
677 }else
678 goto jeover;
679 }else{
680 res *= base;
681 res += currc;
684 if(*++cbuf == '\0' || --clen == 0)
685 break;
687 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
688 if(currc >= base)
689 goto jebase;
692 jleave:
694 ui64_t uimask;
696 switch(rv & n__IDEC_MODE_LIMIT_MASK){
697 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
698 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
699 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
700 default: uimask = UI64_MAX; break;
702 if(rv & n_IDEC_MODE_SIGNED_TYPE)
703 uimask >>= 1;
705 if(res & ~uimask){
706 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
707 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
708 if(res > uimask + 1){
709 res = uimask << 1;
710 res &= ~uimask;
711 }else{
712 res = -res;
713 break;
715 }else
716 res = uimask;
717 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
718 rv |= n_IDEC_STATE_EOVERFLOW;
719 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
720 res = -res;
721 }while(0);
723 switch(rv & n__IDEC_MODE_LIMIT_MASK){
724 case n_IDEC_MODE_LIMIT_8BIT:
725 if(rv & n_IDEC_MODE_SIGNED_TYPE)
726 *(si8_t*)resp = (si8_t)res;
727 else
728 *(ui8_t*)resp = (ui8_t)res;
729 break;
730 case n_IDEC_MODE_LIMIT_16BIT:
731 if(rv & n_IDEC_MODE_SIGNED_TYPE)
732 *(si16_t*)resp = (si16_t)res;
733 else
734 *(ui16_t*)resp = (ui16_t)res;
735 break;
736 case n_IDEC_MODE_LIMIT_32BIT:
737 if(rv & n_IDEC_MODE_SIGNED_TYPE)
738 *(si32_t*)resp = (si32_t)res;
739 else
740 *(ui32_t*)resp = (ui32_t)res;
741 break;
742 default:
743 if(rv & n_IDEC_MODE_SIGNED_TYPE)
744 *(si64_t*)resp = (si64_t)res;
745 else
746 *(ui64_t*)resp = (ui64_t)res;
747 break;
750 if(endptr_or_null != NULL)
751 *endptr_or_null = cbuf;
752 if(*cbuf == '\0' || clen == 0)
753 rv |= n_IDEC_STATE_CONSUMED;
754 NYD_LEAVE;
755 return rv;
757 jeinval:
758 rv |= n_IDEC_STATE_EINVAL;
759 goto j_maxval;
760 jebase:
761 /* Not a base error for terminator and whitespace! */
762 if(*cbuf != '\0' && !spacechar(*cbuf))
763 rv |= n_IDEC_STATE_EBASE;
764 goto jleave;
766 jeover:
767 /* Overflow error: consume input until bad character or length out */
768 for(;;){
769 if(*++cbuf == '\0' || --clen == 0)
770 break;
771 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
772 if(currc >= base)
773 break;
776 rv |= n_IDEC_STATE_EOVERFLOW;
777 j_maxval:
778 if(rv & n_IDEC_MODE_SIGNED_TYPE)
779 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
780 : (ui64_t)SI64_MAX;
781 else
782 res = UI64_MAX;
783 rv &= ~n_IDEC_STATE_SEEN_MINUS;
784 goto jleave;
787 FL ui32_t
788 n_torek_hash(char const *name){
789 /* Chris Torek's hash */
790 char c;
791 ui32_t h;
792 NYD2_ENTER;
794 for(h = 0; (c = *name++) != '\0';)
795 h = (h * 33) + c;
796 NYD2_LEAVE;
797 return h;
800 FL ui32_t
801 n_torek_ihashn(char const *dat, size_t len){
802 /* See n_torek_hash() */
803 char c;
804 ui32_t h;
805 NYD2_ENTER;
807 if(len == UIZ_MAX)
808 for(h = 0; (c = *dat++) != '\0';)
809 h = (h * 33) + lowerconv(c);
810 else
811 for(h = 0; len > 0; --len){
812 c = *dat++;
813 h = (h * 33) + lowerconv(c);
815 NYD2_LEAVE;
816 return h;
819 FL ui32_t
820 n_prime_next(ui32_t n){
821 static ui32_t const primes[] = {
822 5, 11, 23, 47, 97, 157, 283,
823 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
824 131071, 262139, 524287, 1048573, 2097143, 4194301,
825 8388593, 16777213, 33554393, 67108859, 134217689,
826 268435399, 536870909, 1073741789, 2147483647
828 ui32_t i, mprime;
829 NYD2_ENTER;
831 i = (n < primes[n_NELEM(primes) / 4] ? 0
832 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
833 : n_NELEM(primes) / 2));
835 do if((mprime = primes[i]) > n)
836 break;
837 while(++i < n_NELEM(primes));
839 if(i == n_NELEM(primes) && mprime < n)
840 mprime = n;
841 NYD2_LEAVE;
842 return mprime;
845 FL char const *
846 n_getdeadletter(void){
847 char const *cp;
848 bool_t bla;
849 NYD_ENTER;
851 bla = FAL0;
852 jredo:
853 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
854 if(cp == NULL || strlen(cp) >= PATH_MAX){
855 if(!bla){
856 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
857 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
858 ok_vclear(DEAD);
859 bla = TRU1;
860 goto jredo;
861 }else{
862 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
863 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
866 NYD_LEAVE;
867 return cp;
870 FL char *
871 n_nodename(bool_t mayoverride){
872 static char *sys_hostname, *hostname; /* XXX free-at-exit */
874 struct utsname ut;
875 char *hn;
876 #ifdef HAVE_SOCKETS
877 # ifdef HAVE_GETADDRINFO
878 struct addrinfo hints, *res;
879 # else
880 struct hostent *hent;
881 # endif
882 #endif
883 NYD2_ENTER;
885 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
887 }else if((hn = sys_hostname) == NULL){
888 uname(&ut);
889 hn = ut.nodename;
890 #ifdef HAVE_SOCKETS
891 # ifdef HAVE_GETADDRINFO
892 memset(&hints, 0, sizeof hints);
893 hints.ai_family = AF_UNSPEC;
894 hints.ai_flags = AI_CANONNAME;
895 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
896 if(res->ai_canonname != NULL){
897 size_t l;
899 l = strlen(res->ai_canonname) +1;
900 hn = n_lofi_alloc(l);
901 memcpy(hn, res->ai_canonname, l);
903 freeaddrinfo(res);
905 # else
906 hent = gethostbyname(hn);
907 if(hent != NULL)
908 hn = hent->h_name;
909 # endif
910 #endif
911 sys_hostname = sstrdup(hn);
912 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
913 if(hn != ut.nodename)
914 n_lofi_free(hn);
915 #endif
916 hn = sys_hostname;
919 if(hostname != NULL && hostname != sys_hostname)
920 n_free(hostname);
921 hostname = sstrdup(hn);
922 NYD2_LEAVE;
923 return hostname;
926 #ifdef HAVE_IDNA
927 FL bool_t
928 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
929 char *idna_utf8;
930 bool_t rv;
931 NYD_ENTER;
933 if((rv = (ilen == 0)))
934 goto jleave;
936 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
937 ibuf = savestrbuf(ibuf, ilen);
938 ilen = 0;
940 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
941 ibuf);
942 if(idna_utf8 == NULL)
943 goto jleave;
945 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
946 /* C99 */{
947 char *idna_ascii;
949 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
950 out = n_string_assign_cp(out, idna_ascii);
951 idn_free(idna_ascii);
952 rv = TRU1;
953 ilen = out->s_len;
956 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
957 ilen = strlen(idna_utf8);
958 jredo:
959 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
960 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
961 case idn_buffer_overflow:
962 ilen += HOST_NAME_MAX +1;
963 goto jredo;
964 case idn_success:
965 rv = TRU1;
966 ilen = strlen(out->s_dat);
967 break;
968 default:
969 ilen = 0;
970 break;
973 # else
974 # error Unknown HAVE_IDNA
975 # endif
976 jleave:
977 out = n_string_trunc(out, ilen);
978 NYD_LEAVE;
979 return rv;
981 #endif /* HAVE_IDNA */
983 FL char *
984 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
985 struct str b64;
986 char *data, *cp;
987 size_t i;
988 NYD_ENTER;
990 #ifndef HAVE_POSIX_RANDOM
991 if(a_aux_rand == NULL)
992 a_aux_rand_init();
993 #endif
995 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
996 * with PAD stripped is still longer than what the user requests, easy way */
997 data = n_lofi_alloc(i = length + 3);
999 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1000 #ifndef HAVE_POSIX_RANDOM
1001 while(i-- > 0)
1002 data[i] = (char)a_aux_rand_get8();
1003 #else
1004 for(cp = data; i > 0;){
1005 union {ui32_t i4; char c[4];} r;
1006 size_t j;
1008 r.i4 = (ui32_t)arc4random();
1009 switch((j = i & 3)){
1010 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1011 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1012 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1013 default: cp[0] = r.c[0]; break;
1015 cp += j;
1016 i -= j;
1018 #endif
1019 }else{
1020 for(cp = data; i > 0;){
1021 union {ui32_t i4; char c[4];} r;
1022 size_t j;
1024 r.i4 = ++*reprocnt_or_null;
1025 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1026 char x;
1028 x = r.c[0];
1029 r.c[0] = r.c[3];
1030 r.c[3] = x;
1031 x = r.c[1];
1032 r.c[1] = r.c[2];
1033 r.c[2] = x;
1035 switch((j = i & 3)){
1036 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1037 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1038 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1039 default: cp[0] = r.c[0]; break;
1041 cp += j;
1042 i -= j;
1046 assert(length + 3 < UIZ_MAX / 4);
1047 b64_encode_buf(&b64, data, length + 3,
1048 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1049 n_lofi_free(data);
1051 assert(b64.l >= length);
1052 b64.s[length] = '\0';
1053 NYD_LEAVE;
1054 return b64.s;
1057 FL si8_t
1058 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1060 si8_t rv;
1061 NYD_ENTER;
1063 assert(inlen == 0 || inbuf != NULL);
1065 if (inlen == UIZ_MAX)
1066 inlen = strlen(inbuf);
1068 if (inlen == 0)
1069 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1070 else {
1071 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1072 !ascncasecmp(inbuf, "true", inlen) ||
1073 !ascncasecmp(inbuf, "yes", inlen) ||
1074 !ascncasecmp(inbuf, "on", inlen))
1075 rv = 1;
1076 else if ((inlen == 1 &&
1077 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1078 !ascncasecmp(inbuf, "false", inlen) ||
1079 !ascncasecmp(inbuf, "no", inlen) ||
1080 !ascncasecmp(inbuf, "off", inlen))
1081 rv = 0;
1082 else {
1083 ui64_t ib;
1085 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1086 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1087 ) != n_IDEC_STATE_CONSUMED)
1088 rv = -1;
1089 else
1090 rv = (ib != 0);
1093 NYD_LEAVE;
1094 return rv;
1097 FL si8_t
1098 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1100 si8_t rv;
1101 NYD_ENTER;
1103 assert(inlen == 0 || inbuf != NULL);
1105 if (inlen == UIZ_MAX)
1106 inlen = strlen(inbuf);
1108 if (inlen == 0)
1109 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1110 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1111 !ascncasecmp(inbuf, "ask-", 4) &&
1112 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1113 (n_psonce & n_PSO_INTERACTIVE))
1114 rv = getapproval(prompt, rv);
1115 NYD_LEAVE;
1116 return rv;
1119 FL bool_t
1120 n_is_all_or_aster(char const *name){
1121 bool_t rv;
1122 NYD_ENTER;
1124 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1125 NYD_LEAVE;
1126 return rv;
1129 FL struct n_timespec const *
1130 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1131 static struct n_timespec ts_now;
1132 NYD2_ENTER;
1134 if(n_psonce & n_PSO_REPRODUCIBLE){
1135 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1136 ts_now.ts_nsec = 0;
1137 }else if(force_update || ts_now.ts_sec == 0){
1138 #ifdef HAVE_CLOCK_GETTIME
1139 struct timespec ts;
1141 clock_gettime(CLOCK_REALTIME, &ts);
1142 ts_now.ts_sec = (si64_t)ts.tv_sec;
1143 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1144 #elif defined HAVE_GETTIMEOFDAY
1145 struct timeval tv;
1147 gettimeofday(&tv, NULL);
1148 ts_now.ts_sec = (si64_t)tv.tv_sec;
1149 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1150 #else
1151 ts_now.ts_sec = (si64_t)time(NULL);
1152 ts_now.ts_nsec = 0;
1153 #endif
1155 NYD2_LEAVE;
1156 return &ts_now;
1159 FL void
1160 time_current_update(struct time_current *tc, bool_t full_update)
1162 NYD_ENTER;
1163 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1164 if (full_update) {
1165 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1166 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1167 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1169 NYD_LEAVE;
1172 FL uiz_t
1173 n_msleep(uiz_t millis, bool_t ignint){
1174 uiz_t rv;
1175 NYD2_ENTER;
1177 #ifdef HAVE_NANOSLEEP
1178 /* C99 */{
1179 struct timespec ts, trem;
1180 int i;
1182 ts.tv_sec = millis / 1000;
1183 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1185 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1186 ts = trem;
1187 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1190 #elif defined HAVE_SLEEP
1191 if((millis /= 1000) == 0)
1192 millis = 1;
1193 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1194 millis = rv;
1195 #else
1196 # error Configuration should have detected a function for sleeping.
1197 #endif
1199 NYD2_LEAVE;
1200 return rv;
1203 FL void
1204 n_err(char const *format, ...){
1205 va_list ap;
1206 NYD2_ENTER;
1208 va_start(ap, format);
1209 #ifdef HAVE_ERRORS
1210 if(n_psonce & n_PSO_INTERACTIVE)
1211 n_verr(format, ap);
1212 else
1213 #endif
1215 size_t len;
1216 bool_t doname;
1218 doname = FAL0;
1220 while(*format == '\n'){
1221 doname = TRU1;
1222 putc('\n', n_stderr);
1223 ++format;
1226 if(doname)
1227 a_aux_err_linelen = 0;
1229 if((len = strlen(format)) > 0){
1230 if(doname || a_aux_err_linelen == 0){
1231 char const *cp;
1233 if(*(cp = ok_vlook(log_prefix)) != '\0')
1234 fputs(cp, n_stderr);
1236 vfprintf(n_stderr, format, ap);
1238 /* C99 */{
1239 size_t i = len;
1241 if(format[--len] == '\n'){
1242 a_aux_err_linelen = (i -= ++len);
1243 break;
1245 ++a_aux_err_linelen;
1246 }while(len > 0);
1250 fflush(n_stderr);
1252 va_end(ap);
1253 NYD2_LEAVE;
1256 FL void
1257 n_verr(char const *format, va_list ap){
1258 #ifdef HAVE_ERRORS
1259 struct a_aux_err_node *enp;
1260 #endif
1261 bool_t doname;
1262 size_t len;
1263 NYD2_ENTER;
1265 doname = FAL0;
1267 while(*format == '\n'){
1268 putc('\n', n_stderr);
1269 doname = TRU1;
1270 ++format;
1273 if(doname){
1274 a_aux_err_linelen = 0;
1275 #ifdef HAVE_ERRORS
1276 if(n_psonce & n_PSO_INTERACTIVE){
1277 if((enp = a_aux_err_tail) != NULL &&
1278 (enp->ae_str.s_len > 0 &&
1279 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1280 n_string_push_c(&enp->ae_str, '\n');
1282 #endif
1285 if((len = strlen(format)) == 0)
1286 goto jleave;
1287 #ifdef HAVE_ERRORS
1288 n_pstate |= n_PS_ERRORS_PROMPT;
1289 #endif
1291 if(doname || a_aux_err_linelen == 0){
1292 char const *cp;
1294 if(*(cp = ok_vlook(log_prefix)) != '\0')
1295 fputs(cp, n_stderr);
1298 /* C99 */{
1299 size_t i = len;
1301 if(format[--len] == '\n'){
1302 a_aux_err_linelen = (i -= ++len);
1303 break;
1305 ++a_aux_err_linelen;
1306 }while(len > 0);
1309 #ifdef HAVE_ERRORS
1310 if(!(n_psonce & n_PSO_INTERACTIVE))
1311 #endif
1312 vfprintf(n_stderr, format, ap);
1313 #ifdef HAVE_ERRORS
1314 else{
1315 int imax, i;
1316 n_LCTAV(ERRORS_MAX > 3);
1318 /* Link it into the `errors' message ring */
1319 if((enp = a_aux_err_tail) == NULL){
1320 jcreat:
1321 enp = smalloc(sizeof *enp);
1322 enp->ae_next = NULL;
1323 n_string_creat(&enp->ae_str);
1324 if(a_aux_err_tail != NULL)
1325 a_aux_err_tail->ae_next = enp;
1326 else
1327 a_aux_err_head = enp;
1328 a_aux_err_tail = enp;
1329 ++a_aux_err_cnt;
1330 }else if(doname ||
1331 (enp->ae_str.s_len > 0 &&
1332 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1333 if(a_aux_err_cnt < ERRORS_MAX)
1334 goto jcreat;
1336 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1337 a_aux_err_tail->ae_next = enp;
1338 a_aux_err_tail = enp;
1339 enp->ae_next = NULL;
1340 n_string_trunc(&enp->ae_str, 0);
1343 # ifdef HAVE_N_VA_COPY
1344 imax = 64;
1345 # else
1346 imax = n_MIN(LINESIZE, 1024);
1347 # endif
1348 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1349 # ifdef HAVE_N_VA_COPY
1350 va_list vac;
1352 n_va_copy(vac, ap);
1353 # else
1354 # define vac ap
1355 # endif
1357 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1358 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1359 # ifdef HAVE_N_VA_COPY
1360 va_end(vac);
1361 # else
1362 # undef vac
1363 # endif
1364 if(i <= 0)
1365 goto jleave;
1366 if(UICMP(z, i, >=, imax)){
1367 # ifdef HAVE_N_VA_COPY
1368 /* XXX Check overflow for upcoming LEN+++i! */
1369 n_string_trunc(&enp->ae_str, len);
1370 continue;
1371 # else
1372 i = (int)strlen(&enp->ae_str.s_dat[len]);
1373 # endif
1375 break;
1377 n_string_trunc(&enp->ae_str, len + (size_t)i);
1379 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1381 #endif /* HAVE_ERRORS */
1383 jleave:
1384 fflush(n_stderr);
1385 NYD2_LEAVE;
1388 FL void
1389 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1390 va_list ap;
1391 NYD_X;
1393 va_start(ap, format);
1394 vfprintf(n_stderr, format, ap);
1395 va_end(ap);
1396 fflush(n_stderr);
1399 FL void
1400 n_perr(char const *msg, int errval){
1401 int e;
1402 char const *fmt;
1403 NYD2_ENTER;
1405 if(msg == NULL){
1406 fmt = "%s%s\n";
1407 msg = n_empty;
1408 }else
1409 fmt = "%s: %s\n";
1411 e = (errval == 0) ? n_err_no : errval;
1412 n_err(fmt, msg, n_err_to_doc(e));
1413 if(errval == 0)
1414 n_err_no = e;
1415 NYD2_LEAVE;
1418 FL void
1419 n_alert(char const *format, ...){
1420 va_list ap;
1421 NYD2_ENTER;
1423 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1425 va_start(ap, format);
1426 n_verr(format, ap);
1427 va_end(ap);
1429 n_err("\n");
1430 NYD2_LEAVE;
1433 FL void
1434 n_panic(char const *format, ...){
1435 va_list ap;
1436 NYD2_ENTER;
1438 if(a_aux_err_linelen > 0){
1439 putc('\n', n_stderr);
1440 a_aux_err_linelen = 0;
1442 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1444 va_start(ap, format);
1445 vfprintf(n_stderr, format, ap);
1446 va_end(ap);
1448 putc('\n', n_stderr);
1449 fflush(n_stderr);
1450 NYD2_LEAVE;
1451 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1454 #ifdef HAVE_ERRORS
1455 FL int
1456 c_errors(void *v){
1457 char **argv = v;
1458 struct a_aux_err_node *enp;
1459 NYD_ENTER;
1461 if(*argv == NULL)
1462 goto jlist;
1463 if(argv[1] != NULL)
1464 goto jerr;
1465 if(!asccasecmp(*argv, "show"))
1466 goto jlist;
1467 if(!asccasecmp(*argv, "clear"))
1468 goto jclear;
1469 jerr:
1470 fprintf(n_stderr,
1471 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1472 v = NULL;
1473 jleave:
1474 NYD_LEAVE;
1475 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1477 jlist:{
1478 FILE *fp;
1479 size_t i;
1481 if(a_aux_err_head == NULL){
1482 fprintf(n_stderr, _("The error ring is empty\n"));
1483 goto jleave;
1486 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1487 NULL){
1488 fprintf(n_stderr, _("tmpfile"));
1489 v = NULL;
1490 goto jleave;
1493 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1494 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1495 /* We don't know whether last string ended with NL; be simple XXX */
1496 putc('\n', fp);
1498 page_or_print(fp, 0);
1499 Fclose(fp);
1501 /* FALLTHRU */
1503 jclear:
1504 a_aux_err_tail = NULL;
1505 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1506 a_aux_err_linelen = 0;
1507 while((enp = a_aux_err_head) != NULL){
1508 a_aux_err_head = enp->ae_next;
1509 n_string_gut(&enp->ae_str);
1510 free(enp);
1512 goto jleave;
1514 #endif /* HAVE_ERRORS */
1516 FL char const *
1517 n_err_to_doc(si32_t eno){
1518 char const *rv;
1519 struct a_aux_err_map const *aemp;
1520 NYD2_ENTER;
1522 aemp = a_aux_err_map_from_no(eno);
1523 rv = &a_aux_err_docs[aemp->aem_docoff];
1524 NYD2_LEAVE;
1525 return rv;
1528 FL char const *
1529 n_err_to_name(si32_t eno){
1530 char const *rv;
1531 struct a_aux_err_map const *aemp;
1532 NYD2_ENTER;
1534 aemp = a_aux_err_map_from_no(eno);
1535 rv = &a_aux_err_names[aemp->aem_nameoff];
1536 NYD2_LEAVE;
1537 return rv;
1540 FL si32_t
1541 n_err_from_name(char const *name){
1542 struct a_aux_err_map const *aemp;
1543 ui32_t hash, i, j, x;
1544 si32_t rv;
1545 NYD2_ENTER;
1547 hash = n_torek_hash(name);
1549 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1550 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1551 break;
1553 aemp = &a_aux_err_map[x];
1554 if(aemp->aem_hash == hash &&
1555 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1556 rv = aemp->aem_err_no;
1557 goto jleave;
1560 if(++i == a_AUX_ERR_REV_PRIME){
1561 #ifdef a_AUX_ERR_REV_WRAPAROUND
1562 i = 0;
1563 #else
1564 break;
1565 #endif
1569 /* Have not found it. But wait, it could be that the user did, e.g.,
1570 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1571 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1572 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1573 ) == n_IDEC_STATE_CONSUMED){
1574 aemp = a_aux_err_map_from_no(rv);
1575 rv = aemp->aem_err_no;
1576 goto jleave;
1579 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1580 jleave:
1581 NYD2_LEAVE;
1582 return rv;
1585 #ifdef HAVE_REGEX
1586 FL char const *
1587 n_regex_err_to_doc(const regex_t *rep, int e){
1588 char *cp;
1589 size_t i;
1590 NYD2_ENTER;
1592 i = regerror(e, rep, NULL, 0) +1;
1593 cp = salloc(i);
1594 regerror(e, rep, cp, i);
1595 NYD2_LEAVE;
1596 return cp;
1598 #endif
1600 /* s-it-mode */