Add n_ICONV_UNIREPL, and we can 0xFFFD
[s-mailx.git] / auxlily.c
blob81f4449537a578bfb99f52dbcb2676f43ec9364e
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 = n_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 % n_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] % n_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') >= n_NELEM(atoi16) ||
355 (i2 = (ui8_t)hex[1] - '0') >= n_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[n_NELEM(primes) / 4] ? 0
417 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
418 : n_NELEM(primes) / 2));
420 if ((mprime = primes[i]) > n)
421 break;
422 while (++i < n_NELEM(primes));
423 if (i == n_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 n_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 %s)\n"
447 " The `errors' command manages this message ring\n"),
448 V_(n_error));
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(V_(n_error), '\0', ccp_base);
469 #endif
470 n_NATCH_CHAR( cclen_base = strlen(ccp_base); )
472 dfmaxlen = 0; /* keep CC happy */
473 trigger = FAL0;
474 jredo:
475 ccp = ccp_base;
476 n_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_shexp_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 const *
538 n_getdeadletter(void){
539 char const *cp_base, *cp;
540 NYD_ENTER;
542 cp_base = NULL;
543 jredo:
544 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
545 if(cp == NULL || strlen(cp) >= PATH_MAX){
546 if(cp_base == NULL){
547 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
548 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
549 ok_vclear(DEAD);
550 goto jredo;
551 }else{
552 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
553 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
556 NYD_LEAVE;
557 return cp;
560 FL char *
561 nodename(int mayoverride)
563 static char *sys_hostname, *hostname; /* XXX free-at-exit */
565 struct utsname ut;
566 char *hn;
567 #ifdef HAVE_SOCKETS
568 # ifdef HAVE_GETADDRINFO
569 struct addrinfo hints, *res;
570 # else
571 struct hostent *hent;
572 # endif
573 #endif
574 NYD_ENTER;
576 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
578 } else if ((hn = sys_hostname) == NULL) {
579 uname(&ut);
580 hn = ut.nodename;
581 #ifdef HAVE_SOCKETS
582 # ifdef HAVE_GETADDRINFO
583 memset(&hints, 0, sizeof hints);
584 hints.ai_family = AF_UNSPEC;
585 hints.ai_flags = AI_CANONNAME;
586 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
587 if (res->ai_canonname != NULL) {
588 size_t l = strlen(res->ai_canonname) +1;
590 hn = ac_alloc(l);
591 memcpy(hn, res->ai_canonname, l);
593 freeaddrinfo(res);
595 # else
596 hent = gethostbyname(hn);
597 if (hent != NULL)
598 hn = hent->h_name;
599 # endif
600 #endif
601 sys_hostname = sstrdup(hn);
602 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
603 if (hn != ut.nodename)
604 ac_free(hn);
605 #endif
606 hn = sys_hostname;
609 if (hostname != NULL && hostname != sys_hostname)
610 free(hostname);
611 hostname = sstrdup(hn);
612 NYD_LEAVE;
613 return hostname;
616 FL char *
617 getrandstring(size_t length)
619 struct str b64;
620 char *data;
621 size_t i;
622 NYD_ENTER;
624 #ifndef HAVE_POSIX_RANDOM
625 if (_rand == NULL)
626 _rand_init();
627 #endif
629 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
630 * with PAD stripped is still longer than what the user requests, easy way */
631 data = ac_alloc(i = length + 3);
633 #ifndef HAVE_POSIX_RANDOM
634 while (i-- > 0)
635 data[i] = (char)_rand_get8();
636 #else
637 { char *cp = data;
639 while (i > 0) {
640 union {ui32_t i4; char c[4];} r;
641 size_t j;
643 r.i4 = (ui32_t)arc4random();
644 switch ((j = i & 3)) {
645 case 0: cp[3] = r.c[3]; j = 4;
646 case 3: cp[2] = r.c[2];
647 case 2: cp[1] = r.c[1];
648 default: cp[0] = r.c[0]; break;
650 cp += j;
651 i -= j;
654 #endif
656 assert(length + 3 < UIZ_MAX / 4);
657 b64_encode_buf(&b64, data, length + 3,
658 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
659 ac_free(data);
661 assert(b64.l >= length);
662 b64.s[length] = '\0';
663 NYD_LEAVE;
664 return b64.s;
667 FL si8_t
668 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
670 char *dat, *eptr;
671 sl_i sli;
672 si8_t rv;
673 NYD_ENTER;
675 assert(inlen == 0 || inbuf != NULL);
677 if (inlen == UIZ_MAX)
678 inlen = strlen(inbuf);
680 if (inlen == 0)
681 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
682 else {
683 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
684 !ascncasecmp(inbuf, "true", inlen) ||
685 !ascncasecmp(inbuf, "yes", inlen) ||
686 !ascncasecmp(inbuf, "on", inlen))
687 rv = 1;
688 else if ((inlen == 1 &&
689 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
690 !ascncasecmp(inbuf, "false", inlen) ||
691 !ascncasecmp(inbuf, "no", inlen) ||
692 !ascncasecmp(inbuf, "off", inlen))
693 rv = 0;
694 else {
695 dat = ac_alloc(inlen +1);
696 memcpy(dat, inbuf, inlen);
697 dat[inlen] = '\0';
699 sli = strtol(dat, &eptr, 0);
700 if (*dat != '\0' && *eptr == '\0')
701 rv = (sli != 0);
702 else
703 rv = -1;
705 ac_free(dat);
708 NYD_LEAVE;
709 return rv;
712 FL si8_t
713 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
715 si8_t rv;
716 NYD_ENTER;
718 assert(inlen == 0 || inbuf != NULL);
720 if (inlen == UIZ_MAX)
721 inlen = strlen(inbuf);
723 if (inlen == 0)
724 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
725 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
726 !ascncasecmp(inbuf, "ask-", 4) &&
727 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
728 (options & OPT_INTERACTIVE))
729 rv = getapproval(prompt, rv);
730 NYD_LEAVE;
731 return rv;
734 FL bool_t
735 n_is_all_or_aster(char const *name){
736 bool_t rv;
737 NYD_ENTER;
739 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
740 NYD_LEAVE;
741 return rv;
744 FL time_t
745 n_time_epoch(void)
747 #ifdef HAVE_CLOCK_GETTIME
748 struct timespec ts;
749 #elif defined HAVE_GETTIMEOFDAY
750 struct timeval ts;
751 #endif
752 time_t rv;
753 char const *cp;
754 NYD2_ENTER;
756 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){ /* TODO */
757 /* TODO This is marked "posnum", b and therefore 0<=X<=UINT_MAX.
758 * TODO This means we have a Sun, 07 Feb 2106 07:28:15 +0100 problem.
759 * TODO Therefore we need a num_ui64= type in v15 */
760 rv = (time_t)strtoul(cp, NULL, 0);
761 }else{
762 #ifdef HAVE_CLOCK_GETTIME
763 clock_gettime(CLOCK_REALTIME, &ts);
764 rv = (time_t)ts.tv_sec;
765 #elif defined HAVE_GETTIMEOFDAY
766 gettimeofday(&ts, NULL);
767 rv = (time_t)ts.tv_sec;
768 #else
769 rv = time(NULL);
770 #endif
772 NYD2_LEAVE;
773 return rv;
776 FL void
777 time_current_update(struct time_current *tc, bool_t full_update)
779 NYD_ENTER;
780 tc->tc_time = n_time_epoch();
781 if (full_update) {
782 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
783 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
784 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
786 NYD_LEAVE;
789 FL uiz_t
790 n_msleep(uiz_t millis, bool_t ignint){
791 uiz_t rv;
792 NYD2_ENTER;
794 #ifdef HAVE_NANOSLEEP
795 /* C99 */{
796 struct timespec ts, trem;
797 int i;
799 ts.tv_sec = millis / 1000;
800 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
802 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
803 ts = trem;
804 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
807 #elif defined HAVE_SLEEP
808 if((millis /= 1000) == 0)
809 millis = 1;
810 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
811 millis = rv;
812 #else
813 # error Configuration should have detected a function for sleeping.
814 #endif
816 NYD2_LEAVE;
817 return rv;
820 FL void
821 n_err(char const *format, ...){
822 va_list ap;
823 NYD2_ENTER;
825 va_start(ap, format);
826 #ifdef HAVE_ERRORS
827 if(options & OPT_INTERACTIVE)
828 n_verr(format, ap);
829 else
830 #endif
832 size_t len;
833 bool_t doname, doflush;
835 doflush = FAL0;
836 while(*format == '\n'){
837 doflush = TRU1;
838 putc('\n', stderr);
839 ++format;
842 if((doname = doflush))
843 a_aux_err_linelen = 0;
845 if((len = strlen(format)) > 0){
846 if(doname || a_aux_err_linelen == 0)
847 fputs(VAL_UAGENT ": ", stderr);
848 vfprintf(stderr, format, ap);
850 /* C99 */{
851 size_t i = len;
853 if(format[--len] == '\n'){
854 a_aux_err_linelen = (i -= ++len);
855 break;
857 ++a_aux_err_linelen;
858 }while(len > 0);
862 if(doflush)
863 fflush(stderr);
865 va_end(ap);
866 NYD2_LEAVE;
869 FL void
870 n_verr(char const *format, va_list ap){
871 /* Check use cases of PS_ERRORS_NOTED, too! */
872 #ifdef HAVE_ERRORS
873 struct a_aux_err_node *enp;
874 #endif
875 bool_t doname, doflush;
876 size_t len;
877 NYD2_ENTER;
879 doflush = FAL0;
880 while(*format == '\n'){
881 doflush = TRU1;
882 putc('\n', stderr);
883 ++format;
886 if((doname = doflush)){
887 a_aux_err_linelen = 0;
888 #ifdef HAVE_ERRORS
889 if(options & OPT_INTERACTIVE){
890 if((enp = a_aux_err_tail) != NULL &&
891 (enp->ae_str.s_len > 0 &&
892 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
893 n_string_push_c(&enp->ae_str, '\n');
895 #endif
898 if((len = strlen(format)) == 0)
899 goto jleave;
901 if(doname || a_aux_err_linelen == 0)
902 fputs(VAL_UAGENT ": ", stderr);
904 /* C99 */{
905 size_t i = len;
907 if(format[--len] == '\n'){
908 a_aux_err_linelen = (i -= ++len);
909 break;
911 ++a_aux_err_linelen;
912 }while(len > 0);
915 #ifdef HAVE_ERRORS
916 if(!(options & OPT_INTERACTIVE))
917 #endif
918 vfprintf(stderr, format, ap);
919 #ifdef HAVE_ERRORS
920 else{
921 int imax, i;
922 n_LCTAV(ERRORS_MAX > 3);
924 /* Link it into the `errors' message ring */
925 if((enp = a_aux_err_tail) == NULL){
926 jcreat:
927 enp = smalloc(sizeof *enp);
928 enp->ae_next = NULL;
929 n_string_creat(&enp->ae_str);
930 if(a_aux_err_tail != NULL)
931 a_aux_err_tail->ae_next = enp;
932 else
933 a_aux_err_head = enp;
934 a_aux_err_tail = enp;
935 ++a_aux_err_cnt;
936 }else if(doname ||
937 (enp->ae_str.s_len > 0 &&
938 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
939 if(a_aux_err_cnt < ERRORS_MAX)
940 goto jcreat;
942 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
943 a_aux_err_tail->ae_next = enp;
944 a_aux_err_tail = enp;
945 enp->ae_next = NULL;
946 n_string_trunc(&enp->ae_str, 0);
949 # ifdef HAVE_N_VA_COPY
950 imax = 64;
951 # else
952 imax = n_MIN(LINESIZE, 1024);
953 # endif
954 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
955 # ifdef HAVE_N_VA_COPY
956 va_list vac;
958 n_va_copy(vac, ap);
959 # else
960 # define vac ap
961 # endif
963 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
964 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
965 # ifdef HAVE_N_VA_COPY
966 va_end(vac);
967 # else
968 # undef vac
969 # endif
970 if(i <= 0)
971 goto jleave;
972 if(UICMP(z, i, >=, imax)){
973 # ifdef HAVE_N_VA_COPY
974 /* XXX Check overflow for upcoming LEN+++i! */
975 n_string_trunc(&enp->ae_str, len);
976 continue;
977 # else
978 i = (int)strlen(&enp->ae_str.s_dat[len]);
979 # endif
981 break;
983 n_string_trunc(&enp->ae_str, len + (size_t)i);
985 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
987 #endif /* HAVE_ERRORS */
989 jleave:
990 if(doflush)
991 fflush(stderr);
992 NYD2_LEAVE;
995 FL void
996 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
997 va_list ap;
998 NYD_X;
1000 va_start(ap, format);
1001 vfprintf(stderr, format, ap);
1002 va_end(ap);
1003 fflush(stderr);
1006 FL void
1007 n_perr(char const *msg, int errval){
1008 char const *fmt;
1009 NYD2_ENTER;
1011 if(msg == NULL){
1012 fmt = "%s%s\n";
1013 msg = "";
1014 }else
1015 fmt = "%s: %s\n";
1017 if(errval == 0)
1018 errval = errno;
1020 n_err(fmt, msg, strerror(errval));
1021 NYD2_LEAVE;
1024 FL void
1025 n_alert(char const *format, ...){
1026 va_list ap;
1027 NYD2_ENTER;
1029 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1031 va_start(ap, format);
1032 n_verr(format, ap);
1033 va_end(ap);
1035 n_err("\n");
1036 NYD2_LEAVE;
1039 FL void
1040 n_panic(char const *format, ...){
1041 va_list ap;
1042 NYD2_ENTER;
1044 if(a_aux_err_linelen > 0){
1045 putc('\n', stderr);
1046 a_aux_err_linelen = 0;
1048 fprintf(stderr, VAL_UAGENT ": Panic: ");
1050 va_start(ap, format);
1051 vfprintf(stderr, format, ap);
1052 va_end(ap);
1054 putc('\n', stderr);
1055 fflush(stderr);
1056 NYD2_LEAVE;
1057 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1060 #ifdef HAVE_ERRORS
1061 FL int
1062 c_errors(void *v){
1063 char **argv = v;
1064 struct a_aux_err_node *enp;
1065 NYD_ENTER;
1067 if(*argv == NULL)
1068 goto jlist;
1069 if(argv[1] != NULL)
1070 goto jerr;
1071 if(!asccasecmp(*argv, "show"))
1072 goto jlist;
1073 if(!asccasecmp(*argv, "clear"))
1074 goto jclear;
1075 jerr:
1076 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1077 v = NULL;
1078 jleave:
1079 NYD_LEAVE;
1080 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1082 jlist:{
1083 FILE *fp;
1084 size_t i;
1086 if(a_aux_err_head == NULL){
1087 fprintf(stderr, _("The error ring is empty\n"));
1088 goto jleave;
1091 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1092 NULL){
1093 fprintf(stderr, _("tmpfile"));
1094 v = NULL;
1095 goto jleave;
1098 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1099 fprintf(fp, "%4" PRIuZ ". %u B: %s",
1100 ++i, enp->ae_str.s_len, n_string_cp(&enp->ae_str));
1101 /* We don't know whether last string ended with NL; be simple */
1102 putc('\n', fp);
1104 page_or_print(fp, 0);
1105 Fclose(fp);
1107 /* FALLTHRU */
1109 jclear:
1110 a_aux_err_tail = NULL;
1111 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1112 a_aux_err_linelen = 0;
1113 while((enp = a_aux_err_head) != NULL){
1114 a_aux_err_head = enp->ae_next;
1115 n_string_gut(&enp->ae_str);
1116 free(enp);
1118 goto jleave;
1120 #endif /* HAVE_ERRORS */
1122 /* s-it-mode */