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 /* TODO *ttycharset* may be set several times during startup unless
347 * TODO we gain a mechanism that -S fixates a setting during startup,
348 * TODO effectively turning later adjustments (during startup) in noop */
349 if((cp
= nl_langinfo(CODESET
)) != NULL
)
350 ok_vset(ttycharset
, cp
);
354 # ifdef HAVE_C90AMEND1
355 if(n_mb_cur_max
> 1){
356 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
357 n_psonce
|= n_PSO_UNICODE
;
360 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
361 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
362 n_psonce
|= n_PSO_UNICODE
;
363 /* Reset possibly messed up state; luckily this also gives us an
364 * indication whether the encoding has locking shift state sequences */
365 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
366 n_psonce
|= n_PSO_ENC_MBSTATE
;
380 if((cp
= ok_vlook(screen
)) != NULL
){
381 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
394 n_pager_get(char const **env_addon
){
398 rv
= ok_vlook(PAGER
);
400 if(env_addon
!= NULL
){
402 /* Update the manual upon any changes:
403 * *colour-pager*, $PAGER */
404 if(strstr(rv
, "less") != NULL
){
405 if(getenv("LESS") == NULL
)
406 *env_addon
= "LESS=RXi";
407 }else if(strstr(rv
, "lv") != NULL
){
408 if(getenv("LV") == NULL
)
409 *env_addon
= "LV=-c";
417 page_or_print(FILE *fp
, size_t lines
)
425 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
429 rows
= (size_t)n_scrnheight
;
431 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
433 if (rows
> 0 && lines
== 0) {
434 while ((c
= getc(fp
)) != EOF
)
435 if (c
== '\n' && ++lines
>= rows
)
441 char const *env_add
[2], *pager
;
443 pager
= n_pager_get(&env_add
[0]);
445 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
451 while ((c
= getc(fp
)) != EOF
)
458 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
459 char const **adjusted_or_null
)
461 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
462 char const *cp
, *orig_name
;
463 enum protocol rv
= PROTO_UNKNOWN
;
466 if(name
[0] == '%' && name
[1] == ':')
470 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
474 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
475 if(!strncmp(name
, "file", sizeof("file") -1) ||
476 !strncmp(name
, "mbox", sizeof("mbox") -1))
478 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
480 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
484 n_err(_("No POP3 support compiled in\n"));
486 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
487 #if defined HAVE_POP3 && defined HAVE_SSL
490 n_err(_("No POP3S support compiled in\n"));
493 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
497 n_err(_("No IMAP support compiled in\n"));
499 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
500 #if defined HAVE_IMAP && defined HAVE_SSL
503 n_err(_("No IMAPS support compiled in\n"));
513 if(check_stat
|| try_hooks
){
514 struct n_file_type ft
;
519 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
520 memcpy(np
, name
, sz
+ 1);
522 if(!stat(name
, &stb
)){
523 if(S_ISDIR(stb
.st_mode
) &&
524 (memcpy(&np
[sz
], "/tmp", 5),
525 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
526 (memcpy(&np
[sz
], "/new", 5),
527 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
528 (memcpy(&np
[sz
], "/cur", 5),
529 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
531 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
532 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
533 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
534 !asccasecmp(cp
, "maildir"))
540 if(adjusted_or_null
!= NULL
)
541 *adjusted_or_null
= orig_name
;
547 n_c_to_hex_base16(char store
[3], char c
){
548 static char const itoa16
[] = "0123456789ABCDEF";
552 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
553 c
= ((ui8_t
)c
>> 4) & 0x0F;
554 store
[0] = itoa16
[(ui8_t
)c
];
560 n_c_from_hex_base16(char const hex
[2]){
561 static ui8_t
const atoi16
[] = {
562 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
563 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
564 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
565 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
566 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
567 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
568 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
574 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
575 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
579 if ((i1
| i2
) & 0xF0u
)
593 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
594 enum n_idec_mode idm
, char const **endptr_or_null
){
595 /* XXX Brute simple and */
598 enum n_idec_state rv
;
601 idm
&= n__IDEC_MODE_MASK
;
602 rv
= n_IDEC_STATE_NONE
| idm
;
611 assert(base
!= 1 && base
<= 36);
612 /*if(base == 1 || base > 36)
616 while(spacechar(*cbuf
))
617 if(*++cbuf
== '\0' || --clen
== 0)
623 rv
|= n_IDEC_STATE_SEEN_MINUS
;
626 if(*++cbuf
== '\0' || --clen
== 0)
631 /* Base detection/skip */
635 /* Character must be valid for base */
636 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
640 /* 0 always valid as is, fallback base 10 */
641 if(*++cbuf
== '\0' || --clen
== 0)
644 /* Base "detection" */
645 if(base
== 0 || base
== 2 || base
== 16){
656 if((base
& 16) == 0){
658 /* Char after prefix must be valid. However, after some error
659 * in the tor software all libraries (which had to) turned to
660 * an interpretation of the C standard which says that the
661 * prefix may optionally precede an otherwise valid sequence,
662 * which means that "0x" is not a STATE_INVAL error but gives
663 * a "0" result with a "STATE_BASE" error and a rest of "x" */
666 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
){
671 if(*++cbuf
== '\0' || --clen
== 0)
674 /* Character must be valid for base, invalid otherwise */
675 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
688 /* Character must be valid for base, _EBASE otherwise */
689 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
694 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
698 if(res
> UI64_MAX
- currc
)
708 if(*++cbuf
== '\0' || --clen
== 0)
711 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
720 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
721 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
722 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
723 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
724 default: uimask
= UI64_MAX
; break;
726 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
730 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
731 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
732 if(res
> uimask
+ 1){
741 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
742 rv
|= n_IDEC_STATE_EOVERFLOW
;
743 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
747 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
748 case n_IDEC_MODE_LIMIT_8BIT
:
749 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
750 *(si8_t
*)resp
= (si8_t
)res
;
752 *(ui8_t
*)resp
= (ui8_t
)res
;
754 case n_IDEC_MODE_LIMIT_16BIT
:
755 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
756 *(si16_t
*)resp
= (si16_t
)res
;
758 *(ui16_t
*)resp
= (ui16_t
)res
;
760 case n_IDEC_MODE_LIMIT_32BIT
:
761 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
762 *(si32_t
*)resp
= (si32_t
)res
;
764 *(ui32_t
*)resp
= (ui32_t
)res
;
767 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
768 *(si64_t
*)resp
= (si64_t
)res
;
770 *(ui64_t
*)resp
= (ui64_t
)res
;
774 if(endptr_or_null
!= NULL
)
775 *endptr_or_null
= cbuf
;
776 if(*cbuf
== '\0' || clen
== 0)
777 rv
|= n_IDEC_STATE_CONSUMED
;
782 rv
|= n_IDEC_STATE_EINVAL
;
785 /* Not a base error for terminator and whitespace! */
786 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
787 rv
|= n_IDEC_STATE_EBASE
;
791 /* Overflow error: consume input until bad character or length out */
793 if(*++cbuf
== '\0' || --clen
== 0)
795 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
800 rv
|= n_IDEC_STATE_EOVERFLOW
;
802 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
803 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
807 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
812 n_torek_hash(char const *name
){
813 /* Chris Torek's hash */
818 for(h
= 0; (c
= *name
++) != '\0';)
825 n_torek_ihashn(char const *dat
, size_t len
){
826 /* See n_torek_hash() */
832 for(h
= 0; (c
= *dat
++) != '\0';)
833 h
= (h
* 33) + lowerconv(c
);
835 for(h
= 0; len
> 0; --len
){
837 h
= (h
* 33) + lowerconv(c
);
844 n_prime_next(ui32_t n
){
845 static ui32_t
const primes
[] = {
846 5, 11, 23, 47, 97, 157, 283,
847 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
848 131071, 262139, 524287, 1048573, 2097143, 4194301,
849 8388593, 16777213, 33554393, 67108859, 134217689,
850 268435399, 536870909, 1073741789, 2147483647
855 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
856 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
857 : n_NELEM(primes
) / 2));
859 do if((mprime
= primes
[i
]) > n
)
861 while(++i
< n_NELEM(primes
));
863 if(i
== n_NELEM(primes
) && mprime
< n
)
870 n_getdeadletter(void){
877 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
878 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
880 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
881 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
886 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
887 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
895 n_nodename(bool_t mayoverride
){
896 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
901 # ifdef HAVE_GETADDRINFO
902 struct addrinfo hints
, *res
;
904 struct hostent
*hent
;
909 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
911 }else if((hn
= sys_hostname
) == NULL
){
915 # ifdef HAVE_GETADDRINFO
916 memset(&hints
, 0, sizeof hints
);
917 hints
.ai_family
= AF_UNSPEC
;
918 hints
.ai_flags
= AI_CANONNAME
;
919 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
920 if(res
->ai_canonname
!= NULL
){
923 l
= strlen(res
->ai_canonname
) +1;
924 hn
= n_lofi_alloc(l
);
925 memcpy(hn
, res
->ai_canonname
, l
);
930 hent
= gethostbyname(hn
);
935 sys_hostname
= sstrdup(hn
);
936 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
937 if(hn
!= ut
.nodename
)
943 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
945 hostname
= sstrdup(hn
);
952 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
957 if((rv
= (ilen
== 0)))
960 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
961 ibuf
= savestrbuf(ibuf
, ilen
);
964 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
966 if(idna_utf8
== NULL
)
969 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
973 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
974 out
= n_string_assign_cp(out
, idna_ascii
);
975 idn_free(idna_ascii
);
980 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
981 ilen
= strlen(idna_utf8
);
983 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
984 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
985 case idn_buffer_overflow
:
986 ilen
+= HOST_NAME_MAX
+1;
990 ilen
= strlen(out
->s_dat
);
998 # error Unknown HAVE_IDNA
1001 out
= n_string_trunc(out
, ilen
);
1005 #endif /* HAVE_IDNA */
1008 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1010 char *indat
, *cp
, *oudat
;
1011 size_t i
, inlen
, oulen
;
1014 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1015 n_psonce
|= n_PSO_RANDOM_INIT
;
1017 if(n_poption
& n_PO_D_V
){
1020 #if n_RANDOM_USE_XSSL
1021 prngn
= "*SSL RAND_*";
1022 #elif defined HAVE_POSIX_RANDOM
1023 prngn
= "POSIX/arc4random";
1025 prngn
= "builtin ARC4";
1027 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1030 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1035 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1036 * with PAD stripped is still longer than what the user requests, easy way.
1037 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1038 * include the base64 PAD characters in our random string: give some pad */
1040 if((inlen
= i
% 3) != 0)
1049 inlen
= inlen
+ (inlen
<< 1);
1051 indat
= n_lofi_alloc(inlen
+1);
1053 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1054 #if n_RANDOM_USE_XSSL
1055 ssl_rand_bytes(indat
, inlen
);
1056 #elif !defined HAVE_POSIX_RANDOM
1057 for(i
= inlen
; i
-- > 0;)
1058 indat
[i
] = (char)a_aux_rand_get8();
1060 for(cp
= indat
, i
= inlen
; i
> 0;){
1061 union {ui32_t i4
; char c
[4];} r
;
1064 r
.i4
= (ui32_t
)arc4random();
1065 switch((j
= i
& 3)){
1066 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1067 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1068 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1069 default: cp
[0] = r
.c
[0]; break;
1076 for(cp
= indat
, i
= inlen
; i
> 0;){
1077 union {ui32_t i4
; char c
[4];} r
;
1080 r
.i4
= ++*reprocnt_or_null
;
1081 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1091 switch((j
= i
& 3)){
1092 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1093 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1094 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1095 default: cp
[0] = r
.c
[0]; break;
1102 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1104 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1105 assert(b64
.l
>= len
);
1106 memcpy(dat
, b64
.s
, len
);
1118 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1122 dat
= n_autorec_alloc(len
+1);
1123 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1129 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1134 assert(inlen
== 0 || inbuf
!= NULL
);
1136 if (inlen
== UIZ_MAX
)
1137 inlen
= strlen(inbuf
);
1140 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1142 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1143 !ascncasecmp(inbuf
, "true", inlen
) ||
1144 !ascncasecmp(inbuf
, "yes", inlen
) ||
1145 !ascncasecmp(inbuf
, "on", inlen
))
1147 else if ((inlen
== 1 &&
1148 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1149 !ascncasecmp(inbuf
, "false", inlen
) ||
1150 !ascncasecmp(inbuf
, "no", inlen
) ||
1151 !ascncasecmp(inbuf
, "off", inlen
))
1156 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1157 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1158 ) != n_IDEC_STATE_CONSUMED
)
1169 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1174 assert(inlen
== 0 || inbuf
!= NULL
);
1176 if (inlen
== UIZ_MAX
)
1177 inlen
= strlen(inbuf
);
1180 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1181 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1182 !ascncasecmp(inbuf
, "ask-", 4) &&
1183 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1184 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1185 rv
= getapproval(prompt
, rv
);
1191 n_is_all_or_aster(char const *name
){
1195 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1200 FL
struct n_timespec
const *
1201 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1202 static struct n_timespec ts_now
;
1205 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1206 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1208 }else if(force_update
|| ts_now
.ts_sec
== 0){
1209 #ifdef HAVE_CLOCK_GETTIME
1212 clock_gettime(CLOCK_REALTIME
, &ts
);
1213 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1214 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1215 #elif defined HAVE_GETTIMEOFDAY
1218 gettimeofday(&tv
, NULL
);
1219 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1220 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1222 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1231 time_current_update(struct time_current
*tc
, bool_t full_update
)
1234 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1236 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1237 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1238 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1244 n_msleep(uiz_t millis
, bool_t ignint
){
1248 #ifdef HAVE_NANOSLEEP
1250 struct timespec ts
, trem
;
1253 ts
.tv_sec
= millis
/ 1000;
1254 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1256 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1258 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1261 #elif defined HAVE_SLEEP
1262 if((millis
/= 1000) == 0)
1264 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1267 # error Configuration should have detected a function for sleeping.
1275 n_err(char const *format
, ...){
1279 va_start(ap
, format
);
1281 if(n_psonce
& n_PSO_INTERACTIVE
)
1291 while(*format
== '\n'){
1293 putc('\n', n_stderr
);
1298 a_aux_err_linelen
= 0;
1300 if((len
= strlen(format
)) > 0){
1301 if(doname
|| a_aux_err_linelen
== 0){
1304 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1305 fputs(cp
, n_stderr
);
1307 vfprintf(n_stderr
, format
, ap
);
1312 if(format
[--len
] == '\n'){
1313 a_aux_err_linelen
= (i
-= ++len
);
1316 ++a_aux_err_linelen
;
1328 n_verr(char const *format
, va_list ap
){
1330 struct a_aux_err_node
*enp
;
1338 while(*format
== '\n'){
1339 putc('\n', n_stderr
);
1345 a_aux_err_linelen
= 0;
1347 if(n_psonce
& n_PSO_INTERACTIVE
){
1348 if((enp
= a_aux_err_tail
) != NULL
&&
1349 (enp
->ae_str
.s_len
> 0 &&
1350 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1351 n_string_push_c(&enp
->ae_str
, '\n');
1356 if((len
= strlen(format
)) == 0)
1359 n_pstate
|= n_PS_ERRORS_PROMPT
;
1362 if(doname
|| a_aux_err_linelen
== 0){
1365 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1366 fputs(cp
, n_stderr
);
1372 if(format
[--len
] == '\n'){
1373 a_aux_err_linelen
= (i
-= ++len
);
1376 ++a_aux_err_linelen
;
1381 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1383 vfprintf(n_stderr
, format
, ap
);
1387 n_LCTAV(ERRORS_MAX
> 3);
1389 /* Link it into the `errors' message ring */
1390 if((enp
= a_aux_err_tail
) == NULL
){
1392 enp
= smalloc(sizeof *enp
);
1393 enp
->ae_next
= NULL
;
1394 n_string_creat(&enp
->ae_str
);
1395 if(a_aux_err_tail
!= NULL
)
1396 a_aux_err_tail
->ae_next
= enp
;
1398 a_aux_err_head
= enp
;
1399 a_aux_err_tail
= enp
;
1402 (enp
->ae_str
.s_len
> 0 &&
1403 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1404 if(a_aux_err_cnt
< ERRORS_MAX
)
1407 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1408 a_aux_err_tail
->ae_next
= enp
;
1409 a_aux_err_tail
= enp
;
1410 enp
->ae_next
= NULL
;
1411 n_string_trunc(&enp
->ae_str
, 0);
1414 # ifdef HAVE_N_VA_COPY
1417 imax
= n_MIN(LINESIZE
, 1024);
1419 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1420 # ifdef HAVE_N_VA_COPY
1428 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1429 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1430 # ifdef HAVE_N_VA_COPY
1437 if(UICMP(z
, i
, >=, imax
)){
1438 # ifdef HAVE_N_VA_COPY
1439 /* XXX Check overflow for upcoming LEN+++i! */
1440 n_string_trunc(&enp
->ae_str
, len
);
1443 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1448 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1450 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1452 #endif /* HAVE_ERRORS */
1460 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1464 va_start(ap
, format
);
1465 vfprintf(n_stderr
, format
, ap
);
1471 n_perr(char const *msg
, int errval
){
1482 e
= (errval
== 0) ? n_err_no
: errval
;
1483 n_err(fmt
, msg
, n_err_to_doc(e
));
1490 n_alert(char const *format
, ...){
1494 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1496 va_start(ap
, format
);
1505 n_panic(char const *format
, ...){
1509 if(a_aux_err_linelen
> 0){
1510 putc('\n', n_stderr
);
1511 a_aux_err_linelen
= 0;
1513 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1515 va_start(ap
, format
);
1516 vfprintf(n_stderr
, format
, ap
);
1519 putc('\n', n_stderr
);
1522 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1529 struct a_aux_err_node
*enp
;
1536 if(!asccasecmp(*argv
, "show"))
1538 if(!asccasecmp(*argv
, "clear"))
1542 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1546 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1552 if(a_aux_err_head
== NULL
){
1553 fprintf(n_stderr
, _("The error ring is empty\n"));
1557 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1559 fprintf(n_stderr
, _("tmpfile"));
1564 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1565 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1566 /* We don't know whether last string ended with NL; be simple XXX */
1569 page_or_print(fp
, 0);
1575 a_aux_err_tail
= NULL
;
1576 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1577 a_aux_err_linelen
= 0;
1578 while((enp
= a_aux_err_head
) != NULL
){
1579 a_aux_err_head
= enp
->ae_next
;
1580 n_string_gut(&enp
->ae_str
);
1585 #endif /* HAVE_ERRORS */
1588 n_err_to_doc(si32_t eno
){
1590 struct a_aux_err_map
const *aemp
;
1593 aemp
= a_aux_err_map_from_no(eno
);
1594 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1600 n_err_to_name(si32_t eno
){
1602 struct a_aux_err_map
const *aemp
;
1605 aemp
= a_aux_err_map_from_no(eno
);
1606 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1612 n_err_from_name(char const *name
){
1613 struct a_aux_err_map
const *aemp
;
1614 ui32_t hash
, i
, j
, x
;
1618 hash
= n_torek_hash(name
);
1620 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1621 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1624 aemp
= &a_aux_err_map
[x
];
1625 if(aemp
->aem_hash
== hash
&&
1626 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1627 rv
= aemp
->aem_err_no
;
1631 if(++i
== a_AUX_ERR_REV_PRIME
){
1632 #ifdef a_AUX_ERR_REV_WRAPAROUND
1640 /* Have not found it. But wait, it could be that the user did, e.g.,
1641 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1642 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1643 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1644 ) == n_IDEC_STATE_CONSUMED
){
1645 aemp
= a_aux_err_map_from_no(rv
);
1646 rv
= aemp
->aem_err_no
;
1650 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1658 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1663 i
= regerror(e
, rep
, NULL
, 0) +1;
1665 regerror(e
, rep
, cp
, i
);