a_aux_idec_atoi[] is indeed constant (DATA -> RODATA!)
[s-mailx.git] / auxlily.c
blob45d6f99a2bda48b82c66ea6e1ed2d67161febf34
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 #if defined HAVE_GETRANDOM && !n_RANDOM_USE_XSSL
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 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
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 const 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 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
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 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
164 static void a_aux_rand_init(void);
165 n_INLINE 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 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
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 * However, support sequential reading to avoid possible hangs that have
193 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
194 * HAVE_GETRANDOM is #defined) */
195 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
196 "Buffer too large to be served without n_ERR_INTR error");
197 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
198 "Buffer too small to serve used array indices");
199 /* C99 */{
200 size_t o, i;
202 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
203 ssize_t gr;
205 gr = HAVE_GETRANDOM(&a_aux_rand->a._dat[o], i);
206 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
207 a_aux_rand->a._dat[84]];
208 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
209 a_aux_rand->a._dat[42]];
210 /* ..but be on the safe side */
211 if(gr > 0){
212 i -= (size_t)gr;
213 if(i == 0)
214 break;
215 o += (size_t)gr;
217 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
218 "waiting a bit\n"));
219 n_msleep(250, FAL0);
223 # else /* HAVE_GETRANDOM */
224 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
225 bool_t ok;
227 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
228 sizeof(a_aux_rand->a._dat)));
229 close(u.fd);
231 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
232 a_aux_rand->a._dat[84]];
233 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
234 a_aux_rand->a._dat[42]];
235 if(ok)
236 goto jleave;
239 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
240 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
241 ui32_t t, k;
243 # ifdef HAVE_CLOCK_GETTIME
244 clock_gettime(CLOCK_REALTIME, &ts);
245 t = (ui32_t)ts.tv_nsec;
246 # else
247 gettimeofday(&ts, NULL);
248 t = (ui32_t)ts.tv_usec;
249 # endif
250 if(rnd & 1)
251 t = (t >> 16) | (t << 16);
252 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
253 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
254 if(rnd == 7 || rnd == 17)
255 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
256 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
257 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
258 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
259 if((rnd & 3) == 3)
260 seed ^= n_prime_next(seed);
264 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
265 a_aux_rand_get8();
266 jleave:
267 # endif /* !HAVE_GETRANDOM */
268 NYD2_LEAVE;
271 n_INLINE ui8_t
272 a_aux_rand_get8(void){
273 ui8_t si, sj;
275 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
276 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
277 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
278 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
279 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
282 # ifndef HAVE_GETRANDOM
283 static ui32_t
284 a_aux_rand_weak(ui32_t seed){
285 /* From "Random number generators: good ones are hard to find",
286 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
287 * October 1988, p. 1195.
288 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
289 ui32_t hi;
291 if(seed == 0)
292 seed = 123459876;
293 hi = seed / 127773;
294 seed %= 127773;
295 seed = (seed * 16807) - (hi * 2836);
296 if((si32_t)seed < 0)
297 seed += SI32_MAX;
298 return seed;
300 # endif /* HAVE_GETRANDOM */
301 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
303 static struct a_aux_err_map const *
304 a_aux_err_map_from_no(si32_t eno){
305 si32_t ecmp;
306 size_t asz;
307 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
308 struct a_aux_err_map const *aemp;
309 NYD2_ENTER;
311 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
313 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
314 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
315 asz != 0; asz >>= 1){
316 tmp = &adat[asz >> 1];
317 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
318 aemp = &a_aux_err_map[(*tmp)[1]];
319 break;
321 if(ecmp > 0){
322 adat = &tmp[1];
323 --asz;
327 NYD2_LEAVE;
328 return aemp;
331 FL void
332 n_locale_init(void){
333 NYD2_ENTER;
335 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
337 #ifndef HAVE_SETLOCALE
338 n_mb_cur_max = 1;
339 #else
340 setlocale(LC_ALL, n_empty);
341 n_mb_cur_max = MB_CUR_MAX;
342 # ifdef HAVE_NL_LANGINFO
343 /* C99 */{
344 char const *cp;
346 if((cp = nl_langinfo(CODESET)) != NULL)
347 /* (Will log during startup if user set that via -S) */
348 ok_vset(ttycharset, cp);
350 # endif /* HAVE_SETLOCALE */
352 # ifdef HAVE_C90AMEND1
353 if(n_mb_cur_max > 1){
354 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
355 n_psonce |= n_PSO_UNICODE;
356 # else
357 wchar_t wc;
358 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
359 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
360 n_psonce |= n_PSO_UNICODE;
361 /* Reset possibly messed up state; luckily this also gives us an
362 * indication whether the encoding has locking shift state sequences */
363 if(mbtowc(&wc, NULL, n_mb_cur_max))
364 n_psonce |= n_PSO_ENC_MBSTATE;
365 # endif
367 # endif
368 #endif /* HAVE_C90AMEND1 */
369 NYD2_LEAVE;
372 FL size_t
373 n_screensize(void){
374 char const *cp;
375 uiz_t rv;
376 NYD2_ENTER;
378 if((cp = ok_vlook(screen)) != NULL){
379 n_idec_uiz_cp(&rv, cp, 0, NULL);
380 if(rv == 0)
381 rv = n_scrnheight;
382 }else
383 rv = n_scrnheight;
385 if(rv > 2)
386 rv -= 2;
387 NYD2_LEAVE;
388 return rv;
391 FL char const *
392 n_pager_get(char const **env_addon){
393 char const *rv;
394 NYD_ENTER;
396 rv = ok_vlook(PAGER);
398 if(env_addon != NULL){
399 *env_addon = NULL;
400 /* Update the manual upon any changes:
401 * *colour-pager*, $PAGER */
402 if(strstr(rv, "less") != NULL){
403 if(getenv("LESS") == NULL)
404 *env_addon = "LESS=RXi";
405 }else if(strstr(rv, "lv") != NULL){
406 if(getenv("LV") == NULL)
407 *env_addon = "LV=-c";
410 NYD_LEAVE;
411 return rv;
414 FL void
415 page_or_print(FILE *fp, size_t lines)
417 int c;
418 char const *cp;
419 NYD_ENTER;
421 fflush_rewind(fp);
423 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
424 size_t rows;
426 if(*cp == '\0')
427 rows = (size_t)n_scrnheight;
428 else
429 n_idec_uiz_cp(&rows, cp, 0, NULL);
431 if (rows > 0 && lines == 0) {
432 while ((c = getc(fp)) != EOF)
433 if (c == '\n' && ++lines >= rows)
434 break;
435 really_rewind(fp);
438 if (lines >= rows) {
439 char const *env_add[2], *pager;
441 pager = n_pager_get(&env_add[0]);
442 env_add[1] = NULL;
443 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
444 env_add, NULL);
445 goto jleave;
449 while ((c = getc(fp)) != EOF)
450 putc(c, n_stdout);
451 jleave:
452 NYD_LEAVE;
455 FL enum protocol
456 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
457 char const **adjusted_or_null)
459 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
460 char const *cp, *orig_name;
461 enum protocol rv = PROTO_UNKNOWN;
462 NYD_ENTER;
464 if(name[0] == '%' && name[1] == ':')
465 name += 2;
466 orig_name = name;
468 for (cp = name; *cp && *cp != ':'; cp++)
469 if (!alnumchar(*cp))
470 goto jfile;
472 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
473 if(!strncmp(name, "file", sizeof("file") -1) ||
474 !strncmp(name, "mbox", sizeof("mbox") -1))
475 rv = PROTO_FILE;
476 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
477 rv = PROTO_MAILDIR;
478 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
479 #ifdef HAVE_POP3
480 rv = PROTO_POP3;
481 #else
482 n_err(_("No POP3 support compiled in\n"));
483 #endif
484 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
485 #if defined HAVE_POP3 && defined HAVE_SSL
486 rv = PROTO_POP3;
487 #else
488 n_err(_("No POP3S support compiled in\n"));
489 #endif
491 else if(!strncmp(name, "imap", sizeof("imap") -1)){
492 #ifdef HAVE_IMAP
493 rv = PROTO_IMAP;
494 #else
495 n_err(_("No IMAP support compiled in\n"));
496 #endif
497 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
498 #if defined HAVE_IMAP && defined HAVE_SSL
499 rv = PROTO_IMAP;
500 #else
501 n_err(_("No IMAPS support compiled in\n"));
502 #endif
504 orig_name = &cp[3];
505 goto jleave;
508 jfile:
509 rv = PROTO_FILE;
511 if(check_stat || try_hooks){
512 struct n_file_type ft;
513 struct stat stb;
514 char *np;
515 size_t sz;
517 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
518 memcpy(np, name, sz + 1);
520 if(!stat(name, &stb)){
521 if(S_ISDIR(stb.st_mode) &&
522 (memcpy(&np[sz], "/tmp", 5),
523 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
524 (memcpy(&np[sz], "/new", 5),
525 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
526 (memcpy(&np[sz], "/cur", 5),
527 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
528 rv = PROTO_MAILDIR;
529 }else if(try_hooks && n_filetype_trial(&ft, name))
530 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
531 else if((cp = ok_vlook(newfolders)) != NULL &&
532 !asccasecmp(cp, "maildir"))
533 rv = PROTO_MAILDIR;
535 n_lofi_free(np);
537 jleave:
538 if(adjusted_or_null != NULL)
539 *adjusted_or_null = orig_name;
540 NYD_LEAVE;
541 return rv;
544 FL char *
545 n_c_to_hex_base16(char store[3], char c){
546 static char const itoa16[] = "0123456789ABCDEF";
547 NYD2_ENTER;
549 store[2] = '\0';
550 store[1] = itoa16[(ui8_t)c & 0x0F];
551 c = ((ui8_t)c >> 4) & 0x0F;
552 store[0] = itoa16[(ui8_t)c];
553 NYD2_LEAVE;
554 return store;
557 FL si32_t
558 n_c_from_hex_base16(char const hex[2]){
559 static ui8_t const atoi16[] = {
560 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
561 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
562 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
563 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
564 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
565 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
566 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
568 ui8_t i1, i2;
569 si32_t rv;
570 NYD2_ENTER;
572 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
573 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
574 goto jerr;
575 i1 = atoi16[i1];
576 i2 = atoi16[i2];
577 if ((i1 | i2) & 0xF0u)
578 goto jerr;
579 rv = i1;
580 rv <<= 4;
581 rv += i2;
582 jleave:
583 NYD2_LEAVE;
584 return rv;
585 jerr:
586 rv = -1;
587 goto jleave;
590 FL enum n_idec_state
591 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
592 enum n_idec_mode idm, char const **endptr_or_null){
593 /* XXX Brute simple and */
594 ui8_t currc;
595 ui64_t res, cut;
596 enum n_idec_state rv;
597 NYD_ENTER;
599 idm &= n__IDEC_MODE_MASK;
600 rv = n_IDEC_STATE_NONE | idm;
601 res = 0;
603 if(clen == UIZ_MAX){
604 if(*cbuf == '\0')
605 goto jeinval;
606 }else if(clen == 0)
607 goto jeinval;
609 assert(base != 1 && base <= 36);
610 /*if(base == 1 || base > 36)
611 * goto jeinval;*/
613 /* Leading WS */
614 while(spacechar(*cbuf))
615 if(*++cbuf == '\0' || --clen == 0)
616 goto jeinval;
618 /* Check sign */
619 switch(*cbuf){
620 case '-':
621 rv |= n_IDEC_STATE_SEEN_MINUS;
622 /* FALLTHROUGH */
623 case '+':
624 if(*++cbuf == '\0' || --clen == 0)
625 goto jeinval;
626 break;
629 /* Base detection/skip */
630 if(*cbuf != '0'){
631 if(base == 0){
632 base = 10;
634 /* Support BASE#number prefix, where BASE is decimal 2-36 */
635 if(clen > 1){
636 char c1, c2;
638 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
639 (((c2 = cbuf[1]) == '#') ||
640 (c2 >= '0' && c2 <= '9' && clen > 2 && cbuf[2] == '#'))){
641 base = a_aux_idec_atoi[(ui8_t)c1];
642 if(c2 == '#')
643 clen -= 2, cbuf += 2;
644 else{
645 clen -= 3, cbuf += 3;
646 base *= 10;
647 base += a_aux_idec_atoi[(ui8_t)c2];
649 if(base < 2 || base > 36)
650 goto jeinval;
655 /* Character must be valid for base */
656 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
657 if(currc >= base)
658 goto jeinval;
659 }else{
660 /* 0 always valid as is, fallback base 10 */
661 if(*++cbuf == '\0' || --clen == 0)
662 goto jleave;
664 /* Base "detection" */
665 if(base == 0 || base == 2 || base == 16){
666 switch(*cbuf){
667 case 'x':
668 case 'X':
669 if((base & 2) == 0){
670 base = 0x10;
671 goto jprefix_skip;
673 break;
674 case 'b':
675 case 'B':
676 if((base & 16) == 0){
677 base = 2; /* 0b10 */
678 /* Char after prefix must be valid. However, after some error
679 * in the tor software all libraries (which had to) turned to
680 * an interpretation of the C standard which says that the
681 * prefix may optionally precede an otherwise valid sequence,
682 * which means that "0x" is not a STATE_INVAL error but gives
683 * a "0" result with a "STATE_BASE" error and a rest of "x" */
684 jprefix_skip:
685 #if 1
686 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
687 --clen, ++cbuf;
688 #else
689 if(*++cbuf == '\0' || --clen == 0)
690 goto jeinval;
692 /* Character must be valid for base, invalid otherwise */
693 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
694 if(currc >= base)
695 goto jeinval;
696 #endif
698 break;
699 default:
700 if(base == 0)
701 base = 010;
702 break;
706 /* Character must be valid for base, _EBASE otherwise */
707 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
708 if(currc >= base)
709 goto jebase;
712 for(cut = a_aux_idec_cutlimit[base - 2];;){
713 if(res >= cut){
714 if(res == cut){
715 res *= base;
716 if(res > UI64_MAX - currc)
717 goto jeover;
718 res += currc;
719 }else
720 goto jeover;
721 }else{
722 res *= base;
723 res += currc;
726 if(*++cbuf == '\0' || --clen == 0)
727 break;
729 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
730 if(currc >= base)
731 goto jebase;
734 jleave:
736 ui64_t uimask;
738 switch(rv & n__IDEC_MODE_LIMIT_MASK){
739 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
740 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
741 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
742 default: uimask = UI64_MAX; break;
744 if(rv & n_IDEC_MODE_SIGNED_TYPE)
745 uimask >>= 1;
747 if(res & ~uimask){
748 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
749 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
750 if(res > uimask + 1){
751 res = uimask << 1;
752 res &= ~uimask;
753 }else{
754 res = -res;
755 break;
757 }else
758 res = uimask;
759 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
760 rv |= n_IDEC_STATE_EOVERFLOW;
761 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
762 res = -res;
763 }while(0);
765 switch(rv & n__IDEC_MODE_LIMIT_MASK){
766 case n_IDEC_MODE_LIMIT_8BIT:
767 if(rv & n_IDEC_MODE_SIGNED_TYPE)
768 *(si8_t*)resp = (si8_t)res;
769 else
770 *(ui8_t*)resp = (ui8_t)res;
771 break;
772 case n_IDEC_MODE_LIMIT_16BIT:
773 if(rv & n_IDEC_MODE_SIGNED_TYPE)
774 *(si16_t*)resp = (si16_t)res;
775 else
776 *(ui16_t*)resp = (ui16_t)res;
777 break;
778 case n_IDEC_MODE_LIMIT_32BIT:
779 if(rv & n_IDEC_MODE_SIGNED_TYPE)
780 *(si32_t*)resp = (si32_t)res;
781 else
782 *(ui32_t*)resp = (ui32_t)res;
783 break;
784 default:
785 if(rv & n_IDEC_MODE_SIGNED_TYPE)
786 *(si64_t*)resp = (si64_t)res;
787 else
788 *(ui64_t*)resp = (ui64_t)res;
789 break;
792 if(endptr_or_null != NULL)
793 *endptr_or_null = cbuf;
794 if(*cbuf == '\0' || clen == 0)
795 rv |= n_IDEC_STATE_CONSUMED;
796 NYD_LEAVE;
797 return rv;
799 jeinval:
800 rv |= n_IDEC_STATE_EINVAL;
801 goto j_maxval;
802 jebase:
803 /* Not a base error for terminator and whitespace! */
804 if(*cbuf != '\0' && !spacechar(*cbuf))
805 rv |= n_IDEC_STATE_EBASE;
806 goto jleave;
808 jeover:
809 /* Overflow error: consume input until bad character or length out */
810 for(;;){
811 if(*++cbuf == '\0' || --clen == 0)
812 break;
813 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
814 if(currc >= base)
815 break;
818 rv |= n_IDEC_STATE_EOVERFLOW;
819 j_maxval:
820 if(rv & n_IDEC_MODE_SIGNED_TYPE)
821 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
822 : (ui64_t)SI64_MAX;
823 else
824 res = UI64_MAX;
825 rv &= ~n_IDEC_STATE_SEEN_MINUS;
826 goto jleave;
829 FL ui32_t
830 n_torek_hash(char const *name){
831 /* Chris Torek's hash */
832 char c;
833 ui32_t h;
834 NYD2_ENTER;
836 for(h = 0; (c = *name++) != '\0';)
837 h = (h * 33) + c;
838 NYD2_LEAVE;
839 return h;
842 FL ui32_t
843 n_torek_ihashn(char const *dat, size_t len){
844 /* See n_torek_hash() */
845 char c;
846 ui32_t h;
847 NYD2_ENTER;
849 if(len == UIZ_MAX)
850 for(h = 0; (c = *dat++) != '\0';)
851 h = (h * 33) + lowerconv(c);
852 else
853 for(h = 0; len > 0; --len){
854 c = *dat++;
855 h = (h * 33) + lowerconv(c);
857 NYD2_LEAVE;
858 return h;
861 FL ui32_t
862 n_prime_next(ui32_t n){
863 static ui32_t const primes[] = {
864 5, 11, 23, 47, 97, 157, 283,
865 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
866 131071, 262139, 524287, 1048573, 2097143, 4194301,
867 8388593, 16777213, 33554393, 67108859, 134217689,
868 268435399, 536870909, 1073741789, 2147483647
870 ui32_t i, mprime;
871 NYD2_ENTER;
873 i = (n < primes[n_NELEM(primes) / 4] ? 0
874 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
875 : n_NELEM(primes) / 2));
877 do if((mprime = primes[i]) > n)
878 break;
879 while(++i < n_NELEM(primes));
881 if(i == n_NELEM(primes) && mprime < n)
882 mprime = n;
883 NYD2_LEAVE;
884 return mprime;
887 FL char const *
888 n_getdeadletter(void){
889 char const *cp;
890 bool_t bla;
891 NYD_ENTER;
893 bla = FAL0;
894 jredo:
895 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
896 if(cp == NULL || strlen(cp) >= PATH_MAX){
897 if(!bla){
898 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
899 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
900 ok_vclear(DEAD);
901 bla = TRU1;
902 goto jredo;
903 }else{
904 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
905 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
908 NYD_LEAVE;
909 return cp;
912 FL char *
913 n_nodename(bool_t mayoverride){
914 static char *sys_hostname, *hostname; /* XXX free-at-exit */
916 struct utsname ut;
917 char *hn;
918 #ifdef HAVE_SOCKETS
919 # ifdef HAVE_GETADDRINFO
920 struct addrinfo hints, *res;
921 # else
922 struct hostent *hent;
923 # endif
924 #endif
925 NYD2_ENTER;
927 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
929 }else if((hn = sys_hostname) == NULL){
930 uname(&ut);
931 hn = ut.nodename;
932 #ifdef HAVE_SOCKETS
933 # ifdef HAVE_GETADDRINFO
934 memset(&hints, 0, sizeof hints);
935 hints.ai_family = AF_UNSPEC;
936 hints.ai_flags = AI_CANONNAME;
937 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
938 if(res->ai_canonname != NULL){
939 size_t l;
941 l = strlen(res->ai_canonname) +1;
942 hn = n_lofi_alloc(l);
943 memcpy(hn, res->ai_canonname, l);
945 freeaddrinfo(res);
947 # else
948 hent = gethostbyname(hn);
949 if(hent != NULL)
950 hn = hent->h_name;
951 # endif
952 #endif
953 sys_hostname = sstrdup(hn);
954 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
955 if(hn != ut.nodename)
956 n_lofi_free(hn);
957 #endif
958 hn = sys_hostname;
961 if(hostname != NULL && hostname != sys_hostname)
962 n_free(hostname);
963 hostname = sstrdup(hn);
964 NYD2_LEAVE;
965 return hostname;
968 #ifdef HAVE_IDNA
969 FL bool_t
970 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
971 char *idna_utf8;
972 bool_t rv;
973 NYD_ENTER;
975 if((rv = (ilen == 0)))
976 goto jleave;
978 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
979 ibuf = savestrbuf(ibuf, ilen);
980 ilen = 0;
982 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
983 ibuf);
984 if(idna_utf8 == NULL)
985 goto jleave;
987 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
988 /* C99 */{
989 char *idna_ascii;
991 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
992 out = n_string_assign_cp(out, idna_ascii);
993 idn_free(idna_ascii);
994 rv = TRU1;
995 ilen = out->s_len;
998 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
999 ilen = strlen(idna_utf8);
1000 jredo:
1001 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
1002 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1003 case idn_buffer_overflow:
1004 ilen += HOST_NAME_MAX +1;
1005 goto jredo;
1006 case idn_success:
1007 rv = TRU1;
1008 ilen = strlen(out->s_dat);
1009 break;
1010 default:
1011 ilen = 0;
1012 break;
1015 # else
1016 # error Unknown HAVE_IDNA
1017 # endif
1018 jleave:
1019 out = n_string_trunc(out, ilen);
1020 NYD_LEAVE;
1021 return rv;
1023 #endif /* HAVE_IDNA */
1025 FL char *
1026 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1027 struct str b64;
1028 char *indat, *cp, *oudat;
1029 size_t i, inlen, oulen;
1030 NYD_ENTER;
1032 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1033 n_psonce |= n_PSO_RANDOM_INIT;
1035 if(n_poption & n_PO_D_V){
1036 char const *prngn;
1038 #if n_RANDOM_USE_XSSL
1039 prngn = "*SSL RAND_*";
1040 #elif defined HAVE_POSIX_RANDOM
1041 prngn = "POSIX/arc4random";
1042 #else
1043 prngn = "builtin ARC4";
1044 #endif
1045 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1048 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1049 a_aux_rand_init();
1050 #endif
1053 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1054 * with PAD stripped is still longer than what the user requests, easy way.
1055 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1056 * include the base64 PAD characters in our random string: give some pad */
1057 i = len;
1058 if((inlen = i % 3) != 0)
1059 i += 3 - inlen;
1060 jinc1:
1061 inlen = i >> 2;
1062 oulen = inlen << 2;
1063 if(oulen < len){
1064 i += 3;
1065 goto jinc1;
1067 inlen = inlen + (inlen << 1);
1069 indat = n_lofi_alloc(inlen +1);
1071 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1072 #if n_RANDOM_USE_XSSL
1073 ssl_rand_bytes(indat, inlen);
1074 #elif !defined HAVE_POSIX_RANDOM
1075 for(i = inlen; i-- > 0;)
1076 indat[i] = (char)a_aux_rand_get8();
1077 #else
1078 for(cp = indat, i = inlen; i > 0;){
1079 union {ui32_t i4; char c[4];} r;
1080 size_t j;
1082 r.i4 = (ui32_t)arc4random();
1083 switch((j = i & 3)){
1084 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1085 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1086 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1087 default: cp[0] = r.c[0]; break;
1089 cp += j;
1090 i -= j;
1092 #endif
1093 }else{
1094 for(cp = indat, i = inlen; i > 0;){
1095 union {ui32_t i4; char c[4];} r;
1096 size_t j;
1098 r.i4 = ++*reprocnt_or_null;
1099 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1100 char x;
1102 x = r.c[0];
1103 r.c[0] = r.c[3];
1104 r.c[3] = x;
1105 x = r.c[1];
1106 r.c[1] = r.c[2];
1107 r.c[2] = x;
1109 switch((j = i & 3)){
1110 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1111 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1112 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1113 default: cp[0] = r.c[0]; break;
1115 cp += j;
1116 i -= j;
1120 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1121 b64.s = oudat;
1122 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1123 assert(b64.l >= len);
1124 memcpy(dat, b64.s, len);
1125 dat[len] = '\0';
1126 if(oudat != dat)
1127 n_lofi_free(oudat);
1129 n_lofi_free(indat);
1131 NYD_LEAVE;
1132 return dat;
1135 FL char *
1136 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1137 char *dat;
1138 NYD_ENTER;
1140 dat = n_autorec_alloc(len +1);
1141 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1142 NYD_LEAVE;
1143 return dat;
1146 FL si8_t
1147 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1149 si8_t rv;
1150 NYD_ENTER;
1152 assert(inlen == 0 || inbuf != NULL);
1154 if (inlen == UIZ_MAX)
1155 inlen = strlen(inbuf);
1157 if (inlen == 0)
1158 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1159 else {
1160 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1161 !ascncasecmp(inbuf, "true", inlen) ||
1162 !ascncasecmp(inbuf, "yes", inlen) ||
1163 !ascncasecmp(inbuf, "on", inlen))
1164 rv = 1;
1165 else if ((inlen == 1 &&
1166 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1167 !ascncasecmp(inbuf, "false", inlen) ||
1168 !ascncasecmp(inbuf, "no", inlen) ||
1169 !ascncasecmp(inbuf, "off", inlen))
1170 rv = 0;
1171 else {
1172 ui64_t ib;
1174 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1175 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1176 ) != n_IDEC_STATE_CONSUMED)
1177 rv = -1;
1178 else
1179 rv = (ib != 0);
1182 NYD_LEAVE;
1183 return rv;
1186 FL si8_t
1187 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1189 si8_t rv;
1190 NYD_ENTER;
1192 assert(inlen == 0 || inbuf != NULL);
1194 if (inlen == UIZ_MAX)
1195 inlen = strlen(inbuf);
1197 if (inlen == 0)
1198 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1199 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1200 !ascncasecmp(inbuf, "ask-", 4) &&
1201 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1202 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1203 rv = getapproval(prompt, rv);
1204 NYD_LEAVE;
1205 return rv;
1208 FL bool_t
1209 n_is_all_or_aster(char const *name){
1210 bool_t rv;
1211 NYD_ENTER;
1213 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1214 NYD_LEAVE;
1215 return rv;
1218 FL struct n_timespec const *
1219 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1220 static struct n_timespec ts_now;
1221 NYD2_ENTER;
1223 if(n_psonce & n_PSO_REPRODUCIBLE){
1224 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1225 ts_now.ts_nsec = 0;
1226 }else if(force_update || ts_now.ts_sec == 0){
1227 #ifdef HAVE_CLOCK_GETTIME
1228 struct timespec ts;
1230 clock_gettime(CLOCK_REALTIME, &ts);
1231 ts_now.ts_sec = (si64_t)ts.tv_sec;
1232 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1233 #elif defined HAVE_GETTIMEOFDAY
1234 struct timeval tv;
1236 gettimeofday(&tv, NULL);
1237 ts_now.ts_sec = (si64_t)tv.tv_sec;
1238 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1239 #else
1240 ts_now.ts_sec = (si64_t)time(NULL);
1241 ts_now.ts_nsec = 0;
1242 #endif
1244 NYD2_LEAVE;
1245 return &ts_now;
1248 FL void
1249 time_current_update(struct time_current *tc, bool_t full_update)
1251 NYD_ENTER;
1252 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1253 if (full_update) {
1254 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1255 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1256 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1258 NYD_LEAVE;
1261 FL uiz_t
1262 n_msleep(uiz_t millis, bool_t ignint){
1263 uiz_t rv;
1264 NYD2_ENTER;
1266 #ifdef HAVE_NANOSLEEP
1267 /* C99 */{
1268 struct timespec ts, trem;
1269 int i;
1271 ts.tv_sec = millis / 1000;
1272 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1274 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1275 ts = trem;
1276 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1279 #elif defined HAVE_SLEEP
1280 if((millis /= 1000) == 0)
1281 millis = 1;
1282 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1283 millis = rv;
1284 #else
1285 # error Configuration should have detected a function for sleeping.
1286 #endif
1288 NYD2_LEAVE;
1289 return rv;
1292 FL void
1293 n_err(char const *format, ...){
1294 va_list ap;
1295 NYD2_ENTER;
1297 va_start(ap, format);
1298 #ifdef HAVE_ERRORS
1299 if(n_psonce & n_PSO_INTERACTIVE)
1300 n_verr(format, ap);
1301 else
1302 #endif
1304 size_t len;
1305 bool_t doname;
1307 doname = FAL0;
1309 while(*format == '\n'){
1310 doname = TRU1;
1311 putc('\n', n_stderr);
1312 ++format;
1315 if(doname)
1316 a_aux_err_linelen = 0;
1318 if((len = strlen(format)) > 0){
1319 if(doname || a_aux_err_linelen == 0){
1320 char const *cp;
1322 if(*(cp = ok_vlook(log_prefix)) != '\0')
1323 fputs(cp, n_stderr);
1325 vfprintf(n_stderr, format, ap);
1327 /* C99 */{
1328 size_t i = len;
1330 if(format[--len] == '\n'){
1331 a_aux_err_linelen = (i -= ++len);
1332 break;
1334 ++a_aux_err_linelen;
1335 }while(len > 0);
1339 fflush(n_stderr);
1341 va_end(ap);
1342 NYD2_LEAVE;
1345 FL void
1346 n_verr(char const *format, va_list ap){
1347 #ifdef HAVE_ERRORS
1348 struct a_aux_err_node *enp;
1349 #endif
1350 bool_t doname;
1351 size_t len;
1352 NYD2_ENTER;
1354 doname = FAL0;
1356 while(*format == '\n'){
1357 putc('\n', n_stderr);
1358 doname = TRU1;
1359 ++format;
1362 if(doname){
1363 a_aux_err_linelen = 0;
1364 #ifdef HAVE_ERRORS
1365 if(n_psonce & n_PSO_INTERACTIVE){
1366 if((enp = a_aux_err_tail) != NULL &&
1367 (enp->ae_str.s_len > 0 &&
1368 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1369 n_string_push_c(&enp->ae_str, '\n');
1371 #endif
1374 if((len = strlen(format)) == 0)
1375 goto jleave;
1376 #ifdef HAVE_ERRORS
1377 n_pstate |= n_PS_ERRORS_PROMPT;
1378 #endif
1380 if(doname || a_aux_err_linelen == 0){
1381 char const *cp;
1383 if(*(cp = ok_vlook(log_prefix)) != '\0')
1384 fputs(cp, n_stderr);
1387 /* C99 */{
1388 size_t i = len;
1390 if(format[--len] == '\n'){
1391 a_aux_err_linelen = (i -= ++len);
1392 break;
1394 ++a_aux_err_linelen;
1395 }while(len > 0);
1398 #ifdef HAVE_ERRORS
1399 if(!(n_psonce & n_PSO_INTERACTIVE))
1400 #endif
1401 vfprintf(n_stderr, format, ap);
1402 #ifdef HAVE_ERRORS
1403 else{
1404 int imax, i;
1405 n_LCTAV(ERRORS_MAX > 3);
1407 /* Link it into the `errors' message ring */
1408 if((enp = a_aux_err_tail) == NULL){
1409 jcreat:
1410 enp = smalloc(sizeof *enp);
1411 enp->ae_next = NULL;
1412 n_string_creat(&enp->ae_str);
1413 if(a_aux_err_tail != NULL)
1414 a_aux_err_tail->ae_next = enp;
1415 else
1416 a_aux_err_head = enp;
1417 a_aux_err_tail = enp;
1418 ++a_aux_err_cnt;
1419 }else if(doname ||
1420 (enp->ae_str.s_len > 0 &&
1421 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1422 if(a_aux_err_cnt < ERRORS_MAX)
1423 goto jcreat;
1425 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1426 a_aux_err_tail->ae_next = enp;
1427 a_aux_err_tail = enp;
1428 enp->ae_next = NULL;
1429 n_string_trunc(&enp->ae_str, 0);
1432 # ifdef HAVE_N_VA_COPY
1433 imax = 64;
1434 # else
1435 imax = n_MIN(LINESIZE, 1024);
1436 # endif
1437 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1438 # ifdef HAVE_N_VA_COPY
1439 va_list vac;
1441 n_va_copy(vac, ap);
1442 # else
1443 # define vac ap
1444 # endif
1446 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1447 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1448 # ifdef HAVE_N_VA_COPY
1449 va_end(vac);
1450 # else
1451 # undef vac
1452 # endif
1453 if(i <= 0)
1454 goto jleave;
1455 if(UICMP(z, i, >=, imax)){
1456 # ifdef HAVE_N_VA_COPY
1457 /* XXX Check overflow for upcoming LEN+++i! */
1458 n_string_trunc(&enp->ae_str, len);
1459 continue;
1460 # else
1461 i = (int)strlen(&enp->ae_str.s_dat[len]);
1462 # endif
1464 break;
1466 n_string_trunc(&enp->ae_str, len + (size_t)i);
1468 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1470 #endif /* HAVE_ERRORS */
1472 jleave:
1473 fflush(n_stderr);
1474 NYD2_LEAVE;
1477 FL void
1478 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1479 va_list ap;
1480 NYD_X;
1482 va_start(ap, format);
1483 vfprintf(n_stderr, format, ap);
1484 va_end(ap);
1485 fflush(n_stderr);
1488 FL void
1489 n_perr(char const *msg, int errval){
1490 int e;
1491 char const *fmt;
1492 NYD2_ENTER;
1494 if(msg == NULL){
1495 fmt = "%s%s\n";
1496 msg = n_empty;
1497 }else
1498 fmt = "%s: %s\n";
1500 e = (errval == 0) ? n_err_no : errval;
1501 n_err(fmt, msg, n_err_to_doc(e));
1502 if(errval == 0)
1503 n_err_no = e;
1504 NYD2_LEAVE;
1507 FL void
1508 n_alert(char const *format, ...){
1509 va_list ap;
1510 NYD2_ENTER;
1512 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1514 va_start(ap, format);
1515 n_verr(format, ap);
1516 va_end(ap);
1518 n_err("\n");
1519 NYD2_LEAVE;
1522 FL void
1523 n_panic(char const *format, ...){
1524 va_list ap;
1525 NYD2_ENTER;
1527 if(a_aux_err_linelen > 0){
1528 putc('\n', n_stderr);
1529 a_aux_err_linelen = 0;
1531 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1533 va_start(ap, format);
1534 vfprintf(n_stderr, format, ap);
1535 va_end(ap);
1537 putc('\n', n_stderr);
1538 fflush(n_stderr);
1539 NYD2_LEAVE;
1540 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1543 #ifdef HAVE_ERRORS
1544 FL int
1545 c_errors(void *v){
1546 char **argv = v;
1547 struct a_aux_err_node *enp;
1548 NYD_ENTER;
1550 if(*argv == NULL)
1551 goto jlist;
1552 if(argv[1] != NULL)
1553 goto jerr;
1554 if(!asccasecmp(*argv, "show"))
1555 goto jlist;
1556 if(!asccasecmp(*argv, "clear"))
1557 goto jclear;
1558 jerr:
1559 fprintf(n_stderr,
1560 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1561 v = NULL;
1562 jleave:
1563 NYD_LEAVE;
1564 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1566 jlist:{
1567 FILE *fp;
1568 size_t i;
1570 if(a_aux_err_head == NULL){
1571 fprintf(n_stderr, _("The error ring is empty\n"));
1572 goto jleave;
1575 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1576 NULL){
1577 fprintf(n_stderr, _("tmpfile"));
1578 v = NULL;
1579 goto jleave;
1582 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1583 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1584 /* We don't know whether last string ended with NL; be simple XXX */
1585 putc('\n', fp);
1587 page_or_print(fp, 0);
1588 Fclose(fp);
1590 /* FALLTHRU */
1592 jclear:
1593 a_aux_err_tail = NULL;
1594 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1595 a_aux_err_linelen = 0;
1596 while((enp = a_aux_err_head) != NULL){
1597 a_aux_err_head = enp->ae_next;
1598 n_string_gut(&enp->ae_str);
1599 free(enp);
1601 goto jleave;
1603 #endif /* HAVE_ERRORS */
1605 FL char const *
1606 n_err_to_doc(si32_t eno){
1607 char const *rv;
1608 struct a_aux_err_map const *aemp;
1609 NYD2_ENTER;
1611 aemp = a_aux_err_map_from_no(eno);
1612 rv = &a_aux_err_docs[aemp->aem_docoff];
1613 NYD2_LEAVE;
1614 return rv;
1617 FL char const *
1618 n_err_to_name(si32_t eno){
1619 char const *rv;
1620 struct a_aux_err_map const *aemp;
1621 NYD2_ENTER;
1623 aemp = a_aux_err_map_from_no(eno);
1624 rv = &a_aux_err_names[aemp->aem_nameoff];
1625 NYD2_LEAVE;
1626 return rv;
1629 FL si32_t
1630 n_err_from_name(char const *name){
1631 struct a_aux_err_map const *aemp;
1632 ui32_t hash, i, j, x;
1633 si32_t rv;
1634 NYD2_ENTER;
1636 hash = n_torek_hash(name);
1638 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1639 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1640 break;
1642 aemp = &a_aux_err_map[x];
1643 if(aemp->aem_hash == hash &&
1644 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1645 rv = aemp->aem_err_no;
1646 goto jleave;
1649 if(++i == a_AUX_ERR_REV_PRIME){
1650 #ifdef a_AUX_ERR_REV_WRAPAROUND
1651 i = 0;
1652 #else
1653 break;
1654 #endif
1658 /* Have not found it. But wait, it could be that the user did, e.g.,
1659 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1660 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1661 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1662 ) == n_IDEC_STATE_CONSUMED){
1663 aemp = a_aux_err_map_from_no(rv);
1664 rv = aemp->aem_err_no;
1665 goto jleave;
1668 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1669 jleave:
1670 NYD2_LEAVE;
1671 return rv;
1674 #ifdef HAVE_REGEX
1675 FL char const *
1676 n_regex_err_to_doc(const regex_t *rep, int e){
1677 char *cp;
1678 size_t i;
1679 NYD2_ENTER;
1681 i = regerror(e, rep, NULL, 0) +1;
1682 cp = salloc(i);
1683 regerror(e, rep, cp, i);
1684 NYD2_LEAVE;
1685 return cp;
1687 #endif
1689 /* s-it-mode */