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_errno
; /* The OS errno 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 for errno, 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
^= nextprime(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 torek_hash(char const *name
)
707 /* Chris Torek's hash.
708 * NOTE: need to change *at least* mk-okey-map.pl when changing the
713 while (*name
!= '\0') {
722 torek_ihashn(char const *dat
, size_t len
){
723 /* See torek_hash() */
728 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
729 h
= (h
* 33) + lowerconv(c
);
737 static ui32_t
const primes
[] = {
738 5, 11, 23, 47, 97, 157, 283,
739 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
740 131071, 262139, 524287, 1048573, 2097143, 4194301,
741 8388593, 16777213, 33554393, 67108859, 134217689,
742 268435399, 536870909, 1073741789, 2147483647
748 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
749 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
750 : n_NELEM(primes
) / 2));
752 if ((mprime
= primes
[i
]) > n
)
754 while (++i
< n_NELEM(primes
));
755 if (i
== n_NELEM(primes
) && mprime
< n
)
762 n_getdeadletter(void){
763 char const *cp_base
, *cp
;
768 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
769 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
771 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
772 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
776 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
777 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
785 nodename(int mayoverride
)
787 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
792 # ifdef HAVE_GETADDRINFO
793 struct addrinfo hints
, *res
;
795 struct hostent
*hent
;
800 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
802 } else if ((hn
= sys_hostname
) == NULL
) {
806 # ifdef HAVE_GETADDRINFO
807 memset(&hints
, 0, sizeof hints
);
808 hints
.ai_family
= AF_UNSPEC
;
809 hints
.ai_flags
= AI_CANONNAME
;
810 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
811 if (res
->ai_canonname
!= NULL
) {
812 size_t l
= strlen(res
->ai_canonname
) +1;
815 memcpy(hn
, res
->ai_canonname
, l
);
820 hent
= gethostbyname(hn
);
825 sys_hostname
= sstrdup(hn
);
826 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
827 if (hn
!= ut
.nodename
)
833 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
835 hostname
= sstrdup(hn
);
841 getrandstring(size_t length
){
847 #ifndef HAVE_POSIX_RANDOM
848 if(a_aux_rand
== NULL
)
852 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
853 * with PAD stripped is still longer than what the user requests, easy way */
854 data
= n_lofi_alloc(i
= length
+ 3);
856 #ifndef HAVE_POSIX_RANDOM
858 data
[i
] = (char)a_aux_rand_get8();
862 for(cp
= data
; i
> 0;){
863 union {ui32_t i4
; char c
[4];} r
;
866 r
.i4
= (ui32_t
)arc4random();
868 case 0: cp
[3] = r
.c
[3]; j
= 4;
869 case 3: cp
[2] = r
.c
[2];
870 case 2: cp
[1] = r
.c
[1];
871 default: cp
[0] = r
.c
[0]; break;
879 assert(length
+ 3 < UIZ_MAX
/ 4);
880 b64_encode_buf(&b64
, data
, length
+ 3,
881 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
884 assert(b64
.l
>= length
);
885 b64
.s
[length
] = '\0';
891 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
896 assert(inlen
== 0 || inbuf
!= NULL
);
898 if (inlen
== UIZ_MAX
)
899 inlen
= strlen(inbuf
);
902 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
904 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
905 !ascncasecmp(inbuf
, "true", inlen
) ||
906 !ascncasecmp(inbuf
, "yes", inlen
) ||
907 !ascncasecmp(inbuf
, "on", inlen
))
909 else if ((inlen
== 1 &&
910 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
911 !ascncasecmp(inbuf
, "false", inlen
) ||
912 !ascncasecmp(inbuf
, "no", inlen
) ||
913 !ascncasecmp(inbuf
, "off", inlen
))
918 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
919 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
920 ) != n_IDEC_STATE_CONSUMED
)
931 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
936 assert(inlen
== 0 || inbuf
!= NULL
);
938 if (inlen
== UIZ_MAX
)
939 inlen
= strlen(inbuf
);
942 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
943 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
944 !ascncasecmp(inbuf
, "ask-", 4) &&
945 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
946 (n_psonce
& n_PSO_INTERACTIVE
))
947 rv
= getapproval(prompt
, rv
);
953 n_is_all_or_aster(char const *name
){
957 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
965 #ifdef HAVE_CLOCK_GETTIME
967 #elif defined HAVE_GETTIMEOFDAY
974 if((cp
= ok_vlook(SOURCE_DATE_EPOCH
)) != NULL
){
977 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib
, cp
, 0, NULL
);
982 #ifdef HAVE_CLOCK_GETTIME
983 clock_gettime(CLOCK_REALTIME
, &ts
);
984 rv
= (time_t)ts
.tv_sec
;
985 #elif defined HAVE_GETTIMEOFDAY
986 gettimeofday(&ts
, NULL
);
987 rv
= (time_t)ts
.tv_sec
;
997 time_current_update(struct time_current
*tc
, bool_t full_update
)
1000 tc
->tc_time
= n_time_epoch();
1002 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1003 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1004 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1010 n_msleep(uiz_t millis
, bool_t ignint
){
1014 #ifdef HAVE_NANOSLEEP
1016 struct timespec ts
, trem
;
1019 ts
.tv_sec
= millis
/ 1000;
1020 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1022 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1024 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1027 #elif defined HAVE_SLEEP
1028 if((millis
/= 1000) == 0)
1030 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1033 # error Configuration should have detected a function for sleeping.
1041 n_err(char const *format
, ...){
1045 va_start(ap
, format
);
1047 if(n_psonce
& n_PSO_INTERACTIVE
)
1053 bool_t doname
, doflush
;
1056 while(*format
== '\n'){
1058 putc('\n', n_stderr
);
1062 if((doname
= doflush
))
1063 a_aux_err_linelen
= 0;
1065 if((len
= strlen(format
)) > 0){
1066 if(doname
|| a_aux_err_linelen
== 0)
1067 fputs(VAL_UAGENT
": ", n_stderr
);
1068 vfprintf(n_stderr
, format
, ap
);
1073 if(format
[--len
] == '\n'){
1074 a_aux_err_linelen
= (i
-= ++len
);
1077 ++a_aux_err_linelen
;
1090 n_verr(char const *format
, va_list ap
){
1092 struct a_aux_err_node
*enp
;
1100 while(*format
== '\n'){
1101 putc('\n', n_stderr
);
1107 a_aux_err_linelen
= 0;
1109 if(n_psonce
& n_PSO_INTERACTIVE
){
1110 if((enp
= a_aux_err_tail
) != NULL
&&
1111 (enp
->ae_str
.s_len
> 0 &&
1112 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1113 n_string_push_c(&enp
->ae_str
, '\n');
1118 if((len
= strlen(format
)) == 0)
1121 n_pstate
|= n_PS_ERRORS_PROMPT
;
1124 if(doname
|| a_aux_err_linelen
== 0)
1125 fputs(VAL_UAGENT
": ", n_stderr
);
1130 if(format
[--len
] == '\n'){
1131 a_aux_err_linelen
= (i
-= ++len
);
1134 ++a_aux_err_linelen
;
1139 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1141 vfprintf(n_stderr
, format
, ap
);
1145 n_LCTAV(ERRORS_MAX
> 3);
1147 /* Link it into the `errors' message ring */
1148 if((enp
= a_aux_err_tail
) == NULL
){
1150 enp
= smalloc(sizeof *enp
);
1151 enp
->ae_next
= NULL
;
1152 n_string_creat(&enp
->ae_str
);
1153 if(a_aux_err_tail
!= NULL
)
1154 a_aux_err_tail
->ae_next
= enp
;
1156 a_aux_err_head
= enp
;
1157 a_aux_err_tail
= enp
;
1160 (enp
->ae_str
.s_len
> 0 &&
1161 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1162 if(a_aux_err_cnt
< ERRORS_MAX
)
1165 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1166 a_aux_err_tail
->ae_next
= enp
;
1167 a_aux_err_tail
= enp
;
1168 enp
->ae_next
= NULL
;
1169 n_string_trunc(&enp
->ae_str
, 0);
1172 # ifdef HAVE_N_VA_COPY
1175 imax
= n_MIN(LINESIZE
, 1024);
1177 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1178 # ifdef HAVE_N_VA_COPY
1186 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1187 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1188 # ifdef HAVE_N_VA_COPY
1195 if(UICMP(z
, i
, >=, imax
)){
1196 # ifdef HAVE_N_VA_COPY
1197 /* XXX Check overflow for upcoming LEN+++i! */
1198 n_string_trunc(&enp
->ae_str
, len
);
1201 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1206 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1208 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1210 #endif /* HAVE_ERRORS */
1218 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1222 va_start(ap
, format
);
1223 vfprintf(n_stderr
, format
, ap
);
1229 n_perr(char const *msg
, int errval
){
1240 e
= (errval
== 0) ? n_err_no
: errval
;
1241 n_err(fmt
, msg
, n_err_to_doc(e
));
1248 n_alert(char const *format
, ...){
1252 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1254 va_start(ap
, format
);
1263 n_panic(char const *format
, ...){
1267 if(a_aux_err_linelen
> 0){
1268 putc('\n', n_stderr
);
1269 a_aux_err_linelen
= 0;
1271 fprintf(n_stderr
, VAL_UAGENT
": Panic: ");
1273 va_start(ap
, format
);
1274 vfprintf(n_stderr
, format
, ap
);
1277 putc('\n', n_stderr
);
1280 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1287 struct a_aux_err_node
*enp
;
1294 if(!asccasecmp(*argv
, "show"))
1296 if(!asccasecmp(*argv
, "clear"))
1300 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1304 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1310 if(a_aux_err_head
== NULL
){
1311 fprintf(n_stderr
, _("The error ring is empty\n"));
1315 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1317 fprintf(n_stderr
, _("tmpfile"));
1322 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1323 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1324 /* We don't know whether last string ended with NL; be simple XXX */
1327 page_or_print(fp
, 0);
1333 a_aux_err_tail
= NULL
;
1334 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1335 a_aux_err_linelen
= 0;
1336 while((enp
= a_aux_err_head
) != NULL
){
1337 a_aux_err_head
= enp
->ae_next
;
1338 n_string_gut(&enp
->ae_str
);
1343 #endif /* HAVE_ERRORS */
1346 n_err_to_doc(si32_t eno
){
1348 struct a_aux_err_map
const *aemp
;
1351 aemp
= a_aux_err_map_from_no(eno
);
1352 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1358 n_err_to_name(si32_t eno
){
1360 struct a_aux_err_map
const *aemp
;
1363 aemp
= a_aux_err_map_from_no(eno
);
1364 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1370 n_err_from_name(char const *name
){
1371 struct a_aux_err_map
const *aemp
;
1372 ui32_t hash
, i
, j
, x
;
1376 hash
= torek_hash(name
);
1378 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1379 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1382 aemp
= &a_aux_err_map
[x
];
1383 if(aemp
->aem_hash
== hash
&&
1384 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1385 rv
= aemp
->aem_errno
;
1389 if(++i
== a_AUX_ERR_REV_PRIME
){
1390 #ifdef a_AUX_ERR_REV_WRAPAROUND
1398 /* Have not found it. But wait, it could be that the user did, e.g.,
1399 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1400 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1401 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1402 ) == n_IDEC_STATE_CONSUMED
){
1403 aemp
= a_aux_err_map_from_no(rv
);
1404 rv
= aemp
->aem_errno
;
1408 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_errno
;
1416 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1421 i
= regerror(e
, rep
, NULL
, 0) +1;
1423 regerror(e
, rep
, cp
, i
);