Move path related code to new path.c
[s-mailx.git] / auxlily.c
blobebcc12e60d000894e4df69e1f2bea1fd3c8a4f92
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 #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 int
307 argcount(char **argv)
309 char **ap;
310 NYD_ENTER;
312 for (ap = argv; *ap++ != NULL;)
314 NYD_LEAVE;
315 return (int)PTR2SIZE(ap - argv - 1);
318 FL int
319 screensize(void)
321 int s;
322 char *cp;
323 NYD_ENTER;
325 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
326 s = scrnheight - 2; /* XXX no magics */
327 NYD_LEAVE;
328 return s;
331 FL char const *
332 n_pager_get(char const **env_addon)
334 char const *cp;
335 NYD_ENTER;
337 cp = ok_vlook(PAGER);
338 if (cp == NULL || *cp == '\0')
339 cp = XPAGER;
341 if (env_addon != NULL) {
342 *env_addon = NULL;
343 /* Update the manual upon any changes:
344 * *colour-pager*, $PAGER */
345 if(strstr(rv, "less") != NULL){
346 if(!env_blook("LESS", TRU1))
347 *env_addon =
348 #ifdef HAVE_TERMCAP
349 (pstate & PS_TERMCAP_CA_MODE) ? "LESS=Ri"
350 : !(pstate & PS_TERMCAP_DISABLE) ? "LESS=FRi" :
351 #endif
352 "LESS=FRXi";
353 }else if(strstr(rv, "lv") != NULL){
354 if(!env_blook("LV", TRU1))
355 *env_addon = "LV=-c";
358 NYD_LEAVE;
359 return cp;
362 FL void
363 page_or_print(FILE *fp, size_t lines)
365 int c;
366 char const *cp;
367 NYD_ENTER;
369 fflush_rewind(fp);
371 if ((options & OPT_INTERACTIVE) && (pstate & PS_STARTED) &&
372 (cp = ok_vlook(crt)) != NULL) {
373 char *eptr;
374 union {sl_i sli; size_t rows;} u;
376 u.sli = strtol(cp, &eptr, 0);
377 u.rows = (*cp != '\0' && *eptr == '\0')
378 ? (size_t)u.sli : (size_t)scrnheight;
380 if (u.rows > 0 && lines == 0) {
381 while ((c = getc(fp)) != EOF)
382 if (c == '\n' && ++lines >= u.rows)
383 break;
384 really_rewind(fp);
387 if (lines >= u.rows) {
388 run_command(n_pager_get(NULL), 0, fileno(fp), COMMAND_FD_PASS,
389 NULL, NULL, NULL, NULL);
390 goto jleave;
394 while ((c = getc(fp)) != EOF)
395 putchar(c);
396 jleave:
397 NYD_LEAVE;
400 FL enum protocol
401 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
403 struct stat st;
404 char const *cp;
405 char *np;
406 size_t sz;
407 enum protocol rv = PROTO_UNKNOWN;
408 NYD_ENTER;
410 temporary_protocol_ext = NULL;
412 if (name[0] == '%' && name[1] == ':')
413 name += 2;
414 for (cp = name; *cp && *cp != ':'; cp++)
415 if (!alnumchar(*cp))
416 goto jfile;
418 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
419 if (!strncmp(name, "pop3://", 7)) {
420 #ifdef HAVE_POP3
421 rv = PROTO_POP3;
422 #else
423 n_err(_("No POP3 support compiled in\n"));
424 #endif
425 } else if (!strncmp(name, "pop3s://", 8)) {
426 #if defined HAVE_POP3 && defined HAVE_SSL
427 rv = PROTO_POP3;
428 #else
429 # ifndef HAVE_POP3
430 n_err(_("No POP3 support compiled in\n"));
431 # endif
432 # ifndef HAVE_SSL
433 n_err(_("No SSL support compiled in\n"));
434 # endif
435 #endif
437 goto jleave;
440 /* TODO This is the de facto maildir code and thus belongs into there!
441 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
442 * TODO or (more likely) in addition to *newfolders*) */
443 jfile:
444 rv = PROTO_FILE;
445 np = ac_alloc((sz = strlen(name)) + 4 +1);
446 memcpy(np, name, sz + 1);
447 if (!stat(name, &st)) {
448 if (S_ISDIR(st.st_mode) &&
449 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
450 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
451 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
452 rv = PROTO_MAILDIR;
453 } else {
454 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
455 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
456 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
457 temporary_protocol_ext = cp;
458 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
459 rv = PROTO_MAILDIR;
461 ac_free(np);
462 jleave:
463 NYD_LEAVE;
464 return rv;
467 FL ui32_t
468 torek_hash(char const *name)
470 /* Chris Torek's hash.
471 * NOTE: need to change *at least* mk-okey-map.pl when changing the
472 * algorithm!! */
473 ui32_t h = 0;
474 NYD_ENTER;
476 while (*name != '\0') {
477 h *= 33;
478 h += *name++;
480 NYD_LEAVE;
481 return h;
484 FL unsigned
485 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
487 unsigned h = 0, g;
488 NYD_ENTER;
490 cp--;
491 while (*++cp) {
492 h = (h << 4 & 0xffffffff) + (*cp&0377);
493 if ((g = h & 0xf0000000) != 0) {
494 h = h ^ g >> 24;
495 h = h ^ g;
498 NYD_LEAVE;
499 return h;
502 FL ui32_t
503 nextprime(ui32_t n)
505 static ui32_t const primes[] = {
506 5, 11, 23, 47, 97, 157, 283,
507 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
508 131071, 262139, 524287, 1048573, 2097143, 4194301,
509 8388593, 16777213, 33554393, 67108859, 134217689,
510 268435399, 536870909, 1073741789, 2147483647
513 ui32_t i, mprime;
514 NYD_ENTER;
516 i = (n < primes[NELEM(primes) / 4] ? 0
517 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
518 : NELEM(primes) / 2));
520 if ((mprime = primes[i]) > n)
521 break;
522 while (++i < NELEM(primes));
523 if (i == NELEM(primes) && mprime < n)
524 mprime = n;
525 NYD_LEAVE;
526 return mprime;
529 FL char *
530 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
532 struct passwd *pwp;
533 size_t nl, rl;
534 char const *rp, *np;
535 char *rv;
536 bool_t err;
537 NYD2_ENTER;
539 err = FAL0;
541 if (s[0] != '~')
542 goto jasis;
544 if (*(rp = s + 1) == '/' || *rp == '\0')
545 np = homedir;
546 else {
547 if ((rp = strchr(s + 1, '/')) == NULL)
548 rp = (np = UNCONST(s)) + 1;
549 else {
550 nl = PTR2SIZE(rp - s);
551 np = savestrbuf(s, nl);
554 if ((pwp = getpwnam(np)) == NULL) {
555 err = TRU1;
556 goto jasis;
558 np = pwp->pw_name;
561 nl = strlen(np);
562 rl = strlen(rp);
563 rv = salloc(nl + 1 + rl +1);
564 memcpy(rv, np, nl);
565 if (rl > 0) {
566 memcpy(rv + nl, rp, rl);
567 nl += rl;
569 rv[nl] = '\0';
570 goto jleave;
572 jasis:
573 rv = savestr(s);
574 jleave:
575 if (err_or_null != NULL)
576 *err_or_null = err;
577 NYD2_LEAVE;
578 return rv;
581 FL char *
582 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
584 struct shvar_stack top;
585 char *rv;
586 NYD2_ENTER;
588 memset(&top, 0, sizeof top);
590 top.shs_value = s;
591 if ((top.shs_err = err_or_null) != NULL)
592 *err_or_null = FAL0;
593 top.shs_bsesc = bsescape;
594 rv = _sh_exp_var(&top);
595 NYD2_LEAVE;
596 return rv;
599 FL int
600 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
602 char const *xs;
603 int c, n;
604 NYD2_ENTER;
606 xs = *s;
608 if ((c = *xs & 0xFF) == '\0')
609 goto jleave;
610 ++xs;
611 if (c != '\\')
612 goto jleave;
614 switch ((c = *xs & 0xFF)) {
615 case 'a': c = '\a'; break;
616 case 'b': c = '\b'; break;
617 case 'c': c = PROMPT_STOP; break;
618 case 'f': c = '\f'; break;
619 case 'n': c = '\n'; break;
620 case 'r': c = '\r'; break;
621 case 't': c = '\t'; break;
622 case 'v': c = '\v'; break;
624 /* Hexadecimal TODO uses ASCII */
625 case 'X':
626 case 'x': {
627 static ui8_t const hexatoi[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
628 #undef a_HEX
629 #define a_HEX(n) \
630 hexatoi[(ui8_t)((n) - ((n) <= '9' ? 48 : ((n) <= 'F' ? 55 : 87)))]
632 c = 0;
633 ++xs;
634 if(hexchar(*xs))
635 c = a_HEX(*xs);
636 else{
637 --xs;
638 if(options & OPT_D_V)
639 n_err(_("Invalid \"\\xNUMBER\" notation in \"%s\"\n"), xs - 1);
640 c = '\\';
641 goto jleave;
643 ++xs;
644 if(hexchar(*xs)){
645 c <<= 4;
646 c += a_HEX(*xs);
647 ++xs;
649 goto jleave;
651 #undef a_HEX
653 /* octal, with optional 0 prefix */
654 case '0':
655 ++xs;
656 if(0){
657 default:
658 if(*xs == '\0'){
659 c = '\\';
660 break;
663 for (c = 0, n = 3; n-- > 0 && octalchar(*xs); ++xs) {
664 c <<= 3;
665 c |= *xs - '0';
667 goto jleave;
669 /* S-nail extension for nice (get)prompt(()) support */
670 case '&':
671 case '?':
672 case '$':
673 case '@':
674 if (use_nail_extensions) {
675 switch (c) {
676 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
677 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
678 case '$': c = PROMPT_DOLLAR; break;
679 case '@': c = PROMPT_AT; break;
681 break;
684 /* FALLTHRU */
685 case '\0':
686 /* A sole <backslash> at EOS is treated as-is! */
687 c = '\\';
688 /* FALLTHRU */
689 case '\\':
690 break;
693 ++xs;
694 jleave:
695 *s = xs;
696 NYD2_LEAVE;
697 return c;
700 FL char *
701 getprompt(void) /* TODO evaluate only as necessary (needs a bit) PART OF UI! */
702 { /* FIXME getprompt must mb->wc->mb+reset seq! */
703 static char buf[PROMPT_BUFFER_SIZE];
705 char *cp;
706 char const *ccp_base, *ccp;
707 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
708 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
709 NYD_ENTER;
711 /* No other place to place this */
712 #ifdef HAVE_ERRORS
713 if (options & OPT_INTERACTIVE) {
714 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
715 pstate |= PS_ERRORS_NOTED;
716 fprintf(stderr, _("There are new messages in the error message ring "
717 "(denoted by \"#ERR#\")\n"
718 " The `errors' command manages this message ring\n"));
721 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
722 a_aux_err_cnt_noted = a_aux_err_cnt;
723 } else
724 trigger = FAL0;
725 #endif
727 cp = buf;
728 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
729 #ifdef HAVE_ERRORS
730 if (trigger)
731 ccp_base = "";
732 else
733 #endif
734 goto jleave;
736 #ifdef HAVE_ERRORS
737 if (trigger)
738 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
739 #endif
740 NATCH_CHAR( cclen_base = strlen(ccp_base); )
742 dfmaxlen = 0; /* keep CC happy */
743 trigger = FAL0;
744 jredo:
745 ccp = ccp_base;
746 NATCH_CHAR( cclen = cclen_base; )
747 maxlen = sizeof(buf) -1;
749 for (;;) {
750 size_t l;
751 int c;
753 if (maxlen == 0)
754 goto jleave;
755 #ifdef HAVE_NATCH_CHAR
756 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
757 if (c <= 0) {
758 mblen(NULL, 0);
759 if (c < 0) {
760 *buf = '?';
761 cp = buf + 1;
762 goto jleave;
764 break;
765 } else if ((l = c) > 1) {
766 if (trigger) {
767 memcpy(cp, ccp, l);
768 cp += l;
770 ccp += l;
771 maxlen -= l;
772 continue;
773 } else
774 #endif
775 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
776 if (trigger)
777 *cp++ = (char)c;
778 --maxlen;
779 continue;
781 if (c == 0 || c == PROMPT_STOP)
782 break;
784 if (trigger) {
785 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
786 if (a == NULL)
787 a = "";
788 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
789 cp += l;
790 maxlen -= l;
791 dfmaxlen -= l;
796 if (!trigger) {
797 trigger = TRU1;
798 dfmaxlen = maxlen;
799 goto jredo;
801 jleave:
802 *cp = '\0';
803 NYD_LEAVE;
804 return buf;
807 FL char *
808 nodename(int mayoverride)
810 static char *sys_hostname, *hostname; /* XXX free-at-exit */
812 struct utsname ut;
813 char *hn;
814 #ifdef HAVE_SOCKETS
815 # ifdef HAVE_GETADDRINFO
816 struct addrinfo hints, *res;
817 # else
818 struct hostent *hent;
819 # endif
820 #endif
821 NYD_ENTER;
823 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
825 } else if ((hn = sys_hostname) == NULL) {
826 uname(&ut);
827 hn = ut.nodename;
828 #ifdef HAVE_SOCKETS
829 # ifdef HAVE_GETADDRINFO
830 memset(&hints, 0, sizeof hints);
831 hints.ai_family = AF_UNSPEC;
832 hints.ai_flags = AI_CANONNAME;
833 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
834 if (res->ai_canonname != NULL) {
835 size_t l = strlen(res->ai_canonname) +1;
837 hn = ac_alloc(l);
838 memcpy(hn, res->ai_canonname, l);
840 freeaddrinfo(res);
842 # else
843 hent = gethostbyname(hn);
844 if (hent != NULL)
845 hn = hent->h_name;
846 # endif
847 #endif
848 sys_hostname = sstrdup(hn);
849 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
850 if (hn != ut.nodename)
851 ac_free(hn);
852 #endif
853 hn = sys_hostname;
856 if (hostname != NULL && hostname != sys_hostname)
857 free(hostname);
858 hostname = sstrdup(hn);
859 NYD_LEAVE;
860 return hostname;
863 FL char *
864 getrandstring(size_t length)
866 struct str b64;
867 char *data;
868 size_t i;
869 NYD_ENTER;
871 #ifndef HAVE_POSIX_RANDOM
872 if (_rand == NULL)
873 _rand_init();
874 #endif
876 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
877 * with PAD stripped is still longer than what the user requests, easy way */
878 data = ac_alloc(i = length + 3);
880 #ifndef HAVE_POSIX_RANDOM
881 while (i-- > 0)
882 data[i] = (char)_rand_get8();
883 #else
884 { char *cp = data;
886 while (i > 0) {
887 union {ui32_t i4; char c[4];} r;
888 size_t j;
890 r.i4 = (ui32_t)arc4random();
891 switch ((j = i & 3)) {
892 case 0: cp[3] = r.c[3]; j = 4;
893 case 3: cp[2] = r.c[2];
894 case 2: cp[1] = r.c[1];
895 default: cp[0] = r.c[0]; break;
897 cp += j;
898 i -= j;
901 #endif
903 b64_encode_buf(&b64, data, length + 3,
904 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
905 ac_free(data);
907 assert(b64.l >= length);
908 b64.s[length] = '\0';
909 NYD_LEAVE;
910 return b64.s;
913 FL size_t
914 field_detect_width(char const *buf, size_t blen){
915 size_t rv;
916 NYD2_ENTER;
918 if(blen == UIZ_MAX)
919 blen = (buf == NULL) ? 0 : strlen(buf);
920 assert(blen == 0 || buf != NULL);
922 if((rv = blen) > 0){
923 #ifdef HAVE_C90AMEND1
924 mbstate_t mbs;
925 wchar_t wc;
927 memset(&mbs, 0, sizeof mbs);
929 for(rv = 0; blen > 0;){
930 size_t i = mbrtowc(&wc, buf, blen, &mbs);
932 switch(i){
933 case (size_t)-2:
934 case (size_t)-1:
935 rv = (size_t)-1;
936 /* FALLTHRU */
937 case 0:
938 blen = 0;
939 break;
940 default:
941 buf += i;
942 blen -= i;
943 # ifdef HAVE_WCWIDTH
944 /* C99 */{
945 int w = wcwidth(wc);
947 if(w > 0)
948 rv += w;
949 else if(wc == '\t')
950 ++rv;
952 # else
953 if(iswprint(wc))
954 rv += 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
955 else if(wc == '\t')
956 ++rv;
957 # endif
958 break;
961 #endif /* HAVE_C90AMEND1 */
963 NYD2_LEAVE;
964 return rv;
967 FL size_t
968 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
970 size_t rv;
971 NYD_ENTER;
973 #ifdef HAVE_NATCH_CHAR
974 maxlen = MIN(maxlen, blen);
975 for (rv = 0; maxlen > 0;) {
976 int ml = mblen(buf, maxlen);
977 if (ml <= 0) {
978 mblen(NULL, 0);
979 break;
981 buf += ml;
982 rv += ml;
983 maxlen -= ml;
985 #else
986 rv = MIN(blen, maxlen);
987 #endif
988 NYD_LEAVE;
989 return rv;
992 FL size_t
993 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
995 NATCH_CHAR( struct bidi_info bi; )
996 size_t rv NATCH_CHAR( COMMA i );
997 NYD_ENTER;
999 rv = 0;
1000 if (maxlen-- == 0)
1001 goto j_leave;
1003 #ifdef HAVE_NATCH_CHAR
1004 bidi_info_create(&bi);
1005 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1006 bi.bi_end.l = 0;
1007 goto jnobidi;
1010 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1011 maxlen -= i;
1012 else
1013 goto jleave;
1015 if ((i = bi.bi_start.l) > 0) {
1016 memcpy(store, bi.bi_start.s, i);
1017 store += i;
1018 rv += i;
1021 jnobidi:
1022 while (maxlen > 0) {
1023 int ml = mblen(buf, blen);
1024 if (ml <= 0) {
1025 mblen(NULL, 0);
1026 break;
1028 if (UICMP(z, maxlen, <, ml))
1029 break;
1030 if (ml == 1)
1031 *store = *buf;
1032 else
1033 memcpy(store, buf, ml);
1034 store += ml;
1035 buf += ml;
1036 rv += ml;
1037 maxlen -= ml;
1040 if ((i = bi.bi_end.l) > 0) {
1041 memcpy(store, bi.bi_end.s, i);
1042 store += i;
1043 rv += i;
1045 jleave:
1046 *store = '\0';
1048 #else
1049 rv = MIN(blen, maxlen);
1050 memcpy(store, buf, rv);
1051 store[rv] = '\0';
1052 #endif
1053 j_leave:
1054 NYD_LEAVE;
1055 return rv;
1058 FL char *
1059 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1061 NATCH_CHAR( struct bidi_info bi; )
1062 int col_orig = col, n, sz;
1063 bool_t isbidi, isuni, istab, isrepl;
1064 char *nb, *np;
1065 NYD_ENTER;
1067 /* Bidi only on request and when there is 8-bit data */
1068 isbidi = isuni = FAL0;
1069 #ifdef HAVE_NATCH_CHAR
1070 isuni = ((options & OPT_UNICODE) != 0);
1071 bidi_info_create(&bi);
1072 if (bi.bi_start.l == 0)
1073 goto jnobidi;
1074 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1075 goto jnobidi;
1077 if ((size_t)col >= bi.bi_pad)
1078 col -= bi.bi_pad;
1079 else
1080 col = 0;
1081 jnobidi:
1082 #endif
1084 np = nb = salloc(mb_cur_max * strlen(cp) +
1085 ((fill ? col : 0)
1086 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1087 +1));
1089 #ifdef HAVE_NATCH_CHAR
1090 if (isbidi) {
1091 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1092 np += bi.bi_start.l;
1094 #endif
1096 while (*cp != '\0') {
1097 istab = FAL0;
1098 #ifdef HAVE_C90AMEND1
1099 if (mb_cur_max > 1) {
1100 wchar_t wc;
1102 n = 1;
1103 isrepl = TRU1;
1104 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1105 sz = 1;
1106 else if (wc == L'\t') {
1107 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1108 isrepl = FAL0;
1109 istab = TRU1;
1110 } else if (iswprint(wc)) {
1111 # ifndef HAVE_WCWIDTH
1112 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1113 # else
1114 if ((n = wcwidth(wc)) == -1)
1115 n = 1;
1116 else
1117 # endif
1118 isrepl = FAL0;
1120 } else
1121 #endif
1123 n = sz = 1;
1124 istab = (*cp == '\t');
1125 isrepl = !(istab || isprint((uc_i)*cp));
1128 if (n > col)
1129 break;
1130 col -= n;
1132 if (isrepl) {
1133 if (isuni) {
1134 np[0] = (char)0xEFu;
1135 np[1] = (char)0xBFu;
1136 np[2] = (char)0xBDu;
1137 np += 3;
1138 } else
1139 *np++ = '?';
1140 cp += sz;
1141 } else if (istab || (sz == 1 && spacechar(*cp))) {
1142 *np++ = ' ';
1143 ++cp;
1144 } else
1145 while (sz--)
1146 *np++ = *cp++;
1149 if (fill && col != 0) {
1150 if (fill > 0) {
1151 memmove(nb + col, nb, PTR2SIZE(np - nb));
1152 memset(nb, ' ', col);
1153 } else
1154 memset(np, ' ', col);
1155 np += col;
1156 col = 0;
1159 #ifdef HAVE_NATCH_CHAR
1160 if (isbidi) {
1161 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1162 np += bi.bi_end.l;
1164 #endif
1166 *np = '\0';
1167 if (cols_decr_used_or_null != NULL)
1168 *cols_decr_used_or_null -= col_orig - col;
1169 NYD_LEAVE;
1170 return nb;
1173 FL void
1174 makeprint(struct str const *in, struct str *out)
1176 char const *inp, *maxp;
1177 char *outp;
1178 DBG( size_t msz; )
1179 NYD_ENTER;
1181 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1182 inp = in->s;
1183 maxp = inp + in->l;
1185 #ifdef HAVE_NATCH_CHAR
1186 if (mb_cur_max > 1) {
1187 char mbb[MB_LEN_MAX + 1];
1188 wchar_t wc;
1189 int i, n;
1190 bool_t isuni = ((options & OPT_UNICODE) != 0);
1192 out->l = 0;
1193 while (inp < maxp) {
1194 if (*inp & 0200)
1195 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1196 else {
1197 wc = *inp;
1198 n = 1;
1200 if (n == -1) {
1201 /* FIXME Why mbtowc() resetting here?
1202 * FIXME what about ISO 2022-JP plus -- those
1203 * FIXME will loose shifts, then!
1204 * FIXME THUS - we'd need special "known points"
1205 * FIXME to do so - say, after a newline!!
1206 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1207 mbtowc(&wc, NULL, mb_cur_max);
1208 wc = isuni ? 0xFFFD : '?';
1209 n = 1;
1210 } else if (n == 0)
1211 n = 1;
1212 inp += n;
1213 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1214 wc != '\t') {
1215 if ((wc & ~(wchar_t)037) == 0)
1216 wc = isuni ? 0x2400 | wc : '?';
1217 else if (wc == 0177)
1218 wc = isuni ? 0x2421 : '?';
1219 else
1220 wc = isuni ? 0x2426 : '?';
1221 }else if(isuni){ /* TODO ctext */
1222 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1223 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1224 continue;
1225 /* And some zero-width messes */
1226 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1227 continue;
1228 /* Oh about the ISO C wide character interfaces, baby! */
1229 if(wc == 0xFEFF)
1230 continue;
1232 if ((n = wctomb(mbb, wc)) <= 0)
1233 continue;
1234 out->l += n;
1235 assert(out->l < msz);
1236 for (i = 0; i < n; ++i)
1237 *outp++ = mbb[i];
1239 } else
1240 #endif /* NATCH_CHAR */
1242 int c;
1243 while (inp < maxp) {
1244 c = *inp++ & 0377;
1245 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1246 c = '?';
1247 *outp++ = c;
1249 out->l = in->l;
1251 out->s[out->l] = '\0';
1252 NYD_LEAVE;
1255 FL size_t
1256 delctrl(char *cp, size_t len)
1258 size_t x, y;
1259 NYD_ENTER;
1261 for (x = y = 0; x < len; ++x)
1262 if (!cntrlchar(cp[x]))
1263 cp[y++] = cp[x];
1264 cp[y] = '\0';
1265 NYD_LEAVE;
1266 return y;
1269 FL char *
1270 prstr(char const *s)
1272 struct str in, out;
1273 char *rp;
1274 NYD_ENTER;
1276 in.s = UNCONST(s);
1277 in.l = strlen(s);
1278 makeprint(&in, &out);
1279 rp = savestrbuf(out.s, out.l);
1280 free(out.s);
1281 NYD_LEAVE;
1282 return rp;
1285 FL int
1286 prout(char const *s, size_t sz, FILE *fp)
1288 struct str in, out;
1289 int n;
1290 NYD_ENTER;
1292 in.s = UNCONST(s);
1293 in.l = sz;
1294 makeprint(&in, &out);
1295 n = fwrite(out.s, 1, out.l, fp);
1296 free(out.s);
1297 NYD_LEAVE;
1298 return n;
1301 FL size_t
1302 putuc(int u, int c, FILE *fp)
1304 size_t rv;
1305 NYD_ENTER;
1306 UNUSED(u);
1308 #ifdef HAVE_NATCH_CHAR
1309 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1310 char mbb[MB_LEN_MAX];
1311 int i, n;
1313 if ((n = wctomb(mbb, u)) > 0) {
1314 rv = wcwidth(u);
1315 for (i = 0; i < n; ++i)
1316 if (putc(mbb[i] & 0377, fp) == EOF) {
1317 rv = 0;
1318 break;
1320 } else if (n == 0)
1321 rv = (putc('\0', fp) != EOF);
1322 else
1323 rv = 0;
1324 } else
1325 #endif
1326 rv = (putc(c, fp) != EOF);
1327 NYD_LEAVE;
1328 return rv;
1331 FL bool_t
1332 bidi_info_needed(char const *bdat, size_t blen)
1334 bool_t rv = FAL0;
1335 NYD_ENTER;
1337 #ifdef HAVE_NATCH_CHAR
1338 if (options & OPT_UNICODE)
1339 while (blen > 0) {
1340 /* TODO Checking for BIDI character: use S-CText fromutf8
1341 * TODO plus isrighttoleft (or whatever there will be)! */
1342 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1343 if (c == UI32_MAX)
1344 break;
1346 if (c <= 0x05BE)
1347 continue;
1349 /* (Very very fuzzy, awaiting S-CText for good) */
1350 if ((c >= 0x05BE && c <= 0x08E3) ||
1351 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1352 (c >= 0xFE70 && c <= 0xFEFC) ||
1353 (c >= 0x10800 && c <= 0x10C48) ||
1354 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1355 rv = TRU1;
1356 break;
1359 #endif /* HAVE_NATCH_CHAR */
1360 NYD_LEAVE;
1361 return rv;
1364 FL void
1365 bidi_info_create(struct bidi_info *bip)
1367 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1368 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1369 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1370 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1371 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1372 NATCH_CHAR( char const *hb; )
1373 NYD_ENTER;
1375 memset(bip, 0, sizeof *bip);
1376 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1378 #ifdef HAVE_NATCH_CHAR
1379 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1380 switch (*hb) {
1381 case '3':
1382 bip->bi_pad = 2;
1383 /* FALLTHRU */
1384 case '2':
1385 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1386 break;
1387 case '1':
1388 bip->bi_pad = 2;
1389 /* FALLTHRU */
1390 default:
1391 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1392 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1393 break;
1395 bip->bi_start.l = bip->bi_end.l = 3;
1397 #endif
1398 NYD_LEAVE;
1401 FL si8_t
1402 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1404 char *dat, *eptr;
1405 sl_i sli;
1406 si8_t rv;
1407 NYD_ENTER;
1409 assert(inlen == 0 || inbuf != NULL);
1411 if (inlen == UIZ_MAX)
1412 inlen = strlen(inbuf);
1414 if (inlen == 0)
1415 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1416 else {
1417 if ((inlen == 1 && *inbuf == '1') ||
1418 !ascncasecmp(inbuf, "true", inlen) ||
1419 !ascncasecmp(inbuf, "yes", inlen) ||
1420 !ascncasecmp(inbuf, "on", inlen))
1421 rv = 1;
1422 else if ((inlen == 1 && *inbuf == '0') ||
1423 !ascncasecmp(inbuf, "false", inlen) ||
1424 !ascncasecmp(inbuf, "no", inlen) ||
1425 !ascncasecmp(inbuf, "off", inlen))
1426 rv = 0;
1427 else {
1428 dat = ac_alloc(inlen +1);
1429 memcpy(dat, inbuf, inlen);
1430 dat[inlen] = '\0';
1432 sli = strtol(dat, &eptr, 0);
1433 if (*dat != '\0' && *eptr == '\0')
1434 rv = (sli != 0);
1435 else
1436 rv = -1;
1438 ac_free(dat);
1441 NYD_LEAVE;
1442 return rv;
1445 FL si8_t
1446 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1448 si8_t rv;
1449 NYD_ENTER;
1451 assert(inlen == 0 || inbuf != NULL);
1453 if (inlen == UIZ_MAX)
1454 inlen = strlen(inbuf);
1456 if (inlen == 0)
1457 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1458 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1459 !ascncasecmp(inbuf, "ask-", 4) &&
1460 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1461 (options & OPT_INTERACTIVE))
1462 rv = getapproval(prompt, rv);
1463 NYD_LEAVE;
1464 return rv;
1467 FL bool_t
1468 n_is_all_or_aster(char const *name){
1469 bool_t rv;
1470 NYD_ENTER;
1472 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1473 NYD_LEAVE;
1474 return rv;
1477 FL time_t
1478 n_time_epoch(void)
1480 #ifdef HAVE_CLOCK_GETTIME
1481 struct timespec ts;
1482 #elif defined HAVE_GETTIMEOFDAY
1483 struct timeval ts;
1484 #endif
1485 time_t rv;
1486 NYD2_ENTER;
1488 #ifdef HAVE_CLOCK_GETTIME
1489 clock_gettime(CLOCK_REALTIME, &ts);
1490 rv = (time_t)ts.tv_sec;
1491 #elif defined HAVE_GETTIMEOFDAY
1492 gettimeofday(&ts, NULL);
1493 rv = (time_t)ts.tv_sec;
1494 #else
1495 rv = time(NULL);
1496 #endif
1497 NYD2_LEAVE;
1498 return rv;
1501 FL void
1502 time_current_update(struct time_current *tc, bool_t full_update)
1504 NYD_ENTER;
1505 tc->tc_time = n_time_epoch();
1506 if (full_update) {
1507 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1508 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1509 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1511 NYD_LEAVE;
1514 FL uiz_t
1515 n_msleep(uiz_t millis, bool_t ignint){
1516 uiz_t rv;
1517 NYD2_ENTER;
1519 #ifdef HAVE_NANOSLEEP
1520 /* C99 */{
1521 struct timespec ts, trem;
1522 int i;
1524 ts.tv_sec = millis / 1000;
1525 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1527 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1528 ts = trem;
1529 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1532 #elif defined HAVE_SLEEP
1533 if((millis /= 1000) == 0)
1534 millis = 1;
1535 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1536 millis = rv;
1537 #else
1538 # error Configuration should have detected a function for sleeping.
1539 #endif
1541 NYD2_LEAVE;
1542 return rv;
1545 FL void
1546 n_err(char const *format, ...){
1547 va_list ap;
1548 NYD2_ENTER;
1550 va_start(ap, format);
1551 #ifdef HAVE_ERRORS
1552 if(options & OPT_INTERACTIVE)
1553 n_verr(format, ap);
1554 else
1555 #endif
1557 if(a_aux_err_dirty++ == 0)
1558 fputs(UAGENT ": ", stderr);
1559 vfprintf(stderr, format, ap);
1560 if(strchr(format, '\n') != NULL){ /* TODO */
1561 a_aux_err_dirty = 0;
1562 fflush(stderr);
1565 va_end(ap);
1566 NYD2_LEAVE;
1569 FL void
1570 n_verr(char const *format, va_list ap){
1571 /* Check use cases of PS_ERRORS_NOTED, too! */
1572 #ifdef HAVE_ERRORS
1573 char buf[LINESIZE], *xbuf;
1574 int lmax, l;
1575 struct a_aux_err_node *enp;
1577 LCTA(ERRORS_MAX > 3);
1578 #endif
1579 NYD2_ENTER;
1581 if(a_aux_err_dirty++ == 0)
1582 fputs(UAGENT ": ", stderr);
1584 #ifdef HAVE_ERRORS
1585 if(!(options & OPT_INTERACTIVE))
1586 #endif
1588 vfprintf(stderr, format, ap);
1589 goto jleave;
1592 #ifdef HAVE_ERRORS
1593 xbuf = buf;
1594 lmax = sizeof buf;
1595 jredo:
1596 l = vsnprintf(xbuf, lmax, format, ap);
1597 if (l <= 0)
1598 goto jleave;
1599 if (UICMP(z, l, >=, lmax)) {
1600 /* FIXME Cannot reuse va_list
1601 lmax = ++l;
1602 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1603 goto jredo;
1607 fwrite(xbuf, 1, l, stderr);
1609 /* Link it into the `errors' message ring */
1610 if((enp = a_aux_err_tail) == NULL){
1611 jcreat:
1612 enp = scalloc(1, sizeof *enp);
1613 if(a_aux_err_tail != NULL)
1614 a_aux_err_tail->ae_next = enp;
1615 else
1616 a_aux_err_head = enp;
1617 a_aux_err_tail = enp;
1618 ++a_aux_err_cnt;
1619 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1620 if(a_aux_err_cnt < ERRORS_MAX)
1621 goto jcreat;
1623 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1624 a_aux_err_tail->ae_next = enp;
1625 a_aux_err_tail = enp;
1626 free(enp->ae_str.s);
1627 memset(enp, 0, sizeof *enp);
1630 n_str_add_buf(&enp->ae_str, xbuf, l);
1632 if(xbuf != buf)
1633 free(xbuf);
1634 #endif /* HAVE_ERRORS */
1636 jleave:
1637 /* If the format ends with newline, be clean again */
1638 /* C99 */{
1639 size_t i = strlen(format);
1641 if(i > 0 && format[i - 1] == '\n'){
1642 fflush(stderr);
1643 a_aux_err_dirty = 0;
1646 NYD2_LEAVE;
1649 FL void
1650 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1651 va_list ap;
1652 NYD_X;
1654 va_start(ap, format);
1655 vfprintf(stderr, format, ap);
1656 va_end(ap);
1657 fflush(stderr);
1660 FL void
1661 n_perr(char const *msg, int errval){
1662 char const *fmt;
1663 NYD2_ENTER;
1665 if(msg == NULL){
1666 fmt = "%s%s\n";
1667 msg = "";
1668 }else
1669 fmt = "%s: %s\n";
1671 if(errval == 0)
1672 errval = errno;
1674 n_err(fmt, msg, strerror(errval));
1675 NYD2_LEAVE;
1678 FL void
1679 n_alert(char const *format, ...){
1680 va_list ap;
1681 NYD2_ENTER;
1683 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
1685 va_start(ap, format);
1686 n_verr(format, ap);
1687 va_end(ap);
1689 n_err("\n");
1690 NYD2_LEAVE;
1693 FL void
1694 n_panic(char const *format, ...){
1695 va_list ap;
1696 NYD2_ENTER;
1698 if(a_aux_err_dirty > 0){
1699 putc('\n', stderr);
1700 a_aux_err_dirty = 0;
1702 fprintf(stderr, UAGENT ": Panic: ");
1704 va_start(ap, format);
1705 vfprintf(stderr, format, ap);
1706 va_end(ap);
1708 putc('\n', stderr);
1709 fflush(stderr);
1710 NYD2_LEAVE;
1711 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1714 #ifdef HAVE_ERRORS
1715 FL int
1716 c_errors(void *v){
1717 char **argv = v;
1718 struct a_aux_err_node *enp;
1719 NYD_ENTER;
1721 if(*argv == NULL)
1722 goto jlist;
1723 if(argv[1] != NULL)
1724 goto jerr;
1725 if(!asccasecmp(*argv, "show"))
1726 goto jlist;
1727 if(!asccasecmp(*argv, "clear"))
1728 goto jclear;
1729 jerr:
1730 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1731 v = NULL;
1732 jleave:
1733 NYD_LEAVE;
1734 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1736 jlist:{
1737 FILE *fp;
1738 size_t i;
1740 if(a_aux_err_head == NULL){
1741 fprintf(stderr, _("The error ring is empty\n"));
1742 goto jleave;
1745 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1746 NULL){
1747 fprintf(stderr, _("tmpfile"));
1748 v = NULL;
1749 goto jleave;
1752 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1753 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1754 ++i, enp->ae_str.l, enp->ae_str.s);
1755 /* We don't know wether last string ended with NL; be simple */
1756 putc('\n', fp);
1758 page_or_print(fp, 0);
1759 Fclose(fp);
1761 /* FALLTHRU */
1763 jclear:
1764 a_aux_err_tail = NULL;
1765 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1766 while((enp = a_aux_err_head) != NULL){
1767 a_aux_err_head = enp->ae_next;
1768 free(enp->ae_str.s);
1769 free(enp);
1771 goto jleave;
1773 #endif /* HAVE_ERRORS */
1775 /* s-it-mode */