mime_fromhdr(): normalize \0 and \n in encoded-words (mailspoof)
[s-mailx.git] / auxlily.c
blob7162aefe56d7b2401bb8fce057f3b7b63cc6590d
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_psonce & n_PSO_REPRODUCIBLE){
1234 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1235 ts_now.ts_nsec = 0;
1236 }else if(force_update || ts_now.ts_sec == 0){
1237 #ifdef HAVE_CLOCK_GETTIME
1238 struct timespec ts;
1240 clock_gettime(CLOCK_REALTIME, &ts);
1241 ts_now.ts_sec = (si64_t)ts.tv_sec;
1242 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1243 #elif defined HAVE_GETTIMEOFDAY
1244 struct timeval tv;
1246 gettimeofday(&tv, NULL);
1247 ts_now.ts_sec = (si64_t)tv.tv_sec;
1248 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1249 #else
1250 ts_now.ts_sec = (si64_t)time(NULL);
1251 ts_now.ts_nsec = 0;
1252 #endif
1254 NYD2_LEAVE;
1255 return &ts_now;
1258 FL void
1259 time_current_update(struct time_current *tc, bool_t full_update)
1261 NYD_ENTER;
1262 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1263 if (full_update) {
1264 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1265 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1266 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1268 NYD_LEAVE;
1271 FL uiz_t
1272 n_msleep(uiz_t millis, bool_t ignint){
1273 uiz_t rv;
1274 NYD2_ENTER;
1276 #ifdef HAVE_NANOSLEEP
1277 /* C99 */{
1278 struct timespec ts, trem;
1279 int i;
1281 ts.tv_sec = millis / 1000;
1282 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1284 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1285 ts = trem;
1286 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1289 #elif defined HAVE_SLEEP
1290 if((millis /= 1000) == 0)
1291 millis = 1;
1292 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1293 millis = rv;
1294 #else
1295 # error Configuration should have detected a function for sleeping.
1296 #endif
1298 NYD2_LEAVE;
1299 return rv;
1302 FL void
1303 n_err(char const *format, ...){
1304 va_list ap;
1305 NYD2_ENTER;
1307 va_start(ap, format);
1308 #ifdef HAVE_ERRORS
1309 if(n_psonce & n_PSO_INTERACTIVE)
1310 n_verr(format, ap);
1311 else
1312 #endif
1314 size_t len;
1315 bool_t doname;
1317 doname = FAL0;
1319 while(*format == '\n'){
1320 doname = TRU1;
1321 putc('\n', n_stderr);
1322 ++format;
1325 if(doname)
1326 a_aux_err_linelen = 0;
1328 if((len = strlen(format)) > 0){
1329 if(doname || a_aux_err_linelen == 0){
1330 char const *cp;
1332 if(*(cp = ok_vlook(log_prefix)) != '\0')
1333 fputs(cp, n_stderr);
1335 vfprintf(n_stderr, format, ap);
1337 /* C99 */{
1338 size_t i = len;
1340 if(format[--len] == '\n'){
1341 a_aux_err_linelen = (i -= ++len);
1342 break;
1344 ++a_aux_err_linelen;
1345 }while(len > 0);
1349 fflush(n_stderr);
1351 va_end(ap);
1352 NYD2_LEAVE;
1355 FL void
1356 n_verr(char const *format, va_list ap){
1357 #ifdef HAVE_ERRORS
1358 struct a_aux_err_node *enp;
1359 #endif
1360 bool_t doname;
1361 size_t len;
1362 NYD2_ENTER;
1364 doname = FAL0;
1366 while(*format == '\n'){
1367 putc('\n', n_stderr);
1368 doname = TRU1;
1369 ++format;
1372 if(doname){
1373 a_aux_err_linelen = 0;
1374 #ifdef HAVE_ERRORS
1375 if(n_psonce & n_PSO_INTERACTIVE){
1376 if((enp = a_aux_err_tail) != NULL &&
1377 (enp->ae_str.s_len > 0 &&
1378 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1379 n_string_push_c(&enp->ae_str, '\n');
1381 #endif
1384 if((len = strlen(format)) == 0)
1385 goto jleave;
1386 #ifdef HAVE_ERRORS
1387 n_pstate |= n_PS_ERRORS_PROMPT;
1388 #endif
1390 if(doname || a_aux_err_linelen == 0){
1391 char const *cp;
1393 if(*(cp = ok_vlook(log_prefix)) != '\0')
1394 fputs(cp, n_stderr);
1397 /* C99 */{
1398 size_t i = len;
1400 if(format[--len] == '\n'){
1401 a_aux_err_linelen = (i -= ++len);
1402 break;
1404 ++a_aux_err_linelen;
1405 }while(len > 0);
1408 #ifdef HAVE_ERRORS
1409 if(!(n_psonce & n_PSO_INTERACTIVE))
1410 #endif
1411 vfprintf(n_stderr, format, ap);
1412 #ifdef HAVE_ERRORS
1413 else{
1414 int imax, i;
1415 n_LCTAV(ERRORS_MAX > 3);
1417 /* Link it into the `errors' message ring */
1418 if((enp = a_aux_err_tail) == NULL){
1419 jcreat:
1420 enp = smalloc(sizeof *enp);
1421 enp->ae_next = NULL;
1422 n_string_creat(&enp->ae_str);
1423 if(a_aux_err_tail != NULL)
1424 a_aux_err_tail->ae_next = enp;
1425 else
1426 a_aux_err_head = enp;
1427 a_aux_err_tail = enp;
1428 ++a_aux_err_cnt;
1429 }else if(doname ||
1430 (enp->ae_str.s_len > 0 &&
1431 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1432 if(a_aux_err_cnt < ERRORS_MAX)
1433 goto jcreat;
1435 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1436 a_aux_err_tail->ae_next = enp;
1437 a_aux_err_tail = enp;
1438 enp->ae_next = NULL;
1439 n_string_trunc(&enp->ae_str, 0);
1442 # ifdef HAVE_N_VA_COPY
1443 imax = 64;
1444 # else
1445 imax = n_MIN(LINESIZE, 1024);
1446 # endif
1447 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1448 # ifdef HAVE_N_VA_COPY
1449 va_list vac;
1451 n_va_copy(vac, ap);
1452 # else
1453 # define vac ap
1454 # endif
1456 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1457 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1458 # ifdef HAVE_N_VA_COPY
1459 va_end(vac);
1460 # else
1461 # undef vac
1462 # endif
1463 if(i <= 0)
1464 goto jleave;
1465 if(UICMP(z, i, >=, imax)){
1466 # ifdef HAVE_N_VA_COPY
1467 /* XXX Check overflow for upcoming LEN+++i! */
1468 n_string_trunc(&enp->ae_str, len);
1469 continue;
1470 # else
1471 i = (int)strlen(&enp->ae_str.s_dat[len]);
1472 # endif
1474 break;
1476 n_string_trunc(&enp->ae_str, len + (size_t)i);
1478 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1480 #endif /* HAVE_ERRORS */
1482 jleave:
1483 fflush(n_stderr);
1484 NYD2_LEAVE;
1487 FL void
1488 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1489 va_list ap;
1490 NYD_X;
1492 va_start(ap, format);
1493 vfprintf(n_stderr, format, ap);
1494 va_end(ap);
1495 fflush(n_stderr);
1498 FL void
1499 n_perr(char const *msg, int errval){
1500 int e;
1501 char const *fmt;
1502 NYD2_ENTER;
1504 if(msg == NULL){
1505 fmt = "%s%s\n";
1506 msg = n_empty;
1507 }else
1508 fmt = "%s: %s\n";
1510 e = (errval == 0) ? n_err_no : errval;
1511 n_err(fmt, msg, n_err_to_doc(e));
1512 if(errval == 0)
1513 n_err_no = e;
1514 NYD2_LEAVE;
1517 FL void
1518 n_alert(char const *format, ...){
1519 va_list ap;
1520 NYD2_ENTER;
1522 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1524 va_start(ap, format);
1525 n_verr(format, ap);
1526 va_end(ap);
1528 n_err("\n");
1529 NYD2_LEAVE;
1532 FL void
1533 n_panic(char const *format, ...){
1534 va_list ap;
1535 NYD2_ENTER;
1537 if(a_aux_err_linelen > 0){
1538 putc('\n', n_stderr);
1539 a_aux_err_linelen = 0;
1541 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1543 va_start(ap, format);
1544 vfprintf(n_stderr, format, ap);
1545 va_end(ap);
1547 putc('\n', n_stderr);
1548 fflush(n_stderr);
1549 NYD2_LEAVE;
1550 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1553 #ifdef HAVE_ERRORS
1554 FL int
1555 c_errors(void *v){
1556 char **argv = v;
1557 struct a_aux_err_node *enp;
1558 NYD_ENTER;
1560 if(*argv == NULL)
1561 goto jlist;
1562 if(argv[1] != NULL)
1563 goto jerr;
1564 if(!asccasecmp(*argv, "show"))
1565 goto jlist;
1566 if(!asccasecmp(*argv, "clear"))
1567 goto jclear;
1568 jerr:
1569 fprintf(n_stderr,
1570 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1571 v = NULL;
1572 jleave:
1573 NYD_LEAVE;
1574 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1576 jlist:{
1577 FILE *fp;
1578 size_t i;
1580 if(a_aux_err_head == NULL){
1581 fprintf(n_stderr, _("The error ring is empty\n"));
1582 goto jleave;
1585 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1586 NULL){
1587 fprintf(n_stderr, _("tmpfile"));
1588 v = NULL;
1589 goto jleave;
1592 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1593 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1594 /* We don't know whether last string ended with NL; be simple XXX */
1595 putc('\n', fp);
1597 page_or_print(fp, 0);
1598 Fclose(fp);
1600 /* FALLTHRU */
1602 jclear:
1603 a_aux_err_tail = NULL;
1604 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1605 a_aux_err_linelen = 0;
1606 while((enp = a_aux_err_head) != NULL){
1607 a_aux_err_head = enp->ae_next;
1608 n_string_gut(&enp->ae_str);
1609 free(enp);
1611 goto jleave;
1613 #endif /* HAVE_ERRORS */
1615 FL char const *
1616 n_err_to_doc(si32_t eno){
1617 char const *rv;
1618 struct a_aux_err_map const *aemp;
1619 NYD2_ENTER;
1621 aemp = a_aux_err_map_from_no(eno);
1622 rv = &a_aux_err_docs[aemp->aem_docoff];
1623 NYD2_LEAVE;
1624 return rv;
1627 FL char const *
1628 n_err_to_name(si32_t eno){
1629 char const *rv;
1630 struct a_aux_err_map const *aemp;
1631 NYD2_ENTER;
1633 aemp = a_aux_err_map_from_no(eno);
1634 rv = &a_aux_err_names[aemp->aem_nameoff];
1635 NYD2_LEAVE;
1636 return rv;
1639 FL si32_t
1640 n_err_from_name(char const *name){
1641 struct a_aux_err_map const *aemp;
1642 ui32_t hash, i, j, x;
1643 si32_t rv;
1644 NYD2_ENTER;
1646 hash = n_torek_hash(name);
1648 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1649 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1650 break;
1652 aemp = &a_aux_err_map[x];
1653 if(aemp->aem_hash == hash &&
1654 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1655 rv = aemp->aem_err_no;
1656 goto jleave;
1659 if(++i == a_AUX_ERR_REV_PRIME){
1660 #ifdef a_AUX_ERR_REV_WRAPAROUND
1661 i = 0;
1662 #else
1663 break;
1664 #endif
1668 /* Have not found it. But wait, it could be that the user did, e.g.,
1669 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1670 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1671 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1672 ) == n_IDEC_STATE_CONSUMED){
1673 aemp = a_aux_err_map_from_no(rv);
1674 rv = aemp->aem_err_no;
1675 goto jleave;
1678 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1679 jleave:
1680 NYD2_LEAVE;
1681 return rv;
1684 #ifdef HAVE_REGEX
1685 FL char const *
1686 n_regex_err_to_doc(const regex_t *rep, int e){
1687 char *cp;
1688 size_t i;
1689 NYD2_ENTER;
1691 i = regerror(e, rep, NULL, 0) +1;
1692 cp = salloc(i);
1693 regerror(e, rep, cp, i);
1694 NYD2_LEAVE;
1695 return cp;
1697 #endif
1699 /* s-it-mode */