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>
67 struct mem_chunk
*mc_prev
;
68 struct mem_chunk
*mc_next
;
78 struct mem_chunk
*p_c
;
82 #endif /* HAVE_DEBUG */
84 /* NYD, memory pool 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
;
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 */
105 static char * _colour_iso6429(char const *wish
);
109 static void _nyd_print(struct nyd_info
*nip
);
114 _colour_iso6429(char const *wish
)
116 char const * const wish_orig
= wish
;
117 char *xwish
, *cp
, cfg
[3] = {0, 0, 0};
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
);
129 /* Iterate over the colour spec */
130 while ((cp
= n_strsep(&xwish
, ',', TRU1
)) != NULL
) {
131 char *y
, *x
= strchr(cp
, '=');
134 fprintf(stderr
, tr(527,
135 "Invalid colour specification \"%s\": >>> %s <<<\n"),
141 /* TODO convert the ft/fg/bg parser into a table-based one! */
142 if (!asccasecmp(cp
, "ft")) {
143 if (!asccasecmp(x
, "bold"))
145 else if (!asccasecmp(x
, "inverse"))
147 else if (!asccasecmp(x
, "underline"))
151 } else if (!asccasecmp(cp
, "fg")) {
154 } else if (!asccasecmp(cp
, "bg")) {
157 if (!asccasecmp(x
, "black"))
159 else if (!asccasecmp(x
, "blue"))
161 else if (!asccasecmp(x
, "green"))
163 else if (!asccasecmp(x
, "red"))
165 else if (!asccasecmp(x
, "brown"))
167 else if (!asccasecmp(x
, "magenta"))
169 else if (!asccasecmp(x
, "cyan"))
171 else if (!asccasecmp(x
, "white"))
179 /* Restore our salloc() buffer, create return value */
180 xwish
= UNCONST(wish
);
181 if (cfg
[0] || cfg
[1] || cfg
[2]) {
195 if (cfg
[0] || cfg
[1])
205 return UNCONST(wish
);
207 #endif /* HAVE_COLOUR */
211 _nyd_print(struct nyd_info
*nip
) /* XXX like SFSYS;no magics;jumps:lvl wrong */
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
));
221 if (u
.z
> sizeof buf
)
222 u
.z
= sizeof buf
- 1; /* (Skip \0) */
223 write(STDERR_FILENO
, buf
, u
.z
);
229 panic(char const *format
, ...)
234 fprintf(stderr
, tr(1, "Panic: "));
236 va_start(ap
, format
);
237 vfprintf(stderr
, format
, ap
);
243 abort(); /* Was exit(EXIT_ERR); for a while, but no */
247 alert(char const *format
, ...)
252 fprintf(stderr
, tr(1, "Panic: "));
254 va_start(ap
, format
);
255 vfprintf(stderr
, format
, ap
);
264 safe_signal(int signum
, sighandler_type handler
)
266 struct sigaction nact
, oact
;
270 nact
.sa_handler
= handler
;
271 sigemptyset(&nact
.sa_mask
);
274 nact
.sa_flags
|= SA_RESTART
;
276 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
285 if (_alls_depth
++ == 0) {
286 sigfillset(&_alls_nset
);
287 sigdelset(&_alls_nset
, SIGABRT
);
289 sigdelset(&_alls_nset
, SIGBUS
);
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
);
306 if (--_alls_depth
== 0)
307 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
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
);
329 if (--_hold_sigdepth
== 0)
330 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
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
))
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
--);
352 _nyd_oncrash(int signo
)
354 struct sigaction xact
;
356 struct nyd_info
*nip
;
359 xact
.sa_handler
= SIG_DFL
;
360 sigemptyset(&xact
.sa_mask
);
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
)
368 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
372 sigaddset(&xset
, signo
);
373 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
381 touch(struct message
*mp
)
384 mp
->m_flag
|= MTOUCH
;
385 if (!(mp
->m_flag
& MREAD
))
386 mp
->m_flag
|= MREAD
| MSTATUS
;
391 is_dir(char const *name
)
397 if (!stat(name
, &sbuf
))
398 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
404 argcount(char **argv
)
409 for (ap
= argv
; *ap
++ != NULL
;)
412 return (int)PTR2SIZE(ap
- argv
- 1);
422 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
423 s
= scrnheight
- 4; /* XXX no magics */
434 cp
= ok_vlook(PAGER
);
435 if (cp
== NULL
|| *cp
== '\0')
442 paging_seems_sensible(void)
448 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
)
449 rv
= (*cp
!= '\0') ? (size_t)atol(cp
) : (size_t)scrnheight
;
455 page_or_print(FILE *fp
, size_t lines
)
463 if ((rows
= paging_seems_sensible()) != 0 && lines
== 0) {
464 while ((c
= getc(fp
)) != EOF
)
465 if (c
== '\n' && ++lines
> rows
)
470 if (rows
!= 0 && lines
>= rows
)
471 run_command(get_pager(), 0, fileno(fp
), -1, NULL
, NULL
, NULL
);
473 while ((c
= getc(fp
)) != EOF
)
479 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
485 enum protocol rv
= PROTO_UNKNOWN
;
488 if (name
[0] == '%' && name
[1] == ':')
490 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
494 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
495 if (!strncmp(name
, "pop3://", 7)) {
499 fprintf(stderr
, tr(216, "No POP3 support compiled in.\n"));
501 } else if (!strncmp(name
, "pop3s://", 8)) {
502 #if defined HAVE_POP3 && defined HAVE_SSL
506 fprintf(stderr
, tr(216, "No POP3 support compiled in.\n"));
509 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
512 } else if (!strncmp(name
, "imap://", 7)) {
516 fprintf(stderr
, tr(269, "No IMAP support compiled in.\n"));
518 } else if (!strncmp(name
, "imaps://", 8)) {
519 #if defined HAVE_IMAP && defined HAVE_SSL
523 fprintf(stderr
, tr(269, "No IMAP support compiled in.\n"));
526 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
533 /* TODO This is the de facto maildir code and thus belongs into there!
534 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
535 * TODO or (more likely) in addition to *newfolders*) */
538 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
539 memcpy(np
, name
, sz
+ 1);
540 if (!stat(name
, &st
)) {
541 if (S_ISDIR(st
.st_mode
) &&
542 (strcpy(&np
[sz
], "/tmp"), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
543 (strcpy(&np
[sz
], "/new"), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
544 (strcpy(&np
[sz
], "/cur"), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
546 } else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
555 torek_hash(char const *name
)
557 /* Chris Torek's hash.
558 * NOTE: need to change *at least* create-okey-map.pl when changing the
563 while (*name
!= '\0') {
572 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
579 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
580 if ((g
= h
& 0xf0000000) != 0) {
592 static ui32_t
const primes
[] = {
593 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
594 131071, 262139, 524287, 1048573, 2097143, 4194301,
595 8388593, 16777213, 33554393, 67108859, 134217689,
596 268435399, 536870909, 1073741789, 2147483647
599 ui32_t mprime
= 7, cutlim
;
603 cutlim
= (n
< 65536 ? n
<< 2 : (n
< 262144 ? n
<< 1 : n
));
605 for (i
= 0; i
< NELEM(primes
); i
++)
606 if ((mprime
= primes
[i
]) >= cutlim
)
608 if (i
== NELEM(primes
) && mprime
< n
)
615 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
623 if ((c
= *xs
& 0xFF) == '\0')
629 switch ((c
= *xs
& 0xFF)) {
631 case 'a': c
= '\a'; break;
632 case 'b': c
= '\b'; break;
633 case 'c': c
= PROMPT_STOP
; break;
634 case 'f': c
= '\f'; break;
635 case 'n': c
= '\n'; break;
636 case 'r': c
= '\r'; break;
637 case 't': c
= '\t'; break;
638 case 'v': c
= '\v'; break;
640 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
645 /* S-nail extension for nice (get)prompt(()) support */
650 if (use_nail_extensions
) {
652 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
653 case '?': c
= exec_last_comm_error
? '1' : '0'; break;
654 case '$': c
= PROMPT_DOLLAR
; break;
655 case '@': c
= PROMPT_AT
; break;
661 /* A sole <backslash> at EOS is treated as-is! */
677 static char buf
[PROMPT_BUFFER_SIZE
];
683 if ((ccp
= ok_vlook(prompt
)) == NULL
|| *ccp
== '\0')
686 for (; PTRCMP(cp
, <, buf
+ sizeof(buf
) - 1); ++cp
) {
689 int c
= expand_shell_escape(&ccp
, TRU1
);
695 if (c
== 0 || c
== PROMPT_STOP
)
698 a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
702 if (PTRCMP(cp
+ l
, >=, buf
+ sizeof(buf
) - 1))
716 nodename(int mayoverride
)
718 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
724 struct addrinfo hints
, *res
;
726 struct hostent
*hent
;
731 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
733 } else if ((hn
= sys_hostname
) == NULL
) {
738 memset(&hints
, 0, sizeof hints
);
739 hints
.ai_family
= AF_UNSPEC
;
740 hints
.ai_socktype
= SOCK_DGRAM
; /* (dummy) */
741 hints
.ai_flags
= AI_CANONNAME
;
742 if (getaddrinfo(hn
, "0", &hints
, &res
) == 0) {
743 if (res
->ai_canonname
!= NULL
) {
744 size_t l
= strlen(res
->ai_canonname
) +1;
746 memcpy(hn
, res
->ai_canonname
, l
);
751 hent
= gethostbyname(hn
);
756 sys_hostname
= sstrdup(hn
);
757 #if defined HAVE_SOCKETS && defined HAVE_IPV6
758 if (hn
!= ut
.nodename
)
764 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
766 hostname
= sstrdup(hn
);
772 url_parse(struct url
*urlp
, enum cproto cproto
, char const *data
)
774 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
777 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
785 memset(urlp
, 0, sizeof *urlp
);
786 urlp
->url_input
= data
;
787 urlp
->url_cproto
= cproto
;
789 /* Network protocol */
790 #define _protox(X,Y) \
791 urlp->url_portno = Y;\
792 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
793 urlp->url_proto[sizeof(X) -1] = '\0';\
794 urlp->url_proto_len = sizeof(X) -1;\
795 urlp->url_proto_xlen = sizeof(X "://") -1
796 #define __if(X,Y,Z) \
797 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
799 data += sizeof(X "://") -1;\
800 do { Z; } while (0);\
803 #define _if(X,Y) __if(X, Y, (void)0)
805 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
807 # define _ifs(X,Y) goto jeproto;
814 _if ("submission", 587)
825 _protox("pop3", 110);
834 _protox("imap", 143);
846 if (strstr(data
, "://") != NULL
) {
847 #if !defined __ALLPROTO || !defined HAVE_SSL
850 fprintf(stderr
, tr(574, "URL `proto://' prefix invalid: `%s'\n"),
856 /* User and password, I */
858 if ((cp
= UNCONST(last_at_before_slash(data
))) == NULL
)
861 urlp
->url_had_user
= TRU1
;
862 urlp
->url_user
.s
= savestrbuf(data
, urlp
->url_user
.l
= PTR2SIZE(cp
- data
));
865 /* And also have a password? */
866 if ((cp
= strchr(urlp
->url_user
.s
, ':')) != NULL
) {
867 x
= urlp
->url_user
.s
;
868 urlp
->url_user
.s
= savestrbuf(x
, urlp
->url_user
.l
= PTR2SIZE(cp
- x
));
869 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= urlxdec(++cp
));
870 urlp
->url_pass_enc
.l
= strlen(
871 urlp
->url_pass_enc
.s
= urlxenc(urlp
->url_pass
.s
));
874 /* Servername and port -- and possible path suffix */
876 if ((cp
= strchr(data
, ':')) != NULL
) { /* TODO URL parse, IPv6 support */
880 urlp
->url_port
= x
= savestr(x
= cp
+ 1);
881 if ((x
= strchr(x
, '/')) != NULL
)
883 l
= strtol(urlp
->url_port
, &eptr
, 10);
884 if (*eptr
!= '\0' || l
<= 0 || UICMP(32, l
, >=, 0xFFFFu
)) {
885 fprintf(stderr
, tr(573, "URL with invalid port number: `%s'\n"),
889 urlp
->url_portno
= (ui16_t
)l
;
891 if ((x
= strchr(data
, '/')) != NULL
)
892 data
= savestrbuf(data
, PTR2SIZE(x
- data
));
893 cp
= UNCONST(data
+ strlen(data
));
896 /* A (non-empty) path may only occur with IMAP */
897 if (x
!= NULL
&& x
[1] != '\0') {
898 if (cproto
!= CPROTO_IMAP
) {
899 fprintf(stderr
, tr(575, "URL protocol doesn't support paths: `%s'\n"),
903 urlp
->url_path
.l
= strlen(++x
);
904 urlp
->url_path
.s
= savestrbuf(x
, urlp
->url_path
.l
);
907 urlp
->url_host
.s
= savestrbuf(data
, urlp
->url_host
.l
= PTR2SIZE(cp
- data
));
909 for (cp
= urlp
->url_host
.s
, i
= urlp
->url_host
.l
; i
!= 0; ++cp
, --i
)
910 *cp
= lowerconv(*cp
);
915 struct str
*s
= &urlp
->url_hp
;
917 s
->s
= salloc(urlp
->url_host
.l
+ 1 + sizeof("65536")-1 +1);
918 memcpy(s
->s
, urlp
->url_host
.s
, i
= urlp
->url_host
.l
);
919 if (urlp
->url_port
!= NULL
) {
920 size_t j
= strlen(urlp
->url_port
);
922 memcpy(s
->s
+ i
, urlp
->url_port
, j
);
930 * If there was no user in the URL, do we have *user-HOST* or *user*? */
931 if (!urlp
->url_had_user
) {
932 char const usrstr
[] = "user-";
933 size_t const usrlen
= sizeof(usrstr
) -1;
935 cp
= ac_alloc(usrlen
+ urlp
->url_hp
.l
+1);
936 memcpy(cp
, usrstr
, usrlen
);
937 memcpy(cp
+ usrlen
, urlp
->url_hp
.s
, urlp
->url_hp
.l
+1);
938 if ((urlp
->url_user
.s
= vok_vlook(cp
)) == NULL
) {
939 cp
[usrlen
- 1] = '\0';
940 if ((urlp
->url_user
.s
= vok_vlook(cp
)) == NULL
)
941 urlp
->url_user
.s
= UNCONST(myname
);
946 urlp
->url_user
.l
= strlen(urlp
->url_user
.s
= urlxdec(urlp
->url_user
.s
));
947 urlp
->url_user_enc
.l
= strlen(
948 urlp
->url_user_enc
.s
= urlxenc(urlp
->url_user
.s
));
951 if (urlp
->url_user_enc
.l
== 0)
952 urlp
->url_uhp
= urlp
->url_hp
;
954 struct str
*s
= &urlp
->url_uhp
;
955 size_t i
= urlp
->url_user_enc
.l
;
957 s
->s
= salloc(i
+ 1 + urlp
->url_hp
.l
+1);
959 memcpy(s
->s
, urlp
->url_user_enc
.s
, i
);
962 memcpy(s
->s
+ i
, urlp
->url_hp
.s
, urlp
->url_hp
.l
+1);
968 * For SMTP we apply ridiculously complicated *v15-compat* plus
969 * *smtp-hostname* / *hostname* dependent rules */
972 if (cproto
== CPROTO_SMTP
&& ok_blook(v15_compat
) &&
973 (cp
= ok_vlook(smtp_hostname
)) != NULL
) {
976 h
.s
= savestrbuf(cp
, h
.l
= strlen(cp
));
980 if (urlp
->url_user_enc
.l
== 0)
983 struct str
*s
= &urlp
->url_uh
;
984 size_t i
= urlp
->url_user_enc
.l
;
986 s
->s
= salloc(i
+ 1 + h
.l
+1);
988 memcpy(s
->s
, urlp
->url_user_enc
.s
, i
);
991 memcpy(s
->s
+ i
, h
.s
, h
.l
+1);
997 /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */
999 char *ud
= salloc((i
= urlp
->url_proto_xlen
+ urlp
->url_uhp
.l
) +
1000 1 + urlp
->url_path
.l
+1);
1002 urlp
->url_proto
[urlp
->url_proto_len
] = ':';
1003 memcpy(sstpcpy(ud
, urlp
->url_proto
), urlp
->url_uhp
.s
, urlp
->url_uhp
.l
+1);
1004 urlp
->url_proto
[urlp
->url_proto_len
] = '\0';
1006 if (urlp
->url_path
.l
== 0)
1007 urlp
->url_puhp
= urlp
->url_puhpp
= ud
;
1009 urlp
->url_puhp
= savestrbuf(ud
, i
);
1010 urlp
->url_puhpp
= ud
;
1013 memcpy(ud
, urlp
->url_path
.s
, urlp
->url_path
.l
+1);
1018 #endif /* __ANYPROTO */
1027 ccred_lookup_old(struct ccred
*ccp
, enum cproto cproto
, char const *addr
)
1029 char const *pname
, *pxstr
, *authdef
;
1030 size_t pxlen
, addrlen
, i
;
1033 enum {NONE
=0, WANT_PASS
=1<<0, REQ_PASS
=1<<1, WANT_USER
=1<<2, REQ_USER
=1<<3}
1035 bool_t addr_is_nuser
= FAL0
; /* XXX v15.0 legacy! v15_compat */
1038 memset(ccp
, 0, sizeof *ccp
);
1044 pxstr
= "smtp-auth";
1045 pxlen
= sizeof("smtp-auth") -1;
1046 authmask
= AUTHTYPE_NONE
| AUTHTYPE_PLAIN
| AUTHTYPE_LOGIN
|
1047 AUTHTYPE_CRAM_MD5
| AUTHTYPE_GSSAPI
;
1049 addr_is_nuser
= TRU1
;
1053 pxstr
= "pop3-auth";
1054 pxlen
= sizeof("pop3-auth") -1;
1055 authmask
= AUTHTYPE_PLAIN
;
1060 pxstr
= "imap-auth";
1061 pxlen
= sizeof("imap-auth") -1;
1062 authmask
= AUTHTYPE_LOGIN
| AUTHTYPE_CRAM_MD5
| AUTHTYPE_GSSAPI
;
1067 ccp
->cc_cproto
= cproto
;
1068 addrlen
= strlen(addr
);
1069 vbuf
= ac_alloc(pxlen
+ addrlen
+ sizeof("-password-")-1 +1);
1070 memcpy(vbuf
, pxstr
, pxlen
);
1072 /* Authentication type */
1074 memcpy(vbuf
+ pxlen
+ 1, addr
, addrlen
+1);
1075 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1077 if ((s
= vok_vlook(vbuf
)) == NULL
)
1078 s
= UNCONST(authdef
);
1081 if (!asccasecmp(s
, "none")) {
1082 ccp
->cc_auth
= "NONE";
1083 ccp
->cc_authtype
= AUTHTYPE_NONE
;
1085 } else if (!asccasecmp(s
, "plain")) {
1086 ccp
->cc_auth
= "PLAIN";
1087 ccp
->cc_authtype
= AUTHTYPE_PLAIN
;
1088 ware
= REQ_PASS
| REQ_USER
;
1089 } else if (!asccasecmp(s
, "login")) {
1090 ccp
->cc_auth
= "LOGIN";
1091 ccp
->cc_authtype
= AUTHTYPE_LOGIN
;
1092 ware
= REQ_PASS
| REQ_USER
;
1093 } else if (!asccasecmp(s
, "cram-md5")) {
1094 ccp
->cc_auth
= "CRAM-MD5";
1095 ccp
->cc_authtype
= AUTHTYPE_CRAM_MD5
;
1096 ware
= REQ_PASS
| REQ_USER
;
1097 } else if (!asccasecmp(s
, "gssapi")) {
1098 ccp
->cc_auth
= "GSS-API";
1099 ccp
->cc_authtype
= AUTHTYPE_GSSAPI
;
1101 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1102 } else if (!asccasecmp(cp
, "sasl")) {
1103 ware
= REQ_USER
| WANT_PASS
;
1108 if (!(ccp
->cc_authtype
& authmask
)) {
1109 fprintf(stderr
, tr(273, "Unsupported %s authentication method: %s\n"),
1115 if (ccp
->cc_authtype
== AUTHTYPE_CRAM_MD5
) {
1116 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
1122 if (ccp
->cc_authtype
== AUTHTYPE_GSSAPI
) {
1123 fprintf(stderr
, tr(272, "No GSS-API support compiled in.\n"));
1130 if (!(ware
& (WANT_USER
| REQ_USER
)))
1133 if (!addr_is_nuser
) {
1134 if ((s
= UNCONST(last_at_before_slash(addr
))) != NULL
) {
1135 ccp
->cc_user
.s
= urlxdec(savestrbuf(addr
, PTR2SIZE(s
- addr
)));
1136 ccp
->cc_user
.l
= strlen(ccp
->cc_user
.s
);
1137 } else if (ware
& REQ_USER
)
1142 memcpy(vbuf
+ pxlen
, "-user-", i
= sizeof("-user-") -1);
1144 memcpy(vbuf
+ i
, addr
, addrlen
+1);
1145 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1147 if ((s
= vok_vlook(vbuf
)) == NULL
&& (ware
& REQ_USER
)) {
1148 if ((s
= getuser(NULL
)) != NULL
)
1151 jgetuser
: /* TODO v15.0: today we simply bail, but we should call getuser().
1152 * TODO even better: introduce `PROTO-user' and `PROTO-pass' and
1153 * TODO check that first, then! change control flow, grow `vbuf' */
1154 fprintf(stderr
, tr(274,
1155 "A user is necessary for %s authentication.\n"), pname
);
1161 ccp
->cc_user
.l
= strlen(ccp
->cc_user
.s
= s
);
1165 if (!(ware
& (WANT_PASS
| REQ_PASS
)))
1168 if (!addr_is_nuser
) {
1169 memcpy(vbuf
, "password-", i
= sizeof("password-") -1);
1171 memcpy(vbuf
+ pxlen
, "-password-", i
= sizeof("-password-") -1);
1174 memcpy(vbuf
+ i
, addr
, addrlen
+1);
1175 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1177 if ((!addr_is_nuser
|| (s
= vok_vlook(vbuf
)) == NULL
) &&
1178 (ware
& REQ_PASS
)) {
1179 if ((s
= getpassword(NULL
)) == NULL
) {
1180 fprintf(stderr
, tr(275,
1181 "A password is necessary for %s authentication.\n"), pname
);
1187 ccp
->cc_pass
.l
= strlen(ccp
->cc_pass
.s
= urlxdec(s
));
1192 return (ccp
!= NULL
);
1196 ccred_lookup(struct ccred
*ccp
, struct url
*urlp
)
1198 char const *pstr
, *authdef
;
1202 enum {NONE
=0, WANT_PASS
=1<<0, REQ_PASS
=1<<1, WANT_USER
=1<<2, REQ_USER
=1<<3}
1206 memset(ccp
, 0, sizeof *ccp
);
1207 ccp
->cc_user
= urlp
->url_user
;
1209 switch ((ccp
->cc_cproto
= urlp
->url_cproto
)) {
1213 plen
= sizeof("smtp") -1;
1214 authmask
= AUTHTYPE_NONE
| AUTHTYPE_PLAIN
| AUTHTYPE_LOGIN
|
1215 AUTHTYPE_CRAM_MD5
| AUTHTYPE_GSSAPI
;
1220 plen
= sizeof("pop3") -1;
1221 authmask
= AUTHTYPE_PLAIN
;
1226 plen
= sizeof("imap") -1;
1227 authmask
= AUTHTYPE_LOGIN
| AUTHTYPE_CRAM_MD5
| AUTHTYPE_GSSAPI
;
1232 /* Note: "password-" is longer than "-auth-", ditto .url_uhp and .url_hp */
1233 vbuf
= ac_alloc(plen
+ sizeof("password-")-1 + urlp
->url_uhp
.l
+1);
1234 memcpy(vbuf
, pstr
, plen
);
1236 /* Authentication type */
1237 memcpy(vbuf
+ plen
, "-auth-", i
= sizeof("-auth-") -1);
1239 /* -USER@HOST, -HOST, '' */
1240 memcpy(vbuf
+ i
, urlp
->url_uhp
.s
, urlp
->url_uhp
.l
+1);
1241 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1242 memcpy(vbuf
+ i
, urlp
->url_hp
.s
, urlp
->url_hp
.l
+1);
1243 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1244 vbuf
[plen
+ sizeof("-auth") -1] = '\0';
1245 if ((s
= vok_vlook(vbuf
)) == NULL
)
1246 s
= UNCONST(authdef
);
1250 if (!asccasecmp(s
, "none")) {
1251 ccp
->cc_auth
= "NONE";
1252 ccp
->cc_authtype
= AUTHTYPE_NONE
;
1254 } else if (!asccasecmp(s
, "plain")) {
1255 ccp
->cc_auth
= "PLAIN";
1256 ccp
->cc_authtype
= AUTHTYPE_PLAIN
;
1257 ware
= REQ_PASS
| REQ_USER
;
1258 } else if (!asccasecmp(s
, "login")) {
1259 ccp
->cc_auth
= "LOGIN";
1260 ccp
->cc_authtype
= AUTHTYPE_LOGIN
;
1261 ware
= REQ_PASS
| REQ_USER
;
1262 } else if (!asccasecmp(s
, "cram-md5")) {
1263 ccp
->cc_auth
= "CRAM-MD5";
1264 ccp
->cc_authtype
= AUTHTYPE_CRAM_MD5
;
1265 ware
= REQ_PASS
| REQ_USER
;
1266 } else if (!asccasecmp(s
, "gssapi")) {
1267 ccp
->cc_auth
= "GSS-API";
1268 ccp
->cc_authtype
= AUTHTYPE_GSSAPI
;
1270 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1271 } else if (!asccasecmp(cp
, "sasl")) {
1272 ware
= REQ_USER
| WANT_PASS
;
1277 if (!(ccp
->cc_authtype
& authmask
)) {
1278 fprintf(stderr
, tr(273, "Unsupported %s authentication method: %s\n"),
1284 if (ccp
->cc_authtype
== AUTHTYPE_CRAM_MD5
) {
1285 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
1291 if (ccp
->cc_authtype
== AUTHTYPE_GSSAPI
) {
1292 fprintf(stderr
, tr(272, "No GSS-API support compiled in.\n"));
1299 if ((ccp
->cc_pass
= urlp
->url_pass
).s
!= NULL
)
1302 memcpy(vbuf
, "password-", i
= sizeof("password-") -1);
1303 /* -USER@HOST, -HOST, '' */
1304 memcpy(vbuf
+ i
, urlp
->url_uhp
.s
, urlp
->url_uhp
.l
+1);
1305 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1306 memcpy(vbuf
+ i
, urlp
->url_hp
.s
, urlp
->url_hp
.l
+1);
1307 if ((s
= vok_vlook(vbuf
)) == NULL
) {
1309 if ((s
= vok_vlook(vbuf
)) == NULL
&& (ware
& REQ_PASS
)) {
1310 if ((s
= getpassword(NULL
)) != NULL
)
1313 fprintf(stderr
, tr(275,
1314 "A password is necessary for %s authentication.\n"), pstr
);
1321 ccp
->cc_pass
.l
= strlen(ccp
->cc_pass
.s
= urlxdec(s
));
1326 return (ccp
!= NULL
);
1330 getrandstring(size_t length
)
1332 static unsigned char nodedigest
[16];
1346 data
= ac_alloc(length
);
1348 if ((fd
= open("/dev/urandom", O_RDONLY
)) == -1 ||
1349 length
!= (size_t)read(fd
, data
, length
)) {
1356 md5_update(&ctx
, (unsigned char*)cp
, strlen(cp
));
1357 md5_final(nodedigest
, &ctx
);
1359 /* In that case it's only used for boundaries and Message-Id:s so that
1360 * srand(3) should suffice */
1362 for (i
= 0; i
< sizeof(nodedigest
); ++i
)
1363 nodedigest
[i
] = (unsigned char)(cp
[i
% j
] ^ rand());
1366 for (i
= 0; i
< length
; i
++)
1367 data
[i
] = (char)((int)(255 * (rand() / (RAND_MAX
+ 1.0))) ^
1368 nodedigest
[i
% sizeof nodedigest
]);
1373 b64_encode_buf(&b64
, data
, length
, B64_SALLOC
);
1375 assert(length
< b64
.l
);
1376 b64
.s
[length
] = '\0';
1383 md5tohex(char hex
[MD5TOHEX_SIZE
], void const *vp
)
1385 char const *cp
= vp
;
1389 for (i
= 0; i
< MD5TOHEX_SIZE
/ 2; i
++) {
1391 hex
[j
] = hexchar((cp
[i
] & 0xf0) >> 4);
1392 hex
[++j
] = hexchar(cp
[i
] & 0x0f);
1399 cram_md5_string(char const *user
, char const *pass
, char const *b64
)
1402 char digest
[16], *cp
;
1407 in
.s
= UNCONST(b64
);
1408 in
.l
= strlen(in
.s
);
1409 b64_decode(&out
, &in
, NULL
);
1410 assert(out
.s
!= NULL
);
1412 hmac_md5((unsigned char*)out
.s
, out
.l
, UNCONST(pass
), strlen(pass
), digest
);
1414 cp
= md5tohex(salloc(MD5TOHEX_SIZE
+1), digest
);
1417 in
.l
= lu
+ MD5TOHEX_SIZE
+1;
1418 in
.s
= ac_alloc(lu
+ 1 + MD5TOHEX_SIZE
+1);
1419 memcpy(in
.s
, user
, lu
);
1421 memcpy(in
.s
+ lu
, cp
, MD5TOHEX_SIZE
);
1422 b64_encode(&out
, &in
, B64_SALLOC
| B64_CRLF
);
1429 hmac_md5(unsigned char *text
, int text_len
, unsigned char *key
, int key_len
,
1433 * This code is taken from
1435 * Network Working Group H. Krawczyk
1436 * Request for Comments: 2104 IBM
1437 * Category: Informational M. Bellare
1444 * HMAC: Keyed-Hashing for Message Authentication
1447 unsigned char k_ipad
[65]; /* inner padding - key XORd with ipad */
1448 unsigned char k_opad
[65]; /* outer padding - key XORd with opad */
1449 unsigned char tk
[16];
1453 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1458 md5_update(&tctx
, key
, key_len
);
1459 md5_final(tk
, &tctx
);
1465 /* the HMAC_MD5 transform looks like:
1467 * MD5(K XOR opad, MD5(K XOR ipad, text))
1469 * where K is an n byte key
1470 * ipad is the byte 0x36 repeated 64 times
1471 * opad is the byte 0x5c repeated 64 times
1472 * and text is the data being protected */
1474 /* start out by storing key in pads */
1475 memset(k_ipad
, 0, sizeof k_ipad
);
1476 memset(k_opad
, 0, sizeof k_opad
);
1477 memcpy(k_ipad
, key
, key_len
);
1478 memcpy(k_opad
, key
, key_len
);
1480 /* XOR key with ipad and opad values */
1481 for (i
=0; i
<64; i
++) {
1486 /* perform inner MD5 */
1487 md5_init(&context
); /* init context for 1st pass */
1488 md5_update(&context
, k_ipad
, 64); /* start with inner pad */
1489 md5_update(&context
, text
, text_len
); /* then text of datagram */
1490 md5_final(digest
, &context
); /* finish up 1st pass */
1492 /* perform outer MD5 */
1493 md5_init(&context
); /* init context for 2nd pass */
1494 md5_update(&context
, k_opad
, 64); /* start with outer pad */
1495 md5_update(&context
, digest
, 16); /* then results of 1st hash */
1496 md5_final(digest
, &context
); /* finish up 2nd pass */
1499 #endif /* HAVE_MD5 */
1502 makedir(char const *name
)
1505 enum okay rv
= STOP
;
1508 if (!mkdir(name
, 0700))
1512 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1513 S_ISDIR(st
.st_mode
))
1522 cwget(struct cw
*cw
)
1524 enum okay rv
= STOP
;
1527 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1529 if (fchdir(cw
->cw_fd
) == -1) {
1540 cwret(struct cw
*cw
)
1542 enum okay rv
= STOP
;
1545 if (!fchdir(cw
->cw_fd
))
1552 cwrelse(struct cw
*cw
)
1559 #else /* !HAVE_FCHDIR */
1561 cwget(struct cw
*cw
)
1563 enum okay rv
= STOP
;
1566 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1573 cwret(struct cw
*cw
)
1575 enum okay rv
= STOP
;
1578 if (!chdir(cw
->cw_wd
))
1585 cwrelse(struct cw
*cw
)
1591 #endif /* !HAVE_FCHDIR */
1594 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1596 int col_orig
= col
, n
, sz
;
1600 np
= nb
= salloc(mb_cur_max
* strlen(cp
) + col
+1);
1603 if (mb_cur_max
> 1) {
1606 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1608 else if ((n
= wcwidth(wc
)) == -1)
1616 if (sz
== 1 && spacechar(*cp
)) {
1624 if (fill
&& col
!= 0) {
1626 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1627 memset(nb
, ' ', col
);
1629 memset(np
, ' ', col
);
1635 if (cols_decr_used_or_null
!= NULL
)
1636 *cols_decr_used_or_null
-= col_orig
- col
;
1642 makeprint(struct str
const *in
, struct str
*out
)
1644 static int print_all_chars
= -1;
1646 char const *inp
, *maxp
;
1651 if (print_all_chars
== -1)
1652 print_all_chars
= ok_blook(print_all_chars
);
1655 out
->s
= outp
= smalloc(msz
);
1659 if (print_all_chars
) {
1661 memcpy(outp
, inp
, out
->l
);
1665 #ifdef HAVE_C90AMEND1
1666 if (mb_cur_max
> 1) {
1667 char mbb
[MB_LEN_MAX
+ 1];
1671 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1674 while (inp
< maxp
) {
1676 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1682 /* FIXME Why mbtowc() resetting here?
1683 * FIXME what about ISO 2022-JP plus -- those
1684 * FIXME will loose shifts, then!
1685 * FIXME THUS - we'd need special "known points"
1686 * FIXME to do so - say, after a newline!!
1687 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1688 mbtowc(&wc
, NULL
, mb_cur_max
);
1689 wc
= isuni
? 0xFFFD : '?';
1694 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1696 if ((wc
& ~(wchar_t)037) == 0)
1697 wc
= isuni
? 0x2400 | wc
: '?';
1698 else if (wc
== 0177)
1699 wc
= isuni
? 0x2421 : '?';
1701 wc
= isuni
? 0x2426 : '?';
1703 if ((n
= wctomb(mbb
, wc
)) <= 0)
1706 if (out
->l
>= msz
- 1) {
1707 dist
= outp
- out
->s
;
1708 out
->s
= srealloc(out
->s
, msz
+= 32);
1709 outp
= &out
->s
[dist
];
1711 for (i
= 0; i
< n
; ++i
)
1715 #endif /* C90AMEND1 */
1718 while (inp
< maxp
) {
1720 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1727 out
->s
[out
->l
] = '\0';
1732 prstr(char const *s
)
1740 makeprint(&in
, &out
);
1741 rp
= savestrbuf(out
.s
, out
.l
);
1748 prout(char const *s
, size_t sz
, FILE *fp
)
1756 makeprint(&in
, &out
);
1757 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1764 putuc(int u
, int c
, FILE *fp
)
1770 #ifdef HAVE_C90AMEND1
1771 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1772 char mbb
[MB_LEN_MAX
];
1775 if ((n
= wctomb(mbb
, u
)) > 0) {
1777 for (i
= 0; i
< n
; ++i
)
1778 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1783 rv
= (putc('\0', fp
) != EOF
);
1788 rv
= (putc(c
, fp
) != EOF
);
1795 colour_table_create(char const *pager_used
)
1797 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1799 struct colour_table
*ct
;
1802 if (ok_blook(colour_disable
))
1805 /* If pager, check wether it is allowed to use colour */
1806 if (pager_used
!= NULL
) {
1809 if ((u
.cp
= ok_vlook(colour_pagers
)) == NULL
)
1810 u
.ccp
= COLOUR_PAGERS
;
1811 pager
= savestr(u
.cp
);
1813 while ((u
.cp
= n_strsep(&pager
, ',', TRU1
)) != NULL
)
1814 if (strstr(pager_used
, u
.cp
) != NULL
)
1819 /* $TERM is different in that we default to false unless whitelisted */
1821 char *term
, *okterms
;
1823 /* Don't use getenv(), but force copy-in into our own tables.. */
1824 if ((term
= _var_voklook("TERM")) == NULL
)
1826 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1827 okterms
= UNCONST(COLOUR_TERMS
);
1828 okterms
= savestr(okterms
);
1831 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1832 if (!strncmp(u
.cp
, term
, i
))
1838 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1841 enum colourspec cspec
;
1844 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1845 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1846 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1847 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1848 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1851 for (i
= 0; i
< NELEM(map
); ++i
) {
1852 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1853 u
.ccp
= map
[i
].defval
;
1854 u
.cp
= _colour_iso6429(u
.ccp
);
1855 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1856 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1859 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1860 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1862 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1863 u
.ccp
= COLOUR_USER_HEADERS
;
1864 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1865 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1871 colour_put(FILE *fp
, enum colourspec cs
)
1874 if (colour_table
!= NULL
) {
1875 struct str
const *cp
= colour_get(cs
);
1877 fwrite(cp
->s
, cp
->l
, 1, fp
);
1883 colour_put_header(FILE *fp
, char const *name
)
1885 enum colourspec cs
= COLOURSPEC_HEADER
;
1886 struct str
const *uheads
;
1887 char *cp
, *cp_base
, *x
;
1891 if (colour_table
== NULL
)
1893 /* Normal header colours if there are no user headers */
1894 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1895 if (uheads
->s
== NULL
)
1898 /* Iterate over all entries in the *colour-user-headers* list */
1899 cp
= ac_alloc(uheads
->l
+1);
1900 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1902 namelen
= strlen(name
);
1903 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1904 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1905 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1906 cs
= COLOURSPEC_UHEADER
;
1918 colour_reset(FILE *fp
)
1921 if (colour_table
!= NULL
)
1922 fwrite("\033[0m", 4, 1, fp
);
1926 FL
struct str
const *
1927 colour_get(enum colourspec cs
)
1929 struct str
const *rv
= NULL
;
1932 if (colour_table
!= NULL
)
1933 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1938 #endif /* HAVE_COLOUR */
1941 time_current_update(struct time_current
*tc
, bool_t full_update
)
1944 tc
->tc_time
= time(NULL
);
1946 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1947 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1948 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1954 _out_of_memory(void)
1961 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
1968 if ((rv
= malloc(s
)) == NULL
)
1975 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1984 else if ((rv
= realloc(v
, s
)) == NULL
)
1991 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1998 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2004 #else /* !HAVE_DEBUG */
2005 CTA(sizeof(char) == sizeof(ui8_t
));
2007 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2008 # define _HOPE_SET(C) \
2010 union mem_ptr __xl, __xu;\
2011 struct mem_chunk *__xc;\
2012 __xl.p_p = (C).p_p;\
2013 __xc = __xl.p_c - 1;\
2016 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2017 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2018 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2019 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2020 __xu.p_ui8p += __xc->mc_size - 8;\
2021 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2022 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2023 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2024 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2026 # define _HOPE_GET_TRACE(C,BAD) \
2032 # define _HOPE_GET(C,BAD) \
2034 union mem_ptr __xl, __xu;\
2035 struct mem_chunk *__xc;\
2037 __xl.p_p = (C).p_p;\
2039 (C).p_cp = __xl.p_cp;\
2040 __xc = __xl.p_c - 1;\
2043 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2044 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2045 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2046 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2047 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2048 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2049 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2050 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2053 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
2054 __xl.p_p, __i, mdbg_file, mdbg_line);\
2057 __xu.p_ui8p += __xc->mc_size - 8;\
2059 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2060 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2061 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2062 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2063 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2064 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2065 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2066 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2069 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
2070 __xl.p_p, __i, mdbg_file, mdbg_line);\
2073 alert(" ..canary last seen: %s, line %u",\
2074 __xc->mc_file, __xc->mc_line);\
2078 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2085 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2087 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2089 p
.p_c
->mc_prev
= NULL
;
2090 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2091 _mem_list
->mc_prev
= p
.p_c
;
2092 p
.p_c
->mc_file
= mdbg_file
;
2093 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2094 p
.p_c
->mc_isfree
= FAL0
;
2095 p
.p_c
->mc_size
= (ui32_t
)s
;
2096 _mem_list
= p
.p_c
++;
2101 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2104 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2110 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2116 if ((p
.p_p
= v
) == NULL
) {
2117 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2121 _HOPE_GET(p
, isbad
);
2123 if (p
.p_c
->mc_isfree
) {
2124 fprintf(stderr
, "srealloc(): region freed! At %s, line %d\n"
2125 "\tLast seen: %s, line %d\n",
2126 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2130 if (p
.p_c
== _mem_list
)
2131 _mem_list
= p
.p_c
->mc_next
;
2133 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2134 if (p
.p_c
->mc_next
!= NULL
)
2135 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2138 _mem_mcur
-= p
.p_c
->mc_size
;
2142 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2144 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2146 p
.p_c
->mc_prev
= NULL
;
2147 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2148 _mem_list
->mc_prev
= p
.p_c
;
2149 p
.p_c
->mc_file
= mdbg_file
;
2150 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2151 p
.p_c
->mc_isfree
= FAL0
;
2152 p
.p_c
->mc_size
= (ui32_t
)s
;
2153 _mem_list
= p
.p_c
++;
2158 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2161 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2168 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2178 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2180 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2182 memset(p
.p_p
, 0, size
);
2183 p
.p_c
->mc_prev
= NULL
;
2184 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2185 _mem_list
->mc_prev
= p
.p_c
;
2186 p
.p_c
->mc_file
= mdbg_file
;
2187 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2188 p
.p_c
->mc_isfree
= FAL0
;
2189 p
.p_c
->mc_size
= (ui32_t
)size
;
2190 _mem_list
= p
.p_c
++;
2195 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2198 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2204 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2210 if ((p
.p_p
= v
) == NULL
) {
2211 fprintf(stderr
, "sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2215 _HOPE_GET(p
, isbad
);
2217 if (p
.p_c
->mc_isfree
) {
2218 fprintf(stderr
, "sfree(): double-free avoided at %s, line %d\n"
2219 "\tLast seen: %s, line %d\n",
2220 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2224 if (p
.p_c
== _mem_list
)
2225 _mem_list
= p
.p_c
->mc_next
;
2227 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2228 if (p
.p_c
->mc_next
!= NULL
)
2229 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2230 p
.p_c
->mc_isfree
= TRU1
;
2233 _mem_mcur
-= p
.p_c
->mc_size
;
2235 if (options
& OPT_DEBUG
) {
2236 p
.p_c
->mc_next
= _mem_free
;
2248 size_t c
= 0, s
= 0;
2251 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2254 s
+= p
.p_c
->mc_size
;
2255 p
.p_c
= p
.p_c
->mc_next
;
2260 if (options
& OPT_DEBUG
)
2261 fprintf(stderr
, "smemreset(): freed %" ZFMT
" chunks/%" ZFMT
" bytes\n",
2267 c_smemtrace(void *v
)
2269 /* For _HOPE_GET() */
2270 char const * const mdbg_file
= "smemtrace()";
2271 int const mdbg_line
= -1;
2273 union mem_ptr p
, xp
;
2279 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
2285 fprintf(fp
, "Memory statistics:\n"
2286 " Count cur/peek/all: %7" ZFMT
"/%7" ZFMT
"/%10" ZFMT
"\n"
2287 " Bytes cur/peek/all: %7" ZFMT
"/%7" ZFMT
"/%10" ZFMT
"\n\n",
2288 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2290 fprintf(fp
, "Currently allocated memory chunks:\n");
2291 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2292 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2295 _HOPE_GET_TRACE(xp
, isbad
);
2296 fprintf(fp
, "%s%p (%5" ZFMT
" bytes): %s, line %u\n",
2297 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2298 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2302 if (options
& OPT_DEBUG
) {
2303 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2304 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2307 _HOPE_GET_TRACE(xp
, isbad
);
2308 fprintf(fp
, "%s%p (%5" ZFMT
" bytes): %s, line %u\n",
2309 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2310 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2311 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2315 page_or_print(fp
, lines
);
2323 # ifdef _HAVE_MEMCHECK
2325 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2327 union mem_ptr p
, xp
;
2328 bool_t anybad
= FAL0
, isbad
;
2332 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2333 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2336 _HOPE_GET_TRACE(xp
, isbad
);
2340 "! CANARY ERROR: %p (%5" ZFMT
" bytes): %s, line %u\n",
2341 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2342 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2346 if (options
& OPT_DEBUG
) {
2347 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2350 _HOPE_GET_TRACE(xp
, isbad
);
2354 "! CANARY ERROR: %p (%5" ZFMT
" bytes): %s, line %u\n",
2355 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2356 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2363 # endif /* _HAVE_MEMCHECK */
2367 # undef _HOPE_GET_TRACE
2369 #endif /* HAVE_DEBUG */
2371 /* vim:set fenc=utf-8:s-it-mode */