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 char const *inp
, *maxp
;
1430 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1434 #ifdef HAVE_NATCH_CHAR
1435 if (mb_cur_max
> 1) {
1436 char mbb
[MB_LEN_MAX
+ 1];
1439 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1442 while (inp
< maxp
) {
1444 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1450 /* FIXME Why mbtowc() resetting here?
1451 * FIXME what about ISO 2022-JP plus -- those
1452 * FIXME will loose shifts, then!
1453 * FIXME THUS - we'd need special "known points"
1454 * FIXME to do so - say, after a newline!!
1455 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1456 mbtowc(&wc
, NULL
, mb_cur_max
);
1457 wc
= isuni
? 0xFFFD : '?';
1462 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1464 if ((wc
& ~(wchar_t)037) == 0)
1465 wc
= isuni
? 0x2400 | wc
: '?';
1466 else if (wc
== 0177)
1467 wc
= isuni
? 0x2421 : '?';
1469 wc
= isuni
? 0x2426 : '?';
1470 }else if(isuni
){ /* TODO ctext */
1471 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1472 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1474 /* And some zero-width messes */
1475 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1477 /* Oh about the ISO C wide character interfaces, baby! */
1481 if ((n
= wctomb(mbb
, wc
)) <= 0)
1484 assert(out
->l
< msz
);
1485 for (i
= 0; i
< n
; ++i
)
1489 #endif /* NATCH_CHAR */
1492 while (inp
< maxp
) {
1494 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1500 out
->s
[out
->l
] = '\0';
1505 delctrl(char *cp
, size_t len
)
1510 for (x
= y
= 0; x
< len
; ++x
)
1511 if (!cntrlchar(cp
[x
]))
1519 prstr(char const *s
)
1527 makeprint(&in
, &out
);
1528 rp
= savestrbuf(out
.s
, out
.l
);
1535 prout(char const *s
, size_t sz
, FILE *fp
)
1543 makeprint(&in
, &out
);
1544 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1551 putuc(int u
, int c
, FILE *fp
)
1557 #ifdef HAVE_NATCH_CHAR
1558 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1559 char mbb
[MB_LEN_MAX
];
1562 if ((n
= wctomb(mbb
, u
)) > 0) {
1564 for (i
= 0; i
< n
; ++i
)
1565 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1570 rv
= (putc('\0', fp
) != EOF
);
1575 rv
= (putc(c
, fp
) != EOF
);
1581 bidi_info_needed(char const *bdat
, size_t blen
)
1586 #ifdef HAVE_NATCH_CHAR
1587 if (options
& OPT_UNICODE
)
1589 /* TODO Checking for BIDI character: use S-CText fromutf8
1590 * TODO plus isrighttoleft (or whatever there will be)! */
1591 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1598 /* (Very very fuzzy, awaiting S-CText for good) */
1599 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1600 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1601 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1602 (c
>= 0x10800 && c
<= 0x10C48) ||
1603 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1608 #endif /* HAVE_NATCH_CHAR */
1614 bidi_info_create(struct bidi_info
*bip
)
1616 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1617 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1618 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1619 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1620 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1621 NATCH_CHAR( char const *hb
; )
1624 memset(bip
, 0, sizeof *bip
);
1625 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1627 #ifdef HAVE_NATCH_CHAR
1628 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1634 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1640 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1641 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1644 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1651 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1658 assert(inlen
== 0 || inbuf
!= NULL
);
1660 if (inlen
== UIZ_MAX
)
1661 inlen
= strlen(inbuf
);
1664 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1666 if ((inlen
== 1 && *inbuf
== '1') ||
1667 !ascncasecmp(inbuf
, "true", inlen
) ||
1668 !ascncasecmp(inbuf
, "yes", inlen
) ||
1669 !ascncasecmp(inbuf
, "on", inlen
))
1671 else if ((inlen
== 1 && *inbuf
== '0') ||
1672 !ascncasecmp(inbuf
, "false", inlen
) ||
1673 !ascncasecmp(inbuf
, "no", inlen
) ||
1674 !ascncasecmp(inbuf
, "off", inlen
))
1677 dat
= ac_alloc(inlen
+1);
1678 memcpy(dat
, inbuf
, inlen
);
1681 sli
= strtol(dat
, &eptr
, 0);
1682 if (*dat
!= '\0' && *eptr
== '\0')
1695 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1700 assert(inlen
== 0 || inbuf
!= NULL
);
1702 if (inlen
== UIZ_MAX
)
1703 inlen
= strlen(inbuf
);
1706 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1707 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1708 !ascncasecmp(inbuf
, "ask-", 4) &&
1709 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1710 (options
& OPT_INTERACTIVE
))
1711 rv
= getapproval(prompt
, rv
);
1719 #ifdef HAVE_CLOCK_GETTIME
1721 #elif defined HAVE_GETTIMEOFDAY
1727 #ifdef HAVE_CLOCK_GETTIME
1728 clock_gettime(CLOCK_REALTIME
, &ts
);
1729 rv
= (time_t)ts
.tv_sec
;
1730 #elif defined HAVE_GETTIMEOFDAY
1731 gettimeofday(&ts
, NULL
);
1732 rv
= (time_t)ts
.tv_sec
;
1741 time_current_update(struct time_current
*tc
, bool_t full_update
)
1744 tc
->tc_time
= n_time_epoch();
1746 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1747 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1748 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1754 n_msleep(uiz_t millis
, bool_t ignint
){
1758 #ifdef HAVE_NANOSLEEP
1760 struct timespec ts
, trem
;
1763 ts
.tv_sec
= millis
/ 1000;
1764 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1766 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1768 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1771 #elif defined HAVE_SLEEP
1772 if((millis
/= 1000) == 0)
1774 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1777 # error Configuration should have detected a function for sleeping.
1785 n_err(char const *format
, ...){
1789 va_start(ap
, format
);
1791 if(options
& OPT_INTERACTIVE
)
1796 if(a_aux_err_dirty
++ == 0)
1797 fputs(UAGENT
": ", stderr
);
1798 vfprintf(stderr
, format
, ap
);
1799 if(strchr(format
, '\n') != NULL
){ /* TODO */
1800 a_aux_err_dirty
= 0;
1809 n_verr(char const *format
, va_list ap
){
1810 /* Check use cases of PS_ERRORS_NOTED, too! */
1812 char buf
[LINESIZE
], *xbuf
;
1814 struct a_aux_err_node
*enp
;
1816 LCTA(ERRORS_MAX
> 3);
1820 if(a_aux_err_dirty
++ == 0)
1821 fputs(UAGENT
": ", stderr
);
1824 if(!(options
& OPT_INTERACTIVE
))
1827 vfprintf(stderr
, format
, ap
);
1835 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1838 if (UICMP(z
, l
, >=, lmax
)) {
1839 /* FIXME Cannot reuse va_list
1841 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1846 fwrite(xbuf
, 1, l
, stderr
);
1848 /* Link it into the `errors' message ring */
1849 if((enp
= a_aux_err_tail
) == NULL
){
1851 enp
= scalloc(1, sizeof *enp
);
1852 if(a_aux_err_tail
!= NULL
)
1853 a_aux_err_tail
->ae_next
= enp
;
1855 a_aux_err_head
= enp
;
1856 a_aux_err_tail
= enp
;
1858 }else if(enp
->ae_str
.l
> 0 && enp
->ae_str
.s
[enp
->ae_str
.l
- 1] == '\n'){
1859 if(a_aux_err_cnt
< ERRORS_MAX
)
1862 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1863 a_aux_err_tail
->ae_next
= enp
;
1864 a_aux_err_tail
= enp
;
1865 free(enp
->ae_str
.s
);
1866 memset(enp
, 0, sizeof *enp
);
1869 n_str_add_buf(&enp
->ae_str
, xbuf
, l
);
1873 #endif /* HAVE_ERRORS */
1876 /* If the format ends with newline, be clean again */
1878 size_t i
= strlen(format
);
1880 if(i
> 0 && format
[i
- 1] == '\n'){
1882 a_aux_err_dirty
= 0;
1889 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1893 va_start(ap
, format
);
1894 vfprintf(stderr
, format
, ap
);
1900 n_perr(char const *msg
, int errval
){
1913 n_err(fmt
, msg
, strerror(errval
));
1918 n_alert(char const *format
, ...){
1922 n_err(a_aux_err_dirty
> 0 ? _("\nAlert: ") : _("Alert: "));
1924 va_start(ap
, format
);
1933 n_panic(char const *format
, ...){
1937 if(a_aux_err_dirty
> 0){
1939 a_aux_err_dirty
= 0;
1941 fprintf(stderr
, UAGENT
": Panic: ");
1943 va_start(ap
, format
);
1944 vfprintf(stderr
, format
, ap
);
1950 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1957 struct a_aux_err_node
*enp
;
1964 if(!asccasecmp(*argv
, "show"))
1966 if(!asccasecmp(*argv
, "clear"))
1969 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1973 return v
== NULL
? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
1979 if(a_aux_err_head
== NULL
){
1980 fprintf(stderr
, _("The error ring is empty\n"));
1984 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1986 fprintf(stderr
, _("tmpfile"));
1991 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
1992 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
1993 ++i
, enp
->ae_str
.l
, enp
->ae_str
.s
);
1994 /* We don't know wether last string ended with NL; be simple */
1997 page_or_print(fp
, 0);
2003 a_aux_err_tail
= NULL
;
2004 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
2005 while((enp
= a_aux_err_head
) != NULL
){
2006 a_aux_err_head
= enp
->ae_next
;
2007 free(enp
->ae_str
.s
);
2012 #endif /* HAVE_ERRORS */
2016 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2023 if ((rv
= malloc(s
)) == NULL
)
2024 n_panic(_("no memory"));
2030 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2039 else if ((rv
= realloc(v
, s
)) == NULL
)
2040 n_panic(_("no memory"));
2046 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2053 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2054 n_panic(_("no memory"));
2059 #else /* !HAVE_DEBUG */
2060 CTA(sizeof(char) == sizeof(ui8_t
));
2062 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2063 # define _HOPE_SET(C) \
2065 union mem_ptr __xl, __xu;\
2066 struct mem_chunk *__xc;\
2067 __xl.p_p = (C).p_p;\
2068 __xc = __xl.p_c - 1;\
2071 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2072 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2073 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2074 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2075 __xu.p_ui8p += __xc->mc_size - 8;\
2076 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2077 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2078 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2079 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2081 # define _HOPE_GET_TRACE(C,BAD) \
2087 # define _HOPE_GET(C,BAD) \
2089 union mem_ptr __xl, __xu;\
2090 struct mem_chunk *__xc;\
2092 __xl.p_p = (C).p_p;\
2094 (C).p_cp = __xl.p_cp;\
2095 __xc = __xl.p_c - 1;\
2098 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2099 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2100 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2101 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2102 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2103 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2104 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2105 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2108 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2109 __xl.p_p, __i, mdbg_file, mdbg_line);\
2112 __xu.p_ui8p += __xc->mc_size - 8;\
2114 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2115 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2116 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2117 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2118 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2119 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2120 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2121 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2124 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2125 __xl.p_p, __i, mdbg_file, mdbg_line);\
2128 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2129 __xc->mc_file, __xc->mc_line);\
2133 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2140 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2141 n_panic("smalloc(): allocation too large: %s, line %d",
2142 mdbg_file
, mdbg_line
);
2143 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2145 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2146 n_panic(_("no memory"));
2147 p
.p_c
->mc_prev
= NULL
;
2148 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2149 _mem_list
->mc_prev
= p
.p_c
;
2150 p
.p_c
->mc_file
= mdbg_file
;
2151 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2152 p
.p_c
->mc_isfree
= FAL0
;
2153 p
.p_c
->mc_size
= (ui32_t
)s
;
2155 _mem_list
= p
.p_c
++;
2160 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2163 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2169 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2175 if ((p
.p_p
= v
) == NULL
) {
2176 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2180 _HOPE_GET(p
, isbad
);
2182 if (p
.p_c
->mc_isfree
) {
2183 n_err("srealloc(): region freed! At %s, line %d\n"
2184 "\tLast seen: %s, line %" PRIu16
"\n",
2185 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2189 if (p
.p_c
== _mem_list
)
2190 _mem_list
= p
.p_c
->mc_next
;
2192 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2193 if (p
.p_c
->mc_next
!= NULL
)
2194 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2197 _mem_mcur
-= p
.p_c
->mc_size
;
2201 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2202 n_panic("srealloc(): allocation too large: %s, line %d",
2203 mdbg_file
, mdbg_line
);
2204 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2206 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2207 n_panic(_("no memory"));
2208 p
.p_c
->mc_prev
= NULL
;
2209 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2210 _mem_list
->mc_prev
= p
.p_c
;
2211 p
.p_c
->mc_file
= mdbg_file
;
2212 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2213 p
.p_c
->mc_isfree
= FAL0
;
2214 p
.p_c
->mc_size
= (ui32_t
)s
;
2215 _mem_list
= p
.p_c
++;
2220 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2223 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2230 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2239 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2240 n_panic("scalloc(): allocation size too large: %s, line %d",
2241 mdbg_file
, mdbg_line
);
2242 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2243 n_panic("scalloc(): allocation count too large: %s, line %d",
2244 mdbg_file
, mdbg_line
);
2247 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2249 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2250 n_panic(_("no memory"));
2251 memset(p
.p_p
, 0, size
);
2252 p
.p_c
->mc_prev
= NULL
;
2253 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2254 _mem_list
->mc_prev
= p
.p_c
;
2255 p
.p_c
->mc_file
= mdbg_file
;
2256 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2257 p
.p_c
->mc_isfree
= FAL0
;
2258 p
.p_c
->mc_size
= (ui32_t
)size
;
2259 _mem_list
= p
.p_c
++;
2264 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2267 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2273 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2279 if ((p
.p_p
= v
) == NULL
) {
2280 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2284 _HOPE_GET(p
, isbad
);
2286 if (p
.p_c
->mc_isfree
) {
2287 n_err("sfree(): double-free avoided at %s, line %d\n"
2288 "\tLast seen: %s, line %" PRIu16
"\n",
2289 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2293 if (p
.p_c
== _mem_list
)
2294 _mem_list
= p
.p_c
->mc_next
;
2296 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2297 if (p
.p_c
->mc_next
!= NULL
)
2298 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2299 p
.p_c
->mc_isfree
= TRU1
;
2300 /* Trash contents (also see [21c05f8]) */
2301 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2304 _mem_mcur
-= p
.p_c
->mc_size
;
2306 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2307 p
.p_c
->mc_next
= _mem_free
;
2319 size_t c
= 0, s
= 0;
2324 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2327 s
+= p
.p_c
->mc_size
;
2328 p
.p_c
= p
.p_c
->mc_next
;
2333 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2334 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2339 c_smemtrace(void *v
)
2341 /* For _HOPE_GET() */
2342 char const * const mdbg_file
= "smemtrace()";
2343 int const mdbg_line
= -1;
2345 union mem_ptr p
, xp
;
2351 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2352 n_perr("tmpfile", 0);
2356 fprintf(fp
, "Memory statistics:\n"
2357 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2358 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2359 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2361 fprintf(fp
, "Currently allocated memory chunks:\n");
2362 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2363 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2366 _HOPE_GET_TRACE(xp
, isbad
);
2367 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2368 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2369 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2373 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2374 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2375 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2378 _HOPE_GET_TRACE(xp
, isbad
);
2379 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2380 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2381 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2382 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2386 page_or_print(fp
, lines
);
2396 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2398 union mem_ptr p
, xp
;
2399 bool_t anybad
= FAL0
, isbad
;
2403 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2404 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2407 _HOPE_GET_TRACE(xp
, isbad
);
2411 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2412 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2413 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2417 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2418 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2421 _HOPE_GET_TRACE(xp
, isbad
);
2425 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2426 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2427 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2434 # endif /* HAVE_DEVEL */
2438 # undef _HOPE_GET_TRACE
2440 #endif /* HAVE_DEBUG */