mk-release.inc: auto-append checksum file to announcement mail
[s-mailx.git] / auxlily.c
blob533035d56aeb51698eb27a53338f642260a1c9d7
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 = 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 % 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 = (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') >= NELEM(atoi16) ||
355 (i2 = (ui8_t)hex[1] - '0') >= 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[NELEM(primes) / 4] ? 0
417 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
418 : NELEM(primes) / 2));
420 if ((mprime = primes[i]) > n)
421 break;
422 while (++i < NELEM(primes));
423 if (i == NELEM(primes) && mprime < n)
424 mprime = n;
425 NYD_LEAVE;
426 return mprime;
429 FL char *
430 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
431 { /* FIXME getprompt must mb->wc->mb+reset seq! */
432 static char buf[PROMPT_BUFFER_SIZE];
434 char *cp;
435 char const *ccp_base, *ccp;
436 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
437 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
438 NYD_ENTER;
440 /* No other place to place this */
441 #ifdef HAVE_ERRORS
442 if (options & OPT_INTERACTIVE) {
443 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
444 pstate |= PS_ERRORS_NOTED;
445 fprintf(stderr, _("There are new messages in the error message ring "
446 "(denoted by #ERR#)\n"
447 " The `errors' command manages this message ring\n"));
450 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
451 a_aux_err_cnt_noted = a_aux_err_cnt;
452 } else
453 trigger = FAL0;
454 #endif
456 cp = buf;
457 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
458 #ifdef HAVE_ERRORS
459 if (trigger)
460 ccp_base = "";
461 else
462 #endif
463 goto jleave;
465 #ifdef HAVE_ERRORS
466 if (trigger)
467 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
468 #endif
469 NATCH_CHAR( cclen_base = strlen(ccp_base); )
471 dfmaxlen = 0; /* keep CC happy */
472 trigger = FAL0;
473 jredo:
474 ccp = ccp_base;
475 NATCH_CHAR( cclen = cclen_base; )
476 maxlen = sizeof(buf) -1;
478 for (;;) {
479 size_t l;
480 int c;
482 if (maxlen == 0)
483 goto jleave;
484 #ifdef HAVE_NATCH_CHAR
485 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
486 if (c <= 0) {
487 mblen(NULL, 0);
488 if (c < 0) {
489 *buf = '?';
490 cp = buf + 1;
491 goto jleave;
493 break;
494 } else if ((l = c) > 1) {
495 if (trigger) {
496 memcpy(cp, ccp, l);
497 cp += l;
499 ccp += l;
500 maxlen -= l;
501 continue;
502 } else
503 #endif
504 if ((c = n_shexp_expand_escape(&ccp, TRU1)) > 0) {
505 if (trigger)
506 *cp++ = (char)c;
507 --maxlen;
508 continue;
510 if (c == 0 || c == PROMPT_STOP)
511 break;
513 if (trigger) {
514 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
515 if (a == NULL)
516 a = "";
517 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
518 cp += l;
519 maxlen -= l;
520 dfmaxlen -= l;
525 if (!trigger) {
526 trigger = TRU1;
527 dfmaxlen = maxlen;
528 goto jredo;
530 jleave:
531 *cp = '\0';
532 NYD_LEAVE;
533 return buf;
536 FL char const *
537 n_getdeadletter(void){
538 char const *cp_base, *cp;
539 NYD_ENTER;
541 cp_base = NULL;
542 jredo:
543 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
544 if(cp == NULL || strlen(cp) >= PATH_MAX){
545 if(cp_base == NULL){
546 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
547 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
548 ok_vclear(DEAD);
549 goto jredo;
550 }else{
551 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
552 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
555 NYD_LEAVE;
556 return cp;
559 FL char *
560 nodename(int mayoverride)
562 static char *sys_hostname, *hostname; /* XXX free-at-exit */
564 struct utsname ut;
565 char *hn;
566 #ifdef HAVE_SOCKETS
567 # ifdef HAVE_GETADDRINFO
568 struct addrinfo hints, *res;
569 # else
570 struct hostent *hent;
571 # endif
572 #endif
573 NYD_ENTER;
575 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
577 } else if ((hn = sys_hostname) == NULL) {
578 uname(&ut);
579 hn = ut.nodename;
580 #ifdef HAVE_SOCKETS
581 # ifdef HAVE_GETADDRINFO
582 memset(&hints, 0, sizeof hints);
583 hints.ai_family = AF_UNSPEC;
584 hints.ai_flags = AI_CANONNAME;
585 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
586 if (res->ai_canonname != NULL) {
587 size_t l = strlen(res->ai_canonname) +1;
589 hn = ac_alloc(l);
590 memcpy(hn, res->ai_canonname, l);
592 freeaddrinfo(res);
594 # else
595 hent = gethostbyname(hn);
596 if (hent != NULL)
597 hn = hent->h_name;
598 # endif
599 #endif
600 sys_hostname = sstrdup(hn);
601 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
602 if (hn != ut.nodename)
603 ac_free(hn);
604 #endif
605 hn = sys_hostname;
608 if (hostname != NULL && hostname != sys_hostname)
609 free(hostname);
610 hostname = sstrdup(hn);
611 NYD_LEAVE;
612 return hostname;
615 FL char *
616 getrandstring(size_t length)
618 struct str b64;
619 char *data;
620 size_t i;
621 NYD_ENTER;
623 #ifndef HAVE_POSIX_RANDOM
624 if (_rand == NULL)
625 _rand_init();
626 #endif
628 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
629 * with PAD stripped is still longer than what the user requests, easy way */
630 data = ac_alloc(i = length + 3);
632 #ifndef HAVE_POSIX_RANDOM
633 while (i-- > 0)
634 data[i] = (char)_rand_get8();
635 #else
636 { char *cp = data;
638 while (i > 0) {
639 union {ui32_t i4; char c[4];} r;
640 size_t j;
642 r.i4 = (ui32_t)arc4random();
643 switch ((j = i & 3)) {
644 case 0: cp[3] = r.c[3]; j = 4;
645 case 3: cp[2] = r.c[2];
646 case 2: cp[1] = r.c[1];
647 default: cp[0] = r.c[0]; break;
649 cp += j;
650 i -= j;
653 #endif
655 b64_encode_buf(&b64, data, length + 3,
656 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
657 ac_free(data);
659 assert(b64.l >= length);
660 b64.s[length] = '\0';
661 NYD_LEAVE;
662 return b64.s;
665 FL si8_t
666 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
668 char *dat, *eptr;
669 sl_i sli;
670 si8_t rv;
671 NYD_ENTER;
673 assert(inlen == 0 || inbuf != NULL);
675 if (inlen == UIZ_MAX)
676 inlen = strlen(inbuf);
678 if (inlen == 0)
679 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
680 else {
681 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
682 !ascncasecmp(inbuf, "true", inlen) ||
683 !ascncasecmp(inbuf, "yes", inlen) ||
684 !ascncasecmp(inbuf, "on", inlen))
685 rv = 1;
686 else if ((inlen == 1 &&
687 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
688 !ascncasecmp(inbuf, "false", inlen) ||
689 !ascncasecmp(inbuf, "no", inlen) ||
690 !ascncasecmp(inbuf, "off", inlen))
691 rv = 0;
692 else {
693 dat = ac_alloc(inlen +1);
694 memcpy(dat, inbuf, inlen);
695 dat[inlen] = '\0';
697 sli = strtol(dat, &eptr, 0);
698 if (*dat != '\0' && *eptr == '\0')
699 rv = (sli != 0);
700 else
701 rv = -1;
703 ac_free(dat);
706 NYD_LEAVE;
707 return rv;
710 FL si8_t
711 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
713 si8_t rv;
714 NYD_ENTER;
716 assert(inlen == 0 || inbuf != NULL);
718 if (inlen == UIZ_MAX)
719 inlen = strlen(inbuf);
721 if (inlen == 0)
722 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
723 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
724 !ascncasecmp(inbuf, "ask-", 4) &&
725 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
726 (options & OPT_INTERACTIVE))
727 rv = getapproval(prompt, rv);
728 NYD_LEAVE;
729 return rv;
732 FL bool_t
733 n_is_all_or_aster(char const *name){
734 bool_t rv;
735 NYD_ENTER;
737 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
738 NYD_LEAVE;
739 return rv;
742 FL time_t
743 n_time_epoch(void)
745 #ifdef HAVE_CLOCK_GETTIME
746 struct timespec ts;
747 #elif defined HAVE_GETTIMEOFDAY
748 struct timeval ts;
749 #endif
750 time_t rv;
751 char const *cp;
752 NYD2_ENTER;
754 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){ /* TODO */
755 /* TODO This is marked "posnum", b and therefore 0<=X<=UINT_MAX.
756 * TODO This means we have a Sun, 07 Feb 2106 07:28:15 +0100 problem.
757 * TODO Therefore we need a num_ui64= type in v15 */
758 rv = (time_t)strtoul(cp, NULL, 0);
759 }else{
760 #ifdef HAVE_CLOCK_GETTIME
761 clock_gettime(CLOCK_REALTIME, &ts);
762 rv = (time_t)ts.tv_sec;
763 #elif defined HAVE_GETTIMEOFDAY
764 gettimeofday(&ts, NULL);
765 rv = (time_t)ts.tv_sec;
766 #else
767 rv = time(NULL);
768 #endif
770 NYD2_LEAVE;
771 return rv;
774 FL void
775 time_current_update(struct time_current *tc, bool_t full_update)
777 NYD_ENTER;
778 tc->tc_time = n_time_epoch();
779 if (full_update) {
780 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
781 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
782 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
784 NYD_LEAVE;
787 FL uiz_t
788 n_msleep(uiz_t millis, bool_t ignint){
789 uiz_t rv;
790 NYD2_ENTER;
792 #ifdef HAVE_NANOSLEEP
793 /* C99 */{
794 struct timespec ts, trem;
795 int i;
797 ts.tv_sec = millis / 1000;
798 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
800 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
801 ts = trem;
802 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
805 #elif defined HAVE_SLEEP
806 if((millis /= 1000) == 0)
807 millis = 1;
808 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
809 millis = rv;
810 #else
811 # error Configuration should have detected a function for sleeping.
812 #endif
814 NYD2_LEAVE;
815 return rv;
818 FL void
819 n_err(char const *format, ...){
820 va_list ap;
821 NYD2_ENTER;
823 va_start(ap, format);
824 #ifdef HAVE_ERRORS
825 if(options & OPT_INTERACTIVE)
826 n_verr(format, ap);
827 else
828 #endif
830 size_t len;
831 bool_t doname, doflush;
833 doflush = FAL0;
834 while(*format == '\n'){
835 doflush = TRU1;
836 putc('\n', stderr);
837 ++format;
840 if((doname = doflush))
841 a_aux_err_linelen = 0;
843 if((len = strlen(format)) > 0){
844 if(doname || a_aux_err_linelen == 0)
845 fputs(VAL_UAGENT ": ", stderr);
846 vfprintf(stderr, format, ap);
848 /* C99 */{
849 size_t i = len;
851 if(format[--len] == '\n'){
852 a_aux_err_linelen = (i -= ++len);
853 break;
855 ++a_aux_err_linelen;
856 }while(len > 0);
860 if(doflush)
861 fflush(stderr);
863 va_end(ap);
864 NYD2_LEAVE;
867 FL void
868 n_verr(char const *format, va_list ap){
869 /* Check use cases of PS_ERRORS_NOTED, too! */
870 #ifdef HAVE_ERRORS
871 struct a_aux_err_node *enp;
872 #endif
873 bool_t doname, doflush;
874 size_t len;
875 NYD2_ENTER;
877 doflush = FAL0;
878 while(*format == '\n'){
879 doflush = TRU1;
880 putc('\n', stderr);
881 ++format;
884 if((doname = doflush)){
885 a_aux_err_linelen = 0;
886 #ifdef HAVE_ERRORS
887 if(options & OPT_INTERACTIVE){
888 if((enp = a_aux_err_tail) != NULL &&
889 (enp->ae_str.s_len > 0 &&
890 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
891 n_string_push_c(&enp->ae_str, '\n');
893 #endif
896 if((len = strlen(format)) == 0)
897 goto jleave;
899 if(doname || a_aux_err_linelen == 0)
900 fputs(VAL_UAGENT ": ", stderr);
902 /* C99 */{
903 size_t i = len;
905 if(format[--len] == '\n'){
906 a_aux_err_linelen = (i -= ++len);
907 break;
909 ++a_aux_err_linelen;
910 }while(len > 0);
913 #ifdef HAVE_ERRORS
914 if(!(options & OPT_INTERACTIVE))
915 #endif
916 vfprintf(stderr, format, ap);
917 #ifdef HAVE_ERRORS
918 else{
919 int imax, i;
920 LCTA(ERRORS_MAX > 3);
922 /* Link it into the `errors' message ring */
923 if((enp = a_aux_err_tail) == NULL){
924 jcreat:
925 enp = smalloc(sizeof *enp);
926 enp->ae_next = NULL;
927 n_string_creat(&enp->ae_str);
928 if(a_aux_err_tail != NULL)
929 a_aux_err_tail->ae_next = enp;
930 else
931 a_aux_err_head = enp;
932 a_aux_err_tail = enp;
933 ++a_aux_err_cnt;
934 }else if(doname ||
935 (enp->ae_str.s_len > 0 &&
936 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
937 if(a_aux_err_cnt < ERRORS_MAX)
938 goto jcreat;
940 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
941 a_aux_err_tail->ae_next = enp;
942 a_aux_err_tail = enp;
943 enp->ae_next = NULL;
944 n_string_trunc(&enp->ae_str, 0);
947 # ifdef HAVE_VA_COPY
948 imax = 64;
949 # else
950 imax = LINESIZE;
951 # endif
952 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
953 # ifdef HAVE_VA_COPY
954 va_list vac;
956 va_copy(vac, ap);
957 # else
958 # define vac ap
959 # endif
960 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
961 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
962 # ifdef HAVE_VA_COPY
963 va_end(vac);
964 # else
965 # undef vac
966 # endif
967 if(i <= 0)
968 goto jleave;
969 if(UICMP(z, i, >=, imax)){
970 # ifdef HAVE_VA_COPY
971 /* XXX Check overflow for upcoming LEN+++i! */
972 n_string_trunc(&enp->ae_str, len);
973 continue;
974 # else
975 i = (int)strlen(&enp->ae_str.s_dat[len]);
976 # endif
978 break;
980 n_string_trunc(&enp->ae_str, len + (size_t)i);
982 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
984 #endif /* HAVE_ERRORS */
986 jleave:
987 if(doflush)
988 fflush(stderr);
989 NYD2_LEAVE;
992 FL void
993 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
994 va_list ap;
995 NYD_X;
997 va_start(ap, format);
998 vfprintf(stderr, format, ap);
999 va_end(ap);
1000 fflush(stderr);
1003 FL void
1004 n_perr(char const *msg, int errval){
1005 char const *fmt;
1006 NYD2_ENTER;
1008 if(msg == NULL){
1009 fmt = "%s%s\n";
1010 msg = "";
1011 }else
1012 fmt = "%s: %s\n";
1014 if(errval == 0)
1015 errval = errno;
1017 n_err(fmt, msg, strerror(errval));
1018 NYD2_LEAVE;
1021 FL void
1022 n_alert(char const *format, ...){
1023 va_list ap;
1024 NYD2_ENTER;
1026 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1028 va_start(ap, format);
1029 n_verr(format, ap);
1030 va_end(ap);
1032 n_err("\n");
1033 NYD2_LEAVE;
1036 FL void
1037 n_panic(char const *format, ...){
1038 va_list ap;
1039 NYD2_ENTER;
1041 if(a_aux_err_linelen > 0){
1042 putc('\n', stderr);
1043 a_aux_err_linelen = 0;
1045 fprintf(stderr, VAL_UAGENT ": Panic: ");
1047 va_start(ap, format);
1048 vfprintf(stderr, format, ap);
1049 va_end(ap);
1051 putc('\n', stderr);
1052 fflush(stderr);
1053 NYD2_LEAVE;
1054 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1057 #ifdef HAVE_ERRORS
1058 FL int
1059 c_errors(void *v){
1060 char **argv = v;
1061 struct a_aux_err_node *enp;
1062 NYD_ENTER;
1064 if(*argv == NULL)
1065 goto jlist;
1066 if(argv[1] != NULL)
1067 goto jerr;
1068 if(!asccasecmp(*argv, "show"))
1069 goto jlist;
1070 if(!asccasecmp(*argv, "clear"))
1071 goto jclear;
1072 jerr:
1073 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1074 v = NULL;
1075 jleave:
1076 NYD_LEAVE;
1077 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1079 jlist:{
1080 FILE *fp;
1081 size_t i;
1083 if(a_aux_err_head == NULL){
1084 fprintf(stderr, _("The error ring is empty\n"));
1085 goto jleave;
1088 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1089 NULL){
1090 fprintf(stderr, _("tmpfile"));
1091 v = NULL;
1092 goto jleave;
1095 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1096 fprintf(fp, "%4" PRIuZ ". %u B: %s",
1097 ++i, enp->ae_str.s_len, n_string_cp(&enp->ae_str));
1098 /* We don't know whether last string ended with NL; be simple */
1099 putc('\n', fp);
1101 page_or_print(fp, 0);
1102 Fclose(fp);
1104 /* FALLTHRU */
1106 jclear:
1107 a_aux_err_tail = NULL;
1108 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1109 a_aux_err_linelen = 0;
1110 while((enp = a_aux_err_head) != NULL){
1111 a_aux_err_head = enp->ae_next;
1112 n_string_gut(&enp->ae_str);
1113 free(enp);
1115 goto jleave;
1117 #endif /* HAVE_ERRORS */
1119 /* s-it-mode */