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
)
393 *env_addon
= "LESS=RXi";
394 }else if(strstr(rv
, "lv") != NULL
){
395 if(getenv("LV") == NULL
)
396 *env_addon
= "LV=-c";
404 page_or_print(FILE *fp
, size_t lines
)
412 if (n_go_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
416 rows
= (size_t)n_scrnheight
;
418 n_idec_uiz_cp(&rows
, cp
, 0, NULL
);
420 if (rows
> 0 && lines
== 0) {
421 while ((c
= getc(fp
)) != EOF
)
422 if (c
== '\n' && ++lines
>= rows
)
428 char const *env_add
[2], *pager
;
430 pager
= n_pager_get(&env_add
[0]);
432 n_child_run(pager
, NULL
, fileno(fp
), n_CHILD_FD_PASS
, NULL
,NULL
,NULL
,
438 while ((c
= getc(fp
)) != EOF
)
445 which_protocol(char const *name
, bool_t check_stat
, bool_t try_hooks
,
446 char const **adjusted_or_null
)
448 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
449 char const *cp
, *orig_name
;
450 enum protocol rv
= PROTO_UNKNOWN
;
453 if(name
[0] == '%' && name
[1] == ':')
457 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
461 if(cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/'){
462 if(!strncmp(name
, "file", sizeof("file") -1) ||
463 !strncmp(name
, "mbox", sizeof("mbox") -1))
465 else if(!strncmp(name
, "maildir", sizeof("maildir") -1))
467 else if(!strncmp(name
, "pop3", sizeof("pop3") -1)){
471 n_err(_("No POP3 support compiled in\n"));
473 }else if(!strncmp(name
, "pop3s", sizeof("pop3s") -1)){
474 #if defined HAVE_POP3 && defined HAVE_SSL
477 n_err(_("No POP3S support compiled in\n"));
480 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
484 n_err(_("No IMAP support compiled in\n"));
486 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
487 #if defined HAVE_IMAP && defined HAVE_SSL
490 n_err(_("No IMAPS support compiled in\n"));
500 if(check_stat
|| try_hooks
){
501 struct n_file_type ft
;
506 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
507 memcpy(np
, name
, sz
+ 1);
509 if(!stat(name
, &stb
)){
510 if(S_ISDIR(stb
.st_mode
) &&
511 (memcpy(&np
[sz
], "/tmp", 5),
512 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
513 (memcpy(&np
[sz
], "/new", 5),
514 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
515 (memcpy(&np
[sz
], "/cur", 5),
516 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
518 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
519 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
520 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
521 !asccasecmp(cp
, "maildir"))
527 if(adjusted_or_null
!= NULL
)
528 *adjusted_or_null
= orig_name
;
534 n_c_to_hex_base16(char store
[3], char c
){
535 static char const itoa16
[] = "0123456789ABCDEF";
539 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
540 c
= ((ui8_t
)c
>> 4) & 0x0F;
541 store
[0] = itoa16
[(ui8_t
)c
];
547 n_c_from_hex_base16(char const hex
[2]){
548 static ui8_t
const atoi16
[] = {
549 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
550 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
551 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
552 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
553 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
554 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
555 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
561 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
562 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
566 if ((i1
| i2
) & 0xF0u
)
580 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
581 enum n_idec_mode idm
, char const **endptr_or_null
){
582 /* XXX Brute simple and */
585 enum n_idec_state rv
;
588 idm
&= n__IDEC_MODE_MASK
;
589 rv
= n_IDEC_STATE_NONE
| idm
;
598 assert(base
!= 1 && base
<= 36);
599 /*if(base == 1 || base > 36)
603 while(spacechar(*cbuf
))
604 if(*++cbuf
== '\0' || --clen
== 0)
610 rv
|= n_IDEC_STATE_SEEN_MINUS
;
613 if(*++cbuf
== '\0' || --clen
== 0)
618 /* Base detection/skip */
622 /* Character must be valid for base */
623 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
627 /* 0 always valid as is, fallback base 10 */
628 if(*++cbuf
== '\0' || --clen
== 0)
631 /* Base "detection" */
632 if(base
== 0 || base
== 2 || base
== 16){
643 if((base
& 16) == 0){
645 /* Char after prefix must be valid. However, after some error
646 * in the tor software all libraries (which had to) turned to
647 * an interpretation of the C standard which says that the
648 * prefix may optionally precede an otherwise valid sequence,
649 * which means that "0x" is not a STATE_INVAL error but gives
650 * a "0" result with a "STATE_BASE" error and a rest of "x" */
653 if(clen
> 1 && a_aux_idec_atoi
[(ui8_t
)cbuf
[1]] < base
){
658 if(*++cbuf
== '\0' || --clen
== 0)
661 /* Character must be valid for base, invalid otherwise */
662 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
675 /* Character must be valid for base, _EBASE otherwise */
676 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
681 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
685 if(res
> UI64_MAX
- currc
)
695 if(*++cbuf
== '\0' || --clen
== 0)
698 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
707 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
708 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
709 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
710 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
711 default: uimask
= UI64_MAX
; break;
713 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
717 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
718 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
719 if(res
> uimask
+ 1){
728 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
729 rv
|= n_IDEC_STATE_EOVERFLOW
;
730 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
734 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
735 case n_IDEC_MODE_LIMIT_8BIT
:
736 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
737 *(si8_t
*)resp
= (si8_t
)res
;
739 *(ui8_t
*)resp
= (ui8_t
)res
;
741 case n_IDEC_MODE_LIMIT_16BIT
:
742 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
743 *(si16_t
*)resp
= (si16_t
)res
;
745 *(ui16_t
*)resp
= (ui16_t
)res
;
747 case n_IDEC_MODE_LIMIT_32BIT
:
748 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
749 *(si32_t
*)resp
= (si32_t
)res
;
751 *(ui32_t
*)resp
= (ui32_t
)res
;
754 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
755 *(si64_t
*)resp
= (si64_t
)res
;
757 *(ui64_t
*)resp
= (ui64_t
)res
;
761 if(endptr_or_null
!= NULL
)
762 *endptr_or_null
= cbuf
;
763 if(*cbuf
== '\0' || clen
== 0)
764 rv
|= n_IDEC_STATE_CONSUMED
;
769 rv
|= n_IDEC_STATE_EINVAL
;
772 /* Not a base error for terminator and whitespace! */
773 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
774 rv
|= n_IDEC_STATE_EBASE
;
778 /* Overflow error: consume input until bad character or length out */
780 if(*++cbuf
== '\0' || --clen
== 0)
782 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
787 rv
|= n_IDEC_STATE_EOVERFLOW
;
789 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
790 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
794 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
799 n_torek_hash(char const *name
){
800 /* Chris Torek's hash */
805 for(h
= 0; (c
= *name
++) != '\0';)
812 n_torek_ihashn(char const *dat
, size_t len
){
813 /* See n_torek_hash() */
819 for(h
= 0; (c
= *dat
++) != '\0';)
820 h
= (h
* 33) + lowerconv(c
);
822 for(h
= 0; len
> 0; --len
){
824 h
= (h
* 33) + lowerconv(c
);
831 n_prime_next(ui32_t n
){
832 static ui32_t
const primes
[] = {
833 5, 11, 23, 47, 97, 157, 283,
834 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
835 131071, 262139, 524287, 1048573, 2097143, 4194301,
836 8388593, 16777213, 33554393, 67108859, 134217689,
837 268435399, 536870909, 1073741789, 2147483647
842 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
843 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
844 : n_NELEM(primes
) / 2));
846 do if((mprime
= primes
[i
]) > n
)
848 while(++i
< n_NELEM(primes
));
850 if(i
== n_NELEM(primes
) && mprime
< n
)
857 n_getdeadletter(void){
864 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
865 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
867 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
868 VAL_DEAD
, n_shexp_quote_cp((cp
== NULL
? n_empty
: cp
), FAL0
));
873 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
874 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
882 n_nodename(bool_t mayoverride
){
883 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
888 # ifdef HAVE_GETADDRINFO
889 struct addrinfo hints
, *res
;
891 struct hostent
*hent
;
896 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
898 }else if((hn
= sys_hostname
) == NULL
){
902 # ifdef HAVE_GETADDRINFO
903 memset(&hints
, 0, sizeof hints
);
904 hints
.ai_family
= AF_UNSPEC
;
905 hints
.ai_flags
= AI_CANONNAME
;
906 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
907 if(res
->ai_canonname
!= NULL
){
910 l
= strlen(res
->ai_canonname
) +1;
911 hn
= n_lofi_alloc(l
);
912 memcpy(hn
, res
->ai_canonname
, l
);
917 hent
= gethostbyname(hn
);
922 sys_hostname
= sstrdup(hn
);
923 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
924 if(hn
!= ut
.nodename
)
930 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
932 hostname
= sstrdup(hn
);
939 n_idna_to_ascii(struct n_string
*out
, char const *ibuf
, size_t ilen
){
944 if((rv
= (ilen
== 0)))
947 if(ibuf
[ilen
] != '\0') /* TODO n_idna_to_ascii: optimise */
948 ibuf
= savestrbuf(ibuf
, ilen
);
951 idna_utf8
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
953 if(idna_utf8
== NULL
)
956 # if HAVE_IDNA == HAVE_IDNA_LIBIDNA
960 if(idna_to_ascii_8z(idna_utf8
, &idna_ascii
, 0) == IDNA_SUCCESS
){
961 out
= n_string_assign_cp(out
, idna_ascii
);
962 idn_free(idna_ascii
);
967 # elif HAVE_IDNA == HAVE_IDNA_IDNKIT
968 ilen
= strlen(idna_utf8
);
970 switch(idn_encodename((IDN_ENCODE_APP
& ~IDN_LOCALCONV
), idna_utf8
,
971 n_string_resize(n_string_trunc(out
, 0), ilen
)->s_dat
, ilen
)){
972 case idn_buffer_overflow
:
973 ilen
+= HOST_NAME_MAX
+1;
977 ilen
= strlen(out
->s_dat
);
985 # error Unknown HAVE_IDNA
988 out
= n_string_trunc(out
, ilen
);
992 #endif /* HAVE_IDNA */
995 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
1001 #ifndef HAVE_POSIX_RANDOM
1002 if(a_aux_rand
== NULL
)
1006 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1007 * with PAD stripped is still longer than what the user requests, easy way */
1008 data
= n_lofi_alloc(i
= length
+ 3);
1010 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
1011 #ifndef HAVE_POSIX_RANDOM
1013 data
[i
] = (char)a_aux_rand_get8();
1015 for(cp
= data
; i
> 0;){
1016 union {ui32_t i4
; char c
[4];} r
;
1019 r
.i4
= (ui32_t
)arc4random();
1020 switch((j
= i
& 3)){
1021 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1022 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1023 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1024 default: cp
[0] = r
.c
[0]; break;
1031 for(cp
= data
; i
> 0;){
1032 union {ui32_t i4
; char c
[4];} r
;
1035 r
.i4
= ++*reprocnt_or_null
;
1036 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
1046 switch((j
= i
& 3)){
1047 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
1048 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
1049 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
1050 default: cp
[0] = r
.c
[0]; break;
1057 assert(length
+ 3 < UIZ_MAX
/ 4);
1058 b64_encode_buf(&b64
, data
, length
+ 3,
1059 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1062 assert(b64
.l
>= length
);
1063 b64
.s
[length
] = '\0';
1069 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1074 assert(inlen
== 0 || inbuf
!= NULL
);
1076 if (inlen
== UIZ_MAX
)
1077 inlen
= strlen(inbuf
);
1080 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1082 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1083 !ascncasecmp(inbuf
, "true", inlen
) ||
1084 !ascncasecmp(inbuf
, "yes", inlen
) ||
1085 !ascncasecmp(inbuf
, "on", inlen
))
1087 else if ((inlen
== 1 &&
1088 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1089 !ascncasecmp(inbuf
, "false", inlen
) ||
1090 !ascncasecmp(inbuf
, "no", inlen
) ||
1091 !ascncasecmp(inbuf
, "off", inlen
))
1096 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1097 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1098 ) != n_IDEC_STATE_CONSUMED
)
1109 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1114 assert(inlen
== 0 || inbuf
!= NULL
);
1116 if (inlen
== UIZ_MAX
)
1117 inlen
= strlen(inbuf
);
1120 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1121 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1122 !ascncasecmp(inbuf
, "ask-", 4) &&
1123 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1124 (n_psonce
& n_PSO_INTERACTIVE
))
1125 rv
= getapproval(prompt
, rv
);
1131 n_is_all_or_aster(char const *name
){
1135 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1140 FL
struct n_timespec
const *
1141 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1142 static struct n_timespec ts_now
;
1145 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1146 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1148 }else if(force_update
|| ts_now
.ts_sec
== 0){
1149 #ifdef HAVE_CLOCK_GETTIME
1152 clock_gettime(CLOCK_REALTIME
, &ts
);
1153 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1154 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1155 #elif defined HAVE_GETTIMEOFDAY
1158 gettimeofday(&tv
, NULL
);
1159 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1160 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1162 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1171 time_current_update(struct time_current
*tc
, bool_t full_update
)
1174 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1176 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1177 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1178 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1184 n_msleep(uiz_t millis
, bool_t ignint
){
1188 #ifdef HAVE_NANOSLEEP
1190 struct timespec ts
, trem
;
1193 ts
.tv_sec
= millis
/ 1000;
1194 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1196 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1198 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1201 #elif defined HAVE_SLEEP
1202 if((millis
/= 1000) == 0)
1204 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1207 # error Configuration should have detected a function for sleeping.
1215 n_err(char const *format
, ...){
1219 va_start(ap
, format
);
1221 if(n_psonce
& n_PSO_INTERACTIVE
)
1231 while(*format
== '\n'){
1233 putc('\n', n_stderr
);
1238 a_aux_err_linelen
= 0;
1240 if((len
= strlen(format
)) > 0){
1241 if(doname
|| a_aux_err_linelen
== 0){
1244 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1245 fputs(cp
, n_stderr
);
1247 vfprintf(n_stderr
, format
, ap
);
1252 if(format
[--len
] == '\n'){
1253 a_aux_err_linelen
= (i
-= ++len
);
1256 ++a_aux_err_linelen
;
1268 n_verr(char const *format
, va_list ap
){
1270 struct a_aux_err_node
*enp
;
1278 while(*format
== '\n'){
1279 putc('\n', n_stderr
);
1285 a_aux_err_linelen
= 0;
1287 if(n_psonce
& n_PSO_INTERACTIVE
){
1288 if((enp
= a_aux_err_tail
) != NULL
&&
1289 (enp
->ae_str
.s_len
> 0 &&
1290 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1291 n_string_push_c(&enp
->ae_str
, '\n');
1296 if((len
= strlen(format
)) == 0)
1299 n_pstate
|= n_PS_ERRORS_PROMPT
;
1302 if(doname
|| a_aux_err_linelen
== 0){
1305 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1306 fputs(cp
, n_stderr
);
1312 if(format
[--len
] == '\n'){
1313 a_aux_err_linelen
= (i
-= ++len
);
1316 ++a_aux_err_linelen
;
1321 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1323 vfprintf(n_stderr
, format
, ap
);
1327 n_LCTAV(ERRORS_MAX
> 3);
1329 /* Link it into the `errors' message ring */
1330 if((enp
= a_aux_err_tail
) == NULL
){
1332 enp
= smalloc(sizeof *enp
);
1333 enp
->ae_next
= NULL
;
1334 n_string_creat(&enp
->ae_str
);
1335 if(a_aux_err_tail
!= NULL
)
1336 a_aux_err_tail
->ae_next
= enp
;
1338 a_aux_err_head
= enp
;
1339 a_aux_err_tail
= enp
;
1342 (enp
->ae_str
.s_len
> 0 &&
1343 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1344 if(a_aux_err_cnt
< ERRORS_MAX
)
1347 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1348 a_aux_err_tail
->ae_next
= enp
;
1349 a_aux_err_tail
= enp
;
1350 enp
->ae_next
= NULL
;
1351 n_string_trunc(&enp
->ae_str
, 0);
1354 # ifdef HAVE_N_VA_COPY
1357 imax
= n_MIN(LINESIZE
, 1024);
1359 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1360 # ifdef HAVE_N_VA_COPY
1368 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1369 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1370 # ifdef HAVE_N_VA_COPY
1377 if(UICMP(z
, i
, >=, imax
)){
1378 # ifdef HAVE_N_VA_COPY
1379 /* XXX Check overflow for upcoming LEN+++i! */
1380 n_string_trunc(&enp
->ae_str
, len
);
1383 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1388 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1390 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1392 #endif /* HAVE_ERRORS */
1400 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1404 va_start(ap
, format
);
1405 vfprintf(n_stderr
, format
, ap
);
1411 n_perr(char const *msg
, int errval
){
1422 e
= (errval
== 0) ? n_err_no
: errval
;
1423 n_err(fmt
, msg
, n_err_to_doc(e
));
1430 n_alert(char const *format
, ...){
1434 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1436 va_start(ap
, format
);
1445 n_panic(char const *format
, ...){
1449 if(a_aux_err_linelen
> 0){
1450 putc('\n', n_stderr
);
1451 a_aux_err_linelen
= 0;
1453 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1455 va_start(ap
, format
);
1456 vfprintf(n_stderr
, format
, ap
);
1459 putc('\n', n_stderr
);
1462 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1469 struct a_aux_err_node
*enp
;
1476 if(!asccasecmp(*argv
, "show"))
1478 if(!asccasecmp(*argv
, "clear"))
1482 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1486 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1492 if(a_aux_err_head
== NULL
){
1493 fprintf(n_stderr
, _("The error ring is empty\n"));
1497 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1499 fprintf(n_stderr
, _("tmpfile"));
1504 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1505 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1506 /* We don't know whether last string ended with NL; be simple XXX */
1509 page_or_print(fp
, 0);
1515 a_aux_err_tail
= NULL
;
1516 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1517 a_aux_err_linelen
= 0;
1518 while((enp
= a_aux_err_head
) != NULL
){
1519 a_aux_err_head
= enp
->ae_next
;
1520 n_string_gut(&enp
->ae_str
);
1525 #endif /* HAVE_ERRORS */
1528 n_err_to_doc(si32_t eno
){
1530 struct a_aux_err_map
const *aemp
;
1533 aemp
= a_aux_err_map_from_no(eno
);
1534 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1540 n_err_to_name(si32_t eno
){
1542 struct a_aux_err_map
const *aemp
;
1545 aemp
= a_aux_err_map_from_no(eno
);
1546 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1552 n_err_from_name(char const *name
){
1553 struct a_aux_err_map
const *aemp
;
1554 ui32_t hash
, i
, j
, x
;
1558 hash
= n_torek_hash(name
);
1560 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1561 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1564 aemp
= &a_aux_err_map
[x
];
1565 if(aemp
->aem_hash
== hash
&&
1566 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1567 rv
= aemp
->aem_err_no
;
1571 if(++i
== a_AUX_ERR_REV_PRIME
){
1572 #ifdef a_AUX_ERR_REV_WRAPAROUND
1580 /* Have not found it. But wait, it could be that the user did, e.g.,
1581 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1582 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1583 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1584 ) == n_IDEC_STATE_CONSUMED
){
1585 aemp
= a_aux_err_map_from_no(rv
);
1586 rv
= aemp
->aem_err_no
;
1590 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1598 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1603 i
= regerror(e
, rep
, NULL
, 0) +1;
1605 regerror(e
, rep
, cp
, i
);