nail.1: drop `(un)?setenv' and *bsdset*; add `environ'
[s-mailx.git] / auxlily.c
blobfdcd1b3330632df8cc2db58637f2a14f6c78bb58
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>.
6 */
7 /*
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
13 * are met:
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
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #ifdef HAVE_SOCKETS
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
47 # endif
49 # include <netdb.h>
50 #endif
52 #ifndef HAVE_POSIX_RANDOM
53 union rand_state {
54 struct rand_arc4 {
55 ui8_t __pad[6];
56 ui8_t _i;
57 ui8_t _j;
58 ui8_t _dat[256];
59 } a;
60 ui8_t b8[sizeof(struct rand_arc4)];
61 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
63 #endif
65 #ifdef HAVE_ERRORS
66 struct a_aux_err_node{
67 struct a_aux_err_node *ae_next;
68 struct str ae_str;
70 #endif
72 #ifndef HAVE_POSIX_RANDOM
73 static union rand_state *_rand;
74 #endif
76 /* Error ring, for `errors' */
77 #ifdef HAVE_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;
80 #endif
81 static size_t a_aux_err_dirty;
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);
89 #endif
91 #ifndef HAVE_POSIX_RANDOM
92 static void
93 _rand_init(void)
95 # ifdef HAVE_CLOCK_GETTIME
96 struct timespec ts;
97 # else
98 struct timeval ts;
99 # endif
100 union {int fd; size_t i;} u;
101 ui32_t seed, rnd;
102 NYD2_ENTER;
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));
109 close(u.fd);
110 if (ok)
111 goto jleave;
114 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
115 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
116 size_t t, k;
118 # ifdef HAVE_CLOCK_GETTIME
119 clock_gettime(CLOCK_REALTIME, &ts);
120 t = (ui32_t)ts.tv_nsec;
121 # else
122 gettimeofday(&ts, NULL);
123 t = (ui32_t)ts.tv_usec;
124 # endif
125 if (rnd & 1)
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]);
134 if ((rnd & 3) == 3)
135 seed ^= nextprime(seed);
139 for (u.i = 5 * sizeof(_rand->b8); u.i != 0; --u.i)
140 _rand_get8();
141 jleave:
142 NYD2_LEAVE;
145 static ui32_t
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.) */
152 ui32_t hi;
154 if (seed == 0)
155 seed = 123459876;
156 hi = seed / 127773;
157 seed %= 127773;
158 seed = (seed * 16807) - (hi * 2836);
159 if ((si32_t)seed < 0)
160 seed += SI32_MAX;
161 return seed;
164 SINLINE ui8_t
165 _rand_get8(void)
167 ui8_t si, sj;
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 */
177 FL int
178 screensize(void)
180 int s;
181 char *cp;
182 NYD_ENTER;
184 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
185 s = scrnheight - 2; /* XXX no magics */
186 NYD_LEAVE;
187 return s;
190 FL char const *
191 n_pager_get(char const **env_addon){
192 char const *rv;
193 NYD_ENTER;
195 rv = ok_vlook(PAGER);
197 if(env_addon != NULL){
198 *env_addon = NULL;
199 /* Update the manual upon any changes:
200 * *colour-pager*, $PAGER */
201 if(strstr(rv, "less") != NULL){
202 if(getenv("LESS") == NULL)
203 *env_addon =
204 #ifdef HAVE_TERMCAP
205 (pstate & PS_TERMCAP_CA_MODE) ? "LESS=Ri"
206 : !(pstate & PS_TERMCAP_DISABLE) ? "LESS=FRi" :
207 #endif
208 "LESS=FRXi";
209 }else if(strstr(rv, "lv") != NULL){
210 if(getenv("LV") == NULL)
211 *env_addon = "LV=-c";
214 NYD_LEAVE;
215 return rv;
218 FL void
219 page_or_print(FILE *fp, size_t lines)
221 int c;
222 char const *cp;
223 NYD_ENTER;
225 fflush_rewind(fp);
227 if (n_source_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
228 char *eptr;
229 union {sl_i sli; size_t rows;} u;
231 u.sli = strtol(cp, &eptr, 0);
232 u.rows = (*cp != '\0' && *eptr == '\0')
233 ? (size_t)u.sli : (size_t)scrnheight;
235 if (u.rows > 0 && lines == 0) {
236 while ((c = getc(fp)) != EOF)
237 if (c == '\n' && ++lines >= u.rows)
238 break;
239 really_rewind(fp);
242 if (lines >= u.rows) {
243 run_command(n_pager_get(NULL), 0, fileno(fp), COMMAND_FD_PASS,
244 NULL, NULL, NULL, NULL);
245 goto jleave;
249 while ((c = getc(fp)) != EOF)
250 putchar(c);
251 jleave:
252 NYD_LEAVE;
255 FL enum protocol
256 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
258 struct stat st;
259 char const *cp;
260 char *np;
261 size_t sz;
262 enum protocol rv = PROTO_UNKNOWN;
263 NYD_ENTER;
265 temporary_protocol_ext = NULL;
267 if (name[0] == '%' && name[1] == ':')
268 name += 2;
269 for (cp = name; *cp && *cp != ':'; cp++)
270 if (!alnumchar(*cp))
271 goto jfile;
273 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
274 if (!strncmp(name, "pop3://", 7)) {
275 #ifdef HAVE_POP3
276 rv = PROTO_POP3;
277 #else
278 n_err(_("No POP3 support compiled in\n"));
279 #endif
280 } else if (!strncmp(name, "pop3s://", 8)) {
281 #if defined HAVE_POP3 && defined HAVE_SSL
282 rv = PROTO_POP3;
283 #else
284 # ifndef HAVE_POP3
285 n_err(_("No POP3 support compiled in\n"));
286 # endif
287 # ifndef HAVE_SSL
288 n_err(_("No SSL support compiled in\n"));
289 # endif
290 #endif
292 goto jleave;
295 /* TODO This is the de facto maildir code and thus belongs into there!
296 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
297 * TODO or (more likely) in addition to *newfolders*) */
298 jfile:
299 rv = PROTO_FILE;
300 np = ac_alloc((sz = strlen(name)) + 4 +1);
301 memcpy(np, name, sz + 1);
302 if (!stat(name, &st)) {
303 if (S_ISDIR(st.st_mode) &&
304 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
305 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
306 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
307 rv = PROTO_MAILDIR;
308 } else {
309 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
310 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
311 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
312 temporary_protocol_ext = cp;
313 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
314 rv = PROTO_MAILDIR;
316 ac_free(np);
317 jleave:
318 NYD_LEAVE;
319 return rv;
322 FL ui32_t
323 torek_hash(char const *name)
325 /* Chris Torek's hash.
326 * NOTE: need to change *at least* mk-okey-map.pl when changing the
327 * algorithm!! */
328 ui32_t h = 0;
329 NYD_ENTER;
331 while (*name != '\0') {
332 h *= 33;
333 h += *name++;
335 NYD_LEAVE;
336 return h;
339 FL unsigned
340 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
342 unsigned h = 0, g;
343 NYD_ENTER;
345 cp--;
346 while (*++cp) {
347 h = (h << 4 & 0xffffffff) + (*cp&0377);
348 if ((g = h & 0xf0000000) != 0) {
349 h = h ^ g >> 24;
350 h = h ^ g;
353 NYD_LEAVE;
354 return h;
357 FL ui32_t
358 nextprime(ui32_t n)
360 static ui32_t const primes[] = {
361 5, 11, 23, 47, 97, 157, 283,
362 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
363 131071, 262139, 524287, 1048573, 2097143, 4194301,
364 8388593, 16777213, 33554393, 67108859, 134217689,
365 268435399, 536870909, 1073741789, 2147483647
368 ui32_t i, mprime;
369 NYD_ENTER;
371 i = (n < primes[NELEM(primes) / 4] ? 0
372 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
373 : NELEM(primes) / 2));
375 if ((mprime = primes[i]) > n)
376 break;
377 while (++i < NELEM(primes));
378 if (i == NELEM(primes) && mprime < n)
379 mprime = n;
380 NYD_LEAVE;
381 return mprime;
384 FL char *
385 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
386 { /* FIXME getprompt must mb->wc->mb+reset seq! */
387 static char buf[PROMPT_BUFFER_SIZE];
389 char *cp;
390 char const *ccp_base, *ccp;
391 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
392 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
393 NYD_ENTER;
395 /* No other place to place this */
396 #ifdef HAVE_ERRORS
397 if (options & OPT_INTERACTIVE) {
398 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
399 pstate |= PS_ERRORS_NOTED;
400 fprintf(stderr, _("There are new messages in the error message ring "
401 "(denoted by \"#ERR#\")\n"
402 " The `errors' command manages this message ring\n"));
405 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
406 a_aux_err_cnt_noted = a_aux_err_cnt;
407 } else
408 trigger = FAL0;
409 #endif
411 cp = buf;
412 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
413 #ifdef HAVE_ERRORS
414 if (trigger)
415 ccp_base = "";
416 else
417 #endif
418 goto jleave;
420 #ifdef HAVE_ERRORS
421 if (trigger)
422 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
423 #endif
424 NATCH_CHAR( cclen_base = strlen(ccp_base); )
426 dfmaxlen = 0; /* keep CC happy */
427 trigger = FAL0;
428 jredo:
429 ccp = ccp_base;
430 NATCH_CHAR( cclen = cclen_base; )
431 maxlen = sizeof(buf) -1;
433 for (;;) {
434 size_t l;
435 int c;
437 if (maxlen == 0)
438 goto jleave;
439 #ifdef HAVE_NATCH_CHAR
440 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
441 if (c <= 0) {
442 mblen(NULL, 0);
443 if (c < 0) {
444 *buf = '?';
445 cp = buf + 1;
446 goto jleave;
448 break;
449 } else if ((l = c) > 1) {
450 if (trigger) {
451 memcpy(cp, ccp, l);
452 cp += l;
454 ccp += l;
455 maxlen -= l;
456 continue;
457 } else
458 #endif
459 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
460 if (trigger)
461 *cp++ = (char)c;
462 --maxlen;
463 continue;
465 if (c == 0 || c == PROMPT_STOP)
466 break;
468 if (trigger) {
469 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
470 if (a == NULL)
471 a = "";
472 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
473 cp += l;
474 maxlen -= l;
475 dfmaxlen -= l;
480 if (!trigger) {
481 trigger = TRU1;
482 dfmaxlen = maxlen;
483 goto jredo;
485 jleave:
486 *cp = '\0';
487 NYD_LEAVE;
488 return buf;
491 FL char *
492 nodename(int mayoverride)
494 static char *sys_hostname, *hostname; /* XXX free-at-exit */
496 struct utsname ut;
497 char *hn;
498 #ifdef HAVE_SOCKETS
499 # ifdef HAVE_GETADDRINFO
500 struct addrinfo hints, *res;
501 # else
502 struct hostent *hent;
503 # endif
504 #endif
505 NYD_ENTER;
507 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
509 } else if ((hn = sys_hostname) == NULL) {
510 uname(&ut);
511 hn = ut.nodename;
512 #ifdef HAVE_SOCKETS
513 # ifdef HAVE_GETADDRINFO
514 memset(&hints, 0, sizeof hints);
515 hints.ai_family = AF_UNSPEC;
516 hints.ai_flags = AI_CANONNAME;
517 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
518 if (res->ai_canonname != NULL) {
519 size_t l = strlen(res->ai_canonname) +1;
521 hn = ac_alloc(l);
522 memcpy(hn, res->ai_canonname, l);
524 freeaddrinfo(res);
526 # else
527 hent = gethostbyname(hn);
528 if (hent != NULL)
529 hn = hent->h_name;
530 # endif
531 #endif
532 sys_hostname = sstrdup(hn);
533 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
534 if (hn != ut.nodename)
535 ac_free(hn);
536 #endif
537 hn = sys_hostname;
540 if (hostname != NULL && hostname != sys_hostname)
541 free(hostname);
542 hostname = sstrdup(hn);
543 NYD_LEAVE;
544 return hostname;
547 FL char *
548 getrandstring(size_t length)
550 struct str b64;
551 char *data;
552 size_t i;
553 NYD_ENTER;
555 #ifndef HAVE_POSIX_RANDOM
556 if (_rand == NULL)
557 _rand_init();
558 #endif
560 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
561 * with PAD stripped is still longer than what the user requests, easy way */
562 data = ac_alloc(i = length + 3);
564 #ifndef HAVE_POSIX_RANDOM
565 while (i-- > 0)
566 data[i] = (char)_rand_get8();
567 #else
568 { char *cp = data;
570 while (i > 0) {
571 union {ui32_t i4; char c[4];} r;
572 size_t j;
574 r.i4 = (ui32_t)arc4random();
575 switch ((j = i & 3)) {
576 case 0: cp[3] = r.c[3]; j = 4;
577 case 3: cp[2] = r.c[2];
578 case 2: cp[1] = r.c[1];
579 default: cp[0] = r.c[0]; break;
581 cp += j;
582 i -= j;
585 #endif
587 b64_encode_buf(&b64, data, length + 3,
588 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
589 ac_free(data);
591 assert(b64.l >= length);
592 b64.s[length] = '\0';
593 NYD_LEAVE;
594 return b64.s;
597 FL si8_t
598 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
600 char *dat, *eptr;
601 sl_i sli;
602 si8_t rv;
603 NYD_ENTER;
605 assert(inlen == 0 || inbuf != NULL);
607 if (inlen == UIZ_MAX)
608 inlen = strlen(inbuf);
610 if (inlen == 0)
611 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
612 else {
613 if ((inlen == 1 && *inbuf == '1') ||
614 !ascncasecmp(inbuf, "true", inlen) ||
615 !ascncasecmp(inbuf, "yes", inlen) ||
616 !ascncasecmp(inbuf, "on", inlen))
617 rv = 1;
618 else if ((inlen == 1 && *inbuf == '0') ||
619 !ascncasecmp(inbuf, "false", inlen) ||
620 !ascncasecmp(inbuf, "no", inlen) ||
621 !ascncasecmp(inbuf, "off", inlen))
622 rv = 0;
623 else {
624 dat = ac_alloc(inlen +1);
625 memcpy(dat, inbuf, inlen);
626 dat[inlen] = '\0';
628 sli = strtol(dat, &eptr, 0);
629 if (*dat != '\0' && *eptr == '\0')
630 rv = (sli != 0);
631 else
632 rv = -1;
634 ac_free(dat);
637 NYD_LEAVE;
638 return rv;
641 FL si8_t
642 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
644 si8_t rv;
645 NYD_ENTER;
647 assert(inlen == 0 || inbuf != NULL);
649 if (inlen == UIZ_MAX)
650 inlen = strlen(inbuf);
652 if (inlen == 0)
653 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
654 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
655 !ascncasecmp(inbuf, "ask-", 4) &&
656 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
657 (options & OPT_INTERACTIVE))
658 rv = getapproval(prompt, rv);
659 NYD_LEAVE;
660 return rv;
663 FL bool_t
664 n_is_all_or_aster(char const *name){
665 bool_t rv;
666 NYD_ENTER;
668 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
669 NYD_LEAVE;
670 return rv;
673 FL time_t
674 n_time_epoch(void)
676 #ifdef HAVE_CLOCK_GETTIME
677 struct timespec ts;
678 #elif defined HAVE_GETTIMEOFDAY
679 struct timeval ts;
680 #endif
681 time_t rv;
682 NYD2_ENTER;
684 #ifdef HAVE_CLOCK_GETTIME
685 clock_gettime(CLOCK_REALTIME, &ts);
686 rv = (time_t)ts.tv_sec;
687 #elif defined HAVE_GETTIMEOFDAY
688 gettimeofday(&ts, NULL);
689 rv = (time_t)ts.tv_sec;
690 #else
691 rv = time(NULL);
692 #endif
693 NYD2_LEAVE;
694 return rv;
697 FL void
698 time_current_update(struct time_current *tc, bool_t full_update)
700 NYD_ENTER;
701 tc->tc_time = n_time_epoch();
702 if (full_update) {
703 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
704 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
705 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
707 NYD_LEAVE;
710 FL uiz_t
711 n_msleep(uiz_t millis, bool_t ignint){
712 uiz_t rv;
713 NYD2_ENTER;
715 #ifdef HAVE_NANOSLEEP
716 /* C99 */{
717 struct timespec ts, trem;
718 int i;
720 ts.tv_sec = millis / 1000;
721 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
723 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
724 ts = trem;
725 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
728 #elif defined HAVE_SLEEP
729 if((millis /= 1000) == 0)
730 millis = 1;
731 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
732 millis = rv;
733 #else
734 # error Configuration should have detected a function for sleeping.
735 #endif
737 NYD2_LEAVE;
738 return rv;
741 FL void
742 n_err(char const *format, ...){
743 va_list ap;
744 NYD2_ENTER;
746 va_start(ap, format);
747 #ifdef HAVE_ERRORS
748 if(options & OPT_INTERACTIVE)
749 n_verr(format, ap);
750 else
751 #endif
753 if(a_aux_err_dirty++ == 0)
754 fputs(UAGENT ": ", stderr);
755 vfprintf(stderr, format, ap);
756 if(strchr(format, '\n') != NULL){ /* TODO */
757 a_aux_err_dirty = 0;
758 fflush(stderr);
761 va_end(ap);
762 NYD2_LEAVE;
765 FL void
766 n_verr(char const *format, va_list ap){
767 /* Check use cases of PS_ERRORS_NOTED, too! */
768 #ifdef HAVE_ERRORS
769 char buf[LINESIZE], *xbuf;
770 int lmax, l;
771 struct a_aux_err_node *enp;
773 LCTA(ERRORS_MAX > 3);
774 #endif
775 NYD2_ENTER;
777 if(a_aux_err_dirty++ == 0)
778 fputs(UAGENT ": ", stderr);
780 #ifdef HAVE_ERRORS
781 if(!(options & OPT_INTERACTIVE))
782 #endif
784 vfprintf(stderr, format, ap);
785 goto jleave;
788 #ifdef HAVE_ERRORS
789 xbuf = buf;
790 lmax = sizeof buf;
791 jredo:
792 l = vsnprintf(xbuf, lmax, format, ap);
793 if (l <= 0)
794 goto jleave;
795 if (UICMP(z, l, >=, lmax)) {
796 /* FIXME Cannot reuse va_list
797 lmax = ++l;
798 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
799 goto jredo;
803 fwrite(xbuf, 1, l, stderr);
805 /* Link it into the `errors' message ring */
806 if((enp = a_aux_err_tail) == NULL){
807 jcreat:
808 enp = scalloc(1, sizeof *enp);
809 if(a_aux_err_tail != NULL)
810 a_aux_err_tail->ae_next = enp;
811 else
812 a_aux_err_head = enp;
813 a_aux_err_tail = enp;
814 ++a_aux_err_cnt;
815 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
816 if(a_aux_err_cnt < ERRORS_MAX)
817 goto jcreat;
819 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
820 a_aux_err_tail->ae_next = enp;
821 a_aux_err_tail = enp;
822 free(enp->ae_str.s);
823 memset(enp, 0, sizeof *enp);
826 n_str_add_buf(&enp->ae_str, xbuf, l);
828 if(xbuf != buf)
829 free(xbuf);
830 #endif /* HAVE_ERRORS */
832 jleave:
833 /* If the format ends with newline, be clean again */
834 /* C99 */{
835 size_t i = strlen(format);
837 if(i > 0 && format[i - 1] == '\n'){
838 fflush(stderr);
839 a_aux_err_dirty = 0;
842 NYD2_LEAVE;
845 FL void
846 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
847 va_list ap;
848 NYD_X;
850 va_start(ap, format);
851 vfprintf(stderr, format, ap);
852 va_end(ap);
853 fflush(stderr);
856 FL void
857 n_perr(char const *msg, int errval){
858 char const *fmt;
859 NYD2_ENTER;
861 if(msg == NULL){
862 fmt = "%s%s\n";
863 msg = "";
864 }else
865 fmt = "%s: %s\n";
867 if(errval == 0)
868 errval = errno;
870 n_err(fmt, msg, strerror(errval));
871 NYD2_LEAVE;
874 FL void
875 n_alert(char const *format, ...){
876 va_list ap;
877 NYD2_ENTER;
879 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
881 va_start(ap, format);
882 n_verr(format, ap);
883 va_end(ap);
885 n_err("\n");
886 NYD2_LEAVE;
889 FL void
890 n_panic(char const *format, ...){
891 va_list ap;
892 NYD2_ENTER;
894 if(a_aux_err_dirty > 0){
895 putc('\n', stderr);
896 a_aux_err_dirty = 0;
898 fprintf(stderr, UAGENT ": Panic: ");
900 va_start(ap, format);
901 vfprintf(stderr, format, ap);
902 va_end(ap);
904 putc('\n', stderr);
905 fflush(stderr);
906 NYD2_LEAVE;
907 abort(); /* Was exit(EXIT_ERR); for a while, but no */
910 #ifdef HAVE_ERRORS
911 FL int
912 c_errors(void *v){
913 char **argv = v;
914 struct a_aux_err_node *enp;
915 NYD_ENTER;
917 if(*argv == NULL)
918 goto jlist;
919 if(argv[1] != NULL)
920 goto jerr;
921 if(!asccasecmp(*argv, "show"))
922 goto jlist;
923 if(!asccasecmp(*argv, "clear"))
924 goto jclear;
925 jerr:
926 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
927 v = NULL;
928 jleave:
929 NYD_LEAVE;
930 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
932 jlist:{
933 FILE *fp;
934 size_t i;
936 if(a_aux_err_head == NULL){
937 fprintf(stderr, _("The error ring is empty\n"));
938 goto jleave;
941 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
942 NULL){
943 fprintf(stderr, _("tmpfile"));
944 v = NULL;
945 goto jleave;
948 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
949 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
950 ++i, enp->ae_str.l, enp->ae_str.s);
951 /* We don't know wether last string ended with NL; be simple */
952 putc('\n', fp);
954 page_or_print(fp, 0);
955 Fclose(fp);
957 /* FALLTHRU */
959 jclear:
960 a_aux_err_tail = NULL;
961 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
962 while((enp = a_aux_err_head) != NULL){
963 a_aux_err_head = enp->ae_next;
964 free(enp->ae_str.s);
965 free(enp);
967 goto jleave;
969 #endif /* HAVE_ERRORS */
971 /* s-it-mode */