Move string dope from strings.c to the new memory.c..
[s-mailx.git] / auxlily.c
blobf5f87f32feb996e4419d347686faeea4fd68be8c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions.
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 #include <ctype.h>
45 #include <pwd.h>
47 #ifdef HAVE_SOCKETS
48 # ifdef HAVE_GETADDRINFO
49 # include <sys/socket.h>
50 # endif
52 # include <netdb.h>
53 #endif
55 #ifndef HAVE_POSIX_RANDOM
56 union rand_state {
57 struct rand_arc4 {
58 ui8_t __pad[6];
59 ui8_t _i;
60 ui8_t _j;
61 ui8_t _dat[256];
62 } a;
63 ui8_t b8[sizeof(struct rand_arc4)];
64 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
66 #endif
68 struct shvar_stack {
69 struct shvar_stack *shs_next; /* Outer stack frame */
70 char const *shs_value; /* Remaining value to expand */
71 size_t shs_len; /* gth of .shs_dat this level */
72 char const *shs_dat; /* Result data of this level */
73 bool_t *shs_err; /* Or NULL */
74 bool_t shs_bsesc; /* Shall backslash escaping be performed */
77 #ifdef HAVE_ERRORS
78 struct a_aux_err_node{
79 struct a_aux_err_node *ae_next;
80 struct str ae_str;
82 #endif
84 #ifndef HAVE_POSIX_RANDOM
85 static union rand_state *_rand;
86 #endif
88 /* Error ring, for `errors' */
89 #ifdef HAVE_ERRORS
90 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
91 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
92 #endif
93 static size_t a_aux_err_dirty;
95 /* Our ARC4 random generator with its completely unacademical pseudo
96 * initialization (shall /dev/urandom fail) */
97 #ifndef HAVE_POSIX_RANDOM
98 static void _rand_init(void);
99 static ui32_t _rand_weak(ui32_t seed);
100 SINLINE ui8_t _rand_get8(void);
101 #endif
103 /* Perform shell variable expansion */
104 static char * _sh_exp_var(struct shvar_stack *shsp);
106 #ifndef HAVE_POSIX_RANDOM
107 static void
108 _rand_init(void)
110 # ifdef HAVE_CLOCK_GETTIME
111 struct timespec ts;
112 # else
113 struct timeval ts;
114 # endif
115 union {int fd; size_t i;} u;
116 ui32_t seed, rnd;
117 NYD2_ENTER;
119 _rand = smalloc(sizeof *_rand);
121 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
122 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
124 close(u.fd);
125 if (ok)
126 goto jleave;
129 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
130 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
131 size_t t, k;
133 # ifdef HAVE_CLOCK_GETTIME
134 clock_gettime(CLOCK_REALTIME, &ts);
135 t = (ui32_t)ts.tv_nsec;
136 # else
137 gettimeofday(&ts, NULL);
138 t = (ui32_t)ts.tv_usec;
139 # endif
140 if (rnd & 1)
141 t = (t >> 16) | (t << 16);
142 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
143 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
144 if (rnd == 7 || rnd == 17)
145 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
146 k = _rand->b32[u.i] % NELEM(_rand->b32);
147 _rand->b32[k] ^= _rand->b32[u.i];
148 seed ^= _rand_weak(_rand->b32[k]);
149 if ((rnd & 3) == 3)
150 seed ^= nextprime(seed);
154 for (u.i = 5 * sizeof(_rand->b8); u.i != 0; --u.i)
155 _rand_get8();
156 jleave:
157 NYD2_LEAVE;
160 static ui32_t
161 _rand_weak(ui32_t seed)
163 /* From "Random number generators: good ones are hard to find",
164 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
165 * October 1988, p. 1195.
166 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
167 ui32_t hi;
169 if (seed == 0)
170 seed = 123459876;
171 hi = seed / 127773;
172 seed %= 127773;
173 seed = (seed * 16807) - (hi * 2836);
174 if ((si32_t)seed < 0)
175 seed += SI32_MAX;
176 return seed;
179 SINLINE ui8_t
180 _rand_get8(void)
182 ui8_t si, sj;
184 si = _rand->a._dat[++_rand->a._i];
185 sj = _rand->a._dat[_rand->a._j += si];
186 _rand->a._dat[_rand->a._i] = sj;
187 _rand->a._dat[_rand->a._j] = si;
188 return _rand->a._dat[(ui8_t)(si + sj)];
190 #endif /* HAVE_POSIX_RANDOM */
192 static char *
193 _sh_exp_var(struct shvar_stack *shsp)
195 struct shvar_stack next, *np, *tmp;
196 char const *vp;
197 char lc, c, *cp, *rv;
198 size_t i;
199 NYD2_ENTER;
201 if (*(vp = shsp->shs_value) != '$') {
202 bool_t bsesc = shsp->shs_bsesc;
203 union {bool_t hadbs; char c;} u = {FAL0};
205 shsp->shs_dat = vp;
206 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
207 if (c == '$' && lc != '\\')
208 break;
209 if (!bsesc)
210 continue;
211 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
213 shsp->shs_len = i;
215 if (u.hadbs) {
216 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
218 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
219 if (u.c != '\\' || lc == '\\')
220 *rv++ = u.c;
221 lc = (lc == '\\') ? '\0' : u.c;
223 *rv = '\0';
225 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
227 } else {
228 if ((lc = (*++vp == '{')))
229 ++vp;
231 /* POSIX says
232 * Environment variable names used by the utilities in the Shell and
233 * Utilities volume of POSIX.1-2008 consist solely of uppercase
234 * letters, digits, and the <underscore> ('_') from the characters
235 * defined in Portable Character Set and do not begin with a digit.
236 * Other characters may be permitted by an implementation;
237 * applications shall tolerate the presence of such names.
238 * We do support the hyphen "-" because it is common for mailx. */
239 shsp->shs_dat = vp;
240 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
241 if (!alnumchar(c) && c != '_' && c != '-')
242 break;
244 if (lc) {
245 if (c != '}') {
246 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
247 shsp->shs_value);
248 shsp->shs_len = strlen(shsp->shs_value);
249 shsp->shs_dat = shsp->shs_value;
250 if (shsp->shs_err != NULL)
251 *shsp->shs_err = TRU1;
252 goto junroll;
254 c = *++vp;
257 shsp->shs_len = i;
258 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
259 shsp->shs_len = strlen(shsp->shs_dat = cp);
261 if (c != '\0')
262 goto jrecurse;
264 /* That level made the great and completed encoding. Build result */
265 junroll:
266 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
267 i += np->shs_len;
268 tmp = np->shs_next;
269 np->shs_next = shsp;
270 shsp = np;
271 np = tmp;
274 cp = rv = salloc(i +1);
275 while (shsp != NULL) {
276 np = shsp;
277 shsp = shsp->shs_next;
278 memcpy(cp, np->shs_dat, np->shs_len);
279 cp += np->shs_len;
281 *cp = '\0';
283 jleave:
284 NYD2_LEAVE;
285 return rv;
286 jrecurse:
287 memset(&next, 0, sizeof next);
288 next.shs_next = shsp;
289 next.shs_value = vp;
290 next.shs_err = shsp->shs_err;
291 next.shs_bsesc = shsp->shs_bsesc;
292 rv = _sh_exp_var(&next);
293 goto jleave;
296 FL void
297 touch(struct message *mp)
299 NYD_ENTER;
300 mp->m_flag |= MTOUCH;
301 if (!(mp->m_flag & MREAD))
302 mp->m_flag |= MREAD | MSTATUS;
303 NYD_LEAVE;
306 FL bool_t
307 is_dir(char const *name)
309 struct stat sbuf;
310 bool_t rv;
311 NYD_ENTER;
313 for (rv = FAL0;;)
314 if (!stat(name, &sbuf)) {
315 rv = (S_ISDIR(sbuf.st_mode) != 0);
316 break;
317 } else if (errno != EINTR)
318 break;
319 NYD_LEAVE;
320 return rv;
323 FL int
324 argcount(char **argv)
326 char **ap;
327 NYD_ENTER;
329 for (ap = argv; *ap++ != NULL;)
331 NYD_LEAVE;
332 return (int)PTR2SIZE(ap - argv - 1);
335 FL int
336 screensize(void)
338 int s;
339 char *cp;
340 NYD_ENTER;
342 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
343 s = scrnheight - 2; /* XXX no magics */
344 NYD_LEAVE;
345 return s;
348 FL char const *
349 n_pager_get(char const **env_addon)
351 char const *cp;
352 NYD_ENTER;
354 cp = ok_vlook(PAGER);
355 if (cp == NULL || *cp == '\0')
356 cp = XPAGER;
358 if (env_addon != NULL) {
359 *env_addon = NULL;
360 /* Update the manual upon any changes:
361 * *colour-pager*, $PAGER */
362 if(strstr(rv, "less") != NULL){
363 if(!env_blook("LESS", TRU1))
364 *env_addon =
365 #ifdef HAVE_TERMCAP
366 (pstate & PS_TERMCAP_CA_MODE) ? "LESS=Ri"
367 : !(pstate & PS_TERMCAP_DISABLE) ? "LESS=FRi" :
368 #endif
369 "LESS=FRXi";
370 }else if(strstr(rv, "lv") != NULL){
371 if(!env_blook("LV", TRU1))
372 *env_addon = "LV=-c";
375 NYD_LEAVE;
376 return cp;
379 FL void
380 page_or_print(FILE *fp, size_t lines)
382 int c;
383 char const *cp;
384 NYD_ENTER;
386 fflush_rewind(fp);
388 if ((options & OPT_INTERACTIVE) && (pstate & PS_STARTED) &&
389 (cp = ok_vlook(crt)) != NULL) {
390 char *eptr;
391 union {sl_i sli; size_t rows;} u;
393 u.sli = strtol(cp, &eptr, 0);
394 u.rows = (*cp != '\0' && *eptr == '\0')
395 ? (size_t)u.sli : (size_t)scrnheight;
397 if (u.rows > 0 && lines == 0) {
398 while ((c = getc(fp)) != EOF)
399 if (c == '\n' && ++lines >= u.rows)
400 break;
401 really_rewind(fp);
404 if (lines >= u.rows) {
405 run_command(n_pager_get(NULL), 0, fileno(fp), COMMAND_FD_PASS,
406 NULL, NULL, NULL, NULL);
407 goto jleave;
411 while ((c = getc(fp)) != EOF)
412 putchar(c);
413 jleave:
414 NYD_LEAVE;
417 FL enum protocol
418 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
420 struct stat st;
421 char const *cp;
422 char *np;
423 size_t sz;
424 enum protocol rv = PROTO_UNKNOWN;
425 NYD_ENTER;
427 temporary_protocol_ext = NULL;
429 if (name[0] == '%' && name[1] == ':')
430 name += 2;
431 for (cp = name; *cp && *cp != ':'; cp++)
432 if (!alnumchar(*cp))
433 goto jfile;
435 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
436 if (!strncmp(name, "pop3://", 7)) {
437 #ifdef HAVE_POP3
438 rv = PROTO_POP3;
439 #else
440 n_err(_("No POP3 support compiled in\n"));
441 #endif
442 } else if (!strncmp(name, "pop3s://", 8)) {
443 #if defined HAVE_POP3 && defined HAVE_SSL
444 rv = PROTO_POP3;
445 #else
446 # ifndef HAVE_POP3
447 n_err(_("No POP3 support compiled in\n"));
448 # endif
449 # ifndef HAVE_SSL
450 n_err(_("No SSL support compiled in\n"));
451 # endif
452 #endif
454 goto jleave;
457 /* TODO This is the de facto maildir code and thus belongs into there!
458 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
459 * TODO or (more likely) in addition to *newfolders*) */
460 jfile:
461 rv = PROTO_FILE;
462 np = ac_alloc((sz = strlen(name)) + 4 +1);
463 memcpy(np, name, sz + 1);
464 if (!stat(name, &st)) {
465 if (S_ISDIR(st.st_mode) &&
466 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
467 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
468 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
469 rv = PROTO_MAILDIR;
470 } else {
471 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
472 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
473 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
474 temporary_protocol_ext = cp;
475 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
476 rv = PROTO_MAILDIR;
478 ac_free(np);
479 jleave:
480 NYD_LEAVE;
481 return rv;
484 FL ui32_t
485 torek_hash(char const *name)
487 /* Chris Torek's hash.
488 * NOTE: need to change *at least* mk-okey-map.pl when changing the
489 * algorithm!! */
490 ui32_t h = 0;
491 NYD_ENTER;
493 while (*name != '\0') {
494 h *= 33;
495 h += *name++;
497 NYD_LEAVE;
498 return h;
501 FL unsigned
502 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
504 unsigned h = 0, g;
505 NYD_ENTER;
507 cp--;
508 while (*++cp) {
509 h = (h << 4 & 0xffffffff) + (*cp&0377);
510 if ((g = h & 0xf0000000) != 0) {
511 h = h ^ g >> 24;
512 h = h ^ g;
515 NYD_LEAVE;
516 return h;
519 FL ui32_t
520 nextprime(ui32_t n)
522 static ui32_t const primes[] = {
523 5, 11, 23, 47, 97, 157, 283,
524 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
525 131071, 262139, 524287, 1048573, 2097143, 4194301,
526 8388593, 16777213, 33554393, 67108859, 134217689,
527 268435399, 536870909, 1073741789, 2147483647
530 ui32_t i, mprime;
531 NYD_ENTER;
533 i = (n < primes[NELEM(primes) / 4] ? 0
534 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
535 : NELEM(primes) / 2));
537 if ((mprime = primes[i]) > n)
538 break;
539 while (++i < NELEM(primes));
540 if (i == NELEM(primes) && mprime < n)
541 mprime = n;
542 NYD_LEAVE;
543 return mprime;
546 FL char *
547 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
549 struct passwd *pwp;
550 size_t nl, rl;
551 char const *rp, *np;
552 char *rv;
553 bool_t err;
554 NYD2_ENTER;
556 err = FAL0;
558 if (s[0] != '~')
559 goto jasis;
561 if (*(rp = s + 1) == '/' || *rp == '\0')
562 np = homedir;
563 else {
564 if ((rp = strchr(s + 1, '/')) == NULL)
565 rp = (np = UNCONST(s)) + 1;
566 else {
567 nl = PTR2SIZE(rp - s);
568 np = savestrbuf(s, nl);
571 if ((pwp = getpwnam(np)) == NULL) {
572 err = TRU1;
573 goto jasis;
575 np = pwp->pw_name;
578 nl = strlen(np);
579 rl = strlen(rp);
580 rv = salloc(nl + 1 + rl +1);
581 memcpy(rv, np, nl);
582 if (rl > 0) {
583 memcpy(rv + nl, rp, rl);
584 nl += rl;
586 rv[nl] = '\0';
587 goto jleave;
589 jasis:
590 rv = savestr(s);
591 jleave:
592 if (err_or_null != NULL)
593 *err_or_null = err;
594 NYD2_LEAVE;
595 return rv;
598 FL char *
599 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
601 struct shvar_stack top;
602 char *rv;
603 NYD2_ENTER;
605 memset(&top, 0, sizeof top);
607 top.shs_value = s;
608 if ((top.shs_err = err_or_null) != NULL)
609 *err_or_null = FAL0;
610 top.shs_bsesc = bsescape;
611 rv = _sh_exp_var(&top);
612 NYD2_LEAVE;
613 return rv;
616 FL int
617 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
619 char const *xs;
620 int c, n;
621 NYD2_ENTER;
623 xs = *s;
625 if ((c = *xs & 0xFF) == '\0')
626 goto jleave;
627 ++xs;
628 if (c != '\\')
629 goto jleave;
631 switch ((c = *xs & 0xFF)) {
632 case 'a': c = '\a'; break;
633 case 'b': c = '\b'; break;
634 case 'c': c = PROMPT_STOP; break;
635 case 'f': c = '\f'; break;
636 case 'n': c = '\n'; break;
637 case 'r': c = '\r'; break;
638 case 't': c = '\t'; break;
639 case 'v': c = '\v'; break;
641 /* Hexadecimal TODO uses ASCII */
642 case 'X':
643 case 'x': {
644 static ui8_t const hexatoi[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
645 #undef a_HEX
646 #define a_HEX(n) \
647 hexatoi[(ui8_t)((n) - ((n) <= '9' ? 48 : ((n) <= 'F' ? 55 : 87)))]
649 c = 0;
650 ++xs;
651 if(hexchar(*xs))
652 c = a_HEX(*xs);
653 else{
654 --xs;
655 if(options & OPT_D_V)
656 n_err(_("Invalid \"\\xNUMBER\" notation in \"%s\"\n"), xs - 1);
657 c = '\\';
658 goto jleave;
660 ++xs;
661 if(hexchar(*xs)){
662 c <<= 4;
663 c += a_HEX(*xs);
664 ++xs;
666 goto jleave;
668 #undef a_HEX
670 /* octal, with optional 0 prefix */
671 case '0':
672 ++xs;
673 if(0){
674 default:
675 if(*xs == '\0'){
676 c = '\\';
677 break;
680 for (c = 0, n = 3; n-- > 0 && octalchar(*xs); ++xs) {
681 c <<= 3;
682 c |= *xs - '0';
684 goto jleave;
686 /* S-nail extension for nice (get)prompt(()) support */
687 case '&':
688 case '?':
689 case '$':
690 case '@':
691 if (use_nail_extensions) {
692 switch (c) {
693 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
694 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
695 case '$': c = PROMPT_DOLLAR; break;
696 case '@': c = PROMPT_AT; break;
698 break;
701 /* FALLTHRU */
702 case '\0':
703 /* A sole <backslash> at EOS is treated as-is! */
704 c = '\\';
705 /* FALLTHRU */
706 case '\\':
707 break;
710 ++xs;
711 jleave:
712 *s = xs;
713 NYD2_LEAVE;
714 return c;
717 FL char *
718 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
719 { /* FIXME getprompt must mb->wc->mb+reset seq! */
720 static char buf[PROMPT_BUFFER_SIZE];
722 char *cp;
723 char const *ccp_base, *ccp;
724 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
725 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
726 NYD_ENTER;
728 /* No other place to place this */
729 #ifdef HAVE_ERRORS
730 if (options & OPT_INTERACTIVE) {
731 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
732 pstate |= PS_ERRORS_NOTED;
733 fprintf(stderr, _("There are new messages in the error message ring "
734 "(denoted by \"#ERR#\")\n"
735 " The `errors' command manages this message ring\n"));
738 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
739 a_aux_err_cnt_noted = a_aux_err_cnt;
740 } else
741 trigger = FAL0;
742 #endif
744 cp = buf;
745 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
746 #ifdef HAVE_ERRORS
747 if (trigger)
748 ccp_base = "";
749 else
750 #endif
751 goto jleave;
753 #ifdef HAVE_ERRORS
754 if (trigger)
755 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
756 #endif
757 NATCH_CHAR( cclen_base = strlen(ccp_base); )
759 dfmaxlen = 0; /* keep CC happy */
760 trigger = FAL0;
761 jredo:
762 ccp = ccp_base;
763 NATCH_CHAR( cclen = cclen_base; )
764 maxlen = sizeof(buf) -1;
766 for (;;) {
767 size_t l;
768 int c;
770 if (maxlen == 0)
771 goto jleave;
772 #ifdef HAVE_NATCH_CHAR
773 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
774 if (c <= 0) {
775 mblen(NULL, 0);
776 if (c < 0) {
777 *buf = '?';
778 cp = buf + 1;
779 goto jleave;
781 break;
782 } else if ((l = c) > 1) {
783 if (trigger) {
784 memcpy(cp, ccp, l);
785 cp += l;
787 ccp += l;
788 maxlen -= l;
789 continue;
790 } else
791 #endif
792 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
793 if (trigger)
794 *cp++ = (char)c;
795 --maxlen;
796 continue;
798 if (c == 0 || c == PROMPT_STOP)
799 break;
801 if (trigger) {
802 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
803 if (a == NULL)
804 a = "";
805 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
806 cp += l;
807 maxlen -= l;
808 dfmaxlen -= l;
813 if (!trigger) {
814 trigger = TRU1;
815 dfmaxlen = maxlen;
816 goto jredo;
818 jleave:
819 *cp = '\0';
820 NYD_LEAVE;
821 return buf;
824 FL char *
825 nodename(int mayoverride)
827 static char *sys_hostname, *hostname; /* XXX free-at-exit */
829 struct utsname ut;
830 char *hn;
831 #ifdef HAVE_SOCKETS
832 # ifdef HAVE_GETADDRINFO
833 struct addrinfo hints, *res;
834 # else
835 struct hostent *hent;
836 # endif
837 #endif
838 NYD_ENTER;
840 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
842 } else if ((hn = sys_hostname) == NULL) {
843 uname(&ut);
844 hn = ut.nodename;
845 #ifdef HAVE_SOCKETS
846 # ifdef HAVE_GETADDRINFO
847 memset(&hints, 0, sizeof hints);
848 hints.ai_family = AF_UNSPEC;
849 hints.ai_flags = AI_CANONNAME;
850 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
851 if (res->ai_canonname != NULL) {
852 size_t l = strlen(res->ai_canonname) +1;
854 hn = ac_alloc(l);
855 memcpy(hn, res->ai_canonname, l);
857 freeaddrinfo(res);
859 # else
860 hent = gethostbyname(hn);
861 if (hent != NULL)
862 hn = hent->h_name;
863 # endif
864 #endif
865 sys_hostname = sstrdup(hn);
866 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
867 if (hn != ut.nodename)
868 ac_free(hn);
869 #endif
870 hn = sys_hostname;
873 if (hostname != NULL && hostname != sys_hostname)
874 free(hostname);
875 hostname = sstrdup(hn);
876 NYD_LEAVE;
877 return hostname;
880 FL char *
881 getrandstring(size_t length)
883 struct str b64;
884 char *data;
885 size_t i;
886 NYD_ENTER;
888 #ifndef HAVE_POSIX_RANDOM
889 if (_rand == NULL)
890 _rand_init();
891 #endif
893 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
894 * with PAD stripped is still longer than what the user requests, easy way */
895 data = ac_alloc(i = length + 3);
897 #ifndef HAVE_POSIX_RANDOM
898 while (i-- > 0)
899 data[i] = (char)_rand_get8();
900 #else
901 { char *cp = data;
903 while (i > 0) {
904 union {ui32_t i4; char c[4];} r;
905 size_t j;
907 r.i4 = (ui32_t)arc4random();
908 switch ((j = i & 3)) {
909 case 0: cp[3] = r.c[3]; j = 4;
910 case 3: cp[2] = r.c[2];
911 case 2: cp[1] = r.c[1];
912 default: cp[0] = r.c[0]; break;
914 cp += j;
915 i -= j;
918 #endif
920 b64_encode_buf(&b64, data, length + 3,
921 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
922 ac_free(data);
924 assert(b64.l >= length);
925 b64.s[length] = '\0';
926 NYD_LEAVE;
927 return b64.s;
930 FL enum okay
931 makedir(char const *name)
933 struct stat st;
934 enum okay rv = STOP;
935 NYD_ENTER;
937 if (!mkdir(name, 0700))
938 rv = OKAY;
939 else {
940 int e = errno;
941 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
942 S_ISDIR(st.st_mode))
943 rv = OKAY;
945 NYD_LEAVE;
946 return rv;
949 #ifdef HAVE_FCHDIR
950 FL enum okay
951 cwget(struct cw *cw)
953 enum okay rv = STOP;
954 NYD_ENTER;
956 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
957 goto jleave;
958 if (fchdir(cw->cw_fd) == -1) {
959 close(cw->cw_fd);
960 goto jleave;
962 rv = OKAY;
963 jleave:
964 NYD_LEAVE;
965 return rv;
968 FL enum okay
969 cwret(struct cw *cw)
971 enum okay rv = STOP;
972 NYD_ENTER;
974 if (!fchdir(cw->cw_fd))
975 rv = OKAY;
976 NYD_LEAVE;
977 return rv;
980 FL void
981 cwrelse(struct cw *cw)
983 NYD_ENTER;
984 close(cw->cw_fd);
985 NYD_LEAVE;
988 #else /* !HAVE_FCHDIR */
989 FL enum okay
990 cwget(struct cw *cw)
992 enum okay rv = STOP;
993 NYD_ENTER;
995 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
996 rv = OKAY;
997 NYD_LEAVE;
998 return rv;
1001 FL enum okay
1002 cwret(struct cw *cw)
1004 enum okay rv = STOP;
1005 NYD_ENTER;
1007 if (!chdir(cw->cw_wd))
1008 rv = OKAY;
1009 NYD_LEAVE;
1010 return rv;
1013 FL void
1014 cwrelse(struct cw *cw)
1016 NYD_ENTER;
1017 UNUSED(cw);
1018 NYD_LEAVE;
1020 #endif /* !HAVE_FCHDIR */
1022 FL size_t
1023 field_detect_width(char const *buf, size_t blen){
1024 size_t rv;
1025 NYD2_ENTER;
1027 if(blen == UIZ_MAX)
1028 blen = (buf == NULL) ? 0 : strlen(buf);
1029 assert(blen == 0 || buf != NULL);
1031 if((rv = blen) > 0){
1032 #ifdef HAVE_C90AMEND1
1033 mbstate_t mbs;
1034 wchar_t wc;
1036 memset(&mbs, 0, sizeof mbs);
1038 for(rv = 0; blen > 0;){
1039 size_t i = mbrtowc(&wc, buf, blen, &mbs);
1041 switch(i){
1042 case (size_t)-2:
1043 case (size_t)-1:
1044 rv = (size_t)-1;
1045 /* FALLTHRU */
1046 case 0:
1047 blen = 0;
1048 break;
1049 default:
1050 buf += i;
1051 blen -= i;
1052 # ifdef HAVE_WCWIDTH
1053 /* C99 */{
1054 int w = wcwidth(wc);
1056 if(w > 0)
1057 rv += w;
1058 else if(wc == '\t')
1059 ++rv;
1061 # else
1062 if(iswprint(wc))
1063 rv += 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1064 else if(wc == '\t')
1065 ++rv;
1066 # endif
1067 break;
1070 #endif /* HAVE_C90AMEND1 */
1072 NYD2_LEAVE;
1073 return rv;
1076 FL size_t
1077 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1079 size_t rv;
1080 NYD_ENTER;
1082 #ifdef HAVE_NATCH_CHAR
1083 maxlen = MIN(maxlen, blen);
1084 for (rv = 0; maxlen > 0;) {
1085 int ml = mblen(buf, maxlen);
1086 if (ml <= 0) {
1087 mblen(NULL, 0);
1088 break;
1090 buf += ml;
1091 rv += ml;
1092 maxlen -= ml;
1094 #else
1095 rv = MIN(blen, maxlen);
1096 #endif
1097 NYD_LEAVE;
1098 return rv;
1101 FL size_t
1102 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1104 NATCH_CHAR( struct bidi_info bi; )
1105 size_t rv NATCH_CHAR( COMMA i );
1106 NYD_ENTER;
1108 rv = 0;
1109 if (maxlen-- == 0)
1110 goto j_leave;
1112 #ifdef HAVE_NATCH_CHAR
1113 bidi_info_create(&bi);
1114 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1115 bi.bi_end.l = 0;
1116 goto jnobidi;
1119 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1120 maxlen -= i;
1121 else
1122 goto jleave;
1124 if ((i = bi.bi_start.l) > 0) {
1125 memcpy(store, bi.bi_start.s, i);
1126 store += i;
1127 rv += i;
1130 jnobidi:
1131 while (maxlen > 0) {
1132 int ml = mblen(buf, blen);
1133 if (ml <= 0) {
1134 mblen(NULL, 0);
1135 break;
1137 if (UICMP(z, maxlen, <, ml))
1138 break;
1139 if (ml == 1)
1140 *store = *buf;
1141 else
1142 memcpy(store, buf, ml);
1143 store += ml;
1144 buf += ml;
1145 rv += ml;
1146 maxlen -= ml;
1149 if ((i = bi.bi_end.l) > 0) {
1150 memcpy(store, bi.bi_end.s, i);
1151 store += i;
1152 rv += i;
1154 jleave:
1155 *store = '\0';
1157 #else
1158 rv = MIN(blen, maxlen);
1159 memcpy(store, buf, rv);
1160 store[rv] = '\0';
1161 #endif
1162 j_leave:
1163 NYD_LEAVE;
1164 return rv;
1167 FL char *
1168 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1170 NATCH_CHAR( struct bidi_info bi; )
1171 int col_orig = col, n, sz;
1172 bool_t isbidi, isuni, istab, isrepl;
1173 char *nb, *np;
1174 NYD_ENTER;
1176 /* Bidi only on request and when there is 8-bit data */
1177 isbidi = isuni = FAL0;
1178 #ifdef HAVE_NATCH_CHAR
1179 isuni = ((options & OPT_UNICODE) != 0);
1180 bidi_info_create(&bi);
1181 if (bi.bi_start.l == 0)
1182 goto jnobidi;
1183 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1184 goto jnobidi;
1186 if ((size_t)col >= bi.bi_pad)
1187 col -= bi.bi_pad;
1188 else
1189 col = 0;
1190 jnobidi:
1191 #endif
1193 np = nb = salloc(mb_cur_max * strlen(cp) +
1194 ((fill ? col : 0)
1195 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1196 +1));
1198 #ifdef HAVE_NATCH_CHAR
1199 if (isbidi) {
1200 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1201 np += bi.bi_start.l;
1203 #endif
1205 while (*cp != '\0') {
1206 istab = FAL0;
1207 #ifdef HAVE_C90AMEND1
1208 if (mb_cur_max > 1) {
1209 wchar_t wc;
1211 n = 1;
1212 isrepl = TRU1;
1213 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1214 sz = 1;
1215 else if (wc == L'\t') {
1216 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1217 isrepl = FAL0;
1218 istab = TRU1;
1219 } else if (iswprint(wc)) {
1220 # ifndef HAVE_WCWIDTH
1221 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1222 # else
1223 if ((n = wcwidth(wc)) == -1)
1224 n = 1;
1225 else
1226 # endif
1227 isrepl = FAL0;
1229 } else
1230 #endif
1232 n = sz = 1;
1233 istab = (*cp == '\t');
1234 isrepl = !(istab || isprint((uc_i)*cp));
1237 if (n > col)
1238 break;
1239 col -= n;
1241 if (isrepl) {
1242 if (isuni) {
1243 np[0] = (char)0xEFu;
1244 np[1] = (char)0xBFu;
1245 np[2] = (char)0xBDu;
1246 np += 3;
1247 } else
1248 *np++ = '?';
1249 cp += sz;
1250 } else if (istab || (sz == 1 && spacechar(*cp))) {
1251 *np++ = ' ';
1252 ++cp;
1253 } else
1254 while (sz--)
1255 *np++ = *cp++;
1258 if (fill && col != 0) {
1259 if (fill > 0) {
1260 memmove(nb + col, nb, PTR2SIZE(np - nb));
1261 memset(nb, ' ', col);
1262 } else
1263 memset(np, ' ', col);
1264 np += col;
1265 col = 0;
1268 #ifdef HAVE_NATCH_CHAR
1269 if (isbidi) {
1270 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1271 np += bi.bi_end.l;
1273 #endif
1275 *np = '\0';
1276 if (cols_decr_used_or_null != NULL)
1277 *cols_decr_used_or_null -= col_orig - col;
1278 NYD_LEAVE;
1279 return nb;
1282 FL void
1283 makeprint(struct str const *in, struct str *out)
1285 char const *inp, *maxp;
1286 char *outp;
1287 DBG( size_t msz; )
1288 NYD_ENTER;
1290 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1291 inp = in->s;
1292 maxp = inp + in->l;
1294 #ifdef HAVE_NATCH_CHAR
1295 if (mb_cur_max > 1) {
1296 char mbb[MB_LEN_MAX + 1];
1297 wchar_t wc;
1298 int i, n;
1299 bool_t isuni = ((options & OPT_UNICODE) != 0);
1301 out->l = 0;
1302 while (inp < maxp) {
1303 if (*inp & 0200)
1304 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1305 else {
1306 wc = *inp;
1307 n = 1;
1309 if (n == -1) {
1310 /* FIXME Why mbtowc() resetting here?
1311 * FIXME what about ISO 2022-JP plus -- those
1312 * FIXME will loose shifts, then!
1313 * FIXME THUS - we'd need special "known points"
1314 * FIXME to do so - say, after a newline!!
1315 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1316 mbtowc(&wc, NULL, mb_cur_max);
1317 wc = isuni ? 0xFFFD : '?';
1318 n = 1;
1319 } else if (n == 0)
1320 n = 1;
1321 inp += n;
1322 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1323 wc != '\t') {
1324 if ((wc & ~(wchar_t)037) == 0)
1325 wc = isuni ? 0x2400 | wc : '?';
1326 else if (wc == 0177)
1327 wc = isuni ? 0x2421 : '?';
1328 else
1329 wc = isuni ? 0x2426 : '?';
1330 }else if(isuni){ /* TODO ctext */
1331 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1332 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1333 continue;
1334 /* And some zero-width messes */
1335 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1336 continue;
1337 /* Oh about the ISO C wide character interfaces, baby! */
1338 if(wc == 0xFEFF)
1339 continue;
1341 if ((n = wctomb(mbb, wc)) <= 0)
1342 continue;
1343 out->l += n;
1344 assert(out->l < msz);
1345 for (i = 0; i < n; ++i)
1346 *outp++ = mbb[i];
1348 } else
1349 #endif /* NATCH_CHAR */
1351 int c;
1352 while (inp < maxp) {
1353 c = *inp++ & 0377;
1354 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1355 c = '?';
1356 *outp++ = c;
1358 out->l = in->l;
1360 out->s[out->l] = '\0';
1361 NYD_LEAVE;
1364 FL size_t
1365 delctrl(char *cp, size_t len)
1367 size_t x, y;
1368 NYD_ENTER;
1370 for (x = y = 0; x < len; ++x)
1371 if (!cntrlchar(cp[x]))
1372 cp[y++] = cp[x];
1373 cp[y] = '\0';
1374 NYD_LEAVE;
1375 return y;
1378 FL char *
1379 prstr(char const *s)
1381 struct str in, out;
1382 char *rp;
1383 NYD_ENTER;
1385 in.s = UNCONST(s);
1386 in.l = strlen(s);
1387 makeprint(&in, &out);
1388 rp = savestrbuf(out.s, out.l);
1389 free(out.s);
1390 NYD_LEAVE;
1391 return rp;
1394 FL int
1395 prout(char const *s, size_t sz, FILE *fp)
1397 struct str in, out;
1398 int n;
1399 NYD_ENTER;
1401 in.s = UNCONST(s);
1402 in.l = sz;
1403 makeprint(&in, &out);
1404 n = fwrite(out.s, 1, out.l, fp);
1405 free(out.s);
1406 NYD_LEAVE;
1407 return n;
1410 FL size_t
1411 putuc(int u, int c, FILE *fp)
1413 size_t rv;
1414 NYD_ENTER;
1415 UNUSED(u);
1417 #ifdef HAVE_NATCH_CHAR
1418 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1419 char mbb[MB_LEN_MAX];
1420 int i, n;
1422 if ((n = wctomb(mbb, u)) > 0) {
1423 rv = wcwidth(u);
1424 for (i = 0; i < n; ++i)
1425 if (putc(mbb[i] & 0377, fp) == EOF) {
1426 rv = 0;
1427 break;
1429 } else if (n == 0)
1430 rv = (putc('\0', fp) != EOF);
1431 else
1432 rv = 0;
1433 } else
1434 #endif
1435 rv = (putc(c, fp) != EOF);
1436 NYD_LEAVE;
1437 return rv;
1440 FL bool_t
1441 bidi_info_needed(char const *bdat, size_t blen)
1443 bool_t rv = FAL0;
1444 NYD_ENTER;
1446 #ifdef HAVE_NATCH_CHAR
1447 if (options & OPT_UNICODE)
1448 while (blen > 0) {
1449 /* TODO Checking for BIDI character: use S-CText fromutf8
1450 * TODO plus isrighttoleft (or whatever there will be)! */
1451 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1452 if (c == UI32_MAX)
1453 break;
1455 if (c <= 0x05BE)
1456 continue;
1458 /* (Very very fuzzy, awaiting S-CText for good) */
1459 if ((c >= 0x05BE && c <= 0x08E3) ||
1460 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1461 (c >= 0xFE70 && c <= 0xFEFC) ||
1462 (c >= 0x10800 && c <= 0x10C48) ||
1463 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1464 rv = TRU1;
1465 break;
1468 #endif /* HAVE_NATCH_CHAR */
1469 NYD_LEAVE;
1470 return rv;
1473 FL void
1474 bidi_info_create(struct bidi_info *bip)
1476 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1477 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1478 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1479 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1480 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1481 NATCH_CHAR( char const *hb; )
1482 NYD_ENTER;
1484 memset(bip, 0, sizeof *bip);
1485 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1487 #ifdef HAVE_NATCH_CHAR
1488 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1489 switch (*hb) {
1490 case '3':
1491 bip->bi_pad = 2;
1492 /* FALLTHRU */
1493 case '2':
1494 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1495 break;
1496 case '1':
1497 bip->bi_pad = 2;
1498 /* FALLTHRU */
1499 default:
1500 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1501 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1502 break;
1504 bip->bi_start.l = bip->bi_end.l = 3;
1506 #endif
1507 NYD_LEAVE;
1510 FL si8_t
1511 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1513 char *dat, *eptr;
1514 sl_i sli;
1515 si8_t rv;
1516 NYD_ENTER;
1518 assert(inlen == 0 || inbuf != NULL);
1520 if (inlen == UIZ_MAX)
1521 inlen = strlen(inbuf);
1523 if (inlen == 0)
1524 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1525 else {
1526 if ((inlen == 1 && *inbuf == '1') ||
1527 !ascncasecmp(inbuf, "true", inlen) ||
1528 !ascncasecmp(inbuf, "yes", inlen) ||
1529 !ascncasecmp(inbuf, "on", inlen))
1530 rv = 1;
1531 else if ((inlen == 1 && *inbuf == '0') ||
1532 !ascncasecmp(inbuf, "false", inlen) ||
1533 !ascncasecmp(inbuf, "no", inlen) ||
1534 !ascncasecmp(inbuf, "off", inlen))
1535 rv = 0;
1536 else {
1537 dat = ac_alloc(inlen +1);
1538 memcpy(dat, inbuf, inlen);
1539 dat[inlen] = '\0';
1541 sli = strtol(dat, &eptr, 0);
1542 if (*dat != '\0' && *eptr == '\0')
1543 rv = (sli != 0);
1544 else
1545 rv = -1;
1547 ac_free(dat);
1550 NYD_LEAVE;
1551 return rv;
1554 FL si8_t
1555 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1557 si8_t rv;
1558 NYD_ENTER;
1560 assert(inlen == 0 || inbuf != NULL);
1562 if (inlen == UIZ_MAX)
1563 inlen = strlen(inbuf);
1565 if (inlen == 0)
1566 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1567 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1568 !ascncasecmp(inbuf, "ask-", 4) &&
1569 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1570 (options & OPT_INTERACTIVE))
1571 rv = getapproval(prompt, rv);
1572 NYD_LEAVE;
1573 return rv;
1576 FL bool_t
1577 n_is_all_or_aster(char const *name){
1578 bool_t rv;
1579 NYD_ENTER;
1581 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1582 NYD_LEAVE;
1583 return rv;
1586 FL time_t
1587 n_time_epoch(void)
1589 #ifdef HAVE_CLOCK_GETTIME
1590 struct timespec ts;
1591 #elif defined HAVE_GETTIMEOFDAY
1592 struct timeval ts;
1593 #endif
1594 time_t rv;
1595 NYD2_ENTER;
1597 #ifdef HAVE_CLOCK_GETTIME
1598 clock_gettime(CLOCK_REALTIME, &ts);
1599 rv = (time_t)ts.tv_sec;
1600 #elif defined HAVE_GETTIMEOFDAY
1601 gettimeofday(&ts, NULL);
1602 rv = (time_t)ts.tv_sec;
1603 #else
1604 rv = time(NULL);
1605 #endif
1606 NYD2_LEAVE;
1607 return rv;
1610 FL void
1611 time_current_update(struct time_current *tc, bool_t full_update)
1613 NYD_ENTER;
1614 tc->tc_time = n_time_epoch();
1615 if (full_update) {
1616 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1617 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1618 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1620 NYD_LEAVE;
1623 FL uiz_t
1624 n_msleep(uiz_t millis, bool_t ignint){
1625 uiz_t rv;
1626 NYD2_ENTER;
1628 #ifdef HAVE_NANOSLEEP
1629 /* C99 */{
1630 struct timespec ts, trem;
1631 int i;
1633 ts.tv_sec = millis / 1000;
1634 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1636 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1637 ts = trem;
1638 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1641 #elif defined HAVE_SLEEP
1642 if((millis /= 1000) == 0)
1643 millis = 1;
1644 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1645 millis = rv;
1646 #else
1647 # error Configuration should have detected a function for sleeping.
1648 #endif
1650 NYD2_LEAVE;
1651 return rv;
1654 FL void
1655 n_err(char const *format, ...){
1656 va_list ap;
1657 NYD2_ENTER;
1659 va_start(ap, format);
1660 #ifdef HAVE_ERRORS
1661 if(options & OPT_INTERACTIVE)
1662 n_verr(format, ap);
1663 else
1664 #endif
1666 if(a_aux_err_dirty++ == 0)
1667 fputs(UAGENT ": ", stderr);
1668 vfprintf(stderr, format, ap);
1669 if(strchr(format, '\n') != NULL){ /* TODO */
1670 a_aux_err_dirty = 0;
1671 fflush(stderr);
1674 va_end(ap);
1675 NYD2_LEAVE;
1678 FL void
1679 n_verr(char const *format, va_list ap){
1680 /* Check use cases of PS_ERRORS_NOTED, too! */
1681 #ifdef HAVE_ERRORS
1682 char buf[LINESIZE], *xbuf;
1683 int lmax, l;
1684 struct a_aux_err_node *enp;
1686 LCTA(ERRORS_MAX > 3);
1687 #endif
1688 NYD2_ENTER;
1690 if(a_aux_err_dirty++ == 0)
1691 fputs(UAGENT ": ", stderr);
1693 #ifdef HAVE_ERRORS
1694 if(!(options & OPT_INTERACTIVE))
1695 #endif
1697 vfprintf(stderr, format, ap);
1698 goto jleave;
1701 #ifdef HAVE_ERRORS
1702 xbuf = buf;
1703 lmax = sizeof buf;
1704 jredo:
1705 l = vsnprintf(xbuf, lmax, format, ap);
1706 if (l <= 0)
1707 goto jleave;
1708 if (UICMP(z, l, >=, lmax)) {
1709 /* FIXME Cannot reuse va_list
1710 lmax = ++l;
1711 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1712 goto jredo;
1716 fwrite(xbuf, 1, l, stderr);
1718 /* Link it into the `errors' message ring */
1719 if((enp = a_aux_err_tail) == NULL){
1720 jcreat:
1721 enp = scalloc(1, sizeof *enp);
1722 if(a_aux_err_tail != NULL)
1723 a_aux_err_tail->ae_next = enp;
1724 else
1725 a_aux_err_head = enp;
1726 a_aux_err_tail = enp;
1727 ++a_aux_err_cnt;
1728 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1729 if(a_aux_err_cnt < ERRORS_MAX)
1730 goto jcreat;
1732 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1733 a_aux_err_tail->ae_next = enp;
1734 a_aux_err_tail = enp;
1735 free(enp->ae_str.s);
1736 memset(enp, 0, sizeof *enp);
1739 n_str_add_buf(&enp->ae_str, xbuf, l);
1741 if(xbuf != buf)
1742 free(xbuf);
1743 #endif /* HAVE_ERRORS */
1745 jleave:
1746 /* If the format ends with newline, be clean again */
1747 /* C99 */{
1748 size_t i = strlen(format);
1750 if(i > 0 && format[i - 1] == '\n'){
1751 fflush(stderr);
1752 a_aux_err_dirty = 0;
1755 NYD2_LEAVE;
1758 FL void
1759 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1760 va_list ap;
1761 NYD_X;
1763 va_start(ap, format);
1764 vfprintf(stderr, format, ap);
1765 va_end(ap);
1766 fflush(stderr);
1769 FL void
1770 n_perr(char const *msg, int errval){
1771 char const *fmt;
1772 NYD2_ENTER;
1774 if(msg == NULL){
1775 fmt = "%s%s\n";
1776 msg = "";
1777 }else
1778 fmt = "%s: %s\n";
1780 if(errval == 0)
1781 errval = errno;
1783 n_err(fmt, msg, strerror(errval));
1784 NYD2_LEAVE;
1787 FL void
1788 n_alert(char const *format, ...){
1789 va_list ap;
1790 NYD2_ENTER;
1792 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
1794 va_start(ap, format);
1795 n_verr(format, ap);
1796 va_end(ap);
1798 n_err("\n");
1799 NYD2_LEAVE;
1802 FL void
1803 n_panic(char const *format, ...){
1804 va_list ap;
1805 NYD2_ENTER;
1807 if(a_aux_err_dirty > 0){
1808 putc('\n', stderr);
1809 a_aux_err_dirty = 0;
1811 fprintf(stderr, UAGENT ": Panic: ");
1813 va_start(ap, format);
1814 vfprintf(stderr, format, ap);
1815 va_end(ap);
1817 putc('\n', stderr);
1818 fflush(stderr);
1819 NYD2_LEAVE;
1820 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1823 #ifdef HAVE_ERRORS
1824 FL int
1825 c_errors(void *v){
1826 char **argv = v;
1827 struct a_aux_err_node *enp;
1828 NYD_ENTER;
1830 if(*argv == NULL)
1831 goto jlist;
1832 if(argv[1] != NULL)
1833 goto jerr;
1834 if(!asccasecmp(*argv, "show"))
1835 goto jlist;
1836 if(!asccasecmp(*argv, "clear"))
1837 goto jclear;
1838 jerr:
1839 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1840 v = NULL;
1841 jleave:
1842 NYD_LEAVE;
1843 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1845 jlist:{
1846 FILE *fp;
1847 size_t i;
1849 if(a_aux_err_head == NULL){
1850 fprintf(stderr, _("The error ring is empty\n"));
1851 goto jleave;
1854 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1855 NULL){
1856 fprintf(stderr, _("tmpfile"));
1857 v = NULL;
1858 goto jleave;
1861 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1862 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1863 ++i, enp->ae_str.l, enp->ae_str.s);
1864 /* We don't know wether last string ended with NL; be simple */
1865 putc('\n', fp);
1867 page_or_print(fp, 0);
1868 Fclose(fp);
1870 /* FALLTHRU */
1872 jclear:
1873 a_aux_err_tail = NULL;
1874 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1875 while((enp = a_aux_err_head) != NULL){
1876 a_aux_err_head = enp->ae_next;
1877 free(enp->ae_str.s);
1878 free(enp);
1880 goto jleave;
1882 #endif /* HAVE_ERRORS */
1884 /* s-it-mode */