Support multiple choice via VAL_IDNA (Stuart Henderson)
[s-mailx.git] / auxlily.c
blob082ad153f3ae5d6306c092ddc53c1420b3eba100
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
60 # include HAVE_GETRANDOM_HEADER
61 #endif
63 #ifdef HAVE_IDNA
64 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
65 # include <idn2.h>
66 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
67 # include <idna.h>
68 # include <idn-free.h>
69 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
70 # include <idn/api.h>
71 # endif
72 #endif
74 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
75 union rand_state{
76 struct rand_arc4{
77 ui8_t _dat[256];
78 ui8_t _i;
79 ui8_t _j;
80 ui8_t __pad[6];
81 } a;
82 ui8_t b8[sizeof(struct rand_arc4)];
83 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
85 #endif
87 #ifdef HAVE_ERRORS
88 struct a_aux_err_node{
89 struct a_aux_err_node *ae_next;
90 struct n_string ae_str;
92 #endif
94 struct a_aux_err_map{
95 ui32_t aem_hash; /* Hash of name */
96 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
97 ui32_t aem_docoff; /* Into a_aux_err docs[] */
98 si32_t aem_err_no; /* The OS error value for this one */
101 /* IDEC: byte to integer value lookup table */
102 static ui8_t const a_aux_idec_atoi[256] = {
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,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
108 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
109 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
110 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
111 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
112 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
113 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
114 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
115 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
128 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
131 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
132 #define a_X(X) (UI64_MAX / (X))
133 static ui64_t const a_aux_idec_cutlimit[35] = {
134 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
135 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
136 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
137 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
138 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
140 #undef a_X
142 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
143 static ui8_t const a_aux_ienc_shifts[35] = {
144 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
145 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
147 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
150 /* IENC: integer to byte lookup tables */
151 static char const a_aux_ienc_itoa_upper[36] =
152 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
153 static char const a_aux_ienc_itoa_lower[36] =
154 "0123456789abcdefghijklmnopqrstuvwxyz";
156 /* Include the constant make-errors.sh output */
157 #include <gen-errors.h>
159 /* And these things come from mk-config.h (config-time make-errors.sh output) */
160 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
161 #undef a_X
162 #define a_X(N,I) {N,I},
163 n__ERR_NUMBER_TO_MAPOFF
164 #undef a_X
167 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
168 static union rand_state *a_aux_rand;
169 #endif
171 /* Error ring, for `errors' */
172 #ifdef HAVE_ERRORS
173 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
174 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
175 #endif
176 static size_t a_aux_err_linelen;
178 /* Our ARC4 random generator with its completely unacademical pseudo
179 * initialization (shall /dev/urandom fail) */
180 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t a_aux_rand_get8(void);
183 static ui32_t a_aux_rand_weak(ui32_t seed);
184 #endif
186 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
187 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
189 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
190 static void
191 a_aux_rand_init(void){
192 # ifdef HAVE_CLOCK_GETTIME
193 struct timespec ts;
194 # else
195 struct timeval ts;
196 # endif
197 union {int fd; size_t i;} u;
198 ui32_t seed, rnd;
199 NYD2_ENTER;
201 a_aux_rand = n_alloc(sizeof *a_aux_rand);
203 # ifdef HAVE_GETRANDOM
204 /* getrandom(2) guarantees 256 without n_ERR_INTR..
205 * However, support sequential reading to avoid possible hangs that have
206 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
207 * HAVE_GETRANDOM is #defined) */
208 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
209 "Buffer too large to be served without n_ERR_INTR error");
210 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
211 "Buffer too small to serve used array indices");
212 /* C99 */{
213 size_t o, i;
215 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
216 ssize_t gr;
218 gr = HAVE_GETRANDOM(&a_aux_rand->a._dat[o], i);
219 if(gr == -1 && n_err_no == n_ERR_NOSYS)
220 break;
221 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
222 a_aux_rand->a._dat[84]];
223 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
224 a_aux_rand->a._dat[42]];
225 /* ..but be on the safe side */
226 if(gr > 0){
227 i -= (size_t)gr;
228 if(i == 0)
229 goto jleave;
230 o += (size_t)gr;
232 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
233 "waiting a bit\n"));
234 n_msleep(250, FAL0);
238 # elif !defined HAVE_NOEXTRANDOM
239 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
240 bool_t ok;
242 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
243 sizeof(a_aux_rand->a._dat)));
244 close(u.fd);
246 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
247 a_aux_rand->a._dat[84]];
248 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
249 a_aux_rand->a._dat[42]];
250 if(ok)
251 goto jleave;
253 # endif
255 /* As a fallback, a homebrew seed */
256 n_err(_("PseudoRandomNumberGenerator: generating homebrew seed\n"));
257 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
258 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
259 ui32_t t, k;
261 # ifdef HAVE_CLOCK_GETTIME
262 clock_gettime(CLOCK_REALTIME, &ts);
263 t = (ui32_t)ts.tv_nsec;
264 # else
265 gettimeofday(&ts, NULL);
266 t = (ui32_t)ts.tv_usec;
267 # endif
268 if(rnd & 1)
269 t = (t >> 16) | (t << 16);
270 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
271 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
272 if(rnd == 7 || rnd == 17)
273 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
274 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
275 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
276 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
277 if((rnd & 3) == 3)
278 seed ^= n_prime_next(seed);
282 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
283 a_aux_rand_get8();
284 jleave:
285 NYD2_LEAVE;
288 n_INLINE ui8_t
289 a_aux_rand_get8(void){
290 ui8_t si, sj;
292 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
293 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
294 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
295 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
296 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
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_POSIX_RANDOM && !HAVE_SSL_RANDOM */
318 static struct a_aux_err_map const *
319 a_aux_err_map_from_no(si32_t eno){
320 si32_t ecmp;
321 size_t asz;
322 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
323 struct a_aux_err_map const *aemp;
324 NYD2_ENTER;
326 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
328 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
329 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
330 asz != 0; asz >>= 1){
331 tmp = &adat[asz >> 1];
332 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
333 aemp = &a_aux_err_map[(*tmp)[1]];
334 break;
336 if(ecmp > 0){
337 adat = &tmp[1];
338 --asz;
342 NYD2_LEAVE;
343 return aemp;
346 FL void
347 n_locale_init(void){
348 NYD2_ENTER;
350 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
352 #ifndef HAVE_SETLOCALE
353 n_mb_cur_max = 1;
354 #else
355 setlocale(LC_ALL, n_empty);
356 n_mb_cur_max = MB_CUR_MAX;
357 # ifdef HAVE_NL_LANGINFO
358 /* C99 */{
359 char const *cp;
361 if((cp = nl_langinfo(CODESET)) != NULL)
362 /* (Will log during startup if user set that via -S) */
363 ok_vset(ttycharset, cp);
365 # endif /* HAVE_SETLOCALE */
367 # ifdef HAVE_C90AMEND1
368 if(n_mb_cur_max > 1){
369 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
370 n_psonce |= n_PSO_UNICODE;
371 # else
372 wchar_t wc;
373 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
374 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
375 n_psonce |= n_PSO_UNICODE;
376 /* Reset possibly messed up state; luckily this also gives us an
377 * indication whether the encoding has locking shift state sequences */
378 if(mbtowc(&wc, NULL, n_mb_cur_max))
379 n_psonce |= n_PSO_ENC_MBSTATE;
380 # endif
382 # endif
383 #endif /* HAVE_C90AMEND1 */
384 NYD2_LEAVE;
387 FL size_t
388 n_screensize(void){
389 char const *cp;
390 uiz_t rv;
391 NYD2_ENTER;
393 if((cp = ok_vlook(screen)) != NULL){
394 n_idec_uiz_cp(&rv, cp, 0, NULL);
395 if(rv == 0)
396 rv = n_scrnheight;
397 }else
398 rv = n_scrnheight;
400 if(rv > 2)
401 rv -= 2;
402 NYD2_LEAVE;
403 return rv;
406 FL char const *
407 n_pager_get(char const **env_addon){
408 char const *rv;
409 NYD_ENTER;
411 rv = ok_vlook(PAGER);
413 if(env_addon != NULL){
414 *env_addon = NULL;
415 /* Update the manual upon any changes:
416 * *colour-pager*, $PAGER */
417 if(strstr(rv, "less") != NULL){
418 if(getenv("LESS") == NULL)
419 *env_addon = "LESS=RXi";
420 }else if(strstr(rv, "lv") != NULL){
421 if(getenv("LV") == NULL)
422 *env_addon = "LV=-c";
425 NYD_LEAVE;
426 return rv;
429 FL void
430 page_or_print(FILE *fp, size_t lines)
432 int c;
433 char const *cp;
434 NYD_ENTER;
436 fflush_rewind(fp);
438 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
439 size_t rows;
441 if(*cp == '\0')
442 rows = (size_t)n_scrnheight;
443 else
444 n_idec_uiz_cp(&rows, cp, 0, NULL);
446 if (rows > 0 && lines == 0) {
447 while ((c = getc(fp)) != EOF)
448 if (c == '\n' && ++lines >= rows)
449 break;
450 really_rewind(fp);
453 if (lines >= rows) {
454 char const *env_add[2], *pager;
456 pager = n_pager_get(&env_add[0]);
457 env_add[1] = NULL;
458 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
459 env_add, NULL);
460 goto jleave;
464 while ((c = getc(fp)) != EOF)
465 putc(c, n_stdout);
466 jleave:
467 NYD_LEAVE;
470 FL enum protocol
471 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
472 char const **adjusted_or_null)
474 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
475 char const *cp, *orig_name;
476 enum protocol rv = PROTO_UNKNOWN;
477 NYD_ENTER;
479 if(name[0] == '%' && name[1] == ':')
480 name += 2;
481 orig_name = name;
483 for (cp = name; *cp && *cp != ':'; cp++)
484 if (!alnumchar(*cp))
485 goto jfile;
487 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
488 if(!strncmp(name, "file", sizeof("file") -1) ||
489 !strncmp(name, "mbox", sizeof("mbox") -1))
490 rv = PROTO_FILE;
491 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
492 rv = PROTO_MAILDIR;
493 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
494 #ifdef HAVE_POP3
495 rv = PROTO_POP3;
496 #else
497 n_err(_("No POP3 support compiled in\n"));
498 #endif
499 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
500 #if defined HAVE_POP3 && defined HAVE_SSL
501 rv = PROTO_POP3;
502 #else
503 n_err(_("No POP3S support compiled in\n"));
504 #endif
506 else if(!strncmp(name, "imap", sizeof("imap") -1)){
507 #ifdef HAVE_IMAP
508 rv = PROTO_IMAP;
509 #else
510 n_err(_("No IMAP support compiled in\n"));
511 #endif
512 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
513 #if defined HAVE_IMAP && defined HAVE_SSL
514 rv = PROTO_IMAP;
515 #else
516 n_err(_("No IMAPS support compiled in\n"));
517 #endif
519 orig_name = &cp[3];
520 goto jleave;
523 jfile:
524 rv = PROTO_FILE;
526 if(check_stat || try_hooks){
527 struct n_file_type ft;
528 struct stat stb;
529 char *np;
530 size_t sz;
532 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
533 memcpy(np, name, sz + 1);
535 if(!stat(name, &stb)){
536 if(S_ISDIR(stb.st_mode) &&
537 (memcpy(&np[sz], "/tmp", 5),
538 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
539 (memcpy(&np[sz], "/new", 5),
540 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
541 (memcpy(&np[sz], "/cur", 5),
542 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
543 rv = PROTO_MAILDIR;
544 }else if(try_hooks && n_filetype_trial(&ft, name))
545 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
546 else if((cp = ok_vlook(newfolders)) != NULL &&
547 !asccasecmp(cp, "maildir"))
548 rv = PROTO_MAILDIR;
550 n_lofi_free(np);
552 jleave:
553 if(adjusted_or_null != NULL)
554 *adjusted_or_null = orig_name;
555 NYD_LEAVE;
556 return rv;
559 FL char *
560 n_c_to_hex_base16(char store[3], char c){
561 static char const itoa16[] = "0123456789ABCDEF";
562 NYD2_ENTER;
564 store[2] = '\0';
565 store[1] = itoa16[(ui8_t)c & 0x0F];
566 c = ((ui8_t)c >> 4) & 0x0F;
567 store[0] = itoa16[(ui8_t)c];
568 NYD2_LEAVE;
569 return store;
572 FL si32_t
573 n_c_from_hex_base16(char const hex[2]){
574 static ui8_t const atoi16[] = {
575 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
576 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
577 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
578 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
579 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
581 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
583 ui8_t i1, i2;
584 si32_t rv;
585 NYD2_ENTER;
587 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
588 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
589 goto jerr;
590 i1 = atoi16[i1];
591 i2 = atoi16[i2];
592 if ((i1 | i2) & 0xF0u)
593 goto jerr;
594 rv = i1;
595 rv <<= 4;
596 rv += i2;
597 jleave:
598 NYD2_LEAVE;
599 return rv;
600 jerr:
601 rv = -1;
602 goto jleave;
605 FL enum n_idec_state
606 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
607 enum n_idec_mode idm, char const **endptr_or_null){
608 ui8_t currc;
609 ui64_t res, cut;
610 enum n_idec_state rv;
611 NYD_ENTER;
613 idm &= n__IDEC_MODE_MASK;
614 rv = n_IDEC_STATE_NONE | idm;
615 res = 0;
617 if(clen == UIZ_MAX){
618 if(*cbuf == '\0')
619 goto jeinval;
620 }else if(clen == 0)
621 goto jeinval;
623 assert(base != 1 && base <= 36);
624 /*if(base == 1 || base > 36)
625 * goto jeinval;*/
627 /* Leading WS */
628 while(spacechar(*cbuf))
629 if(*++cbuf == '\0' || --clen == 0)
630 goto jeinval;
632 /* Check sign */
633 switch(*cbuf){
634 case '-':
635 rv |= n_IDEC_STATE_SEEN_MINUS;
636 /* FALLTHROUGH */
637 case '+':
638 if(*++cbuf == '\0' || --clen == 0)
639 goto jeinval;
640 break;
643 /* Base detection/skip */
644 if(*cbuf != '0'){
645 if(base == 0){
646 base = 10;
648 /* Support BASE#number prefix, where BASE is decimal 2-36 */
649 if(clen > 2){
650 char c1, c2, c3;
652 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
653 (((c2 = cbuf[1]) == '#') ||
654 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
655 base = a_aux_idec_atoi[(ui8_t)c1];
656 if(c2 == '#')
657 c3 = cbuf[2];
658 else{
659 c3 = cbuf[3];
660 base *= 10; /* xxx Inline atoi decimal base */
661 base += a_aux_idec_atoi[(ui8_t)c2];
664 /* We do not interpret this as BASE#number at all if either we
665 * did not get a valid base or if the first char is not valid
666 * according to base, to comply to the latest interpretion of
667 * "prefix", see comment for standard prefixes below */
668 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
669 base = 10;
670 else if(c2 == '#')
671 clen -= 2, cbuf += 2;
672 else
673 clen -= 3, cbuf += 3;
678 /* Character must be valid for base */
679 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
680 if(currc >= base)
681 goto jeinval;
682 }else{
683 /* 0 always valid as is, fallback base 10 */
684 if(*++cbuf == '\0' || --clen == 0)
685 goto jleave;
687 /* Base "detection" */
688 if(base == 0 || base == 2 || base == 16){
689 switch(*cbuf){
690 case 'x':
691 case 'X':
692 if((base & 2) == 0){
693 base = 0x10;
694 goto jprefix_skip;
696 break;
697 case 'b':
698 case 'B':
699 if((base & 16) == 0){
700 base = 2; /* 0b10 */
701 /* Char after prefix must be valid. However, after some error
702 * in the tor software all libraries (which had to) turned to
703 * an interpretation of the C standard which says that the
704 * prefix may optionally precede an otherwise valid sequence,
705 * which means that "0x" is not a STATE_INVAL error but gives
706 * a "0" result with a "STATE_BASE" error and a rest of "x" */
707 jprefix_skip:
708 #if 1
709 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
710 --clen, ++cbuf;
711 #else
712 if(*++cbuf == '\0' || --clen == 0)
713 goto jeinval;
715 /* Character must be valid for base, invalid otherwise */
716 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
717 if(currc >= base)
718 goto jeinval;
719 #endif
721 break;
722 default:
723 if(base == 0)
724 base = 010;
725 break;
729 /* Character must be valid for base, _EBASE otherwise */
730 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
731 if(currc >= base)
732 goto jebase;
735 for(cut = a_aux_idec_cutlimit[base - 2];;){
736 if(res >= cut){
737 if(res == cut){
738 res *= base;
739 if(res > UI64_MAX - currc)
740 goto jeover;
741 res += currc;
742 }else
743 goto jeover;
744 }else{
745 res *= base;
746 res += currc;
749 if(*++cbuf == '\0' || --clen == 0)
750 break;
752 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
753 if(currc >= base)
754 goto jebase;
757 jleave:
759 ui64_t uimask;
761 switch(rv & n__IDEC_MODE_LIMIT_MASK){
762 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
763 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
764 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
765 default: uimask = UI64_MAX; break;
767 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
768 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
769 uimask >>= 1;
771 if(res & ~uimask){
772 /* XXX never entered unless _SIGNED_TYPE! */
773 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
774 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
775 if(res > uimask + 1){
776 res = uimask << 1;
777 res &= ~uimask;
778 }else{
779 res = -res;
780 break;
782 }else
783 res = uimask;
784 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
785 rv |= n_IDEC_STATE_EOVERFLOW;
786 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
787 res = -res;
788 }while(0);
790 switch(rv & n__IDEC_MODE_LIMIT_MASK){
791 case n_IDEC_MODE_LIMIT_8BIT:
792 if(rv & n_IDEC_MODE_SIGNED_TYPE)
793 *(si8_t*)resp = (si8_t)res;
794 else
795 *(ui8_t*)resp = (ui8_t)res;
796 break;
797 case n_IDEC_MODE_LIMIT_16BIT:
798 if(rv & n_IDEC_MODE_SIGNED_TYPE)
799 *(si16_t*)resp = (si16_t)res;
800 else
801 *(ui16_t*)resp = (ui16_t)res;
802 break;
803 case n_IDEC_MODE_LIMIT_32BIT:
804 if(rv & n_IDEC_MODE_SIGNED_TYPE)
805 *(si32_t*)resp = (si32_t)res;
806 else
807 *(ui32_t*)resp = (ui32_t)res;
808 break;
809 default:
810 if(rv & n_IDEC_MODE_SIGNED_TYPE)
811 *(si64_t*)resp = (si64_t)res;
812 else
813 *(ui64_t*)resp = (ui64_t)res;
814 break;
817 if(endptr_or_null != NULL)
818 *endptr_or_null = cbuf;
819 if(*cbuf == '\0' || clen == 0)
820 rv |= n_IDEC_STATE_CONSUMED;
821 NYD_LEAVE;
822 return rv;
824 jeinval:
825 rv |= n_IDEC_STATE_EINVAL;
826 goto j_maxval;
827 jebase:
828 /* Not a base error for terminator and whitespace! */
829 if(*cbuf != '\0' && !spacechar(*cbuf))
830 rv |= n_IDEC_STATE_EBASE;
831 goto jleave;
833 jeover:
834 /* Overflow error: consume input until bad character or length out */
835 for(;;){
836 if(*++cbuf == '\0' || --clen == 0)
837 break;
838 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
839 if(currc >= base)
840 break;
843 rv |= n_IDEC_STATE_EOVERFLOW;
844 j_maxval:
845 if(rv & n_IDEC_MODE_SIGNED_TYPE)
846 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
847 : (ui64_t)SI64_MAX;
848 else
849 res = UI64_MAX;
850 rv &= ~n_IDEC_STATE_SEEN_MINUS;
851 goto jleave;
854 FL char *
855 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
856 enum n_ienc_mode iem){
857 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
859 ui8_t shiftmodu;
860 char const *itoa;
861 char *rv;
862 NYD_ENTER;
864 iem &= n__IENC_MODE_MASK;
866 assert(base != 1 && base <= 36);
867 /*if(base == 1 || base > 36){
868 * rv = NULL;
869 * goto jleave;
870 *}*/
872 rv = &cbuf[n_IENC_BUFFER_SIZE];
873 *--rv = '\0';
874 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
875 : a_aux_ienc_itoa_upper;
877 if((si64_t)value < 0){
878 iem |= a_ISNEG;
879 if(iem & n_IENC_MODE_SIGNED_TYPE){
880 /* self->is_negative = TRU1; */
881 value = -value;
885 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
886 --base; /* convert to mask */
888 *--rv = itoa[value & base];
889 value >>= shiftmodu;
890 }while(value != 0);
892 if(!(iem & n_IENC_MODE_NO_PREFIX)){
893 /* self->before_prefix = cp; */
894 if(shiftmodu == 4)
895 *--rv = 'x';
896 else if(shiftmodu == 1)
897 *--rv = 'b';
898 else if(shiftmodu != 3){
899 ++base; /* Reconvert from mask */
900 goto jnumber_sign_prefix;
902 *--rv = '0';
904 }else{
906 shiftmodu = value % base;
907 value /= base;
908 *--rv = itoa[shiftmodu];
909 }while(value != 0);
911 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
912 jnumber_sign_prefix:
913 value = base;
914 base = 10;
915 *--rv = '#';
917 shiftmodu = value % base;
918 value /= base;
919 *--rv = itoa[shiftmodu];
920 }while(value != 0);
923 if(iem & n_IENC_MODE_SIGNED_TYPE){
924 char c;
926 if(iem & a_ISNEG)
927 c = '-';
928 else if(iem & n_IENC_MODE_SIGNED_PLUS)
929 c = '+';
930 else if(iem & n_IENC_MODE_SIGNED_SPACE)
931 c = ' ';
932 else
933 c = '\0';
935 if(c != '\0')
936 *--rv = c;
939 NYD_LEAVE;
940 return rv;
943 FL ui32_t
944 n_torek_hash(char const *name){
945 /* Chris Torek's hash */
946 char c;
947 ui32_t h;
948 NYD2_ENTER;
950 for(h = 0; (c = *name++) != '\0';)
951 h = (h * 33) + c;
952 NYD2_LEAVE;
953 return h;
956 FL ui32_t
957 n_torek_ihashn(char const *dat, size_t len){
958 /* See n_torek_hash() */
959 char c;
960 ui32_t h;
961 NYD2_ENTER;
963 if(len == UIZ_MAX)
964 for(h = 0; (c = *dat++) != '\0';)
965 h = (h * 33) + lowerconv(c);
966 else
967 for(h = 0; len > 0; --len){
968 c = *dat++;
969 h = (h * 33) + lowerconv(c);
971 NYD2_LEAVE;
972 return h;
975 FL ui32_t
976 n_prime_next(ui32_t n){
977 static ui32_t const primes[] = {
978 5, 11, 23, 47, 97, 157, 283,
979 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
980 131071, 262139, 524287, 1048573, 2097143, 4194301,
981 8388593, 16777213, 33554393, 67108859, 134217689,
982 268435399, 536870909, 1073741789, 2147483647
984 ui32_t i, mprime;
985 NYD2_ENTER;
987 i = (n < primes[n_NELEM(primes) / 4] ? 0
988 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
989 : n_NELEM(primes) / 2));
991 do if((mprime = primes[i]) > n)
992 break;
993 while(++i < n_NELEM(primes));
995 if(i == n_NELEM(primes) && mprime < n)
996 mprime = n;
997 NYD2_LEAVE;
998 return mprime;
1001 FL char const *
1002 n_getdeadletter(void){
1003 char const *cp;
1004 bool_t bla;
1005 NYD_ENTER;
1007 bla = FAL0;
1008 jredo:
1009 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1010 if(cp == NULL || strlen(cp) >= PATH_MAX){
1011 if(!bla){
1012 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1013 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1014 ok_vclear(DEAD);
1015 bla = TRU1;
1016 goto jredo;
1017 }else{
1018 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1019 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1022 NYD_LEAVE;
1023 return cp;
1026 FL char *
1027 n_nodename(bool_t mayoverride){
1028 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1030 struct utsname ut;
1031 char *hn;
1032 #ifdef HAVE_SOCKETS
1033 # ifdef HAVE_GETADDRINFO
1034 struct addrinfo hints, *res;
1035 # else
1036 struct hostent *hent;
1037 # endif
1038 #endif
1039 NYD2_ENTER;
1041 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1043 }else if((hn = sys_hostname) == NULL){
1044 bool_t lofi;
1046 lofi = FAL0;
1047 uname(&ut);
1048 hn = ut.nodename;
1050 #ifdef HAVE_SOCKETS
1051 # ifdef HAVE_GETADDRINFO
1052 memset(&hints, 0, sizeof hints);
1053 hints.ai_family = AF_UNSPEC;
1054 hints.ai_flags = AI_CANONNAME;
1055 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1056 if(res->ai_canonname != NULL){
1057 size_t l;
1059 l = strlen(res->ai_canonname) +1;
1060 hn = n_lofi_alloc(l);
1061 lofi = TRU1;
1062 memcpy(hn, res->ai_canonname, l);
1064 freeaddrinfo(res);
1066 # else
1067 hent = gethostbyname(hn);
1068 if(hent != NULL)
1069 hn = hent->h_name;
1070 # endif
1071 #endif /* HAVE_SOCKETS */
1073 #ifdef HAVE_IDNA
1074 /* C99 */{
1075 struct n_string cnv;
1077 n_string_creat(&cnv);
1078 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1079 n_panic(_("The system hostname is invalid, "
1080 "IDNA conversion failed: %s\n"),
1081 n_shexp_quote_cp(hn, FAL0));
1082 sys_hostname = n_string_cp(&cnv);
1083 n_string_drop_ownership(&cnv);
1084 /*n_string_gut(&cnv);*/
1086 #else
1087 sys_hostname = sstrdup(hn);
1088 #endif
1090 if(lofi)
1091 n_lofi_free(hn);
1092 hn = sys_hostname;
1095 if(hostname != NULL && hostname != sys_hostname)
1096 n_free(hostname);
1097 hostname = sstrdup(hn);
1098 NYD2_LEAVE;
1099 return hostname;
1102 #ifdef HAVE_IDNA
1103 FL bool_t
1104 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1105 char *idna_utf8;
1106 bool_t lofi, rv;
1107 NYD_ENTER;
1109 if(ilen == UIZ_MAX)
1110 ilen = strlen(ibuf);
1112 lofi = FAL0;
1114 if((rv = (ilen == 0)))
1115 goto jleave;
1116 if(ibuf[ilen] != '\0'){
1117 lofi = TRU1;
1118 idna_utf8 = n_lofi_alloc(ilen +1);
1119 memcpy(idna_utf8, ibuf, ilen);
1120 idna_utf8[ilen] = '\0';
1121 ibuf = idna_utf8;
1123 ilen = 0;
1125 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1126 if(n_psonce & n_PSO_UNICODE)
1127 # endif
1128 idna_utf8 = n_UNCONST(ibuf);
1129 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1130 else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
1131 ok_vlook(ttycharset), ibuf)) == NULL)
1132 goto jleave;
1133 # endif
1135 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1136 /* C99 */{
1137 char *idna_ascii;
1138 int f, rc;
1140 f = IDN2_NONTRANSITIONAL;
1141 jidn2_redo:
1142 if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
1143 out = n_string_assign_cp(out, idna_ascii);
1144 idn2_free(idna_ascii);
1145 rv = TRU1;
1146 ilen = out->s_len;
1147 }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
1148 f = IDN2_TRANSITIONAL;
1149 goto jidn2_redo;
1153 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1154 /* C99 */{
1155 char *idna_ascii;
1157 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1158 out = n_string_assign_cp(out, idna_ascii);
1159 idn_free(idna_ascii);
1160 rv = TRU1;
1161 ilen = out->s_len;
1165 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1166 ilen = strlen(idna_utf8);
1167 jredo:
1168 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
1169 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1170 case idn_buffer_overflow:
1171 ilen += HOST_NAME_MAX +1;
1172 goto jredo;
1173 case idn_success:
1174 rv = TRU1;
1175 ilen = strlen(out->s_dat);
1176 break;
1177 default:
1178 ilen = 0;
1179 break;
1182 # else
1183 # error Unknown HAVE_IDNA
1184 # endif
1185 jleave:
1186 if(lofi)
1187 n_lofi_free(n_UNCONST(ibuf));
1188 out = n_string_trunc(out, ilen);
1189 NYD_LEAVE;
1190 return rv;
1192 #endif /* HAVE_IDNA */
1194 FL char *
1195 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1196 struct str b64;
1197 char *indat, *cp, *oudat;
1198 size_t i, inlen, oulen;
1199 NYD_ENTER;
1201 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1202 n_psonce |= n_PSO_RANDOM_INIT;
1204 if(n_poption & n_PO_D_V){
1205 char const *prngn;
1207 #if defined HAVE_POSIX_RANDOM
1208 prngn = "POSIX/arc4random";
1209 #elif defined HAVE_SSL_RANDOM
1210 prngn = "*SSL RAND_*";
1211 #elif defined HAVE_GETRANDOM
1212 prngn = "getrandom(2/3) + builtin ARC4";
1213 #elif !defined HAVE_NOEXTRANDOM
1214 prngn = "/dev/urandom + builtin ARC4";
1215 #else
1216 prngn = "builtin ARC4";
1217 #endif
1218 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn);
1221 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
1222 a_aux_rand_init();
1223 #endif
1226 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1227 * with PAD stripped is still longer than what the user requests, easy way.
1228 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1229 * include the base64 PAD characters in our random string: give some pad */
1230 i = len;
1231 if((inlen = i % 3) != 0)
1232 i += 3 - inlen;
1233 jinc1:
1234 inlen = i >> 2;
1235 oulen = inlen << 2;
1236 if(oulen < len){
1237 i += 3;
1238 goto jinc1;
1240 inlen = inlen + (inlen << 1);
1242 indat = n_lofi_alloc(inlen +1);
1244 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1245 #ifdef HAVE_SSL_RANDOM
1246 ssl_rand_bytes(indat, inlen);
1247 #elif !defined HAVE_POSIX_RANDOM
1248 for(i = inlen; i-- > 0;)
1249 indat[i] = (char)a_aux_rand_get8();
1250 #else
1251 for(cp = indat, i = inlen; i > 0;){
1252 union {ui32_t i4; char c[4];} r;
1253 size_t j;
1255 r.i4 = (ui32_t)arc4random();
1256 switch((j = i & 3)){
1257 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1258 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1259 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1260 default: cp[0] = r.c[0]; break;
1262 cp += j;
1263 i -= j;
1265 #endif
1266 }else{
1267 for(cp = indat, i = inlen; i > 0;){
1268 union {ui32_t i4; char c[4];} r;
1269 size_t j;
1271 r.i4 = ++*reprocnt_or_null;
1272 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1273 char x;
1275 x = r.c[0];
1276 r.c[0] = r.c[3];
1277 r.c[3] = x;
1278 x = r.c[1];
1279 r.c[1] = r.c[2];
1280 r.c[2] = x;
1282 switch((j = i & 3)){
1283 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1284 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1285 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1286 default: cp[0] = r.c[0]; break;
1288 cp += j;
1289 i -= j;
1293 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1294 b64.s = oudat;
1295 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1296 assert(b64.l >= len);
1297 memcpy(dat, b64.s, len);
1298 dat[len] = '\0';
1299 if(oudat != dat)
1300 n_lofi_free(oudat);
1302 n_lofi_free(indat);
1304 NYD_LEAVE;
1305 return dat;
1308 FL char *
1309 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1310 char *dat;
1311 NYD_ENTER;
1313 dat = n_autorec_alloc(len +1);
1314 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1315 NYD_LEAVE;
1316 return dat;
1319 FL bool_t
1320 n_boolify(char const *inbuf, uiz_t inlen, bool_t emptyrv){
1321 bool_t rv;
1322 NYD2_ENTER;
1323 assert(inlen == 0 || inbuf != NULL);
1325 if(inlen == UIZ_MAX)
1326 inlen = strlen(inbuf);
1328 if(inlen == 0)
1329 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1330 else{
1331 if((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1332 !ascncasecmp(inbuf, "true", inlen) ||
1333 !ascncasecmp(inbuf, "yes", inlen) ||
1334 !ascncasecmp(inbuf, "on", inlen))
1335 rv = TRU1;
1336 else if((inlen == 1 &&
1337 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1338 !ascncasecmp(inbuf, "false", inlen) ||
1339 !ascncasecmp(inbuf, "no", inlen) ||
1340 !ascncasecmp(inbuf, "off", inlen))
1341 rv = FAL0;
1342 else{
1343 ui64_t ib;
1345 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1346 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1347 ) != n_IDEC_STATE_CONSUMED)
1348 rv = TRUM1;
1349 else
1350 rv = (ib != 0);
1353 NYD2_LEAVE;
1354 return rv;
1357 FL bool_t
1358 n_quadify(char const *inbuf, uiz_t inlen, char const *prompt, bool_t emptyrv){
1359 bool_t rv;
1360 NYD2_ENTER;
1361 assert(inlen == 0 || inbuf != NULL);
1363 if(inlen == UIZ_MAX)
1364 inlen = strlen(inbuf);
1366 if(inlen == 0)
1367 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1368 else if((rv = n_boolify(inbuf, inlen, emptyrv)) < FAL0 &&
1369 !ascncasecmp(inbuf, "ask-", 4) &&
1370 (rv = n_boolify(&inbuf[4], inlen - 4, emptyrv)) >= FAL0 &&
1371 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1372 rv = getapproval(prompt, rv);
1373 NYD2_LEAVE;
1374 return rv;
1377 FL bool_t
1378 n_is_all_or_aster(char const *name){
1379 bool_t rv;
1380 NYD2_ENTER;
1382 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1383 NYD2_LEAVE;
1384 return rv;
1387 FL struct n_timespec const *
1388 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1389 static struct n_timespec ts_now;
1390 NYD2_ENTER;
1392 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1393 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1394 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1395 ts_now.ts_nsec = 0;
1396 }else if(force_update || ts_now.ts_sec == 0){
1397 #ifdef HAVE_CLOCK_GETTIME
1398 struct timespec ts;
1400 clock_gettime(CLOCK_REALTIME, &ts);
1401 ts_now.ts_sec = (si64_t)ts.tv_sec;
1402 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1403 #elif defined HAVE_GETTIMEOFDAY
1404 struct timeval tv;
1406 gettimeofday(&tv, NULL);
1407 ts_now.ts_sec = (si64_t)tv.tv_sec;
1408 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1409 #else
1410 ts_now.ts_sec = (si64_t)time(NULL);
1411 ts_now.ts_nsec = 0;
1412 #endif
1415 /* Just in case.. */
1416 if(n_UNLIKELY(ts_now.ts_sec < 0))
1417 ts_now.ts_sec = 0;
1418 NYD2_LEAVE;
1419 return &ts_now;
1422 FL void
1423 time_current_update(struct time_current *tc, bool_t full_update){
1424 NYD_ENTER;
1425 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1427 if(full_update){
1428 char *cp;
1429 struct tm *tmp;
1430 time_t t;
1432 t = tc->tc_time;
1433 jredo:
1434 if((tmp = gmtime(&t)) == NULL){
1435 t = 0;
1436 goto jredo;
1438 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1439 if((tmp = localtime(&t)) == NULL){
1440 t = 0;
1441 goto jredo;
1443 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1444 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1445 *cp++ = '\n';
1446 *cp = '\0';
1447 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1449 NYD_LEAVE;
1452 FL char *
1453 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1454 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1455 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1456 * ISO C requires us to use the above format string,
1457 * even if it will not fit in the buffer. Thus asctime_r
1458 * is _supposed_ to crash if the fields in tm are too large.
1459 * We follow this behavior and crash "gracefully" to warn
1460 * application developers that they may not be so lucky
1461 * on other implementations (e.g. stack smashing..).
1462 * So we need to do it on our own or the libc may kill us */
1463 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1465 si32_t y, md, th, tm, ts;
1466 char const *wdn, *mn;
1467 struct tm const *tmp;
1468 NYD_ENTER;
1470 if((tmp = localtime_or_nil) == NULL){
1471 time_t t;
1473 t = (time_t)secsepoch;
1474 jredo:
1475 if((tmp = localtime(&t)) == NULL){
1476 /* TODO error log */
1477 t = 0;
1478 goto jredo;
1482 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1483 y = 1970;
1484 wdn = n_weekday_names[4];
1485 mn = n_month_names[0];
1486 md = 1;
1487 th = tm = ts = 0;
1488 }else{
1489 y += 1900;
1490 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1491 ? n_weekday_names[tmp->tm_wday] : n_qm;
1492 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1493 ? n_month_names[tmp->tm_mon] : n_qm;
1495 if((md = tmp->tm_mday) < 1 || md > 31)
1496 md = 1;
1498 if((th = tmp->tm_hour) < 0 || th > 23)
1499 th = 0;
1500 if((tm = tmp->tm_min) < 0 || tm > 59)
1501 tm = 0;
1502 if((ts = tmp->tm_sec) < 0 || ts > 60)
1503 ts = 0;
1506 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1507 wdn, mn, md, th, tm, ts, y);
1508 NYD_LEAVE;
1509 return buf;
1512 FL uiz_t
1513 n_msleep(uiz_t millis, bool_t ignint){
1514 uiz_t rv;
1515 NYD2_ENTER;
1517 #ifdef HAVE_NANOSLEEP
1518 /* C99 */{
1519 struct timespec ts, trem;
1520 int i;
1522 ts.tv_sec = millis / 1000;
1523 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1525 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1526 ts = trem;
1527 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1530 #elif defined HAVE_SLEEP
1531 if((millis /= 1000) == 0)
1532 millis = 1;
1533 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1534 millis = rv;
1535 #else
1536 # error Configuration should have detected a function for sleeping.
1537 #endif
1539 NYD2_LEAVE;
1540 return rv;
1543 FL void
1544 n_err(char const *format, ...){
1545 va_list ap;
1546 NYD2_ENTER;
1548 va_start(ap, format);
1549 #ifdef HAVE_ERRORS
1550 if(n_psonce & n_PSO_INTERACTIVE)
1551 n_verr(format, ap);
1552 else
1553 #endif
1555 size_t len;
1556 bool_t doname;
1558 doname = FAL0;
1560 while(*format == '\n'){
1561 doname = TRU1;
1562 putc('\n', n_stderr);
1563 ++format;
1566 if(doname)
1567 a_aux_err_linelen = 0;
1569 if((len = strlen(format)) > 0){
1570 if(doname || a_aux_err_linelen == 0){
1571 char const *cp;
1573 if(*(cp = ok_vlook(log_prefix)) != '\0')
1574 fputs(cp, n_stderr);
1576 vfprintf(n_stderr, format, ap);
1578 /* C99 */{
1579 size_t i = len;
1581 if(format[--len] == '\n'){
1582 a_aux_err_linelen = (i -= ++len);
1583 break;
1585 ++a_aux_err_linelen;
1586 }while(len > 0);
1590 fflush(n_stderr);
1592 va_end(ap);
1593 NYD2_LEAVE;
1596 FL void
1597 n_verr(char const *format, va_list ap){
1598 #ifdef HAVE_ERRORS
1599 struct a_aux_err_node *enp;
1600 #endif
1601 bool_t doname;
1602 size_t len;
1603 NYD2_ENTER;
1605 doname = FAL0;
1607 while(*format == '\n'){
1608 putc('\n', n_stderr);
1609 doname = TRU1;
1610 ++format;
1613 if(doname){
1614 a_aux_err_linelen = 0;
1615 #ifdef HAVE_ERRORS
1616 if(n_psonce & n_PSO_INTERACTIVE){
1617 if((enp = a_aux_err_tail) != NULL &&
1618 (enp->ae_str.s_len > 0 &&
1619 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1620 n_string_push_c(&enp->ae_str, '\n');
1622 #endif
1625 if((len = strlen(format)) == 0)
1626 goto jleave;
1627 #ifdef HAVE_ERRORS
1628 n_pstate |= n_PS_ERRORS_PROMPT;
1629 #endif
1631 if(doname || a_aux_err_linelen == 0){
1632 char const *cp;
1634 if(*(cp = ok_vlook(log_prefix)) != '\0')
1635 fputs(cp, n_stderr);
1638 /* C99 */{
1639 size_t i = len;
1641 if(format[--len] == '\n'){
1642 a_aux_err_linelen = (i -= ++len);
1643 break;
1645 ++a_aux_err_linelen;
1646 }while(len > 0);
1649 #ifdef HAVE_ERRORS
1650 if(!(n_psonce & n_PSO_INTERACTIVE))
1651 #endif
1652 vfprintf(n_stderr, format, ap);
1653 #ifdef HAVE_ERRORS
1654 else{
1655 int imax, i;
1656 n_LCTAV(ERRORS_MAX > 3);
1658 /* Link it into the `errors' message ring */
1659 if((enp = a_aux_err_tail) == NULL){
1660 jcreat:
1661 enp = smalloc(sizeof *enp);
1662 enp->ae_next = NULL;
1663 n_string_creat(&enp->ae_str);
1664 if(a_aux_err_tail != NULL)
1665 a_aux_err_tail->ae_next = enp;
1666 else
1667 a_aux_err_head = enp;
1668 a_aux_err_tail = enp;
1669 ++a_aux_err_cnt;
1670 }else if(doname ||
1671 (enp->ae_str.s_len > 0 &&
1672 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1673 if(a_aux_err_cnt < ERRORS_MAX)
1674 goto jcreat;
1676 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1677 a_aux_err_tail->ae_next = enp;
1678 a_aux_err_tail = enp;
1679 enp->ae_next = NULL;
1680 n_string_trunc(&enp->ae_str, 0);
1683 # ifdef HAVE_N_VA_COPY
1684 imax = 64;
1685 # else
1686 imax = n_MIN(LINESIZE, 1024);
1687 # endif
1688 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1689 # ifdef HAVE_N_VA_COPY
1690 va_list vac;
1692 n_va_copy(vac, ap);
1693 # else
1694 # define vac ap
1695 # endif
1697 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1698 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1699 # ifdef HAVE_N_VA_COPY
1700 va_end(vac);
1701 # else
1702 # undef vac
1703 # endif
1704 if(i <= 0)
1705 goto jleave;
1706 if(UICMP(z, i, >=, imax)){
1707 # ifdef HAVE_N_VA_COPY
1708 /* XXX Check overflow for upcoming LEN+++i! */
1709 n_string_trunc(&enp->ae_str, len);
1710 continue;
1711 # else
1712 i = (int)strlen(&enp->ae_str.s_dat[len]);
1713 # endif
1715 break;
1717 n_string_trunc(&enp->ae_str, len + (size_t)i);
1719 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1721 #endif /* HAVE_ERRORS */
1723 jleave:
1724 fflush(n_stderr);
1725 NYD2_LEAVE;
1728 FL void
1729 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1730 va_list ap;
1731 NYD_X;
1733 va_start(ap, format);
1734 vfprintf(n_stderr, format, ap);
1735 va_end(ap);
1736 fflush(n_stderr);
1739 FL void
1740 n_perr(char const *msg, int errval){
1741 int e;
1742 char const *fmt;
1743 NYD2_ENTER;
1745 if(msg == NULL){
1746 fmt = "%s%s\n";
1747 msg = n_empty;
1748 }else
1749 fmt = "%s: %s\n";
1751 e = (errval == 0) ? n_err_no : errval;
1752 n_err(fmt, msg, n_err_to_doc(e));
1753 if(errval == 0)
1754 n_err_no = e;
1755 NYD2_LEAVE;
1758 FL void
1759 n_alert(char const *format, ...){
1760 va_list ap;
1761 NYD2_ENTER;
1763 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1765 va_start(ap, format);
1766 n_verr(format, ap);
1767 va_end(ap);
1769 n_err("\n");
1770 NYD2_LEAVE;
1773 FL void
1774 n_panic(char const *format, ...){
1775 va_list ap;
1776 NYD2_ENTER;
1778 if(a_aux_err_linelen > 0){
1779 putc('\n', n_stderr);
1780 a_aux_err_linelen = 0;
1782 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1784 va_start(ap, format);
1785 vfprintf(n_stderr, format, ap);
1786 va_end(ap);
1788 putc('\n', n_stderr);
1789 fflush(n_stderr);
1790 NYD2_LEAVE;
1791 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1794 #ifdef HAVE_ERRORS
1795 FL int
1796 c_errors(void *v){
1797 char **argv = v;
1798 struct a_aux_err_node *enp;
1799 NYD_ENTER;
1801 if(*argv == NULL)
1802 goto jlist;
1803 if(argv[1] != NULL)
1804 goto jerr;
1805 if(!asccasecmp(*argv, "show"))
1806 goto jlist;
1807 if(!asccasecmp(*argv, "clear"))
1808 goto jclear;
1809 jerr:
1810 fprintf(n_stderr,
1811 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1812 v = NULL;
1813 jleave:
1814 NYD_LEAVE;
1815 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1817 jlist:{
1818 FILE *fp;
1819 size_t i;
1821 if(a_aux_err_head == NULL){
1822 fprintf(n_stderr, _("The error ring is empty\n"));
1823 goto jleave;
1826 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1827 NULL){
1828 fprintf(n_stderr, _("tmpfile"));
1829 v = NULL;
1830 goto jleave;
1833 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1834 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1835 /* We don't know whether last string ended with NL; be simple XXX */
1836 putc('\n', fp);
1838 page_or_print(fp, 0);
1839 Fclose(fp);
1841 /* FALLTHRU */
1843 jclear:
1844 a_aux_err_tail = NULL;
1845 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1846 a_aux_err_linelen = 0;
1847 while((enp = a_aux_err_head) != NULL){
1848 a_aux_err_head = enp->ae_next;
1849 n_string_gut(&enp->ae_str);
1850 free(enp);
1852 goto jleave;
1854 #endif /* HAVE_ERRORS */
1856 FL char const *
1857 n_err_to_doc(si32_t eno){
1858 char const *rv;
1859 struct a_aux_err_map const *aemp;
1860 NYD2_ENTER;
1862 aemp = a_aux_err_map_from_no(eno);
1863 rv = &a_aux_err_docs[aemp->aem_docoff];
1864 NYD2_LEAVE;
1865 return rv;
1868 FL char const *
1869 n_err_to_name(si32_t eno){
1870 char const *rv;
1871 struct a_aux_err_map const *aemp;
1872 NYD2_ENTER;
1874 aemp = a_aux_err_map_from_no(eno);
1875 rv = &a_aux_err_names[aemp->aem_nameoff];
1876 NYD2_LEAVE;
1877 return rv;
1880 FL si32_t
1881 n_err_from_name(char const *name){
1882 struct a_aux_err_map const *aemp;
1883 ui32_t hash, i, j, x;
1884 si32_t rv;
1885 NYD2_ENTER;
1887 hash = n_torek_hash(name);
1889 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1890 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1891 break;
1893 aemp = &a_aux_err_map[x];
1894 if(aemp->aem_hash == hash &&
1895 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1896 rv = aemp->aem_err_no;
1897 goto jleave;
1900 if(++i == a_AUX_ERR_REV_PRIME){
1901 #ifdef a_AUX_ERR_REV_WRAPAROUND
1902 i = 0;
1903 #else
1904 break;
1905 #endif
1909 /* Have not found it. But wait, it could be that the user did, e.g.,
1910 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1911 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1912 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1913 ) == n_IDEC_STATE_CONSUMED){
1914 aemp = a_aux_err_map_from_no(rv);
1915 rv = aemp->aem_err_no;
1916 goto jleave;
1919 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1920 jleave:
1921 NYD2_LEAVE;
1922 return rv;
1925 #ifdef HAVE_REGEX
1926 FL char const *
1927 n_regex_err_to_doc(const regex_t *rep, int e){
1928 char *cp;
1929 size_t i;
1930 NYD2_ENTER;
1932 i = regerror(e, rep, NULL, 0) +1;
1933 cp = salloc(i);
1934 regerror(e, rep, cp, i);
1935 NYD2_LEAVE;
1936 return cp;
1938 #endif
1940 /* s-it-mode */