n_shexp_parse_token(): (finally) get rid of CC warning
[s-mailx.git] / auxlily.c
blob45e7cf0b9a024f39cddc454239c0d5143dd2e540
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 - 2018 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 /* IDEC: byte to integer value lookup table */
101 static ui8_t const a_aux_idec_atoi[256] = {
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,0xFF,0xFF,
106 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
107 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
109 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
110 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
111 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
112 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
113 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
114 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
127 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
130 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
131 #define a_X(X) (UI64_MAX / (X))
132 static ui64_t const a_aux_idec_cutlimit[35] = {
133 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
134 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
135 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
136 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
137 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
139 #undef a_X
141 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
142 static ui8_t const a_aux_ienc_shifts[35] = {
143 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
144 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
146 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
149 /* IENC: integer to byte lookup tables */
150 static char const a_aux_ienc_itoa_upper[36] =
151 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
152 static char const a_aux_ienc_itoa_lower[36] =
153 "0123456789abcdefghijklmnopqrstuvwxyz";
155 /* Include the constant make-errors.sh output */
156 #include <gen-errors.h>
158 /* And these things come from mk-config.h (config-time make-errors.sh output) */
159 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
160 #undef a_X
161 #define a_X(N,I) {N,I},
162 n__ERR_NUMBER_TO_MAPOFF
163 #undef a_X
166 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
167 static union rand_state *a_aux_rand;
168 #endif
170 /* Error ring, for `errors' */
171 #ifdef HAVE_ERRORS
172 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
173 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
174 #endif
175 static size_t a_aux_err_linelen;
177 /* Our ARC4 random generator with its completely unacademical pseudo
178 * initialization (shall /dev/urandom fail) */
179 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
180 static void a_aux_rand_init(void);
181 n_INLINE ui8_t a_aux_rand_get8(void);
182 # ifndef HAVE_GETRANDOM
183 static ui32_t a_aux_rand_weak(ui32_t seed);
184 # endif
185 #endif
187 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
188 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
190 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
191 static void
192 a_aux_rand_init(void){
193 # ifndef HAVE_GETRANDOM
194 # ifdef HAVE_CLOCK_GETTIME
195 struct timespec ts;
196 # else
197 struct timeval ts;
198 # endif
199 union {int fd; size_t i;} u;
200 ui32_t seed, rnd;
201 # endif
202 NYD2_ENTER;
204 a_aux_rand = n_alloc(sizeof *a_aux_rand);
206 # ifdef HAVE_GETRANDOM
207 /* getrandom(2) guarantees 256 without n_ERR_INTR..
208 * However, support sequential reading to avoid possible hangs that have
209 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
210 * HAVE_GETRANDOM is #defined) */
211 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
212 "Buffer too large to be served without n_ERR_INTR error");
213 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
214 "Buffer too small to serve used array indices");
215 /* C99 */{
216 size_t o, i;
218 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
219 ssize_t gr;
221 gr = HAVE_GETRANDOM(&a_aux_rand->a._dat[o], i);
222 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
223 a_aux_rand->a._dat[84]];
224 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
225 a_aux_rand->a._dat[42]];
226 /* ..but be on the safe side */
227 if(gr > 0){
228 i -= (size_t)gr;
229 if(i == 0)
230 break;
231 o += (size_t)gr;
233 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
234 "waiting a bit\n"));
235 n_msleep(250, FAL0);
239 # else /* HAVE_GETRANDOM */
240 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
241 bool_t ok;
243 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
244 sizeof(a_aux_rand->a._dat)));
245 close(u.fd);
247 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
248 a_aux_rand->a._dat[84]];
249 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
250 a_aux_rand->a._dat[42]];
251 if(ok)
252 goto jleave;
255 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
256 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
257 ui32_t t, k;
259 # ifdef HAVE_CLOCK_GETTIME
260 clock_gettime(CLOCK_REALTIME, &ts);
261 t = (ui32_t)ts.tv_nsec;
262 # else
263 gettimeofday(&ts, NULL);
264 t = (ui32_t)ts.tv_usec;
265 # endif
266 if(rnd & 1)
267 t = (t >> 16) | (t << 16);
268 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
269 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
270 if(rnd == 7 || rnd == 17)
271 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
272 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
273 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
274 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
275 if((rnd & 3) == 3)
276 seed ^= n_prime_next(seed);
280 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
281 a_aux_rand_get8();
282 jleave:
283 # endif /* !HAVE_GETRANDOM */
284 NYD2_LEAVE;
287 n_INLINE ui8_t
288 a_aux_rand_get8(void){
289 ui8_t si, sj;
291 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
292 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
293 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
294 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
295 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
298 # ifndef HAVE_GETRANDOM
299 static ui32_t
300 a_aux_rand_weak(ui32_t seed){
301 /* From "Random number generators: good ones are hard to find",
302 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
303 * October 1988, p. 1195.
304 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
305 ui32_t hi;
307 if(seed == 0)
308 seed = 123459876;
309 hi = seed / 127773;
310 seed %= 127773;
311 seed = (seed * 16807) - (hi * 2836);
312 if((si32_t)seed < 0)
313 seed += SI32_MAX;
314 return seed;
316 # endif /* HAVE_GETRANDOM */
317 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
319 static struct a_aux_err_map const *
320 a_aux_err_map_from_no(si32_t eno){
321 si32_t ecmp;
322 size_t asz;
323 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
324 struct a_aux_err_map const *aemp;
325 NYD2_ENTER;
327 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
329 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
330 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
331 asz != 0; asz >>= 1){
332 tmp = &adat[asz >> 1];
333 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
334 aemp = &a_aux_err_map[(*tmp)[1]];
335 break;
337 if(ecmp > 0){
338 adat = &tmp[1];
339 --asz;
343 NYD2_LEAVE;
344 return aemp;
347 FL void
348 n_locale_init(void){
349 NYD2_ENTER;
351 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
353 #ifndef HAVE_SETLOCALE
354 n_mb_cur_max = 1;
355 #else
356 setlocale(LC_ALL, n_empty);
357 n_mb_cur_max = MB_CUR_MAX;
358 # ifdef HAVE_NL_LANGINFO
359 /* C99 */{
360 char const *cp;
362 if((cp = nl_langinfo(CODESET)) != NULL)
363 /* (Will log during startup if user set that via -S) */
364 ok_vset(ttycharset, cp);
366 # endif /* HAVE_SETLOCALE */
368 # ifdef HAVE_C90AMEND1
369 if(n_mb_cur_max > 1){
370 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
371 n_psonce |= n_PSO_UNICODE;
372 # else
373 wchar_t wc;
374 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
375 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
376 n_psonce |= n_PSO_UNICODE;
377 /* Reset possibly messed up state; luckily this also gives us an
378 * indication whether the encoding has locking shift state sequences */
379 if(mbtowc(&wc, NULL, n_mb_cur_max))
380 n_psonce |= n_PSO_ENC_MBSTATE;
381 # endif
383 # endif
384 #endif /* HAVE_C90AMEND1 */
385 NYD2_LEAVE;
388 FL size_t
389 n_screensize(void){
390 char const *cp;
391 uiz_t rv;
392 NYD2_ENTER;
394 if((cp = ok_vlook(screen)) != NULL){
395 n_idec_uiz_cp(&rv, cp, 0, NULL);
396 if(rv == 0)
397 rv = n_scrnheight;
398 }else
399 rv = n_scrnheight;
401 if(rv > 2)
402 rv -= 2;
403 NYD2_LEAVE;
404 return rv;
407 FL char const *
408 n_pager_get(char const **env_addon){
409 char const *rv;
410 NYD_ENTER;
412 rv = ok_vlook(PAGER);
414 if(env_addon != NULL){
415 *env_addon = NULL;
416 /* Update the manual upon any changes:
417 * *colour-pager*, $PAGER */
418 if(strstr(rv, "less") != NULL){
419 if(getenv("LESS") == NULL)
420 *env_addon = "LESS=RXi";
421 }else if(strstr(rv, "lv") != NULL){
422 if(getenv("LV") == NULL)
423 *env_addon = "LV=-c";
426 NYD_LEAVE;
427 return rv;
430 FL void
431 page_or_print(FILE *fp, size_t lines)
433 int c;
434 char const *cp;
435 NYD_ENTER;
437 fflush_rewind(fp);
439 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
440 size_t rows;
442 if(*cp == '\0')
443 rows = (size_t)n_scrnheight;
444 else
445 n_idec_uiz_cp(&rows, cp, 0, NULL);
447 if (rows > 0 && lines == 0) {
448 while ((c = getc(fp)) != EOF)
449 if (c == '\n' && ++lines >= rows)
450 break;
451 really_rewind(fp);
454 if (lines >= rows) {
455 char const *env_add[2], *pager;
457 pager = n_pager_get(&env_add[0]);
458 env_add[1] = NULL;
459 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
460 env_add, NULL);
461 goto jleave;
465 while ((c = getc(fp)) != EOF)
466 putc(c, n_stdout);
467 jleave:
468 NYD_LEAVE;
471 FL enum protocol
472 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
473 char const **adjusted_or_null)
475 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
476 char const *cp, *orig_name;
477 enum protocol rv = PROTO_UNKNOWN;
478 NYD_ENTER;
480 if(name[0] == '%' && name[1] == ':')
481 name += 2;
482 orig_name = name;
484 for (cp = name; *cp && *cp != ':'; cp++)
485 if (!alnumchar(*cp))
486 goto jfile;
488 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
489 if(!strncmp(name, "file", sizeof("file") -1) ||
490 !strncmp(name, "mbox", sizeof("mbox") -1))
491 rv = PROTO_FILE;
492 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
493 rv = PROTO_MAILDIR;
494 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
495 #ifdef HAVE_POP3
496 rv = PROTO_POP3;
497 #else
498 n_err(_("No POP3 support compiled in\n"));
499 #endif
500 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
501 #if defined HAVE_POP3 && defined HAVE_SSL
502 rv = PROTO_POP3;
503 #else
504 n_err(_("No POP3S support compiled in\n"));
505 #endif
507 else if(!strncmp(name, "imap", sizeof("imap") -1)){
508 #ifdef HAVE_IMAP
509 rv = PROTO_IMAP;
510 #else
511 n_err(_("No IMAP support compiled in\n"));
512 #endif
513 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
514 #if defined HAVE_IMAP && defined HAVE_SSL
515 rv = PROTO_IMAP;
516 #else
517 n_err(_("No IMAPS support compiled in\n"));
518 #endif
520 orig_name = &cp[3];
521 goto jleave;
524 jfile:
525 rv = PROTO_FILE;
527 if(check_stat || try_hooks){
528 struct n_file_type ft;
529 struct stat stb;
530 char *np;
531 size_t sz;
533 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
534 memcpy(np, name, sz + 1);
536 if(!stat(name, &stb)){
537 if(S_ISDIR(stb.st_mode) &&
538 (memcpy(&np[sz], "/tmp", 5),
539 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
540 (memcpy(&np[sz], "/new", 5),
541 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
542 (memcpy(&np[sz], "/cur", 5),
543 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
544 rv = PROTO_MAILDIR;
545 }else if(try_hooks && n_filetype_trial(&ft, name))
546 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
547 else if((cp = ok_vlook(newfolders)) != NULL &&
548 !asccasecmp(cp, "maildir"))
549 rv = PROTO_MAILDIR;
551 n_lofi_free(np);
553 jleave:
554 if(adjusted_or_null != NULL)
555 *adjusted_or_null = orig_name;
556 NYD_LEAVE;
557 return rv;
560 FL char *
561 n_c_to_hex_base16(char store[3], char c){
562 static char const itoa16[] = "0123456789ABCDEF";
563 NYD2_ENTER;
565 store[2] = '\0';
566 store[1] = itoa16[(ui8_t)c & 0x0F];
567 c = ((ui8_t)c >> 4) & 0x0F;
568 store[0] = itoa16[(ui8_t)c];
569 NYD2_LEAVE;
570 return store;
573 FL si32_t
574 n_c_from_hex_base16(char const hex[2]){
575 static ui8_t const atoi16[] = {
576 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
577 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
578 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
579 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
582 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
584 ui8_t i1, i2;
585 si32_t rv;
586 NYD2_ENTER;
588 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
589 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
590 goto jerr;
591 i1 = atoi16[i1];
592 i2 = atoi16[i2];
593 if ((i1 | i2) & 0xF0u)
594 goto jerr;
595 rv = i1;
596 rv <<= 4;
597 rv += i2;
598 jleave:
599 NYD2_LEAVE;
600 return rv;
601 jerr:
602 rv = -1;
603 goto jleave;
606 FL enum n_idec_state
607 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
608 enum n_idec_mode idm, char const **endptr_or_null){
609 ui8_t currc;
610 ui64_t res, cut;
611 enum n_idec_state rv;
612 NYD_ENTER;
614 idm &= n__IDEC_MODE_MASK;
615 rv = n_IDEC_STATE_NONE | idm;
616 res = 0;
618 if(clen == UIZ_MAX){
619 if(*cbuf == '\0')
620 goto jeinval;
621 }else if(clen == 0)
622 goto jeinval;
624 assert(base != 1 && base <= 36);
625 /*if(base == 1 || base > 36)
626 * goto jeinval;*/
628 /* Leading WS */
629 while(spacechar(*cbuf))
630 if(*++cbuf == '\0' || --clen == 0)
631 goto jeinval;
633 /* Check sign */
634 switch(*cbuf){
635 case '-':
636 rv |= n_IDEC_STATE_SEEN_MINUS;
637 /* FALLTHROUGH */
638 case '+':
639 if(*++cbuf == '\0' || --clen == 0)
640 goto jeinval;
641 break;
644 /* Base detection/skip */
645 if(*cbuf != '0'){
646 if(base == 0){
647 base = 10;
649 /* Support BASE#number prefix, where BASE is decimal 2-36 */
650 if(clen > 2){
651 char c1, c2, c3;
653 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
654 (((c2 = cbuf[1]) == '#') ||
655 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
656 base = a_aux_idec_atoi[(ui8_t)c1];
657 if(c2 == '#')
658 c3 = cbuf[2];
659 else{
660 c3 = cbuf[3];
661 base *= 10; /* xxx Inline atoi decimal base */
662 base += a_aux_idec_atoi[(ui8_t)c2];
665 /* We do not interpret this as BASE#number at all if either we
666 * did not get a valid base or if the first char is not valid
667 * according to base, to comply to the latest interpretion of
668 * "prefix", see comment for standard prefixes below */
669 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
670 base = 10;
671 else if(c2 == '#')
672 clen -= 2, cbuf += 2;
673 else
674 clen -= 3, cbuf += 3;
679 /* Character must be valid for base */
680 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
681 if(currc >= base)
682 goto jeinval;
683 }else{
684 /* 0 always valid as is, fallback base 10 */
685 if(*++cbuf == '\0' || --clen == 0)
686 goto jleave;
688 /* Base "detection" */
689 if(base == 0 || base == 2 || base == 16){
690 switch(*cbuf){
691 case 'x':
692 case 'X':
693 if((base & 2) == 0){
694 base = 0x10;
695 goto jprefix_skip;
697 break;
698 case 'b':
699 case 'B':
700 if((base & 16) == 0){
701 base = 2; /* 0b10 */
702 /* Char after prefix must be valid. However, after some error
703 * in the tor software all libraries (which had to) turned to
704 * an interpretation of the C standard which says that the
705 * prefix may optionally precede an otherwise valid sequence,
706 * which means that "0x" is not a STATE_INVAL error but gives
707 * a "0" result with a "STATE_BASE" error and a rest of "x" */
708 jprefix_skip:
709 #if 1
710 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
711 --clen, ++cbuf;
712 #else
713 if(*++cbuf == '\0' || --clen == 0)
714 goto jeinval;
716 /* Character must be valid for base, invalid otherwise */
717 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
718 if(currc >= base)
719 goto jeinval;
720 #endif
722 break;
723 default:
724 if(base == 0)
725 base = 010;
726 break;
730 /* Character must be valid for base, _EBASE otherwise */
731 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
732 if(currc >= base)
733 goto jebase;
736 for(cut = a_aux_idec_cutlimit[base - 2];;){
737 if(res >= cut){
738 if(res == cut){
739 res *= base;
740 if(res > UI64_MAX - currc)
741 goto jeover;
742 res += currc;
743 }else
744 goto jeover;
745 }else{
746 res *= base;
747 res += currc;
750 if(*++cbuf == '\0' || --clen == 0)
751 break;
753 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
754 if(currc >= base)
755 goto jebase;
758 jleave:
760 ui64_t uimask;
762 switch(rv & n__IDEC_MODE_LIMIT_MASK){
763 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
764 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
765 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
766 default: uimask = UI64_MAX; break;
768 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
769 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
770 uimask >>= 1;
772 if(res & ~uimask){
773 /* XXX never entered unless _SIGNED_TYPE! */
774 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
775 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
776 if(res > uimask + 1){
777 res = uimask << 1;
778 res &= ~uimask;
779 }else{
780 res = -res;
781 break;
783 }else
784 res = uimask;
785 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
786 rv |= n_IDEC_STATE_EOVERFLOW;
787 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
788 res = -res;
789 }while(0);
791 switch(rv & n__IDEC_MODE_LIMIT_MASK){
792 case n_IDEC_MODE_LIMIT_8BIT:
793 if(rv & n_IDEC_MODE_SIGNED_TYPE)
794 *(si8_t*)resp = (si8_t)res;
795 else
796 *(ui8_t*)resp = (ui8_t)res;
797 break;
798 case n_IDEC_MODE_LIMIT_16BIT:
799 if(rv & n_IDEC_MODE_SIGNED_TYPE)
800 *(si16_t*)resp = (si16_t)res;
801 else
802 *(ui16_t*)resp = (ui16_t)res;
803 break;
804 case n_IDEC_MODE_LIMIT_32BIT:
805 if(rv & n_IDEC_MODE_SIGNED_TYPE)
806 *(si32_t*)resp = (si32_t)res;
807 else
808 *(ui32_t*)resp = (ui32_t)res;
809 break;
810 default:
811 if(rv & n_IDEC_MODE_SIGNED_TYPE)
812 *(si64_t*)resp = (si64_t)res;
813 else
814 *(ui64_t*)resp = (ui64_t)res;
815 break;
818 if(endptr_or_null != NULL)
819 *endptr_or_null = cbuf;
820 if(*cbuf == '\0' || clen == 0)
821 rv |= n_IDEC_STATE_CONSUMED;
822 NYD_LEAVE;
823 return rv;
825 jeinval:
826 rv |= n_IDEC_STATE_EINVAL;
827 goto j_maxval;
828 jebase:
829 /* Not a base error for terminator and whitespace! */
830 if(*cbuf != '\0' && !spacechar(*cbuf))
831 rv |= n_IDEC_STATE_EBASE;
832 goto jleave;
834 jeover:
835 /* Overflow error: consume input until bad character or length out */
836 for(;;){
837 if(*++cbuf == '\0' || --clen == 0)
838 break;
839 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
840 if(currc >= base)
841 break;
844 rv |= n_IDEC_STATE_EOVERFLOW;
845 j_maxval:
846 if(rv & n_IDEC_MODE_SIGNED_TYPE)
847 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
848 : (ui64_t)SI64_MAX;
849 else
850 res = UI64_MAX;
851 rv &= ~n_IDEC_STATE_SEEN_MINUS;
852 goto jleave;
855 FL char *
856 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
857 enum n_ienc_mode iem){
858 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
860 ui8_t shiftmodu;
861 char const *itoa;
862 char *rv;
863 NYD_ENTER;
865 iem &= n__IENC_MODE_MASK;
867 assert(base != 1 && base <= 36);
868 /*if(base == 1 || base > 36){
869 * rv = NULL;
870 * goto jleave;
871 *}*/
873 rv = &cbuf[n_IENC_BUFFER_SIZE];
874 *--rv = '\0';
875 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
876 : a_aux_ienc_itoa_upper;
878 if((si64_t)value < 0){
879 iem |= a_ISNEG;
880 if(iem & n_IENC_MODE_SIGNED_TYPE){
881 /* self->is_negative = TRU1; */
882 value = -value;
886 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
887 --base; /* convert to mask */
889 *--rv = itoa[value & base];
890 value >>= shiftmodu;
891 }while(value != 0);
893 if(!(iem & n_IENC_MODE_NO_PREFIX)){
894 /* self->before_prefix = cp; */
895 if(shiftmodu == 4)
896 *--rv = 'x';
897 else if(shiftmodu == 1)
898 *--rv = 'b';
899 else if(shiftmodu != 3){
900 ++base; /* Reconvert from mask */
901 goto jnumber_sign_prefix;
903 *--rv = '0';
905 }else{
907 shiftmodu = value % base;
908 value /= base;
909 *--rv = itoa[shiftmodu];
910 }while(value != 0);
912 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
913 jnumber_sign_prefix:
914 value = base;
915 base = 10;
916 *--rv = '#';
918 shiftmodu = value % base;
919 value /= base;
920 *--rv = itoa[shiftmodu];
921 }while(value != 0);
924 if(iem & n_IENC_MODE_SIGNED_TYPE){
925 char c;
927 if(iem & a_ISNEG)
928 c = '-';
929 else if(iem & n_IENC_MODE_SIGNED_PLUS)
930 c = '+';
931 else if(iem & n_IENC_MODE_SIGNED_SPACE)
932 c = ' ';
933 else
934 c = '\0';
936 if(c != '\0')
937 *--rv = c;
940 NYD_LEAVE;
941 return rv;
944 FL ui32_t
945 n_torek_hash(char const *name){
946 /* Chris Torek's hash */
947 char c;
948 ui32_t h;
949 NYD2_ENTER;
951 for(h = 0; (c = *name++) != '\0';)
952 h = (h * 33) + c;
953 NYD2_LEAVE;
954 return h;
957 FL ui32_t
958 n_torek_ihashn(char const *dat, size_t len){
959 /* See n_torek_hash() */
960 char c;
961 ui32_t h;
962 NYD2_ENTER;
964 if(len == UIZ_MAX)
965 for(h = 0; (c = *dat++) != '\0';)
966 h = (h * 33) + lowerconv(c);
967 else
968 for(h = 0; len > 0; --len){
969 c = *dat++;
970 h = (h * 33) + lowerconv(c);
972 NYD2_LEAVE;
973 return h;
976 FL ui32_t
977 n_prime_next(ui32_t n){
978 static ui32_t const primes[] = {
979 5, 11, 23, 47, 97, 157, 283,
980 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
981 131071, 262139, 524287, 1048573, 2097143, 4194301,
982 8388593, 16777213, 33554393, 67108859, 134217689,
983 268435399, 536870909, 1073741789, 2147483647
985 ui32_t i, mprime;
986 NYD2_ENTER;
988 i = (n < primes[n_NELEM(primes) / 4] ? 0
989 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
990 : n_NELEM(primes) / 2));
992 do if((mprime = primes[i]) > n)
993 break;
994 while(++i < n_NELEM(primes));
996 if(i == n_NELEM(primes) && mprime < n)
997 mprime = n;
998 NYD2_LEAVE;
999 return mprime;
1002 FL char const *
1003 n_getdeadletter(void){
1004 char const *cp;
1005 bool_t bla;
1006 NYD_ENTER;
1008 bla = FAL0;
1009 jredo:
1010 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1011 if(cp == NULL || strlen(cp) >= PATH_MAX){
1012 if(!bla){
1013 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1014 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1015 ok_vclear(DEAD);
1016 bla = TRU1;
1017 goto jredo;
1018 }else{
1019 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1020 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1023 NYD_LEAVE;
1024 return cp;
1027 FL char *
1028 n_nodename(bool_t mayoverride){
1029 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1031 struct utsname ut;
1032 char *hn;
1033 #ifdef HAVE_SOCKETS
1034 # ifdef HAVE_GETADDRINFO
1035 struct addrinfo hints, *res;
1036 # else
1037 struct hostent *hent;
1038 # endif
1039 #endif
1040 NYD2_ENTER;
1042 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1044 }else if((hn = sys_hostname) == NULL){
1045 bool_t lofi;
1047 lofi = FAL0;
1048 uname(&ut);
1049 hn = ut.nodename;
1051 #ifdef HAVE_SOCKETS
1052 # ifdef HAVE_GETADDRINFO
1053 memset(&hints, 0, sizeof hints);
1054 hints.ai_family = AF_UNSPEC;
1055 hints.ai_flags = AI_CANONNAME;
1056 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1057 if(res->ai_canonname != NULL){
1058 size_t l;
1060 l = strlen(res->ai_canonname) +1;
1061 hn = n_lofi_alloc(l);
1062 lofi = TRU1;
1063 memcpy(hn, res->ai_canonname, l);
1065 freeaddrinfo(res);
1067 # else
1068 hent = gethostbyname(hn);
1069 if(hent != NULL)
1070 hn = hent->h_name;
1071 # endif
1072 #endif /* HAVE_SOCKETS */
1074 #ifdef HAVE_IDNA
1075 /* C99 */{
1076 struct n_string cnv;
1078 n_string_creat(&cnv);
1079 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1080 n_panic(_("The system hostname is invalid, "
1081 "IDNA conversion failed: %s\n"),
1082 n_shexp_quote_cp(hn, FAL0));
1083 sys_hostname = n_string_cp(&cnv);
1084 n_string_drop_ownership(&cnv);
1085 /*n_string_gut(&cnv);*/
1087 #else
1088 sys_hostname = sstrdup(hn);
1089 #endif
1091 if(lofi)
1092 n_lofi_free(hn);
1093 hn = sys_hostname;
1096 if(hostname != NULL && hostname != sys_hostname)
1097 n_free(hostname);
1098 hostname = sstrdup(hn);
1099 NYD2_LEAVE;
1100 return hostname;
1103 #ifdef HAVE_IDNA
1104 FL bool_t
1105 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1106 char *idna_utf8;
1107 bool_t lofi, rv;
1108 NYD_ENTER;
1110 if(ilen == UIZ_MAX)
1111 ilen = strlen(ibuf);
1113 lofi = FAL0;
1115 if((rv = (ilen == 0)))
1116 goto jleave;
1117 if(ibuf[ilen] != '\0'){
1118 lofi = TRU1;
1119 idna_utf8 = n_lofi_alloc(ilen +1);
1120 memcpy(idna_utf8, ibuf, ilen);
1121 idna_utf8[ilen] = '\0';
1122 ibuf = idna_utf8;
1124 ilen = 0;
1126 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
1127 ibuf);
1128 if(idna_utf8 == NULL)
1129 goto jleave;
1131 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
1132 /* C99 */{
1133 char *idna_ascii;
1135 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1136 out = n_string_assign_cp(out, idna_ascii);
1137 idn_free(idna_ascii);
1138 rv = TRU1;
1139 ilen = out->s_len;
1142 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1143 ilen = strlen(idna_utf8);
1144 jredo:
1145 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
1146 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1147 case idn_buffer_overflow:
1148 ilen += HOST_NAME_MAX +1;
1149 goto jredo;
1150 case idn_success:
1151 rv = TRU1;
1152 ilen = strlen(out->s_dat);
1153 break;
1154 default:
1155 ilen = 0;
1156 break;
1159 # else
1160 # error Unknown HAVE_IDNA
1161 # endif
1162 jleave:
1163 if(lofi)
1164 n_lofi_free(n_UNCONST(ibuf));
1165 out = n_string_trunc(out, ilen);
1166 NYD_LEAVE;
1167 return rv;
1169 #endif /* HAVE_IDNA */
1171 FL char *
1172 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1173 struct str b64;
1174 char *indat, *cp, *oudat;
1175 size_t i, inlen, oulen;
1176 NYD_ENTER;
1178 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1179 n_psonce |= n_PSO_RANDOM_INIT;
1181 if(n_poption & n_PO_D_V){
1182 char const *prngn;
1184 #if n_RANDOM_USE_XSSL
1185 prngn = "*SSL RAND_*";
1186 #elif defined HAVE_POSIX_RANDOM
1187 prngn = "POSIX/arc4random";
1188 #else
1189 prngn = "builtin ARC4";
1190 #endif
1191 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1194 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1195 a_aux_rand_init();
1196 #endif
1199 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1200 * with PAD stripped is still longer than what the user requests, easy way.
1201 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1202 * include the base64 PAD characters in our random string: give some pad */
1203 i = len;
1204 if((inlen = i % 3) != 0)
1205 i += 3 - inlen;
1206 jinc1:
1207 inlen = i >> 2;
1208 oulen = inlen << 2;
1209 if(oulen < len){
1210 i += 3;
1211 goto jinc1;
1213 inlen = inlen + (inlen << 1);
1215 indat = n_lofi_alloc(inlen +1);
1217 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1218 #if n_RANDOM_USE_XSSL
1219 ssl_rand_bytes(indat, inlen);
1220 #elif !defined HAVE_POSIX_RANDOM
1221 for(i = inlen; i-- > 0;)
1222 indat[i] = (char)a_aux_rand_get8();
1223 #else
1224 for(cp = indat, i = inlen; i > 0;){
1225 union {ui32_t i4; char c[4];} r;
1226 size_t j;
1228 r.i4 = (ui32_t)arc4random();
1229 switch((j = i & 3)){
1230 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1231 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1232 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1233 default: cp[0] = r.c[0]; break;
1235 cp += j;
1236 i -= j;
1238 #endif
1239 }else{
1240 for(cp = indat, i = inlen; i > 0;){
1241 union {ui32_t i4; char c[4];} r;
1242 size_t j;
1244 r.i4 = ++*reprocnt_or_null;
1245 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1246 char x;
1248 x = r.c[0];
1249 r.c[0] = r.c[3];
1250 r.c[3] = x;
1251 x = r.c[1];
1252 r.c[1] = r.c[2];
1253 r.c[2] = x;
1255 switch((j = i & 3)){
1256 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1257 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1258 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1259 default: cp[0] = r.c[0]; break;
1261 cp += j;
1262 i -= j;
1266 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1267 b64.s = oudat;
1268 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1269 assert(b64.l >= len);
1270 memcpy(dat, b64.s, len);
1271 dat[len] = '\0';
1272 if(oudat != dat)
1273 n_lofi_free(oudat);
1275 n_lofi_free(indat);
1277 NYD_LEAVE;
1278 return dat;
1281 FL char *
1282 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1283 char *dat;
1284 NYD_ENTER;
1286 dat = n_autorec_alloc(len +1);
1287 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1288 NYD_LEAVE;
1289 return dat;
1292 FL si8_t
1293 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1295 si8_t rv;
1296 NYD_ENTER;
1298 assert(inlen == 0 || inbuf != NULL);
1300 if (inlen == UIZ_MAX)
1301 inlen = strlen(inbuf);
1303 if (inlen == 0)
1304 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1305 else {
1306 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1307 !ascncasecmp(inbuf, "true", inlen) ||
1308 !ascncasecmp(inbuf, "yes", inlen) ||
1309 !ascncasecmp(inbuf, "on", inlen))
1310 rv = 1;
1311 else if ((inlen == 1 &&
1312 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1313 !ascncasecmp(inbuf, "false", inlen) ||
1314 !ascncasecmp(inbuf, "no", inlen) ||
1315 !ascncasecmp(inbuf, "off", inlen))
1316 rv = 0;
1317 else {
1318 ui64_t ib;
1320 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1321 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1322 ) != n_IDEC_STATE_CONSUMED)
1323 rv = -1;
1324 else
1325 rv = (ib != 0);
1328 NYD_LEAVE;
1329 return rv;
1332 FL si8_t
1333 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1335 si8_t rv;
1336 NYD_ENTER;
1338 assert(inlen == 0 || inbuf != NULL);
1340 if (inlen == UIZ_MAX)
1341 inlen = strlen(inbuf);
1343 if (inlen == 0)
1344 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1345 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1346 !ascncasecmp(inbuf, "ask-", 4) &&
1347 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1348 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1349 rv = getapproval(prompt, rv);
1350 NYD_LEAVE;
1351 return rv;
1354 FL bool_t
1355 n_is_all_or_aster(char const *name){
1356 bool_t rv;
1357 NYD_ENTER;
1359 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1360 NYD_LEAVE;
1361 return rv;
1364 FL struct n_timespec const *
1365 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1366 static struct n_timespec ts_now;
1367 NYD2_ENTER;
1369 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1370 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1371 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1372 ts_now.ts_nsec = 0;
1373 }else if(force_update || ts_now.ts_sec == 0){
1374 #ifdef HAVE_CLOCK_GETTIME
1375 struct timespec ts;
1377 clock_gettime(CLOCK_REALTIME, &ts);
1378 ts_now.ts_sec = (si64_t)ts.tv_sec;
1379 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1380 #elif defined HAVE_GETTIMEOFDAY
1381 struct timeval tv;
1383 gettimeofday(&tv, NULL);
1384 ts_now.ts_sec = (si64_t)tv.tv_sec;
1385 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1386 #else
1387 ts_now.ts_sec = (si64_t)time(NULL);
1388 ts_now.ts_nsec = 0;
1389 #endif
1392 /* Just in case.. */
1393 if(n_UNLIKELY(ts_now.ts_sec < 0))
1394 ts_now.ts_sec = 0;
1395 NYD2_LEAVE;
1396 return &ts_now;
1399 FL void
1400 time_current_update(struct time_current *tc, bool_t full_update){
1401 NYD_ENTER;
1402 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1404 if(full_update){
1405 char *cp;
1406 struct tm *tmp;
1407 time_t t;
1409 t = tc->tc_time;
1410 jredo:
1411 if((tmp = gmtime(&t)) == NULL){
1412 t = 0;
1413 goto jredo;
1415 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1416 if((tmp = localtime(&t)) == NULL){
1417 t = 0;
1418 goto jredo;
1420 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1421 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1422 *cp++ = '\n';
1423 *cp = '\0';
1424 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1426 NYD_LEAVE;
1429 FL char *
1430 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1431 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1432 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1433 * ISO C requires us to use the above format string,
1434 * even if it will not fit in the buffer. Thus asctime_r
1435 * is _supposed_ to crash if the fields in tm are too large.
1436 * We follow this behavior and crash "gracefully" to warn
1437 * application developers that they may not be so lucky
1438 * on other implementations (e.g. stack smashing..).
1439 * So we need to do it on our own or the libc may kill us */
1440 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1442 si32_t y, md, th, tm, ts;
1443 char const *wdn, *mn;
1444 struct tm const *tmp;
1445 NYD_ENTER;
1447 if((tmp = localtime_or_nil) == NULL){
1448 time_t t;
1450 t = (time_t)secsepoch;
1451 jredo:
1452 if((tmp = localtime(&t)) == NULL){
1453 /* TODO error log */
1454 t = 0;
1455 goto jredo;
1459 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1460 y = 1970;
1461 wdn = n_weekday_names[4];
1462 mn = n_month_names[0];
1463 md = 1;
1464 th = tm = ts = 0;
1465 }else{
1466 y += 1900;
1467 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1468 ? n_weekday_names[tmp->tm_wday] : n_qm;
1469 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1470 ? n_month_names[tmp->tm_mon] : n_qm;
1472 if((md = tmp->tm_mday) < 1 || md > 31)
1473 md = 1;
1475 if((th = tmp->tm_hour) < 0 || th > 23)
1476 th = 0;
1477 if((tm = tmp->tm_min) < 0 || tm > 59)
1478 tm = 0;
1479 if((ts = tmp->tm_sec) < 0 || ts > 60)
1480 ts = 0;
1483 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1484 wdn, mn, md, th, tm, ts, y);
1485 NYD_LEAVE;
1486 return buf;
1489 FL uiz_t
1490 n_msleep(uiz_t millis, bool_t ignint){
1491 uiz_t rv;
1492 NYD2_ENTER;
1494 #ifdef HAVE_NANOSLEEP
1495 /* C99 */{
1496 struct timespec ts, trem;
1497 int i;
1499 ts.tv_sec = millis / 1000;
1500 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1502 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1503 ts = trem;
1504 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1507 #elif defined HAVE_SLEEP
1508 if((millis /= 1000) == 0)
1509 millis = 1;
1510 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1511 millis = rv;
1512 #else
1513 # error Configuration should have detected a function for sleeping.
1514 #endif
1516 NYD2_LEAVE;
1517 return rv;
1520 FL void
1521 n_err(char const *format, ...){
1522 va_list ap;
1523 NYD2_ENTER;
1525 va_start(ap, format);
1526 #ifdef HAVE_ERRORS
1527 if(n_psonce & n_PSO_INTERACTIVE)
1528 n_verr(format, ap);
1529 else
1530 #endif
1532 size_t len;
1533 bool_t doname;
1535 doname = FAL0;
1537 while(*format == '\n'){
1538 doname = TRU1;
1539 putc('\n', n_stderr);
1540 ++format;
1543 if(doname)
1544 a_aux_err_linelen = 0;
1546 if((len = strlen(format)) > 0){
1547 if(doname || a_aux_err_linelen == 0){
1548 char const *cp;
1550 if(*(cp = ok_vlook(log_prefix)) != '\0')
1551 fputs(cp, n_stderr);
1553 vfprintf(n_stderr, format, ap);
1555 /* C99 */{
1556 size_t i = len;
1558 if(format[--len] == '\n'){
1559 a_aux_err_linelen = (i -= ++len);
1560 break;
1562 ++a_aux_err_linelen;
1563 }while(len > 0);
1567 fflush(n_stderr);
1569 va_end(ap);
1570 NYD2_LEAVE;
1573 FL void
1574 n_verr(char const *format, va_list ap){
1575 #ifdef HAVE_ERRORS
1576 struct a_aux_err_node *enp;
1577 #endif
1578 bool_t doname;
1579 size_t len;
1580 NYD2_ENTER;
1582 doname = FAL0;
1584 while(*format == '\n'){
1585 putc('\n', n_stderr);
1586 doname = TRU1;
1587 ++format;
1590 if(doname){
1591 a_aux_err_linelen = 0;
1592 #ifdef HAVE_ERRORS
1593 if(n_psonce & n_PSO_INTERACTIVE){
1594 if((enp = a_aux_err_tail) != NULL &&
1595 (enp->ae_str.s_len > 0 &&
1596 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1597 n_string_push_c(&enp->ae_str, '\n');
1599 #endif
1602 if((len = strlen(format)) == 0)
1603 goto jleave;
1604 #ifdef HAVE_ERRORS
1605 n_pstate |= n_PS_ERRORS_PROMPT;
1606 #endif
1608 if(doname || a_aux_err_linelen == 0){
1609 char const *cp;
1611 if(*(cp = ok_vlook(log_prefix)) != '\0')
1612 fputs(cp, n_stderr);
1615 /* C99 */{
1616 size_t i = len;
1618 if(format[--len] == '\n'){
1619 a_aux_err_linelen = (i -= ++len);
1620 break;
1622 ++a_aux_err_linelen;
1623 }while(len > 0);
1626 #ifdef HAVE_ERRORS
1627 if(!(n_psonce & n_PSO_INTERACTIVE))
1628 #endif
1629 vfprintf(n_stderr, format, ap);
1630 #ifdef HAVE_ERRORS
1631 else{
1632 int imax, i;
1633 n_LCTAV(ERRORS_MAX > 3);
1635 /* Link it into the `errors' message ring */
1636 if((enp = a_aux_err_tail) == NULL){
1637 jcreat:
1638 enp = smalloc(sizeof *enp);
1639 enp->ae_next = NULL;
1640 n_string_creat(&enp->ae_str);
1641 if(a_aux_err_tail != NULL)
1642 a_aux_err_tail->ae_next = enp;
1643 else
1644 a_aux_err_head = enp;
1645 a_aux_err_tail = enp;
1646 ++a_aux_err_cnt;
1647 }else if(doname ||
1648 (enp->ae_str.s_len > 0 &&
1649 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1650 if(a_aux_err_cnt < ERRORS_MAX)
1651 goto jcreat;
1653 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1654 a_aux_err_tail->ae_next = enp;
1655 a_aux_err_tail = enp;
1656 enp->ae_next = NULL;
1657 n_string_trunc(&enp->ae_str, 0);
1660 # ifdef HAVE_N_VA_COPY
1661 imax = 64;
1662 # else
1663 imax = n_MIN(LINESIZE, 1024);
1664 # endif
1665 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1666 # ifdef HAVE_N_VA_COPY
1667 va_list vac;
1669 n_va_copy(vac, ap);
1670 # else
1671 # define vac ap
1672 # endif
1674 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1675 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1676 # ifdef HAVE_N_VA_COPY
1677 va_end(vac);
1678 # else
1679 # undef vac
1680 # endif
1681 if(i <= 0)
1682 goto jleave;
1683 if(UICMP(z, i, >=, imax)){
1684 # ifdef HAVE_N_VA_COPY
1685 /* XXX Check overflow for upcoming LEN+++i! */
1686 n_string_trunc(&enp->ae_str, len);
1687 continue;
1688 # else
1689 i = (int)strlen(&enp->ae_str.s_dat[len]);
1690 # endif
1692 break;
1694 n_string_trunc(&enp->ae_str, len + (size_t)i);
1696 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1698 #endif /* HAVE_ERRORS */
1700 jleave:
1701 fflush(n_stderr);
1702 NYD2_LEAVE;
1705 FL void
1706 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1707 va_list ap;
1708 NYD_X;
1710 va_start(ap, format);
1711 vfprintf(n_stderr, format, ap);
1712 va_end(ap);
1713 fflush(n_stderr);
1716 FL void
1717 n_perr(char const *msg, int errval){
1718 int e;
1719 char const *fmt;
1720 NYD2_ENTER;
1722 if(msg == NULL){
1723 fmt = "%s%s\n";
1724 msg = n_empty;
1725 }else
1726 fmt = "%s: %s\n";
1728 e = (errval == 0) ? n_err_no : errval;
1729 n_err(fmt, msg, n_err_to_doc(e));
1730 if(errval == 0)
1731 n_err_no = e;
1732 NYD2_LEAVE;
1735 FL void
1736 n_alert(char const *format, ...){
1737 va_list ap;
1738 NYD2_ENTER;
1740 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1742 va_start(ap, format);
1743 n_verr(format, ap);
1744 va_end(ap);
1746 n_err("\n");
1747 NYD2_LEAVE;
1750 FL void
1751 n_panic(char const *format, ...){
1752 va_list ap;
1753 NYD2_ENTER;
1755 if(a_aux_err_linelen > 0){
1756 putc('\n', n_stderr);
1757 a_aux_err_linelen = 0;
1759 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1761 va_start(ap, format);
1762 vfprintf(n_stderr, format, ap);
1763 va_end(ap);
1765 putc('\n', n_stderr);
1766 fflush(n_stderr);
1767 NYD2_LEAVE;
1768 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1771 #ifdef HAVE_ERRORS
1772 FL int
1773 c_errors(void *v){
1774 char **argv = v;
1775 struct a_aux_err_node *enp;
1776 NYD_ENTER;
1778 if(*argv == NULL)
1779 goto jlist;
1780 if(argv[1] != NULL)
1781 goto jerr;
1782 if(!asccasecmp(*argv, "show"))
1783 goto jlist;
1784 if(!asccasecmp(*argv, "clear"))
1785 goto jclear;
1786 jerr:
1787 fprintf(n_stderr,
1788 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1789 v = NULL;
1790 jleave:
1791 NYD_LEAVE;
1792 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1794 jlist:{
1795 FILE *fp;
1796 size_t i;
1798 if(a_aux_err_head == NULL){
1799 fprintf(n_stderr, _("The error ring is empty\n"));
1800 goto jleave;
1803 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1804 NULL){
1805 fprintf(n_stderr, _("tmpfile"));
1806 v = NULL;
1807 goto jleave;
1810 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1811 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1812 /* We don't know whether last string ended with NL; be simple XXX */
1813 putc('\n', fp);
1815 page_or_print(fp, 0);
1816 Fclose(fp);
1818 /* FALLTHRU */
1820 jclear:
1821 a_aux_err_tail = NULL;
1822 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1823 a_aux_err_linelen = 0;
1824 while((enp = a_aux_err_head) != NULL){
1825 a_aux_err_head = enp->ae_next;
1826 n_string_gut(&enp->ae_str);
1827 free(enp);
1829 goto jleave;
1831 #endif /* HAVE_ERRORS */
1833 FL char const *
1834 n_err_to_doc(si32_t eno){
1835 char const *rv;
1836 struct a_aux_err_map const *aemp;
1837 NYD2_ENTER;
1839 aemp = a_aux_err_map_from_no(eno);
1840 rv = &a_aux_err_docs[aemp->aem_docoff];
1841 NYD2_LEAVE;
1842 return rv;
1845 FL char const *
1846 n_err_to_name(si32_t eno){
1847 char const *rv;
1848 struct a_aux_err_map const *aemp;
1849 NYD2_ENTER;
1851 aemp = a_aux_err_map_from_no(eno);
1852 rv = &a_aux_err_names[aemp->aem_nameoff];
1853 NYD2_LEAVE;
1854 return rv;
1857 FL si32_t
1858 n_err_from_name(char const *name){
1859 struct a_aux_err_map const *aemp;
1860 ui32_t hash, i, j, x;
1861 si32_t rv;
1862 NYD2_ENTER;
1864 hash = n_torek_hash(name);
1866 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1867 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1868 break;
1870 aemp = &a_aux_err_map[x];
1871 if(aemp->aem_hash == hash &&
1872 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1873 rv = aemp->aem_err_no;
1874 goto jleave;
1877 if(++i == a_AUX_ERR_REV_PRIME){
1878 #ifdef a_AUX_ERR_REV_WRAPAROUND
1879 i = 0;
1880 #else
1881 break;
1882 #endif
1886 /* Have not found it. But wait, it could be that the user did, e.g.,
1887 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1888 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1889 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1890 ) == n_IDEC_STATE_CONSUMED){
1891 aemp = a_aux_err_map_from_no(rv);
1892 rv = aemp->aem_err_no;
1893 goto jleave;
1896 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1897 jleave:
1898 NYD2_LEAVE;
1899 return rv;
1902 #ifdef HAVE_REGEX
1903 FL char const *
1904 n_regex_err_to_doc(const regex_t *rep, int e){
1905 char *cp;
1906 size_t i;
1907 NYD2_ENTER;
1909 i = regerror(e, rep, NULL, 0) +1;
1910 cp = salloc(i);
1911 regerror(e, rep, cp, i);
1912 NYD2_LEAVE;
1913 return cp;
1915 #endif
1917 /* s-it-mode */