Adjust OpenCSW & pkgsrc paths, drop CONFIG=MEDIUM (Alexander Harm)..
[s-mailx.git] / auxlily.c
blob76ab9e92b6c392474c4945f7c0b37c56d9df2b26
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 #ifdef HAVE_GETRANDOM
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 #ifndef HAVE_POSIX_RANDOM
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 #ifndef HAVE_POSIX_RANDOM
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 #ifndef HAVE_POSIX_RANDOM
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 #ifndef HAVE_POSIX_RANDOM
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 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
193 "Buffer too large to be served without n_ERR_INTR error");
194 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
195 "Buffer too small to serve used array indices");
196 for(;;){
197 ssize_t gr;
199 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
200 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
201 a_aux_rand->a._dat[84]];
202 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
203 a_aux_rand->a._dat[42]];
204 /* ..but be on the safe side */
205 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
206 break;
207 n_msleep(250, FAL0);
210 # else
211 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
212 bool_t ok;
214 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
215 sizeof(a_aux_rand->a._dat)));
216 close(u.fd);
218 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
219 a_aux_rand->a._dat[84]];
220 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
221 a_aux_rand->a._dat[42]];
222 if(ok)
223 goto jleave;
226 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
227 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
228 ui32_t t, k;
230 # ifdef HAVE_CLOCK_GETTIME
231 clock_gettime(CLOCK_REALTIME, &ts);
232 t = (ui32_t)ts.tv_nsec;
233 # else
234 gettimeofday(&ts, NULL);
235 t = (ui32_t)ts.tv_usec;
236 # endif
237 if(rnd & 1)
238 t = (t >> 16) | (t << 16);
239 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
240 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
241 if(rnd == 7 || rnd == 17)
242 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
243 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
244 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
245 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
246 if((rnd & 3) == 3)
247 seed ^= n_prime_next(seed);
251 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
252 a_aux_rand_get8();
253 jleave:
254 # endif /* !HAVE_GETRANDOM */
255 NYD2_LEAVE;
258 SINLINE ui8_t
259 a_aux_rand_get8(void){
260 ui8_t si, sj;
262 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
263 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
264 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
265 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
266 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
269 # ifndef HAVE_GETRANDOM
270 static ui32_t
271 a_aux_rand_weak(ui32_t seed){
272 /* From "Random number generators: good ones are hard to find",
273 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
274 * October 1988, p. 1195.
275 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
276 ui32_t hi;
278 if(seed == 0)
279 seed = 123459876;
280 hi = seed / 127773;
281 seed %= 127773;
282 seed = (seed * 16807) - (hi * 2836);
283 if((si32_t)seed < 0)
284 seed += SI32_MAX;
285 return seed;
287 # endif /* HAVE_GETRANDOM */
288 #endif /* !HAVE_POSIX_RANDOM */
290 static struct a_aux_err_map const *
291 a_aux_err_map_from_no(si32_t eno){
292 si32_t ecmp;
293 size_t asz;
294 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
295 struct a_aux_err_map const *aemp;
296 NYD2_ENTER;
298 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
300 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
301 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
302 asz != 0; asz >>= 1){
303 tmp = &adat[asz >> 1];
304 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
305 aemp = &a_aux_err_map[(*tmp)[1]];
306 break;
308 if(ecmp > 0){
309 adat = &tmp[1];
310 --asz;
314 NYD2_LEAVE;
315 return aemp;
318 FL void
319 n_locale_init(void){
320 NYD2_ENTER;
322 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
324 #ifndef HAVE_SETLOCALE
325 n_mb_cur_max = 1;
326 #else
327 setlocale(LC_ALL, n_empty);
328 n_mb_cur_max = MB_CUR_MAX;
329 # ifdef HAVE_NL_LANGINFO
330 /* C99 */{
331 char const *cp;
333 /* TODO *ttycharset* may be set several times during startup unless
334 * TODO we gain a mechanism that -S fixates a setting during startup,
335 * TODO effectively turning later adjustments (during startup) in noop */
336 if((cp = nl_langinfo(CODESET)) != NULL)
337 ok_vset(ttycharset, cp);
339 # endif
341 # ifdef HAVE_C90AMEND1
342 if(n_mb_cur_max > 1){
343 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
344 n_psonce |= n_PSO_UNICODE;
345 # else
346 wchar_t wc;
347 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
348 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
349 n_psonce |= n_PSO_UNICODE;
350 /* Reset possibly messed up state; luckily this also gives us an
351 * indication whether the encoding has locking shift state sequences */
352 if(mbtowc(&wc, NULL, n_mb_cur_max))
353 n_psonce |= n_PSO_ENC_MBSTATE;
354 # endif
356 # endif
357 #endif
358 NYD2_LEAVE;
361 FL size_t
362 n_screensize(void){
363 char const *cp;
364 uiz_t rv;
365 NYD2_ENTER;
367 if((cp = ok_vlook(screen)) != NULL){
368 n_idec_uiz_cp(&rv, cp, 0, NULL);
369 if(rv == 0)
370 rv = n_scrnheight;
371 }else
372 rv = n_scrnheight;
374 if(rv > 2)
375 rv -= 2;
376 NYD2_LEAVE;
377 return rv;
380 FL char const *
381 n_pager_get(char const **env_addon){
382 char const *rv;
383 NYD_ENTER;
385 rv = ok_vlook(PAGER);
387 if(env_addon != NULL){
388 *env_addon = NULL;
389 /* Update the manual upon any changes:
390 * *colour-pager*, $PAGER */
391 if(strstr(rv, "less") != NULL){
392 if(getenv("LESS") == NULL)
393 *env_addon = "LESS=RXi";
394 }else if(strstr(rv, "lv") != NULL){
395 if(getenv("LV") == NULL)
396 *env_addon = "LV=-c";
399 NYD_LEAVE;
400 return rv;
403 FL void
404 page_or_print(FILE *fp, size_t lines)
406 int c;
407 char const *cp;
408 NYD_ENTER;
410 fflush_rewind(fp);
412 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
413 size_t rows;
415 if(*cp == '\0')
416 rows = (size_t)n_scrnheight;
417 else
418 n_idec_uiz_cp(&rows, cp, 0, NULL);
420 if (rows > 0 && lines == 0) {
421 while ((c = getc(fp)) != EOF)
422 if (c == '\n' && ++lines >= rows)
423 break;
424 really_rewind(fp);
427 if (lines >= rows) {
428 char const *env_add[2], *pager;
430 pager = n_pager_get(&env_add[0]);
431 env_add[1] = NULL;
432 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
433 env_add, NULL);
434 goto jleave;
438 while ((c = getc(fp)) != EOF)
439 putc(c, n_stdout);
440 jleave:
441 NYD_LEAVE;
444 FL enum protocol
445 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
446 char const **adjusted_or_null)
448 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
449 char const *cp, *orig_name;
450 enum protocol rv = PROTO_UNKNOWN;
451 NYD_ENTER;
453 if(name[0] == '%' && name[1] == ':')
454 name += 2;
455 orig_name = name;
457 for (cp = name; *cp && *cp != ':'; cp++)
458 if (!alnumchar(*cp))
459 goto jfile;
461 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
462 if(!strncmp(name, "file", sizeof("file") -1) ||
463 !strncmp(name, "mbox", sizeof("mbox") -1))
464 rv = PROTO_FILE;
465 else if(!strncmp(name, "maildir", sizeof("maildir") -1))
466 rv = PROTO_MAILDIR;
467 else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
468 #ifdef HAVE_POP3
469 rv = PROTO_POP3;
470 #else
471 n_err(_("No POP3 support compiled in\n"));
472 #endif
473 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
474 #if defined HAVE_POP3 && defined HAVE_SSL
475 rv = PROTO_POP3;
476 #else
477 n_err(_("No POP3S support compiled in\n"));
478 #endif
480 else if(!strncmp(name, "imap", sizeof("imap") -1)){
481 #ifdef HAVE_IMAP
482 rv = PROTO_IMAP;
483 #else
484 n_err(_("No IMAP support compiled in\n"));
485 #endif
486 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
487 #if defined HAVE_IMAP && defined HAVE_SSL
488 rv = PROTO_IMAP;
489 #else
490 n_err(_("No IMAPS support compiled in\n"));
491 #endif
493 orig_name = &cp[3];
494 goto jleave;
497 jfile:
498 rv = PROTO_FILE;
500 if(check_stat || try_hooks){
501 struct n_file_type ft;
502 struct stat stb;
503 char *np;
504 size_t sz;
506 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
507 memcpy(np, name, sz + 1);
509 if(!stat(name, &stb)){
510 if(S_ISDIR(stb.st_mode) &&
511 (memcpy(&np[sz], "/tmp", 5),
512 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
513 (memcpy(&np[sz], "/new", 5),
514 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
515 (memcpy(&np[sz], "/cur", 5),
516 !stat(np, &stb) && S_ISDIR(stb.st_mode)))
517 rv = PROTO_MAILDIR;
518 }else if(try_hooks && n_filetype_trial(&ft, name))
519 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
520 else if((cp = ok_vlook(newfolders)) != NULL &&
521 !asccasecmp(cp, "maildir"))
522 rv = PROTO_MAILDIR;
524 n_lofi_free(np);
526 jleave:
527 if(adjusted_or_null != NULL)
528 *adjusted_or_null = orig_name;
529 NYD_LEAVE;
530 return rv;
533 FL char *
534 n_c_to_hex_base16(char store[3], char c){
535 static char const itoa16[] = "0123456789ABCDEF";
536 NYD2_ENTER;
538 store[2] = '\0';
539 store[1] = itoa16[(ui8_t)c & 0x0F];
540 c = ((ui8_t)c >> 4) & 0x0F;
541 store[0] = itoa16[(ui8_t)c];
542 NYD2_LEAVE;
543 return store;
546 FL si32_t
547 n_c_from_hex_base16(char const hex[2]){
548 static ui8_t const atoi16[] = {
549 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
550 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
551 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
552 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
553 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
554 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
555 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
557 ui8_t i1, i2;
558 si32_t rv;
559 NYD2_ENTER;
561 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
562 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
563 goto jerr;
564 i1 = atoi16[i1];
565 i2 = atoi16[i2];
566 if ((i1 | i2) & 0xF0u)
567 goto jerr;
568 rv = i1;
569 rv <<= 4;
570 rv += i2;
571 jleave:
572 NYD2_LEAVE;
573 return rv;
574 jerr:
575 rv = -1;
576 goto jleave;
579 FL enum n_idec_state
580 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
581 enum n_idec_mode idm, char const **endptr_or_null){
582 /* XXX Brute simple and */
583 ui8_t currc;
584 ui64_t res, cut;
585 enum n_idec_state rv;
586 NYD_ENTER;
588 idm &= n__IDEC_MODE_MASK;
589 rv = n_IDEC_STATE_NONE | idm;
590 res = 0;
592 if(clen == UIZ_MAX){
593 if(*cbuf == '\0')
594 goto jeinval;
595 }else if(clen == 0)
596 goto jeinval;
598 assert(base != 1 && base <= 36);
599 /*if(base == 1 || base > 36)
600 * goto jeinval;*/
602 /* Leading WS */
603 while(spacechar(*cbuf))
604 if(*++cbuf == '\0' || --clen == 0)
605 goto jeinval;
607 /* Check sign */
608 switch(*cbuf){
609 case '-':
610 rv |= n_IDEC_STATE_SEEN_MINUS;
611 /* FALLTHROUGH */
612 case '+':
613 if(*++cbuf == '\0' || --clen == 0)
614 goto jeinval;
615 break;
618 /* Base detection/skip */
619 if(*cbuf != '0'){
620 if(base == 0)
621 base = 10;
622 /* Character must be valid for base */
623 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
624 if(currc >= base)
625 goto jeinval;
626 }else{
627 /* 0 always valid as is, fallback base 10 */
628 if(*++cbuf == '\0' || --clen == 0)
629 goto jleave;
631 /* Base "detection" */
632 if(base == 0 || base == 2 || base == 16){
633 switch(*cbuf){
634 case 'x':
635 case 'X':
636 if((base & 2) == 0){
637 base = 0x10;
638 goto jprefix_skip;
640 break;
641 case 'b':
642 case 'B':
643 if((base & 16) == 0){
644 base = 2; /* 0b10 */
645 /* Char after prefix must be valid. However, after some error
646 * in the tor software all libraries (which had to) turned to
647 * an interpretation of the C standard which says that the
648 * prefix may optionally precede an otherwise valid sequence,
649 * which means that "0x" is not a STATE_INVAL error but gives
650 * a "0" result with a "STATE_BASE" error and a rest of "x" */
651 jprefix_skip:
652 #if 1
653 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base){
654 --clen;
655 ++cbuf;
657 #else
658 if(*++cbuf == '\0' || --clen == 0)
659 goto jeinval;
661 /* Character must be valid for base, invalid otherwise */
662 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
663 if(currc >= base)
664 goto jeinval;
665 #endif
667 break;
668 default:
669 if(base == 0)
670 base = 010;
671 break;
675 /* Character must be valid for base, _EBASE otherwise */
676 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
677 if(currc >= base)
678 goto jebase;
681 for(cut = a_aux_idec_cutlimit[base - 2];;){
682 if(res >= cut){
683 if(res == cut){
684 res *= base;
685 if(res > UI64_MAX - currc)
686 goto jeover;
687 res += currc;
688 }else
689 goto jeover;
690 }else{
691 res *= base;
692 res += currc;
695 if(*++cbuf == '\0' || --clen == 0)
696 break;
698 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
699 if(currc >= base)
700 goto jebase;
703 jleave:
705 ui64_t uimask;
707 switch(rv & n__IDEC_MODE_LIMIT_MASK){
708 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
709 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
710 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
711 default: uimask = UI64_MAX; break;
713 if(rv & n_IDEC_MODE_SIGNED_TYPE)
714 uimask >>= 1;
716 if(res & ~uimask){
717 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
718 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
719 if(res > uimask + 1){
720 res = uimask << 1;
721 res &= ~uimask;
722 }else{
723 res = -res;
724 break;
726 }else
727 res = uimask;
728 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
729 rv |= n_IDEC_STATE_EOVERFLOW;
730 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
731 res = -res;
732 }while(0);
734 switch(rv & n__IDEC_MODE_LIMIT_MASK){
735 case n_IDEC_MODE_LIMIT_8BIT:
736 if(rv & n_IDEC_MODE_SIGNED_TYPE)
737 *(si8_t*)resp = (si8_t)res;
738 else
739 *(ui8_t*)resp = (ui8_t)res;
740 break;
741 case n_IDEC_MODE_LIMIT_16BIT:
742 if(rv & n_IDEC_MODE_SIGNED_TYPE)
743 *(si16_t*)resp = (si16_t)res;
744 else
745 *(ui16_t*)resp = (ui16_t)res;
746 break;
747 case n_IDEC_MODE_LIMIT_32BIT:
748 if(rv & n_IDEC_MODE_SIGNED_TYPE)
749 *(si32_t*)resp = (si32_t)res;
750 else
751 *(ui32_t*)resp = (ui32_t)res;
752 break;
753 default:
754 if(rv & n_IDEC_MODE_SIGNED_TYPE)
755 *(si64_t*)resp = (si64_t)res;
756 else
757 *(ui64_t*)resp = (ui64_t)res;
758 break;
761 if(endptr_or_null != NULL)
762 *endptr_or_null = cbuf;
763 if(*cbuf == '\0' || clen == 0)
764 rv |= n_IDEC_STATE_CONSUMED;
765 NYD_LEAVE;
766 return rv;
768 jeinval:
769 rv |= n_IDEC_STATE_EINVAL;
770 goto j_maxval;
771 jebase:
772 /* Not a base error for terminator and whitespace! */
773 if(*cbuf != '\0' && !spacechar(*cbuf))
774 rv |= n_IDEC_STATE_EBASE;
775 goto jleave;
777 jeover:
778 /* Overflow error: consume input until bad character or length out */
779 for(;;){
780 if(*++cbuf == '\0' || --clen == 0)
781 break;
782 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
783 if(currc >= base)
784 break;
787 rv |= n_IDEC_STATE_EOVERFLOW;
788 j_maxval:
789 if(rv & n_IDEC_MODE_SIGNED_TYPE)
790 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
791 : (ui64_t)SI64_MAX;
792 else
793 res = UI64_MAX;
794 rv &= ~n_IDEC_STATE_SEEN_MINUS;
795 goto jleave;
798 FL ui32_t
799 n_torek_hash(char const *name){
800 /* Chris Torek's hash */
801 char c;
802 ui32_t h;
803 NYD2_ENTER;
805 for(h = 0; (c = *name++) != '\0';)
806 h = (h * 33) + c;
807 NYD2_LEAVE;
808 return h;
811 FL ui32_t
812 n_torek_ihashn(char const *dat, size_t len){
813 /* See n_torek_hash() */
814 char c;
815 ui32_t h;
816 NYD2_ENTER;
818 if(len == UIZ_MAX)
819 for(h = 0; (c = *dat++) != '\0';)
820 h = (h * 33) + lowerconv(c);
821 else
822 for(h = 0; len > 0; --len){
823 c = *dat++;
824 h = (h * 33) + lowerconv(c);
826 NYD2_LEAVE;
827 return h;
830 FL ui32_t
831 n_prime_next(ui32_t n){
832 static ui32_t const primes[] = {
833 5, 11, 23, 47, 97, 157, 283,
834 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
835 131071, 262139, 524287, 1048573, 2097143, 4194301,
836 8388593, 16777213, 33554393, 67108859, 134217689,
837 268435399, 536870909, 1073741789, 2147483647
839 ui32_t i, mprime;
840 NYD2_ENTER;
842 i = (n < primes[n_NELEM(primes) / 4] ? 0
843 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
844 : n_NELEM(primes) / 2));
846 do if((mprime = primes[i]) > n)
847 break;
848 while(++i < n_NELEM(primes));
850 if(i == n_NELEM(primes) && mprime < n)
851 mprime = n;
852 NYD2_LEAVE;
853 return mprime;
856 FL char const *
857 n_getdeadletter(void){
858 char const *cp;
859 bool_t bla;
860 NYD_ENTER;
862 bla = FAL0;
863 jredo:
864 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
865 if(cp == NULL || strlen(cp) >= PATH_MAX){
866 if(!bla){
867 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
868 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
869 ok_vclear(DEAD);
870 bla = TRU1;
871 goto jredo;
872 }else{
873 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
874 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
877 NYD_LEAVE;
878 return cp;
881 FL char *
882 n_nodename(bool_t mayoverride){
883 static char *sys_hostname, *hostname; /* XXX free-at-exit */
885 struct utsname ut;
886 char *hn;
887 #ifdef HAVE_SOCKETS
888 # ifdef HAVE_GETADDRINFO
889 struct addrinfo hints, *res;
890 # else
891 struct hostent *hent;
892 # endif
893 #endif
894 NYD2_ENTER;
896 if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
898 }else if((hn = sys_hostname) == NULL){
899 uname(&ut);
900 hn = ut.nodename;
901 #ifdef HAVE_SOCKETS
902 # ifdef HAVE_GETADDRINFO
903 memset(&hints, 0, sizeof hints);
904 hints.ai_family = AF_UNSPEC;
905 hints.ai_flags = AI_CANONNAME;
906 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
907 if(res->ai_canonname != NULL){
908 size_t l;
910 l = strlen(res->ai_canonname) +1;
911 hn = n_lofi_alloc(l);
912 memcpy(hn, res->ai_canonname, l);
914 freeaddrinfo(res);
916 # else
917 hent = gethostbyname(hn);
918 if(hent != NULL)
919 hn = hent->h_name;
920 # endif
921 #endif
922 sys_hostname = sstrdup(hn);
923 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
924 if(hn != ut.nodename)
925 n_lofi_free(hn);
926 #endif
927 hn = sys_hostname;
930 if(hostname != NULL && hostname != sys_hostname)
931 n_free(hostname);
932 hostname = sstrdup(hn);
933 NYD2_LEAVE;
934 return hostname;
937 #ifdef HAVE_IDNA
938 FL bool_t
939 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
940 char *idna_utf8;
941 bool_t rv;
942 NYD_ENTER;
944 if((rv = (ilen == 0)))
945 goto jleave;
947 if(ibuf[ilen] != '\0') /* TODO n_idna_to_ascii: optimise */
948 ibuf = savestrbuf(ibuf, ilen);
949 ilen = 0;
951 idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
952 ibuf);
953 if(idna_utf8 == NULL)
954 goto jleave;
956 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
957 /* C99 */{
958 char *idna_ascii;
960 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
961 out = n_string_assign_cp(out, idna_ascii);
962 idn_free(idna_ascii);
963 rv = TRU1;
964 ilen = out->s_len;
967 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
968 ilen = strlen(idna_utf8);
969 jredo:
970 switch(idn_encodename((IDN_ENCODE_APP & ~IDN_LOCALCONV), idna_utf8,
971 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
972 case idn_buffer_overflow:
973 ilen += HOST_NAME_MAX +1;
974 goto jredo;
975 case idn_success:
976 rv = TRU1;
977 ilen = strlen(out->s_dat);
978 break;
979 default:
980 ilen = 0;
981 break;
984 # else
985 # error Unknown HAVE_IDNA
986 # endif
987 jleave:
988 out = n_string_trunc(out, ilen);
989 NYD_LEAVE;
990 return rv;
992 #endif /* HAVE_IDNA */
994 FL char *
995 n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){
996 struct str b64;
997 char *data, *cp;
998 size_t i;
999 NYD_ENTER;
1001 #ifndef HAVE_POSIX_RANDOM
1002 if(a_aux_rand == NULL)
1003 a_aux_rand_init();
1004 #endif
1006 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1007 * with PAD stripped is still longer than what the user requests, easy way */
1008 data = n_lofi_alloc(i = length + 3);
1010 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1011 #ifndef HAVE_POSIX_RANDOM
1012 while(i-- > 0)
1013 data[i] = (char)a_aux_rand_get8();
1014 #else
1015 for(cp = data; i > 0;){
1016 union {ui32_t i4; char c[4];} r;
1017 size_t j;
1019 r.i4 = (ui32_t)arc4random();
1020 switch((j = i & 3)){
1021 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1022 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1023 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1024 default: cp[0] = r.c[0]; break;
1026 cp += j;
1027 i -= j;
1029 #endif
1030 }else{
1031 for(cp = data; i > 0;){
1032 union {ui32_t i4; char c[4];} r;
1033 size_t j;
1035 r.i4 = ++*reprocnt_or_null;
1036 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1037 char x;
1039 x = r.c[0];
1040 r.c[0] = r.c[3];
1041 r.c[3] = x;
1042 x = r.c[1];
1043 r.c[1] = r.c[2];
1044 r.c[2] = x;
1046 switch((j = i & 3)){
1047 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1048 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1049 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1050 default: cp[0] = r.c[0]; break;
1052 cp += j;
1053 i -= j;
1057 assert(length + 3 < UIZ_MAX / 4);
1058 b64_encode_buf(&b64, data, length + 3,
1059 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1060 n_lofi_free(data);
1062 assert(b64.l >= length);
1063 b64.s[length] = '\0';
1064 NYD_LEAVE;
1065 return b64.s;
1068 FL si8_t
1069 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1071 si8_t rv;
1072 NYD_ENTER;
1074 assert(inlen == 0 || inbuf != NULL);
1076 if (inlen == UIZ_MAX)
1077 inlen = strlen(inbuf);
1079 if (inlen == 0)
1080 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1081 else {
1082 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1083 !ascncasecmp(inbuf, "true", inlen) ||
1084 !ascncasecmp(inbuf, "yes", inlen) ||
1085 !ascncasecmp(inbuf, "on", inlen))
1086 rv = 1;
1087 else if ((inlen == 1 &&
1088 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1089 !ascncasecmp(inbuf, "false", inlen) ||
1090 !ascncasecmp(inbuf, "no", inlen) ||
1091 !ascncasecmp(inbuf, "off", inlen))
1092 rv = 0;
1093 else {
1094 ui64_t ib;
1096 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1097 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1098 ) != n_IDEC_STATE_CONSUMED)
1099 rv = -1;
1100 else
1101 rv = (ib != 0);
1104 NYD_LEAVE;
1105 return rv;
1108 FL si8_t
1109 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1111 si8_t rv;
1112 NYD_ENTER;
1114 assert(inlen == 0 || inbuf != NULL);
1116 if (inlen == UIZ_MAX)
1117 inlen = strlen(inbuf);
1119 if (inlen == 0)
1120 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1121 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1122 !ascncasecmp(inbuf, "ask-", 4) &&
1123 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1124 (n_psonce & n_PSO_INTERACTIVE))
1125 rv = getapproval(prompt, rv);
1126 NYD_LEAVE;
1127 return rv;
1130 FL bool_t
1131 n_is_all_or_aster(char const *name){
1132 bool_t rv;
1133 NYD_ENTER;
1135 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1136 NYD_LEAVE;
1137 return rv;
1140 FL struct n_timespec const *
1141 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1142 static struct n_timespec ts_now;
1143 NYD2_ENTER;
1145 if(n_psonce & n_PSO_REPRODUCIBLE){
1146 (void)n_idec_ui64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1147 ts_now.ts_nsec = 0;
1148 }else if(force_update || ts_now.ts_sec == 0){
1149 #ifdef HAVE_CLOCK_GETTIME
1150 struct timespec ts;
1152 clock_gettime(CLOCK_REALTIME, &ts);
1153 ts_now.ts_sec = (si64_t)ts.tv_sec;
1154 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1155 #elif defined HAVE_GETTIMEOFDAY
1156 struct timeval tv;
1158 gettimeofday(&tv, NULL);
1159 ts_now.ts_sec = (si64_t)tv.tv_sec;
1160 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1161 #else
1162 ts_now.ts_sec = (si64_t)time(NULL);
1163 ts_now.ts_nsec = 0;
1164 #endif
1166 NYD2_LEAVE;
1167 return &ts_now;
1170 FL void
1171 time_current_update(struct time_current *tc, bool_t full_update)
1173 NYD_ENTER;
1174 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1175 if (full_update) {
1176 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1177 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1178 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1180 NYD_LEAVE;
1183 FL uiz_t
1184 n_msleep(uiz_t millis, bool_t ignint){
1185 uiz_t rv;
1186 NYD2_ENTER;
1188 #ifdef HAVE_NANOSLEEP
1189 /* C99 */{
1190 struct timespec ts, trem;
1191 int i;
1193 ts.tv_sec = millis / 1000;
1194 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1196 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1197 ts = trem;
1198 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1201 #elif defined HAVE_SLEEP
1202 if((millis /= 1000) == 0)
1203 millis = 1;
1204 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1205 millis = rv;
1206 #else
1207 # error Configuration should have detected a function for sleeping.
1208 #endif
1210 NYD2_LEAVE;
1211 return rv;
1214 FL void
1215 n_err(char const *format, ...){
1216 va_list ap;
1217 NYD2_ENTER;
1219 va_start(ap, format);
1220 #ifdef HAVE_ERRORS
1221 if(n_psonce & n_PSO_INTERACTIVE)
1222 n_verr(format, ap);
1223 else
1224 #endif
1226 size_t len;
1227 bool_t doname;
1229 doname = FAL0;
1231 while(*format == '\n'){
1232 doname = TRU1;
1233 putc('\n', n_stderr);
1234 ++format;
1237 if(doname)
1238 a_aux_err_linelen = 0;
1240 if((len = strlen(format)) > 0){
1241 if(doname || a_aux_err_linelen == 0){
1242 char const *cp;
1244 if(*(cp = ok_vlook(log_prefix)) != '\0')
1245 fputs(cp, n_stderr);
1247 vfprintf(n_stderr, format, ap);
1249 /* C99 */{
1250 size_t i = len;
1252 if(format[--len] == '\n'){
1253 a_aux_err_linelen = (i -= ++len);
1254 break;
1256 ++a_aux_err_linelen;
1257 }while(len > 0);
1261 fflush(n_stderr);
1263 va_end(ap);
1264 NYD2_LEAVE;
1267 FL void
1268 n_verr(char const *format, va_list ap){
1269 #ifdef HAVE_ERRORS
1270 struct a_aux_err_node *enp;
1271 #endif
1272 bool_t doname;
1273 size_t len;
1274 NYD2_ENTER;
1276 doname = FAL0;
1278 while(*format == '\n'){
1279 putc('\n', n_stderr);
1280 doname = TRU1;
1281 ++format;
1284 if(doname){
1285 a_aux_err_linelen = 0;
1286 #ifdef HAVE_ERRORS
1287 if(n_psonce & n_PSO_INTERACTIVE){
1288 if((enp = a_aux_err_tail) != NULL &&
1289 (enp->ae_str.s_len > 0 &&
1290 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1291 n_string_push_c(&enp->ae_str, '\n');
1293 #endif
1296 if((len = strlen(format)) == 0)
1297 goto jleave;
1298 #ifdef HAVE_ERRORS
1299 n_pstate |= n_PS_ERRORS_PROMPT;
1300 #endif
1302 if(doname || a_aux_err_linelen == 0){
1303 char const *cp;
1305 if(*(cp = ok_vlook(log_prefix)) != '\0')
1306 fputs(cp, n_stderr);
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);
1320 #ifdef HAVE_ERRORS
1321 if(!(n_psonce & n_PSO_INTERACTIVE))
1322 #endif
1323 vfprintf(n_stderr, format, ap);
1324 #ifdef HAVE_ERRORS
1325 else{
1326 int imax, i;
1327 n_LCTAV(ERRORS_MAX > 3);
1329 /* Link it into the `errors' message ring */
1330 if((enp = a_aux_err_tail) == NULL){
1331 jcreat:
1332 enp = smalloc(sizeof *enp);
1333 enp->ae_next = NULL;
1334 n_string_creat(&enp->ae_str);
1335 if(a_aux_err_tail != NULL)
1336 a_aux_err_tail->ae_next = enp;
1337 else
1338 a_aux_err_head = enp;
1339 a_aux_err_tail = enp;
1340 ++a_aux_err_cnt;
1341 }else if(doname ||
1342 (enp->ae_str.s_len > 0 &&
1343 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1344 if(a_aux_err_cnt < ERRORS_MAX)
1345 goto jcreat;
1347 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1348 a_aux_err_tail->ae_next = enp;
1349 a_aux_err_tail = enp;
1350 enp->ae_next = NULL;
1351 n_string_trunc(&enp->ae_str, 0);
1354 # ifdef HAVE_N_VA_COPY
1355 imax = 64;
1356 # else
1357 imax = n_MIN(LINESIZE, 1024);
1358 # endif
1359 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1360 # ifdef HAVE_N_VA_COPY
1361 va_list vac;
1363 n_va_copy(vac, ap);
1364 # else
1365 # define vac ap
1366 # endif
1368 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1369 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1370 # ifdef HAVE_N_VA_COPY
1371 va_end(vac);
1372 # else
1373 # undef vac
1374 # endif
1375 if(i <= 0)
1376 goto jleave;
1377 if(UICMP(z, i, >=, imax)){
1378 # ifdef HAVE_N_VA_COPY
1379 /* XXX Check overflow for upcoming LEN+++i! */
1380 n_string_trunc(&enp->ae_str, len);
1381 continue;
1382 # else
1383 i = (int)strlen(&enp->ae_str.s_dat[len]);
1384 # endif
1386 break;
1388 n_string_trunc(&enp->ae_str, len + (size_t)i);
1390 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1392 #endif /* HAVE_ERRORS */
1394 jleave:
1395 fflush(n_stderr);
1396 NYD2_LEAVE;
1399 FL void
1400 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1401 va_list ap;
1402 NYD_X;
1404 va_start(ap, format);
1405 vfprintf(n_stderr, format, ap);
1406 va_end(ap);
1407 fflush(n_stderr);
1410 FL void
1411 n_perr(char const *msg, int errval){
1412 int e;
1413 char const *fmt;
1414 NYD2_ENTER;
1416 if(msg == NULL){
1417 fmt = "%s%s\n";
1418 msg = n_empty;
1419 }else
1420 fmt = "%s: %s\n";
1422 e = (errval == 0) ? n_err_no : errval;
1423 n_err(fmt, msg, n_err_to_doc(e));
1424 if(errval == 0)
1425 n_err_no = e;
1426 NYD2_LEAVE;
1429 FL void
1430 n_alert(char const *format, ...){
1431 va_list ap;
1432 NYD2_ENTER;
1434 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1436 va_start(ap, format);
1437 n_verr(format, ap);
1438 va_end(ap);
1440 n_err("\n");
1441 NYD2_LEAVE;
1444 FL void
1445 n_panic(char const *format, ...){
1446 va_list ap;
1447 NYD2_ENTER;
1449 if(a_aux_err_linelen > 0){
1450 putc('\n', n_stderr);
1451 a_aux_err_linelen = 0;
1453 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1455 va_start(ap, format);
1456 vfprintf(n_stderr, format, ap);
1457 va_end(ap);
1459 putc('\n', n_stderr);
1460 fflush(n_stderr);
1461 NYD2_LEAVE;
1462 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1465 #ifdef HAVE_ERRORS
1466 FL int
1467 c_errors(void *v){
1468 char **argv = v;
1469 struct a_aux_err_node *enp;
1470 NYD_ENTER;
1472 if(*argv == NULL)
1473 goto jlist;
1474 if(argv[1] != NULL)
1475 goto jerr;
1476 if(!asccasecmp(*argv, "show"))
1477 goto jlist;
1478 if(!asccasecmp(*argv, "clear"))
1479 goto jclear;
1480 jerr:
1481 fprintf(n_stderr,
1482 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1483 v = NULL;
1484 jleave:
1485 NYD_LEAVE;
1486 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1488 jlist:{
1489 FILE *fp;
1490 size_t i;
1492 if(a_aux_err_head == NULL){
1493 fprintf(n_stderr, _("The error ring is empty\n"));
1494 goto jleave;
1497 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1498 NULL){
1499 fprintf(n_stderr, _("tmpfile"));
1500 v = NULL;
1501 goto jleave;
1504 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1505 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1506 /* We don't know whether last string ended with NL; be simple XXX */
1507 putc('\n', fp);
1509 page_or_print(fp, 0);
1510 Fclose(fp);
1512 /* FALLTHRU */
1514 jclear:
1515 a_aux_err_tail = NULL;
1516 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1517 a_aux_err_linelen = 0;
1518 while((enp = a_aux_err_head) != NULL){
1519 a_aux_err_head = enp->ae_next;
1520 n_string_gut(&enp->ae_str);
1521 free(enp);
1523 goto jleave;
1525 #endif /* HAVE_ERRORS */
1527 FL char const *
1528 n_err_to_doc(si32_t eno){
1529 char const *rv;
1530 struct a_aux_err_map const *aemp;
1531 NYD2_ENTER;
1533 aemp = a_aux_err_map_from_no(eno);
1534 rv = &a_aux_err_docs[aemp->aem_docoff];
1535 NYD2_LEAVE;
1536 return rv;
1539 FL char const *
1540 n_err_to_name(si32_t eno){
1541 char const *rv;
1542 struct a_aux_err_map const *aemp;
1543 NYD2_ENTER;
1545 aemp = a_aux_err_map_from_no(eno);
1546 rv = &a_aux_err_names[aemp->aem_nameoff];
1547 NYD2_LEAVE;
1548 return rv;
1551 FL si32_t
1552 n_err_from_name(char const *name){
1553 struct a_aux_err_map const *aemp;
1554 ui32_t hash, i, j, x;
1555 si32_t rv;
1556 NYD2_ENTER;
1558 hash = n_torek_hash(name);
1560 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1561 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1562 break;
1564 aemp = &a_aux_err_map[x];
1565 if(aemp->aem_hash == hash &&
1566 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1567 rv = aemp->aem_err_no;
1568 goto jleave;
1571 if(++i == a_AUX_ERR_REV_PRIME){
1572 #ifdef a_AUX_ERR_REV_WRAPAROUND
1573 i = 0;
1574 #else
1575 break;
1576 #endif
1580 /* Have not found it. But wait, it could be that the user did, e.g.,
1581 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1582 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1583 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1584 ) == n_IDEC_STATE_CONSUMED){
1585 aemp = a_aux_err_map_from_no(rv);
1586 rv = aemp->aem_err_no;
1587 goto jleave;
1590 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1591 jleave:
1592 NYD2_LEAVE;
1593 return rv;
1596 #ifdef HAVE_REGEX
1597 FL char const *
1598 n_regex_err_to_doc(const regex_t *rep, int e){
1599 char *cp;
1600 size_t i;
1601 NYD2_ENTER;
1603 i = regerror(e, rep, NULL, 0) +1;
1604 cp = salloc(i);
1605 regerror(e, rep, cp, i);
1606 NYD2_LEAVE;
1607 return cp;
1609 #endif
1611 /* s-it-mode */