1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions that don't fit anywhere else.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
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
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
42 #include <sys/utsname.h>
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
59 #if defined HAVE_GETRANDOM
60 # include HAVE_GETRANDOM_HEADER
64 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
66 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
68 # include <idn-free.h>
69 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
74 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
82 ui8_t b8
[sizeof(struct rand_arc4
)];
83 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
88 struct a_aux_err_node
{
89 struct a_aux_err_node
*ae_next
;
90 struct n_string ae_str
;
95 ui32_t aem_hash
; /* Hash of name */
96 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
97 ui32_t aem_docoff
; /* Into a_aux_err docs[] */
98 si32_t aem_err_no
; /* The OS error value for this one */
101 /* IDEC: byte to integer value lookup table */
102 static ui8_t
const a_aux_idec_atoi
[256] = {
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,0xFF,0xFF,
106 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
108 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
109 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
110 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
111 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
112 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
113 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
114 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
115 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
127 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
128 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
131 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
132 #define a_X(X) (UI64_MAX / (X))
133 static ui64_t
const a_aux_idec_cutlimit
[35] = {
134 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
135 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
136 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
137 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
138 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
142 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
143 static ui8_t
const a_aux_ienc_shifts
[35] = {
144 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
145 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
147 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
150 /* IENC: integer to byte lookup tables */
151 static char const a_aux_ienc_itoa_upper
[36] =
152 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
153 static char const a_aux_ienc_itoa_lower
[36] =
154 "0123456789abcdefghijklmnopqrstuvwxyz";
156 /* Include the constant make-errors.sh output */
157 #include <gen-errors.h>
159 /* And these things come from mk-config.h (config-time make-errors.sh output) */
160 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
162 #define a_X(N,I) {N,I},
163 n__ERR_NUMBER_TO_MAPOFF
167 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
168 static union rand_state
*a_aux_rand
;
171 /* Error ring, for `errors' */
173 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
174 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
176 static size_t a_aux_err_linelen
;
178 /* Our ARC4 random generator with its completely unacademical pseudo
179 * initialization (shall /dev/urandom fail) */
180 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t
a_aux_rand_get8(void);
183 static ui32_t
a_aux_rand_weak(ui32_t seed
);
186 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
187 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
189 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
191 a_aux_rand_init(void){
192 # ifdef HAVE_CLOCK_GETTIME
197 union {int fd
; size_t i
;} u
;
201 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
203 # ifdef HAVE_GETRANDOM
204 /* getrandom(2) guarantees 256 without n_ERR_INTR..
205 * However, support sequential reading to avoid possible hangs that have
206 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
207 * HAVE_GETRANDOM is #defined) */
208 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
209 "Buffer too large to be served without n_ERR_INTR error");
210 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
211 "Buffer too small to serve used array indices");
215 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
218 gr
= HAVE_GETRANDOM(&a_aux_rand
->a
._dat
[o
], i
);
219 if(gr
== -1 && n_err_no
== n_ERR_NOSYS
)
221 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
222 a_aux_rand
->a
._dat
[84]];
223 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
224 a_aux_rand
->a
._dat
[42]];
225 /* ..but be on the safe side */
232 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
238 # elif !defined HAVE_NOEXTRANDOM
239 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
242 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
243 sizeof(a_aux_rand
->a
._dat
)));
246 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
247 a_aux_rand
->a
._dat
[84]];
248 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
249 a_aux_rand
->a
._dat
[42]];
255 /* As a fallback, a homebrew seed */
256 n_err(_("PseudoRandomNumberGenerator: generating homebrew seed\n"));
257 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
258 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
261 # ifdef HAVE_CLOCK_GETTIME
262 clock_gettime(CLOCK_REALTIME
, &ts
);
263 t
= (ui32_t
)ts
.tv_nsec
;
265 gettimeofday(&ts
, NULL
);
266 t
= (ui32_t
)ts
.tv_usec
;
269 t
= (t
>> 16) | (t
<< 16);
270 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
271 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
272 if(rnd
== 7 || rnd
== 17)
273 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
274 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
275 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
276 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
278 seed
^= n_prime_next(seed
);
282 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
289 a_aux_rand_get8(void){
292 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
293 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
294 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
295 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
296 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
300 a_aux_rand_weak(ui32_t seed
){
301 /* From "Random number generators: good ones are hard to find",
302 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
303 * October 1988, p. 1195.
304 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
311 seed
= (seed
* 16807) - (hi
* 2836);
316 #endif /* !HAVE_POSIX_RANDOM && !HAVE_SSL_RANDOM */
318 static struct a_aux_err_map
const *
319 a_aux_err_map_from_no(si32_t eno
){
322 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
323 struct a_aux_err_map
const *aemp
;
326 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
328 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
329 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
330 asz
!= 0; asz
>>= 1){
331 tmp
= &adat
[asz
>> 1];
332 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
333 aemp
= &a_aux_err_map
[(*tmp
)[1]];
350 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
352 #ifndef HAVE_SETLOCALE
355 setlocale(LC_ALL
, n_empty
);
356 n_mb_cur_max
= MB_CUR_MAX
;
357 # ifdef HAVE_NL_LANGINFO
361 if((cp
= nl_langinfo(CODESET
)) != NULL
)
362 /* (Will log during startup if user set that via -S) */
363 ok_vset(ttycharset
, cp
);
365 # endif /* HAVE_SETLOCALE */
367 # ifdef HAVE_C90AMEND1
368 if(n_mb_cur_max
> 1){
369 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
370 n_psonce
|= n_PSO_UNICODE
;
373 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
374 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
375 n_psonce
|= n_PSO_UNICODE
;
376 /* Reset possibly messed up state; luckily this also gives us an
377 * indication whether the encoding has locking shift state sequences */
378 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
379 n_psonce
|= n_PSO_ENC_MBSTATE
;
383 #endif /* HAVE_C90AMEND1 */
393 if((cp
= ok_vlook(screen
)) != NULL
){
394 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
407 n_pager_get(char const **env_addon
){
411 rv
= ok_vlook(PAGER
);
413 if(env_addon
!= NULL
){
415 /* Update the manual upon any changes:
416 * *colour-pager*, $PAGER */
417 if(strstr(rv
, "less") != NULL
){
418 if(getenv("LESS") == NULL
)
419 *env_addon
= "LESS=RXi";
420 }else if(strstr(rv
, "lv") != NULL
){
421 if(getenv("LV") == NULL
)
422 *env_addon
= "LV=-c";
430 page_or_print(FILE *fp
, size_t lines
)
438 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
442 rows
= (size_t)n_scrnheight
;
444 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
446 if (rows
> 0 && lines
== 0) {
447 while ((c
= getc(fp
)) != EOF
)
448 if (c
== '\n' && ++lines
>= rows
)
454 char const *env_add
[2], *pager
;
456 pager
= n_pager_get(&env_add
[0]);
458 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
464 while ((c
= getc(fp
)) != EOF
)
471 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
472 char const **adjusted_or_null
)
474 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
475 char const *cp
, *orig_name
;
476 enum protocol rv
= PROTO_UNKNOWN
;
479 if(name
[0] == '%' && name
[1] == ':')
483 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
487 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
488 if(!strncmp(name
, "file", sizeof("file") -1) ||
489 !strncmp(name
, "mbox", sizeof("mbox") -1))
491 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
493 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
497 n_err(_("No POP3 support compiled in\n"));
499 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
500 #if defined HAVE_POP3 && defined HAVE_SSL
503 n_err(_("No POP3S support compiled in\n"));
506 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
510 n_err(_("No IMAP support compiled in\n"));
512 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
513 #if defined HAVE_IMAP && defined HAVE_SSL
516 n_err(_("No IMAPS support compiled in\n"));
526 if(check_stat
|| try_hooks
){
527 struct n_file_type ft
;
532 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
533 memcpy(np
, name
, sz
+ 1);
535 if(!stat(name
, &stb
)){
536 if(S_ISDIR(stb
.st_mode
) &&
537 (memcpy(&np
[sz
], "/tmp", 5),
538 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
539 (memcpy(&np
[sz
], "/new", 5),
540 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
541 (memcpy(&np
[sz
], "/cur", 5),
542 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
544 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
545 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
546 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
547 !asccasecmp(cp
, "maildir"))
553 if(adjusted_or_null
!= NULL
)
554 *adjusted_or_null
= orig_name
;
560 n_c_to_hex_base16(char store
[3], char c
){
561 static char const itoa16
[] = "0123456789ABCDEF";
565 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
566 c
= ((ui8_t
)c
>> 4) & 0x0F;
567 store
[0] = itoa16
[(ui8_t
)c
];
573 n_c_from_hex_base16(char const hex
[2]){
574 static ui8_t
const atoi16
[] = {
575 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
576 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
577 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
578 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
579 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
581 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
587 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
588 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
592 if ((i1
| i2
) & 0xF0u
)
606 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
607 enum n_idec_mode idm
, char const **endptr_or_null
){
610 enum n_idec_state rv
;
613 idm
&= n__IDEC_MODE_MASK
;
614 rv
= n_IDEC_STATE_NONE
| idm
;
623 assert(base
!= 1 && base
<= 36);
624 /*if(base == 1 || base > 36)
628 while(spacechar(*cbuf
))
629 if(*++cbuf
== '\0' || --clen
== 0)
635 rv
|= n_IDEC_STATE_SEEN_MINUS
;
638 if(*++cbuf
== '\0' || --clen
== 0)
643 /* Base detection/skip */
648 /* Support BASE#number prefix, where BASE is decimal 2-36 */
652 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
653 (((c2
= cbuf
[1]) == '#') ||
654 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
655 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
660 base
*= 10; /* xxx Inline atoi decimal base */
661 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
664 /* We do not interpret this as BASE#number at all if either we
665 * did not get a valid base or if the first char is not valid
666 * according to base, to comply to the latest interpretion of
667 * "prefix", see comment for standard prefixes below */
668 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
671 clen
-= 2, cbuf
+= 2;
673 clen
-= 3, cbuf
+= 3;
678 /* Character must be valid for base */
679 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
683 /* 0 always valid as is, fallback base 10 */
684 if(*++cbuf
== '\0' || --clen
== 0)
687 /* Base "detection" */
688 if(base
== 0 || base
== 2 || base
== 16){
699 if((base
& 16) == 0){
701 /* Char after prefix must be valid. However, after some error
702 * in the tor software all libraries (which had to) turned to
703 * an interpretation of the C standard which says that the
704 * prefix may optionally precede an otherwise valid sequence,
705 * which means that "0x" is not a STATE_INVAL error but gives
706 * a "0" result with a "STATE_BASE" error and a rest of "x" */
709 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
712 if(*++cbuf
== '\0' || --clen
== 0)
715 /* Character must be valid for base, invalid otherwise */
716 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
729 /* Character must be valid for base, _EBASE otherwise */
730 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
735 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
739 if(res
> UI64_MAX
- currc
)
749 if(*++cbuf
== '\0' || --clen
== 0)
752 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
761 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
762 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
763 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
764 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
765 default: uimask
= UI64_MAX
; break;
767 if((rv
& n_IDEC_MODE_SIGNED_TYPE
) &&
768 (!(rv
& n_IDEC_MODE_POW2BASE_UNSIGNED
) || !n_ISPOW2(base
)))
772 /* XXX never entered unless _SIGNED_TYPE! */
773 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
774 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
775 if(res
> uimask
+ 1){
784 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
785 rv
|= n_IDEC_STATE_EOVERFLOW
;
786 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
790 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
791 case n_IDEC_MODE_LIMIT_8BIT
:
792 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
793 *(si8_t
*)resp
= (si8_t
)res
;
795 *(ui8_t
*)resp
= (ui8_t
)res
;
797 case n_IDEC_MODE_LIMIT_16BIT
:
798 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
799 *(si16_t
*)resp
= (si16_t
)res
;
801 *(ui16_t
*)resp
= (ui16_t
)res
;
803 case n_IDEC_MODE_LIMIT_32BIT
:
804 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
805 *(si32_t
*)resp
= (si32_t
)res
;
807 *(ui32_t
*)resp
= (ui32_t
)res
;
810 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
811 *(si64_t
*)resp
= (si64_t
)res
;
813 *(ui64_t
*)resp
= (ui64_t
)res
;
817 if(endptr_or_null
!= NULL
)
818 *endptr_or_null
= cbuf
;
819 if(*cbuf
== '\0' || clen
== 0)
820 rv
|= n_IDEC_STATE_CONSUMED
;
825 rv
|= n_IDEC_STATE_EINVAL
;
828 /* Not a base error for terminator and whitespace! */
829 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
830 rv
|= n_IDEC_STATE_EBASE
;
834 /* Overflow error: consume input until bad character or length out */
836 if(*++cbuf
== '\0' || --clen
== 0)
838 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
843 rv
|= n_IDEC_STATE_EOVERFLOW
;
845 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
846 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
850 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
855 n_ienc_buf(char cbuf
[n_IENC_BUFFER_SIZE
], ui64_t value
, ui8_t base
,
856 enum n_ienc_mode iem
){
857 enum{a_ISNEG
= 1u<<n__IENC_MODE_SHIFT
};
864 iem
&= n__IENC_MODE_MASK
;
866 assert(base
!= 1 && base
<= 36);
867 /*if(base == 1 || base > 36){
872 rv
= &cbuf
[n_IENC_BUFFER_SIZE
];
874 itoa
= (iem
& n_IENC_MODE_LOWERCASE
) ? a_aux_ienc_itoa_lower
875 : a_aux_ienc_itoa_upper
;
877 if((si64_t
)value
< 0){
879 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
880 /* self->is_negative = TRU1; */
885 if((shiftmodu
= a_aux_ienc_shifts
[base
- 2]) != 0){
886 --base
; /* convert to mask */
888 *--rv
= itoa
[value
& base
];
892 if(!(iem
& n_IENC_MODE_NO_PREFIX
)){
893 /* self->before_prefix = cp; */
896 else if(shiftmodu
== 1)
898 else if(shiftmodu
!= 3){
899 ++base
; /* Reconvert from mask */
900 goto jnumber_sign_prefix
;
906 shiftmodu
= value
% base
;
908 *--rv
= itoa
[shiftmodu
];
911 if(!(iem
& n_IENC_MODE_NO_PREFIX
) && base
!= 10){
917 shiftmodu
= value
% base
;
919 *--rv
= itoa
[shiftmodu
];
923 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
928 else if(iem
& n_IENC_MODE_SIGNED_PLUS
)
930 else if(iem
& n_IENC_MODE_SIGNED_SPACE
)
944 n_torek_hash(char const *name
){
945 /* Chris Torek's hash */
950 for(h
= 0; (c
= *name
++) != '\0';)
957 n_torek_ihashn(char const *dat
, size_t len
){
958 /* See n_torek_hash() */
964 for(h
= 0; (c
= *dat
++) != '\0';)
965 h
= (h
* 33) + lowerconv(c
);
967 for(h
= 0; len
> 0; --len
){
969 h
= (h
* 33) + lowerconv(c
);
976 n_prime_next(ui32_t n
){
977 static ui32_t
const primes
[] = {
978 5, 11, 23, 47, 97, 157, 283,
979 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
980 131071, 262139, 524287, 1048573, 2097143, 4194301,
981 8388593, 16777213, 33554393, 67108859, 134217689,
982 268435399, 536870909, 1073741789, 2147483647
987 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
988 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
989 : n_NELEM(primes
) / 2));
991 do if((mprime
= primes
[i
]) > n
)
993 while(++i
< n_NELEM(primes
));
995 if(i
== n_NELEM(primes
) && mprime
< n
)
1002 n_getdeadletter(void){
1009 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
1010 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
1012 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1013 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
1018 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
1019 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
1027 n_nodename(bool_t mayoverride
){
1028 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1033 # ifdef HAVE_GETADDRINFO
1034 struct addrinfo hints
, *res
;
1036 struct hostent
*hent
;
1041 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
1043 }else if((hn
= sys_hostname
) == NULL
){
1051 # ifdef HAVE_GETADDRINFO
1052 memset(&hints
, 0, sizeof hints
);
1053 hints
.ai_family
= AF_UNSPEC
;
1054 hints
.ai_flags
= AI_CANONNAME
;
1055 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
1056 if(res
->ai_canonname
!= NULL
){
1059 l
= strlen(res
->ai_canonname
) +1;
1060 hn
= n_lofi_alloc(l
);
1062 memcpy(hn
, res
->ai_canonname
, l
);
1067 hent
= gethostbyname(hn
);
1071 #endif /* HAVE_SOCKETS */
1075 struct n_string cnv
;
1077 n_string_creat(&cnv
);
1078 if(!n_idna_to_ascii(&cnv
, hn
, UIZ_MAX
))
1079 n_panic(_("The system hostname is invalid, "
1080 "IDNA conversion failed: %s\n"),
1081 n_shexp_quote_cp(hn
, FAL0
));
1082 sys_hostname
= n_string_cp(&cnv
);
1083 n_string_drop_ownership(&cnv
);
1084 /*n_string_gut(&cnv);*/
1087 sys_hostname
= sstrdup(hn
);
1095 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
1097 hostname
= sstrdup(hn
);
1104 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
1110 ilen
= strlen(ibuf
);
1114 if((rv
= (ilen
== 0)))
1116 if(ibuf
[ilen
] != '\0'){
1118 idna_utf8
= n_lofi_alloc(ilen
+1);
1119 memcpy(idna_utf8
, ibuf
, ilen
);
1120 idna_utf8
[ilen
] = '\0';
1125 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1126 if(n_psonce
& n_PSO_UNICODE
)
1128 idna_utf8
= n_UNCONST(ibuf
);
1129 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1130 else if((idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8",
1131 ok_vlook(ttycharset
), ibuf
)) == NULL
)
1135 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1140 f
= IDN2_NONTRANSITIONAL
;
1142 if((rc
= idn2_to_ascii_8z(idna_utf8
, &idna_ascii
, f
)) == IDN2_OK
){
1143 out
= n_string_assign_cp(out
, idna_ascii
);
1144 idn2_free(idna_ascii
);
1147 }else if(rc
== IDN2_DISALLOWED
&& f
!= IDN2_TRANSITIONAL
){
1148 f
= IDN2_TRANSITIONAL
;
1153 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1157 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1158 out
= n_string_assign_cp(out
, idna_ascii
);
1159 idn_free(idna_ascii
);
1165 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1166 ilen
= strlen(idna_utf8
);
1168 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
1169 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1170 case idn_buffer_overflow
:
1171 ilen
+= HOST_NAME_MAX
+1;
1175 ilen
= strlen(out
->s_dat
);
1183 # error Unknown HAVE_IDNA
1187 n_lofi_free(n_UNCONST(ibuf
));
1188 out
= n_string_trunc(out
, ilen
);
1192 #endif /* HAVE_IDNA */
1195 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1197 char *indat
, *cp
, *oudat
;
1198 size_t i
, inlen
, oulen
;
1201 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1202 n_psonce
|= n_PSO_RANDOM_INIT
;
1204 if(n_poption
& n_PO_D_V
){
1207 #if defined HAVE_POSIX_RANDOM
1208 prngn
= "POSIX/arc4random";
1209 #elif defined HAVE_SSL_RANDOM
1210 prngn
= "*SSL RAND_*";
1211 #elif defined HAVE_GETRANDOM
1212 prngn
= "getrandom(2/3) + builtin ARC4";
1213 #elif !defined HAVE_NOEXTRANDOM
1214 prngn
= "/dev/urandom + builtin ARC4";
1216 prngn
= "builtin ARC4";
1218 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn
);
1221 #if !defined HAVE_POSIX_RANDOM && !defined HAVE_SSL_RANDOM
1226 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1227 * with PAD stripped is still longer than what the user requests, easy way.
1228 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1229 * include the base64 PAD characters in our random string: give some pad */
1231 if((inlen
= i
% 3) != 0)
1240 inlen
= inlen
+ (inlen
<< 1);
1242 indat
= n_lofi_alloc(inlen
+1);
1244 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1245 #ifdef HAVE_SSL_RANDOM
1246 ssl_rand_bytes(indat
, inlen
);
1247 #elif !defined HAVE_POSIX_RANDOM
1248 for(i
= inlen
; i
-- > 0;)
1249 indat
[i
] = (char)a_aux_rand_get8();
1251 for(cp
= indat
, i
= inlen
; i
> 0;){
1252 union {ui32_t i4
; char c
[4];} r
;
1255 r
.i4
= (ui32_t
)arc4random();
1256 switch((j
= i
& 3)){
1257 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1258 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1259 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1260 default: cp
[0] = r
.c
[0]; break;
1267 for(cp
= indat
, i
= inlen
; i
> 0;){
1268 union {ui32_t i4
; char c
[4];} r
;
1271 r
.i4
= ++*reprocnt_or_null
;
1272 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1282 switch((j
= i
& 3)){
1283 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1284 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1285 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1286 default: cp
[0] = r
.c
[0]; break;
1293 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1295 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1296 assert(b64
.l
>= len
);
1297 memcpy(dat
, b64
.s
, len
);
1309 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1313 dat
= n_autorec_alloc(len
+1);
1314 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1320 n_boolify(char const *inbuf
, uiz_t inlen
, bool_t emptyrv
){
1323 assert(inlen
== 0 || inbuf
!= NULL
);
1325 if(inlen
== UIZ_MAX
)
1326 inlen
= strlen(inbuf
);
1329 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1331 if((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1332 !ascncasecmp(inbuf
, "true", inlen
) ||
1333 !ascncasecmp(inbuf
, "yes", inlen
) ||
1334 !ascncasecmp(inbuf
, "on", inlen
))
1336 else if((inlen
== 1 &&
1337 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1338 !ascncasecmp(inbuf
, "false", inlen
) ||
1339 !ascncasecmp(inbuf
, "no", inlen
) ||
1340 !ascncasecmp(inbuf
, "off", inlen
))
1345 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1346 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1347 ) != n_IDEC_STATE_CONSUMED
)
1358 n_quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, bool_t emptyrv
){
1361 assert(inlen
== 0 || inbuf
!= NULL
);
1363 if(inlen
== UIZ_MAX
)
1364 inlen
= strlen(inbuf
);
1367 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1368 else if((rv
= n_boolify(inbuf
, inlen
, emptyrv
)) < FAL0
&&
1369 !ascncasecmp(inbuf
, "ask-", 4) &&
1370 (rv
= n_boolify(&inbuf
[4], inlen
- 4, emptyrv
)) >= FAL0
&&
1371 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1372 rv
= getapproval(prompt
, rv
);
1378 n_is_all_or_aster(char const *name
){
1382 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1387 FL
struct n_timespec
const *
1388 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1389 static struct n_timespec ts_now
;
1392 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1393 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1394 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1396 }else if(force_update
|| ts_now
.ts_sec
== 0){
1397 #ifdef HAVE_CLOCK_GETTIME
1400 clock_gettime(CLOCK_REALTIME
, &ts
);
1401 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1402 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1403 #elif defined HAVE_GETTIMEOFDAY
1406 gettimeofday(&tv
, NULL
);
1407 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1408 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1410 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1415 /* Just in case.. */
1416 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1423 time_current_update(struct time_current
*tc
, bool_t full_update
){
1425 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1434 if((tmp
= gmtime(&t
)) == NULL
){
1438 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1439 if((tmp
= localtime(&t
)) == NULL
){
1443 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1444 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1447 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1453 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1454 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1455 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1456 * ISO C requires us to use the above format string,
1457 * even if it will not fit in the buffer. Thus asctime_r
1458 * is _supposed_ to crash if the fields in tm are too large.
1459 * We follow this behavior and crash "gracefully" to warn
1460 * application developers that they may not be so lucky
1461 * on other implementations (e.g. stack smashing..).
1462 * So we need to do it on our own or the libc may kill us */
1463 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1465 si32_t y
, md
, th
, tm
, ts
;
1466 char const *wdn
, *mn
;
1467 struct tm
const *tmp
;
1470 if((tmp
= localtime_or_nil
) == NULL
){
1473 t
= (time_t)secsepoch
;
1475 if((tmp
= localtime(&t
)) == NULL
){
1476 /* TODO error log */
1482 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1484 wdn
= n_weekday_names
[4];
1485 mn
= n_month_names
[0];
1490 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1491 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1492 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1493 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1495 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1498 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1500 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1502 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1506 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1507 wdn
, mn
, md
, th
, tm
, ts
, y
);
1513 n_msleep(uiz_t millis
, bool_t ignint
){
1517 #ifdef HAVE_NANOSLEEP
1519 struct timespec ts
, trem
;
1522 ts
.tv_sec
= millis
/ 1000;
1523 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1525 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1527 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1530 #elif defined HAVE_SLEEP
1531 if((millis
/= 1000) == 0)
1533 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1536 # error Configuration should have detected a function for sleeping.
1544 n_err(char const *format
, ...){
1548 va_start(ap
, format
);
1550 if(n_psonce
& n_PSO_INTERACTIVE
)
1560 while(*format
== '\n'){
1562 putc('\n', n_stderr
);
1567 a_aux_err_linelen
= 0;
1569 if((len
= strlen(format
)) > 0){
1570 if(doname
|| a_aux_err_linelen
== 0){
1573 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1574 fputs(cp
, n_stderr
);
1576 vfprintf(n_stderr
, format
, ap
);
1581 if(format
[--len
] == '\n'){
1582 a_aux_err_linelen
= (i
-= ++len
);
1585 ++a_aux_err_linelen
;
1597 n_verr(char const *format
, va_list ap
){
1599 struct a_aux_err_node
*enp
;
1607 while(*format
== '\n'){
1608 putc('\n', n_stderr
);
1614 a_aux_err_linelen
= 0;
1616 if(n_psonce
& n_PSO_INTERACTIVE
){
1617 if((enp
= a_aux_err_tail
) != NULL
&&
1618 (enp
->ae_str
.s_len
> 0 &&
1619 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1620 n_string_push_c(&enp
->ae_str
, '\n');
1625 if((len
= strlen(format
)) == 0)
1628 n_pstate
|= n_PS_ERRORS_PROMPT
;
1631 if(doname
|| a_aux_err_linelen
== 0){
1634 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1635 fputs(cp
, n_stderr
);
1641 if(format
[--len
] == '\n'){
1642 a_aux_err_linelen
= (i
-= ++len
);
1645 ++a_aux_err_linelen
;
1650 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1652 vfprintf(n_stderr
, format
, ap
);
1656 n_LCTAV(ERRORS_MAX
> 3);
1658 /* Link it into the `errors' message ring */
1659 if((enp
= a_aux_err_tail
) == NULL
){
1661 enp
= smalloc(sizeof *enp
);
1662 enp
->ae_next
= NULL
;
1663 n_string_creat(&enp
->ae_str
);
1664 if(a_aux_err_tail
!= NULL
)
1665 a_aux_err_tail
->ae_next
= enp
;
1667 a_aux_err_head
= enp
;
1668 a_aux_err_tail
= enp
;
1671 (enp
->ae_str
.s_len
> 0 &&
1672 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1673 if(a_aux_err_cnt
< ERRORS_MAX
)
1676 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1677 a_aux_err_tail
->ae_next
= enp
;
1678 a_aux_err_tail
= enp
;
1679 enp
->ae_next
= NULL
;
1680 n_string_trunc(&enp
->ae_str
, 0);
1683 # ifdef HAVE_N_VA_COPY
1686 imax
= n_MIN(LINESIZE
, 1024);
1688 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1689 # ifdef HAVE_N_VA_COPY
1697 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1698 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1699 # ifdef HAVE_N_VA_COPY
1706 if(UICMP(z
, i
, >=, imax
)){
1707 # ifdef HAVE_N_VA_COPY
1708 /* XXX Check overflow for upcoming LEN+++i! */
1709 n_string_trunc(&enp
->ae_str
, len
);
1712 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1717 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1719 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1721 #endif /* HAVE_ERRORS */
1729 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1733 va_start(ap
, format
);
1734 vfprintf(n_stderr
, format
, ap
);
1740 n_perr(char const *msg
, int errval
){
1751 e
= (errval
== 0) ? n_err_no
: errval
;
1752 n_err(fmt
, msg
, n_err_to_doc(e
));
1759 n_alert(char const *format
, ...){
1763 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1765 va_start(ap
, format
);
1774 n_panic(char const *format
, ...){
1778 if(a_aux_err_linelen
> 0){
1779 putc('\n', n_stderr
);
1780 a_aux_err_linelen
= 0;
1782 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1784 va_start(ap
, format
);
1785 vfprintf(n_stderr
, format
, ap
);
1788 putc('\n', n_stderr
);
1791 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1798 struct a_aux_err_node
*enp
;
1805 if(!asccasecmp(*argv
, "show"))
1807 if(!asccasecmp(*argv
, "clear"))
1811 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1815 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1821 if(a_aux_err_head
== NULL
){
1822 fprintf(n_stderr
, _("The error ring is empty\n"));
1826 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1828 fprintf(n_stderr
, _("tmpfile"));
1833 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1834 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1835 /* We don't know whether last string ended with NL; be simple XXX */
1838 page_or_print(fp
, 0);
1844 a_aux_err_tail
= NULL
;
1845 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1846 a_aux_err_linelen
= 0;
1847 while((enp
= a_aux_err_head
) != NULL
){
1848 a_aux_err_head
= enp
->ae_next
;
1849 n_string_gut(&enp
->ae_str
);
1854 #endif /* HAVE_ERRORS */
1857 n_err_to_doc(si32_t eno
){
1859 struct a_aux_err_map
const *aemp
;
1862 aemp
= a_aux_err_map_from_no(eno
);
1863 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1869 n_err_to_name(si32_t eno
){
1871 struct a_aux_err_map
const *aemp
;
1874 aemp
= a_aux_err_map_from_no(eno
);
1875 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1881 n_err_from_name(char const *name
){
1882 struct a_aux_err_map
const *aemp
;
1883 ui32_t hash
, i
, j
, x
;
1887 hash
= n_torek_hash(name
);
1889 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1890 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1893 aemp
= &a_aux_err_map
[x
];
1894 if(aemp
->aem_hash
== hash
&&
1895 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1896 rv
= aemp
->aem_err_no
;
1900 if(++i
== a_AUX_ERR_REV_PRIME
){
1901 #ifdef a_AUX_ERR_REV_WRAPAROUND
1909 /* Have not found it. But wait, it could be that the user did, e.g.,
1910 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1911 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1912 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1913 ) == n_IDEC_STATE_CONSUMED
){
1914 aemp
= a_aux_err_map_from_no(rv
);
1915 rv
= aemp
->aem_err_no
;
1919 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1927 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1932 i
= regerror(e
, rep
, NULL
, 0) +1;
1934 regerror(e
, rep
, cp
, i
);