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
=".gz", 4), !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
=".bz2",5), !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.
707 * NOTE: need to change *at least* mk-okey-map.pl when changing the
713 for(h
= 0; (c
= *name
++) != '\0';)
720 n_torek_ihashn(char const *dat
, size_t len
){
721 /* See n_torek_hash() */
726 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
727 h
= (h
* 33) + lowerconv(c
);
733 n_prime_next(ui32_t n
){
734 static ui32_t
const primes
[] = {
735 5, 11, 23, 47, 97, 157, 283,
736 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
737 131071, 262139, 524287, 1048573, 2097143, 4194301,
738 8388593, 16777213, 33554393, 67108859, 134217689,
739 268435399, 536870909, 1073741789, 2147483647
744 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
745 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
746 : n_NELEM(primes
) / 2));
748 do if((mprime
= primes
[i
]) > n
)
750 while(++i
< n_NELEM(primes
));
752 if(i
== n_NELEM(primes
) && mprime
< n
)
759 n_getdeadletter(void){
760 char const *cp_base
, *cp
;
765 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
766 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
768 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
769 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
773 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
774 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
782 n_nodename(bool_t mayoverride
){
783 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
788 # ifdef HAVE_GETADDRINFO
789 struct addrinfo hints
, *res
;
791 struct hostent
*hent
;
796 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
798 }else if((hn
= sys_hostname
) == NULL
){
802 # ifdef HAVE_GETADDRINFO
803 memset(&hints
, 0, sizeof hints
);
804 hints
.ai_family
= AF_UNSPEC
;
805 hints
.ai_flags
= AI_CANONNAME
;
806 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
807 if(res
->ai_canonname
!= NULL
){
810 l
= strlen(res
->ai_canonname
) +1;
811 hn
= n_lofi_alloc(l
);
812 memcpy(hn
, res
->ai_canonname
, l
);
817 hent
= gethostbyname(hn
);
822 sys_hostname
= sstrdup(hn
);
823 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
824 if(hn
!= ut
.nodename
)
830 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
832 hostname
= sstrdup(hn
);
838 n_random_create_cp(size_t length
){
844 #ifndef HAVE_POSIX_RANDOM
845 if(a_aux_rand
== NULL
)
849 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
850 * with PAD stripped is still longer than what the user requests, easy way */
851 data
= n_lofi_alloc(i
= length
+ 3);
853 #ifndef HAVE_POSIX_RANDOM
855 data
[i
] = (char)a_aux_rand_get8();
859 for(cp
= data
; i
> 0;){
860 union {ui32_t i4
; char c
[4];} r
;
863 r
.i4
= (ui32_t
)arc4random();
865 case 0: cp
[3] = r
.c
[3]; j
= 4;
866 case 3: cp
[2] = r
.c
[2];
867 case 2: cp
[1] = r
.c
[1];
868 default: cp
[0] = r
.c
[0]; break;
876 assert(length
+ 3 < UIZ_MAX
/ 4);
877 b64_encode_buf(&b64
, data
, length
+ 3,
878 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
881 assert(b64
.l
>= length
);
882 b64
.s
[length
] = '\0';
888 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
893 assert(inlen
== 0 || inbuf
!= NULL
);
895 if (inlen
== UIZ_MAX
)
896 inlen
= strlen(inbuf
);
899 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
901 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
902 !ascncasecmp(inbuf
, "true", inlen
) ||
903 !ascncasecmp(inbuf
, "yes", inlen
) ||
904 !ascncasecmp(inbuf
, "on", inlen
))
906 else if ((inlen
== 1 &&
907 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
908 !ascncasecmp(inbuf
, "false", inlen
) ||
909 !ascncasecmp(inbuf
, "no", inlen
) ||
910 !ascncasecmp(inbuf
, "off", inlen
))
915 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
916 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
917 ) != n_IDEC_STATE_CONSUMED
)
928 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
933 assert(inlen
== 0 || inbuf
!= NULL
);
935 if (inlen
== UIZ_MAX
)
936 inlen
= strlen(inbuf
);
939 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
940 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
941 !ascncasecmp(inbuf
, "ask-", 4) &&
942 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
943 (n_psonce
& n_PSO_INTERACTIVE
))
944 rv
= getapproval(prompt
, rv
);
950 n_is_all_or_aster(char const *name
){
954 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
962 #ifdef HAVE_CLOCK_GETTIME
964 #elif defined HAVE_GETTIMEOFDAY
971 if((cp
= ok_vlook(SOURCE_DATE_EPOCH
)) != NULL
){
974 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib
, cp
, 0, NULL
);
979 #ifdef HAVE_CLOCK_GETTIME
980 clock_gettime(CLOCK_REALTIME
, &ts
);
981 rv
= (time_t)ts
.tv_sec
;
982 #elif defined HAVE_GETTIMEOFDAY
983 gettimeofday(&ts
, NULL
);
984 rv
= (time_t)ts
.tv_sec
;
994 time_current_update(struct time_current
*tc
, bool_t full_update
)
997 tc
->tc_time
= n_time_epoch();
999 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1000 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1001 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1007 n_msleep(uiz_t millis
, bool_t ignint
){
1011 #ifdef HAVE_NANOSLEEP
1013 struct timespec ts
, trem
;
1016 ts
.tv_sec
= millis
/ 1000;
1017 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1019 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1021 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1024 #elif defined HAVE_SLEEP
1025 if((millis
/= 1000) == 0)
1027 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1030 # error Configuration should have detected a function for sleeping.
1038 n_err(char const *format
, ...){
1042 va_start(ap
, format
);
1044 if(n_psonce
& n_PSO_INTERACTIVE
)
1050 bool_t doname
, doflush
;
1053 while(*format
== '\n'){
1055 putc('\n', n_stderr
);
1059 if((doname
= doflush
))
1060 a_aux_err_linelen
= 0;
1062 if((len
= strlen(format
)) > 0){
1063 if(doname
|| a_aux_err_linelen
== 0)
1064 fputs(VAL_UAGENT
": ", n_stderr
);
1065 vfprintf(n_stderr
, format
, ap
);
1070 if(format
[--len
] == '\n'){
1071 a_aux_err_linelen
= (i
-= ++len
);
1074 ++a_aux_err_linelen
;
1087 n_verr(char const *format
, va_list ap
){
1089 struct a_aux_err_node
*enp
;
1097 while(*format
== '\n'){
1098 putc('\n', n_stderr
);
1104 a_aux_err_linelen
= 0;
1106 if(n_psonce
& n_PSO_INTERACTIVE
){
1107 if((enp
= a_aux_err_tail
) != NULL
&&
1108 (enp
->ae_str
.s_len
> 0 &&
1109 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1110 n_string_push_c(&enp
->ae_str
, '\n');
1115 if((len
= strlen(format
)) == 0)
1118 n_pstate
|= n_PS_ERRORS_PROMPT
;
1121 if(doname
|| a_aux_err_linelen
== 0)
1122 fputs(VAL_UAGENT
": ", n_stderr
);
1127 if(format
[--len
] == '\n'){
1128 a_aux_err_linelen
= (i
-= ++len
);
1131 ++a_aux_err_linelen
;
1136 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1138 vfprintf(n_stderr
, format
, ap
);
1142 n_LCTAV(ERRORS_MAX
> 3);
1144 /* Link it into the `errors' message ring */
1145 if((enp
= a_aux_err_tail
) == NULL
){
1147 enp
= smalloc(sizeof *enp
);
1148 enp
->ae_next
= NULL
;
1149 n_string_creat(&enp
->ae_str
);
1150 if(a_aux_err_tail
!= NULL
)
1151 a_aux_err_tail
->ae_next
= enp
;
1153 a_aux_err_head
= enp
;
1154 a_aux_err_tail
= enp
;
1157 (enp
->ae_str
.s_len
> 0 &&
1158 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1159 if(a_aux_err_cnt
< ERRORS_MAX
)
1162 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1163 a_aux_err_tail
->ae_next
= enp
;
1164 a_aux_err_tail
= enp
;
1165 enp
->ae_next
= NULL
;
1166 n_string_trunc(&enp
->ae_str
, 0);
1169 # ifdef HAVE_N_VA_COPY
1172 imax
= n_MIN(LINESIZE
, 1024);
1174 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1175 # ifdef HAVE_N_VA_COPY
1183 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1184 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1185 # ifdef HAVE_N_VA_COPY
1192 if(UICMP(z
, i
, >=, imax
)){
1193 # ifdef HAVE_N_VA_COPY
1194 /* XXX Check overflow for upcoming LEN+++i! */
1195 n_string_trunc(&enp
->ae_str
, len
);
1198 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1203 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1205 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1207 #endif /* HAVE_ERRORS */
1215 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1219 va_start(ap
, format
);
1220 vfprintf(n_stderr
, format
, ap
);
1226 n_perr(char const *msg
, int errval
){
1237 e
= (errval
== 0) ? n_err_no
: errval
;
1238 n_err(fmt
, msg
, n_err_to_doc(e
));
1245 n_alert(char const *format
, ...){
1249 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1251 va_start(ap
, format
);
1260 n_panic(char const *format
, ...){
1264 if(a_aux_err_linelen
> 0){
1265 putc('\n', n_stderr
);
1266 a_aux_err_linelen
= 0;
1268 fprintf(n_stderr
, VAL_UAGENT
": Panic: ");
1270 va_start(ap
, format
);
1271 vfprintf(n_stderr
, format
, ap
);
1274 putc('\n', n_stderr
);
1277 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1284 struct a_aux_err_node
*enp
;
1291 if(!asccasecmp(*argv
, "show"))
1293 if(!asccasecmp(*argv
, "clear"))
1297 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1301 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1307 if(a_aux_err_head
== NULL
){
1308 fprintf(n_stderr
, _("The error ring is empty\n"));
1312 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1314 fprintf(n_stderr
, _("tmpfile"));
1319 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1320 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1321 /* We don't know whether last string ended with NL; be simple XXX */
1324 page_or_print(fp
, 0);
1330 a_aux_err_tail
= NULL
;
1331 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1332 a_aux_err_linelen
= 0;
1333 while((enp
= a_aux_err_head
) != NULL
){
1334 a_aux_err_head
= enp
->ae_next
;
1335 n_string_gut(&enp
->ae_str
);
1340 #endif /* HAVE_ERRORS */
1343 n_err_to_doc(si32_t eno
){
1345 struct a_aux_err_map
const *aemp
;
1348 aemp
= a_aux_err_map_from_no(eno
);
1349 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1355 n_err_to_name(si32_t eno
){
1357 struct a_aux_err_map
const *aemp
;
1360 aemp
= a_aux_err_map_from_no(eno
);
1361 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1367 n_err_from_name(char const *name
){
1368 struct a_aux_err_map
const *aemp
;
1369 ui32_t hash
, i
, j
, x
;
1373 hash
= n_torek_hash(name
);
1375 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1376 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1379 aemp
= &a_aux_err_map
[x
];
1380 if(aemp
->aem_hash
== hash
&&
1381 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1382 rv
= aemp
->aem_err_no
;
1386 if(++i
== a_AUX_ERR_REV_PRIME
){
1387 #ifdef a_AUX_ERR_REV_WRAPAROUND
1395 /* Have not found it. But wait, it could be that the user did, e.g.,
1396 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1397 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1398 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1399 ) == n_IDEC_STATE_CONSUMED
){
1400 aemp
= a_aux_err_map_from_no(rv
);
1401 rv
= aemp
->aem_err_no
;
1405 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1413 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1418 i
= regerror(e
, rep
, NULL
, 0) +1;
1420 regerror(e
, rep
, cp
, i
);