Merge branch 'topic/netrc'
[s-mailx.git] / auxlily.c
blob14e29a2bdf7343cf6cf732ef45fc2a33835fae1e
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 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/utsname.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <fcntl.h>
50 #ifdef HAVE_SOCKETS
51 # ifdef HAVE_IPV6
52 # include <sys/socket.h>
53 # endif
55 # include <netdb.h>
56 #endif
58 #ifdef HAVE_DEBUG
59 struct nyd_info {
60 char const *ni_file;
61 char const *ni_fun;
62 ui32_t ni_chirp_line;
63 ui32_t ni_level;
66 struct mem_chunk {
67 struct mem_chunk *mc_prev;
68 struct mem_chunk *mc_next;
69 char const *mc_file;
70 ui16_t mc_line;
71 ui8_t mc_isfree;
72 ui8_t __dummy;
73 ui32_t mc_size;
76 union mem_ptr {
77 void *p_p;
78 struct mem_chunk *p_c;
79 char *p_cp;
80 ui8_t *p_ui8p;
82 #endif /* HAVE_DEBUG */
84 /* NYD, memory pool debug */
85 #ifdef HAVE_DEBUG
86 static ui32_t _nyd_curr, _nyd_level;
87 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
89 static size_t _mem_aall, _mem_acur, _mem_amax,
90 _mem_mall, _mem_mcur, _mem_mmax;
92 static struct mem_chunk *_mem_list, *_mem_free;
93 #endif
95 /* {hold,rele}_all_sigs() */
96 static size_t _alls_depth;
97 static sigset_t _alls_nset, _alls_oset;
99 /* {hold,rele}_sigs() */
100 static size_t _hold_sigdepth;
101 static sigset_t _hold_nset, _hold_oset;
103 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
104 #ifdef HAVE_COLOUR
105 static char * _colour_iso6429(char const *wish);
106 #endif
108 #ifdef HAVE_DEBUG
109 static void _nyd_print(struct nyd_info *nip);
110 #endif
112 #ifdef HAVE_COLOUR
113 static char *
114 _colour_iso6429(char const *wish)
116 char const * const wish_orig = wish;
117 char *xwish, *cp, cfg[3] = {0, 0, 0};
118 NYD_ENTER;
120 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
121 * value, ensure we have enough room for that */
123 size_t i = strlen(wish) +1;
124 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
125 memcpy(xwish, wish, i);
126 wish = xwish;
129 /* Iterate over the colour spec */
130 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
131 char *y, *x = strchr(cp, '=');
132 if (x == NULL) {
133 jbail:
134 fprintf(stderr, tr(527,
135 "Invalid colour specification \"%s\": >>> %s <<<\n"),
136 wish_orig, cp);
137 continue;
139 *x++ = '\0';
141 /* TODO convert the ft/fg/bg parser into a table-based one! */
142 if (!asccasecmp(cp, "ft")) {
143 if (!asccasecmp(x, "bold"))
144 cfg[0] = '1';
145 else if (!asccasecmp(x, "inverse"))
146 cfg[0] = '7';
147 else if (!asccasecmp(x, "underline"))
148 cfg[0] = '4';
149 else
150 goto jbail;
151 } else if (!asccasecmp(cp, "fg")) {
152 y = cfg + 1;
153 goto jiter_colour;
154 } else if (!asccasecmp(cp, "bg")) {
155 y = cfg + 2;
156 jiter_colour:
157 if (!asccasecmp(x, "black"))
158 *y = '0';
159 else if (!asccasecmp(x, "blue"))
160 *y = '4';
161 else if (!asccasecmp(x, "green"))
162 *y = '2';
163 else if (!asccasecmp(x, "red"))
164 *y = '1';
165 else if (!asccasecmp(x, "brown"))
166 *y = '3';
167 else if (!asccasecmp(x, "magenta"))
168 *y = '5';
169 else if (!asccasecmp(x, "cyan"))
170 *y = '6';
171 else if (!asccasecmp(x, "white"))
172 *y = '7';
173 else
174 goto jbail;
175 } else
176 goto jbail;
179 /* Restore our salloc() buffer, create return value */
180 xwish = UNCONST(wish);
181 if (cfg[0] || cfg[1] || cfg[2]) {
182 xwish[0] = '\033';
183 xwish[1] = '[';
184 xwish += 2;
185 if (cfg[0])
186 *xwish++ = cfg[0];
187 if (cfg[1]) {
188 if (cfg[0])
189 *xwish++ = ';';
190 xwish[0] = '3';
191 xwish[1] = cfg[1];
192 xwish += 2;
194 if (cfg[2]) {
195 if (cfg[0] || cfg[1])
196 *xwish++ = ';';
197 xwish[0] = '4';
198 xwish[1] = cfg[2];
199 xwish += 2;
201 *xwish++ = 'm';
203 *xwish = '\0';
204 NYD_LEAVE;
205 return UNCONST(wish);
207 #endif /* HAVE_COLOUR */
209 #ifdef HAVE_DEBUG
210 static void
211 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
213 char buf[80];
214 union {int i; size_t z;} u;
216 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
217 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
218 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
219 if (u.i > 0) {
220 u.z = u.i;
221 if (u.z > sizeof buf)
222 u.z = sizeof buf - 1; /* (Skip \0) */
223 write(STDERR_FILENO, buf, u.z);
226 #endif
228 FL void
229 panic(char const *format, ...)
231 va_list ap;
232 NYD_ENTER;
234 fprintf(stderr, tr(1, "Panic: "));
236 va_start(ap, format);
237 vfprintf(stderr, format, ap);
238 va_end(ap);
240 fputs("\n", stderr);
241 fflush(stderr);
242 NYD_LEAVE;
243 abort(); /* Was exit(EXIT_ERR); for a while, but no */
246 FL void
247 alert(char const *format, ...)
249 va_list ap;
250 NYD_ENTER;
252 fprintf(stderr, tr(1, "Panic: "));
254 va_start(ap, format);
255 vfprintf(stderr, format, ap);
256 va_end(ap);
258 fputs("\n", stderr);
259 fflush(stderr);
260 NYD_LEAVE;
263 FL sighandler_type
264 safe_signal(int signum, sighandler_type handler)
266 struct sigaction nact, oact;
267 sighandler_type rv;
268 NYD_ENTER;
270 nact.sa_handler = handler;
271 sigemptyset(&nact.sa_mask);
272 nact.sa_flags = 0;
273 #ifdef SA_RESTART
274 nact.sa_flags |= SA_RESTART;
275 #endif
276 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
277 NYD_LEAVE;
278 return rv;
281 FL void
282 hold_all_sigs(void)
284 NYD_ENTER;
285 if (_alls_depth++ == 0) {
286 sigfillset(&_alls_nset);
287 sigdelset(&_alls_nset, SIGABRT);
288 #ifdef SIGBUS
289 sigdelset(&_alls_nset, SIGBUS);
290 #endif
291 sigdelset(&_alls_nset, SIGCHLD);
292 sigdelset(&_alls_nset, SIGFPE);
293 sigdelset(&_alls_nset, SIGILL);
294 sigdelset(&_alls_nset, SIGKILL);
295 sigdelset(&_alls_nset, SIGSEGV);
296 sigdelset(&_alls_nset, SIGSTOP);
297 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
299 NYD_LEAVE;
302 FL void
303 rele_all_sigs(void)
305 NYD_ENTER;
306 if (--_alls_depth == 0)
307 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
308 NYD_LEAVE;
311 FL void
312 hold_sigs(void)
314 NYD_ENTER;
315 if (_hold_sigdepth++ == 0) {
316 sigemptyset(&_hold_nset);
317 sigaddset(&_hold_nset, SIGHUP);
318 sigaddset(&_hold_nset, SIGINT);
319 sigaddset(&_hold_nset, SIGQUIT);
320 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
322 NYD_LEAVE;
325 FL void
326 rele_sigs(void)
328 NYD_ENTER;
329 if (--_hold_sigdepth == 0)
330 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
331 NYD_LEAVE;
334 #ifdef HAVE_DEBUG
335 FL void
336 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
338 struct nyd_info *nip = _nyd_infos;
340 if (_nyd_curr != NELEM(_nyd_infos))
341 nip += _nyd_curr++;
342 else
343 _nyd_curr = 1;
344 nip->ni_file = file;
345 nip->ni_fun = fun;
346 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
347 nip->ni_level = ((act == 0) ? _nyd_level
348 : (act == 1) ? ++_nyd_level : _nyd_level--);
351 FL void
352 _nyd_oncrash(int signo)
354 struct sigaction xact;
355 sigset_t xset;
356 struct nyd_info *nip;
357 size_t i;
359 xact.sa_handler = SIG_DFL;
360 sigemptyset(&xact.sa_mask);
361 xact.sa_flags = 0;
362 sigaction(signo, &xact, NULL);
364 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
365 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
366 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
367 _nyd_print(nip++);
368 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
369 _nyd_print(nip++);
371 sigemptyset(&xset);
372 sigaddset(&xset, signo);
373 sigprocmask(SIG_UNBLOCK, &xset, NULL);
374 kill(0, signo);
375 for (;;)
376 _exit(EXIT_ERR);
378 #endif
380 FL void
381 touch(struct message *mp)
383 NYD_ENTER;
384 mp->m_flag |= MTOUCH;
385 if (!(mp->m_flag & MREAD))
386 mp->m_flag |= MREAD | MSTATUS;
387 NYD_LEAVE;
390 FL bool_t
391 is_dir(char const *name)
393 struct stat sbuf;
394 bool_t rv = FAL0;
395 NYD_ENTER;
397 if (!stat(name, &sbuf))
398 rv = (S_ISDIR(sbuf.st_mode) != 0);
399 NYD_LEAVE;
400 return rv;
403 FL int
404 argcount(char **argv)
406 char **ap;
407 NYD_ENTER;
409 for (ap = argv; *ap++ != NULL;)
411 NYD_LEAVE;
412 return (int)PTR2SIZE(ap - argv - 1);
415 FL int
416 screensize(void)
418 int s;
419 char *cp;
420 NYD_ENTER;
422 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
423 s = scrnheight - 4; /* XXX no magics */
424 NYD_LEAVE;
425 return s;
428 FL char const *
429 get_pager(char const **env_addon)
431 char const *cp;
432 NYD_ENTER;
434 cp = ok_vlook(PAGER);
435 if (cp == NULL || *cp == '\0')
436 cp = XPAGER;
438 if (env_addon != NULL) {
439 if (strstr(cp, "less") != NULL) {
440 if (getenv("LESS") == NULL)
441 *env_addon = "LESS=FRSXi";
442 } else if (strstr(cp, "lv") != NULL) {
443 if (getenv("LV") == NULL)
444 *env_addon = "LV=-c";
445 } else
446 *env_addon = NULL;
448 NYD_LEAVE;
449 return cp;
452 FL size_t
453 paging_seems_sensible(void)
455 size_t rv = 0;
456 char const *cp;
457 NYD_ENTER;
459 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
460 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
461 NYD_LEAVE;
462 return rv;
465 FL void
466 page_or_print(FILE *fp, size_t lines)
468 size_t rows;
469 int c;
470 NYD_ENTER;
472 fflush_rewind(fp);
474 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
475 while ((c = getc(fp)) != EOF)
476 if (c == '\n' && ++lines > rows)
477 break;
478 really_rewind(fp);
481 if (rows != 0 && lines >= rows)
482 run_command(get_pager(NULL), 0, fileno(fp), -1, NULL, NULL, NULL);
483 else
484 while ((c = getc(fp)) != EOF)
485 putchar(c);
486 NYD_LEAVE;
489 FL enum protocol
490 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
492 struct stat st;
493 char const *cp;
494 char *np;
495 size_t sz;
496 enum protocol rv = PROTO_UNKNOWN;
497 NYD_ENTER;
499 if (name[0] == '%' && name[1] == ':')
500 name += 2;
501 for (cp = name; *cp && *cp != ':'; cp++)
502 if (!alnumchar(*cp))
503 goto jfile;
505 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
506 if (!strncmp(name, "pop3://", 7)) {
507 #ifdef HAVE_POP3
508 rv = PROTO_POP3;
509 #else
510 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
511 #endif
512 } else if (!strncmp(name, "pop3s://", 8)) {
513 #if defined HAVE_POP3 && defined HAVE_SSL
514 rv = PROTO_POP3;
515 #else
516 # ifndef HAVE_POP3
517 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
518 # endif
519 # ifndef HAVE_SSL
520 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
521 # endif
522 #endif
523 } else if (!strncmp(name, "imap://", 7)) {
524 #ifdef HAVE_IMAP
525 rv = PROTO_IMAP;
526 #else
527 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
528 #endif
529 } else if (!strncmp(name, "imaps://", 8)) {
530 #if defined HAVE_IMAP && defined HAVE_SSL
531 rv = PROTO_IMAP;
532 #else
533 # ifndef HAVE_IMAP
534 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
535 # endif
536 # ifndef HAVE_SSL
537 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
538 # endif
539 #endif
541 goto jleave;
544 /* TODO This is the de facto maildir code and thus belongs into there!
545 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
546 * TODO or (more likely) in addition to *newfolders*) */
547 jfile:
548 rv = PROTO_FILE;
549 np = ac_alloc((sz = strlen(name)) + 4 +1);
550 memcpy(np, name, sz + 1);
551 if (!stat(name, &st)) {
552 if (S_ISDIR(st.st_mode) &&
553 (memcpy(np+sz, "/tmp", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
554 (memcpy(np+sz, "/new", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
555 (memcpy(np+sz, "/cur", 4), !stat(np, &st) && S_ISDIR(st.st_mode)))
556 rv = PROTO_MAILDIR;
557 } else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
558 rv = PROTO_MAILDIR;
559 ac_free(np);
560 jleave:
561 NYD_LEAVE;
562 return rv;
565 FL ui32_t
566 torek_hash(char const *name)
568 /* Chris Torek's hash.
569 * NOTE: need to change *at least* create-okey-map.pl when changing the
570 * algorithm!! */
571 ui32_t h = 0;
572 NYD_ENTER;
574 while (*name != '\0') {
575 h *= 33;
576 h += *name++;
578 NYD_LEAVE;
579 return h;
582 FL unsigned
583 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
585 unsigned h = 0, g;
586 NYD_ENTER;
588 cp--;
589 while (*++cp) {
590 h = (h << 4 & 0xffffffff) + (*cp&0377);
591 if ((g = h & 0xf0000000) != 0) {
592 h = h ^ g >> 24;
593 h = h ^ g;
596 NYD_LEAVE;
597 return h;
600 FL ui32_t
601 nextprime(ui32_t n)
603 static ui32_t const primes[] = {
604 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
605 131071, 262139, 524287, 1048573, 2097143, 4194301,
606 8388593, 16777213, 33554393, 67108859, 134217689,
607 268435399, 536870909, 1073741789, 2147483647
610 ui32_t mprime = 7, cutlim;
611 size_t i;
612 NYD_ENTER;
614 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
616 for (i = 0; i < NELEM(primes); i++)
617 if ((mprime = primes[i]) >= cutlim)
618 break;
619 if (i == NELEM(primes) && mprime < n)
620 mprime = n;
621 NYD_LEAVE;
622 return mprime;
625 FL int
626 expand_shell_escape(char const **s, bool_t use_nail_extensions)
628 char const *xs;
629 int c, n;
630 NYD_ENTER;
632 xs = *s;
634 if ((c = *xs & 0xFF) == '\0')
635 goto jleave;
636 ++xs;
637 if (c != '\\')
638 goto jleave;
640 switch ((c = *xs & 0xFF)) {
641 case '\\': break;
642 case 'a': c = '\a'; break;
643 case 'b': c = '\b'; break;
644 case 'c': c = PROMPT_STOP; break;
645 case 'f': c = '\f'; break;
646 case 'n': c = '\n'; break;
647 case 'r': c = '\r'; break;
648 case 't': c = '\t'; break;
649 case 'v': c = '\v'; break;
650 case '0':
651 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
652 c <<= 3;
653 c |= *xs - '0';
655 goto jleave;
656 /* S-nail extension for nice (get)prompt(()) support */
657 case '&':
658 case '?':
659 case '$':
660 case '@':
661 if (use_nail_extensions) {
662 switch (c) {
663 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
664 case '?': c = exec_last_comm_error ? '1' : '0'; break;
665 case '$': c = PROMPT_DOLLAR; break;
666 case '@': c = PROMPT_AT; break;
668 break;
670 /* FALLTHRU */
671 case '\0':
672 /* A sole <backslash> at EOS is treated as-is! */
673 /* FALLTHRU */
674 default:
675 c = '\\';
676 goto jleave;
678 ++xs;
679 jleave:
680 *s = xs;
681 NYD_LEAVE;
682 return c;
685 FL char *
686 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
688 static char buf[PROMPT_BUFFER_SIZE];
690 char *cp;
691 char const *ccp_base, *ccp;
692 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
693 bool_t run2;
694 NYD_ENTER;
696 cp = buf;
697 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0')
698 goto jleave;
699 NATCH_CHAR( cclen_base = strlen(ccp_base); )
701 dfmaxlen = 0; /* keep CC happy */
702 run2 = FAL0;
703 jredo:
704 ccp = ccp_base;
705 NATCH_CHAR( cclen = cclen_base; )
706 maxlen = sizeof(buf) -1;
708 for (;;) {
709 size_t l;
710 int c;
712 if (maxlen == 0)
713 goto jleave;
714 #ifdef HAVE_NATCH_CHAR
715 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
716 if (c <= 0) {
717 mblen(NULL, 0);
718 if (c < 0) {
719 *buf = '?';
720 cp = buf + 1;
721 goto jleave;
723 break;
724 } else if ((l = c) > 1) {
725 if (run2) {
726 memcpy(cp, ccp, l);
727 cp += l;
729 ccp += l;
730 maxlen -= l;
731 continue;
732 } else
733 #endif
734 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
735 if (run2)
736 *cp++ = (char)c;
737 --maxlen;
738 continue;
740 if (c == 0 || c == PROMPT_STOP)
741 break;
743 if (run2) {
744 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
745 if (a == NULL)
746 a = "";
747 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
748 cp += l;
749 maxlen -= l;
750 dfmaxlen -= l;
755 if (!run2) {
756 run2 = TRU1;
757 dfmaxlen = maxlen;
758 goto jredo;
760 jleave:
761 *cp = '\0';
762 NYD_LEAVE;
763 return buf;
766 FL char *
767 nodename(int mayoverride)
769 static char *sys_hostname, *hostname; /* XXX free-at-exit */
771 struct utsname ut;
772 char *hn;
773 #ifdef HAVE_SOCKETS
774 # ifdef HAVE_IPV6
775 struct addrinfo hints, *res;
776 # else
777 struct hostent *hent;
778 # endif
779 #endif
780 NYD_ENTER;
782 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
784 } else if ((hn = sys_hostname) == NULL) {
785 uname(&ut);
786 hn = ut.nodename;
787 #ifdef HAVE_SOCKETS
788 # ifdef HAVE_IPV6
789 memset(&hints, 0, sizeof hints);
790 hints.ai_family = AF_UNSPEC;
791 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
792 hints.ai_flags = AI_CANONNAME;
793 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
794 if (res->ai_canonname != NULL) {
795 size_t l = strlen(res->ai_canonname) +1;
796 hn = ac_alloc(l);
797 memcpy(hn, res->ai_canonname, l);
799 freeaddrinfo(res);
801 # else
802 hent = gethostbyname(hn);
803 if (hent != NULL)
804 hn = hent->h_name;
805 # endif
806 #endif
807 sys_hostname = sstrdup(hn);
808 #if defined HAVE_SOCKETS && defined HAVE_IPV6
809 if (hn != ut.nodename)
810 ac_free(hn);
811 #endif
812 hn = sys_hostname;
815 if (hostname != NULL && hostname != sys_hostname)
816 free(hostname);
817 hostname = sstrdup(hn);
818 NYD_LEAVE;
819 return hostname;
822 FL char *
823 getrandstring(size_t length)
825 static unsigned char nodedigest[16];
826 static pid_t pid;
828 struct str b64;
829 char *data, *cp;
830 size_t i;
831 int fd = -1;
832 #ifdef HAVE_MD5
833 md5_ctx ctx;
834 #else
835 size_t j;
836 #endif
837 NYD_ENTER;
839 data = ac_alloc(length);
841 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
842 length != (size_t)read(fd, data, length)) {
843 if (pid == 0) {
844 pid = getpid();
845 srand(pid);
846 cp = nodename(0);
847 #ifdef HAVE_MD5
848 md5_init(&ctx);
849 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
850 md5_final(nodedigest, &ctx);
851 #else
852 /* In that case it's only used for boundaries and Message-Id:s so that
853 * srand(3) should suffice */
854 j = strlen(cp) + 1;
855 for (i = 0; i < sizeof(nodedigest); ++i)
856 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
857 #endif
859 for (i = 0; i < length; i++)
860 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
861 nodedigest[i % sizeof nodedigest]);
863 if (fd >= 0)
864 close(fd);
866 b64_encode_buf(&b64, data, length, B64_SALLOC);
867 ac_free(data);
868 assert(length < b64.l);
869 b64.s[length] = '\0';
870 NYD_LEAVE;
871 return b64.s;
874 FL enum okay
875 makedir(char const *name)
877 struct stat st;
878 enum okay rv = STOP;
879 NYD_ENTER;
881 if (!mkdir(name, 0700))
882 rv = OKAY;
883 else {
884 int e = errno;
885 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
886 S_ISDIR(st.st_mode))
887 rv = OKAY;
889 NYD_LEAVE;
890 return rv;
893 #ifdef HAVE_FCHDIR
894 FL enum okay
895 cwget(struct cw *cw)
897 enum okay rv = STOP;
898 NYD_ENTER;
900 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
901 goto jleave;
902 if (fchdir(cw->cw_fd) == -1) {
903 close(cw->cw_fd);
904 goto jleave;
906 rv = OKAY;
907 jleave:
908 NYD_LEAVE;
909 return rv;
912 FL enum okay
913 cwret(struct cw *cw)
915 enum okay rv = STOP;
916 NYD_ENTER;
918 if (!fchdir(cw->cw_fd))
919 rv = OKAY;
920 NYD_LEAVE;
921 return rv;
924 FL void
925 cwrelse(struct cw *cw)
927 NYD_ENTER;
928 close(cw->cw_fd);
929 NYD_LEAVE;
932 #else /* !HAVE_FCHDIR */
933 FL enum okay
934 cwget(struct cw *cw)
936 enum okay rv = STOP;
937 NYD_ENTER;
939 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
940 rv = OKAY;
941 NYD_LEAVE;
942 return rv;
945 FL enum okay
946 cwret(struct cw *cw)
948 enum okay rv = STOP;
949 NYD_ENTER;
951 if (!chdir(cw->cw_wd))
952 rv = OKAY;
953 NYD_LEAVE;
954 return rv;
957 FL void
958 cwrelse(struct cw *cw)
960 NYD_ENTER;
961 UNUSED(cw);
962 NYD_LEAVE;
964 #endif /* !HAVE_FCHDIR */
966 FL size_t
967 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
969 size_t rv;
970 NYD_ENTER;
972 #ifdef HAVE_NATCH_CHAR
973 maxlen = MIN(maxlen, blen);
974 for (rv = 0; maxlen > 0;) {
975 int ml = mblen(buf, maxlen);
976 if (ml <= 0) {
977 mblen(NULL, 0);
978 break;
980 buf += ml;
981 rv += ml;
982 maxlen -= ml;
984 #else
985 rv = MIN(blen, maxlen);
986 #endif
987 NYD_LEAVE;
988 return rv;
991 FL size_t
992 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
994 NATCH_CHAR( struct bidi_info bi; )
995 size_t rv NATCH_CHAR( COMMA i );
996 NYD_ENTER;
998 rv = 0;
999 if (maxlen-- == 0)
1000 goto j_leave;
1002 #ifdef HAVE_NATCH_CHAR
1003 bidi_info_create(&bi);
1004 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1005 bi.bi_end.l = 0;
1006 goto jnobidi;
1009 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1010 maxlen -= i;
1011 else
1012 goto jleave;
1014 if ((i = bi.bi_start.l) > 0) {
1015 memcpy(store, bi.bi_start.s, i);
1016 store += i;
1017 rv += i;
1020 jnobidi:
1021 while (maxlen > 0) {
1022 int ml = mblen(buf, blen);
1023 if (ml <= 0) {
1024 mblen(NULL, 0);
1025 break;
1027 if (UICMP(z, maxlen, <, ml))
1028 break;
1029 if (ml == 1)
1030 *store = *buf;
1031 else
1032 memcpy(store, buf, ml);
1033 store += ml;
1034 buf += ml;
1035 rv += ml;
1036 maxlen -= ml;
1039 if ((i = bi.bi_end.l) > 0) {
1040 memcpy(store, bi.bi_end.s, i);
1041 store += i;
1042 rv += i;
1044 jleave:
1045 *store = '\0';
1047 #else
1048 rv = MIN(blen, maxlen);
1049 memcpy(store, buf, rv);
1050 store[rv] = '\0';
1051 #endif
1052 j_leave:
1053 NYD_LEAVE;
1054 return rv;
1057 FL char *
1058 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1060 NATCH_CHAR( struct bidi_info bi; )
1061 int col_orig = col, n, sz;
1062 bool_t isbidi, isuni, isrepl;
1063 char *nb, *np;
1064 NYD_ENTER;
1066 /* Bidi only on request and when there is 8-bit data */
1067 isbidi = isuni = FAL0;
1068 #ifdef HAVE_NATCH_CHAR
1069 isuni = ((options & OPT_UNICODE) != 0);
1070 bidi_info_create(&bi);
1071 if (bi.bi_start.l == 0)
1072 goto jnobidi;
1073 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1074 goto jnobidi;
1076 if ((size_t)col >= bi.bi_pad)
1077 col -= bi.bi_pad;
1078 else
1079 col = 0;
1080 jnobidi:
1081 #endif
1083 np = nb = salloc(mb_cur_max * strlen(cp) +
1084 ((fill ? col : 0)
1085 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1086 +1));
1088 #ifdef HAVE_NATCH_CHAR
1089 if (isbidi) {
1090 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1091 np += bi.bi_start.l;
1093 #endif
1095 while (*cp != '\0') {
1096 #ifdef HAVE_C90AMEND1
1097 if (mb_cur_max > 1) {
1098 wchar_t wc;
1100 n = 1;
1101 isrepl = TRU1;
1102 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1103 sz = 1;
1104 else if (iswprint(wc)) {
1105 # ifndef HAVE_WCWIDTH
1106 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1107 # else
1108 if ((n = wcwidth(wc)) == -1)
1109 n = 1;
1110 else
1111 # endif
1112 isrepl = FAL0;
1114 } else
1115 #endif
1116 n = sz = 1,
1117 isrepl = !isprint(*cp);
1119 if (n > col)
1120 break;
1121 col -= n;
1123 if (isrepl) {
1124 if (isuni) {
1125 np[0] = (char)0xEFu;
1126 np[1] = (char)0xBFu;
1127 np[2] = (char)0xBDu;
1128 np += 3;
1129 } else
1130 *np++ = '?';
1131 cp += sz;
1132 } else if (sz == 1 && spacechar(*cp)) {
1133 *np++ = ' ';
1134 ++cp;
1135 } else
1136 while (sz--)
1137 *np++ = *cp++;
1140 if (fill && col != 0) {
1141 if (fill > 0) {
1142 memmove(nb + col, nb, PTR2SIZE(np - nb));
1143 memset(nb, ' ', col);
1144 } else
1145 memset(np, ' ', col);
1146 np += col;
1147 col = 0;
1150 #ifdef HAVE_NATCH_CHAR
1151 if (isbidi) {
1152 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1153 np += bi.bi_end.l;
1155 #endif
1157 *np = '\0';
1158 if (cols_decr_used_or_null != NULL)
1159 *cols_decr_used_or_null -= col_orig - col;
1160 NYD_LEAVE;
1161 return nb;
1164 FL void
1165 makeprint(struct str const *in, struct str *out)
1167 static int print_all_chars = -1;
1169 char const *inp, *maxp;
1170 char *outp;
1171 DBG( size_t msz; )
1172 NYD_ENTER;
1174 if (print_all_chars == -1)
1175 print_all_chars = ok_blook(print_all_chars);
1177 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1178 inp = in->s;
1179 maxp = inp + in->l;
1181 if (print_all_chars) {
1182 out->l = in->l;
1183 memcpy(outp, inp, out->l);
1184 goto jleave;
1187 #ifdef HAVE_NATCH_CHAR
1188 if (mb_cur_max > 1) {
1189 char mbb[MB_LEN_MAX + 1];
1190 wchar_t wc;
1191 int i, n;
1192 bool_t isuni = ((options & OPT_UNICODE) != 0);
1194 out->l = 0;
1195 while (inp < maxp) {
1196 if (*inp & 0200)
1197 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1198 else {
1199 wc = *inp;
1200 n = 1;
1202 if (n == -1) {
1203 /* FIXME Why mbtowc() resetting here?
1204 * FIXME what about ISO 2022-JP plus -- those
1205 * FIXME will loose shifts, then!
1206 * FIXME THUS - we'd need special "known points"
1207 * FIXME to do so - say, after a newline!!
1208 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1209 mbtowc(&wc, NULL, mb_cur_max);
1210 wc = isuni ? 0xFFFD : '?';
1211 n = 1;
1212 } else if (n == 0)
1213 n = 1;
1214 inp += n;
1215 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1216 wc != '\t') {
1217 if ((wc & ~(wchar_t)037) == 0)
1218 wc = isuni ? 0x2400 | wc : '?';
1219 else if (wc == 0177)
1220 wc = isuni ? 0x2421 : '?';
1221 else
1222 wc = isuni ? 0x2426 : '?';
1224 if ((n = wctomb(mbb, wc)) <= 0)
1225 continue;
1226 out->l += n;
1227 assert(out->l < msz);
1228 for (i = 0; i < n; ++i)
1229 *outp++ = mbb[i];
1231 } else
1232 #endif /* NATCH_CHAR */
1234 int c;
1235 while (inp < maxp) {
1236 c = *inp++ & 0377;
1237 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1238 c = '?';
1239 *outp++ = c;
1241 out->l = in->l;
1243 jleave:
1244 out->s[out->l] = '\0';
1245 NYD_LEAVE;
1248 FL char *
1249 prstr(char const *s)
1251 struct str in, out;
1252 char *rp;
1253 NYD_ENTER;
1255 in.s = UNCONST(s);
1256 in.l = strlen(s);
1257 makeprint(&in, &out);
1258 rp = savestrbuf(out.s, out.l);
1259 free(out.s);
1260 NYD_LEAVE;
1261 return rp;
1264 FL int
1265 prout(char const *s, size_t sz, FILE *fp)
1267 struct str in, out;
1268 int n;
1269 NYD_ENTER;
1271 in.s = UNCONST(s);
1272 in.l = sz;
1273 makeprint(&in, &out);
1274 n = fwrite(out.s, 1, out.l, fp);
1275 free(out.s);
1276 NYD_LEAVE;
1277 return n;
1280 FL size_t
1281 putuc(int u, int c, FILE *fp)
1283 size_t rv;
1284 NYD_ENTER;
1285 UNUSED(u);
1287 #ifdef HAVE_NATCH_CHAR
1288 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1289 char mbb[MB_LEN_MAX];
1290 int i, n;
1292 if ((n = wctomb(mbb, u)) > 0) {
1293 rv = wcwidth(u);
1294 for (i = 0; i < n; ++i)
1295 if (putc(mbb[i] & 0377, fp) == EOF) {
1296 rv = 0;
1297 break;
1299 } else if (n == 0)
1300 rv = (putc('\0', fp) != EOF);
1301 else
1302 rv = 0;
1303 } else
1304 #endif
1305 rv = (putc(c, fp) != EOF);
1306 NYD_LEAVE;
1307 return rv;
1310 FL bool_t
1311 bidi_info_needed(char const *bdat, size_t blen)
1313 bool_t rv = FAL0;
1314 NYD_ENTER;
1316 #ifdef HAVE_NATCH_CHAR
1317 if (options & OPT_UNICODE)
1318 for (; blen > 0; ++bdat, --blen) {
1319 if ((ui8_t)*bdat > 0x7F) {
1320 /* TODO Checking for BIDI character: use S-CText fromutf8
1321 * TODO plus isrighttoleft (or whatever there will be)! */
1322 ui32_t c, x = (ui8_t)*bdat;
1324 if ((x & 0xE0) == 0xC0) {
1325 if (blen < 2)
1326 break;
1327 blen -= 1;
1328 c = x & ~0xC0;
1329 } else if ((x & 0xF0) == 0xE0) {
1330 if (blen < 3)
1331 break;
1332 blen -= 2;
1333 c = x & ~0xE0;
1334 c <<= 6;
1335 x = (ui8_t)*++bdat;
1336 c |= x & 0x7F;
1337 } else {
1338 if (blen < 4)
1339 break;
1340 blen -= 3;
1341 c = x & ~0xF0;
1342 c <<= 6;
1343 x = (ui8_t)*++bdat;
1344 c |= x & 0x7F;
1345 c <<= 6;
1346 x = (ui8_t)*++bdat;
1347 c |= x & 0x7F;
1349 c <<= 6;
1350 x = (ui8_t)*++bdat;
1351 c |= x & 0x7F;
1353 /* (Very very fuzzy, awaiting S-CText for good) */
1354 if ((c >= 0x05BE && c <= 0x08E3) ||
1355 (c >= 0xFB1D && c <= 0xFEFC) ||
1356 (c >= 0x10800 && c <= 0x10C48) ||
1357 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1358 rv = TRU1;
1359 break;
1363 #endif /* HAVE_NATCH_CHAR */
1364 NYD_LEAVE;
1365 return rv;
1368 FL void
1369 bidi_info_create(struct bidi_info *bip)
1371 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1372 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1373 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1374 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1375 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1376 NATCH_CHAR( char const *hb; )
1377 NYD_ENTER;
1379 memset(bip, 0, sizeof *bip);
1380 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1382 #ifdef HAVE_NATCH_CHAR
1383 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1384 switch (*hb) {
1385 case '3':
1386 bip->bi_pad = 2;
1387 /* FALLTHRU */
1388 case '2':
1389 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1390 break;
1391 case '1':
1392 bip->bi_pad = 2;
1393 /* FALLTHRU */
1394 default:
1395 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1396 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1397 break;
1399 bip->bi_start.l = bip->bi_end.l = 3;
1401 #endif
1402 NYD_LEAVE;
1405 #ifdef HAVE_COLOUR
1406 FL void
1407 colour_table_create(bool_t pager_used)
1409 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1410 size_t i;
1411 struct colour_table *ct;
1412 NYD_ENTER;
1414 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1415 goto jleave;
1416 else {
1417 char *term, *okterms;
1419 /* Don't use getenv(), but force copy-in into our own tables.. */
1420 if ((term = _var_voklook("TERM")) == NULL)
1421 goto jleave;
1422 /* terminfo rocks: if we find "color", assume it's right */
1423 if (strstr(term, "color") != NULL)
1424 goto jok;
1425 if ((okterms = ok_vlook(colour_terms)) == NULL)
1426 okterms = UNCONST(COLOUR_TERMS);
1427 okterms = savestr(okterms);
1429 i = strlen(term);
1430 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1431 if (!strncmp(u.cp, term, i))
1432 goto jok;
1433 goto jleave;
1436 jok:
1437 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1438 { static struct {
1439 enum okeys okey;
1440 enum colourspec cspec;
1441 char const *defval;
1442 } const map[] = {
1443 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1444 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1445 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1446 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1447 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1450 for (i = 0; i < NELEM(map); ++i) {
1451 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1452 u.ccp = map[i].defval;
1453 u.cp = _colour_iso6429(u.ccp);
1454 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1455 ct->ct_csinfo[map[i].cspec].s = u.cp;
1458 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1459 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1461 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1462 u.ccp = COLOUR_USER_HEADERS;
1463 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1464 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1465 jleave:
1466 NYD_LEAVE;
1469 FL void
1470 colour_put(FILE *fp, enum colourspec cs)
1472 NYD_ENTER;
1473 if (colour_table != NULL) {
1474 struct str const *cp = colour_get(cs);
1476 fwrite(cp->s, cp->l, 1, fp);
1478 NYD_LEAVE;
1481 FL void
1482 colour_put_header(FILE *fp, char const *name)
1484 enum colourspec cs = COLOURSPEC_HEADER;
1485 struct str const *uheads;
1486 char *cp, *cp_base, *x;
1487 size_t namelen;
1488 NYD_ENTER;
1490 if (colour_table == NULL)
1491 goto j_leave;
1492 /* Normal header colours if there are no user headers */
1493 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1494 if (uheads->s == NULL)
1495 goto jleave;
1497 /* Iterate over all entries in the *colour-user-headers* list */
1498 cp = ac_alloc(uheads->l +1);
1499 memcpy(cp, uheads->s, uheads->l +1);
1500 cp_base = cp;
1501 namelen = strlen(name);
1502 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1503 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1504 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1505 cs = COLOURSPEC_UHEADER;
1506 break;
1509 ac_free(cp_base);
1510 jleave:
1511 colour_put(fp, cs);
1512 j_leave:
1513 NYD_LEAVE;
1516 FL void
1517 colour_reset(FILE *fp)
1519 NYD_ENTER;
1520 if (colour_table != NULL)
1521 fwrite("\033[0m", 4, 1, fp);
1522 NYD_LEAVE;
1525 FL struct str const *
1526 colour_get(enum colourspec cs)
1528 struct str const *rv = NULL;
1529 NYD_ENTER;
1531 if (colour_table != NULL)
1532 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1533 rv = NULL;
1534 NYD_LEAVE;
1535 return rv;
1537 #endif /* HAVE_COLOUR */
1539 FL void
1540 time_current_update(struct time_current *tc, bool_t full_update)
1542 NYD_ENTER;
1543 tc->tc_time = time(NULL);
1544 if (full_update) {
1545 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1546 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1547 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1549 NYD_LEAVE;
1552 static void
1553 _out_of_memory(void)
1555 panic("no memory");
1558 #ifndef HAVE_DEBUG
1559 FL void *
1560 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1562 void *rv;
1563 NYD_ENTER;
1565 if (s == 0)
1566 s = 1;
1567 if ((rv = malloc(s)) == NULL)
1568 _out_of_memory();
1569 NYD_LEAVE;
1570 return rv;
1573 FL void *
1574 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1576 void *rv;
1577 NYD_ENTER;
1579 if (s == 0)
1580 s = 1;
1581 if (v == NULL)
1582 rv = smalloc(s);
1583 else if ((rv = realloc(v, s)) == NULL)
1584 _out_of_memory();
1585 NYD_LEAVE;
1586 return rv;
1589 FL void *
1590 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1592 void *rv;
1593 NYD_ENTER;
1595 if (size == 0)
1596 size = 1;
1597 if ((rv = calloc(nmemb, size)) == NULL)
1598 _out_of_memory();
1599 NYD_LEAVE;
1600 return rv;
1603 #else /* !HAVE_DEBUG */
1604 CTA(sizeof(char) == sizeof(ui8_t));
1606 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1607 # define _HOPE_SET(C) \
1608 do {\
1609 union mem_ptr __xl, __xu;\
1610 struct mem_chunk *__xc;\
1611 __xl.p_p = (C).p_p;\
1612 __xc = __xl.p_c - 1;\
1613 __xu.p_p = __xc;\
1614 (C).p_cp += 8;\
1615 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1616 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1617 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1618 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1619 __xu.p_ui8p += __xc->mc_size - 8;\
1620 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1621 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1622 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1623 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1624 } while (0)
1625 # define _HOPE_GET_TRACE(C,BAD) \
1626 do {\
1627 (C).p_cp += 8;\
1628 _HOPE_GET(C, BAD);\
1629 (C).p_cp += 8;\
1630 } while(0)
1631 # define _HOPE_GET(C,BAD) \
1632 do {\
1633 union mem_ptr __xl, __xu;\
1634 struct mem_chunk *__xc;\
1635 ui32_t __i;\
1636 __xl.p_p = (C).p_p;\
1637 __xl.p_cp -= 8;\
1638 (C).p_cp = __xl.p_cp;\
1639 __xc = __xl.p_c - 1;\
1640 (BAD) = FAL0;\
1641 __i = 0;\
1642 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1643 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1644 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1645 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1646 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1647 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1648 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1649 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1650 if (__i != 0) {\
1651 (BAD) = TRU1;\
1652 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1653 __xl.p_p, __i, mdbg_file, mdbg_line);\
1655 __xu.p_p = __xc;\
1656 __xu.p_ui8p += __xc->mc_size - 8;\
1657 __i = 0;\
1658 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1659 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1660 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1661 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1662 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1663 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1664 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1665 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1666 if (__i != 0) {\
1667 (BAD) = TRU1;\
1668 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1669 __xl.p_p, __i, mdbg_file, mdbg_line);\
1671 if (BAD)\
1672 alert(" ..canary last seen: %s, line %u",\
1673 __xc->mc_file, __xc->mc_line);\
1674 } while (0)
1676 FL void *
1677 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1679 union mem_ptr p;
1680 NYD_ENTER;
1682 if (s == 0)
1683 s = 1;
1684 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1686 if ((p.p_p = (malloc)(s)) == NULL)
1687 _out_of_memory();
1688 p.p_c->mc_prev = NULL;
1689 if ((p.p_c->mc_next = _mem_list) != NULL)
1690 _mem_list->mc_prev = p.p_c;
1691 p.p_c->mc_file = mdbg_file;
1692 p.p_c->mc_line = (ui16_t)mdbg_line;
1693 p.p_c->mc_isfree = FAL0;
1694 p.p_c->mc_size = (ui32_t)s;
1695 _mem_list = p.p_c++;
1696 _HOPE_SET(p);
1698 ++_mem_aall;
1699 ++_mem_acur;
1700 _mem_amax = MAX(_mem_amax, _mem_acur);
1701 _mem_mall += s;
1702 _mem_mcur += s;
1703 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1704 NYD_LEAVE;
1705 return p.p_p;
1708 FL void *
1709 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1711 union mem_ptr p;
1712 bool_t isbad;
1713 NYD_ENTER;
1715 if ((p.p_p = v) == NULL) {
1716 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
1717 goto jleave;
1720 _HOPE_GET(p, isbad);
1721 --p.p_c;
1722 if (p.p_c->mc_isfree) {
1723 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1724 "\tLast seen: %s, line %d\n",
1725 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1726 goto jforce;
1729 if (p.p_c == _mem_list)
1730 _mem_list = p.p_c->mc_next;
1731 else
1732 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1733 if (p.p_c->mc_next != NULL)
1734 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1736 --_mem_acur;
1737 _mem_mcur -= p.p_c->mc_size;
1738 jforce:
1739 if (s == 0)
1740 s = 1;
1741 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1743 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
1744 _out_of_memory();
1745 p.p_c->mc_prev = NULL;
1746 if ((p.p_c->mc_next = _mem_list) != NULL)
1747 _mem_list->mc_prev = p.p_c;
1748 p.p_c->mc_file = mdbg_file;
1749 p.p_c->mc_line = (ui16_t)mdbg_line;
1750 p.p_c->mc_isfree = FAL0;
1751 p.p_c->mc_size = (ui32_t)s;
1752 _mem_list = p.p_c++;
1753 _HOPE_SET(p);
1755 ++_mem_aall;
1756 ++_mem_acur;
1757 _mem_amax = MAX(_mem_amax, _mem_acur);
1758 _mem_mall += s;
1759 _mem_mcur += s;
1760 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1761 jleave:
1762 NYD_LEAVE;
1763 return p.p_p;
1766 FL void *
1767 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1769 union mem_ptr p;
1770 NYD_ENTER;
1772 if (size == 0)
1773 size = 1;
1774 if (nmemb == 0)
1775 nmemb = 1;
1776 size *= nmemb;
1777 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
1779 if ((p.p_p = (malloc)(size)) == NULL)
1780 _out_of_memory();
1781 memset(p.p_p, 0, size);
1782 p.p_c->mc_prev = NULL;
1783 if ((p.p_c->mc_next = _mem_list) != NULL)
1784 _mem_list->mc_prev = p.p_c;
1785 p.p_c->mc_file = mdbg_file;
1786 p.p_c->mc_line = (ui16_t)mdbg_line;
1787 p.p_c->mc_isfree = FAL0;
1788 p.p_c->mc_size = (ui32_t)size;
1789 _mem_list = p.p_c++;
1790 _HOPE_SET(p);
1792 ++_mem_aall;
1793 ++_mem_acur;
1794 _mem_amax = MAX(_mem_amax, _mem_acur);
1795 _mem_mall += size;
1796 _mem_mcur += size;
1797 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1798 NYD_LEAVE;
1799 return p.p_p;
1802 FL void
1803 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1805 union mem_ptr p;
1806 bool_t isbad;
1807 NYD_ENTER;
1809 if ((p.p_p = v) == NULL) {
1810 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1811 goto jleave;
1814 _HOPE_GET(p, isbad);
1815 --p.p_c;
1816 if (p.p_c->mc_isfree) {
1817 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1818 "\tLast seen: %s, line %d\n",
1819 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1820 goto jleave;
1823 if (p.p_c == _mem_list)
1824 _mem_list = p.p_c->mc_next;
1825 else
1826 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1827 if (p.p_c->mc_next != NULL)
1828 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1829 p.p_c->mc_isfree = TRU1;
1831 --_mem_acur;
1832 _mem_mcur -= p.p_c->mc_size;
1834 if (options & OPT_DEBUG) {
1835 p.p_c->mc_next = _mem_free;
1836 _mem_free = p.p_c;
1837 } else
1838 (free)(p.p_c);
1839 jleave:
1840 NYD_LEAVE;
1843 FL void
1844 smemreset(void)
1846 union mem_ptr p;
1847 size_t c = 0, s = 0;
1848 NYD_ENTER;
1850 for (p.p_c = _mem_free; p.p_c != NULL;) {
1851 void *vp = p.p_c;
1852 ++c;
1853 s += p.p_c->mc_size;
1854 p.p_c = p.p_c->mc_next;
1855 (free)(vp);
1857 _mem_free = NULL;
1859 if (options & OPT_DEBUG)
1860 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1861 c, s);
1862 NYD_LEAVE;
1865 FL int
1866 c_smemtrace(void *v)
1868 /* For _HOPE_GET() */
1869 char const * const mdbg_file = "smemtrace()";
1870 int const mdbg_line = -1;
1871 FILE *fp;
1872 union mem_ptr p, xp;
1873 bool_t isbad;
1874 size_t lines;
1875 NYD_ENTER;
1877 v = (void*)0x1;
1878 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1879 NULL) {
1880 perror("tmpfile");
1881 goto jleave;
1884 fprintf(fp, "Memory statistics:\n"
1885 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
1886 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
1887 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
1889 fprintf(fp, "Currently allocated memory chunks:\n");
1890 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1891 ++lines, p.p_c = p.p_c->mc_next) {
1892 xp = p;
1893 ++xp.p_c;
1894 _HOPE_GET_TRACE(xp, isbad);
1895 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1896 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1897 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
1898 p.p_c->mc_line);
1901 if (options & OPT_DEBUG) {
1902 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1903 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1904 xp = p;
1905 ++xp.p_c;
1906 _HOPE_GET_TRACE(xp, isbad);
1907 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1908 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1909 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1910 p.p_c->mc_file, p.p_c->mc_line);
1914 page_or_print(fp, lines);
1915 Fclose(fp);
1916 v = NULL;
1917 jleave:
1918 NYD_LEAVE;
1919 return (v != NULL);
1922 # ifdef _HAVE_MEMCHECK
1923 FL bool_t
1924 _smemcheck(char const *mdbg_file, int mdbg_line)
1926 union mem_ptr p, xp;
1927 bool_t anybad = FAL0, isbad;
1928 size_t lines;
1929 NYD_ENTER;
1931 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1932 ++lines, p.p_c = p.p_c->mc_next) {
1933 xp = p;
1934 ++xp.p_c;
1935 _HOPE_GET_TRACE(xp, isbad);
1936 if (isbad) {
1937 anybad = TRU1;
1938 fprintf(stderr,
1939 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1940 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1941 p.p_c->mc_file, p.p_c->mc_line);
1945 if (options & OPT_DEBUG) {
1946 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1947 xp = p;
1948 ++xp.p_c;
1949 _HOPE_GET_TRACE(xp, isbad);
1950 if (isbad) {
1951 anybad = TRU1;
1952 fprintf(stderr,
1953 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1954 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1955 p.p_c->mc_file, p.p_c->mc_line);
1959 NYD_LEAVE;
1960 return anybad;
1962 # endif /* _HAVE_MEMCHECK */
1964 # undef _HOPE_SIZE
1965 # undef _HOPE_SET
1966 # undef _HOPE_GET_TRACE
1967 # undef _HOPE_GET
1968 #endif /* HAVE_DEBUG */
1970 /* vim:set fenc=utf-8:s-it-mode */