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 && !n_RANDOM_USE_XSSL
60 # include HAVE_GETRANDOM_HEADER
64 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
66 # include <idn-free.h>
67 # include <stringprep.h>
68 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
73 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
81 ui8_t b8
[sizeof(struct rand_arc4
)];
82 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
87 struct a_aux_err_node
{
88 struct a_aux_err_node
*ae_next
;
89 struct n_string ae_str
;
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 /* IDEC: byte to integer value lookup table */
101 static ui8_t
const a_aux_idec_atoi
[256] = {
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,0xFF,0xFF,
106 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
107 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
109 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
110 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
111 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
112 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
113 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
114 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
127 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
130 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
131 #define a_X(X) (UI64_MAX / (X))
132 static ui64_t
const a_aux_idec_cutlimit
[35] = {
133 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
134 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
135 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
136 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
137 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
141 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
142 static ui8_t
const a_aux_ienc_shifts
[35] = {
143 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
144 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
146 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
149 /* IENC: integer to byte lookup tables */
150 static char const a_aux_ienc_itoa_upper
[36] =
151 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
152 static char const a_aux_ienc_itoa_lower
[36] =
153 "0123456789abcdefghijklmnopqrstuvwxyz";
155 /* Include the constant make-errors.sh output */
156 #include <gen-errors.h>
158 /* And these things come from mk-config.h (config-time make-errors.sh output) */
159 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
161 #define a_X(N,I) {N,I},
162 n__ERR_NUMBER_TO_MAPOFF
166 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
167 static union rand_state
*a_aux_rand
;
170 /* Error ring, for `errors' */
172 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
173 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
175 static size_t a_aux_err_linelen
;
177 /* Our ARC4 random generator with its completely unacademical pseudo
178 * initialization (shall /dev/urandom fail) */
179 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
180 static void a_aux_rand_init(void);
181 n_INLINE ui8_t
a_aux_rand_get8(void);
182 # ifndef HAVE_GETRANDOM
183 static ui32_t
a_aux_rand_weak(ui32_t seed
);
187 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
188 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
190 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
192 a_aux_rand_init(void){
193 # ifndef HAVE_GETRANDOM
194 # ifdef HAVE_CLOCK_GETTIME
199 union {int fd
; size_t i
;} u
;
204 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
206 # ifdef HAVE_GETRANDOM
207 /* getrandom(2) guarantees 256 without n_ERR_INTR..
208 * However, support sequential reading to avoid possible hangs that have
209 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
210 * HAVE_GETRANDOM is #defined) */
211 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
212 "Buffer too large to be served without n_ERR_INTR error");
213 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
214 "Buffer too small to serve used array indices");
218 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
221 gr
= HAVE_GETRANDOM(&a_aux_rand
->a
._dat
[o
], i
);
222 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
223 a_aux_rand
->a
._dat
[84]];
224 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
225 a_aux_rand
->a
._dat
[42]];
226 /* ..but be on the safe side */
233 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
239 # else /* HAVE_GETRANDOM */
240 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
243 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
244 sizeof(a_aux_rand
->a
._dat
)));
247 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
248 a_aux_rand
->a
._dat
[84]];
249 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
250 a_aux_rand
->a
._dat
[42]];
255 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
256 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
259 # ifdef HAVE_CLOCK_GETTIME
260 clock_gettime(CLOCK_REALTIME
, &ts
);
261 t
= (ui32_t
)ts
.tv_nsec
;
263 gettimeofday(&ts
, NULL
);
264 t
= (ui32_t
)ts
.tv_usec
;
267 t
= (t
>> 16) | (t
<< 16);
268 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
269 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
270 if(rnd
== 7 || rnd
== 17)
271 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
272 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
273 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
274 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
276 seed
^= n_prime_next(seed
);
280 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
283 # endif /* !HAVE_GETRANDOM */
288 a_aux_rand_get8(void){
291 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
292 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
293 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
294 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
295 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
298 # ifndef HAVE_GETRANDOM
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_GETRANDOM */
317 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
319 static struct a_aux_err_map
const *
320 a_aux_err_map_from_no(si32_t eno
){
323 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
324 struct a_aux_err_map
const *aemp
;
327 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
329 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
330 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
331 asz
!= 0; asz
>>= 1){
332 tmp
= &adat
[asz
>> 1];
333 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
334 aemp
= &a_aux_err_map
[(*tmp
)[1]];
351 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
353 #ifndef HAVE_SETLOCALE
356 setlocale(LC_ALL
, n_empty
);
357 n_mb_cur_max
= MB_CUR_MAX
;
358 # ifdef HAVE_NL_LANGINFO
362 if((cp
= nl_langinfo(CODESET
)) != NULL
)
363 /* (Will log during startup if user set that via -S) */
364 ok_vset(ttycharset
, cp
);
366 # endif /* HAVE_SETLOCALE */
368 # ifdef HAVE_C90AMEND1
369 if(n_mb_cur_max
> 1){
370 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
371 n_psonce
|= n_PSO_UNICODE
;
374 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
375 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
376 n_psonce
|= n_PSO_UNICODE
;
377 /* Reset possibly messed up state; luckily this also gives us an
378 * indication whether the encoding has locking shift state sequences */
379 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
380 n_psonce
|= n_PSO_ENC_MBSTATE
;
384 #endif /* HAVE_C90AMEND1 */
394 if((cp
= ok_vlook(screen
)) != NULL
){
395 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
408 n_pager_get(char const **env_addon
){
412 rv
= ok_vlook(PAGER
);
414 if(env_addon
!= NULL
){
416 /* Update the manual upon any changes:
417 * *colour-pager*, $PAGER */
418 if(strstr(rv
, "less") != NULL
){
419 if(getenv("LESS") == NULL
)
420 *env_addon
= "LESS=RXi";
421 }else if(strstr(rv
, "lv") != NULL
){
422 if(getenv("LV") == NULL
)
423 *env_addon
= "LV=-c";
431 page_or_print(FILE *fp
, size_t lines
)
439 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
443 rows
= (size_t)n_scrnheight
;
445 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
447 if (rows
> 0 && lines
== 0) {
448 while ((c
= getc(fp
)) != EOF
)
449 if (c
== '\n' && ++lines
>= rows
)
455 char const *env_add
[2], *pager
;
457 pager
= n_pager_get(&env_add
[0]);
459 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
465 while ((c
= getc(fp
)) != EOF
)
472 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
473 char const **adjusted_or_null
)
475 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
476 char const *cp
, *orig_name
;
477 enum protocol rv
= PROTO_UNKNOWN
;
480 if(name
[0] == '%' && name
[1] == ':')
484 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
488 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
489 if(!strncmp(name
, "file", sizeof("file") -1) ||
490 !strncmp(name
, "mbox", sizeof("mbox") -1))
492 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
494 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
498 n_err(_("No POP3 support compiled in\n"));
500 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
501 #if defined HAVE_POP3 && defined HAVE_SSL
504 n_err(_("No POP3S support compiled in\n"));
507 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
511 n_err(_("No IMAP support compiled in\n"));
513 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
514 #if defined HAVE_IMAP && defined HAVE_SSL
517 n_err(_("No IMAPS support compiled in\n"));
527 if(check_stat
|| try_hooks
){
528 struct n_file_type ft
;
533 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
534 memcpy(np
, name
, sz
+ 1);
536 if(!stat(name
, &stb
)){
537 if(S_ISDIR(stb
.st_mode
) &&
538 (memcpy(&np
[sz
], "/tmp", 5),
539 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
540 (memcpy(&np
[sz
], "/new", 5),
541 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
542 (memcpy(&np
[sz
], "/cur", 5),
543 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
545 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
546 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
547 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
548 !asccasecmp(cp
, "maildir"))
554 if(adjusted_or_null
!= NULL
)
555 *adjusted_or_null
= orig_name
;
561 n_c_to_hex_base16(char store
[3], char c
){
562 static char const itoa16
[] = "0123456789ABCDEF";
566 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
567 c
= ((ui8_t
)c
>> 4) & 0x0F;
568 store
[0] = itoa16
[(ui8_t
)c
];
574 n_c_from_hex_base16(char const hex
[2]){
575 static ui8_t
const atoi16
[] = {
576 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
577 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
578 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
579 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
582 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
588 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
589 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
593 if ((i1
| i2
) & 0xF0u
)
607 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
608 enum n_idec_mode idm
, char const **endptr_or_null
){
611 enum n_idec_state rv
;
614 idm
&= n__IDEC_MODE_MASK
;
615 rv
= n_IDEC_STATE_NONE
| idm
;
624 assert(base
!= 1 && base
<= 36);
625 /*if(base == 1 || base > 36)
629 while(spacechar(*cbuf
))
630 if(*++cbuf
== '\0' || --clen
== 0)
636 rv
|= n_IDEC_STATE_SEEN_MINUS
;
639 if(*++cbuf
== '\0' || --clen
== 0)
644 /* Base detection/skip */
649 /* Support BASE#number prefix, where BASE is decimal 2-36 */
653 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
654 (((c2
= cbuf
[1]) == '#') ||
655 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
656 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
661 base
*= 10; /* xxx Inline atoi decimal base */
662 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
665 /* We do not interpret this as BASE#number at all if either we
666 * did not get a valid base or if the first char is not valid
667 * according to base, to comply to the latest interpretion of
668 * "prefix", see comment for standard prefixes below */
669 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
672 clen
-= 2, cbuf
+= 2;
674 clen
-= 3, cbuf
+= 3;
679 /* Character must be valid for base */
680 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
684 /* 0 always valid as is, fallback base 10 */
685 if(*++cbuf
== '\0' || --clen
== 0)
688 /* Base "detection" */
689 if(base
== 0 || base
== 2 || base
== 16){
700 if((base
& 16) == 0){
702 /* Char after prefix must be valid. However, after some error
703 * in the tor software all libraries (which had to) turned to
704 * an interpretation of the C standard which says that the
705 * prefix may optionally precede an otherwise valid sequence,
706 * which means that "0x" is not a STATE_INVAL error but gives
707 * a "0" result with a "STATE_BASE" error and a rest of "x" */
710 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
713 if(*++cbuf
== '\0' || --clen
== 0)
716 /* Character must be valid for base, invalid otherwise */
717 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
730 /* Character must be valid for base, _EBASE otherwise */
731 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
736 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
740 if(res
> UI64_MAX
- currc
)
750 if(*++cbuf
== '\0' || --clen
== 0)
753 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
762 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
763 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
764 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
765 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
766 default: uimask
= UI64_MAX
; break;
768 if((rv
& n_IDEC_MODE_SIGNED_TYPE
) &&
769 (!(rv
& n_IDEC_MODE_POW2BASE_UNSIGNED
) || !n_ISPOW2(base
)))
773 /* XXX never entered unless _SIGNED_TYPE! */
774 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
775 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
776 if(res
> uimask
+ 1){
785 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
786 rv
|= n_IDEC_STATE_EOVERFLOW
;
787 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
791 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
792 case n_IDEC_MODE_LIMIT_8BIT
:
793 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
794 *(si8_t
*)resp
= (si8_t
)res
;
796 *(ui8_t
*)resp
= (ui8_t
)res
;
798 case n_IDEC_MODE_LIMIT_16BIT
:
799 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
800 *(si16_t
*)resp
= (si16_t
)res
;
802 *(ui16_t
*)resp
= (ui16_t
)res
;
804 case n_IDEC_MODE_LIMIT_32BIT
:
805 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
806 *(si32_t
*)resp
= (si32_t
)res
;
808 *(ui32_t
*)resp
= (ui32_t
)res
;
811 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
812 *(si64_t
*)resp
= (si64_t
)res
;
814 *(ui64_t
*)resp
= (ui64_t
)res
;
818 if(endptr_or_null
!= NULL
)
819 *endptr_or_null
= cbuf
;
820 if(*cbuf
== '\0' || clen
== 0)
821 rv
|= n_IDEC_STATE_CONSUMED
;
826 rv
|= n_IDEC_STATE_EINVAL
;
829 /* Not a base error for terminator and whitespace! */
830 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
831 rv
|= n_IDEC_STATE_EBASE
;
835 /* Overflow error: consume input until bad character or length out */
837 if(*++cbuf
== '\0' || --clen
== 0)
839 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
844 rv
|= n_IDEC_STATE_EOVERFLOW
;
846 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
847 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
851 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
856 n_ienc_buf(char cbuf
[n_IENC_BUFFER_SIZE
], ui64_t value
, ui8_t base
,
857 enum n_ienc_mode iem
){
858 enum{a_ISNEG
= 1u<<n__IENC_MODE_SHIFT
};
865 iem
&= n__IENC_MODE_MASK
;
867 assert(base
!= 1 && base
<= 36);
868 /*if(base == 1 || base > 36){
873 rv
= &cbuf
[n_IENC_BUFFER_SIZE
];
875 itoa
= (iem
& n_IENC_MODE_LOWERCASE
) ? a_aux_ienc_itoa_lower
876 : a_aux_ienc_itoa_upper
;
878 if((si64_t
)value
< 0){
880 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
881 /* self->is_negative = TRU1; */
886 if((shiftmodu
= a_aux_ienc_shifts
[base
- 2]) != 0){
887 --base
; /* convert to mask */
889 *--rv
= itoa
[value
& base
];
893 if(!(iem
& n_IENC_MODE_NO_PREFIX
)){
894 /* self->before_prefix = cp; */
897 else if(shiftmodu
== 1)
899 else if(shiftmodu
!= 3){
900 ++base
; /* Reconvert from mask */
901 goto jnumber_sign_prefix
;
907 shiftmodu
= value
% base
;
909 *--rv
= itoa
[shiftmodu
];
912 if(!(iem
& n_IENC_MODE_NO_PREFIX
) && base
!= 10){
918 shiftmodu
= value
% base
;
920 *--rv
= itoa
[shiftmodu
];
924 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
929 else if(iem
& n_IENC_MODE_SIGNED_PLUS
)
931 else if(iem
& n_IENC_MODE_SIGNED_SPACE
)
945 n_torek_hash(char const *name
){
946 /* Chris Torek's hash */
951 for(h
= 0; (c
= *name
++) != '\0';)
958 n_torek_ihashn(char const *dat
, size_t len
){
959 /* See n_torek_hash() */
965 for(h
= 0; (c
= *dat
++) != '\0';)
966 h
= (h
* 33) + lowerconv(c
);
968 for(h
= 0; len
> 0; --len
){
970 h
= (h
* 33) + lowerconv(c
);
977 n_prime_next(ui32_t n
){
978 static ui32_t
const primes
[] = {
979 5, 11, 23, 47, 97, 157, 283,
980 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
981 131071, 262139, 524287, 1048573, 2097143, 4194301,
982 8388593, 16777213, 33554393, 67108859, 134217689,
983 268435399, 536870909, 1073741789, 2147483647
988 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
989 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
990 : n_NELEM(primes
) / 2));
992 do if((mprime
= primes
[i
]) > n
)
994 while(++i
< n_NELEM(primes
));
996 if(i
== n_NELEM(primes
) && mprime
< n
)
1003 n_getdeadletter(void){
1010 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
1011 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
1013 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1014 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
1019 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
1020 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
1028 n_nodename(bool_t mayoverride
){
1029 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1034 # ifdef HAVE_GETADDRINFO
1035 struct addrinfo hints
, *res
;
1037 struct hostent
*hent
;
1042 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
1044 }else if((hn
= sys_hostname
) == NULL
){
1052 # ifdef HAVE_GETADDRINFO
1053 memset(&hints
, 0, sizeof hints
);
1054 hints
.ai_family
= AF_UNSPEC
;
1055 hints
.ai_flags
= AI_CANONNAME
;
1056 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
1057 if(res
->ai_canonname
!= NULL
){
1060 l
= strlen(res
->ai_canonname
) +1;
1061 hn
= n_lofi_alloc(l
);
1063 memcpy(hn
, res
->ai_canonname
, l
);
1068 hent
= gethostbyname(hn
);
1072 #endif /* HAVE_SOCKETS */
1076 struct n_string cnv
;
1078 n_string_creat(&cnv
);
1079 if(!n_idna_to_ascii(&cnv
, hn
, UIZ_MAX
))
1080 n_panic(_("The system hostname is invalid, "
1081 "IDNA conversion failed: %s\n"),
1082 n_shexp_quote_cp(hn
, FAL0
));
1083 sys_hostname
= n_string_cp(&cnv
);
1084 n_string_drop_ownership(&cnv
);
1085 /*n_string_gut(&cnv);*/
1088 sys_hostname
= sstrdup(hn
);
1096 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
1098 hostname
= sstrdup(hn
);
1105 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
1111 ilen
= strlen(ibuf
);
1115 if((rv
= (ilen
== 0)))
1117 if(ibuf
[ilen
] != '\0'){
1119 idna_utf8
= n_lofi_alloc(ilen
+1);
1120 memcpy(idna_utf8
, ibuf
, ilen
);
1121 idna_utf8
[ilen
] = '\0';
1126 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
1128 if(idna_utf8
== NULL
)
1131 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
1135 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1136 out
= n_string_assign_cp(out
, idna_ascii
);
1137 idn_free(idna_ascii
);
1142 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1143 ilen
= strlen(idna_utf8
);
1145 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
1146 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1147 case idn_buffer_overflow
:
1148 ilen
+= HOST_NAME_MAX
+1;
1152 ilen
= strlen(out
->s_dat
);
1160 # error Unknown HAVE_IDNA
1164 n_lofi_free(n_UNCONST(ibuf
));
1165 out
= n_string_trunc(out
, ilen
);
1169 #endif /* HAVE_IDNA */
1172 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1174 char *indat
, *cp
, *oudat
;
1175 size_t i
, inlen
, oulen
;
1178 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1179 n_psonce
|= n_PSO_RANDOM_INIT
;
1181 if(n_poption
& n_PO_D_V
){
1184 #if n_RANDOM_USE_XSSL
1185 prngn
= "*SSL RAND_*";
1186 #elif defined HAVE_POSIX_RANDOM
1187 prngn
= "POSIX/arc4random";
1189 prngn
= "builtin ARC4";
1191 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1194 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1199 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1200 * with PAD stripped is still longer than what the user requests, easy way.
1201 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1202 * include the base64 PAD characters in our random string: give some pad */
1204 if((inlen
= i
% 3) != 0)
1213 inlen
= inlen
+ (inlen
<< 1);
1215 indat
= n_lofi_alloc(inlen
+1);
1217 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1218 #if n_RANDOM_USE_XSSL
1219 ssl_rand_bytes(indat
, inlen
);
1220 #elif !defined HAVE_POSIX_RANDOM
1221 for(i
= inlen
; i
-- > 0;)
1222 indat
[i
] = (char)a_aux_rand_get8();
1224 for(cp
= indat
, i
= inlen
; i
> 0;){
1225 union {ui32_t i4
; char c
[4];} r
;
1228 r
.i4
= (ui32_t
)arc4random();
1229 switch((j
= i
& 3)){
1230 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1231 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1232 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1233 default: cp
[0] = r
.c
[0]; break;
1240 for(cp
= indat
, i
= inlen
; i
> 0;){
1241 union {ui32_t i4
; char c
[4];} r
;
1244 r
.i4
= ++*reprocnt_or_null
;
1245 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1255 switch((j
= i
& 3)){
1256 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1257 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1258 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1259 default: cp
[0] = r
.c
[0]; break;
1266 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1268 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1269 assert(b64
.l
>= len
);
1270 memcpy(dat
, b64
.s
, len
);
1282 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1286 dat
= n_autorec_alloc(len
+1);
1287 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1293 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1298 assert(inlen
== 0 || inbuf
!= NULL
);
1300 if (inlen
== UIZ_MAX
)
1301 inlen
= strlen(inbuf
);
1304 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1306 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1307 !ascncasecmp(inbuf
, "true", inlen
) ||
1308 !ascncasecmp(inbuf
, "yes", inlen
) ||
1309 !ascncasecmp(inbuf
, "on", inlen
))
1311 else if ((inlen
== 1 &&
1312 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1313 !ascncasecmp(inbuf
, "false", inlen
) ||
1314 !ascncasecmp(inbuf
, "no", inlen
) ||
1315 !ascncasecmp(inbuf
, "off", inlen
))
1320 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1321 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1322 ) != n_IDEC_STATE_CONSUMED
)
1333 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1338 assert(inlen
== 0 || inbuf
!= NULL
);
1340 if (inlen
== UIZ_MAX
)
1341 inlen
= strlen(inbuf
);
1344 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1345 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1346 !ascncasecmp(inbuf
, "ask-", 4) &&
1347 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1348 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1349 rv
= getapproval(prompt
, rv
);
1355 n_is_all_or_aster(char const *name
){
1359 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1364 FL
struct n_timespec
const *
1365 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1366 static struct n_timespec ts_now
;
1369 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1370 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1371 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1373 }else if(force_update
|| ts_now
.ts_sec
== 0){
1374 #ifdef HAVE_CLOCK_GETTIME
1377 clock_gettime(CLOCK_REALTIME
, &ts
);
1378 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1379 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1380 #elif defined HAVE_GETTIMEOFDAY
1383 gettimeofday(&tv
, NULL
);
1384 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1385 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1387 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1392 /* Just in case.. */
1393 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1400 time_current_update(struct time_current
*tc
, bool_t full_update
){
1402 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1411 if((tmp
= gmtime(&t
)) == NULL
){
1415 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1416 if((tmp
= localtime(&t
)) == NULL
){
1420 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1421 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1424 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1430 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1431 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1432 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1433 * ISO C requires us to use the above format string,
1434 * even if it will not fit in the buffer. Thus asctime_r
1435 * is _supposed_ to crash if the fields in tm are too large.
1436 * We follow this behavior and crash "gracefully" to warn
1437 * application developers that they may not be so lucky
1438 * on other implementations (e.g. stack smashing..).
1439 * So we need to do it on our own or the libc may kill us */
1440 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1442 si32_t y
, md
, th
, tm
, ts
;
1443 char const *wdn
, *mn
;
1444 struct tm
const *tmp
;
1447 if((tmp
= localtime_or_nil
) == NULL
){
1450 t
= (time_t)secsepoch
;
1452 if((tmp
= localtime(&t
)) == NULL
){
1453 /* TODO error log */
1459 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1461 wdn
= n_weekday_names
[4];
1462 mn
= n_month_names
[0];
1467 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1468 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1469 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1470 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1472 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1475 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1477 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1479 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1483 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1484 wdn
, mn
, md
, th
, tm
, ts
, y
);
1490 n_msleep(uiz_t millis
, bool_t ignint
){
1494 #ifdef HAVE_NANOSLEEP
1496 struct timespec ts
, trem
;
1499 ts
.tv_sec
= millis
/ 1000;
1500 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1502 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1504 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1507 #elif defined HAVE_SLEEP
1508 if((millis
/= 1000) == 0)
1510 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1513 # error Configuration should have detected a function for sleeping.
1521 n_err(char const *format
, ...){
1525 va_start(ap
, format
);
1527 if(n_psonce
& n_PSO_INTERACTIVE
)
1537 while(*format
== '\n'){
1539 putc('\n', n_stderr
);
1544 a_aux_err_linelen
= 0;
1546 if((len
= strlen(format
)) > 0){
1547 if(doname
|| a_aux_err_linelen
== 0){
1550 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1551 fputs(cp
, n_stderr
);
1553 vfprintf(n_stderr
, format
, ap
);
1558 if(format
[--len
] == '\n'){
1559 a_aux_err_linelen
= (i
-= ++len
);
1562 ++a_aux_err_linelen
;
1574 n_verr(char const *format
, va_list ap
){
1576 struct a_aux_err_node
*enp
;
1584 while(*format
== '\n'){
1585 putc('\n', n_stderr
);
1591 a_aux_err_linelen
= 0;
1593 if(n_psonce
& n_PSO_INTERACTIVE
){
1594 if((enp
= a_aux_err_tail
) != NULL
&&
1595 (enp
->ae_str
.s_len
> 0 &&
1596 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1597 n_string_push_c(&enp
->ae_str
, '\n');
1602 if((len
= strlen(format
)) == 0)
1605 n_pstate
|= n_PS_ERRORS_PROMPT
;
1608 if(doname
|| a_aux_err_linelen
== 0){
1611 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1612 fputs(cp
, n_stderr
);
1618 if(format
[--len
] == '\n'){
1619 a_aux_err_linelen
= (i
-= ++len
);
1622 ++a_aux_err_linelen
;
1627 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1629 vfprintf(n_stderr
, format
, ap
);
1633 n_LCTAV(ERRORS_MAX
> 3);
1635 /* Link it into the `errors' message ring */
1636 if((enp
= a_aux_err_tail
) == NULL
){
1638 enp
= smalloc(sizeof *enp
);
1639 enp
->ae_next
= NULL
;
1640 n_string_creat(&enp
->ae_str
);
1641 if(a_aux_err_tail
!= NULL
)
1642 a_aux_err_tail
->ae_next
= enp
;
1644 a_aux_err_head
= enp
;
1645 a_aux_err_tail
= enp
;
1648 (enp
->ae_str
.s_len
> 0 &&
1649 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1650 if(a_aux_err_cnt
< ERRORS_MAX
)
1653 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1654 a_aux_err_tail
->ae_next
= enp
;
1655 a_aux_err_tail
= enp
;
1656 enp
->ae_next
= NULL
;
1657 n_string_trunc(&enp
->ae_str
, 0);
1660 # ifdef HAVE_N_VA_COPY
1663 imax
= n_MIN(LINESIZE
, 1024);
1665 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1666 # ifdef HAVE_N_VA_COPY
1674 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1675 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1676 # ifdef HAVE_N_VA_COPY
1683 if(UICMP(z
, i
, >=, imax
)){
1684 # ifdef HAVE_N_VA_COPY
1685 /* XXX Check overflow for upcoming LEN+++i! */
1686 n_string_trunc(&enp
->ae_str
, len
);
1689 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1694 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1696 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1698 #endif /* HAVE_ERRORS */
1706 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1710 va_start(ap
, format
);
1711 vfprintf(n_stderr
, format
, ap
);
1717 n_perr(char const *msg
, int errval
){
1728 e
= (errval
== 0) ? n_err_no
: errval
;
1729 n_err(fmt
, msg
, n_err_to_doc(e
));
1736 n_alert(char const *format
, ...){
1740 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1742 va_start(ap
, format
);
1751 n_panic(char const *format
, ...){
1755 if(a_aux_err_linelen
> 0){
1756 putc('\n', n_stderr
);
1757 a_aux_err_linelen
= 0;
1759 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1761 va_start(ap
, format
);
1762 vfprintf(n_stderr
, format
, ap
);
1765 putc('\n', n_stderr
);
1768 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1775 struct a_aux_err_node
*enp
;
1782 if(!asccasecmp(*argv
, "show"))
1784 if(!asccasecmp(*argv
, "clear"))
1788 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1792 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1798 if(a_aux_err_head
== NULL
){
1799 fprintf(n_stderr
, _("The error ring is empty\n"));
1803 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1805 fprintf(n_stderr
, _("tmpfile"));
1810 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1811 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1812 /* We don't know whether last string ended with NL; be simple XXX */
1815 page_or_print(fp
, 0);
1821 a_aux_err_tail
= NULL
;
1822 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1823 a_aux_err_linelen
= 0;
1824 while((enp
= a_aux_err_head
) != NULL
){
1825 a_aux_err_head
= enp
->ae_next
;
1826 n_string_gut(&enp
->ae_str
);
1831 #endif /* HAVE_ERRORS */
1834 n_err_to_doc(si32_t eno
){
1836 struct a_aux_err_map
const *aemp
;
1839 aemp
= a_aux_err_map_from_no(eno
);
1840 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1846 n_err_to_name(si32_t eno
){
1848 struct a_aux_err_map
const *aemp
;
1851 aemp
= a_aux_err_map_from_no(eno
);
1852 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1858 n_err_from_name(char const *name
){
1859 struct a_aux_err_map
const *aemp
;
1860 ui32_t hash
, i
, j
, x
;
1864 hash
= n_torek_hash(name
);
1866 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1867 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1870 aemp
= &a_aux_err_map
[x
];
1871 if(aemp
->aem_hash
== hash
&&
1872 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1873 rv
= aemp
->aem_err_no
;
1877 if(++i
== a_AUX_ERR_REV_PRIME
){
1878 #ifdef a_AUX_ERR_REV_WRAPAROUND
1886 /* Have not found it. But wait, it could be that the user did, e.g.,
1887 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1888 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1889 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1890 ) == n_IDEC_STATE_CONSUMED
){
1891 aemp
= a_aux_err_map_from_no(rv
);
1892 rv
= aemp
->aem_err_no
;
1896 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1904 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1909 i
= regerror(e
, rep
, NULL
, 0) +1;
1911 regerror(e
, rep
, cp
, i
);