Drop fakedate(), add n_time_ctime(); be super-careful, henceforth
[s-mailx.git] / auxlily.c
blobf78ddded43e6882435e6132debe0ff074cdf191b
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions that don't fit anywhere else.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #ifdef HAVE_SOCKETS
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
47 # endif
49 # include <netdb.h>
50 #endif
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
54 #endif
55 #ifdef HAVE_SETLOCALE
56 # include <locale.h>
57 #endif
59 #if defined HAVE_GETRANDOM && !n_RANDOM_USE_XSSL
60 # include HAVE_GETRANDOM_HEADER
61 #endif
63 #ifdef HAVE_IDNA
64 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
65 # include <idna.h>
66 # include <idn-free.h>
67 # include <stringprep.h>
68 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
69 # include <idn/api.h>
70 # endif
71 #endif
73 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
74 union rand_state{
75 struct rand_arc4{
76 ui8_t _dat[256];
77 ui8_t _i;
78 ui8_t _j;
79 ui8_t __pad[6];
80 } a;
81 ui8_t b8[sizeof(struct rand_arc4)];
82 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
84 #endif
86 #ifdef HAVE_ERRORS
87 struct a_aux_err_node{
88 struct a_aux_err_node *ae_next;
89 struct n_string ae_str;
91 #endif
93 struct a_aux_err_map{
94 ui32_t aem_hash; /* Hash of name */
95 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
96 ui32_t aem_docoff; /* Into a_aux_err docs[] */
97 si32_t aem_err_no; /* The OS error value for this one */
100 static ui8_t const a_aux_idec_atoi[256] = {
101 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
102 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
103 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
104 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
105 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
106 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
108 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
109 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
110 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
111 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
112 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
113 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
114 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
115 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
117 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
121 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
122 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
123 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
124 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
125 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
126 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
129 #define a_X(X) ((ui64_t)-1 / (X))
130 static ui64_t const a_aux_idec_cutlimit[35] = {
131 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
132 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
133 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
134 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
135 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
137 #undef a_X
139 /* Include the constant make-errors.sh output */
140 #include <gen-errors.h>
142 /* And these things come from mk-config.h (config-time make-errors.sh output) */
143 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
144 #undef a_X
145 #define a_X(N,I) {N,I},
146 n__ERR_NUMBER_TO_MAPOFF
147 #undef a_X
150 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
151 static union rand_state *a_aux_rand;
152 #endif
154 /* Error ring, for `errors' */
155 #ifdef HAVE_ERRORS
156 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
157 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
158 #endif
159 static size_t a_aux_err_linelen;
161 /* Our ARC4 random generator with its completely unacademical pseudo
162 * initialization (shall /dev/urandom fail) */
163 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
164 static void a_aux_rand_init(void);
165 n_INLINE ui8_t a_aux_rand_get8(void);
166 # ifndef HAVE_GETRANDOM
167 static ui32_t a_aux_rand_weak(ui32_t seed);
168 # endif
169 #endif
171 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
172 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
174 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
175 static void
176 a_aux_rand_init(void){
177 # ifndef HAVE_GETRANDOM
178 # ifdef HAVE_CLOCK_GETTIME
179 struct timespec ts;
180 # else
181 struct timeval ts;
182 # endif
183 union {int fd; size_t i;} u;
184 ui32_t seed, rnd;
185 # endif
186 NYD2_ENTER;
188 a_aux_rand = n_alloc(sizeof *a_aux_rand);
190 # ifdef HAVE_GETRANDOM
191 /* getrandom(2) guarantees 256 without n_ERR_INTR..
192 * However, support sequential reading to avoid possible hangs that have
193 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
194 * HAVE_GETRANDOM is #defined) */
195 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
196 "Buffer too large to be served without n_ERR_INTR error");
197 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
198 "Buffer too small to serve used array indices");
199 /* C99 */{
200 size_t o, i;
202 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
203 ssize_t gr;
205 gr = HAVE_GETRANDOM(&a_aux_rand->a._dat[o], i);
206 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
207 a_aux_rand->a._dat[84]];
208 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
209 a_aux_rand->a._dat[42]];
210 /* ..but be on the safe side */
211 if(gr > 0){
212 i -= (size_t)gr;
213 if(i == 0)
214 break;
215 o += (size_t)gr;
217 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
218 "waiting a bit\n"));
219 n_msleep(250, FAL0);
223 # else /* HAVE_GETRANDOM */
224 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
225 bool_t ok;
227 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
228 sizeof(a_aux_rand->a._dat)));
229 close(u.fd);
231 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
232 a_aux_rand->a._dat[84]];
233 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
234 a_aux_rand->a._dat[42]];
235 if(ok)
236 goto jleave;
239 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
240 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
241 ui32_t t, k;
243 # ifdef HAVE_CLOCK_GETTIME
244 clock_gettime(CLOCK_REALTIME, &ts);
245 t = (ui32_t)ts.tv_nsec;
246 # else
247 gettimeofday(&ts, NULL);
248 t = (ui32_t)ts.tv_usec;
249 # endif
250 if(rnd & 1)
251 t = (t >> 16) | (t << 16);
252 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
253 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
254 if(rnd == 7 || rnd == 17)
255 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
256 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
257 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
258 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
259 if((rnd & 3) == 3)
260 seed ^= n_prime_next(seed);
264 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
265 a_aux_rand_get8();
266 jleave:
267 # endif /* !HAVE_GETRANDOM */
268 NYD2_LEAVE;
271 n_INLINE ui8_t
272 a_aux_rand_get8(void){
273 ui8_t si, sj;
275 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
276 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
277 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
278 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
279 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
282 # ifndef HAVE_GETRANDOM
283 static ui32_t
284 a_aux_rand_weak(ui32_t seed){
285 /* From "Random number generators: good ones are hard to find",
286 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
287 * October 1988, p. 1195.
288 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
289 ui32_t hi;
291 if(seed == 0)
292 seed = 123459876;
293 hi = seed / 127773;
294 seed %= 127773;
295 seed = (seed * 16807) - (hi * 2836);
296 if((si32_t)seed < 0)
297 seed += SI32_MAX;
298 return seed;
300 # endif /* HAVE_GETRANDOM */
301 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
303 static struct a_aux_err_map const *
304 a_aux_err_map_from_no(si32_t eno){
305 si32_t ecmp;
306 size_t asz;
307 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
308 struct a_aux_err_map const *aemp;
309 NYD2_ENTER;
311 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
313 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
314 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
315 asz != 0; asz >>= 1){
316 tmp = &adat[asz >> 1];
317 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
318 aemp = &a_aux_err_map[(*tmp)[1]];
319 break;
321 if(ecmp > 0){
322 adat = &tmp[1];
323 --asz;
327 NYD2_LEAVE;
328 return aemp;
331 FL void
332 n_locale_init(void){
333 NYD2_ENTER;
335 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
337 #ifndef HAVE_SETLOCALE
338 n_mb_cur_max = 1;
339 #else
340 setlocale(LC_ALL, n_empty);
341 n_mb_cur_max = MB_CUR_MAX;
342 # ifdef HAVE_NL_LANGINFO
343 /* C99 */{
344 char const *cp;
346 if((cp = nl_langinfo(CODESET)) != NULL)
347 /* (Will log during startup if user set that via -S) */
348 ok_vset(ttycharset, cp);
350 # endif /* HAVE_SETLOCALE */
352 # ifdef HAVE_C90AMEND1
353 if(n_mb_cur_max > 1){
354 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
355 n_psonce |= n_PSO_UNICODE;
356 # else
357 wchar_t wc;
358 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
359 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
360 n_psonce |= n_PSO_UNICODE;
361 /* Reset possibly messed up state; luckily this also gives us an
362 * indication whether the encoding has locking shift state sequences */
363 if(mbtowc(&wc, NULL, n_mb_cur_max))
364 n_psonce |= n_PSO_ENC_MBSTATE;
365 # endif
367 # endif
368 #endif /* HAVE_C90AMEND1 */
369 NYD2_LEAVE;
372 FL size_t
373 n_screensize(void){
374 char const *cp;
375 uiz_t rv;
376 NYD2_ENTER;
378 if((cp = ok_vlook(screen)) != NULL){
379 n_idec_uiz_cp(&rv, cp, 0, NULL);
380 if(rv == 0)
381 rv = n_scrnheight;
382 }else
383 rv = n_scrnheight;
385 if(rv > 2)
386 rv -= 2;
387 NYD2_LEAVE;
388 return rv;
391 FL char const *
392 n_pager_get(char const **env_addon){
393 char const *rv;
394 NYD_ENTER;
396 rv = ok_vlook(PAGER);
398 if(env_addon != NULL){
399 *env_addon = NULL;
400 /* Update the manual upon any changes:
401 * *colour-pager*, $PAGER */
402 if(strstr(rv, "less") != NULL){
403 if(getenv("LESS") == NULL)
404 *env_addon = "LESS=RXi";
405 }else if(strstr(rv, "lv") != NULL){
406 if(getenv("LV") == NULL)
407 *env_addon = "LV=-c";
410 NYD_LEAVE;
411 return rv;
414 FL void
415 page_or_print(FILE *fp, size_t lines)
417 int c;
418 char const *cp;
419 NYD_ENTER;
421 fflush_rewind(fp);
423 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
424 size_t rows;
426 if(*cp == '\0')
427 rows = (size_t)n_scrnheight;
428 else
429 n_idec_uiz_cp(&rows, cp, 0, NULL);
431 if (rows > 0 && lines == 0) {
432 while ((c = getc(fp)) != EOF)
433 if (c == '\n' && ++lines >= rows)
434 break;
435 really_rewind(fp);
438 if (lines >= rows) {
439 char const *env_add[2], *pager;
441 pager = n_pager_get(&env_add[0]);
442 env_add[1] = NULL;
443 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
444 env_add, NULL);
445 goto jleave;
449 while ((c = getc(fp)) != EOF)
450 putc(c, n_stdout);
451 jleave:
452 NYD_LEAVE;
455 FL enum protocol
456 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
457 char const **adjusted_or_null)
459 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
460 char const *cp, *orig_name;
461 enum protocol rv = PROTO_UNKNOWN;
462 NYD_ENTER;
464 if(name[0] == '%' && name[1] == ':')
465 name += 2;
466 orig_name = name;
468 for (cp = name; *cp && *cp != ':'; cp++)
469 if (!alnumchar(*cp))
470 goto jfile;
472 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
473 if(!strncmp(name, "file", sizeof("file") -1) ||
474 !strncmp(name, "mbox", sizeof("mbox") -1))
475 rv = PROTO_FILE;
476 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
477 rv = PROTO_MAILDIR;
478 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
479 #ifdef HAVE_POP3
480 rv = PROTO_POP3;
481 #else
482 n_err(_("No POP3 support compiled in\n"));
483 #endif
484 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
485 #if defined HAVE_POP3 && defined HAVE_SSL
486 rv = PROTO_POP3;
487 #else
488 n_err(_("No POP3S support compiled in\n"));
489 #endif
491 else if(!strncmp(name, "imap", sizeof("imap") -1)){
492 #ifdef HAVE_IMAP
493 rv = PROTO_IMAP;
494 #else
495 n_err(_("No IMAP support compiled in\n"));
496 #endif
497 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
498 #if defined HAVE_IMAP && defined HAVE_SSL
499 rv = PROTO_IMAP;
500 #else
501 n_err(_("No IMAPS support compiled in\n"));
502 #endif
504 orig_name = &cp[3];
505 goto jleave;
508 jfile:
509 rv = PROTO_FILE;
511 if(check_stat || try_hooks){
512 struct n_file_type ft;
513 struct stat stb;
514 char *np;
515 size_t sz;
517 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
518 memcpy(np, name, sz + 1);
520 if(!stat(name, &stb)){
521 if(S_ISDIR(stb.st_mode) &&
522 (memcpy(&np[sz], "/tmp", 5),
523 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
524 (memcpy(&np[sz], "/new", 5),
525 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
526 (memcpy(&np[sz], "/cur", 5),
527 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
528 rv = PROTO_MAILDIR;
529 }else if(try_hooks && n_filetype_trial(&ft, name))
530 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
531 else if((cp = ok_vlook(newfolders)) != NULL &&
532 !asccasecmp(cp, "maildir"))
533 rv = PROTO_MAILDIR;
535 n_lofi_free(np);
537 jleave:
538 if(adjusted_or_null != NULL)
539 *adjusted_or_null = orig_name;
540 NYD_LEAVE;
541 return rv;
544 FL char *
545 n_c_to_hex_base16(char store[3], char c){
546 static char const itoa16[] = "0123456789ABCDEF";
547 NYD2_ENTER;
549 store[2] = '\0';
550 store[1] = itoa16[(ui8_t)c & 0x0F];
551 c = ((ui8_t)c >> 4) & 0x0F;
552 store[0] = itoa16[(ui8_t)c];
553 NYD2_LEAVE;
554 return store;
557 FL si32_t
558 n_c_from_hex_base16(char const hex[2]){
559 static ui8_t const atoi16[] = {
560 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
561 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
562 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
563 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
564 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
565 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
566 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
568 ui8_t i1, i2;
569 si32_t rv;
570 NYD2_ENTER;
572 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
573 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
574 goto jerr;
575 i1 = atoi16[i1];
576 i2 = atoi16[i2];
577 if ((i1 | i2) & 0xF0u)
578 goto jerr;
579 rv = i1;
580 rv <<= 4;
581 rv += i2;
582 jleave:
583 NYD2_LEAVE;
584 return rv;
585 jerr:
586 rv = -1;
587 goto jleave;
590 FL enum n_idec_state
591 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
592 enum n_idec_mode idm, char const **endptr_or_null){
593 /* XXX Brute simple and */
594 ui8_t currc;
595 ui64_t res, cut;
596 enum n_idec_state rv;
597 NYD_ENTER;
599 idm &= n__IDEC_MODE_MASK;
600 rv = n_IDEC_STATE_NONE | idm;
601 res = 0;
603 if(clen == UIZ_MAX){
604 if(*cbuf == '\0')
605 goto jeinval;
606 }else if(clen == 0)
607 goto jeinval;
609 assert(base != 1 && base <= 36);
610 /*if(base == 1 || base > 36)
611 * goto jeinval;*/
613 /* Leading WS */
614 while(spacechar(*cbuf))
615 if(*++cbuf == '\0' || --clen == 0)
616 goto jeinval;
618 /* Check sign */
619 switch(*cbuf){
620 case '-':
621 rv |= n_IDEC_STATE_SEEN_MINUS;
622 /* FALLTHROUGH */
623 case '+':
624 if(*++cbuf == '\0' || --clen == 0)
625 goto jeinval;
626 break;
629 /* Base detection/skip */
630 if(*cbuf != '0'){
631 if(base == 0){
632 base = 10;
634 /* Support BASE#number prefix, where BASE is decimal 2-36.
635 * Enter this only if anything is to follow after that prefix*/
636 if(clen > 2){
637 char c1, c2, c3;
639 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
640 (((c2 = cbuf[1]) == '#') ||
641 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
642 base = a_aux_idec_atoi[(ui8_t)c1];
643 if(c2 == '#'){
644 c3 = cbuf[2];
645 }else{
646 c3 = cbuf[3];
647 base *= 10; /* xxx Inline atoi decimal base */
648 base += a_aux_idec_atoi[(ui8_t)c2];
651 /* We do not interpret this as BASE#number at all if either we
652 * did not get a valid base or if the first char is not valid
653 * according to base, to comply to the latest interpretion of
654 * "prefix", see comment for standard prefixes below */
655 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
656 base = 10;
657 else if(c2 == '#')
658 clen -= 2, cbuf += 2;
659 else
660 clen -= 3, cbuf += 3;
665 /* Character must be valid for base */
666 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
667 if(currc >= base)
668 goto jeinval;
669 }else{
670 /* 0 always valid as is, fallback base 10 */
671 if(*++cbuf == '\0' || --clen == 0)
672 goto jleave;
674 /* Base "detection" */
675 if(base == 0 || base == 2 || base == 16){
676 switch(*cbuf){
677 case 'x':
678 case 'X':
679 if((base & 2) == 0){
680 base = 0x10;
681 goto jprefix_skip;
683 break;
684 case 'b':
685 case 'B':
686 if((base & 16) == 0){
687 base = 2; /* 0b10 */
688 /* Char after prefix must be valid. However, after some error
689 * in the tor software all libraries (which had to) turned to
690 * an interpretation of the C standard which says that the
691 * prefix may optionally precede an otherwise valid sequence,
692 * which means that "0x" is not a STATE_INVAL error but gives
693 * a "0" result with a "STATE_BASE" error and a rest of "x" */
694 jprefix_skip:
695 #if 1
696 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
697 --clen, ++cbuf;
698 #else
699 if(*++cbuf == '\0' || --clen == 0)
700 goto jeinval;
702 /* Character must be valid for base, invalid otherwise */
703 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
704 if(currc >= base)
705 goto jeinval;
706 #endif
708 break;
709 default:
710 if(base == 0)
711 base = 010;
712 break;
716 /* Character must be valid for base, _EBASE otherwise */
717 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
718 if(currc >= base)
719 goto jebase;
722 for(cut = a_aux_idec_cutlimit[base - 2];;){
723 if(res >= cut){
724 if(res == cut){
725 res *= base;
726 if(res > UI64_MAX - currc)
727 goto jeover;
728 res += currc;
729 }else
730 goto jeover;
731 }else{
732 res *= base;
733 res += currc;
736 if(*++cbuf == '\0' || --clen == 0)
737 break;
739 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
740 if(currc >= base)
741 goto jebase;
744 jleave:
746 ui64_t uimask;
748 switch(rv & n__IDEC_MODE_LIMIT_MASK){
749 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
750 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
751 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
752 default: uimask = UI64_MAX; break;
754 if(rv & n_IDEC_MODE_SIGNED_TYPE)
755 uimask >>= 1;
757 if(res & ~uimask){
758 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
759 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
760 if(res > uimask + 1){
761 res = uimask << 1;
762 res &= ~uimask;
763 }else{
764 res = -res;
765 break;
767 }else
768 res = uimask;
769 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
770 rv |= n_IDEC_STATE_EOVERFLOW;
771 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
772 res = -res;
773 }while(0);
775 switch(rv & n__IDEC_MODE_LIMIT_MASK){
776 case n_IDEC_MODE_LIMIT_8BIT:
777 if(rv & n_IDEC_MODE_SIGNED_TYPE)
778 *(si8_t*)resp = (si8_t)res;
779 else
780 *(ui8_t*)resp = (ui8_t)res;
781 break;
782 case n_IDEC_MODE_LIMIT_16BIT:
783 if(rv & n_IDEC_MODE_SIGNED_TYPE)
784 *(si16_t*)resp = (si16_t)res;
785 else
786 *(ui16_t*)resp = (ui16_t)res;
787 break;
788 case n_IDEC_MODE_LIMIT_32BIT:
789 if(rv & n_IDEC_MODE_SIGNED_TYPE)
790 *(si32_t*)resp = (si32_t)res;
791 else
792 *(ui32_t*)resp = (ui32_t)res;
793 break;
794 default:
795 if(rv & n_IDEC_MODE_SIGNED_TYPE)
796 *(si64_t*)resp = (si64_t)res;
797 else
798 *(ui64_t*)resp = (ui64_t)res;
799 break;
802 if(endptr_or_null != NULL)
803 *endptr_or_null = cbuf;
804 if(*cbuf == '\0' || clen == 0)
805 rv |= n_IDEC_STATE_CONSUMED;
806 NYD_LEAVE;
807 return rv;
809 jeinval:
810 rv |= n_IDEC_STATE_EINVAL;
811 goto j_maxval;
812 jebase:
813 /* Not a base error for terminator and whitespace! */
814 if(*cbuf != '\0' && !spacechar(*cbuf))
815 rv |= n_IDEC_STATE_EBASE;
816 goto jleave;
818 jeover:
819 /* Overflow error: consume input until bad character or length out */
820 for(;;){
821 if(*++cbuf == '\0' || --clen == 0)
822 break;
823 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
824 if(currc >= base)
825 break;
828 rv |= n_IDEC_STATE_EOVERFLOW;
829 j_maxval:
830 if(rv & n_IDEC_MODE_SIGNED_TYPE)
831 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
832 : (ui64_t)SI64_MAX;
833 else
834 res = UI64_MAX;
835 rv &= ~n_IDEC_STATE_SEEN_MINUS;
836 goto jleave;
839 FL ui32_t
840 n_torek_hash(char const *name){
841 /* Chris Torek's hash */
842 char c;
843 ui32_t h;
844 NYD2_ENTER;
846 for(h = 0; (c = *name++) != '\0';)
847 h = (h * 33) + c;
848 NYD2_LEAVE;
849 return h;
852 FL ui32_t
853 n_torek_ihashn(char const *dat, size_t len){
854 /* See n_torek_hash() */
855 char c;
856 ui32_t h;
857 NYD2_ENTER;
859 if(len == UIZ_MAX)
860 for(h = 0; (c = *dat++) != '\0';)
861 h = (h * 33) + lowerconv(c);
862 else
863 for(h = 0; len > 0; --len){
864 c = *dat++;
865 h = (h * 33) + lowerconv(c);
867 NYD2_LEAVE;
868 return h;
871 FL ui32_t
872 n_prime_next(ui32_t n){
873 static ui32_t const primes[] = {
874 5, 11, 23, 47, 97, 157, 283,
875 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
876 131071, 262139, 524287, 1048573, 2097143, 4194301,
877 8388593, 16777213, 33554393, 67108859, 134217689,
878 268435399, 536870909, 1073741789, 2147483647
880 ui32_t i, mprime;
881 NYD2_ENTER;
883 i = (n < primes[n_NELEM(primes) / 4] ? 0
884 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
885 : n_NELEM(primes) / 2));
887 do if((mprime = primes[i]) > n)
888 break;
889 while(++i < n_NELEM(primes));
891 if(i == n_NELEM(primes) && mprime < n)
892 mprime = n;
893 NYD2_LEAVE;
894 return mprime;
897 FL char const *
898 n_getdeadletter(void){
899 char const *cp;
900 bool_t bla;
901 NYD_ENTER;
903 bla = FAL0;
904 jredo:
905 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
906 if(cp == NULL || strlen(cp) >= PATH_MAX){
907 if(!bla){
908 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
909 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
910 ok_vclear(DEAD);
911 bla = TRU1;
912 goto jredo;
913 }else{
914 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
915 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
918 NYD_LEAVE;
919 return cp;
922 FL char *
923 n_nodename(bool_t mayoverride){
924 static char *sys_hostname, *hostname; /* XXX free-at-exit */
926 struct utsname ut;
927 char *hn;
928 #ifdef HAVE_SOCKETS
929 # ifdef HAVE_GETADDRINFO
930 struct addrinfo hints, *res;
931 # else
932 struct hostent *hent;
933 # endif
934 #endif
935 NYD2_ENTER;
937 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
939 }else if((hn = sys_hostname) == NULL){
940 uname(&ut);
941 hn = ut.nodename;
942 #ifdef HAVE_SOCKETS
943 # ifdef HAVE_GETADDRINFO
944 memset(&hints, 0, sizeof hints);
945 hints.ai_family = AF_UNSPEC;
946 hints.ai_flags = AI_CANONNAME;
947 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
948 if(res->ai_canonname != NULL){
949 size_t l;
951 l = strlen(res->ai_canonname) +1;
952 hn = n_lofi_alloc(l);
953 memcpy(hn, res->ai_canonname, l);
955 freeaddrinfo(res);
957 # else
958 hent = gethostbyname(hn);
959 if(hent != NULL)
960 hn = hent->h_name;
961 # endif
962 #endif
963 sys_hostname = sstrdup(hn);
964 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
965 if(hn != ut.nodename)
966 n_lofi_free(hn);
967 #endif
968 hn = sys_hostname;
971 if(hostname != NULL && hostname != sys_hostname)
972 n_free(hostname);
973 hostname = sstrdup(hn);
974 NYD2_LEAVE;
975 return hostname;
978 #ifdef HAVE_IDNA
979 FL bool_t
980 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
981 char *idna_utf8;
982 bool_t rv;
983 NYD_ENTER;
985 if((rv = (ilen == 0)))
986 goto jleave;
988 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
989 ibuf = savestrbuf(ibuf, ilen);
990 ilen = 0;
992 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
993 ibuf);
994 if(idna_utf8 == NULL)
995 goto jleave;
997 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
998 /* C99 */{
999 char *idna_ascii;
1001 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1002 out = n_string_assign_cp(out, idna_ascii);
1003 idn_free(idna_ascii);
1004 rv = TRU1;
1005 ilen = out->s_len;
1008 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1009 ilen = strlen(idna_utf8);
1010 jredo:
1011 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
1012 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1013 case idn_buffer_overflow:
1014 ilen += HOST_NAME_MAX +1;
1015 goto jredo;
1016 case idn_success:
1017 rv = TRU1;
1018 ilen = strlen(out->s_dat);
1019 break;
1020 default:
1021 ilen = 0;
1022 break;
1025 # else
1026 # error Unknown HAVE_IDNA
1027 # endif
1028 jleave:
1029 out = n_string_trunc(out, ilen);
1030 NYD_LEAVE;
1031 return rv;
1033 #endif /* HAVE_IDNA */
1035 FL char *
1036 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1037 struct str b64;
1038 char *indat, *cp, *oudat;
1039 size_t i, inlen, oulen;
1040 NYD_ENTER;
1042 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1043 n_psonce |= n_PSO_RANDOM_INIT;
1045 if(n_poption & n_PO_D_V){
1046 char const *prngn;
1048 #if n_RANDOM_USE_XSSL
1049 prngn = "*SSL RAND_*";
1050 #elif defined HAVE_POSIX_RANDOM
1051 prngn = "POSIX/arc4random";
1052 #else
1053 prngn = "builtin ARC4";
1054 #endif
1055 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1058 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1059 a_aux_rand_init();
1060 #endif
1063 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1064 * with PAD stripped is still longer than what the user requests, easy way.
1065 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1066 * include the base64 PAD characters in our random string: give some pad */
1067 i = len;
1068 if((inlen = i % 3) != 0)
1069 i += 3 - inlen;
1070 jinc1:
1071 inlen = i >> 2;
1072 oulen = inlen << 2;
1073 if(oulen < len){
1074 i += 3;
1075 goto jinc1;
1077 inlen = inlen + (inlen << 1);
1079 indat = n_lofi_alloc(inlen +1);
1081 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1082 #if n_RANDOM_USE_XSSL
1083 ssl_rand_bytes(indat, inlen);
1084 #elif !defined HAVE_POSIX_RANDOM
1085 for(i = inlen; i-- > 0;)
1086 indat[i] = (char)a_aux_rand_get8();
1087 #else
1088 for(cp = indat, i = inlen; i > 0;){
1089 union {ui32_t i4; char c[4];} r;
1090 size_t j;
1092 r.i4 = (ui32_t)arc4random();
1093 switch((j = i & 3)){
1094 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1095 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1096 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1097 default: cp[0] = r.c[0]; break;
1099 cp += j;
1100 i -= j;
1102 #endif
1103 }else{
1104 for(cp = indat, i = inlen; i > 0;){
1105 union {ui32_t i4; char c[4];} r;
1106 size_t j;
1108 r.i4 = ++*reprocnt_or_null;
1109 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1110 char x;
1112 x = r.c[0];
1113 r.c[0] = r.c[3];
1114 r.c[3] = x;
1115 x = r.c[1];
1116 r.c[1] = r.c[2];
1117 r.c[2] = x;
1119 switch((j = i & 3)){
1120 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1121 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1122 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1123 default: cp[0] = r.c[0]; break;
1125 cp += j;
1126 i -= j;
1130 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1131 b64.s = oudat;
1132 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1133 assert(b64.l >= len);
1134 memcpy(dat, b64.s, len);
1135 dat[len] = '\0';
1136 if(oudat != dat)
1137 n_lofi_free(oudat);
1139 n_lofi_free(indat);
1141 NYD_LEAVE;
1142 return dat;
1145 FL char *
1146 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1147 char *dat;
1148 NYD_ENTER;
1150 dat = n_autorec_alloc(len +1);
1151 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1152 NYD_LEAVE;
1153 return dat;
1156 FL si8_t
1157 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1159 si8_t rv;
1160 NYD_ENTER;
1162 assert(inlen == 0 || inbuf != NULL);
1164 if (inlen == UIZ_MAX)
1165 inlen = strlen(inbuf);
1167 if (inlen == 0)
1168 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1169 else {
1170 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1171 !ascncasecmp(inbuf, "true", inlen) ||
1172 !ascncasecmp(inbuf, "yes", inlen) ||
1173 !ascncasecmp(inbuf, "on", inlen))
1174 rv = 1;
1175 else if ((inlen == 1 &&
1176 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1177 !ascncasecmp(inbuf, "false", inlen) ||
1178 !ascncasecmp(inbuf, "no", inlen) ||
1179 !ascncasecmp(inbuf, "off", inlen))
1180 rv = 0;
1181 else {
1182 ui64_t ib;
1184 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1185 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1186 ) != n_IDEC_STATE_CONSUMED)
1187 rv = -1;
1188 else
1189 rv = (ib != 0);
1192 NYD_LEAVE;
1193 return rv;
1196 FL si8_t
1197 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1199 si8_t rv;
1200 NYD_ENTER;
1202 assert(inlen == 0 || inbuf != NULL);
1204 if (inlen == UIZ_MAX)
1205 inlen = strlen(inbuf);
1207 if (inlen == 0)
1208 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1209 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1210 !ascncasecmp(inbuf, "ask-", 4) &&
1211 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1212 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1213 rv = getapproval(prompt, rv);
1214 NYD_LEAVE;
1215 return rv;
1218 FL bool_t
1219 n_is_all_or_aster(char const *name){
1220 bool_t rv;
1221 NYD_ENTER;
1223 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1224 NYD_LEAVE;
1225 return rv;
1228 FL struct n_timespec const *
1229 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1230 static struct n_timespec ts_now;
1231 NYD2_ENTER;
1233 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1234 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1235 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1236 ts_now.ts_nsec = 0;
1237 }else if(force_update || ts_now.ts_sec == 0){
1238 #ifdef HAVE_CLOCK_GETTIME
1239 struct timespec ts;
1241 clock_gettime(CLOCK_REALTIME, &ts);
1242 ts_now.ts_sec = (si64_t)ts.tv_sec;
1243 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1244 #elif defined HAVE_GETTIMEOFDAY
1245 struct timeval tv;
1247 gettimeofday(&tv, NULL);
1248 ts_now.ts_sec = (si64_t)tv.tv_sec;
1249 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1250 #else
1251 ts_now.ts_sec = (si64_t)time(NULL);
1252 ts_now.ts_nsec = 0;
1253 #endif
1256 /* Just in case.. */
1257 if(n_UNLIKELY(ts_now.ts_sec < 0))
1258 ts_now.ts_sec = 0;
1259 NYD2_LEAVE;
1260 return &ts_now;
1263 FL void
1264 time_current_update(struct time_current *tc, bool_t full_update){
1265 NYD_ENTER;
1266 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1268 if(full_update){
1269 char *cp;
1270 struct tm *tmp;
1271 time_t t;
1273 t = tc->tc_time;
1274 jredo:
1275 if((tmp = gmtime(&t)) == NULL){
1276 t = 0;
1277 goto jredo;
1279 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1280 if((tmp = localtime(&t)) == NULL){
1281 t = 0;
1282 goto jredo;
1284 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1285 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1286 *cp++ = '\n';
1287 *cp = '\0';
1288 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1290 NYD_LEAVE;
1293 FL char *
1294 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1295 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1296 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1297 * ISO C requires us to use the above format string,
1298 * even if it will not fit in the buffer. Thus asctime_r
1299 * is _supposed_ to crash if the fields in tm are too large.
1300 * We follow this behavior and crash "gracefully" to warn
1301 * application developers that they may not be so lucky
1302 * on other implementations (e.g. stack smashing..).
1303 * So we need to do it on our own or the libc may kill us */
1304 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1306 si32_t y, md, th, tm, ts;
1307 char const *wdn, *mn;
1308 struct tm const *tmp;
1309 NYD_ENTER;
1311 if((tmp = localtime_or_nil) == NULL){
1312 time_t t;
1314 t = (time_t)secsepoch;
1315 jredo:
1316 if((tmp = localtime(&t)) == NULL){
1317 /* TODO error log */
1318 t = 0;
1319 goto jredo;
1323 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1324 y = 1970;
1325 wdn = n_weekday_names[4];
1326 mn = n_month_names[0];
1327 md = 1;
1328 th = tm = ts = 0;
1329 }else{
1330 y += 1900;
1331 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1332 ? n_weekday_names[tmp->tm_wday] : n_qm;
1333 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1334 ? n_month_names[tmp->tm_mon] : n_qm;
1336 if((md = tmp->tm_mday) < 1 || md > 31)
1337 md = 1;
1339 if((th = tmp->tm_hour) < 0 || th > 23)
1340 th = 0;
1341 if((tm = tmp->tm_min) < 0 || tm > 59)
1342 tm = 0;
1343 if((ts = tmp->tm_sec) < 0 || ts > 60)
1344 ts = 0;
1347 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1348 wdn, mn, md, th, tm, ts, y);
1349 NYD_LEAVE;
1350 return buf;
1353 FL uiz_t
1354 n_msleep(uiz_t millis, bool_t ignint){
1355 uiz_t rv;
1356 NYD2_ENTER;
1358 #ifdef HAVE_NANOSLEEP
1359 /* C99 */{
1360 struct timespec ts, trem;
1361 int i;
1363 ts.tv_sec = millis / 1000;
1364 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1366 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1367 ts = trem;
1368 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1371 #elif defined HAVE_SLEEP
1372 if((millis /= 1000) == 0)
1373 millis = 1;
1374 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1375 millis = rv;
1376 #else
1377 # error Configuration should have detected a function for sleeping.
1378 #endif
1380 NYD2_LEAVE;
1381 return rv;
1384 FL void
1385 n_err(char const *format, ...){
1386 va_list ap;
1387 NYD2_ENTER;
1389 va_start(ap, format);
1390 #ifdef HAVE_ERRORS
1391 if(n_psonce & n_PSO_INTERACTIVE)
1392 n_verr(format, ap);
1393 else
1394 #endif
1396 size_t len;
1397 bool_t doname;
1399 doname = FAL0;
1401 while(*format == '\n'){
1402 doname = TRU1;
1403 putc('\n', n_stderr);
1404 ++format;
1407 if(doname)
1408 a_aux_err_linelen = 0;
1410 if((len = strlen(format)) > 0){
1411 if(doname || a_aux_err_linelen == 0){
1412 char const *cp;
1414 if(*(cp = ok_vlook(log_prefix)) != '\0')
1415 fputs(cp, n_stderr);
1417 vfprintf(n_stderr, format, ap);
1419 /* C99 */{
1420 size_t i = len;
1422 if(format[--len] == '\n'){
1423 a_aux_err_linelen = (i -= ++len);
1424 break;
1426 ++a_aux_err_linelen;
1427 }while(len > 0);
1431 fflush(n_stderr);
1433 va_end(ap);
1434 NYD2_LEAVE;
1437 FL void
1438 n_verr(char const *format, va_list ap){
1439 #ifdef HAVE_ERRORS
1440 struct a_aux_err_node *enp;
1441 #endif
1442 bool_t doname;
1443 size_t len;
1444 NYD2_ENTER;
1446 doname = FAL0;
1448 while(*format == '\n'){
1449 putc('\n', n_stderr);
1450 doname = TRU1;
1451 ++format;
1454 if(doname){
1455 a_aux_err_linelen = 0;
1456 #ifdef HAVE_ERRORS
1457 if(n_psonce & n_PSO_INTERACTIVE){
1458 if((enp = a_aux_err_tail) != NULL &&
1459 (enp->ae_str.s_len > 0 &&
1460 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1461 n_string_push_c(&enp->ae_str, '\n');
1463 #endif
1466 if((len = strlen(format)) == 0)
1467 goto jleave;
1468 #ifdef HAVE_ERRORS
1469 n_pstate |= n_PS_ERRORS_PROMPT;
1470 #endif
1472 if(doname || a_aux_err_linelen == 0){
1473 char const *cp;
1475 if(*(cp = ok_vlook(log_prefix)) != '\0')
1476 fputs(cp, n_stderr);
1479 /* C99 */{
1480 size_t i = len;
1482 if(format[--len] == '\n'){
1483 a_aux_err_linelen = (i -= ++len);
1484 break;
1486 ++a_aux_err_linelen;
1487 }while(len > 0);
1490 #ifdef HAVE_ERRORS
1491 if(!(n_psonce & n_PSO_INTERACTIVE))
1492 #endif
1493 vfprintf(n_stderr, format, ap);
1494 #ifdef HAVE_ERRORS
1495 else{
1496 int imax, i;
1497 n_LCTAV(ERRORS_MAX > 3);
1499 /* Link it into the `errors' message ring */
1500 if((enp = a_aux_err_tail) == NULL){
1501 jcreat:
1502 enp = smalloc(sizeof *enp);
1503 enp->ae_next = NULL;
1504 n_string_creat(&enp->ae_str);
1505 if(a_aux_err_tail != NULL)
1506 a_aux_err_tail->ae_next = enp;
1507 else
1508 a_aux_err_head = enp;
1509 a_aux_err_tail = enp;
1510 ++a_aux_err_cnt;
1511 }else if(doname ||
1512 (enp->ae_str.s_len > 0 &&
1513 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1514 if(a_aux_err_cnt < ERRORS_MAX)
1515 goto jcreat;
1517 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1518 a_aux_err_tail->ae_next = enp;
1519 a_aux_err_tail = enp;
1520 enp->ae_next = NULL;
1521 n_string_trunc(&enp->ae_str, 0);
1524 # ifdef HAVE_N_VA_COPY
1525 imax = 64;
1526 # else
1527 imax = n_MIN(LINESIZE, 1024);
1528 # endif
1529 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1530 # ifdef HAVE_N_VA_COPY
1531 va_list vac;
1533 n_va_copy(vac, ap);
1534 # else
1535 # define vac ap
1536 # endif
1538 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1539 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1540 # ifdef HAVE_N_VA_COPY
1541 va_end(vac);
1542 # else
1543 # undef vac
1544 # endif
1545 if(i <= 0)
1546 goto jleave;
1547 if(UICMP(z, i, >=, imax)){
1548 # ifdef HAVE_N_VA_COPY
1549 /* XXX Check overflow for upcoming LEN+++i! */
1550 n_string_trunc(&enp->ae_str, len);
1551 continue;
1552 # else
1553 i = (int)strlen(&enp->ae_str.s_dat[len]);
1554 # endif
1556 break;
1558 n_string_trunc(&enp->ae_str, len + (size_t)i);
1560 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1562 #endif /* HAVE_ERRORS */
1564 jleave:
1565 fflush(n_stderr);
1566 NYD2_LEAVE;
1569 FL void
1570 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1571 va_list ap;
1572 NYD_X;
1574 va_start(ap, format);
1575 vfprintf(n_stderr, format, ap);
1576 va_end(ap);
1577 fflush(n_stderr);
1580 FL void
1581 n_perr(char const *msg, int errval){
1582 int e;
1583 char const *fmt;
1584 NYD2_ENTER;
1586 if(msg == NULL){
1587 fmt = "%s%s\n";
1588 msg = n_empty;
1589 }else
1590 fmt = "%s: %s\n";
1592 e = (errval == 0) ? n_err_no : errval;
1593 n_err(fmt, msg, n_err_to_doc(e));
1594 if(errval == 0)
1595 n_err_no = e;
1596 NYD2_LEAVE;
1599 FL void
1600 n_alert(char const *format, ...){
1601 va_list ap;
1602 NYD2_ENTER;
1604 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1606 va_start(ap, format);
1607 n_verr(format, ap);
1608 va_end(ap);
1610 n_err("\n");
1611 NYD2_LEAVE;
1614 FL void
1615 n_panic(char const *format, ...){
1616 va_list ap;
1617 NYD2_ENTER;
1619 if(a_aux_err_linelen > 0){
1620 putc('\n', n_stderr);
1621 a_aux_err_linelen = 0;
1623 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1625 va_start(ap, format);
1626 vfprintf(n_stderr, format, ap);
1627 va_end(ap);
1629 putc('\n', n_stderr);
1630 fflush(n_stderr);
1631 NYD2_LEAVE;
1632 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1635 #ifdef HAVE_ERRORS
1636 FL int
1637 c_errors(void *v){
1638 char **argv = v;
1639 struct a_aux_err_node *enp;
1640 NYD_ENTER;
1642 if(*argv == NULL)
1643 goto jlist;
1644 if(argv[1] != NULL)
1645 goto jerr;
1646 if(!asccasecmp(*argv, "show"))
1647 goto jlist;
1648 if(!asccasecmp(*argv, "clear"))
1649 goto jclear;
1650 jerr:
1651 fprintf(n_stderr,
1652 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1653 v = NULL;
1654 jleave:
1655 NYD_LEAVE;
1656 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1658 jlist:{
1659 FILE *fp;
1660 size_t i;
1662 if(a_aux_err_head == NULL){
1663 fprintf(n_stderr, _("The error ring is empty\n"));
1664 goto jleave;
1667 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1668 NULL){
1669 fprintf(n_stderr, _("tmpfile"));
1670 v = NULL;
1671 goto jleave;
1674 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1675 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1676 /* We don't know whether last string ended with NL; be simple XXX */
1677 putc('\n', fp);
1679 page_or_print(fp, 0);
1680 Fclose(fp);
1682 /* FALLTHRU */
1684 jclear:
1685 a_aux_err_tail = NULL;
1686 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1687 a_aux_err_linelen = 0;
1688 while((enp = a_aux_err_head) != NULL){
1689 a_aux_err_head = enp->ae_next;
1690 n_string_gut(&enp->ae_str);
1691 free(enp);
1693 goto jleave;
1695 #endif /* HAVE_ERRORS */
1697 FL char const *
1698 n_err_to_doc(si32_t eno){
1699 char const *rv;
1700 struct a_aux_err_map const *aemp;
1701 NYD2_ENTER;
1703 aemp = a_aux_err_map_from_no(eno);
1704 rv = &a_aux_err_docs[aemp->aem_docoff];
1705 NYD2_LEAVE;
1706 return rv;
1709 FL char const *
1710 n_err_to_name(si32_t eno){
1711 char const *rv;
1712 struct a_aux_err_map const *aemp;
1713 NYD2_ENTER;
1715 aemp = a_aux_err_map_from_no(eno);
1716 rv = &a_aux_err_names[aemp->aem_nameoff];
1717 NYD2_LEAVE;
1718 return rv;
1721 FL si32_t
1722 n_err_from_name(char const *name){
1723 struct a_aux_err_map const *aemp;
1724 ui32_t hash, i, j, x;
1725 si32_t rv;
1726 NYD2_ENTER;
1728 hash = n_torek_hash(name);
1730 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1731 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1732 break;
1734 aemp = &a_aux_err_map[x];
1735 if(aemp->aem_hash == hash &&
1736 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1737 rv = aemp->aem_err_no;
1738 goto jleave;
1741 if(++i == a_AUX_ERR_REV_PRIME){
1742 #ifdef a_AUX_ERR_REV_WRAPAROUND
1743 i = 0;
1744 #else
1745 break;
1746 #endif
1750 /* Have not found it. But wait, it could be that the user did, e.g.,
1751 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1752 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1753 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1754 ) == n_IDEC_STATE_CONSUMED){
1755 aemp = a_aux_err_map_from_no(rv);
1756 rv = aemp->aem_err_no;
1757 goto jleave;
1760 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1761 jleave:
1762 NYD2_LEAVE;
1763 return rv;
1766 #ifdef HAVE_REGEX
1767 FL char const *
1768 n_regex_err_to_doc(const regex_t *rep, int e){
1769 char *cp;
1770 size_t i;
1771 NYD2_ENTER;
1773 i = regerror(e, rep, NULL, 0) +1;
1774 cp = salloc(i);
1775 regerror(e, rep, cp, i);
1776 NYD2_LEAVE;
1777 return cp;
1779 #endif
1781 /* s-it-mode */