mk-conf.sh, mk-mk.in: diversify..
[s-mailx.git] / auxlily.c
blob8d105828731fecf7f6e21dbeedf297259b71572e
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){
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 if(a_aux_err_dirty++ == 0)
800 fputs(UAGENT ": ", stderr);
801 vfprintf(stderr, format, ap);
802 if(strchr(format, '\n') != NULL){ /* TODO */
803 a_aux_err_dirty = 0;
804 fflush(stderr);
807 va_end(ap);
808 NYD2_LEAVE;
811 FL void
812 n_verr(char const *format, va_list ap){
813 /* Check use cases of PS_ERRORS_NOTED, too! */
814 #ifdef HAVE_ERRORS
815 char buf[LINESIZE], *xbuf;
816 int lmax, l;
817 struct a_aux_err_node *enp;
819 LCTA(ERRORS_MAX > 3);
820 #endif
821 NYD2_ENTER;
823 if(a_aux_err_dirty++ == 0)
824 fputs(UAGENT ": ", stderr);
826 #ifdef HAVE_ERRORS
827 if(!(options & OPT_INTERACTIVE))
828 #endif
830 vfprintf(stderr, format, ap);
831 goto jleave;
834 #ifdef HAVE_ERRORS
835 xbuf = buf;
836 lmax = sizeof buf;
837 jredo:
838 l = vsnprintf(xbuf, lmax, format, ap);
839 if (l <= 0)
840 goto jleave;
841 if (UICMP(z, l, >=, lmax)) {
842 /* FIXME Cannot reuse va_list
843 lmax = ++l;
844 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
845 goto jredo;
849 fwrite(xbuf, 1, l, stderr);
851 /* Link it into the `errors' message ring */
852 if((enp = a_aux_err_tail) == NULL){
853 jcreat:
854 enp = scalloc(1, sizeof *enp);
855 if(a_aux_err_tail != NULL)
856 a_aux_err_tail->ae_next = enp;
857 else
858 a_aux_err_head = enp;
859 a_aux_err_tail = enp;
860 ++a_aux_err_cnt;
861 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
862 if(a_aux_err_cnt < ERRORS_MAX)
863 goto jcreat;
865 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
866 a_aux_err_tail->ae_next = enp;
867 a_aux_err_tail = enp;
868 free(enp->ae_str.s);
869 memset(enp, 0, sizeof *enp);
872 n_str_add_buf(&enp->ae_str, xbuf, l);
874 if(xbuf != buf)
875 free(xbuf);
876 #endif /* HAVE_ERRORS */
878 jleave:
879 /* If the format ends with newline, be clean again */
880 /* C99 */{
881 size_t i = strlen(format);
883 if(i > 0 && format[i - 1] == '\n'){
884 fflush(stderr);
885 a_aux_err_dirty = 0;
888 NYD2_LEAVE;
891 FL void
892 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
893 va_list ap;
894 NYD_X;
896 va_start(ap, format);
897 vfprintf(stderr, format, ap);
898 va_end(ap);
899 fflush(stderr);
902 FL void
903 n_perr(char const *msg, int errval){
904 char const *fmt;
905 NYD2_ENTER;
907 if(msg == NULL){
908 fmt = "%s%s\n";
909 msg = "";
910 }else
911 fmt = "%s: %s\n";
913 if(errval == 0)
914 errval = errno;
916 n_err(fmt, msg, strerror(errval));
917 NYD2_LEAVE;
920 FL void
921 n_alert(char const *format, ...){
922 va_list ap;
923 NYD2_ENTER;
925 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
927 va_start(ap, format);
928 n_verr(format, ap);
929 va_end(ap);
931 n_err("\n");
932 NYD2_LEAVE;
935 FL void
936 n_panic(char const *format, ...){
937 va_list ap;
938 NYD2_ENTER;
940 if(a_aux_err_dirty > 0){
941 putc('\n', stderr);
942 a_aux_err_dirty = 0;
944 fprintf(stderr, UAGENT ": Panic: ");
946 va_start(ap, format);
947 vfprintf(stderr, format, ap);
948 va_end(ap);
950 putc('\n', stderr);
951 fflush(stderr);
952 NYD2_LEAVE;
953 abort(); /* Was exit(EXIT_ERR); for a while, but no */
956 #ifdef HAVE_ERRORS
957 FL int
958 c_errors(void *v){
959 char **argv = v;
960 struct a_aux_err_node *enp;
961 NYD_ENTER;
963 if(*argv == NULL)
964 goto jlist;
965 if(argv[1] != NULL)
966 goto jerr;
967 if(!asccasecmp(*argv, "show"))
968 goto jlist;
969 if(!asccasecmp(*argv, "clear"))
970 goto jclear;
971 jerr:
972 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
973 v = NULL;
974 jleave:
975 NYD_LEAVE;
976 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
978 jlist:{
979 FILE *fp;
980 size_t i;
982 if(a_aux_err_head == NULL){
983 fprintf(stderr, _("The error ring is empty\n"));
984 goto jleave;
987 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
988 NULL){
989 fprintf(stderr, _("tmpfile"));
990 v = NULL;
991 goto jleave;
994 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
995 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
996 ++i, enp->ae_str.l, enp->ae_str.s);
997 /* We don't know wether last string ended with NL; be simple */
998 putc('\n', fp);
1000 page_or_print(fp, 0);
1001 Fclose(fp);
1003 /* FALLTHRU */
1005 jclear:
1006 a_aux_err_tail = NULL;
1007 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1008 while((enp = a_aux_err_head) != NULL){
1009 a_aux_err_head = enp->ae_next;
1010 free(enp->ae_str.s);
1011 free(enp);
1013 goto jleave;
1015 #endif /* HAVE_ERRORS */
1017 /* s-it-mode */