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 #ifdef HAVE_NL_LANGINFO
57 # include <langinfo.h>
63 #ifndef HAVE_POSIX_RANDOM
71 ui8_t b8
[sizeof(struct rand_arc4
)];
72 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
77 struct a_aux_err_node
{
78 struct a_aux_err_node
*ae_next
;
79 struct n_string ae_str
;
84 ui32_t aem_hash
; /* Hash of name */
85 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
86 ui32_t aem_docoff
; /* Into a_aux_err docs[] */
87 si32_t aem_err_no
; /* The OS error value for this one */
90 static ui8_t a_aux_idec_atoi
[256] = {
91 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
92 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
93 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
94 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
95 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
96 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
97 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
98 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
99 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
100 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
101 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
102 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
103 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
110 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
111 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
112 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
113 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
114 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
115 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
119 #define a_X(X) ((ui64_t)-1 / (X))
120 static ui64_t
const a_aux_idec_cutlimit
[35] = {
121 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
122 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
123 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
124 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
125 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
129 /* Include the constant make-errors.sh output */
130 #include "gen-errors.h"
132 /* And these things come from mk-config.h (config-time make-errors.sh output) */
133 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
135 #define a_X(N,I) {N,I},
136 n__ERR_NUMBER_TO_MAPOFF
140 #ifndef HAVE_POSIX_RANDOM
141 static union rand_state
*a_aux_rand
;
144 /* Error ring, for `errors' */
146 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
147 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
149 static size_t a_aux_err_linelen
;
151 /* Our ARC4 random generator with its completely unacademical pseudo
152 * initialization (shall /dev/urandom fail) */
153 #ifndef HAVE_POSIX_RANDOM
154 static void a_aux_rand_init(void);
155 SINLINE ui8_t
a_aux_rand_get8(void);
156 # ifndef HAVE_GETRANDOM
157 static ui32_t
a_aux_rand_weak(ui32_t seed
);
161 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
162 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
164 #ifndef HAVE_POSIX_RANDOM
166 a_aux_rand_init(void){
167 # ifndef HAVE_GETRANDOM
168 # ifdef HAVE_CLOCK_GETTIME
173 union {int fd
; size_t i
;} u
;
178 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
180 # ifdef HAVE_GETRANDOM
181 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
182 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
183 "Buffer too large to be served without n_ERR_INTR error");
184 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
185 "Buffer too small to serve used array indices");
189 gr
= HAVE_GETRANDOM(a_aux_rand
->a
._dat
, sizeof a_aux_rand
->a
._dat
);
190 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
191 a_aux_rand
->a
._dat
[84]];
192 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
193 a_aux_rand
->a
._dat
[42]];
194 /* ..but be on the safe side */
195 if(UICMP(z
, gr
, ==, sizeof(a_aux_rand
->a
._dat
)))
201 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
204 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
205 sizeof(a_aux_rand
->a
._dat
)));
208 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
209 a_aux_rand
->a
._dat
[84]];
210 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
211 a_aux_rand
->a
._dat
[42]];
216 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
217 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
220 # ifdef HAVE_CLOCK_GETTIME
221 clock_gettime(CLOCK_REALTIME
, &ts
);
222 t
= (ui32_t
)ts
.tv_nsec
;
224 gettimeofday(&ts
, NULL
);
225 t
= (ui32_t
)ts
.tv_usec
;
228 t
= (t
>> 16) | (t
<< 16);
229 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
230 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
231 if(rnd
== 7 || rnd
== 17)
232 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
233 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
234 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
235 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
237 seed
^= n_prime_next(seed
);
241 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
244 # endif /* !HAVE_GETRANDOM */
249 a_aux_rand_get8(void){
252 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
253 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
254 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
255 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
256 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
259 # ifndef HAVE_GETRANDOM
261 a_aux_rand_weak(ui32_t seed
){
262 /* From "Random number generators: good ones are hard to find",
263 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
264 * October 1988, p. 1195.
265 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
272 seed
= (seed
* 16807) - (hi
* 2836);
277 # endif /* HAVE_GETRANDOM */
278 #endif /* !HAVE_POSIX_RANDOM */
280 static struct a_aux_err_map
const *
281 a_aux_err_map_from_no(si32_t eno
){
284 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
285 struct a_aux_err_map
const *aemp
;
288 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
290 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
291 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
292 asz
!= 0; asz
>>= 1){
293 tmp
= &adat
[asz
>> 1];
294 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
295 aemp
= &a_aux_err_map
[(*tmp
)[1]];
312 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
314 #ifndef HAVE_SETLOCALE
317 setlocale(LC_ALL
, n_empty
);
318 n_mb_cur_max
= MB_CUR_MAX
;
319 # ifdef HAVE_NL_LANGINFO
323 /* TODO *ttycharset* may be set several times during startup unless
324 * TODO we gain a mechanism that -S fixates a setting during startup,
325 * TODO effectively turning later adjustments (during startup) in noop */
326 if((cp
= nl_langinfo(CODESET
)) != NULL
)
327 ok_vset(ttycharset
, cp
);
331 # ifdef HAVE_C90AMEND1
332 if(n_mb_cur_max
> 1){
333 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
334 n_psonce
|= n_PSO_UNICODE
;
337 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
338 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
339 n_psonce
|= n_PSO_UNICODE
;
340 /* Reset possibly messed up state; luckily this also gives us an
341 * indication whether the encoding has locking shift state sequences */
342 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
343 n_psonce
|= n_PSO_ENC_MBSTATE
;
357 if((cp
= ok_vlook(screen
)) != NULL
){
358 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
371 n_pager_get(char const **env_addon
){
375 rv
= ok_vlook(PAGER
);
377 if(env_addon
!= NULL
){
379 /* Update the manual upon any changes:
380 * *colour-pager*, $PAGER */
381 if(strstr(rv
, "less") != NULL
){
382 if(getenv("LESS") == NULL
)
385 (n_psonce
& n_PSO_TERMCAP_CA_MODE
) ? "LESS=Ri"
386 : !(n_psonce
& n_PSO_TERMCAP_DISABLE
) ? "LESS=FRi" :
389 }else if(strstr(rv
, "lv") != NULL
){
390 if(getenv("LV") == NULL
)
391 *env_addon
= "LV=-c";
399 page_or_print(FILE *fp
, size_t lines
)
407 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
411 rows
= (size_t)n_scrnheight
;
413 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
415 if (rows
> 0 && lines
== 0) {
416 while ((c
= getc(fp
)) != EOF
)
417 if (c
== '\n' && ++lines
>= rows
)
423 char const *env_add
[2], *pager
;
425 pager
= n_pager_get(&env_add
[0]);
427 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
433 while ((c
= getc(fp
)) != EOF
)
440 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
441 char const **adjusted_or_null
)
443 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
444 char const *cp
, *orig_name
;
445 enum protocol rv
= PROTO_UNKNOWN
;
448 if(name
[0] == '%' && name
[1] == ':')
452 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
456 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
457 if(!strncmp(name
, "file", sizeof("file") -1) ||
458 !strncmp(name
, "mbox", sizeof("mbox") -1))
460 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
462 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
466 n_err(_("No POP3 support compiled in\n"));
468 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
469 #if defined HAVE_POP3 && defined HAVE_SSL
472 n_err(_("No POP3S support compiled in\n"));
482 if(check_stat
|| try_hooks
){
483 struct n_file_type ft
;
488 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
489 memcpy(np
, name
, sz
+ 1);
491 if(!stat(name
, &stb
)){
492 if(S_ISDIR(stb
.st_mode
) &&
493 (memcpy(&np
[sz
], "/tmp", 5),
494 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
495 (memcpy(&np
[sz
], "/new", 5),
496 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
497 (memcpy(&np
[sz
], "/cur", 5),
498 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
500 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
501 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
502 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
503 !asccasecmp(cp
, "maildir"))
509 if(adjusted_or_null
!= NULL
)
510 *adjusted_or_null
= orig_name
;
516 n_c_to_hex_base16(char store
[3], char c
){
517 static char const itoa16
[] = "0123456789ABCDEF";
521 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
522 c
= ((ui8_t
)c
>> 4) & 0x0F;
523 store
[0] = itoa16
[(ui8_t
)c
];
529 n_c_from_hex_base16(char const hex
[2]){
530 static ui8_t
const atoi16
[] = {
531 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
532 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
533 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
534 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
535 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
536 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
537 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
543 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
544 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
548 if ((i1
| i2
) & 0xF0u
)
562 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
563 enum n_idec_mode idm
, char const **endptr_or_null
){
564 /* XXX Brute simple and */
567 enum n_idec_state rv
;
570 idm
&= n__IDEC_MODE_MASK
;
571 rv
= n_IDEC_STATE_NONE
| idm
;
581 while(spacechar(*cbuf
))
582 if(*++cbuf
== '\0' || --clen
== 0)
588 rv
|= n_IDEC_STATE_SEEN_MINUS
;
591 if(*++cbuf
== '\0' || --clen
== 0)
596 /* Base detection/skip */
600 /* Character must be valid for base */
601 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
605 /* 0 always valid as is, fallback base 10 */
606 if(*++cbuf
== '\0' || --clen
== 0)
609 /* Base "detection" */
610 if(base
== 0 || base
== 2 || base
== 16){
621 if((base
& 16) == 0){
623 /* Char after prefix must be valid */
625 if(*++cbuf
== '\0' || --clen
== 0)
628 /* Character must be valid for base, invalid otherwise */
629 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
641 /* Character must be valid for base, _EBASE otherwise */
642 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
647 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
651 if(res
> UI64_MAX
- currc
)
661 if(*++cbuf
== '\0' || --clen
== 0)
664 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
673 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
674 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
675 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
676 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
677 default: uimask
= UI64_MAX
; break;
679 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
683 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
684 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
685 if(res
> uimask
+ 1){
694 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
695 rv
|= n_IDEC_STATE_EOVERFLOW
;
696 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
700 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
701 case n_IDEC_MODE_LIMIT_8BIT
:
702 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
703 *(si8_t
*)resp
= (si8_t
)res
;
705 *(ui8_t
*)resp
= (ui8_t
)res
;
707 case n_IDEC_MODE_LIMIT_16BIT
:
708 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
709 *(si16_t
*)resp
= (si16_t
)res
;
711 *(ui16_t
*)resp
= (ui16_t
)res
;
713 case n_IDEC_MODE_LIMIT_32BIT
:
714 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
715 *(si32_t
*)resp
= (si32_t
)res
;
717 *(ui32_t
*)resp
= (ui32_t
)res
;
720 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
721 *(si64_t
*)resp
= (si64_t
)res
;
723 *(ui64_t
*)resp
= (ui64_t
)res
;
727 if(endptr_or_null
!= NULL
)
728 *endptr_or_null
= cbuf
;
729 if(*cbuf
== '\0' || clen
== 0)
730 rv
|= n_IDEC_STATE_CONSUMED
;
735 rv
|= n_IDEC_STATE_EINVAL
;
738 /* Not a base error for terminator and whitespace! */
739 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
740 rv
|= n_IDEC_STATE_EBASE
;
744 /* Overflow error: consume input until bad character or length out */
746 if(*++cbuf
== '\0' || --clen
== 0)
748 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
753 rv
|= n_IDEC_STATE_EOVERFLOW
;
755 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
756 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
760 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
765 n_torek_hash(char const *name
){
766 /* Chris Torek's hash */
771 for(h
= 0; (c
= *name
++) != '\0';)
778 n_torek_ihashn(char const *dat
, size_t len
){
779 /* See n_torek_hash() */
785 for(h
= 0; (c
= *dat
++) != '\0';)
786 h
= (h
* 33) + lowerconv(c
);
788 for(h
= 0; len
> 0; --len
){
790 h
= (h
* 33) + lowerconv(c
);
797 n_prime_next(ui32_t n
){
798 static ui32_t
const primes
[] = {
799 5, 11, 23, 47, 97, 157, 283,
800 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
801 131071, 262139, 524287, 1048573, 2097143, 4194301,
802 8388593, 16777213, 33554393, 67108859, 134217689,
803 268435399, 536870909, 1073741789, 2147483647
808 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
809 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
810 : n_NELEM(primes
) / 2));
812 do if((mprime
= primes
[i
]) > n
)
814 while(++i
< n_NELEM(primes
));
816 if(i
== n_NELEM(primes
) && mprime
< n
)
823 n_getdeadletter(void){
824 char const *cp_base
, *cp
;
829 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
830 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
832 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
833 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
837 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
838 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
846 n_nodename(bool_t mayoverride
){
847 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
852 # ifdef HAVE_GETADDRINFO
853 struct addrinfo hints
, *res
;
855 struct hostent
*hent
;
860 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
862 }else if((hn
= sys_hostname
) == NULL
){
866 # ifdef HAVE_GETADDRINFO
867 memset(&hints
, 0, sizeof hints
);
868 hints
.ai_family
= AF_UNSPEC
;
869 hints
.ai_flags
= AI_CANONNAME
;
870 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
871 if(res
->ai_canonname
!= NULL
){
874 l
= strlen(res
->ai_canonname
) +1;
875 hn
= n_lofi_alloc(l
);
876 memcpy(hn
, res
->ai_canonname
, l
);
881 hent
= gethostbyname(hn
);
886 sys_hostname
= sstrdup(hn
);
887 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
888 if(hn
!= ut
.nodename
)
894 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
896 hostname
= sstrdup(hn
);
902 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
908 #ifndef HAVE_POSIX_RANDOM
909 if(a_aux_rand
== NULL
)
913 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
914 * with PAD stripped is still longer than what the user requests, easy way */
915 data
= n_lofi_alloc(i
= length
+ 3);
917 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
918 #ifndef HAVE_POSIX_RANDOM
920 data
[i
] = (char)a_aux_rand_get8();
922 for(cp
= data
; i
> 0;){
923 union {ui32_t i4
; char c
[4];} r
;
926 r
.i4
= (ui32_t
)arc4random();
928 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
929 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
930 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
931 default: cp
[0] = r
.c
[0]; break;
938 for(cp
= data
; i
> 0;){
939 union {ui32_t i4
; char c
[4];} r
;
942 r
.i4
= ++*reprocnt_or_null
;
943 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
954 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
955 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
956 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
957 default: cp
[0] = r
.c
[0]; break;
964 assert(length
+ 3 < UIZ_MAX
/ 4);
965 b64_encode_buf(&b64
, data
, length
+ 3,
966 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
969 assert(b64
.l
>= length
);
970 b64
.s
[length
] = '\0';
976 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
981 assert(inlen
== 0 || inbuf
!= NULL
);
983 if (inlen
== UIZ_MAX
)
984 inlen
= strlen(inbuf
);
987 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
989 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
990 !ascncasecmp(inbuf
, "true", inlen
) ||
991 !ascncasecmp(inbuf
, "yes", inlen
) ||
992 !ascncasecmp(inbuf
, "on", inlen
))
994 else if ((inlen
== 1 &&
995 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
996 !ascncasecmp(inbuf
, "false", inlen
) ||
997 !ascncasecmp(inbuf
, "no", inlen
) ||
998 !ascncasecmp(inbuf
, "off", inlen
))
1003 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1004 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1005 ) != n_IDEC_STATE_CONSUMED
)
1016 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1021 assert(inlen
== 0 || inbuf
!= NULL
);
1023 if (inlen
== UIZ_MAX
)
1024 inlen
= strlen(inbuf
);
1027 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1028 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1029 !ascncasecmp(inbuf
, "ask-", 4) &&
1030 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1031 (n_psonce
& n_PSO_INTERACTIVE
))
1032 rv
= getapproval(prompt
, rv
);
1038 n_is_all_or_aster(char const *name
){
1042 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1047 FL
struct n_timespec
const *
1048 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1049 static struct n_timespec ts_now
;
1052 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1053 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1055 }else if(force_update
|| ts_now
.ts_sec
== 0){
1056 #ifdef HAVE_CLOCK_GETTIME
1059 clock_gettime(CLOCK_REALTIME
, &ts
);
1060 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1061 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1062 #elif defined HAVE_GETTIMEOFDAY
1065 gettimeofday(&tv
, NULL
);
1066 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1067 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1069 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1078 time_current_update(struct time_current
*tc
, bool_t full_update
)
1081 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1083 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1084 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1085 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1091 n_msleep(uiz_t millis
, bool_t ignint
){
1095 #ifdef HAVE_NANOSLEEP
1097 struct timespec ts
, trem
;
1100 ts
.tv_sec
= millis
/ 1000;
1101 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1103 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1105 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1108 #elif defined HAVE_SLEEP
1109 if((millis
/= 1000) == 0)
1111 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1114 # error Configuration should have detected a function for sleeping.
1122 n_err(char const *format
, ...){
1126 va_start(ap
, format
);
1128 if(n_psonce
& n_PSO_INTERACTIVE
)
1138 while(*format
== '\n'){
1140 putc('\n', n_stderr
);
1145 a_aux_err_linelen
= 0;
1147 if((len
= strlen(format
)) > 0){
1148 if(doname
|| a_aux_err_linelen
== 0){
1151 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1152 fputs(cp
, n_stderr
);
1154 vfprintf(n_stderr
, format
, ap
);
1159 if(format
[--len
] == '\n'){
1160 a_aux_err_linelen
= (i
-= ++len
);
1163 ++a_aux_err_linelen
;
1175 n_verr(char const *format
, va_list ap
){
1177 struct a_aux_err_node
*enp
;
1185 while(*format
== '\n'){
1186 putc('\n', n_stderr
);
1192 a_aux_err_linelen
= 0;
1194 if(n_psonce
& n_PSO_INTERACTIVE
){
1195 if((enp
= a_aux_err_tail
) != NULL
&&
1196 (enp
->ae_str
.s_len
> 0 &&
1197 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1198 n_string_push_c(&enp
->ae_str
, '\n');
1203 if((len
= strlen(format
)) == 0)
1206 n_pstate
|= n_PS_ERRORS_PROMPT
;
1209 if(doname
|| a_aux_err_linelen
== 0){
1212 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1213 fputs(cp
, n_stderr
);
1219 if(format
[--len
] == '\n'){
1220 a_aux_err_linelen
= (i
-= ++len
);
1223 ++a_aux_err_linelen
;
1228 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1230 vfprintf(n_stderr
, format
, ap
);
1234 n_LCTAV(ERRORS_MAX
> 3);
1236 /* Link it into the `errors' message ring */
1237 if((enp
= a_aux_err_tail
) == NULL
){
1239 enp
= smalloc(sizeof *enp
);
1240 enp
->ae_next
= NULL
;
1241 n_string_creat(&enp
->ae_str
);
1242 if(a_aux_err_tail
!= NULL
)
1243 a_aux_err_tail
->ae_next
= enp
;
1245 a_aux_err_head
= enp
;
1246 a_aux_err_tail
= enp
;
1249 (enp
->ae_str
.s_len
> 0 &&
1250 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1251 if(a_aux_err_cnt
< ERRORS_MAX
)
1254 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1255 a_aux_err_tail
->ae_next
= enp
;
1256 a_aux_err_tail
= enp
;
1257 enp
->ae_next
= NULL
;
1258 n_string_trunc(&enp
->ae_str
, 0);
1261 # ifdef HAVE_N_VA_COPY
1264 imax
= n_MIN(LINESIZE
, 1024);
1266 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1267 # ifdef HAVE_N_VA_COPY
1275 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1276 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1277 # ifdef HAVE_N_VA_COPY
1284 if(UICMP(z
, i
, >=, imax
)){
1285 # ifdef HAVE_N_VA_COPY
1286 /* XXX Check overflow for upcoming LEN+++i! */
1287 n_string_trunc(&enp
->ae_str
, len
);
1290 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1295 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1297 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1299 #endif /* HAVE_ERRORS */
1307 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1311 va_start(ap
, format
);
1312 vfprintf(n_stderr
, format
, ap
);
1318 n_perr(char const *msg
, int errval
){
1329 e
= (errval
== 0) ? n_err_no
: errval
;
1330 n_err(fmt
, msg
, n_err_to_doc(e
));
1337 n_alert(char const *format
, ...){
1341 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1343 va_start(ap
, format
);
1352 n_panic(char const *format
, ...){
1356 if(a_aux_err_linelen
> 0){
1357 putc('\n', n_stderr
);
1358 a_aux_err_linelen
= 0;
1360 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1362 va_start(ap
, format
);
1363 vfprintf(n_stderr
, format
, ap
);
1366 putc('\n', n_stderr
);
1369 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1376 struct a_aux_err_node
*enp
;
1383 if(!asccasecmp(*argv
, "show"))
1385 if(!asccasecmp(*argv
, "clear"))
1389 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1393 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1399 if(a_aux_err_head
== NULL
){
1400 fprintf(n_stderr
, _("The error ring is empty\n"));
1404 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1406 fprintf(n_stderr
, _("tmpfile"));
1411 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1412 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1413 /* We don't know whether last string ended with NL; be simple XXX */
1416 page_or_print(fp
, 0);
1422 a_aux_err_tail
= NULL
;
1423 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1424 a_aux_err_linelen
= 0;
1425 while((enp
= a_aux_err_head
) != NULL
){
1426 a_aux_err_head
= enp
->ae_next
;
1427 n_string_gut(&enp
->ae_str
);
1432 #endif /* HAVE_ERRORS */
1435 n_err_to_doc(si32_t eno
){
1437 struct a_aux_err_map
const *aemp
;
1440 aemp
= a_aux_err_map_from_no(eno
);
1441 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1447 n_err_to_name(si32_t eno
){
1449 struct a_aux_err_map
const *aemp
;
1452 aemp
= a_aux_err_map_from_no(eno
);
1453 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1459 n_err_from_name(char const *name
){
1460 struct a_aux_err_map
const *aemp
;
1461 ui32_t hash
, i
, j
, x
;
1465 hash
= n_torek_hash(name
);
1467 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1468 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1471 aemp
= &a_aux_err_map
[x
];
1472 if(aemp
->aem_hash
== hash
&&
1473 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1474 rv
= aemp
->aem_err_no
;
1478 if(++i
== a_AUX_ERR_REV_PRIME
){
1479 #ifdef a_AUX_ERR_REV_WRAPAROUND
1487 /* Have not found it. But wait, it could be that the user did, e.g.,
1488 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1489 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1490 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1491 ) == n_IDEC_STATE_CONSUMED
){
1492 aemp
= a_aux_err_map_from_no(rv
);
1493 rv
= aemp
->aem_err_no
;
1497 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1505 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1510 i
= regerror(e
, rep
, NULL
, 0) +1;
1512 regerror(e
, rep
, cp
, i
);