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>
69 struct mem_chunk
*mc_prev
;
70 struct mem_chunk
*mc_next
;
80 struct mem_chunk
*p_c
;
86 /* NYD, memory pool debug */
88 static ui32_t _nyd_curr
, _nyd_level
;
89 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
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
;
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 */
109 static char * _colour_iso6429(char const *wish
);
113 static void _nyd_print(struct nyd_info
*nip
);
118 _colour_iso6429(char const *wish
)
120 char const * const wish_orig
= wish
;
121 char *xwish
, *cp
, cfg
[3] = {0, 0, 0};
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
);
133 /* Iterate over the colour spec */
134 while ((cp
= n_strsep(&xwish
, ',', TRU1
)) != NULL
) {
135 char *y
, *x
= strchr(cp
, '=');
139 "Invalid colour specification \"%s\": >>> %s <<<\n"),
145 /* TODO convert the ft/fg/bg parser into a table-based one! */
146 if (!asccasecmp(cp
, "ft")) {
147 if (!asccasecmp(x
, "bold"))
149 else if (!asccasecmp(x
, "inverse"))
151 else if (!asccasecmp(x
, "underline"))
155 } else if (!asccasecmp(cp
, "fg")) {
158 } else if (!asccasecmp(cp
, "bg")) {
161 if (!asccasecmp(x
, "black"))
163 else if (!asccasecmp(x
, "blue"))
165 else if (!asccasecmp(x
, "green"))
167 else if (!asccasecmp(x
, "red"))
169 else if (!asccasecmp(x
, "brown"))
171 else if (!asccasecmp(x
, "magenta"))
173 else if (!asccasecmp(x
, "cyan"))
175 else if (!asccasecmp(x
, "white"))
183 /* Restore our salloc() buffer, create return value */
184 xwish
= UNCONST(wish
);
185 if (cfg
[0] || cfg
[1] || cfg
[2]) {
199 if (cfg
[0] || cfg
[1])
209 return UNCONST(wish
);
211 #endif /* HAVE_COLOUR */
215 _nyd_print(struct nyd_info
*nip
) /* XXX like SFSYS;no magics;jumps:lvl wrong */
218 union {int i
; size_t z
;} u
;
220 u
.i
= snprintf(buf
, sizeof buf
,
221 "%c [%2" PRIu32
"] %.25s (%.16s:%" PRIu32
")\n",
222 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
223 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
226 if (u
.z
> sizeof buf
)
227 u
.z
= sizeof buf
- 1; /* (Skip \0) */
228 write(STDERR_FILENO
, buf
, u
.z
);
234 panic(char const *format
, ...)
239 fprintf(stderr
, _("Panic: "));
241 va_start(ap
, format
);
242 vfprintf(stderr
, format
, ap
);
248 abort(); /* Was exit(EXIT_ERR); for a while, but no */
252 alert(char const *format
, ...)
257 fprintf(stderr
, _("Panic: "));
259 va_start(ap
, format
);
260 vfprintf(stderr
, format
, ap
);
269 safe_signal(int signum
, sighandler_type handler
)
271 struct sigaction nact
, oact
;
275 nact
.sa_handler
= handler
;
276 sigemptyset(&nact
.sa_mask
);
279 nact
.sa_flags
|= SA_RESTART
;
281 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
290 if (_alls_depth
++ == 0) {
291 sigfillset(&_alls_nset
);
292 sigdelset(&_alls_nset
, SIGABRT
);
294 sigdelset(&_alls_nset
, SIGBUS
);
296 sigdelset(&_alls_nset
, SIGCHLD
);
297 sigdelset(&_alls_nset
, SIGFPE
);
298 sigdelset(&_alls_nset
, SIGILL
);
299 sigdelset(&_alls_nset
, SIGKILL
);
300 sigdelset(&_alls_nset
, SIGSEGV
);
301 sigdelset(&_alls_nset
, SIGSTOP
);
302 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
311 if (--_alls_depth
== 0)
312 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
320 if (_hold_sigdepth
++ == 0) {
321 sigemptyset(&_hold_nset
);
322 sigaddset(&_hold_nset
, SIGHUP
);
323 sigaddset(&_hold_nset
, SIGINT
);
324 sigaddset(&_hold_nset
, SIGQUIT
);
325 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
334 if (--_hold_sigdepth
== 0)
335 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
341 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
343 struct nyd_info
*nip
= _nyd_infos
;
345 if (_nyd_curr
!= NELEM(_nyd_infos
))
351 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
352 nip
->ni_level
= ((act
== 0) ? _nyd_level
353 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
357 _nyd_oncrash(int signo
)
359 struct sigaction xact
;
361 struct nyd_info
*nip
;
364 xact
.sa_handler
= SIG_DFL
;
365 sigemptyset(&xact
.sa_mask
);
367 sigaction(signo
, &xact
, NULL
);
369 fprintf(stderr
, "\n\nNYD: program dying due to signal %d:\n", signo
);
370 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
371 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
373 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
377 sigaddset(&xset
, signo
);
378 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
383 #endif /* HAVE_NYD */
386 touch(struct message
*mp
)
389 mp
->m_flag
|= MTOUCH
;
390 if (!(mp
->m_flag
& MREAD
))
391 mp
->m_flag
|= MREAD
| MSTATUS
;
396 is_dir(char const *name
)
402 if (!stat(name
, &sbuf
))
403 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
409 argcount(char **argv
)
414 for (ap
= argv
; *ap
++ != NULL
;)
417 return (int)PTR2SIZE(ap
- argv
- 1);
427 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
428 s
= scrnheight
- 4; /* XXX no magics */
434 get_pager(char const **env_addon
)
439 cp
= ok_vlook(PAGER
);
440 if (cp
== NULL
|| *cp
== '\0')
443 if (env_addon
!= NULL
) {
445 if (strstr(cp
, "less") != NULL
) {
446 if (getenv("LESS") == NULL
)
447 *env_addon
= "LESS=FRSXi";
448 } else if (strstr(cp
, "lv") != NULL
) {
449 if (getenv("LV") == NULL
)
450 *env_addon
= "LV=-c";
458 paging_seems_sensible(void)
464 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
)
465 rv
= (*cp
!= '\0') ? (size_t)atol(cp
) : (size_t)scrnheight
;
471 page_or_print(FILE *fp
, size_t lines
)
479 if ((rows
= paging_seems_sensible()) != 0 && lines
== 0) {
480 while ((c
= getc(fp
)) != EOF
)
481 if (c
== '\n' && ++lines
> rows
)
486 if (rows
!= 0 && lines
>= rows
)
487 run_command(get_pager(NULL
), 0, fileno(fp
), -1, NULL
, NULL
, NULL
);
489 while ((c
= getc(fp
)) != EOF
)
495 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
501 enum protocol rv
= PROTO_UNKNOWN
;
504 temporary_protocol_ext
= NULL
;
506 if (name
[0] == '%' && name
[1] == ':')
508 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
512 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
513 if (!strncmp(name
, "pop3://", 7)) {
517 fprintf(stderr
, _("No POP3 support compiled in.\n"));
519 } else if (!strncmp(name
, "pop3s://", 8)) {
520 #if defined HAVE_POP3 && defined HAVE_SSL
524 fprintf(stderr
, _("No POP3 support compiled in.\n"));
527 fprintf(stderr
, _("No SSL support compiled in.\n"));
530 } else if (!strncmp(name
, "imap://", 7)) {
534 fprintf(stderr
, _("No IMAP support compiled in.\n"));
536 } else if (!strncmp(name
, "imaps://", 8)) {
537 #if defined HAVE_IMAP && defined HAVE_SSL
541 fprintf(stderr
, _("No IMAP support compiled in.\n"));
544 fprintf(stderr
, _("No SSL support compiled in.\n"));
551 /* TODO This is the de facto maildir code and thus belongs into there!
552 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
553 * TODO or (more likely) in addition to *newfolders*) */
556 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
557 memcpy(np
, name
, sz
+ 1);
558 if (!stat(name
, &st
)) {
559 if (S_ISDIR(st
.st_mode
) &&
560 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
561 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
562 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
565 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
566 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
567 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
568 temporary_protocol_ext
= cp
;
569 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
579 torek_hash(char const *name
)
581 /* Chris Torek's hash.
582 * NOTE: need to change *at least* create-okey-map.pl when changing the
587 while (*name
!= '\0') {
596 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
603 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
604 if ((g
= h
& 0xf0000000) != 0) {
616 static ui32_t
const primes
[] = {
617 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
618 131071, 262139, 524287, 1048573, 2097143, 4194301,
619 8388593, 16777213, 33554393, 67108859, 134217689,
620 268435399, 536870909, 1073741789, 2147483647
623 ui32_t mprime
= 7, cutlim
;
627 cutlim
= (n
< 65536 ? n
<< 2 : (n
< 262144 ? n
<< 1 : n
));
629 for (i
= 0; i
< NELEM(primes
); i
++)
630 if ((mprime
= primes
[i
]) >= cutlim
)
632 if (i
== NELEM(primes
) && mprime
< n
)
639 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
647 if ((c
= *xs
& 0xFF) == '\0')
653 switch ((c
= *xs
& 0xFF)) {
655 case 'a': c
= '\a'; break;
656 case 'b': c
= '\b'; break;
657 case 'c': c
= PROMPT_STOP
; break;
658 case 'f': c
= '\f'; break;
659 case 'n': c
= '\n'; break;
660 case 'r': c
= '\r'; break;
661 case 't': c
= '\t'; break;
662 case 'v': c
= '\v'; break;
664 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
669 /* S-nail extension for nice (get)prompt(()) support */
674 if (use_nail_extensions
) {
676 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
677 case '?': c
= exec_last_comm_error
? '1' : '0'; break;
678 case '$': c
= PROMPT_DOLLAR
; break;
679 case '@': c
= PROMPT_AT
; break;
685 /* A sole <backslash> at EOS is treated as-is! */
699 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
701 static char buf
[PROMPT_BUFFER_SIZE
];
704 char const *ccp_base
, *ccp
;
705 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
710 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0')
712 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
714 dfmaxlen
= 0; /* keep CC happy */
718 NATCH_CHAR( cclen
= cclen_base
; )
719 maxlen
= sizeof(buf
) -1;
727 #ifdef HAVE_NATCH_CHAR
728 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
737 } else if ((l
= c
) > 1) {
747 if ((c
= expand_shell_escape(&ccp
, TRU1
)) > 0) {
753 if (c
== 0 || c
== PROMPT_STOP
)
757 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
760 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
780 nodename(int mayoverride
)
782 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
788 struct addrinfo hints
, *res
;
790 struct hostent
*hent
;
795 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
797 } else if ((hn
= sys_hostname
) == NULL
) {
802 memset(&hints
, 0, sizeof hints
);
803 hints
.ai_family
= AF_UNSPEC
;
804 hints
.ai_socktype
= SOCK_DGRAM
; /* (dummy) */
805 hints
.ai_flags
= AI_CANONNAME
;
806 if (getaddrinfo(hn
, "0", &hints
, &res
) == 0) {
807 if (res
->ai_canonname
!= NULL
) {
808 size_t l
= strlen(res
->ai_canonname
) +1;
810 memcpy(hn
, res
->ai_canonname
, l
);
815 hent
= gethostbyname(hn
);
820 sys_hostname
= sstrdup(hn
);
821 #if defined HAVE_SOCKETS && defined HAVE_IPV6
822 if (hn
!= ut
.nodename
)
828 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
830 hostname
= sstrdup(hn
);
836 getrandstring(size_t length
)
838 static unsigned char nodedigest
[16];
852 data
= ac_alloc(length
);
854 if ((fd
= open("/dev/urandom", O_RDONLY
)) == -1 ||
855 length
!= (size_t)read(fd
, data
, length
)) {
862 md5_update(&ctx
, (unsigned char*)cp
, strlen(cp
));
863 md5_final(nodedigest
, &ctx
);
865 /* In that case it's only used for boundaries and Message-Id:s so that
866 * srand(3) should suffice */
868 for (i
= 0; i
< sizeof(nodedigest
); ++i
)
869 nodedigest
[i
] = (unsigned char)(cp
[i
% j
] ^ rand());
872 for (i
= 0; i
< length
; i
++)
873 data
[i
] = (char)((int)(255 * (rand() / (RAND_MAX
+ 1.0))) ^
874 nodedigest
[i
% sizeof nodedigest
]);
879 b64_encode_buf(&b64
, data
, length
, B64_SALLOC
);
881 assert(length
< b64
.l
);
882 b64
.s
[length
] = '\0';
884 /* Base64 includes + and /, replace them with _ and - */
885 for (data
= b64
.s
; length
-- > 0; ++data
)
888 else if (*data
== '/')
895 makedir(char const *name
)
901 if (!mkdir(name
, 0700))
905 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
920 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
922 if (fchdir(cw
->cw_fd
) == -1) {
938 if (!fchdir(cw
->cw_fd
))
945 cwrelse(struct cw
*cw
)
952 #else /* !HAVE_FCHDIR */
959 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
971 if (!chdir(cw
->cw_wd
))
978 cwrelse(struct cw
*cw
)
984 #endif /* !HAVE_FCHDIR */
987 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
992 #ifdef HAVE_NATCH_CHAR
993 maxlen
= MIN(maxlen
, blen
);
994 for (rv
= 0; maxlen
> 0;) {
995 int ml
= mblen(buf
, maxlen
);
1005 rv
= MIN(blen
, maxlen
);
1012 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1014 NATCH_CHAR( struct bidi_info bi
; )
1015 size_t rv
NATCH_CHAR( COMMA i
);
1022 #ifdef HAVE_NATCH_CHAR
1023 bidi_info_create(&bi
);
1024 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1029 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1034 if ((i
= bi
.bi_start
.l
) > 0) {
1035 memcpy(store
, bi
.bi_start
.s
, i
);
1041 while (maxlen
> 0) {
1042 int ml
= mblen(buf
, blen
);
1047 if (UICMP(z
, maxlen
, <, ml
))
1052 memcpy(store
, buf
, ml
);
1059 if ((i
= bi
.bi_end
.l
) > 0) {
1060 memcpy(store
, bi
.bi_end
.s
, i
);
1068 rv
= MIN(blen
, maxlen
);
1069 memcpy(store
, buf
, rv
);
1078 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1080 NATCH_CHAR( struct bidi_info bi
; )
1081 int col_orig
= col
, n
, sz
;
1082 bool_t isbidi
, isuni
, istab
, isrepl
;
1086 /* Bidi only on request and when there is 8-bit data */
1087 isbidi
= isuni
= FAL0
;
1088 #ifdef HAVE_NATCH_CHAR
1089 isuni
= ((options
& OPT_UNICODE
) != 0);
1090 bidi_info_create(&bi
);
1091 if (bi
.bi_start
.l
== 0)
1093 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1096 if ((size_t)col
>= bi
.bi_pad
)
1103 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1105 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1108 #ifdef HAVE_NATCH_CHAR
1110 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1111 np
+= bi
.bi_start
.l
;
1115 while (*cp
!= '\0') {
1117 #ifdef HAVE_C90AMEND1
1118 if (mb_cur_max
> 1) {
1123 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1125 else if (wc
== L
'\t') {
1126 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1129 } else if (iswprint(wc
)) {
1130 # ifndef HAVE_WCWIDTH
1131 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1133 if ((n
= wcwidth(wc
)) == -1)
1143 istab
= (*cp
== '\t');
1144 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1153 np
[0] = (char)0xEFu
;
1154 np
[1] = (char)0xBFu
;
1155 np
[2] = (char)0xBDu
;
1160 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1168 if (fill
&& col
!= 0) {
1170 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1171 memset(nb
, ' ', col
);
1173 memset(np
, ' ', col
);
1178 #ifdef HAVE_NATCH_CHAR
1180 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1186 if (cols_decr_used_or_null
!= NULL
)
1187 *cols_decr_used_or_null
-= col_orig
- col
;
1193 makeprint(struct str
const *in
, struct str
*out
)
1195 static int print_all_chars
= -1;
1197 char const *inp
, *maxp
;
1202 if (print_all_chars
== -1)
1203 print_all_chars
= ok_blook(print_all_chars
);
1205 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
);
1209 if (print_all_chars
) {
1211 memcpy(outp
, inp
, out
->l
);
1215 #ifdef HAVE_NATCH_CHAR
1216 if (mb_cur_max
> 1) {
1217 char mbb
[MB_LEN_MAX
+ 1];
1220 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1223 while (inp
< maxp
) {
1225 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1231 /* FIXME Why mbtowc() resetting here?
1232 * FIXME what about ISO 2022-JP plus -- those
1233 * FIXME will loose shifts, then!
1234 * FIXME THUS - we'd need special "known points"
1235 * FIXME to do so - say, after a newline!!
1236 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1237 mbtowc(&wc
, NULL
, mb_cur_max
);
1238 wc
= isuni
? 0xFFFD : '?';
1243 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1245 if ((wc
& ~(wchar_t)037) == 0)
1246 wc
= isuni
? 0x2400 | wc
: '?';
1247 else if (wc
== 0177)
1248 wc
= isuni
? 0x2421 : '?';
1250 wc
= isuni
? 0x2426 : '?';
1252 if ((n
= wctomb(mbb
, wc
)) <= 0)
1255 assert(out
->l
< msz
);
1256 for (i
= 0; i
< n
; ++i
)
1260 #endif /* NATCH_CHAR */
1263 while (inp
< maxp
) {
1265 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1272 out
->s
[out
->l
] = '\0';
1277 prstr(char const *s
)
1285 makeprint(&in
, &out
);
1286 rp
= savestrbuf(out
.s
, out
.l
);
1293 prout(char const *s
, size_t sz
, FILE *fp
)
1301 makeprint(&in
, &out
);
1302 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1309 putuc(int u
, int c
, FILE *fp
)
1315 #ifdef HAVE_NATCH_CHAR
1316 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1317 char mbb
[MB_LEN_MAX
];
1320 if ((n
= wctomb(mbb
, u
)) > 0) {
1322 for (i
= 0; i
< n
; ++i
)
1323 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1328 rv
= (putc('\0', fp
) != EOF
);
1333 rv
= (putc(c
, fp
) != EOF
);
1339 bidi_info_needed(char const *bdat
, size_t blen
)
1344 #ifdef HAVE_NATCH_CHAR
1345 if (options
& OPT_UNICODE
)
1346 for (; blen
> 0; ++bdat
, --blen
) {
1347 if ((ui8_t
)*bdat
> 0x7F) {
1348 /* TODO Checking for BIDI character: use S-CText fromutf8
1349 * TODO plus isrighttoleft (or whatever there will be)! */
1350 ui32_t c
, x
= (ui8_t
)*bdat
;
1352 if ((x
& 0xE0) == 0xC0) {
1357 } else if ((x
& 0xF0) == 0xE0) {
1381 /* (Very very fuzzy, awaiting S-CText for good) */
1382 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1383 (c
>= 0xFB1D && c
<= 0xFEFC) ||
1384 (c
>= 0x10800 && c
<= 0x10C48) ||
1385 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1391 #endif /* HAVE_NATCH_CHAR */
1397 bidi_info_create(struct bidi_info
*bip
)
1399 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1400 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1401 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1402 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1403 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1404 NATCH_CHAR( char const *hb
; )
1407 memset(bip
, 0, sizeof *bip
);
1408 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1410 #ifdef HAVE_NATCH_CHAR
1411 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1417 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1423 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1424 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1427 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1435 colour_table_create(bool_t pager_used
)
1437 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1439 struct colour_table
*ct
;
1442 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1445 char *term
, *okterms
;
1447 /* Don't use getenv(), but force copy-in into our own tables.. */
1448 if ((term
= _var_voklook("TERM")) == NULL
)
1450 /* terminfo rocks: if we find "color", assume it's right */
1451 if (strstr(term
, "color") != NULL
)
1453 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1454 okterms
= UNCONST(COLOUR_TERMS
);
1455 okterms
= savestr(okterms
);
1458 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1459 if (!strncmp(u
.cp
, term
, i
))
1465 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1468 enum colourspec cspec
;
1471 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1472 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1473 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1474 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1475 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1478 for (i
= 0; i
< NELEM(map
); ++i
) {
1479 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1480 u
.ccp
= map
[i
].defval
;
1481 u
.cp
= _colour_iso6429(u
.ccp
);
1482 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1483 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1486 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1487 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1489 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1490 u
.ccp
= COLOUR_USER_HEADERS
;
1491 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1492 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1498 colour_put(FILE *fp
, enum colourspec cs
)
1501 if (colour_table
!= NULL
) {
1502 struct str
const *cp
= colour_get(cs
);
1504 fwrite(cp
->s
, cp
->l
, 1, fp
);
1510 colour_put_header(FILE *fp
, char const *name
)
1512 enum colourspec cs
= COLOURSPEC_HEADER
;
1513 struct str
const *uheads
;
1514 char *cp
, *cp_base
, *x
;
1518 if (colour_table
== NULL
)
1520 /* Normal header colours if there are no user headers */
1521 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1522 if (uheads
->s
== NULL
)
1525 /* Iterate over all entries in the *colour-user-headers* list */
1526 cp
= ac_alloc(uheads
->l
+1);
1527 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1529 namelen
= strlen(name
);
1530 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1531 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1532 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1533 cs
= COLOURSPEC_UHEADER
;
1545 colour_reset(FILE *fp
)
1548 if (colour_table
!= NULL
)
1549 fwrite("\033[0m", 4, 1, fp
);
1553 FL
struct str
const *
1554 colour_get(enum colourspec cs
)
1556 struct str
const *rv
= NULL
;
1559 if (colour_table
!= NULL
)
1560 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1565 #endif /* HAVE_COLOUR */
1568 time_current_update(struct time_current
*tc
, bool_t full_update
)
1571 tc
->tc_time
= time(NULL
);
1573 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1574 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1575 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1581 _out_of_memory(void)
1588 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
1595 if ((rv
= malloc(s
)) == NULL
)
1602 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1611 else if ((rv
= realloc(v
, s
)) == NULL
)
1618 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1625 if ((rv
= calloc(nmemb
, size
)) == NULL
)
1631 #else /* !HAVE_DEBUG */
1632 CTA(sizeof(char) == sizeof(ui8_t
));
1634 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1635 # define _HOPE_SET(C) \
1637 union mem_ptr __xl, __xu;\
1638 struct mem_chunk *__xc;\
1639 __xl.p_p = (C).p_p;\
1640 __xc = __xl.p_c - 1;\
1643 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1644 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1645 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1646 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1647 __xu.p_ui8p += __xc->mc_size - 8;\
1648 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1649 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1650 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1651 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1653 # define _HOPE_GET_TRACE(C,BAD) \
1659 # define _HOPE_GET(C,BAD) \
1661 union mem_ptr __xl, __xu;\
1662 struct mem_chunk *__xc;\
1664 __xl.p_p = (C).p_p;\
1666 (C).p_cp = __xl.p_cp;\
1667 __xc = __xl.p_c - 1;\
1670 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1671 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1672 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1673 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1674 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1675 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1676 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1677 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1680 alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
1681 __xl.p_p, __i, mdbg_file, mdbg_line);\
1684 __xu.p_ui8p += __xc->mc_size - 8;\
1686 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1687 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1688 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1689 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1690 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1691 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1692 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1693 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1696 alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
1697 __xl.p_p, __i, mdbg_file, mdbg_line);\
1700 alert(" ..canary last seen: %s, line %" PRIu16 "",\
1701 __xc->mc_file, __xc->mc_line);\
1705 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
1712 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
1714 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
1716 p
.p_c
->mc_prev
= NULL
;
1717 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
1718 _mem_list
->mc_prev
= p
.p_c
;
1719 p
.p_c
->mc_file
= mdbg_file
;
1720 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
1721 p
.p_c
->mc_isfree
= FAL0
;
1722 p
.p_c
->mc_size
= (ui32_t
)s
;
1723 _mem_list
= p
.p_c
++;
1728 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
1731 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
1737 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
1743 if ((p
.p_p
= v
) == NULL
) {
1744 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
1748 _HOPE_GET(p
, isbad
);
1750 if (p
.p_c
->mc_isfree
) {
1751 fprintf(stderr
, "srealloc(): region freed! At %s, line %d\n"
1752 "\tLast seen: %s, line %" PRIu16
"\n",
1753 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
1757 if (p
.p_c
== _mem_list
)
1758 _mem_list
= p
.p_c
->mc_next
;
1760 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
1761 if (p
.p_c
->mc_next
!= NULL
)
1762 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
1765 _mem_mcur
-= p
.p_c
->mc_size
;
1769 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
1771 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
1773 p
.p_c
->mc_prev
= NULL
;
1774 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
1775 _mem_list
->mc_prev
= p
.p_c
;
1776 p
.p_c
->mc_file
= mdbg_file
;
1777 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
1778 p
.p_c
->mc_isfree
= FAL0
;
1779 p
.p_c
->mc_size
= (ui32_t
)s
;
1780 _mem_list
= p
.p_c
++;
1785 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
1788 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
1795 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
1805 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
1807 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
1809 memset(p
.p_p
, 0, size
);
1810 p
.p_c
->mc_prev
= NULL
;
1811 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
1812 _mem_list
->mc_prev
= p
.p_c
;
1813 p
.p_c
->mc_file
= mdbg_file
;
1814 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
1815 p
.p_c
->mc_isfree
= FAL0
;
1816 p
.p_c
->mc_size
= (ui32_t
)size
;
1817 _mem_list
= p
.p_c
++;
1822 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
1825 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
1831 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
1837 if ((p
.p_p
= v
) == NULL
) {
1838 fprintf(stderr
, "sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
1842 _HOPE_GET(p
, isbad
);
1844 if (p
.p_c
->mc_isfree
) {
1845 fprintf(stderr
, "sfree(): double-free avoided at %s, line %d\n"
1846 "\tLast seen: %s, line %" PRIu16
"\n",
1847 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
1851 if (p
.p_c
== _mem_list
)
1852 _mem_list
= p
.p_c
->mc_next
;
1854 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
1855 if (p
.p_c
->mc_next
!= NULL
)
1856 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
1857 p
.p_c
->mc_isfree
= TRU1
;
1860 _mem_mcur
-= p
.p_c
->mc_size
;
1862 if (options
& OPT_DEBUG
) {
1863 p
.p_c
->mc_next
= _mem_free
;
1875 size_t c
= 0, s
= 0;
1878 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
1881 s
+= p
.p_c
->mc_size
;
1882 p
.p_c
= p
.p_c
->mc_next
;
1887 if (options
& OPT_DEBUG
)
1888 fprintf(stderr
, "smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n",
1894 c_smemtrace(void *v
)
1896 /* For _HOPE_GET() */
1897 char const * const mdbg_file
= "smemtrace()";
1898 int const mdbg_line
= -1;
1900 union mem_ptr p
, xp
;
1906 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
1912 fprintf(fp
, "Memory statistics:\n"
1913 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
1914 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
1915 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
1917 fprintf(fp
, "Currently allocated memory chunks:\n");
1918 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
1919 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
1922 _HOPE_GET_TRACE(xp
, isbad
);
1923 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
1924 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
1925 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
1929 if (options
& OPT_DEBUG
) {
1930 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
1931 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
1934 _HOPE_GET_TRACE(xp
, isbad
);
1935 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
1936 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
1937 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
1938 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
1942 page_or_print(fp
, lines
);
1950 # ifdef _HAVE_MEMCHECK
1952 _smemcheck(char const *mdbg_file
, int mdbg_line
)
1954 union mem_ptr p
, xp
;
1955 bool_t anybad
= FAL0
, isbad
;
1959 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
1960 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
1963 _HOPE_GET_TRACE(xp
, isbad
);
1967 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
1968 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
1969 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
1973 if (options
& OPT_DEBUG
) {
1974 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
1977 _HOPE_GET_TRACE(xp
, isbad
);
1981 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
1982 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
1983 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
1990 # endif /* _HAVE_MEMCHECK */
1994 # undef _HOPE_GET_TRACE
1996 #endif /* HAVE_DEBUG */