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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
42 #include <sys/utsname.h>
48 # ifdef HAVE_GETADDRINFO
49 # include <sys/socket.h>
55 #ifndef HAVE_POSIX_RANDOM
63 ui8_t b8
[sizeof(struct rand_arc4
)];
64 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
79 struct mem_chunk
*mc_prev
;
80 struct mem_chunk
*mc_next
;
90 struct mem_chunk
*p_c
;
98 struct err_node
*en_next
;
103 #ifndef HAVE_POSIX_RANDOM
104 static union rand_state
*_rand
;
107 /* {hold,rele}_all_sigs() */
108 static size_t _alls_depth
;
109 static sigset_t _alls_nset
, _alls_oset
;
111 /* {hold,rele}_sigs() */
112 static size_t _hold_sigdepth
;
113 static sigset_t _hold_nset
, _hold_oset
;
115 /* NYD, memory pool debug */
117 static ui32_t _nyd_curr
, _nyd_level
;
118 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
121 /* Error ring, for `errors' */
123 static struct err_node
*_err_head
, *_err_tail
;
124 static size_t _err_cnt
, _err_cnt_noted
;
128 static size_t _mem_aall
, _mem_acur
, _mem_amax
,
129 _mem_mall
, _mem_mcur
, _mem_mmax
;
131 static struct mem_chunk
*_mem_list
, *_mem_free
;
134 /* Our ARC4 random generator with its completely unacademical pseudo
135 * initialization (shall /dev/urandom fail) */
136 #ifndef HAVE_POSIX_RANDOM
137 static void _rand_init(void);
138 static ui32_t
_rand_weak(ui32_t seed
);
139 SINLINE ui8_t
_rand_get8(void);
142 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
144 static char * _colour_iso6429(char const *wish
);
148 static void _nyd_print(int fd
, struct nyd_info
*nip
);
151 #ifndef HAVE_POSIX_RANDOM
155 # ifdef HAVE_CLOCK_GETTIME
160 union {int fd
; size_t i
;} u
;
164 _rand
= smalloc(sizeof *_rand
);
166 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
167 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
174 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
175 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
178 # ifdef HAVE_CLOCK_GETTIME
179 clock_gettime(CLOCK_REALTIME
, &ts
);
180 t
= (ui32_t
)ts
.tv_nsec
;
182 gettimeofday(&ts
, NULL
);
183 t
= (ui32_t
)ts
.tv_usec
;
186 t
= (t
>> 16) | (t
<< 16);
187 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
188 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
189 if (rnd
== 7 || rnd
== 17)
190 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
191 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
192 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
193 seed
^= _rand_weak(_rand
->b32
[k
]);
195 seed
^= nextprime(seed
);
199 for (u
.i
= 11 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
206 _rand_weak(ui32_t seed
)
208 /* From "Random number generators: good ones are hard to find",
209 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
210 * October 1988, p. 1195.
211 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
218 seed
= (seed
* 16807) - (hi
* 2836);
219 if ((si32_t
)seed
< 0)
229 si
= _rand
->a
._dat
[++_rand
->a
._i
];
230 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
231 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
232 _rand
->a
._dat
[_rand
->a
._j
] = si
;
233 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
235 #endif /* HAVE_POSIX_RANDOM */
239 _colour_iso6429(char const *wish
)
245 {"bold", '1'}, {"underline", '4'}, {"inverse", '7'}
247 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
248 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
250 char const * const wish_orig
= wish
;
251 char *xwish
, *cp
, cfg
[3] = {0, 0, 0};
254 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
255 * value, ensure we have enough room for that */
257 size_t i
= strlen(wish
) +1;
258 xwish
= salloc(MAX(i
, sizeof("\033[1;30;40m")));
259 memcpy(xwish
, wish
, i
);
263 /* Iterate over the colour spec */
264 while ((cp
= n_strsep(&xwish
, ',', TRU1
)) != NULL
) {
265 char *y
, *x
= strchr(cp
, '=');
268 n_err(_("Invalid colour specification \"%s\": %s\n"), wish_orig
, cp
);
273 if (!asccasecmp(cp
, "ft")) {
274 for (idp
= fta
;; ++idp
)
275 if (idp
== fta
+ NELEM(fta
))
277 else if (!asccasecmp(x
, idp
->id_name
)) {
278 cfg
[0] = idp
->id_modc
;
281 } else if (!asccasecmp(cp
, "fg")) {
284 } else if (!asccasecmp(cp
, "bg")) {
287 for (idp
= ca
;; ++idp
)
288 if (idp
== ca
+ NELEM(ca
))
290 else if (!asccasecmp(x
, idp
->id_name
)) {
298 /* Restore our salloc() buffer, create return value */
299 xwish
= UNCONST(wish
);
300 if (cfg
[0] || cfg
[1] || cfg
[2]) {
314 if (cfg
[0] || cfg
[1])
324 return UNCONST(wish
);
326 #endif /* HAVE_COLOUR */
330 _nyd_print(int fd
, struct nyd_info
*nip
)
333 union {int i
; size_t z
;} u
;
335 u
.i
= snprintf(buf
, sizeof buf
,
336 "%c [%2" PRIu32
"] %.25s (%.16s:%" PRIu32
")\n",
337 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
338 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
341 if (u
.z
> sizeof buf
)
342 u
.z
= sizeof buf
- 1; /* (Skip \0) */
352 kill(getpid(), signo
);
357 safe_signal(int signum
, sighandler_type handler
)
359 struct sigaction nact
, oact
;
363 nact
.sa_handler
= handler
;
364 sigemptyset(&nact
.sa_mask
);
367 nact
.sa_flags
|= SA_RESTART
;
369 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
378 if (_alls_depth
++ == 0) {
379 sigfillset(&_alls_nset
);
380 sigdelset(&_alls_nset
, SIGABRT
);
382 sigdelset(&_alls_nset
, SIGBUS
);
384 sigdelset(&_alls_nset
, SIGCHLD
);
385 sigdelset(&_alls_nset
, SIGFPE
);
386 sigdelset(&_alls_nset
, SIGILL
);
387 sigdelset(&_alls_nset
, SIGKILL
);
388 sigdelset(&_alls_nset
, SIGSEGV
);
389 sigdelset(&_alls_nset
, SIGSTOP
);
390 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
399 if (--_alls_depth
== 0)
400 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
408 if (_hold_sigdepth
++ == 0) {
409 sigemptyset(&_hold_nset
);
410 sigaddset(&_hold_nset
, SIGHUP
);
411 sigaddset(&_hold_nset
, SIGINT
);
412 sigaddset(&_hold_nset
, SIGQUIT
);
413 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
422 if (--_hold_sigdepth
== 0)
423 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
429 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
431 struct nyd_info
*nip
= _nyd_infos
;
433 if (_nyd_curr
!= NELEM(_nyd_infos
))
439 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
440 nip
->ni_level
= ((act
== 0) ? _nyd_level
441 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
445 _nyd_oncrash(int signo
)
447 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
448 struct sigaction xact
;
452 struct nyd_info
*nip
;
454 LCTA(sizeof("./") -1 + sizeof(UAGENT
) -1 + sizeof(".dat") < PATH_MAX
);
456 xact
.sa_handler
= SIG_DFL
;
457 sigemptyset(&xact
.sa_mask
);
459 sigaction(signo
, &xact
, NULL
);
462 fnl
= sizeof(UAGENT
) -1;
464 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
465 (cp
= pathbuf
)[0] = '.';
468 memcpy(cp
= pathbuf
, tempdir
, i
);
469 cp
[i
++] = '/'; /* xxx pathsep */
470 memcpy(cp
+= i
, UAGENT
, fnl
);
472 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
473 fnl
= i
+ sizeof(".dat") -1;
475 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
479 # define _X(X) (X), sizeof(X) -1
480 write(fd
, _X("\n\nNYD: program dying due to signal "));
482 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
486 *--cp
= "0123456789"[i
% 10];
489 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
491 write(fd
, _X(":\n"));
493 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
494 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
495 _nyd_print(fd
, nip
++);
496 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
497 _nyd_print(fd
, nip
++);
499 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
501 if (fd
!= STDERR_FILENO
) {
502 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
503 write(STDERR_FILENO
, pathbuf
, fnl
);
504 write(STDERR_FILENO
, _X("\n"));
511 sigaddset(&xset
, signo
);
512 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
517 #endif /* HAVE_NYD */
520 touch(struct message
*mp
)
523 mp
->m_flag
|= MTOUCH
;
524 if (!(mp
->m_flag
& MREAD
))
525 mp
->m_flag
|= MREAD
| MSTATUS
;
530 is_dir(char const *name
)
537 if (!stat(name
, &sbuf
)) {
538 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
540 } else if (errno
!= EINTR
)
547 argcount(char **argv
)
552 for (ap
= argv
; *ap
++ != NULL
;)
555 return (int)PTR2SIZE(ap
- argv
- 1);
565 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
566 s
= scrnheight
- 2; /* XXX no magics */
572 get_pager(char const **env_addon
)
577 cp
= ok_vlook(PAGER
);
578 if (cp
== NULL
|| *cp
== '\0')
581 if (env_addon
!= NULL
) {
583 if (strstr(cp
, "less") != NULL
) {
584 if (!env_blook("LESS", TRU1
))
585 *env_addon
= "LESS=FRSXi";
586 } else if (strstr(cp
, "lv") != NULL
) {
587 if (!env_blook("LV", TRU1
))
588 *env_addon
= "LV=-c";
596 page_or_print(FILE *fp
, size_t lines
)
604 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
) {
606 union {sl_i sli
; size_t rows
;} u
;
608 u
.sli
= strtol(cp
, &eptr
, 0);
609 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
610 ? (size_t)u
.sli
: (size_t)scrnheight
;
612 if (u
.rows
> 0 && lines
== 0) {
613 while ((c
= getc(fp
)) != EOF
)
614 if (c
== '\n' && ++lines
>= u
.rows
)
619 if (lines
>= u
.rows
) {
620 run_command(get_pager(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
621 NULL
, NULL
, NULL
, NULL
);
626 while ((c
= getc(fp
)) != EOF
)
633 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
639 enum protocol rv
= PROTO_UNKNOWN
;
642 temporary_protocol_ext
= NULL
;
644 if (name
[0] == '%' && name
[1] == ':')
646 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
650 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
651 if (!strncmp(name
, "pop3://", 7)) {
655 n_err(_("No POP3 support compiled in\n"));
657 } else if (!strncmp(name
, "pop3s://", 8)) {
658 #if defined HAVE_POP3 && defined HAVE_SSL
662 n_err(_("No POP3 support compiled in\n"));
665 n_err(_("No SSL support compiled in\n"));
668 } else if (!strncmp(name
, "imap://", 7)) {
672 fprintf(stderr
, _("No IMAP support compiled in.\n"));
674 } else if (!strncmp(name
, "imaps://", 8)) {
675 #if defined HAVE_IMAP && defined HAVE_SSL
679 fprintf(stderr
, _("No IMAP support compiled in.\n"));
682 fprintf(stderr
, _("No SSL support compiled in.\n"));
689 /* TODO This is the de facto maildir code and thus belongs into there!
690 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
691 * TODO or (more likely) in addition to *newfolders*) */
694 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
695 memcpy(np
, name
, sz
+ 1);
696 if (!stat(name
, &st
)) {
697 if (S_ISDIR(st
.st_mode
) &&
698 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
699 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
700 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
703 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
704 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
705 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
706 temporary_protocol_ext
= cp
;
707 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
717 torek_hash(char const *name
)
719 /* Chris Torek's hash.
720 * NOTE: need to change *at least* create-okey-map.pl when changing the
725 while (*name
!= '\0') {
734 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
741 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
742 if ((g
= h
& 0xf0000000) != 0) {
754 static ui32_t
const primes
[] = {
755 5, 11, 23, 47, 97, 157, 283,
756 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
757 131071, 262139, 524287, 1048573, 2097143, 4194301,
758 8388593, 16777213, 33554393, 67108859, 134217689,
759 268435399, 536870909, 1073741789, 2147483647
765 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
766 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
767 : NELEM(primes
) / 2));
769 if ((mprime
= primes
[i
]) > n
)
771 while (++i
< NELEM(primes
));
772 if (i
== NELEM(primes
) && mprime
< n
)
779 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
787 if ((c
= *xs
& 0xFF) == '\0')
793 switch ((c
= *xs
& 0xFF)) {
795 case 'a': c
= '\a'; break;
796 case 'b': c
= '\b'; break;
797 case 'c': c
= PROMPT_STOP
; break;
798 case 'f': c
= '\f'; break;
799 case 'n': c
= '\n'; break;
800 case 'r': c
= '\r'; break;
801 case 't': c
= '\t'; break;
802 case 'v': c
= '\v'; break;
804 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
809 /* S-nail extension for nice (get)prompt(()) support */
814 if (use_nail_extensions
) {
816 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
817 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
818 case '$': c
= PROMPT_DOLLAR
; break;
819 case '@': c
= PROMPT_AT
; break;
825 /* A sole <backslash> at EOS is treated as-is! */
839 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
841 static char buf
[PROMPT_BUFFER_SIZE
];
844 char const *ccp_base
, *ccp
;
845 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
846 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
849 /* No other place to place this */
851 if (options
& OPT_INTERACTIVE
) {
852 if (!(pstate
& PS_ERRORS_NOTED
) && _err_head
!= NULL
) {
853 pstate
|= PS_ERRORS_NOTED
;
854 fprintf(stderr
, _("There are new messages in the error message ring "
855 "(denoted by \"#ERR#\")\n"
856 " The `errors' command manages this message ring\n"));
859 if ((trigger
= (_err_cnt_noted
!= _err_cnt
)))
860 _err_cnt_noted
= _err_cnt
;
866 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
876 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
878 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
880 dfmaxlen
= 0; /* keep CC happy */
884 NATCH_CHAR( cclen
= cclen_base
; )
885 maxlen
= sizeof(buf
) -1;
893 #ifdef HAVE_NATCH_CHAR
894 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
903 } else if ((l
= c
) > 1) {
913 if ((c
= expand_shell_escape(&ccp
, TRU1
)) > 0) {
919 if (c
== 0 || c
== PROMPT_STOP
)
923 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
926 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
946 nodename(int mayoverride
)
948 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
953 # ifdef HAVE_GETADDRINFO
954 struct addrinfo hints
, *res
;
956 struct hostent
*hent
;
961 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
963 } else if ((hn
= sys_hostname
) == NULL
) {
967 # ifdef HAVE_GETADDRINFO
968 memset(&hints
, 0, sizeof hints
);
969 hints
.ai_family
= AF_UNSPEC
;
970 hints
.ai_flags
= AI_CANONNAME
;
971 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
972 if (res
->ai_canonname
!= NULL
) {
973 size_t l
= strlen(res
->ai_canonname
) +1;
976 memcpy(hn
, res
->ai_canonname
, l
);
981 hent
= gethostbyname(hn
);
986 sys_hostname
= sstrdup(hn
);
987 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
988 if (hn
!= ut
.nodename
)
994 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
996 hostname
= sstrdup(hn
);
1002 getrandstring(size_t length
)
1009 #ifndef HAVE_POSIX_RANDOM
1014 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1015 * with PAD stripped is still longer than what the user requests, easy way */
1016 data
= ac_alloc(i
= length
+ 3);
1018 #ifndef HAVE_POSIX_RANDOM
1020 data
[i
] = (char)_rand_get8();
1025 union {ui32_t i4
; char c
[4];} r
;
1028 r
.i4
= (ui32_t
)arc4random();
1029 switch ((j
= i
& 3)) {
1030 case 0: cp
[3] = r
.c
[3]; j
= 4;
1031 case 3: cp
[2] = r
.c
[2];
1032 case 2: cp
[1] = r
.c
[1];
1033 default: cp
[0] = r
.c
[0]; break;
1041 b64_encode_buf(&b64
, data
, length
+ 3,
1042 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1045 assert(b64
.l
>= length
);
1046 b64
.s
[length
] = '\0';
1052 makedir(char const *name
)
1055 enum okay rv
= STOP
;
1058 if (!mkdir(name
, 0700))
1062 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1063 S_ISDIR(st
.st_mode
))
1072 cwget(struct cw
*cw
)
1074 enum okay rv
= STOP
;
1077 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1079 if (fchdir(cw
->cw_fd
) == -1) {
1090 cwret(struct cw
*cw
)
1092 enum okay rv
= STOP
;
1095 if (!fchdir(cw
->cw_fd
))
1102 cwrelse(struct cw
*cw
)
1109 #else /* !HAVE_FCHDIR */
1111 cwget(struct cw
*cw
)
1113 enum okay rv
= STOP
;
1116 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1123 cwret(struct cw
*cw
)
1125 enum okay rv
= STOP
;
1128 if (!chdir(cw
->cw_wd
))
1135 cwrelse(struct cw
*cw
)
1141 #endif /* !HAVE_FCHDIR */
1144 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1149 #ifdef HAVE_NATCH_CHAR
1150 maxlen
= MIN(maxlen
, blen
);
1151 for (rv
= 0; maxlen
> 0;) {
1152 int ml
= mblen(buf
, maxlen
);
1162 rv
= MIN(blen
, maxlen
);
1169 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1171 NATCH_CHAR( struct bidi_info bi
; )
1172 size_t rv
NATCH_CHAR( COMMA i
);
1179 #ifdef HAVE_NATCH_CHAR
1180 bidi_info_create(&bi
);
1181 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1186 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1191 if ((i
= bi
.bi_start
.l
) > 0) {
1192 memcpy(store
, bi
.bi_start
.s
, i
);
1198 while (maxlen
> 0) {
1199 int ml
= mblen(buf
, blen
);
1204 if (UICMP(z
, maxlen
, <, ml
))
1209 memcpy(store
, buf
, ml
);
1216 if ((i
= bi
.bi_end
.l
) > 0) {
1217 memcpy(store
, bi
.bi_end
.s
, i
);
1225 rv
= MIN(blen
, maxlen
);
1226 memcpy(store
, buf
, rv
);
1235 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1237 NATCH_CHAR( struct bidi_info bi
; )
1238 int col_orig
= col
, n
, sz
;
1239 bool_t isbidi
, isuni
, istab
, isrepl
;
1243 /* Bidi only on request and when there is 8-bit data */
1244 isbidi
= isuni
= FAL0
;
1245 #ifdef HAVE_NATCH_CHAR
1246 isuni
= ((options
& OPT_UNICODE
) != 0);
1247 bidi_info_create(&bi
);
1248 if (bi
.bi_start
.l
== 0)
1250 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1253 if ((size_t)col
>= bi
.bi_pad
)
1260 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1262 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1265 #ifdef HAVE_NATCH_CHAR
1267 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1268 np
+= bi
.bi_start
.l
;
1272 while (*cp
!= '\0') {
1274 #ifdef HAVE_C90AMEND1
1275 if (mb_cur_max
> 1) {
1280 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1282 else if (wc
== L
'\t') {
1283 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1286 } else if (iswprint(wc
)) {
1287 # ifndef HAVE_WCWIDTH
1288 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1290 if ((n
= wcwidth(wc
)) == -1)
1300 istab
= (*cp
== '\t');
1301 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1310 np
[0] = (char)0xEFu
;
1311 np
[1] = (char)0xBFu
;
1312 np
[2] = (char)0xBDu
;
1317 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1325 if (fill
&& col
!= 0) {
1327 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1328 memset(nb
, ' ', col
);
1330 memset(np
, ' ', col
);
1335 #ifdef HAVE_NATCH_CHAR
1337 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1343 if (cols_decr_used_or_null
!= NULL
)
1344 *cols_decr_used_or_null
-= col_orig
- col
;
1350 makeprint(struct str
const *in
, struct str
*out
)
1352 static int print_all_chars
= -1;
1354 char const *inp
, *maxp
;
1359 if (print_all_chars
== -1)
1360 print_all_chars
= ok_blook(print_all_chars
);
1362 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1366 if (print_all_chars
) {
1368 memcpy(outp
, inp
, out
->l
);
1372 #ifdef HAVE_NATCH_CHAR
1373 if (mb_cur_max
> 1) {
1374 char mbb
[MB_LEN_MAX
+ 1];
1377 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1380 while (inp
< maxp
) {
1382 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1388 /* FIXME Why mbtowc() resetting here?
1389 * FIXME what about ISO 2022-JP plus -- those
1390 * FIXME will loose shifts, then!
1391 * FIXME THUS - we'd need special "known points"
1392 * FIXME to do so - say, after a newline!!
1393 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1394 mbtowc(&wc
, NULL
, mb_cur_max
);
1395 wc
= isuni
? 0xFFFD : '?';
1400 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1402 if ((wc
& ~(wchar_t)037) == 0)
1403 wc
= isuni
? 0x2400 | wc
: '?';
1404 else if (wc
== 0177)
1405 wc
= isuni
? 0x2421 : '?';
1407 wc
= isuni
? 0x2426 : '?';
1408 }else if(isuni
){ /* TODO ctext */
1409 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1410 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1412 /* And some zero-width messes */
1413 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1415 /* Oh about the ISO C wide character interfaces, baby! */
1419 if ((n
= wctomb(mbb
, wc
)) <= 0)
1422 assert(out
->l
< msz
);
1423 for (i
= 0; i
< n
; ++i
)
1427 #endif /* NATCH_CHAR */
1430 while (inp
< maxp
) {
1432 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1439 out
->s
[out
->l
] = '\0';
1444 delctrl(char *cp
, size_t len
)
1449 for (x
= y
= 0; x
< len
; ++x
)
1450 if (!cntrlchar(cp
[x
]))
1458 prstr(char const *s
)
1466 makeprint(&in
, &out
);
1467 rp
= savestrbuf(out
.s
, out
.l
);
1474 prout(char const *s
, size_t sz
, FILE *fp
)
1482 makeprint(&in
, &out
);
1483 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1490 putuc(int u
, int c
, FILE *fp
)
1496 #ifdef HAVE_NATCH_CHAR
1497 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1498 char mbb
[MB_LEN_MAX
];
1501 if ((n
= wctomb(mbb
, u
)) > 0) {
1503 for (i
= 0; i
< n
; ++i
)
1504 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1509 rv
= (putc('\0', fp
) != EOF
);
1514 rv
= (putc(c
, fp
) != EOF
);
1520 bidi_info_needed(char const *bdat
, size_t blen
)
1525 #ifdef HAVE_NATCH_CHAR
1526 if (options
& OPT_UNICODE
)
1528 /* TODO Checking for BIDI character: use S-CText fromutf8
1529 * TODO plus isrighttoleft (or whatever there will be)! */
1530 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1537 /* (Very very fuzzy, awaiting S-CText for good) */
1538 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1539 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1540 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1541 (c
>= 0x10800 && c
<= 0x10C48) ||
1542 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1547 #endif /* HAVE_NATCH_CHAR */
1553 bidi_info_create(struct bidi_info
*bip
)
1555 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1556 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1557 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1558 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1559 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1560 NATCH_CHAR( char const *hb
; )
1563 memset(bip
, 0, sizeof *bip
);
1564 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1566 #ifdef HAVE_NATCH_CHAR
1567 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1573 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1579 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1580 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1583 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1591 colour_table_create(bool_t pager_used
)
1593 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1595 struct colour_table
*ct
;
1598 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1601 char *term
, *okterms
;
1603 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
1605 /* terminfo rocks: if we find "color", assume it's right */
1606 if (strstr(term
, "color") != NULL
)
1608 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1609 okterms
= UNCONST(COLOUR_TERMS
);
1610 okterms
= savestr(okterms
);
1613 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1614 if (!strncmp(u
.cp
, term
, i
))
1620 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1623 enum colourspec cspec
;
1626 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1627 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1628 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1629 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1630 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1633 for (i
= 0; i
< NELEM(map
); ++i
) {
1634 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1635 u
.ccp
= map
[i
].defval
;
1636 u
.cp
= _colour_iso6429(u
.ccp
);
1637 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1638 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1641 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1642 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1644 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1645 u
.ccp
= COLOUR_USER_HEADERS
;
1646 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1647 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1653 colour_put(FILE *fp
, enum colourspec cs
)
1656 if (colour_table
!= NULL
) {
1657 struct str
const *cp
= colour_get(cs
);
1659 fwrite(cp
->s
, cp
->l
, 1, fp
);
1665 colour_put_header(FILE *fp
, char const *name
)
1667 enum colourspec cs
= COLOURSPEC_HEADER
;
1668 struct str
const *uheads
;
1669 char *cp
, *cp_base
, *x
;
1673 if (colour_table
== NULL
)
1675 /* Normal header colours if there are no user headers */
1676 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1677 if (uheads
->s
== NULL
)
1680 /* Iterate over all entries in the *colour-user-headers* list */
1681 cp
= ac_alloc(uheads
->l
+1);
1682 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1684 namelen
= strlen(name
);
1685 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1686 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1687 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1688 cs
= COLOURSPEC_UHEADER
;
1700 colour_reset(FILE *fp
)
1703 if (colour_table
!= NULL
)
1704 fwrite("\033[0m", 4, 1, fp
);
1708 FL
struct str
const *
1709 colour_get(enum colourspec cs
)
1711 struct str
const *rv
= NULL
;
1714 if (colour_table
!= NULL
)
1715 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1720 #endif /* HAVE_COLOUR */
1723 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1730 assert(inlen
== 0 || inbuf
!= NULL
);
1732 if (inlen
== UIZ_MAX
)
1733 inlen
= strlen(inbuf
);
1736 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1738 if ((inlen
== 1 && *inbuf
== '1') ||
1739 !ascncasecmp(inbuf
, "true", inlen
) ||
1740 !ascncasecmp(inbuf
, "yes", inlen
) ||
1741 !ascncasecmp(inbuf
, "on", inlen
))
1743 else if ((inlen
== 1 && *inbuf
== '0') ||
1744 !ascncasecmp(inbuf
, "false", inlen
) ||
1745 !ascncasecmp(inbuf
, "no", inlen
) ||
1746 !ascncasecmp(inbuf
, "off", inlen
))
1749 dat
= ac_alloc(inlen
+1);
1750 memcpy(dat
, inbuf
, inlen
);
1753 sli
= strtol(dat
, &eptr
, 0);
1754 if (*dat
!= '\0' && *eptr
== '\0')
1767 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1772 assert(inlen
== 0 || inbuf
!= NULL
);
1774 if (inlen
== UIZ_MAX
)
1775 inlen
= strlen(inbuf
);
1778 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1779 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1780 !ascncasecmp(inbuf
, "ask-", 4) &&
1781 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1782 (options
& OPT_INTERACTIVE
)) {
1784 fputs(prompt
, stdout
);
1785 rv
= getapproval(NULL
, rv
);
1794 #ifdef HAVE_CLOCK_GETTIME
1796 #elif defined HAVE_GETTIMEOFDAY
1802 #ifdef HAVE_CLOCK_GETTIME
1803 clock_gettime(CLOCK_REALTIME
, &ts
);
1804 rv
= (time_t)ts
.tv_sec
;
1805 #elif defined HAVE_GETTIMEOFDAY
1806 gettimeofday(&ts
, NULL
);
1807 rv
= (time_t)ts
.tv_sec
;
1816 time_current_update(struct time_current
*tc
, bool_t full_update
)
1819 tc
->tc_time
= n_time_epoch();
1821 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1822 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1823 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1829 n_err(char const *format
, ...)
1834 va_start(ap
, format
);
1836 if (options
& OPT_INTERACTIVE
)
1841 vfprintf(stderr
, format
, ap
);
1842 if (strchr(format
, '\n') != NULL
) /* TODO */
1850 n_verr(char const *format
, va_list ap
)
1852 /* Check use cases of PS_ERRORS_NOTED, too! */
1854 char buf
[LINESIZE
], *xbuf
;
1856 struct err_node
*enp
;
1858 LCTA(ERRORS_MAX
> 3);
1863 if (!(options
& OPT_INTERACTIVE
))
1866 vfprintf(stderr
, format
, ap
);
1874 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1877 if (UICMP(z
, l
, >=, lmax
)) {
1878 /* FIXME Cannot reuse va_list
1880 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1885 fwrite(xbuf
, 1, l
, stderr
);
1887 /* Link it into the `errors' message ring */
1888 if ((enp
= _err_tail
) == NULL
) {
1890 enp
= scalloc(1, sizeof *enp
);
1891 if (_err_tail
!= NULL
)
1892 _err_tail
->en_next
= enp
;
1897 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
1898 if (_err_cnt
< ERRORS_MAX
)
1901 _err_head
= (enp
= _err_head
)->en_next
;
1902 _err_tail
->en_next
= enp
;
1904 free(enp
->en_str
.s
);
1905 memset(enp
, 0, sizeof *enp
);
1908 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
1912 #endif /* HAVE_ERRORS */
1915 if (strchr(format
, '\n') != NULL
) /* TODO */
1921 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
1926 va_start(ap
, format
);
1927 vfprintf(stderr
, format
, ap
);
1933 n_perr(char const *msg
, int errval
)
1947 n_err(fmt
, msg
, strerror(errval
));
1952 n_alert(char const *format
, ...)
1957 n_err(_("Alert: "));
1959 va_start(ap
, format
);
1968 n_panic(char const *format
, ...)
1973 fprintf(stderr
, _("Panic: "));
1975 va_start(ap
, format
);
1976 vfprintf(stderr
, format
, ap
);
1982 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1990 struct err_node
*enp
;
1995 if (argv
[1] != NULL
)
1997 if (!asccasecmp(*argv
, "show"))
1999 if (!asccasecmp(*argv
, "clear"))
2002 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2006 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
2012 if (_err_head
== NULL
) {
2013 fprintf(stderr
, _("The error ring is empty\n"));
2017 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)
2019 fprintf(stderr
, _("tmpfile"));
2024 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
2025 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2026 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
2027 /* We don't know wether last string ended with NL; be simple */
2030 page_or_print(fp
, 0);
2037 _err_cnt
= _err_cnt_noted
= 0;
2038 while ((enp
= _err_head
) != NULL
) {
2039 _err_head
= enp
->en_next
;
2040 free(enp
->en_str
.s
);
2045 #endif /* HAVE_ERRORS */
2049 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2056 if ((rv
= malloc(s
)) == NULL
)
2057 n_panic(_("no memory"));
2063 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2072 else if ((rv
= realloc(v
, s
)) == NULL
)
2073 n_panic(_("no memory"));
2079 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2086 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2087 n_panic(_("no memory"));
2092 #else /* !HAVE_DEBUG */
2093 CTA(sizeof(char) == sizeof(ui8_t
));
2095 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2096 # define _HOPE_SET(C) \
2098 union mem_ptr __xl, __xu;\
2099 struct mem_chunk *__xc;\
2100 __xl.p_p = (C).p_p;\
2101 __xc = __xl.p_c - 1;\
2104 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2105 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2106 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2107 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2108 __xu.p_ui8p += __xc->mc_size - 8;\
2109 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2110 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2111 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2112 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2114 # define _HOPE_GET_TRACE(C,BAD) \
2120 # define _HOPE_GET(C,BAD) \
2122 union mem_ptr __xl, __xu;\
2123 struct mem_chunk *__xc;\
2125 __xl.p_p = (C).p_p;\
2127 (C).p_cp = __xl.p_cp;\
2128 __xc = __xl.p_c - 1;\
2131 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2132 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2133 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2134 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2135 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2136 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2137 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2138 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2141 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2142 __xl.p_p, __i, mdbg_file, mdbg_line);\
2145 __xu.p_ui8p += __xc->mc_size - 8;\
2147 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2148 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2149 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2150 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2151 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2152 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2153 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2154 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2157 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2158 __xl.p_p, __i, mdbg_file, mdbg_line);\
2161 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2162 __xc->mc_file, __xc->mc_line);\
2166 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2173 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2174 n_panic("smalloc(): allocation too large: %s, line %d",
2175 mdbg_file
, mdbg_line
);
2176 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2178 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2179 n_panic(_("no memory"));
2180 p
.p_c
->mc_prev
= NULL
;
2181 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2182 _mem_list
->mc_prev
= p
.p_c
;
2183 p
.p_c
->mc_file
= mdbg_file
;
2184 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2185 p
.p_c
->mc_isfree
= FAL0
;
2186 p
.p_c
->mc_size
= (ui32_t
)s
;
2188 _mem_list
= p
.p_c
++;
2193 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2196 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2202 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2208 if ((p
.p_p
= v
) == NULL
) {
2209 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2213 _HOPE_GET(p
, isbad
);
2215 if (p
.p_c
->mc_isfree
) {
2216 n_err("srealloc(): region freed! At %s, line %d\n"
2217 "\tLast seen: %s, line %" PRIu16
"\n",
2218 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2222 if (p
.p_c
== _mem_list
)
2223 _mem_list
= p
.p_c
->mc_next
;
2225 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2226 if (p
.p_c
->mc_next
!= NULL
)
2227 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2230 _mem_mcur
-= p
.p_c
->mc_size
;
2234 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2235 n_panic("srealloc(): allocation too large: %s, line %d",
2236 mdbg_file
, mdbg_line
);
2237 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2239 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2240 n_panic(_("no memory"));
2241 p
.p_c
->mc_prev
= NULL
;
2242 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2243 _mem_list
->mc_prev
= p
.p_c
;
2244 p
.p_c
->mc_file
= mdbg_file
;
2245 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2246 p
.p_c
->mc_isfree
= FAL0
;
2247 p
.p_c
->mc_size
= (ui32_t
)s
;
2248 _mem_list
= p
.p_c
++;
2253 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2256 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2263 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2272 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2273 n_panic("scalloc(): allocation size too large: %s, line %d",
2274 mdbg_file
, mdbg_line
);
2275 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2276 n_panic("scalloc(): allocation count too large: %s, line %d",
2277 mdbg_file
, mdbg_line
);
2280 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2282 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2283 n_panic(_("no memory"));
2284 memset(p
.p_p
, 0, size
);
2285 p
.p_c
->mc_prev
= NULL
;
2286 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2287 _mem_list
->mc_prev
= p
.p_c
;
2288 p
.p_c
->mc_file
= mdbg_file
;
2289 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2290 p
.p_c
->mc_isfree
= FAL0
;
2291 p
.p_c
->mc_size
= (ui32_t
)size
;
2292 _mem_list
= p
.p_c
++;
2297 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2300 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2306 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2312 if ((p
.p_p
= v
) == NULL
) {
2313 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2317 _HOPE_GET(p
, isbad
);
2319 if (p
.p_c
->mc_isfree
) {
2320 n_err("sfree(): double-free avoided at %s, line %d\n"
2321 "\tLast seen: %s, line %" PRIu16
"\n",
2322 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2326 if (p
.p_c
== _mem_list
)
2327 _mem_list
= p
.p_c
->mc_next
;
2329 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2330 if (p
.p_c
->mc_next
!= NULL
)
2331 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2332 p
.p_c
->mc_isfree
= TRU1
;
2333 /* Trash contents (also see [21c05f8]) */
2334 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2337 _mem_mcur
-= p
.p_c
->mc_size
;
2339 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2340 p
.p_c
->mc_next
= _mem_free
;
2352 size_t c
= 0, s
= 0;
2357 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2360 s
+= p
.p_c
->mc_size
;
2361 p
.p_c
= p
.p_c
->mc_next
;
2366 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2367 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2372 c_smemtrace(void *v
)
2374 /* For _HOPE_GET() */
2375 char const * const mdbg_file
= "smemtrace()";
2376 int const mdbg_line
= -1;
2378 union mem_ptr p
, xp
;
2384 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
2386 n_perr("tmpfile", 0);
2390 fprintf(fp
, "Memory statistics:\n"
2391 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2392 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2393 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2395 fprintf(fp
, "Currently allocated memory chunks:\n");
2396 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2397 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2400 _HOPE_GET_TRACE(xp
, isbad
);
2401 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2402 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2403 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2407 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2408 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2409 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2412 _HOPE_GET_TRACE(xp
, isbad
);
2413 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2414 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2415 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2416 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2420 page_or_print(fp
, lines
);
2430 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2432 union mem_ptr p
, xp
;
2433 bool_t anybad
= FAL0
, isbad
;
2437 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2438 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2441 _HOPE_GET_TRACE(xp
, isbad
);
2445 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2446 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2447 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2451 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2452 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2455 _HOPE_GET_TRACE(xp
, isbad
);
2459 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2460 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2461 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2468 # endif /* HAVE_DEVEL */
2472 # undef _HOPE_GET_TRACE
2474 #endif /* HAVE_DEBUG */