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
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 */
638 if(((c1
= cbuf
[0]) >= '0' && c1
<= '9') &&
639 (((c2
= cbuf
[1]) == '#') ||
640 (c2
>= '0' && c2
<= '9' && clen
> 2 && cbuf
[2] == '#'))){
641 base
= a_aux_idec_atoi
[(ui8_t
)c1
];
643 clen
-= 2, cbuf
+= 2;
645 clen
-= 3, cbuf
+= 3;
647 base
+= a_aux_idec_atoi
[(ui8_t
)c2
];
649 if(base
< 2 || base
> 36)
655 /* Character must be valid for base */
656 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
660 /* 0 always valid as is, fallback base 10 */
661 if(*++cbuf
== '\0' || --clen
== 0)
664 /* Base "detection" */
665 if(base
== 0 || base
== 2 || base
== 16){
676 if((base
& 16) == 0){
678 /* Char after prefix must be valid. However, after some error
679 * in the tor software all libraries (which had to) turned to
680 * an interpretation of the C standard which says that the
681 * prefix may optionally precede an otherwise valid sequence,
682 * which means that "0x" is not a STATE_INVAL error but gives
683 * a "0" result with a "STATE_BASE" error and a rest of "x" */
686 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
)
689 if(*++cbuf
== '\0' || --clen
== 0)
692 /* Character must be valid for base, invalid otherwise */
693 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
706 /* Character must be valid for base, _EBASE otherwise */
707 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
712 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
716 if(res
> UI64_MAX
- currc
)
726 if(*++cbuf
== '\0' || --clen
== 0)
729 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
738 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
739 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
740 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
741 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
742 default: uimask
= UI64_MAX
; break;
744 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
748 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
749 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
750 if(res
> uimask
+ 1){
759 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
760 rv
|= n_IDEC_STATE_EOVERFLOW
;
761 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
765 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
766 case n_IDEC_MODE_LIMIT_8BIT
:
767 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
768 *(si8_t
*)resp
= (si8_t
)res
;
770 *(ui8_t
*)resp
= (ui8_t
)res
;
772 case n_IDEC_MODE_LIMIT_16BIT
:
773 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
774 *(si16_t
*)resp
= (si16_t
)res
;
776 *(ui16_t
*)resp
= (ui16_t
)res
;
778 case n_IDEC_MODE_LIMIT_32BIT
:
779 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
780 *(si32_t
*)resp
= (si32_t
)res
;
782 *(ui32_t
*)resp
= (ui32_t
)res
;
785 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
786 *(si64_t
*)resp
= (si64_t
)res
;
788 *(ui64_t
*)resp
= (ui64_t
)res
;
792 if(endptr_or_null
!= NULL
)
793 *endptr_or_null
= cbuf
;
794 if(*cbuf
== '\0' || clen
== 0)
795 rv
|= n_IDEC_STATE_CONSUMED
;
800 rv
|= n_IDEC_STATE_EINVAL
;
803 /* Not a base error for terminator and whitespace! */
804 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
805 rv
|= n_IDEC_STATE_EBASE
;
809 /* Overflow error: consume input until bad character or length out */
811 if(*++cbuf
== '\0' || --clen
== 0)
813 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
818 rv
|= n_IDEC_STATE_EOVERFLOW
;
820 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
821 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
825 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
830 n_torek_hash(char const *name
){
831 /* Chris Torek's hash */
836 for(h
= 0; (c
= *name
++) != '\0';)
843 n_torek_ihashn(char const *dat
, size_t len
){
844 /* See n_torek_hash() */
850 for(h
= 0; (c
= *dat
++) != '\0';)
851 h
= (h
* 33) + lowerconv(c
);
853 for(h
= 0; len
> 0; --len
){
855 h
= (h
* 33) + lowerconv(c
);
862 n_prime_next(ui32_t n
){
863 static ui32_t
const primes
[] = {
864 5, 11, 23, 47, 97, 157, 283,
865 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
866 131071, 262139, 524287, 1048573, 2097143, 4194301,
867 8388593, 16777213, 33554393, 67108859, 134217689,
868 268435399, 536870909, 1073741789, 2147483647
873 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
874 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
875 : n_NELEM(primes
) / 2));
877 do if((mprime
= primes
[i
]) > n
)
879 while(++i
< n_NELEM(primes
));
881 if(i
== n_NELEM(primes
) && mprime
< n
)
888 n_getdeadletter(void){
895 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
896 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
898 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
899 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
904 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
905 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
913 n_nodename(bool_t mayoverride
){
914 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
919 # ifdef HAVE_GETADDRINFO
920 struct addrinfo hints
, *res
;
922 struct hostent
*hent
;
927 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
929 }else if((hn
= sys_hostname
) == NULL
){
933 # ifdef HAVE_GETADDRINFO
934 memset(&hints
, 0, sizeof hints
);
935 hints
.ai_family
= AF_UNSPEC
;
936 hints
.ai_flags
= AI_CANONNAME
;
937 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
938 if(res
->ai_canonname
!= NULL
){
941 l
= strlen(res
->ai_canonname
) +1;
942 hn
= n_lofi_alloc(l
);
943 memcpy(hn
, res
->ai_canonname
, l
);
948 hent
= gethostbyname(hn
);
953 sys_hostname
= sstrdup(hn
);
954 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
955 if(hn
!= ut
.nodename
)
961 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
963 hostname
= sstrdup(hn
);
970 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
975 if((rv
= (ilen
== 0)))
978 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
979 ibuf
= savestrbuf(ibuf
, ilen
);
982 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
984 if(idna_utf8
== NULL
)
987 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
991 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
992 out
= n_string_assign_cp(out
, idna_ascii
);
993 idn_free(idna_ascii
);
998 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
999 ilen
= strlen(idna_utf8
);
1001 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
1002 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
1003 case idn_buffer_overflow
:
1004 ilen
+= HOST_NAME_MAX
+1;
1008 ilen
= strlen(out
->s_dat
);
1016 # error Unknown HAVE_IDNA
1019 out
= n_string_trunc(out
, ilen
);
1023 #endif /* HAVE_IDNA */
1026 n_random_create_buf(char *dat
, size_t len
, ui32_t
*reprocnt_or_null
){
1028 char *indat
, *cp
, *oudat
;
1029 size_t i
, inlen
, oulen
;
1032 if(!(n_psonce
& n_PSO_RANDOM_INIT
)){
1033 n_psonce
|= n_PSO_RANDOM_INIT
;
1035 if(n_poption
& n_PO_D_V
){
1038 #if n_RANDOM_USE_XSSL
1039 prngn
= "*SSL RAND_*";
1040 #elif defined HAVE_POSIX_RANDOM
1041 prngn
= "POSIX/arc4random";
1043 prngn
= "builtin ARC4";
1045 n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn
);
1048 #if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL
1053 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1054 * with PAD stripped is still longer than what the user requests, easy way.
1055 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1056 * include the base64 PAD characters in our random string: give some pad */
1058 if((inlen
= i
% 3) != 0)
1067 inlen
= inlen
+ (inlen
<< 1);
1069 indat
= n_lofi_alloc(inlen
+1);
1071 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1072 #if n_RANDOM_USE_XSSL
1073 ssl_rand_bytes(indat
, inlen
);
1074 #elif !defined HAVE_POSIX_RANDOM
1075 for(i
= inlen
; i
-- > 0;)
1076 indat
[i
] = (char)a_aux_rand_get8();
1078 for(cp
= indat
, i
= inlen
; i
> 0;){
1079 union {ui32_t i4
; char c
[4];} r
;
1082 r
.i4
= (ui32_t
)arc4random();
1083 switch((j
= i
& 3)){
1084 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1085 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1086 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1087 default: cp
[0] = r
.c
[0]; break;
1094 for(cp
= indat
, i
= inlen
; i
> 0;){
1095 union {ui32_t i4
; char c
[4];} r
;
1098 r
.i4
= ++*reprocnt_or_null
;
1099 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1109 switch((j
= i
& 3)){
1110 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1111 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1112 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1113 default: cp
[0] = r
.c
[0]; break;
1120 oudat
= (len
>= oulen
) ? dat
: n_lofi_alloc(oulen
+1);
1122 b64_encode_buf(&b64
, indat
, inlen
, B64_BUF
| B64_RFC4648URL
| B64_NOPAD
);
1123 assert(b64
.l
>= len
);
1124 memcpy(dat
, b64
.s
, len
);
1136 n_random_create_cp(size_t len
, ui32_t
*reprocnt_or_null
){
1140 dat
= n_autorec_alloc(len
+1);
1141 dat
= n_random_create_buf(dat
, len
, reprocnt_or_null
);
1147 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1152 assert(inlen
== 0 || inbuf
!= NULL
);
1154 if (inlen
== UIZ_MAX
)
1155 inlen
= strlen(inbuf
);
1158 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1160 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1161 !ascncasecmp(inbuf
, "true", inlen
) ||
1162 !ascncasecmp(inbuf
, "yes", inlen
) ||
1163 !ascncasecmp(inbuf
, "on", inlen
))
1165 else if ((inlen
== 1 &&
1166 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1167 !ascncasecmp(inbuf
, "false", inlen
) ||
1168 !ascncasecmp(inbuf
, "no", inlen
) ||
1169 !ascncasecmp(inbuf
, "off", inlen
))
1174 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1175 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1176 ) != n_IDEC_STATE_CONSUMED
)
1187 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1192 assert(inlen
== 0 || inbuf
!= NULL
);
1194 if (inlen
== UIZ_MAX
)
1195 inlen
= strlen(inbuf
);
1198 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1199 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1200 !ascncasecmp(inbuf
, "ask-", 4) &&
1201 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1202 (n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1203 rv
= getapproval(prompt
, rv
);
1209 n_is_all_or_aster(char const *name
){
1213 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1218 FL
struct n_timespec
const *
1219 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1220 static struct n_timespec ts_now
;
1223 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1224 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1226 }else if(force_update
|| ts_now
.ts_sec
== 0){
1227 #ifdef HAVE_CLOCK_GETTIME
1230 clock_gettime(CLOCK_REALTIME
, &ts
);
1231 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1232 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1233 #elif defined HAVE_GETTIMEOFDAY
1236 gettimeofday(&tv
, NULL
);
1237 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1238 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1240 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1249 time_current_update(struct time_current
*tc
, bool_t full_update
)
1252 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1254 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1255 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1256 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1262 n_msleep(uiz_t millis
, bool_t ignint
){
1266 #ifdef HAVE_NANOSLEEP
1268 struct timespec ts
, trem
;
1271 ts
.tv_sec
= millis
/ 1000;
1272 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1274 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1276 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1279 #elif defined HAVE_SLEEP
1280 if((millis
/= 1000) == 0)
1282 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1285 # error Configuration should have detected a function for sleeping.
1293 n_err(char const *format
, ...){
1297 va_start(ap
, format
);
1299 if(n_psonce
& n_PSO_INTERACTIVE
)
1309 while(*format
== '\n'){
1311 putc('\n', n_stderr
);
1316 a_aux_err_linelen
= 0;
1318 if((len
= strlen(format
)) > 0){
1319 if(doname
|| a_aux_err_linelen
== 0){
1322 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1323 fputs(cp
, n_stderr
);
1325 vfprintf(n_stderr
, format
, ap
);
1330 if(format
[--len
] == '\n'){
1331 a_aux_err_linelen
= (i
-= ++len
);
1334 ++a_aux_err_linelen
;
1346 n_verr(char const *format
, va_list ap
){
1348 struct a_aux_err_node
*enp
;
1356 while(*format
== '\n'){
1357 putc('\n', n_stderr
);
1363 a_aux_err_linelen
= 0;
1365 if(n_psonce
& n_PSO_INTERACTIVE
){
1366 if((enp
= a_aux_err_tail
) != NULL
&&
1367 (enp
->ae_str
.s_len
> 0 &&
1368 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1369 n_string_push_c(&enp
->ae_str
, '\n');
1374 if((len
= strlen(format
)) == 0)
1377 n_pstate
|= n_PS_ERRORS_PROMPT
;
1380 if(doname
|| a_aux_err_linelen
== 0){
1383 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1384 fputs(cp
, n_stderr
);
1390 if(format
[--len
] == '\n'){
1391 a_aux_err_linelen
= (i
-= ++len
);
1394 ++a_aux_err_linelen
;
1399 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1401 vfprintf(n_stderr
, format
, ap
);
1405 n_LCTAV(ERRORS_MAX
> 3);
1407 /* Link it into the `errors' message ring */
1408 if((enp
= a_aux_err_tail
) == NULL
){
1410 enp
= smalloc(sizeof *enp
);
1411 enp
->ae_next
= NULL
;
1412 n_string_creat(&enp
->ae_str
);
1413 if(a_aux_err_tail
!= NULL
)
1414 a_aux_err_tail
->ae_next
= enp
;
1416 a_aux_err_head
= enp
;
1417 a_aux_err_tail
= enp
;
1420 (enp
->ae_str
.s_len
> 0 &&
1421 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1422 if(a_aux_err_cnt
< ERRORS_MAX
)
1425 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1426 a_aux_err_tail
->ae_next
= enp
;
1427 a_aux_err_tail
= enp
;
1428 enp
->ae_next
= NULL
;
1429 n_string_trunc(&enp
->ae_str
, 0);
1432 # ifdef HAVE_N_VA_COPY
1435 imax
= n_MIN(LINESIZE
, 1024);
1437 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1438 # ifdef HAVE_N_VA_COPY
1446 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1447 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1448 # ifdef HAVE_N_VA_COPY
1455 if(UICMP(z
, i
, >=, imax
)){
1456 # ifdef HAVE_N_VA_COPY
1457 /* XXX Check overflow for upcoming LEN+++i! */
1458 n_string_trunc(&enp
->ae_str
, len
);
1461 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1466 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1468 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1470 #endif /* HAVE_ERRORS */
1478 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1482 va_start(ap
, format
);
1483 vfprintf(n_stderr
, format
, ap
);
1489 n_perr(char const *msg
, int errval
){
1500 e
= (errval
== 0) ? n_err_no
: errval
;
1501 n_err(fmt
, msg
, n_err_to_doc(e
));
1508 n_alert(char const *format
, ...){
1512 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1514 va_start(ap
, format
);
1523 n_panic(char const *format
, ...){
1527 if(a_aux_err_linelen
> 0){
1528 putc('\n', n_stderr
);
1529 a_aux_err_linelen
= 0;
1531 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1533 va_start(ap
, format
);
1534 vfprintf(n_stderr
, format
, ap
);
1537 putc('\n', n_stderr
);
1540 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1547 struct a_aux_err_node
*enp
;
1554 if(!asccasecmp(*argv
, "show"))
1556 if(!asccasecmp(*argv
, "clear"))
1560 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1564 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1570 if(a_aux_err_head
== NULL
){
1571 fprintf(n_stderr
, _("The error ring is empty\n"));
1575 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1577 fprintf(n_stderr
, _("tmpfile"));
1582 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1583 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1584 /* We don't know whether last string ended with NL; be simple XXX */
1587 page_or_print(fp
, 0);
1593 a_aux_err_tail
= NULL
;
1594 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1595 a_aux_err_linelen
= 0;
1596 while((enp
= a_aux_err_head
) != NULL
){
1597 a_aux_err_head
= enp
->ae_next
;
1598 n_string_gut(&enp
->ae_str
);
1603 #endif /* HAVE_ERRORS */
1606 n_err_to_doc(si32_t eno
){
1608 struct a_aux_err_map
const *aemp
;
1611 aemp
= a_aux_err_map_from_no(eno
);
1612 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1618 n_err_to_name(si32_t eno
){
1620 struct a_aux_err_map
const *aemp
;
1623 aemp
= a_aux_err_map_from_no(eno
);
1624 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1630 n_err_from_name(char const *name
){
1631 struct a_aux_err_map
const *aemp
;
1632 ui32_t hash
, i
, j
, x
;
1636 hash
= n_torek_hash(name
);
1638 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1639 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1642 aemp
= &a_aux_err_map
[x
];
1643 if(aemp
->aem_hash
== hash
&&
1644 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1645 rv
= aemp
->aem_err_no
;
1649 if(++i
== a_AUX_ERR_REV_PRIME
){
1650 #ifdef a_AUX_ERR_REV_WRAPAROUND
1658 /* Have not found it. But wait, it could be that the user did, e.g.,
1659 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1660 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1661 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1662 ) == n_IDEC_STATE_CONSUMED
){
1663 aemp
= a_aux_err_map_from_no(rv
);
1664 rv
= aemp
->aem_err_no
;
1668 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1676 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1681 i
= regerror(e
, rep
, NULL
, 0) +1;
1683 regerror(e
, rep
, cp
, i
);