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 : '?';
1407 }else if(isuni
){ /* TODO ctext */
1408 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1409 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1411 /* And some zero-width messes */
1412 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1414 /* Oh about the ISO C wide character interfaces, baby! */
1418 if ((n
= wctomb(mbb
, wc
)) <= 0)
1421 assert(out
->l
< msz
);
1422 for (i
= 0; i
< n
; ++i
)
1426 #endif /* NATCH_CHAR */
1429 while (inp
< maxp
) {
1431 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1438 out
->s
[out
->l
] = '\0';
1443 delctrl(char *cp
, size_t len
)
1448 for (x
= y
= 0; x
< len
; ++x
)
1449 if (!cntrlchar(cp
[x
]))
1457 prstr(char const *s
)
1465 makeprint(&in
, &out
);
1466 rp
= savestrbuf(out
.s
, out
.l
);
1473 prout(char const *s
, size_t sz
, FILE *fp
)
1481 makeprint(&in
, &out
);
1482 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1489 putuc(int u
, int c
, FILE *fp
)
1495 #ifdef HAVE_NATCH_CHAR
1496 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1497 char mbb
[MB_LEN_MAX
];
1500 if ((n
= wctomb(mbb
, u
)) > 0) {
1502 for (i
= 0; i
< n
; ++i
)
1503 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1508 rv
= (putc('\0', fp
) != EOF
);
1513 rv
= (putc(c
, fp
) != EOF
);
1519 bidi_info_needed(char const *bdat
, size_t blen
)
1524 #ifdef HAVE_NATCH_CHAR
1525 if (options
& OPT_UNICODE
)
1527 /* TODO Checking for BIDI character: use S-CText fromutf8
1528 * TODO plus isrighttoleft (or whatever there will be)! */
1529 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1536 /* (Very very fuzzy, awaiting S-CText for good) */
1537 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1538 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1539 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1540 (c
>= 0x10800 && c
<= 0x10C48) ||
1541 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1546 #endif /* HAVE_NATCH_CHAR */
1552 bidi_info_create(struct bidi_info
*bip
)
1554 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1555 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1556 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1557 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1558 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1559 NATCH_CHAR( char const *hb
; )
1562 memset(bip
, 0, sizeof *bip
);
1563 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1565 #ifdef HAVE_NATCH_CHAR
1566 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1572 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1578 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1579 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1582 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1590 colour_table_create(bool_t pager_used
)
1592 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1594 struct colour_table
*ct
;
1597 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1600 char *term
, *okterms
;
1602 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
1604 /* terminfo rocks: if we find "color", assume it's right */
1605 if (strstr(term
, "color") != NULL
)
1607 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1608 okterms
= UNCONST(COLOUR_TERMS
);
1609 okterms
= savestr(okterms
);
1612 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1613 if (!strncmp(u
.cp
, term
, i
))
1619 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1622 enum colourspec cspec
;
1625 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1626 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1627 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1628 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1629 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1632 for (i
= 0; i
< NELEM(map
); ++i
) {
1633 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1634 u
.ccp
= map
[i
].defval
;
1635 u
.cp
= _colour_iso6429(u
.ccp
);
1636 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1637 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1640 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1641 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1643 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1644 u
.ccp
= COLOUR_USER_HEADERS
;
1645 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1646 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1652 colour_put(FILE *fp
, enum colourspec cs
)
1655 if (colour_table
!= NULL
) {
1656 struct str
const *cp
= colour_get(cs
);
1658 fwrite(cp
->s
, cp
->l
, 1, fp
);
1664 colour_put_header(FILE *fp
, char const *name
)
1666 enum colourspec cs
= COLOURSPEC_HEADER
;
1667 struct str
const *uheads
;
1668 char *cp
, *cp_base
, *x
;
1672 if (colour_table
== NULL
)
1674 /* Normal header colours if there are no user headers */
1675 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1676 if (uheads
->s
== NULL
)
1679 /* Iterate over all entries in the *colour-user-headers* list */
1680 cp
= ac_alloc(uheads
->l
+1);
1681 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1683 namelen
= strlen(name
);
1684 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1685 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1686 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1687 cs
= COLOURSPEC_UHEADER
;
1699 colour_reset(FILE *fp
)
1702 if (colour_table
!= NULL
)
1703 fwrite("\033[0m", 4, 1, fp
);
1707 FL
struct str
const *
1708 colour_get(enum colourspec cs
)
1710 struct str
const *rv
= NULL
;
1713 if (colour_table
!= NULL
)
1714 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1719 #endif /* HAVE_COLOUR */
1722 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1729 assert(inlen
== 0 || inbuf
!= NULL
);
1731 if (inlen
== UIZ_MAX
)
1732 inlen
= strlen(inbuf
);
1735 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1737 if ((inlen
== 1 && *inbuf
== '1') ||
1738 !ascncasecmp(inbuf
, "true", inlen
) ||
1739 !ascncasecmp(inbuf
, "yes", inlen
) ||
1740 !ascncasecmp(inbuf
, "on", inlen
))
1742 else if ((inlen
== 1 && *inbuf
== '0') ||
1743 !ascncasecmp(inbuf
, "false", inlen
) ||
1744 !ascncasecmp(inbuf
, "no", inlen
) ||
1745 !ascncasecmp(inbuf
, "off", inlen
))
1748 dat
= ac_alloc(inlen
+1);
1749 memcpy(dat
, inbuf
, inlen
);
1752 sli
= strtol(dat
, &eptr
, 0);
1753 if (*dat
!= '\0' && *eptr
== '\0')
1766 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1771 assert(inlen
== 0 || inbuf
!= NULL
);
1773 if (inlen
== UIZ_MAX
)
1774 inlen
= strlen(inbuf
);
1777 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1778 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1779 !ascncasecmp(inbuf
, "ask-", 4) &&
1780 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1781 (options
& OPT_INTERACTIVE
)) {
1783 fputs(prompt
, stdout
);
1784 rv
= getapproval(NULL
, rv
);
1793 #ifdef HAVE_CLOCK_GETTIME
1795 #elif defined HAVE_GETTIMEOFDAY
1801 #ifdef HAVE_CLOCK_GETTIME
1802 clock_gettime(CLOCK_REALTIME
, &ts
);
1803 rv
= (time_t)ts
.tv_sec
;
1804 #elif defined HAVE_GETTIMEOFDAY
1805 gettimeofday(&ts
, NULL
);
1806 rv
= (time_t)ts
.tv_sec
;
1815 time_current_update(struct time_current
*tc
, bool_t full_update
)
1818 tc
->tc_time
= n_time_epoch();
1820 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1821 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1822 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1828 n_err(char const *format
, ...)
1833 va_start(ap
, format
);
1835 if (options
& OPT_INTERACTIVE
)
1840 vfprintf(stderr
, format
, ap
);
1841 if (strchr(format
, '\n') != NULL
) /* TODO */
1849 n_verr(char const *format
, va_list ap
)
1851 /* Check use cases of PS_ERRORS_NOTED, too! */
1853 char buf
[LINESIZE
], *xbuf
;
1855 struct err_node
*enp
;
1857 LCTA(ERRORS_MAX
> 3);
1862 if (!(options
& OPT_INTERACTIVE
))
1865 vfprintf(stderr
, format
, ap
);
1873 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1876 if (UICMP(z
, l
, >=, lmax
)) {
1877 /* FIXME Cannot reuse va_list
1879 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1884 fwrite(xbuf
, 1, l
, stderr
);
1886 /* Link it into the `errors' message ring */
1887 if ((enp
= _err_tail
) == NULL
) {
1889 enp
= scalloc(1, sizeof *enp
);
1890 if (_err_tail
!= NULL
)
1891 _err_tail
->en_next
= enp
;
1896 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
1897 if (_err_cnt
< ERRORS_MAX
)
1900 _err_head
= (enp
= _err_head
)->en_next
;
1901 _err_tail
->en_next
= enp
;
1903 free(enp
->en_str
.s
);
1904 memset(enp
, 0, sizeof *enp
);
1907 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
1911 #endif /* HAVE_ERRORS */
1914 if (strchr(format
, '\n') != NULL
) /* TODO */
1920 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
1925 va_start(ap
, format
);
1926 vfprintf(stderr
, format
, ap
);
1932 n_perr(char const *msg
, int errval
)
1946 n_err(fmt
, msg
, strerror(errval
));
1951 n_alert(char const *format
, ...)
1956 n_err(_("Alert: "));
1958 va_start(ap
, format
);
1967 n_panic(char const *format
, ...)
1972 fprintf(stderr
, _("Panic: "));
1974 va_start(ap
, format
);
1975 vfprintf(stderr
, format
, ap
);
1981 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1989 struct err_node
*enp
;
1994 if (argv
[1] != NULL
)
1996 if (!asccasecmp(*argv
, "show"))
1998 if (!asccasecmp(*argv
, "clear"))
2001 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2005 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
2011 if (_err_head
== NULL
) {
2012 fprintf(stderr
, _("The error ring is empty\n"));
2016 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)
2018 fprintf(stderr
, _("tmpfile"));
2023 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
2024 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2025 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
2026 /* We don't know wether last string ended with NL; be simple */
2029 page_or_print(fp
, 0);
2036 _err_cnt
= _err_cnt_noted
= 0;
2037 while ((enp
= _err_head
) != NULL
) {
2038 _err_head
= enp
->en_next
;
2039 free(enp
->en_str
.s
);
2044 #endif /* HAVE_ERRORS */
2048 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2055 if ((rv
= malloc(s
)) == NULL
)
2056 n_panic(_("no memory"));
2062 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2071 else if ((rv
= realloc(v
, s
)) == NULL
)
2072 n_panic(_("no memory"));
2078 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2085 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2086 n_panic(_("no memory"));
2091 #else /* !HAVE_DEBUG */
2092 CTA(sizeof(char) == sizeof(ui8_t
));
2094 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2095 # define _HOPE_SET(C) \
2097 union mem_ptr __xl, __xu;\
2098 struct mem_chunk *__xc;\
2099 __xl.p_p = (C).p_p;\
2100 __xc = __xl.p_c - 1;\
2103 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2104 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2105 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2106 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2107 __xu.p_ui8p += __xc->mc_size - 8;\
2108 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2109 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2110 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2111 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2113 # define _HOPE_GET_TRACE(C,BAD) \
2119 # define _HOPE_GET(C,BAD) \
2121 union mem_ptr __xl, __xu;\
2122 struct mem_chunk *__xc;\
2124 __xl.p_p = (C).p_p;\
2126 (C).p_cp = __xl.p_cp;\
2127 __xc = __xl.p_c - 1;\
2130 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2131 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2132 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2133 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2134 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2135 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2136 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2137 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2140 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2141 __xl.p_p, __i, mdbg_file, mdbg_line);\
2144 __xu.p_ui8p += __xc->mc_size - 8;\
2146 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2147 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2148 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2149 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2150 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2151 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2152 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2153 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2156 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2157 __xl.p_p, __i, mdbg_file, mdbg_line);\
2160 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2161 __xc->mc_file, __xc->mc_line);\
2165 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2172 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2173 n_panic("smalloc(): allocation too large: %s, line %d",
2174 mdbg_file
, mdbg_line
);
2175 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2177 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2178 n_panic(_("no memory"));
2179 p
.p_c
->mc_prev
= NULL
;
2180 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2181 _mem_list
->mc_prev
= p
.p_c
;
2182 p
.p_c
->mc_file
= mdbg_file
;
2183 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2184 p
.p_c
->mc_isfree
= FAL0
;
2185 p
.p_c
->mc_size
= (ui32_t
)s
;
2187 _mem_list
= p
.p_c
++;
2192 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2195 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2201 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2207 if ((p
.p_p
= v
) == NULL
) {
2208 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2212 _HOPE_GET(p
, isbad
);
2214 if (p
.p_c
->mc_isfree
) {
2215 n_err("srealloc(): region freed! At %s, line %d\n"
2216 "\tLast seen: %s, line %" PRIu16
"\n",
2217 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2221 if (p
.p_c
== _mem_list
)
2222 _mem_list
= p
.p_c
->mc_next
;
2224 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2225 if (p
.p_c
->mc_next
!= NULL
)
2226 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2229 _mem_mcur
-= p
.p_c
->mc_size
;
2233 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2234 n_panic("srealloc(): allocation too large: %s, line %d",
2235 mdbg_file
, mdbg_line
);
2236 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2238 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2239 n_panic(_("no memory"));
2240 p
.p_c
->mc_prev
= NULL
;
2241 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2242 _mem_list
->mc_prev
= p
.p_c
;
2243 p
.p_c
->mc_file
= mdbg_file
;
2244 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2245 p
.p_c
->mc_isfree
= FAL0
;
2246 p
.p_c
->mc_size
= (ui32_t
)s
;
2247 _mem_list
= p
.p_c
++;
2252 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2255 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2262 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2271 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2272 n_panic("scalloc(): allocation size too large: %s, line %d",
2273 mdbg_file
, mdbg_line
);
2274 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2275 n_panic("scalloc(): allocation count too large: %s, line %d",
2276 mdbg_file
, mdbg_line
);
2279 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2281 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2282 n_panic(_("no memory"));
2283 memset(p
.p_p
, 0, size
);
2284 p
.p_c
->mc_prev
= NULL
;
2285 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2286 _mem_list
->mc_prev
= p
.p_c
;
2287 p
.p_c
->mc_file
= mdbg_file
;
2288 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2289 p
.p_c
->mc_isfree
= FAL0
;
2290 p
.p_c
->mc_size
= (ui32_t
)size
;
2291 _mem_list
= p
.p_c
++;
2296 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2299 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2305 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2311 if ((p
.p_p
= v
) == NULL
) {
2312 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2316 _HOPE_GET(p
, isbad
);
2318 if (p
.p_c
->mc_isfree
) {
2319 n_err("sfree(): double-free avoided at %s, line %d\n"
2320 "\tLast seen: %s, line %" PRIu16
"\n",
2321 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2325 if (p
.p_c
== _mem_list
)
2326 _mem_list
= p
.p_c
->mc_next
;
2328 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2329 if (p
.p_c
->mc_next
!= NULL
)
2330 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2331 p
.p_c
->mc_isfree
= TRU1
;
2332 /* Trash contents (also see [21c05f8]) */
2333 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2336 _mem_mcur
-= p
.p_c
->mc_size
;
2338 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2339 p
.p_c
->mc_next
= _mem_free
;
2351 size_t c
= 0, s
= 0;
2356 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2359 s
+= p
.p_c
->mc_size
;
2360 p
.p_c
= p
.p_c
->mc_next
;
2365 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2366 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2371 c_smemtrace(void *v
)
2373 /* For _HOPE_GET() */
2374 char const * const mdbg_file
= "smemtrace()";
2375 int const mdbg_line
= -1;
2377 union mem_ptr p
, xp
;
2383 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
2385 n_perr("tmpfile", 0);
2389 fprintf(fp
, "Memory statistics:\n"
2390 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2391 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2392 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2394 fprintf(fp
, "Currently allocated memory chunks:\n");
2395 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2396 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2399 _HOPE_GET_TRACE(xp
, isbad
);
2400 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2401 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2402 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2406 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2407 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2408 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2411 _HOPE_GET_TRACE(xp
, isbad
);
2412 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2413 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2414 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2415 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2419 page_or_print(fp
, lines
);
2429 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2431 union mem_ptr p
, xp
;
2432 bool_t anybad
= FAL0
, isbad
;
2436 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2437 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2440 _HOPE_GET_TRACE(xp
, isbad
);
2444 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2445 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2446 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2450 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2451 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2454 _HOPE_GET_TRACE(xp
, isbad
);
2458 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2459 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2460 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2467 # endif /* HAVE_DEVEL */
2471 # undef _HOPE_GET_TRACE
2473 #endif /* HAVE_DEBUG */