FIX maildir "yet-exists" hashmap!..
[s-mailx.git] / auxlily.c
blob432534696dd605ea734f418275a0d52de748b8cb
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 - 2017 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 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 SINLINE 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 SINLINE 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 /* Avoid logging if user set that via -S! */
348 n_PS_ROOT_BLOCK(ok_vset(ttycharset, cp));
350 # endif
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
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;
633 /* Character must be valid for base */
634 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
635 if(currc >= base)
636 goto jeinval;
637 }else{
638 /* 0 always valid as is, fallback base 10 */
639 if(*++cbuf == '\0' || --clen == 0)
640 goto jleave;
642 /* Base "detection" */
643 if(base == 0 || base == 2 || base == 16){
644 switch(*cbuf){
645 case 'x':
646 case 'X':
647 if((base & 2) == 0){
648 base = 0x10;
649 goto jprefix_skip;
651 break;
652 case 'b':
653 case 'B':
654 if((base & 16) == 0){
655 base = 2; /* 0b10 */
656 /* Char after prefix must be valid. However, after some error
657 * in the tor software all libraries (which had to) turned to
658 * an interpretation of the C standard which says that the
659 * prefix may optionally precede an otherwise valid sequence,
660 * which means that "0x" is not a STATE_INVAL error but gives
661 * a "0" result with a "STATE_BASE" error and a rest of "x" */
662 jprefix_skip:
663 #if 1
664 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base){
665 --clen;
666 ++cbuf;
668 #else
669 if(*++cbuf == '\0' || --clen == 0)
670 goto jeinval;
672 /* Character must be valid for base, invalid otherwise */
673 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
674 if(currc >= base)
675 goto jeinval;
676 #endif
678 break;
679 default:
680 if(base == 0)
681 base = 010;
682 break;
686 /* Character must be valid for base, _EBASE otherwise */
687 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
688 if(currc >= base)
689 goto jebase;
692 for(cut = a_aux_idec_cutlimit[base - 2];;){
693 if(res >= cut){
694 if(res == cut){
695 res *= base;
696 if(res > UI64_MAX - currc)
697 goto jeover;
698 res += currc;
699 }else
700 goto jeover;
701 }else{
702 res *= base;
703 res += currc;
706 if(*++cbuf == '\0' || --clen == 0)
707 break;
709 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
710 if(currc >= base)
711 goto jebase;
714 jleave:
716 ui64_t uimask;
718 switch(rv & n__IDEC_MODE_LIMIT_MASK){
719 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
720 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
721 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
722 default: uimask = UI64_MAX; break;
724 if(rv & n_IDEC_MODE_SIGNED_TYPE)
725 uimask >>= 1;
727 if(res & ~uimask){
728 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
729 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
730 if(res > uimask + 1){
731 res = uimask << 1;
732 res &= ~uimask;
733 }else{
734 res = -res;
735 break;
737 }else
738 res = uimask;
739 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
740 rv |= n_IDEC_STATE_EOVERFLOW;
741 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
742 res = -res;
743 }while(0);
745 switch(rv & n__IDEC_MODE_LIMIT_MASK){
746 case n_IDEC_MODE_LIMIT_8BIT:
747 if(rv & n_IDEC_MODE_SIGNED_TYPE)
748 *(si8_t*)resp = (si8_t)res;
749 else
750 *(ui8_t*)resp = (ui8_t)res;
751 break;
752 case n_IDEC_MODE_LIMIT_16BIT:
753 if(rv & n_IDEC_MODE_SIGNED_TYPE)
754 *(si16_t*)resp = (si16_t)res;
755 else
756 *(ui16_t*)resp = (ui16_t)res;
757 break;
758 case n_IDEC_MODE_LIMIT_32BIT:
759 if(rv & n_IDEC_MODE_SIGNED_TYPE)
760 *(si32_t*)resp = (si32_t)res;
761 else
762 *(ui32_t*)resp = (ui32_t)res;
763 break;
764 default:
765 if(rv & n_IDEC_MODE_SIGNED_TYPE)
766 *(si64_t*)resp = (si64_t)res;
767 else
768 *(ui64_t*)resp = (ui64_t)res;
769 break;
772 if(endptr_or_null != NULL)
773 *endptr_or_null = cbuf;
774 if(*cbuf == '\0' || clen == 0)
775 rv |= n_IDEC_STATE_CONSUMED;
776 NYD_LEAVE;
777 return rv;
779 jeinval:
780 rv |= n_IDEC_STATE_EINVAL;
781 goto j_maxval;
782 jebase:
783 /* Not a base error for terminator and whitespace! */
784 if(*cbuf != '\0' && !spacechar(*cbuf))
785 rv |= n_IDEC_STATE_EBASE;
786 goto jleave;
788 jeover:
789 /* Overflow error: consume input until bad character or length out */
790 for(;;){
791 if(*++cbuf == '\0' || --clen == 0)
792 break;
793 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
794 if(currc >= base)
795 break;
798 rv |= n_IDEC_STATE_EOVERFLOW;
799 j_maxval:
800 if(rv & n_IDEC_MODE_SIGNED_TYPE)
801 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
802 : (ui64_t)SI64_MAX;
803 else
804 res = UI64_MAX;
805 rv &= ~n_IDEC_STATE_SEEN_MINUS;
806 goto jleave;
809 FL ui32_t
810 n_torek_hash(char const *name){
811 /* Chris Torek's hash */
812 char c;
813 ui32_t h;
814 NYD2_ENTER;
816 for(h = 0; (c = *name++) != '\0';)
817 h = (h * 33) + c;
818 NYD2_LEAVE;
819 return h;
822 FL ui32_t
823 n_torek_ihashn(char const *dat, size_t len){
824 /* See n_torek_hash() */
825 char c;
826 ui32_t h;
827 NYD2_ENTER;
829 if(len == UIZ_MAX)
830 for(h = 0; (c = *dat++) != '\0';)
831 h = (h * 33) + lowerconv(c);
832 else
833 for(h = 0; len > 0; --len){
834 c = *dat++;
835 h = (h * 33) + lowerconv(c);
837 NYD2_LEAVE;
838 return h;
841 FL ui32_t
842 n_prime_next(ui32_t n){
843 static ui32_t const primes[] = {
844 5, 11, 23, 47, 97, 157, 283,
845 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
846 131071, 262139, 524287, 1048573, 2097143, 4194301,
847 8388593, 16777213, 33554393, 67108859, 134217689,
848 268435399, 536870909, 1073741789, 2147483647
850 ui32_t i, mprime;
851 NYD2_ENTER;
853 i = (n < primes[n_NELEM(primes) / 4] ? 0
854 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
855 : n_NELEM(primes) / 2));
857 do if((mprime = primes[i]) > n)
858 break;
859 while(++i < n_NELEM(primes));
861 if(i == n_NELEM(primes) && mprime < n)
862 mprime = n;
863 NYD2_LEAVE;
864 return mprime;
867 FL char const *
868 n_getdeadletter(void){
869 char const *cp;
870 bool_t bla;
871 NYD_ENTER;
873 bla = FAL0;
874 jredo:
875 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
876 if(cp == NULL || strlen(cp) >= PATH_MAX){
877 if(!bla){
878 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
879 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
880 ok_vclear(DEAD);
881 bla = TRU1;
882 goto jredo;
883 }else{
884 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
885 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
888 NYD_LEAVE;
889 return cp;
892 FL char *
893 n_nodename(bool_t mayoverride){
894 static char *sys_hostname, *hostname; /* XXX free-at-exit */
896 struct utsname ut;
897 char *hn;
898 #ifdef HAVE_SOCKETS
899 # ifdef HAVE_GETADDRINFO
900 struct addrinfo hints, *res;
901 # else
902 struct hostent *hent;
903 # endif
904 #endif
905 NYD2_ENTER;
907 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
909 }else if((hn = sys_hostname) == NULL){
910 uname(&ut);
911 hn = ut.nodename;
912 #ifdef HAVE_SOCKETS
913 # ifdef HAVE_GETADDRINFO
914 memset(&hints, 0, sizeof hints);
915 hints.ai_family = AF_UNSPEC;
916 hints.ai_flags = AI_CANONNAME;
917 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
918 if(res->ai_canonname != NULL){
919 size_t l;
921 l = strlen(res->ai_canonname) +1;
922 hn = n_lofi_alloc(l);
923 memcpy(hn, res->ai_canonname, l);
925 freeaddrinfo(res);
927 # else
928 hent = gethostbyname(hn);
929 if(hent != NULL)
930 hn = hent->h_name;
931 # endif
932 #endif
933 sys_hostname = sstrdup(hn);
934 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
935 if(hn != ut.nodename)
936 n_lofi_free(hn);
937 #endif
938 hn = sys_hostname;
941 if(hostname != NULL && hostname != sys_hostname)
942 n_free(hostname);
943 hostname = sstrdup(hn);
944 NYD2_LEAVE;
945 return hostname;
948 #ifdef HAVE_IDNA
949 FL bool_t
950 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
951 char *idna_utf8;
952 bool_t rv;
953 NYD_ENTER;
955 if((rv = (ilen == 0)))
956 goto jleave;
958 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
959 ibuf = savestrbuf(ibuf, ilen);
960 ilen = 0;
962 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
963 ibuf);
964 if(idna_utf8 == NULL)
965 goto jleave;
967 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
968 /* C99 */{
969 char *idna_ascii;
971 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
972 out = n_string_assign_cp(out, idna_ascii);
973 idn_free(idna_ascii);
974 rv = TRU1;
975 ilen = out->s_len;
978 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
979 ilen = strlen(idna_utf8);
980 jredo:
981 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
982 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
983 case idn_buffer_overflow:
984 ilen += HOST_NAME_MAX +1;
985 goto jredo;
986 case idn_success:
987 rv = TRU1;
988 ilen = strlen(out->s_dat);
989 break;
990 default:
991 ilen = 0;
992 break;
995 # else
996 # error Unknown HAVE_IDNA
997 # endif
998 jleave:
999 out = n_string_trunc(out, ilen);
1000 NYD_LEAVE;
1001 return rv;
1003 #endif /* HAVE_IDNA */
1005 FL char *
1006 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1007 struct str b64;
1008 char *indat, *cp, *oudat;
1009 size_t i, inlen, oulen;
1010 NYD_ENTER;
1012 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1013 n_psonce |= n_PSO_RANDOM_INIT;
1015 if(n_poption & n_PO_D_V){
1016 char const *prngn;
1018 #if n_RANDOM_USE_XSSL
1019 prngn = "*SSL RAND_*";
1020 #elif defined HAVE_POSIX_RANDOM
1021 prngn = "POSIX/arc4random";
1022 #else
1023 prngn = "builtin ARC4";
1024 #endif
1025 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1028 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1029 a_aux_rand_init();
1030 #endif
1033 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1034 * with PAD stripped is still longer than what the user requests, easy way.
1035 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1036 * include the base64 PAD characters in our random string: give some pad */
1037 i = len;
1038 if((inlen = i % 3) != 0)
1039 i += 3 - inlen;
1040 jinc1:
1041 inlen = i >> 2;
1042 oulen = inlen << 2;
1043 if(oulen < len){
1044 i += 3;
1045 goto jinc1;
1047 inlen = inlen + (inlen << 1);
1049 indat = n_lofi_alloc(inlen +1);
1051 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1052 #if n_RANDOM_USE_XSSL
1053 ssl_rand_bytes(indat, inlen);
1054 #elif !defined HAVE_POSIX_RANDOM
1055 for(i = inlen; i-- > 0;)
1056 indat[i] = (char)a_aux_rand_get8();
1057 #else
1058 for(cp = indat, i = inlen; i > 0;){
1059 union {ui32_t i4; char c[4];} r;
1060 size_t j;
1062 r.i4 = (ui32_t)arc4random();
1063 switch((j = i & 3)){
1064 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1065 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1066 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1067 default: cp[0] = r.c[0]; break;
1069 cp += j;
1070 i -= j;
1072 #endif
1073 }else{
1074 for(cp = indat, i = inlen; i > 0;){
1075 union {ui32_t i4; char c[4];} r;
1076 size_t j;
1078 r.i4 = ++*reprocnt_or_null;
1079 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1080 char x;
1082 x = r.c[0];
1083 r.c[0] = r.c[3];
1084 r.c[3] = x;
1085 x = r.c[1];
1086 r.c[1] = r.c[2];
1087 r.c[2] = x;
1089 switch((j = i & 3)){
1090 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1091 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1092 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1093 default: cp[0] = r.c[0]; break;
1095 cp += j;
1096 i -= j;
1100 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1101 b64.s = oudat;
1102 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1103 assert(b64.l >= len);
1104 memcpy(dat, b64.s, len);
1105 dat[len] = '\0';
1106 if(oudat != dat)
1107 n_lofi_free(oudat);
1109 n_lofi_free(indat);
1111 NYD_LEAVE;
1112 return dat;
1115 FL char *
1116 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1117 char *dat;
1118 NYD_ENTER;
1120 dat = n_autorec_alloc(len +1);
1121 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1122 NYD_LEAVE;
1123 return dat;
1126 FL si8_t
1127 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1129 si8_t rv;
1130 NYD_ENTER;
1132 assert(inlen == 0 || inbuf != NULL);
1134 if (inlen == UIZ_MAX)
1135 inlen = strlen(inbuf);
1137 if (inlen == 0)
1138 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1139 else {
1140 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1141 !ascncasecmp(inbuf, "true", inlen) ||
1142 !ascncasecmp(inbuf, "yes", inlen) ||
1143 !ascncasecmp(inbuf, "on", inlen))
1144 rv = 1;
1145 else if ((inlen == 1 &&
1146 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1147 !ascncasecmp(inbuf, "false", inlen) ||
1148 !ascncasecmp(inbuf, "no", inlen) ||
1149 !ascncasecmp(inbuf, "off", inlen))
1150 rv = 0;
1151 else {
1152 ui64_t ib;
1154 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1155 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1156 ) != n_IDEC_STATE_CONSUMED)
1157 rv = -1;
1158 else
1159 rv = (ib != 0);
1162 NYD_LEAVE;
1163 return rv;
1166 FL si8_t
1167 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1169 si8_t rv;
1170 NYD_ENTER;
1172 assert(inlen == 0 || inbuf != NULL);
1174 if (inlen == UIZ_MAX)
1175 inlen = strlen(inbuf);
1177 if (inlen == 0)
1178 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1179 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1180 !ascncasecmp(inbuf, "ask-", 4) &&
1181 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1182 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1183 rv = getapproval(prompt, rv);
1184 NYD_LEAVE;
1185 return rv;
1188 FL bool_t
1189 n_is_all_or_aster(char const *name){
1190 bool_t rv;
1191 NYD_ENTER;
1193 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1194 NYD_LEAVE;
1195 return rv;
1198 FL struct n_timespec const *
1199 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1200 static struct n_timespec ts_now;
1201 NYD2_ENTER;
1203 if(n_psonce & n_PSO_REPRODUCIBLE){
1204 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1205 ts_now.ts_nsec = 0;
1206 }else if(force_update || ts_now.ts_sec == 0){
1207 #ifdef HAVE_CLOCK_GETTIME
1208 struct timespec ts;
1210 clock_gettime(CLOCK_REALTIME, &ts);
1211 ts_now.ts_sec = (si64_t)ts.tv_sec;
1212 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1213 #elif defined HAVE_GETTIMEOFDAY
1214 struct timeval tv;
1216 gettimeofday(&tv, NULL);
1217 ts_now.ts_sec = (si64_t)tv.tv_sec;
1218 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1219 #else
1220 ts_now.ts_sec = (si64_t)time(NULL);
1221 ts_now.ts_nsec = 0;
1222 #endif
1224 NYD2_LEAVE;
1225 return &ts_now;
1228 FL void
1229 time_current_update(struct time_current *tc, bool_t full_update)
1231 NYD_ENTER;
1232 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1233 if (full_update) {
1234 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1235 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1236 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1238 NYD_LEAVE;
1241 FL uiz_t
1242 n_msleep(uiz_t millis, bool_t ignint){
1243 uiz_t rv;
1244 NYD2_ENTER;
1246 #ifdef HAVE_NANOSLEEP
1247 /* C99 */{
1248 struct timespec ts, trem;
1249 int i;
1251 ts.tv_sec = millis / 1000;
1252 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1254 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1255 ts = trem;
1256 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1259 #elif defined HAVE_SLEEP
1260 if((millis /= 1000) == 0)
1261 millis = 1;
1262 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1263 millis = rv;
1264 #else
1265 # error Configuration should have detected a function for sleeping.
1266 #endif
1268 NYD2_LEAVE;
1269 return rv;
1272 FL void
1273 n_err(char const *format, ...){
1274 va_list ap;
1275 NYD2_ENTER;
1277 va_start(ap, format);
1278 #ifdef HAVE_ERRORS
1279 if(n_psonce & n_PSO_INTERACTIVE)
1280 n_verr(format, ap);
1281 else
1282 #endif
1284 size_t len;
1285 bool_t doname;
1287 doname = FAL0;
1289 while(*format == '\n'){
1290 doname = TRU1;
1291 putc('\n', n_stderr);
1292 ++format;
1295 if(doname)
1296 a_aux_err_linelen = 0;
1298 if((len = strlen(format)) > 0){
1299 if(doname || a_aux_err_linelen == 0){
1300 char const *cp;
1302 if(*(cp = ok_vlook(log_prefix)) != '\0')
1303 fputs(cp, n_stderr);
1305 vfprintf(n_stderr, format, ap);
1307 /* C99 */{
1308 size_t i = len;
1310 if(format[--len] == '\n'){
1311 a_aux_err_linelen = (i -= ++len);
1312 break;
1314 ++a_aux_err_linelen;
1315 }while(len > 0);
1319 fflush(n_stderr);
1321 va_end(ap);
1322 NYD2_LEAVE;
1325 FL void
1326 n_verr(char const *format, va_list ap){
1327 #ifdef HAVE_ERRORS
1328 struct a_aux_err_node *enp;
1329 #endif
1330 bool_t doname;
1331 size_t len;
1332 NYD2_ENTER;
1334 doname = FAL0;
1336 while(*format == '\n'){
1337 putc('\n', n_stderr);
1338 doname = TRU1;
1339 ++format;
1342 if(doname){
1343 a_aux_err_linelen = 0;
1344 #ifdef HAVE_ERRORS
1345 if(n_psonce & n_PSO_INTERACTIVE){
1346 if((enp = a_aux_err_tail) != NULL &&
1347 (enp->ae_str.s_len > 0 &&
1348 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1349 n_string_push_c(&enp->ae_str, '\n');
1351 #endif
1354 if((len = strlen(format)) == 0)
1355 goto jleave;
1356 #ifdef HAVE_ERRORS
1357 n_pstate |= n_PS_ERRORS_PROMPT;
1358 #endif
1360 if(doname || a_aux_err_linelen == 0){
1361 char const *cp;
1363 if(*(cp = ok_vlook(log_prefix)) != '\0')
1364 fputs(cp, n_stderr);
1367 /* C99 */{
1368 size_t i = len;
1370 if(format[--len] == '\n'){
1371 a_aux_err_linelen = (i -= ++len);
1372 break;
1374 ++a_aux_err_linelen;
1375 }while(len > 0);
1378 #ifdef HAVE_ERRORS
1379 if(!(n_psonce & n_PSO_INTERACTIVE))
1380 #endif
1381 vfprintf(n_stderr, format, ap);
1382 #ifdef HAVE_ERRORS
1383 else{
1384 int imax, i;
1385 n_LCTAV(ERRORS_MAX > 3);
1387 /* Link it into the `errors' message ring */
1388 if((enp = a_aux_err_tail) == NULL){
1389 jcreat:
1390 enp = smalloc(sizeof *enp);
1391 enp->ae_next = NULL;
1392 n_string_creat(&enp->ae_str);
1393 if(a_aux_err_tail != NULL)
1394 a_aux_err_tail->ae_next = enp;
1395 else
1396 a_aux_err_head = enp;
1397 a_aux_err_tail = enp;
1398 ++a_aux_err_cnt;
1399 }else if(doname ||
1400 (enp->ae_str.s_len > 0 &&
1401 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1402 if(a_aux_err_cnt < ERRORS_MAX)
1403 goto jcreat;
1405 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1406 a_aux_err_tail->ae_next = enp;
1407 a_aux_err_tail = enp;
1408 enp->ae_next = NULL;
1409 n_string_trunc(&enp->ae_str, 0);
1412 # ifdef HAVE_N_VA_COPY
1413 imax = 64;
1414 # else
1415 imax = n_MIN(LINESIZE, 1024);
1416 # endif
1417 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1418 # ifdef HAVE_N_VA_COPY
1419 va_list vac;
1421 n_va_copy(vac, ap);
1422 # else
1423 # define vac ap
1424 # endif
1426 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1427 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1428 # ifdef HAVE_N_VA_COPY
1429 va_end(vac);
1430 # else
1431 # undef vac
1432 # endif
1433 if(i <= 0)
1434 goto jleave;
1435 if(UICMP(z, i, >=, imax)){
1436 # ifdef HAVE_N_VA_COPY
1437 /* XXX Check overflow for upcoming LEN+++i! */
1438 n_string_trunc(&enp->ae_str, len);
1439 continue;
1440 # else
1441 i = (int)strlen(&enp->ae_str.s_dat[len]);
1442 # endif
1444 break;
1446 n_string_trunc(&enp->ae_str, len + (size_t)i);
1448 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1450 #endif /* HAVE_ERRORS */
1452 jleave:
1453 fflush(n_stderr);
1454 NYD2_LEAVE;
1457 FL void
1458 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1459 va_list ap;
1460 NYD_X;
1462 va_start(ap, format);
1463 vfprintf(n_stderr, format, ap);
1464 va_end(ap);
1465 fflush(n_stderr);
1468 FL void
1469 n_perr(char const *msg, int errval){
1470 int e;
1471 char const *fmt;
1472 NYD2_ENTER;
1474 if(msg == NULL){
1475 fmt = "%s%s\n";
1476 msg = n_empty;
1477 }else
1478 fmt = "%s: %s\n";
1480 e = (errval == 0) ? n_err_no : errval;
1481 n_err(fmt, msg, n_err_to_doc(e));
1482 if(errval == 0)
1483 n_err_no = e;
1484 NYD2_LEAVE;
1487 FL void
1488 n_alert(char const *format, ...){
1489 va_list ap;
1490 NYD2_ENTER;
1492 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1494 va_start(ap, format);
1495 n_verr(format, ap);
1496 va_end(ap);
1498 n_err("\n");
1499 NYD2_LEAVE;
1502 FL void
1503 n_panic(char const *format, ...){
1504 va_list ap;
1505 NYD2_ENTER;
1507 if(a_aux_err_linelen > 0){
1508 putc('\n', n_stderr);
1509 a_aux_err_linelen = 0;
1511 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1513 va_start(ap, format);
1514 vfprintf(n_stderr, format, ap);
1515 va_end(ap);
1517 putc('\n', n_stderr);
1518 fflush(n_stderr);
1519 NYD2_LEAVE;
1520 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1523 #ifdef HAVE_ERRORS
1524 FL int
1525 c_errors(void *v){
1526 char **argv = v;
1527 struct a_aux_err_node *enp;
1528 NYD_ENTER;
1530 if(*argv == NULL)
1531 goto jlist;
1532 if(argv[1] != NULL)
1533 goto jerr;
1534 if(!asccasecmp(*argv, "show"))
1535 goto jlist;
1536 if(!asccasecmp(*argv, "clear"))
1537 goto jclear;
1538 jerr:
1539 fprintf(n_stderr,
1540 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1541 v = NULL;
1542 jleave:
1543 NYD_LEAVE;
1544 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1546 jlist:{
1547 FILE *fp;
1548 size_t i;
1550 if(a_aux_err_head == NULL){
1551 fprintf(n_stderr, _("The error ring is empty\n"));
1552 goto jleave;
1555 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1556 NULL){
1557 fprintf(n_stderr, _("tmpfile"));
1558 v = NULL;
1559 goto jleave;
1562 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1563 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1564 /* We don't know whether last string ended with NL; be simple XXX */
1565 putc('\n', fp);
1567 page_or_print(fp, 0);
1568 Fclose(fp);
1570 /* FALLTHRU */
1572 jclear:
1573 a_aux_err_tail = NULL;
1574 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1575 a_aux_err_linelen = 0;
1576 while((enp = a_aux_err_head) != NULL){
1577 a_aux_err_head = enp->ae_next;
1578 n_string_gut(&enp->ae_str);
1579 free(enp);
1581 goto jleave;
1583 #endif /* HAVE_ERRORS */
1585 FL char const *
1586 n_err_to_doc(si32_t eno){
1587 char const *rv;
1588 struct a_aux_err_map const *aemp;
1589 NYD2_ENTER;
1591 aemp = a_aux_err_map_from_no(eno);
1592 rv = &a_aux_err_docs[aemp->aem_docoff];
1593 NYD2_LEAVE;
1594 return rv;
1597 FL char const *
1598 n_err_to_name(si32_t eno){
1599 char const *rv;
1600 struct a_aux_err_map const *aemp;
1601 NYD2_ENTER;
1603 aemp = a_aux_err_map_from_no(eno);
1604 rv = &a_aux_err_names[aemp->aem_nameoff];
1605 NYD2_LEAVE;
1606 return rv;
1609 FL si32_t
1610 n_err_from_name(char const *name){
1611 struct a_aux_err_map const *aemp;
1612 ui32_t hash, i, j, x;
1613 si32_t rv;
1614 NYD2_ENTER;
1616 hash = n_torek_hash(name);
1618 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1619 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1620 break;
1622 aemp = &a_aux_err_map[x];
1623 if(aemp->aem_hash == hash &&
1624 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1625 rv = aemp->aem_err_no;
1626 goto jleave;
1629 if(++i == a_AUX_ERR_REV_PRIME){
1630 #ifdef a_AUX_ERR_REV_WRAPAROUND
1631 i = 0;
1632 #else
1633 break;
1634 #endif
1638 /* Have not found it. But wait, it could be that the user did, e.g.,
1639 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1640 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1641 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1642 ) == n_IDEC_STATE_CONSUMED){
1643 aemp = a_aux_err_map_from_no(rv);
1644 rv = aemp->aem_err_no;
1645 goto jleave;
1648 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1649 jleave:
1650 NYD2_LEAVE;
1651 return rv;
1654 #ifdef HAVE_REGEX
1655 FL char const *
1656 n_regex_err_to_doc(const regex_t *rep, int e){
1657 char *cp;
1658 size_t i;
1659 NYD2_ENTER;
1661 i = regerror(e, rep, NULL, 0) +1;
1662 cp = salloc(i);
1663 regerror(e, rep, cp, i);
1664 NYD2_LEAVE;
1665 return cp;
1667 #endif
1669 /* s-it-mode */