Add n_signal(), our future (v15) sigaction(2) call-in
[s-mailx.git] / auxlily.c
blob364d1b2aa9aedf328fd7d9ceedd226da3daa405e
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 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 = 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){
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 = 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 run_command(n_pager_get(NULL), 0, fileno(fp), COMMAND_FD_PASS,
243 NULL, NULL, NULL, NULL);
244 goto jleave;
248 while ((c = getc(fp)) != EOF)
249 putchar(c);
250 jleave:
251 NYD_LEAVE;
254 FL enum protocol
255 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
257 struct stat st;
258 char const *cp;
259 char *np;
260 size_t sz;
261 enum protocol rv = PROTO_UNKNOWN;
262 NYD_ENTER;
264 temporary_protocol_ext = NULL;
266 if (name[0] == '%' && name[1] == ':')
267 name += 2;
268 for (cp = name; *cp && *cp != ':'; cp++)
269 if (!alnumchar(*cp))
270 goto jfile;
272 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
273 if (!strncmp(name, "pop3://", 7)) {
274 #ifdef HAVE_POP3
275 rv = PROTO_POP3;
276 #else
277 n_err(_("No POP3 support compiled in\n"));
278 #endif
279 } else if (!strncmp(name, "pop3s://", 8)) {
280 #if defined HAVE_POP3 && defined HAVE_SSL
281 rv = PROTO_POP3;
282 #else
283 # ifndef HAVE_POP3
284 n_err(_("No POP3 support compiled in\n"));
285 # endif
286 # ifndef HAVE_SSL
287 n_err(_("No SSL support compiled in\n"));
288 # endif
289 #endif
291 goto jleave;
294 /* TODO This is the de facto maildir code and thus belongs into there!
295 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
296 * TODO or (more likely) in addition to *newfolders*) */
297 jfile:
298 rv = PROTO_FILE;
299 np = ac_alloc((sz = strlen(name)) + 4 +1);
300 memcpy(np, name, sz + 1);
301 if (!stat(name, &st)) {
302 if (S_ISDIR(st.st_mode) &&
303 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
304 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
305 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
306 rv = PROTO_MAILDIR;
307 } else {
308 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
309 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
310 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
311 temporary_protocol_ext = cp;
312 else if ((cp = ok_vlook(newfolders)) != NULL &&
313 !asccasecmp(cp, "maildir"))
314 rv = PROTO_MAILDIR;
316 ac_free(np);
317 jleave:
318 NYD_LEAVE;
319 return rv;
322 FL char *
323 n_c_to_hex_base16(char store[3], char c){
324 static char const itoa16[] = "0123456789ABCDEF";
325 NYD2_ENTER;
327 store[2] = '\0';
328 store[1] = itoa16[(ui8_t)c & 0x0F];
329 c = ((ui8_t)c >> 4) & 0x0F;
330 store[0] = itoa16[(ui8_t)c];
331 NYD2_LEAVE;
332 return store;
335 FL si32_t
336 n_c_from_hex_base16(char const hex[2]){
337 static ui8_t const atoi16[] = {
338 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
339 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
340 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
341 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
342 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
343 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
344 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
346 ui8_t i1, i2;
347 si32_t rv;
348 NYD2_ENTER;
350 if ((i1 = (ui8_t)hex[0] - '0') >= NELEM(atoi16) ||
351 (i2 = (ui8_t)hex[1] - '0') >= NELEM(atoi16))
352 goto jerr;
353 i1 = atoi16[i1];
354 i2 = atoi16[i2];
355 if ((i1 | i2) & 0xF0u)
356 goto jerr;
357 rv = i1;
358 rv <<= 4;
359 rv += i2;
360 jleave:
361 NYD2_LEAVE;
362 return rv;
363 jerr:
364 rv = -1;
365 goto jleave;
368 FL ui32_t
369 torek_hash(char const *name)
371 /* Chris Torek's hash.
372 * NOTE: need to change *at least* mk-okey-map.pl when changing the
373 * algorithm!! */
374 ui32_t h = 0;
375 NYD_ENTER;
377 while (*name != '\0') {
378 h *= 33;
379 h += *name++;
381 NYD_LEAVE;
382 return h;
385 FL unsigned
386 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
388 unsigned h = 0, g;
389 NYD_ENTER;
391 cp--;
392 while (*++cp) {
393 h = (h << 4 & 0xffffffff) + (*cp&0377);
394 if ((g = h & 0xf0000000) != 0) {
395 h = h ^ g >> 24;
396 h = h ^ g;
399 NYD_LEAVE;
400 return h;
403 FL ui32_t
404 nextprime(ui32_t n)
406 static ui32_t const primes[] = {
407 5, 11, 23, 47, 97, 157, 283,
408 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
409 131071, 262139, 524287, 1048573, 2097143, 4194301,
410 8388593, 16777213, 33554393, 67108859, 134217689,
411 268435399, 536870909, 1073741789, 2147483647
414 ui32_t i, mprime;
415 NYD_ENTER;
417 i = (n < primes[NELEM(primes) / 4] ? 0
418 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
419 : NELEM(primes) / 2));
421 if ((mprime = primes[i]) > n)
422 break;
423 while (++i < NELEM(primes));
424 if (i == NELEM(primes) && mprime < n)
425 mprime = n;
426 NYD_LEAVE;
427 return mprime;
430 FL char *
431 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
432 { /* FIXME getprompt must mb->wc->mb+reset seq! */
433 static char buf[PROMPT_BUFFER_SIZE];
435 char *cp;
436 char const *ccp_base, *ccp;
437 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
438 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
439 NYD_ENTER;
441 /* No other place to place this */
442 #ifdef HAVE_ERRORS
443 if (options & OPT_INTERACTIVE) {
444 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
445 pstate |= PS_ERRORS_NOTED;
446 fprintf(stderr, _("There are new messages in the error message ring "
447 "(denoted by \"#ERR#\")\n"
448 " The `errors' command manages this message ring\n"));
451 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
452 a_aux_err_cnt_noted = a_aux_err_cnt;
453 } else
454 trigger = FAL0;
455 #endif
457 cp = buf;
458 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
459 #ifdef HAVE_ERRORS
460 if (trigger)
461 ccp_base = "";
462 else
463 #endif
464 goto jleave;
466 #ifdef HAVE_ERRORS
467 if (trigger)
468 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
469 #endif
470 NATCH_CHAR( cclen_base = strlen(ccp_base); )
472 dfmaxlen = 0; /* keep CC happy */
473 trigger = FAL0;
474 jredo:
475 ccp = ccp_base;
476 NATCH_CHAR( cclen = cclen_base; )
477 maxlen = sizeof(buf) -1;
479 for (;;) {
480 size_t l;
481 int c;
483 if (maxlen == 0)
484 goto jleave;
485 #ifdef HAVE_NATCH_CHAR
486 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
487 if (c <= 0) {
488 mblen(NULL, 0);
489 if (c < 0) {
490 *buf = '?';
491 cp = buf + 1;
492 goto jleave;
494 break;
495 } else if ((l = c) > 1) {
496 if (trigger) {
497 memcpy(cp, ccp, l);
498 cp += l;
500 ccp += l;
501 maxlen -= l;
502 continue;
503 } else
504 #endif
505 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
506 if (trigger)
507 *cp++ = (char)c;
508 --maxlen;
509 continue;
511 if (c == 0 || c == PROMPT_STOP)
512 break;
514 if (trigger) {
515 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
516 if (a == NULL)
517 a = "";
518 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
519 cp += l;
520 maxlen -= l;
521 dfmaxlen -= l;
526 if (!trigger) {
527 trigger = TRU1;
528 dfmaxlen = maxlen;
529 goto jredo;
531 jleave:
532 *cp = '\0';
533 NYD_LEAVE;
534 return buf;
537 FL char *
538 nodename(int mayoverride)
540 static char *sys_hostname, *hostname; /* XXX free-at-exit */
542 struct utsname ut;
543 char *hn;
544 #ifdef HAVE_SOCKETS
545 # ifdef HAVE_GETADDRINFO
546 struct addrinfo hints, *res;
547 # else
548 struct hostent *hent;
549 # endif
550 #endif
551 NYD_ENTER;
553 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
555 } else if ((hn = sys_hostname) == NULL) {
556 uname(&ut);
557 hn = ut.nodename;
558 #ifdef HAVE_SOCKETS
559 # ifdef HAVE_GETADDRINFO
560 memset(&hints, 0, sizeof hints);
561 hints.ai_family = AF_UNSPEC;
562 hints.ai_flags = AI_CANONNAME;
563 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
564 if (res->ai_canonname != NULL) {
565 size_t l = strlen(res->ai_canonname) +1;
567 hn = ac_alloc(l);
568 memcpy(hn, res->ai_canonname, l);
570 freeaddrinfo(res);
572 # else
573 hent = gethostbyname(hn);
574 if (hent != NULL)
575 hn = hent->h_name;
576 # endif
577 #endif
578 sys_hostname = sstrdup(hn);
579 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
580 if (hn != ut.nodename)
581 ac_free(hn);
582 #endif
583 hn = sys_hostname;
586 if (hostname != NULL && hostname != sys_hostname)
587 free(hostname);
588 hostname = sstrdup(hn);
589 NYD_LEAVE;
590 return hostname;
593 FL char *
594 getrandstring(size_t length)
596 struct str b64;
597 char *data;
598 size_t i;
599 NYD_ENTER;
601 #ifndef HAVE_POSIX_RANDOM
602 if (_rand == NULL)
603 _rand_init();
604 #endif
606 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
607 * with PAD stripped is still longer than what the user requests, easy way */
608 data = ac_alloc(i = length + 3);
610 #ifndef HAVE_POSIX_RANDOM
611 while (i-- > 0)
612 data[i] = (char)_rand_get8();
613 #else
614 { char *cp = data;
616 while (i > 0) {
617 union {ui32_t i4; char c[4];} r;
618 size_t j;
620 r.i4 = (ui32_t)arc4random();
621 switch ((j = i & 3)) {
622 case 0: cp[3] = r.c[3]; j = 4;
623 case 3: cp[2] = r.c[2];
624 case 2: cp[1] = r.c[1];
625 default: cp[0] = r.c[0]; break;
627 cp += j;
628 i -= j;
631 #endif
633 b64_encode_buf(&b64, data, length + 3,
634 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
635 ac_free(data);
637 assert(b64.l >= length);
638 b64.s[length] = '\0';
639 NYD_LEAVE;
640 return b64.s;
643 FL si8_t
644 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
646 char *dat, *eptr;
647 sl_i sli;
648 si8_t rv;
649 NYD_ENTER;
651 assert(inlen == 0 || inbuf != NULL);
653 if (inlen == UIZ_MAX)
654 inlen = strlen(inbuf);
656 if (inlen == 0)
657 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
658 else {
659 if ((inlen == 1 && *inbuf == '1') ||
660 !ascncasecmp(inbuf, "true", inlen) ||
661 !ascncasecmp(inbuf, "yes", inlen) ||
662 !ascncasecmp(inbuf, "on", inlen))
663 rv = 1;
664 else if ((inlen == 1 && *inbuf == '0') ||
665 !ascncasecmp(inbuf, "false", inlen) ||
666 !ascncasecmp(inbuf, "no", inlen) ||
667 !ascncasecmp(inbuf, "off", inlen))
668 rv = 0;
669 else {
670 dat = ac_alloc(inlen +1);
671 memcpy(dat, inbuf, inlen);
672 dat[inlen] = '\0';
674 sli = strtol(dat, &eptr, 0);
675 if (*dat != '\0' && *eptr == '\0')
676 rv = (sli != 0);
677 else
678 rv = -1;
680 ac_free(dat);
683 NYD_LEAVE;
684 return rv;
687 FL si8_t
688 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
690 si8_t rv;
691 NYD_ENTER;
693 assert(inlen == 0 || inbuf != NULL);
695 if (inlen == UIZ_MAX)
696 inlen = strlen(inbuf);
698 if (inlen == 0)
699 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
700 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
701 !ascncasecmp(inbuf, "ask-", 4) &&
702 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
703 (options & OPT_INTERACTIVE))
704 rv = getapproval(prompt, rv);
705 NYD_LEAVE;
706 return rv;
709 FL bool_t
710 n_is_all_or_aster(char const *name){
711 bool_t rv;
712 NYD_ENTER;
714 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
715 NYD_LEAVE;
716 return rv;
719 FL time_t
720 n_time_epoch(void)
722 #ifdef HAVE_CLOCK_GETTIME
723 struct timespec ts;
724 #elif defined HAVE_GETTIMEOFDAY
725 struct timeval ts;
726 #endif
727 time_t rv;
728 NYD2_ENTER;
730 #ifdef HAVE_CLOCK_GETTIME
731 clock_gettime(CLOCK_REALTIME, &ts);
732 rv = (time_t)ts.tv_sec;
733 #elif defined HAVE_GETTIMEOFDAY
734 gettimeofday(&ts, NULL);
735 rv = (time_t)ts.tv_sec;
736 #else
737 rv = time(NULL);
738 #endif
739 NYD2_LEAVE;
740 return rv;
743 FL void
744 time_current_update(struct time_current *tc, bool_t full_update)
746 NYD_ENTER;
747 tc->tc_time = n_time_epoch();
748 if (full_update) {
749 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
750 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
751 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
753 NYD_LEAVE;
756 FL uiz_t
757 n_msleep(uiz_t millis, bool_t ignint){
758 uiz_t rv;
759 NYD2_ENTER;
761 #ifdef HAVE_NANOSLEEP
762 /* C99 */{
763 struct timespec ts, trem;
764 int i;
766 ts.tv_sec = millis / 1000;
767 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
769 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
770 ts = trem;
771 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
774 #elif defined HAVE_SLEEP
775 if((millis /= 1000) == 0)
776 millis = 1;
777 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
778 millis = rv;
779 #else
780 # error Configuration should have detected a function for sleeping.
781 #endif
783 NYD2_LEAVE;
784 return rv;
787 FL void
788 n_err(char const *format, ...){
789 va_list ap;
790 NYD2_ENTER;
792 va_start(ap, format);
793 #ifdef HAVE_ERRORS
794 if(options & OPT_INTERACTIVE)
795 n_verr(format, ap);
796 else
797 #endif
799 size_t len;
800 bool_t doname, doflush;
802 doflush = FAL0;
803 while(*format == '\n'){
804 doflush = TRU1;
805 putc('\n', stderr);
806 ++format;
809 if((doname = doflush))
810 a_aux_err_linelen = 0;
812 if((len = strlen(format)) > 0){
813 if(doname || a_aux_err_linelen == 0)
814 fputs(UAGENT ": ", stderr);
815 vfprintf(stderr, format, ap);
817 /* C99 */{
818 size_t i = len;
820 if(format[--len] == '\n'){
821 a_aux_err_linelen = (i -= ++len);
822 break;
824 ++a_aux_err_linelen;
825 }while(len > 0);
829 if(doflush)
830 fflush(stderr);
832 va_end(ap);
833 NYD2_LEAVE;
836 FL void
837 n_verr(char const *format, va_list ap){
838 /* Check use cases of PS_ERRORS_NOTED, too! */
839 #ifdef HAVE_ERRORS
840 struct a_aux_err_node *enp;
841 #endif
842 bool_t doname, doflush;
843 size_t len;
844 NYD2_ENTER;
846 doflush = FAL0;
847 while(*format == '\n'){
848 doflush = TRU1;
849 putc('\n', stderr);
850 ++format;
853 if((doname = doflush)){
854 a_aux_err_linelen = 0;
855 #ifdef HAVE_ERRORS
856 if(options & OPT_INTERACTIVE){
857 if((enp = a_aux_err_tail) != NULL &&
858 (enp->ae_str.s_len > 0 &&
859 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
860 n_string_push_c(&enp->ae_str, '\n');
862 #endif
865 if((len = strlen(format)) == 0)
866 goto jleave;
868 if(doname || a_aux_err_linelen == 0)
869 fputs(UAGENT ": ", stderr);
871 /* C99 */{
872 size_t i = len;
874 if(format[--len] == '\n'){
875 a_aux_err_linelen = (i -= ++len);
876 break;
878 ++a_aux_err_linelen;
879 }while(len > 0);
882 #ifdef HAVE_ERRORS
883 if(!(options & OPT_INTERACTIVE))
884 #endif
885 vfprintf(stderr, format, ap);
886 #ifdef HAVE_ERRORS
887 else{
888 int imax, i;
889 LCTA(ERRORS_MAX > 3);
891 /* Link it into the `errors' message ring */
892 if((enp = a_aux_err_tail) == NULL){
893 jcreat:
894 enp = smalloc(sizeof *enp);
895 enp->ae_next = NULL;
896 n_string_creat(&enp->ae_str);
897 if(a_aux_err_tail != NULL)
898 a_aux_err_tail->ae_next = enp;
899 else
900 a_aux_err_head = enp;
901 a_aux_err_tail = enp;
902 ++a_aux_err_cnt;
903 }else if(doname ||
904 (enp->ae_str.s_len > 0 &&
905 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
906 if(a_aux_err_cnt < ERRORS_MAX)
907 goto jcreat;
909 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
910 a_aux_err_tail->ae_next = enp;
911 a_aux_err_tail = enp;
912 enp->ae_next = NULL;
913 n_string_trunc(&enp->ae_str, 0);
916 # ifdef HAVE_VA_COPY
917 imax = 64;
918 # else
919 imax = LINESIZE;
920 # endif
921 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
922 # ifdef HAVE_VA_COPY
923 va_list vac;
925 va_copy(vac, ap);
926 # else
927 # define vac ap
928 # endif
929 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
930 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
931 # ifdef HAVE_VA_COPY
932 va_end(vac);
933 # else
934 # undef vac
935 # endif
936 if(i <= 0)
937 goto jleave;
938 if(UICMP(z, i, >=, imax)){
939 # ifdef HAVE_VA_COPY
940 /* XXX Check overflow for upcoming LEN+++i! */
941 n_string_trunc(&enp->ae_str, len);
942 continue;
943 # else
944 i = (int)strlen(&enp->ae_str.s_dat[len]);
945 # endif
947 break;
949 n_string_trunc(&enp->ae_str, len + (size_t)i);
951 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
953 #endif /* HAVE_ERRORS */
955 jleave:
956 if(doflush)
957 fflush(stderr);
958 NYD2_LEAVE;
961 FL void
962 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
963 va_list ap;
964 NYD_X;
966 va_start(ap, format);
967 vfprintf(stderr, format, ap);
968 va_end(ap);
969 fflush(stderr);
972 FL void
973 n_perr(char const *msg, int errval){
974 char const *fmt;
975 NYD2_ENTER;
977 if(msg == NULL){
978 fmt = "%s%s\n";
979 msg = "";
980 }else
981 fmt = "%s: %s\n";
983 if(errval == 0)
984 errval = errno;
986 n_err(fmt, msg, strerror(errval));
987 NYD2_LEAVE;
990 FL void
991 n_alert(char const *format, ...){
992 va_list ap;
993 NYD2_ENTER;
995 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
997 va_start(ap, format);
998 n_verr(format, ap);
999 va_end(ap);
1001 n_err("\n");
1002 NYD2_LEAVE;
1005 FL void
1006 n_panic(char const *format, ...){
1007 va_list ap;
1008 NYD2_ENTER;
1010 if(a_aux_err_linelen > 0){
1011 putc('\n', stderr);
1012 a_aux_err_linelen = 0;
1014 fprintf(stderr, UAGENT ": Panic: ");
1016 va_start(ap, format);
1017 vfprintf(stderr, format, ap);
1018 va_end(ap);
1020 putc('\n', stderr);
1021 fflush(stderr);
1022 NYD2_LEAVE;
1023 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1026 #ifdef HAVE_ERRORS
1027 FL int
1028 c_errors(void *v){
1029 char **argv = v;
1030 struct a_aux_err_node *enp;
1031 NYD_ENTER;
1033 if(*argv == NULL)
1034 goto jlist;
1035 if(argv[1] != NULL)
1036 goto jerr;
1037 if(!asccasecmp(*argv, "show"))
1038 goto jlist;
1039 if(!asccasecmp(*argv, "clear"))
1040 goto jclear;
1041 jerr:
1042 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1043 v = NULL;
1044 jleave:
1045 NYD_LEAVE;
1046 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1048 jlist:{
1049 FILE *fp;
1050 size_t i;
1052 if(a_aux_err_head == NULL){
1053 fprintf(stderr, _("The error ring is empty\n"));
1054 goto jleave;
1057 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1058 NULL){
1059 fprintf(stderr, _("tmpfile"));
1060 v = NULL;
1061 goto jleave;
1064 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1065 fprintf(fp, "%4" PRIuZ ". %u B: %s",
1066 ++i, enp->ae_str.s_len, n_string_cp(&enp->ae_str));
1067 /* We don't know wether last string ended with NL; be simple */
1068 putc('\n', fp);
1070 page_or_print(fp, 0);
1071 Fclose(fp);
1073 /* FALLTHRU */
1075 jclear:
1076 a_aux_err_tail = NULL;
1077 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1078 a_aux_err_linelen = 0;
1079 while((enp = a_aux_err_head) != NULL){
1080 a_aux_err_head = enp->ae_next;
1081 n_string_gut(&enp->ae_str);
1082 free(enp);
1084 goto jleave;
1086 #endif /* HAVE_ERRORS */
1088 /* s-it-mode */