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 # include HAVE_GETRANDOM_HEADER
49 # ifdef HAVE_GETADDRINFO
50 # include <sys/socket.h>
56 #ifndef HAVE_POSIX_RANDOM
64 ui8_t b8
[sizeof(struct rand_arc4
)];
65 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
70 struct a_aux_err_node
{
71 struct a_aux_err_node
*ae_next
;
72 struct n_string ae_str
;
77 ui32_t aem_hash
; /* Hash of name */
78 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
79 ui32_t aem_docoff
; /* Into a_aux_err docs[] */
80 si32_t aem_err_no
; /* The OS error value for this one */
83 static ui8_t a_aux_idec_atoi
[256] = {
84 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
85 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
86 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
87 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
88 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
89 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
90 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
91 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
92 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
93 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
94 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
95 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
96 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
97 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
98 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
99 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
100 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
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,0xFF,0xFF,
106 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
109 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
112 #define a_X(X) ((ui64_t)-1 / (X))
113 static ui64_t
const a_aux_idec_cutlimit
[35] = {
114 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
115 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
116 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
117 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
118 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
122 /* Include the constant make-errors.sh output */
123 #include "gen-errors.h"
125 /* And these things come from mk-config.h (config-time make-errors.sh output) */
126 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
128 #define a_X(N,I) {N,I},
129 n__ERR_NUMBER_TO_MAPOFF
133 #ifndef HAVE_POSIX_RANDOM
134 static union rand_state
*a_aux_rand
;
137 /* Error ring, for `errors' */
139 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
140 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
142 static size_t a_aux_err_linelen
;
144 /* Our ARC4 random generator with its completely unacademical pseudo
145 * initialization (shall /dev/urandom fail) */
146 #ifndef HAVE_POSIX_RANDOM
147 static void a_aux_rand_init(void);
148 SINLINE ui8_t
a_aux_rand_get8(void);
149 # ifndef HAVE_GETRANDOM
150 static ui32_t
a_aux_rand_weak(ui32_t seed
);
154 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
155 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
157 #ifndef HAVE_POSIX_RANDOM
159 a_aux_rand_init(void){
160 # ifndef HAVE_GETRANDOM
161 # ifdef HAVE_CLOCK_GETTIME
166 union {int fd
; size_t i
;} u
;
171 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
173 # ifdef HAVE_GETRANDOM
174 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
175 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
176 "Buffer too large to be served without n_ERR_INTR error");
177 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
178 "Buffer too small to serve used array indices");
182 gr
= HAVE_GETRANDOM(a_aux_rand
->a
._dat
, sizeof a_aux_rand
->a
._dat
);
183 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
184 a_aux_rand
->a
._dat
[84]];
185 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
186 a_aux_rand
->a
._dat
[42]];
187 /* ..but be on the safe side */
188 if(UICMP(z
, gr
, ==, sizeof(a_aux_rand
->a
._dat
)))
194 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
197 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
198 sizeof(a_aux_rand
->a
._dat
)));
201 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
202 a_aux_rand
->a
._dat
[84]];
203 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
204 a_aux_rand
->a
._dat
[42]];
209 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
210 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
213 # ifdef HAVE_CLOCK_GETTIME
214 clock_gettime(CLOCK_REALTIME
, &ts
);
215 t
= (ui32_t
)ts
.tv_nsec
;
217 gettimeofday(&ts
, NULL
);
218 t
= (ui32_t
)ts
.tv_usec
;
221 t
= (t
>> 16) | (t
<< 16);
222 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
223 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
224 if(rnd
== 7 || rnd
== 17)
225 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
226 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
227 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
228 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
230 seed
^= n_prime_next(seed
);
234 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
237 # endif /* !HAVE_GETRANDOM */
242 a_aux_rand_get8(void){
245 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
246 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
247 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
248 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
249 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
252 # ifndef HAVE_GETRANDOM
254 a_aux_rand_weak(ui32_t seed
){
255 /* From "Random number generators: good ones are hard to find",
256 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
257 * October 1988, p. 1195.
258 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
265 seed
= (seed
* 16807) - (hi
* 2836);
270 # endif /* HAVE_GETRANDOM */
271 #endif /* !HAVE_POSIX_RANDOM */
273 static struct a_aux_err_map
const *
274 a_aux_err_map_from_no(si32_t eno
){
277 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
278 struct a_aux_err_map
const *aemp
;
281 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
283 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
284 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
285 asz
!= 0; asz
>>= 1){
286 tmp
= &adat
[asz
>> 1];
287 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
288 aemp
= &a_aux_err_map
[(*tmp
)[1]];
307 if((cp
= ok_vlook(screen
)) != NULL
){
308 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
321 n_pager_get(char const **env_addon
){
325 rv
= ok_vlook(PAGER
);
327 if(env_addon
!= NULL
){
329 /* Update the manual upon any changes:
330 * *colour-pager*, $PAGER */
331 if(strstr(rv
, "less") != NULL
){
332 if(getenv("LESS") == NULL
)
335 (n_psonce
& n_PSO_TERMCAP_CA_MODE
) ? "LESS=Ri"
336 : !(n_psonce
& n_PSO_TERMCAP_DISABLE
) ? "LESS=FRi" :
339 }else if(strstr(rv
, "lv") != NULL
){
340 if(getenv("LV") == NULL
)
341 *env_addon
= "LV=-c";
349 page_or_print(FILE *fp
, size_t lines
)
357 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
361 rows
= (size_t)n_scrnheight
;
363 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
365 if (rows
> 0 && lines
== 0) {
366 while ((c
= getc(fp
)) != EOF
)
367 if (c
== '\n' && ++lines
>= rows
)
373 char const *env_add
[2], *pager
;
375 pager
= n_pager_get(&env_add
[0]);
377 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
383 while ((c
= getc(fp
)) != EOF
)
390 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
391 char const **adjusted_or_null
)
393 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
394 char const *cp
, *orig_name
;
395 enum protocol rv
= PROTO_UNKNOWN
;
398 if(name
[0] == '%' && name
[1] == ':')
402 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
406 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
407 if(!strncmp(name
, "file", sizeof("file") -1) ||
408 !strncmp(name
, "mbox", sizeof("mbox") -1))
410 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
412 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
416 n_err(_("No POP3 support compiled in\n"));
418 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
419 #if defined HAVE_POP3 && defined HAVE_SSL
422 n_err(_("No POP3S support compiled in\n"));
432 if(check_stat
|| try_hooks
){
433 struct n_file_type ft
;
438 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
439 memcpy(np
, name
, sz
+ 1);
441 if(!stat(name
, &stb
)){
442 if(S_ISDIR(stb
.st_mode
) &&
443 (memcpy(&np
[sz
], "/tmp", 5),
444 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
445 (memcpy(&np
[sz
], "/new", 5),
446 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
447 (memcpy(&np
[sz
], "/cur", 5),
448 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
450 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
451 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
452 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
453 !asccasecmp(cp
, "maildir"))
459 if(adjusted_or_null
!= NULL
)
460 *adjusted_or_null
= orig_name
;
466 n_c_to_hex_base16(char store
[3], char c
){
467 static char const itoa16
[] = "0123456789ABCDEF";
471 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
472 c
= ((ui8_t
)c
>> 4) & 0x0F;
473 store
[0] = itoa16
[(ui8_t
)c
];
479 n_c_from_hex_base16(char const hex
[2]){
480 static ui8_t
const atoi16
[] = {
481 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
482 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
483 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
484 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
485 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
486 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
487 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
493 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
494 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
498 if ((i1
| i2
) & 0xF0u
)
512 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
513 enum n_idec_mode idm
, char const **endptr_or_null
){
514 /* XXX Brute simple and */
517 enum n_idec_state rv
;
520 idm
&= n__IDEC_MODE_MASK
;
521 rv
= n_IDEC_STATE_NONE
| idm
;
531 while(spacechar(*cbuf
))
532 if(*++cbuf
== '\0' || --clen
== 0)
538 rv
|= n_IDEC_STATE_SEEN_MINUS
;
541 if(*++cbuf
== '\0' || --clen
== 0)
546 /* Base detection/skip */
550 /* Character must be valid for base */
551 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
555 /* 0 always valid as is, fallback base 10 */
556 if(*++cbuf
== '\0' || --clen
== 0)
559 /* Base "detection" */
560 if(base
== 0 || base
== 2 || base
== 16){
571 if((base
& 16) == 0){
573 /* Char after prefix must be valid */
575 if(*++cbuf
== '\0' || --clen
== 0)
578 /* Character must be valid for base, invalid otherwise */
579 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
591 /* Character must be valid for base, _EBASE otherwise */
592 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
597 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
601 if(res
> UI64_MAX
- currc
)
611 if(*++cbuf
== '\0' || --clen
== 0)
614 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
623 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
624 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
625 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
626 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
627 default: uimask
= UI64_MAX
; break;
629 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
633 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
634 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
635 if(res
> uimask
+ 1){
644 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
645 rv
|= n_IDEC_STATE_EOVERFLOW
;
646 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
650 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
651 case n_IDEC_MODE_LIMIT_8BIT
:
652 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
653 *(si8_t
*)resp
= (si8_t
)res
;
655 *(ui8_t
*)resp
= (ui8_t
)res
;
657 case n_IDEC_MODE_LIMIT_16BIT
:
658 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
659 *(si16_t
*)resp
= (si16_t
)res
;
661 *(ui16_t
*)resp
= (ui16_t
)res
;
663 case n_IDEC_MODE_LIMIT_32BIT
:
664 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
665 *(si32_t
*)resp
= (si32_t
)res
;
667 *(ui32_t
*)resp
= (ui32_t
)res
;
670 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
671 *(si64_t
*)resp
= (si64_t
)res
;
673 *(ui64_t
*)resp
= (ui64_t
)res
;
677 if(endptr_or_null
!= NULL
)
678 *endptr_or_null
= cbuf
;
679 if(*cbuf
== '\0' || clen
== 0)
680 rv
|= n_IDEC_STATE_CONSUMED
;
685 rv
|= n_IDEC_STATE_EINVAL
;
688 /* Not a base error for terminator and whitespace! */
689 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
690 rv
|= n_IDEC_STATE_EBASE
;
694 /* Overflow error: consume input until bad character or length out */
696 if(*++cbuf
== '\0' || --clen
== 0)
698 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
703 rv
|= n_IDEC_STATE_EOVERFLOW
;
705 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
706 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
710 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
715 n_torek_hash(char const *name
){
716 /* Chris Torek's hash */
721 for(h
= 0; (c
= *name
++) != '\0';)
728 n_torek_ihashn(char const *dat
, size_t len
){
729 /* See n_torek_hash() */
734 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
735 h
= (h
* 33) + lowerconv(c
);
741 n_prime_next(ui32_t n
){
742 static ui32_t
const primes
[] = {
743 5, 11, 23, 47, 97, 157, 283,
744 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
745 131071, 262139, 524287, 1048573, 2097143, 4194301,
746 8388593, 16777213, 33554393, 67108859, 134217689,
747 268435399, 536870909, 1073741789, 2147483647
752 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
753 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
754 : n_NELEM(primes
) / 2));
756 do if((mprime
= primes
[i
]) > n
)
758 while(++i
< n_NELEM(primes
));
760 if(i
== n_NELEM(primes
) && mprime
< n
)
767 n_getdeadletter(void){
768 char const *cp_base
, *cp
;
773 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
774 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
776 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
777 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
781 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
782 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
790 n_nodename(bool_t mayoverride
){
791 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
796 # ifdef HAVE_GETADDRINFO
797 struct addrinfo hints
, *res
;
799 struct hostent
*hent
;
804 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
806 }else if((hn
= sys_hostname
) == NULL
){
810 # ifdef HAVE_GETADDRINFO
811 memset(&hints
, 0, sizeof hints
);
812 hints
.ai_family
= AF_UNSPEC
;
813 hints
.ai_flags
= AI_CANONNAME
;
814 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
815 if(res
->ai_canonname
!= NULL
){
818 l
= strlen(res
->ai_canonname
) +1;
819 hn
= n_lofi_alloc(l
);
820 memcpy(hn
, res
->ai_canonname
, l
);
825 hent
= gethostbyname(hn
);
830 sys_hostname
= sstrdup(hn
);
831 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
832 if(hn
!= ut
.nodename
)
838 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
840 hostname
= sstrdup(hn
);
846 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
852 #ifndef HAVE_POSIX_RANDOM
853 if(a_aux_rand
== NULL
)
857 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
858 * with PAD stripped is still longer than what the user requests, easy way */
859 data
= n_lofi_alloc(i
= length
+ 3);
861 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
862 #ifndef HAVE_POSIX_RANDOM
864 data
[i
] = (char)a_aux_rand_get8();
866 for(cp
= data
; i
> 0;){
867 union {ui32_t i4
; char c
[4];} r
;
870 r
.i4
= (ui32_t
)arc4random();
872 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
873 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
874 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
875 default: cp
[0] = r
.c
[0]; break;
882 for(cp
= data
; i
> 0;){
883 union {ui32_t i4
; char c
[4];} r
;
886 r
.i4
= ++*reprocnt_or_null
;
887 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
898 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
899 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
900 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
901 default: cp
[0] = r
.c
[0]; break;
908 assert(length
+ 3 < UIZ_MAX
/ 4);
909 b64_encode_buf(&b64
, data
, length
+ 3,
910 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
913 assert(b64
.l
>= length
);
914 b64
.s
[length
] = '\0';
920 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
925 assert(inlen
== 0 || inbuf
!= NULL
);
927 if (inlen
== UIZ_MAX
)
928 inlen
= strlen(inbuf
);
931 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
933 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
934 !ascncasecmp(inbuf
, "true", inlen
) ||
935 !ascncasecmp(inbuf
, "yes", inlen
) ||
936 !ascncasecmp(inbuf
, "on", inlen
))
938 else if ((inlen
== 1 &&
939 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
940 !ascncasecmp(inbuf
, "false", inlen
) ||
941 !ascncasecmp(inbuf
, "no", inlen
) ||
942 !ascncasecmp(inbuf
, "off", inlen
))
947 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
948 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
949 ) != n_IDEC_STATE_CONSUMED
)
960 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
965 assert(inlen
== 0 || inbuf
!= NULL
);
967 if (inlen
== UIZ_MAX
)
968 inlen
= strlen(inbuf
);
971 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
972 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
973 !ascncasecmp(inbuf
, "ask-", 4) &&
974 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
975 (n_psonce
& n_PSO_INTERACTIVE
))
976 rv
= getapproval(prompt
, rv
);
982 n_is_all_or_aster(char const *name
){
986 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
991 FL
struct n_timespec
const *
992 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
993 static struct n_timespec ts_now
;
996 if(n_psonce
& n_PSO_REPRODUCIBLE
){
997 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
999 }else if(force_update
|| ts_now
.ts_sec
== 0){
1000 #ifdef HAVE_CLOCK_GETTIME
1003 clock_gettime(CLOCK_REALTIME
, &ts
);
1004 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1005 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1006 #elif defined HAVE_GETTIMEOFDAY
1009 gettimeofday(&tv
, NULL
);
1010 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1011 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1013 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1022 time_current_update(struct time_current
*tc
, bool_t full_update
)
1025 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1027 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1028 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1029 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1035 n_msleep(uiz_t millis
, bool_t ignint
){
1039 #ifdef HAVE_NANOSLEEP
1041 struct timespec ts
, trem
;
1044 ts
.tv_sec
= millis
/ 1000;
1045 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1047 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1049 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1052 #elif defined HAVE_SLEEP
1053 if((millis
/= 1000) == 0)
1055 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1058 # error Configuration should have detected a function for sleeping.
1066 n_err(char const *format
, ...){
1070 va_start(ap
, format
);
1072 if(n_psonce
& n_PSO_INTERACTIVE
)
1078 bool_t doname
, doflush
;
1081 while(*format
== '\n'){
1083 putc('\n', n_stderr
);
1087 if((doname
= doflush
))
1088 a_aux_err_linelen
= 0;
1090 if((len
= strlen(format
)) > 0){
1091 if(doname
|| a_aux_err_linelen
== 0){
1094 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1095 fputs(cp
, n_stderr
);
1097 vfprintf(n_stderr
, format
, ap
);
1102 if(format
[--len
] == '\n'){
1103 a_aux_err_linelen
= (i
-= ++len
);
1106 ++a_aux_err_linelen
;
1119 n_verr(char const *format
, va_list ap
){
1121 struct a_aux_err_node
*enp
;
1129 while(*format
== '\n'){
1130 putc('\n', n_stderr
);
1136 a_aux_err_linelen
= 0;
1138 if(n_psonce
& n_PSO_INTERACTIVE
){
1139 if((enp
= a_aux_err_tail
) != NULL
&&
1140 (enp
->ae_str
.s_len
> 0 &&
1141 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1142 n_string_push_c(&enp
->ae_str
, '\n');
1147 if((len
= strlen(format
)) == 0)
1150 n_pstate
|= n_PS_ERRORS_PROMPT
;
1153 if(doname
|| a_aux_err_linelen
== 0){
1156 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1157 fputs(cp
, n_stderr
);
1163 if(format
[--len
] == '\n'){
1164 a_aux_err_linelen
= (i
-= ++len
);
1167 ++a_aux_err_linelen
;
1172 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1174 vfprintf(n_stderr
, format
, ap
);
1178 n_LCTAV(ERRORS_MAX
> 3);
1180 /* Link it into the `errors' message ring */
1181 if((enp
= a_aux_err_tail
) == NULL
){
1183 enp
= smalloc(sizeof *enp
);
1184 enp
->ae_next
= NULL
;
1185 n_string_creat(&enp
->ae_str
);
1186 if(a_aux_err_tail
!= NULL
)
1187 a_aux_err_tail
->ae_next
= enp
;
1189 a_aux_err_head
= enp
;
1190 a_aux_err_tail
= enp
;
1193 (enp
->ae_str
.s_len
> 0 &&
1194 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1195 if(a_aux_err_cnt
< ERRORS_MAX
)
1198 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1199 a_aux_err_tail
->ae_next
= enp
;
1200 a_aux_err_tail
= enp
;
1201 enp
->ae_next
= NULL
;
1202 n_string_trunc(&enp
->ae_str
, 0);
1205 # ifdef HAVE_N_VA_COPY
1208 imax
= n_MIN(LINESIZE
, 1024);
1210 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1211 # ifdef HAVE_N_VA_COPY
1219 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1220 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1221 # ifdef HAVE_N_VA_COPY
1228 if(UICMP(z
, i
, >=, imax
)){
1229 # ifdef HAVE_N_VA_COPY
1230 /* XXX Check overflow for upcoming LEN+++i! */
1231 n_string_trunc(&enp
->ae_str
, len
);
1234 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1239 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1241 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1243 #endif /* HAVE_ERRORS */
1251 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1255 va_start(ap
, format
);
1256 vfprintf(n_stderr
, format
, ap
);
1262 n_perr(char const *msg
, int errval
){
1273 e
= (errval
== 0) ? n_err_no
: errval
;
1274 n_err(fmt
, msg
, n_err_to_doc(e
));
1281 n_alert(char const *format
, ...){
1285 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1287 va_start(ap
, format
);
1296 n_panic(char const *format
, ...){
1300 if(a_aux_err_linelen
> 0){
1301 putc('\n', n_stderr
);
1302 a_aux_err_linelen
= 0;
1304 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1306 va_start(ap
, format
);
1307 vfprintf(n_stderr
, format
, ap
);
1310 putc('\n', n_stderr
);
1313 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1320 struct a_aux_err_node
*enp
;
1327 if(!asccasecmp(*argv
, "show"))
1329 if(!asccasecmp(*argv
, "clear"))
1333 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1337 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1343 if(a_aux_err_head
== NULL
){
1344 fprintf(n_stderr
, _("The error ring is empty\n"));
1348 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1350 fprintf(n_stderr
, _("tmpfile"));
1355 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1356 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1357 /* We don't know whether last string ended with NL; be simple XXX */
1360 page_or_print(fp
, 0);
1366 a_aux_err_tail
= NULL
;
1367 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1368 a_aux_err_linelen
= 0;
1369 while((enp
= a_aux_err_head
) != NULL
){
1370 a_aux_err_head
= enp
->ae_next
;
1371 n_string_gut(&enp
->ae_str
);
1376 #endif /* HAVE_ERRORS */
1379 n_err_to_doc(si32_t eno
){
1381 struct a_aux_err_map
const *aemp
;
1384 aemp
= a_aux_err_map_from_no(eno
);
1385 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1391 n_err_to_name(si32_t eno
){
1393 struct a_aux_err_map
const *aemp
;
1396 aemp
= a_aux_err_map_from_no(eno
);
1397 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1403 n_err_from_name(char const *name
){
1404 struct a_aux_err_map
const *aemp
;
1405 ui32_t hash
, i
, j
, x
;
1409 hash
= n_torek_hash(name
);
1411 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1412 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1415 aemp
= &a_aux_err_map
[x
];
1416 if(aemp
->aem_hash
== hash
&&
1417 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1418 rv
= aemp
->aem_err_no
;
1422 if(++i
== a_AUX_ERR_REV_PRIME
){
1423 #ifdef a_AUX_ERR_REV_WRAPAROUND
1431 /* Have not found it. But wait, it could be that the user did, e.g.,
1432 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1433 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1434 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1435 ) == n_IDEC_STATE_CONSUMED
){
1436 aemp
= a_aux_err_map_from_no(rv
);
1437 rv
= aemp
->aem_err_no
;
1441 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1449 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1454 i
= regerror(e
, rep
, NULL
, 0) +1;
1456 regerror(e
, rep
, cp
, i
);