make-config.sh: accept CC=cc (Norman Ramsey)
[s-mailx.git] / auxlily.c
blob564f374975f2362d07faf674db253b132ea4bf8e
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 /* TODO *ttycharset* may be set several times during startup unless
347 * TODO we gain a mechanism that -S fixates a setting during startup,
348 * TODO effectively turning later adjustments (during startup) in noop */
349 if((cp = nl_langinfo(CODESET)) != NULL)
350 ok_vset(ttycharset, cp);
352 # endif
354 # ifdef HAVE_C90AMEND1
355 if(n_mb_cur_max > 1){
356 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
357 n_psonce |= n_PSO_UNICODE;
358 # else
359 wchar_t wc;
360 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
361 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
362 n_psonce |= n_PSO_UNICODE;
363 /* Reset possibly messed up state; luckily this also gives us an
364 * indication whether the encoding has locking shift state sequences */
365 if(mbtowc(&wc, NULL, n_mb_cur_max))
366 n_psonce |= n_PSO_ENC_MBSTATE;
367 # endif
369 # endif
370 #endif
371 NYD2_LEAVE;
374 FL size_t
375 n_screensize(void){
376 char const *cp;
377 uiz_t rv;
378 NYD2_ENTER;
380 if((cp = ok_vlook(screen)) != NULL){
381 n_idec_uiz_cp(&rv, cp, 0, NULL);
382 if(rv == 0)
383 rv = n_scrnheight;
384 }else
385 rv = n_scrnheight;
387 if(rv > 2)
388 rv -= 2;
389 NYD2_LEAVE;
390 return rv;
393 FL char const *
394 n_pager_get(char const **env_addon){
395 char const *rv;
396 NYD_ENTER;
398 rv = ok_vlook(PAGER);
400 if(env_addon != NULL){
401 *env_addon = NULL;
402 /* Update the manual upon any changes:
403 * *colour-pager*, $PAGER */
404 if(strstr(rv, "less") != NULL){
405 if(getenv("LESS") == NULL)
406 *env_addon = "LESS=RXi";
407 }else if(strstr(rv, "lv") != NULL){
408 if(getenv("LV") == NULL)
409 *env_addon = "LV=-c";
412 NYD_LEAVE;
413 return rv;
416 FL void
417 page_or_print(FILE *fp, size_t lines)
419 int c;
420 char const *cp;
421 NYD_ENTER;
423 fflush_rewind(fp);
425 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
426 size_t rows;
428 if(*cp == '\0')
429 rows = (size_t)n_scrnheight;
430 else
431 n_idec_uiz_cp(&rows, cp, 0, NULL);
433 if (rows > 0 && lines == 0) {
434 while ((c = getc(fp)) != EOF)
435 if (c == '\n' && ++lines >= rows)
436 break;
437 really_rewind(fp);
440 if (lines >= rows) {
441 char const *env_add[2], *pager;
443 pager = n_pager_get(&env_add[0]);
444 env_add[1] = NULL;
445 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
446 env_add, NULL);
447 goto jleave;
451 while ((c = getc(fp)) != EOF)
452 putc(c, n_stdout);
453 jleave:
454 NYD_LEAVE;
457 FL enum protocol
458 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
459 char const **adjusted_or_null)
461 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
462 char const *cp, *orig_name;
463 enum protocol rv = PROTO_UNKNOWN;
464 NYD_ENTER;
466 if(name[0] == '%' && name[1] == ':')
467 name += 2;
468 orig_name = name;
470 for (cp = name; *cp && *cp != ':'; cp++)
471 if (!alnumchar(*cp))
472 goto jfile;
474 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
475 if(!strncmp(name, "file", sizeof("file") -1) ||
476 !strncmp(name, "mbox", sizeof("mbox") -1))
477 rv = PROTO_FILE;
478 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
479 rv = PROTO_MAILDIR;
480 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
481 #ifdef HAVE_POP3
482 rv = PROTO_POP3;
483 #else
484 n_err(_("No POP3 support compiled in\n"));
485 #endif
486 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
487 #if defined HAVE_POP3 && defined HAVE_SSL
488 rv = PROTO_POP3;
489 #else
490 n_err(_("No POP3S support compiled in\n"));
491 #endif
493 else if(!strncmp(name, "imap", sizeof("imap") -1)){
494 #ifdef HAVE_IMAP
495 rv = PROTO_IMAP;
496 #else
497 n_err(_("No IMAP support compiled in\n"));
498 #endif
499 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
500 #if defined HAVE_IMAP && defined HAVE_SSL
501 rv = PROTO_IMAP;
502 #else
503 n_err(_("No IMAPS support compiled in\n"));
504 #endif
506 orig_name = &cp[3];
507 goto jleave;
510 jfile:
511 rv = PROTO_FILE;
513 if(check_stat || try_hooks){
514 struct n_file_type ft;
515 struct stat stb;
516 char *np;
517 size_t sz;
519 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
520 memcpy(np, name, sz + 1);
522 if(!stat(name, &stb)){
523 if(S_ISDIR(stb.st_mode) &&
524 (memcpy(&np[sz], "/tmp", 5),
525 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
526 (memcpy(&np[sz], "/new", 5),
527 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
528 (memcpy(&np[sz], "/cur", 5),
529 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
530 rv = PROTO_MAILDIR;
531 }else if(try_hooks && n_filetype_trial(&ft, name))
532 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
533 else if((cp = ok_vlook(newfolders)) != NULL &&
534 !asccasecmp(cp, "maildir"))
535 rv = PROTO_MAILDIR;
537 n_lofi_free(np);
539 jleave:
540 if(adjusted_or_null != NULL)
541 *adjusted_or_null = orig_name;
542 NYD_LEAVE;
543 return rv;
546 FL char *
547 n_c_to_hex_base16(char store[3], char c){
548 static char const itoa16[] = "0123456789ABCDEF";
549 NYD2_ENTER;
551 store[2] = '\0';
552 store[1] = itoa16[(ui8_t)c & 0x0F];
553 c = ((ui8_t)c >> 4) & 0x0F;
554 store[0] = itoa16[(ui8_t)c];
555 NYD2_LEAVE;
556 return store;
559 FL si32_t
560 n_c_from_hex_base16(char const hex[2]){
561 static ui8_t const atoi16[] = {
562 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
563 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
564 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
565 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
566 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
567 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
568 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
570 ui8_t i1, i2;
571 si32_t rv;
572 NYD2_ENTER;
574 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
575 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
576 goto jerr;
577 i1 = atoi16[i1];
578 i2 = atoi16[i2];
579 if ((i1 | i2) & 0xF0u)
580 goto jerr;
581 rv = i1;
582 rv <<= 4;
583 rv += i2;
584 jleave:
585 NYD2_LEAVE;
586 return rv;
587 jerr:
588 rv = -1;
589 goto jleave;
592 FL enum n_idec_state
593 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
594 enum n_idec_mode idm, char const **endptr_or_null){
595 /* XXX Brute simple and */
596 ui8_t currc;
597 ui64_t res, cut;
598 enum n_idec_state rv;
599 NYD_ENTER;
601 idm &= n__IDEC_MODE_MASK;
602 rv = n_IDEC_STATE_NONE | idm;
603 res = 0;
605 if(clen == UIZ_MAX){
606 if(*cbuf == '\0')
607 goto jeinval;
608 }else if(clen == 0)
609 goto jeinval;
611 assert(base != 1 && base <= 36);
612 /*if(base == 1 || base > 36)
613 * goto jeinval;*/
615 /* Leading WS */
616 while(spacechar(*cbuf))
617 if(*++cbuf == '\0' || --clen == 0)
618 goto jeinval;
620 /* Check sign */
621 switch(*cbuf){
622 case '-':
623 rv |= n_IDEC_STATE_SEEN_MINUS;
624 /* FALLTHROUGH */
625 case '+':
626 if(*++cbuf == '\0' || --clen == 0)
627 goto jeinval;
628 break;
631 /* Base detection/skip */
632 if(*cbuf != '0'){
633 if(base == 0)
634 base = 10;
635 /* Character must be valid for base */
636 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
637 if(currc >= base)
638 goto jeinval;
639 }else{
640 /* 0 always valid as is, fallback base 10 */
641 if(*++cbuf == '\0' || --clen == 0)
642 goto jleave;
644 /* Base "detection" */
645 if(base == 0 || base == 2 || base == 16){
646 switch(*cbuf){
647 case 'x':
648 case 'X':
649 if((base & 2) == 0){
650 base = 0x10;
651 goto jprefix_skip;
653 break;
654 case 'b':
655 case 'B':
656 if((base & 16) == 0){
657 base = 2; /* 0b10 */
658 /* Char after prefix must be valid. However, after some error
659 * in the tor software all libraries (which had to) turned to
660 * an interpretation of the C standard which says that the
661 * prefix may optionally precede an otherwise valid sequence,
662 * which means that "0x" is not a STATE_INVAL error but gives
663 * a "0" result with a "STATE_BASE" error and a rest of "x" */
664 jprefix_skip:
665 #if 1
666 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base){
667 --clen;
668 ++cbuf;
670 #else
671 if(*++cbuf == '\0' || --clen == 0)
672 goto jeinval;
674 /* Character must be valid for base, invalid otherwise */
675 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
676 if(currc >= base)
677 goto jeinval;
678 #endif
680 break;
681 default:
682 if(base == 0)
683 base = 010;
684 break;
688 /* Character must be valid for base, _EBASE otherwise */
689 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
690 if(currc >= base)
691 goto jebase;
694 for(cut = a_aux_idec_cutlimit[base - 2];;){
695 if(res >= cut){
696 if(res == cut){
697 res *= base;
698 if(res > UI64_MAX - currc)
699 goto jeover;
700 res += currc;
701 }else
702 goto jeover;
703 }else{
704 res *= base;
705 res += currc;
708 if(*++cbuf == '\0' || --clen == 0)
709 break;
711 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
712 if(currc >= base)
713 goto jebase;
716 jleave:
718 ui64_t uimask;
720 switch(rv & n__IDEC_MODE_LIMIT_MASK){
721 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
722 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
723 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
724 default: uimask = UI64_MAX; break;
726 if(rv & n_IDEC_MODE_SIGNED_TYPE)
727 uimask >>= 1;
729 if(res & ~uimask){
730 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
731 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
732 if(res > uimask + 1){
733 res = uimask << 1;
734 res &= ~uimask;
735 }else{
736 res = -res;
737 break;
739 }else
740 res = uimask;
741 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
742 rv |= n_IDEC_STATE_EOVERFLOW;
743 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
744 res = -res;
745 }while(0);
747 switch(rv & n__IDEC_MODE_LIMIT_MASK){
748 case n_IDEC_MODE_LIMIT_8BIT:
749 if(rv & n_IDEC_MODE_SIGNED_TYPE)
750 *(si8_t*)resp = (si8_t)res;
751 else
752 *(ui8_t*)resp = (ui8_t)res;
753 break;
754 case n_IDEC_MODE_LIMIT_16BIT:
755 if(rv & n_IDEC_MODE_SIGNED_TYPE)
756 *(si16_t*)resp = (si16_t)res;
757 else
758 *(ui16_t*)resp = (ui16_t)res;
759 break;
760 case n_IDEC_MODE_LIMIT_32BIT:
761 if(rv & n_IDEC_MODE_SIGNED_TYPE)
762 *(si32_t*)resp = (si32_t)res;
763 else
764 *(ui32_t*)resp = (ui32_t)res;
765 break;
766 default:
767 if(rv & n_IDEC_MODE_SIGNED_TYPE)
768 *(si64_t*)resp = (si64_t)res;
769 else
770 *(ui64_t*)resp = (ui64_t)res;
771 break;
774 if(endptr_or_null != NULL)
775 *endptr_or_null = cbuf;
776 if(*cbuf == '\0' || clen == 0)
777 rv |= n_IDEC_STATE_CONSUMED;
778 NYD_LEAVE;
779 return rv;
781 jeinval:
782 rv |= n_IDEC_STATE_EINVAL;
783 goto j_maxval;
784 jebase:
785 /* Not a base error for terminator and whitespace! */
786 if(*cbuf != '\0' && !spacechar(*cbuf))
787 rv |= n_IDEC_STATE_EBASE;
788 goto jleave;
790 jeover:
791 /* Overflow error: consume input until bad character or length out */
792 for(;;){
793 if(*++cbuf == '\0' || --clen == 0)
794 break;
795 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
796 if(currc >= base)
797 break;
800 rv |= n_IDEC_STATE_EOVERFLOW;
801 j_maxval:
802 if(rv & n_IDEC_MODE_SIGNED_TYPE)
803 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
804 : (ui64_t)SI64_MAX;
805 else
806 res = UI64_MAX;
807 rv &= ~n_IDEC_STATE_SEEN_MINUS;
808 goto jleave;
811 FL ui32_t
812 n_torek_hash(char const *name){
813 /* Chris Torek's hash */
814 char c;
815 ui32_t h;
816 NYD2_ENTER;
818 for(h = 0; (c = *name++) != '\0';)
819 h = (h * 33) + c;
820 NYD2_LEAVE;
821 return h;
824 FL ui32_t
825 n_torek_ihashn(char const *dat, size_t len){
826 /* See n_torek_hash() */
827 char c;
828 ui32_t h;
829 NYD2_ENTER;
831 if(len == UIZ_MAX)
832 for(h = 0; (c = *dat++) != '\0';)
833 h = (h * 33) + lowerconv(c);
834 else
835 for(h = 0; len > 0; --len){
836 c = *dat++;
837 h = (h * 33) + lowerconv(c);
839 NYD2_LEAVE;
840 return h;
843 FL ui32_t
844 n_prime_next(ui32_t n){
845 static ui32_t const primes[] = {
846 5, 11, 23, 47, 97, 157, 283,
847 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
848 131071, 262139, 524287, 1048573, 2097143, 4194301,
849 8388593, 16777213, 33554393, 67108859, 134217689,
850 268435399, 536870909, 1073741789, 2147483647
852 ui32_t i, mprime;
853 NYD2_ENTER;
855 i = (n < primes[n_NELEM(primes) / 4] ? 0
856 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
857 : n_NELEM(primes) / 2));
859 do if((mprime = primes[i]) > n)
860 break;
861 while(++i < n_NELEM(primes));
863 if(i == n_NELEM(primes) && mprime < n)
864 mprime = n;
865 NYD2_LEAVE;
866 return mprime;
869 FL char const *
870 n_getdeadletter(void){
871 char const *cp;
872 bool_t bla;
873 NYD_ENTER;
875 bla = FAL0;
876 jredo:
877 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
878 if(cp == NULL || strlen(cp) >= PATH_MAX){
879 if(!bla){
880 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
881 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
882 ok_vclear(DEAD);
883 bla = TRU1;
884 goto jredo;
885 }else{
886 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
887 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
890 NYD_LEAVE;
891 return cp;
894 FL char *
895 n_nodename(bool_t mayoverride){
896 static char *sys_hostname, *hostname; /* XXX free-at-exit */
898 struct utsname ut;
899 char *hn;
900 #ifdef HAVE_SOCKETS
901 # ifdef HAVE_GETADDRINFO
902 struct addrinfo hints, *res;
903 # else
904 struct hostent *hent;
905 # endif
906 #endif
907 NYD2_ENTER;
909 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
911 }else if((hn = sys_hostname) == NULL){
912 uname(&ut);
913 hn = ut.nodename;
914 #ifdef HAVE_SOCKETS
915 # ifdef HAVE_GETADDRINFO
916 memset(&hints, 0, sizeof hints);
917 hints.ai_family = AF_UNSPEC;
918 hints.ai_flags = AI_CANONNAME;
919 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
920 if(res->ai_canonname != NULL){
921 size_t l;
923 l = strlen(res->ai_canonname) +1;
924 hn = n_lofi_alloc(l);
925 memcpy(hn, res->ai_canonname, l);
927 freeaddrinfo(res);
929 # else
930 hent = gethostbyname(hn);
931 if(hent != NULL)
932 hn = hent->h_name;
933 # endif
934 #endif
935 sys_hostname = sstrdup(hn);
936 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
937 if(hn != ut.nodename)
938 n_lofi_free(hn);
939 #endif
940 hn = sys_hostname;
943 if(hostname != NULL && hostname != sys_hostname)
944 n_free(hostname);
945 hostname = sstrdup(hn);
946 NYD2_LEAVE;
947 return hostname;
950 #ifdef HAVE_IDNA
951 FL bool_t
952 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
953 char *idna_utf8;
954 bool_t rv;
955 NYD_ENTER;
957 if((rv = (ilen == 0)))
958 goto jleave;
960 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
961 ibuf = savestrbuf(ibuf, ilen);
962 ilen = 0;
964 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
965 ibuf);
966 if(idna_utf8 == NULL)
967 goto jleave;
969 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
970 /* C99 */{
971 char *idna_ascii;
973 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
974 out = n_string_assign_cp(out, idna_ascii);
975 idn_free(idna_ascii);
976 rv = TRU1;
977 ilen = out->s_len;
980 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
981 ilen = strlen(idna_utf8);
982 jredo:
983 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
984 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
985 case idn_buffer_overflow:
986 ilen += HOST_NAME_MAX +1;
987 goto jredo;
988 case idn_success:
989 rv = TRU1;
990 ilen = strlen(out->s_dat);
991 break;
992 default:
993 ilen = 0;
994 break;
997 # else
998 # error Unknown HAVE_IDNA
999 # endif
1000 jleave:
1001 out = n_string_trunc(out, ilen);
1002 NYD_LEAVE;
1003 return rv;
1005 #endif /* HAVE_IDNA */
1007 FL char *
1008 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1009 struct str b64;
1010 char *indat, *cp, *oudat;
1011 size_t i, inlen, oulen;
1012 NYD_ENTER;
1014 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1015 n_psonce |= n_PSO_RANDOM_INIT;
1017 if(n_poption & n_PO_D_V){
1018 char const *prngn;
1020 #if n_RANDOM_USE_XSSL
1021 prngn = "*SSL RAND_*";
1022 #elif defined HAVE_POSIX_RANDOM
1023 prngn = "POSIX/arc4random";
1024 #else
1025 prngn = "builtin ARC4";
1026 #endif
1027 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn);
1030 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1031 a_aux_rand_init();
1032 #endif
1035 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1036 * with PAD stripped is still longer than what the user requests, easy way.
1037 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1038 * include the base64 PAD characters in our random string: give some pad */
1039 i = len;
1040 if((inlen = i % 3) != 0)
1041 i += 3 - inlen;
1042 jinc1:
1043 inlen = i >> 2;
1044 oulen = inlen << 2;
1045 if(oulen < len){
1046 i += 3;
1047 goto jinc1;
1049 inlen = inlen + (inlen << 1);
1051 indat = n_lofi_alloc(inlen +1);
1053 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1054 #if n_RANDOM_USE_XSSL
1055 ssl_rand_bytes(indat, inlen);
1056 #elif !defined HAVE_POSIX_RANDOM
1057 for(i = inlen; i-- > 0;)
1058 indat[i] = (char)a_aux_rand_get8();
1059 #else
1060 for(cp = indat, i = inlen; i > 0;){
1061 union {ui32_t i4; char c[4];} r;
1062 size_t j;
1064 r.i4 = (ui32_t)arc4random();
1065 switch((j = i & 3)){
1066 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1067 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1068 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1069 default: cp[0] = r.c[0]; break;
1071 cp += j;
1072 i -= j;
1074 #endif
1075 }else{
1076 for(cp = indat, i = inlen; i > 0;){
1077 union {ui32_t i4; char c[4];} r;
1078 size_t j;
1080 r.i4 = ++*reprocnt_or_null;
1081 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1082 char x;
1084 x = r.c[0];
1085 r.c[0] = r.c[3];
1086 r.c[3] = x;
1087 x = r.c[1];
1088 r.c[1] = r.c[2];
1089 r.c[2] = x;
1091 switch((j = i & 3)){
1092 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1093 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1094 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1095 default: cp[0] = r.c[0]; break;
1097 cp += j;
1098 i -= j;
1102 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1103 b64.s = oudat;
1104 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1105 assert(b64.l >= len);
1106 memcpy(dat, b64.s, len);
1107 dat[len] = '\0';
1108 if(oudat != dat)
1109 n_lofi_free(oudat);
1111 n_lofi_free(indat);
1113 NYD_LEAVE;
1114 return dat;
1117 FL char *
1118 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1119 char *dat;
1120 NYD_ENTER;
1122 dat = n_autorec_alloc(len +1);
1123 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1124 NYD_LEAVE;
1125 return dat;
1128 FL si8_t
1129 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1131 si8_t rv;
1132 NYD_ENTER;
1134 assert(inlen == 0 || inbuf != NULL);
1136 if (inlen == UIZ_MAX)
1137 inlen = strlen(inbuf);
1139 if (inlen == 0)
1140 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1141 else {
1142 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1143 !ascncasecmp(inbuf, "true", inlen) ||
1144 !ascncasecmp(inbuf, "yes", inlen) ||
1145 !ascncasecmp(inbuf, "on", inlen))
1146 rv = 1;
1147 else if ((inlen == 1 &&
1148 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1149 !ascncasecmp(inbuf, "false", inlen) ||
1150 !ascncasecmp(inbuf, "no", inlen) ||
1151 !ascncasecmp(inbuf, "off", inlen))
1152 rv = 0;
1153 else {
1154 ui64_t ib;
1156 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1157 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1158 ) != n_IDEC_STATE_CONSUMED)
1159 rv = -1;
1160 else
1161 rv = (ib != 0);
1164 NYD_LEAVE;
1165 return rv;
1168 FL si8_t
1169 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1171 si8_t rv;
1172 NYD_ENTER;
1174 assert(inlen == 0 || inbuf != NULL);
1176 if (inlen == UIZ_MAX)
1177 inlen = strlen(inbuf);
1179 if (inlen == 0)
1180 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1181 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1182 !ascncasecmp(inbuf, "ask-", 4) &&
1183 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1184 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1185 rv = getapproval(prompt, rv);
1186 NYD_LEAVE;
1187 return rv;
1190 FL bool_t
1191 n_is_all_or_aster(char const *name){
1192 bool_t rv;
1193 NYD_ENTER;
1195 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1196 NYD_LEAVE;
1197 return rv;
1200 FL struct n_timespec const *
1201 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1202 static struct n_timespec ts_now;
1203 NYD2_ENTER;
1205 if(n_psonce & n_PSO_REPRODUCIBLE){
1206 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1207 ts_now.ts_nsec = 0;
1208 }else if(force_update || ts_now.ts_sec == 0){
1209 #ifdef HAVE_CLOCK_GETTIME
1210 struct timespec ts;
1212 clock_gettime(CLOCK_REALTIME, &ts);
1213 ts_now.ts_sec = (si64_t)ts.tv_sec;
1214 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1215 #elif defined HAVE_GETTIMEOFDAY
1216 struct timeval tv;
1218 gettimeofday(&tv, NULL);
1219 ts_now.ts_sec = (si64_t)tv.tv_sec;
1220 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1221 #else
1222 ts_now.ts_sec = (si64_t)time(NULL);
1223 ts_now.ts_nsec = 0;
1224 #endif
1226 NYD2_LEAVE;
1227 return &ts_now;
1230 FL void
1231 time_current_update(struct time_current *tc, bool_t full_update)
1233 NYD_ENTER;
1234 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1235 if (full_update) {
1236 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1237 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1238 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1240 NYD_LEAVE;
1243 FL uiz_t
1244 n_msleep(uiz_t millis, bool_t ignint){
1245 uiz_t rv;
1246 NYD2_ENTER;
1248 #ifdef HAVE_NANOSLEEP
1249 /* C99 */{
1250 struct timespec ts, trem;
1251 int i;
1253 ts.tv_sec = millis / 1000;
1254 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1256 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1257 ts = trem;
1258 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1261 #elif defined HAVE_SLEEP
1262 if((millis /= 1000) == 0)
1263 millis = 1;
1264 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1265 millis = rv;
1266 #else
1267 # error Configuration should have detected a function for sleeping.
1268 #endif
1270 NYD2_LEAVE;
1271 return rv;
1274 FL void
1275 n_err(char const *format, ...){
1276 va_list ap;
1277 NYD2_ENTER;
1279 va_start(ap, format);
1280 #ifdef HAVE_ERRORS
1281 if(n_psonce & n_PSO_INTERACTIVE)
1282 n_verr(format, ap);
1283 else
1284 #endif
1286 size_t len;
1287 bool_t doname;
1289 doname = FAL0;
1291 while(*format == '\n'){
1292 doname = TRU1;
1293 putc('\n', n_stderr);
1294 ++format;
1297 if(doname)
1298 a_aux_err_linelen = 0;
1300 if((len = strlen(format)) > 0){
1301 if(doname || a_aux_err_linelen == 0){
1302 char const *cp;
1304 if(*(cp = ok_vlook(log_prefix)) != '\0')
1305 fputs(cp, n_stderr);
1307 vfprintf(n_stderr, format, ap);
1309 /* C99 */{
1310 size_t i = len;
1312 if(format[--len] == '\n'){
1313 a_aux_err_linelen = (i -= ++len);
1314 break;
1316 ++a_aux_err_linelen;
1317 }while(len > 0);
1321 fflush(n_stderr);
1323 va_end(ap);
1324 NYD2_LEAVE;
1327 FL void
1328 n_verr(char const *format, va_list ap){
1329 #ifdef HAVE_ERRORS
1330 struct a_aux_err_node *enp;
1331 #endif
1332 bool_t doname;
1333 size_t len;
1334 NYD2_ENTER;
1336 doname = FAL0;
1338 while(*format == '\n'){
1339 putc('\n', n_stderr);
1340 doname = TRU1;
1341 ++format;
1344 if(doname){
1345 a_aux_err_linelen = 0;
1346 #ifdef HAVE_ERRORS
1347 if(n_psonce & n_PSO_INTERACTIVE){
1348 if((enp = a_aux_err_tail) != NULL &&
1349 (enp->ae_str.s_len > 0 &&
1350 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1351 n_string_push_c(&enp->ae_str, '\n');
1353 #endif
1356 if((len = strlen(format)) == 0)
1357 goto jleave;
1358 #ifdef HAVE_ERRORS
1359 n_pstate |= n_PS_ERRORS_PROMPT;
1360 #endif
1362 if(doname || a_aux_err_linelen == 0){
1363 char const *cp;
1365 if(*(cp = ok_vlook(log_prefix)) != '\0')
1366 fputs(cp, n_stderr);
1369 /* C99 */{
1370 size_t i = len;
1372 if(format[--len] == '\n'){
1373 a_aux_err_linelen = (i -= ++len);
1374 break;
1376 ++a_aux_err_linelen;
1377 }while(len > 0);
1380 #ifdef HAVE_ERRORS
1381 if(!(n_psonce & n_PSO_INTERACTIVE))
1382 #endif
1383 vfprintf(n_stderr, format, ap);
1384 #ifdef HAVE_ERRORS
1385 else{
1386 int imax, i;
1387 n_LCTAV(ERRORS_MAX > 3);
1389 /* Link it into the `errors' message ring */
1390 if((enp = a_aux_err_tail) == NULL){
1391 jcreat:
1392 enp = smalloc(sizeof *enp);
1393 enp->ae_next = NULL;
1394 n_string_creat(&enp->ae_str);
1395 if(a_aux_err_tail != NULL)
1396 a_aux_err_tail->ae_next = enp;
1397 else
1398 a_aux_err_head = enp;
1399 a_aux_err_tail = enp;
1400 ++a_aux_err_cnt;
1401 }else if(doname ||
1402 (enp->ae_str.s_len > 0 &&
1403 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1404 if(a_aux_err_cnt < ERRORS_MAX)
1405 goto jcreat;
1407 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1408 a_aux_err_tail->ae_next = enp;
1409 a_aux_err_tail = enp;
1410 enp->ae_next = NULL;
1411 n_string_trunc(&enp->ae_str, 0);
1414 # ifdef HAVE_N_VA_COPY
1415 imax = 64;
1416 # else
1417 imax = n_MIN(LINESIZE, 1024);
1418 # endif
1419 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1420 # ifdef HAVE_N_VA_COPY
1421 va_list vac;
1423 n_va_copy(vac, ap);
1424 # else
1425 # define vac ap
1426 # endif
1428 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1429 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1430 # ifdef HAVE_N_VA_COPY
1431 va_end(vac);
1432 # else
1433 # undef vac
1434 # endif
1435 if(i <= 0)
1436 goto jleave;
1437 if(UICMP(z, i, >=, imax)){
1438 # ifdef HAVE_N_VA_COPY
1439 /* XXX Check overflow for upcoming LEN+++i! */
1440 n_string_trunc(&enp->ae_str, len);
1441 continue;
1442 # else
1443 i = (int)strlen(&enp->ae_str.s_dat[len]);
1444 # endif
1446 break;
1448 n_string_trunc(&enp->ae_str, len + (size_t)i);
1450 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1452 #endif /* HAVE_ERRORS */
1454 jleave:
1455 fflush(n_stderr);
1456 NYD2_LEAVE;
1459 FL void
1460 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1461 va_list ap;
1462 NYD_X;
1464 va_start(ap, format);
1465 vfprintf(n_stderr, format, ap);
1466 va_end(ap);
1467 fflush(n_stderr);
1470 FL void
1471 n_perr(char const *msg, int errval){
1472 int e;
1473 char const *fmt;
1474 NYD2_ENTER;
1476 if(msg == NULL){
1477 fmt = "%s%s\n";
1478 msg = n_empty;
1479 }else
1480 fmt = "%s: %s\n";
1482 e = (errval == 0) ? n_err_no : errval;
1483 n_err(fmt, msg, n_err_to_doc(e));
1484 if(errval == 0)
1485 n_err_no = e;
1486 NYD2_LEAVE;
1489 FL void
1490 n_alert(char const *format, ...){
1491 va_list ap;
1492 NYD2_ENTER;
1494 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1496 va_start(ap, format);
1497 n_verr(format, ap);
1498 va_end(ap);
1500 n_err("\n");
1501 NYD2_LEAVE;
1504 FL void
1505 n_panic(char const *format, ...){
1506 va_list ap;
1507 NYD2_ENTER;
1509 if(a_aux_err_linelen > 0){
1510 putc('\n', n_stderr);
1511 a_aux_err_linelen = 0;
1513 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1515 va_start(ap, format);
1516 vfprintf(n_stderr, format, ap);
1517 va_end(ap);
1519 putc('\n', n_stderr);
1520 fflush(n_stderr);
1521 NYD2_LEAVE;
1522 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1525 #ifdef HAVE_ERRORS
1526 FL int
1527 c_errors(void *v){
1528 char **argv = v;
1529 struct a_aux_err_node *enp;
1530 NYD_ENTER;
1532 if(*argv == NULL)
1533 goto jlist;
1534 if(argv[1] != NULL)
1535 goto jerr;
1536 if(!asccasecmp(*argv, "show"))
1537 goto jlist;
1538 if(!asccasecmp(*argv, "clear"))
1539 goto jclear;
1540 jerr:
1541 fprintf(n_stderr,
1542 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1543 v = NULL;
1544 jleave:
1545 NYD_LEAVE;
1546 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1548 jlist:{
1549 FILE *fp;
1550 size_t i;
1552 if(a_aux_err_head == NULL){
1553 fprintf(n_stderr, _("The error ring is empty\n"));
1554 goto jleave;
1557 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1558 NULL){
1559 fprintf(n_stderr, _("tmpfile"));
1560 v = NULL;
1561 goto jleave;
1564 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1565 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1566 /* We don't know whether last string ended with NL; be simple XXX */
1567 putc('\n', fp);
1569 page_or_print(fp, 0);
1570 Fclose(fp);
1572 /* FALLTHRU */
1574 jclear:
1575 a_aux_err_tail = NULL;
1576 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1577 a_aux_err_linelen = 0;
1578 while((enp = a_aux_err_head) != NULL){
1579 a_aux_err_head = enp->ae_next;
1580 n_string_gut(&enp->ae_str);
1581 free(enp);
1583 goto jleave;
1585 #endif /* HAVE_ERRORS */
1587 FL char const *
1588 n_err_to_doc(si32_t eno){
1589 char const *rv;
1590 struct a_aux_err_map const *aemp;
1591 NYD2_ENTER;
1593 aemp = a_aux_err_map_from_no(eno);
1594 rv = &a_aux_err_docs[aemp->aem_docoff];
1595 NYD2_LEAVE;
1596 return rv;
1599 FL char const *
1600 n_err_to_name(si32_t eno){
1601 char const *rv;
1602 struct a_aux_err_map const *aemp;
1603 NYD2_ENTER;
1605 aemp = a_aux_err_map_from_no(eno);
1606 rv = &a_aux_err_names[aemp->aem_nameoff];
1607 NYD2_LEAVE;
1608 return rv;
1611 FL si32_t
1612 n_err_from_name(char const *name){
1613 struct a_aux_err_map const *aemp;
1614 ui32_t hash, i, j, x;
1615 si32_t rv;
1616 NYD2_ENTER;
1618 hash = n_torek_hash(name);
1620 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1621 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1622 break;
1624 aemp = &a_aux_err_map[x];
1625 if(aemp->aem_hash == hash &&
1626 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1627 rv = aemp->aem_err_no;
1628 goto jleave;
1631 if(++i == a_AUX_ERR_REV_PRIME){
1632 #ifdef a_AUX_ERR_REV_WRAPAROUND
1633 i = 0;
1634 #else
1635 break;
1636 #endif
1640 /* Have not found it. But wait, it could be that the user did, e.g.,
1641 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1642 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1643 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1644 ) == n_IDEC_STATE_CONSUMED){
1645 aemp = a_aux_err_map_from_no(rv);
1646 rv = aemp->aem_err_no;
1647 goto jleave;
1650 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1651 jleave:
1652 NYD2_LEAVE;
1653 return rv;
1656 #ifdef HAVE_REGEX
1657 FL char const *
1658 n_regex_err_to_doc(const regex_t *rep, int e){
1659 char *cp;
1660 size_t i;
1661 NYD2_ENTER;
1663 i = regerror(e, rep, NULL, 0) +1;
1664 cp = salloc(i);
1665 regerror(e, rep, cp, i);
1666 NYD2_LEAVE;
1667 return cp;
1669 #endif
1671 /* s-it-mode */