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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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>
47 # ifdef HAVE_GETADDRINFO
48 # include <sys/socket.h>
54 #ifndef HAVE_POSIX_RANDOM
62 ui8_t b8
[sizeof(struct rand_arc4
)];
63 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
68 struct a_aux_err_node
{
69 struct a_aux_err_node
*ae_next
;
74 #ifndef HAVE_POSIX_RANDOM
75 static union rand_state
*_rand
;
78 /* Error ring, for `errors' */
80 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
81 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
83 static size_t a_aux_err_dirty
;
85 /* Our ARC4 random generator with its completely unacademical pseudo
86 * initialization (shall /dev/urandom fail) */
87 #ifndef HAVE_POSIX_RANDOM
88 static void _rand_init(void);
89 static ui32_t
_rand_weak(ui32_t seed
);
90 SINLINE ui8_t
_rand_get8(void);
93 #ifndef HAVE_POSIX_RANDOM
97 # ifdef HAVE_CLOCK_GETTIME
102 union {int fd
; size_t i
;} u
;
106 _rand
= smalloc(sizeof *_rand
);
108 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
109 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
116 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
117 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
120 # ifdef HAVE_CLOCK_GETTIME
121 clock_gettime(CLOCK_REALTIME
, &ts
);
122 t
= (ui32_t
)ts
.tv_nsec
;
124 gettimeofday(&ts
, NULL
);
125 t
= (ui32_t
)ts
.tv_usec
;
128 t
= (t
>> 16) | (t
<< 16);
129 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
130 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
131 if (rnd
== 7 || rnd
== 17)
132 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
133 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
134 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
135 seed
^= _rand_weak(_rand
->b32
[k
]);
137 seed
^= nextprime(seed
);
141 for (u
.i
= 5 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
148 _rand_weak(ui32_t seed
)
150 /* From "Random number generators: good ones are hard to find",
151 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
152 * October 1988, p. 1195.
153 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
160 seed
= (seed
* 16807) - (hi
* 2836);
161 if ((si32_t
)seed
< 0)
171 si
= _rand
->a
._dat
[++_rand
->a
._i
];
172 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
173 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
174 _rand
->a
._dat
[_rand
->a
._j
] = si
;
175 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
177 #endif /* HAVE_POSIX_RANDOM */
186 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
187 s
= scrnheight
- 2; /* XXX no magics */
193 n_pager_get(char const **env_addon
)
198 cp
= ok_vlook(PAGER
);
199 if (cp
== NULL
|| *cp
== '\0')
202 if (env_addon
!= NULL
) {
204 /* Update the manual upon any changes:
205 * *colour-pager*, $PAGER */
206 if(strstr(rv
, "less") != NULL
){
207 if(!env_blook("LESS", TRU1
))
210 (pstate
& PS_TERMCAP_CA_MODE
) ? "LESS=Ri"
211 : !(pstate
& PS_TERMCAP_DISABLE
) ? "LESS=FRi" :
214 }else if(strstr(rv
, "lv") != NULL
){
215 if(!env_blook("LV", TRU1
))
216 *env_addon
= "LV=-c";
224 page_or_print(FILE *fp
, size_t lines
)
232 if ((options
& OPT_INTERACTIVE
) && (pstate
& PS_STARTED
) &&
233 (cp
= ok_vlook(crt
)) != NULL
) {
235 union {sl_i sli
; size_t rows
;} u
;
237 u
.sli
= strtol(cp
, &eptr
, 0);
238 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
239 ? (size_t)u
.sli
: (size_t)scrnheight
;
241 if (u
.rows
> 0 && lines
== 0) {
242 while ((c
= getc(fp
)) != EOF
)
243 if (c
== '\n' && ++lines
>= u
.rows
)
248 if (lines
>= u
.rows
) {
249 run_command(n_pager_get(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
250 NULL
, NULL
, NULL
, NULL
);
255 while ((c
= getc(fp
)) != EOF
)
262 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
268 enum protocol rv
= PROTO_UNKNOWN
;
271 temporary_protocol_ext
= NULL
;
273 if (name
[0] == '%' && name
[1] == ':')
275 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
279 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
280 if (!strncmp(name
, "pop3://", 7)) {
284 n_err(_("No POP3 support compiled in\n"));
286 } else if (!strncmp(name
, "pop3s://", 8)) {
287 #if defined HAVE_POP3 && defined HAVE_SSL
291 n_err(_("No POP3 support compiled in\n"));
294 n_err(_("No SSL support compiled in\n"));
301 /* TODO This is the de facto maildir code and thus belongs into there!
302 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
303 * TODO or (more likely) in addition to *newfolders*) */
306 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
307 memcpy(np
, name
, sz
+ 1);
308 if (!stat(name
, &st
)) {
309 if (S_ISDIR(st
.st_mode
) &&
310 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
311 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
312 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
315 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
316 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
317 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
318 temporary_protocol_ext
= cp
;
319 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
329 torek_hash(char const *name
)
331 /* Chris Torek's hash.
332 * NOTE: need to change *at least* mk-okey-map.pl when changing the
337 while (*name
!= '\0') {
346 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
353 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
354 if ((g
= h
& 0xf0000000) != 0) {
366 static ui32_t
const primes
[] = {
367 5, 11, 23, 47, 97, 157, 283,
368 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
369 131071, 262139, 524287, 1048573, 2097143, 4194301,
370 8388593, 16777213, 33554393, 67108859, 134217689,
371 268435399, 536870909, 1073741789, 2147483647
377 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
378 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
379 : NELEM(primes
) / 2));
381 if ((mprime
= primes
[i
]) > n
)
383 while (++i
< NELEM(primes
));
384 if (i
== NELEM(primes
) && mprime
< n
)
391 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
392 { /* FIXME getprompt must mb->wc->mb+reset seq! */
393 static char buf
[PROMPT_BUFFER_SIZE
];
396 char const *ccp_base
, *ccp
;
397 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
398 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
401 /* No other place to place this */
403 if (options
& OPT_INTERACTIVE
) {
404 if (!(pstate
& PS_ERRORS_NOTED
) && a_aux_err_head
!= NULL
) {
405 pstate
|= PS_ERRORS_NOTED
;
406 fprintf(stderr
, _("There are new messages in the error message ring "
407 "(denoted by \"#ERR#\")\n"
408 " The `errors' command manages this message ring\n"));
411 if ((trigger
= (a_aux_err_cnt_noted
!= a_aux_err_cnt
)))
412 a_aux_err_cnt_noted
= a_aux_err_cnt
;
418 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
428 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
430 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
432 dfmaxlen
= 0; /* keep CC happy */
436 NATCH_CHAR( cclen
= cclen_base
; )
437 maxlen
= sizeof(buf
) -1;
445 #ifdef HAVE_NATCH_CHAR
446 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
455 } else if ((l
= c
) > 1) {
465 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
471 if (c
== 0 || c
== PROMPT_STOP
)
475 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
478 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
498 nodename(int mayoverride
)
500 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
505 # ifdef HAVE_GETADDRINFO
506 struct addrinfo hints
, *res
;
508 struct hostent
*hent
;
513 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
515 } else if ((hn
= sys_hostname
) == NULL
) {
519 # ifdef HAVE_GETADDRINFO
520 memset(&hints
, 0, sizeof hints
);
521 hints
.ai_family
= AF_UNSPEC
;
522 hints
.ai_flags
= AI_CANONNAME
;
523 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
524 if (res
->ai_canonname
!= NULL
) {
525 size_t l
= strlen(res
->ai_canonname
) +1;
528 memcpy(hn
, res
->ai_canonname
, l
);
533 hent
= gethostbyname(hn
);
538 sys_hostname
= sstrdup(hn
);
539 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
540 if (hn
!= ut
.nodename
)
546 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
548 hostname
= sstrdup(hn
);
554 getrandstring(size_t length
)
561 #ifndef HAVE_POSIX_RANDOM
566 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
567 * with PAD stripped is still longer than what the user requests, easy way */
568 data
= ac_alloc(i
= length
+ 3);
570 #ifndef HAVE_POSIX_RANDOM
572 data
[i
] = (char)_rand_get8();
577 union {ui32_t i4
; char c
[4];} r
;
580 r
.i4
= (ui32_t
)arc4random();
581 switch ((j
= i
& 3)) {
582 case 0: cp
[3] = r
.c
[3]; j
= 4;
583 case 3: cp
[2] = r
.c
[2];
584 case 2: cp
[1] = r
.c
[1];
585 default: cp
[0] = r
.c
[0]; break;
593 b64_encode_buf(&b64
, data
, length
+ 3,
594 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
597 assert(b64
.l
>= length
);
598 b64
.s
[length
] = '\0';
604 field_detect_width(char const *buf
, size_t blen
){
609 blen
= (buf
== NULL
) ? 0 : strlen(buf
);
610 assert(blen
== 0 || buf
!= NULL
);
613 #ifdef HAVE_C90AMEND1
617 memset(&mbs
, 0, sizeof mbs
);
619 for(rv
= 0; blen
> 0;){
620 size_t i
= mbrtowc(&wc
, buf
, blen
, &mbs
);
644 rv
+= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
651 #endif /* HAVE_C90AMEND1 */
658 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
663 #ifdef HAVE_NATCH_CHAR
664 maxlen
= MIN(maxlen
, blen
);
665 for (rv
= 0; maxlen
> 0;) {
666 int ml
= mblen(buf
, maxlen
);
676 rv
= MIN(blen
, maxlen
);
683 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
685 NATCH_CHAR( struct bidi_info bi
; )
686 size_t rv
NATCH_CHAR( COMMA i
);
693 #ifdef HAVE_NATCH_CHAR
694 bidi_info_create(&bi
);
695 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
700 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
705 if ((i
= bi
.bi_start
.l
) > 0) {
706 memcpy(store
, bi
.bi_start
.s
, i
);
713 int ml
= mblen(buf
, blen
);
718 if (UICMP(z
, maxlen
, <, ml
))
723 memcpy(store
, buf
, ml
);
730 if ((i
= bi
.bi_end
.l
) > 0) {
731 memcpy(store
, bi
.bi_end
.s
, i
);
739 rv
= MIN(blen
, maxlen
);
740 memcpy(store
, buf
, rv
);
749 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
751 NATCH_CHAR( struct bidi_info bi
; )
752 int col_orig
= col
, n
, sz
;
753 bool_t isbidi
, isuni
, istab
, isrepl
;
757 /* Bidi only on request and when there is 8-bit data */
758 isbidi
= isuni
= FAL0
;
759 #ifdef HAVE_NATCH_CHAR
760 isuni
= ((options
& OPT_UNICODE
) != 0);
761 bidi_info_create(&bi
);
762 if (bi
.bi_start
.l
== 0)
764 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
767 if ((size_t)col
>= bi
.bi_pad
)
774 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
776 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
779 #ifdef HAVE_NATCH_CHAR
781 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
786 while (*cp
!= '\0') {
788 #ifdef HAVE_C90AMEND1
789 if (mb_cur_max
> 1) {
794 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
796 else if (wc
== L
'\t') {
797 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
800 } else if (iswprint(wc
)) {
801 # ifndef HAVE_WCWIDTH
802 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
804 if ((n
= wcwidth(wc
)) == -1)
814 istab
= (*cp
== '\t');
815 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
831 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
839 if (fill
&& col
!= 0) {
841 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
842 memset(nb
, ' ', col
);
844 memset(np
, ' ', col
);
849 #ifdef HAVE_NATCH_CHAR
851 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
857 if (cols_decr_used_or_null
!= NULL
)
858 *cols_decr_used_or_null
-= col_orig
- col
;
864 makeprint(struct str
const *in
, struct str
*out
)
866 char const *inp
, *maxp
;
871 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
875 #ifdef HAVE_NATCH_CHAR
876 if (mb_cur_max
> 1) {
877 char mbb
[MB_LEN_MAX
+ 1];
880 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
885 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
891 /* FIXME Why mbtowc() resetting here?
892 * FIXME what about ISO 2022-JP plus -- those
893 * FIXME will loose shifts, then!
894 * FIXME THUS - we'd need special "known points"
895 * FIXME to do so - say, after a newline!!
896 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
897 mbtowc(&wc
, NULL
, mb_cur_max
);
898 wc
= isuni
? 0xFFFD : '?';
903 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
905 if ((wc
& ~(wchar_t)037) == 0)
906 wc
= isuni
? 0x2400 | wc
: '?';
908 wc
= isuni
? 0x2421 : '?';
910 wc
= isuni
? 0x2426 : '?';
911 }else if(isuni
){ /* TODO ctext */
912 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
913 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
915 /* And some zero-width messes */
916 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
918 /* Oh about the ISO C wide character interfaces, baby! */
922 if ((n
= wctomb(mbb
, wc
)) <= 0)
925 assert(out
->l
< msz
);
926 for (i
= 0; i
< n
; ++i
)
930 #endif /* NATCH_CHAR */
935 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
941 out
->s
[out
->l
] = '\0';
946 delctrl(char *cp
, size_t len
)
951 for (x
= y
= 0; x
< len
; ++x
)
952 if (!cntrlchar(cp
[x
]))
968 makeprint(&in
, &out
);
969 rp
= savestrbuf(out
.s
, out
.l
);
976 prout(char const *s
, size_t sz
, FILE *fp
)
984 makeprint(&in
, &out
);
985 n
= fwrite(out
.s
, 1, out
.l
, fp
);
992 putuc(int u
, int c
, FILE *fp
)
998 #ifdef HAVE_NATCH_CHAR
999 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1000 char mbb
[MB_LEN_MAX
];
1003 if ((n
= wctomb(mbb
, u
)) > 0) {
1005 for (i
= 0; i
< n
; ++i
)
1006 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1011 rv
= (putc('\0', fp
) != EOF
);
1016 rv
= (putc(c
, fp
) != EOF
);
1022 bidi_info_needed(char const *bdat
, size_t blen
)
1027 #ifdef HAVE_NATCH_CHAR
1028 if (options
& OPT_UNICODE
)
1030 /* TODO Checking for BIDI character: use S-CText fromutf8
1031 * TODO plus isrighttoleft (or whatever there will be)! */
1032 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1039 /* (Very very fuzzy, awaiting S-CText for good) */
1040 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1041 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1042 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1043 (c
>= 0x10800 && c
<= 0x10C48) ||
1044 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1049 #endif /* HAVE_NATCH_CHAR */
1055 bidi_info_create(struct bidi_info
*bip
)
1057 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1058 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1059 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1060 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1061 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1062 NATCH_CHAR( char const *hb
; )
1065 memset(bip
, 0, sizeof *bip
);
1066 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1068 #ifdef HAVE_NATCH_CHAR
1069 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1075 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1081 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1082 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1085 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1092 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1099 assert(inlen
== 0 || inbuf
!= NULL
);
1101 if (inlen
== UIZ_MAX
)
1102 inlen
= strlen(inbuf
);
1105 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1107 if ((inlen
== 1 && *inbuf
== '1') ||
1108 !ascncasecmp(inbuf
, "true", inlen
) ||
1109 !ascncasecmp(inbuf
, "yes", inlen
) ||
1110 !ascncasecmp(inbuf
, "on", inlen
))
1112 else if ((inlen
== 1 && *inbuf
== '0') ||
1113 !ascncasecmp(inbuf
, "false", inlen
) ||
1114 !ascncasecmp(inbuf
, "no", inlen
) ||
1115 !ascncasecmp(inbuf
, "off", inlen
))
1118 dat
= ac_alloc(inlen
+1);
1119 memcpy(dat
, inbuf
, inlen
);
1122 sli
= strtol(dat
, &eptr
, 0);
1123 if (*dat
!= '\0' && *eptr
== '\0')
1136 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1141 assert(inlen
== 0 || inbuf
!= NULL
);
1143 if (inlen
== UIZ_MAX
)
1144 inlen
= strlen(inbuf
);
1147 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1148 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1149 !ascncasecmp(inbuf
, "ask-", 4) &&
1150 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1151 (options
& OPT_INTERACTIVE
))
1152 rv
= getapproval(prompt
, rv
);
1158 n_is_all_or_aster(char const *name
){
1162 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1170 #ifdef HAVE_CLOCK_GETTIME
1172 #elif defined HAVE_GETTIMEOFDAY
1178 #ifdef HAVE_CLOCK_GETTIME
1179 clock_gettime(CLOCK_REALTIME
, &ts
);
1180 rv
= (time_t)ts
.tv_sec
;
1181 #elif defined HAVE_GETTIMEOFDAY
1182 gettimeofday(&ts
, NULL
);
1183 rv
= (time_t)ts
.tv_sec
;
1192 time_current_update(struct time_current
*tc
, bool_t full_update
)
1195 tc
->tc_time
= n_time_epoch();
1197 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1198 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1199 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1205 n_msleep(uiz_t millis
, bool_t ignint
){
1209 #ifdef HAVE_NANOSLEEP
1211 struct timespec ts
, trem
;
1214 ts
.tv_sec
= millis
/ 1000;
1215 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1217 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1219 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1222 #elif defined HAVE_SLEEP
1223 if((millis
/= 1000) == 0)
1225 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1228 # error Configuration should have detected a function for sleeping.
1236 n_err(char const *format
, ...){
1240 va_start(ap
, format
);
1242 if(options
& OPT_INTERACTIVE
)
1247 if(a_aux_err_dirty
++ == 0)
1248 fputs(UAGENT
": ", stderr
);
1249 vfprintf(stderr
, format
, ap
);
1250 if(strchr(format
, '\n') != NULL
){ /* TODO */
1251 a_aux_err_dirty
= 0;
1260 n_verr(char const *format
, va_list ap
){
1261 /* Check use cases of PS_ERRORS_NOTED, too! */
1263 char buf
[LINESIZE
], *xbuf
;
1265 struct a_aux_err_node
*enp
;
1267 LCTA(ERRORS_MAX
> 3);
1271 if(a_aux_err_dirty
++ == 0)
1272 fputs(UAGENT
": ", stderr
);
1275 if(!(options
& OPT_INTERACTIVE
))
1278 vfprintf(stderr
, format
, ap
);
1286 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1289 if (UICMP(z
, l
, >=, lmax
)) {
1290 /* FIXME Cannot reuse va_list
1292 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1297 fwrite(xbuf
, 1, l
, stderr
);
1299 /* Link it into the `errors' message ring */
1300 if((enp
= a_aux_err_tail
) == NULL
){
1302 enp
= scalloc(1, sizeof *enp
);
1303 if(a_aux_err_tail
!= NULL
)
1304 a_aux_err_tail
->ae_next
= enp
;
1306 a_aux_err_head
= enp
;
1307 a_aux_err_tail
= enp
;
1309 }else if(enp
->ae_str
.l
> 0 && enp
->ae_str
.s
[enp
->ae_str
.l
- 1] == '\n'){
1310 if(a_aux_err_cnt
< ERRORS_MAX
)
1313 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1314 a_aux_err_tail
->ae_next
= enp
;
1315 a_aux_err_tail
= enp
;
1316 free(enp
->ae_str
.s
);
1317 memset(enp
, 0, sizeof *enp
);
1320 n_str_add_buf(&enp
->ae_str
, xbuf
, l
);
1324 #endif /* HAVE_ERRORS */
1327 /* If the format ends with newline, be clean again */
1329 size_t i
= strlen(format
);
1331 if(i
> 0 && format
[i
- 1] == '\n'){
1333 a_aux_err_dirty
= 0;
1340 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1344 va_start(ap
, format
);
1345 vfprintf(stderr
, format
, ap
);
1351 n_perr(char const *msg
, int errval
){
1364 n_err(fmt
, msg
, strerror(errval
));
1369 n_alert(char const *format
, ...){
1373 n_err(a_aux_err_dirty
> 0 ? _("\nAlert: ") : _("Alert: "));
1375 va_start(ap
, format
);
1384 n_panic(char const *format
, ...){
1388 if(a_aux_err_dirty
> 0){
1390 a_aux_err_dirty
= 0;
1392 fprintf(stderr
, UAGENT
": Panic: ");
1394 va_start(ap
, format
);
1395 vfprintf(stderr
, format
, ap
);
1401 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1408 struct a_aux_err_node
*enp
;
1415 if(!asccasecmp(*argv
, "show"))
1417 if(!asccasecmp(*argv
, "clear"))
1420 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1424 return v
== NULL
? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1430 if(a_aux_err_head
== NULL
){
1431 fprintf(stderr
, _("The error ring is empty\n"));
1435 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1437 fprintf(stderr
, _("tmpfile"));
1442 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1443 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
1444 ++i
, enp
->ae_str
.l
, enp
->ae_str
.s
);
1445 /* We don't know wether last string ended with NL; be simple */
1448 page_or_print(fp
, 0);
1454 a_aux_err_tail
= NULL
;
1455 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1456 while((enp
= a_aux_err_head
) != NULL
){
1457 a_aux_err_head
= enp
->ae_next
;
1458 free(enp
->ae_str
.s
);
1463 #endif /* HAVE_ERRORS */