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() */
735 for(h
= 0; (c
= *dat
++) != '\0';)
736 h
= (h
* 33) + lowerconv(c
);
738 for(h
= 0; len
> 0; --len
){
740 h
= (h
* 33) + lowerconv(c
);
747 n_prime_next(ui32_t n
){
748 static ui32_t
const primes
[] = {
749 5, 11, 23, 47, 97, 157, 283,
750 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
751 131071, 262139, 524287, 1048573, 2097143, 4194301,
752 8388593, 16777213, 33554393, 67108859, 134217689,
753 268435399, 536870909, 1073741789, 2147483647
758 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
759 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
760 : n_NELEM(primes
) / 2));
762 do if((mprime
= primes
[i
]) > n
)
764 while(++i
< n_NELEM(primes
));
766 if(i
== n_NELEM(primes
) && mprime
< n
)
773 n_getdeadletter(void){
774 char const *cp_base
, *cp
;
779 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
780 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
782 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
783 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
787 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
788 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
796 n_nodename(bool_t mayoverride
){
797 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
802 # ifdef HAVE_GETADDRINFO
803 struct addrinfo hints
, *res
;
805 struct hostent
*hent
;
810 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
812 }else if((hn
= sys_hostname
) == NULL
){
816 # ifdef HAVE_GETADDRINFO
817 memset(&hints
, 0, sizeof hints
);
818 hints
.ai_family
= AF_UNSPEC
;
819 hints
.ai_flags
= AI_CANONNAME
;
820 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
821 if(res
->ai_canonname
!= NULL
){
824 l
= strlen(res
->ai_canonname
) +1;
825 hn
= n_lofi_alloc(l
);
826 memcpy(hn
, res
->ai_canonname
, l
);
831 hent
= gethostbyname(hn
);
836 sys_hostname
= sstrdup(hn
);
837 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
838 if(hn
!= ut
.nodename
)
844 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
846 hostname
= sstrdup(hn
);
852 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
858 #ifndef HAVE_POSIX_RANDOM
859 if(a_aux_rand
== NULL
)
863 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
864 * with PAD stripped is still longer than what the user requests, easy way */
865 data
= n_lofi_alloc(i
= length
+ 3);
867 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
868 #ifndef HAVE_POSIX_RANDOM
870 data
[i
] = (char)a_aux_rand_get8();
872 for(cp
= data
; i
> 0;){
873 union {ui32_t i4
; char c
[4];} r
;
876 r
.i4
= (ui32_t
)arc4random();
878 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
879 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
880 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
881 default: cp
[0] = r
.c
[0]; break;
888 for(cp
= data
; i
> 0;){
889 union {ui32_t i4
; char c
[4];} r
;
892 r
.i4
= ++*reprocnt_or_null
;
893 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
904 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
905 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
906 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
907 default: cp
[0] = r
.c
[0]; break;
914 assert(length
+ 3 < UIZ_MAX
/ 4);
915 b64_encode_buf(&b64
, data
, length
+ 3,
916 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
919 assert(b64
.l
>= length
);
920 b64
.s
[length
] = '\0';
926 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
931 assert(inlen
== 0 || inbuf
!= NULL
);
933 if (inlen
== UIZ_MAX
)
934 inlen
= strlen(inbuf
);
937 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
939 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
940 !ascncasecmp(inbuf
, "true", inlen
) ||
941 !ascncasecmp(inbuf
, "yes", inlen
) ||
942 !ascncasecmp(inbuf
, "on", inlen
))
944 else if ((inlen
== 1 &&
945 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
946 !ascncasecmp(inbuf
, "false", inlen
) ||
947 !ascncasecmp(inbuf
, "no", inlen
) ||
948 !ascncasecmp(inbuf
, "off", inlen
))
953 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
954 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
955 ) != n_IDEC_STATE_CONSUMED
)
966 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
971 assert(inlen
== 0 || inbuf
!= NULL
);
973 if (inlen
== UIZ_MAX
)
974 inlen
= strlen(inbuf
);
977 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
978 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
979 !ascncasecmp(inbuf
, "ask-", 4) &&
980 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
981 (n_psonce
& n_PSO_INTERACTIVE
))
982 rv
= getapproval(prompt
, rv
);
988 n_is_all_or_aster(char const *name
){
992 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
997 FL
struct n_timespec
const *
998 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
999 static struct n_timespec ts_now
;
1002 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1003 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1005 }else if(force_update
|| ts_now
.ts_sec
== 0){
1006 #ifdef HAVE_CLOCK_GETTIME
1009 clock_gettime(CLOCK_REALTIME
, &ts
);
1010 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1011 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1012 #elif defined HAVE_GETTIMEOFDAY
1015 gettimeofday(&tv
, NULL
);
1016 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1017 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1019 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1028 time_current_update(struct time_current
*tc
, bool_t full_update
)
1031 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1033 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1034 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1035 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1041 n_msleep(uiz_t millis
, bool_t ignint
){
1045 #ifdef HAVE_NANOSLEEP
1047 struct timespec ts
, trem
;
1050 ts
.tv_sec
= millis
/ 1000;
1051 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1053 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1055 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1058 #elif defined HAVE_SLEEP
1059 if((millis
/= 1000) == 0)
1061 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1064 # error Configuration should have detected a function for sleeping.
1072 n_err(char const *format
, ...){
1076 va_start(ap
, format
);
1078 if(n_psonce
& n_PSO_INTERACTIVE
)
1084 bool_t doname
, doflush
;
1087 while(*format
== '\n'){
1089 putc('\n', n_stderr
);
1093 if((doname
= doflush
))
1094 a_aux_err_linelen
= 0;
1096 if((len
= strlen(format
)) > 0){
1097 if(doname
|| a_aux_err_linelen
== 0){
1100 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1101 fputs(cp
, n_stderr
);
1103 vfprintf(n_stderr
, format
, ap
);
1108 if(format
[--len
] == '\n'){
1109 a_aux_err_linelen
= (i
-= ++len
);
1112 ++a_aux_err_linelen
;
1125 n_verr(char const *format
, va_list ap
){
1127 struct a_aux_err_node
*enp
;
1135 while(*format
== '\n'){
1136 putc('\n', n_stderr
);
1142 a_aux_err_linelen
= 0;
1144 if(n_psonce
& n_PSO_INTERACTIVE
){
1145 if((enp
= a_aux_err_tail
) != NULL
&&
1146 (enp
->ae_str
.s_len
> 0 &&
1147 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1148 n_string_push_c(&enp
->ae_str
, '\n');
1153 if((len
= strlen(format
)) == 0)
1156 n_pstate
|= n_PS_ERRORS_PROMPT
;
1159 if(doname
|| a_aux_err_linelen
== 0){
1162 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1163 fputs(cp
, n_stderr
);
1169 if(format
[--len
] == '\n'){
1170 a_aux_err_linelen
= (i
-= ++len
);
1173 ++a_aux_err_linelen
;
1178 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1180 vfprintf(n_stderr
, format
, ap
);
1184 n_LCTAV(ERRORS_MAX
> 3);
1186 /* Link it into the `errors' message ring */
1187 if((enp
= a_aux_err_tail
) == NULL
){
1189 enp
= smalloc(sizeof *enp
);
1190 enp
->ae_next
= NULL
;
1191 n_string_creat(&enp
->ae_str
);
1192 if(a_aux_err_tail
!= NULL
)
1193 a_aux_err_tail
->ae_next
= enp
;
1195 a_aux_err_head
= enp
;
1196 a_aux_err_tail
= enp
;
1199 (enp
->ae_str
.s_len
> 0 &&
1200 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1201 if(a_aux_err_cnt
< ERRORS_MAX
)
1204 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1205 a_aux_err_tail
->ae_next
= enp
;
1206 a_aux_err_tail
= enp
;
1207 enp
->ae_next
= NULL
;
1208 n_string_trunc(&enp
->ae_str
, 0);
1211 # ifdef HAVE_N_VA_COPY
1214 imax
= n_MIN(LINESIZE
, 1024);
1216 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1217 # ifdef HAVE_N_VA_COPY
1225 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1226 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1227 # ifdef HAVE_N_VA_COPY
1234 if(UICMP(z
, i
, >=, imax
)){
1235 # ifdef HAVE_N_VA_COPY
1236 /* XXX Check overflow for upcoming LEN+++i! */
1237 n_string_trunc(&enp
->ae_str
, len
);
1240 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1245 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1247 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1249 #endif /* HAVE_ERRORS */
1257 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1261 va_start(ap
, format
);
1262 vfprintf(n_stderr
, format
, ap
);
1268 n_perr(char const *msg
, int errval
){
1279 e
= (errval
== 0) ? n_err_no
: errval
;
1280 n_err(fmt
, msg
, n_err_to_doc(e
));
1287 n_alert(char const *format
, ...){
1291 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1293 va_start(ap
, format
);
1302 n_panic(char const *format
, ...){
1306 if(a_aux_err_linelen
> 0){
1307 putc('\n', n_stderr
);
1308 a_aux_err_linelen
= 0;
1310 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1312 va_start(ap
, format
);
1313 vfprintf(n_stderr
, format
, ap
);
1316 putc('\n', n_stderr
);
1319 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1326 struct a_aux_err_node
*enp
;
1333 if(!asccasecmp(*argv
, "show"))
1335 if(!asccasecmp(*argv
, "clear"))
1339 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1343 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1349 if(a_aux_err_head
== NULL
){
1350 fprintf(n_stderr
, _("The error ring is empty\n"));
1354 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1356 fprintf(n_stderr
, _("tmpfile"));
1361 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1362 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1363 /* We don't know whether last string ended with NL; be simple XXX */
1366 page_or_print(fp
, 0);
1372 a_aux_err_tail
= NULL
;
1373 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1374 a_aux_err_linelen
= 0;
1375 while((enp
= a_aux_err_head
) != NULL
){
1376 a_aux_err_head
= enp
->ae_next
;
1377 n_string_gut(&enp
->ae_str
);
1382 #endif /* HAVE_ERRORS */
1385 n_err_to_doc(si32_t eno
){
1387 struct a_aux_err_map
const *aemp
;
1390 aemp
= a_aux_err_map_from_no(eno
);
1391 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1397 n_err_to_name(si32_t eno
){
1399 struct a_aux_err_map
const *aemp
;
1402 aemp
= a_aux_err_map_from_no(eno
);
1403 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1409 n_err_from_name(char const *name
){
1410 struct a_aux_err_map
const *aemp
;
1411 ui32_t hash
, i
, j
, x
;
1415 hash
= n_torek_hash(name
);
1417 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1418 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1421 aemp
= &a_aux_err_map
[x
];
1422 if(aemp
->aem_hash
== hash
&&
1423 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1424 rv
= aemp
->aem_err_no
;
1428 if(++i
== a_AUX_ERR_REV_PRIME
){
1429 #ifdef a_AUX_ERR_REV_WRAPAROUND
1437 /* Have not found it. But wait, it could be that the user did, e.g.,
1438 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1439 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1440 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1441 ) == n_IDEC_STATE_CONSUMED
){
1442 aemp
= a_aux_err_map_from_no(rv
);
1443 rv
= aemp
->aem_err_no
;
1447 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1455 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1460 i
= regerror(e
, rep
, NULL
, 0) +1;
1462 regerror(e
, rep
, cp
, i
);