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 - 2017 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 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 SINLINE 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 /* Avoid logging if user set that via -S! */
348 n_PS_ROOT_BLOCK(ok_vset(ttycharset
, cp
));
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
;
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 */
633 /* Character must be valid for base */
634 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
638 /* 0 always valid as is, fallback base 10 */
639 if(*++cbuf
== '\0' || --clen
== 0)
642 /* Base "detection" */
643 if(base
== 0 || base
== 2 || base
== 16){
654 if((base
& 16) == 0){
656 /* Char after prefix must be valid. However, after some error
657 * in the tor software all libraries (which had to) turned to
658 * an interpretation of the C standard which says that the
659 * prefix may optionally precede an otherwise valid sequence,
660 * which means that "0x" is not a STATE_INVAL error but gives
661 * a "0" result with a "STATE_BASE" error and a rest of "x" */
664 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
){
669 if(*++cbuf
== '\0' || --clen
== 0)
672 /* Character must be valid for base, invalid otherwise */
673 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
686 /* Character must be valid for base, _EBASE otherwise */
687 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
692 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
696 if(res
> UI64_MAX
- currc
)
706 if(*++cbuf
== '\0' || --clen
== 0)
709 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
718 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
719 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
720 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
721 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
722 default: uimask
= UI64_MAX
; break;
724 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
728 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
729 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
730 if(res
> uimask
+ 1){
739 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
740 rv
|= n_IDEC_STATE_EOVERFLOW
;
741 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
745 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
746 case n_IDEC_MODE_LIMIT_8BIT
:
747 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
748 *(si8_t
*)resp
= (si8_t
)res
;
750 *(ui8_t
*)resp
= (ui8_t
)res
;
752 case n_IDEC_MODE_LIMIT_16BIT
:
753 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
754 *(si16_t
*)resp
= (si16_t
)res
;
756 *(ui16_t
*)resp
= (ui16_t
)res
;
758 case n_IDEC_MODE_LIMIT_32BIT
:
759 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
760 *(si32_t
*)resp
= (si32_t
)res
;
762 *(ui32_t
*)resp
= (ui32_t
)res
;
765 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
766 *(si64_t
*)resp
= (si64_t
)res
;
768 *(ui64_t
*)resp
= (ui64_t
)res
;
772 if(endptr_or_null
!= NULL
)
773 *endptr_or_null
= cbuf
;
774 if(*cbuf
== '\0' || clen
== 0)
775 rv
|= n_IDEC_STATE_CONSUMED
;
780 rv
|= n_IDEC_STATE_EINVAL
;
783 /* Not a base error for terminator and whitespace! */
784 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
785 rv
|= n_IDEC_STATE_EBASE
;
789 /* Overflow error: consume input until bad character or length out */
791 if(*++cbuf
== '\0' || --clen
== 0)
793 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
798 rv
|= n_IDEC_STATE_EOVERFLOW
;
800 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
801 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
805 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
810 n_torek_hash(char const *name
){
811 /* Chris Torek's hash */
816 for(h
= 0; (c
= *name
++) != '\0';)
823 n_torek_ihashn(char const *dat
, size_t len
){
824 /* See n_torek_hash() */
830 for(h
= 0; (c
= *dat
++) != '\0';)
831 h
= (h
* 33) + lowerconv(c
);
833 for(h
= 0; len
> 0; --len
){
835 h
= (h
* 33) + lowerconv(c
);
842 n_prime_next(ui32_t n
){
843 static ui32_t
const primes
[] = {
844 5, 11, 23, 47, 97, 157, 283,
845 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
846 131071, 262139, 524287, 1048573, 2097143, 4194301,
847 8388593, 16777213, 33554393, 67108859, 134217689,
848 268435399, 536870909, 1073741789, 2147483647
853 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
854 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
855 : n_NELEM(primes
) / 2));
857 do if((mprime
= primes
[i
]) > n
)
859 while(++i
< n_NELEM(primes
));
861 if(i
== n_NELEM(primes
) && mprime
< n
)
868 n_getdeadletter(void){
875 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
876 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
878 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
879 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
884 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
885 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
893 n_nodename(bool_t mayoverride
){
894 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
899 # ifdef HAVE_GETADDRINFO
900 struct addrinfo hints
, *res
;
902 struct hostent
*hent
;
907 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
909 }else if((hn
= sys_hostname
) == NULL
){
913 # ifdef HAVE_GETADDRINFO
914 memset(&hints
, 0, sizeof hints
);
915 hints
.ai_family
= AF_UNSPEC
;
916 hints
.ai_flags
= AI_CANONNAME
;
917 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
918 if(res
->ai_canonname
!= NULL
){
921 l
= strlen(res
->ai_canonname
) +1;
922 hn
= n_lofi_alloc(l
);
923 memcpy(hn
, res
->ai_canonname
, l
);
928 hent
= gethostbyname(hn
);
933 sys_hostname
= sstrdup(hn
);
934 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
935 if(hn
!= ut
.nodename
)
941 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
943 hostname
= sstrdup(hn
);
950 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
955 if((rv
= (ilen
== 0)))
958 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
959 ibuf
= savestrbuf(ibuf
, ilen
);
962 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
964 if(idna_utf8
== NULL
)
967 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
971 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
972 out
= n_string_assign_cp(out
, idna_ascii
);
973 idn_free(idna_ascii
);
978 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
979 ilen
= strlen(idna_utf8
);
981 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
982 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
983 case idn_buffer_overflow
:
984 ilen
+= HOST_NAME_MAX
+1;
988 ilen
= strlen(out
->s_dat
);
996 # error Unknown HAVE_IDNA
999 out
= n_string_trunc(out
, ilen
);
1003 #endif /* HAVE_IDNA */
1006 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1008 char *indat
, *cp
, *oudat
;
1009 size_t i
, inlen
, oulen
;
1012 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1013 n_psonce
|= n_PSO_RANDOM_INIT
;
1015 if(n_poption
& n_PO_D_V
){
1018 #if n_RANDOM_USE_XSSL
1019 prngn
= "*SSL RAND_*";
1020 #elif defined HAVE_POSIX_RANDOM
1021 prngn
= "POSIX/arc4random";
1023 prngn
= "builtin ARC4";
1025 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1028 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1033 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1034 * with PAD stripped is still longer than what the user requests, easy way.
1035 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1036 * include the base64 PAD characters in our random string: give some pad */
1038 if((inlen
= i
% 3) != 0)
1047 inlen
= inlen
+ (inlen
<< 1);
1049 indat
= n_lofi_alloc(inlen
+1);
1051 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1052 #if n_RANDOM_USE_XSSL
1053 ssl_rand_bytes(indat
, inlen
);
1054 #elif !defined HAVE_POSIX_RANDOM
1055 for(i
= inlen
; i
-- > 0;)
1056 indat
[i
] = (char)a_aux_rand_get8();
1058 for(cp
= indat
, i
= inlen
; i
> 0;){
1059 union {ui32_t i4
; char c
[4];} r
;
1062 r
.i4
= (ui32_t
)arc4random();
1063 switch((j
= i
& 3)){
1064 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1065 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1066 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1067 default: cp
[0] = r
.c
[0]; break;
1074 for(cp
= indat
, i
= inlen
; i
> 0;){
1075 union {ui32_t i4
; char c
[4];} r
;
1078 r
.i4
= ++*reprocnt_or_null
;
1079 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1089 switch((j
= i
& 3)){
1090 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1091 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1092 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1093 default: cp
[0] = r
.c
[0]; break;
1100 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1102 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1103 assert(b64
.l
>= len
);
1104 memcpy(dat
, b64
.s
, len
);
1116 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1120 dat
= n_autorec_alloc(len
+1);
1121 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1127 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1132 assert(inlen
== 0 || inbuf
!= NULL
);
1134 if (inlen
== UIZ_MAX
)
1135 inlen
= strlen(inbuf
);
1138 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1140 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1141 !ascncasecmp(inbuf
, "true", inlen
) ||
1142 !ascncasecmp(inbuf
, "yes", inlen
) ||
1143 !ascncasecmp(inbuf
, "on", inlen
))
1145 else if ((inlen
== 1 &&
1146 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1147 !ascncasecmp(inbuf
, "false", inlen
) ||
1148 !ascncasecmp(inbuf
, "no", inlen
) ||
1149 !ascncasecmp(inbuf
, "off", inlen
))
1154 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1155 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1156 ) != n_IDEC_STATE_CONSUMED
)
1167 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1172 assert(inlen
== 0 || inbuf
!= NULL
);
1174 if (inlen
== UIZ_MAX
)
1175 inlen
= strlen(inbuf
);
1178 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1179 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1180 !ascncasecmp(inbuf
, "ask-", 4) &&
1181 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1182 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1183 rv
= getapproval(prompt
, rv
);
1189 n_is_all_or_aster(char const *name
){
1193 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1198 FL
struct n_timespec
const *
1199 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1200 static struct n_timespec ts_now
;
1203 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1204 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1206 }else if(force_update
|| ts_now
.ts_sec
== 0){
1207 #ifdef HAVE_CLOCK_GETTIME
1210 clock_gettime(CLOCK_REALTIME
, &ts
);
1211 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1212 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1213 #elif defined HAVE_GETTIMEOFDAY
1216 gettimeofday(&tv
, NULL
);
1217 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1218 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1220 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1229 time_current_update(struct time_current
*tc
, bool_t full_update
)
1232 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1234 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1235 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1236 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1242 n_msleep(uiz_t millis
, bool_t ignint
){
1246 #ifdef HAVE_NANOSLEEP
1248 struct timespec ts
, trem
;
1251 ts
.tv_sec
= millis
/ 1000;
1252 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1254 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1256 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1259 #elif defined HAVE_SLEEP
1260 if((millis
/= 1000) == 0)
1262 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1265 # error Configuration should have detected a function for sleeping.
1273 n_err(char const *format
, ...){
1277 va_start(ap
, format
);
1279 if(n_psonce
& n_PSO_INTERACTIVE
)
1289 while(*format
== '\n'){
1291 putc('\n', n_stderr
);
1296 a_aux_err_linelen
= 0;
1298 if((len
= strlen(format
)) > 0){
1299 if(doname
|| a_aux_err_linelen
== 0){
1302 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1303 fputs(cp
, n_stderr
);
1305 vfprintf(n_stderr
, format
, ap
);
1310 if(format
[--len
] == '\n'){
1311 a_aux_err_linelen
= (i
-= ++len
);
1314 ++a_aux_err_linelen
;
1326 n_verr(char const *format
, va_list ap
){
1328 struct a_aux_err_node
*enp
;
1336 while(*format
== '\n'){
1337 putc('\n', n_stderr
);
1343 a_aux_err_linelen
= 0;
1345 if(n_psonce
& n_PSO_INTERACTIVE
){
1346 if((enp
= a_aux_err_tail
) != NULL
&&
1347 (enp
->ae_str
.s_len
> 0 &&
1348 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1349 n_string_push_c(&enp
->ae_str
, '\n');
1354 if((len
= strlen(format
)) == 0)
1357 n_pstate
|= n_PS_ERRORS_PROMPT
;
1360 if(doname
|| a_aux_err_linelen
== 0){
1363 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1364 fputs(cp
, n_stderr
);
1370 if(format
[--len
] == '\n'){
1371 a_aux_err_linelen
= (i
-= ++len
);
1374 ++a_aux_err_linelen
;
1379 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1381 vfprintf(n_stderr
, format
, ap
);
1385 n_LCTAV(ERRORS_MAX
> 3);
1387 /* Link it into the `errors' message ring */
1388 if((enp
= a_aux_err_tail
) == NULL
){
1390 enp
= smalloc(sizeof *enp
);
1391 enp
->ae_next
= NULL
;
1392 n_string_creat(&enp
->ae_str
);
1393 if(a_aux_err_tail
!= NULL
)
1394 a_aux_err_tail
->ae_next
= enp
;
1396 a_aux_err_head
= enp
;
1397 a_aux_err_tail
= enp
;
1400 (enp
->ae_str
.s_len
> 0 &&
1401 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1402 if(a_aux_err_cnt
< ERRORS_MAX
)
1405 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1406 a_aux_err_tail
->ae_next
= enp
;
1407 a_aux_err_tail
= enp
;
1408 enp
->ae_next
= NULL
;
1409 n_string_trunc(&enp
->ae_str
, 0);
1412 # ifdef HAVE_N_VA_COPY
1415 imax
= n_MIN(LINESIZE
, 1024);
1417 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1418 # ifdef HAVE_N_VA_COPY
1426 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1427 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1428 # ifdef HAVE_N_VA_COPY
1435 if(UICMP(z
, i
, >=, imax
)){
1436 # ifdef HAVE_N_VA_COPY
1437 /* XXX Check overflow for upcoming LEN+++i! */
1438 n_string_trunc(&enp
->ae_str
, len
);
1441 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1446 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1448 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1450 #endif /* HAVE_ERRORS */
1458 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1462 va_start(ap
, format
);
1463 vfprintf(n_stderr
, format
, ap
);
1469 n_perr(char const *msg
, int errval
){
1480 e
= (errval
== 0) ? n_err_no
: errval
;
1481 n_err(fmt
, msg
, n_err_to_doc(e
));
1488 n_alert(char const *format
, ...){
1492 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1494 va_start(ap
, format
);
1503 n_panic(char const *format
, ...){
1507 if(a_aux_err_linelen
> 0){
1508 putc('\n', n_stderr
);
1509 a_aux_err_linelen
= 0;
1511 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1513 va_start(ap
, format
);
1514 vfprintf(n_stderr
, format
, ap
);
1517 putc('\n', n_stderr
);
1520 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1527 struct a_aux_err_node
*enp
;
1534 if(!asccasecmp(*argv
, "show"))
1536 if(!asccasecmp(*argv
, "clear"))
1540 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1544 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1550 if(a_aux_err_head
== NULL
){
1551 fprintf(n_stderr
, _("The error ring is empty\n"));
1555 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1557 fprintf(n_stderr
, _("tmpfile"));
1562 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1563 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1564 /* We don't know whether last string ended with NL; be simple XXX */
1567 page_or_print(fp
, 0);
1573 a_aux_err_tail
= NULL
;
1574 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1575 a_aux_err_linelen
= 0;
1576 while((enp
= a_aux_err_head
) != NULL
){
1577 a_aux_err_head
= enp
->ae_next
;
1578 n_string_gut(&enp
->ae_str
);
1583 #endif /* HAVE_ERRORS */
1586 n_err_to_doc(si32_t eno
){
1588 struct a_aux_err_map
const *aemp
;
1591 aemp
= a_aux_err_map_from_no(eno
);
1592 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1598 n_err_to_name(si32_t eno
){
1600 struct a_aux_err_map
const *aemp
;
1603 aemp
= a_aux_err_map_from_no(eno
);
1604 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1610 n_err_from_name(char const *name
){
1611 struct a_aux_err_map
const *aemp
;
1612 ui32_t hash
, i
, j
, x
;
1616 hash
= n_torek_hash(name
);
1618 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1619 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1622 aemp
= &a_aux_err_map
[x
];
1623 if(aemp
->aem_hash
== hash
&&
1624 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1625 rv
= aemp
->aem_err_no
;
1629 if(++i
== a_AUX_ERR_REV_PRIME
){
1630 #ifdef a_AUX_ERR_REV_WRAPAROUND
1638 /* Have not found it. But wait, it could be that the user did, e.g.,
1639 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1640 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1641 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1642 ) == n_IDEC_STATE_CONSUMED
){
1643 aemp
= a_aux_err_map_from_no(rv
);
1644 rv
= aemp
->aem_err_no
;
1648 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1656 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1661 i
= regerror(e
, rep
, NULL
, 0) +1;
1663 regerror(e
, rep
, cp
, i
);