THANKS: v14.7.5
[s-mailx.git] / auxlily.c
blobc0e9e58dfa79d67675710a07e4097a7f73e6ab1b
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_NYD
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;
65 #endif
67 #ifdef HAVE_DEBUG
68 struct mem_chunk {
69 struct mem_chunk *mc_prev;
70 struct mem_chunk *mc_next;
71 char const *mc_file;
72 ui16_t mc_line;
73 ui8_t mc_isfree;
74 ui8_t __dummy;
75 ui32_t mc_size;
78 union mem_ptr {
79 void *p_p;
80 struct mem_chunk *p_c;
81 char *p_cp;
82 ui8_t *p_ui8p;
84 #endif
86 /* NYD, memory pool debug */
87 #ifdef HAVE_NYD
88 static ui32_t _nyd_curr, _nyd_level;
89 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
90 #endif
92 #ifdef HAVE_DEBUG
93 static size_t _mem_aall, _mem_acur, _mem_amax,
94 _mem_mall, _mem_mcur, _mem_mmax;
96 static struct mem_chunk *_mem_list, *_mem_free;
97 #endif
99 /* {hold,rele}_all_sigs() */
100 static size_t _alls_depth;
101 static sigset_t _alls_nset, _alls_oset;
103 /* {hold,rele}_sigs() */
104 static size_t _hold_sigdepth;
105 static sigset_t _hold_nset, _hold_oset;
107 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
108 #ifdef HAVE_COLOUR
109 static char * _colour_iso6429(char const *wish);
110 #endif
112 #ifdef HAVE_NYD
113 static void _nyd_print(struct nyd_info *nip);
114 #endif
116 #ifdef HAVE_COLOUR
117 static char *
118 _colour_iso6429(char const *wish)
120 char const * const wish_orig = wish;
121 char *xwish, *cp, cfg[3] = {0, 0, 0};
122 NYD_ENTER;
124 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
125 * value, ensure we have enough room for that */
127 size_t i = strlen(wish) +1;
128 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
129 memcpy(xwish, wish, i);
130 wish = xwish;
133 /* Iterate over the colour spec */
134 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
135 char *y, *x = strchr(cp, '=');
136 if (x == NULL) {
137 jbail:
138 fprintf(stderr, _(
139 "Invalid colour specification \"%s\": >>> %s <<<\n"),
140 wish_orig, cp);
141 continue;
143 *x++ = '\0';
145 /* TODO convert the ft/fg/bg parser into a table-based one! */
146 if (!asccasecmp(cp, "ft")) {
147 if (!asccasecmp(x, "bold"))
148 cfg[0] = '1';
149 else if (!asccasecmp(x, "inverse"))
150 cfg[0] = '7';
151 else if (!asccasecmp(x, "underline"))
152 cfg[0] = '4';
153 else
154 goto jbail;
155 } else if (!asccasecmp(cp, "fg")) {
156 y = cfg + 1;
157 goto jiter_colour;
158 } else if (!asccasecmp(cp, "bg")) {
159 y = cfg + 2;
160 jiter_colour:
161 if (!asccasecmp(x, "black"))
162 *y = '0';
163 else if (!asccasecmp(x, "blue"))
164 *y = '4';
165 else if (!asccasecmp(x, "green"))
166 *y = '2';
167 else if (!asccasecmp(x, "red"))
168 *y = '1';
169 else if (!asccasecmp(x, "brown"))
170 *y = '3';
171 else if (!asccasecmp(x, "magenta"))
172 *y = '5';
173 else if (!asccasecmp(x, "cyan"))
174 *y = '6';
175 else if (!asccasecmp(x, "white"))
176 *y = '7';
177 else
178 goto jbail;
179 } else
180 goto jbail;
183 /* Restore our salloc() buffer, create return value */
184 xwish = UNCONST(wish);
185 if (cfg[0] || cfg[1] || cfg[2]) {
186 xwish[0] = '\033';
187 xwish[1] = '[';
188 xwish += 2;
189 if (cfg[0])
190 *xwish++ = cfg[0];
191 if (cfg[1]) {
192 if (cfg[0])
193 *xwish++ = ';';
194 xwish[0] = '3';
195 xwish[1] = cfg[1];
196 xwish += 2;
198 if (cfg[2]) {
199 if (cfg[0] || cfg[1])
200 *xwish++ = ';';
201 xwish[0] = '4';
202 xwish[1] = cfg[2];
203 xwish += 2;
205 *xwish++ = 'm';
207 *xwish = '\0';
208 NYD_LEAVE;
209 return UNCONST(wish);
211 #endif /* HAVE_COLOUR */
213 #ifdef HAVE_NYD
214 static void
215 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
217 char buf[80];
218 union {int i; size_t z;} u;
220 u.i = snprintf(buf, sizeof buf, "%c [%2u] %.25s (%.16s:%u)\n",
221 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
222 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
223 if (u.i > 0) {
224 u.z = u.i;
225 if (u.z > sizeof buf)
226 u.z = sizeof buf - 1; /* (Skip \0) */
227 write(STDERR_FILENO, buf, u.z);
230 #endif
232 FL void
233 panic(char const *format, ...)
235 va_list ap;
236 NYD2_ENTER;
238 fprintf(stderr, _("Panic: "));
240 va_start(ap, format);
241 vfprintf(stderr, format, ap);
242 va_end(ap);
244 fputs("\n", stderr);
245 fflush(stderr);
246 NYD2_LEAVE;
247 abort(); /* Was exit(EXIT_ERR); for a while, but no */
250 FL void
251 alert(char const *format, ...)
253 va_list ap;
254 NYD2_ENTER;
256 fprintf(stderr, _("Panic: "));
258 va_start(ap, format);
259 vfprintf(stderr, format, ap);
260 va_end(ap);
262 fputs("\n", stderr);
263 fflush(stderr);
264 NYD2_LEAVE;
267 FL sighandler_type
268 safe_signal(int signum, sighandler_type handler)
270 struct sigaction nact, oact;
271 sighandler_type rv;
272 NYD2_ENTER;
274 nact.sa_handler = handler;
275 sigemptyset(&nact.sa_mask);
276 nact.sa_flags = 0;
277 #ifdef SA_RESTART
278 nact.sa_flags |= SA_RESTART;
279 #endif
280 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
281 NYD2_LEAVE;
282 return rv;
285 FL void
286 hold_all_sigs(void)
288 NYD2_ENTER;
289 if (_alls_depth++ == 0) {
290 sigfillset(&_alls_nset);
291 sigdelset(&_alls_nset, SIGABRT);
292 #ifdef SIGBUS
293 sigdelset(&_alls_nset, SIGBUS);
294 #endif
295 sigdelset(&_alls_nset, SIGCHLD);
296 sigdelset(&_alls_nset, SIGFPE);
297 sigdelset(&_alls_nset, SIGILL);
298 sigdelset(&_alls_nset, SIGKILL);
299 sigdelset(&_alls_nset, SIGSEGV);
300 sigdelset(&_alls_nset, SIGSTOP);
301 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
303 NYD2_LEAVE;
306 FL void
307 rele_all_sigs(void)
309 NYD2_ENTER;
310 if (--_alls_depth == 0)
311 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
312 NYD2_LEAVE;
315 FL void
316 hold_sigs(void)
318 NYD2_ENTER;
319 if (_hold_sigdepth++ == 0) {
320 sigemptyset(&_hold_nset);
321 sigaddset(&_hold_nset, SIGHUP);
322 sigaddset(&_hold_nset, SIGINT);
323 sigaddset(&_hold_nset, SIGQUIT);
324 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
326 NYD2_LEAVE;
329 FL void
330 rele_sigs(void)
332 NYD2_ENTER;
333 if (--_hold_sigdepth == 0)
334 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
335 NYD2_LEAVE;
338 #ifdef HAVE_NYD
339 FL void
340 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
342 struct nyd_info *nip = _nyd_infos;
344 if (_nyd_curr != NELEM(_nyd_infos))
345 nip += _nyd_curr++;
346 else
347 _nyd_curr = 1;
348 nip->ni_file = file;
349 nip->ni_fun = fun;
350 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
351 nip->ni_level = ((act == 0) ? _nyd_level
352 : (act == 1) ? ++_nyd_level : _nyd_level--);
355 FL void
356 _nyd_oncrash(int signo)
358 struct sigaction xact;
359 sigset_t xset;
360 struct nyd_info *nip;
361 size_t i;
363 xact.sa_handler = SIG_DFL;
364 sigemptyset(&xact.sa_mask);
365 xact.sa_flags = 0;
366 sigaction(signo, &xact, NULL);
368 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
369 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
370 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
371 _nyd_print(nip++);
372 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
373 _nyd_print(nip++);
375 sigemptyset(&xset);
376 sigaddset(&xset, signo);
377 sigprocmask(SIG_UNBLOCK, &xset, NULL);
378 kill(0, signo);
379 for (;;)
380 _exit(EXIT_ERR);
382 #endif /* HAVE_NYD */
384 FL void
385 touch(struct message *mp)
387 NYD_ENTER;
388 mp->m_flag |= MTOUCH;
389 if (!(mp->m_flag & MREAD))
390 mp->m_flag |= MREAD | MSTATUS;
391 NYD_LEAVE;
394 FL bool_t
395 is_dir(char const *name)
397 struct stat sbuf;
398 bool_t rv = FAL0;
399 NYD_ENTER;
401 if (!stat(name, &sbuf))
402 rv = (S_ISDIR(sbuf.st_mode) != 0);
403 NYD_LEAVE;
404 return rv;
407 FL int
408 argcount(char **argv)
410 char **ap;
411 NYD_ENTER;
413 for (ap = argv; *ap++ != NULL;)
415 NYD_LEAVE;
416 return (int)PTR2SIZE(ap - argv - 1);
419 FL int
420 screensize(void)
422 int s;
423 char *cp;
424 NYD_ENTER;
426 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
427 s = scrnheight - 4; /* XXX no magics */
428 NYD_LEAVE;
429 return s;
432 FL char const *
433 get_pager(char const **env_addon)
435 char const *cp;
436 NYD_ENTER;
438 cp = ok_vlook(PAGER);
439 if (cp == NULL || *cp == '\0')
440 cp = XPAGER;
442 if (env_addon != NULL) {
443 *env_addon = NULL;
444 if (strstr(cp, "less") != NULL) {
445 if (getenv("LESS") == NULL)
446 *env_addon = "LESS=FRSXi";
447 } else if (strstr(cp, "lv") != NULL) {
448 if (getenv("LV") == NULL)
449 *env_addon = "LV=-c";
452 NYD_LEAVE;
453 return cp;
456 FL size_t
457 paging_seems_sensible(void)
459 size_t rv = 0;
460 char const *cp;
461 NYD_ENTER;
463 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
464 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
465 NYD_LEAVE;
466 return rv;
469 FL void
470 page_or_print(FILE *fp, size_t lines)
472 size_t rows;
473 int c;
474 NYD_ENTER;
476 fflush_rewind(fp);
478 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
479 while ((c = getc(fp)) != EOF)
480 if (c == '\n' && ++lines > rows)
481 break;
482 really_rewind(fp);
485 if (rows != 0 && lines >= rows)
486 run_command(get_pager(NULL), 0, fileno(fp), -1, NULL, NULL, NULL);
487 else
488 while ((c = getc(fp)) != EOF)
489 putchar(c);
490 NYD_LEAVE;
493 FL enum protocol
494 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
496 struct stat st;
497 char const *cp;
498 char *np;
499 size_t sz;
500 enum protocol rv = PROTO_UNKNOWN;
501 NYD_ENTER;
503 temporary_protocol_ext = NULL;
505 if (name[0] == '%' && name[1] == ':')
506 name += 2;
507 for (cp = name; *cp && *cp != ':'; cp++)
508 if (!alnumchar(*cp))
509 goto jfile;
511 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
512 if (!strncmp(name, "pop3://", 7)) {
513 #ifdef HAVE_POP3
514 rv = PROTO_POP3;
515 #else
516 fprintf(stderr, _("No POP3 support compiled in.\n"));
517 #endif
518 } else if (!strncmp(name, "pop3s://", 8)) {
519 #if defined HAVE_POP3 && defined HAVE_SSL
520 rv = PROTO_POP3;
521 #else
522 # ifndef HAVE_POP3
523 fprintf(stderr, _("No POP3 support compiled in.\n"));
524 # endif
525 # ifndef HAVE_SSL
526 fprintf(stderr, _("No SSL support compiled in.\n"));
527 # endif
528 #endif
529 } else if (!strncmp(name, "imap://", 7)) {
530 #ifdef HAVE_IMAP
531 rv = PROTO_IMAP;
532 #else
533 fprintf(stderr, _("No IMAP support compiled in.\n"));
534 #endif
535 } else if (!strncmp(name, "imaps://", 8)) {
536 #if defined HAVE_IMAP && defined HAVE_SSL
537 rv = PROTO_IMAP;
538 #else
539 # ifndef HAVE_IMAP
540 fprintf(stderr, _("No IMAP support compiled in.\n"));
541 # endif
542 # ifndef HAVE_SSL
543 fprintf(stderr, _("No SSL support compiled in.\n"));
544 # endif
545 #endif
547 goto jleave;
550 /* TODO This is the de facto maildir code and thus belongs into there!
551 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
552 * TODO or (more likely) in addition to *newfolders*) */
553 jfile:
554 rv = PROTO_FILE;
555 np = ac_alloc((sz = strlen(name)) + 4 +1);
556 memcpy(np, name, sz + 1);
557 if (!stat(name, &st)) {
558 if (S_ISDIR(st.st_mode) &&
559 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
560 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
561 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
562 rv = PROTO_MAILDIR;
563 } else {
564 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
565 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
566 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
567 temporary_protocol_ext = cp;
568 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
569 rv = PROTO_MAILDIR;
571 ac_free(np);
572 jleave:
573 NYD_LEAVE;
574 return rv;
577 FL ui32_t
578 torek_hash(char const *name)
580 /* Chris Torek's hash.
581 * NOTE: need to change *at least* create-okey-map.pl when changing the
582 * algorithm!! */
583 ui32_t h = 0;
584 NYD_ENTER;
586 while (*name != '\0') {
587 h *= 33;
588 h += *name++;
590 NYD_LEAVE;
591 return h;
594 FL unsigned
595 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
597 unsigned h = 0, g;
598 NYD_ENTER;
600 cp--;
601 while (*++cp) {
602 h = (h << 4 & 0xffffffff) + (*cp&0377);
603 if ((g = h & 0xf0000000) != 0) {
604 h = h ^ g >> 24;
605 h = h ^ g;
608 NYD_LEAVE;
609 return h;
612 FL ui32_t
613 nextprime(ui32_t n)
615 static ui32_t const primes[] = {
616 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
617 131071, 262139, 524287, 1048573, 2097143, 4194301,
618 8388593, 16777213, 33554393, 67108859, 134217689,
619 268435399, 536870909, 1073741789, 2147483647
622 ui32_t mprime = 7, cutlim;
623 size_t i;
624 NYD_ENTER;
626 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
628 for (i = 0; i < NELEM(primes); i++)
629 if ((mprime = primes[i]) >= cutlim)
630 break;
631 if (i == NELEM(primes) && mprime < n)
632 mprime = n;
633 NYD_LEAVE;
634 return mprime;
637 FL int
638 expand_shell_escape(char const **s, bool_t use_nail_extensions)
640 char const *xs;
641 int c, n;
642 NYD2_ENTER;
644 xs = *s;
646 if ((c = *xs & 0xFF) == '\0')
647 goto jleave;
648 ++xs;
649 if (c != '\\')
650 goto jleave;
652 switch ((c = *xs & 0xFF)) {
653 case '\\': break;
654 case 'a': c = '\a'; break;
655 case 'b': c = '\b'; break;
656 case 'c': c = PROMPT_STOP; break;
657 case 'f': c = '\f'; break;
658 case 'n': c = '\n'; break;
659 case 'r': c = '\r'; break;
660 case 't': c = '\t'; break;
661 case 'v': c = '\v'; break;
662 case '0':
663 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
664 c <<= 3;
665 c |= *xs - '0';
667 goto jleave;
668 /* S-nail extension for nice (get)prompt(()) support */
669 case '&':
670 case '?':
671 case '$':
672 case '@':
673 if (use_nail_extensions) {
674 switch (c) {
675 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
676 case '?': c = exec_last_comm_error ? '1' : '0'; break;
677 case '$': c = PROMPT_DOLLAR; break;
678 case '@': c = PROMPT_AT; break;
680 break;
682 /* FALLTHRU */
683 case '\0':
684 /* A sole <backslash> at EOS is treated as-is! */
685 /* FALLTHRU */
686 default:
687 c = '\\';
688 goto jleave;
690 ++xs;
691 jleave:
692 *s = xs;
693 NYD2_LEAVE;
694 return c;
697 FL char *
698 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
700 static char buf[PROMPT_BUFFER_SIZE];
702 char *cp;
703 char const *ccp_base, *ccp;
704 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
705 bool_t run2;
706 NYD_ENTER;
708 cp = buf;
709 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0')
710 goto jleave;
711 NATCH_CHAR( cclen_base = strlen(ccp_base); )
713 dfmaxlen = 0; /* keep CC happy */
714 run2 = FAL0;
715 jredo:
716 ccp = ccp_base;
717 NATCH_CHAR( cclen = cclen_base; )
718 maxlen = sizeof(buf) -1;
720 for (;;) {
721 size_t l;
722 int c;
724 if (maxlen == 0)
725 goto jleave;
726 #ifdef HAVE_NATCH_CHAR
727 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
728 if (c <= 0) {
729 mblen(NULL, 0);
730 if (c < 0) {
731 *buf = '?';
732 cp = buf + 1;
733 goto jleave;
735 break;
736 } else if ((l = c) > 1) {
737 if (run2) {
738 memcpy(cp, ccp, l);
739 cp += l;
741 ccp += l;
742 maxlen -= l;
743 continue;
744 } else
745 #endif
746 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
747 if (run2)
748 *cp++ = (char)c;
749 --maxlen;
750 continue;
752 if (c == 0 || c == PROMPT_STOP)
753 break;
755 if (run2) {
756 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
757 if (a == NULL)
758 a = "";
759 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
760 cp += l;
761 maxlen -= l;
762 dfmaxlen -= l;
767 if (!run2) {
768 run2 = TRU1;
769 dfmaxlen = maxlen;
770 goto jredo;
772 jleave:
773 *cp = '\0';
774 NYD_LEAVE;
775 return buf;
778 FL char *
779 nodename(int mayoverride)
781 static char *sys_hostname, *hostname; /* XXX free-at-exit */
783 struct utsname ut;
784 char *hn;
785 #ifdef HAVE_SOCKETS
786 # ifdef HAVE_IPV6
787 struct addrinfo hints, *res;
788 # else
789 struct hostent *hent;
790 # endif
791 #endif
792 NYD_ENTER;
794 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
796 } else if ((hn = sys_hostname) == NULL) {
797 uname(&ut);
798 hn = ut.nodename;
799 #ifdef HAVE_SOCKETS
800 # ifdef HAVE_IPV6
801 memset(&hints, 0, sizeof hints);
802 hints.ai_family = AF_UNSPEC;
803 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
804 hints.ai_flags = AI_CANONNAME;
805 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
806 if (res->ai_canonname != NULL) {
807 size_t l = strlen(res->ai_canonname) +1;
808 hn = ac_alloc(l);
809 memcpy(hn, res->ai_canonname, l);
811 freeaddrinfo(res);
813 # else
814 hent = gethostbyname(hn);
815 if (hent != NULL)
816 hn = hent->h_name;
817 # endif
818 #endif
819 sys_hostname = sstrdup(hn);
820 #if defined HAVE_SOCKETS && defined HAVE_IPV6
821 if (hn != ut.nodename)
822 ac_free(hn);
823 #endif
824 hn = sys_hostname;
827 if (hostname != NULL && hostname != sys_hostname)
828 free(hostname);
829 hostname = sstrdup(hn);
830 NYD_LEAVE;
831 return hostname;
834 FL char *
835 getrandstring(size_t length)
837 static unsigned char nodedigest[16];
838 static pid_t pid;
840 struct str b64;
841 char *data, *cp;
842 size_t i;
843 int fd = -1;
844 #ifdef HAVE_MD5
845 md5_ctx ctx;
846 #else
847 size_t j;
848 #endif
849 NYD_ENTER;
851 data = ac_alloc(length);
853 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
854 length != (size_t)read(fd, data, length)) {
855 if (pid == 0) {
856 pid = getpid();
857 srand(pid);
858 cp = nodename(0);
859 #ifdef HAVE_MD5
860 md5_init(&ctx);
861 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
862 md5_final(nodedigest, &ctx);
863 #else
864 /* In that case it's only used for boundaries and Message-Id:s so that
865 * srand(3) should suffice */
866 j = strlen(cp) + 1;
867 for (i = 0; i < sizeof(nodedigest); ++i)
868 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
869 #endif
871 for (i = 0; i < length; i++)
872 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
873 nodedigest[i % sizeof nodedigest]);
875 if (fd >= 0)
876 close(fd);
878 b64_encode_buf(&b64, data, length, B64_SALLOC);
879 ac_free(data);
880 assert(length < b64.l);
881 b64.s[length] = '\0';
883 /* Base64 includes + and /, replace them with _ and - */
884 for (data = b64.s; length-- > 0; ++data)
885 if (*data == '+')
886 *data = '_';
887 else if (*data == '/')
888 *data = '-';
889 NYD_LEAVE;
890 return b64.s;
893 FL enum okay
894 makedir(char const *name)
896 struct stat st;
897 enum okay rv = STOP;
898 NYD_ENTER;
900 if (!mkdir(name, 0700))
901 rv = OKAY;
902 else {
903 int e = errno;
904 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
905 S_ISDIR(st.st_mode))
906 rv = OKAY;
908 NYD_LEAVE;
909 return rv;
912 #ifdef HAVE_FCHDIR
913 FL enum okay
914 cwget(struct cw *cw)
916 enum okay rv = STOP;
917 NYD_ENTER;
919 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
920 goto jleave;
921 if (fchdir(cw->cw_fd) == -1) {
922 close(cw->cw_fd);
923 goto jleave;
925 rv = OKAY;
926 jleave:
927 NYD_LEAVE;
928 return rv;
931 FL enum okay
932 cwret(struct cw *cw)
934 enum okay rv = STOP;
935 NYD_ENTER;
937 if (!fchdir(cw->cw_fd))
938 rv = OKAY;
939 NYD_LEAVE;
940 return rv;
943 FL void
944 cwrelse(struct cw *cw)
946 NYD_ENTER;
947 close(cw->cw_fd);
948 NYD_LEAVE;
951 #else /* !HAVE_FCHDIR */
952 FL enum okay
953 cwget(struct cw *cw)
955 enum okay rv = STOP;
956 NYD_ENTER;
958 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
959 rv = OKAY;
960 NYD_LEAVE;
961 return rv;
964 FL enum okay
965 cwret(struct cw *cw)
967 enum okay rv = STOP;
968 NYD_ENTER;
970 if (!chdir(cw->cw_wd))
971 rv = OKAY;
972 NYD_LEAVE;
973 return rv;
976 FL void
977 cwrelse(struct cw *cw)
979 NYD_ENTER;
980 UNUSED(cw);
981 NYD_LEAVE;
983 #endif /* !HAVE_FCHDIR */
985 FL size_t
986 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
988 size_t rv;
989 NYD_ENTER;
991 #ifdef HAVE_NATCH_CHAR
992 maxlen = MIN(maxlen, blen);
993 for (rv = 0; maxlen > 0;) {
994 int ml = mblen(buf, maxlen);
995 if (ml <= 0) {
996 mblen(NULL, 0);
997 break;
999 buf += ml;
1000 rv += ml;
1001 maxlen -= ml;
1003 #else
1004 rv = MIN(blen, maxlen);
1005 #endif
1006 NYD_LEAVE;
1007 return rv;
1010 FL size_t
1011 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1013 NATCH_CHAR( struct bidi_info bi; )
1014 size_t rv NATCH_CHAR( COMMA i );
1015 NYD_ENTER;
1017 rv = 0;
1018 if (maxlen-- == 0)
1019 goto j_leave;
1021 #ifdef HAVE_NATCH_CHAR
1022 bidi_info_create(&bi);
1023 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1024 bi.bi_end.l = 0;
1025 goto jnobidi;
1028 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1029 maxlen -= i;
1030 else
1031 goto jleave;
1033 if ((i = bi.bi_start.l) > 0) {
1034 memcpy(store, bi.bi_start.s, i);
1035 store += i;
1036 rv += i;
1039 jnobidi:
1040 while (maxlen > 0) {
1041 int ml = mblen(buf, blen);
1042 if (ml <= 0) {
1043 mblen(NULL, 0);
1044 break;
1046 if (UICMP(z, maxlen, <, ml))
1047 break;
1048 if (ml == 1)
1049 *store = *buf;
1050 else
1051 memcpy(store, buf, ml);
1052 store += ml;
1053 buf += ml;
1054 rv += ml;
1055 maxlen -= ml;
1058 if ((i = bi.bi_end.l) > 0) {
1059 memcpy(store, bi.bi_end.s, i);
1060 store += i;
1061 rv += i;
1063 jleave:
1064 *store = '\0';
1066 #else
1067 rv = MIN(blen, maxlen);
1068 memcpy(store, buf, rv);
1069 store[rv] = '\0';
1070 #endif
1071 j_leave:
1072 NYD_LEAVE;
1073 return rv;
1076 FL char *
1077 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1079 NATCH_CHAR( struct bidi_info bi; )
1080 int col_orig = col, n, sz;
1081 bool_t isbidi, isuni, istab, isrepl;
1082 char *nb, *np;
1083 NYD_ENTER;
1085 /* Bidi only on request and when there is 8-bit data */
1086 isbidi = isuni = FAL0;
1087 #ifdef HAVE_NATCH_CHAR
1088 isuni = ((options & OPT_UNICODE) != 0);
1089 bidi_info_create(&bi);
1090 if (bi.bi_start.l == 0)
1091 goto jnobidi;
1092 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1093 goto jnobidi;
1095 if ((size_t)col >= bi.bi_pad)
1096 col -= bi.bi_pad;
1097 else
1098 col = 0;
1099 jnobidi:
1100 #endif
1102 np = nb = salloc(mb_cur_max * strlen(cp) +
1103 ((fill ? col : 0)
1104 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1105 +1));
1107 #ifdef HAVE_NATCH_CHAR
1108 if (isbidi) {
1109 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1110 np += bi.bi_start.l;
1112 #endif
1114 while (*cp != '\0') {
1115 istab = FAL0;
1116 #ifdef HAVE_C90AMEND1
1117 if (mb_cur_max > 1) {
1118 wchar_t wc;
1120 n = 1;
1121 isrepl = TRU1;
1122 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1123 sz = 1;
1124 else if (wc == L'\t') {
1125 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1126 isrepl = FAL0;
1127 istab = TRU1;
1128 } else if (iswprint(wc)) {
1129 # ifndef HAVE_WCWIDTH
1130 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1131 # else
1132 if ((n = wcwidth(wc)) == -1)
1133 n = 1;
1134 else
1135 # endif
1136 isrepl = FAL0;
1138 } else
1139 #endif
1141 n = sz = 1;
1142 istab = (*cp == '\t');
1143 isrepl = !(istab || isprint((uc_it)*cp));
1146 if (n > col)
1147 break;
1148 col -= n;
1150 if (isrepl) {
1151 if (isuni) {
1152 np[0] = (char)0xEFu;
1153 np[1] = (char)0xBFu;
1154 np[2] = (char)0xBDu;
1155 np += 3;
1156 } else
1157 *np++ = '?';
1158 cp += sz;
1159 } else if (istab || (sz == 1 && spacechar(*cp))) {
1160 *np++ = ' ';
1161 ++cp;
1162 } else
1163 while (sz--)
1164 *np++ = *cp++;
1167 if (fill && col != 0) {
1168 if (fill > 0) {
1169 memmove(nb + col, nb, PTR2SIZE(np - nb));
1170 memset(nb, ' ', col);
1171 } else
1172 memset(np, ' ', col);
1173 np += col;
1174 col = 0;
1177 #ifdef HAVE_NATCH_CHAR
1178 if (isbidi) {
1179 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1180 np += bi.bi_end.l;
1182 #endif
1184 *np = '\0';
1185 if (cols_decr_used_or_null != NULL)
1186 *cols_decr_used_or_null -= col_orig - col;
1187 NYD_LEAVE;
1188 return nb;
1191 FL void
1192 makeprint(struct str const *in, struct str *out)
1194 static int print_all_chars = -1;
1196 char const *inp, *maxp;
1197 char *outp;
1198 DBG( size_t msz; )
1199 NYD_ENTER;
1201 if (print_all_chars == -1)
1202 print_all_chars = ok_blook(print_all_chars);
1204 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1205 inp = in->s;
1206 maxp = inp + in->l;
1208 if (print_all_chars) {
1209 out->l = in->l;
1210 memcpy(outp, inp, out->l);
1211 goto jleave;
1214 #ifdef HAVE_NATCH_CHAR
1215 if (mb_cur_max > 1) {
1216 char mbb[MB_LEN_MAX + 1];
1217 wchar_t wc;
1218 int i, n;
1219 bool_t isuni = ((options & OPT_UNICODE) != 0);
1221 out->l = 0;
1222 while (inp < maxp) {
1223 if (*inp & 0200)
1224 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1225 else {
1226 wc = *inp;
1227 n = 1;
1229 if (n == -1) {
1230 /* FIXME Why mbtowc() resetting here?
1231 * FIXME what about ISO 2022-JP plus -- those
1232 * FIXME will loose shifts, then!
1233 * FIXME THUS - we'd need special "known points"
1234 * FIXME to do so - say, after a newline!!
1235 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1236 mbtowc(&wc, NULL, mb_cur_max);
1237 wc = isuni ? 0xFFFD : '?';
1238 n = 1;
1239 } else if (n == 0)
1240 n = 1;
1241 inp += n;
1242 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1243 wc != '\t') {
1244 if ((wc & ~(wchar_t)037) == 0)
1245 wc = isuni ? 0x2400 | wc : '?';
1246 else if (wc == 0177)
1247 wc = isuni ? 0x2421 : '?';
1248 else
1249 wc = isuni ? 0x2426 : '?';
1251 if ((n = wctomb(mbb, wc)) <= 0)
1252 continue;
1253 out->l += n;
1254 assert(out->l < msz);
1255 for (i = 0; i < n; ++i)
1256 *outp++ = mbb[i];
1258 } else
1259 #endif /* NATCH_CHAR */
1261 int c;
1262 while (inp < maxp) {
1263 c = *inp++ & 0377;
1264 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1265 c = '?';
1266 *outp++ = c;
1268 out->l = in->l;
1270 jleave:
1271 out->s[out->l] = '\0';
1272 NYD_LEAVE;
1275 FL char *
1276 prstr(char const *s)
1278 struct str in, out;
1279 char *rp;
1280 NYD_ENTER;
1282 in.s = UNCONST(s);
1283 in.l = strlen(s);
1284 makeprint(&in, &out);
1285 rp = savestrbuf(out.s, out.l);
1286 free(out.s);
1287 NYD_LEAVE;
1288 return rp;
1291 FL int
1292 prout(char const *s, size_t sz, FILE *fp)
1294 struct str in, out;
1295 int n;
1296 NYD_ENTER;
1298 in.s = UNCONST(s);
1299 in.l = sz;
1300 makeprint(&in, &out);
1301 n = fwrite(out.s, 1, out.l, fp);
1302 free(out.s);
1303 NYD_LEAVE;
1304 return n;
1307 FL size_t
1308 putuc(int u, int c, FILE *fp)
1310 size_t rv;
1311 NYD_ENTER;
1312 UNUSED(u);
1314 #ifdef HAVE_NATCH_CHAR
1315 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1316 char mbb[MB_LEN_MAX];
1317 int i, n;
1319 if ((n = wctomb(mbb, u)) > 0) {
1320 rv = wcwidth(u);
1321 for (i = 0; i < n; ++i)
1322 if (putc(mbb[i] & 0377, fp) == EOF) {
1323 rv = 0;
1324 break;
1326 } else if (n == 0)
1327 rv = (putc('\0', fp) != EOF);
1328 else
1329 rv = 0;
1330 } else
1331 #endif
1332 rv = (putc(c, fp) != EOF);
1333 NYD_LEAVE;
1334 return rv;
1337 FL bool_t
1338 bidi_info_needed(char const *bdat, size_t blen)
1340 bool_t rv = FAL0;
1341 NYD_ENTER;
1343 #ifdef HAVE_NATCH_CHAR
1344 if (options & OPT_UNICODE)
1345 for (; blen > 0; ++bdat, --blen) {
1346 if ((ui8_t)*bdat > 0x7F) {
1347 /* TODO Checking for BIDI character: use S-CText fromutf8
1348 * TODO plus isrighttoleft (or whatever there will be)! */
1349 ui32_t c, x = (ui8_t)*bdat;
1351 if ((x & 0xE0) == 0xC0) {
1352 if (blen < 2)
1353 break;
1354 blen -= 1;
1355 c = x & ~0xC0;
1356 } else if ((x & 0xF0) == 0xE0) {
1357 if (blen < 3)
1358 break;
1359 blen -= 2;
1360 c = x & ~0xE0;
1361 c <<= 6;
1362 x = (ui8_t)*++bdat;
1363 c |= x & 0x7F;
1364 } else {
1365 if (blen < 4)
1366 break;
1367 blen -= 3;
1368 c = x & ~0xF0;
1369 c <<= 6;
1370 x = (ui8_t)*++bdat;
1371 c |= x & 0x7F;
1372 c <<= 6;
1373 x = (ui8_t)*++bdat;
1374 c |= x & 0x7F;
1376 c <<= 6;
1377 x = (ui8_t)*++bdat;
1378 c |= x & 0x7F;
1380 /* (Very very fuzzy, awaiting S-CText for good) */
1381 if ((c >= 0x05BE && c <= 0x08E3) ||
1382 (c >= 0xFB1D && c <= 0xFEFC) ||
1383 (c >= 0x10800 && c <= 0x10C48) ||
1384 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1385 rv = TRU1;
1386 break;
1390 #endif /* HAVE_NATCH_CHAR */
1391 NYD_LEAVE;
1392 return rv;
1395 FL void
1396 bidi_info_create(struct bidi_info *bip)
1398 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1399 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1400 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1401 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1402 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1403 NATCH_CHAR( char const *hb; )
1404 NYD_ENTER;
1406 memset(bip, 0, sizeof *bip);
1407 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1409 #ifdef HAVE_NATCH_CHAR
1410 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1411 switch (*hb) {
1412 case '3':
1413 bip->bi_pad = 2;
1414 /* FALLTHRU */
1415 case '2':
1416 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1417 break;
1418 case '1':
1419 bip->bi_pad = 2;
1420 /* FALLTHRU */
1421 default:
1422 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1423 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1424 break;
1426 bip->bi_start.l = bip->bi_end.l = 3;
1428 #endif
1429 NYD_LEAVE;
1432 #ifdef HAVE_COLOUR
1433 FL void
1434 colour_table_create(bool_t pager_used)
1436 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1437 size_t i;
1438 struct colour_table *ct;
1439 NYD_ENTER;
1441 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1442 goto jleave;
1443 else {
1444 char *term, *okterms;
1446 /* Don't use getenv(), but force copy-in into our own tables.. */
1447 if ((term = _var_voklook("TERM")) == NULL)
1448 goto jleave;
1449 /* terminfo rocks: if we find "color", assume it's right */
1450 if (strstr(term, "color") != NULL)
1451 goto jok;
1452 if ((okterms = ok_vlook(colour_terms)) == NULL)
1453 okterms = UNCONST(COLOUR_TERMS);
1454 okterms = savestr(okterms);
1456 i = strlen(term);
1457 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1458 if (!strncmp(u.cp, term, i))
1459 goto jok;
1460 goto jleave;
1463 jok:
1464 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1465 { static struct {
1466 enum okeys okey;
1467 enum colourspec cspec;
1468 char const *defval;
1469 } const map[] = {
1470 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1471 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1472 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1473 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1474 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1477 for (i = 0; i < NELEM(map); ++i) {
1478 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1479 u.ccp = map[i].defval;
1480 u.cp = _colour_iso6429(u.ccp);
1481 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1482 ct->ct_csinfo[map[i].cspec].s = u.cp;
1485 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1486 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1488 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1489 u.ccp = COLOUR_USER_HEADERS;
1490 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1491 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1492 jleave:
1493 NYD_LEAVE;
1496 FL void
1497 colour_put(FILE *fp, enum colourspec cs)
1499 NYD_ENTER;
1500 if (colour_table != NULL) {
1501 struct str const *cp = colour_get(cs);
1503 fwrite(cp->s, cp->l, 1, fp);
1505 NYD_LEAVE;
1508 FL void
1509 colour_put_header(FILE *fp, char const *name)
1511 enum colourspec cs = COLOURSPEC_HEADER;
1512 struct str const *uheads;
1513 char *cp, *cp_base, *x;
1514 size_t namelen;
1515 NYD_ENTER;
1517 if (colour_table == NULL)
1518 goto j_leave;
1519 /* Normal header colours if there are no user headers */
1520 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1521 if (uheads->s == NULL)
1522 goto jleave;
1524 /* Iterate over all entries in the *colour-user-headers* list */
1525 cp = ac_alloc(uheads->l +1);
1526 memcpy(cp, uheads->s, uheads->l +1);
1527 cp_base = cp;
1528 namelen = strlen(name);
1529 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1530 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1531 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1532 cs = COLOURSPEC_UHEADER;
1533 break;
1536 ac_free(cp_base);
1537 jleave:
1538 colour_put(fp, cs);
1539 j_leave:
1540 NYD_LEAVE;
1543 FL void
1544 colour_reset(FILE *fp)
1546 NYD_ENTER;
1547 if (colour_table != NULL)
1548 fwrite("\033[0m", 4, 1, fp);
1549 NYD_LEAVE;
1552 FL struct str const *
1553 colour_get(enum colourspec cs)
1555 struct str const *rv = NULL;
1556 NYD_ENTER;
1558 if (colour_table != NULL)
1559 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1560 rv = NULL;
1561 NYD_LEAVE;
1562 return rv;
1564 #endif /* HAVE_COLOUR */
1566 FL void
1567 time_current_update(struct time_current *tc, bool_t full_update)
1569 NYD_ENTER;
1570 tc->tc_time = time(NULL);
1571 if (full_update) {
1572 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1573 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1574 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1576 NYD_LEAVE;
1579 static void
1580 _out_of_memory(void)
1582 panic("no memory");
1585 #ifndef HAVE_DEBUG
1586 FL void *
1587 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1589 void *rv;
1590 NYD2_ENTER;
1592 if (s == 0)
1593 s = 1;
1594 if ((rv = malloc(s)) == NULL)
1595 _out_of_memory();
1596 NYD2_LEAVE;
1597 return rv;
1600 FL void *
1601 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1603 void *rv;
1604 NYD2_ENTER;
1606 if (s == 0)
1607 s = 1;
1608 if (v == NULL)
1609 rv = smalloc(s);
1610 else if ((rv = realloc(v, s)) == NULL)
1611 _out_of_memory();
1612 NYD2_LEAVE;
1613 return rv;
1616 FL void *
1617 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1619 void *rv;
1620 NYD2_ENTER;
1622 if (size == 0)
1623 size = 1;
1624 if ((rv = calloc(nmemb, size)) == NULL)
1625 _out_of_memory();
1626 NYD2_LEAVE;
1627 return rv;
1630 #else /* !HAVE_DEBUG */
1631 CTA(sizeof(char) == sizeof(ui8_t));
1633 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1634 # define _HOPE_SET(C) \
1635 do {\
1636 union mem_ptr __xl, __xu;\
1637 struct mem_chunk *__xc;\
1638 __xl.p_p = (C).p_p;\
1639 __xc = __xl.p_c - 1;\
1640 __xu.p_p = __xc;\
1641 (C).p_cp += 8;\
1642 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1643 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1644 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1645 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1646 __xu.p_ui8p += __xc->mc_size - 8;\
1647 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1648 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1649 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1650 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1651 } while (0)
1652 # define _HOPE_GET_TRACE(C,BAD) \
1653 do {\
1654 (C).p_cp += 8;\
1655 _HOPE_GET(C, BAD);\
1656 (C).p_cp += 8;\
1657 } while(0)
1658 # define _HOPE_GET(C,BAD) \
1659 do {\
1660 union mem_ptr __xl, __xu;\
1661 struct mem_chunk *__xc;\
1662 ui32_t __i;\
1663 __xl.p_p = (C).p_p;\
1664 __xl.p_cp -= 8;\
1665 (C).p_cp = __xl.p_cp;\
1666 __xc = __xl.p_c - 1;\
1667 (BAD) = FAL0;\
1668 __i = 0;\
1669 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1670 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1671 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1672 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1673 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1674 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1675 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1676 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1677 if (__i != 0) {\
1678 (BAD) = TRU1;\
1679 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1680 __xl.p_p, __i, mdbg_file, mdbg_line);\
1682 __xu.p_p = __xc;\
1683 __xu.p_ui8p += __xc->mc_size - 8;\
1684 __i = 0;\
1685 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1686 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1687 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1688 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1689 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1690 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1691 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1692 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1693 if (__i != 0) {\
1694 (BAD) = TRU1;\
1695 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1696 __xl.p_p, __i, mdbg_file, mdbg_line);\
1698 if (BAD)\
1699 alert(" ..canary last seen: %s, line %u",\
1700 __xc->mc_file, __xc->mc_line);\
1701 } while (0)
1703 FL void *
1704 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1706 union mem_ptr p;
1707 NYD2_ENTER;
1709 if (s == 0)
1710 s = 1;
1711 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1713 if ((p.p_p = (malloc)(s)) == NULL)
1714 _out_of_memory();
1715 p.p_c->mc_prev = NULL;
1716 if ((p.p_c->mc_next = _mem_list) != NULL)
1717 _mem_list->mc_prev = p.p_c;
1718 p.p_c->mc_file = mdbg_file;
1719 p.p_c->mc_line = (ui16_t)mdbg_line;
1720 p.p_c->mc_isfree = FAL0;
1721 p.p_c->mc_size = (ui32_t)s;
1722 _mem_list = p.p_c++;
1723 _HOPE_SET(p);
1725 ++_mem_aall;
1726 ++_mem_acur;
1727 _mem_amax = MAX(_mem_amax, _mem_acur);
1728 _mem_mall += s;
1729 _mem_mcur += s;
1730 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1731 NYD2_LEAVE;
1732 return p.p_p;
1735 FL void *
1736 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1738 union mem_ptr p;
1739 bool_t isbad;
1740 NYD2_ENTER;
1742 if ((p.p_p = v) == NULL) {
1743 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
1744 goto jleave;
1747 _HOPE_GET(p, isbad);
1748 --p.p_c;
1749 if (p.p_c->mc_isfree) {
1750 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1751 "\tLast seen: %s, line %d\n",
1752 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1753 goto jforce;
1756 if (p.p_c == _mem_list)
1757 _mem_list = p.p_c->mc_next;
1758 else
1759 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1760 if (p.p_c->mc_next != NULL)
1761 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1763 --_mem_acur;
1764 _mem_mcur -= p.p_c->mc_size;
1765 jforce:
1766 if (s == 0)
1767 s = 1;
1768 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1770 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
1771 _out_of_memory();
1772 p.p_c->mc_prev = NULL;
1773 if ((p.p_c->mc_next = _mem_list) != NULL)
1774 _mem_list->mc_prev = p.p_c;
1775 p.p_c->mc_file = mdbg_file;
1776 p.p_c->mc_line = (ui16_t)mdbg_line;
1777 p.p_c->mc_isfree = FAL0;
1778 p.p_c->mc_size = (ui32_t)s;
1779 _mem_list = p.p_c++;
1780 _HOPE_SET(p);
1782 ++_mem_aall;
1783 ++_mem_acur;
1784 _mem_amax = MAX(_mem_amax, _mem_acur);
1785 _mem_mall += s;
1786 _mem_mcur += s;
1787 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1788 jleave:
1789 NYD2_LEAVE;
1790 return p.p_p;
1793 FL void *
1794 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1796 union mem_ptr p;
1797 NYD2_ENTER;
1799 if (size == 0)
1800 size = 1;
1801 if (nmemb == 0)
1802 nmemb = 1;
1803 size *= nmemb;
1804 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
1806 if ((p.p_p = (malloc)(size)) == NULL)
1807 _out_of_memory();
1808 memset(p.p_p, 0, size);
1809 p.p_c->mc_prev = NULL;
1810 if ((p.p_c->mc_next = _mem_list) != NULL)
1811 _mem_list->mc_prev = p.p_c;
1812 p.p_c->mc_file = mdbg_file;
1813 p.p_c->mc_line = (ui16_t)mdbg_line;
1814 p.p_c->mc_isfree = FAL0;
1815 p.p_c->mc_size = (ui32_t)size;
1816 _mem_list = p.p_c++;
1817 _HOPE_SET(p);
1819 ++_mem_aall;
1820 ++_mem_acur;
1821 _mem_amax = MAX(_mem_amax, _mem_acur);
1822 _mem_mall += size;
1823 _mem_mcur += size;
1824 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1825 NYD2_LEAVE;
1826 return p.p_p;
1829 FL void
1830 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1832 union mem_ptr p;
1833 bool_t isbad;
1834 NYD2_ENTER;
1836 if ((p.p_p = v) == NULL) {
1837 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1838 goto jleave;
1841 _HOPE_GET(p, isbad);
1842 --p.p_c;
1843 if (p.p_c->mc_isfree) {
1844 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1845 "\tLast seen: %s, line %d\n",
1846 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1847 goto jleave;
1850 if (p.p_c == _mem_list)
1851 _mem_list = p.p_c->mc_next;
1852 else
1853 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1854 if (p.p_c->mc_next != NULL)
1855 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1856 p.p_c->mc_isfree = TRU1;
1858 --_mem_acur;
1859 _mem_mcur -= p.p_c->mc_size;
1861 if (options & OPT_DEBUG) {
1862 p.p_c->mc_next = _mem_free;
1863 _mem_free = p.p_c;
1864 } else
1865 (free)(p.p_c);
1866 jleave:
1867 NYD2_LEAVE;
1870 FL void
1871 smemreset(void)
1873 union mem_ptr p;
1874 size_t c = 0, s = 0;
1875 NYD_ENTER;
1877 for (p.p_c = _mem_free; p.p_c != NULL;) {
1878 void *vp = p.p_c;
1879 ++c;
1880 s += p.p_c->mc_size;
1881 p.p_c = p.p_c->mc_next;
1882 (free)(vp);
1884 _mem_free = NULL;
1886 if (options & OPT_DEBUG)
1887 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1888 c, s);
1889 NYD_LEAVE;
1892 FL int
1893 c_smemtrace(void *v)
1895 /* For _HOPE_GET() */
1896 char const * const mdbg_file = "smemtrace()";
1897 int const mdbg_line = -1;
1898 FILE *fp;
1899 union mem_ptr p, xp;
1900 bool_t isbad;
1901 size_t lines;
1902 NYD_ENTER;
1904 v = (void*)0x1;
1905 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1906 NULL) {
1907 perror("tmpfile");
1908 goto jleave;
1911 fprintf(fp, "Memory statistics:\n"
1912 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
1913 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
1914 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
1916 fprintf(fp, "Currently allocated memory chunks:\n");
1917 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1918 ++lines, p.p_c = p.p_c->mc_next) {
1919 xp = p;
1920 ++xp.p_c;
1921 _HOPE_GET_TRACE(xp, isbad);
1922 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1923 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1924 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
1925 p.p_c->mc_line);
1928 if (options & OPT_DEBUG) {
1929 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1930 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1931 xp = p;
1932 ++xp.p_c;
1933 _HOPE_GET_TRACE(xp, isbad);
1934 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1935 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1936 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1937 p.p_c->mc_file, p.p_c->mc_line);
1941 page_or_print(fp, lines);
1942 Fclose(fp);
1943 v = NULL;
1944 jleave:
1945 NYD_LEAVE;
1946 return (v != NULL);
1949 # ifdef _HAVE_MEMCHECK
1950 FL bool_t
1951 _smemcheck(char const *mdbg_file, int mdbg_line)
1953 union mem_ptr p, xp;
1954 bool_t anybad = FAL0, isbad;
1955 size_t lines;
1956 NYD_ENTER;
1958 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1959 ++lines, p.p_c = p.p_c->mc_next) {
1960 xp = p;
1961 ++xp.p_c;
1962 _HOPE_GET_TRACE(xp, isbad);
1963 if (isbad) {
1964 anybad = TRU1;
1965 fprintf(stderr,
1966 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1967 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1968 p.p_c->mc_file, p.p_c->mc_line);
1972 if (options & OPT_DEBUG) {
1973 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1974 xp = p;
1975 ++xp.p_c;
1976 _HOPE_GET_TRACE(xp, isbad);
1977 if (isbad) {
1978 anybad = TRU1;
1979 fprintf(stderr,
1980 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1981 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1982 p.p_c->mc_file, p.p_c->mc_line);
1986 NYD_LEAVE;
1987 return anybad;
1989 # endif /* _HAVE_MEMCHECK */
1991 # undef _HOPE_SIZE
1992 # undef _HOPE_SET
1993 # undef _HOPE_GET_TRACE
1994 # undef _HOPE_GET
1995 #endif /* HAVE_DEBUG */
1997 /* s-it-mode */