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_LIBIDN2
66 # elif HAVE_IDNA == HAVE_IDNA_LIBIDNA
68 # include <idn-free.h>
69 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
74 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
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 && !n_RANDOM_USE_XSSL
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 && !n_RANDOM_USE_XSSL
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t
a_aux_rand_get8(void);
183 # ifndef HAVE_GETRANDOM
184 static ui32_t
a_aux_rand_weak(ui32_t seed
);
188 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
189 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
191 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
193 a_aux_rand_init(void){
194 # ifndef HAVE_GETRANDOM
195 # ifdef HAVE_CLOCK_GETTIME
200 union {int fd
; size_t i
;} u
;
205 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
207 # ifdef HAVE_GETRANDOM
208 /* getrandom(2) guarantees 256 without n_ERR_INTR..
209 * However, support sequential reading to avoid possible hangs that have
210 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
211 * HAVE_GETRANDOM is #defined) */
212 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
213 "Buffer too large to be served without n_ERR_INTR error");
214 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
215 "Buffer too small to serve used array indices");
219 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
222 gr
= HAVE_GETRANDOM(&a_aux_rand
->a
._dat
[o
], i
);
223 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
224 a_aux_rand
->a
._dat
[84]];
225 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
226 a_aux_rand
->a
._dat
[42]];
227 /* ..but be on the safe side */
234 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
240 # else /* HAVE_GETRANDOM */
241 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
244 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
245 sizeof(a_aux_rand
->a
._dat
)));
248 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
249 a_aux_rand
->a
._dat
[84]];
250 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
251 a_aux_rand
->a
._dat
[42]];
256 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
257 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
260 # ifdef HAVE_CLOCK_GETTIME
261 clock_gettime(CLOCK_REALTIME
, &ts
);
262 t
= (ui32_t
)ts
.tv_nsec
;
264 gettimeofday(&ts
, NULL
);
265 t
= (ui32_t
)ts
.tv_usec
;
268 t
= (t
>> 16) | (t
<< 16);
269 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
270 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
271 if(rnd
== 7 || rnd
== 17)
272 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
273 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
274 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
275 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
277 seed
^= n_prime_next(seed
);
281 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
284 # endif /* !HAVE_GETRANDOM */
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
)];
299 # ifndef HAVE_GETRANDOM
301 a_aux_rand_weak(ui32_t seed
){
302 /* From "Random number generators: good ones are hard to find",
303 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
304 * October 1988, p. 1195.
305 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
312 seed
= (seed
* 16807) - (hi
* 2836);
317 # endif /* HAVE_GETRANDOM */
318 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
320 static struct a_aux_err_map
const *
321 a_aux_err_map_from_no(si32_t eno
){
324 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
325 struct a_aux_err_map
const *aemp
;
328 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
330 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
331 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
332 asz
!= 0; asz
>>= 1){
333 tmp
= &adat
[asz
>> 1];
334 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
335 aemp
= &a_aux_err_map
[(*tmp
)[1]];
352 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
354 #ifndef HAVE_SETLOCALE
357 setlocale(LC_ALL
, n_empty
);
358 n_mb_cur_max
= MB_CUR_MAX
;
359 # ifdef HAVE_NL_LANGINFO
363 if((cp
= nl_langinfo(CODESET
)) != NULL
)
364 /* (Will log during startup if user set that via -S) */
365 ok_vset(ttycharset
, cp
);
367 # endif /* HAVE_SETLOCALE */
369 # ifdef HAVE_C90AMEND1
370 if(n_mb_cur_max
> 1){
371 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
372 n_psonce
|= n_PSO_UNICODE
;
375 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
376 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
377 n_psonce
|= n_PSO_UNICODE
;
378 /* Reset possibly messed up state; luckily this also gives us an
379 * indication whether the encoding has locking shift state sequences */
380 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
381 n_psonce
|= n_PSO_ENC_MBSTATE
;
385 #endif /* HAVE_C90AMEND1 */
395 if((cp
= ok_vlook(screen
)) != NULL
){
396 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
409 n_pager_get(char const **env_addon
){
413 rv
= ok_vlook(PAGER
);
415 if(env_addon
!= NULL
){
417 /* Update the manual upon any changes:
418 * *colour-pager*, $PAGER */
419 if(strstr(rv
, "less") != NULL
){
420 if(getenv("LESS") == NULL
)
421 *env_addon
= "LESS=RXi";
422 }else if(strstr(rv
, "lv") != NULL
){
423 if(getenv("LV") == NULL
)
424 *env_addon
= "LV=-c";
432 page_or_print(FILE *fp
, size_t lines
)
440 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
444 rows
= (size_t)n_scrnheight
;
446 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
448 if (rows
> 0 && lines
== 0) {
449 while ((c
= getc(fp
)) != EOF
)
450 if (c
== '\n' && ++lines
>= rows
)
456 char const *env_add
[2], *pager
;
458 pager
= n_pager_get(&env_add
[0]);
460 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
466 while ((c
= getc(fp
)) != EOF
)
473 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
474 char const **adjusted_or_null
)
476 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
477 char const *cp
, *orig_name
;
478 enum protocol rv
= PROTO_UNKNOWN
;
481 if(name
[0] == '%' && name
[1] == ':')
485 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
489 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
490 if(!strncmp(name
, "file", sizeof("file") -1) ||
491 !strncmp(name
, "mbox", sizeof("mbox") -1))
493 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
495 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
499 n_err(_("No POP3 support compiled in\n"));
501 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
502 #if defined HAVE_POP3 && defined HAVE_SSL
505 n_err(_("No POP3S support compiled in\n"));
508 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
512 n_err(_("No IMAP support compiled in\n"));
514 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
515 #if defined HAVE_IMAP && defined HAVE_SSL
518 n_err(_("No IMAPS support compiled in\n"));
528 if(check_stat
|| try_hooks
){
529 struct n_file_type ft
;
534 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
535 memcpy(np
, name
, sz
+ 1);
537 if(!stat(name
, &stb
)){
538 if(S_ISDIR(stb
.st_mode
) &&
539 (memcpy(&np
[sz
], "/tmp", 5),
540 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
541 (memcpy(&np
[sz
], "/new", 5),
542 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
543 (memcpy(&np
[sz
], "/cur", 5),
544 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
546 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
547 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
548 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
549 !asccasecmp(cp
, "maildir"))
555 if(adjusted_or_null
!= NULL
)
556 *adjusted_or_null
= orig_name
;
562 n_c_to_hex_base16(char store
[3], char c
){
563 static char const itoa16
[] = "0123456789ABCDEF";
567 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
568 c
= ((ui8_t
)c
>> 4) & 0x0F;
569 store
[0] = itoa16
[(ui8_t
)c
];
575 n_c_from_hex_base16(char const hex
[2]){
576 static ui8_t
const atoi16
[] = {
577 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
578 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
579 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
582 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
583 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
589 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
590 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
594 if ((i1
| i2
) & 0xF0u
)
608 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
609 enum n_idec_mode idm
, char const **endptr_or_null
){
612 enum n_idec_state rv
;
615 idm
&= n__IDEC_MODE_MASK
;
616 rv
= n_IDEC_STATE_NONE
| idm
;
625 assert(base
!= 1 && base
<= 36);
626 /*if(base == 1 || base > 36)
630 while(spacechar(*cbuf
))
631 if(*++cbuf
== '\0' || --clen
== 0)
637 rv
|= n_IDEC_STATE_SEEN_MINUS
;
640 if(*++cbuf
== '\0' || --clen
== 0)
645 /* Base detection/skip */
650 /* Support BASE#number prefix, where BASE is decimal 2-36 */
654 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
655 (((c2
= cbuf
[1]) == '#') ||
656 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
657 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
662 base
*= 10; /* xxx Inline atoi decimal base */
663 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
666 /* We do not interpret this as BASE#number at all if either we
667 * did not get a valid base or if the first char is not valid
668 * according to base, to comply to the latest interpretion of
669 * "prefix", see comment for standard prefixes below */
670 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
673 clen
-= 2, cbuf
+= 2;
675 clen
-= 3, cbuf
+= 3;
680 /* Character must be valid for base */
681 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
685 /* 0 always valid as is, fallback base 10 */
686 if(*++cbuf
== '\0' || --clen
== 0)
689 /* Base "detection" */
690 if(base
== 0 || base
== 2 || base
== 16){
701 if((base
& 16) == 0){
703 /* Char after prefix must be valid. However, after some error
704 * in the tor software all libraries (which had to) turned to
705 * an interpretation of the C standard which says that the
706 * prefix may optionally precede an otherwise valid sequence,
707 * which means that "0x" is not a STATE_INVAL error but gives
708 * a "0" result with a "STATE_BASE" error and a rest of "x" */
711 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
714 if(*++cbuf
== '\0' || --clen
== 0)
717 /* Character must be valid for base, invalid otherwise */
718 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
731 /* Character must be valid for base, _EBASE otherwise */
732 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
737 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
741 if(res
> UI64_MAX
- currc
)
751 if(*++cbuf
== '\0' || --clen
== 0)
754 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
763 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
764 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
765 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
766 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
767 default: uimask
= UI64_MAX
; break;
769 if((rv
& n_IDEC_MODE_SIGNED_TYPE
) &&
770 (!(rv
& n_IDEC_MODE_POW2BASE_UNSIGNED
) || !n_ISPOW2(base
)))
774 /* XXX never entered unless _SIGNED_TYPE! */
775 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
776 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
777 if(res
> uimask
+ 1){
786 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
787 rv
|= n_IDEC_STATE_EOVERFLOW
;
788 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
792 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
793 case n_IDEC_MODE_LIMIT_8BIT
:
794 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
795 *(si8_t
*)resp
= (si8_t
)res
;
797 *(ui8_t
*)resp
= (ui8_t
)res
;
799 case n_IDEC_MODE_LIMIT_16BIT
:
800 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
801 *(si16_t
*)resp
= (si16_t
)res
;
803 *(ui16_t
*)resp
= (ui16_t
)res
;
805 case n_IDEC_MODE_LIMIT_32BIT
:
806 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
807 *(si32_t
*)resp
= (si32_t
)res
;
809 *(ui32_t
*)resp
= (ui32_t
)res
;
812 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
813 *(si64_t
*)resp
= (si64_t
)res
;
815 *(ui64_t
*)resp
= (ui64_t
)res
;
819 if(endptr_or_null
!= NULL
)
820 *endptr_or_null
= cbuf
;
821 if(*cbuf
== '\0' || clen
== 0)
822 rv
|= n_IDEC_STATE_CONSUMED
;
827 rv
|= n_IDEC_STATE_EINVAL
;
830 /* Not a base error for terminator and whitespace! */
831 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
832 rv
|= n_IDEC_STATE_EBASE
;
836 /* Overflow error: consume input until bad character or length out */
838 if(*++cbuf
== '\0' || --clen
== 0)
840 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
845 rv
|= n_IDEC_STATE_EOVERFLOW
;
847 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
848 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
852 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
857 n_ienc_buf(char cbuf
[n_IENC_BUFFER_SIZE
], ui64_t value
, ui8_t base
,
858 enum n_ienc_mode iem
){
859 enum{a_ISNEG
= 1u<<n__IENC_MODE_SHIFT
};
866 iem
&= n__IENC_MODE_MASK
;
868 assert(base
!= 1 && base
<= 36);
869 /*if(base == 1 || base > 36){
874 rv
= &cbuf
[n_IENC_BUFFER_SIZE
];
876 itoa
= (iem
& n_IENC_MODE_LOWERCASE
) ? a_aux_ienc_itoa_lower
877 : a_aux_ienc_itoa_upper
;
879 if((si64_t
)value
< 0){
881 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
882 /* self->is_negative = TRU1; */
887 if((shiftmodu
= a_aux_ienc_shifts
[base
- 2]) != 0){
888 --base
; /* convert to mask */
890 *--rv
= itoa
[value
& base
];
894 if(!(iem
& n_IENC_MODE_NO_PREFIX
)){
895 /* self->before_prefix = cp; */
898 else if(shiftmodu
== 1)
900 else if(shiftmodu
!= 3){
901 ++base
; /* Reconvert from mask */
902 goto jnumber_sign_prefix
;
908 shiftmodu
= value
% base
;
910 *--rv
= itoa
[shiftmodu
];
913 if(!(iem
& n_IENC_MODE_NO_PREFIX
) && base
!= 10){
919 shiftmodu
= value
% base
;
921 *--rv
= itoa
[shiftmodu
];
925 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
930 else if(iem
& n_IENC_MODE_SIGNED_PLUS
)
932 else if(iem
& n_IENC_MODE_SIGNED_SPACE
)
946 n_torek_hash(char const *name
){
947 /* Chris Torek's hash */
952 for(h
= 0; (c
= *name
++) != '\0';)
959 n_torek_ihashn(char const *dat
, size_t len
){
960 /* See n_torek_hash() */
966 for(h
= 0; (c
= *dat
++) != '\0';)
967 h
= (h
* 33) + lowerconv(c
);
969 for(h
= 0; len
> 0; --len
){
971 h
= (h
* 33) + lowerconv(c
);
978 n_prime_next(ui32_t n
){
979 static ui32_t
const primes
[] = {
980 5, 11, 23, 47, 97, 157, 283,
981 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
982 131071, 262139, 524287, 1048573, 2097143, 4194301,
983 8388593, 16777213, 33554393, 67108859, 134217689,
984 268435399, 536870909, 1073741789, 2147483647
989 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
990 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
991 : n_NELEM(primes
) / 2));
993 do if((mprime
= primes
[i
]) > n
)
995 while(++i
< n_NELEM(primes
));
997 if(i
== n_NELEM(primes
) && mprime
< n
)
1004 n_getdeadletter(void){
1011 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
1012 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
1014 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1015 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
1020 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
1021 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
1029 n_nodename(bool_t mayoverride
){
1030 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1035 # ifdef HAVE_GETADDRINFO
1036 struct addrinfo hints
, *res
;
1038 struct hostent
*hent
;
1043 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
1045 }else if((hn
= sys_hostname
) == NULL
){
1053 # ifdef HAVE_GETADDRINFO
1054 memset(&hints
, 0, sizeof hints
);
1055 hints
.ai_family
= AF_UNSPEC
;
1056 hints
.ai_flags
= AI_CANONNAME
;
1057 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
1058 if(res
->ai_canonname
!= NULL
){
1061 l
= strlen(res
->ai_canonname
) +1;
1062 hn
= n_lofi_alloc(l
);
1064 memcpy(hn
, res
->ai_canonname
, l
);
1069 hent
= gethostbyname(hn
);
1073 #endif /* HAVE_SOCKETS */
1077 struct n_string cnv
;
1079 n_string_creat(&cnv
);
1080 if(!n_idna_to_ascii(&cnv
, hn
, UIZ_MAX
))
1081 n_panic(_("The system hostname is invalid, "
1082 "IDNA conversion failed: %s\n"),
1083 n_shexp_quote_cp(hn
, FAL0
));
1084 sys_hostname
= n_string_cp(&cnv
);
1085 n_string_drop_ownership(&cnv
);
1086 /*n_string_gut(&cnv);*/
1089 sys_hostname
= sstrdup(hn
);
1097 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
1099 hostname
= sstrdup(hn
);
1106 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
1112 ilen
= strlen(ibuf
);
1116 if((rv
= (ilen
== 0)))
1118 if(ibuf
[ilen
] != '\0'){
1120 idna_utf8
= n_lofi_alloc(ilen
+1);
1121 memcpy(idna_utf8
, ibuf
, ilen
);
1122 idna_utf8
[ilen
] = '\0';
1127 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1128 if(n_psonce
& n_PSO_UNICODE
)
1130 idna_utf8
= n_UNCONST(ibuf
);
1131 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1132 else if((idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8",
1133 ok_vlook(ttycharset
), ibuf
)) == NULL
)
1137 # if HAVE_IDNA == HAVE_IDNA_LIBIDN2
1142 f
= IDN2_NONTRANSITIONAL
;
1144 if((rc
= idn2_to_ascii_8z(idna_utf8
, &idna_ascii
, f
)) == IDN2_OK
){
1145 out
= n_string_assign_cp(out
, idna_ascii
);
1146 idn2_free(idna_ascii
);
1149 }else if(rc
== IDN2_DISALLOWED
&& f
!= IDN2_TRANSITIONAL
){
1150 f
= IDN2_TRANSITIONAL
;
1155 # elif HAVE_IDNA == HAVE_IDNA_LIBIDNA
1159 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1160 out
= n_string_assign_cp(out
, idna_ascii
);
1161 idn_free(idna_ascii
);
1167 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1168 ilen
= strlen(idna_utf8
);
1170 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
1171 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1172 case idn_buffer_overflow
:
1173 ilen
+= HOST_NAME_MAX
+1;
1177 ilen
= strlen(out
->s_dat
);
1185 # error Unknown HAVE_IDNA
1189 n_lofi_free(n_UNCONST(ibuf
));
1190 out
= n_string_trunc(out
, ilen
);
1194 #endif /* HAVE_IDNA */
1197 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1199 char *indat
, *cp
, *oudat
;
1200 size_t i
, inlen
, oulen
;
1203 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1204 n_psonce
|= n_PSO_RANDOM_INIT
;
1206 if(n_poption
& n_PO_D_V
){
1209 #if n_RANDOM_USE_XSSL
1210 prngn
= "*SSL RAND_*";
1211 #elif defined HAVE_POSIX_RANDOM
1212 prngn
= "POSIX/arc4random";
1214 prngn
= "builtin ARC4";
1216 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1219 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1224 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1225 * with PAD stripped is still longer than what the user requests, easy way.
1226 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1227 * include the base64 PAD characters in our random string: give some pad */
1229 if((inlen
= i
% 3) != 0)
1238 inlen
= inlen
+ (inlen
<< 1);
1240 indat
= n_lofi_alloc(inlen
+1);
1242 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1243 #if n_RANDOM_USE_XSSL
1244 ssl_rand_bytes(indat
, inlen
);
1245 #elif !defined HAVE_POSIX_RANDOM
1246 for(i
= inlen
; i
-- > 0;)
1247 indat
[i
] = (char)a_aux_rand_get8();
1249 for(cp
= indat
, i
= inlen
; i
> 0;){
1250 union {ui32_t i4
; char c
[4];} r
;
1253 r
.i4
= (ui32_t
)arc4random();
1254 switch((j
= i
& 3)){
1255 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1256 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1257 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1258 default: cp
[0] = r
.c
[0]; break;
1265 for(cp
= indat
, i
= inlen
; i
> 0;){
1266 union {ui32_t i4
; char c
[4];} r
;
1269 r
.i4
= ++*reprocnt_or_null
;
1270 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1280 switch((j
= i
& 3)){
1281 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1282 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1283 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1284 default: cp
[0] = r
.c
[0]; break;
1291 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1293 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1294 assert(b64
.l
>= len
);
1295 memcpy(dat
, b64
.s
, len
);
1307 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1311 dat
= n_autorec_alloc(len
+1);
1312 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1318 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1323 assert(inlen
== 0 || inbuf
!= NULL
);
1325 if (inlen
== UIZ_MAX
)
1326 inlen
= strlen(inbuf
);
1329 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
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 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1363 assert(inlen
== 0 || inbuf
!= NULL
);
1365 if (inlen
== UIZ_MAX
)
1366 inlen
= strlen(inbuf
);
1369 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1370 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1371 !ascncasecmp(inbuf
, "ask-", 4) &&
1372 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1373 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1374 rv
= getapproval(prompt
, rv
);
1380 n_is_all_or_aster(char const *name
){
1384 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1389 FL
struct n_timespec
const *
1390 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1391 static struct n_timespec ts_now
;
1394 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1395 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1396 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1398 }else if(force_update
|| ts_now
.ts_sec
== 0){
1399 #ifdef HAVE_CLOCK_GETTIME
1402 clock_gettime(CLOCK_REALTIME
, &ts
);
1403 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1404 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1405 #elif defined HAVE_GETTIMEOFDAY
1408 gettimeofday(&tv
, NULL
);
1409 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1410 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1412 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1417 /* Just in case.. */
1418 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1425 time_current_update(struct time_current
*tc
, bool_t full_update
){
1427 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1436 if((tmp
= gmtime(&t
)) == NULL
){
1440 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1441 if((tmp
= localtime(&t
)) == NULL
){
1445 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1446 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1449 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1455 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1456 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1457 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1458 * ISO C requires us to use the above format string,
1459 * even if it will not fit in the buffer. Thus asctime_r
1460 * is _supposed_ to crash if the fields in tm are too large.
1461 * We follow this behavior and crash "gracefully" to warn
1462 * application developers that they may not be so lucky
1463 * on other implementations (e.g. stack smashing..).
1464 * So we need to do it on our own or the libc may kill us */
1465 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1467 si32_t y
, md
, th
, tm
, ts
;
1468 char const *wdn
, *mn
;
1469 struct tm
const *tmp
;
1472 if((tmp
= localtime_or_nil
) == NULL
){
1475 t
= (time_t)secsepoch
;
1477 if((tmp
= localtime(&t
)) == NULL
){
1478 /* TODO error log */
1484 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1486 wdn
= n_weekday_names
[4];
1487 mn
= n_month_names
[0];
1492 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1493 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1494 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1495 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1497 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1500 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1502 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1504 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1508 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1509 wdn
, mn
, md
, th
, tm
, ts
, y
);
1515 n_msleep(uiz_t millis
, bool_t ignint
){
1519 #ifdef HAVE_NANOSLEEP
1521 struct timespec ts
, trem
;
1524 ts
.tv_sec
= millis
/ 1000;
1525 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1527 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1529 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1532 #elif defined HAVE_SLEEP
1533 if((millis
/= 1000) == 0)
1535 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1538 # error Configuration should have detected a function for sleeping.
1546 n_err(char const *format
, ...){
1550 va_start(ap
, format
);
1552 if(n_psonce
& n_PSO_INTERACTIVE
)
1562 while(*format
== '\n'){
1564 putc('\n', n_stderr
);
1569 a_aux_err_linelen
= 0;
1571 if((len
= strlen(format
)) > 0){
1572 if(doname
|| a_aux_err_linelen
== 0){
1575 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1576 fputs(cp
, n_stderr
);
1578 vfprintf(n_stderr
, format
, ap
);
1583 if(format
[--len
] == '\n'){
1584 a_aux_err_linelen
= (i
-= ++len
);
1587 ++a_aux_err_linelen
;
1599 n_verr(char const *format
, va_list ap
){
1601 struct a_aux_err_node
*enp
;
1609 while(*format
== '\n'){
1610 putc('\n', n_stderr
);
1616 a_aux_err_linelen
= 0;
1618 if(n_psonce
& n_PSO_INTERACTIVE
){
1619 if((enp
= a_aux_err_tail
) != NULL
&&
1620 (enp
->ae_str
.s_len
> 0 &&
1621 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1622 n_string_push_c(&enp
->ae_str
, '\n');
1627 if((len
= strlen(format
)) == 0)
1630 n_pstate
|= n_PS_ERRORS_PROMPT
;
1633 if(doname
|| a_aux_err_linelen
== 0){
1636 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1637 fputs(cp
, n_stderr
);
1643 if(format
[--len
] == '\n'){
1644 a_aux_err_linelen
= (i
-= ++len
);
1647 ++a_aux_err_linelen
;
1652 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1654 vfprintf(n_stderr
, format
, ap
);
1658 n_LCTAV(ERRORS_MAX
> 3);
1660 /* Link it into the `errors' message ring */
1661 if((enp
= a_aux_err_tail
) == NULL
){
1663 enp
= smalloc(sizeof *enp
);
1664 enp
->ae_next
= NULL
;
1665 n_string_creat(&enp
->ae_str
);
1666 if(a_aux_err_tail
!= NULL
)
1667 a_aux_err_tail
->ae_next
= enp
;
1669 a_aux_err_head
= enp
;
1670 a_aux_err_tail
= enp
;
1673 (enp
->ae_str
.s_len
> 0 &&
1674 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1675 if(a_aux_err_cnt
< ERRORS_MAX
)
1678 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1679 a_aux_err_tail
->ae_next
= enp
;
1680 a_aux_err_tail
= enp
;
1681 enp
->ae_next
= NULL
;
1682 n_string_trunc(&enp
->ae_str
, 0);
1685 # ifdef HAVE_N_VA_COPY
1688 imax
= n_MIN(LINESIZE
, 1024);
1690 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1691 # ifdef HAVE_N_VA_COPY
1699 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1700 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1701 # ifdef HAVE_N_VA_COPY
1708 if(UICMP(z
, i
, >=, imax
)){
1709 # ifdef HAVE_N_VA_COPY
1710 /* XXX Check overflow for upcoming LEN+++i! */
1711 n_string_trunc(&enp
->ae_str
, len
);
1714 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1719 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1721 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1723 #endif /* HAVE_ERRORS */
1731 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1735 va_start(ap
, format
);
1736 vfprintf(n_stderr
, format
, ap
);
1742 n_perr(char const *msg
, int errval
){
1753 e
= (errval
== 0) ? n_err_no
: errval
;
1754 n_err(fmt
, msg
, n_err_to_doc(e
));
1761 n_alert(char const *format
, ...){
1765 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1767 va_start(ap
, format
);
1776 n_panic(char const *format
, ...){
1780 if(a_aux_err_linelen
> 0){
1781 putc('\n', n_stderr
);
1782 a_aux_err_linelen
= 0;
1784 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1786 va_start(ap
, format
);
1787 vfprintf(n_stderr
, format
, ap
);
1790 putc('\n', n_stderr
);
1793 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1800 struct a_aux_err_node
*enp
;
1807 if(!asccasecmp(*argv
, "show"))
1809 if(!asccasecmp(*argv
, "clear"))
1813 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1817 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1823 if(a_aux_err_head
== NULL
){
1824 fprintf(n_stderr
, _("The error ring is empty\n"));
1828 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1830 fprintf(n_stderr
, _("tmpfile"));
1835 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1836 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1837 /* We don't know whether last string ended with NL; be simple XXX */
1840 page_or_print(fp
, 0);
1846 a_aux_err_tail
= NULL
;
1847 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1848 a_aux_err_linelen
= 0;
1849 while((enp
= a_aux_err_head
) != NULL
){
1850 a_aux_err_head
= enp
->ae_next
;
1851 n_string_gut(&enp
->ae_str
);
1856 #endif /* HAVE_ERRORS */
1859 n_err_to_doc(si32_t eno
){
1861 struct a_aux_err_map
const *aemp
;
1864 aemp
= a_aux_err_map_from_no(eno
);
1865 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1871 n_err_to_name(si32_t eno
){
1873 struct a_aux_err_map
const *aemp
;
1876 aemp
= a_aux_err_map_from_no(eno
);
1877 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1883 n_err_from_name(char const *name
){
1884 struct a_aux_err_map
const *aemp
;
1885 ui32_t hash
, i
, j
, x
;
1889 hash
= n_torek_hash(name
);
1891 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1892 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1895 aemp
= &a_aux_err_map
[x
];
1896 if(aemp
->aem_hash
== hash
&&
1897 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1898 rv
= aemp
->aem_err_no
;
1902 if(++i
== a_AUX_ERR_REV_PRIME
){
1903 #ifdef a_AUX_ERR_REV_WRAPAROUND
1911 /* Have not found it. But wait, it could be that the user did, e.g.,
1912 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1913 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1914 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1915 ) == n_IDEC_STATE_CONSUMED
){
1916 aemp
= a_aux_err_map_from_no(rv
);
1917 rv
= aemp
->aem_err_no
;
1921 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1929 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1934 i
= regerror(e
, rep
, NULL
, 0) +1;
1936 regerror(e
, rep
, cp
, i
);