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 mk-errors.sh output */
123 #include "gen-errors.h"
125 /* And these things come from config.h (config-time mk-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
= smalloc(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 to large to be served without n_ERR_INTR error");
180 gr
= HAVE_GETRANDOM(a_aux_rand
->a
._dat
, sizeof a_aux_rand
->a
._dat
);
181 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
182 a_aux_rand
->a
._dat
[84]];
183 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
184 a_aux_rand
->a
._dat
[42]];
185 /* ..but be on the safe side */
186 if(UICMP(z
, gr
, ==, sizeof(a_aux_rand
->a
._dat
)))
192 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
195 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
196 sizeof(a_aux_rand
->a
._dat
)));
199 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
200 a_aux_rand
->a
._dat
[84]];
201 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
202 a_aux_rand
->a
._dat
[42]];
207 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
208 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
211 # ifdef HAVE_CLOCK_GETTIME
212 clock_gettime(CLOCK_REALTIME
, &ts
);
213 t
= (ui32_t
)ts
.tv_nsec
;
215 gettimeofday(&ts
, NULL
);
216 t
= (ui32_t
)ts
.tv_usec
;
219 t
= (t
>> 16) | (t
<< 16);
220 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
221 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
222 if(rnd
== 7 || rnd
== 17)
223 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
224 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
225 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
226 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
228 seed
^= n_prime_next(seed
);
232 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
235 # endif /* !HAVE_GETRANDOM */
240 a_aux_rand_get8(void){
243 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
244 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
245 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
246 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
247 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
250 # ifndef HAVE_GETRANDOM
252 a_aux_rand_weak(ui32_t seed
){
253 /* From "Random number generators: good ones are hard to find",
254 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
255 * October 1988, p. 1195.
256 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
263 seed
= (seed
* 16807) - (hi
* 2836);
268 # endif /* HAVE_GETRANDOM */
269 #endif /* !HAVE_POSIX_RANDOM */
271 static struct a_aux_err_map
const *
272 a_aux_err_map_from_no(si32_t eno
){
275 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
276 struct a_aux_err_map
const *aemp
;
279 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
281 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
282 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
283 asz
!= 0; asz
>>= 1){
284 tmp
= &adat
[asz
>> 1];
285 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
286 aemp
= &a_aux_err_map
[(*tmp
)[1]];
305 if((cp
= ok_vlook(screen
)) != NULL
){
306 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
319 n_pager_get(char const **env_addon
){
323 rv
= ok_vlook(PAGER
);
325 if(env_addon
!= NULL
){
327 /* Update the manual upon any changes:
328 * *colour-pager*, $PAGER */
329 if(strstr(rv
, "less") != NULL
){
330 if(getenv("LESS") == NULL
)
333 (n_psonce
& n_PSO_TERMCAP_CA_MODE
) ? "LESS=Ri"
334 : !(n_psonce
& n_PSO_TERMCAP_DISABLE
) ? "LESS=FRi" :
337 }else if(strstr(rv
, "lv") != NULL
){
338 if(getenv("LV") == NULL
)
339 *env_addon
= "LV=-c";
347 page_or_print(FILE *fp
, size_t lines
)
355 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
359 rows
= (size_t)n_scrnheight
;
361 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
363 if (rows
> 0 && lines
== 0) {
364 while ((c
= getc(fp
)) != EOF
)
365 if (c
== '\n' && ++lines
>= rows
)
371 char const *env_add
[2], *pager
;
373 pager
= n_pager_get(&env_add
[0]);
375 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
381 while ((c
= getc(fp
)) != EOF
)
388 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
394 enum protocol rv
= PROTO_UNKNOWN
;
397 temporary_protocol_ext
= NULL
;
399 if (name
[0] == '%' && name
[1] == ':')
401 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
405 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
406 if (!strncmp(name
, "pop3://", 7)) {
410 n_err(_("No POP3 support compiled in\n"));
412 } else if (!strncmp(name
, "pop3s://", 8)) {
413 #if defined HAVE_POP3 && defined HAVE_SSL
417 n_err(_("No POP3 support compiled in\n"));
420 n_err(_("No SSL support compiled in\n"));
427 /* TODO This is the de facto maildir code and thus belongs into there!
428 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
429 * TODO or (more likely) in addition to *newfolders*) */
432 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
433 memcpy(np
, name
, sz
+ 1);
434 if (!stat(name
, &st
)) {
435 if (S_ISDIR(st
.st_mode
) &&
436 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
437 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
438 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
441 if ((memcpy(np
+sz
, cp
=".zst",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
442 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
443 (memcpy(np
+sz
, cp
=".gz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)))
444 temporary_protocol_ext
= cp
;
445 else if ((cp
= ok_vlook(newfolders
)) != NULL
&&
446 !asccasecmp(cp
, "maildir"))
456 n_c_to_hex_base16(char store
[3], char c
){
457 static char const itoa16
[] = "0123456789ABCDEF";
461 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
462 c
= ((ui8_t
)c
>> 4) & 0x0F;
463 store
[0] = itoa16
[(ui8_t
)c
];
469 n_c_from_hex_base16(char const hex
[2]){
470 static ui8_t
const atoi16
[] = {
471 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
472 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
473 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
474 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
475 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
476 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
477 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
483 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
484 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
488 if ((i1
| i2
) & 0xF0u
)
502 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
503 enum n_idec_mode idm
, char const **endptr_or_null
){
504 /* XXX Brute simple and */
507 enum n_idec_state rv
;
510 idm
&= n__IDEC_MODE_MASK
;
511 rv
= n_IDEC_STATE_NONE
| idm
;
521 while(spacechar(*cbuf
))
522 if(*++cbuf
== '\0' || --clen
== 0)
528 rv
|= n_IDEC_STATE_SEEN_MINUS
;
531 if(*++cbuf
== '\0' || --clen
== 0)
536 /* Base detection/skip */
540 /* Character must be valid for base */
541 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
545 /* 0 always valid as is, fallback base 10 */
546 if(*++cbuf
== '\0' || --clen
== 0)
549 /* Base "detection" */
550 if(base
== 0 || base
== 2 || base
== 16){
561 if((base
& 16) == 0){
563 /* Char after prefix must be valid */
565 if(*++cbuf
== '\0' || --clen
== 0)
568 /* Character must be valid for base, invalid otherwise */
569 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
581 /* Character must be valid for base, _EBASE otherwise */
582 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
587 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
591 if(res
> UI64_MAX
- currc
)
601 if(*++cbuf
== '\0' || --clen
== 0)
604 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
613 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
614 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
615 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
616 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
617 default: uimask
= UI64_MAX
; break;
619 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
623 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
624 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
625 if(res
> uimask
+ 1){
634 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
635 rv
|= n_IDEC_STATE_EOVERFLOW
;
636 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
640 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
641 case n_IDEC_MODE_LIMIT_8BIT
:
642 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
643 *(si8_t
*)resp
= (si8_t
)res
;
645 *(ui8_t
*)resp
= (ui8_t
)res
;
647 case n_IDEC_MODE_LIMIT_16BIT
:
648 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
649 *(si16_t
*)resp
= (si16_t
)res
;
651 *(ui16_t
*)resp
= (ui16_t
)res
;
653 case n_IDEC_MODE_LIMIT_32BIT
:
654 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
655 *(si32_t
*)resp
= (si32_t
)res
;
657 *(ui32_t
*)resp
= (ui32_t
)res
;
660 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
661 *(si64_t
*)resp
= (si64_t
)res
;
663 *(ui64_t
*)resp
= (ui64_t
)res
;
667 if(endptr_or_null
!= NULL
)
668 *endptr_or_null
= cbuf
;
669 if(*cbuf
== '\0' || clen
== 0)
670 rv
|= n_IDEC_STATE_CONSUMED
;
675 rv
|= n_IDEC_STATE_EINVAL
;
678 /* Not a base error for terminator and whitespace! */
679 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
680 rv
|= n_IDEC_STATE_EBASE
;
684 /* Overflow error: consume input until bad character or length out */
686 if(*++cbuf
== '\0' || --clen
== 0)
688 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
693 rv
|= n_IDEC_STATE_EOVERFLOW
;
695 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
696 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
700 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
705 n_torek_hash(char const *name
){
706 /* Chris Torek's hash */
711 for(h
= 0; (c
= *name
++) != '\0';)
718 n_torek_ihashn(char const *dat
, size_t len
){
719 /* See n_torek_hash() */
724 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
725 h
= (h
* 33) + lowerconv(c
);
731 n_prime_next(ui32_t n
){
732 static ui32_t
const primes
[] = {
733 5, 11, 23, 47, 97, 157, 283,
734 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
735 131071, 262139, 524287, 1048573, 2097143, 4194301,
736 8388593, 16777213, 33554393, 67108859, 134217689,
737 268435399, 536870909, 1073741789, 2147483647
742 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
743 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
744 : n_NELEM(primes
) / 2));
746 do if((mprime
= primes
[i
]) > n
)
748 while(++i
< n_NELEM(primes
));
750 if(i
== n_NELEM(primes
) && mprime
< n
)
757 n_getdeadletter(void){
758 char const *cp_base
, *cp
;
763 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
764 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
766 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
767 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
771 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
772 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
780 n_nodename(bool_t mayoverride
){
781 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
786 # ifdef HAVE_GETADDRINFO
787 struct addrinfo hints
, *res
;
789 struct hostent
*hent
;
794 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
796 }else if((hn
= sys_hostname
) == NULL
){
800 # ifdef HAVE_GETADDRINFO
801 memset(&hints
, 0, sizeof hints
);
802 hints
.ai_family
= AF_UNSPEC
;
803 hints
.ai_flags
= AI_CANONNAME
;
804 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
805 if(res
->ai_canonname
!= NULL
){
808 l
= strlen(res
->ai_canonname
) +1;
809 hn
= n_lofi_alloc(l
);
810 memcpy(hn
, res
->ai_canonname
, l
);
815 hent
= gethostbyname(hn
);
820 sys_hostname
= sstrdup(hn
);
821 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
822 if(hn
!= ut
.nodename
)
828 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
830 hostname
= sstrdup(hn
);
836 n_random_create_cp(size_t length
){
842 #ifndef HAVE_POSIX_RANDOM
843 if(a_aux_rand
== NULL
)
847 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
848 * with PAD stripped is still longer than what the user requests, easy way */
849 data
= n_lofi_alloc(i
= length
+ 3);
851 #ifndef HAVE_POSIX_RANDOM
853 data
[i
] = (char)a_aux_rand_get8();
857 for(cp
= data
; i
> 0;){
858 union {ui32_t i4
; char c
[4];} r
;
861 r
.i4
= (ui32_t
)arc4random();
863 case 0: cp
[3] = r
.c
[3]; j
= 4;
864 case 3: cp
[2] = r
.c
[2];
865 case 2: cp
[1] = r
.c
[1];
866 default: cp
[0] = r
.c
[0]; break;
874 assert(length
+ 3 < UIZ_MAX
/ 4);
875 b64_encode_buf(&b64
, data
, length
+ 3,
876 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
879 assert(b64
.l
>= length
);
880 b64
.s
[length
] = '\0';
886 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
891 assert(inlen
== 0 || inbuf
!= NULL
);
893 if (inlen
== UIZ_MAX
)
894 inlen
= strlen(inbuf
);
897 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
899 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
900 !ascncasecmp(inbuf
, "true", inlen
) ||
901 !ascncasecmp(inbuf
, "yes", inlen
) ||
902 !ascncasecmp(inbuf
, "on", inlen
))
904 else if ((inlen
== 1 &&
905 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
906 !ascncasecmp(inbuf
, "false", inlen
) ||
907 !ascncasecmp(inbuf
, "no", inlen
) ||
908 !ascncasecmp(inbuf
, "off", inlen
))
913 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
914 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
915 ) != n_IDEC_STATE_CONSUMED
)
926 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, 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;
938 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
939 !ascncasecmp(inbuf
, "ask-", 4) &&
940 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
941 (n_psonce
& n_PSO_INTERACTIVE
))
942 rv
= getapproval(prompt
, rv
);
948 n_is_all_or_aster(char const *name
){
952 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
960 #ifdef HAVE_CLOCK_GETTIME
962 #elif defined HAVE_GETTIMEOFDAY
969 if((cp
= ok_vlook(SOURCE_DATE_EPOCH
)) != NULL
){
972 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib
, cp
, 0, NULL
);
977 #ifdef HAVE_CLOCK_GETTIME
978 clock_gettime(CLOCK_REALTIME
, &ts
);
979 rv
= (time_t)ts
.tv_sec
;
980 #elif defined HAVE_GETTIMEOFDAY
981 gettimeofday(&ts
, NULL
);
982 rv
= (time_t)ts
.tv_sec
;
992 time_current_update(struct time_current
*tc
, bool_t full_update
)
995 tc
->tc_time
= n_time_epoch();
997 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
998 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
999 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1005 n_msleep(uiz_t millis
, bool_t ignint
){
1009 #ifdef HAVE_NANOSLEEP
1011 struct timespec ts
, trem
;
1014 ts
.tv_sec
= millis
/ 1000;
1015 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1017 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1019 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1022 #elif defined HAVE_SLEEP
1023 if((millis
/= 1000) == 0)
1025 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1028 # error Configuration should have detected a function for sleeping.
1036 n_err(char const *format
, ...){
1040 va_start(ap
, format
);
1042 if(n_psonce
& n_PSO_INTERACTIVE
)
1048 bool_t doname
, doflush
;
1051 while(*format
== '\n'){
1053 putc('\n', n_stderr
);
1057 if((doname
= doflush
))
1058 a_aux_err_linelen
= 0;
1060 if((len
= strlen(format
)) > 0){
1061 if(doname
|| a_aux_err_linelen
== 0){
1064 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1065 fputs(cp
, n_stderr
);
1067 vfprintf(n_stderr
, format
, ap
);
1072 if(format
[--len
] == '\n'){
1073 a_aux_err_linelen
= (i
-= ++len
);
1076 ++a_aux_err_linelen
;
1089 n_verr(char const *format
, va_list ap
){
1091 struct a_aux_err_node
*enp
;
1099 while(*format
== '\n'){
1100 putc('\n', n_stderr
);
1106 a_aux_err_linelen
= 0;
1108 if(n_psonce
& n_PSO_INTERACTIVE
){
1109 if((enp
= a_aux_err_tail
) != NULL
&&
1110 (enp
->ae_str
.s_len
> 0 &&
1111 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1112 n_string_push_c(&enp
->ae_str
, '\n');
1117 if((len
= strlen(format
)) == 0)
1120 n_pstate
|= n_PS_ERRORS_PROMPT
;
1123 if(doname
|| a_aux_err_linelen
== 0){
1126 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1127 fputs(cp
, n_stderr
);
1133 if(format
[--len
] == '\n'){
1134 a_aux_err_linelen
= (i
-= ++len
);
1137 ++a_aux_err_linelen
;
1142 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1144 vfprintf(n_stderr
, format
, ap
);
1148 n_LCTAV(ERRORS_MAX
> 3);
1150 /* Link it into the `errors' message ring */
1151 if((enp
= a_aux_err_tail
) == NULL
){
1153 enp
= smalloc(sizeof *enp
);
1154 enp
->ae_next
= NULL
;
1155 n_string_creat(&enp
->ae_str
);
1156 if(a_aux_err_tail
!= NULL
)
1157 a_aux_err_tail
->ae_next
= enp
;
1159 a_aux_err_head
= enp
;
1160 a_aux_err_tail
= enp
;
1163 (enp
->ae_str
.s_len
> 0 &&
1164 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1165 if(a_aux_err_cnt
< ERRORS_MAX
)
1168 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1169 a_aux_err_tail
->ae_next
= enp
;
1170 a_aux_err_tail
= enp
;
1171 enp
->ae_next
= NULL
;
1172 n_string_trunc(&enp
->ae_str
, 0);
1175 # ifdef HAVE_N_VA_COPY
1178 imax
= n_MIN(LINESIZE
, 1024);
1180 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1181 # ifdef HAVE_N_VA_COPY
1189 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1190 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1191 # ifdef HAVE_N_VA_COPY
1198 if(UICMP(z
, i
, >=, imax
)){
1199 # ifdef HAVE_N_VA_COPY
1200 /* XXX Check overflow for upcoming LEN+++i! */
1201 n_string_trunc(&enp
->ae_str
, len
);
1204 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1209 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1211 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1213 #endif /* HAVE_ERRORS */
1221 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1225 va_start(ap
, format
);
1226 vfprintf(n_stderr
, format
, ap
);
1232 n_perr(char const *msg
, int errval
){
1243 e
= (errval
== 0) ? n_err_no
: errval
;
1244 n_err(fmt
, msg
, n_err_to_doc(e
));
1251 n_alert(char const *format
, ...){
1255 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1257 va_start(ap
, format
);
1266 n_panic(char const *format
, ...){
1270 if(a_aux_err_linelen
> 0){
1271 putc('\n', n_stderr
);
1272 a_aux_err_linelen
= 0;
1274 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1276 va_start(ap
, format
);
1277 vfprintf(n_stderr
, format
, ap
);
1280 putc('\n', n_stderr
);
1283 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1290 struct a_aux_err_node
*enp
;
1297 if(!asccasecmp(*argv
, "show"))
1299 if(!asccasecmp(*argv
, "clear"))
1303 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1307 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1313 if(a_aux_err_head
== NULL
){
1314 fprintf(n_stderr
, _("The error ring is empty\n"));
1318 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1320 fprintf(n_stderr
, _("tmpfile"));
1325 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1326 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1327 /* We don't know whether last string ended with NL; be simple XXX */
1330 page_or_print(fp
, 0);
1336 a_aux_err_tail
= NULL
;
1337 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1338 a_aux_err_linelen
= 0;
1339 while((enp
= a_aux_err_head
) != NULL
){
1340 a_aux_err_head
= enp
->ae_next
;
1341 n_string_gut(&enp
->ae_str
);
1346 #endif /* HAVE_ERRORS */
1349 n_err_to_doc(si32_t eno
){
1351 struct a_aux_err_map
const *aemp
;
1354 aemp
= a_aux_err_map_from_no(eno
);
1355 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1361 n_err_to_name(si32_t eno
){
1363 struct a_aux_err_map
const *aemp
;
1366 aemp
= a_aux_err_map_from_no(eno
);
1367 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1373 n_err_from_name(char const *name
){
1374 struct a_aux_err_map
const *aemp
;
1375 ui32_t hash
, i
, j
, x
;
1379 hash
= n_torek_hash(name
);
1381 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1382 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1385 aemp
= &a_aux_err_map
[x
];
1386 if(aemp
->aem_hash
== hash
&&
1387 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1388 rv
= aemp
->aem_err_no
;
1392 if(++i
== a_AUX_ERR_REV_PRIME
){
1393 #ifdef a_AUX_ERR_REV_WRAPAROUND
1401 /* Have not found it. But wait, it could be that the user did, e.g.,
1402 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1403 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1404 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1405 ) == n_IDEC_STATE_CONSUMED
){
1406 aemp
= a_aux_err_map_from_no(rv
);
1407 rv
= aemp
->aem_err_no
;
1411 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1419 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1424 i
= regerror(e
, rep
, NULL
, 0) +1;
1426 regerror(e
, rep
, cp
, i
);