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
), -1, NULL
, NULL
, NULL
);
625 while ((c
= getc(fp
)) != EOF
)
632 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
638 enum protocol rv
= PROTO_UNKNOWN
;
641 temporary_protocol_ext
= NULL
;
643 if (name
[0] == '%' && name
[1] == ':')
645 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
649 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
650 if (!strncmp(name
, "pop3://", 7)) {
654 n_err(_("No POP3 support compiled in\n"));
656 } else if (!strncmp(name
, "pop3s://", 8)) {
657 #if defined HAVE_POP3 && defined HAVE_SSL
661 n_err(_("No POP3 support compiled in\n"));
664 n_err(_("No SSL support compiled in\n"));
667 } else if (!strncmp(name
, "imap://", 7)) {
671 fprintf(stderr
, _("No IMAP support compiled in.\n"));
673 } else if (!strncmp(name
, "imaps://", 8)) {
674 #if defined HAVE_IMAP && defined HAVE_SSL
678 fprintf(stderr
, _("No IMAP support compiled in.\n"));
681 fprintf(stderr
, _("No SSL support compiled in.\n"));
688 /* TODO This is the de facto maildir code and thus belongs into there!
689 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
690 * TODO or (more likely) in addition to *newfolders*) */
693 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
694 memcpy(np
, name
, sz
+ 1);
695 if (!stat(name
, &st
)) {
696 if (S_ISDIR(st
.st_mode
) &&
697 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
698 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
699 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
702 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
703 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
704 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
705 temporary_protocol_ext
= cp
;
706 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
716 torek_hash(char const *name
)
718 /* Chris Torek's hash.
719 * NOTE: need to change *at least* create-okey-map.pl when changing the
724 while (*name
!= '\0') {
733 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
740 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
741 if ((g
= h
& 0xf0000000) != 0) {
753 static ui32_t
const primes
[] = {
754 5, 11, 23, 47, 97, 157, 283,
755 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
756 131071, 262139, 524287, 1048573, 2097143, 4194301,
757 8388593, 16777213, 33554393, 67108859, 134217689,
758 268435399, 536870909, 1073741789, 2147483647
764 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
765 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
766 : NELEM(primes
) / 2));
768 if ((mprime
= primes
[i
]) > n
)
770 while (++i
< NELEM(primes
));
771 if (i
== NELEM(primes
) && mprime
< n
)
778 expand_shell_escape(char const **s
, bool_t use_nail_extensions
)
786 if ((c
= *xs
& 0xFF) == '\0')
792 switch ((c
= *xs
& 0xFF)) {
794 case 'a': c
= '\a'; break;
795 case 'b': c
= '\b'; break;
796 case 'c': c
= PROMPT_STOP
; break;
797 case 'f': c
= '\f'; break;
798 case 'n': c
= '\n'; break;
799 case 'r': c
= '\r'; break;
800 case 't': c
= '\t'; break;
801 case 'v': c
= '\v'; break;
803 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
808 /* S-nail extension for nice (get)prompt(()) support */
813 if (use_nail_extensions
) {
815 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
816 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
817 case '$': c
= PROMPT_DOLLAR
; break;
818 case '@': c
= PROMPT_AT
; break;
824 /* A sole <backslash> at EOS is treated as-is! */
838 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
840 static char buf
[PROMPT_BUFFER_SIZE
];
843 char const *ccp_base
, *ccp
;
844 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
845 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
848 /* No other place to place this */
850 if (options
& OPT_INTERACTIVE
) {
851 if (!(pstate
& PS_ERRORS_NOTED
) && _err_head
!= NULL
) {
852 pstate
|= PS_ERRORS_NOTED
;
853 fprintf(stderr
, _("There are new messages in the error message ring "
854 "(denoted by \"#ERR#\")\n"
855 " The `errors' command manages this message ring\n"));
858 if ((trigger
= (_err_cnt_noted
!= _err_cnt
)))
859 _err_cnt_noted
= _err_cnt
;
865 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
875 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
877 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
879 dfmaxlen
= 0; /* keep CC happy */
883 NATCH_CHAR( cclen
= cclen_base
; )
884 maxlen
= sizeof(buf
) -1;
892 #ifdef HAVE_NATCH_CHAR
893 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
902 } else if ((l
= c
) > 1) {
912 if ((c
= expand_shell_escape(&ccp
, TRU1
)) > 0) {
918 if (c
== 0 || c
== PROMPT_STOP
)
922 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
925 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
945 nodename(int mayoverride
)
947 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
952 # ifdef HAVE_GETADDRINFO
953 struct addrinfo hints
, *res
;
955 struct hostent
*hent
;
960 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
962 } else if ((hn
= sys_hostname
) == NULL
) {
966 # ifdef HAVE_GETADDRINFO
967 memset(&hints
, 0, sizeof hints
);
968 hints
.ai_family
= AF_UNSPEC
;
969 hints
.ai_flags
= AI_CANONNAME
;
970 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
971 if (res
->ai_canonname
!= NULL
) {
972 size_t l
= strlen(res
->ai_canonname
) +1;
975 memcpy(hn
, res
->ai_canonname
, l
);
980 hent
= gethostbyname(hn
);
985 sys_hostname
= sstrdup(hn
);
986 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
987 if (hn
!= ut
.nodename
)
993 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
995 hostname
= sstrdup(hn
);
1001 getrandstring(size_t length
)
1008 #ifndef HAVE_POSIX_RANDOM
1013 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1014 * with PAD stripped is still longer than what the user requests, easy way */
1015 data
= ac_alloc(i
= length
+ 3);
1017 #ifndef HAVE_POSIX_RANDOM
1019 data
[i
] = (char)_rand_get8();
1024 union {ui32_t i4
; char c
[4];} r
;
1027 r
.i4
= (ui32_t
)arc4random();
1028 switch ((j
= i
& 3)) {
1029 case 0: cp
[3] = r
.c
[3]; j
= 4;
1030 case 3: cp
[2] = r
.c
[2];
1031 case 2: cp
[1] = r
.c
[1];
1032 default: cp
[0] = r
.c
[0]; break;
1040 b64_encode_buf(&b64
, data
, length
+ 3,
1041 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1044 assert(b64
.l
>= length
);
1045 b64
.s
[length
] = '\0';
1051 makedir(char const *name
)
1054 enum okay rv
= STOP
;
1057 if (!mkdir(name
, 0700))
1061 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1062 S_ISDIR(st
.st_mode
))
1071 cwget(struct cw
*cw
)
1073 enum okay rv
= STOP
;
1076 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1078 if (fchdir(cw
->cw_fd
) == -1) {
1089 cwret(struct cw
*cw
)
1091 enum okay rv
= STOP
;
1094 if (!fchdir(cw
->cw_fd
))
1101 cwrelse(struct cw
*cw
)
1108 #else /* !HAVE_FCHDIR */
1110 cwget(struct cw
*cw
)
1112 enum okay rv
= STOP
;
1115 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1122 cwret(struct cw
*cw
)
1124 enum okay rv
= STOP
;
1127 if (!chdir(cw
->cw_wd
))
1134 cwrelse(struct cw
*cw
)
1140 #endif /* !HAVE_FCHDIR */
1143 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1148 #ifdef HAVE_NATCH_CHAR
1149 maxlen
= MIN(maxlen
, blen
);
1150 for (rv
= 0; maxlen
> 0;) {
1151 int ml
= mblen(buf
, maxlen
);
1161 rv
= MIN(blen
, maxlen
);
1168 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1170 NATCH_CHAR( struct bidi_info bi
; )
1171 size_t rv
NATCH_CHAR( COMMA i
);
1178 #ifdef HAVE_NATCH_CHAR
1179 bidi_info_create(&bi
);
1180 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1185 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1190 if ((i
= bi
.bi_start
.l
) > 0) {
1191 memcpy(store
, bi
.bi_start
.s
, i
);
1197 while (maxlen
> 0) {
1198 int ml
= mblen(buf
, blen
);
1203 if (UICMP(z
, maxlen
, <, ml
))
1208 memcpy(store
, buf
, ml
);
1215 if ((i
= bi
.bi_end
.l
) > 0) {
1216 memcpy(store
, bi
.bi_end
.s
, i
);
1224 rv
= MIN(blen
, maxlen
);
1225 memcpy(store
, buf
, rv
);
1234 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1236 NATCH_CHAR( struct bidi_info bi
; )
1237 int col_orig
= col
, n
, sz
;
1238 bool_t isbidi
, isuni
, istab
, isrepl
;
1242 /* Bidi only on request and when there is 8-bit data */
1243 isbidi
= isuni
= FAL0
;
1244 #ifdef HAVE_NATCH_CHAR
1245 isuni
= ((options
& OPT_UNICODE
) != 0);
1246 bidi_info_create(&bi
);
1247 if (bi
.bi_start
.l
== 0)
1249 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1252 if ((size_t)col
>= bi
.bi_pad
)
1259 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1261 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1264 #ifdef HAVE_NATCH_CHAR
1266 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1267 np
+= bi
.bi_start
.l
;
1271 while (*cp
!= '\0') {
1273 #ifdef HAVE_C90AMEND1
1274 if (mb_cur_max
> 1) {
1279 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1281 else if (wc
== L
'\t') {
1282 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1285 } else if (iswprint(wc
)) {
1286 # ifndef HAVE_WCWIDTH
1287 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1289 if ((n
= wcwidth(wc
)) == -1)
1299 istab
= (*cp
== '\t');
1300 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1309 np
[0] = (char)0xEFu
;
1310 np
[1] = (char)0xBFu
;
1311 np
[2] = (char)0xBDu
;
1316 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1324 if (fill
&& col
!= 0) {
1326 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1327 memset(nb
, ' ', col
);
1329 memset(np
, ' ', col
);
1334 #ifdef HAVE_NATCH_CHAR
1336 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1342 if (cols_decr_used_or_null
!= NULL
)
1343 *cols_decr_used_or_null
-= col_orig
- col
;
1349 makeprint(struct str
const *in
, struct str
*out
)
1351 static int print_all_chars
= -1;
1353 char const *inp
, *maxp
;
1358 if (print_all_chars
== -1)
1359 print_all_chars
= ok_blook(print_all_chars
);
1361 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1365 if (print_all_chars
) {
1367 memcpy(outp
, inp
, out
->l
);
1371 #ifdef HAVE_NATCH_CHAR
1372 if (mb_cur_max
> 1) {
1373 char mbb
[MB_LEN_MAX
+ 1];
1376 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1379 while (inp
< maxp
) {
1381 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1387 /* FIXME Why mbtowc() resetting here?
1388 * FIXME what about ISO 2022-JP plus -- those
1389 * FIXME will loose shifts, then!
1390 * FIXME THUS - we'd need special "known points"
1391 * FIXME to do so - say, after a newline!!
1392 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1393 mbtowc(&wc
, NULL
, mb_cur_max
);
1394 wc
= isuni
? 0xFFFD : '?';
1399 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1401 if ((wc
& ~(wchar_t)037) == 0)
1402 wc
= isuni
? 0x2400 | wc
: '?';
1403 else if (wc
== 0177)
1404 wc
= isuni
? 0x2421 : '?';
1406 wc
= isuni
? 0x2426 : '?';
1408 if ((n
= wctomb(mbb
, wc
)) <= 0)
1411 assert(out
->l
< msz
);
1412 for (i
= 0; i
< n
; ++i
)
1416 #endif /* NATCH_CHAR */
1419 while (inp
< maxp
) {
1421 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1428 out
->s
[out
->l
] = '\0';
1433 delctrl(char *cp
, size_t len
)
1438 for (x
= y
= 0; x
< len
; ++x
)
1439 if (!cntrlchar(cp
[x
]))
1447 prstr(char const *s
)
1455 makeprint(&in
, &out
);
1456 rp
= savestrbuf(out
.s
, out
.l
);
1463 prout(char const *s
, size_t sz
, FILE *fp
)
1471 makeprint(&in
, &out
);
1472 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1479 putuc(int u
, int c
, FILE *fp
)
1485 #ifdef HAVE_NATCH_CHAR
1486 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1487 char mbb
[MB_LEN_MAX
];
1490 if ((n
= wctomb(mbb
, u
)) > 0) {
1492 for (i
= 0; i
< n
; ++i
)
1493 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1498 rv
= (putc('\0', fp
) != EOF
);
1503 rv
= (putc(c
, fp
) != EOF
);
1509 bidi_info_needed(char const *bdat
, size_t blen
)
1514 #ifdef HAVE_NATCH_CHAR
1515 if (options
& OPT_UNICODE
)
1517 /* TODO Checking for BIDI character: use S-CText fromutf8
1518 * TODO plus isrighttoleft (or whatever there will be)! */
1519 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1526 /* (Very very fuzzy, awaiting S-CText for good) */
1527 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1528 (c
>= 0xFB1D && c
<= 0xFEFC) ||
1529 (c
>= 0x10800 && c
<= 0x10C48) ||
1530 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1535 #endif /* HAVE_NATCH_CHAR */
1541 bidi_info_create(struct bidi_info
*bip
)
1543 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1544 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1545 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1546 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1547 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1548 NATCH_CHAR( char const *hb
; )
1551 memset(bip
, 0, sizeof *bip
);
1552 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1554 #ifdef HAVE_NATCH_CHAR
1555 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1561 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1567 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1568 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1571 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1579 colour_table_create(bool_t pager_used
)
1581 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1583 struct colour_table
*ct
;
1586 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1589 char *term
, *okterms
;
1591 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
1593 /* terminfo rocks: if we find "color", assume it's right */
1594 if (strstr(term
, "color") != NULL
)
1596 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1597 okterms
= UNCONST(COLOUR_TERMS
);
1598 okterms
= savestr(okterms
);
1601 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1602 if (!strncmp(u
.cp
, term
, i
))
1608 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1611 enum colourspec cspec
;
1614 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1615 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1616 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1617 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1618 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1621 for (i
= 0; i
< NELEM(map
); ++i
) {
1622 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1623 u
.ccp
= map
[i
].defval
;
1624 u
.cp
= _colour_iso6429(u
.ccp
);
1625 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1626 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1629 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1630 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1632 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1633 u
.ccp
= COLOUR_USER_HEADERS
;
1634 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1635 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1641 colour_put(FILE *fp
, enum colourspec cs
)
1644 if (colour_table
!= NULL
) {
1645 struct str
const *cp
= colour_get(cs
);
1647 fwrite(cp
->s
, cp
->l
, 1, fp
);
1653 colour_put_header(FILE *fp
, char const *name
)
1655 enum colourspec cs
= COLOURSPEC_HEADER
;
1656 struct str
const *uheads
;
1657 char *cp
, *cp_base
, *x
;
1661 if (colour_table
== NULL
)
1663 /* Normal header colours if there are no user headers */
1664 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1665 if (uheads
->s
== NULL
)
1668 /* Iterate over all entries in the *colour-user-headers* list */
1669 cp
= ac_alloc(uheads
->l
+1);
1670 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1672 namelen
= strlen(name
);
1673 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1674 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1675 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1676 cs
= COLOURSPEC_UHEADER
;
1688 colour_reset(FILE *fp
)
1691 if (colour_table
!= NULL
)
1692 fwrite("\033[0m", 4, 1, fp
);
1696 FL
struct str
const *
1697 colour_get(enum colourspec cs
)
1699 struct str
const *rv
= NULL
;
1702 if (colour_table
!= NULL
)
1703 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1708 #endif /* HAVE_COLOUR */
1711 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1718 assert(inlen
== 0 || inbuf
!= NULL
);
1720 if (inlen
== UIZ_MAX
)
1721 inlen
= strlen(inbuf
);
1724 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1726 if ((inlen
== 1 && *inbuf
== '1') ||
1727 !ascncasecmp(inbuf
, "true", inlen
) ||
1728 !ascncasecmp(inbuf
, "yes", inlen
) ||
1729 !ascncasecmp(inbuf
, "on", inlen
))
1731 else if ((inlen
== 1 && *inbuf
== '0') ||
1732 !ascncasecmp(inbuf
, "false", inlen
) ||
1733 !ascncasecmp(inbuf
, "no", inlen
) ||
1734 !ascncasecmp(inbuf
, "off", inlen
))
1737 dat
= ac_alloc(inlen
+1);
1738 memcpy(dat
, inbuf
, inlen
);
1741 sli
= strtol(dat
, &eptr
, 0);
1742 if (*dat
!= '\0' && *eptr
== '\0')
1755 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1760 assert(inlen
== 0 || inbuf
!= NULL
);
1762 if (inlen
== UIZ_MAX
)
1763 inlen
= strlen(inbuf
);
1766 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1767 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1768 !ascncasecmp(inbuf
, "ask-", 4) &&
1769 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1770 (options
& OPT_INTERACTIVE
)) {
1772 fputs(prompt
, stdout
);
1773 rv
= getapproval(NULL
, rv
);
1782 #ifdef HAVE_CLOCK_GETTIME
1784 #elif defined HAVE_GETTIMEOFDAY
1790 #ifdef HAVE_CLOCK_GETTIME
1791 clock_gettime(CLOCK_REALTIME
, &ts
);
1792 rv
= (time_t)ts
.tv_sec
;
1793 #elif defined HAVE_GETTIMEOFDAY
1794 gettimeofday(&ts
, NULL
);
1795 rv
= (time_t)ts
.tv_sec
;
1804 time_current_update(struct time_current
*tc
, bool_t full_update
)
1807 tc
->tc_time
= n_time_epoch();
1809 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1810 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1811 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1817 n_err(char const *format
, ...)
1822 va_start(ap
, format
);
1824 if (options
& OPT_INTERACTIVE
)
1829 vfprintf(stderr
, format
, ap
);
1830 if (strchr(format
, '\n') != NULL
) /* TODO */
1838 n_verr(char const *format
, va_list ap
)
1840 /* Check use cases of PS_ERRORS_NOTED, too! */
1842 char buf
[LINESIZE
], *xbuf
= buf
;
1843 int l
= sizeof(buf
) -1;
1844 struct err_node
*enp
;
1846 LCTA(ERRORS_MAX
> 3);
1851 if (!(options
& OPT_INTERACTIVE
))
1854 vfprintf(stderr
, format
, ap
);
1860 l
= vsnprintf(xbuf
, l
+1, format
, ap
);
1862 assert(xbuf
== buf
);
1865 if (UICMP(z
, l
, >=, sizeof buf
)) {
1867 xbuf
= smalloc(l
+1);
1874 fwrite(xbuf
, 1, l
, stderr
);
1876 /* Link it into the `errors' message ring */
1877 if ((enp
= _err_tail
) == NULL
) {
1879 enp
= scalloc(1, sizeof *enp
);
1880 if (_err_tail
!= NULL
)
1881 _err_tail
->en_next
= enp
;
1886 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
1887 if (_err_cnt
< ERRORS_MAX
)
1890 _err_head
= (enp
= _err_head
)->en_next
;
1891 _err_tail
->en_next
= enp
;
1893 free(enp
->en_str
.s
);
1894 memset(enp
, 0, sizeof *enp
);
1897 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
1901 #endif /* HAVE_ERRORS */
1904 if (strchr(format
, '\n') != NULL
) /* TODO */
1910 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
1915 va_start(ap
, format
);
1916 vfprintf(stderr
, format
, ap
);
1922 n_perr(char const *msg
, int errval
)
1936 n_err(fmt
, msg
, strerror(errval
));
1941 n_alert(char const *format
, ...)
1946 n_err(_("Alert: "));
1948 va_start(ap
, format
);
1957 n_panic(char const *format
, ...)
1962 fprintf(stderr
, _("Panic: "));
1964 va_start(ap
, format
);
1965 vfprintf(stderr
, format
, ap
);
1971 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1979 struct err_node
*enp
;
1984 if (argv
[1] != NULL
)
1986 if (!asccasecmp(*argv
, "show"))
1988 if (!asccasecmp(*argv
, "clear"))
1991 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1995 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
2001 if (_err_head
== NULL
) {
2002 fprintf(stderr
, _("The error ring is empty\n"));
2006 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)
2008 fprintf(stderr
, _("tmpfile"));
2013 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
2014 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2015 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
2016 /* We don't know wether last string ended with NL; be simple */
2019 page_or_print(fp
, 0);
2026 _err_cnt
= _err_cnt_noted
= 0;
2027 while ((enp
= _err_head
) != NULL
) {
2028 _err_head
= enp
->en_next
;
2029 free(enp
->en_str
.s
);
2034 #endif /* HAVE_ERRORS */
2038 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2045 if ((rv
= malloc(s
)) == NULL
)
2046 n_panic(_("no memory"));
2052 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2061 else if ((rv
= realloc(v
, s
)) == NULL
)
2062 n_panic(_("no memory"));
2068 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2075 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2076 n_panic(_("no memory"));
2081 #else /* !HAVE_DEBUG */
2082 CTA(sizeof(char) == sizeof(ui8_t
));
2084 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2085 # define _HOPE_SET(C) \
2087 union mem_ptr __xl, __xu;\
2088 struct mem_chunk *__xc;\
2089 __xl.p_p = (C).p_p;\
2090 __xc = __xl.p_c - 1;\
2093 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2094 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2095 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2096 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2097 __xu.p_ui8p += __xc->mc_size - 8;\
2098 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2099 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2100 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2101 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2103 # define _HOPE_GET_TRACE(C,BAD) \
2109 # define _HOPE_GET(C,BAD) \
2111 union mem_ptr __xl, __xu;\
2112 struct mem_chunk *__xc;\
2114 __xl.p_p = (C).p_p;\
2116 (C).p_cp = __xl.p_cp;\
2117 __xc = __xl.p_c - 1;\
2120 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2121 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2122 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2123 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2124 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2125 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2126 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2127 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2130 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2131 __xl.p_p, __i, mdbg_file, mdbg_line);\
2134 __xu.p_ui8p += __xc->mc_size - 8;\
2136 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2137 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2138 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2139 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2140 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2141 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2142 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2143 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2146 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2147 __xl.p_p, __i, mdbg_file, mdbg_line);\
2150 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2151 __xc->mc_file, __xc->mc_line);\
2155 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2162 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2163 n_panic("smalloc(): allocation too large: %s, line %d",
2164 mdbg_file
, mdbg_line
);
2165 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2167 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2168 n_panic(_("no memory"));
2169 p
.p_c
->mc_prev
= NULL
;
2170 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2171 _mem_list
->mc_prev
= p
.p_c
;
2172 p
.p_c
->mc_file
= mdbg_file
;
2173 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2174 p
.p_c
->mc_isfree
= FAL0
;
2175 p
.p_c
->mc_size
= (ui32_t
)s
;
2177 _mem_list
= p
.p_c
++;
2182 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2185 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2191 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2197 if ((p
.p_p
= v
) == NULL
) {
2198 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2202 _HOPE_GET(p
, isbad
);
2204 if (p
.p_c
->mc_isfree
) {
2205 n_err("srealloc(): region freed! At %s, line %d\n"
2206 "\tLast seen: %s, line %" PRIu16
"\n",
2207 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2211 if (p
.p_c
== _mem_list
)
2212 _mem_list
= p
.p_c
->mc_next
;
2214 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2215 if (p
.p_c
->mc_next
!= NULL
)
2216 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2219 _mem_mcur
-= p
.p_c
->mc_size
;
2223 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2224 n_panic("srealloc(): allocation too large: %s, line %d",
2225 mdbg_file
, mdbg_line
);
2226 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2228 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2229 n_panic(_("no memory"));
2230 p
.p_c
->mc_prev
= NULL
;
2231 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2232 _mem_list
->mc_prev
= p
.p_c
;
2233 p
.p_c
->mc_file
= mdbg_file
;
2234 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2235 p
.p_c
->mc_isfree
= FAL0
;
2236 p
.p_c
->mc_size
= (ui32_t
)s
;
2237 _mem_list
= p
.p_c
++;
2242 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2245 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2252 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2261 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2262 n_panic("scalloc(): allocation size too large: %s, line %d",
2263 mdbg_file
, mdbg_line
);
2264 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2265 n_panic("scalloc(): allocation count too large: %s, line %d",
2266 mdbg_file
, mdbg_line
);
2269 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2271 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2272 n_panic(_("no memory"));
2273 memset(p
.p_p
, 0, size
);
2274 p
.p_c
->mc_prev
= NULL
;
2275 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2276 _mem_list
->mc_prev
= p
.p_c
;
2277 p
.p_c
->mc_file
= mdbg_file
;
2278 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2279 p
.p_c
->mc_isfree
= FAL0
;
2280 p
.p_c
->mc_size
= (ui32_t
)size
;
2281 _mem_list
= p
.p_c
++;
2286 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2289 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2295 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2301 if ((p
.p_p
= v
) == NULL
) {
2302 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2306 _HOPE_GET(p
, isbad
);
2308 if (p
.p_c
->mc_isfree
) {
2309 n_err("sfree(): double-free avoided at %s, line %d\n"
2310 "\tLast seen: %s, line %" PRIu16
"\n",
2311 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2315 if (p
.p_c
== _mem_list
)
2316 _mem_list
= p
.p_c
->mc_next
;
2318 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2319 if (p
.p_c
->mc_next
!= NULL
)
2320 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2321 p
.p_c
->mc_isfree
= TRU1
;
2322 /* Trash contents (also see [21c05f8]) */
2323 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2326 _mem_mcur
-= p
.p_c
->mc_size
;
2328 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2329 p
.p_c
->mc_next
= _mem_free
;
2341 size_t c
= 0, s
= 0;
2346 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2349 s
+= p
.p_c
->mc_size
;
2350 p
.p_c
= p
.p_c
->mc_next
;
2355 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2356 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2361 c_smemtrace(void *v
)
2363 /* For _HOPE_GET() */
2364 char const * const mdbg_file
= "smemtrace()";
2365 int const mdbg_line
= -1;
2367 union mem_ptr p
, xp
;
2373 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
2375 n_perr("tmpfile", 0);
2379 fprintf(fp
, "Memory statistics:\n"
2380 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2381 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2382 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2384 fprintf(fp
, "Currently allocated memory chunks:\n");
2385 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2386 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2389 _HOPE_GET_TRACE(xp
, isbad
);
2390 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2391 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2392 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2396 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2397 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2398 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2401 _HOPE_GET_TRACE(xp
, isbad
);
2402 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2403 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2404 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2405 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2409 page_or_print(fp
, lines
);
2419 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2421 union mem_ptr p
, xp
;
2422 bool_t anybad
= FAL0
, isbad
;
2426 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2427 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2430 _HOPE_GET_TRACE(xp
, isbad
);
2434 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2435 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2436 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2440 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2441 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2444 _HOPE_GET_TRACE(xp
, isbad
);
2448 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2449 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2450 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2457 # endif /* HAVE_DEVEL */
2461 # undef _HOPE_GET_TRACE
2463 #endif /* HAVE_DEBUG */