Show the Content-Description:, as applicable
[s-mailx.git] / auxlily.c
blobec1b1a8eef740293636ff86be509cba3230d1d82
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>.
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 n_string 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_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);
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 = n_NELEM(_rand->b32); u.i-- != 0;) {
116 ui32_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 % 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]);
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){
179 ul_i s;
180 char *cp;
181 NYD2_ENTER;
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 */
187 s = INT_MAX;
188 NYD2_LEAVE;
189 return (int)s;
192 FL char const *
193 n_pager_get(char const **env_addon){
194 char const *rv;
195 NYD_ENTER;
197 rv = ok_vlook(PAGER);
199 if(env_addon != NULL){
200 *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)
205 *env_addon =
206 #ifdef HAVE_TERMCAP
207 (pstate & PS_TERMCAP_CA_MODE) ? "LESS=Ri"
208 : !(pstate & PS_TERMCAP_DISABLE) ? "LESS=FRi" :
209 #endif
210 "LESS=FRXi";
211 }else if(strstr(rv, "lv") != NULL){
212 if(getenv("LV") == NULL)
213 *env_addon = "LV=-c";
216 NYD_LEAVE;
217 return rv;
220 FL void
221 page_or_print(FILE *fp, size_t lines)
223 int c;
224 char const *cp;
225 NYD_ENTER;
227 fflush_rewind(fp);
229 if (n_source_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
230 size_t rows;
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)
237 break;
238 really_rewind(fp);
241 if (lines >= rows) {
242 char const *env_add[2], *pager;
244 pager = n_pager_get(&env_add[0]);
245 env_add[1] = NULL;
246 run_command(pager, NULL, fileno(fp), COMMAND_FD_PASS, NULL,NULL,NULL,
247 env_add);
248 goto jleave;
252 while ((c = getc(fp)) != EOF)
253 putchar(c);
254 jleave:
255 NYD_LEAVE;
258 FL enum protocol
259 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
261 struct stat st;
262 char const *cp;
263 char *np;
264 size_t sz;
265 enum protocol rv = PROTO_UNKNOWN;
266 NYD_ENTER;
268 temporary_protocol_ext = NULL;
270 if (name[0] == '%' && name[1] == ':')
271 name += 2;
272 for (cp = name; *cp && *cp != ':'; cp++)
273 if (!alnumchar(*cp))
274 goto jfile;
276 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
277 if (!strncmp(name, "pop3://", 7)) {
278 #ifdef HAVE_POP3
279 rv = PROTO_POP3;
280 #else
281 n_err(_("No POP3 support compiled in\n"));
282 #endif
283 } else if (!strncmp(name, "pop3s://", 8)) {
284 #if defined HAVE_POP3 && defined HAVE_SSL
285 rv = PROTO_POP3;
286 #else
287 # ifndef HAVE_POP3
288 n_err(_("No POP3 support compiled in\n"));
289 # endif
290 # ifndef HAVE_SSL
291 n_err(_("No SSL support compiled in\n"));
292 # endif
293 #endif
295 goto jleave;
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*) */
301 jfile:
302 rv = PROTO_FILE;
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)))
310 rv = PROTO_MAILDIR;
311 } else {
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"))
318 rv = PROTO_MAILDIR;
320 ac_free(np);
321 jleave:
322 NYD_LEAVE;
323 return rv;
326 FL char *
327 n_c_to_hex_base16(char store[3], char c){
328 static char const itoa16[] = "0123456789ABCDEF";
329 NYD2_ENTER;
331 store[2] = '\0';
332 store[1] = itoa16[(ui8_t)c & 0x0F];
333 c = ((ui8_t)c >> 4) & 0x0F;
334 store[0] = itoa16[(ui8_t)c];
335 NYD2_LEAVE;
336 return store;
339 FL si32_t
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 */
350 ui8_t i1, i2;
351 si32_t rv;
352 NYD2_ENTER;
354 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
355 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
356 goto jerr;
357 i1 = atoi16[i1];
358 i2 = atoi16[i2];
359 if ((i1 | i2) & 0xF0u)
360 goto jerr;
361 rv = i1;
362 rv <<= 4;
363 rv += i2;
364 jleave:
365 NYD2_LEAVE;
366 return rv;
367 jerr:
368 rv = -1;
369 goto jleave;
372 FL ui32_t
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
377 * algorithm!! */
378 ui32_t h = 0;
379 NYD_ENTER;
381 while (*name != '\0') {
382 h *= 33;
383 h += *name++;
385 NYD_LEAVE;
386 return h;
389 FL ui32_t
390 torek_ihashn(char const *dat, size_t len){
391 /* See torek_hash() */
392 char c;
393 ui32_t h;
394 NYD_ENTER;
396 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
397 h = (h * 33) + lowerconv(c);
398 NYD_LEAVE;
399 return h;
402 FL ui32_t
403 nextprime(ui32_t n)
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
413 ui32_t i, mprime;
414 NYD_ENTER;
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)
421 break;
422 while (++i < n_NELEM(primes));
423 if (i == n_NELEM(primes) && mprime < n)
424 mprime = n;
425 NYD_LEAVE;
426 return mprime;
429 FL char const *
430 n_getdeadletter(void){
431 char const *cp_base, *cp;
432 NYD_ENTER;
434 cp_base = NULL;
435 jredo:
436 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
437 if(cp == NULL || strlen(cp) >= PATH_MAX){
438 if(cp_base == NULL){
439 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
440 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
441 ok_vclear(DEAD);
442 goto jredo;
443 }else{
444 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
445 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
448 NYD_LEAVE;
449 return cp;
452 FL char *
453 nodename(int mayoverride)
455 static char *sys_hostname, *hostname; /* XXX free-at-exit */
457 struct utsname ut;
458 char *hn;
459 #ifdef HAVE_SOCKETS
460 # ifdef HAVE_GETADDRINFO
461 struct addrinfo hints, *res;
462 # else
463 struct hostent *hent;
464 # endif
465 #endif
466 NYD_ENTER;
468 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
470 } else if ((hn = sys_hostname) == NULL) {
471 uname(&ut);
472 hn = ut.nodename;
473 #ifdef HAVE_SOCKETS
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;
482 hn = ac_alloc(l);
483 memcpy(hn, res->ai_canonname, l);
485 freeaddrinfo(res);
487 # else
488 hent = gethostbyname(hn);
489 if (hent != NULL)
490 hn = hent->h_name;
491 # endif
492 #endif
493 sys_hostname = sstrdup(hn);
494 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
495 if (hn != ut.nodename)
496 ac_free(hn);
497 #endif
498 hn = sys_hostname;
501 if (hostname != NULL && hostname != sys_hostname)
502 free(hostname);
503 hostname = sstrdup(hn);
504 NYD_LEAVE;
505 return hostname;
508 FL char *
509 getrandstring(size_t length)
511 struct str b64;
512 char *data;
513 size_t i;
514 NYD_ENTER;
516 #ifndef HAVE_POSIX_RANDOM
517 if (_rand == NULL)
518 _rand_init();
519 #endif
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
526 while (i-- > 0)
527 data[i] = (char)_rand_get8();
528 #else
529 { char *cp = data;
531 while (i > 0) {
532 union {ui32_t i4; char c[4];} r;
533 size_t j;
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;
542 cp += j;
543 i -= j;
546 #endif
548 assert(length + 3 < UIZ_MAX / 4);
549 b64_encode_buf(&b64, data, length + 3,
550 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
551 ac_free(data);
553 assert(b64.l >= length);
554 b64.s[length] = '\0';
555 NYD_LEAVE;
556 return b64.s;
559 FL si8_t
560 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
562 char *dat, *eptr;
563 sl_i sli;
564 si8_t rv;
565 NYD_ENTER;
567 assert(inlen == 0 || inbuf != NULL);
569 if (inlen == UIZ_MAX)
570 inlen = strlen(inbuf);
572 if (inlen == 0)
573 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
574 else {
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))
579 rv = 1;
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))
585 rv = 0;
586 else {
587 dat = ac_alloc(inlen +1);
588 memcpy(dat, inbuf, inlen);
589 dat[inlen] = '\0';
591 sli = strtol(dat, &eptr, 0);
592 if (*dat != '\0' && *eptr == '\0')
593 rv = (sli != 0);
594 else
595 rv = -1;
597 ac_free(dat);
600 NYD_LEAVE;
601 return rv;
604 FL si8_t
605 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
607 si8_t rv;
608 NYD_ENTER;
610 assert(inlen == 0 || inbuf != NULL);
612 if (inlen == UIZ_MAX)
613 inlen = strlen(inbuf);
615 if (inlen == 0)
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);
622 NYD_LEAVE;
623 return rv;
626 FL bool_t
627 n_is_all_or_aster(char const *name){
628 bool_t rv;
629 NYD_ENTER;
631 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
632 NYD_LEAVE;
633 return rv;
636 FL time_t
637 n_time_epoch(void)
639 #ifdef HAVE_CLOCK_GETTIME
640 struct timespec ts;
641 #elif defined HAVE_GETTIMEOFDAY
642 struct timeval ts;
643 #endif
644 time_t rv;
645 char const *cp;
646 NYD2_ENTER;
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);
653 }else{
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;
660 #else
661 rv = time(NULL);
662 #endif
664 NYD2_LEAVE;
665 return rv;
668 FL void
669 time_current_update(struct time_current *tc, bool_t full_update)
671 NYD_ENTER;
672 tc->tc_time = n_time_epoch();
673 if (full_update) {
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));
678 NYD_LEAVE;
681 FL uiz_t
682 n_msleep(uiz_t millis, bool_t ignint){
683 uiz_t rv;
684 NYD2_ENTER;
686 #ifdef HAVE_NANOSLEEP
687 /* C99 */{
688 struct timespec ts, trem;
689 int i;
691 ts.tv_sec = millis / 1000;
692 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
694 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
695 ts = trem;
696 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
699 #elif defined HAVE_SLEEP
700 if((millis /= 1000) == 0)
701 millis = 1;
702 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
703 millis = rv;
704 #else
705 # error Configuration should have detected a function for sleeping.
706 #endif
708 NYD2_LEAVE;
709 return rv;
712 FL void
713 n_err(char const *format, ...){
714 va_list ap;
715 NYD2_ENTER;
717 va_start(ap, format);
718 #ifdef HAVE_ERRORS
719 if(options & OPT_INTERACTIVE)
720 n_verr(format, ap);
721 else
722 #endif
724 size_t len;
725 bool_t doname, doflush;
727 doflush = FAL0;
728 while(*format == '\n'){
729 doflush = TRU1;
730 putc('\n', stderr);
731 ++format;
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);
742 /* C99 */{
743 size_t i = len;
745 if(format[--len] == '\n'){
746 a_aux_err_linelen = (i -= ++len);
747 break;
749 ++a_aux_err_linelen;
750 }while(len > 0);
754 if(doflush)
755 fflush(stderr);
757 va_end(ap);
758 NYD2_LEAVE;
761 FL void
762 n_verr(char const *format, va_list ap){
763 /* Check use cases of PS_ERRORS_NOTED, too! */
764 #ifdef HAVE_ERRORS
765 struct a_aux_err_node *enp;
766 #endif
767 bool_t doname, doflush;
768 size_t len;
769 NYD2_ENTER;
771 doflush = FAL0;
772 while(*format == '\n'){
773 doflush = TRU1;
774 putc('\n', stderr);
775 ++format;
778 if((doname = doflush)){
779 a_aux_err_linelen = 0;
780 #ifdef HAVE_ERRORS
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');
787 #endif
790 if((len = strlen(format)) == 0)
791 goto jleave;
792 #ifdef HAVE_ERRORS
793 pstate |= PS_ERRORS_PROMPT;
794 #endif
796 if(doname || a_aux_err_linelen == 0)
797 fputs(VAL_UAGENT ": ", stderr);
799 /* C99 */{
800 size_t i = len;
802 if(format[--len] == '\n'){
803 a_aux_err_linelen = (i -= ++len);
804 break;
806 ++a_aux_err_linelen;
807 }while(len > 0);
810 #ifdef HAVE_ERRORS
811 if(!(options & OPT_INTERACTIVE))
812 #endif
813 vfprintf(stderr, format, ap);
814 #ifdef HAVE_ERRORS
815 else{
816 int imax, i;
817 n_LCTAV(ERRORS_MAX > 3);
819 /* Link it into the `errors' message ring */
820 if((enp = a_aux_err_tail) == NULL){
821 jcreat:
822 enp = smalloc(sizeof *enp);
823 enp->ae_next = NULL;
824 n_string_creat(&enp->ae_str);
825 if(a_aux_err_tail != NULL)
826 a_aux_err_tail->ae_next = enp;
827 else
828 a_aux_err_head = enp;
829 a_aux_err_tail = enp;
830 ++a_aux_err_cnt;
831 }else if(doname ||
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)
835 goto jcreat;
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;
840 enp->ae_next = NULL;
841 n_string_trunc(&enp->ae_str, 0);
844 # ifdef HAVE_N_VA_COPY
845 imax = 64;
846 # else
847 imax = n_MIN(LINESIZE, 1024);
848 # endif
849 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
850 # ifdef HAVE_N_VA_COPY
851 va_list vac;
853 n_va_copy(vac, ap);
854 # else
855 # define vac ap
856 # endif
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
861 va_end(vac);
862 # else
863 # undef vac
864 # endif
865 if(i <= 0)
866 goto jleave;
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);
871 continue;
872 # else
873 i = (int)strlen(&enp->ae_str.s_dat[len]);
874 # endif
876 break;
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 */
884 jleave:
885 if(doflush)
886 fflush(stderr);
887 NYD2_LEAVE;
890 FL void
891 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
892 va_list ap;
893 NYD_X;
895 va_start(ap, format);
896 vfprintf(stderr, format, ap);
897 va_end(ap);
898 fflush(stderr);
901 FL void
902 n_perr(char const *msg, int errval){
903 char const *fmt;
904 NYD2_ENTER;
906 if(msg == NULL){
907 fmt = "%s%s\n";
908 msg = n_empty;
909 }else
910 fmt = "%s: %s\n";
912 if(errval == 0)
913 errval = errno;
915 n_err(fmt, msg, strerror(errval));
916 NYD2_LEAVE;
919 FL void
920 n_alert(char const *format, ...){
921 va_list ap;
922 NYD2_ENTER;
924 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
926 va_start(ap, format);
927 n_verr(format, ap);
928 va_end(ap);
930 n_err("\n");
931 NYD2_LEAVE;
934 FL void
935 n_panic(char const *format, ...){
936 va_list ap;
937 NYD2_ENTER;
939 if(a_aux_err_linelen > 0){
940 putc('\n', stderr);
941 a_aux_err_linelen = 0;
943 fprintf(stderr, VAL_UAGENT ": Panic: ");
945 va_start(ap, format);
946 vfprintf(stderr, format, ap);
947 va_end(ap);
949 putc('\n', stderr);
950 fflush(stderr);
951 NYD2_LEAVE;
952 abort(); /* Was exit(EXIT_ERR); for a while, but no */
955 #ifdef HAVE_ERRORS
956 FL int
957 c_errors(void *v){
958 char **argv = v;
959 struct a_aux_err_node *enp;
960 NYD_ENTER;
962 if(*argv == NULL)
963 goto jlist;
964 if(argv[1] != NULL)
965 goto jerr;
966 if(!asccasecmp(*argv, "show"))
967 goto jlist;
968 if(!asccasecmp(*argv, "clear"))
969 goto jclear;
970 jerr:
971 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
972 v = NULL;
973 jleave:
974 NYD_LEAVE;
975 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
977 jlist:{
978 FILE *fp;
979 size_t i;
981 if(a_aux_err_head == NULL){
982 fprintf(stderr, _("The error ring is empty\n"));
983 goto jleave;
986 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
987 NULL){
988 fprintf(stderr, _("tmpfile"));
989 v = NULL;
990 goto jleave;
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 */
996 putc('\n', fp);
998 page_or_print(fp, 0);
999 Fclose(fp);
1001 /* FALLTHRU */
1003 jclear:
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);
1010 free(enp);
1012 goto jleave;
1014 #endif /* HAVE_ERRORS */
1016 /* s-it-mode */