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>
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
52 #ifndef HAVE_POSIX_RANDOM
60 ui8_t b8
[sizeof(struct rand_arc4
)];
61 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
66 struct a_aux_err_node
{
67 struct a_aux_err_node
*ae_next
;
68 struct n_string ae_str
;
72 #ifndef HAVE_POSIX_RANDOM
73 static union rand_state
*_rand
;
76 /* Error ring, for `errors' */
78 static struct a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
79 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
81 static size_t a_aux_err_linelen
;
83 /* Our ARC4 random generator with its completely unacademical pseudo
84 * initialization (shall /dev/urandom fail) */
85 #ifndef HAVE_POSIX_RANDOM
86 static void _rand_init(void);
87 static ui32_t
_rand_weak(ui32_t seed
);
88 SINLINE ui8_t
_rand_get8(void);
91 #ifndef HAVE_POSIX_RANDOM
95 # ifdef HAVE_CLOCK_GETTIME
100 union {int fd
; size_t i
;} u
;
104 _rand
= smalloc(sizeof *_rand
);
106 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
107 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
114 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
115 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
118 # ifdef HAVE_CLOCK_GETTIME
119 clock_gettime(CLOCK_REALTIME
, &ts
);
120 t
= (ui32_t
)ts
.tv_nsec
;
122 gettimeofday(&ts
, NULL
);
123 t
= (ui32_t
)ts
.tv_usec
;
126 t
= (t
>> 16) | (t
<< 16);
127 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
128 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
129 if (rnd
== 7 || rnd
== 17)
130 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
131 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
132 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
133 seed
^= _rand_weak(_rand
->b32
[k
]);
135 seed
^= nextprime(seed
);
139 for (u
.i
= 5 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
146 _rand_weak(ui32_t seed
)
148 /* From "Random number generators: good ones are hard to find",
149 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
150 * October 1988, p. 1195.
151 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
158 seed
= (seed
* 16807) - (hi
* 2836);
159 if ((si32_t
)seed
< 0)
169 si
= _rand
->a
._dat
[++_rand
->a
._i
];
170 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
171 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
172 _rand
->a
._dat
[_rand
->a
._j
] = si
;
173 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
175 #endif /* HAVE_POSIX_RANDOM */
183 if((cp
= ok_vlook(screen
)) == NULL
|| (s
= strtoul(cp
, NULL
, 0)) == 0)
185 s
-= 2; /* XXX no magics */
186 if(s
> INT_MAX
) /* TODO function should return unsigned */
193 n_pager_get(char const **env_addon
){
197 rv
= ok_vlook(PAGER
);
199 if(env_addon
!= NULL
){
201 /* Update the manual upon any changes:
202 * *colour-pager*, $PAGER */
203 if(strstr(rv
, "less") != NULL
){
204 if(getenv("LESS") == NULL
)
207 (pstate
& PS_TERMCAP_CA_MODE
) ? "LESS=Ri"
208 : !(pstate
& PS_TERMCAP_DISABLE
) ? "LESS=FRi" :
211 }else if(strstr(rv
, "lv") != NULL
){
212 if(getenv("LV") == NULL
)
213 *env_addon
= "LV=-c";
221 page_or_print(FILE *fp
, size_t lines
)
229 if (n_source_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
232 rows
= (*cp
== '\0') ? (size_t)scrnheight
: strtoul(cp
, NULL
, 0);
234 if (rows
> 0 && lines
== 0) {
235 while ((c
= getc(fp
)) != EOF
)
236 if (c
== '\n' && ++lines
>= rows
)
242 char const *env_add
[2], *pager
;
244 pager
= n_pager_get(&env_add
[0]);
246 run_command(pager
, NULL
, fileno(fp
), COMMAND_FD_PASS
, NULL
,NULL
,NULL
,
252 while ((c
= getc(fp
)) != EOF
)
259 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
265 enum protocol rv
= PROTO_UNKNOWN
;
268 temporary_protocol_ext
= NULL
;
270 if (name
[0] == '%' && name
[1] == ':')
272 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
276 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
277 if (!strncmp(name
, "pop3://", 7)) {
281 n_err(_("No POP3 support compiled in\n"));
283 } else if (!strncmp(name
, "pop3s://", 8)) {
284 #if defined HAVE_POP3 && defined HAVE_SSL
288 n_err(_("No POP3 support compiled in\n"));
291 n_err(_("No SSL support compiled in\n"));
298 /* TODO This is the de facto maildir code and thus belongs into there!
299 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
300 * TODO or (more likely) in addition to *newfolders*) */
303 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
304 memcpy(np
, name
, sz
+ 1);
305 if (!stat(name
, &st
)) {
306 if (S_ISDIR(st
.st_mode
) &&
307 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
308 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
309 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
312 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
313 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
314 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
315 temporary_protocol_ext
= cp
;
316 else if ((cp
= ok_vlook(newfolders
)) != NULL
&&
317 !asccasecmp(cp
, "maildir"))
327 n_c_to_hex_base16(char store
[3], char c
){
328 static char const itoa16
[] = "0123456789ABCDEF";
332 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
333 c
= ((ui8_t
)c
>> 4) & 0x0F;
334 store
[0] = itoa16
[(ui8_t
)c
];
340 n_c_from_hex_base16(char const hex
[2]){
341 static ui8_t
const atoi16
[] = {
342 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
343 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
344 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
345 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
346 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
347 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
348 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
354 if ((i1
= (ui8_t
)hex
[0] - '0') >= NELEM(atoi16
) ||
355 (i2
= (ui8_t
)hex
[1] - '0') >= NELEM(atoi16
))
359 if ((i1
| i2
) & 0xF0u
)
373 torek_hash(char const *name
)
375 /* Chris Torek's hash.
376 * NOTE: need to change *at least* mk-okey-map.pl when changing the
381 while (*name
!= '\0') {
390 torek_ihashn(char const *dat
, size_t len
){
391 /* See torek_hash() */
396 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
397 h
= (h
* 33) + lowerconv(c
);
403 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
410 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
411 if ((g
= h
& 0xf0000000) != 0) {
423 static ui32_t
const primes
[] = {
424 5, 11, 23, 47, 97, 157, 283,
425 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
426 131071, 262139, 524287, 1048573, 2097143, 4194301,
427 8388593, 16777213, 33554393, 67108859, 134217689,
428 268435399, 536870909, 1073741789, 2147483647
434 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
435 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
436 : NELEM(primes
) / 2));
438 if ((mprime
= primes
[i
]) > n
)
440 while (++i
< NELEM(primes
));
441 if (i
== NELEM(primes
) && mprime
< n
)
448 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
449 { /* FIXME getprompt must mb->wc->mb+reset seq! */
450 static char buf
[PROMPT_BUFFER_SIZE
];
453 char const *ccp_base
, *ccp
;
454 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
455 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
458 /* No other place to place this */
460 if (options
& OPT_INTERACTIVE
) {
461 if (!(pstate
& PS_ERRORS_NOTED
) && a_aux_err_head
!= NULL
) {
462 pstate
|= PS_ERRORS_NOTED
;
463 fprintf(stderr
, _("There are new messages in the error message ring "
464 "(denoted by \"#ERR#\")\n"
465 " The `errors' command manages this message ring\n"));
468 if ((trigger
= (a_aux_err_cnt_noted
!= a_aux_err_cnt
)))
469 a_aux_err_cnt_noted
= a_aux_err_cnt
;
475 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
485 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
487 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
489 dfmaxlen
= 0; /* keep CC happy */
493 NATCH_CHAR( cclen
= cclen_base
; )
494 maxlen
= sizeof(buf
) -1;
502 #ifdef HAVE_NATCH_CHAR
503 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
512 } else if ((l
= c
) > 1) {
522 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
528 if (c
== 0 || c
== PROMPT_STOP
)
532 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
535 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
555 nodename(int mayoverride
)
557 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
562 # ifdef HAVE_GETADDRINFO
563 struct addrinfo hints
, *res
;
565 struct hostent
*hent
;
570 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
572 } else if ((hn
= sys_hostname
) == NULL
) {
576 # ifdef HAVE_GETADDRINFO
577 memset(&hints
, 0, sizeof hints
);
578 hints
.ai_family
= AF_UNSPEC
;
579 hints
.ai_flags
= AI_CANONNAME
;
580 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
581 if (res
->ai_canonname
!= NULL
) {
582 size_t l
= strlen(res
->ai_canonname
) +1;
585 memcpy(hn
, res
->ai_canonname
, l
);
590 hent
= gethostbyname(hn
);
595 sys_hostname
= sstrdup(hn
);
596 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
597 if (hn
!= ut
.nodename
)
603 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
605 hostname
= sstrdup(hn
);
611 getrandstring(size_t length
)
618 #ifndef HAVE_POSIX_RANDOM
623 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
624 * with PAD stripped is still longer than what the user requests, easy way */
625 data
= ac_alloc(i
= length
+ 3);
627 #ifndef HAVE_POSIX_RANDOM
629 data
[i
] = (char)_rand_get8();
634 union {ui32_t i4
; char c
[4];} r
;
637 r
.i4
= (ui32_t
)arc4random();
638 switch ((j
= i
& 3)) {
639 case 0: cp
[3] = r
.c
[3]; j
= 4;
640 case 3: cp
[2] = r
.c
[2];
641 case 2: cp
[1] = r
.c
[1];
642 default: cp
[0] = r
.c
[0]; break;
650 b64_encode_buf(&b64
, data
, length
+ 3,
651 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
654 assert(b64
.l
>= length
);
655 b64
.s
[length
] = '\0';
661 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
668 assert(inlen
== 0 || inbuf
!= NULL
);
670 if (inlen
== UIZ_MAX
)
671 inlen
= strlen(inbuf
);
674 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
676 if ((inlen
== 1 && *inbuf
== '1') ||
677 !ascncasecmp(inbuf
, "true", inlen
) ||
678 !ascncasecmp(inbuf
, "yes", inlen
) ||
679 !ascncasecmp(inbuf
, "on", inlen
))
681 else if ((inlen
== 1 && *inbuf
== '0') ||
682 !ascncasecmp(inbuf
, "false", inlen
) ||
683 !ascncasecmp(inbuf
, "no", inlen
) ||
684 !ascncasecmp(inbuf
, "off", inlen
))
687 dat
= ac_alloc(inlen
+1);
688 memcpy(dat
, inbuf
, inlen
);
691 sli
= strtol(dat
, &eptr
, 0);
692 if (*dat
!= '\0' && *eptr
== '\0')
705 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
710 assert(inlen
== 0 || inbuf
!= NULL
);
712 if (inlen
== UIZ_MAX
)
713 inlen
= strlen(inbuf
);
716 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
717 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
718 !ascncasecmp(inbuf
, "ask-", 4) &&
719 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
720 (options
& OPT_INTERACTIVE
))
721 rv
= getapproval(prompt
, rv
);
727 n_is_all_or_aster(char const *name
){
731 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
739 #ifdef HAVE_CLOCK_GETTIME
741 #elif defined HAVE_GETTIMEOFDAY
747 #ifdef HAVE_CLOCK_GETTIME
748 clock_gettime(CLOCK_REALTIME
, &ts
);
749 rv
= (time_t)ts
.tv_sec
;
750 #elif defined HAVE_GETTIMEOFDAY
751 gettimeofday(&ts
, NULL
);
752 rv
= (time_t)ts
.tv_sec
;
761 time_current_update(struct time_current
*tc
, bool_t full_update
)
764 tc
->tc_time
= n_time_epoch();
766 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
767 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
768 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
774 n_msleep(uiz_t millis
, bool_t ignint
){
778 #ifdef HAVE_NANOSLEEP
780 struct timespec ts
, trem
;
783 ts
.tv_sec
= millis
/ 1000;
784 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
786 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
788 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
791 #elif defined HAVE_SLEEP
792 if((millis
/= 1000) == 0)
794 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
797 # error Configuration should have detected a function for sleeping.
805 n_err(char const *format
, ...){
809 va_start(ap
, format
);
811 if(options
& OPT_INTERACTIVE
)
817 bool_t doname
, doflush
;
820 while(*format
== '\n'){
826 if((doname
= doflush
))
827 a_aux_err_linelen
= 0;
829 if((len
= strlen(format
)) > 0){
830 if(doname
|| a_aux_err_linelen
== 0)
831 fputs(UAGENT
": ", stderr
);
832 vfprintf(stderr
, format
, ap
);
837 if(format
[--len
] == '\n'){
838 a_aux_err_linelen
= (i
-= ++len
);
854 n_verr(char const *format
, va_list ap
){
855 /* Check use cases of PS_ERRORS_NOTED, too! */
857 struct a_aux_err_node
*enp
;
859 bool_t doname
, doflush
;
864 while(*format
== '\n'){
870 if((doname
= doflush
)){
871 a_aux_err_linelen
= 0;
873 if(options
& OPT_INTERACTIVE
){
874 if((enp
= a_aux_err_tail
) != NULL
&&
875 (enp
->ae_str
.s_len
> 0 &&
876 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
877 n_string_push_c(&enp
->ae_str
, '\n');
882 if((len
= strlen(format
)) == 0)
885 if(doname
|| a_aux_err_linelen
== 0)
886 fputs(UAGENT
": ", stderr
);
891 if(format
[--len
] == '\n'){
892 a_aux_err_linelen
= (i
-= ++len
);
900 if(!(options
& OPT_INTERACTIVE
))
902 vfprintf(stderr
, format
, ap
);
906 LCTA(ERRORS_MAX
> 3);
908 /* Link it into the `errors' message ring */
909 if((enp
= a_aux_err_tail
) == NULL
){
911 enp
= smalloc(sizeof *enp
);
913 n_string_creat(&enp
->ae_str
);
914 if(a_aux_err_tail
!= NULL
)
915 a_aux_err_tail
->ae_next
= enp
;
917 a_aux_err_head
= enp
;
918 a_aux_err_tail
= enp
;
921 (enp
->ae_str
.s_len
> 0 &&
922 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
923 if(a_aux_err_cnt
< ERRORS_MAX
)
926 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
927 a_aux_err_tail
->ae_next
= enp
;
928 a_aux_err_tail
= enp
;
930 n_string_trunc(&enp
->ae_str
, 0);
938 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
946 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
947 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
955 if(UICMP(z
, i
, >=, imax
)){
957 /* XXX Check overflow for upcoming LEN+++i! */
958 n_string_trunc(&enp
->ae_str
, len
);
961 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
966 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
968 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, stderr
);
970 #endif /* HAVE_ERRORS */
979 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
983 va_start(ap
, format
);
984 vfprintf(stderr
, format
, ap
);
990 n_perr(char const *msg
, int errval
){
1003 n_err(fmt
, msg
, strerror(errval
));
1008 n_alert(char const *format
, ...){
1012 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
1014 va_start(ap
, format
);
1023 n_panic(char const *format
, ...){
1027 if(a_aux_err_linelen
> 0){
1029 a_aux_err_linelen
= 0;
1031 fprintf(stderr
, UAGENT
": Panic: ");
1033 va_start(ap
, format
);
1034 vfprintf(stderr
, format
, ap
);
1040 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1047 struct a_aux_err_node
*enp
;
1054 if(!asccasecmp(*argv
, "show"))
1056 if(!asccasecmp(*argv
, "clear"))
1059 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1063 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1069 if(a_aux_err_head
== NULL
){
1070 fprintf(stderr
, _("The error ring is empty\n"));
1074 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1076 fprintf(stderr
, _("tmpfile"));
1081 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1082 fprintf(fp
, "%4" PRIuZ
". %u B: %s",
1083 ++i
, enp
->ae_str
.s_len
, n_string_cp(&enp
->ae_str
));
1084 /* We don't know wether last string ended with NL; be simple */
1087 page_or_print(fp
, 0);
1093 a_aux_err_tail
= NULL
;
1094 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1095 a_aux_err_linelen
= 0;
1096 while((enp
= a_aux_err_head
) != NULL
){
1097 a_aux_err_head
= enp
->ae_next
;
1098 n_string_gut(&enp
->ae_str
);
1103 #endif /* HAVE_ERRORS */