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){
843 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
844 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
846 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
847 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
852 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
853 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
861 n_nodename(bool_t mayoverride
){
862 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
867 # ifdef HAVE_GETADDRINFO
868 struct addrinfo hints
, *res
;
870 struct hostent
*hent
;
875 if(mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0'){
877 }else if((hn
= sys_hostname
) == NULL
){
881 # ifdef HAVE_GETADDRINFO
882 memset(&hints
, 0, sizeof hints
);
883 hints
.ai_family
= AF_UNSPEC
;
884 hints
.ai_flags
= AI_CANONNAME
;
885 if(getaddrinfo(hn
, NULL
, &hints
, &res
) == 0){
886 if(res
->ai_canonname
!= NULL
){
889 l
= strlen(res
->ai_canonname
) +1;
890 hn
= n_lofi_alloc(l
);
891 memcpy(hn
, res
->ai_canonname
, l
);
896 hent
= gethostbyname(hn
);
901 sys_hostname
= sstrdup(hn
);
902 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
903 if(hn
!= ut
.nodename
)
909 if(hostname
!= NULL
&& hostname
!= sys_hostname
)
911 hostname
= sstrdup(hn
);
917 n_random_create_cp(size_t length
, ui32_t
*reprocnt_or_null
){
923 #ifndef HAVE_POSIX_RANDOM
924 if(a_aux_rand
== NULL
)
928 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
929 * with PAD stripped is still longer than what the user requests, easy way */
930 data
= n_lofi_alloc(i
= length
+ 3);
932 if(!(n_psonce
& n_PSO_REPRODUCIBLE
) || reprocnt_or_null
== NULL
){
933 #ifndef HAVE_POSIX_RANDOM
935 data
[i
] = (char)a_aux_rand_get8();
937 for(cp
= data
; i
> 0;){
938 union {ui32_t i4
; char c
[4];} r
;
941 r
.i4
= (ui32_t
)arc4random();
943 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
944 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
945 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
946 default: cp
[0] = r
.c
[0]; break;
953 for(cp
= data
; i
> 0;){
954 union {ui32_t i4
; char c
[4];} r
;
957 r
.i4
= ++*reprocnt_or_null
;
958 if(n_psonce
& n_PSO_BIG_ENDIAN
){ /* TODO BSWAP */
969 case 0: cp
[3] = r
.c
[3]; j
= 4; /* FALLTHRU */
970 case 3: cp
[2] = r
.c
[2]; /* FALLTHRU */
971 case 2: cp
[1] = r
.c
[1]; /* FALLTHRU */
972 default: cp
[0] = r
.c
[0]; break;
979 assert(length
+ 3 < UIZ_MAX
/ 4);
980 b64_encode_buf(&b64
, data
, length
+ 3,
981 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
984 assert(b64
.l
>= length
);
985 b64
.s
[length
] = '\0';
991 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
996 assert(inlen
== 0 || inbuf
!= NULL
);
998 if (inlen
== UIZ_MAX
)
999 inlen
= strlen(inbuf
);
1002 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1004 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
1005 !ascncasecmp(inbuf
, "true", inlen
) ||
1006 !ascncasecmp(inbuf
, "yes", inlen
) ||
1007 !ascncasecmp(inbuf
, "on", inlen
))
1009 else if ((inlen
== 1 &&
1010 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
1011 !ascncasecmp(inbuf
, "false", inlen
) ||
1012 !ascncasecmp(inbuf
, "no", inlen
) ||
1013 !ascncasecmp(inbuf
, "off", inlen
))
1018 if((n_idec_buf(&ib
, inbuf
, inlen
, 0, 0, NULL
1019 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1020 ) != n_IDEC_STATE_CONSUMED
)
1031 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1036 assert(inlen
== 0 || inbuf
!= NULL
);
1038 if (inlen
== UIZ_MAX
)
1039 inlen
= strlen(inbuf
);
1042 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1043 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1044 !ascncasecmp(inbuf
, "ask-", 4) &&
1045 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1046 (n_psonce
& n_PSO_INTERACTIVE
))
1047 rv
= getapproval(prompt
, rv
);
1053 n_is_all_or_aster(char const *name
){
1057 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1062 FL
struct n_timespec
const *
1063 n_time_now(bool_t force_update
){ /* TODO event loop update IF cmd requests! */
1064 static struct n_timespec ts_now
;
1067 if(n_psonce
& n_PSO_REPRODUCIBLE
){
1068 (void)n_idec_ui64_cp(&ts_now
.ts_sec
, ok_vlook(SOURCE_DATE_EPOCH
), 0,NULL
);
1070 }else if(force_update
|| ts_now
.ts_sec
== 0){
1071 #ifdef HAVE_CLOCK_GETTIME
1074 clock_gettime(CLOCK_REALTIME
, &ts
);
1075 ts_now
.ts_sec
= (si64_t
)ts
.tv_sec
;
1076 ts_now
.ts_nsec
= (siz_t
)ts
.tv_nsec
;
1077 #elif defined HAVE_GETTIMEOFDAY
1080 gettimeofday(&tv
, NULL
);
1081 ts_now
.ts_sec
= (si64_t
)tv
.tv_sec
;
1082 ts_now
.ts_nsec
= (siz_t
)tv
.tv_usec
* 1000;
1084 ts_now
.ts_sec
= (si64_t
)time(NULL
);
1093 time_current_update(struct time_current
*tc
, bool_t full_update
)
1096 tc
->tc_time
= (time_t)n_time_now(TRU1
)->ts_sec
;
1098 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1099 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1100 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1106 n_msleep(uiz_t millis
, bool_t ignint
){
1110 #ifdef HAVE_NANOSLEEP
1112 struct timespec ts
, trem
;
1115 ts
.tv_sec
= millis
/ 1000;
1116 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1118 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1120 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1123 #elif defined HAVE_SLEEP
1124 if((millis
/= 1000) == 0)
1126 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1129 # error Configuration should have detected a function for sleeping.
1137 n_err(char const *format
, ...){
1141 va_start(ap
, format
);
1143 if(n_psonce
& n_PSO_INTERACTIVE
)
1153 while(*format
== '\n'){
1155 putc('\n', n_stderr
);
1160 a_aux_err_linelen
= 0;
1162 if((len
= strlen(format
)) > 0){
1163 if(doname
|| a_aux_err_linelen
== 0){
1166 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1167 fputs(cp
, n_stderr
);
1169 vfprintf(n_stderr
, format
, ap
);
1174 if(format
[--len
] == '\n'){
1175 a_aux_err_linelen
= (i
-= ++len
);
1178 ++a_aux_err_linelen
;
1190 n_verr(char const *format
, va_list ap
){
1192 struct a_aux_err_node
*enp
;
1200 while(*format
== '\n'){
1201 putc('\n', n_stderr
);
1207 a_aux_err_linelen
= 0;
1209 if(n_psonce
& n_PSO_INTERACTIVE
){
1210 if((enp
= a_aux_err_tail
) != NULL
&&
1211 (enp
->ae_str
.s_len
> 0 &&
1212 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
1213 n_string_push_c(&enp
->ae_str
, '\n');
1218 if((len
= strlen(format
)) == 0)
1221 n_pstate
|= n_PS_ERRORS_PROMPT
;
1224 if(doname
|| a_aux_err_linelen
== 0){
1227 if(*(cp
= ok_vlook(log_prefix
)) != '\0')
1228 fputs(cp
, n_stderr
);
1234 if(format
[--len
] == '\n'){
1235 a_aux_err_linelen
= (i
-= ++len
);
1238 ++a_aux_err_linelen
;
1243 if(!(n_psonce
& n_PSO_INTERACTIVE
))
1245 vfprintf(n_stderr
, format
, ap
);
1249 n_LCTAV(ERRORS_MAX
> 3);
1251 /* Link it into the `errors' message ring */
1252 if((enp
= a_aux_err_tail
) == NULL
){
1254 enp
= smalloc(sizeof *enp
);
1255 enp
->ae_next
= NULL
;
1256 n_string_creat(&enp
->ae_str
);
1257 if(a_aux_err_tail
!= NULL
)
1258 a_aux_err_tail
->ae_next
= enp
;
1260 a_aux_err_head
= enp
;
1261 a_aux_err_tail
= enp
;
1264 (enp
->ae_str
.s_len
> 0 &&
1265 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
1266 if(a_aux_err_cnt
< ERRORS_MAX
)
1269 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1270 a_aux_err_tail
->ae_next
= enp
;
1271 a_aux_err_tail
= enp
;
1272 enp
->ae_next
= NULL
;
1273 n_string_trunc(&enp
->ae_str
, 0);
1276 # ifdef HAVE_N_VA_COPY
1279 imax
= n_MIN(LINESIZE
, 1024);
1281 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
1282 # ifdef HAVE_N_VA_COPY
1290 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
1291 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
1292 # ifdef HAVE_N_VA_COPY
1299 if(UICMP(z
, i
, >=, imax
)){
1300 # ifdef HAVE_N_VA_COPY
1301 /* XXX Check overflow for upcoming LEN+++i! */
1302 n_string_trunc(&enp
->ae_str
, len
);
1305 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
1310 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
1312 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, n_stderr
);
1314 #endif /* HAVE_ERRORS */
1322 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1326 va_start(ap
, format
);
1327 vfprintf(n_stderr
, format
, ap
);
1333 n_perr(char const *msg
, int errval
){
1344 e
= (errval
== 0) ? n_err_no
: errval
;
1345 n_err(fmt
, msg
, n_err_to_doc(e
));
1352 n_alert(char const *format
, ...){
1356 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1358 va_start(ap
, format
);
1367 n_panic(char const *format
, ...){
1371 if(a_aux_err_linelen
> 0){
1372 putc('\n', n_stderr
);
1373 a_aux_err_linelen
= 0;
1375 fprintf(n_stderr
, "%sPanic: ", ok_vlook(log_prefix
));
1377 va_start(ap
, format
);
1378 vfprintf(n_stderr
, format
, ap
);
1381 putc('\n', n_stderr
);
1384 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1391 struct a_aux_err_node
*enp
;
1398 if(!asccasecmp(*argv
, "show"))
1400 if(!asccasecmp(*argv
, "clear"))
1404 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1408 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1414 if(a_aux_err_head
== NULL
){
1415 fprintf(n_stderr
, _("The error ring is empty\n"));
1419 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1421 fprintf(n_stderr
, _("tmpfile"));
1426 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1427 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1428 /* We don't know whether last string ended with NL; be simple XXX */
1431 page_or_print(fp
, 0);
1437 a_aux_err_tail
= NULL
;
1438 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1439 a_aux_err_linelen
= 0;
1440 while((enp
= a_aux_err_head
) != NULL
){
1441 a_aux_err_head
= enp
->ae_next
;
1442 n_string_gut(&enp
->ae_str
);
1447 #endif /* HAVE_ERRORS */
1450 n_err_to_doc(si32_t eno
){
1452 struct a_aux_err_map
const *aemp
;
1455 aemp
= a_aux_err_map_from_no(eno
);
1456 rv
= &a_aux_err_docs
[aemp
->aem_docoff
];
1462 n_err_to_name(si32_t eno
){
1464 struct a_aux_err_map
const *aemp
;
1467 aemp
= a_aux_err_map_from_no(eno
);
1468 rv
= &a_aux_err_names
[aemp
->aem_nameoff
];
1474 n_err_from_name(char const *name
){
1475 struct a_aux_err_map
const *aemp
;
1476 ui32_t hash
, i
, j
, x
;
1480 hash
= n_torek_hash(name
);
1482 for(i
= hash
% a_AUX_ERR_REV_PRIME
, j
= 0; j
<= a_AUX_ERR_REV_LONGEST
; ++j
){
1483 if((x
= a_aux_err_revmap
[i
]) == a_AUX_ERR_REV_ILL
)
1486 aemp
= &a_aux_err_map
[x
];
1487 if(aemp
->aem_hash
== hash
&&
1488 !strcmp(&a_aux_err_names
[aemp
->aem_nameoff
], name
)){
1489 rv
= aemp
->aem_err_no
;
1493 if(++i
== a_AUX_ERR_REV_PRIME
){
1494 #ifdef a_AUX_ERR_REV_WRAPAROUND
1502 /* Have not found it. But wait, it could be that the user did, e.g.,
1503 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1504 if((n_idec_si32_cp(&rv
, name
, 0, NULL
) &
1505 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
1506 ) == n_IDEC_STATE_CONSUMED
){
1507 aemp
= a_aux_err_map_from_no(rv
);
1508 rv
= aemp
->aem_err_no
;
1512 rv
= a_aux_err_map
[n__ERR_NUMBER_VOIDOFF
].aem_err_no
;
1520 n_regex_err_to_doc(const regex_t
*rep
, int e
){
1525 i
= regerror(e
, rep
, NULL
, 0) +1;
1527 regerror(e
, rep
, cp
, i
);