collect(): ~[FfMmUu]: should default to the "dot" (Andrew Gee)
[s-mailx.git] / auxlily.c
blob16ff6c092a39b2b684029acdf0f0e82d4f20471e
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 HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
60 # include n_RANDOM_GETRANDOM_H
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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[] (if HAVE_DOCSTRINGS) */
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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 # if HAVE_RANDOM == n_RANDOM_IMPL_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 = n_RANDOM_GETRANDOM_FUN(&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 HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
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 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
254 # error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
255 # endif
257 /* As a fallback, a homebrew seed */
258 if(n_poption & n_PO_D_V)
259 n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
260 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
261 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
262 ui32_t t, k;
264 # ifdef HAVE_CLOCK_GETTIME
265 clock_gettime(CLOCK_REALTIME, &ts);
266 t = (ui32_t)ts.tv_nsec;
267 # else
268 gettimeofday(&ts, NULL);
269 t = (ui32_t)ts.tv_usec;
270 # endif
271 if(rnd & 1)
272 t = (t >> 16) | (t << 16);
273 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
274 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
275 if(rnd == 7 || rnd == 17)
276 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
277 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
278 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
279 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
280 if((rnd & 3) == 3)
281 seed ^= n_prime_next(seed);
285 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
286 a_aux_rand_get8();
287 jleave:
288 NYD2_LEAVE;
291 n_INLINE ui8_t
292 a_aux_rand_get8(void){
293 ui8_t si, sj;
295 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
296 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
297 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
298 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
299 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
302 static ui32_t
303 a_aux_rand_weak(ui32_t seed){
304 /* From "Random number generators: good ones are hard to find",
305 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
306 * October 1988, p. 1195.
307 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
308 ui32_t hi;
310 if(seed == 0)
311 seed = 123459876;
312 hi = seed / 127773;
313 seed %= 127773;
314 seed = (seed * 16807) - (hi * 2836);
315 if((si32_t)seed < 0)
316 seed += SI32_MAX;
317 return seed;
319 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_SSL */
321 static struct a_aux_err_map const *
322 a_aux_err_map_from_no(si32_t eno){
323 si32_t ecmp;
324 size_t asz;
325 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
326 struct a_aux_err_map const *aemp;
327 NYD2_ENTER;
329 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
331 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
332 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
333 asz != 0; asz >>= 1){
334 tmp = &adat[asz >> 1];
335 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
336 aemp = &a_aux_err_map[(*tmp)[1]];
337 break;
339 if(ecmp > 0){
340 adat = &tmp[1];
341 --asz;
345 NYD2_LEAVE;
346 return aemp;
349 FL void
350 n_locale_init(void){
351 NYD2_ENTER;
353 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
355 #ifndef HAVE_SETLOCALE
356 n_mb_cur_max = 1;
357 #else
358 setlocale(LC_ALL, n_empty);
359 n_mb_cur_max = MB_CUR_MAX;
360 # ifdef HAVE_NL_LANGINFO
361 /* C99 */{
362 char const *cp;
364 if((cp = nl_langinfo(CODESET)) != NULL)
365 /* (Will log during startup if user set that via -S) */
366 ok_vset(ttycharset, cp);
368 # endif /* HAVE_SETLOCALE */
370 # ifdef HAVE_C90AMEND1
371 if(n_mb_cur_max > 1){
372 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
373 n_psonce |= n_PSO_UNICODE;
374 # else
375 wchar_t wc;
376 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
377 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
378 n_psonce |= n_PSO_UNICODE;
379 /* Reset possibly messed up state; luckily this also gives us an
380 * indication whether the encoding has locking shift state sequences */
381 if(mbtowc(&wc, NULL, n_mb_cur_max))
382 n_psonce |= n_PSO_ENC_MBSTATE;
383 # endif
385 # endif
386 #endif /* HAVE_C90AMEND1 */
387 NYD2_LEAVE;
390 FL size_t
391 n_screensize(void){
392 char const *cp;
393 uiz_t rv;
394 NYD2_ENTER;
396 if((cp = ok_vlook(screen)) != NULL){
397 n_idec_uiz_cp(&rv, cp, 0, NULL);
398 if(rv == 0)
399 rv = n_scrnheight;
400 }else
401 rv = n_scrnheight;
403 if(rv > 2)
404 rv -= 2;
405 NYD2_LEAVE;
406 return rv;
409 FL char const *
410 n_pager_get(char const **env_addon){
411 char const *rv;
412 NYD_ENTER;
414 rv = ok_vlook(PAGER);
416 if(env_addon != NULL){
417 *env_addon = NULL;
418 /* Update the manual upon any changes:
419 * *colour-pager*, $PAGER */
420 if(strstr(rv, "less") != NULL){
421 if(getenv("LESS") == NULL)
422 *env_addon = "LESS=RXi";
423 }else if(strstr(rv, "lv") != NULL){
424 if(getenv("LV") == NULL)
425 *env_addon = "LV=-c";
428 NYD_LEAVE;
429 return rv;
432 FL void
433 page_or_print(FILE *fp, size_t lines)
435 int c;
436 char const *cp;
437 NYD_ENTER;
439 fflush_rewind(fp);
441 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
442 size_t rows;
444 if(*cp == '\0')
445 rows = (size_t)n_scrnheight;
446 else
447 n_idec_uiz_cp(&rows, cp, 0, NULL);
449 if (rows > 0 && lines == 0) {
450 while ((c = getc(fp)) != EOF)
451 if (c == '\n' && ++lines >= rows)
452 break;
453 really_rewind(fp);
456 if (lines >= rows) {
457 char const *env_add[2], *pager;
459 pager = n_pager_get(&env_add[0]);
460 env_add[1] = NULL;
461 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
462 env_add, NULL);
463 goto jleave;
467 while ((c = getc(fp)) != EOF)
468 putc(c, n_stdout);
469 jleave:
470 NYD_LEAVE;
473 FL enum protocol
474 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
475 char const **adjusted_or_null)
477 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
478 char const *cp, *orig_name;
479 enum protocol rv = PROTO_UNKNOWN;
480 NYD_ENTER;
482 if(name[0] == '%' && name[1] == ':')
483 name += 2;
484 orig_name = name;
486 for (cp = name; *cp && *cp != ':'; cp++)
487 if (!alnumchar(*cp))
488 goto jfile;
490 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
491 if(!strncmp(name, "file", sizeof("file") -1) ||
492 !strncmp(name, "mbox", sizeof("mbox") -1))
493 rv = PROTO_FILE;
494 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
495 rv = PROTO_MAILDIR;
496 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
497 #ifdef HAVE_POP3
498 rv = PROTO_POP3;
499 #else
500 n_err(_("No POP3 support compiled in\n"));
501 #endif
502 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
503 #if defined HAVE_POP3 && defined HAVE_SSL
504 rv = PROTO_POP3;
505 #else
506 n_err(_("No POP3S support compiled in\n"));
507 #endif
509 else if(!strncmp(name, "imap", sizeof("imap") -1)){
510 #ifdef HAVE_IMAP
511 rv = PROTO_IMAP;
512 #else
513 n_err(_("No IMAP support compiled in\n"));
514 #endif
515 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
516 #if defined HAVE_IMAP && defined HAVE_SSL
517 rv = PROTO_IMAP;
518 #else
519 n_err(_("No IMAPS support compiled in\n"));
520 #endif
522 orig_name = &cp[3];
523 goto jleave;
526 jfile:
527 rv = PROTO_FILE;
529 if(check_stat || try_hooks){
530 struct n_file_type ft;
531 struct stat stb;
532 char *np;
533 size_t sz;
535 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
536 memcpy(np, name, sz + 1);
538 if(!stat(name, &stb)){
539 if(S_ISDIR(stb.st_mode) &&
540 (memcpy(&np[sz], "/tmp", 5),
541 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
542 (memcpy(&np[sz], "/new", 5),
543 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
544 (memcpy(&np[sz], "/cur", 5),
545 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
546 rv = PROTO_MAILDIR;
547 }else if(try_hooks && n_filetype_trial(&ft, name))
548 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
549 else if((cp = ok_vlook(newfolders)) != NULL &&
550 !asccasecmp(cp, "maildir"))
551 rv = PROTO_MAILDIR;
553 n_lofi_free(np);
555 jleave:
556 if(adjusted_or_null != NULL)
557 *adjusted_or_null = orig_name;
558 NYD_LEAVE;
559 return rv;
562 FL char *
563 n_c_to_hex_base16(char store[3], char c){
564 static char const itoa16[] = "0123456789ABCDEF";
565 NYD2_ENTER;
567 store[2] = '\0';
568 store[1] = itoa16[(ui8_t)c & 0x0F];
569 c = ((ui8_t)c >> 4) & 0x0F;
570 store[0] = itoa16[(ui8_t)c];
571 NYD2_LEAVE;
572 return store;
575 FL si32_t
576 n_c_from_hex_base16(char const hex[2]){
577 static ui8_t const atoi16[] = {
578 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
579 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
580 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
582 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
583 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
584 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
586 ui8_t i1, i2;
587 si32_t rv;
588 NYD2_ENTER;
590 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
591 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
592 goto jerr;
593 i1 = atoi16[i1];
594 i2 = atoi16[i2];
595 if ((i1 | i2) & 0xF0u)
596 goto jerr;
597 rv = i1;
598 rv <<= 4;
599 rv += i2;
600 jleave:
601 NYD2_LEAVE;
602 return rv;
603 jerr:
604 rv = -1;
605 goto jleave;
608 FL enum n_idec_state
609 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
610 enum n_idec_mode idm, char const **endptr_or_null){
611 ui8_t currc;
612 ui64_t res, cut;
613 enum n_idec_state rv;
614 NYD_ENTER;
616 idm &= n__IDEC_MODE_MASK;
617 rv = n_IDEC_STATE_NONE | idm;
618 res = 0;
620 if(clen == UIZ_MAX){
621 if(*cbuf == '\0')
622 goto jeinval;
623 }else if(clen == 0)
624 goto jeinval;
626 assert(base != 1 && base <= 36);
627 /*if(base == 1 || base > 36)
628 * goto jeinval;*/
630 /* Leading WS */
631 while(spacechar(*cbuf))
632 if(*++cbuf == '\0' || --clen == 0)
633 goto jeinval;
635 /* Check sign */
636 switch(*cbuf){
637 case '-':
638 rv |= n_IDEC_STATE_SEEN_MINUS;
639 /* FALLTHROUGH */
640 case '+':
641 if(*++cbuf == '\0' || --clen == 0)
642 goto jeinval;
643 break;
646 /* Base detection/skip */
647 if(*cbuf != '0'){
648 if(base == 0){
649 base = 10;
651 /* Support BASE#number prefix, where BASE is decimal 2-36 */
652 if(clen > 2){
653 char c1, c2, c3;
655 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
656 (((c2 = cbuf[1]) == '#') ||
657 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
658 base = a_aux_idec_atoi[(ui8_t)c1];
659 if(c2 == '#')
660 c3 = cbuf[2];
661 else{
662 c3 = cbuf[3];
663 base *= 10; /* xxx Inline atoi decimal base */
664 base += a_aux_idec_atoi[(ui8_t)c2];
667 /* We do not interpret this as BASE#number at all if either we
668 * did not get a valid base or if the first char is not valid
669 * according to base, to comply to the latest interpretion of
670 * "prefix", see comment for standard prefixes below */
671 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
672 base = 10;
673 else if(c2 == '#')
674 clen -= 2, cbuf += 2;
675 else
676 clen -= 3, cbuf += 3;
681 /* Character must be valid for base */
682 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
683 if(currc >= base)
684 goto jeinval;
685 }else{
686 /* 0 always valid as is, fallback base 10 */
687 if(*++cbuf == '\0' || --clen == 0)
688 goto jleave;
690 /* Base "detection" */
691 if(base == 0 || base == 2 || base == 16){
692 switch(*cbuf){
693 case 'x':
694 case 'X':
695 if((base & 2) == 0){
696 base = 0x10;
697 goto jprefix_skip;
699 break;
700 case 'b':
701 case 'B':
702 if((base & 16) == 0){
703 base = 2; /* 0b10 */
704 /* Char after prefix must be valid. However, after some error
705 * in the tor software all libraries (which had to) turned to
706 * an interpretation of the C standard which says that the
707 * prefix may optionally precede an otherwise valid sequence,
708 * which means that "0x" is not a STATE_INVAL error but gives
709 * a "0" result with a "STATE_BASE" error and a rest of "x" */
710 jprefix_skip:
711 #if 1
712 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
713 --clen, ++cbuf;
714 #else
715 if(*++cbuf == '\0' || --clen == 0)
716 goto jeinval;
718 /* Character must be valid for base, invalid otherwise */
719 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
720 if(currc >= base)
721 goto jeinval;
722 #endif
724 break;
725 default:
726 if(base == 0)
727 base = 010;
728 break;
732 /* Character must be valid for base, _EBASE otherwise */
733 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
734 if(currc >= base)
735 goto jebase;
738 for(cut = a_aux_idec_cutlimit[base - 2];;){
739 if(res >= cut){
740 if(res == cut){
741 res *= base;
742 if(res > UI64_MAX - currc)
743 goto jeover;
744 res += currc;
745 }else
746 goto jeover;
747 }else{
748 res *= base;
749 res += currc;
752 if(*++cbuf == '\0' || --clen == 0)
753 break;
755 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
756 if(currc >= base)
757 goto jebase;
760 jleave:
762 ui64_t uimask;
764 switch(rv & n__IDEC_MODE_LIMIT_MASK){
765 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
766 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
767 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
768 default: uimask = UI64_MAX; break;
770 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
771 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
772 uimask >>= 1;
774 if(res & ~uimask){
775 /* XXX never entered unless _SIGNED_TYPE! */
776 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
777 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
778 if(res > uimask + 1){
779 res = uimask << 1;
780 res &= ~uimask;
781 }else{
782 res = -res;
783 break;
785 }else
786 res = uimask;
787 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
788 rv |= n_IDEC_STATE_EOVERFLOW;
789 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
790 res = -res;
791 }while(0);
793 switch(rv & n__IDEC_MODE_LIMIT_MASK){
794 case n_IDEC_MODE_LIMIT_8BIT:
795 if(rv & n_IDEC_MODE_SIGNED_TYPE)
796 *(si8_t*)resp = (si8_t)res;
797 else
798 *(ui8_t*)resp = (ui8_t)res;
799 break;
800 case n_IDEC_MODE_LIMIT_16BIT:
801 if(rv & n_IDEC_MODE_SIGNED_TYPE)
802 *(si16_t*)resp = (si16_t)res;
803 else
804 *(ui16_t*)resp = (ui16_t)res;
805 break;
806 case n_IDEC_MODE_LIMIT_32BIT:
807 if(rv & n_IDEC_MODE_SIGNED_TYPE)
808 *(si32_t*)resp = (si32_t)res;
809 else
810 *(ui32_t*)resp = (ui32_t)res;
811 break;
812 default:
813 if(rv & n_IDEC_MODE_SIGNED_TYPE)
814 *(si64_t*)resp = (si64_t)res;
815 else
816 *(ui64_t*)resp = (ui64_t)res;
817 break;
820 if(endptr_or_null != NULL)
821 *endptr_or_null = cbuf;
822 if(*cbuf == '\0' || clen == 0)
823 rv |= n_IDEC_STATE_CONSUMED;
824 NYD_LEAVE;
825 return rv;
827 jeinval:
828 rv |= n_IDEC_STATE_EINVAL;
829 goto j_maxval;
830 jebase:
831 /* Not a base error for terminator and whitespace! */
832 if(*cbuf != '\0' && !spacechar(*cbuf))
833 rv |= n_IDEC_STATE_EBASE;
834 goto jleave;
836 jeover:
837 /* Overflow error: consume input until bad character or length out */
838 for(;;){
839 if(*++cbuf == '\0' || --clen == 0)
840 break;
841 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
842 if(currc >= base)
843 break;
846 rv |= n_IDEC_STATE_EOVERFLOW;
847 j_maxval:
848 if(rv & n_IDEC_MODE_SIGNED_TYPE)
849 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
850 : (ui64_t)SI64_MAX;
851 else
852 res = UI64_MAX;
853 rv &= ~n_IDEC_STATE_SEEN_MINUS;
854 goto jleave;
857 FL char *
858 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
859 enum n_ienc_mode iem){
860 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
862 ui8_t shiftmodu;
863 char const *itoa;
864 char *rv;
865 NYD_ENTER;
867 iem &= n__IENC_MODE_MASK;
869 assert(base != 1 && base <= 36);
870 /*if(base == 1 || base > 36){
871 * rv = NULL;
872 * goto jleave;
873 *}*/
875 rv = &cbuf[n_IENC_BUFFER_SIZE];
876 *--rv = '\0';
877 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
878 : a_aux_ienc_itoa_upper;
880 if((si64_t)value < 0){
881 iem |= a_ISNEG;
882 if(iem & n_IENC_MODE_SIGNED_TYPE){
883 /* self->is_negative = TRU1; */
884 value = -value;
888 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
889 --base; /* convert to mask */
891 *--rv = itoa[value & base];
892 value >>= shiftmodu;
893 }while(value != 0);
895 if(!(iem & n_IENC_MODE_NO_PREFIX)){
896 /* self->before_prefix = cp; */
897 if(shiftmodu == 4)
898 *--rv = 'x';
899 else if(shiftmodu == 1)
900 *--rv = 'b';
901 else if(shiftmodu != 3){
902 ++base; /* Reconvert from mask */
903 goto jnumber_sign_prefix;
905 *--rv = '0';
907 }else{
909 shiftmodu = value % base;
910 value /= base;
911 *--rv = itoa[shiftmodu];
912 }while(value != 0);
914 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
915 jnumber_sign_prefix:
916 value = base;
917 base = 10;
918 *--rv = '#';
920 shiftmodu = value % base;
921 value /= base;
922 *--rv = itoa[shiftmodu];
923 }while(value != 0);
926 if(iem & n_IENC_MODE_SIGNED_TYPE){
927 char c;
929 if(iem & a_ISNEG)
930 c = '-';
931 else if(iem & n_IENC_MODE_SIGNED_PLUS)
932 c = '+';
933 else if(iem & n_IENC_MODE_SIGNED_SPACE)
934 c = ' ';
935 else
936 c = '\0';
938 if(c != '\0')
939 *--rv = c;
942 NYD_LEAVE;
943 return rv;
946 FL ui32_t
947 n_torek_hash(char const *name){
948 /* Chris Torek's hash */
949 char c;
950 ui32_t h;
951 NYD2_ENTER;
953 for(h = 0; (c = *name++) != '\0';)
954 h = (h * 33) + c;
955 NYD2_LEAVE;
956 return h;
959 FL ui32_t
960 n_torek_ihashn(char const *dat, size_t len){
961 /* See n_torek_hash() */
962 char c;
963 ui32_t h;
964 NYD2_ENTER;
966 if(len == UIZ_MAX)
967 for(h = 0; (c = *dat++) != '\0';)
968 h = (h * 33) + lowerconv(c);
969 else
970 for(h = 0; len > 0; --len){
971 c = *dat++;
972 h = (h * 33) + lowerconv(c);
974 NYD2_LEAVE;
975 return h;
978 FL ui32_t
979 n_prime_next(ui32_t n){
980 static ui32_t const primes[] = {
981 5, 11, 23, 47, 97, 157, 283,
982 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
983 131071, 262139, 524287, 1048573, 2097143, 4194301,
984 8388593, 16777213, 33554393, 67108859, 134217689,
985 268435399, 536870909, 1073741789, 2147483647
987 ui32_t i, mprime;
988 NYD2_ENTER;
990 i = (n < primes[n_NELEM(primes) / 4] ? 0
991 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
992 : n_NELEM(primes) / 2));
994 do if((mprime = primes[i]) > n)
995 break;
996 while(++i < n_NELEM(primes));
998 if(i == n_NELEM(primes) && mprime < n)
999 mprime = n;
1000 NYD2_LEAVE;
1001 return mprime;
1004 FL char const *
1005 n_getdeadletter(void){
1006 char const *cp;
1007 bool_t bla;
1008 NYD_ENTER;
1010 bla = FAL0;
1011 jredo:
1012 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1013 if(cp == NULL || strlen(cp) >= PATH_MAX){
1014 if(!bla){
1015 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1016 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1017 ok_vclear(DEAD);
1018 bla = TRU1;
1019 goto jredo;
1020 }else{
1021 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1022 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1025 NYD_LEAVE;
1026 return cp;
1029 FL char *
1030 n_nodename(bool_t mayoverride){
1031 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1033 struct utsname ut;
1034 char *hn;
1035 #ifdef HAVE_SOCKETS
1036 # ifdef HAVE_GETADDRINFO
1037 struct addrinfo hints, *res;
1038 # else
1039 struct hostent *hent;
1040 # endif
1041 #endif
1042 NYD2_ENTER;
1044 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1046 }else if((hn = sys_hostname) == NULL){
1047 bool_t lofi;
1049 lofi = FAL0;
1050 uname(&ut);
1051 hn = ut.nodename;
1053 #ifdef HAVE_SOCKETS
1054 # ifdef HAVE_GETADDRINFO
1055 memset(&hints, 0, sizeof hints);
1056 hints.ai_family = AF_UNSPEC;
1057 hints.ai_flags = AI_CANONNAME;
1058 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1059 if(res->ai_canonname != NULL){
1060 size_t l;
1062 l = strlen(res->ai_canonname) +1;
1063 hn = n_lofi_alloc(l);
1064 lofi = TRU1;
1065 memcpy(hn, res->ai_canonname, l);
1067 freeaddrinfo(res);
1069 # else
1070 hent = gethostbyname(hn);
1071 if(hent != NULL)
1072 hn = hent->h_name;
1073 # endif
1074 #endif /* HAVE_SOCKETS */
1076 #ifdef HAVE_IDNA
1077 /* C99 */{
1078 struct n_string cnv;
1080 n_string_creat(&cnv);
1081 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1082 n_panic(_("The system hostname is invalid, "
1083 "IDNA conversion failed: %s\n"),
1084 n_shexp_quote_cp(hn, FAL0));
1085 sys_hostname = n_string_cp(&cnv);
1086 n_string_drop_ownership(&cnv);
1087 /*n_string_gut(&cnv);*/
1089 #else
1090 sys_hostname = sstrdup(hn);
1091 #endif
1093 if(lofi)
1094 n_lofi_free(hn);
1095 hn = sys_hostname;
1098 if(hostname != NULL && hostname != sys_hostname)
1099 n_free(hostname);
1100 hostname = sstrdup(hn);
1101 NYD2_LEAVE;
1102 return hostname;
1105 #ifdef HAVE_IDNA
1106 FL bool_t
1107 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1108 char *idna_utf8;
1109 bool_t lofi, rv;
1110 NYD_ENTER;
1112 if(ilen == UIZ_MAX)
1113 ilen = strlen(ibuf);
1115 lofi = FAL0;
1117 if((rv = (ilen == 0)))
1118 goto jleave;
1119 if(ibuf[ilen] != '\0'){
1120 lofi = TRU1;
1121 idna_utf8 = n_lofi_alloc(ilen +1);
1122 memcpy(idna_utf8, ibuf, ilen);
1123 idna_utf8[ilen] = '\0';
1124 ibuf = idna_utf8;
1126 ilen = 0;
1128 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1129 if(n_psonce & n_PSO_UNICODE)
1130 # endif
1131 idna_utf8 = n_UNCONST(ibuf);
1132 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1133 else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
1134 ok_vlook(ttycharset), ibuf)) == NULL)
1135 goto jleave;
1136 # endif
1138 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1139 /* C99 */{
1140 char *idna_ascii;
1141 int f, rc;
1143 f = IDN2_NONTRANSITIONAL;
1144 jidn2_redo:
1145 if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
1146 out = n_string_assign_cp(out, idna_ascii);
1147 idn2_free(idna_ascii);
1148 rv = TRU1;
1149 ilen = out->s_len;
1150 }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
1151 f = IDN2_TRANSITIONAL;
1152 goto jidn2_redo;
1156 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1157 /* C99 */{
1158 char *idna_ascii;
1160 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1161 out = n_string_assign_cp(out, idna_ascii);
1162 idn_free(idna_ascii);
1163 rv = TRU1;
1164 ilen = out->s_len;
1168 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1169 ilen = strlen(idna_utf8);
1170 jredo:
1171 switch(idn_encodename(
1172 /* LOCALCONV changed meaning in v2 and is no longer available for
1173 * encoding. This makes sense, bu */
1175 # ifdef IDN_UNICODECONV /* v2 */
1176 IDN_ENCODE_APP & ~IDN_UNICODECONV
1177 # else
1178 IDN_DELIMMAP | IDN_LOCALMAP | IDN_NAMEPREP | IDN_IDNCONV |
1179 IDN_LENCHECK | IDN_ASCCHECK
1180 # endif
1181 ), idna_utf8,
1182 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1183 case idn_buffer_overflow:
1184 ilen += HOST_NAME_MAX +1;
1185 goto jredo;
1186 case idn_success:
1187 rv = TRU1;
1188 ilen = strlen(out->s_dat);
1189 break;
1190 default:
1191 ilen = 0;
1192 break;
1195 # else
1196 # error Unknown HAVE_IDNA
1197 # endif
1198 jleave:
1199 if(lofi)
1200 n_lofi_free(n_UNCONST(ibuf));
1201 out = n_string_trunc(out, ilen);
1202 NYD_LEAVE;
1203 return rv;
1205 #endif /* HAVE_IDNA */
1207 FL char *
1208 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1209 struct str b64;
1210 char *indat, *cp, *oudat;
1211 size_t i, inlen, oulen;
1212 NYD_ENTER;
1214 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1215 n_psonce |= n_PSO_RANDOM_INIT;
1217 if(n_poption & n_PO_D_V){
1218 char const *prngn;
1220 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
1221 prngn = "arc4random";
1222 #elif HAVE_RANDOM == n_RANDOM_IMPL_SSL
1223 prngn = "*SSL RAND_*";
1224 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
1225 prngn = "getrandom(2/3) + builtin ARC4";
1226 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
1227 prngn = "/dev/urandom + builtin ARC4";
1228 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
1229 prngn = "builtin ARC4";
1230 #else
1231 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
1232 #endif
1233 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn);
1236 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
1237 a_aux_rand_init();
1238 #endif
1241 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1242 * with PAD stripped is still longer than what the user requests, easy way.
1243 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1244 * include the base64 PAD characters in our random string: give some pad */
1245 i = len;
1246 if((inlen = i % 3) != 0)
1247 i += 3 - inlen;
1248 jinc1:
1249 inlen = i >> 2;
1250 oulen = inlen << 2;
1251 if(oulen < len){
1252 i += 3;
1253 goto jinc1;
1255 inlen = inlen + (inlen << 1);
1257 indat = n_lofi_alloc(inlen +1);
1259 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1260 #if HAVE_RANDOM == n_RANDOM_IMPL_SSL
1261 ssl_rand_bytes(indat, inlen);
1262 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
1263 for(i = inlen; i-- > 0;)
1264 indat[i] = (char)a_aux_rand_get8();
1265 #else
1266 for(cp = indat, i = inlen; i > 0;){
1267 union {ui32_t i4; char c[4];} r;
1268 size_t j;
1270 r.i4 = (ui32_t)arc4random();
1271 switch((j = i & 3)){
1272 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1273 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1274 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1275 default: cp[0] = r.c[0]; break;
1277 cp += j;
1278 i -= j;
1280 #endif
1281 }else{
1282 for(cp = indat, i = inlen; i > 0;){
1283 union {ui32_t i4; char c[4];} r;
1284 size_t j;
1286 r.i4 = ++*reprocnt_or_null;
1287 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1288 char x;
1290 x = r.c[0];
1291 r.c[0] = r.c[3];
1292 r.c[3] = x;
1293 x = r.c[1];
1294 r.c[1] = r.c[2];
1295 r.c[2] = x;
1297 switch((j = i & 3)){
1298 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1299 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1300 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1301 default: cp[0] = r.c[0]; break;
1303 cp += j;
1304 i -= j;
1308 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1309 b64.s = oudat;
1310 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1311 assert(b64.l >= len);
1312 memcpy(dat, b64.s, len);
1313 dat[len] = '\0';
1314 if(oudat != dat)
1315 n_lofi_free(oudat);
1317 n_lofi_free(indat);
1319 NYD_LEAVE;
1320 return dat;
1323 FL char *
1324 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1325 char *dat;
1326 NYD_ENTER;
1328 dat = n_autorec_alloc(len +1);
1329 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1330 NYD_LEAVE;
1331 return dat;
1334 FL bool_t
1335 n_boolify(char const *inbuf, uiz_t inlen, bool_t emptyrv){
1336 bool_t rv;
1337 NYD2_ENTER;
1338 assert(inlen == 0 || inbuf != NULL);
1340 if(inlen == UIZ_MAX)
1341 inlen = strlen(inbuf);
1343 if(inlen == 0)
1344 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1345 else{
1346 if((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1347 !ascncasecmp(inbuf, "true", inlen) ||
1348 !ascncasecmp(inbuf, "yes", inlen) ||
1349 !ascncasecmp(inbuf, "on", inlen))
1350 rv = TRU1;
1351 else if((inlen == 1 &&
1352 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1353 !ascncasecmp(inbuf, "false", inlen) ||
1354 !ascncasecmp(inbuf, "no", inlen) ||
1355 !ascncasecmp(inbuf, "off", inlen))
1356 rv = FAL0;
1357 else{
1358 ui64_t ib;
1360 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1361 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1362 ) != n_IDEC_STATE_CONSUMED)
1363 rv = TRUM1;
1364 else
1365 rv = (ib != 0);
1368 NYD2_LEAVE;
1369 return rv;
1372 FL bool_t
1373 n_quadify(char const *inbuf, uiz_t inlen, char const *prompt, bool_t emptyrv){
1374 bool_t rv;
1375 NYD2_ENTER;
1376 assert(inlen == 0 || inbuf != NULL);
1378 if(inlen == UIZ_MAX)
1379 inlen = strlen(inbuf);
1381 if(inlen == 0)
1382 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1383 else if((rv = n_boolify(inbuf, inlen, emptyrv)) < FAL0 &&
1384 !ascncasecmp(inbuf, "ask-", 4) &&
1385 (rv = n_boolify(&inbuf[4], inlen - 4, emptyrv)) >= FAL0 &&
1386 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1387 rv = getapproval(prompt, rv);
1388 NYD2_LEAVE;
1389 return rv;
1392 FL bool_t
1393 n_is_all_or_aster(char const *name){
1394 bool_t rv;
1395 NYD2_ENTER;
1397 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1398 NYD2_LEAVE;
1399 return rv;
1402 FL struct n_timespec const *
1403 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1404 static struct n_timespec ts_now;
1405 NYD2_ENTER;
1407 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1408 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1409 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1410 ts_now.ts_nsec = 0;
1411 }else if(force_update || ts_now.ts_sec == 0){
1412 #ifdef HAVE_CLOCK_GETTIME
1413 struct timespec ts;
1415 clock_gettime(CLOCK_REALTIME, &ts);
1416 ts_now.ts_sec = (si64_t)ts.tv_sec;
1417 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1418 #elif defined HAVE_GETTIMEOFDAY
1419 struct timeval tv;
1421 gettimeofday(&tv, NULL);
1422 ts_now.ts_sec = (si64_t)tv.tv_sec;
1423 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1424 #else
1425 ts_now.ts_sec = (si64_t)time(NULL);
1426 ts_now.ts_nsec = 0;
1427 #endif
1430 /* Just in case.. */
1431 if(n_UNLIKELY(ts_now.ts_sec < 0))
1432 ts_now.ts_sec = 0;
1433 NYD2_LEAVE;
1434 return &ts_now;
1437 FL void
1438 time_current_update(struct time_current *tc, bool_t full_update){
1439 NYD_ENTER;
1440 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1442 if(full_update){
1443 char *cp;
1444 struct tm *tmp;
1445 time_t t;
1447 t = tc->tc_time;
1448 jredo:
1449 if((tmp = gmtime(&t)) == NULL){
1450 t = 0;
1451 goto jredo;
1453 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1454 if((tmp = localtime(&t)) == NULL){
1455 t = 0;
1456 goto jredo;
1458 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1459 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1460 *cp++ = '\n';
1461 *cp = '\0';
1462 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1464 NYD_LEAVE;
1467 FL char *
1468 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1469 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1470 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1471 * ISO C requires us to use the above format string,
1472 * even if it will not fit in the buffer. Thus asctime_r
1473 * is _supposed_ to crash if the fields in tm are too large.
1474 * We follow this behavior and crash "gracefully" to warn
1475 * application developers that they may not be so lucky
1476 * on other implementations (e.g. stack smashing..).
1477 * So we need to do it on our own or the libc may kill us */
1478 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1480 si32_t y, md, th, tm, ts;
1481 char const *wdn, *mn;
1482 struct tm const *tmp;
1483 NYD_ENTER;
1485 if((tmp = localtime_or_nil) == NULL){
1486 time_t t;
1488 t = (time_t)secsepoch;
1489 jredo:
1490 if((tmp = localtime(&t)) == NULL){
1491 /* TODO error log */
1492 t = 0;
1493 goto jredo;
1497 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1498 y = 1970;
1499 wdn = n_weekday_names[4];
1500 mn = n_month_names[0];
1501 md = 1;
1502 th = tm = ts = 0;
1503 }else{
1504 y += 1900;
1505 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1506 ? n_weekday_names[tmp->tm_wday] : n_qm;
1507 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1508 ? n_month_names[tmp->tm_mon] : n_qm;
1510 if((md = tmp->tm_mday) < 1 || md > 31)
1511 md = 1;
1513 if((th = tmp->tm_hour) < 0 || th > 23)
1514 th = 0;
1515 if((tm = tmp->tm_min) < 0 || tm > 59)
1516 tm = 0;
1517 if((ts = tmp->tm_sec) < 0 || ts > 60)
1518 ts = 0;
1521 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1522 wdn, mn, md, th, tm, ts, y);
1523 NYD_LEAVE;
1524 return buf;
1527 FL uiz_t
1528 n_msleep(uiz_t millis, bool_t ignint){
1529 uiz_t rv;
1530 NYD2_ENTER;
1532 #ifdef HAVE_NANOSLEEP
1533 /* C99 */{
1534 struct timespec ts, trem;
1535 int i;
1537 ts.tv_sec = millis / 1000;
1538 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1540 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1541 ts = trem;
1542 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1545 #elif defined HAVE_SLEEP
1546 if((millis /= 1000) == 0)
1547 millis = 1;
1548 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1549 millis = rv;
1550 #else
1551 # error Configuration should have detected a function for sleeping.
1552 #endif
1554 NYD2_LEAVE;
1555 return rv;
1558 FL void
1559 n_err(char const *format, ...){
1560 va_list ap;
1561 NYD2_ENTER;
1563 va_start(ap, format);
1564 #ifdef HAVE_ERRORS
1565 if(n_psonce & n_PSO_INTERACTIVE)
1566 n_verr(format, ap);
1567 else
1568 #endif
1570 size_t len;
1571 bool_t doname;
1573 doname = FAL0;
1575 while(*format == '\n'){
1576 doname = TRU1;
1577 putc('\n', n_stderr);
1578 ++format;
1581 if(doname)
1582 a_aux_err_linelen = 0;
1584 if((len = strlen(format)) > 0){
1585 if(doname || a_aux_err_linelen == 0){
1586 char const *cp;
1588 if(*(cp = ok_vlook(log_prefix)) != '\0')
1589 fputs(cp, n_stderr);
1591 vfprintf(n_stderr, format, ap);
1593 /* C99 */{
1594 size_t i = len;
1596 if(format[--len] == '\n'){
1597 a_aux_err_linelen = (i -= ++len);
1598 break;
1600 ++a_aux_err_linelen;
1601 }while(len > 0);
1605 fflush(n_stderr);
1607 va_end(ap);
1608 NYD2_LEAVE;
1611 FL void
1612 n_verr(char const *format, va_list ap){
1613 #ifdef HAVE_ERRORS
1614 struct a_aux_err_node *enp;
1615 #endif
1616 bool_t doname;
1617 size_t len;
1618 NYD2_ENTER;
1620 doname = FAL0;
1622 while(*format == '\n'){
1623 putc('\n', n_stderr);
1624 doname = TRU1;
1625 ++format;
1628 if(doname){
1629 a_aux_err_linelen = 0;
1630 #ifdef HAVE_ERRORS
1631 if(n_psonce & n_PSO_INTERACTIVE){
1632 if((enp = a_aux_err_tail) != NULL &&
1633 (enp->ae_str.s_len > 0 &&
1634 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1635 n_string_push_c(&enp->ae_str, '\n');
1637 #endif
1640 if((len = strlen(format)) == 0)
1641 goto jleave;
1642 #ifdef HAVE_ERRORS
1643 n_pstate |= n_PS_ERRORS_PROMPT;
1644 #endif
1646 if(doname || a_aux_err_linelen == 0){
1647 char const *cp;
1649 if(*(cp = ok_vlook(log_prefix)) != '\0')
1650 fputs(cp, n_stderr);
1653 /* C99 */{
1654 size_t i = len;
1656 if(format[--len] == '\n'){
1657 a_aux_err_linelen = (i -= ++len);
1658 break;
1660 ++a_aux_err_linelen;
1661 }while(len > 0);
1664 #ifdef HAVE_ERRORS
1665 if(!(n_psonce & n_PSO_INTERACTIVE))
1666 #endif
1667 vfprintf(n_stderr, format, ap);
1668 #ifdef HAVE_ERRORS
1669 else{
1670 int imax, i;
1671 n_LCTAV(ERRORS_MAX > 3);
1673 /* Link it into the `errors' message ring */
1674 if((enp = a_aux_err_tail) == NULL){
1675 jcreat:
1676 enp = smalloc(sizeof *enp);
1677 enp->ae_next = NULL;
1678 n_string_creat(&enp->ae_str);
1679 if(a_aux_err_tail != NULL)
1680 a_aux_err_tail->ae_next = enp;
1681 else
1682 a_aux_err_head = enp;
1683 a_aux_err_tail = enp;
1684 ++a_aux_err_cnt;
1685 }else if(doname ||
1686 (enp->ae_str.s_len > 0 &&
1687 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1688 if(a_aux_err_cnt < ERRORS_MAX)
1689 goto jcreat;
1691 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1692 a_aux_err_tail->ae_next = enp;
1693 a_aux_err_tail = enp;
1694 enp->ae_next = NULL;
1695 n_string_trunc(&enp->ae_str, 0);
1698 # ifdef HAVE_N_VA_COPY
1699 imax = 64;
1700 # else
1701 imax = n_MIN(LINESIZE, 1024);
1702 # endif
1703 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1704 # ifdef HAVE_N_VA_COPY
1705 va_list vac;
1707 n_va_copy(vac, ap);
1708 # else
1709 # define vac ap
1710 # endif
1712 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1713 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1714 # ifdef HAVE_N_VA_COPY
1715 va_end(vac);
1716 # else
1717 # undef vac
1718 # endif
1719 if(i <= 0)
1720 goto jleave;
1721 if(UICMP(z, i, >=, imax)){
1722 # ifdef HAVE_N_VA_COPY
1723 /* XXX Check overflow for upcoming LEN+++i! */
1724 n_string_trunc(&enp->ae_str, len);
1725 continue;
1726 # else
1727 i = (int)strlen(&enp->ae_str.s_dat[len]);
1728 # endif
1730 break;
1732 n_string_trunc(&enp->ae_str, len + (size_t)i);
1734 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1736 #endif /* HAVE_ERRORS */
1738 jleave:
1739 fflush(n_stderr);
1740 NYD2_LEAVE;
1743 FL void
1744 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1745 va_list ap;
1746 NYD_X;
1748 va_start(ap, format);
1749 vfprintf(n_stderr, format, ap);
1750 va_end(ap);
1751 fflush(n_stderr);
1754 FL void
1755 n_perr(char const *msg, int errval){
1756 int e;
1757 char const *fmt;
1758 NYD2_ENTER;
1760 if(msg == NULL){
1761 fmt = "%s%s\n";
1762 msg = n_empty;
1763 }else
1764 fmt = "%s: %s\n";
1766 e = (errval == 0) ? n_err_no : errval;
1767 n_err(fmt, msg, n_err_to_doc(e));
1768 if(errval == 0)
1769 n_err_no = e;
1770 NYD2_LEAVE;
1773 FL void
1774 n_alert(char const *format, ...){
1775 va_list ap;
1776 NYD2_ENTER;
1778 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1780 va_start(ap, format);
1781 n_verr(format, ap);
1782 va_end(ap);
1784 n_err("\n");
1785 NYD2_LEAVE;
1788 FL void
1789 n_panic(char const *format, ...){
1790 va_list ap;
1791 NYD2_ENTER;
1793 if(a_aux_err_linelen > 0){
1794 putc('\n', n_stderr);
1795 a_aux_err_linelen = 0;
1797 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1799 va_start(ap, format);
1800 vfprintf(n_stderr, format, ap);
1801 va_end(ap);
1803 putc('\n', n_stderr);
1804 fflush(n_stderr);
1805 NYD2_LEAVE;
1806 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1809 #ifdef HAVE_ERRORS
1810 FL int
1811 c_errors(void *v){
1812 char **argv = v;
1813 struct a_aux_err_node *enp;
1814 NYD_ENTER;
1816 if(*argv == NULL)
1817 goto jlist;
1818 if(argv[1] != NULL)
1819 goto jerr;
1820 if(!asccasecmp(*argv, "show"))
1821 goto jlist;
1822 if(!asccasecmp(*argv, "clear"))
1823 goto jclear;
1824 jerr:
1825 fprintf(n_stderr,
1826 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1827 v = NULL;
1828 jleave:
1829 NYD_LEAVE;
1830 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1832 jlist:{
1833 FILE *fp;
1834 size_t i;
1836 if(a_aux_err_head == NULL){
1837 fprintf(n_stderr, _("The error ring is empty\n"));
1838 goto jleave;
1841 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1842 NULL){
1843 fprintf(n_stderr, _("tmpfile"));
1844 v = NULL;
1845 goto jleave;
1848 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1849 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1850 /* We don't know whether last string ended with NL; be simple XXX */
1851 putc('\n', fp);
1853 page_or_print(fp, 0);
1854 Fclose(fp);
1856 /* FALLTHRU */
1858 jclear:
1859 a_aux_err_tail = NULL;
1860 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1861 a_aux_err_linelen = 0;
1862 while((enp = a_aux_err_head) != NULL){
1863 a_aux_err_head = enp->ae_next;
1864 n_string_gut(&enp->ae_str);
1865 free(enp);
1867 goto jleave;
1869 #endif /* HAVE_ERRORS */
1871 FL char const *
1872 n_err_to_doc(si32_t eno){
1873 char const *rv;
1874 struct a_aux_err_map const *aemp;
1875 NYD2_ENTER;
1877 aemp = a_aux_err_map_from_no(eno);
1878 #ifdef HAVE_DOCSTRINGS
1879 rv = &a_aux_err_docs[aemp->aem_docoff];
1880 #else
1881 rv = &a_aux_err_names[aemp->aem_nameoff];
1882 #endif
1883 NYD2_LEAVE;
1884 return rv;
1887 FL char const *
1888 n_err_to_name(si32_t eno){
1889 char const *rv;
1890 struct a_aux_err_map const *aemp;
1891 NYD2_ENTER;
1893 aemp = a_aux_err_map_from_no(eno);
1894 rv = &a_aux_err_names[aemp->aem_nameoff];
1895 NYD2_LEAVE;
1896 return rv;
1899 FL si32_t
1900 n_err_from_name(char const *name){
1901 struct a_aux_err_map const *aemp;
1902 ui32_t hash, i, j, x;
1903 si32_t rv;
1904 NYD2_ENTER;
1906 hash = n_torek_hash(name);
1908 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1909 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1910 break;
1912 aemp = &a_aux_err_map[x];
1913 if(aemp->aem_hash == hash &&
1914 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1915 rv = aemp->aem_err_no;
1916 goto jleave;
1919 if(++i == a_AUX_ERR_REV_PRIME){
1920 #ifdef a_AUX_ERR_REV_WRAPAROUND
1921 i = 0;
1922 #else
1923 break;
1924 #endif
1928 /* Have not found it. But wait, it could be that the user did, e.g.,
1929 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1930 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1931 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1932 ) == n_IDEC_STATE_CONSUMED){
1933 aemp = a_aux_err_map_from_no(rv);
1934 rv = aemp->aem_err_no;
1935 goto jleave;
1938 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1939 jleave:
1940 NYD2_LEAVE;
1941 return rv;
1944 #ifdef HAVE_REGEX
1945 FL char const *
1946 n_regex_err_to_doc(const regex_t *rep, int e){
1947 char *cp;
1948 size_t i;
1949 NYD2_ENTER;
1951 i = regerror(e, rep, NULL, 0) +1;
1952 cp = salloc(i);
1953 regerror(e, rep, cp, i);
1954 NYD2_LEAVE;
1955 return cp;
1957 #endif
1959 /* s-it-mode */