1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions that don't fit anywhere else.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
42 #include <sys/utsname.h>
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
59 #if defined HAVE_GETRANDOM && !n_RANDOM_USE_XSSL
60 # include HAVE_GETRANDOM_HEADER
64 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
66 # include <idn-free.h>
67 # include <stringprep.h>
68 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
73 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
81 ui8_t b8
[sizeof(struct rand_arc4
)];
82 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
87 struct a_aux_err_node
{
88 struct a_aux_err_node
*ae_next
;
89 struct n_string ae_str
;
94 ui32_t aem_hash
; /* Hash of name */
95 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
96 ui32_t aem_docoff
; /* Into a_aux_err docs[] */
97 si32_t aem_err_no
; /* The OS error value for this one */
100 static ui8_t
const a_aux_idec_atoi
[256] = {
101 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
102 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
103 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
104 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
105 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
106 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
108 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
109 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
110 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
111 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
112 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
113 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
114 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
115 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
117 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
121 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
122 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
123 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
124 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
125 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
126 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
129 #define a_X(X) ((ui64_t)-1 / (X))
130 static ui64_t
const a_aux_idec_cutlimit
[35] = {
131 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
132 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
133 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
134 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
135 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
139 /* Include the constant make-errors.sh output */
140 #include <gen-errors.h>
142 /* And these things come from mk-config.h (config-time make-errors.sh output) */
143 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
145 #define a_X(N,I) {N,I},
146 n__ERR_NUMBER_TO_MAPOFF
150 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
151 static union rand_state
*a_aux_rand
;
154 /* Error ring, for `errors' */
156 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
157 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
159 static size_t a_aux_err_linelen
;
161 /* Our ARC4 random generator with its completely unacademical pseudo
162 * initialization (shall /dev/urandom fail) */
163 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
164 static void a_aux_rand_init(void);
165 n_INLINE ui8_t
a_aux_rand_get8(void);
166 # ifndef HAVE_GETRANDOM
167 static ui32_t
a_aux_rand_weak(ui32_t seed
);
171 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
172 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
174 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
176 a_aux_rand_init(void){
177 # ifndef HAVE_GETRANDOM
178 # ifdef HAVE_CLOCK_GETTIME
183 union {int fd
; size_t i
;} u
;
188 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
190 # ifdef HAVE_GETRANDOM
191 /* getrandom(2) guarantees 256 without n_ERR_INTR..
192 * However, support sequential reading to avoid possible hangs that have
193 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
194 * HAVE_GETRANDOM is #defined) */
195 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
196 "Buffer too large to be served without n_ERR_INTR error");
197 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
198 "Buffer too small to serve used array indices");
202 for(o
= 0, i
= sizeof a_aux_rand
->a
._dat
;;){
205 gr
= HAVE_GETRANDOM(&a_aux_rand
->a
._dat
[o
], i
);
206 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
207 a_aux_rand
->a
._dat
[84]];
208 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
209 a_aux_rand
->a
._dat
[42]];
210 /* ..but be on the safe side */
217 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
223 # else /* HAVE_GETRANDOM */
224 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
227 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
228 sizeof(a_aux_rand
->a
._dat
)));
231 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
232 a_aux_rand
->a
._dat
[84]];
233 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
234 a_aux_rand
->a
._dat
[42]];
239 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
240 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
243 # ifdef HAVE_CLOCK_GETTIME
244 clock_gettime(CLOCK_REALTIME
, &ts
);
245 t
= (ui32_t
)ts
.tv_nsec
;
247 gettimeofday(&ts
, NULL
);
248 t
= (ui32_t
)ts
.tv_usec
;
251 t
= (t
>> 16) | (t
<< 16);
252 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
253 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
254 if(rnd
== 7 || rnd
== 17)
255 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
256 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
257 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
258 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
260 seed
^= n_prime_next(seed
);
264 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
267 # endif /* !HAVE_GETRANDOM */
272 a_aux_rand_get8(void){
275 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
276 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
277 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
278 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
279 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
282 # ifndef HAVE_GETRANDOM
284 a_aux_rand_weak(ui32_t seed
){
285 /* From "Random number generators: good ones are hard to find",
286 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
287 * October 1988, p. 1195.
288 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
295 seed
= (seed
* 16807) - (hi
* 2836);
300 # endif /* HAVE_GETRANDOM */
301 #endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */
303 static struct a_aux_err_map
const *
304 a_aux_err_map_from_no(si32_t eno
){
307 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
308 struct a_aux_err_map
const *aemp
;
311 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
313 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
314 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
315 asz
!= 0; asz
>>= 1){
316 tmp
= &adat
[asz
>> 1];
317 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
318 aemp
= &a_aux_err_map
[(*tmp
)[1]];
335 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
337 #ifndef HAVE_SETLOCALE
340 setlocale(LC_ALL
, n_empty
);
341 n_mb_cur_max
= MB_CUR_MAX
;
342 # ifdef HAVE_NL_LANGINFO
346 if((cp
= nl_langinfo(CODESET
)) != NULL
)
347 /* (Will log during startup if user set that via -S) */
348 ok_vset(ttycharset
, cp
);
350 # endif /* HAVE_SETLOCALE */
352 # ifdef HAVE_C90AMEND1
353 if(n_mb_cur_max
> 1){
354 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
355 n_psonce
|= n_PSO_UNICODE
;
358 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
359 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
360 n_psonce
|= n_PSO_UNICODE
;
361 /* Reset possibly messed up state; luckily this also gives us an
362 * indication whether the encoding has locking shift state sequences */
363 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
364 n_psonce
|= n_PSO_ENC_MBSTATE
;
368 #endif /* HAVE_C90AMEND1 */
378 if((cp
= ok_vlook(screen
)) != NULL
){
379 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
392 n_pager_get(char const **env_addon
){
396 rv
= ok_vlook(PAGER
);
398 if(env_addon
!= NULL
){
400 /* Update the manual upon any changes:
401 * *colour-pager*, $PAGER */
402 if(strstr(rv
, "less") != NULL
){
403 if(getenv("LESS") == NULL
)
404 *env_addon
= "LESS=RXi";
405 }else if(strstr(rv
, "lv") != NULL
){
406 if(getenv("LV") == NULL
)
407 *env_addon
= "LV=-c";
415 page_or_print(FILE *fp
, size_t lines
)
423 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
427 rows
= (size_t)n_scrnheight
;
429 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
431 if (rows
> 0 && lines
== 0) {
432 while ((c
= getc(fp
)) != EOF
)
433 if (c
== '\n' && ++lines
>= rows
)
439 char const *env_add
[2], *pager
;
441 pager
= n_pager_get(&env_add
[0]);
443 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
449 while ((c
= getc(fp
)) != EOF
)
456 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
457 char const **adjusted_or_null
)
459 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
460 char const *cp
, *orig_name
;
461 enum protocol rv
= PROTO_UNKNOWN
;
464 if(name
[0] == '%' && name
[1] == ':')
468 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
472 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
473 if(!strncmp(name
, "file", sizeof("file") -1) ||
474 !strncmp(name
, "mbox", sizeof("mbox") -1))
476 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
478 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
482 n_err(_("No POP3 support compiled in\n"));
484 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
485 #if defined HAVE_POP3 && defined HAVE_SSL
488 n_err(_("No POP3S support compiled in\n"));
491 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
495 n_err(_("No IMAP support compiled in\n"));
497 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
498 #if defined HAVE_IMAP && defined HAVE_SSL
501 n_err(_("No IMAPS support compiled in\n"));
511 if(check_stat
|| try_hooks
){
512 struct n_file_type ft
;
517 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
518 memcpy(np
, name
, sz
+ 1);
520 if(!stat(name
, &stb
)){
521 if(S_ISDIR(stb
.st_mode
) &&
522 (memcpy(&np
[sz
], "/tmp", 5),
523 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
524 (memcpy(&np
[sz
], "/new", 5),
525 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
526 (memcpy(&np
[sz
], "/cur", 5),
527 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
529 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
530 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
531 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
532 !asccasecmp(cp
, "maildir"))
538 if(adjusted_or_null
!= NULL
)
539 *adjusted_or_null
= orig_name
;
545 n_c_to_hex_base16(char store
[3], char c
){
546 static char const itoa16
[] = "0123456789ABCDEF";
550 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
551 c
= ((ui8_t
)c
>> 4) & 0x0F;
552 store
[0] = itoa16
[(ui8_t
)c
];
558 n_c_from_hex_base16(char const hex
[2]){
559 static ui8_t
const atoi16
[] = {
560 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
561 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
562 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
563 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
564 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
565 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
566 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
572 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
573 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
577 if ((i1
| i2
) & 0xF0u
)
591 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
592 enum n_idec_mode idm
, char const **endptr_or_null
){
593 /* XXX Brute simple and */
596 enum n_idec_state rv
;
599 idm
&= n__IDEC_MODE_MASK
;
600 rv
= n_IDEC_STATE_NONE
| idm
;
609 assert(base
!= 1 && base
<= 36);
610 /*if(base == 1 || base > 36)
614 while(spacechar(*cbuf
))
615 if(*++cbuf
== '\0' || --clen
== 0)
621 rv
|= n_IDEC_STATE_SEEN_MINUS
;
624 if(*++cbuf
== '\0' || --clen
== 0)
629 /* Base detection/skip */
634 /* Support BASE#number prefix, where BASE is decimal 2-36.
635 * Enter this only if anything is to follow after that prefix*/
639 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
640 (((c2
= cbuf
[1]) == '#') ||
641 (c2
>= '0' && c2
<= '9' && clen
> 3 && cbuf
[2] == '#'))){
642 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
647 base
*= 10; /* xxx Inline atoi decimal base */
648 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
651 /* We do not interpret this as BASE#number at all if either we
652 * did not get a valid base or if the first char is not valid
653 * according to base, to comply to the latest interpretion of
654 * "prefix", see comment for standard prefixes below */
655 if(base
< 2 || base
> 36 || a_aux_idec_atoi
[(ui8_t
)c3
] >= base
)
658 clen
-= 2, cbuf
+= 2;
660 clen
-= 3, cbuf
+= 3;
665 /* Character must be valid for base */
666 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
670 /* 0 always valid as is, fallback base 10 */
671 if(*++cbuf
== '\0' || --clen
== 0)
674 /* Base "detection" */
675 if(base
== 0 || base
== 2 || base
== 16){
686 if((base
& 16) == 0){
688 /* Char after prefix must be valid. However, after some error
689 * in the tor software all libraries (which had to) turned to
690 * an interpretation of the C standard which says that the
691 * prefix may optionally precede an otherwise valid sequence,
692 * which means that "0x" is not a STATE_INVAL error but gives
693 * a "0" result with a "STATE_BASE" error and a rest of "x" */
696 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
699 if(*++cbuf
== '\0' || --clen
== 0)
702 /* Character must be valid for base, invalid otherwise */
703 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
716 /* Character must be valid for base, _EBASE otherwise */
717 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
722 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
726 if(res
> UI64_MAX
- currc
)
736 if(*++cbuf
== '\0' || --clen
== 0)
739 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
748 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
749 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
750 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
751 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
752 default: uimask
= UI64_MAX
; break;
754 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
758 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
759 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
760 if(res
> uimask
+ 1){
769 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
770 rv
|= n_IDEC_STATE_EOVERFLOW
;
771 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
775 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
776 case n_IDEC_MODE_LIMIT_8BIT
:
777 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
778 *(si8_t
*)resp
= (si8_t
)res
;
780 *(ui8_t
*)resp
= (ui8_t
)res
;
782 case n_IDEC_MODE_LIMIT_16BIT
:
783 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
784 *(si16_t
*)resp
= (si16_t
)res
;
786 *(ui16_t
*)resp
= (ui16_t
)res
;
788 case n_IDEC_MODE_LIMIT_32BIT
:
789 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
790 *(si32_t
*)resp
= (si32_t
)res
;
792 *(ui32_t
*)resp
= (ui32_t
)res
;
795 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
796 *(si64_t
*)resp
= (si64_t
)res
;
798 *(ui64_t
*)resp
= (ui64_t
)res
;
802 if(endptr_or_null
!= NULL
)
803 *endptr_or_null
= cbuf
;
804 if(*cbuf
== '\0' || clen
== 0)
805 rv
|= n_IDEC_STATE_CONSUMED
;
810 rv
|= n_IDEC_STATE_EINVAL
;
813 /* Not a base error for terminator and whitespace! */
814 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
815 rv
|= n_IDEC_STATE_EBASE
;
819 /* Overflow error: consume input until bad character or length out */
821 if(*++cbuf
== '\0' || --clen
== 0)
823 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
828 rv
|= n_IDEC_STATE_EOVERFLOW
;
830 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
831 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
835 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
840 n_torek_hash(char const *name
){
841 /* Chris Torek's hash */
846 for(h
= 0; (c
= *name
++) != '\0';)
853 n_torek_ihashn(char const *dat
, size_t len
){
854 /* See n_torek_hash() */
860 for(h
= 0; (c
= *dat
++) != '\0';)
861 h
= (h
* 33) + lowerconv(c
);
863 for(h
= 0; len
> 0; --len
){
865 h
= (h
* 33) + lowerconv(c
);
872 n_prime_next(ui32_t n
){
873 static ui32_t
const primes
[] = {
874 5, 11, 23, 47, 97, 157, 283,
875 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
876 131071, 262139, 524287, 1048573, 2097143, 4194301,
877 8388593, 16777213, 33554393, 67108859, 134217689,
878 268435399, 536870909, 1073741789, 2147483647
883 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
884 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
885 : n_NELEM(primes
) / 2));
887 do if((mprime
= primes
[i
]) > n
)
889 while(++i
< n_NELEM(primes
));
891 if(i
== n_NELEM(primes
) && mprime
< n
)
898 n_getdeadletter(void){
905 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
906 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
908 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
909 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
914 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
915 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
923 n_nodename(bool_t mayoverride
){
924 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
929 # ifdef HAVE_GETADDRINFO
930 struct addrinfo hints
, *res
;
932 struct hostent
*hent
;
937 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
939 }else if((hn
= sys_hostname
) == NULL
){
943 # ifdef HAVE_GETADDRINFO
944 memset(&hints
, 0, sizeof hints
);
945 hints
.ai_family
= AF_UNSPEC
;
946 hints
.ai_flags
= AI_CANONNAME
;
947 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
948 if(res
->ai_canonname
!= NULL
){
951 l
= strlen(res
->ai_canonname
) +1;
952 hn
= n_lofi_alloc(l
);
953 memcpy(hn
, res
->ai_canonname
, l
);
958 hent
= gethostbyname(hn
);
963 sys_hostname
= sstrdup(hn
);
964 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
965 if(hn
!= ut
.nodename
)
971 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
973 hostname
= sstrdup(hn
);
980 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
985 if((rv
= (ilen
== 0)))
988 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
989 ibuf
= savestrbuf(ibuf
, ilen
);
992 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
994 if(idna_utf8
== NULL
)
997 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
1001 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
1002 out
= n_string_assign_cp(out
, idna_ascii
);
1003 idn_free(idna_ascii
);
1008 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
1009 ilen
= strlen(idna_utf8
);
1011 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
1012 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1013 case idn_buffer_overflow
:
1014 ilen
+= HOST_NAME_MAX
+1;
1018 ilen
= strlen(out
->s_dat
);
1026 # error Unknown HAVE_IDNA
1029 out
= n_string_trunc(out
, ilen
);
1033 #endif /* HAVE_IDNA */
1036 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1038 char *indat
, *cp
, *oudat
;
1039 size_t i
, inlen
, oulen
;
1042 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1043 n_psonce
|= n_PSO_RANDOM_INIT
;
1045 if(n_poption
& n_PO_D_V
){
1048 #if n_RANDOM_USE_XSSL
1049 prngn
= "*SSL RAND_*";
1050 #elif defined HAVE_POSIX_RANDOM
1051 prngn
= "POSIX/arc4random";
1053 prngn
= "builtin ARC4";
1055 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1058 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1063 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1064 * with PAD stripped is still longer than what the user requests, easy way.
1065 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1066 * include the base64 PAD characters in our random string: give some pad */
1068 if((inlen
= i
% 3) != 0)
1077 inlen
= inlen
+ (inlen
<< 1);
1079 indat
= n_lofi_alloc(inlen
+1);
1081 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1082 #if n_RANDOM_USE_XSSL
1083 ssl_rand_bytes(indat
, inlen
);
1084 #elif !defined HAVE_POSIX_RANDOM
1085 for(i
= inlen
; i
-- > 0;)
1086 indat
[i
] = (char)a_aux_rand_get8();
1088 for(cp
= indat
, i
= inlen
; i
> 0;){
1089 union {ui32_t i4
; char c
[4];} r
;
1092 r
.i4
= (ui32_t
)arc4random();
1093 switch((j
= i
& 3)){
1094 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1095 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1096 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1097 default: cp
[0] = r
.c
[0]; break;
1104 for(cp
= indat
, i
= inlen
; i
> 0;){
1105 union {ui32_t i4
; char c
[4];} r
;
1108 r
.i4
= ++*reprocnt_or_null
;
1109 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1119 switch((j
= i
& 3)){
1120 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1121 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1122 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1123 default: cp
[0] = r
.c
[0]; break;
1130 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1132 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1133 assert(b64
.l
>= len
);
1134 memcpy(dat
, b64
.s
, len
);
1146 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1150 dat
= n_autorec_alloc(len
+1);
1151 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1157 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1162 assert(inlen
== 0 || inbuf
!= NULL
);
1164 if (inlen
== UIZ_MAX
)
1165 inlen
= strlen(inbuf
);
1168 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1170 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1171 !ascncasecmp(inbuf
, "true", inlen
) ||
1172 !ascncasecmp(inbuf
, "yes", inlen
) ||
1173 !ascncasecmp(inbuf
, "on", inlen
))
1175 else if ((inlen
== 1 &&
1176 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1177 !ascncasecmp(inbuf
, "false", inlen
) ||
1178 !ascncasecmp(inbuf
, "no", inlen
) ||
1179 !ascncasecmp(inbuf
, "off", inlen
))
1184 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1185 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1186 ) != n_IDEC_STATE_CONSUMED
)
1197 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1202 assert(inlen
== 0 || inbuf
!= NULL
);
1204 if (inlen
== UIZ_MAX
)
1205 inlen
= strlen(inbuf
);
1208 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1209 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1210 !ascncasecmp(inbuf
, "ask-", 4) &&
1211 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1212 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1213 rv
= getapproval(prompt
, rv
);
1219 n_is_all_or_aster(char const *name
){
1223 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1228 FL
struct n_timespec
const *
1229 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1230 static struct n_timespec ts_now
;
1233 if(n_UNLIKELY((n_psonce
& n_PSO_REPRODUCIBLE
) != 0)){
1234 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1235 (void)n_idec_si64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1237 }else if(force_update
|| ts_now
.ts_sec
== 0){
1238 #ifdef HAVE_CLOCK_GETTIME
1241 clock_gettime(CLOCK_REALTIME
, &ts
);
1242 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1243 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1244 #elif defined HAVE_GETTIMEOFDAY
1247 gettimeofday(&tv
, NULL
);
1248 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1249 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1251 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1256 /* Just in case.. */
1257 if(n_UNLIKELY(ts_now
.ts_sec
< 0))
1264 time_current_update(struct time_current
*tc
, bool_t full_update
){
1266 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1275 if((tmp
= gmtime(&t
)) == NULL
){
1279 memcpy(&tc
->tc_gm
, tmp
, sizeof tc
->tc_gm
);
1280 if((tmp
= localtime(&t
)) == NULL
){
1284 memcpy(&tc
->tc_local
, tmp
, sizeof tc
->tc_local
);
1285 cp
= sstpcpy(tc
->tc_ctime
, n_time_ctime((si64_t
)tc
->tc_time
, tmp
));
1288 assert(PTR2SIZE(++cp
- tc
->tc_ctime
) < sizeof(tc
->tc_ctime
));
1294 n_time_ctime(si64_t secsepoch
, struct tm
const *localtime_or_nil
){/* TODO err*/
1295 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1296 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1297 * ISO C requires us to use the above format string,
1298 * even if it will not fit in the buffer. Thus asctime_r
1299 * is _supposed_ to crash if the fields in tm are too large.
1300 * We follow this behavior and crash "gracefully" to warn
1301 * application developers that they may not be so lucky
1302 * on other implementations (e.g. stack smashing..).
1303 * So we need to do it on our own or the libc may kill us */
1304 static char buf
[32]; /* TODO static buffer (-> datetime_to_format()) */
1306 si32_t y
, md
, th
, tm
, ts
;
1307 char const *wdn
, *mn
;
1308 struct tm
const *tmp
;
1311 if((tmp
= localtime_or_nil
) == NULL
){
1314 t
= (time_t)secsepoch
;
1316 if((tmp
= localtime(&t
)) == NULL
){
1317 /* TODO error log */
1323 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
1325 wdn
= n_weekday_names
[4];
1326 mn
= n_month_names
[0];
1331 wdn
= (tmp
->tm_wday
>= 0 && tmp
->tm_wday
<= 6)
1332 ? n_weekday_names
[tmp
->tm_wday
] : n_qm
;
1333 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
1334 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
1336 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
1339 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
1341 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
1343 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
1347 (void)snprintf(buf
, sizeof buf
, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1348 wdn
, mn
, md
, th
, tm
, ts
, y
);
1354 n_msleep(uiz_t millis
, bool_t ignint
){
1358 #ifdef HAVE_NANOSLEEP
1360 struct timespec ts
, trem
;
1363 ts
.tv_sec
= millis
/ 1000;
1364 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1366 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1368 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1371 #elif defined HAVE_SLEEP
1372 if((millis
/= 1000) == 0)
1374 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1377 # error Configuration should have detected a function for sleeping.
1385 n_err(char const *format
, ...){
1389 va_start(ap
, format
);
1391 if(n_psonce
& n_PSO_INTERACTIVE
)
1401 while(*format
== '\n'){
1403 putc('\n', n_stderr
);
1408 a_aux_err_linelen
= 0;
1410 if((len
= strlen(format
)) > 0){
1411 if(doname
|| a_aux_err_linelen
== 0){
1414 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1415 fputs(cp
, n_stderr
);
1417 vfprintf(n_stderr
, format
, ap
);
1422 if(format
[--len
] == '\n'){
1423 a_aux_err_linelen
= (i
-= ++len
);
1426 ++a_aux_err_linelen
;
1438 n_verr(char const *format
, va_list ap
){
1440 struct a_aux_err_node
*enp
;
1448 while(*format
== '\n'){
1449 putc('\n', n_stderr
);
1455 a_aux_err_linelen
= 0;
1457 if(n_psonce
& n_PSO_INTERACTIVE
){
1458 if((enp
= a_aux_err_tail
) != NULL
&&
1459 (enp
->ae_str
.s_len
> 0 &&
1460 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1461 n_string_push_c(&enp
->ae_str
, '\n');
1466 if((len
= strlen(format
)) == 0)
1469 n_pstate
|= n_PS_ERRORS_PROMPT
;
1472 if(doname
|| a_aux_err_linelen
== 0){
1475 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1476 fputs(cp
, n_stderr
);
1482 if(format
[--len
] == '\n'){
1483 a_aux_err_linelen
= (i
-= ++len
);
1486 ++a_aux_err_linelen
;
1491 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1493 vfprintf(n_stderr
, format
, ap
);
1497 n_LCTAV(ERRORS_MAX
> 3);
1499 /* Link it into the `errors' message ring */
1500 if((enp
= a_aux_err_tail
) == NULL
){
1502 enp
= smalloc(sizeof *enp
);
1503 enp
->ae_next
= NULL
;
1504 n_string_creat(&enp
->ae_str
);
1505 if(a_aux_err_tail
!= NULL
)
1506 a_aux_err_tail
->ae_next
= enp
;
1508 a_aux_err_head
= enp
;
1509 a_aux_err_tail
= enp
;
1512 (enp
->ae_str
.s_len
> 0 &&
1513 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1514 if(a_aux_err_cnt
< ERRORS_MAX
)
1517 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1518 a_aux_err_tail
->ae_next
= enp
;
1519 a_aux_err_tail
= enp
;
1520 enp
->ae_next
= NULL
;
1521 n_string_trunc(&enp
->ae_str
, 0);
1524 # ifdef HAVE_N_VA_COPY
1527 imax
= n_MIN(LINESIZE
, 1024);
1529 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1530 # ifdef HAVE_N_VA_COPY
1538 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1539 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1540 # ifdef HAVE_N_VA_COPY
1547 if(UICMP(z
, i
, >=, imax
)){
1548 # ifdef HAVE_N_VA_COPY
1549 /* XXX Check overflow for upcoming LEN+++i! */
1550 n_string_trunc(&enp
->ae_str
, len
);
1553 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1558 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1560 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1562 #endif /* HAVE_ERRORS */
1570 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1574 va_start(ap
, format
);
1575 vfprintf(n_stderr
, format
, ap
);
1581 n_perr(char const *msg
, int errval
){
1592 e
= (errval
== 0) ? n_err_no
: errval
;
1593 n_err(fmt
, msg
, n_err_to_doc(e
));
1600 n_alert(char const *format
, ...){
1604 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1606 va_start(ap
, format
);
1615 n_panic(char const *format
, ...){
1619 if(a_aux_err_linelen
> 0){
1620 putc('\n', n_stderr
);
1621 a_aux_err_linelen
= 0;
1623 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1625 va_start(ap
, format
);
1626 vfprintf(n_stderr
, format
, ap
);
1629 putc('\n', n_stderr
);
1632 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1639 struct a_aux_err_node
*enp
;
1646 if(!asccasecmp(*argv
, "show"))
1648 if(!asccasecmp(*argv
, "clear"))
1652 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1656 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1662 if(a_aux_err_head
== NULL
){
1663 fprintf(n_stderr
, _("The error ring is empty\n"));
1667 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1669 fprintf(n_stderr
, _("tmpfile"));
1674 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1675 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1676 /* We don't know whether last string ended with NL; be simple XXX */
1679 page_or_print(fp
, 0);
1685 a_aux_err_tail
= NULL
;
1686 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1687 a_aux_err_linelen
= 0;
1688 while((enp
= a_aux_err_head
) != NULL
){
1689 a_aux_err_head
= enp
->ae_next
;
1690 n_string_gut(&enp
->ae_str
);
1695 #endif /* HAVE_ERRORS */
1698 n_err_to_doc(si32_t eno
){
1700 struct a_aux_err_map
const *aemp
;
1703 aemp
= a_aux_err_map_from_no(eno
);
1704 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1710 n_err_to_name(si32_t eno
){
1712 struct a_aux_err_map
const *aemp
;
1715 aemp
= a_aux_err_map_from_no(eno
);
1716 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1722 n_err_from_name(char const *name
){
1723 struct a_aux_err_map
const *aemp
;
1724 ui32_t hash
, i
, j
, x
;
1728 hash
= n_torek_hash(name
);
1730 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1731 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1734 aemp
= &a_aux_err_map
[x
];
1735 if(aemp
->aem_hash
== hash
&&
1736 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1737 rv
= aemp
->aem_err_no
;
1741 if(++i
== a_AUX_ERR_REV_PRIME
){
1742 #ifdef a_AUX_ERR_REV_WRAPAROUND
1750 /* Have not found it. But wait, it could be that the user did, e.g.,
1751 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1752 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1753 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1754 ) == n_IDEC_STATE_CONSUMED
){
1755 aemp
= a_aux_err_map_from_no(rv
);
1756 rv
= aemp
->aem_err_no
;
1760 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1768 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1773 i
= regerror(e
, rep
, NULL
, 0) +1;
1775 regerror(e
, rep
, cp
, i
);