THANKS: Rich Felker
[s-mailx.git] / auxlily.c
blobc6e6f8d06996d8740f1889baa4c2c32ffcb82b2f
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_LIBIDN2
65 # include <idn2.h>
66 # elif HAVE_IDNA == HAVE_IDNA_LIBIDNA
67 # include <idna.h>
68 # include <idn-free.h>
69 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
70 # include <idn/api.h>
71 # endif
72 #endif
74 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
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 && !n_RANDOM_USE_XSSL
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 && !n_RANDOM_USE_XSSL
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t a_aux_rand_get8(void);
183 # ifndef HAVE_GETRANDOM
184 static ui32_t a_aux_rand_weak(ui32_t seed);
185 # endif
186 #endif
188 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
189 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
191 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
192 static void
193 a_aux_rand_init(void){
194 # ifndef HAVE_GETRANDOM
195 # ifdef HAVE_CLOCK_GETTIME
196 struct timespec ts;
197 # else
198 struct timeval ts;
199 # endif
200 union {int fd; size_t i;} u;
201 ui32_t seed, rnd;
202 # endif
203 NYD2_ENTER;
205 a_aux_rand = n_alloc(sizeof *a_aux_rand);
207 # ifdef HAVE_GETRANDOM
208 /* getrandom(2) guarantees 256 without n_ERR_INTR..
209 * However, support sequential reading to avoid possible hangs that have
210 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
211 * HAVE_GETRANDOM is #defined) */
212 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
213 "Buffer too large to be served without n_ERR_INTR error");
214 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
215 "Buffer too small to serve used array indices");
216 /* C99 */{
217 size_t o, i;
219 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
220 ssize_t gr;
222 gr = HAVE_GETRANDOM(&a_aux_rand->a._dat[o], i);
223 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
224 a_aux_rand->a._dat[84]];
225 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
226 a_aux_rand->a._dat[42]];
227 /* ..but be on the safe side */
228 if(gr > 0){
229 i -= (size_t)gr;
230 if(i == 0)
231 break;
232 o += (size_t)gr;
234 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
235 "waiting a bit\n"));
236 n_msleep(250, FAL0);
240 # else /* HAVE_GETRANDOM */
241 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
242 bool_t ok;
244 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
245 sizeof(a_aux_rand->a._dat)));
246 close(u.fd);
248 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
249 a_aux_rand->a._dat[84]];
250 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
251 a_aux_rand->a._dat[42]];
252 if(ok)
253 goto jleave;
256 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
257 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
258 ui32_t t, k;
260 # ifdef HAVE_CLOCK_GETTIME
261 clock_gettime(CLOCK_REALTIME, &ts);
262 t = (ui32_t)ts.tv_nsec;
263 # else
264 gettimeofday(&ts, NULL);
265 t = (ui32_t)ts.tv_usec;
266 # endif
267 if(rnd & 1)
268 t = (t >> 16) | (t << 16);
269 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
270 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
271 if(rnd == 7 || rnd == 17)
272 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
273 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
274 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
275 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
276 if((rnd & 3) == 3)
277 seed ^= n_prime_next(seed);
281 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
282 a_aux_rand_get8();
283 jleave:
284 # endif /* !HAVE_GETRANDOM */
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 # ifndef HAVE_GETRANDOM
300 static ui32_t
301 a_aux_rand_weak(ui32_t seed){
302 /* From "Random number generators: good ones are hard to find",
303 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
304 * October 1988, p. 1195.
305 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
306 ui32_t hi;
308 if(seed == 0)
309 seed = 123459876;
310 hi = seed / 127773;
311 seed %= 127773;
312 seed = (seed * 16807) - (hi * 2836);
313 if((si32_t)seed < 0)
314 seed += SI32_MAX;
315 return seed;
317 # endif /* HAVE_GETRANDOM */
318 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
320 static struct a_aux_err_map const *
321 a_aux_err_map_from_no(si32_t eno){
322 si32_t ecmp;
323 size_t asz;
324 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
325 struct a_aux_err_map const *aemp;
326 NYD2_ENTER;
328 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
330 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
331 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
332 asz != 0; asz >>= 1){
333 tmp = &adat[asz >> 1];
334 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
335 aemp = &a_aux_err_map[(*tmp)[1]];
336 break;
338 if(ecmp > 0){
339 adat = &tmp[1];
340 --asz;
344 NYD2_LEAVE;
345 return aemp;
348 FL void
349 n_locale_init(void){
350 NYD2_ENTER;
352 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
354 #ifndef HAVE_SETLOCALE
355 n_mb_cur_max = 1;
356 #else
357 setlocale(LC_ALL, n_empty);
358 n_mb_cur_max = MB_CUR_MAX;
359 # ifdef HAVE_NL_LANGINFO
360 /* C99 */{
361 char const *cp;
363 if((cp = nl_langinfo(CODESET)) != NULL)
364 /* (Will log during startup if user set that via -S) */
365 ok_vset(ttycharset, cp);
367 # endif /* HAVE_SETLOCALE */
369 # ifdef HAVE_C90AMEND1
370 if(n_mb_cur_max > 1){
371 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
372 n_psonce |= n_PSO_UNICODE;
373 # else
374 wchar_t wc;
375 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
376 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
377 n_psonce |= n_PSO_UNICODE;
378 /* Reset possibly messed up state; luckily this also gives us an
379 * indication whether the encoding has locking shift state sequences */
380 if(mbtowc(&wc, NULL, n_mb_cur_max))
381 n_psonce |= n_PSO_ENC_MBSTATE;
382 # endif
384 # endif
385 #endif /* HAVE_C90AMEND1 */
386 NYD2_LEAVE;
389 FL size_t
390 n_screensize(void){
391 char const *cp;
392 uiz_t rv;
393 NYD2_ENTER;
395 if((cp = ok_vlook(screen)) != NULL){
396 n_idec_uiz_cp(&rv, cp, 0, NULL);
397 if(rv == 0)
398 rv = n_scrnheight;
399 }else
400 rv = n_scrnheight;
402 if(rv > 2)
403 rv -= 2;
404 NYD2_LEAVE;
405 return rv;
408 FL char const *
409 n_pager_get(char const **env_addon){
410 char const *rv;
411 NYD_ENTER;
413 rv = ok_vlook(PAGER);
415 if(env_addon != NULL){
416 *env_addon = NULL;
417 /* Update the manual upon any changes:
418 * *colour-pager*, $PAGER */
419 if(strstr(rv, "less") != NULL){
420 if(getenv("LESS") == NULL)
421 *env_addon = "LESS=RXi";
422 }else if(strstr(rv, "lv") != NULL){
423 if(getenv("LV") == NULL)
424 *env_addon = "LV=-c";
427 NYD_LEAVE;
428 return rv;
431 FL void
432 page_or_print(FILE *fp, size_t lines)
434 int c;
435 char const *cp;
436 NYD_ENTER;
438 fflush_rewind(fp);
440 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
441 size_t rows;
443 if(*cp == '\0')
444 rows = (size_t)n_scrnheight;
445 else
446 n_idec_uiz_cp(&rows, cp, 0, NULL);
448 if (rows > 0 && lines == 0) {
449 while ((c = getc(fp)) != EOF)
450 if (c == '\n' && ++lines >= rows)
451 break;
452 really_rewind(fp);
455 if (lines >= rows) {
456 char const *env_add[2], *pager;
458 pager = n_pager_get(&env_add[0]);
459 env_add[1] = NULL;
460 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
461 env_add, NULL);
462 goto jleave;
466 while ((c = getc(fp)) != EOF)
467 putc(c, n_stdout);
468 jleave:
469 NYD_LEAVE;
472 FL enum protocol
473 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
474 char const **adjusted_or_null)
476 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
477 char const *cp, *orig_name;
478 enum protocol rv = PROTO_UNKNOWN;
479 NYD_ENTER;
481 if(name[0] == '%' && name[1] == ':')
482 name += 2;
483 orig_name = name;
485 for (cp = name; *cp && *cp != ':'; cp++)
486 if (!alnumchar(*cp))
487 goto jfile;
489 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
490 if(!strncmp(name, "file", sizeof("file") -1) ||
491 !strncmp(name, "mbox", sizeof("mbox") -1))
492 rv = PROTO_FILE;
493 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
494 rv = PROTO_MAILDIR;
495 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
496 #ifdef HAVE_POP3
497 rv = PROTO_POP3;
498 #else
499 n_err(_("No POP3 support compiled in\n"));
500 #endif
501 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
502 #if defined HAVE_POP3 && defined HAVE_SSL
503 rv = PROTO_POP3;
504 #else
505 n_err(_("No POP3S support compiled in\n"));
506 #endif
508 else if(!strncmp(name, "imap", sizeof("imap") -1)){
509 #ifdef HAVE_IMAP
510 rv = PROTO_IMAP;
511 #else
512 n_err(_("No IMAP support compiled in\n"));
513 #endif
514 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
515 #if defined HAVE_IMAP && defined HAVE_SSL
516 rv = PROTO_IMAP;
517 #else
518 n_err(_("No IMAPS support compiled in\n"));
519 #endif
521 orig_name = &cp[3];
522 goto jleave;
525 jfile:
526 rv = PROTO_FILE;
528 if(check_stat || try_hooks){
529 struct n_file_type ft;
530 struct stat stb;
531 char *np;
532 size_t sz;
534 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
535 memcpy(np, name, sz + 1);
537 if(!stat(name, &stb)){
538 if(S_ISDIR(stb.st_mode) &&
539 (memcpy(&np[sz], "/tmp", 5),
540 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
541 (memcpy(&np[sz], "/new", 5),
542 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
543 (memcpy(&np[sz], "/cur", 5),
544 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
545 rv = PROTO_MAILDIR;
546 }else if(try_hooks && n_filetype_trial(&ft, name))
547 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
548 else if((cp = ok_vlook(newfolders)) != NULL &&
549 !asccasecmp(cp, "maildir"))
550 rv = PROTO_MAILDIR;
552 n_lofi_free(np);
554 jleave:
555 if(adjusted_or_null != NULL)
556 *adjusted_or_null = orig_name;
557 NYD_LEAVE;
558 return rv;
561 FL char *
562 n_c_to_hex_base16(char store[3], char c){
563 static char const itoa16[] = "0123456789ABCDEF";
564 NYD2_ENTER;
566 store[2] = '\0';
567 store[1] = itoa16[(ui8_t)c & 0x0F];
568 c = ((ui8_t)c >> 4) & 0x0F;
569 store[0] = itoa16[(ui8_t)c];
570 NYD2_LEAVE;
571 return store;
574 FL si32_t
575 n_c_from_hex_base16(char const hex[2]){
576 static ui8_t const atoi16[] = {
577 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
578 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
579 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
582 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
583 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
585 ui8_t i1, i2;
586 si32_t rv;
587 NYD2_ENTER;
589 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
590 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
591 goto jerr;
592 i1 = atoi16[i1];
593 i2 = atoi16[i2];
594 if ((i1 | i2) & 0xF0u)
595 goto jerr;
596 rv = i1;
597 rv <<= 4;
598 rv += i2;
599 jleave:
600 NYD2_LEAVE;
601 return rv;
602 jerr:
603 rv = -1;
604 goto jleave;
607 FL enum n_idec_state
608 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
609 enum n_idec_mode idm, char const **endptr_or_null){
610 ui8_t currc;
611 ui64_t res, cut;
612 enum n_idec_state rv;
613 NYD_ENTER;
615 idm &= n__IDEC_MODE_MASK;
616 rv = n_IDEC_STATE_NONE | idm;
617 res = 0;
619 if(clen == UIZ_MAX){
620 if(*cbuf == '\0')
621 goto jeinval;
622 }else if(clen == 0)
623 goto jeinval;
625 assert(base != 1 && base <= 36);
626 /*if(base == 1 || base > 36)
627 * goto jeinval;*/
629 /* Leading WS */
630 while(spacechar(*cbuf))
631 if(*++cbuf == '\0' || --clen == 0)
632 goto jeinval;
634 /* Check sign */
635 switch(*cbuf){
636 case '-':
637 rv |= n_IDEC_STATE_SEEN_MINUS;
638 /* FALLTHROUGH */
639 case '+':
640 if(*++cbuf == '\0' || --clen == 0)
641 goto jeinval;
642 break;
645 /* Base detection/skip */
646 if(*cbuf != '0'){
647 if(base == 0){
648 base = 10;
650 /* Support BASE#number prefix, where BASE is decimal 2-36 */
651 if(clen > 2){
652 char c1, c2, c3;
654 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
655 (((c2 = cbuf[1]) == '#') ||
656 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
657 base = a_aux_idec_atoi[(ui8_t)c1];
658 if(c2 == '#')
659 c3 = cbuf[2];
660 else{
661 c3 = cbuf[3];
662 base *= 10; /* xxx Inline atoi decimal base */
663 base += a_aux_idec_atoi[(ui8_t)c2];
666 /* We do not interpret this as BASE#number at all if either we
667 * did not get a valid base or if the first char is not valid
668 * according to base, to comply to the latest interpretion of
669 * "prefix", see comment for standard prefixes below */
670 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
671 base = 10;
672 else if(c2 == '#')
673 clen -= 2, cbuf += 2;
674 else
675 clen -= 3, cbuf += 3;
680 /* Character must be valid for base */
681 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
682 if(currc >= base)
683 goto jeinval;
684 }else{
685 /* 0 always valid as is, fallback base 10 */
686 if(*++cbuf == '\0' || --clen == 0)
687 goto jleave;
689 /* Base "detection" */
690 if(base == 0 || base == 2 || base == 16){
691 switch(*cbuf){
692 case 'x':
693 case 'X':
694 if((base & 2) == 0){
695 base = 0x10;
696 goto jprefix_skip;
698 break;
699 case 'b':
700 case 'B':
701 if((base & 16) == 0){
702 base = 2; /* 0b10 */
703 /* Char after prefix must be valid. However, after some error
704 * in the tor software all libraries (which had to) turned to
705 * an interpretation of the C standard which says that the
706 * prefix may optionally precede an otherwise valid sequence,
707 * which means that "0x" is not a STATE_INVAL error but gives
708 * a "0" result with a "STATE_BASE" error and a rest of "x" */
709 jprefix_skip:
710 #if 1
711 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
712 --clen, ++cbuf;
713 #else
714 if(*++cbuf == '\0' || --clen == 0)
715 goto jeinval;
717 /* Character must be valid for base, invalid otherwise */
718 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
719 if(currc >= base)
720 goto jeinval;
721 #endif
723 break;
724 default:
725 if(base == 0)
726 base = 010;
727 break;
731 /* Character must be valid for base, _EBASE otherwise */
732 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
733 if(currc >= base)
734 goto jebase;
737 for(cut = a_aux_idec_cutlimit[base - 2];;){
738 if(res >= cut){
739 if(res == cut){
740 res *= base;
741 if(res > UI64_MAX - currc)
742 goto jeover;
743 res += currc;
744 }else
745 goto jeover;
746 }else{
747 res *= base;
748 res += currc;
751 if(*++cbuf == '\0' || --clen == 0)
752 break;
754 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
755 if(currc >= base)
756 goto jebase;
759 jleave:
761 ui64_t uimask;
763 switch(rv & n__IDEC_MODE_LIMIT_MASK){
764 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
765 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
766 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
767 default: uimask = UI64_MAX; break;
769 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
770 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
771 uimask >>= 1;
773 if(res & ~uimask){
774 /* XXX never entered unless _SIGNED_TYPE! */
775 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
776 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
777 if(res > uimask + 1){
778 res = uimask << 1;
779 res &= ~uimask;
780 }else{
781 res = -res;
782 break;
784 }else
785 res = uimask;
786 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
787 rv |= n_IDEC_STATE_EOVERFLOW;
788 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
789 res = -res;
790 }while(0);
792 switch(rv & n__IDEC_MODE_LIMIT_MASK){
793 case n_IDEC_MODE_LIMIT_8BIT:
794 if(rv & n_IDEC_MODE_SIGNED_TYPE)
795 *(si8_t*)resp = (si8_t)res;
796 else
797 *(ui8_t*)resp = (ui8_t)res;
798 break;
799 case n_IDEC_MODE_LIMIT_16BIT:
800 if(rv & n_IDEC_MODE_SIGNED_TYPE)
801 *(si16_t*)resp = (si16_t)res;
802 else
803 *(ui16_t*)resp = (ui16_t)res;
804 break;
805 case n_IDEC_MODE_LIMIT_32BIT:
806 if(rv & n_IDEC_MODE_SIGNED_TYPE)
807 *(si32_t*)resp = (si32_t)res;
808 else
809 *(ui32_t*)resp = (ui32_t)res;
810 break;
811 default:
812 if(rv & n_IDEC_MODE_SIGNED_TYPE)
813 *(si64_t*)resp = (si64_t)res;
814 else
815 *(ui64_t*)resp = (ui64_t)res;
816 break;
819 if(endptr_or_null != NULL)
820 *endptr_or_null = cbuf;
821 if(*cbuf == '\0' || clen == 0)
822 rv |= n_IDEC_STATE_CONSUMED;
823 NYD_LEAVE;
824 return rv;
826 jeinval:
827 rv |= n_IDEC_STATE_EINVAL;
828 goto j_maxval;
829 jebase:
830 /* Not a base error for terminator and whitespace! */
831 if(*cbuf != '\0' && !spacechar(*cbuf))
832 rv |= n_IDEC_STATE_EBASE;
833 goto jleave;
835 jeover:
836 /* Overflow error: consume input until bad character or length out */
837 for(;;){
838 if(*++cbuf == '\0' || --clen == 0)
839 break;
840 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
841 if(currc >= base)
842 break;
845 rv |= n_IDEC_STATE_EOVERFLOW;
846 j_maxval:
847 if(rv & n_IDEC_MODE_SIGNED_TYPE)
848 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
849 : (ui64_t)SI64_MAX;
850 else
851 res = UI64_MAX;
852 rv &= ~n_IDEC_STATE_SEEN_MINUS;
853 goto jleave;
856 FL char *
857 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
858 enum n_ienc_mode iem){
859 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
861 ui8_t shiftmodu;
862 char const *itoa;
863 char *rv;
864 NYD_ENTER;
866 iem &= n__IENC_MODE_MASK;
868 assert(base != 1 && base <= 36);
869 /*if(base == 1 || base > 36){
870 * rv = NULL;
871 * goto jleave;
872 *}*/
874 rv = &cbuf[n_IENC_BUFFER_SIZE];
875 *--rv = '\0';
876 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
877 : a_aux_ienc_itoa_upper;
879 if((si64_t)value < 0){
880 iem |= a_ISNEG;
881 if(iem & n_IENC_MODE_SIGNED_TYPE){
882 /* self->is_negative = TRU1; */
883 value = -value;
887 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
888 --base; /* convert to mask */
890 *--rv = itoa[value & base];
891 value >>= shiftmodu;
892 }while(value != 0);
894 if(!(iem & n_IENC_MODE_NO_PREFIX)){
895 /* self->before_prefix = cp; */
896 if(shiftmodu == 4)
897 *--rv = 'x';
898 else if(shiftmodu == 1)
899 *--rv = 'b';
900 else if(shiftmodu != 3){
901 ++base; /* Reconvert from mask */
902 goto jnumber_sign_prefix;
904 *--rv = '0';
906 }else{
908 shiftmodu = value % base;
909 value /= base;
910 *--rv = itoa[shiftmodu];
911 }while(value != 0);
913 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
914 jnumber_sign_prefix:
915 value = base;
916 base = 10;
917 *--rv = '#';
919 shiftmodu = value % base;
920 value /= base;
921 *--rv = itoa[shiftmodu];
922 }while(value != 0);
925 if(iem & n_IENC_MODE_SIGNED_TYPE){
926 char c;
928 if(iem & a_ISNEG)
929 c = '-';
930 else if(iem & n_IENC_MODE_SIGNED_PLUS)
931 c = '+';
932 else if(iem & n_IENC_MODE_SIGNED_SPACE)
933 c = ' ';
934 else
935 c = '\0';
937 if(c != '\0')
938 *--rv = c;
941 NYD_LEAVE;
942 return rv;
945 FL ui32_t
946 n_torek_hash(char const *name){
947 /* Chris Torek's hash */
948 char c;
949 ui32_t h;
950 NYD2_ENTER;
952 for(h = 0; (c = *name++) != '\0';)
953 h = (h * 33) + c;
954 NYD2_LEAVE;
955 return h;
958 FL ui32_t
959 n_torek_ihashn(char const *dat, size_t len){
960 /* See n_torek_hash() */
961 char c;
962 ui32_t h;
963 NYD2_ENTER;
965 if(len == UIZ_MAX)
966 for(h = 0; (c = *dat++) != '\0';)
967 h = (h * 33) + lowerconv(c);
968 else
969 for(h = 0; len > 0; --len){
970 c = *dat++;
971 h = (h * 33) + lowerconv(c);
973 NYD2_LEAVE;
974 return h;
977 FL ui32_t
978 n_prime_next(ui32_t n){
979 static ui32_t const primes[] = {
980 5, 11, 23, 47, 97, 157, 283,
981 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
982 131071, 262139, 524287, 1048573, 2097143, 4194301,
983 8388593, 16777213, 33554393, 67108859, 134217689,
984 268435399, 536870909, 1073741789, 2147483647
986 ui32_t i, mprime;
987 NYD2_ENTER;
989 i = (n < primes[n_NELEM(primes) / 4] ? 0
990 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
991 : n_NELEM(primes) / 2));
993 do if((mprime = primes[i]) > n)
994 break;
995 while(++i < n_NELEM(primes));
997 if(i == n_NELEM(primes) && mprime < n)
998 mprime = n;
999 NYD2_LEAVE;
1000 return mprime;
1003 FL char const *
1004 n_getdeadletter(void){
1005 char const *cp;
1006 bool_t bla;
1007 NYD_ENTER;
1009 bla = FAL0;
1010 jredo:
1011 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1012 if(cp == NULL || strlen(cp) >= PATH_MAX){
1013 if(!bla){
1014 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1015 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1016 ok_vclear(DEAD);
1017 bla = TRU1;
1018 goto jredo;
1019 }else{
1020 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1021 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1024 NYD_LEAVE;
1025 return cp;
1028 FL char *
1029 n_nodename(bool_t mayoverride){
1030 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1032 struct utsname ut;
1033 char *hn;
1034 #ifdef HAVE_SOCKETS
1035 # ifdef HAVE_GETADDRINFO
1036 struct addrinfo hints, *res;
1037 # else
1038 struct hostent *hent;
1039 # endif
1040 #endif
1041 NYD2_ENTER;
1043 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1045 }else if((hn = sys_hostname) == NULL){
1046 bool_t lofi;
1048 lofi = FAL0;
1049 uname(&ut);
1050 hn = ut.nodename;
1052 #ifdef HAVE_SOCKETS
1053 # ifdef HAVE_GETADDRINFO
1054 memset(&hints, 0, sizeof hints);
1055 hints.ai_family = AF_UNSPEC;
1056 hints.ai_flags = AI_CANONNAME;
1057 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1058 if(res->ai_canonname != NULL){
1059 size_t l;
1061 l = strlen(res->ai_canonname) +1;
1062 hn = n_lofi_alloc(l);
1063 lofi = TRU1;
1064 memcpy(hn, res->ai_canonname, l);
1066 freeaddrinfo(res);
1068 # else
1069 hent = gethostbyname(hn);
1070 if(hent != NULL)
1071 hn = hent->h_name;
1072 # endif
1073 #endif /* HAVE_SOCKETS */
1075 #ifdef HAVE_IDNA
1076 /* C99 */{
1077 struct n_string cnv;
1079 n_string_creat(&cnv);
1080 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1081 n_panic(_("The system hostname is invalid, "
1082 "IDNA conversion failed: %s\n"),
1083 n_shexp_quote_cp(hn, FAL0));
1084 sys_hostname = n_string_cp(&cnv);
1085 n_string_drop_ownership(&cnv);
1086 /*n_string_gut(&cnv);*/
1088 #else
1089 sys_hostname = sstrdup(hn);
1090 #endif
1092 if(lofi)
1093 n_lofi_free(hn);
1094 hn = sys_hostname;
1097 if(hostname != NULL && hostname != sys_hostname)
1098 n_free(hostname);
1099 hostname = sstrdup(hn);
1100 NYD2_LEAVE;
1101 return hostname;
1104 #ifdef HAVE_IDNA
1105 FL bool_t
1106 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1107 char *idna_utf8;
1108 bool_t lofi, rv;
1109 NYD_ENTER;
1111 if(ilen == UIZ_MAX)
1112 ilen = strlen(ibuf);
1114 lofi = FAL0;
1116 if((rv = (ilen == 0)))
1117 goto jleave;
1118 if(ibuf[ilen] != '\0'){
1119 lofi = TRU1;
1120 idna_utf8 = n_lofi_alloc(ilen +1);
1121 memcpy(idna_utf8, ibuf, ilen);
1122 idna_utf8[ilen] = '\0';
1123 ibuf = idna_utf8;
1125 ilen = 0;
1127 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1128 if(n_psonce & n_PSO_UNICODE)
1129 # endif
1130 idna_utf8 = n_UNCONST(ibuf);
1131 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1132 else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
1133 ok_vlook(ttycharset), ibuf)) == NULL)
1134 goto jleave;
1135 # endif
1137 # if HAVE_IDNA == HAVE_IDNA_LIBIDN2
1138 /* C99 */{
1139 char *idna_ascii;
1140 int f, rc;
1142 f = IDN2_NONTRANSITIONAL;
1143 jidn2_redo:
1144 if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
1145 out = n_string_assign_cp(out, idna_ascii);
1146 idn2_free(idna_ascii);
1147 rv = TRU1;
1148 ilen = out->s_len;
1149 }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
1150 f = IDN2_TRANSITIONAL;
1151 goto jidn2_redo;
1155 # elif HAVE_IDNA == HAVE_IDNA_LIBIDNA
1156 /* C99 */{
1157 char *idna_ascii;
1159 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1160 out = n_string_assign_cp(out, idna_ascii);
1161 idn_free(idna_ascii);
1162 rv = TRU1;
1163 ilen = out->s_len;
1167 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1168 ilen = strlen(idna_utf8);
1169 jredo:
1170 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
1171 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1172 case idn_buffer_overflow:
1173 ilen += HOST_NAME_MAX +1;
1174 goto jredo;
1175 case idn_success:
1176 rv = TRU1;
1177 ilen = strlen(out->s_dat);
1178 break;
1179 default:
1180 ilen = 0;
1181 break;
1184 # else
1185 # error Unknown HAVE_IDNA
1186 # endif
1187 jleave:
1188 if(lofi)
1189 n_lofi_free(n_UNCONST(ibuf));
1190 out = n_string_trunc(out, ilen);
1191 NYD_LEAVE;
1192 return rv;
1194 #endif /* HAVE_IDNA */
1196 FL char *
1197 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1198 struct str b64;
1199 char *indat, *cp, *oudat;
1200 size_t i, inlen, oulen;
1201 NYD_ENTER;
1203 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1204 n_psonce |= n_PSO_RANDOM_INIT;
1206 if(n_poption & n_PO_D_V){
1207 char const *prngn;
1209 #if n_RANDOM_USE_XSSL
1210 prngn = "*SSL RAND_*";
1211 #elif defined HAVE_POSIX_RANDOM
1212 prngn = "POSIX/arc4random";
1213 #else
1214 prngn = "builtin ARC4";
1215 #endif
1216 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1219 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1220 a_aux_rand_init();
1221 #endif
1224 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1225 * with PAD stripped is still longer than what the user requests, easy way.
1226 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1227 * include the base64 PAD characters in our random string: give some pad */
1228 i = len;
1229 if((inlen = i % 3) != 0)
1230 i += 3 - inlen;
1231 jinc1:
1232 inlen = i >> 2;
1233 oulen = inlen << 2;
1234 if(oulen < len){
1235 i += 3;
1236 goto jinc1;
1238 inlen = inlen + (inlen << 1);
1240 indat = n_lofi_alloc(inlen +1);
1242 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1243 #if n_RANDOM_USE_XSSL
1244 ssl_rand_bytes(indat, inlen);
1245 #elif !defined HAVE_POSIX_RANDOM
1246 for(i = inlen; i-- > 0;)
1247 indat[i] = (char)a_aux_rand_get8();
1248 #else
1249 for(cp = indat, i = inlen; i > 0;){
1250 union {ui32_t i4; char c[4];} r;
1251 size_t j;
1253 r.i4 = (ui32_t)arc4random();
1254 switch((j = i & 3)){
1255 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1256 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1257 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1258 default: cp[0] = r.c[0]; break;
1260 cp += j;
1261 i -= j;
1263 #endif
1264 }else{
1265 for(cp = indat, i = inlen; i > 0;){
1266 union {ui32_t i4; char c[4];} r;
1267 size_t j;
1269 r.i4 = ++*reprocnt_or_null;
1270 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1271 char x;
1273 x = r.c[0];
1274 r.c[0] = r.c[3];
1275 r.c[3] = x;
1276 x = r.c[1];
1277 r.c[1] = r.c[2];
1278 r.c[2] = x;
1280 switch((j = i & 3)){
1281 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1282 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1283 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1284 default: cp[0] = r.c[0]; break;
1286 cp += j;
1287 i -= j;
1291 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1292 b64.s = oudat;
1293 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1294 assert(b64.l >= len);
1295 memcpy(dat, b64.s, len);
1296 dat[len] = '\0';
1297 if(oudat != dat)
1298 n_lofi_free(oudat);
1300 n_lofi_free(indat);
1302 NYD_LEAVE;
1303 return dat;
1306 FL char *
1307 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1308 char *dat;
1309 NYD_ENTER;
1311 dat = n_autorec_alloc(len +1);
1312 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1313 NYD_LEAVE;
1314 return dat;
1317 FL si8_t
1318 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1320 si8_t rv;
1321 NYD_ENTER;
1323 assert(inlen == 0 || inbuf != NULL);
1325 if (inlen == UIZ_MAX)
1326 inlen = strlen(inbuf);
1328 if (inlen == 0)
1329 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
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 = 1;
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 = 0;
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 = -1;
1349 else
1350 rv = (ib != 0);
1353 NYD_LEAVE;
1354 return rv;
1357 FL si8_t
1358 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1360 si8_t rv;
1361 NYD_ENTER;
1363 assert(inlen == 0 || inbuf != NULL);
1365 if (inlen == UIZ_MAX)
1366 inlen = strlen(inbuf);
1368 if (inlen == 0)
1369 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1370 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1371 !ascncasecmp(inbuf, "ask-", 4) &&
1372 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1373 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1374 rv = getapproval(prompt, rv);
1375 NYD_LEAVE;
1376 return rv;
1379 FL bool_t
1380 n_is_all_or_aster(char const *name){
1381 bool_t rv;
1382 NYD_ENTER;
1384 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1385 NYD_LEAVE;
1386 return rv;
1389 FL struct n_timespec const *
1390 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1391 static struct n_timespec ts_now;
1392 NYD2_ENTER;
1394 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1395 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1396 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1397 ts_now.ts_nsec = 0;
1398 }else if(force_update || ts_now.ts_sec == 0){
1399 #ifdef HAVE_CLOCK_GETTIME
1400 struct timespec ts;
1402 clock_gettime(CLOCK_REALTIME, &ts);
1403 ts_now.ts_sec = (si64_t)ts.tv_sec;
1404 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1405 #elif defined HAVE_GETTIMEOFDAY
1406 struct timeval tv;
1408 gettimeofday(&tv, NULL);
1409 ts_now.ts_sec = (si64_t)tv.tv_sec;
1410 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1411 #else
1412 ts_now.ts_sec = (si64_t)time(NULL);
1413 ts_now.ts_nsec = 0;
1414 #endif
1417 /* Just in case.. */
1418 if(n_UNLIKELY(ts_now.ts_sec < 0))
1419 ts_now.ts_sec = 0;
1420 NYD2_LEAVE;
1421 return &ts_now;
1424 FL void
1425 time_current_update(struct time_current *tc, bool_t full_update){
1426 NYD_ENTER;
1427 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1429 if(full_update){
1430 char *cp;
1431 struct tm *tmp;
1432 time_t t;
1434 t = tc->tc_time;
1435 jredo:
1436 if((tmp = gmtime(&t)) == NULL){
1437 t = 0;
1438 goto jredo;
1440 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1441 if((tmp = localtime(&t)) == NULL){
1442 t = 0;
1443 goto jredo;
1445 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1446 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1447 *cp++ = '\n';
1448 *cp = '\0';
1449 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1451 NYD_LEAVE;
1454 FL char *
1455 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1456 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1457 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1458 * ISO C requires us to use the above format string,
1459 * even if it will not fit in the buffer. Thus asctime_r
1460 * is _supposed_ to crash if the fields in tm are too large.
1461 * We follow this behavior and crash "gracefully" to warn
1462 * application developers that they may not be so lucky
1463 * on other implementations (e.g. stack smashing..).
1464 * So we need to do it on our own or the libc may kill us */
1465 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1467 si32_t y, md, th, tm, ts;
1468 char const *wdn, *mn;
1469 struct tm const *tmp;
1470 NYD_ENTER;
1472 if((tmp = localtime_or_nil) == NULL){
1473 time_t t;
1475 t = (time_t)secsepoch;
1476 jredo:
1477 if((tmp = localtime(&t)) == NULL){
1478 /* TODO error log */
1479 t = 0;
1480 goto jredo;
1484 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1485 y = 1970;
1486 wdn = n_weekday_names[4];
1487 mn = n_month_names[0];
1488 md = 1;
1489 th = tm = ts = 0;
1490 }else{
1491 y += 1900;
1492 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1493 ? n_weekday_names[tmp->tm_wday] : n_qm;
1494 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1495 ? n_month_names[tmp->tm_mon] : n_qm;
1497 if((md = tmp->tm_mday) < 1 || md > 31)
1498 md = 1;
1500 if((th = tmp->tm_hour) < 0 || th > 23)
1501 th = 0;
1502 if((tm = tmp->tm_min) < 0 || tm > 59)
1503 tm = 0;
1504 if((ts = tmp->tm_sec) < 0 || ts > 60)
1505 ts = 0;
1508 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1509 wdn, mn, md, th, tm, ts, y);
1510 NYD_LEAVE;
1511 return buf;
1514 FL uiz_t
1515 n_msleep(uiz_t millis, bool_t ignint){
1516 uiz_t rv;
1517 NYD2_ENTER;
1519 #ifdef HAVE_NANOSLEEP
1520 /* C99 */{
1521 struct timespec ts, trem;
1522 int i;
1524 ts.tv_sec = millis / 1000;
1525 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1527 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1528 ts = trem;
1529 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1532 #elif defined HAVE_SLEEP
1533 if((millis /= 1000) == 0)
1534 millis = 1;
1535 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1536 millis = rv;
1537 #else
1538 # error Configuration should have detected a function for sleeping.
1539 #endif
1541 NYD2_LEAVE;
1542 return rv;
1545 FL void
1546 n_err(char const *format, ...){
1547 va_list ap;
1548 NYD2_ENTER;
1550 va_start(ap, format);
1551 #ifdef HAVE_ERRORS
1552 if(n_psonce & n_PSO_INTERACTIVE)
1553 n_verr(format, ap);
1554 else
1555 #endif
1557 size_t len;
1558 bool_t doname;
1560 doname = FAL0;
1562 while(*format == '\n'){
1563 doname = TRU1;
1564 putc('\n', n_stderr);
1565 ++format;
1568 if(doname)
1569 a_aux_err_linelen = 0;
1571 if((len = strlen(format)) > 0){
1572 if(doname || a_aux_err_linelen == 0){
1573 char const *cp;
1575 if(*(cp = ok_vlook(log_prefix)) != '\0')
1576 fputs(cp, n_stderr);
1578 vfprintf(n_stderr, format, ap);
1580 /* C99 */{
1581 size_t i = len;
1583 if(format[--len] == '\n'){
1584 a_aux_err_linelen = (i -= ++len);
1585 break;
1587 ++a_aux_err_linelen;
1588 }while(len > 0);
1592 fflush(n_stderr);
1594 va_end(ap);
1595 NYD2_LEAVE;
1598 FL void
1599 n_verr(char const *format, va_list ap){
1600 #ifdef HAVE_ERRORS
1601 struct a_aux_err_node *enp;
1602 #endif
1603 bool_t doname;
1604 size_t len;
1605 NYD2_ENTER;
1607 doname = FAL0;
1609 while(*format == '\n'){
1610 putc('\n', n_stderr);
1611 doname = TRU1;
1612 ++format;
1615 if(doname){
1616 a_aux_err_linelen = 0;
1617 #ifdef HAVE_ERRORS
1618 if(n_psonce & n_PSO_INTERACTIVE){
1619 if((enp = a_aux_err_tail) != NULL &&
1620 (enp->ae_str.s_len > 0 &&
1621 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1622 n_string_push_c(&enp->ae_str, '\n');
1624 #endif
1627 if((len = strlen(format)) == 0)
1628 goto jleave;
1629 #ifdef HAVE_ERRORS
1630 n_pstate |= n_PS_ERRORS_PROMPT;
1631 #endif
1633 if(doname || a_aux_err_linelen == 0){
1634 char const *cp;
1636 if(*(cp = ok_vlook(log_prefix)) != '\0')
1637 fputs(cp, n_stderr);
1640 /* C99 */{
1641 size_t i = len;
1643 if(format[--len] == '\n'){
1644 a_aux_err_linelen = (i -= ++len);
1645 break;
1647 ++a_aux_err_linelen;
1648 }while(len > 0);
1651 #ifdef HAVE_ERRORS
1652 if(!(n_psonce & n_PSO_INTERACTIVE))
1653 #endif
1654 vfprintf(n_stderr, format, ap);
1655 #ifdef HAVE_ERRORS
1656 else{
1657 int imax, i;
1658 n_LCTAV(ERRORS_MAX > 3);
1660 /* Link it into the `errors' message ring */
1661 if((enp = a_aux_err_tail) == NULL){
1662 jcreat:
1663 enp = smalloc(sizeof *enp);
1664 enp->ae_next = NULL;
1665 n_string_creat(&enp->ae_str);
1666 if(a_aux_err_tail != NULL)
1667 a_aux_err_tail->ae_next = enp;
1668 else
1669 a_aux_err_head = enp;
1670 a_aux_err_tail = enp;
1671 ++a_aux_err_cnt;
1672 }else if(doname ||
1673 (enp->ae_str.s_len > 0 &&
1674 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1675 if(a_aux_err_cnt < ERRORS_MAX)
1676 goto jcreat;
1678 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1679 a_aux_err_tail->ae_next = enp;
1680 a_aux_err_tail = enp;
1681 enp->ae_next = NULL;
1682 n_string_trunc(&enp->ae_str, 0);
1685 # ifdef HAVE_N_VA_COPY
1686 imax = 64;
1687 # else
1688 imax = n_MIN(LINESIZE, 1024);
1689 # endif
1690 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1691 # ifdef HAVE_N_VA_COPY
1692 va_list vac;
1694 n_va_copy(vac, ap);
1695 # else
1696 # define vac ap
1697 # endif
1699 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1700 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1701 # ifdef HAVE_N_VA_COPY
1702 va_end(vac);
1703 # else
1704 # undef vac
1705 # endif
1706 if(i <= 0)
1707 goto jleave;
1708 if(UICMP(z, i, >=, imax)){
1709 # ifdef HAVE_N_VA_COPY
1710 /* XXX Check overflow for upcoming LEN+++i! */
1711 n_string_trunc(&enp->ae_str, len);
1712 continue;
1713 # else
1714 i = (int)strlen(&enp->ae_str.s_dat[len]);
1715 # endif
1717 break;
1719 n_string_trunc(&enp->ae_str, len + (size_t)i);
1721 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1723 #endif /* HAVE_ERRORS */
1725 jleave:
1726 fflush(n_stderr);
1727 NYD2_LEAVE;
1730 FL void
1731 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1732 va_list ap;
1733 NYD_X;
1735 va_start(ap, format);
1736 vfprintf(n_stderr, format, ap);
1737 va_end(ap);
1738 fflush(n_stderr);
1741 FL void
1742 n_perr(char const *msg, int errval){
1743 int e;
1744 char const *fmt;
1745 NYD2_ENTER;
1747 if(msg == NULL){
1748 fmt = "%s%s\n";
1749 msg = n_empty;
1750 }else
1751 fmt = "%s: %s\n";
1753 e = (errval == 0) ? n_err_no : errval;
1754 n_err(fmt, msg, n_err_to_doc(e));
1755 if(errval == 0)
1756 n_err_no = e;
1757 NYD2_LEAVE;
1760 FL void
1761 n_alert(char const *format, ...){
1762 va_list ap;
1763 NYD2_ENTER;
1765 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1767 va_start(ap, format);
1768 n_verr(format, ap);
1769 va_end(ap);
1771 n_err("\n");
1772 NYD2_LEAVE;
1775 FL void
1776 n_panic(char const *format, ...){
1777 va_list ap;
1778 NYD2_ENTER;
1780 if(a_aux_err_linelen > 0){
1781 putc('\n', n_stderr);
1782 a_aux_err_linelen = 0;
1784 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1786 va_start(ap, format);
1787 vfprintf(n_stderr, format, ap);
1788 va_end(ap);
1790 putc('\n', n_stderr);
1791 fflush(n_stderr);
1792 NYD2_LEAVE;
1793 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1796 #ifdef HAVE_ERRORS
1797 FL int
1798 c_errors(void *v){
1799 char **argv = v;
1800 struct a_aux_err_node *enp;
1801 NYD_ENTER;
1803 if(*argv == NULL)
1804 goto jlist;
1805 if(argv[1] != NULL)
1806 goto jerr;
1807 if(!asccasecmp(*argv, "show"))
1808 goto jlist;
1809 if(!asccasecmp(*argv, "clear"))
1810 goto jclear;
1811 jerr:
1812 fprintf(n_stderr,
1813 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1814 v = NULL;
1815 jleave:
1816 NYD_LEAVE;
1817 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1819 jlist:{
1820 FILE *fp;
1821 size_t i;
1823 if(a_aux_err_head == NULL){
1824 fprintf(n_stderr, _("The error ring is empty\n"));
1825 goto jleave;
1828 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1829 NULL){
1830 fprintf(n_stderr, _("tmpfile"));
1831 v = NULL;
1832 goto jleave;
1835 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1836 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1837 /* We don't know whether last string ended with NL; be simple XXX */
1838 putc('\n', fp);
1840 page_or_print(fp, 0);
1841 Fclose(fp);
1843 /* FALLTHRU */
1845 jclear:
1846 a_aux_err_tail = NULL;
1847 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1848 a_aux_err_linelen = 0;
1849 while((enp = a_aux_err_head) != NULL){
1850 a_aux_err_head = enp->ae_next;
1851 n_string_gut(&enp->ae_str);
1852 free(enp);
1854 goto jleave;
1856 #endif /* HAVE_ERRORS */
1858 FL char const *
1859 n_err_to_doc(si32_t eno){
1860 char const *rv;
1861 struct a_aux_err_map const *aemp;
1862 NYD2_ENTER;
1864 aemp = a_aux_err_map_from_no(eno);
1865 rv = &a_aux_err_docs[aemp->aem_docoff];
1866 NYD2_LEAVE;
1867 return rv;
1870 FL char const *
1871 n_err_to_name(si32_t eno){
1872 char const *rv;
1873 struct a_aux_err_map const *aemp;
1874 NYD2_ENTER;
1876 aemp = a_aux_err_map_from_no(eno);
1877 rv = &a_aux_err_names[aemp->aem_nameoff];
1878 NYD2_LEAVE;
1879 return rv;
1882 FL si32_t
1883 n_err_from_name(char const *name){
1884 struct a_aux_err_map const *aemp;
1885 ui32_t hash, i, j, x;
1886 si32_t rv;
1887 NYD2_ENTER;
1889 hash = n_torek_hash(name);
1891 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1892 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1893 break;
1895 aemp = &a_aux_err_map[x];
1896 if(aemp->aem_hash == hash &&
1897 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1898 rv = aemp->aem_err_no;
1899 goto jleave;
1902 if(++i == a_AUX_ERR_REV_PRIME){
1903 #ifdef a_AUX_ERR_REV_WRAPAROUND
1904 i = 0;
1905 #else
1906 break;
1907 #endif
1911 /* Have not found it. But wait, it could be that the user did, e.g.,
1912 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1913 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1914 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1915 ) == n_IDEC_STATE_CONSUMED){
1916 aemp = a_aux_err_map_from_no(rv);
1917 rv = aemp->aem_err_no;
1918 goto jleave;
1921 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1922 jleave:
1923 NYD2_LEAVE;
1924 return rv;
1927 #ifdef HAVE_REGEX
1928 FL char const *
1929 n_regex_err_to_doc(const regex_t *rep, int e){
1930 char *cp;
1931 size_t i;
1932 NYD2_ENTER;
1934 i = regerror(e, rep, NULL, 0) +1;
1935 cp = salloc(i);
1936 regerror(e, rep, cp, i);
1937 NYD2_LEAVE;
1938 return cp;
1940 #endif
1942 /* s-it-mode */