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
- 4; /* 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 data
= ac_alloc(length
);
1015 #ifndef HAVE_POSIX_RANDOM
1016 for (i
= length
; i
-- > 0;)
1017 data
[i
] = (char)_rand_get8();
1021 for (i
= length
; i
> 0;) {
1022 union {ui32_t i4
; char c
[4];} r
;
1025 r
.i4
= (ui32_t
)arc4random();
1026 switch ((j
= i
& 3)) {
1027 case 0: cp
[3] = r
.c
[3]; j
= 4;
1028 case 3: cp
[2] = r
.c
[2];
1029 case 2: cp
[1] = r
.c
[1];
1030 default: cp
[0] = r
.c
[0]; break;
1038 b64_encode_buf(&b64
, data
, length
, B64_SALLOC
| B64_RFC4648URL
);
1041 b64
.s
[length
] = '\0';
1047 makedir(char const *name
)
1050 enum okay rv
= STOP
;
1053 if (!mkdir(name
, 0700))
1057 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1058 S_ISDIR(st
.st_mode
))
1067 cwget(struct cw
*cw
)
1069 enum okay rv
= STOP
;
1072 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1074 if (fchdir(cw
->cw_fd
) == -1) {
1085 cwret(struct cw
*cw
)
1087 enum okay rv
= STOP
;
1090 if (!fchdir(cw
->cw_fd
))
1097 cwrelse(struct cw
*cw
)
1104 #else /* !HAVE_FCHDIR */
1106 cwget(struct cw
*cw
)
1108 enum okay rv
= STOP
;
1111 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1118 cwret(struct cw
*cw
)
1120 enum okay rv
= STOP
;
1123 if (!chdir(cw
->cw_wd
))
1130 cwrelse(struct cw
*cw
)
1136 #endif /* !HAVE_FCHDIR */
1139 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1144 #ifdef HAVE_NATCH_CHAR
1145 maxlen
= MIN(maxlen
, blen
);
1146 for (rv
= 0; maxlen
> 0;) {
1147 int ml
= mblen(buf
, maxlen
);
1157 rv
= MIN(blen
, maxlen
);
1164 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1166 NATCH_CHAR( struct bidi_info bi
; )
1167 size_t rv
NATCH_CHAR( COMMA i
);
1174 #ifdef HAVE_NATCH_CHAR
1175 bidi_info_create(&bi
);
1176 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1181 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1186 if ((i
= bi
.bi_start
.l
) > 0) {
1187 memcpy(store
, bi
.bi_start
.s
, i
);
1193 while (maxlen
> 0) {
1194 int ml
= mblen(buf
, blen
);
1199 if (UICMP(z
, maxlen
, <, ml
))
1204 memcpy(store
, buf
, ml
);
1211 if ((i
= bi
.bi_end
.l
) > 0) {
1212 memcpy(store
, bi
.bi_end
.s
, i
);
1220 rv
= MIN(blen
, maxlen
);
1221 memcpy(store
, buf
, rv
);
1230 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1232 NATCH_CHAR( struct bidi_info bi
; )
1233 int col_orig
= col
, n
, sz
;
1234 bool_t isbidi
, isuni
, istab
, isrepl
;
1238 /* Bidi only on request and when there is 8-bit data */
1239 isbidi
= isuni
= FAL0
;
1240 #ifdef HAVE_NATCH_CHAR
1241 isuni
= ((options
& OPT_UNICODE
) != 0);
1242 bidi_info_create(&bi
);
1243 if (bi
.bi_start
.l
== 0)
1245 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1248 if ((size_t)col
>= bi
.bi_pad
)
1255 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1257 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1260 #ifdef HAVE_NATCH_CHAR
1262 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1263 np
+= bi
.bi_start
.l
;
1267 while (*cp
!= '\0') {
1269 #ifdef HAVE_C90AMEND1
1270 if (mb_cur_max
> 1) {
1275 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1277 else if (wc
== L
'\t') {
1278 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1281 } else if (iswprint(wc
)) {
1282 # ifndef HAVE_WCWIDTH
1283 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1285 if ((n
= wcwidth(wc
)) == -1)
1295 istab
= (*cp
== '\t');
1296 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1305 np
[0] = (char)0xEFu
;
1306 np
[1] = (char)0xBFu
;
1307 np
[2] = (char)0xBDu
;
1312 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1320 if (fill
&& col
!= 0) {
1322 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1323 memset(nb
, ' ', col
);
1325 memset(np
, ' ', col
);
1330 #ifdef HAVE_NATCH_CHAR
1332 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1338 if (cols_decr_used_or_null
!= NULL
)
1339 *cols_decr_used_or_null
-= col_orig
- col
;
1345 makeprint(struct str
const *in
, struct str
*out
)
1347 static int print_all_chars
= -1;
1349 char const *inp
, *maxp
;
1354 if (print_all_chars
== -1)
1355 print_all_chars
= ok_blook(print_all_chars
);
1357 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1361 if (print_all_chars
) {
1363 memcpy(outp
, inp
, out
->l
);
1367 #ifdef HAVE_NATCH_CHAR
1368 if (mb_cur_max
> 1) {
1369 char mbb
[MB_LEN_MAX
+ 1];
1372 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1375 while (inp
< maxp
) {
1377 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1383 /* FIXME Why mbtowc() resetting here?
1384 * FIXME what about ISO 2022-JP plus -- those
1385 * FIXME will loose shifts, then!
1386 * FIXME THUS - we'd need special "known points"
1387 * FIXME to do so - say, after a newline!!
1388 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1389 mbtowc(&wc
, NULL
, mb_cur_max
);
1390 wc
= isuni
? 0xFFFD : '?';
1395 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1397 if ((wc
& ~(wchar_t)037) == 0)
1398 wc
= isuni
? 0x2400 | wc
: '?';
1399 else if (wc
== 0177)
1400 wc
= isuni
? 0x2421 : '?';
1402 wc
= isuni
? 0x2426 : '?';
1404 if ((n
= wctomb(mbb
, wc
)) <= 0)
1407 assert(out
->l
< msz
);
1408 for (i
= 0; i
< n
; ++i
)
1412 #endif /* NATCH_CHAR */
1415 while (inp
< maxp
) {
1417 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1424 out
->s
[out
->l
] = '\0';
1429 delctrl(char *cp
, size_t len
)
1434 for (x
= y
= 0; x
< len
; ++x
)
1435 if (!cntrlchar(cp
[x
]))
1443 prstr(char const *s
)
1451 makeprint(&in
, &out
);
1452 rp
= savestrbuf(out
.s
, out
.l
);
1459 prout(char const *s
, size_t sz
, FILE *fp
)
1467 makeprint(&in
, &out
);
1468 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1475 putuc(int u
, int c
, FILE *fp
)
1481 #ifdef HAVE_NATCH_CHAR
1482 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1483 char mbb
[MB_LEN_MAX
];
1486 if ((n
= wctomb(mbb
, u
)) > 0) {
1488 for (i
= 0; i
< n
; ++i
)
1489 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1494 rv
= (putc('\0', fp
) != EOF
);
1499 rv
= (putc(c
, fp
) != EOF
);
1505 bidi_info_needed(char const *bdat
, size_t blen
)
1510 #ifdef HAVE_NATCH_CHAR
1511 if (options
& OPT_UNICODE
)
1513 /* TODO Checking for BIDI character: use S-CText fromutf8
1514 * TODO plus isrighttoleft (or whatever there will be)! */
1515 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1522 /* (Very very fuzzy, awaiting S-CText for good) */
1523 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1524 (c
>= 0xFB1D && c
<= 0xFEFC) ||
1525 (c
>= 0x10800 && c
<= 0x10C48) ||
1526 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1531 #endif /* HAVE_NATCH_CHAR */
1537 bidi_info_create(struct bidi_info
*bip
)
1539 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1540 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1541 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1542 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1543 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1544 NATCH_CHAR( char const *hb
; )
1547 memset(bip
, 0, sizeof *bip
);
1548 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1550 #ifdef HAVE_NATCH_CHAR
1551 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1557 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1563 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1564 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1567 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1575 colour_table_create(bool_t pager_used
)
1577 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1579 struct colour_table
*ct
;
1582 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1585 char *term
, *okterms
;
1587 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
1589 /* terminfo rocks: if we find "color", assume it's right */
1590 if (strstr(term
, "color") != NULL
)
1592 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1593 okterms
= UNCONST(COLOUR_TERMS
);
1594 okterms
= savestr(okterms
);
1597 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1598 if (!strncmp(u
.cp
, term
, i
))
1604 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1607 enum colourspec cspec
;
1610 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1611 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1612 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1613 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1614 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1617 for (i
= 0; i
< NELEM(map
); ++i
) {
1618 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1619 u
.ccp
= map
[i
].defval
;
1620 u
.cp
= _colour_iso6429(u
.ccp
);
1621 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1622 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1625 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1626 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1628 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1629 u
.ccp
= COLOUR_USER_HEADERS
;
1630 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1631 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1637 colour_put(FILE *fp
, enum colourspec cs
)
1640 if (colour_table
!= NULL
) {
1641 struct str
const *cp
= colour_get(cs
);
1643 fwrite(cp
->s
, cp
->l
, 1, fp
);
1649 colour_put_header(FILE *fp
, char const *name
)
1651 enum colourspec cs
= COLOURSPEC_HEADER
;
1652 struct str
const *uheads
;
1653 char *cp
, *cp_base
, *x
;
1657 if (colour_table
== NULL
)
1659 /* Normal header colours if there are no user headers */
1660 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1661 if (uheads
->s
== NULL
)
1664 /* Iterate over all entries in the *colour-user-headers* list */
1665 cp
= ac_alloc(uheads
->l
+1);
1666 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1668 namelen
= strlen(name
);
1669 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1670 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1671 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1672 cs
= COLOURSPEC_UHEADER
;
1684 colour_reset(FILE *fp
)
1687 if (colour_table
!= NULL
)
1688 fwrite("\033[0m", 4, 1, fp
);
1692 FL
struct str
const *
1693 colour_get(enum colourspec cs
)
1695 struct str
const *rv
= NULL
;
1698 if (colour_table
!= NULL
)
1699 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1704 #endif /* HAVE_COLOUR */
1707 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1714 assert(inlen
== 0 || inbuf
!= NULL
);
1716 if (inlen
== UIZ_MAX
)
1717 inlen
= strlen(inbuf
);
1720 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1722 if ((inlen
== 1 && *inbuf
== '1') ||
1723 !ascncasecmp(inbuf
, "true", inlen
) ||
1724 !ascncasecmp(inbuf
, "yes", inlen
) ||
1725 !ascncasecmp(inbuf
, "on", inlen
))
1727 else if ((inlen
== 1 && *inbuf
== '0') ||
1728 !ascncasecmp(inbuf
, "false", inlen
) ||
1729 !ascncasecmp(inbuf
, "no", inlen
) ||
1730 !ascncasecmp(inbuf
, "off", inlen
))
1733 dat
= ac_alloc(inlen
+1);
1734 memcpy(dat
, inbuf
, inlen
);
1737 sli
= strtol(dat
, &eptr
, 0);
1738 if (*dat
!= '\0' && *eptr
== '\0')
1751 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1756 assert(inlen
== 0 || inbuf
!= NULL
);
1758 if (inlen
== UIZ_MAX
)
1759 inlen
= strlen(inbuf
);
1762 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1763 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1764 !ascncasecmp(inbuf
, "ask-", 4) &&
1765 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1766 (options
& OPT_INTERACTIVE
)) {
1768 fputs(prompt
, stdout
);
1769 rv
= getapproval(NULL
, rv
);
1776 time_current_update(struct time_current
*tc
, bool_t full_update
)
1779 tc
->tc_time
= time(NULL
);
1781 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1782 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1783 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1789 n_err(char const *format
, ...)
1794 va_start(ap
, format
);
1796 if (options
& OPT_INTERACTIVE
)
1801 vfprintf(stderr
, format
, ap
);
1802 if (strchr(format
, '\n') != NULL
) /* TODO */
1810 n_verr(char const *format
, va_list ap
)
1812 /* Check use cases of PS_ERRORS_NOTED, too! */
1814 char buf
[LINESIZE
], *xbuf
= buf
;
1815 int l
= sizeof(buf
) -1;
1816 struct err_node
*enp
;
1818 LCTA(ERRORS_MAX
> 3);
1823 if (!(options
& OPT_INTERACTIVE
))
1826 vfprintf(stderr
, format
, ap
);
1832 l
= vsnprintf(xbuf
, l
+1, format
, ap
);
1834 assert(xbuf
== buf
);
1837 if (UICMP(z
, l
, >=, sizeof buf
)) {
1839 xbuf
= smalloc(l
+1);
1846 fwrite(xbuf
, 1, l
, stderr
);
1848 /* Link it into the `errors' message ring */
1849 if ((enp
= _err_tail
) == NULL
) {
1851 enp
= scalloc(1, sizeof *enp
);
1852 if (_err_tail
!= NULL
)
1853 _err_tail
->en_next
= enp
;
1858 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
1859 if (_err_cnt
< ERRORS_MAX
)
1862 _err_head
= (enp
= _err_head
)->en_next
;
1863 _err_tail
->en_next
= enp
;
1865 free(enp
->en_str
.s
);
1866 memset(enp
, 0, sizeof *enp
);
1869 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
1873 #endif /* HAVE_ERRORS */
1876 if (strchr(format
, '\n') != NULL
) /* TODO */
1882 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
1887 va_start(ap
, format
);
1888 vfprintf(stderr
, format
, ap
);
1894 n_perr(char const *msg
, int errval
)
1908 n_err(fmt
, msg
, strerror(errval
));
1913 n_alert(char const *format
, ...)
1918 n_err(_("Alert: "));
1920 va_start(ap
, format
);
1929 n_panic(char const *format
, ...)
1934 fprintf(stderr
, _("Panic: "));
1936 va_start(ap
, format
);
1937 vfprintf(stderr
, format
, ap
);
1943 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1951 struct err_node
*enp
;
1956 if (argv
[1] != NULL
)
1958 if (!asccasecmp(*argv
, "show"))
1960 if (!asccasecmp(*argv
, "clear"))
1963 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1967 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
1973 if (_err_head
== NULL
) {
1974 fprintf(stderr
, _("The error ring is empty\n"));
1978 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)
1980 fprintf(stderr
, _("tmpfile"));
1985 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
1986 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
1987 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
1988 /* We don't know wether last string ended with NL; be simple */
1991 page_or_print(fp
, 0);
1998 _err_cnt
= _err_cnt_noted
= 0;
1999 while ((enp
= _err_head
) != NULL
) {
2000 _err_head
= enp
->en_next
;
2001 free(enp
->en_str
.s
);
2006 #endif /* HAVE_ERRORS */
2010 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2017 if ((rv
= malloc(s
)) == NULL
)
2018 n_panic(_("no memory"));
2024 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2033 else if ((rv
= realloc(v
, s
)) == NULL
)
2034 n_panic(_("no memory"));
2040 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2047 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2048 n_panic(_("no memory"));
2053 #else /* !HAVE_DEBUG */
2054 CTA(sizeof(char) == sizeof(ui8_t
));
2056 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2057 # define _HOPE_SET(C) \
2059 union mem_ptr __xl, __xu;\
2060 struct mem_chunk *__xc;\
2061 __xl.p_p = (C).p_p;\
2062 __xc = __xl.p_c - 1;\
2065 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2066 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2067 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2068 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2069 __xu.p_ui8p += __xc->mc_size - 8;\
2070 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2071 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2072 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2073 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2075 # define _HOPE_GET_TRACE(C,BAD) \
2081 # define _HOPE_GET(C,BAD) \
2083 union mem_ptr __xl, __xu;\
2084 struct mem_chunk *__xc;\
2086 __xl.p_p = (C).p_p;\
2088 (C).p_cp = __xl.p_cp;\
2089 __xc = __xl.p_c - 1;\
2092 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2093 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2094 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2095 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2096 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2097 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2098 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2099 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2102 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2103 __xl.p_p, __i, mdbg_file, mdbg_line);\
2106 __xu.p_ui8p += __xc->mc_size - 8;\
2108 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2109 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2110 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2111 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2112 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2113 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2114 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2115 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2118 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2119 __xl.p_p, __i, mdbg_file, mdbg_line);\
2122 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2123 __xc->mc_file, __xc->mc_line);\
2127 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2134 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2135 n_panic("smalloc(): allocation too large: %s, line %d",
2136 mdbg_file
, mdbg_line
);
2137 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2139 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2140 n_panic(_("no memory"));
2141 p
.p_c
->mc_prev
= NULL
;
2142 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2143 _mem_list
->mc_prev
= p
.p_c
;
2144 p
.p_c
->mc_file
= mdbg_file
;
2145 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2146 p
.p_c
->mc_isfree
= FAL0
;
2147 p
.p_c
->mc_size
= (ui32_t
)s
;
2149 _mem_list
= p
.p_c
++;
2154 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2157 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2163 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2169 if ((p
.p_p
= v
) == NULL
) {
2170 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2174 _HOPE_GET(p
, isbad
);
2176 if (p
.p_c
->mc_isfree
) {
2177 n_err("srealloc(): region freed! At %s, line %d\n"
2178 "\tLast seen: %s, line %" PRIu16
"\n",
2179 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2183 if (p
.p_c
== _mem_list
)
2184 _mem_list
= p
.p_c
->mc_next
;
2186 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2187 if (p
.p_c
->mc_next
!= NULL
)
2188 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2191 _mem_mcur
-= p
.p_c
->mc_size
;
2195 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2196 n_panic("srealloc(): allocation too large: %s, line %d",
2197 mdbg_file
, mdbg_line
);
2198 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2200 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2201 n_panic(_("no memory"));
2202 p
.p_c
->mc_prev
= NULL
;
2203 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2204 _mem_list
->mc_prev
= p
.p_c
;
2205 p
.p_c
->mc_file
= mdbg_file
;
2206 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2207 p
.p_c
->mc_isfree
= FAL0
;
2208 p
.p_c
->mc_size
= (ui32_t
)s
;
2209 _mem_list
= p
.p_c
++;
2214 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2217 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2224 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2233 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2234 n_panic("scalloc(): allocation size too large: %s, line %d",
2235 mdbg_file
, mdbg_line
);
2236 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2237 n_panic("scalloc(): allocation count too large: %s, line %d",
2238 mdbg_file
, mdbg_line
);
2241 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2243 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2244 n_panic(_("no memory"));
2245 memset(p
.p_p
, 0, size
);
2246 p
.p_c
->mc_prev
= NULL
;
2247 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2248 _mem_list
->mc_prev
= p
.p_c
;
2249 p
.p_c
->mc_file
= mdbg_file
;
2250 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2251 p
.p_c
->mc_isfree
= FAL0
;
2252 p
.p_c
->mc_size
= (ui32_t
)size
;
2253 _mem_list
= p
.p_c
++;
2258 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2261 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2267 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2273 if ((p
.p_p
= v
) == NULL
) {
2274 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2278 _HOPE_GET(p
, isbad
);
2280 if (p
.p_c
->mc_isfree
) {
2281 n_err("sfree(): double-free avoided at %s, line %d\n"
2282 "\tLast seen: %s, line %" PRIu16
"\n",
2283 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2287 if (p
.p_c
== _mem_list
)
2288 _mem_list
= p
.p_c
->mc_next
;
2290 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2291 if (p
.p_c
->mc_next
!= NULL
)
2292 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2293 p
.p_c
->mc_isfree
= TRU1
;
2294 /* Trash contents (also see [21c05f8]) */
2295 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2298 _mem_mcur
-= p
.p_c
->mc_size
;
2300 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2301 p
.p_c
->mc_next
= _mem_free
;
2313 size_t c
= 0, s
= 0;
2318 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2321 s
+= p
.p_c
->mc_size
;
2322 p
.p_c
= p
.p_c
->mc_next
;
2327 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2328 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2333 c_smemtrace(void *v
)
2335 /* For _HOPE_GET() */
2336 char const * const mdbg_file
= "smemtrace()";
2337 int const mdbg_line
= -1;
2339 union mem_ptr p
, xp
;
2345 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
2347 n_perr("tmpfile", 0);
2351 fprintf(fp
, "Memory statistics:\n"
2352 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2353 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2354 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2356 fprintf(fp
, "Currently allocated memory chunks:\n");
2357 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2358 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2361 _HOPE_GET_TRACE(xp
, isbad
);
2362 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2363 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2364 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2368 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2369 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2370 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2373 _HOPE_GET_TRACE(xp
, isbad
);
2374 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2375 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2376 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2377 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2381 page_or_print(fp
, lines
);
2391 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2393 union mem_ptr p
, xp
;
2394 bool_t anybad
= FAL0
, isbad
;
2398 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2399 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2402 _HOPE_GET_TRACE(xp
, isbad
);
2406 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2407 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2408 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2412 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2413 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2416 _HOPE_GET_TRACE(xp
, isbad
);
2420 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2421 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2422 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2429 # endif /* HAVE_DEVEL */
2433 # undef _HOPE_GET_TRACE
2435 #endif /* HAVE_DEBUG */