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 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
42 #include <sys/utsname.h>
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
52 #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
= n_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
% n_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
] % n_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)
184 s
= (ul_i
)scrnheight
;
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') >= n_NELEM(atoi16
) ||
355 (i2
= (ui8_t
)hex
[1] - '0') >= n_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
);
405 static ui32_t
const primes
[] = {
406 5, 11, 23, 47, 97, 157, 283,
407 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
408 131071, 262139, 524287, 1048573, 2097143, 4194301,
409 8388593, 16777213, 33554393, 67108859, 134217689,
410 268435399, 536870909, 1073741789, 2147483647
416 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
417 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
418 : n_NELEM(primes
) / 2));
420 if ((mprime
= primes
[i
]) > n
)
422 while (++i
< n_NELEM(primes
));
423 if (i
== n_NELEM(primes
) && mprime
< n
)
430 n_getdeadletter(void){
431 char const *cp_base
, *cp
;
436 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
437 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
439 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
440 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
444 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
445 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
453 nodename(int mayoverride
)
455 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
460 # ifdef HAVE_GETADDRINFO
461 struct addrinfo hints
, *res
;
463 struct hostent
*hent
;
468 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
470 } else if ((hn
= sys_hostname
) == NULL
) {
474 # ifdef HAVE_GETADDRINFO
475 memset(&hints
, 0, sizeof hints
);
476 hints
.ai_family
= AF_UNSPEC
;
477 hints
.ai_flags
= AI_CANONNAME
;
478 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
479 if (res
->ai_canonname
!= NULL
) {
480 size_t l
= strlen(res
->ai_canonname
) +1;
483 memcpy(hn
, res
->ai_canonname
, l
);
488 hent
= gethostbyname(hn
);
493 sys_hostname
= sstrdup(hn
);
494 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
495 if (hn
!= ut
.nodename
)
501 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
503 hostname
= sstrdup(hn
);
509 getrandstring(size_t length
)
516 #ifndef HAVE_POSIX_RANDOM
521 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
522 * with PAD stripped is still longer than what the user requests, easy way */
523 data
= ac_alloc(i
= length
+ 3);
525 #ifndef HAVE_POSIX_RANDOM
527 data
[i
] = (char)_rand_get8();
532 union {ui32_t i4
; char c
[4];} r
;
535 r
.i4
= (ui32_t
)arc4random();
536 switch ((j
= i
& 3)) {
537 case 0: cp
[3] = r
.c
[3]; j
= 4;
538 case 3: cp
[2] = r
.c
[2];
539 case 2: cp
[1] = r
.c
[1];
540 default: cp
[0] = r
.c
[0]; break;
548 assert(length
+ 3 < UIZ_MAX
/ 4);
549 b64_encode_buf(&b64
, data
, length
+ 3,
550 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
553 assert(b64
.l
>= length
);
554 b64
.s
[length
] = '\0';
560 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
567 assert(inlen
== 0 || inbuf
!= NULL
);
569 if (inlen
== UIZ_MAX
)
570 inlen
= strlen(inbuf
);
573 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
575 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
576 !ascncasecmp(inbuf
, "true", inlen
) ||
577 !ascncasecmp(inbuf
, "yes", inlen
) ||
578 !ascncasecmp(inbuf
, "on", inlen
))
580 else if ((inlen
== 1 &&
581 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
582 !ascncasecmp(inbuf
, "false", inlen
) ||
583 !ascncasecmp(inbuf
, "no", inlen
) ||
584 !ascncasecmp(inbuf
, "off", inlen
))
587 dat
= ac_alloc(inlen
+1);
588 memcpy(dat
, inbuf
, inlen
);
591 sli
= strtol(dat
, &eptr
, 0);
592 if (*dat
!= '\0' && *eptr
== '\0')
605 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
610 assert(inlen
== 0 || inbuf
!= NULL
);
612 if (inlen
== UIZ_MAX
)
613 inlen
= strlen(inbuf
);
616 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
617 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
618 !ascncasecmp(inbuf
, "ask-", 4) &&
619 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
620 (options
& OPT_INTERACTIVE
))
621 rv
= getapproval(prompt
, rv
);
627 n_is_all_or_aster(char const *name
){
631 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
639 #ifdef HAVE_CLOCK_GETTIME
641 #elif defined HAVE_GETTIMEOFDAY
648 if((cp
= ok_vlook(SOURCE_DATE_EPOCH
)) != NULL
){ /* TODO */
649 /* TODO This is marked "posnum", b and therefore 0<=X<=UINT_MAX.
650 * TODO This means we have a Sun, 07 Feb 2106 07:28:15 +0100 problem.
651 * TODO Therefore we need a num_ui64= type in v15 */
652 rv
= (time_t)strtoul(cp
, NULL
, 0);
654 #ifdef HAVE_CLOCK_GETTIME
655 clock_gettime(CLOCK_REALTIME
, &ts
);
656 rv
= (time_t)ts
.tv_sec
;
657 #elif defined HAVE_GETTIMEOFDAY
658 gettimeofday(&ts
, NULL
);
659 rv
= (time_t)ts
.tv_sec
;
669 time_current_update(struct time_current
*tc
, bool_t full_update
)
672 tc
->tc_time
= n_time_epoch();
674 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
675 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
676 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
682 n_msleep(uiz_t millis
, bool_t ignint
){
686 #ifdef HAVE_NANOSLEEP
688 struct timespec ts
, trem
;
691 ts
.tv_sec
= millis
/ 1000;
692 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
694 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
696 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
699 #elif defined HAVE_SLEEP
700 if((millis
/= 1000) == 0)
702 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
705 # error Configuration should have detected a function for sleeping.
713 n_err(char const *format
, ...){
717 va_start(ap
, format
);
719 if(options
& OPT_INTERACTIVE
)
725 bool_t doname
, doflush
;
728 while(*format
== '\n'){
734 if((doname
= doflush
))
735 a_aux_err_linelen
= 0;
737 if((len
= strlen(format
)) > 0){
738 if(doname
|| a_aux_err_linelen
== 0)
739 fputs(VAL_UAGENT
": ", stderr
);
740 vfprintf(stderr
, format
, ap
);
745 if(format
[--len
] == '\n'){
746 a_aux_err_linelen
= (i
-= ++len
);
762 n_verr(char const *format
, va_list ap
){
763 /* Check use cases of PS_ERRORS_NOTED, too! */
765 struct a_aux_err_node
*enp
;
767 bool_t doname
, doflush
;
772 while(*format
== '\n'){
778 if((doname
= doflush
)){
779 a_aux_err_linelen
= 0;
781 if(options
& OPT_INTERACTIVE
){
782 if((enp
= a_aux_err_tail
) != NULL
&&
783 (enp
->ae_str
.s_len
> 0 &&
784 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
785 n_string_push_c(&enp
->ae_str
, '\n');
790 if((len
= strlen(format
)) == 0)
793 pstate
|= PS_ERRORS_PROMPT
;
796 if(doname
|| a_aux_err_linelen
== 0)
797 fputs(VAL_UAGENT
": ", stderr
);
802 if(format
[--len
] == '\n'){
803 a_aux_err_linelen
= (i
-= ++len
);
811 if(!(options
& OPT_INTERACTIVE
))
813 vfprintf(stderr
, format
, ap
);
817 n_LCTAV(ERRORS_MAX
> 3);
819 /* Link it into the `errors' message ring */
820 if((enp
= a_aux_err_tail
) == NULL
){
822 enp
= smalloc(sizeof *enp
);
824 n_string_creat(&enp
->ae_str
);
825 if(a_aux_err_tail
!= NULL
)
826 a_aux_err_tail
->ae_next
= enp
;
828 a_aux_err_head
= enp
;
829 a_aux_err_tail
= enp
;
832 (enp
->ae_str
.s_len
> 0 &&
833 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
834 if(a_aux_err_cnt
< ERRORS_MAX
)
837 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
838 a_aux_err_tail
->ae_next
= enp
;
839 a_aux_err_tail
= enp
;
841 n_string_trunc(&enp
->ae_str
, 0);
844 # ifdef HAVE_N_VA_COPY
847 imax
= n_MIN(LINESIZE
, 1024);
849 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
850 # ifdef HAVE_N_VA_COPY
858 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
859 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
860 # ifdef HAVE_N_VA_COPY
867 if(UICMP(z
, i
, >=, imax
)){
868 # ifdef HAVE_N_VA_COPY
869 /* XXX Check overflow for upcoming LEN+++i! */
870 n_string_trunc(&enp
->ae_str
, len
);
873 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
878 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
880 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, stderr
);
882 #endif /* HAVE_ERRORS */
891 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
895 va_start(ap
, format
);
896 vfprintf(stderr
, format
, ap
);
902 n_perr(char const *msg
, int errval
){
915 n_err(fmt
, msg
, strerror(errval
));
920 n_alert(char const *format
, ...){
924 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
926 va_start(ap
, format
);
935 n_panic(char const *format
, ...){
939 if(a_aux_err_linelen
> 0){
941 a_aux_err_linelen
= 0;
943 fprintf(stderr
, VAL_UAGENT
": Panic: ");
945 va_start(ap
, format
);
946 vfprintf(stderr
, format
, ap
);
952 abort(); /* Was exit(EXIT_ERR); for a while, but no */
959 struct a_aux_err_node
*enp
;
966 if(!asccasecmp(*argv
, "show"))
968 if(!asccasecmp(*argv
, "clear"))
971 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
975 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
981 if(a_aux_err_head
== NULL
){
982 fprintf(stderr
, _("The error ring is empty\n"));
986 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
988 fprintf(stderr
, _("tmpfile"));
993 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
994 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
995 /* We don't know whether last string ended with NL; be simple XXX */
998 page_or_print(fp
, 0);
1004 a_aux_err_tail
= NULL
;
1005 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1006 a_aux_err_linelen
= 0;
1007 while((enp
= a_aux_err_head
) != NULL
){
1008 a_aux_err_head
= enp
->ae_next
;
1009 n_string_gut(&enp
->ae_str
);
1014 #endif /* HAVE_ERRORS */