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
)];
78 struct shvar_stack
*shs_next
; /* Outer stack frame */
79 char const *shs_value
; /* Remaining value to expand */
80 size_t shs_len
; /* gth of .shs_dat this level */
81 char const *shs_dat
; /* Result data of this level */
82 bool_t
*shs_err
; /* Or NULL */
83 bool_t shs_bsesc
; /* Shall backslash escaping be performed */
88 struct err_node
*en_next
;
95 struct mem_chunk
*mc_prev
;
96 struct mem_chunk
*mc_next
;
106 struct mem_chunk
*p_c
;
112 #ifndef HAVE_POSIX_RANDOM
113 static union rand_state
*_rand
;
116 /* {hold,rele}_all_sigs() */
117 static size_t _alls_depth
;
118 static sigset_t _alls_nset
, _alls_oset
;
120 /* {hold,rele}_sigs() */
121 static size_t _hold_sigdepth
;
122 static sigset_t _hold_nset
, _hold_oset
;
124 /* NYD, memory pool debug */
126 static ui32_t _nyd_curr
, _nyd_level
;
127 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
130 /* Error ring, for `errors' */
132 static struct err_node
*_err_head
, *_err_tail
;
133 static size_t _err_cnt
, _err_cnt_noted
;
137 static size_t _mem_aall
, _mem_acur
, _mem_amax
,
138 _mem_mall
, _mem_mcur
, _mem_mmax
;
140 static struct mem_chunk
*_mem_list
, *_mem_free
;
143 /* Our ARC4 random generator with its completely unacademical pseudo
144 * initialization (shall /dev/urandom fail) */
145 #ifndef HAVE_POSIX_RANDOM
146 static void _rand_init(void);
147 static ui32_t
_rand_weak(ui32_t seed
);
148 SINLINE ui8_t
_rand_get8(void);
152 static void _nyd_print(int fd
, struct nyd_info
*nip
);
155 /* Perform shell variable expansion */
156 static char * _sh_exp_var(struct shvar_stack
*shsp
);
158 #ifndef HAVE_POSIX_RANDOM
162 # ifdef HAVE_CLOCK_GETTIME
167 union {int fd
; size_t i
;} u
;
171 _rand
= smalloc(sizeof *_rand
);
173 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
174 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
181 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
182 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
185 # ifdef HAVE_CLOCK_GETTIME
186 clock_gettime(CLOCK_REALTIME
, &ts
);
187 t
= (ui32_t
)ts
.tv_nsec
;
189 gettimeofday(&ts
, NULL
);
190 t
= (ui32_t
)ts
.tv_usec
;
193 t
= (t
>> 16) | (t
<< 16);
194 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
195 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
196 if (rnd
== 7 || rnd
== 17)
197 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
198 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
199 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
200 seed
^= _rand_weak(_rand
->b32
[k
]);
202 seed
^= nextprime(seed
);
206 for (u
.i
= 11 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
213 _rand_weak(ui32_t seed
)
215 /* From "Random number generators: good ones are hard to find",
216 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
217 * October 1988, p. 1195.
218 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
225 seed
= (seed
* 16807) - (hi
* 2836);
226 if ((si32_t
)seed
< 0)
236 si
= _rand
->a
._dat
[++_rand
->a
._i
];
237 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
238 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
239 _rand
->a
._dat
[_rand
->a
._j
] = si
;
240 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
242 #endif /* HAVE_POSIX_RANDOM */
246 _nyd_print(int fd
, struct nyd_info
*nip
)
249 union {int i
; size_t z
;} u
;
251 u
.i
= snprintf(buf
, sizeof buf
,
252 "%c [%2" PRIu32
"] %.25s (%.16s:%" PRIu32
")\n",
253 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
254 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
257 if (u
.z
> sizeof buf
)
258 u
.z
= sizeof buf
- 1; /* (Skip \0) */
265 _sh_exp_var(struct shvar_stack
*shsp
)
267 struct shvar_stack next
, *np
, *tmp
;
269 char lc
, c
, *cp
, *rv
;
273 if (*(vp
= shsp
->shs_value
) != '$') {
274 bool_t bsesc
= shsp
->shs_bsesc
;
275 union {bool_t hadbs
; char c
;} u
= {FAL0
};
278 for (lc
= '\0', i
= 0; ((c
= *vp
) != '\0'); ++i
, ++vp
) {
279 if (c
== '$' && lc
!= '\\')
283 lc
= (lc
== '\\') ? (u
.hadbs
= TRU1
, '\0') : c
;
288 shsp
->shs_dat
= cp
= savestrbuf(shsp
->shs_dat
, i
);
290 for (lc
= '\0', rv
= cp
; (u
.c
= *cp
++) != '\0';) {
291 if (u
.c
!= '\\' || lc
== '\\')
293 lc
= (lc
== '\\') ? '\0' : u
.c
;
297 shsp
->shs_len
= PTR2SIZE(rv
- shsp
->shs_dat
);
300 if ((lc
= (*++vp
== '{')))
304 * Environment variable names used by the utilities in the Shell and
305 * Utilities volume of POSIX.1-2008 consist solely of uppercase
306 * letters, digits, and the <underscore> ('_') from the characters
307 * defined in Portable Character Set and do not begin with a digit.
308 * Other characters may be permitted by an implementation;
309 * applications shall tolerate the presence of such names. */
311 for (i
= 0; (c
= *vp
) != '\0'; ++i
, ++vp
)
312 if (!alnumchar(c
) && c
!= '_')
317 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
319 shsp
->shs_len
= strlen(shsp
->shs_value
);
320 shsp
->shs_dat
= shsp
->shs_value
;
321 if (shsp
->shs_err
!= NULL
)
322 *shsp
->shs_err
= TRU1
;
329 if ((cp
= vok_vlook(savestrbuf(shsp
->shs_dat
, i
))) != NULL
)
330 shsp
->shs_len
= strlen(shsp
->shs_dat
= cp
);
335 /* That level made the great and completed encoding. Build result */
337 for (i
= 0, np
= shsp
, shsp
= NULL
; np
!= NULL
;) {
345 cp
= rv
= salloc(i
+1);
346 while (shsp
!= NULL
) {
348 shsp
= shsp
->shs_next
;
349 memcpy(cp
, np
->shs_dat
, np
->shs_len
);
358 memset(&next
, 0, sizeof next
);
359 next
.shs_next
= shsp
;
361 next
.shs_err
= shsp
->shs_err
;
362 next
.shs_bsesc
= shsp
->shs_bsesc
;
363 rv
= _sh_exp_var(&next
);
371 kill(getpid(), signo
);
376 safe_signal(int signum
, sighandler_type handler
)
378 struct sigaction nact
, oact
;
382 nact
.sa_handler
= handler
;
383 sigemptyset(&nact
.sa_mask
);
386 nact
.sa_flags
|= SA_RESTART
;
388 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
397 if (_alls_depth
++ == 0) {
398 sigfillset(&_alls_nset
);
399 sigdelset(&_alls_nset
, SIGABRT
);
401 sigdelset(&_alls_nset
, SIGBUS
);
403 sigdelset(&_alls_nset
, SIGCHLD
);
404 sigdelset(&_alls_nset
, SIGFPE
);
405 sigdelset(&_alls_nset
, SIGILL
);
406 sigdelset(&_alls_nset
, SIGKILL
);
407 sigdelset(&_alls_nset
, SIGSEGV
);
408 sigdelset(&_alls_nset
, SIGSTOP
);
409 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
418 if (--_alls_depth
== 0)
419 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
427 if (_hold_sigdepth
++ == 0) {
428 sigemptyset(&_hold_nset
);
429 sigaddset(&_hold_nset
, SIGHUP
);
430 sigaddset(&_hold_nset
, SIGINT
);
431 sigaddset(&_hold_nset
, SIGQUIT
);
432 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
441 if (--_hold_sigdepth
== 0)
442 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
448 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
450 struct nyd_info
*nip
= _nyd_infos
;
452 if (_nyd_curr
!= NELEM(_nyd_infos
))
458 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
459 nip
->ni_level
= ((act
== 0) ? _nyd_level
460 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
464 _nyd_oncrash(int signo
)
466 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
467 struct sigaction xact
;
471 struct nyd_info
*nip
;
473 LCTA(sizeof("./") -1 + sizeof(UAGENT
) -1 + sizeof(".dat") < PATH_MAX
);
475 xact
.sa_handler
= SIG_DFL
;
476 sigemptyset(&xact
.sa_mask
);
478 sigaction(signo
, &xact
, NULL
);
481 fnl
= sizeof(UAGENT
) -1;
483 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
484 (cp
= pathbuf
)[0] = '.';
487 memcpy(cp
= pathbuf
, tempdir
, i
);
488 cp
[i
++] = '/'; /* xxx pathsep */
489 memcpy(cp
+= i
, UAGENT
, fnl
);
491 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
492 fnl
= i
+ sizeof(".dat") -1;
494 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
498 # define _X(X) (X), sizeof(X) -1
499 write(fd
, _X("\n\nNYD: program dying due to signal "));
501 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
505 *--cp
= "0123456789"[i
% 10];
508 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
510 write(fd
, _X(":\n"));
512 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
513 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
514 _nyd_print(fd
, nip
++);
515 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
516 _nyd_print(fd
, nip
++);
518 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
520 if (fd
!= STDERR_FILENO
) {
521 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
522 write(STDERR_FILENO
, pathbuf
, fnl
);
523 write(STDERR_FILENO
, _X("\n"));
530 sigaddset(&xset
, signo
);
531 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
536 #endif /* HAVE_NYD */
539 touch(struct message
*mp
)
542 mp
->m_flag
|= MTOUCH
;
543 if (!(mp
->m_flag
& MREAD
))
544 mp
->m_flag
|= MREAD
| MSTATUS
;
549 is_dir(char const *name
)
556 if (!stat(name
, &sbuf
)) {
557 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
559 } else if (errno
!= EINTR
)
566 argcount(char **argv
)
571 for (ap
= argv
; *ap
++ != NULL
;)
574 return (int)PTR2SIZE(ap
- argv
- 1);
584 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
585 s
= scrnheight
- 2; /* XXX no magics */
591 get_pager(char const **env_addon
)
596 cp
= ok_vlook(PAGER
);
597 if (cp
== NULL
|| *cp
== '\0')
600 if (env_addon
!= NULL
) {
602 if (strstr(cp
, "less") != NULL
) {
603 if (!env_blook("LESS", TRU1
))
604 *env_addon
= "LESS=FRSXi";
605 } else if (strstr(cp
, "lv") != NULL
) {
606 if (!env_blook("LV", TRU1
))
607 *env_addon
= "LV=-c";
615 page_or_print(FILE *fp
, size_t lines
)
623 if ((options
& OPT_INTERACTIVE
) && (cp
= ok_vlook(crt
)) != NULL
) {
625 union {sl_i sli
; size_t rows
;} u
;
627 u
.sli
= strtol(cp
, &eptr
, 0);
628 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
629 ? (size_t)u
.sli
: (size_t)scrnheight
;
631 if (u
.rows
> 0 && lines
== 0) {
632 while ((c
= getc(fp
)) != EOF
)
633 if (c
== '\n' && ++lines
>= u
.rows
)
638 if (lines
>= u
.rows
) {
639 run_command(get_pager(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
640 NULL
, NULL
, NULL
, NULL
);
645 while ((c
= getc(fp
)) != EOF
)
652 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
658 enum protocol rv
= PROTO_UNKNOWN
;
661 temporary_protocol_ext
= NULL
;
663 if (name
[0] == '%' && name
[1] == ':')
665 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
669 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
670 if (!strncmp(name
, "pop3://", 7)) {
674 n_err(_("No POP3 support compiled in\n"));
676 } else if (!strncmp(name
, "pop3s://", 8)) {
677 #if defined HAVE_POP3 && defined HAVE_SSL
681 n_err(_("No POP3 support compiled in\n"));
684 n_err(_("No SSL support compiled in\n"));
687 } else if (!strncmp(name
, "imap://", 7)) {
691 fprintf(stderr
, _("No IMAP support compiled in.\n"));
693 } else if (!strncmp(name
, "imaps://", 8)) {
694 #if defined HAVE_IMAP && defined HAVE_SSL
698 fprintf(stderr
, _("No IMAP support compiled in.\n"));
701 fprintf(stderr
, _("No SSL support compiled in.\n"));
708 /* TODO This is the de facto maildir code and thus belongs into there!
709 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
710 * TODO or (more likely) in addition to *newfolders*) */
713 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
714 memcpy(np
, name
, sz
+ 1);
715 if (!stat(name
, &st
)) {
716 if (S_ISDIR(st
.st_mode
) &&
717 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
718 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
719 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
722 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
723 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
724 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
725 temporary_protocol_ext
= cp
;
726 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
736 torek_hash(char const *name
)
738 /* Chris Torek's hash.
739 * NOTE: need to change *at least* create-okey-map.pl when changing the
744 while (*name
!= '\0') {
753 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
760 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
761 if ((g
= h
& 0xf0000000) != 0) {
773 static ui32_t
const primes
[] = {
774 5, 11, 23, 47, 97, 157, 283,
775 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
776 131071, 262139, 524287, 1048573, 2097143, 4194301,
777 8388593, 16777213, 33554393, 67108859, 134217689,
778 268435399, 536870909, 1073741789, 2147483647
784 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
785 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
786 : NELEM(primes
) / 2));
788 if ((mprime
= primes
[i
]) > n
)
790 while (++i
< NELEM(primes
));
791 if (i
== NELEM(primes
) && mprime
< n
)
798 n_shell_expand_tilde(char const *s
, bool_t
*err_or_null
)
812 if (*(rp
= s
+ 1) == '/' || *rp
== '\0')
815 if ((rp
= strchr(s
+ 1, '/')) == NULL
)
816 rp
= (np
= UNCONST(s
)) + 1;
818 nl
= PTR2SIZE(rp
- s
);
819 np
= savestrbuf(s
, nl
);
822 if ((pwp
= getpwnam(np
)) == NULL
) {
831 rv
= salloc(nl
+ 1 + rl
+1);
834 memcpy(rv
+ nl
, rp
, rl
);
843 if (err_or_null
!= NULL
)
850 n_shell_expand_var(char const *s
, bool_t bsescape
, bool_t
*err_or_null
)
852 struct shvar_stack top
;
856 memset(&top
, 0, sizeof top
);
859 if ((top
.shs_err
= err_or_null
) != NULL
)
861 top
.shs_bsesc
= bsescape
;
862 rv
= _sh_exp_var(&top
);
868 n_shell_expand_escape(char const **s
, bool_t use_nail_extensions
)
876 if ((c
= *xs
& 0xFF) == '\0')
882 switch ((c
= *xs
& 0xFF)) {
884 case 'a': c
= '\a'; break;
885 case 'b': c
= '\b'; break;
886 case 'c': c
= PROMPT_STOP
; break;
887 case 'f': c
= '\f'; break;
888 case 'n': c
= '\n'; break;
889 case 'r': c
= '\r'; break;
890 case 't': c
= '\t'; break;
891 case 'v': c
= '\v'; break;
893 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
898 /* S-nail extension for nice (get)prompt(()) support */
903 if (use_nail_extensions
) {
905 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
906 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
907 case '$': c
= PROMPT_DOLLAR
; break;
908 case '@': c
= PROMPT_AT
; break;
914 /* A sole <backslash> at EOS is treated as-is! */
928 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
930 static char buf
[PROMPT_BUFFER_SIZE
];
933 char const *ccp_base
, *ccp
;
934 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
935 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
938 /* No other place to place this */
940 if (options
& OPT_INTERACTIVE
) {
941 if (!(pstate
& PS_ERRORS_NOTED
) && _err_head
!= NULL
) {
942 pstate
|= PS_ERRORS_NOTED
;
943 fprintf(stderr
, _("There are new messages in the error message ring "
944 "(denoted by \"#ERR#\")\n"
945 " The `errors' command manages this message ring\n"));
948 if ((trigger
= (_err_cnt_noted
!= _err_cnt
)))
949 _err_cnt_noted
= _err_cnt
;
955 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
965 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
967 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
969 dfmaxlen
= 0; /* keep CC happy */
973 NATCH_CHAR( cclen
= cclen_base
; )
974 maxlen
= sizeof(buf
) -1;
982 #ifdef HAVE_NATCH_CHAR
983 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
992 } else if ((l
= c
) > 1) {
1002 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
1008 if (c
== 0 || c
== PROMPT_STOP
)
1012 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
1015 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
1035 nodename(int mayoverride
)
1037 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1042 # ifdef HAVE_GETADDRINFO
1043 struct addrinfo hints
, *res
;
1045 struct hostent
*hent
;
1050 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
1052 } else if ((hn
= sys_hostname
) == NULL
) {
1056 # ifdef HAVE_GETADDRINFO
1057 memset(&hints
, 0, sizeof hints
);
1058 hints
.ai_family
= AF_UNSPEC
;
1059 hints
.ai_flags
= AI_CANONNAME
;
1060 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
1061 if (res
->ai_canonname
!= NULL
) {
1062 size_t l
= strlen(res
->ai_canonname
) +1;
1065 memcpy(hn
, res
->ai_canonname
, l
);
1070 hent
= gethostbyname(hn
);
1075 sys_hostname
= sstrdup(hn
);
1076 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1077 if (hn
!= ut
.nodename
)
1083 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
1085 hostname
= sstrdup(hn
);
1091 getrandstring(size_t length
)
1098 #ifndef HAVE_POSIX_RANDOM
1103 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1104 * with PAD stripped is still longer than what the user requests, easy way */
1105 data
= ac_alloc(i
= length
+ 3);
1107 #ifndef HAVE_POSIX_RANDOM
1109 data
[i
] = (char)_rand_get8();
1114 union {ui32_t i4
; char c
[4];} r
;
1117 r
.i4
= (ui32_t
)arc4random();
1118 switch ((j
= i
& 3)) {
1119 case 0: cp
[3] = r
.c
[3]; j
= 4;
1120 case 3: cp
[2] = r
.c
[2];
1121 case 2: cp
[1] = r
.c
[1];
1122 default: cp
[0] = r
.c
[0]; break;
1130 b64_encode_buf(&b64
, data
, length
+ 3,
1131 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1134 assert(b64
.l
>= length
);
1135 b64
.s
[length
] = '\0';
1141 makedir(char const *name
)
1144 enum okay rv
= STOP
;
1147 if (!mkdir(name
, 0700))
1151 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1152 S_ISDIR(st
.st_mode
))
1161 cwget(struct cw
*cw
)
1163 enum okay rv
= STOP
;
1166 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1168 if (fchdir(cw
->cw_fd
) == -1) {
1179 cwret(struct cw
*cw
)
1181 enum okay rv
= STOP
;
1184 if (!fchdir(cw
->cw_fd
))
1191 cwrelse(struct cw
*cw
)
1198 #else /* !HAVE_FCHDIR */
1200 cwget(struct cw
*cw
)
1202 enum okay rv
= STOP
;
1205 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1212 cwret(struct cw
*cw
)
1214 enum okay rv
= STOP
;
1217 if (!chdir(cw
->cw_wd
))
1224 cwrelse(struct cw
*cw
)
1230 #endif /* !HAVE_FCHDIR */
1233 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1238 #ifdef HAVE_NATCH_CHAR
1239 maxlen
= MIN(maxlen
, blen
);
1240 for (rv
= 0; maxlen
> 0;) {
1241 int ml
= mblen(buf
, maxlen
);
1251 rv
= MIN(blen
, maxlen
);
1258 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1260 NATCH_CHAR( struct bidi_info bi
; )
1261 size_t rv
NATCH_CHAR( COMMA i
);
1268 #ifdef HAVE_NATCH_CHAR
1269 bidi_info_create(&bi
);
1270 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1275 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1280 if ((i
= bi
.bi_start
.l
) > 0) {
1281 memcpy(store
, bi
.bi_start
.s
, i
);
1287 while (maxlen
> 0) {
1288 int ml
= mblen(buf
, blen
);
1293 if (UICMP(z
, maxlen
, <, ml
))
1298 memcpy(store
, buf
, ml
);
1305 if ((i
= bi
.bi_end
.l
) > 0) {
1306 memcpy(store
, bi
.bi_end
.s
, i
);
1314 rv
= MIN(blen
, maxlen
);
1315 memcpy(store
, buf
, rv
);
1324 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1326 NATCH_CHAR( struct bidi_info bi
; )
1327 int col_orig
= col
, n
, sz
;
1328 bool_t isbidi
, isuni
, istab
, isrepl
;
1332 /* Bidi only on request and when there is 8-bit data */
1333 isbidi
= isuni
= FAL0
;
1334 #ifdef HAVE_NATCH_CHAR
1335 isuni
= ((options
& OPT_UNICODE
) != 0);
1336 bidi_info_create(&bi
);
1337 if (bi
.bi_start
.l
== 0)
1339 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1342 if ((size_t)col
>= bi
.bi_pad
)
1349 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1351 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1354 #ifdef HAVE_NATCH_CHAR
1356 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1357 np
+= bi
.bi_start
.l
;
1361 while (*cp
!= '\0') {
1363 #ifdef HAVE_C90AMEND1
1364 if (mb_cur_max
> 1) {
1369 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1371 else if (wc
== L
'\t') {
1372 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1375 } else if (iswprint(wc
)) {
1376 # ifndef HAVE_WCWIDTH
1377 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1379 if ((n
= wcwidth(wc
)) == -1)
1389 istab
= (*cp
== '\t');
1390 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1399 np
[0] = (char)0xEFu
;
1400 np
[1] = (char)0xBFu
;
1401 np
[2] = (char)0xBDu
;
1406 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1414 if (fill
&& col
!= 0) {
1416 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1417 memset(nb
, ' ', col
);
1419 memset(np
, ' ', col
);
1424 #ifdef HAVE_NATCH_CHAR
1426 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1432 if (cols_decr_used_or_null
!= NULL
)
1433 *cols_decr_used_or_null
-= col_orig
- col
;
1439 makeprint(struct str
const *in
, struct str
*out
)
1441 static int print_all_chars
= -1;
1443 char const *inp
, *maxp
;
1448 if (print_all_chars
== -1)
1449 print_all_chars
= ok_blook(print_all_chars
);
1451 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1455 if (print_all_chars
) {
1457 memcpy(outp
, inp
, out
->l
);
1461 #ifdef HAVE_NATCH_CHAR
1462 if (mb_cur_max
> 1) {
1463 char mbb
[MB_LEN_MAX
+ 1];
1466 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1469 while (inp
< maxp
) {
1471 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1477 /* FIXME Why mbtowc() resetting here?
1478 * FIXME what about ISO 2022-JP plus -- those
1479 * FIXME will loose shifts, then!
1480 * FIXME THUS - we'd need special "known points"
1481 * FIXME to do so - say, after a newline!!
1482 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1483 mbtowc(&wc
, NULL
, mb_cur_max
);
1484 wc
= isuni
? 0xFFFD : '?';
1489 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1491 if ((wc
& ~(wchar_t)037) == 0)
1492 wc
= isuni
? 0x2400 | wc
: '?';
1493 else if (wc
== 0177)
1494 wc
= isuni
? 0x2421 : '?';
1496 wc
= isuni
? 0x2426 : '?';
1497 }else if(isuni
){ /* TODO ctext */
1498 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1499 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1501 /* And some zero-width messes */
1502 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1504 /* Oh about the ISO C wide character interfaces, baby! */
1508 if ((n
= wctomb(mbb
, wc
)) <= 0)
1511 assert(out
->l
< msz
);
1512 for (i
= 0; i
< n
; ++i
)
1516 #endif /* NATCH_CHAR */
1519 while (inp
< maxp
) {
1521 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1528 out
->s
[out
->l
] = '\0';
1533 delctrl(char *cp
, size_t len
)
1538 for (x
= y
= 0; x
< len
; ++x
)
1539 if (!cntrlchar(cp
[x
]))
1547 prstr(char const *s
)
1555 makeprint(&in
, &out
);
1556 rp
= savestrbuf(out
.s
, out
.l
);
1563 prout(char const *s
, size_t sz
, FILE *fp
)
1571 makeprint(&in
, &out
);
1572 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1579 putuc(int u
, int c
, FILE *fp
)
1585 #ifdef HAVE_NATCH_CHAR
1586 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1587 char mbb
[MB_LEN_MAX
];
1590 if ((n
= wctomb(mbb
, u
)) > 0) {
1592 for (i
= 0; i
< n
; ++i
)
1593 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1598 rv
= (putc('\0', fp
) != EOF
);
1603 rv
= (putc(c
, fp
) != EOF
);
1609 bidi_info_needed(char const *bdat
, size_t blen
)
1614 #ifdef HAVE_NATCH_CHAR
1615 if (options
& OPT_UNICODE
)
1617 /* TODO Checking for BIDI character: use S-CText fromutf8
1618 * TODO plus isrighttoleft (or whatever there will be)! */
1619 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1626 /* (Very very fuzzy, awaiting S-CText for good) */
1627 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1628 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1629 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1630 (c
>= 0x10800 && c
<= 0x10C48) ||
1631 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1636 #endif /* HAVE_NATCH_CHAR */
1642 bidi_info_create(struct bidi_info
*bip
)
1644 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1645 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1646 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1647 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1648 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1649 NATCH_CHAR( char const *hb
; )
1652 memset(bip
, 0, sizeof *bip
);
1653 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1655 #ifdef HAVE_NATCH_CHAR
1656 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1662 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1668 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1669 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1672 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1679 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1686 assert(inlen
== 0 || inbuf
!= NULL
);
1688 if (inlen
== UIZ_MAX
)
1689 inlen
= strlen(inbuf
);
1692 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1694 if ((inlen
== 1 && *inbuf
== '1') ||
1695 !ascncasecmp(inbuf
, "true", inlen
) ||
1696 !ascncasecmp(inbuf
, "yes", inlen
) ||
1697 !ascncasecmp(inbuf
, "on", inlen
))
1699 else if ((inlen
== 1 && *inbuf
== '0') ||
1700 !ascncasecmp(inbuf
, "false", inlen
) ||
1701 !ascncasecmp(inbuf
, "no", inlen
) ||
1702 !ascncasecmp(inbuf
, "off", inlen
))
1705 dat
= ac_alloc(inlen
+1);
1706 memcpy(dat
, inbuf
, inlen
);
1709 sli
= strtol(dat
, &eptr
, 0);
1710 if (*dat
!= '\0' && *eptr
== '\0')
1723 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1728 assert(inlen
== 0 || inbuf
!= NULL
);
1730 if (inlen
== UIZ_MAX
)
1731 inlen
= strlen(inbuf
);
1734 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1735 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1736 !ascncasecmp(inbuf
, "ask-", 4) &&
1737 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1738 (options
& OPT_INTERACTIVE
)) {
1740 fputs(prompt
, stdout
);
1741 rv
= getapproval(NULL
, rv
);
1750 #ifdef HAVE_CLOCK_GETTIME
1752 #elif defined HAVE_GETTIMEOFDAY
1758 #ifdef HAVE_CLOCK_GETTIME
1759 clock_gettime(CLOCK_REALTIME
, &ts
);
1760 rv
= (time_t)ts
.tv_sec
;
1761 #elif defined HAVE_GETTIMEOFDAY
1762 gettimeofday(&ts
, NULL
);
1763 rv
= (time_t)ts
.tv_sec
;
1772 time_current_update(struct time_current
*tc
, bool_t full_update
)
1775 tc
->tc_time
= n_time_epoch();
1777 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1778 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1779 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1785 n_err(char const *format
, ...)
1790 va_start(ap
, format
);
1792 if (options
& OPT_INTERACTIVE
)
1797 vfprintf(stderr
, format
, ap
);
1798 if (strchr(format
, '\n') != NULL
) /* TODO */
1806 n_verr(char const *format
, va_list ap
)
1808 /* Check use cases of PS_ERRORS_NOTED, too! */
1810 char buf
[LINESIZE
], *xbuf
;
1812 struct err_node
*enp
;
1814 LCTA(ERRORS_MAX
> 3);
1819 if (!(options
& OPT_INTERACTIVE
))
1822 vfprintf(stderr
, format
, ap
);
1830 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1833 if (UICMP(z
, l
, >=, lmax
)) {
1834 /* FIXME Cannot reuse va_list
1836 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1841 fwrite(xbuf
, 1, l
, stderr
);
1843 /* Link it into the `errors' message ring */
1844 if ((enp
= _err_tail
) == NULL
) {
1846 enp
= scalloc(1, sizeof *enp
);
1847 if (_err_tail
!= NULL
)
1848 _err_tail
->en_next
= enp
;
1853 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
1854 if (_err_cnt
< ERRORS_MAX
)
1857 _err_head
= (enp
= _err_head
)->en_next
;
1858 _err_tail
->en_next
= enp
;
1860 free(enp
->en_str
.s
);
1861 memset(enp
, 0, sizeof *enp
);
1864 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
1868 #endif /* HAVE_ERRORS */
1871 if (strchr(format
, '\n') != NULL
) /* TODO */
1877 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
1882 va_start(ap
, format
);
1883 vfprintf(stderr
, format
, ap
);
1889 n_perr(char const *msg
, int errval
)
1903 n_err(fmt
, msg
, strerror(errval
));
1908 n_alert(char const *format
, ...)
1913 n_err(_("Alert: "));
1915 va_start(ap
, format
);
1924 n_panic(char const *format
, ...)
1929 fprintf(stderr
, _("Panic: "));
1931 va_start(ap
, format
);
1932 vfprintf(stderr
, format
, ap
);
1938 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1946 struct err_node
*enp
;
1951 if (argv
[1] != NULL
)
1953 if (!asccasecmp(*argv
, "show"))
1955 if (!asccasecmp(*argv
, "clear"))
1958 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1962 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
1968 if (_err_head
== NULL
) {
1969 fprintf(stderr
, _("The error ring is empty\n"));
1973 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1975 fprintf(stderr
, _("tmpfile"));
1980 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
1981 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
1982 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
1983 /* We don't know wether last string ended with NL; be simple */
1986 page_or_print(fp
, 0);
1993 _err_cnt
= _err_cnt_noted
= 0;
1994 while ((enp
= _err_head
) != NULL
) {
1995 _err_head
= enp
->en_next
;
1996 free(enp
->en_str
.s
);
2001 #endif /* HAVE_ERRORS */
2005 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2012 if ((rv
= malloc(s
)) == NULL
)
2013 n_panic(_("no memory"));
2019 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2028 else if ((rv
= realloc(v
, s
)) == NULL
)
2029 n_panic(_("no memory"));
2035 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2042 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2043 n_panic(_("no memory"));
2048 #else /* !HAVE_DEBUG */
2049 CTA(sizeof(char) == sizeof(ui8_t
));
2051 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2052 # define _HOPE_SET(C) \
2054 union mem_ptr __xl, __xu;\
2055 struct mem_chunk *__xc;\
2056 __xl.p_p = (C).p_p;\
2057 __xc = __xl.p_c - 1;\
2060 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2061 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2062 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2063 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2064 __xu.p_ui8p += __xc->mc_size - 8;\
2065 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2066 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2067 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2068 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2070 # define _HOPE_GET_TRACE(C,BAD) \
2076 # define _HOPE_GET(C,BAD) \
2078 union mem_ptr __xl, __xu;\
2079 struct mem_chunk *__xc;\
2081 __xl.p_p = (C).p_p;\
2083 (C).p_cp = __xl.p_cp;\
2084 __xc = __xl.p_c - 1;\
2087 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2088 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2089 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2090 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2091 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2092 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2093 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2094 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2097 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2098 __xl.p_p, __i, mdbg_file, mdbg_line);\
2101 __xu.p_ui8p += __xc->mc_size - 8;\
2103 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2104 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2105 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2106 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2107 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2108 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2109 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2110 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2113 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2114 __xl.p_p, __i, mdbg_file, mdbg_line);\
2117 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2118 __xc->mc_file, __xc->mc_line);\
2122 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2129 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2130 n_panic("smalloc(): allocation too large: %s, line %d",
2131 mdbg_file
, mdbg_line
);
2132 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2134 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2135 n_panic(_("no memory"));
2136 p
.p_c
->mc_prev
= NULL
;
2137 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2138 _mem_list
->mc_prev
= p
.p_c
;
2139 p
.p_c
->mc_file
= mdbg_file
;
2140 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2141 p
.p_c
->mc_isfree
= FAL0
;
2142 p
.p_c
->mc_size
= (ui32_t
)s
;
2144 _mem_list
= p
.p_c
++;
2149 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2152 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2158 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2164 if ((p
.p_p
= v
) == NULL
) {
2165 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2169 _HOPE_GET(p
, isbad
);
2171 if (p
.p_c
->mc_isfree
) {
2172 n_err("srealloc(): region freed! At %s, line %d\n"
2173 "\tLast seen: %s, line %" PRIu16
"\n",
2174 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2178 if (p
.p_c
== _mem_list
)
2179 _mem_list
= p
.p_c
->mc_next
;
2181 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2182 if (p
.p_c
->mc_next
!= NULL
)
2183 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2186 _mem_mcur
-= p
.p_c
->mc_size
;
2190 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2191 n_panic("srealloc(): allocation too large: %s, line %d",
2192 mdbg_file
, mdbg_line
);
2193 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2195 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2196 n_panic(_("no memory"));
2197 p
.p_c
->mc_prev
= NULL
;
2198 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2199 _mem_list
->mc_prev
= p
.p_c
;
2200 p
.p_c
->mc_file
= mdbg_file
;
2201 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2202 p
.p_c
->mc_isfree
= FAL0
;
2203 p
.p_c
->mc_size
= (ui32_t
)s
;
2204 _mem_list
= p
.p_c
++;
2209 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2212 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2219 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2228 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2229 n_panic("scalloc(): allocation size too large: %s, line %d",
2230 mdbg_file
, mdbg_line
);
2231 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2232 n_panic("scalloc(): allocation count too large: %s, line %d",
2233 mdbg_file
, mdbg_line
);
2236 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2238 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2239 n_panic(_("no memory"));
2240 memset(p
.p_p
, 0, size
);
2241 p
.p_c
->mc_prev
= NULL
;
2242 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2243 _mem_list
->mc_prev
= p
.p_c
;
2244 p
.p_c
->mc_file
= mdbg_file
;
2245 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2246 p
.p_c
->mc_isfree
= FAL0
;
2247 p
.p_c
->mc_size
= (ui32_t
)size
;
2248 _mem_list
= p
.p_c
++;
2253 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2256 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2262 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2268 if ((p
.p_p
= v
) == NULL
) {
2269 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2273 _HOPE_GET(p
, isbad
);
2275 if (p
.p_c
->mc_isfree
) {
2276 n_err("sfree(): double-free avoided at %s, line %d\n"
2277 "\tLast seen: %s, line %" PRIu16
"\n",
2278 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2282 if (p
.p_c
== _mem_list
)
2283 _mem_list
= p
.p_c
->mc_next
;
2285 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2286 if (p
.p_c
->mc_next
!= NULL
)
2287 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2288 p
.p_c
->mc_isfree
= TRU1
;
2289 /* Trash contents (also see [21c05f8]) */
2290 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2293 _mem_mcur
-= p
.p_c
->mc_size
;
2295 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2296 p
.p_c
->mc_next
= _mem_free
;
2308 size_t c
= 0, s
= 0;
2313 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2316 s
+= p
.p_c
->mc_size
;
2317 p
.p_c
= p
.p_c
->mc_next
;
2322 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2323 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2328 c_smemtrace(void *v
)
2330 /* For _HOPE_GET() */
2331 char const * const mdbg_file
= "smemtrace()";
2332 int const mdbg_line
= -1;
2334 union mem_ptr p
, xp
;
2340 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2341 n_perr("tmpfile", 0);
2345 fprintf(fp
, "Memory statistics:\n"
2346 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2347 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2348 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2350 fprintf(fp
, "Currently allocated memory chunks:\n");
2351 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2352 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2355 _HOPE_GET_TRACE(xp
, isbad
);
2356 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2357 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2358 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2362 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2363 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2364 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2367 _HOPE_GET_TRACE(xp
, isbad
);
2368 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2369 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2370 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2371 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2375 page_or_print(fp
, lines
);
2385 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2387 union mem_ptr p
, xp
;
2388 bool_t anybad
= FAL0
, isbad
;
2392 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2393 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2396 _HOPE_GET_TRACE(xp
, isbad
);
2400 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2401 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2402 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2406 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2407 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2410 _HOPE_GET_TRACE(xp
, isbad
);
2414 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2415 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2416 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2423 # endif /* HAVE_DEVEL */
2427 # undef _HOPE_GET_TRACE
2429 #endif /* HAVE_DEBUG */