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
);
112 , O_RDONLY
)) != -1) {
113 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
120 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
121 for (u
.i
= n_NELEM(_rand
->b32
); u
.i
-- != 0;) {
124 # ifdef HAVE_CLOCK_GETTIME
125 clock_gettime(CLOCK_REALTIME
, &ts
);
126 t
= (ui32_t
)ts
.tv_nsec
;
128 gettimeofday(&ts
, NULL
);
129 t
= (ui32_t
)ts
.tv_usec
;
132 t
= (t
>> 16) | (t
<< 16);
133 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
134 _rand
->b32
[t
% n_NELEM(_rand
->b32
)] ^= seed
;
135 if (rnd
== 7 || rnd
== 17)
136 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
137 k
= _rand
->b32
[u
.i
] % n_NELEM(_rand
->b32
);
138 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
139 seed
^= _rand_weak(_rand
->b32
[k
]);
141 seed
^= nextprime(seed
);
145 for (u
.i
= 5 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
152 _rand_weak(ui32_t seed
)
154 /* From "Random number generators: good ones are hard to find",
155 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
156 * October 1988, p. 1195.
157 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
164 seed
= (seed
* 16807) - (hi
* 2836);
165 if ((si32_t
)seed
< 0)
175 si
= _rand
->a
._dat
[++_rand
->a
._i
];
176 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
177 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
178 _rand
->a
._dat
[_rand
->a
._j
] = si
;
179 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
181 #endif /* HAVE_POSIX_RANDOM */
189 if((cp
= ok_vlook(screen
)) == NULL
|| (s
= strtoul(cp
, NULL
, 0)) == 0)
190 s
= (ul_i
)scrnheight
;
191 s
-= 2; /* XXX no magics */
192 if(s
> INT_MAX
) /* TODO function should return unsigned */
199 n_pager_get(char const **env_addon
){
203 rv
= ok_vlook(PAGER
);
205 if(env_addon
!= NULL
){
207 /* Update the manual upon any changes:
208 * *colour-pager*, $PAGER */
209 if(strstr(rv
, "less") != NULL
){
210 if(getenv("LESS") == NULL
)
213 (pstate
& PS_TERMCAP_CA_MODE
) ? "LESS=Ri"
214 : !(pstate
& PS_TERMCAP_DISABLE
) ? "LESS=FRi" :
217 }else if(strstr(rv
, "lv") != NULL
){
218 if(getenv("LV") == NULL
)
219 *env_addon
= "LV=-c";
227 page_or_print(FILE *fp
, size_t lines
)
235 if (n_source_may_yield_control() && (cp
= ok_vlook(crt
)) != NULL
) {
238 rows
= (*cp
== '\0') ? (size_t)scrnheight
: strtoul(cp
, NULL
, 0);
240 if (rows
> 0 && lines
== 0) {
241 while ((c
= getc(fp
)) != EOF
)
242 if (c
== '\n' && ++lines
>= rows
)
248 char const *env_add
[2], *pager
;
250 pager
= n_pager_get(&env_add
[0]);
252 run_command(pager
, NULL
, fileno(fp
), COMMAND_FD_PASS
, NULL
,NULL
,NULL
,
258 while ((c
= getc(fp
)) != EOF
)
265 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
271 enum protocol rv
= PROTO_UNKNOWN
;
274 temporary_protocol_ext
= NULL
;
276 if (name
[0] == '%' && name
[1] == ':')
278 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
282 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
283 if (!strncmp(name
, "pop3://", 7)) {
287 n_err(_("No POP3 support compiled in\n"));
289 } else if (!strncmp(name
, "pop3s://", 8)) {
290 #if defined HAVE_POP3 && defined HAVE_SSL
294 n_err(_("No POP3 support compiled in\n"));
297 n_err(_("No SSL support compiled in\n"));
304 /* TODO This is the de facto maildir code and thus belongs into there!
305 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
306 * TODO or (more likely) in addition to *newfolders*) */
309 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
310 memcpy(np
, name
, sz
+ 1);
311 if (!stat(name
, &st
)) {
312 if (S_ISDIR(st
.st_mode
) &&
313 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
314 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
315 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
318 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
319 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
320 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
321 temporary_protocol_ext
= cp
;
322 else if ((cp
= ok_vlook(newfolders
)) != NULL
&&
323 !asccasecmp(cp
, "maildir"))
333 n_c_to_hex_base16(char store
[3], char c
){
334 static char const itoa16
[] = "0123456789ABCDEF";
338 store
[1] = itoa16
[(ui8_t
)c
& 0x0F];
339 c
= ((ui8_t
)c
>> 4) & 0x0F;
340 store
[0] = itoa16
[(ui8_t
)c
];
346 n_c_from_hex_base16(char const hex
[2]){
347 static ui8_t
const atoi16
[] = {
348 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
349 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
350 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
351 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
352 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
353 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
354 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
360 if ((i1
= (ui8_t
)hex
[0] - '0') >= n_NELEM(atoi16
) ||
361 (i2
= (ui8_t
)hex
[1] - '0') >= n_NELEM(atoi16
))
365 if ((i1
| i2
) & 0xF0u
)
379 torek_hash(char const *name
)
381 /* Chris Torek's hash.
382 * NOTE: need to change *at least* mk-okey-map.pl when changing the
387 while (*name
!= '\0') {
396 torek_ihashn(char const *dat
, size_t len
){
397 /* See torek_hash() */
402 for(h
= 0; len
> 0 && (c
= *dat
++) != '\0'; --len
)
403 h
= (h
* 33) + lowerconv(c
);
411 static ui32_t
const primes
[] = {
412 5, 11, 23, 47, 97, 157, 283,
413 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
414 131071, 262139, 524287, 1048573, 2097143, 4194301,
415 8388593, 16777213, 33554393, 67108859, 134217689,
416 268435399, 536870909, 1073741789, 2147483647
422 i
= (n
< primes
[n_NELEM(primes
) / 4] ? 0
423 : (n
< primes
[n_NELEM(primes
) / 2] ? n_NELEM(primes
) / 4
424 : n_NELEM(primes
) / 2));
426 if ((mprime
= primes
[i
]) > n
)
428 while (++i
< n_NELEM(primes
));
429 if (i
== n_NELEM(primes
) && mprime
< n
)
436 n_getdeadletter(void){
437 char const *cp_base
, *cp
;
442 cp
= fexpand(ok_vlook(DEAD
), FEXP_LOCAL
| FEXP_NSHELL
);
443 if(cp
== NULL
|| strlen(cp
) >= PATH_MAX
){
445 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
446 VAL_DEAD
, n_shexp_quote_cp(cp
, FAL0
));
450 cp
= savecatsep(ok_vlook(TMPDIR
), '/', VAL_DEAD_BASENAME
);
451 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp
);
459 nodename(int mayoverride
)
461 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
466 # ifdef HAVE_GETADDRINFO
467 struct addrinfo hints
, *res
;
469 struct hostent
*hent
;
474 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
476 } else if ((hn
= sys_hostname
) == NULL
) {
480 # ifdef HAVE_GETADDRINFO
481 memset(&hints
, 0, sizeof hints
);
482 hints
.ai_family
= AF_UNSPEC
;
483 hints
.ai_flags
= AI_CANONNAME
;
484 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
485 if (res
->ai_canonname
!= NULL
) {
486 size_t l
= strlen(res
->ai_canonname
) +1;
489 memcpy(hn
, res
->ai_canonname
, l
);
494 hent
= gethostbyname(hn
);
499 sys_hostname
= sstrdup(hn
);
500 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
501 if (hn
!= ut
.nodename
)
507 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
509 hostname
= sstrdup(hn
);
515 getrandstring(size_t length
)
522 #ifndef HAVE_POSIX_RANDOM
527 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
528 * with PAD stripped is still longer than what the user requests, easy way */
529 data
= ac_alloc(i
= length
+ 3);
531 #ifndef HAVE_POSIX_RANDOM
533 data
[i
] = (char)_rand_get8();
538 union {ui32_t i4
; char c
[4];} r
;
541 r
.i4
= (ui32_t
)arc4random();
542 switch ((j
= i
& 3)) {
543 case 0: cp
[3] = r
.c
[3]; j
= 4;
544 case 3: cp
[2] = r
.c
[2];
545 case 2: cp
[1] = r
.c
[1];
546 default: cp
[0] = r
.c
[0]; break;
554 assert(length
+ 3 < UIZ_MAX
/ 4);
555 b64_encode_buf(&b64
, data
, length
+ 3,
556 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
559 assert(b64
.l
>= length
);
560 b64
.s
[length
] = '\0';
566 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
573 assert(inlen
== 0 || inbuf
!= NULL
);
575 if (inlen
== UIZ_MAX
)
576 inlen
= strlen(inbuf
);
579 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
581 if ((inlen
== 1 && (*inbuf
== '1' || *inbuf
== 'y' || *inbuf
== 'Y')) ||
582 !ascncasecmp(inbuf
, "true", inlen
) ||
583 !ascncasecmp(inbuf
, "yes", inlen
) ||
584 !ascncasecmp(inbuf
, "on", inlen
))
586 else if ((inlen
== 1 &&
587 (*inbuf
== '0' || *inbuf
== 'n' || *inbuf
== 'N')) ||
588 !ascncasecmp(inbuf
, "false", inlen
) ||
589 !ascncasecmp(inbuf
, "no", inlen
) ||
590 !ascncasecmp(inbuf
, "off", inlen
))
593 dat
= ac_alloc(inlen
+1);
594 memcpy(dat
, inbuf
, inlen
);
597 sli
= strtol(dat
, &eptr
, 0);
598 if (*dat
!= '\0' && *eptr
== '\0')
611 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
616 assert(inlen
== 0 || inbuf
!= NULL
);
618 if (inlen
== UIZ_MAX
)
619 inlen
= strlen(inbuf
);
622 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
623 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
624 !ascncasecmp(inbuf
, "ask-", 4) &&
625 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
626 (options
& OPT_INTERACTIVE
))
627 rv
= getapproval(prompt
, rv
);
633 n_is_all_or_aster(char const *name
){
637 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
645 #ifdef HAVE_CLOCK_GETTIME
647 #elif defined HAVE_GETTIMEOFDAY
654 if((cp
= ok_vlook(SOURCE_DATE_EPOCH
)) != NULL
){ /* TODO */
655 /* TODO This is marked "posnum", b and therefore 0<=X<=UINT_MAX.
656 * TODO This means we have a Sun, 07 Feb 2106 07:28:15 +0100 problem.
657 * TODO Therefore we need a num_ui64= type in v15 */
658 rv
= (time_t)strtoul(cp
, NULL
, 0);
660 #ifdef HAVE_CLOCK_GETTIME
661 clock_gettime(CLOCK_REALTIME
, &ts
);
662 rv
= (time_t)ts
.tv_sec
;
663 #elif defined HAVE_GETTIMEOFDAY
664 gettimeofday(&ts
, NULL
);
665 rv
= (time_t)ts
.tv_sec
;
675 time_current_update(struct time_current
*tc
, bool_t full_update
)
678 tc
->tc_time
= n_time_epoch();
680 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
681 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
682 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
688 n_msleep(uiz_t millis
, bool_t ignint
){
692 #ifdef HAVE_NANOSLEEP
694 struct timespec ts
, trem
;
697 ts
.tv_sec
= millis
/ 1000;
698 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
700 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
702 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
705 #elif defined HAVE_SLEEP
706 if((millis
/= 1000) == 0)
708 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
711 # error Configuration should have detected a function for sleeping.
719 n_err(char const *format
, ...){
723 va_start(ap
, format
);
725 if(options
& OPT_INTERACTIVE
)
731 bool_t doname
, doflush
;
734 while(*format
== '\n'){
740 if((doname
= doflush
))
741 a_aux_err_linelen
= 0;
743 if((len
= strlen(format
)) > 0){
744 if(doname
|| a_aux_err_linelen
== 0)
745 fputs(VAL_UAGENT
": ", stderr
);
746 vfprintf(stderr
, format
, ap
);
751 if(format
[--len
] == '\n'){
752 a_aux_err_linelen
= (i
-= ++len
);
768 n_verr(char const *format
, va_list ap
){
769 /* Check use cases of PS_ERRORS_NOTED, too! */
771 struct a_aux_err_node
*enp
;
773 bool_t doname
, doflush
;
778 while(*format
== '\n'){
784 if((doname
= doflush
)){
785 a_aux_err_linelen
= 0;
787 if(options
& OPT_INTERACTIVE
){
788 if((enp
= a_aux_err_tail
) != NULL
&&
789 (enp
->ae_str
.s_len
> 0 &&
790 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] != '\n'))
791 n_string_push_c(&enp
->ae_str
, '\n');
796 if((len
= strlen(format
)) == 0)
799 pstate
|= PS_ERRORS_PROMPT
;
802 if(doname
|| a_aux_err_linelen
== 0)
803 fputs(VAL_UAGENT
": ", stderr
);
808 if(format
[--len
] == '\n'){
809 a_aux_err_linelen
= (i
-= ++len
);
817 if(!(options
& OPT_INTERACTIVE
))
819 vfprintf(stderr
, format
, ap
);
823 n_LCTAV(ERRORS_MAX
> 3);
825 /* Link it into the `errors' message ring */
826 if((enp
= a_aux_err_tail
) == NULL
){
828 enp
= smalloc(sizeof *enp
);
830 n_string_creat(&enp
->ae_str
);
831 if(a_aux_err_tail
!= NULL
)
832 a_aux_err_tail
->ae_next
= enp
;
834 a_aux_err_head
= enp
;
835 a_aux_err_tail
= enp
;
838 (enp
->ae_str
.s_len
> 0 &&
839 enp
->ae_str
.s_dat
[enp
->ae_str
.s_len
- 1] == '\n')){
840 if(a_aux_err_cnt
< ERRORS_MAX
)
843 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
844 a_aux_err_tail
->ae_next
= enp
;
845 a_aux_err_tail
= enp
;
847 n_string_trunc(&enp
->ae_str
, 0);
850 # ifdef HAVE_N_VA_COPY
853 imax
= n_MIN(LINESIZE
, 1024);
855 for(i
= imax
;; imax
= ++i
/* xxx could wrap, maybe */){
856 # ifdef HAVE_N_VA_COPY
864 n_string_resize(&enp
->ae_str
, (len
= enp
->ae_str
.s_len
) + (size_t)i
);
865 i
= vsnprintf(&enp
->ae_str
.s_dat
[len
], (size_t)i
, format
, vac
);
866 # ifdef HAVE_N_VA_COPY
873 if(UICMP(z
, i
, >=, imax
)){
874 # ifdef HAVE_N_VA_COPY
875 /* XXX Check overflow for upcoming LEN+++i! */
876 n_string_trunc(&enp
->ae_str
, len
);
879 i
= (int)strlen(&enp
->ae_str
.s_dat
[len
]);
884 n_string_trunc(&enp
->ae_str
, len
+ (size_t)i
);
886 fwrite(&enp
->ae_str
.s_dat
[len
], 1, (size_t)i
, stderr
);
888 #endif /* HAVE_ERRORS */
897 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
901 va_start(ap
, format
);
902 vfprintf(stderr
, format
, ap
);
908 n_perr(char const *msg
, int errval
){
921 n_err(fmt
, msg
, strerror(errval
));
926 n_alert(char const *format
, ...){
930 n_err(a_aux_err_linelen
> 0 ? _("\nAlert: ") : _("Alert: "));
932 va_start(ap
, format
);
941 n_panic(char const *format
, ...){
945 if(a_aux_err_linelen
> 0){
947 a_aux_err_linelen
= 0;
949 fprintf(stderr
, VAL_UAGENT
": Panic: ");
951 va_start(ap
, format
);
952 vfprintf(stderr
, format
, ap
);
958 abort(); /* Was exit(EXIT_ERR); for a while, but no */
965 struct a_aux_err_node
*enp
;
972 if(!asccasecmp(*argv
, "show"))
974 if(!asccasecmp(*argv
, "clear"))
977 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
981 return (v
== NULL
) ? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
987 if(a_aux_err_head
== NULL
){
988 fprintf(stderr
, _("The error ring is empty\n"));
992 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
994 fprintf(stderr
, _("tmpfile"));
999 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1000 fprintf(fp
, "%4" PRIuZ
". %s", ++i
, n_string_cp(&enp
->ae_str
));
1001 /* We don't know whether last string ended with NL; be simple XXX */
1004 page_or_print(fp
, 0);
1010 a_aux_err_tail
= NULL
;
1011 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1012 a_aux_err_linelen
= 0;
1013 while((enp
= a_aux_err_head
) != NULL
){
1014 a_aux_err_head
= enp
->ae_next
;
1015 n_string_gut(&enp
->ae_str
);
1020 #endif /* HAVE_ERRORS */