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 */
87 struct a_aux_err_node
{
88 struct a_aux_err_node
*ae_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 a_aux_err_node
*a_aux_err_head
, *a_aux_err_tail
;
133 static size_t a_aux_err_cnt
, a_aux_err_cnt_noted
;
135 static size_t a_aux_err_dirty
;
138 static size_t _mem_aall
, _mem_acur
, _mem_amax
,
139 _mem_mall
, _mem_mcur
, _mem_mmax
;
141 static struct mem_chunk
*_mem_list
, *_mem_free
;
144 /* Our ARC4 random generator with its completely unacademical pseudo
145 * initialization (shall /dev/urandom fail) */
146 #ifndef HAVE_POSIX_RANDOM
147 static void _rand_init(void);
148 static ui32_t
_rand_weak(ui32_t seed
);
149 SINLINE ui8_t
_rand_get8(void);
153 static void _nyd_print(int fd
, struct nyd_info
*nip
);
156 /* Perform shell variable expansion */
157 static char * _sh_exp_var(struct shvar_stack
*shsp
);
159 #ifndef HAVE_POSIX_RANDOM
163 # ifdef HAVE_CLOCK_GETTIME
168 union {int fd
; size_t i
;} u
;
172 _rand
= smalloc(sizeof *_rand
);
174 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
175 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
182 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
183 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
186 # ifdef HAVE_CLOCK_GETTIME
187 clock_gettime(CLOCK_REALTIME
, &ts
);
188 t
= (ui32_t
)ts
.tv_nsec
;
190 gettimeofday(&ts
, NULL
);
191 t
= (ui32_t
)ts
.tv_usec
;
194 t
= (t
>> 16) | (t
<< 16);
195 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
196 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
197 if (rnd
== 7 || rnd
== 17)
198 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
199 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
200 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
201 seed
^= _rand_weak(_rand
->b32
[k
]);
203 seed
^= nextprime(seed
);
207 for (u
.i
= 11 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
214 _rand_weak(ui32_t seed
)
216 /* From "Random number generators: good ones are hard to find",
217 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
218 * October 1988, p. 1195.
219 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
226 seed
= (seed
* 16807) - (hi
* 2836);
227 if ((si32_t
)seed
< 0)
237 si
= _rand
->a
._dat
[++_rand
->a
._i
];
238 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
239 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
240 _rand
->a
._dat
[_rand
->a
._j
] = si
;
241 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
243 #endif /* HAVE_POSIX_RANDOM */
247 _nyd_print(int fd
, struct nyd_info
*nip
)
250 union {int i
; size_t z
;} u
;
252 u
.i
= snprintf(buf
, sizeof buf
,
253 "%c [%2" PRIu32
"] %.25s (%.16s:%" PRIu32
")\n",
254 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
255 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
258 if (u
.z
> sizeof buf
)
259 u
.z
= sizeof buf
- 1; /* (Skip \0) */
266 _sh_exp_var(struct shvar_stack
*shsp
)
268 struct shvar_stack next
, *np
, *tmp
;
270 char lc
, c
, *cp
, *rv
;
274 if (*(vp
= shsp
->shs_value
) != '$') {
275 bool_t bsesc
= shsp
->shs_bsesc
;
276 union {bool_t hadbs
; char c
;} u
= {FAL0
};
279 for (lc
= '\0', i
= 0; ((c
= *vp
) != '\0'); ++i
, ++vp
) {
280 if (c
== '$' && lc
!= '\\')
284 lc
= (lc
== '\\') ? (u
.hadbs
= TRU1
, '\0') : c
;
289 shsp
->shs_dat
= cp
= savestrbuf(shsp
->shs_dat
, i
);
291 for (lc
= '\0', rv
= cp
; (u
.c
= *cp
++) != '\0';) {
292 if (u
.c
!= '\\' || lc
== '\\')
294 lc
= (lc
== '\\') ? '\0' : u
.c
;
298 shsp
->shs_len
= PTR2SIZE(rv
- shsp
->shs_dat
);
301 if ((lc
= (*++vp
== '{')))
305 * Environment variable names used by the utilities in the Shell and
306 * Utilities volume of POSIX.1-2008 consist solely of uppercase
307 * letters, digits, and the <underscore> ('_') from the characters
308 * defined in Portable Character Set and do not begin with a digit.
309 * Other characters may be permitted by an implementation;
310 * applications shall tolerate the presence of such names. */
312 for (i
= 0; (c
= *vp
) != '\0'; ++i
, ++vp
)
313 if (!alnumchar(c
) && c
!= '_')
318 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
320 shsp
->shs_len
= strlen(shsp
->shs_value
);
321 shsp
->shs_dat
= shsp
->shs_value
;
322 if (shsp
->shs_err
!= NULL
)
323 *shsp
->shs_err
= TRU1
;
330 if ((cp
= vok_vlook(savestrbuf(shsp
->shs_dat
, i
))) != NULL
)
331 shsp
->shs_len
= strlen(shsp
->shs_dat
= cp
);
336 /* That level made the great and completed encoding. Build result */
338 for (i
= 0, np
= shsp
, shsp
= NULL
; np
!= NULL
;) {
346 cp
= rv
= salloc(i
+1);
347 while (shsp
!= NULL
) {
349 shsp
= shsp
->shs_next
;
350 memcpy(cp
, np
->shs_dat
, np
->shs_len
);
359 memset(&next
, 0, sizeof next
);
360 next
.shs_next
= shsp
;
362 next
.shs_err
= shsp
->shs_err
;
363 next
.shs_bsesc
= shsp
->shs_bsesc
;
364 rv
= _sh_exp_var(&next
);
372 kill(getpid(), signo
);
377 safe_signal(int signum
, sighandler_type handler
)
379 struct sigaction nact
, oact
;
383 nact
.sa_handler
= handler
;
384 sigemptyset(&nact
.sa_mask
);
387 nact
.sa_flags
|= SA_RESTART
;
389 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
398 if (_alls_depth
++ == 0) {
399 sigfillset(&_alls_nset
);
400 sigdelset(&_alls_nset
, SIGABRT
);
402 sigdelset(&_alls_nset
, SIGBUS
);
404 sigdelset(&_alls_nset
, SIGCHLD
);
405 sigdelset(&_alls_nset
, SIGFPE
);
406 sigdelset(&_alls_nset
, SIGILL
);
407 sigdelset(&_alls_nset
, SIGKILL
);
408 sigdelset(&_alls_nset
, SIGSEGV
);
409 sigdelset(&_alls_nset
, SIGSTOP
);
410 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
419 if (--_alls_depth
== 0)
420 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
428 if (_hold_sigdepth
++ == 0) {
429 sigemptyset(&_hold_nset
);
430 sigaddset(&_hold_nset
, SIGHUP
);
431 sigaddset(&_hold_nset
, SIGINT
);
432 sigaddset(&_hold_nset
, SIGQUIT
);
433 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
442 if (--_hold_sigdepth
== 0)
443 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
449 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
451 struct nyd_info
*nip
= _nyd_infos
;
453 if (_nyd_curr
!= NELEM(_nyd_infos
))
459 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
460 nip
->ni_level
= ((act
== 0) ? _nyd_level
461 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
465 _nyd_oncrash(int signo
)
467 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
468 struct sigaction xact
;
472 struct nyd_info
*nip
;
474 LCTA(sizeof("./") -1 + sizeof(UAGENT
) -1 + sizeof(".dat") < PATH_MAX
);
476 xact
.sa_handler
= SIG_DFL
;
477 sigemptyset(&xact
.sa_mask
);
479 sigaction(signo
, &xact
, NULL
);
482 fnl
= sizeof(UAGENT
) -1;
484 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
485 (cp
= pathbuf
)[0] = '.';
488 memcpy(cp
= pathbuf
, tempdir
, i
);
489 cp
[i
++] = '/'; /* xxx pathsep */
490 memcpy(cp
+= i
, UAGENT
, fnl
);
492 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
493 fnl
= i
+ sizeof(".dat") -1;
495 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
499 # define _X(X) (X), sizeof(X) -1
500 write(fd
, _X("\n\nNYD: program dying due to signal "));
502 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
506 *--cp
= "0123456789"[i
% 10];
509 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
511 write(fd
, _X(":\n"));
513 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
514 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
515 _nyd_print(fd
, nip
++);
516 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
517 _nyd_print(fd
, nip
++);
519 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
521 if (fd
!= STDERR_FILENO
) {
522 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
523 write(STDERR_FILENO
, pathbuf
, fnl
);
524 write(STDERR_FILENO
, _X("\n"));
531 sigaddset(&xset
, signo
);
532 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
537 #endif /* HAVE_NYD */
540 touch(struct message
*mp
)
543 mp
->m_flag
|= MTOUCH
;
544 if (!(mp
->m_flag
& MREAD
))
545 mp
->m_flag
|= MREAD
| MSTATUS
;
550 is_dir(char const *name
)
557 if (!stat(name
, &sbuf
)) {
558 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
560 } else if (errno
!= EINTR
)
567 argcount(char **argv
)
572 for (ap
= argv
; *ap
++ != NULL
;)
575 return (int)PTR2SIZE(ap
- argv
- 1);
585 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
586 s
= scrnheight
- 2; /* XXX no magics */
592 get_pager(char const **env_addon
)
597 cp
= ok_vlook(PAGER
);
598 if (cp
== NULL
|| *cp
== '\0')
601 if (env_addon
!= NULL
) {
603 if (strstr(cp
, "less") != NULL
) {
604 if (!env_blook("LESS", TRU1
))
605 *env_addon
= "LESS=FRSXi";
606 } else if (strstr(cp
, "lv") != NULL
) {
607 if (!env_blook("LV", TRU1
))
608 *env_addon
= "LV=-c";
616 page_or_print(FILE *fp
, size_t lines
)
624 if ((options
& OPT_INTERACTIVE
) && (cp
= ok_vlook(crt
)) != NULL
) {
626 union {sl_i sli
; size_t rows
;} u
;
628 u
.sli
= strtol(cp
, &eptr
, 0);
629 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
630 ? (size_t)u
.sli
: (size_t)scrnheight
;
632 if (u
.rows
> 0 && lines
== 0) {
633 while ((c
= getc(fp
)) != EOF
)
634 if (c
== '\n' && ++lines
>= u
.rows
)
639 if (lines
>= u
.rows
) {
640 run_command(get_pager(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
641 NULL
, NULL
, NULL
, NULL
);
646 while ((c
= getc(fp
)) != EOF
)
653 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
659 enum protocol rv
= PROTO_UNKNOWN
;
662 temporary_protocol_ext
= NULL
;
664 if (name
[0] == '%' && name
[1] == ':')
666 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
670 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
671 if (!strncmp(name
, "pop3://", 7)) {
675 n_err(_("No POP3 support compiled in\n"));
677 } else if (!strncmp(name
, "pop3s://", 8)) {
678 #if defined HAVE_POP3 && defined HAVE_SSL
682 n_err(_("No POP3 support compiled in\n"));
685 n_err(_("No SSL support compiled in\n"));
692 /* TODO This is the de facto maildir code and thus belongs into there!
693 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
694 * TODO or (more likely) in addition to *newfolders*) */
697 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
698 memcpy(np
, name
, sz
+ 1);
699 if (!stat(name
, &st
)) {
700 if (S_ISDIR(st
.st_mode
) &&
701 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
702 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
703 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
706 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
707 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
708 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
709 temporary_protocol_ext
= cp
;
710 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
720 torek_hash(char const *name
)
722 /* Chris Torek's hash.
723 * NOTE: need to change *at least* create-okey-map.pl when changing the
728 while (*name
!= '\0') {
737 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
744 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
745 if ((g
= h
& 0xf0000000) != 0) {
757 static ui32_t
const primes
[] = {
758 5, 11, 23, 47, 97, 157, 283,
759 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
760 131071, 262139, 524287, 1048573, 2097143, 4194301,
761 8388593, 16777213, 33554393, 67108859, 134217689,
762 268435399, 536870909, 1073741789, 2147483647
768 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
769 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
770 : NELEM(primes
) / 2));
772 if ((mprime
= primes
[i
]) > n
)
774 while (++i
< NELEM(primes
));
775 if (i
== NELEM(primes
) && mprime
< n
)
782 n_shell_expand_tilde(char const *s
, bool_t
*err_or_null
)
796 if (*(rp
= s
+ 1) == '/' || *rp
== '\0')
799 if ((rp
= strchr(s
+ 1, '/')) == NULL
)
800 rp
= (np
= UNCONST(s
)) + 1;
802 nl
= PTR2SIZE(rp
- s
);
803 np
= savestrbuf(s
, nl
);
806 if ((pwp
= getpwnam(np
)) == NULL
) {
815 rv
= salloc(nl
+ 1 + rl
+1);
818 memcpy(rv
+ nl
, rp
, rl
);
827 if (err_or_null
!= NULL
)
834 n_shell_expand_var(char const *s
, bool_t bsescape
, bool_t
*err_or_null
)
836 struct shvar_stack top
;
840 memset(&top
, 0, sizeof top
);
843 if ((top
.shs_err
= err_or_null
) != NULL
)
845 top
.shs_bsesc
= bsescape
;
846 rv
= _sh_exp_var(&top
);
852 n_shell_expand_escape(char const **s
, bool_t use_nail_extensions
)
860 if ((c
= *xs
& 0xFF) == '\0')
866 switch ((c
= *xs
& 0xFF)) {
868 case 'a': c
= '\a'; break;
869 case 'b': c
= '\b'; break;
870 case 'c': c
= PROMPT_STOP
; break;
871 case 'f': c
= '\f'; break;
872 case 'n': c
= '\n'; break;
873 case 'r': c
= '\r'; break;
874 case 't': c
= '\t'; break;
875 case 'v': c
= '\v'; break;
877 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
882 /* S-nail extension for nice (get)prompt(()) support */
887 if (use_nail_extensions
) {
889 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
890 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
891 case '$': c
= PROMPT_DOLLAR
; break;
892 case '@': c
= PROMPT_AT
; break;
898 /* A sole <backslash> at EOS is treated as-is! */
912 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
914 static char buf
[PROMPT_BUFFER_SIZE
];
917 char const *ccp_base
, *ccp
;
918 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
919 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
922 /* No other place to place this */
924 if (options
& OPT_INTERACTIVE
) {
925 if (!(pstate
& PS_ERRORS_NOTED
) && a_aux_err_head
!= NULL
) {
926 pstate
|= PS_ERRORS_NOTED
;
927 fprintf(stderr
, _("There are new messages in the error message ring "
928 "(denoted by \"#ERR#\")\n"
929 " The `errors' command manages this message ring\n"));
932 if ((trigger
= (a_aux_err_cnt_noted
!= a_aux_err_cnt
)))
933 a_aux_err_cnt_noted
= a_aux_err_cnt
;
939 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
949 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
951 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
953 dfmaxlen
= 0; /* keep CC happy */
957 NATCH_CHAR( cclen
= cclen_base
; )
958 maxlen
= sizeof(buf
) -1;
966 #ifdef HAVE_NATCH_CHAR
967 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
976 } else if ((l
= c
) > 1) {
986 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
992 if (c
== 0 || c
== PROMPT_STOP
)
996 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
999 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
1019 nodename(int mayoverride
)
1021 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1026 # ifdef HAVE_GETADDRINFO
1027 struct addrinfo hints
, *res
;
1029 struct hostent
*hent
;
1034 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
1036 } else if ((hn
= sys_hostname
) == NULL
) {
1040 # ifdef HAVE_GETADDRINFO
1041 memset(&hints
, 0, sizeof hints
);
1042 hints
.ai_family
= AF_UNSPEC
;
1043 hints
.ai_flags
= AI_CANONNAME
;
1044 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
1045 if (res
->ai_canonname
!= NULL
) {
1046 size_t l
= strlen(res
->ai_canonname
) +1;
1049 memcpy(hn
, res
->ai_canonname
, l
);
1054 hent
= gethostbyname(hn
);
1059 sys_hostname
= sstrdup(hn
);
1060 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1061 if (hn
!= ut
.nodename
)
1067 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
1069 hostname
= sstrdup(hn
);
1075 getrandstring(size_t length
)
1082 #ifndef HAVE_POSIX_RANDOM
1087 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1088 * with PAD stripped is still longer than what the user requests, easy way */
1089 data
= ac_alloc(i
= length
+ 3);
1091 #ifndef HAVE_POSIX_RANDOM
1093 data
[i
] = (char)_rand_get8();
1098 union {ui32_t i4
; char c
[4];} r
;
1101 r
.i4
= (ui32_t
)arc4random();
1102 switch ((j
= i
& 3)) {
1103 case 0: cp
[3] = r
.c
[3]; j
= 4;
1104 case 3: cp
[2] = r
.c
[2];
1105 case 2: cp
[1] = r
.c
[1];
1106 default: cp
[0] = r
.c
[0]; break;
1114 b64_encode_buf(&b64
, data
, length
+ 3,
1115 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1118 assert(b64
.l
>= length
);
1119 b64
.s
[length
] = '\0';
1125 makedir(char const *name
)
1128 enum okay rv
= STOP
;
1131 if (!mkdir(name
, 0700))
1135 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1136 S_ISDIR(st
.st_mode
))
1145 cwget(struct cw
*cw
)
1147 enum okay rv
= STOP
;
1150 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1152 if (fchdir(cw
->cw_fd
) == -1) {
1163 cwret(struct cw
*cw
)
1165 enum okay rv
= STOP
;
1168 if (!fchdir(cw
->cw_fd
))
1175 cwrelse(struct cw
*cw
)
1182 #else /* !HAVE_FCHDIR */
1184 cwget(struct cw
*cw
)
1186 enum okay rv
= STOP
;
1189 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1196 cwret(struct cw
*cw
)
1198 enum okay rv
= STOP
;
1201 if (!chdir(cw
->cw_wd
))
1208 cwrelse(struct cw
*cw
)
1214 #endif /* !HAVE_FCHDIR */
1217 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1222 #ifdef HAVE_NATCH_CHAR
1223 maxlen
= MIN(maxlen
, blen
);
1224 for (rv
= 0; maxlen
> 0;) {
1225 int ml
= mblen(buf
, maxlen
);
1235 rv
= MIN(blen
, maxlen
);
1242 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1244 NATCH_CHAR( struct bidi_info bi
; )
1245 size_t rv
NATCH_CHAR( COMMA i
);
1252 #ifdef HAVE_NATCH_CHAR
1253 bidi_info_create(&bi
);
1254 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1259 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1264 if ((i
= bi
.bi_start
.l
) > 0) {
1265 memcpy(store
, bi
.bi_start
.s
, i
);
1271 while (maxlen
> 0) {
1272 int ml
= mblen(buf
, blen
);
1277 if (UICMP(z
, maxlen
, <, ml
))
1282 memcpy(store
, buf
, ml
);
1289 if ((i
= bi
.bi_end
.l
) > 0) {
1290 memcpy(store
, bi
.bi_end
.s
, i
);
1298 rv
= MIN(blen
, maxlen
);
1299 memcpy(store
, buf
, rv
);
1308 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1310 NATCH_CHAR( struct bidi_info bi
; )
1311 int col_orig
= col
, n
, sz
;
1312 bool_t isbidi
, isuni
, istab
, isrepl
;
1316 /* Bidi only on request and when there is 8-bit data */
1317 isbidi
= isuni
= FAL0
;
1318 #ifdef HAVE_NATCH_CHAR
1319 isuni
= ((options
& OPT_UNICODE
) != 0);
1320 bidi_info_create(&bi
);
1321 if (bi
.bi_start
.l
== 0)
1323 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1326 if ((size_t)col
>= bi
.bi_pad
)
1333 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1335 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1338 #ifdef HAVE_NATCH_CHAR
1340 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1341 np
+= bi
.bi_start
.l
;
1345 while (*cp
!= '\0') {
1347 #ifdef HAVE_C90AMEND1
1348 if (mb_cur_max
> 1) {
1353 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1355 else if (wc
== L
'\t') {
1356 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1359 } else if (iswprint(wc
)) {
1360 # ifndef HAVE_WCWIDTH
1361 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1363 if ((n
= wcwidth(wc
)) == -1)
1373 istab
= (*cp
== '\t');
1374 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1383 np
[0] = (char)0xEFu
;
1384 np
[1] = (char)0xBFu
;
1385 np
[2] = (char)0xBDu
;
1390 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1398 if (fill
&& col
!= 0) {
1400 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1401 memset(nb
, ' ', col
);
1403 memset(np
, ' ', col
);
1408 #ifdef HAVE_NATCH_CHAR
1410 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1416 if (cols_decr_used_or_null
!= NULL
)
1417 *cols_decr_used_or_null
-= col_orig
- col
;
1423 makeprint(struct str
const *in
, struct str
*out
)
1425 static int print_all_chars
= -1;
1427 char const *inp
, *maxp
;
1432 if (print_all_chars
== -1)
1433 print_all_chars
= ok_blook(print_all_chars
);
1435 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1439 if (print_all_chars
) {
1441 memcpy(outp
, inp
, out
->l
);
1445 #ifdef HAVE_NATCH_CHAR
1446 if (mb_cur_max
> 1) {
1447 char mbb
[MB_LEN_MAX
+ 1];
1450 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1453 while (inp
< maxp
) {
1455 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1461 /* FIXME Why mbtowc() resetting here?
1462 * FIXME what about ISO 2022-JP plus -- those
1463 * FIXME will loose shifts, then!
1464 * FIXME THUS - we'd need special "known points"
1465 * FIXME to do so - say, after a newline!!
1466 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1467 mbtowc(&wc
, NULL
, mb_cur_max
);
1468 wc
= isuni
? 0xFFFD : '?';
1473 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1475 if ((wc
& ~(wchar_t)037) == 0)
1476 wc
= isuni
? 0x2400 | wc
: '?';
1477 else if (wc
== 0177)
1478 wc
= isuni
? 0x2421 : '?';
1480 wc
= isuni
? 0x2426 : '?';
1481 }else if(isuni
){ /* TODO ctext */
1482 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1483 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1485 /* And some zero-width messes */
1486 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1488 /* Oh about the ISO C wide character interfaces, baby! */
1492 if ((n
= wctomb(mbb
, wc
)) <= 0)
1495 assert(out
->l
< msz
);
1496 for (i
= 0; i
< n
; ++i
)
1500 #endif /* NATCH_CHAR */
1503 while (inp
< maxp
) {
1505 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1512 out
->s
[out
->l
] = '\0';
1517 delctrl(char *cp
, size_t len
)
1522 for (x
= y
= 0; x
< len
; ++x
)
1523 if (!cntrlchar(cp
[x
]))
1531 prstr(char const *s
)
1539 makeprint(&in
, &out
);
1540 rp
= savestrbuf(out
.s
, out
.l
);
1547 prout(char const *s
, size_t sz
, FILE *fp
)
1555 makeprint(&in
, &out
);
1556 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1563 putuc(int u
, int c
, FILE *fp
)
1569 #ifdef HAVE_NATCH_CHAR
1570 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1571 char mbb
[MB_LEN_MAX
];
1574 if ((n
= wctomb(mbb
, u
)) > 0) {
1576 for (i
= 0; i
< n
; ++i
)
1577 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1582 rv
= (putc('\0', fp
) != EOF
);
1587 rv
= (putc(c
, fp
) != EOF
);
1593 bidi_info_needed(char const *bdat
, size_t blen
)
1598 #ifdef HAVE_NATCH_CHAR
1599 if (options
& OPT_UNICODE
)
1601 /* TODO Checking for BIDI character: use S-CText fromutf8
1602 * TODO plus isrighttoleft (or whatever there will be)! */
1603 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1610 /* (Very very fuzzy, awaiting S-CText for good) */
1611 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1612 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1613 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1614 (c
>= 0x10800 && c
<= 0x10C48) ||
1615 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1620 #endif /* HAVE_NATCH_CHAR */
1626 bidi_info_create(struct bidi_info
*bip
)
1628 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1629 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1630 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1631 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1632 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1633 NATCH_CHAR( char const *hb
; )
1636 memset(bip
, 0, sizeof *bip
);
1637 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1639 #ifdef HAVE_NATCH_CHAR
1640 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1646 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1652 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1653 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1656 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1663 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1670 assert(inlen
== 0 || inbuf
!= NULL
);
1672 if (inlen
== UIZ_MAX
)
1673 inlen
= strlen(inbuf
);
1676 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1678 if ((inlen
== 1 && *inbuf
== '1') ||
1679 !ascncasecmp(inbuf
, "true", inlen
) ||
1680 !ascncasecmp(inbuf
, "yes", inlen
) ||
1681 !ascncasecmp(inbuf
, "on", inlen
))
1683 else if ((inlen
== 1 && *inbuf
== '0') ||
1684 !ascncasecmp(inbuf
, "false", inlen
) ||
1685 !ascncasecmp(inbuf
, "no", inlen
) ||
1686 !ascncasecmp(inbuf
, "off", inlen
))
1689 dat
= ac_alloc(inlen
+1);
1690 memcpy(dat
, inbuf
, inlen
);
1693 sli
= strtol(dat
, &eptr
, 0);
1694 if (*dat
!= '\0' && *eptr
== '\0')
1707 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1712 assert(inlen
== 0 || inbuf
!= NULL
);
1714 if (inlen
== UIZ_MAX
)
1715 inlen
= strlen(inbuf
);
1718 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1719 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1720 !ascncasecmp(inbuf
, "ask-", 4) &&
1721 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1722 (options
& OPT_INTERACTIVE
)) {
1724 fputs(prompt
, stdout
);
1725 rv
= getapproval(NULL
, rv
);
1734 #ifdef HAVE_CLOCK_GETTIME
1736 #elif defined HAVE_GETTIMEOFDAY
1742 #ifdef HAVE_CLOCK_GETTIME
1743 clock_gettime(CLOCK_REALTIME
, &ts
);
1744 rv
= (time_t)ts
.tv_sec
;
1745 #elif defined HAVE_GETTIMEOFDAY
1746 gettimeofday(&ts
, NULL
);
1747 rv
= (time_t)ts
.tv_sec
;
1756 time_current_update(struct time_current
*tc
, bool_t full_update
)
1759 tc
->tc_time
= n_time_epoch();
1761 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1762 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1763 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1769 n_err(char const *format
, ...){
1773 va_start(ap
, format
);
1775 if(options
& OPT_INTERACTIVE
)
1780 if(a_aux_err_dirty
++ == 0)
1781 fputs(UAGENT
": ", stderr
);
1782 vfprintf(stderr
, format
, ap
);
1783 if(strchr(format
, '\n') != NULL
){ /* TODO */
1784 a_aux_err_dirty
= 0;
1793 n_verr(char const *format
, va_list ap
){
1794 /* Check use cases of PS_ERRORS_NOTED, too! */
1796 char buf
[LINESIZE
], *xbuf
;
1798 struct a_aux_err_node
*enp
;
1800 LCTA(ERRORS_MAX
> 3);
1804 if(a_aux_err_dirty
++ == 0)
1805 fputs(UAGENT
": ", stderr
);
1808 if(!(options
& OPT_INTERACTIVE
))
1811 vfprintf(stderr
, format
, ap
);
1819 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1822 if (UICMP(z
, l
, >=, lmax
)) {
1823 /* FIXME Cannot reuse va_list
1825 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1830 fwrite(xbuf
, 1, l
, stderr
);
1832 /* Link it into the `errors' message ring */
1833 if((enp
= a_aux_err_tail
) == NULL
){
1835 enp
= scalloc(1, sizeof *enp
);
1836 if(a_aux_err_tail
!= NULL
)
1837 a_aux_err_tail
->ae_next
= enp
;
1839 a_aux_err_head
= enp
;
1840 a_aux_err_tail
= enp
;
1842 }else if(enp
->ae_str
.l
> 0 && enp
->ae_str
.s
[enp
->ae_str
.l
- 1] == '\n'){
1843 if(a_aux_err_cnt
< ERRORS_MAX
)
1846 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1847 a_aux_err_tail
->ae_next
= enp
;
1848 a_aux_err_tail
= enp
;
1849 free(enp
->ae_str
.s
);
1850 memset(enp
, 0, sizeof *enp
);
1853 n_str_add_buf(&enp
->ae_str
, xbuf
, l
);
1857 #endif /* HAVE_ERRORS */
1860 /* If the format ends with newline, be clean again */
1862 size_t i
= strlen(format
);
1864 if(i
> 0 && format
[i
- 1] == '\n'){
1866 a_aux_err_dirty
= 0;
1873 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1877 va_start(ap
, format
);
1878 vfprintf(stderr
, format
, ap
);
1884 n_perr(char const *msg
, int errval
){
1897 n_err(fmt
, msg
, strerror(errval
));
1902 n_alert(char const *format
, ...){
1906 n_err(a_aux_err_dirty
> 0 ? _("\nAlert: ") : _("Alert: "));
1908 va_start(ap
, format
);
1917 n_panic(char const *format
, ...){
1921 if(a_aux_err_dirty
> 0){
1923 a_aux_err_dirty
= 0;
1925 fprintf(stderr
, UAGENT
": Panic: ");
1927 va_start(ap
, format
);
1928 vfprintf(stderr
, format
, ap
);
1934 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1941 struct a_aux_err_node
*enp
;
1948 if(!asccasecmp(*argv
, "show"))
1950 if(!asccasecmp(*argv
, "clear"))
1953 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1957 return v
== NULL
? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1963 if(a_aux_err_head
== NULL
){
1964 fprintf(stderr
, _("The error ring is empty\n"));
1968 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1970 fprintf(stderr
, _("tmpfile"));
1975 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1976 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
1977 ++i
, enp
->ae_str
.l
, enp
->ae_str
.s
);
1978 /* We don't know wether last string ended with NL; be simple */
1981 page_or_print(fp
, 0);
1987 a_aux_err_tail
= NULL
;
1988 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
1989 while((enp
= a_aux_err_head
) != NULL
){
1990 a_aux_err_head
= enp
->ae_next
;
1991 free(enp
->ae_str
.s
);
1996 #endif /* HAVE_ERRORS */
2000 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2007 if ((rv
= malloc(s
)) == NULL
)
2008 n_panic(_("no memory"));
2014 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2023 else if ((rv
= realloc(v
, s
)) == NULL
)
2024 n_panic(_("no memory"));
2030 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2037 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2038 n_panic(_("no memory"));
2043 #else /* !HAVE_DEBUG */
2044 CTA(sizeof(char) == sizeof(ui8_t
));
2046 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2047 # define _HOPE_SET(C) \
2049 union mem_ptr __xl, __xu;\
2050 struct mem_chunk *__xc;\
2051 __xl.p_p = (C).p_p;\
2052 __xc = __xl.p_c - 1;\
2055 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2056 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2057 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2058 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2059 __xu.p_ui8p += __xc->mc_size - 8;\
2060 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2061 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2062 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2063 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2065 # define _HOPE_GET_TRACE(C,BAD) \
2071 # define _HOPE_GET(C,BAD) \
2073 union mem_ptr __xl, __xu;\
2074 struct mem_chunk *__xc;\
2076 __xl.p_p = (C).p_p;\
2078 (C).p_cp = __xl.p_cp;\
2079 __xc = __xl.p_c - 1;\
2082 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2083 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2084 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2085 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2086 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2087 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2088 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2089 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2092 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2093 __xl.p_p, __i, mdbg_file, mdbg_line);\
2096 __xu.p_ui8p += __xc->mc_size - 8;\
2098 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2099 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2100 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2101 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2102 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2103 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2104 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2105 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2108 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2109 __xl.p_p, __i, mdbg_file, mdbg_line);\
2112 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2113 __xc->mc_file, __xc->mc_line);\
2117 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2124 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2125 n_panic("smalloc(): allocation too large: %s, line %d",
2126 mdbg_file
, mdbg_line
);
2127 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2129 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2130 n_panic(_("no memory"));
2131 p
.p_c
->mc_prev
= NULL
;
2132 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2133 _mem_list
->mc_prev
= p
.p_c
;
2134 p
.p_c
->mc_file
= mdbg_file
;
2135 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2136 p
.p_c
->mc_isfree
= FAL0
;
2137 p
.p_c
->mc_size
= (ui32_t
)s
;
2139 _mem_list
= p
.p_c
++;
2144 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2147 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2153 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2159 if ((p
.p_p
= v
) == NULL
) {
2160 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2164 _HOPE_GET(p
, isbad
);
2166 if (p
.p_c
->mc_isfree
) {
2167 n_err("srealloc(): region freed! At %s, line %d\n"
2168 "\tLast seen: %s, line %" PRIu16
"\n",
2169 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2173 if (p
.p_c
== _mem_list
)
2174 _mem_list
= p
.p_c
->mc_next
;
2176 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2177 if (p
.p_c
->mc_next
!= NULL
)
2178 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2181 _mem_mcur
-= p
.p_c
->mc_size
;
2185 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2186 n_panic("srealloc(): allocation too large: %s, line %d",
2187 mdbg_file
, mdbg_line
);
2188 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2190 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2191 n_panic(_("no memory"));
2192 p
.p_c
->mc_prev
= NULL
;
2193 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2194 _mem_list
->mc_prev
= p
.p_c
;
2195 p
.p_c
->mc_file
= mdbg_file
;
2196 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2197 p
.p_c
->mc_isfree
= FAL0
;
2198 p
.p_c
->mc_size
= (ui32_t
)s
;
2199 _mem_list
= p
.p_c
++;
2204 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2207 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2214 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2223 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2224 n_panic("scalloc(): allocation size too large: %s, line %d",
2225 mdbg_file
, mdbg_line
);
2226 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2227 n_panic("scalloc(): allocation count too large: %s, line %d",
2228 mdbg_file
, mdbg_line
);
2231 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2233 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2234 n_panic(_("no memory"));
2235 memset(p
.p_p
, 0, size
);
2236 p
.p_c
->mc_prev
= NULL
;
2237 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2238 _mem_list
->mc_prev
= p
.p_c
;
2239 p
.p_c
->mc_file
= mdbg_file
;
2240 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2241 p
.p_c
->mc_isfree
= FAL0
;
2242 p
.p_c
->mc_size
= (ui32_t
)size
;
2243 _mem_list
= p
.p_c
++;
2248 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2251 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2257 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2263 if ((p
.p_p
= v
) == NULL
) {
2264 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2268 _HOPE_GET(p
, isbad
);
2270 if (p
.p_c
->mc_isfree
) {
2271 n_err("sfree(): double-free avoided at %s, line %d\n"
2272 "\tLast seen: %s, line %" PRIu16
"\n",
2273 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2277 if (p
.p_c
== _mem_list
)
2278 _mem_list
= p
.p_c
->mc_next
;
2280 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2281 if (p
.p_c
->mc_next
!= NULL
)
2282 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2283 p
.p_c
->mc_isfree
= TRU1
;
2284 /* Trash contents (also see [21c05f8]) */
2285 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2288 _mem_mcur
-= p
.p_c
->mc_size
;
2290 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2291 p
.p_c
->mc_next
= _mem_free
;
2303 size_t c
= 0, s
= 0;
2308 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2311 s
+= p
.p_c
->mc_size
;
2312 p
.p_c
= p
.p_c
->mc_next
;
2317 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2318 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2323 c_smemtrace(void *v
)
2325 /* For _HOPE_GET() */
2326 char const * const mdbg_file
= "smemtrace()";
2327 int const mdbg_line
= -1;
2329 union mem_ptr p
, xp
;
2335 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2336 n_perr("tmpfile", 0);
2340 fprintf(fp
, "Memory statistics:\n"
2341 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2342 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2343 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2345 fprintf(fp
, "Currently allocated memory chunks:\n");
2346 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2347 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2350 _HOPE_GET_TRACE(xp
, isbad
);
2351 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2352 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2353 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2357 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2358 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2359 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2362 _HOPE_GET_TRACE(xp
, isbad
);
2363 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2364 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2365 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2366 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2370 page_or_print(fp
, lines
);
2380 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2382 union mem_ptr p
, xp
;
2383 bool_t anybad
= FAL0
, isbad
;
2387 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2388 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2391 _HOPE_GET_TRACE(xp
, isbad
);
2395 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2396 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2397 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2401 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2402 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2405 _HOPE_GET_TRACE(xp
, isbad
);
2409 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2410 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2411 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2418 # endif /* HAVE_DEVEL */
2422 # undef _HOPE_GET_TRACE
2424 #endif /* HAVE_DEBUG */