nail.h:ISQUOTE(): use n_WC_C() not L character constant prefix
[s-mailx.git] / auxlily.c
blobeb1e049935e234f37c141c611428b4ba62a10c25
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 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 unsigned
403 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
405 unsigned h = 0, g;
406 NYD_ENTER;
408 cp--;
409 while (*++cp) {
410 h = (h << 4 & 0xffffffff) + (*cp&0377);
411 if ((g = h & 0xf0000000) != 0) {
412 h = h ^ g >> 24;
413 h = h ^ g;
416 NYD_LEAVE;
417 return h;
420 FL ui32_t
421 nextprime(ui32_t n)
423 static ui32_t const primes[] = {
424 5, 11, 23, 47, 97, 157, 283,
425 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
426 131071, 262139, 524287, 1048573, 2097143, 4194301,
427 8388593, 16777213, 33554393, 67108859, 134217689,
428 268435399, 536870909, 1073741789, 2147483647
431 ui32_t i, mprime;
432 NYD_ENTER;
434 i = (n < primes[NELEM(primes) / 4] ? 0
435 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
436 : NELEM(primes) / 2));
438 if ((mprime = primes[i]) > n)
439 break;
440 while (++i < NELEM(primes));
441 if (i == NELEM(primes) && mprime < n)
442 mprime = n;
443 NYD_LEAVE;
444 return mprime;
447 FL char *
448 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
449 { /* FIXME getprompt must mb->wc->mb+reset seq! */
450 static char buf[PROMPT_BUFFER_SIZE];
452 char *cp;
453 char const *ccp_base, *ccp;
454 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
455 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
456 NYD_ENTER;
458 /* No other place to place this */
459 #ifdef HAVE_ERRORS
460 if (options & OPT_INTERACTIVE) {
461 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
462 pstate |= PS_ERRORS_NOTED;
463 fprintf(stderr, _("There are new messages in the error message ring "
464 "(denoted by \"#ERR#\")\n"
465 " The `errors' command manages this message ring\n"));
468 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
469 a_aux_err_cnt_noted = a_aux_err_cnt;
470 } else
471 trigger = FAL0;
472 #endif
474 cp = buf;
475 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
476 #ifdef HAVE_ERRORS
477 if (trigger)
478 ccp_base = "";
479 else
480 #endif
481 goto jleave;
483 #ifdef HAVE_ERRORS
484 if (trigger)
485 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
486 #endif
487 NATCH_CHAR( cclen_base = strlen(ccp_base); )
489 dfmaxlen = 0; /* keep CC happy */
490 trigger = FAL0;
491 jredo:
492 ccp = ccp_base;
493 NATCH_CHAR( cclen = cclen_base; )
494 maxlen = sizeof(buf) -1;
496 for (;;) {
497 size_t l;
498 int c;
500 if (maxlen == 0)
501 goto jleave;
502 #ifdef HAVE_NATCH_CHAR
503 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
504 if (c <= 0) {
505 mblen(NULL, 0);
506 if (c < 0) {
507 *buf = '?';
508 cp = buf + 1;
509 goto jleave;
511 break;
512 } else if ((l = c) > 1) {
513 if (trigger) {
514 memcpy(cp, ccp, l);
515 cp += l;
517 ccp += l;
518 maxlen -= l;
519 continue;
520 } else
521 #endif
522 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
523 if (trigger)
524 *cp++ = (char)c;
525 --maxlen;
526 continue;
528 if (c == 0 || c == PROMPT_STOP)
529 break;
531 if (trigger) {
532 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
533 if (a == NULL)
534 a = "";
535 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
536 cp += l;
537 maxlen -= l;
538 dfmaxlen -= l;
543 if (!trigger) {
544 trigger = TRU1;
545 dfmaxlen = maxlen;
546 goto jredo;
548 jleave:
549 *cp = '\0';
550 NYD_LEAVE;
551 return buf;
554 FL char *
555 nodename(int mayoverride)
557 static char *sys_hostname, *hostname; /* XXX free-at-exit */
559 struct utsname ut;
560 char *hn;
561 #ifdef HAVE_SOCKETS
562 # ifdef HAVE_GETADDRINFO
563 struct addrinfo hints, *res;
564 # else
565 struct hostent *hent;
566 # endif
567 #endif
568 NYD_ENTER;
570 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
572 } else if ((hn = sys_hostname) == NULL) {
573 uname(&ut);
574 hn = ut.nodename;
575 #ifdef HAVE_SOCKETS
576 # ifdef HAVE_GETADDRINFO
577 memset(&hints, 0, sizeof hints);
578 hints.ai_family = AF_UNSPEC;
579 hints.ai_flags = AI_CANONNAME;
580 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
581 if (res->ai_canonname != NULL) {
582 size_t l = strlen(res->ai_canonname) +1;
584 hn = ac_alloc(l);
585 memcpy(hn, res->ai_canonname, l);
587 freeaddrinfo(res);
589 # else
590 hent = gethostbyname(hn);
591 if (hent != NULL)
592 hn = hent->h_name;
593 # endif
594 #endif
595 sys_hostname = sstrdup(hn);
596 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
597 if (hn != ut.nodename)
598 ac_free(hn);
599 #endif
600 hn = sys_hostname;
603 if (hostname != NULL && hostname != sys_hostname)
604 free(hostname);
605 hostname = sstrdup(hn);
606 NYD_LEAVE;
607 return hostname;
610 FL char *
611 getrandstring(size_t length)
613 struct str b64;
614 char *data;
615 size_t i;
616 NYD_ENTER;
618 #ifndef HAVE_POSIX_RANDOM
619 if (_rand == NULL)
620 _rand_init();
621 #endif
623 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
624 * with PAD stripped is still longer than what the user requests, easy way */
625 data = ac_alloc(i = length + 3);
627 #ifndef HAVE_POSIX_RANDOM
628 while (i-- > 0)
629 data[i] = (char)_rand_get8();
630 #else
631 { char *cp = data;
633 while (i > 0) {
634 union {ui32_t i4; char c[4];} r;
635 size_t j;
637 r.i4 = (ui32_t)arc4random();
638 switch ((j = i & 3)) {
639 case 0: cp[3] = r.c[3]; j = 4;
640 case 3: cp[2] = r.c[2];
641 case 2: cp[1] = r.c[1];
642 default: cp[0] = r.c[0]; break;
644 cp += j;
645 i -= j;
648 #endif
650 b64_encode_buf(&b64, data, length + 3,
651 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
652 ac_free(data);
654 assert(b64.l >= length);
655 b64.s[length] = '\0';
656 NYD_LEAVE;
657 return b64.s;
660 FL si8_t
661 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
663 char *dat, *eptr;
664 sl_i sli;
665 si8_t rv;
666 NYD_ENTER;
668 assert(inlen == 0 || inbuf != NULL);
670 if (inlen == UIZ_MAX)
671 inlen = strlen(inbuf);
673 if (inlen == 0)
674 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
675 else {
676 if ((inlen == 1 && *inbuf == '1') ||
677 !ascncasecmp(inbuf, "true", inlen) ||
678 !ascncasecmp(inbuf, "yes", inlen) ||
679 !ascncasecmp(inbuf, "on", inlen))
680 rv = 1;
681 else if ((inlen == 1 && *inbuf == '0') ||
682 !ascncasecmp(inbuf, "false", inlen) ||
683 !ascncasecmp(inbuf, "no", inlen) ||
684 !ascncasecmp(inbuf, "off", inlen))
685 rv = 0;
686 else {
687 dat = ac_alloc(inlen +1);
688 memcpy(dat, inbuf, inlen);
689 dat[inlen] = '\0';
691 sli = strtol(dat, &eptr, 0);
692 if (*dat != '\0' && *eptr == '\0')
693 rv = (sli != 0);
694 else
695 rv = -1;
697 ac_free(dat);
700 NYD_LEAVE;
701 return rv;
704 FL si8_t
705 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
707 si8_t rv;
708 NYD_ENTER;
710 assert(inlen == 0 || inbuf != NULL);
712 if (inlen == UIZ_MAX)
713 inlen = strlen(inbuf);
715 if (inlen == 0)
716 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
717 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
718 !ascncasecmp(inbuf, "ask-", 4) &&
719 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
720 (options & OPT_INTERACTIVE))
721 rv = getapproval(prompt, rv);
722 NYD_LEAVE;
723 return rv;
726 FL bool_t
727 n_is_all_or_aster(char const *name){
728 bool_t rv;
729 NYD_ENTER;
731 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
732 NYD_LEAVE;
733 return rv;
736 FL time_t
737 n_time_epoch(void)
739 #ifdef HAVE_CLOCK_GETTIME
740 struct timespec ts;
741 #elif defined HAVE_GETTIMEOFDAY
742 struct timeval ts;
743 #endif
744 time_t rv;
745 NYD2_ENTER;
747 #ifdef HAVE_CLOCK_GETTIME
748 clock_gettime(CLOCK_REALTIME, &ts);
749 rv = (time_t)ts.tv_sec;
750 #elif defined HAVE_GETTIMEOFDAY
751 gettimeofday(&ts, NULL);
752 rv = (time_t)ts.tv_sec;
753 #else
754 rv = time(NULL);
755 #endif
756 NYD2_LEAVE;
757 return rv;
760 FL void
761 time_current_update(struct time_current *tc, bool_t full_update)
763 NYD_ENTER;
764 tc->tc_time = n_time_epoch();
765 if (full_update) {
766 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
767 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
768 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
770 NYD_LEAVE;
773 FL uiz_t
774 n_msleep(uiz_t millis, bool_t ignint){
775 uiz_t rv;
776 NYD2_ENTER;
778 #ifdef HAVE_NANOSLEEP
779 /* C99 */{
780 struct timespec ts, trem;
781 int i;
783 ts.tv_sec = millis / 1000;
784 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
786 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
787 ts = trem;
788 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
791 #elif defined HAVE_SLEEP
792 if((millis /= 1000) == 0)
793 millis = 1;
794 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
795 millis = rv;
796 #else
797 # error Configuration should have detected a function for sleeping.
798 #endif
800 NYD2_LEAVE;
801 return rv;
804 FL void
805 n_err(char const *format, ...){
806 va_list ap;
807 NYD2_ENTER;
809 va_start(ap, format);
810 #ifdef HAVE_ERRORS
811 if(options & OPT_INTERACTIVE)
812 n_verr(format, ap);
813 else
814 #endif
816 size_t len;
817 bool_t doname, doflush;
819 doflush = FAL0;
820 while(*format == '\n'){
821 doflush = TRU1;
822 putc('\n', stderr);
823 ++format;
826 if((doname = doflush))
827 a_aux_err_linelen = 0;
829 if((len = strlen(format)) > 0){
830 if(doname || a_aux_err_linelen == 0)
831 fputs(UAGENT ": ", stderr);
832 vfprintf(stderr, format, ap);
834 /* C99 */{
835 size_t i = len;
837 if(format[--len] == '\n'){
838 a_aux_err_linelen = (i -= ++len);
839 break;
841 ++a_aux_err_linelen;
842 }while(len > 0);
846 if(doflush)
847 fflush(stderr);
849 va_end(ap);
850 NYD2_LEAVE;
853 FL void
854 n_verr(char const *format, va_list ap){
855 /* Check use cases of PS_ERRORS_NOTED, too! */
856 #ifdef HAVE_ERRORS
857 struct a_aux_err_node *enp;
858 #endif
859 bool_t doname, doflush;
860 size_t len;
861 NYD2_ENTER;
863 doflush = FAL0;
864 while(*format == '\n'){
865 doflush = TRU1;
866 putc('\n', stderr);
867 ++format;
870 if((doname = doflush)){
871 a_aux_err_linelen = 0;
872 #ifdef HAVE_ERRORS
873 if(options & OPT_INTERACTIVE){
874 if((enp = a_aux_err_tail) != NULL &&
875 (enp->ae_str.s_len > 0 &&
876 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
877 n_string_push_c(&enp->ae_str, '\n');
879 #endif
882 if((len = strlen(format)) == 0)
883 goto jleave;
885 if(doname || a_aux_err_linelen == 0)
886 fputs(UAGENT ": ", stderr);
888 /* C99 */{
889 size_t i = len;
891 if(format[--len] == '\n'){
892 a_aux_err_linelen = (i -= ++len);
893 break;
895 ++a_aux_err_linelen;
896 }while(len > 0);
899 #ifdef HAVE_ERRORS
900 if(!(options & OPT_INTERACTIVE))
901 #endif
902 vfprintf(stderr, format, ap);
903 #ifdef HAVE_ERRORS
904 else{
905 int imax, i;
906 LCTA(ERRORS_MAX > 3);
908 /* Link it into the `errors' message ring */
909 if((enp = a_aux_err_tail) == NULL){
910 jcreat:
911 enp = smalloc(sizeof *enp);
912 enp->ae_next = NULL;
913 n_string_creat(&enp->ae_str);
914 if(a_aux_err_tail != NULL)
915 a_aux_err_tail->ae_next = enp;
916 else
917 a_aux_err_head = enp;
918 a_aux_err_tail = enp;
919 ++a_aux_err_cnt;
920 }else if(doname ||
921 (enp->ae_str.s_len > 0 &&
922 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
923 if(a_aux_err_cnt < ERRORS_MAX)
924 goto jcreat;
926 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
927 a_aux_err_tail->ae_next = enp;
928 a_aux_err_tail = enp;
929 enp->ae_next = NULL;
930 n_string_trunc(&enp->ae_str, 0);
933 # ifdef HAVE_VA_COPY
934 imax = 64;
935 # else
936 imax = LINESIZE;
937 # endif
938 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
939 # ifdef HAVE_VA_COPY
940 va_list vac;
942 va_copy(vac, ap);
943 # else
944 # define vac ap
945 # endif
946 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
947 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
948 # ifdef HAVE_VA_COPY
949 va_end(vac);
950 # else
951 # undef vac
952 # endif
953 if(i <= 0)
954 goto jleave;
955 if(UICMP(z, i, >=, imax)){
956 # ifdef HAVE_VA_COPY
957 /* XXX Check overflow for upcoming LEN+++i! */
958 n_string_trunc(&enp->ae_str, len);
959 continue;
960 # else
961 i = (int)strlen(&enp->ae_str.s_dat[len]);
962 # endif
964 break;
966 n_string_trunc(&enp->ae_str, len + (size_t)i);
968 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
970 #endif /* HAVE_ERRORS */
972 jleave:
973 if(doflush)
974 fflush(stderr);
975 NYD2_LEAVE;
978 FL void
979 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
980 va_list ap;
981 NYD_X;
983 va_start(ap, format);
984 vfprintf(stderr, format, ap);
985 va_end(ap);
986 fflush(stderr);
989 FL void
990 n_perr(char const *msg, int errval){
991 char const *fmt;
992 NYD2_ENTER;
994 if(msg == NULL){
995 fmt = "%s%s\n";
996 msg = "";
997 }else
998 fmt = "%s: %s\n";
1000 if(errval == 0)
1001 errval = errno;
1003 n_err(fmt, msg, strerror(errval));
1004 NYD2_LEAVE;
1007 FL void
1008 n_alert(char const *format, ...){
1009 va_list ap;
1010 NYD2_ENTER;
1012 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1014 va_start(ap, format);
1015 n_verr(format, ap);
1016 va_end(ap);
1018 n_err("\n");
1019 NYD2_LEAVE;
1022 FL void
1023 n_panic(char const *format, ...){
1024 va_list ap;
1025 NYD2_ENTER;
1027 if(a_aux_err_linelen > 0){
1028 putc('\n', stderr);
1029 a_aux_err_linelen = 0;
1031 fprintf(stderr, UAGENT ": Panic: ");
1033 va_start(ap, format);
1034 vfprintf(stderr, format, ap);
1035 va_end(ap);
1037 putc('\n', stderr);
1038 fflush(stderr);
1039 NYD2_LEAVE;
1040 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1043 #ifdef HAVE_ERRORS
1044 FL int
1045 c_errors(void *v){
1046 char **argv = v;
1047 struct a_aux_err_node *enp;
1048 NYD_ENTER;
1050 if(*argv == NULL)
1051 goto jlist;
1052 if(argv[1] != NULL)
1053 goto jerr;
1054 if(!asccasecmp(*argv, "show"))
1055 goto jlist;
1056 if(!asccasecmp(*argv, "clear"))
1057 goto jclear;
1058 jerr:
1059 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1060 v = NULL;
1061 jleave:
1062 NYD_LEAVE;
1063 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1065 jlist:{
1066 FILE *fp;
1067 size_t i;
1069 if(a_aux_err_head == NULL){
1070 fprintf(stderr, _("The error ring is empty\n"));
1071 goto jleave;
1074 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1075 NULL){
1076 fprintf(stderr, _("tmpfile"));
1077 v = NULL;
1078 goto jleave;
1081 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1082 fprintf(fp, "%4" PRIuZ ". %u B: %s",
1083 ++i, enp->ae_str.s_len, n_string_cp(&enp->ae_str));
1084 /* We don't know wether last string ended with NL; be simple */
1085 putc('\n', fp);
1087 page_or_print(fp, 0);
1088 Fclose(fp);
1090 /* FALLTHRU */
1092 jclear:
1093 a_aux_err_tail = NULL;
1094 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1095 a_aux_err_linelen = 0;
1096 while((enp = a_aux_err_head) != NULL){
1097 a_aux_err_head = enp->ae_next;
1098 n_string_gut(&enp->ae_str);
1099 free(enp);
1101 goto jleave;
1103 #endif /* HAVE_ERRORS */
1105 /* s-it-mode */