send.c:sendpart(): iconv(3) when `write'ing to a file!
[s-mailx.git] / auxlily.c
blob0b65a7ac78c52496bb11c5f85afad5d25462b257
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 - 2017 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(
107 # if n_OS_OPENBSD
108 "/dev/arandom"
109 # else
110 "/dev/urandom"
111 # endif
112 , O_RDONLY)) != -1) {
113 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
115 close(u.fd);
116 if (ok)
117 goto jleave;
120 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
121 for (u.i = n_NELEM(_rand->b32); u.i-- != 0;) {
122 ui32_t t, k;
124 # ifdef HAVE_CLOCK_GETTIME
125 clock_gettime(CLOCK_REALTIME, &ts);
126 t = (ui32_t)ts.tv_nsec;
127 # else
128 gettimeofday(&ts, NULL);
129 t = (ui32_t)ts.tv_usec;
130 # endif
131 if (rnd & 1)
132 t = (t >> 16) | (t << 16);
133 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
134 _rand->b32[t % n_NELEM(_rand->b32)] ^= seed;
135 if (rnd == 7 || rnd == 17)
136 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
137 k = _rand->b32[u.i] % n_NELEM(_rand->b32);
138 _rand->b32[k] ^= _rand->b32[u.i];
139 seed ^= _rand_weak(_rand->b32[k]);
140 if ((rnd & 3) == 3)
141 seed ^= nextprime(seed);
145 for (u.i = 5 * sizeof(_rand->b8); u.i != 0; --u.i)
146 _rand_get8();
147 jleave:
148 NYD2_LEAVE;
151 static ui32_t
152 _rand_weak(ui32_t seed)
154 /* From "Random number generators: good ones are hard to find",
155 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
156 * October 1988, p. 1195.
157 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
158 ui32_t hi;
160 if (seed == 0)
161 seed = 123459876;
162 hi = seed / 127773;
163 seed %= 127773;
164 seed = (seed * 16807) - (hi * 2836);
165 if ((si32_t)seed < 0)
166 seed += SI32_MAX;
167 return seed;
170 SINLINE ui8_t
171 _rand_get8(void)
173 ui8_t si, sj;
175 si = _rand->a._dat[++_rand->a._i];
176 sj = _rand->a._dat[_rand->a._j += si];
177 _rand->a._dat[_rand->a._i] = sj;
178 _rand->a._dat[_rand->a._j] = si;
179 return _rand->a._dat[(ui8_t)(si + sj)];
181 #endif /* HAVE_POSIX_RANDOM */
183 FL int
184 screensize(void){
185 ul_i s;
186 char *cp;
187 NYD2_ENTER;
189 if((cp = ok_vlook(screen)) == NULL || (s = strtoul(cp, NULL, 0)) == 0)
190 s = (ul_i)scrnheight;
191 s -= 2; /* XXX no magics */
192 if(s > INT_MAX) /* TODO function should return unsigned */
193 s = INT_MAX;
194 NYD2_LEAVE;
195 return (int)s;
198 FL char const *
199 n_pager_get(char const **env_addon){
200 char const *rv;
201 NYD_ENTER;
203 rv = ok_vlook(PAGER);
205 if(env_addon != NULL){
206 *env_addon = NULL;
207 /* Update the manual upon any changes:
208 * *colour-pager*, $PAGER */
209 if(strstr(rv, "less") != NULL){
210 if(getenv("LESS") == NULL)
211 *env_addon =
212 #ifdef HAVE_TERMCAP
213 (pstate & PS_TERMCAP_CA_MODE) ? "LESS=Ri"
214 : !(pstate & PS_TERMCAP_DISABLE) ? "LESS=FRi" :
215 #endif
216 "LESS=FRXi";
217 }else if(strstr(rv, "lv") != NULL){
218 if(getenv("LV") == NULL)
219 *env_addon = "LV=-c";
222 NYD_LEAVE;
223 return rv;
226 FL void
227 page_or_print(FILE *fp, size_t lines)
229 int c;
230 char const *cp;
231 NYD_ENTER;
233 fflush_rewind(fp);
235 if (n_source_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
236 size_t rows;
238 rows = (*cp == '\0') ? (size_t)scrnheight : strtoul(cp, NULL, 0);
240 if (rows > 0 && lines == 0) {
241 while ((c = getc(fp)) != EOF)
242 if (c == '\n' && ++lines >= rows)
243 break;
244 really_rewind(fp);
247 if (lines >= rows) {
248 char const *env_add[2], *pager;
250 pager = n_pager_get(&env_add[0]);
251 env_add[1] = NULL;
252 run_command(pager, NULL, fileno(fp), COMMAND_FD_PASS, NULL,NULL,NULL,
253 env_add);
254 goto jleave;
258 while ((c = getc(fp)) != EOF)
259 putchar(c);
260 jleave:
261 NYD_LEAVE;
264 FL enum protocol
265 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
267 struct stat st;
268 char const *cp;
269 char *np;
270 size_t sz;
271 enum protocol rv = PROTO_UNKNOWN;
272 NYD_ENTER;
274 temporary_protocol_ext = NULL;
276 if (name[0] == '%' && name[1] == ':')
277 name += 2;
278 for (cp = name; *cp && *cp != ':'; cp++)
279 if (!alnumchar(*cp))
280 goto jfile;
282 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
283 if (!strncmp(name, "pop3://", 7)) {
284 #ifdef HAVE_POP3
285 rv = PROTO_POP3;
286 #else
287 n_err(_("No POP3 support compiled in\n"));
288 #endif
289 } else if (!strncmp(name, "pop3s://", 8)) {
290 #if defined HAVE_POP3 && defined HAVE_SSL
291 rv = PROTO_POP3;
292 #else
293 # ifndef HAVE_POP3
294 n_err(_("No POP3 support compiled in\n"));
295 # endif
296 # ifndef HAVE_SSL
297 n_err(_("No SSL support compiled in\n"));
298 # endif
299 #endif
301 goto jleave;
304 /* TODO This is the de facto maildir code and thus belongs into there!
305 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
306 * TODO or (more likely) in addition to *newfolders*) */
307 jfile:
308 rv = PROTO_FILE;
309 np = ac_alloc((sz = strlen(name)) + 4 +1);
310 memcpy(np, name, sz + 1);
311 if (!stat(name, &st)) {
312 if (S_ISDIR(st.st_mode) &&
313 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
314 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
315 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
316 rv = PROTO_MAILDIR;
317 } else {
318 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
319 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
320 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
321 temporary_protocol_ext = cp;
322 else if ((cp = ok_vlook(newfolders)) != NULL &&
323 !asccasecmp(cp, "maildir"))
324 rv = PROTO_MAILDIR;
326 ac_free(np);
327 jleave:
328 NYD_LEAVE;
329 return rv;
332 FL char *
333 n_c_to_hex_base16(char store[3], char c){
334 static char const itoa16[] = "0123456789ABCDEF";
335 NYD2_ENTER;
337 store[2] = '\0';
338 store[1] = itoa16[(ui8_t)c & 0x0F];
339 c = ((ui8_t)c >> 4) & 0x0F;
340 store[0] = itoa16[(ui8_t)c];
341 NYD2_LEAVE;
342 return store;
345 FL si32_t
346 n_c_from_hex_base16(char const hex[2]){
347 static ui8_t const atoi16[] = {
348 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
349 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
350 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
351 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
352 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
353 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
354 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
356 ui8_t i1, i2;
357 si32_t rv;
358 NYD2_ENTER;
360 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
361 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
362 goto jerr;
363 i1 = atoi16[i1];
364 i2 = atoi16[i2];
365 if ((i1 | i2) & 0xF0u)
366 goto jerr;
367 rv = i1;
368 rv <<= 4;
369 rv += i2;
370 jleave:
371 NYD2_LEAVE;
372 return rv;
373 jerr:
374 rv = -1;
375 goto jleave;
378 FL ui32_t
379 torek_hash(char const *name)
381 /* Chris Torek's hash.
382 * NOTE: need to change *at least* mk-okey-map.pl when changing the
383 * algorithm!! */
384 ui32_t h = 0;
385 NYD_ENTER;
387 while (*name != '\0') {
388 h *= 33;
389 h += *name++;
391 NYD_LEAVE;
392 return h;
395 FL ui32_t
396 torek_ihashn(char const *dat, size_t len){
397 /* See torek_hash() */
398 char c;
399 ui32_t h;
400 NYD_ENTER;
402 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
403 h = (h * 33) + lowerconv(c);
404 NYD_LEAVE;
405 return h;
408 FL ui32_t
409 nextprime(ui32_t n)
411 static ui32_t const primes[] = {
412 5, 11, 23, 47, 97, 157, 283,
413 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
414 131071, 262139, 524287, 1048573, 2097143, 4194301,
415 8388593, 16777213, 33554393, 67108859, 134217689,
416 268435399, 536870909, 1073741789, 2147483647
419 ui32_t i, mprime;
420 NYD_ENTER;
422 i = (n < primes[n_NELEM(primes) / 4] ? 0
423 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
424 : n_NELEM(primes) / 2));
426 if ((mprime = primes[i]) > n)
427 break;
428 while (++i < n_NELEM(primes));
429 if (i == n_NELEM(primes) && mprime < n)
430 mprime = n;
431 NYD_LEAVE;
432 return mprime;
435 FL char const *
436 n_getdeadletter(void){
437 char const *cp_base, *cp;
438 NYD_ENTER;
440 cp_base = NULL;
441 jredo:
442 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
443 if(cp == NULL || strlen(cp) >= PATH_MAX){
444 if(cp_base == NULL){
445 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
446 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
447 ok_vclear(DEAD);
448 goto jredo;
449 }else{
450 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
451 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
454 NYD_LEAVE;
455 return cp;
458 FL char *
459 nodename(int mayoverride)
461 static char *sys_hostname, *hostname; /* XXX free-at-exit */
463 struct utsname ut;
464 char *hn;
465 #ifdef HAVE_SOCKETS
466 # ifdef HAVE_GETADDRINFO
467 struct addrinfo hints, *res;
468 # else
469 struct hostent *hent;
470 # endif
471 #endif
472 NYD_ENTER;
474 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
476 } else if ((hn = sys_hostname) == NULL) {
477 uname(&ut);
478 hn = ut.nodename;
479 #ifdef HAVE_SOCKETS
480 # ifdef HAVE_GETADDRINFO
481 memset(&hints, 0, sizeof hints);
482 hints.ai_family = AF_UNSPEC;
483 hints.ai_flags = AI_CANONNAME;
484 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
485 if (res->ai_canonname != NULL) {
486 size_t l = strlen(res->ai_canonname) +1;
488 hn = ac_alloc(l);
489 memcpy(hn, res->ai_canonname, l);
491 freeaddrinfo(res);
493 # else
494 hent = gethostbyname(hn);
495 if (hent != NULL)
496 hn = hent->h_name;
497 # endif
498 #endif
499 sys_hostname = sstrdup(hn);
500 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
501 if (hn != ut.nodename)
502 ac_free(hn);
503 #endif
504 hn = sys_hostname;
507 if (hostname != NULL && hostname != sys_hostname)
508 free(hostname);
509 hostname = sstrdup(hn);
510 NYD_LEAVE;
511 return hostname;
514 FL char *
515 getrandstring(size_t length)
517 struct str b64;
518 char *data;
519 size_t i;
520 NYD_ENTER;
522 #ifndef HAVE_POSIX_RANDOM
523 if (_rand == NULL)
524 _rand_init();
525 #endif
527 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
528 * with PAD stripped is still longer than what the user requests, easy way */
529 data = ac_alloc(i = length + 3);
531 #ifndef HAVE_POSIX_RANDOM
532 while (i-- > 0)
533 data[i] = (char)_rand_get8();
534 #else
535 { char *cp = data;
537 while (i > 0) {
538 union {ui32_t i4; char c[4];} r;
539 size_t j;
541 r.i4 = (ui32_t)arc4random();
542 switch ((j = i & 3)) {
543 case 0: cp[3] = r.c[3]; j = 4;
544 case 3: cp[2] = r.c[2];
545 case 2: cp[1] = r.c[1];
546 default: cp[0] = r.c[0]; break;
548 cp += j;
549 i -= j;
552 #endif
554 assert(length + 3 < UIZ_MAX / 4);
555 b64_encode_buf(&b64, data, length + 3,
556 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
557 ac_free(data);
559 assert(b64.l >= length);
560 b64.s[length] = '\0';
561 NYD_LEAVE;
562 return b64.s;
565 FL si8_t
566 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
568 char *dat, *eptr;
569 sl_i sli;
570 si8_t rv;
571 NYD_ENTER;
573 assert(inlen == 0 || inbuf != NULL);
575 if (inlen == UIZ_MAX)
576 inlen = strlen(inbuf);
578 if (inlen == 0)
579 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
580 else {
581 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
582 !ascncasecmp(inbuf, "true", inlen) ||
583 !ascncasecmp(inbuf, "yes", inlen) ||
584 !ascncasecmp(inbuf, "on", inlen))
585 rv = 1;
586 else if ((inlen == 1 &&
587 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
588 !ascncasecmp(inbuf, "false", inlen) ||
589 !ascncasecmp(inbuf, "no", inlen) ||
590 !ascncasecmp(inbuf, "off", inlen))
591 rv = 0;
592 else {
593 dat = ac_alloc(inlen +1);
594 memcpy(dat, inbuf, inlen);
595 dat[inlen] = '\0';
597 sli = strtol(dat, &eptr, 0);
598 if (*dat != '\0' && *eptr == '\0')
599 rv = (sli != 0);
600 else
601 rv = -1;
603 ac_free(dat);
606 NYD_LEAVE;
607 return rv;
610 FL si8_t
611 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
613 si8_t rv;
614 NYD_ENTER;
616 assert(inlen == 0 || inbuf != NULL);
618 if (inlen == UIZ_MAX)
619 inlen = strlen(inbuf);
621 if (inlen == 0)
622 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
623 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
624 !ascncasecmp(inbuf, "ask-", 4) &&
625 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
626 (options & OPT_INTERACTIVE))
627 rv = getapproval(prompt, rv);
628 NYD_LEAVE;
629 return rv;
632 FL bool_t
633 n_is_all_or_aster(char const *name){
634 bool_t rv;
635 NYD_ENTER;
637 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
638 NYD_LEAVE;
639 return rv;
642 FL time_t
643 n_time_epoch(void)
645 #ifdef HAVE_CLOCK_GETTIME
646 struct timespec ts;
647 #elif defined HAVE_GETTIMEOFDAY
648 struct timeval ts;
649 #endif
650 time_t rv;
651 char const *cp;
652 NYD2_ENTER;
654 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){ /* TODO */
655 /* TODO This is marked "posnum", b and therefore 0<=X<=UINT_MAX.
656 * TODO This means we have a Sun, 07 Feb 2106 07:28:15 +0100 problem.
657 * TODO Therefore we need a num_ui64= type in v15 */
658 rv = (time_t)strtoul(cp, NULL, 0);
659 }else{
660 #ifdef HAVE_CLOCK_GETTIME
661 clock_gettime(CLOCK_REALTIME, &ts);
662 rv = (time_t)ts.tv_sec;
663 #elif defined HAVE_GETTIMEOFDAY
664 gettimeofday(&ts, NULL);
665 rv = (time_t)ts.tv_sec;
666 #else
667 rv = time(NULL);
668 #endif
670 NYD2_LEAVE;
671 return rv;
674 FL void
675 time_current_update(struct time_current *tc, bool_t full_update)
677 NYD_ENTER;
678 tc->tc_time = n_time_epoch();
679 if (full_update) {
680 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
681 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
682 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
684 NYD_LEAVE;
687 FL uiz_t
688 n_msleep(uiz_t millis, bool_t ignint){
689 uiz_t rv;
690 NYD2_ENTER;
692 #ifdef HAVE_NANOSLEEP
693 /* C99 */{
694 struct timespec ts, trem;
695 int i;
697 ts.tv_sec = millis / 1000;
698 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
700 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
701 ts = trem;
702 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
705 #elif defined HAVE_SLEEP
706 if((millis /= 1000) == 0)
707 millis = 1;
708 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
709 millis = rv;
710 #else
711 # error Configuration should have detected a function for sleeping.
712 #endif
714 NYD2_LEAVE;
715 return rv;
718 FL void
719 n_err(char const *format, ...){
720 va_list ap;
721 NYD2_ENTER;
723 va_start(ap, format);
724 #ifdef HAVE_ERRORS
725 if(options & OPT_INTERACTIVE)
726 n_verr(format, ap);
727 else
728 #endif
730 size_t len;
731 bool_t doname, doflush;
733 doflush = FAL0;
734 while(*format == '\n'){
735 doflush = TRU1;
736 putc('\n', stderr);
737 ++format;
740 if((doname = doflush))
741 a_aux_err_linelen = 0;
743 if((len = strlen(format)) > 0){
744 if(doname || a_aux_err_linelen == 0)
745 fputs(VAL_UAGENT ": ", stderr);
746 vfprintf(stderr, format, ap);
748 /* C99 */{
749 size_t i = len;
751 if(format[--len] == '\n'){
752 a_aux_err_linelen = (i -= ++len);
753 break;
755 ++a_aux_err_linelen;
756 }while(len > 0);
760 if(doflush)
761 fflush(stderr);
763 va_end(ap);
764 NYD2_LEAVE;
767 FL void
768 n_verr(char const *format, va_list ap){
769 /* Check use cases of PS_ERRORS_NOTED, too! */
770 #ifdef HAVE_ERRORS
771 struct a_aux_err_node *enp;
772 #endif
773 bool_t doname, doflush;
774 size_t len;
775 NYD2_ENTER;
777 doflush = FAL0;
778 while(*format == '\n'){
779 doflush = TRU1;
780 putc('\n', stderr);
781 ++format;
784 if((doname = doflush)){
785 a_aux_err_linelen = 0;
786 #ifdef HAVE_ERRORS
787 if(options & OPT_INTERACTIVE){
788 if((enp = a_aux_err_tail) != NULL &&
789 (enp->ae_str.s_len > 0 &&
790 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
791 n_string_push_c(&enp->ae_str, '\n');
793 #endif
796 if((len = strlen(format)) == 0)
797 goto jleave;
798 #ifdef HAVE_ERRORS
799 pstate |= PS_ERRORS_PROMPT;
800 #endif
802 if(doname || a_aux_err_linelen == 0)
803 fputs(VAL_UAGENT ": ", stderr);
805 /* C99 */{
806 size_t i = len;
808 if(format[--len] == '\n'){
809 a_aux_err_linelen = (i -= ++len);
810 break;
812 ++a_aux_err_linelen;
813 }while(len > 0);
816 #ifdef HAVE_ERRORS
817 if(!(options & OPT_INTERACTIVE))
818 #endif
819 vfprintf(stderr, format, ap);
820 #ifdef HAVE_ERRORS
821 else{
822 int imax, i;
823 n_LCTAV(ERRORS_MAX > 3);
825 /* Link it into the `errors' message ring */
826 if((enp = a_aux_err_tail) == NULL){
827 jcreat:
828 enp = smalloc(sizeof *enp);
829 enp->ae_next = NULL;
830 n_string_creat(&enp->ae_str);
831 if(a_aux_err_tail != NULL)
832 a_aux_err_tail->ae_next = enp;
833 else
834 a_aux_err_head = enp;
835 a_aux_err_tail = enp;
836 ++a_aux_err_cnt;
837 }else if(doname ||
838 (enp->ae_str.s_len > 0 &&
839 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
840 if(a_aux_err_cnt < ERRORS_MAX)
841 goto jcreat;
843 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
844 a_aux_err_tail->ae_next = enp;
845 a_aux_err_tail = enp;
846 enp->ae_next = NULL;
847 n_string_trunc(&enp->ae_str, 0);
850 # ifdef HAVE_N_VA_COPY
851 imax = 64;
852 # else
853 imax = n_MIN(LINESIZE, 1024);
854 # endif
855 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
856 # ifdef HAVE_N_VA_COPY
857 va_list vac;
859 n_va_copy(vac, ap);
860 # else
861 # define vac ap
862 # endif
864 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
865 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
866 # ifdef HAVE_N_VA_COPY
867 va_end(vac);
868 # else
869 # undef vac
870 # endif
871 if(i <= 0)
872 goto jleave;
873 if(UICMP(z, i, >=, imax)){
874 # ifdef HAVE_N_VA_COPY
875 /* XXX Check overflow for upcoming LEN+++i! */
876 n_string_trunc(&enp->ae_str, len);
877 continue;
878 # else
879 i = (int)strlen(&enp->ae_str.s_dat[len]);
880 # endif
882 break;
884 n_string_trunc(&enp->ae_str, len + (size_t)i);
886 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, stderr);
888 #endif /* HAVE_ERRORS */
890 jleave:
891 if(doflush)
892 fflush(stderr);
893 NYD2_LEAVE;
896 FL void
897 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
898 va_list ap;
899 NYD_X;
901 va_start(ap, format);
902 vfprintf(stderr, format, ap);
903 va_end(ap);
904 fflush(stderr);
907 FL void
908 n_perr(char const *msg, int errval){
909 char const *fmt;
910 NYD2_ENTER;
912 if(msg == NULL){
913 fmt = "%s%s\n";
914 msg = n_empty;
915 }else
916 fmt = "%s: %s\n";
918 if(errval == 0)
919 errval = errno;
921 n_err(fmt, msg, strerror(errval));
922 NYD2_LEAVE;
925 FL void
926 n_alert(char const *format, ...){
927 va_list ap;
928 NYD2_ENTER;
930 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
932 va_start(ap, format);
933 n_verr(format, ap);
934 va_end(ap);
936 n_err("\n");
937 NYD2_LEAVE;
940 FL void
941 n_panic(char const *format, ...){
942 va_list ap;
943 NYD2_ENTER;
945 if(a_aux_err_linelen > 0){
946 putc('\n', stderr);
947 a_aux_err_linelen = 0;
949 fprintf(stderr, VAL_UAGENT ": Panic: ");
951 va_start(ap, format);
952 vfprintf(stderr, format, ap);
953 va_end(ap);
955 putc('\n', stderr);
956 fflush(stderr);
957 NYD2_LEAVE;
958 abort(); /* Was exit(EXIT_ERR); for a while, but no */
961 #ifdef HAVE_ERRORS
962 FL int
963 c_errors(void *v){
964 char **argv = v;
965 struct a_aux_err_node *enp;
966 NYD_ENTER;
968 if(*argv == NULL)
969 goto jlist;
970 if(argv[1] != NULL)
971 goto jerr;
972 if(!asccasecmp(*argv, "show"))
973 goto jlist;
974 if(!asccasecmp(*argv, "clear"))
975 goto jclear;
976 jerr:
977 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
978 v = NULL;
979 jleave:
980 NYD_LEAVE;
981 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
983 jlist:{
984 FILE *fp;
985 size_t i;
987 if(a_aux_err_head == NULL){
988 fprintf(stderr, _("The error ring is empty\n"));
989 goto jleave;
992 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
993 NULL){
994 fprintf(stderr, _("tmpfile"));
995 v = NULL;
996 goto jleave;
999 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1000 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1001 /* We don't know whether last string ended with NL; be simple XXX */
1002 putc('\n', fp);
1004 page_or_print(fp, 0);
1005 Fclose(fp);
1007 /* FALLTHRU */
1009 jclear:
1010 a_aux_err_tail = NULL;
1011 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1012 a_aux_err_linelen = 0;
1013 while((enp = a_aux_err_head) != NULL){
1014 a_aux_err_head = enp->ae_next;
1015 n_string_gut(&enp->ae_str);
1016 free(enp);
1018 goto jleave;
1020 #endif /* HAVE_ERRORS */
1022 /* s-it-mode */