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"));
475 else if(!strncmp(name
, "imap", sizeof("imap") -1)){
479 n_err(_("No IMAP support compiled in\n"));
481 }else if(!strncmp(name
, "imaps", sizeof("imaps") -1)){
482 #if defined HAVE_IMAP && defined HAVE_SSL
485 n_err(_("No IMAPS support compiled in\n"));
495 if(check_stat
|| try_hooks
){
496 struct n_file_type ft
;
501 np
= n_lofi_alloc((sz
= strlen(name
)) + 4 +1);
502 memcpy(np
, name
, sz
+ 1);
504 if(!stat(name
, &stb
)){
505 if(S_ISDIR(stb
.st_mode
) &&
506 (memcpy(&np
[sz
], "/tmp", 5),
507 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
508 (memcpy(&np
[sz
], "/new", 5),
509 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)) &&
510 (memcpy(&np
[sz
], "/cur", 5),
511 !stat(np
, &stb
) && S_ISDIR(stb
.st_mode
)))
513 }else if(try_hooks
&& n_filetype_trial(&ft
, name
))
514 orig_name
= savecatsep(name
, '.', ft
.ft_ext_dat
);
515 else if((cp
= ok_vlook(newfolders
)) != NULL
&&
516 !asccasecmp(cp
, "maildir"))
522 if(adjusted_or_null
!= NULL
)
523 *adjusted_or_null
= orig_name
;
529 n_c_to_hex_base16(char store
[3], char c
){
530 static char const itoa16
[] = "0123456789ABCDEF";
534 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
535 c
= ((ui8_t
)c
>> 4) & 0x0F;
536 store
[0] = itoa16
[(ui8_t
)c
];
542 n_c_from_hex_base16(char const hex
[2]){
543 static ui8_t
const atoi16
[] = {
544 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
545 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
546 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
547 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
548 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
549 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
550 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
556 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
557 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
561 if ((i1
| i2
) & 0xF0u
)
575 n_idec_buf(void *resp
, char const *cbuf
, uiz_t clen
, ui8_t base
,
576 enum n_idec_mode idm
, char const **endptr_or_null
){
577 /* XXX Brute simple and */
580 enum n_idec_state rv
;
583 idm
&= n__IDEC_MODE_MASK
;
584 rv
= n_IDEC_STATE_NONE
| idm
;
594 while(spacechar(*cbuf
))
595 if(*++cbuf
== '\0' || --clen
== 0)
601 rv
|= n_IDEC_STATE_SEEN_MINUS
;
604 if(*++cbuf
== '\0' || --clen
== 0)
609 /* Base detection/skip */
613 /* Character must be valid for base */
614 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
618 /* 0 always valid as is, fallback base 10 */
619 if(*++cbuf
== '\0' || --clen
== 0)
622 /* Base "detection" */
623 if(base
== 0 || base
== 2 || base
== 16){
634 if((base
& 16) == 0){
636 /* Char after prefix must be valid */
638 if(*++cbuf
== '\0' || --clen
== 0)
641 /* Character must be valid for base, invalid otherwise */
642 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
654 /* Character must be valid for base, _EBASE otherwise */
655 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
660 for(cut
= a_aux_idec_cutlimit
[base
- 2];;){
664 if(res
> UI64_MAX
- currc
)
674 if(*++cbuf
== '\0' || --clen
== 0)
677 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
686 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
687 case n_IDEC_MODE_LIMIT_8BIT
: uimask
= UI8_MAX
; break;
688 case n_IDEC_MODE_LIMIT_16BIT
: uimask
= UI16_MAX
; break;
689 case n_IDEC_MODE_LIMIT_32BIT
: uimask
= UI32_MAX
; break;
690 default: uimask
= UI64_MAX
; break;
692 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
696 if((rv
& (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)
697 ) == (n_IDEC_MODE_SIGNED_TYPE
| n_IDEC_STATE_SEEN_MINUS
)){
698 if(res
> uimask
+ 1){
707 if(!(rv
& n_IDEC_MODE_LIMIT_NOERROR
))
708 rv
|= n_IDEC_STATE_EOVERFLOW
;
709 }else if(rv
& n_IDEC_STATE_SEEN_MINUS
)
713 switch(rv
& n__IDEC_MODE_LIMIT_MASK
){
714 case n_IDEC_MODE_LIMIT_8BIT
:
715 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
716 *(si8_t
*)resp
= (si8_t
)res
;
718 *(ui8_t
*)resp
= (ui8_t
)res
;
720 case n_IDEC_MODE_LIMIT_16BIT
:
721 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
722 *(si16_t
*)resp
= (si16_t
)res
;
724 *(ui16_t
*)resp
= (ui16_t
)res
;
726 case n_IDEC_MODE_LIMIT_32BIT
:
727 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
728 *(si32_t
*)resp
= (si32_t
)res
;
730 *(ui32_t
*)resp
= (ui32_t
)res
;
733 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
734 *(si64_t
*)resp
= (si64_t
)res
;
736 *(ui64_t
*)resp
= (ui64_t
)res
;
740 if(endptr_or_null
!= NULL
)
741 *endptr_or_null
= cbuf
;
742 if(*cbuf
== '\0' || clen
== 0)
743 rv
|= n_IDEC_STATE_CONSUMED
;
748 rv
|= n_IDEC_STATE_EINVAL
;
751 /* Not a base error for terminator and whitespace! */
752 if(*cbuf
!= '\0' && !spacechar(*cbuf
))
753 rv
|= n_IDEC_STATE_EBASE
;
757 /* Overflow error: consume input until bad character or length out */
759 if(*++cbuf
== '\0' || --clen
== 0)
761 currc
= a_aux_idec_atoi
[(ui8_t
)*cbuf
];
766 rv
|= n_IDEC_STATE_EOVERFLOW
;
768 if(rv
& n_IDEC_MODE_SIGNED_TYPE
)
769 res
= (rv
& n_IDEC_STATE_SEEN_MINUS
) ? (ui64_t
)SI64_MIN
773 rv
&= ~n_IDEC_STATE_SEEN_MINUS
;
778 n_torek_hash(char const *name
){
779 /* Chris Torek's hash */
784 for(h
= 0; (c
= *name
++) != '\0';)
791 n_torek_ihashn(char const *dat
, size_t len
){
792 /* See n_torek_hash() */
798 for(h
= 0; (c
= *dat
++) != '\0';)
799 h
= (h
* 33) + lowerconv(c
);
801 for(h
= 0; len
> 0; --len
){
803 h
= (h
* 33) + lowerconv(c
);
810 n_prime_next(ui32_t n
){
811 static ui32_t
const primes
[] = {
812 5, 11, 23, 47, 97, 157, 283,
813 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
814 131071, 262139, 524287, 1048573, 2097143, 4194301,
815 8388593, 16777213, 33554393, 67108859, 134217689,
816 268435399, 536870909, 1073741789, 2147483647
821 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
822 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
823 : n_NELEM(primes
) / 2));
825 do if((mprime
= primes
[i
]) > n
)
827 while(++i
< n_NELEM(primes
));
829 if(i
== n_NELEM(primes
) && mprime
< n
)
836 n_getdeadletter(void){
837 char const *cp_base
, *cp
;
842 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
843 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
845 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
846 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
850 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
851 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
859 n_nodename(bool_t mayoverride
){
860 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
865 # ifdef HAVE_GETADDRINFO
866 struct addrinfo hints
, *res
;
868 struct hostent
*hent
;
873 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
875 }else if((hn
= sys_hostname
) == NULL
){
879 # ifdef HAVE_GETADDRINFO
880 memset(&hints
, 0, sizeof hints
);
881 hints
.ai_family
= AF_UNSPEC
;
882 hints
.ai_flags
= AI_CANONNAME
;
883 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
884 if(res
->ai_canonname
!= NULL
){
887 l
= strlen(res
->ai_canonname
) +1;
888 hn
= n_lofi_alloc(l
);
889 memcpy(hn
, res
->ai_canonname
, l
);
894 hent
= gethostbyname(hn
);
899 sys_hostname
= sstrdup(hn
);
900 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
901 if(hn
!= ut
.nodename
)
907 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
909 hostname
= sstrdup(hn
);
915 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
921 #ifndef HAVE_POSIX_RANDOM
922 if(a_aux_rand
== NULL
)
926 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
927 * with PAD stripped is still longer than what the user requests, easy way */
928 data
= n_lofi_alloc(i
= length
+ 3);
930 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
931 #ifndef HAVE_POSIX_RANDOM
933 data
[i
] = (char)a_aux_rand_get8();
935 for(cp
= data
; i
> 0;){
936 union {ui32_t i4
; char c
[4];} r
;
939 r
.i4
= (ui32_t
)arc4random();
941 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
942 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
943 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
944 default: cp
[0] = r
.c
[0]; break;
951 for(cp
= data
; i
> 0;){
952 union {ui32_t i4
; char c
[4];} r
;
955 r
.i4
= ++*reprocnt_or_null
;
956 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
967 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
968 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
969 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
970 default: cp
[0] = r
.c
[0]; break;
977 assert(length
+ 3 < UIZ_MAX
/ 4);
978 b64_encode_buf(&b64
, data
, length
+ 3,
979 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
982 assert(b64
.l
>= length
);
983 b64
.s
[length
] = '\0';
989 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
994 assert(inlen
== 0 || inbuf
!= NULL
);
996 if (inlen
== UIZ_MAX
)
997 inlen
= strlen(inbuf
);
1000 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1002 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1003 !ascncasecmp(inbuf
, "true", inlen
) ||
1004 !ascncasecmp(inbuf
, "yes", inlen
) ||
1005 !ascncasecmp(inbuf
, "on", inlen
))
1007 else if ((inlen
== 1 &&
1008 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1009 !ascncasecmp(inbuf
, "false", inlen
) ||
1010 !ascncasecmp(inbuf
, "no", inlen
) ||
1011 !ascncasecmp(inbuf
, "off", inlen
))
1016 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1017 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1018 ) != n_IDEC_STATE_CONSUMED
)
1029 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1034 assert(inlen
== 0 || inbuf
!= NULL
);
1036 if (inlen
== UIZ_MAX
)
1037 inlen
= strlen(inbuf
);
1040 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1041 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1042 !ascncasecmp(inbuf
, "ask-", 4) &&
1043 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1044 (n_psonce
& n_PSO_INTERACTIVE
))
1045 rv
= getapproval(prompt
, rv
);
1051 n_is_all_or_aster(char const *name
){
1055 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1060 FL
struct n_timespec
const *
1061 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1062 static struct n_timespec ts_now
;
1065 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1066 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1068 }else if(force_update
|| ts_now
.ts_sec
== 0){
1069 #ifdef HAVE_CLOCK_GETTIME
1072 clock_gettime(CLOCK_REALTIME
, &ts
);
1073 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1074 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1075 #elif defined HAVE_GETTIMEOFDAY
1078 gettimeofday(&tv
, NULL
);
1079 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1080 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1082 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1091 time_current_update(struct time_current
*tc
, bool_t full_update
)
1094 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1096 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1097 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1098 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1104 n_msleep(uiz_t millis
, bool_t ignint
){
1108 #ifdef HAVE_NANOSLEEP
1110 struct timespec ts
, trem
;
1113 ts
.tv_sec
= millis
/ 1000;
1114 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1116 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1118 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1121 #elif defined HAVE_SLEEP
1122 if((millis
/= 1000) == 0)
1124 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1127 # error Configuration should have detected a function for sleeping.
1135 n_err(char const *format
, ...){
1139 va_start(ap
, format
);
1141 if(n_psonce
& n_PSO_INTERACTIVE
)
1151 while(*format
== '\n'){
1153 putc('\n', n_stderr
);
1158 a_aux_err_linelen
= 0;
1160 if((len
= strlen(format
)) > 0){
1161 if(doname
|| a_aux_err_linelen
== 0){
1164 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1165 fputs(cp
, n_stderr
);
1167 vfprintf(n_stderr
, format
, ap
);
1172 if(format
[--len
] == '\n'){
1173 a_aux_err_linelen
= (i
-= ++len
);
1176 ++a_aux_err_linelen
;
1188 n_verr(char const *format
, va_list ap
){
1190 struct a_aux_err_node
*enp
;
1198 while(*format
== '\n'){
1199 putc('\n', n_stderr
);
1205 a_aux_err_linelen
= 0;
1207 if(n_psonce
& n_PSO_INTERACTIVE
){
1208 if((enp
= a_aux_err_tail
) != NULL
&&
1209 (enp
->ae_str
.s_len
> 0 &&
1210 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1211 n_string_push_c(&enp
->ae_str
, '\n');
1216 if((len
= strlen(format
)) == 0)
1219 n_pstate
|= n_PS_ERRORS_PROMPT
;
1222 if(doname
|| a_aux_err_linelen
== 0){
1225 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1226 fputs(cp
, n_stderr
);
1232 if(format
[--len
] == '\n'){
1233 a_aux_err_linelen
= (i
-= ++len
);
1236 ++a_aux_err_linelen
;
1241 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1243 vfprintf(n_stderr
, format
, ap
);
1247 n_LCTAV(ERRORS_MAX
> 3);
1249 /* Link it into the `errors' message ring */
1250 if((enp
= a_aux_err_tail
) == NULL
){
1252 enp
= smalloc(sizeof *enp
);
1253 enp
->ae_next
= NULL
;
1254 n_string_creat(&enp
->ae_str
);
1255 if(a_aux_err_tail
!= NULL
)
1256 a_aux_err_tail
->ae_next
= enp
;
1258 a_aux_err_head
= enp
;
1259 a_aux_err_tail
= enp
;
1262 (enp
->ae_str
.s_len
> 0 &&
1263 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1264 if(a_aux_err_cnt
< ERRORS_MAX
)
1267 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1268 a_aux_err_tail
->ae_next
= enp
;
1269 a_aux_err_tail
= enp
;
1270 enp
->ae_next
= NULL
;
1271 n_string_trunc(&enp
->ae_str
, 0);
1274 # ifdef HAVE_N_VA_COPY
1277 imax
= n_MIN(LINESIZE
, 1024);
1279 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1280 # ifdef HAVE_N_VA_COPY
1288 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1289 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1290 # ifdef HAVE_N_VA_COPY
1297 if(UICMP(z
, i
, >=, imax
)){
1298 # ifdef HAVE_N_VA_COPY
1299 /* XXX Check overflow for upcoming LEN+++i! */
1300 n_string_trunc(&enp
->ae_str
, len
);
1303 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1308 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1310 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1312 #endif /* HAVE_ERRORS */
1320 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1324 va_start(ap
, format
);
1325 vfprintf(n_stderr
, format
, ap
);
1331 n_perr(char const *msg
, int errval
){
1342 e
= (errval
== 0) ? n_err_no
: errval
;
1343 n_err(fmt
, msg
, n_err_to_doc(e
));
1350 n_alert(char const *format
, ...){
1354 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1356 va_start(ap
, format
);
1365 n_panic(char const *format
, ...){
1369 if(a_aux_err_linelen
> 0){
1370 putc('\n', n_stderr
);
1371 a_aux_err_linelen
= 0;
1373 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1375 va_start(ap
, format
);
1376 vfprintf(n_stderr
, format
, ap
);
1379 putc('\n', n_stderr
);
1382 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1389 struct a_aux_err_node
*enp
;
1396 if(!asccasecmp(*argv
, "show"))
1398 if(!asccasecmp(*argv
, "clear"))
1402 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1406 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1412 if(a_aux_err_head
== NULL
){
1413 fprintf(n_stderr
, _("The error ring is empty\n"));
1417 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1419 fprintf(n_stderr
, _("tmpfile"));
1424 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1425 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1426 /* We don't know whether last string ended with NL; be simple XXX */
1429 page_or_print(fp
, 0);
1435 a_aux_err_tail
= NULL
;
1436 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1437 a_aux_err_linelen
= 0;
1438 while((enp
= a_aux_err_head
) != NULL
){
1439 a_aux_err_head
= enp
->ae_next
;
1440 n_string_gut(&enp
->ae_str
);
1445 #endif /* HAVE_ERRORS */
1448 n_err_to_doc(si32_t eno
){
1450 struct a_aux_err_map
const *aemp
;
1453 aemp
= a_aux_err_map_from_no(eno
);
1454 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1460 n_err_to_name(si32_t eno
){
1462 struct a_aux_err_map
const *aemp
;
1465 aemp
= a_aux_err_map_from_no(eno
);
1466 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1472 n_err_from_name(char const *name
){
1473 struct a_aux_err_map
const *aemp
;
1474 ui32_t hash
, i
, j
, x
;
1478 hash
= n_torek_hash(name
);
1480 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1481 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1484 aemp
= &a_aux_err_map
[x
];
1485 if(aemp
->aem_hash
== hash
&&
1486 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1487 rv
= aemp
->aem_err_no
;
1491 if(++i
== a_AUX_ERR_REV_PRIME
){
1492 #ifdef a_AUX_ERR_REV_WRAPAROUND
1500 /* Have not found it. But wait, it could be that the user did, e.g.,
1501 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1502 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1503 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1504 ) == n_IDEC_STATE_CONSUMED
){
1505 aemp
= a_aux_err_map_from_no(rv
);
1506 rv
= aemp
->aem_err_no
;
1510 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1518 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1523 i
= regerror(e
, rep
, NULL
, 0) +1;
1525 regerror(e
, rep
, cp
, i
);