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>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #define n_FILE auxlily
39 #ifndef HAVE_AMALGAMATION
43 #include <sys/utsname.h>
46 # ifdef HAVE_GETADDRINFO
47 # include <sys/socket.h>
53 #ifdef HAVE_NL_LANGINFO
54 # include <langinfo.h>
60 #if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
61 # include n_RANDOM_GETRANDOM_H
65 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
67 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
69 # include <idn-free.h>
70 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
75 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
83 ui8_t b8
[sizeof(struct rand_arc4
)];
84 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
89 struct a_aux_err_node
{
90 struct a_aux_err_node
*ae_next
;
91 struct n_string ae_str
;
96 ui32_t aem_hash
; /* Hash of name */
97 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
98 ui32_t aem_docoff
; /* Into a_aux_err docs[] (if HAVE_DOCSTRINGS) */
99 si32_t aem_err_no
; /* The OS error value for this one */
102 /* IDEC: byte to integer value lookup table */
103 static ui8_t
const a_aux_idec_atoi
[256] = {
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,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
109 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
110 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
111 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
112 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
113 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
114 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
115 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
116 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
129 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
132 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
133 #define a_X(X) (UI64_MAX / (X))
134 static ui64_t
const a_aux_idec_cutlimit
[35] = {
135 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
136 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
137 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
138 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
139 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
143 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
144 static ui8_t
const a_aux_ienc_shifts
[35] = {
145 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
146 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
148 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
151 /* IENC: integer to byte lookup tables */
152 static char const a_aux_ienc_itoa_upper
[36] =
153 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
154 static char const a_aux_ienc_itoa_lower
[36] =
155 "0123456789abcdefghijklmnopqrstuvwxyz";
157 /* Include the constant make-errors.sh output */
158 #include <gen-errors.h>
160 /* And these things come from mk-config.h (config-time make-errors.sh output) */
161 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
163 #define a_X(N,I) {N,I},
164 n__ERR_NUMBER_TO_MAPOFF
168 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
169 static union rand_state
*a_aux_rand
;
172 /* Error ring, for `errors' */
174 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
175 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
177 static size_t a_aux_err_linelen
;
179 /* Our ARC4 random generator with its completely unacademical pseudo
180 * initialization (shall /dev/urandom fail) */
181 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
182 static void a_aux_rand_init(void);
183 n_INLINE ui8_t
a_aux_rand_get8(void);
184 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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
192 a_aux_rand_init(void){
193 # ifdef HAVE_CLOCK_GETTIME
198 union {int fd
; size_t i
;} u
;
202 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
204 # if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
205 /* getrandom(2) guarantees 256 without n_ERR_INTR..
206 * However, support sequential reading to avoid possible hangs that have
207 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
208 * HAVE_GETRANDOM is #defined) */
209 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
210 "Buffer too large to be served without n_ERR_INTR error");
211 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
212 "Buffer too small to serve used array indices");
216 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
219 gr
= n_RANDOM_GETRANDOM_FUN(&a_aux_rand
->a
._dat
[o
], i
);
220 if(gr
== -1 && n_err_no
== n_ERR_NOSYS
)
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 # elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
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]];
254 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
255 # error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
258 /* As a fallback, a homebrew seed */
259 if(n_poption
& n_PO_D_V
)
260 n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
261 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
262 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
265 # ifdef HAVE_CLOCK_GETTIME
266 clock_gettime(CLOCK_REALTIME
, &ts
);
267 t
= (ui32_t
)ts
.tv_nsec
;
269 gettimeofday(&ts
, NULL
);
270 t
= (ui32_t
)ts
.tv_usec
;
273 t
= (t
>> 16) | (t
<< 16);
274 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
275 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
276 if(rnd
== 7 || rnd
== 17)
277 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
278 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
279 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
280 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
282 seed
^= n_prime_next(seed
);
286 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
293 a_aux_rand_get8(void){
296 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
297 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
298 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
299 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
300 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
304 a_aux_rand_weak(ui32_t seed
){
305 /* From "Random number generators: good ones are hard to find",
306 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
307 * October 1988, p. 1195.
308 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
315 seed
= (seed
* 16807) - (hi
* 2836);
320 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_TLS */
322 static struct a_aux_err_map
const *
323 a_aux_err_map_from_no(si32_t eno
){
326 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
327 struct a_aux_err_map
const *aemp
;
330 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
332 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
333 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
334 asz
!= 0; asz
>>= 1){
335 tmp
= &adat
[asz
>> 1];
336 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
337 aemp
= &a_aux_err_map
[(*tmp
)[1]];
354 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
356 #ifndef HAVE_SETLOCALE
359 setlocale(LC_ALL
, n_empty
);
360 n_mb_cur_max
= MB_CUR_MAX
;
361 # ifdef HAVE_NL_LANGINFO
365 if((cp
= nl_langinfo(CODESET
)) != NULL
)
366 /* (Will log during startup if user set that via -S) */
367 ok_vset(ttycharset
, cp
);
369 # endif /* HAVE_SETLOCALE */
371 # ifdef HAVE_C90AMEND1
372 if(n_mb_cur_max
> 1){
373 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
374 n_psonce
|= n_PSO_UNICODE
;
377 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
378 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
379 n_psonce
|= n_PSO_UNICODE
;
380 /* Reset possibly messed up state; luckily this also gives us an
381 * indication whether the encoding has locking shift state sequences */
382 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
383 n_psonce
|= n_PSO_ENC_MBSTATE
;
387 #endif /* HAVE_C90AMEND1 */
397 if((cp
= ok_vlook(screen
)) != NULL
){
398 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
411 n_pager_get(char const **env_addon
){
415 rv
= ok_vlook(PAGER
);
417 if(env_addon
!= NULL
){
419 /* Update the manual upon any changes:
420 * *colour-pager*, $PAGER */
421 if(strstr(rv
, "less") != NULL
){
422 if(getenv("LESS") == NULL
)
423 *env_addon
= "LESS=RXi";
424 }else if(strstr(rv
, "lv") != NULL
){
425 if(getenv("LV") == NULL
)
426 *env_addon
= "LV=-c";
434 page_or_print(FILE *fp
, size_t lines
)
442 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
446 rows
= (size_t)n_scrnheight
;
448 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
450 if (rows
> 0 && lines
== 0) {
451 while ((c
= getc(fp
)) != EOF
)
452 if (c
== '\n' && ++lines
>= rows
)
458 char const *env_add
[2], *pager
;
460 pager
= n_pager_get(&env_add
[0]);
462 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
468 while ((c
= getc(fp
)) != EOF
)
475 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
476 char const **adjusted_or_null
)
478 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
479 char const *cp
, *orig_name
;
480 enum protocol rv
= PROTO_UNKNOWN
;
483 if(name
[0] == '%' && name
[1] == ':')
487 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
491 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
492 if(!strncmp(name
, "file", sizeof("file") -1) ||
493 !strncmp(name
, "mbox", sizeof("mbox") -1))
495 else if(!strncmp(name
, "maildir", sizeof("maildir") -1)){
499 n_err(_("No Maildir directory support compiled in\n"));
501 }else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
505 n_err(_("No POP3 support compiled in\n"));
507 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
508 #if defined HAVE_POP3 && defined HAVE_TLS
511 n_err(_("No POP3S support compiled in\n"));
513 }else if(!strncmp(name
, "imap", sizeof("imap") -1)){
517 n_err(_("No IMAP support compiled in\n"));
519 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
520 #if defined HAVE_IMAP && defined HAVE_TLS
523 n_err(_("No IMAPS support compiled in\n"));
533 if(check_stat
|| try_hooks
){
534 struct n_file_type ft
;
539 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
540 memcpy(np
, name
, sz
+ 1);
542 if(!stat(name
, &stb
)){
543 if(S_ISDIR(stb
.st_mode
)
545 && (memcpy(&np
[sz
], "/tmp", 5),
546 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
547 (memcpy(&np
[sz
], "/new", 5),
548 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
549 (memcpy(&np
[sz
], "/cur", 5),
550 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
))
559 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
560 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
561 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
562 !asccasecmp(cp
, "maildir")){
566 n_err(_("*newfolders*: no Maildir directory support compiled in\n"));
573 if(adjusted_or_null
!= NULL
)
574 *adjusted_or_null
= orig_name
;
580 n_c_to_hex_base16(char store
[3], char c
){
581 static char const itoa16
[] = "0123456789ABCDEF";
585 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
586 c
= ((ui8_t
)c
>> 4) & 0x0F;
587 store
[0] = itoa16
[(ui8_t
)c
];
593 n_c_from_hex_base16(char const hex
[2]){
594 static ui8_t
const atoi16
[] = {
595 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
596 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
597 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
598 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
599 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
600 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
601 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
607 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
608 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
612 if ((i1
| i2
) & 0xF0u
)
626 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
627 enum n_idec_mode idm
, char const **endptr_or_null
){
630 enum n_idec_state rv
;
633 idm
&= n__IDEC_MODE_MASK
;
634 rv
= n_IDEC_STATE_NONE
| idm
;
643 assert(base
!= 1 && base
<= 36);
644 /*if(base == 1 || base > 36)
648 while(spacechar(*cbuf
))
649 if(*++cbuf
== '\0' || --clen
== 0)
655 rv
|= n_IDEC_STATE_SEEN_MINUS
;
658 if(*++cbuf
== '\0' || --clen
== 0)
663 /* Base detection/skip */
668 /* Support BASE#number prefix, where BASE is decimal 2-36 */
672 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
673 (((c2
= cbuf
[1]) == '#') ||
674 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
675 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
680 base
*= 10; /* xxx Inline atoi decimal base */
681 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
684 /* We do not interpret this as BASE#number at all if either we
685 * did not get a valid base or if the first char is not valid
686 * according to base, to comply to the latest interpretion of
687 * "prefix", see comment for standard prefixes below */
688 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
691 clen
-= 2, cbuf
+= 2;
693 clen
-= 3, cbuf
+= 3;
698 /* Character must be valid for base */
699 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
703 /* 0 always valid as is, fallback base 10 */
704 if(*++cbuf
== '\0' || --clen
== 0)
707 /* Base "detection" */
708 if(base
== 0 || base
== 2 || base
== 16){
719 if((base
& 16) == 0){
721 /* Char after prefix must be valid. However, after some error
722 * in the tor software all libraries (which had to) turned to
723 * an interpretation of the C standard which says that the
724 * prefix may optionally precede an otherwise valid sequence,
725 * which means that "0x" is not a STATE_INVAL error but gives
726 * a "0" result with a "STATE_BASE" error and a rest of "x" */
729 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
732 if(*++cbuf
== '\0' || --clen
== 0)
735 /* Character must be valid for base, invalid otherwise */
736 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
749 /* Character must be valid for base, _EBASE otherwise */
750 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
755 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
759 if(res
> UI64_MAX
- currc
)
769 if(*++cbuf
== '\0' || --clen
== 0)
772 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
781 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
782 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
783 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
784 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
785 default: uimask
= UI64_MAX
; break;
787 if((rv
& n_IDEC_MODE_SIGNED_TYPE
) &&
788 (!(rv
& n_IDEC_MODE_POW2BASE_UNSIGNED
) || !n_ISPOW2(base
)))
792 /* XXX never entered unless _SIGNED_TYPE! */
793 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
794 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
795 if(res
> uimask
+ 1){
804 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
805 rv
|= n_IDEC_STATE_EOVERFLOW
;
806 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
810 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
811 case n_IDEC_MODE_LIMIT_8BIT
:
812 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
813 *(si8_t
*)resp
= (si8_t
)res
;
815 *(ui8_t
*)resp
= (ui8_t
)res
;
817 case n_IDEC_MODE_LIMIT_16BIT
:
818 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
819 *(si16_t
*)resp
= (si16_t
)res
;
821 *(ui16_t
*)resp
= (ui16_t
)res
;
823 case n_IDEC_MODE_LIMIT_32BIT
:
824 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
825 *(si32_t
*)resp
= (si32_t
)res
;
827 *(ui32_t
*)resp
= (ui32_t
)res
;
830 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
831 *(si64_t
*)resp
= (si64_t
)res
;
833 *(ui64_t
*)resp
= (ui64_t
)res
;
837 if(endptr_or_null
!= NULL
)
838 *endptr_or_null
= cbuf
;
839 if(*cbuf
== '\0' || clen
== 0)
840 rv
|= n_IDEC_STATE_CONSUMED
;
845 rv
|= n_IDEC_STATE_EINVAL
;
848 /* Not a base error for terminator and whitespace! */
849 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
850 rv
|= n_IDEC_STATE_EBASE
;
854 /* Overflow error: consume input until bad character or length out */
856 if(*++cbuf
== '\0' || --clen
== 0)
858 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
863 rv
|= n_IDEC_STATE_EOVERFLOW
;
865 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
866 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
870 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
875 n_ienc_buf(char cbuf
[n_IENC_BUFFER_SIZE
], ui64_t value
, ui8_t base
,
876 enum n_ienc_mode iem
){
877 enum{a_ISNEG
= 1u<<n__IENC_MODE_SHIFT
};
884 iem
&= n__IENC_MODE_MASK
;
886 assert(base
!= 1 && base
<= 36);
887 /*if(base == 1 || base > 36){
892 *(rv
= &cbuf
[n_IENC_BUFFER_SIZE
-1]) = '\0';
893 itoa
= (iem
& n_IENC_MODE_LOWERCASE
) ? a_aux_ienc_itoa_lower
894 : a_aux_ienc_itoa_upper
;
896 if((si64_t
)value
< 0){
898 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
899 /* self->is_negative = TRU1; */
904 if((shiftmodu
= a_aux_ienc_shifts
[base
- 2]) != 0){
905 --base
; /* convert to mask */
907 *--rv
= itoa
[value
& base
];
911 if(!(iem
& n_IENC_MODE_NO_PREFIX
)){
912 /* self->before_prefix = cp; */
915 else if(shiftmodu
== 1)
917 else if(shiftmodu
!= 3){
918 ++base
; /* Reconvert from mask */
919 goto jnumber_sign_prefix
;
925 shiftmodu
= value
% base
;
927 *--rv
= itoa
[shiftmodu
];
930 if(!(iem
& n_IENC_MODE_NO_PREFIX
) && base
!= 10){
936 shiftmodu
= value
% base
;
938 *--rv
= itoa
[shiftmodu
];
942 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
947 else if(iem
& n_IENC_MODE_SIGNED_PLUS
)
949 else if(iem
& n_IENC_MODE_SIGNED_SPACE
)
963 n_torek_hash(char const *name
){
964 /* Chris Torek's hash */
969 for(h
= 0; (c
= *name
++) != '\0';)
976 n_torek_ihashn(char const *dat
, size_t len
){
977 /* See n_torek_hash() */
983 for(h
= 0; (c
= *dat
++) != '\0';)
984 h
= (h
* 33) + lowerconv(c
);
986 for(h
= 0; len
> 0; --len
){
988 h
= (h
* 33) + lowerconv(c
);
995 n_prime_next(ui32_t n
){
996 static ui32_t
const primes
[] = {
997 5, 11, 23, 47, 97, 157, 283,
998 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
999 131071, 262139, 524287, 1048573, 2097143, 4194301,
1000 8388593, 16777213, 33554393, 67108859, 134217689,
1001 268435399, 536870909, 1073741789, 2147483647
1006 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
1007 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
1008 : n_NELEM(primes
) / 2));
1010 do if((mprime
= primes
[i
]) > n
)
1012 while(++i
< n_NELEM(primes
));
1014 if(i
== n_NELEM(primes
) && mprime
< n
)
1021 n_getdeadletter(void){
1028 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
1029 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
1031 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1032 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
1037 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
1038 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
1046 n_nodename(bool_t mayoverride
){
1047 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1052 # ifdef HAVE_GETADDRINFO
1053 struct addrinfo hints
, *res
;
1055 struct hostent
*hent
;
1060 if(n_psonce
& n_PSO_REPRODUCIBLE
)
1061 hn
= n_UNCONST(n_reproducible_name
);
1062 else if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
1064 }else if((hn
= sys_hostname
) == NULL
){
1072 # ifdef HAVE_GETADDRINFO
1073 memset(&hints
, 0, sizeof hints
);
1074 hints
.ai_family
= AF_UNSPEC
;
1075 hints
.ai_flags
= AI_CANONNAME
;
1076 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
1077 if(res
->ai_canonname
!= NULL
){
1080 l
= strlen(res
->ai_canonname
) +1;
1081 hn
= n_lofi_alloc(l
);
1083 memcpy(hn
, res
->ai_canonname
, l
);
1088 hent
= gethostbyname(hn
);
1092 #endif /* HAVE_SOCKETS */
1096 struct n_string cnv
;
1098 n_string_creat(&cnv
);
1099 if(!n_idna_to_ascii(&cnv
, hn
, UIZ_MAX
))
1100 n_panic(_("The system hostname is invalid, "
1101 "IDNA conversion failed: %s\n"),
1102 n_shexp_quote_cp(hn
, FAL0
));
1103 sys_hostname
= n_string_cp(&cnv
);
1104 n_string_drop_ownership(&cnv
);
1105 /*n_string_gut(&cnv);*/
1108 sys_hostname
= sstrdup(hn
);
1116 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
1118 hostname
= sstrdup(hn
);
1125 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
1131 ilen
= strlen(ibuf
);
1135 if((rv
= (ilen
== 0)))
1137 if(ibuf
[ilen
] != '\0'){
1139 idna_utf8
= n_lofi_alloc(ilen
+1);
1140 memcpy(idna_utf8
, ibuf
, ilen
);
1141 idna_utf8
[ilen
] = '\0';
1146 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1147 if(n_psonce
& n_PSO_UNICODE
)
1149 idna_utf8
= n_UNCONST(ibuf
);
1150 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1151 else if((idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8",
1152 ok_vlook(ttycharset
), ibuf
)) == NULL
)
1156 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1161 f
= IDN2_NONTRANSITIONAL
;
1163 if((rc
= idn2_to_ascii_8z(idna_utf8
, &idna_ascii
, f
)) == IDN2_OK
){
1164 out
= n_string_assign_cp(out
, idna_ascii
);
1165 idn2_free(idna_ascii
);
1168 }else if(rc
== IDN2_DISALLOWED
&& f
!= IDN2_TRANSITIONAL
){
1169 f
= IDN2_TRANSITIONAL
;
1174 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1178 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1179 out
= n_string_assign_cp(out
, idna_ascii
);
1180 idn_free(idna_ascii
);
1186 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1187 ilen
= strlen(idna_utf8
);
1189 switch(idn_encodename(
1190 /* LOCALCONV changed meaning in v2 and is no longer available for
1191 * encoding. This makes sense, bu */
1193 # ifdef IDN_UNICODECONV /* v2 */
1194 IDN_ENCODE_APP
& ~IDN_UNICODECONV
1196 IDN_DELIMMAP
| IDN_LOCALMAP
| IDN_NAMEPREP
| IDN_IDNCONV
|
1197 IDN_LENCHECK
| IDN_ASCCHECK
1200 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1201 case idn_buffer_overflow
:
1202 ilen
+= HOST_NAME_MAX
+1;
1206 ilen
= strlen(out
->s_dat
);
1214 # error Unknown HAVE_IDNA
1218 n_lofi_free(n_UNCONST(ibuf
));
1219 out
= n_string_trunc(out
, ilen
);
1223 #endif /* HAVE_IDNA */
1226 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1228 char *indat
, *cp
, *oudat
;
1229 size_t i
, inlen
, oulen
;
1232 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1233 n_psonce
|= n_PSO_RANDOM_INIT
;
1235 if(n_poption
& n_PO_D_V
){
1238 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
1239 prngn
= "arc4random";
1240 #elif HAVE_RANDOM == n_RANDOM_IMPL_TLS
1241 prngn
= "*TLS RAND_*";
1242 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
1243 prngn
= "getrandom(2/3) + builtin ARC4";
1244 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
1245 prngn
= "/dev/urandom + builtin ARC4";
1246 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
1247 prngn
= "builtin ARC4";
1249 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
1251 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn
);
1254 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
1259 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1260 * with PAD stripped is still longer than what the user requests, easy way.
1261 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1262 * include the base64 PAD characters in our random string: give some pad */
1264 if((inlen
= i
% 3) != 0)
1273 inlen
= inlen
+ (inlen
<< 1);
1275 indat
= n_lofi_alloc(inlen
+1);
1277 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1278 #if HAVE_RANDOM == n_RANDOM_IMPL_TLS
1279 n_tls_rand_bytes(indat
, inlen
);
1280 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
1281 for(i
= inlen
; i
-- > 0;)
1282 indat
[i
] = (char)a_aux_rand_get8();
1284 for(cp
= indat
, i
= inlen
; i
> 0;){
1285 union {ui32_t i4
; char c
[4];} r
;
1288 r
.i4
= (ui32_t
)arc4random();
1289 switch((j
= i
& 3)){
1290 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1291 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1292 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1293 default: cp
[0] = r
.c
[0]; break;
1300 for(cp
= indat
, i
= inlen
; i
> 0;){
1301 union {ui32_t i4
; char c
[4];} r
;
1304 r
.i4
= ++*reprocnt_or_null
;
1305 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1315 switch((j
= i
& 3)){
1316 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1317 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1318 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1319 default: cp
[0] = r
.c
[0]; break;
1326 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1328 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1329 assert(b64
.l
>= len
);
1330 memcpy(dat
, b64
.s
, len
);
1342 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1346 dat
= n_autorec_alloc(len
+1);
1347 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1353 n_boolify(char const *inbuf
, uiz_t inlen
, bool_t emptyrv
){
1356 assert(inlen
== 0 || inbuf
!= NULL
);
1358 if(inlen
== UIZ_MAX
)
1359 inlen
= strlen(inbuf
);
1362 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1364 if((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1365 !ascncasecmp(inbuf
, "true", inlen
) ||
1366 !ascncasecmp(inbuf
, "yes", inlen
) ||
1367 !ascncasecmp(inbuf
, "on", inlen
))
1369 else if((inlen
== 1 &&
1370 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1371 !ascncasecmp(inbuf
, "false", inlen
) ||
1372 !ascncasecmp(inbuf
, "no", inlen
) ||
1373 !ascncasecmp(inbuf
, "off", inlen
))
1378 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1379 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1380 ) != n_IDEC_STATE_CONSUMED
)
1391 n_quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, bool_t emptyrv
){
1394 assert(inlen
== 0 || inbuf
!= NULL
);
1396 if(inlen
== UIZ_MAX
)
1397 inlen
= strlen(inbuf
);
1400 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1401 else if((rv
= n_boolify(inbuf
, inlen
, emptyrv
)) < FAL0
&&
1402 !ascncasecmp(inbuf
, "ask-", 4) &&
1403 (rv
= n_boolify(&inbuf
[4], inlen
- 4, emptyrv
)) >= FAL0
&&
1404 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1405 rv
= getapproval(prompt
, rv
);
1411 n_is_all_or_aster(char const *name
){
1415 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1420 FL
struct n_timespec
const *
1421 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1422 static struct n_timespec ts_now
;
1425 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1426 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1427 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1429 }else if(force_update
|| ts_now
.ts_sec
== 0){
1430 #ifdef HAVE_CLOCK_GETTIME
1433 clock_gettime(CLOCK_REALTIME
, &ts
);
1434 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1435 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1436 #elif defined HAVE_GETTIMEOFDAY
1439 gettimeofday(&tv
, NULL
);
1440 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1441 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1443 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1448 /* Just in case.. */
1449 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1456 time_current_update(struct time_current
*tc
, bool_t full_update
){
1458 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1467 if((tmp
= gmtime(&t
)) == NULL
){
1471 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1472 if((tmp
= localtime(&t
)) == NULL
){
1476 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1477 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1480 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1486 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1487 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1488 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1489 * ISO C requires us to use the above format string,
1490 * even if it will not fit in the buffer. Thus asctime_r
1491 * is _supposed_ to crash if the fields in tm are too large.
1492 * We follow this behavior and crash "gracefully" to warn
1493 * application developers that they may not be so lucky
1494 * on other implementations (e.g. stack smashing..).
1495 * So we need to do it on our own or the libc may kill us */
1496 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1498 si32_t y
, md
, th
, tm
, ts
;
1499 char const *wdn
, *mn
;
1500 struct tm
const *tmp
;
1503 if((tmp
= localtime_or_nil
) == NULL
){
1506 t
= (time_t)secsepoch
;
1508 if((tmp
= localtime(&t
)) == NULL
){
1509 /* TODO error log */
1515 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1517 wdn
= n_weekday_names
[4];
1518 mn
= n_month_names
[0];
1523 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1524 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1525 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1526 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1528 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1531 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1533 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1535 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1539 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1540 wdn
, mn
, md
, th
, tm
, ts
, y
);
1546 n_msleep(uiz_t millis
, bool_t ignint
){
1550 #ifdef HAVE_NANOSLEEP
1552 struct timespec ts
, trem
;
1555 ts
.tv_sec
= millis
/ 1000;
1556 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1558 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1560 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1563 #elif defined HAVE_SLEEP
1564 if((millis
/= 1000) == 0)
1566 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1569 # error Configuration should have detected a function for sleeping.
1577 n_err(char const *format
, ...){
1581 va_start(ap
, format
);
1583 if(n_psonce
& n_PSO_INTERACTIVE
)
1593 while(*format
== '\n'){
1595 putc('\n', n_stderr
);
1600 a_aux_err_linelen
= 0;
1602 if((len
= strlen(format
)) > 0){
1603 if(doname
|| a_aux_err_linelen
== 0){
1606 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1607 fputs(cp
, n_stderr
);
1609 vfprintf(n_stderr
, format
, ap
);
1614 if(format
[--len
] == '\n'){
1615 a_aux_err_linelen
= (i
-= ++len
);
1618 ++a_aux_err_linelen
;
1630 n_verr(char const *format
, va_list ap
){
1632 struct a_aux_err_node
*enp
;
1640 while(*format
== '\n'){
1641 putc('\n', n_stderr
);
1647 a_aux_err_linelen
= 0;
1649 if(n_psonce
& n_PSO_INTERACTIVE
){
1650 if((enp
= a_aux_err_tail
) != NULL
&&
1651 (enp
->ae_str
.s_len
> 0 &&
1652 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1653 n_string_push_c(&enp
->ae_str
, '\n');
1658 if((len
= strlen(format
)) == 0)
1661 n_pstate
|= n_PS_ERRORS_PROMPT
;
1664 if(doname
|| a_aux_err_linelen
== 0){
1667 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1668 fputs(cp
, n_stderr
);
1674 if(format
[--len
] == '\n'){
1675 a_aux_err_linelen
= (i
-= ++len
);
1678 ++a_aux_err_linelen
;
1683 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1685 vfprintf(n_stderr
, format
, ap
);
1689 n_LCTAV(ERRORS_MAX
> 3);
1691 /* Link it into the `errors' message ring */
1692 if((enp
= a_aux_err_tail
) == NULL
){
1694 enp
= n_alloc(sizeof *enp
);
1695 enp
->ae_next
= NULL
;
1696 n_string_creat(&enp
->ae_str
);
1697 if(a_aux_err_tail
!= NULL
)
1698 a_aux_err_tail
->ae_next
= enp
;
1700 a_aux_err_head
= enp
;
1701 a_aux_err_tail
= enp
;
1704 (enp
->ae_str
.s_len
> 0 &&
1705 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1706 if(a_aux_err_cnt
< ERRORS_MAX
)
1709 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1710 a_aux_err_tail
->ae_next
= enp
;
1711 a_aux_err_tail
= enp
;
1712 enp
->ae_next
= NULL
;
1713 n_string_trunc(&enp
->ae_str
, 0);
1716 # ifdef HAVE_N_VA_COPY
1719 imax
= n_MIN(LINESIZE
, 1024);
1721 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1722 # ifdef HAVE_N_VA_COPY
1730 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1731 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1732 # ifdef HAVE_N_VA_COPY
1739 if(UICMP(z
, i
, >=, imax
)){
1740 # ifdef HAVE_N_VA_COPY
1741 /* XXX Check overflow for upcoming LEN+++i! */
1742 n_string_trunc(&enp
->ae_str
, len
);
1745 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1750 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1752 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1754 #endif /* HAVE_ERRORS */
1762 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1766 va_start(ap
, format
);
1767 vfprintf(n_stderr
, format
, ap
);
1773 n_perr(char const *msg
, int errval
){
1784 e
= (errval
== 0) ? n_err_no
: errval
;
1785 n_err(fmt
, msg
, n_err_to_doc(e
));
1792 n_alert(char const *format
, ...){
1796 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1798 va_start(ap
, format
);
1807 n_panic(char const *format
, ...){
1811 if(a_aux_err_linelen
> 0){
1812 putc('\n', n_stderr
);
1813 a_aux_err_linelen
= 0;
1815 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1817 va_start(ap
, format
);
1818 vfprintf(n_stderr
, format
, ap
);
1821 putc('\n', n_stderr
);
1824 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1831 struct a_aux_err_node
*enp
;
1838 if(!asccasecmp(*argv
, "show"))
1840 if(!asccasecmp(*argv
, "clear"))
1844 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1848 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1854 if(a_aux_err_head
== NULL
){
1855 fprintf(n_stderr
, _("The error ring is empty\n"));
1859 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1861 fprintf(n_stderr
, _("tmpfile"));
1866 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1867 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1868 /* We don't know whether last string ended with NL; be simple XXX */
1871 page_or_print(fp
, 0);
1877 a_aux_err_tail
= NULL
;
1878 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1879 a_aux_err_linelen
= 0;
1880 while((enp
= a_aux_err_head
) != NULL
){
1881 a_aux_err_head
= enp
->ae_next
;
1882 n_string_gut(&enp
->ae_str
);
1887 #endif /* HAVE_ERRORS */
1890 n_err_to_doc(si32_t eno
){
1892 struct a_aux_err_map
const *aemp
;
1895 aemp
= a_aux_err_map_from_no(eno
);
1896 #ifdef HAVE_DOCSTRINGS
1897 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1899 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1906 n_err_to_name(si32_t eno
){
1908 struct a_aux_err_map
const *aemp
;
1911 aemp
= a_aux_err_map_from_no(eno
);
1912 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1918 n_err_from_name(char const *name
){
1919 struct a_aux_err_map
const *aemp
;
1920 ui32_t hash
, i
, j
, x
;
1924 hash
= n_torek_hash(name
);
1926 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1927 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1930 aemp
= &a_aux_err_map
[x
];
1931 if(aemp
->aem_hash
== hash
&&
1932 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1933 rv
= aemp
->aem_err_no
;
1937 if(++i
== a_AUX_ERR_REV_PRIME
){
1938 #ifdef a_AUX_ERR_REV_WRAPAROUND
1946 /* Have not found it. But wait, it could be that the user did, e.g.,
1947 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1948 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1949 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1950 ) == n_IDEC_STATE_CONSUMED
){
1951 aemp
= a_aux_err_map_from_no(rv
);
1952 rv
= aemp
->aem_err_no
;
1956 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1964 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1969 i
= regerror(e
, rep
, NULL
, 0) +1;
1970 cp
= n_autorec_alloc(i
);
1971 regerror(e
, rep
, cp
, i
);