Move c_flag() etc. to cmd2.c: they belong
[s-mailx.git] / auxlily.c
bloba07b295adac02bf9e7c99c8bedfb0013509175a8
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 ui32_t
386 torek_ihashn(char const *dat, size_t len){
387 /* See torek_hash() */
388 char c;
389 ui32_t h;
390 NYD_ENTER;
392 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
393 h = (h * 33) + lowerconv(c);
394 NYD_LEAVE;
395 return h;
398 FL unsigned
399 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
401 unsigned h = 0, g;
402 NYD_ENTER;
404 cp--;
405 while (*++cp) {
406 h = (h << 4 & 0xffffffff) + (*cp&0377);
407 if ((g = h & 0xf0000000) != 0) {
408 h = h ^ g >> 24;
409 h = h ^ g;
412 NYD_LEAVE;
413 return h;
416 FL ui32_t
417 nextprime(ui32_t n)
419 static ui32_t const primes[] = {
420 5, 11, 23, 47, 97, 157, 283,
421 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
422 131071, 262139, 524287, 1048573, 2097143, 4194301,
423 8388593, 16777213, 33554393, 67108859, 134217689,
424 268435399, 536870909, 1073741789, 2147483647
427 ui32_t i, mprime;
428 NYD_ENTER;
430 i = (n < primes[NELEM(primes) / 4] ? 0
431 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
432 : NELEM(primes) / 2));
434 if ((mprime = primes[i]) > n)
435 break;
436 while (++i < NELEM(primes));
437 if (i == NELEM(primes) && mprime < n)
438 mprime = n;
439 NYD_LEAVE;
440 return mprime;
443 FL char *
444 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
445 { /* FIXME getprompt must mb->wc->mb+reset seq! */
446 static char buf[PROMPT_BUFFER_SIZE];
448 char *cp;
449 char const *ccp_base, *ccp;
450 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
451 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
452 NYD_ENTER;
454 /* No other place to place this */
455 #ifdef HAVE_ERRORS
456 if (options & OPT_INTERACTIVE) {
457 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
458 pstate |= PS_ERRORS_NOTED;
459 fprintf(stderr, _("There are new messages in the error message ring "
460 "(denoted by \"#ERR#\")\n"
461 " The `errors' command manages this message ring\n"));
464 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
465 a_aux_err_cnt_noted = a_aux_err_cnt;
466 } else
467 trigger = FAL0;
468 #endif
470 cp = buf;
471 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
472 #ifdef HAVE_ERRORS
473 if (trigger)
474 ccp_base = "";
475 else
476 #endif
477 goto jleave;
479 #ifdef HAVE_ERRORS
480 if (trigger)
481 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
482 #endif
483 NATCH_CHAR( cclen_base = strlen(ccp_base); )
485 dfmaxlen = 0; /* keep CC happy */
486 trigger = FAL0;
487 jredo:
488 ccp = ccp_base;
489 NATCH_CHAR( cclen = cclen_base; )
490 maxlen = sizeof(buf) -1;
492 for (;;) {
493 size_t l;
494 int c;
496 if (maxlen == 0)
497 goto jleave;
498 #ifdef HAVE_NATCH_CHAR
499 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
500 if (c <= 0) {
501 mblen(NULL, 0);
502 if (c < 0) {
503 *buf = '?';
504 cp = buf + 1;
505 goto jleave;
507 break;
508 } else if ((l = c) > 1) {
509 if (trigger) {
510 memcpy(cp, ccp, l);
511 cp += l;
513 ccp += l;
514 maxlen -= l;
515 continue;
516 } else
517 #endif
518 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
519 if (trigger)
520 *cp++ = (char)c;
521 --maxlen;
522 continue;
524 if (c == 0 || c == PROMPT_STOP)
525 break;
527 if (trigger) {
528 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
529 if (a == NULL)
530 a = "";
531 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
532 cp += l;
533 maxlen -= l;
534 dfmaxlen -= l;
539 if (!trigger) {
540 trigger = TRU1;
541 dfmaxlen = maxlen;
542 goto jredo;
544 jleave:
545 *cp = '\0';
546 NYD_LEAVE;
547 return buf;
550 FL char *
551 nodename(int mayoverride)
553 static char *sys_hostname, *hostname; /* XXX free-at-exit */
555 struct utsname ut;
556 char *hn;
557 #ifdef HAVE_SOCKETS
558 # ifdef HAVE_GETADDRINFO
559 struct addrinfo hints, *res;
560 # else
561 struct hostent *hent;
562 # endif
563 #endif
564 NYD_ENTER;
566 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
568 } else if ((hn = sys_hostname) == NULL) {
569 uname(&ut);
570 hn = ut.nodename;
571 #ifdef HAVE_SOCKETS
572 # ifdef HAVE_GETADDRINFO
573 memset(&hints, 0, sizeof hints);
574 hints.ai_family = AF_UNSPEC;
575 hints.ai_flags = AI_CANONNAME;
576 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
577 if (res->ai_canonname != NULL) {
578 size_t l = strlen(res->ai_canonname) +1;
580 hn = ac_alloc(l);
581 memcpy(hn, res->ai_canonname, l);
583 freeaddrinfo(res);
585 # else
586 hent = gethostbyname(hn);
587 if (hent != NULL)
588 hn = hent->h_name;
589 # endif
590 #endif
591 sys_hostname = sstrdup(hn);
592 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
593 if (hn != ut.nodename)
594 ac_free(hn);
595 #endif
596 hn = sys_hostname;
599 if (hostname != NULL && hostname != sys_hostname)
600 free(hostname);
601 hostname = sstrdup(hn);
602 NYD_LEAVE;
603 return hostname;
606 FL char *
607 getrandstring(size_t length)
609 struct str b64;
610 char *data;
611 size_t i;
612 NYD_ENTER;
614 #ifndef HAVE_POSIX_RANDOM
615 if (_rand == NULL)
616 _rand_init();
617 #endif
619 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
620 * with PAD stripped is still longer than what the user requests, easy way */
621 data = ac_alloc(i = length + 3);
623 #ifndef HAVE_POSIX_RANDOM
624 while (i-- > 0)
625 data[i] = (char)_rand_get8();
626 #else
627 { char *cp = data;
629 while (i > 0) {
630 union {ui32_t i4; char c[4];} r;
631 size_t j;
633 r.i4 = (ui32_t)arc4random();
634 switch ((j = i & 3)) {
635 case 0: cp[3] = r.c[3]; j = 4;
636 case 3: cp[2] = r.c[2];
637 case 2: cp[1] = r.c[1];
638 default: cp[0] = r.c[0]; break;
640 cp += j;
641 i -= j;
644 #endif
646 b64_encode_buf(&b64, data, length + 3,
647 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
648 ac_free(data);
650 assert(b64.l >= length);
651 b64.s[length] = '\0';
652 NYD_LEAVE;
653 return b64.s;
656 FL si8_t
657 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
659 char *dat, *eptr;
660 sl_i sli;
661 si8_t rv;
662 NYD_ENTER;
664 assert(inlen == 0 || inbuf != NULL);
666 if (inlen == UIZ_MAX)
667 inlen = strlen(inbuf);
669 if (inlen == 0)
670 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
671 else {
672 if ((inlen == 1 && *inbuf == '1') ||
673 !ascncasecmp(inbuf, "true", inlen) ||
674 !ascncasecmp(inbuf, "yes", inlen) ||
675 !ascncasecmp(inbuf, "on", inlen))
676 rv = 1;
677 else if ((inlen == 1 && *inbuf == '0') ||
678 !ascncasecmp(inbuf, "false", inlen) ||
679 !ascncasecmp(inbuf, "no", inlen) ||
680 !ascncasecmp(inbuf, "off", inlen))
681 rv = 0;
682 else {
683 dat = ac_alloc(inlen +1);
684 memcpy(dat, inbuf, inlen);
685 dat[inlen] = '\0';
687 sli = strtol(dat, &eptr, 0);
688 if (*dat != '\0' && *eptr == '\0')
689 rv = (sli != 0);
690 else
691 rv = -1;
693 ac_free(dat);
696 NYD_LEAVE;
697 return rv;
700 FL si8_t
701 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
703 si8_t rv;
704 NYD_ENTER;
706 assert(inlen == 0 || inbuf != NULL);
708 if (inlen == UIZ_MAX)
709 inlen = strlen(inbuf);
711 if (inlen == 0)
712 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
713 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
714 !ascncasecmp(inbuf, "ask-", 4) &&
715 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
716 (options & OPT_INTERACTIVE))
717 rv = getapproval(prompt, rv);
718 NYD_LEAVE;
719 return rv;
722 FL bool_t
723 n_is_all_or_aster(char const *name){
724 bool_t rv;
725 NYD_ENTER;
727 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
728 NYD_LEAVE;
729 return rv;
732 FL time_t
733 n_time_epoch(void)
735 #ifdef HAVE_CLOCK_GETTIME
736 struct timespec ts;
737 #elif defined HAVE_GETTIMEOFDAY
738 struct timeval ts;
739 #endif
740 time_t rv;
741 NYD2_ENTER;
743 #ifdef HAVE_CLOCK_GETTIME
744 clock_gettime(CLOCK_REALTIME, &ts);
745 rv = (time_t)ts.tv_sec;
746 #elif defined HAVE_GETTIMEOFDAY
747 gettimeofday(&ts, NULL);
748 rv = (time_t)ts.tv_sec;
749 #else
750 rv = time(NULL);
751 #endif
752 NYD2_LEAVE;
753 return rv;
756 FL void
757 time_current_update(struct time_current *tc, bool_t full_update)
759 NYD_ENTER;
760 tc->tc_time = n_time_epoch();
761 if (full_update) {
762 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
763 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
764 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
766 NYD_LEAVE;
769 FL uiz_t
770 n_msleep(uiz_t millis, bool_t ignint){
771 uiz_t rv;
772 NYD2_ENTER;
774 #ifdef HAVE_NANOSLEEP
775 /* C99 */{
776 struct timespec ts, trem;
777 int i;
779 ts.tv_sec = millis / 1000;
780 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
782 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
783 ts = trem;
784 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
787 #elif defined HAVE_SLEEP
788 if((millis /= 1000) == 0)
789 millis = 1;
790 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
791 millis = rv;
792 #else
793 # error Configuration should have detected a function for sleeping.
794 #endif
796 NYD2_LEAVE;
797 return rv;
800 FL void
801 n_err(char const *format, ...){
802 va_list ap;
803 NYD2_ENTER;
805 va_start(ap, format);
806 #ifdef HAVE_ERRORS
807 if(options & OPT_INTERACTIVE)
808 n_verr(format, ap);
809 else
810 #endif
812 size_t len;
813 bool_t doname, doflush;
815 doflush = FAL0;
816 while(*format == '\n'){
817 doflush = TRU1;
818 putc('\n', stderr);
819 ++format;
822 if((doname = doflush))
823 a_aux_err_linelen = 0;
825 if((len = strlen(format)) > 0){
826 if(doname || a_aux_err_linelen == 0)
827 fputs(UAGENT ": ", stderr);
828 vfprintf(stderr, format, ap);
830 /* C99 */{
831 size_t i = len;
833 if(format[--len] == '\n'){
834 a_aux_err_linelen = (i -= ++len);
835 break;
837 ++a_aux_err_linelen;
838 }while(len > 0);
842 if(doflush)
843 fflush(stderr);
845 va_end(ap);
846 NYD2_LEAVE;
849 FL void
850 n_verr(char const *format, va_list ap){
851 /* Check use cases of PS_ERRORS_NOTED, too! */
852 #ifdef HAVE_ERRORS
853 struct a_aux_err_node *enp;
854 #endif
855 bool_t doname, doflush;
856 size_t len;
857 NYD2_ENTER;
859 doflush = FAL0;
860 while(*format == '\n'){
861 doflush = TRU1;
862 putc('\n', stderr);
863 ++format;
866 if((doname = doflush)){
867 a_aux_err_linelen = 0;
868 #ifdef HAVE_ERRORS
869 if(options & OPT_INTERACTIVE){
870 if((enp = a_aux_err_tail) != NULL &&
871 (enp->ae_str.s_len > 0 &&
872 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
873 n_string_push_c(&enp->ae_str, '\n');
875 #endif
878 if((len = strlen(format)) == 0)
879 goto jleave;
881 if(doname || a_aux_err_linelen == 0)
882 fputs(UAGENT ": ", stderr);
884 /* C99 */{
885 size_t i = len;
887 if(format[--len] == '\n'){
888 a_aux_err_linelen = (i -= ++len);
889 break;
891 ++a_aux_err_linelen;
892 }while(len > 0);
895 #ifdef HAVE_ERRORS
896 if(!(options & OPT_INTERACTIVE))
897 #endif
898 vfprintf(stderr, format, ap);
899 #ifdef HAVE_ERRORS
900 else{
901 int imax, i;
902 LCTA(ERRORS_MAX > 3);
904 /* Link it into the `errors' message ring */
905 if((enp = a_aux_err_tail) == NULL){
906 jcreat:
907 enp = smalloc(sizeof *enp);
908 enp->ae_next = NULL;
909 n_string_creat(&enp->ae_str);
910 if(a_aux_err_tail != NULL)
911 a_aux_err_tail->ae_next = enp;
912 else
913 a_aux_err_head = enp;
914 a_aux_err_tail = enp;
915 ++a_aux_err_cnt;
916 }else if(doname ||
917 (enp->ae_str.s_len > 0 &&
918 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
919 if(a_aux_err_cnt < ERRORS_MAX)
920 goto jcreat;
922 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
923 a_aux_err_tail->ae_next = enp;
924 a_aux_err_tail = enp;
925 enp->ae_next = NULL;
926 n_string_trunc(&enp->ae_str, 0);
929 # ifdef HAVE_VA_COPY
930 imax = 64;
931 # else
932 imax = LINESIZE;
933 # endif
934 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
935 # ifdef HAVE_VA_COPY
936 va_list vac;
938 va_copy(vac, ap);
939 # else
940 # define vac ap
941 # endif
942 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
943 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
944 # ifdef HAVE_VA_COPY
945 va_end(vac);
946 # else
947 # undef vac
948 # endif
949 if(i <= 0)
950 goto jleave;
951 if(UICMP(z, i, >=, imax)){
952 # ifdef HAVE_VA_COPY
953 /* XXX Check overflow for upcoming LEN+++i! */
954 n_string_trunc(&enp->ae_str, len);
955 continue;
956 # else
957 i = (int)strlen(&enp->ae_str.s_dat[len]);
958 # endif
960 break;
962 n_string_trunc(&enp->ae_str, len + (size_t)i);
964 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
966 #endif /* HAVE_ERRORS */
968 jleave:
969 if(doflush)
970 fflush(stderr);
971 NYD2_LEAVE;
974 FL void
975 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
976 va_list ap;
977 NYD_X;
979 va_start(ap, format);
980 vfprintf(stderr, format, ap);
981 va_end(ap);
982 fflush(stderr);
985 FL void
986 n_perr(char const *msg, int errval){
987 char const *fmt;
988 NYD2_ENTER;
990 if(msg == NULL){
991 fmt = "%s%s\n";
992 msg = "";
993 }else
994 fmt = "%s: %s\n";
996 if(errval == 0)
997 errval = errno;
999 n_err(fmt, msg, strerror(errval));
1000 NYD2_LEAVE;
1003 FL void
1004 n_alert(char const *format, ...){
1005 va_list ap;
1006 NYD2_ENTER;
1008 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1010 va_start(ap, format);
1011 n_verr(format, ap);
1012 va_end(ap);
1014 n_err("\n");
1015 NYD2_LEAVE;
1018 FL void
1019 n_panic(char const *format, ...){
1020 va_list ap;
1021 NYD2_ENTER;
1023 if(a_aux_err_linelen > 0){
1024 putc('\n', stderr);
1025 a_aux_err_linelen = 0;
1027 fprintf(stderr, UAGENT ": Panic: ");
1029 va_start(ap, format);
1030 vfprintf(stderr, format, ap);
1031 va_end(ap);
1033 putc('\n', stderr);
1034 fflush(stderr);
1035 NYD2_LEAVE;
1036 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1039 #ifdef HAVE_ERRORS
1040 FL int
1041 c_errors(void *v){
1042 char **argv = v;
1043 struct a_aux_err_node *enp;
1044 NYD_ENTER;
1046 if(*argv == NULL)
1047 goto jlist;
1048 if(argv[1] != NULL)
1049 goto jerr;
1050 if(!asccasecmp(*argv, "show"))
1051 goto jlist;
1052 if(!asccasecmp(*argv, "clear"))
1053 goto jclear;
1054 jerr:
1055 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1056 v = NULL;
1057 jleave:
1058 NYD_LEAVE;
1059 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1061 jlist:{
1062 FILE *fp;
1063 size_t i;
1065 if(a_aux_err_head == NULL){
1066 fprintf(stderr, _("The error ring is empty\n"));
1067 goto jleave;
1070 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1071 NULL){
1072 fprintf(stderr, _("tmpfile"));
1073 v = NULL;
1074 goto jleave;
1077 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1078 fprintf(fp, "%4" PRIuZ ". %u B: %s",
1079 ++i, enp->ae_str.s_len, n_string_cp(&enp->ae_str));
1080 /* We don't know wether last string ended with NL; be simple */
1081 putc('\n', fp);
1083 page_or_print(fp, 0);
1084 Fclose(fp);
1086 /* FALLTHRU */
1088 jclear:
1089 a_aux_err_tail = NULL;
1090 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1091 a_aux_err_linelen = 0;
1092 while((enp = a_aux_err_head) != NULL){
1093 a_aux_err_head = enp->ae_next;
1094 n_string_gut(&enp->ae_str);
1095 free(enp);
1097 goto jleave;
1099 #endif /* HAVE_ERRORS */
1101 /* s-it-mode */