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 HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
60 # include n_RANDOM_GETRANDOM_H
64 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
66 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
68 # include <idn-free.h>
69 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
74 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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[] (if HAVE_DOCSTRINGS) */
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
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 HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t
a_aux_rand_get8(void);
183 static ui32_t
a_aux_rand_weak(ui32_t seed
);
186 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
187 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
189 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
191 a_aux_rand_init(void){
192 # ifdef HAVE_CLOCK_GETTIME
197 union {int fd
; size_t i
;} u
;
201 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
203 # if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
204 /* getrandom(2) guarantees 256 without n_ERR_INTR..
205 * However, support sequential reading to avoid possible hangs that have
206 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
207 * HAVE_GETRANDOM is #defined) */
208 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
209 "Buffer too large to be served without n_ERR_INTR error");
210 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
211 "Buffer too small to serve used array indices");
215 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
218 gr
= n_RANDOM_GETRANDOM_FUN(&a_aux_rand
->a
._dat
[o
], i
);
219 if(gr
== -1 && n_err_no
== n_ERR_NOSYS
)
221 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
222 a_aux_rand
->a
._dat
[84]];
223 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
224 a_aux_rand
->a
._dat
[42]];
225 /* ..but be on the safe side */
232 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
238 # elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
239 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
242 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
243 sizeof(a_aux_rand
->a
._dat
)));
246 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
247 a_aux_rand
->a
._dat
[84]];
248 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
249 a_aux_rand
->a
._dat
[42]];
253 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
254 # error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
257 /* As a fallback, a homebrew seed */
258 if(n_poption
& n_PO_D_V
)
259 n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
260 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
261 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
264 # ifdef HAVE_CLOCK_GETTIME
265 clock_gettime(CLOCK_REALTIME
, &ts
);
266 t
= (ui32_t
)ts
.tv_nsec
;
268 gettimeofday(&ts
, NULL
);
269 t
= (ui32_t
)ts
.tv_usec
;
272 t
= (t
>> 16) | (t
<< 16);
273 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
274 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
275 if(rnd
== 7 || rnd
== 17)
276 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
277 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
278 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
279 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
281 seed
^= n_prime_next(seed
);
285 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
292 a_aux_rand_get8(void){
295 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
296 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
297 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
298 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
299 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
303 a_aux_rand_weak(ui32_t seed
){
304 /* From "Random number generators: good ones are hard to find",
305 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
306 * October 1988, p. 1195.
307 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
314 seed
= (seed
* 16807) - (hi
* 2836);
319 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_SSL */
321 static struct a_aux_err_map
const *
322 a_aux_err_map_from_no(si32_t eno
){
325 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
326 struct a_aux_err_map
const *aemp
;
329 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
331 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
332 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
333 asz
!= 0; asz
>>= 1){
334 tmp
= &adat
[asz
>> 1];
335 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
336 aemp
= &a_aux_err_map
[(*tmp
)[1]];
353 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
355 #ifndef HAVE_SETLOCALE
358 setlocale(LC_ALL
, n_empty
);
359 n_mb_cur_max
= MB_CUR_MAX
;
360 # ifdef HAVE_NL_LANGINFO
364 if((cp
= nl_langinfo(CODESET
)) != NULL
)
365 /* (Will log during startup if user set that via -S) */
366 ok_vset(ttycharset
, cp
);
368 # endif /* HAVE_SETLOCALE */
370 # ifdef HAVE_C90AMEND1
371 if(n_mb_cur_max
> 1){
372 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
373 n_psonce
|= n_PSO_UNICODE
;
376 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
377 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
378 n_psonce
|= n_PSO_UNICODE
;
379 /* Reset possibly messed up state; luckily this also gives us an
380 * indication whether the encoding has locking shift state sequences */
381 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
382 n_psonce
|= n_PSO_ENC_MBSTATE
;
386 #endif /* HAVE_C90AMEND1 */
396 if((cp
= ok_vlook(screen
)) != NULL
){
397 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
410 n_pager_get(char const **env_addon
){
414 rv
= ok_vlook(PAGER
);
416 if(env_addon
!= NULL
){
418 /* Update the manual upon any changes:
419 * *colour-pager*, $PAGER */
420 if(strstr(rv
, "less") != NULL
){
421 if(getenv("LESS") == NULL
)
422 *env_addon
= "LESS=RXi";
423 }else if(strstr(rv
, "lv") != NULL
){
424 if(getenv("LV") == NULL
)
425 *env_addon
= "LV=-c";
433 page_or_print(FILE *fp
, size_t lines
)
441 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
445 rows
= (size_t)n_scrnheight
;
447 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
449 if (rows
> 0 && lines
== 0) {
450 while ((c
= getc(fp
)) != EOF
)
451 if (c
== '\n' && ++lines
>= rows
)
457 char const *env_add
[2], *pager
;
459 pager
= n_pager_get(&env_add
[0]);
461 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
467 while ((c
= getc(fp
)) != EOF
)
474 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
475 char const **adjusted_or_null
)
477 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
478 char const *cp
, *orig_name
;
479 enum protocol rv
= PROTO_UNKNOWN
;
482 if(name
[0] == '%' && name
[1] == ':')
486 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
490 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
491 if(!strncmp(name
, "file", sizeof("file") -1) ||
492 !strncmp(name
, "mbox", sizeof("mbox") -1))
494 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
496 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
500 n_err(_("No POP3 support compiled in\n"));
502 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
503 #if defined HAVE_POP3 && defined HAVE_SSL
506 n_err(_("No POP3S support compiled in\n"));
509 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
513 n_err(_("No IMAP support compiled in\n"));
515 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
516 #if defined HAVE_IMAP && defined HAVE_SSL
519 n_err(_("No IMAPS support compiled in\n"));
529 if(check_stat
|| try_hooks
){
530 struct n_file_type ft
;
535 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
536 memcpy(np
, name
, sz
+ 1);
538 if(!stat(name
, &stb
)){
539 if(S_ISDIR(stb
.st_mode
) &&
540 (memcpy(&np
[sz
], "/tmp", 5),
541 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
542 (memcpy(&np
[sz
], "/new", 5),
543 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
544 (memcpy(&np
[sz
], "/cur", 5),
545 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
547 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
548 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
549 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
550 !asccasecmp(cp
, "maildir"))
556 if(adjusted_or_null
!= NULL
)
557 *adjusted_or_null
= orig_name
;
563 n_c_to_hex_base16(char store
[3], char c
){
564 static char const itoa16
[] = "0123456789ABCDEF";
568 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
569 c
= ((ui8_t
)c
>> 4) & 0x0F;
570 store
[0] = itoa16
[(ui8_t
)c
];
576 n_c_from_hex_base16(char const hex
[2]){
577 static ui8_t
const atoi16
[] = {
578 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
579 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
580 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
581 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
582 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
583 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
584 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
590 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
591 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
595 if ((i1
| i2
) & 0xF0u
)
609 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
610 enum n_idec_mode idm
, char const **endptr_or_null
){
613 enum n_idec_state rv
;
616 idm
&= n__IDEC_MODE_MASK
;
617 rv
= n_IDEC_STATE_NONE
| idm
;
626 assert(base
!= 1 && base
<= 36);
627 /*if(base == 1 || base > 36)
631 while(spacechar(*cbuf
))
632 if(*++cbuf
== '\0' || --clen
== 0)
638 rv
|= n_IDEC_STATE_SEEN_MINUS
;
641 if(*++cbuf
== '\0' || --clen
== 0)
646 /* Base detection/skip */
651 /* Support BASE#number prefix, where BASE is decimal 2-36 */
655 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
656 (((c2
= cbuf
[1]) == '#') ||
657 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
658 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
663 base
*= 10; /* xxx Inline atoi decimal base */
664 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
667 /* We do not interpret this as BASE#number at all if either we
668 * did not get a valid base or if the first char is not valid
669 * according to base, to comply to the latest interpretion of
670 * "prefix", see comment for standard prefixes below */
671 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
674 clen
-= 2, cbuf
+= 2;
676 clen
-= 3, cbuf
+= 3;
681 /* Character must be valid for base */
682 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
686 /* 0 always valid as is, fallback base 10 */
687 if(*++cbuf
== '\0' || --clen
== 0)
690 /* Base "detection" */
691 if(base
== 0 || base
== 2 || base
== 16){
702 if((base
& 16) == 0){
704 /* Char after prefix must be valid. However, after some error
705 * in the tor software all libraries (which had to) turned to
706 * an interpretation of the C standard which says that the
707 * prefix may optionally precede an otherwise valid sequence,
708 * which means that "0x" is not a STATE_INVAL error but gives
709 * a "0" result with a "STATE_BASE" error and a rest of "x" */
712 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
715 if(*++cbuf
== '\0' || --clen
== 0)
718 /* Character must be valid for base, invalid otherwise */
719 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
732 /* Character must be valid for base, _EBASE otherwise */
733 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
738 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
742 if(res
> UI64_MAX
- currc
)
752 if(*++cbuf
== '\0' || --clen
== 0)
755 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
764 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
765 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
766 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
767 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
768 default: uimask
= UI64_MAX
; break;
770 if((rv
& n_IDEC_MODE_SIGNED_TYPE
) &&
771 (!(rv
& n_IDEC_MODE_POW2BASE_UNSIGNED
) || !n_ISPOW2(base
)))
775 /* XXX never entered unless _SIGNED_TYPE! */
776 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
777 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
778 if(res
> uimask
+ 1){
787 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
788 rv
|= n_IDEC_STATE_EOVERFLOW
;
789 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
793 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
794 case n_IDEC_MODE_LIMIT_8BIT
:
795 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
796 *(si8_t
*)resp
= (si8_t
)res
;
798 *(ui8_t
*)resp
= (ui8_t
)res
;
800 case n_IDEC_MODE_LIMIT_16BIT
:
801 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
802 *(si16_t
*)resp
= (si16_t
)res
;
804 *(ui16_t
*)resp
= (ui16_t
)res
;
806 case n_IDEC_MODE_LIMIT_32BIT
:
807 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
808 *(si32_t
*)resp
= (si32_t
)res
;
810 *(ui32_t
*)resp
= (ui32_t
)res
;
813 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
814 *(si64_t
*)resp
= (si64_t
)res
;
816 *(ui64_t
*)resp
= (ui64_t
)res
;
820 if(endptr_or_null
!= NULL
)
821 *endptr_or_null
= cbuf
;
822 if(*cbuf
== '\0' || clen
== 0)
823 rv
|= n_IDEC_STATE_CONSUMED
;
828 rv
|= n_IDEC_STATE_EINVAL
;
831 /* Not a base error for terminator and whitespace! */
832 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
833 rv
|= n_IDEC_STATE_EBASE
;
837 /* Overflow error: consume input until bad character or length out */
839 if(*++cbuf
== '\0' || --clen
== 0)
841 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
846 rv
|= n_IDEC_STATE_EOVERFLOW
;
848 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
849 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
853 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
858 n_ienc_buf(char cbuf
[n_IENC_BUFFER_SIZE
], ui64_t value
, ui8_t base
,
859 enum n_ienc_mode iem
){
860 enum{a_ISNEG
= 1u<<n__IENC_MODE_SHIFT
};
867 iem
&= n__IENC_MODE_MASK
;
869 assert(base
!= 1 && base
<= 36);
870 /*if(base == 1 || base > 36){
875 rv
= &cbuf
[n_IENC_BUFFER_SIZE
];
877 itoa
= (iem
& n_IENC_MODE_LOWERCASE
) ? a_aux_ienc_itoa_lower
878 : a_aux_ienc_itoa_upper
;
880 if((si64_t
)value
< 0){
882 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
883 /* self->is_negative = TRU1; */
888 if((shiftmodu
= a_aux_ienc_shifts
[base
- 2]) != 0){
889 --base
; /* convert to mask */
891 *--rv
= itoa
[value
& base
];
895 if(!(iem
& n_IENC_MODE_NO_PREFIX
)){
896 /* self->before_prefix = cp; */
899 else if(shiftmodu
== 1)
901 else if(shiftmodu
!= 3){
902 ++base
; /* Reconvert from mask */
903 goto jnumber_sign_prefix
;
909 shiftmodu
= value
% base
;
911 *--rv
= itoa
[shiftmodu
];
914 if(!(iem
& n_IENC_MODE_NO_PREFIX
) && base
!= 10){
920 shiftmodu
= value
% base
;
922 *--rv
= itoa
[shiftmodu
];
926 if(iem
& n_IENC_MODE_SIGNED_TYPE
){
931 else if(iem
& n_IENC_MODE_SIGNED_PLUS
)
933 else if(iem
& n_IENC_MODE_SIGNED_SPACE
)
947 n_torek_hash(char const *name
){
948 /* Chris Torek's hash */
953 for(h
= 0; (c
= *name
++) != '\0';)
960 n_torek_ihashn(char const *dat
, size_t len
){
961 /* See n_torek_hash() */
967 for(h
= 0; (c
= *dat
++) != '\0';)
968 h
= (h
* 33) + lowerconv(c
);
970 for(h
= 0; len
> 0; --len
){
972 h
= (h
* 33) + lowerconv(c
);
979 n_prime_next(ui32_t n
){
980 static ui32_t
const primes
[] = {
981 5, 11, 23, 47, 97, 157, 283,
982 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
983 131071, 262139, 524287, 1048573, 2097143, 4194301,
984 8388593, 16777213, 33554393, 67108859, 134217689,
985 268435399, 536870909, 1073741789, 2147483647
990 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
991 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
992 : n_NELEM(primes
) / 2));
994 do if((mprime
= primes
[i
]) > n
)
996 while(++i
< n_NELEM(primes
));
998 if(i
== n_NELEM(primes
) && mprime
< n
)
1005 n_getdeadletter(void){
1012 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
1013 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
1015 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1016 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
1021 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
1022 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
1030 n_nodename(bool_t mayoverride
){
1031 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1036 # ifdef HAVE_GETADDRINFO
1037 struct addrinfo hints
, *res
;
1039 struct hostent
*hent
;
1044 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
1046 }else if((hn
= sys_hostname
) == NULL
){
1054 # ifdef HAVE_GETADDRINFO
1055 memset(&hints
, 0, sizeof hints
);
1056 hints
.ai_family
= AF_UNSPEC
;
1057 hints
.ai_flags
= AI_CANONNAME
;
1058 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
1059 if(res
->ai_canonname
!= NULL
){
1062 l
= strlen(res
->ai_canonname
) +1;
1063 hn
= n_lofi_alloc(l
);
1065 memcpy(hn
, res
->ai_canonname
, l
);
1070 hent
= gethostbyname(hn
);
1074 #endif /* HAVE_SOCKETS */
1078 struct n_string cnv
;
1080 n_string_creat(&cnv
);
1081 if(!n_idna_to_ascii(&cnv
, hn
, UIZ_MAX
))
1082 n_panic(_("The system hostname is invalid, "
1083 "IDNA conversion failed: %s\n"),
1084 n_shexp_quote_cp(hn
, FAL0
));
1085 sys_hostname
= n_string_cp(&cnv
);
1086 n_string_drop_ownership(&cnv
);
1087 /*n_string_gut(&cnv);*/
1090 sys_hostname
= sstrdup(hn
);
1098 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
1100 hostname
= sstrdup(hn
);
1107 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
1113 ilen
= strlen(ibuf
);
1117 if((rv
= (ilen
== 0)))
1119 if(ibuf
[ilen
] != '\0'){
1121 idna_utf8
= n_lofi_alloc(ilen
+1);
1122 memcpy(idna_utf8
, ibuf
, ilen
);
1123 idna_utf8
[ilen
] = '\0';
1128 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1129 if(n_psonce
& n_PSO_UNICODE
)
1131 idna_utf8
= n_UNCONST(ibuf
);
1132 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1133 else if((idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8",
1134 ok_vlook(ttycharset
), ibuf
)) == NULL
)
1138 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1143 f
= IDN2_NONTRANSITIONAL
;
1145 if((rc
= idn2_to_ascii_8z(idna_utf8
, &idna_ascii
, f
)) == IDN2_OK
){
1146 out
= n_string_assign_cp(out
, idna_ascii
);
1147 idn2_free(idna_ascii
);
1150 }else if(rc
== IDN2_DISALLOWED
&& f
!= IDN2_TRANSITIONAL
){
1151 f
= IDN2_TRANSITIONAL
;
1156 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1160 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1161 out
= n_string_assign_cp(out
, idna_ascii
);
1162 idn_free(idna_ascii
);
1168 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1169 ilen
= strlen(idna_utf8
);
1171 switch(idn_encodename(
1172 /* LOCALCONV changed meaning in v2 and is no longer available for
1173 * encoding. This makes sense, bu */
1174 (IDN_ENCODE_APP
& ~(
1175 # ifdef IDN_UNICODECONV
1176 IDN_UNICODECONV
/* v2 */
1181 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1182 case idn_buffer_overflow
:
1183 ilen
+= HOST_NAME_MAX
+1;
1187 ilen
= strlen(out
->s_dat
);
1195 # error Unknown HAVE_IDNA
1199 n_lofi_free(n_UNCONST(ibuf
));
1200 out
= n_string_trunc(out
, ilen
);
1204 #endif /* HAVE_IDNA */
1207 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1209 char *indat
, *cp
, *oudat
;
1210 size_t i
, inlen
, oulen
;
1213 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1214 n_psonce
|= n_PSO_RANDOM_INIT
;
1216 if(n_poption
& n_PO_D_V
){
1219 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
1220 prngn
= "arc4random";
1221 #elif HAVE_RANDOM == n_RANDOM_IMPL_SSL
1222 prngn
= "*SSL RAND_*";
1223 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
1224 prngn
= "getrandom(2/3) + builtin ARC4";
1225 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
1226 prngn
= "/dev/urandom + builtin ARC4";
1227 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
1228 prngn
= "builtin ARC4";
1230 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
1232 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn
);
1235 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_SSL
1240 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1241 * with PAD stripped is still longer than what the user requests, easy way.
1242 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1243 * include the base64 PAD characters in our random string: give some pad */
1245 if((inlen
= i
% 3) != 0)
1254 inlen
= inlen
+ (inlen
<< 1);
1256 indat
= n_lofi_alloc(inlen
+1);
1258 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1259 #if HAVE_RANDOM == n_RANDOM_IMPL_SSL
1260 ssl_rand_bytes(indat
, inlen
);
1261 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
1262 for(i
= inlen
; i
-- > 0;)
1263 indat
[i
] = (char)a_aux_rand_get8();
1265 for(cp
= indat
, i
= inlen
; i
> 0;){
1266 union {ui32_t i4
; char c
[4];} r
;
1269 r
.i4
= (ui32_t
)arc4random();
1270 switch((j
= i
& 3)){
1271 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1272 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1273 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1274 default: cp
[0] = r
.c
[0]; break;
1281 for(cp
= indat
, i
= inlen
; i
> 0;){
1282 union {ui32_t i4
; char c
[4];} r
;
1285 r
.i4
= ++*reprocnt_or_null
;
1286 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1296 switch((j
= i
& 3)){
1297 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1298 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1299 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1300 default: cp
[0] = r
.c
[0]; break;
1307 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1309 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1310 assert(b64
.l
>= len
);
1311 memcpy(dat
, b64
.s
, len
);
1323 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1327 dat
= n_autorec_alloc(len
+1);
1328 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1334 n_boolify(char const *inbuf
, uiz_t inlen
, bool_t emptyrv
){
1337 assert(inlen
== 0 || inbuf
!= NULL
);
1339 if(inlen
== UIZ_MAX
)
1340 inlen
= strlen(inbuf
);
1343 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1345 if((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1346 !ascncasecmp(inbuf
, "true", inlen
) ||
1347 !ascncasecmp(inbuf
, "yes", inlen
) ||
1348 !ascncasecmp(inbuf
, "on", inlen
))
1350 else if((inlen
== 1 &&
1351 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1352 !ascncasecmp(inbuf
, "false", inlen
) ||
1353 !ascncasecmp(inbuf
, "no", inlen
) ||
1354 !ascncasecmp(inbuf
, "off", inlen
))
1359 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1360 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1361 ) != n_IDEC_STATE_CONSUMED
)
1372 n_quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, bool_t emptyrv
){
1375 assert(inlen
== 0 || inbuf
!= NULL
);
1377 if(inlen
== UIZ_MAX
)
1378 inlen
= strlen(inbuf
);
1381 rv
= (emptyrv
>= FAL0
) ? (emptyrv
== FAL0
? FAL0
: TRU1
) : TRU2
;
1382 else if((rv
= n_boolify(inbuf
, inlen
, emptyrv
)) < FAL0
&&
1383 !ascncasecmp(inbuf
, "ask-", 4) &&
1384 (rv
= n_boolify(&inbuf
[4], inlen
- 4, emptyrv
)) >= FAL0
&&
1385 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1386 rv
= getapproval(prompt
, rv
);
1392 n_is_all_or_aster(char const *name
){
1396 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1401 FL
struct n_timespec
const *
1402 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1403 static struct n_timespec ts_now
;
1406 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1407 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1408 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1410 }else if(force_update
|| ts_now
.ts_sec
== 0){
1411 #ifdef HAVE_CLOCK_GETTIME
1414 clock_gettime(CLOCK_REALTIME
, &ts
);
1415 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1416 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1417 #elif defined HAVE_GETTIMEOFDAY
1420 gettimeofday(&tv
, NULL
);
1421 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1422 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1424 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1429 /* Just in case.. */
1430 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1437 time_current_update(struct time_current
*tc
, bool_t full_update
){
1439 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1448 if((tmp
= gmtime(&t
)) == NULL
){
1452 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1453 if((tmp
= localtime(&t
)) == NULL
){
1457 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1458 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1461 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1467 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1468 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1469 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1470 * ISO C requires us to use the above format string,
1471 * even if it will not fit in the buffer. Thus asctime_r
1472 * is _supposed_ to crash if the fields in tm are too large.
1473 * We follow this behavior and crash "gracefully" to warn
1474 * application developers that they may not be so lucky
1475 * on other implementations (e.g. stack smashing..).
1476 * So we need to do it on our own or the libc may kill us */
1477 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1479 si32_t y
, md
, th
, tm
, ts
;
1480 char const *wdn
, *mn
;
1481 struct tm
const *tmp
;
1484 if((tmp
= localtime_or_nil
) == NULL
){
1487 t
= (time_t)secsepoch
;
1489 if((tmp
= localtime(&t
)) == NULL
){
1490 /* TODO error log */
1496 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1498 wdn
= n_weekday_names
[4];
1499 mn
= n_month_names
[0];
1504 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1505 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1506 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1507 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1509 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1512 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1514 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1516 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1520 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1521 wdn
, mn
, md
, th
, tm
, ts
, y
);
1527 n_msleep(uiz_t millis
, bool_t ignint
){
1531 #ifdef HAVE_NANOSLEEP
1533 struct timespec ts
, trem
;
1536 ts
.tv_sec
= millis
/ 1000;
1537 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1539 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1541 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1544 #elif defined HAVE_SLEEP
1545 if((millis
/= 1000) == 0)
1547 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1550 # error Configuration should have detected a function for sleeping.
1558 n_err(char const *format
, ...){
1562 va_start(ap
, format
);
1564 if(n_psonce
& n_PSO_INTERACTIVE
)
1574 while(*format
== '\n'){
1576 putc('\n', n_stderr
);
1581 a_aux_err_linelen
= 0;
1583 if((len
= strlen(format
)) > 0){
1584 if(doname
|| a_aux_err_linelen
== 0){
1587 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1588 fputs(cp
, n_stderr
);
1590 vfprintf(n_stderr
, format
, ap
);
1595 if(format
[--len
] == '\n'){
1596 a_aux_err_linelen
= (i
-= ++len
);
1599 ++a_aux_err_linelen
;
1611 n_verr(char const *format
, va_list ap
){
1613 struct a_aux_err_node
*enp
;
1621 while(*format
== '\n'){
1622 putc('\n', n_stderr
);
1628 a_aux_err_linelen
= 0;
1630 if(n_psonce
& n_PSO_INTERACTIVE
){
1631 if((enp
= a_aux_err_tail
) != NULL
&&
1632 (enp
->ae_str
.s_len
> 0 &&
1633 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1634 n_string_push_c(&enp
->ae_str
, '\n');
1639 if((len
= strlen(format
)) == 0)
1642 n_pstate
|= n_PS_ERRORS_PROMPT
;
1645 if(doname
|| a_aux_err_linelen
== 0){
1648 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1649 fputs(cp
, n_stderr
);
1655 if(format
[--len
] == '\n'){
1656 a_aux_err_linelen
= (i
-= ++len
);
1659 ++a_aux_err_linelen
;
1664 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1666 vfprintf(n_stderr
, format
, ap
);
1670 n_LCTAV(ERRORS_MAX
> 3);
1672 /* Link it into the `errors' message ring */
1673 if((enp
= a_aux_err_tail
) == NULL
){
1675 enp
= smalloc(sizeof *enp
);
1676 enp
->ae_next
= NULL
;
1677 n_string_creat(&enp
->ae_str
);
1678 if(a_aux_err_tail
!= NULL
)
1679 a_aux_err_tail
->ae_next
= enp
;
1681 a_aux_err_head
= enp
;
1682 a_aux_err_tail
= enp
;
1685 (enp
->ae_str
.s_len
> 0 &&
1686 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1687 if(a_aux_err_cnt
< ERRORS_MAX
)
1690 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1691 a_aux_err_tail
->ae_next
= enp
;
1692 a_aux_err_tail
= enp
;
1693 enp
->ae_next
= NULL
;
1694 n_string_trunc(&enp
->ae_str
, 0);
1697 # ifdef HAVE_N_VA_COPY
1700 imax
= n_MIN(LINESIZE
, 1024);
1702 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1703 # ifdef HAVE_N_VA_COPY
1711 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1712 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1713 # ifdef HAVE_N_VA_COPY
1720 if(UICMP(z
, i
, >=, imax
)){
1721 # ifdef HAVE_N_VA_COPY
1722 /* XXX Check overflow for upcoming LEN+++i! */
1723 n_string_trunc(&enp
->ae_str
, len
);
1726 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1731 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1733 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1735 #endif /* HAVE_ERRORS */
1743 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1747 va_start(ap
, format
);
1748 vfprintf(n_stderr
, format
, ap
);
1754 n_perr(char const *msg
, int errval
){
1765 e
= (errval
== 0) ? n_err_no
: errval
;
1766 n_err(fmt
, msg
, n_err_to_doc(e
));
1773 n_alert(char const *format
, ...){
1777 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1779 va_start(ap
, format
);
1788 n_panic(char const *format
, ...){
1792 if(a_aux_err_linelen
> 0){
1793 putc('\n', n_stderr
);
1794 a_aux_err_linelen
= 0;
1796 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1798 va_start(ap
, format
);
1799 vfprintf(n_stderr
, format
, ap
);
1802 putc('\n', n_stderr
);
1805 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1812 struct a_aux_err_node
*enp
;
1819 if(!asccasecmp(*argv
, "show"))
1821 if(!asccasecmp(*argv
, "clear"))
1825 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1829 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1835 if(a_aux_err_head
== NULL
){
1836 fprintf(n_stderr
, _("The error ring is empty\n"));
1840 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1842 fprintf(n_stderr
, _("tmpfile"));
1847 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1848 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1849 /* We don't know whether last string ended with NL; be simple XXX */
1852 page_or_print(fp
, 0);
1858 a_aux_err_tail
= NULL
;
1859 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1860 a_aux_err_linelen
= 0;
1861 while((enp
= a_aux_err_head
) != NULL
){
1862 a_aux_err_head
= enp
->ae_next
;
1863 n_string_gut(&enp
->ae_str
);
1868 #endif /* HAVE_ERRORS */
1871 n_err_to_doc(si32_t eno
){
1873 struct a_aux_err_map
const *aemp
;
1876 aemp
= a_aux_err_map_from_no(eno
);
1877 #ifdef HAVE_DOCSTRINGS
1878 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1880 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1887 n_err_to_name(si32_t eno
){
1889 struct a_aux_err_map
const *aemp
;
1892 aemp
= a_aux_err_map_from_no(eno
);
1893 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1899 n_err_from_name(char const *name
){
1900 struct a_aux_err_map
const *aemp
;
1901 ui32_t hash
, i
, j
, x
;
1905 hash
= n_torek_hash(name
);
1907 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1908 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1911 aemp
= &a_aux_err_map
[x
];
1912 if(aemp
->aem_hash
== hash
&&
1913 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1914 rv
= aemp
->aem_err_no
;
1918 if(++i
== a_AUX_ERR_REV_PRIME
){
1919 #ifdef a_AUX_ERR_REV_WRAPAROUND
1927 /* Have not found it. But wait, it could be that the user did, e.g.,
1928 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1929 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1930 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1931 ) == n_IDEC_STATE_CONSUMED
){
1932 aemp
= a_aux_err_map_from_no(rv
);
1933 rv
= aemp
->aem_err_no
;
1937 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1945 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1950 i
= regerror(e
, rep
, NULL
, 0) +1;
1952 regerror(e
, rep
, cp
, i
);