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>.
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
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
40 #ifndef HAVE_AMALGAMATION
44 #include <sys/utsname.h>
52 # include <sys/socket.h>
73 static ui32_t _nyd_curr
;
74 static ui32_t _nyd_level
;
75 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
78 /* {hold,rele}_all_sigs() */
79 static size_t _alls_depth
;
80 static sigset_t _alls_nset
, _alls_oset
;
82 /* {hold,rele}_sigs() */
83 static size_t _hold_sigdepth
;
84 static sigset_t _hold_nset
, _hold_oset
;
86 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
88 static char * _colour_iso6429(char const *wish
);
92 static void _nyd_print(struct nyd_info
*nip
);
97 _colour_iso6429(char const *wish
)
99 char const * const wish_orig
= wish
;
100 char *xwish
, *cp
, cfg
[3] = {0, 0, 0};
103 /* Since we use salloc(), reuse the strcomma() buffer also for the return
104 * value, ensure we have enough room for that */
106 size_t i
= strlen(wish
) + 1;
107 xwish
= salloc(MAX(i
, sizeof("\033[1;30;40m")));
108 memcpy(xwish
, wish
, i
);
112 /* Iterate over the colour spec */
113 while ((cp
= strcomma(&xwish
, TRU1
)) != NULL
) {
114 char *y
, *x
= strchr(cp
, '=');
117 fprintf(stderr
, tr(527,
118 "Invalid colour specification \"%s\": >>> %s <<<\n"),
124 /* TODO convert the ft/fg/bg parser into a table-based one! */
125 if (!asccasecmp(cp
, "ft")) {
126 if (!asccasecmp(x
, "bold"))
128 else if (!asccasecmp(x
, "inverse"))
130 else if (!asccasecmp(x
, "underline"))
134 } else if (!asccasecmp(cp
, "fg")) {
137 } else if (!asccasecmp(cp
, "bg")) {
140 if (!asccasecmp(x
, "black"))
142 else if (!asccasecmp(x
, "blue"))
144 else if (!asccasecmp(x
, "green"))
146 else if (!asccasecmp(x
, "red"))
148 else if (!asccasecmp(x
, "brown"))
150 else if (!asccasecmp(x
, "magenta"))
152 else if (!asccasecmp(x
, "cyan"))
154 else if (!asccasecmp(x
, "white"))
162 /* Restore our salloc() buffer, create return value */
163 xwish
= UNCONST(wish
);
164 if (cfg
[0] || cfg
[1] || cfg
[2]) {
178 if (cfg
[0] || cfg
[1])
188 return UNCONST(wish
);
190 #endif /* HAVE_COLOUR */
194 _nyd_print(struct nyd_info
*nip
) /* XXX like SFSYS;no magics;jumps:lvl wrong */
197 union {int i
; size_t z
;} u
;
199 u
.i
= snprintf(buf
, sizeof buf
, "%c [%2u] %-25.25s %.16s:%-5u\n",
200 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
201 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
204 if (u
.z
> sizeof buf
)
205 u
.z
= sizeof buf
- 1; /* (Skip \0) */
206 write(STDERR_FILENO
, buf
, u
.z
);
212 panic(char const *format
, ...)
217 fprintf(stderr
, tr(1, "Panic: "));
219 va_start(ap
, format
);
220 vfprintf(stderr
, format
, ap
);
226 abort(); /* Was exit(EXIT_ERR); for a while, but no */
230 alert(char const *format
, ...)
235 fprintf(stderr
, tr(1, "Panic: "));
237 va_start(ap
, format
);
238 vfprintf(stderr
, format
, ap
);
247 safe_signal(int signum
, sighandler_type handler
)
249 struct sigaction nact
, oact
;
253 nact
.sa_handler
= handler
;
254 sigemptyset(&nact
.sa_mask
);
257 nact
.sa_flags
|= SA_RESTART
;
259 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
268 if (_alls_depth
++ == 0) {
269 sigfillset(&_alls_nset
);
270 sigdelset(&_alls_nset
, SIGABRT
);
272 sigdelset(&_alls_nset
, SIGBUS
);
274 sigdelset(&_alls_nset
, SIGCHLD
);
275 sigdelset(&_alls_nset
, SIGFPE
);
276 sigdelset(&_alls_nset
, SIGILL
);
277 sigdelset(&_alls_nset
, SIGKILL
);
278 sigdelset(&_alls_nset
, SIGSEGV
);
279 sigdelset(&_alls_nset
, SIGSTOP
);
280 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
289 if (--_alls_depth
== 0)
290 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
298 if (_hold_sigdepth
++ == 0) {
299 sigemptyset(&_hold_nset
);
300 sigaddset(&_hold_nset
, SIGHUP
);
301 sigaddset(&_hold_nset
, SIGINT
);
302 sigaddset(&_hold_nset
, SIGQUIT
);
303 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
312 if (--_hold_sigdepth
== 0)
313 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
319 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
321 struct nyd_info
*nip
= _nyd_infos
;
323 if (_nyd_curr
!= NELEM(_nyd_infos
))
329 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
330 nip
->ni_level
= ((act
== 0) ? _nyd_level
331 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
335 _nyd_oncrash(int signo
)
337 struct sigaction xact
;
339 struct nyd_info
*nip
;
342 xact
.sa_handler
= SIG_DFL
;
343 sigemptyset(&xact
.sa_mask
);
345 sigaction(signo
, &xact
, NULL
);
347 fprintf(stderr
, "\n\nNYD: program dying due to signal %d:\n", signo
);
348 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
349 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
351 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
355 sigaddset(&xset
, signo
);
356 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
364 touch(struct message
*mp
)
367 mp
->m_flag
|= MTOUCH
;
368 if (!(mp
->m_flag
& MREAD
))
369 mp
->m_flag
|= MREAD
| MSTATUS
;
374 is_dir(char const *name
)
380 if (!stat(name
, &sbuf
))
381 rv
= !!S_ISDIR(sbuf
.st_mode
);
387 argcount(char **argv
)
392 for (ap
= argv
; *ap
++ != NULL
;)
395 return ap
- argv
- 1;
405 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
417 cp
= ok_vlook(PAGER
);
418 if (cp
== NULL
|| *cp
== '\0')
425 paging_seems_sensible(void)
431 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
)
432 rv
= (*cp
!= '\0') ? (size_t)atol(cp
) : (size_t)scrnheight
;
438 page_or_print(FILE *fp
, size_t lines
)
446 if ((rows
= paging_seems_sensible()) != 0 && lines
== 0) {
447 while ((c
= getc(fp
)) != EOF
)
448 if (c
== '\n' && ++lines
> rows
)
453 if (rows
!= 0 && lines
>= rows
)
454 run_command(get_pager(), 0, fileno(fp
), -1, NULL
, NULL
, NULL
);
456 while ((c
= getc(fp
)) != EOF
)
462 which_protocol(char const *name
)
468 enum protocol rv
= PROTO_UNKNOWN
;
471 if (name
[0] == '%' && name
[1] == ':')
473 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
477 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
478 if (strncmp(name
, "pop3://", 7) == 0) {
482 fprintf(stderr
, tr(216, "No POP3 support compiled in.\n"));
486 if (strncmp(name
, "pop3s://", 8) == 0) {
487 #if defined HAVE_POP3 && defined HAVE_SSL
491 fprintf(stderr
, tr(216, "No POP3 support compiled in.\n"));
494 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
499 if (strncmp(name
, "imap://", 7) == 0) {
503 fprintf(stderr
, tr(269, "No IMAP support compiled in.\n"));
507 if (strncmp(name
, "imaps://", 8) == 0) {
508 #if defined HAVE_IMAP && defined HAVE_SSL
512 fprintf(stderr
, tr(269, "No IMAP support compiled in.\n"));
515 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
521 /* TODO This is the de facto maildir code and thus belongs into there! */
524 np
= ac_alloc((sz
= strlen(name
)) + 5);
525 memcpy(np
, name
, sz
+ 1);
526 if (stat(name
, &st
) == 0) {
527 if (S_ISDIR(st
.st_mode
)) {
528 strcpy(&np
[sz
], "/tmp");
529 if (stat(np
, &st
) == 0 && S_ISDIR(st
.st_mode
)) {
530 strcpy(&np
[sz
], "/new");
531 if (stat(np
, &st
) == 0 && S_ISDIR(st
.st_mode
)) {
532 strcpy(&np
[sz
], "/cur");
533 if (stat(np
, &st
) == 0 && S_ISDIR(st
.st_mode
))
539 strcpy(&np
[sz
], ".gz");
540 if (stat(np
, &st
) < 0) {
541 strcpy(&np
[sz
], ".bz2");
542 if (stat(np
, &st
) < 0) {
543 if ((cp
= ok_vlook(newfolders
)) != NULL
&&
544 strcmp(cp
, "maildir") == 0)
557 torek_hash(char const *name
)
559 /* Chris Torek's hash.
560 * NOTE: need to change *at least* create-okey-map.pl when changing the
565 while (*name
!= '\0') {
574 pjw(char const *cp
) /* XXX obsolete that -> torek_hash */
581 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
582 if ((g
= h
& 0xf0000000) != 0) {
594 static ui32_t
const primes
[] = {
595 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
596 131071, 262139, 524287, 1048573, 2097143, 4194301,
597 8388593, 16777213, 33554393, 67108859, 134217689,
598 268435399, 536870909, 1073741789, 2147483647
605 for (i
= 0; i
< NELEM(primes
); i
++)
606 if ((mprime
= primes
[i
]) >= (n
< 65536 ? n
*4 : (n
< 262144u ? n
*2 : n
)))
608 if (i
== NELEM(primes
))
609 mprime
= n
; /* TODO not so prime, but better than failure */
615 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
621 if ((c
= *xs
& 0xFF) == '\0')
627 switch ((c
= *xs
& 0xFF)) {
629 case 'a': c
= '\a'; break;
630 case 'b': c
= '\b'; break;
631 case 'c': c
= PROMPT_STOP
; break;
632 case 'f': c
= '\f'; break;
633 case 'n': c
= '\n'; break;
634 case 'r': c
= '\r'; break;
635 case 't': c
= '\t'; break;
636 case 'v': c
= '\v'; break;
638 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
643 /* S-nail extension for nice (get)prompt(()) support */
648 if (use_nail_extensions
) {
650 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
651 case '?': c
= exec_last_comm_error
? '1' : '0'; break;
652 case '$': c
= PROMPT_DOLLAR
; break;
653 case '@': c
= PROMPT_AT
; break;
659 /* A sole <backslash> at EOS is treated as-is! */
675 static char buf
[PROMPT_BUFFER_SIZE
];
681 if ((ccp
= ok_vlook(prompt
)) == NULL
|| *ccp
== '\0')
684 for (; PTRCMP(cp
, <, buf
+ sizeof(buf
) - 1); ++cp
) {
687 int c
= expand_shell_escape(&ccp
, TRU1
);
693 if (c
== 0 || c
== PROMPT_STOP
)
696 a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
700 if (PTRCMP(cp
+ l
, >=, buf
+ sizeof(buf
) - 1))
714 nodename(int mayoverride
)
716 static char *hostname
;
721 struct addrinfo hints
, *res
;
723 struct hostent
*hent
;
728 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
729 if (hostname
!= NULL
)
731 hostname
= sstrdup(hn
);
732 } else if (hostname
== NULL
) {
737 memset(&hints
, 0, sizeof hints
);
738 hints
.ai_family
= AF_UNSPEC
;
739 hints
.ai_socktype
= SOCK_DGRAM
; /* dummy */
740 hints
.ai_flags
= AI_CANONNAME
;
741 if (getaddrinfo(hn
, "0", &hints
, &res
) == 0) {
742 if (res
->ai_canonname
!= NULL
) {
743 size_t l
= strlen(res
->ai_canonname
);
744 hn
= ac_alloc(l
+ 1);
745 memcpy(hn
, res
->ai_canonname
, l
+ 1);
750 hent
= gethostbyname(hn
);
755 hostname
= sstrdup(hn
);
756 #if defined HAVE_SOCKETS && defined HAVE_IPV6
757 if (hn
!= ut
.nodename
)
766 lookup_password_for_token(char const *token
)
773 var
= ac_alloc(tl
+ 9 +1);
775 memcpy(var
, "password-", 9);
776 memcpy(var
+ 9, token
, tl
);
779 if ((cp
= vok_vlook(var
)) != NULL
)
787 getrandstring(size_t length
)
789 static unsigned char nodedigest
[16];
803 data
= ac_alloc(length
);
804 if ((fd
= open("/dev/urandom", O_RDONLY
)) == -1 ||
805 length
!= (size_t)read(fd
, data
, length
)) {
812 md5_update(&ctx
, (unsigned char*)cp
, strlen(cp
));
813 md5_final(nodedigest
, &ctx
);
815 /* In that case it's only used for boundaries and Message-Id:s so that
816 * srand(3) should suffice */
818 for (i
= 0; i
< sizeof(nodedigest
); ++i
)
819 nodedigest
[i
] = (unsigned char)(cp
[i
% j
] ^ rand());
822 for (i
= 0; i
< length
; i
++)
823 data
[i
] = (char)((int)(255 * (rand() / (RAND_MAX
+ 1.0))) ^
824 nodedigest
[i
% sizeof nodedigest
]);
829 (void)b64_encode_buf(&b64
, data
, length
, B64_SALLOC
);
831 assert(length
< b64
.l
);
832 b64
.s
[length
] = '\0';
839 md5tohex(char hex
[MD5TOHEX_SIZE
], void const *vp
)
845 for (i
= 0; i
< MD5TOHEX_SIZE
/ 2; i
++) {
847 hex
[j
] = hexchar((cp
[i
] & 0xf0) >> 4);
848 hex
[++j
] = hexchar(cp
[i
] & 0x0f);
855 cram_md5_string(char const *user
, char const *pass
, char const *b64
)
858 char digest
[16], *cp
;
865 (void)b64_decode(&out
, &in
, NULL
);
866 assert(out
.s
!= NULL
);
868 hmac_md5((unsigned char*)out
.s
, out
.l
, UNCONST(pass
), strlen(pass
), digest
);
870 cp
= md5tohex(salloc(MD5TOHEX_SIZE
+ 1), digest
);
873 in
.l
= lu
+ MD5TOHEX_SIZE
+1;
874 in
.s
= ac_alloc(lu
+ 1 + MD5TOHEX_SIZE
+1);
875 memcpy(in
.s
, user
, lu
);
877 memcpy(in
.s
+ lu
+ 1, cp
, MD5TOHEX_SIZE
);
878 b64_encode(&out
, &in
, B64_SALLOC
| B64_CRLF
);
883 #endif /* HAVE_MD5 */
886 makedir(char const *name
)
892 if (!mkdir(name
, 0700))
896 if ((e
== EEXIST
|| e
== ENOSYS
) && stat(name
, &st
) == 0 &&
911 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
913 if (fchdir(cw
->cw_fd
) == -1) {
929 if (!fchdir(cw
->cw_fd
))
936 cwrelse(struct cw
*cw
)
943 #else /* !HAVE_FCHDIR */
950 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
962 if (!chdir(cw
->cw_wd
))
969 cwrelse(struct cw
*cw
)
975 #endif /* !HAVE_FCHDIR */
978 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
980 int col_orig
= col
, n
, sz
;
984 np
= nb
= salloc(mb_cur_max
* strlen(cp
) + col
+ 1);
987 if (mb_cur_max
> 1) {
990 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) < 0)
992 else if ((n
= wcwidth(wc
)) < 0)
1000 if (sz
== 1 && spacechar(*cp
)) {
1008 if (fill
&& col
!= 0) {
1010 memmove(nb
+ col
, nb
, (size_t)(np
- nb
));
1011 memset(nb
, ' ', col
);
1013 memset(np
, ' ', col
);
1019 if (cols_decr_used_or_null
!= NULL
)
1020 *cols_decr_used_or_null
-= col_orig
- col
;
1026 makeprint(struct str
const *in
, struct str
*out
)
1028 static int print_all_chars
= -1;
1030 char const *inp
, *maxp
;
1035 if (print_all_chars
== -1)
1036 print_all_chars
= ok_blook(print_all_chars
);
1039 out
->s
= outp
= smalloc(msz
);
1043 if (print_all_chars
) {
1045 memcpy(outp
, inp
, out
->l
);
1049 #ifdef HAVE_C90AMEND1
1050 if (mb_cur_max
> 1) {
1051 char mbb
[MB_LEN_MAX
+ 1];
1057 while (inp
< maxp
) {
1059 n
= mbtowc(&wc
, inp
, maxp
- inp
);
1065 /* FIXME Why mbtowc() resetting here?
1066 * FIXME what about ISO 2022-JP plus -- those
1067 * FIXME will loose shifts, then!
1068 * FIXME THUS - we'd need special "known points"
1069 * FIXME to do so - say, after a newline!!
1070 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1071 (void)mbtowc(&wc
, NULL
, mb_cur_max
);
1072 wc
= utf8
? 0xFFFD : '?';
1077 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1079 if ((wc
& ~(wchar_t)037) == 0)
1080 wc
= utf8
? 0x2400 | wc
: '?';
1081 else if (wc
== 0177)
1082 wc
= utf8
? 0x2421 : '?';
1084 wc
= utf8
? 0x2426 : '?';
1086 if ((n
= wctomb(mbb
, wc
)) <= 0)
1089 if (out
->l
>= msz
- 1) {
1090 dist
= outp
- out
->s
;
1091 out
->s
= srealloc(out
->s
, msz
+= 32);
1092 outp
= &out
->s
[dist
];
1094 for (i
= 0; i
< n
; i
++)
1098 #endif /* C90AMEND1 */
1101 while (inp
< maxp
) {
1103 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1111 out
->s
[out
->l
] = '\0';
1115 prstr(char const *s
)
1123 makeprint(&in
, &out
);
1124 rp
= savestrbuf(out
.s
, out
.l
);
1131 prout(char const *s
, size_t sz
, FILE *fp
)
1139 makeprint(&in
, &out
);
1140 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1147 putuc(int u
, int c
, FILE *fp
)
1153 #ifdef HAVE_C90AMEND1
1154 if (utf8
&& (u
& ~(wchar_t)0177)) {
1155 char mbb
[MB_LEN_MAX
];
1158 if ((n
= wctomb(mbb
, u
)) > 0) {
1160 for (i
= 0; i
< n
; ++i
)
1161 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1166 rv
= (putc('\0', fp
) != EOF
);
1171 rv
= (putc(c
, fp
) != EOF
);
1178 colour_table_create(char const *pager_used
)
1180 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1182 struct colour_table
*ct
;
1185 if (ok_blook(colour_disable
))
1188 /* If pager, check wether it is allowed to use colour */
1189 if (pager_used
!= NULL
) {
1192 if ((u
.cp
= ok_vlook(colour_pagers
)) == NULL
)
1193 u
.ccp
= COLOUR_PAGERS
;
1194 pager
= savestr(u
.cp
);
1196 while ((u
.cp
= strcomma(&pager
, TRU1
)) != NULL
)
1197 if (strstr(pager_used
, u
.cp
) != NULL
)
1202 /* $TERM is different in that we default to false unless whitelisted */
1204 char *term
, *okterms
;
1206 /* Don't use getenv(), but force copy-in into our own tables.. */
1207 if ((term
= _var_voklook("TERM")) == NULL
)
1209 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1210 okterms
= UNCONST(COLOUR_TERMS
);
1211 okterms
= savestr(okterms
);
1214 while ((u
.cp
= strcomma(&okterms
, TRU1
)) != NULL
)
1215 if (!strncmp(u
.cp
, term
, i
))
1221 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1224 enum colourspec cspec
;
1227 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1228 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1229 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1230 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1231 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1234 for (i
= 0; i
< NELEM(map
); ++i
) {
1235 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1236 u
.ccp
= map
[i
].defval
;
1237 u
.cp
= _colour_iso6429(u
.ccp
);
1238 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1239 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1242 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") - 1;
1243 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1245 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1246 u
.ccp
= COLOUR_USER_HEADERS
;
1247 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1248 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1254 colour_put(FILE *fp
, enum colourspec cs
)
1257 if (colour_table
!= NULL
) {
1258 struct str
const *cp
= colour_get(cs
);
1260 fwrite(cp
->s
, cp
->l
, 1, fp
);
1266 colour_put_header(FILE *fp
, char const *name
)
1268 enum colourspec cs
= COLOURSPEC_HEADER
;
1269 struct str
const *uheads
;
1270 char *cp
, *cp_base
, *x
;
1274 if (colour_table
== NULL
)
1276 /* Normal header colours if there are no user headers */
1277 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1278 if (uheads
->s
== NULL
)
1281 /* Iterate over all entries in the *colour-user-headers* list */
1282 cp
= ac_alloc(uheads
->l
+ 1);
1283 memcpy(cp
, uheads
->s
, uheads
->l
+ 1);
1285 namelen
= strlen(name
);
1286 while ((x
= strcomma(&cp
, TRU1
)) != NULL
) {
1287 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1288 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1289 cs
= COLOURSPEC_UHEADER
;
1301 colour_reset(FILE *fp
)
1304 if (colour_table
!= NULL
)
1305 fwrite("\033[0m", 4, 1, fp
);
1309 FL
struct str
const *
1310 colour_get(enum colourspec cs
)
1312 struct str
const *rv
= NULL
;
1315 if (colour_table
!= NULL
)
1316 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1321 #endif /* HAVE_COLOUR */
1324 time_current_update(struct time_current
*tc
, bool_t full_update
)
1327 tc
->tc_time
= time(NULL
);
1329 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1330 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1331 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1337 _out_of_memory(void)
1344 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
1351 if ((rv
= malloc(s
)) == NULL
)
1358 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1367 else if ((rv
= realloc(v
, s
)) == NULL
)
1374 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1381 if ((rv
= calloc(nmemb
, size
)) == NULL
)
1387 #else /* !HAVE_DEBUG */
1388 CTA(sizeof(char) == sizeof(ui8_t
));
1390 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1391 # define _HOPE_SET(C) \
1393 union ptr __xl, __xu;\
1394 struct chunk *__xc;\
1399 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1400 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1401 __xu.ui8p += __xc->size - 8;\
1402 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1403 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1405 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1406 # define _HOPE_GET(C,BAD) \
1408 union ptr __xl, __xu;\
1409 struct chunk *__xc;\
1417 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1418 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1419 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1420 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1421 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1422 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1423 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1424 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1427 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1428 __xl.p, __i, mdbg_file, mdbg_line);\
1431 __xu.ui8p += __xc->size - 8;\
1433 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1434 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1435 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1436 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1437 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1438 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1439 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1440 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1443 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1444 __xl.p, __i, mdbg_file, mdbg_line);\
1447 alert(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1467 struct chunk
*_mlist
, *_mfree
;
1470 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
1477 s
+= sizeof(struct chunk
) + _HOPE_SIZE
;
1479 if ((p
.p
= (malloc
)(s
)) == NULL
)
1482 if ((p
.c
->next
= _mlist
) != NULL
)
1484 p
.c
->file
= mdbg_file
;
1485 p
.c
->line
= (ui16_t
)mdbg_line
;
1487 p
.c
->size
= (ui32_t
)s
;
1495 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1501 if ((p
.p
= v
) == NULL
) {
1502 p
.p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
1506 _HOPE_GET(p
, isbad
);
1509 fprintf(stderr
, "srealloc(): region freed! At %s, line %d\n"
1510 "\tLast seen: %s, line %d\n",
1511 mdbg_file
, mdbg_line
, p
.c
->file
, p
.c
->line
);
1518 p
.c
->prev
->next
= p
.c
->next
;
1519 if (p
.c
->next
!= NULL
)
1520 p
.c
->next
->prev
= p
.c
->prev
;
1525 s
+= sizeof(struct chunk
) + _HOPE_SIZE
;
1527 if ((p
.p
= (realloc
)(p
.c
, s
)) == NULL
)
1530 if ((p
.c
->next
= _mlist
) != NULL
)
1532 p
.c
->file
= mdbg_file
;
1533 p
.c
->line
= (ui16_t
)mdbg_line
;
1535 p
.c
->size
= (ui32_t
)s
;
1544 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1554 size
+= sizeof(struct chunk
) + _HOPE_SIZE
;
1556 if ((p
.p
= (malloc
)(size
)) == NULL
)
1558 memset(p
.p
, 0, size
);
1560 if ((p
.c
->next
= _mlist
) != NULL
)
1562 p
.c
->file
= mdbg_file
;
1563 p
.c
->line
= (ui16_t
)mdbg_line
;
1565 p
.c
->size
= (ui32_t
)size
;
1573 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
1579 if ((p
.p
= v
) == NULL
) {
1580 fprintf(stderr
, "sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
1584 _HOPE_GET(p
, isbad
);
1587 fprintf(stderr
, "sfree(): double-free avoided at %s, line %d\n"
1588 "\tLast seen: %s, line %d\n",
1589 mdbg_file
, mdbg_line
, p
.c
->file
, p
.c
->line
);
1596 p
.c
->prev
->next
= p
.c
->next
;
1597 if (p
.c
->next
!= NULL
)
1598 p
.c
->next
->prev
= p
.c
->prev
;
1601 if (options
& OPT_DEBUG
) {
1614 size_t c
= 0, s
= 0;
1617 for (p
.c
= _mfree
; p
.c
!= NULL
;) {
1626 if (options
& OPT_DEBUG
)
1627 fprintf(stderr
, "smemreset(): freed %" ZFMT
" chunks/%" ZFMT
" bytes\n",
1633 c_smemtrace(void *v
)
1635 /* For _HOPE_GET() */
1636 char const * const mdbg_file
= "smemtrace()";
1637 int const mdbg_line
= -1;
1645 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
1651 fprintf(fp
, "Currently allocated memory chunks:\n");
1652 for (lines
= 0, p
.c
= _mlist
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
) {
1655 _HOPE_GET_TRACE(xp
, isbad
);
1656 fprintf(fp
, "%s%p (%5" ZFMT
" bytes): %s, line %u\n",
1657 (isbad
? "! CANARY ERROR: " : ""), xp
.p
,
1658 (size_t)(p
.c
->size
- sizeof(struct chunk
)), p
.c
->file
, p
.c
->line
);
1661 if (options
& OPT_DEBUG
) {
1662 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
1663 for (p
.c
= _mfree
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
) {
1666 _HOPE_GET_TRACE(xp
, isbad
);
1667 fprintf(fp
, "%s%p (%5" ZFMT
" bytes): %s, line %u\n",
1668 (isbad
? "! CANARY ERROR: " : ""), xp
.p
,
1669 (size_t)(p
.c
->size
- sizeof(struct chunk
)), p
.c
->file
, p
.c
->line
);
1673 page_or_print(fp
, lines
);
1683 _smemcheck(char const *mdbg_file
, int mdbg_line
)
1686 bool_t anybad
= FAL0
, isbad
;
1690 for (lines
= 0, p
.c
= _mlist
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
) {
1693 _HOPE_GET_TRACE(xp
, isbad
);
1697 "! CANARY ERROR: %p (%5" ZFMT
" bytes): %s, line %u\n",
1698 xp
.p
, (size_t)(p
.c
->size
- sizeof(struct chunk
)),
1699 p
.c
->file
, p
.c
->line
);
1703 if (options
& OPT_DEBUG
) {
1704 for (p
.c
= _mfree
; p
.c
!= NULL
; ++lines
, p
.c
= p
.c
->next
) {
1707 _HOPE_GET_TRACE(xp
, isbad
);
1711 "! CANARY ERROR: %p (%5" ZFMT
" bytes): %s, line %u\n",
1712 xp
.p
, (size_t)(p
.c
->size
- sizeof(struct chunk
)),
1713 p
.c
->file
, p
.c
->line
);
1720 # endif /* MEMCHECK */
1721 #endif /* HAVE_DEBUG */
1723 /* vim:set fenc=utf-8:s-it-mode */