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 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
60 # include HAVE_GETRANDOM_HEADER
64 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
66 # include <idn-free.h>
67 # include <stringprep.h>
68 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
73 #ifndef HAVE_POSIX_RANDOM
81 ui8_t b8
[sizeof(struct rand_arc4
)];
82 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
87 struct a_aux_err_node
{
88 struct a_aux_err_node
*ae_next
;
89 struct n_string ae_str
;
94 ui32_t aem_hash
; /* Hash of name */
95 ui32_t aem_nameoff
; /* Into a_aux_err_names[] */
96 ui32_t aem_docoff
; /* Into a_aux_err docs[] */
97 si32_t aem_err_no
; /* The OS error value for this one */
100 static ui8_t a_aux_idec_atoi
[256] = {
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,0x00,0x01,
106 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
108 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
109 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
110 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
111 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
112 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
113 0x21,0x22,0x23,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,0xFF,0xFF,0xFF,0xFF,
117 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
121 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
122 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
123 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
124 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
125 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
126 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
129 #define a_X(X) ((ui64_t)-1 / (X))
130 static ui64_t
const a_aux_idec_cutlimit
[35] = {
131 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
132 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
133 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
134 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
135 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
139 /* Include the constant make-errors.sh output */
140 #include "gen-errors.h"
142 /* And these things come from mk-config.h (config-time make-errors.sh output) */
143 static n__ERR_NUMBER_TYPE
const a_aux_err_no2mapoff
[][2] = {
145 #define a_X(N,I) {N,I},
146 n__ERR_NUMBER_TO_MAPOFF
150 #ifndef HAVE_POSIX_RANDOM
151 static union rand_state
*a_aux_rand
;
154 /* Error ring, for `errors' */
156 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
157 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
159 static size_t a_aux_err_linelen
;
161 /* Our ARC4 random generator with its completely unacademical pseudo
162 * initialization (shall /dev/urandom fail) */
163 #ifndef HAVE_POSIX_RANDOM
164 static void a_aux_rand_init(void);
165 SINLINE ui8_t
a_aux_rand_get8(void);
166 # ifndef HAVE_GETRANDOM
167 static ui32_t
a_aux_rand_weak(ui32_t seed
);
171 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
172 static struct a_aux_err_map
const *a_aux_err_map_from_no(si32_t eno
);
174 #ifndef HAVE_POSIX_RANDOM
176 a_aux_rand_init(void){
177 # ifndef HAVE_GETRANDOM
178 # ifdef HAVE_CLOCK_GETTIME
183 union {int fd
; size_t i
;} u
;
188 a_aux_rand
= n_alloc(sizeof *a_aux_rand
);
190 # ifdef HAVE_GETRANDOM
191 /* getrandom(2) guarantees 256 without n_ERR_INTR.. */
192 n_LCTA(sizeof(a_aux_rand
->a
._dat
) <= 256,
193 "Buffer too large to be served without n_ERR_INTR error");
194 n_LCTA(sizeof(a_aux_rand
->a
._dat
) >= 256,
195 "Buffer too small to serve used array indices");
199 gr
= HAVE_GETRANDOM(a_aux_rand
->a
._dat
, sizeof a_aux_rand
->a
._dat
);
200 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
201 a_aux_rand
->a
._dat
[84]];
202 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
203 a_aux_rand
->a
._dat
[42]];
204 /* ..but be on the safe side */
205 if(UICMP(z
, gr
, ==, sizeof(a_aux_rand
->a
._dat
)))
211 if((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1){
214 ok
= (sizeof(a_aux_rand
->a
._dat
) == (size_t)read(u
.fd
, a_aux_rand
->a
._dat
,
215 sizeof(a_aux_rand
->a
._dat
)));
218 a_aux_rand
->a
._i
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[1] ^
219 a_aux_rand
->a
._dat
[84]];
220 a_aux_rand
->a
._j
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._dat
[65] ^
221 a_aux_rand
->a
._dat
[42]];
226 for(seed
= (uintptr_t)a_aux_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
){
227 for(u
.i
= n_NELEM(a_aux_rand
->b32
); u
.i
-- != 0;){
230 # ifdef HAVE_CLOCK_GETTIME
231 clock_gettime(CLOCK_REALTIME
, &ts
);
232 t
= (ui32_t
)ts
.tv_nsec
;
234 gettimeofday(&ts
, NULL
);
235 t
= (ui32_t
)ts
.tv_usec
;
238 t
= (t
>> 16) | (t
<< 16);
239 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ t
);
240 a_aux_rand
->b32
[t
% n_NELEM(a_aux_rand
->b32
)] ^= seed
;
241 if(rnd
== 7 || rnd
== 17)
242 a_aux_rand
->b32
[u
.i
] ^= a_aux_rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
243 k
= a_aux_rand
->b32
[u
.i
] % n_NELEM(a_aux_rand
->b32
);
244 a_aux_rand
->b32
[k
] ^= a_aux_rand
->b32
[u
.i
];
245 seed
^= a_aux_rand_weak(a_aux_rand
->b32
[k
]);
247 seed
^= n_prime_next(seed
);
251 for(u
.i
= 5 * sizeof(a_aux_rand
->b8
); u
.i
!= 0; --u
.i
)
254 # endif /* !HAVE_GETRANDOM */
259 a_aux_rand_get8(void){
262 si
= a_aux_rand
->a
._dat
[++a_aux_rand
->a
._i
];
263 sj
= a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
+= si
];
264 a_aux_rand
->a
._dat
[a_aux_rand
->a
._i
] = sj
;
265 a_aux_rand
->a
._dat
[a_aux_rand
->a
._j
] = si
;
266 return a_aux_rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
269 # ifndef HAVE_GETRANDOM
271 a_aux_rand_weak(ui32_t seed
){
272 /* From "Random number generators: good ones are hard to find",
273 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
274 * October 1988, p. 1195.
275 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
282 seed
= (seed
* 16807) - (hi
* 2836);
287 # endif /* HAVE_GETRANDOM */
288 #endif /* !HAVE_POSIX_RANDOM */
290 static struct a_aux_err_map
const *
291 a_aux_err_map_from_no(si32_t eno
){
294 n__ERR_NUMBER_TYPE
const (*adat
)[2], (*tmp
)[2];
295 struct a_aux_err_map
const *aemp
;
298 aemp
= &a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
];
300 if(UICMP(z
, n_ABS(eno
), <=, (n__ERR_NUMBER_TYPE
)-1)){
301 for(adat
= a_aux_err_no2mapoff
, asz
= n_NELEM(a_aux_err_no2mapoff
);
302 asz
!= 0; asz
>>= 1){
303 tmp
= &adat
[asz
>> 1];
304 if((ecmp
= (si32_t
)((n__ERR_NUMBER_TYPE
)eno
- (*tmp
)[0])) == 0){
305 aemp
= &a_aux_err_map
[(*tmp
)[1]];
322 n_psonce
&= ~(n_PSO_UNICODE
| n_PSO_ENC_MBSTATE
);
324 #ifndef HAVE_SETLOCALE
327 setlocale(LC_ALL
, n_empty
);
328 n_mb_cur_max
= MB_CUR_MAX
;
329 # ifdef HAVE_NL_LANGINFO
333 /* TODO *ttycharset* may be set several times during startup unless
334 * TODO we gain a mechanism that -S fixates a setting during startup,
335 * TODO effectively turning later adjustments (during startup) in noop */
336 if((cp
= nl_langinfo(CODESET
)) != NULL
)
337 ok_vset(ttycharset
, cp
);
341 # ifdef HAVE_C90AMEND1
342 if(n_mb_cur_max
> 1){
343 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
344 n_psonce
|= n_PSO_UNICODE
;
347 if(mbtowc(&wc
, "\303\266", 2) == 2 && wc
== 0xF6 &&
348 mbtowc(&wc
, "\342\202\254", 3) == 3 && wc
== 0x20AC)
349 n_psonce
|= n_PSO_UNICODE
;
350 /* Reset possibly messed up state; luckily this also gives us an
351 * indication whether the encoding has locking shift state sequences */
352 if(mbtowc(&wc
, NULL
, n_mb_cur_max
))
353 n_psonce
|= n_PSO_ENC_MBSTATE
;
367 if((cp
= ok_vlook(screen
)) != NULL
){
368 n_idec_uiz_cp(&rv
, cp
, 0, NULL
);
381 n_pager_get(char const **env_addon
){
385 rv
= ok_vlook(PAGER
);
387 if(env_addon
!= NULL
){
389 /* Update the manual upon any changes:
390 * *colour-pager*, $PAGER */
391 if(strstr(rv
, "less") != NULL
){
392 if(getenv("LESS") == NULL
)
395 (n_psonce
& n_PSO_TERMCAP_CA_MODE
) ? "LESS=Ri"
396 : !(n_psonce
& n_PSO_TERMCAP_DISABLE
) ? "LESS=FRi" :
399 }else if(strstr(rv
, "lv") != NULL
){
400 if(getenv("LV") == NULL
)
401 *env_addon
= "LV=-c";
409 page_or_print(FILE *fp
, size_t lines
)
417 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
421 rows
= (size_t)n_scrnheight
;
423 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
425 if (rows
> 0 && lines
== 0) {
426 while ((c
= getc(fp
)) != EOF
)
427 if (c
== '\n' && ++lines
>= rows
)
433 char const *env_add
[2], *pager
;
435 pager
= n_pager_get(&env_add
[0]);
437 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
443 while ((c
= getc(fp
)) != EOF
)
450 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
451 char const **adjusted_or_null
)
453 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
454 char const *cp
, *orig_name
;
455 enum protocol rv
= PROTO_UNKNOWN
;
458 if(name
[0] == '%' && name
[1] == ':')
462 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
466 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
467 if(!strncmp(name
, "file", sizeof("file") -1) ||
468 !strncmp(name
, "mbox", sizeof("mbox") -1))
470 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
472 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
476 n_err(_("No POP3 support compiled in\n"));
478 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
479 #if defined HAVE_POP3 && defined HAVE_SSL
482 n_err(_("No POP3S support compiled in\n"));
485 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
489 n_err(_("No IMAP support compiled in\n"));
491 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
492 #if defined HAVE_IMAP && defined HAVE_SSL
495 n_err(_("No IMAPS support compiled in\n"));
505 if(check_stat
|| try_hooks
){
506 struct n_file_type ft
;
511 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
512 memcpy(np
, name
, sz
+ 1);
514 if(!stat(name
, &stb
)){
515 if(S_ISDIR(stb
.st_mode
) &&
516 (memcpy(&np
[sz
], "/tmp", 5),
517 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
518 (memcpy(&np
[sz
], "/new", 5),
519 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
520 (memcpy(&np
[sz
], "/cur", 5),
521 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
523 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
524 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
525 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
526 !asccasecmp(cp
, "maildir"))
532 if(adjusted_or_null
!= NULL
)
533 *adjusted_or_null
= orig_name
;
539 n_c_to_hex_base16(char store
[3], char c
){
540 static char const itoa16
[] = "0123456789ABCDEF";
544 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
545 c
= ((ui8_t
)c
>> 4) & 0x0F;
546 store
[0] = itoa16
[(ui8_t
)c
];
552 n_c_from_hex_base16(char const hex
[2]){
553 static ui8_t
const atoi16
[] = {
554 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
555 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
556 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
557 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
558 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
559 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
560 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
566 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
567 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
571 if ((i1
| i2
) & 0xF0u
)
585 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
586 enum n_idec_mode idm
, char const **endptr_or_null
){
587 /* XXX Brute simple and */
590 enum n_idec_state rv
;
593 idm
&= n__IDEC_MODE_MASK
;
594 rv
= n_IDEC_STATE_NONE
| idm
;
604 while(spacechar(*cbuf
))
605 if(*++cbuf
== '\0' || --clen
== 0)
611 rv
|= n_IDEC_STATE_SEEN_MINUS
;
614 if(*++cbuf
== '\0' || --clen
== 0)
619 /* Base detection/skip */
623 /* Character must be valid for base */
624 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
628 /* 0 always valid as is, fallback base 10 */
629 if(*++cbuf
== '\0' || --clen
== 0)
632 /* Base "detection" */
633 if(base
== 0 || base
== 2 || base
== 16){
644 if((base
& 16) == 0){
646 /* Char after prefix must be valid */
648 if(*++cbuf
== '\0' || --clen
== 0)
651 /* Character must be valid for base, invalid otherwise */
652 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
664 /* Character must be valid for base, _EBASE otherwise */
665 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
670 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
674 if(res
> UI64_MAX
- currc
)
684 if(*++cbuf
== '\0' || --clen
== 0)
687 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
696 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
697 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
698 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
699 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
700 default: uimask
= UI64_MAX
; break;
702 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
706 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
707 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
708 if(res
> uimask
+ 1){
717 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
718 rv
|= n_IDEC_STATE_EOVERFLOW
;
719 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
723 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
724 case n_IDEC_MODE_LIMIT_8BIT
:
725 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
726 *(si8_t
*)resp
= (si8_t
)res
;
728 *(ui8_t
*)resp
= (ui8_t
)res
;
730 case n_IDEC_MODE_LIMIT_16BIT
:
731 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
732 *(si16_t
*)resp
= (si16_t
)res
;
734 *(ui16_t
*)resp
= (ui16_t
)res
;
736 case n_IDEC_MODE_LIMIT_32BIT
:
737 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
738 *(si32_t
*)resp
= (si32_t
)res
;
740 *(ui32_t
*)resp
= (ui32_t
)res
;
743 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
744 *(si64_t
*)resp
= (si64_t
)res
;
746 *(ui64_t
*)resp
= (ui64_t
)res
;
750 if(endptr_or_null
!= NULL
)
751 *endptr_or_null
= cbuf
;
752 if(*cbuf
== '\0' || clen
== 0)
753 rv
|= n_IDEC_STATE_CONSUMED
;
758 rv
|= n_IDEC_STATE_EINVAL
;
761 /* Not a base error for terminator and whitespace! */
762 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
763 rv
|= n_IDEC_STATE_EBASE
;
767 /* Overflow error: consume input until bad character or length out */
769 if(*++cbuf
== '\0' || --clen
== 0)
771 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
776 rv
|= n_IDEC_STATE_EOVERFLOW
;
778 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
779 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
783 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
788 n_torek_hash(char const *name
){
789 /* Chris Torek's hash */
794 for(h
= 0; (c
= *name
++) != '\0';)
801 n_torek_ihashn(char const *dat
, size_t len
){
802 /* See n_torek_hash() */
808 for(h
= 0; (c
= *dat
++) != '\0';)
809 h
= (h
* 33) + lowerconv(c
);
811 for(h
= 0; len
> 0; --len
){
813 h
= (h
* 33) + lowerconv(c
);
820 n_prime_next(ui32_t n
){
821 static ui32_t
const primes
[] = {
822 5, 11, 23, 47, 97, 157, 283,
823 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
824 131071, 262139, 524287, 1048573, 2097143, 4194301,
825 8388593, 16777213, 33554393, 67108859, 134217689,
826 268435399, 536870909, 1073741789, 2147483647
831 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
832 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
833 : n_NELEM(primes
) / 2));
835 do if((mprime
= primes
[i
]) > n
)
837 while(++i
< n_NELEM(primes
));
839 if(i
== n_NELEM(primes
) && mprime
< n
)
846 n_getdeadletter(void){
853 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
854 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
856 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
857 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
862 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
863 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
871 n_nodename(bool_t mayoverride
){
872 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
877 # ifdef HAVE_GETADDRINFO
878 struct addrinfo hints
, *res
;
880 struct hostent
*hent
;
885 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
887 }else if((hn
= sys_hostname
) == NULL
){
891 # ifdef HAVE_GETADDRINFO
892 memset(&hints
, 0, sizeof hints
);
893 hints
.ai_family
= AF_UNSPEC
;
894 hints
.ai_flags
= AI_CANONNAME
;
895 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
896 if(res
->ai_canonname
!= NULL
){
899 l
= strlen(res
->ai_canonname
) +1;
900 hn
= n_lofi_alloc(l
);
901 memcpy(hn
, res
->ai_canonname
, l
);
906 hent
= gethostbyname(hn
);
911 sys_hostname
= sstrdup(hn
);
912 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
913 if(hn
!= ut
.nodename
)
919 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
921 hostname
= sstrdup(hn
);
928 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
933 if((rv
= (ilen
== 0)))
936 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
937 ibuf
= savestrbuf(ibuf
, ilen
);
940 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
942 if(idna_utf8
== NULL
)
945 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
949 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
950 out
= n_string_assign_cp(out
, idna_ascii
);
951 idn_free(idna_ascii
);
956 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
957 ilen
= strlen(idna_utf8
);
959 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
960 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
961 case idn_buffer_overflow
:
962 ilen
+= HOST_NAME_MAX
+1;
966 ilen
= strlen(out
->s_dat
);
974 # error Unknown HAVE_IDNA
977 out
= n_string_trunc(out
, ilen
);
981 #endif /* HAVE_IDNA */
984 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
990 #ifndef HAVE_POSIX_RANDOM
991 if(a_aux_rand
== NULL
)
995 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
996 * with PAD stripped is still longer than what the user requests, easy way */
997 data
= n_lofi_alloc(i
= length
+ 3);
999 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1000 #ifndef HAVE_POSIX_RANDOM
1002 data
[i
] = (char)a_aux_rand_get8();
1004 for(cp
= data
; i
> 0;){
1005 union {ui32_t i4
; char c
[4];} r
;
1008 r
.i4
= (ui32_t
)arc4random();
1009 switch((j
= i
& 3)){
1010 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1011 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1012 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1013 default: cp
[0] = r
.c
[0]; break;
1020 for(cp
= data
; i
> 0;){
1021 union {ui32_t i4
; char c
[4];} r
;
1024 r
.i4
= ++*reprocnt_or_null
;
1025 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1035 switch((j
= i
& 3)){
1036 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1037 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1038 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1039 default: cp
[0] = r
.c
[0]; break;
1046 assert(length
+ 3 < UIZ_MAX
/ 4);
1047 b64_encode_buf(&b64
, data
, length
+ 3,
1048 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1051 assert(b64
.l
>= length
);
1052 b64
.s
[length
] = '\0';
1058 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1063 assert(inlen
== 0 || inbuf
!= NULL
);
1065 if (inlen
== UIZ_MAX
)
1066 inlen
= strlen(inbuf
);
1069 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1071 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1072 !ascncasecmp(inbuf
, "true", inlen
) ||
1073 !ascncasecmp(inbuf
, "yes", inlen
) ||
1074 !ascncasecmp(inbuf
, "on", inlen
))
1076 else if ((inlen
== 1 &&
1077 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1078 !ascncasecmp(inbuf
, "false", inlen
) ||
1079 !ascncasecmp(inbuf
, "no", inlen
) ||
1080 !ascncasecmp(inbuf
, "off", inlen
))
1085 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1086 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1087 ) != n_IDEC_STATE_CONSUMED
)
1098 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1103 assert(inlen
== 0 || inbuf
!= NULL
);
1105 if (inlen
== UIZ_MAX
)
1106 inlen
= strlen(inbuf
);
1109 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1110 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1111 !ascncasecmp(inbuf
, "ask-", 4) &&
1112 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1113 (n_psonce
& n_PSO_INTERACTIVE
))
1114 rv
= getapproval(prompt
, rv
);
1120 n_is_all_or_aster(char const *name
){
1124 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1129 FL
struct n_timespec
const *
1130 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1131 static struct n_timespec ts_now
;
1134 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1135 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1137 }else if(force_update
|| ts_now
.ts_sec
== 0){
1138 #ifdef HAVE_CLOCK_GETTIME
1141 clock_gettime(CLOCK_REALTIME
, &ts
);
1142 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1143 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1144 #elif defined HAVE_GETTIMEOFDAY
1147 gettimeofday(&tv
, NULL
);
1148 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1149 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1151 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1160 time_current_update(struct time_current
*tc
, bool_t full_update
)
1163 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1165 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1166 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1167 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1173 n_msleep(uiz_t millis
, bool_t ignint
){
1177 #ifdef HAVE_NANOSLEEP
1179 struct timespec ts
, trem
;
1182 ts
.tv_sec
= millis
/ 1000;
1183 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1185 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1187 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1190 #elif defined HAVE_SLEEP
1191 if((millis
/= 1000) == 0)
1193 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1196 # error Configuration should have detected a function for sleeping.
1204 n_err(char const *format
, ...){
1208 va_start(ap
, format
);
1210 if(n_psonce
& n_PSO_INTERACTIVE
)
1220 while(*format
== '\n'){
1222 putc('\n', n_stderr
);
1227 a_aux_err_linelen
= 0;
1229 if((len
= strlen(format
)) > 0){
1230 if(doname
|| a_aux_err_linelen
== 0){
1233 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1234 fputs(cp
, n_stderr
);
1236 vfprintf(n_stderr
, format
, ap
);
1241 if(format
[--len
] == '\n'){
1242 a_aux_err_linelen
= (i
-= ++len
);
1245 ++a_aux_err_linelen
;
1257 n_verr(char const *format
, va_list ap
){
1259 struct a_aux_err_node
*enp
;
1267 while(*format
== '\n'){
1268 putc('\n', n_stderr
);
1274 a_aux_err_linelen
= 0;
1276 if(n_psonce
& n_PSO_INTERACTIVE
){
1277 if((enp
= a_aux_err_tail
) != NULL
&&
1278 (enp
->ae_str
.s_len
> 0 &&
1279 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1280 n_string_push_c(&enp
->ae_str
, '\n');
1285 if((len
= strlen(format
)) == 0)
1288 n_pstate
|= n_PS_ERRORS_PROMPT
;
1291 if(doname
|| a_aux_err_linelen
== 0){
1294 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1295 fputs(cp
, n_stderr
);
1301 if(format
[--len
] == '\n'){
1302 a_aux_err_linelen
= (i
-= ++len
);
1305 ++a_aux_err_linelen
;
1310 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1312 vfprintf(n_stderr
, format
, ap
);
1316 n_LCTAV(ERRORS_MAX
> 3);
1318 /* Link it into the `errors' message ring */
1319 if((enp
= a_aux_err_tail
) == NULL
){
1321 enp
= smalloc(sizeof *enp
);
1322 enp
->ae_next
= NULL
;
1323 n_string_creat(&enp
->ae_str
);
1324 if(a_aux_err_tail
!= NULL
)
1325 a_aux_err_tail
->ae_next
= enp
;
1327 a_aux_err_head
= enp
;
1328 a_aux_err_tail
= enp
;
1331 (enp
->ae_str
.s_len
> 0 &&
1332 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1333 if(a_aux_err_cnt
< ERRORS_MAX
)
1336 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1337 a_aux_err_tail
->ae_next
= enp
;
1338 a_aux_err_tail
= enp
;
1339 enp
->ae_next
= NULL
;
1340 n_string_trunc(&enp
->ae_str
, 0);
1343 # ifdef HAVE_N_VA_COPY
1346 imax
= n_MIN(LINESIZE
, 1024);
1348 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1349 # ifdef HAVE_N_VA_COPY
1357 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1358 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1359 # ifdef HAVE_N_VA_COPY
1366 if(UICMP(z
, i
, >=, imax
)){
1367 # ifdef HAVE_N_VA_COPY
1368 /* XXX Check overflow for upcoming LEN+++i! */
1369 n_string_trunc(&enp
->ae_str
, len
);
1372 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1377 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1379 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1381 #endif /* HAVE_ERRORS */
1389 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1393 va_start(ap
, format
);
1394 vfprintf(n_stderr
, format
, ap
);
1400 n_perr(char const *msg
, int errval
){
1411 e
= (errval
== 0) ? n_err_no
: errval
;
1412 n_err(fmt
, msg
, n_err_to_doc(e
));
1419 n_alert(char const *format
, ...){
1423 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1425 va_start(ap
, format
);
1434 n_panic(char const *format
, ...){
1438 if(a_aux_err_linelen
> 0){
1439 putc('\n', n_stderr
);
1440 a_aux_err_linelen
= 0;
1442 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1444 va_start(ap
, format
);
1445 vfprintf(n_stderr
, format
, ap
);
1448 putc('\n', n_stderr
);
1451 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1458 struct a_aux_err_node
*enp
;
1465 if(!asccasecmp(*argv
, "show"))
1467 if(!asccasecmp(*argv
, "clear"))
1471 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1475 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1481 if(a_aux_err_head
== NULL
){
1482 fprintf(n_stderr
, _("The error ring is empty\n"));
1486 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1488 fprintf(n_stderr
, _("tmpfile"));
1493 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1494 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1495 /* We don't know whether last string ended with NL; be simple XXX */
1498 page_or_print(fp
, 0);
1504 a_aux_err_tail
= NULL
;
1505 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1506 a_aux_err_linelen
= 0;
1507 while((enp
= a_aux_err_head
) != NULL
){
1508 a_aux_err_head
= enp
->ae_next
;
1509 n_string_gut(&enp
->ae_str
);
1514 #endif /* HAVE_ERRORS */
1517 n_err_to_doc(si32_t eno
){
1519 struct a_aux_err_map
const *aemp
;
1522 aemp
= a_aux_err_map_from_no(eno
);
1523 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1529 n_err_to_name(si32_t eno
){
1531 struct a_aux_err_map
const *aemp
;
1534 aemp
= a_aux_err_map_from_no(eno
);
1535 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1541 n_err_from_name(char const *name
){
1542 struct a_aux_err_map
const *aemp
;
1543 ui32_t hash
, i
, j
, x
;
1547 hash
= n_torek_hash(name
);
1549 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1550 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1553 aemp
= &a_aux_err_map
[x
];
1554 if(aemp
->aem_hash
== hash
&&
1555 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1556 rv
= aemp
->aem_err_no
;
1560 if(++i
== a_AUX_ERR_REV_PRIME
){
1561 #ifdef a_AUX_ERR_REV_WRAPAROUND
1569 /* Have not found it. But wait, it could be that the user did, e.g.,
1570 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1571 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1572 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1573 ) == n_IDEC_STATE_CONSUMED
){
1574 aemp
= a_aux_err_map_from_no(rv
);
1575 rv
= aemp
->aem_err_no
;
1579 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1587 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1592 i
= regerror(e
, rep
, NULL
, 0) +1;
1594 regerror(e
, rep
, cp
, i
);