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.
311 * We do support the hyphen "-" because it is common for mailx. */
313 for (i
= 0; (c
= *vp
) != '\0'; ++i
, ++vp
)
314 if (!alnumchar(c
) && c
!= '_' && c
!= '-')
319 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
321 shsp
->shs_len
= strlen(shsp
->shs_value
);
322 shsp
->shs_dat
= shsp
->shs_value
;
323 if (shsp
->shs_err
!= NULL
)
324 *shsp
->shs_err
= TRU1
;
331 if ((cp
= vok_vlook(savestrbuf(shsp
->shs_dat
, i
))) != NULL
)
332 shsp
->shs_len
= strlen(shsp
->shs_dat
= cp
);
337 /* That level made the great and completed encoding. Build result */
339 for (i
= 0, np
= shsp
, shsp
= NULL
; np
!= NULL
;) {
347 cp
= rv
= salloc(i
+1);
348 while (shsp
!= NULL
) {
350 shsp
= shsp
->shs_next
;
351 memcpy(cp
, np
->shs_dat
, np
->shs_len
);
360 memset(&next
, 0, sizeof next
);
361 next
.shs_next
= shsp
;
363 next
.shs_err
= shsp
->shs_err
;
364 next
.shs_bsesc
= shsp
->shs_bsesc
;
365 rv
= _sh_exp_var(&next
);
373 kill(getpid(), signo
);
378 safe_signal(int signum
, sighandler_type handler
)
380 struct sigaction nact
, oact
;
384 nact
.sa_handler
= handler
;
385 sigemptyset(&nact
.sa_mask
);
388 nact
.sa_flags
|= SA_RESTART
;
390 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
399 if (_alls_depth
++ == 0) {
400 sigfillset(&_alls_nset
);
401 sigdelset(&_alls_nset
, SIGABRT
);
403 sigdelset(&_alls_nset
, SIGBUS
);
405 sigdelset(&_alls_nset
, SIGCHLD
);
406 sigdelset(&_alls_nset
, SIGFPE
);
407 sigdelset(&_alls_nset
, SIGILL
);
408 sigdelset(&_alls_nset
, SIGKILL
);
409 sigdelset(&_alls_nset
, SIGSEGV
);
410 sigdelset(&_alls_nset
, SIGSTOP
);
411 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
420 if (--_alls_depth
== 0)
421 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
429 if (_hold_sigdepth
++ == 0) {
430 sigemptyset(&_hold_nset
);
431 sigaddset(&_hold_nset
, SIGHUP
);
432 sigaddset(&_hold_nset
, SIGINT
);
433 sigaddset(&_hold_nset
, SIGQUIT
);
434 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
443 if (--_hold_sigdepth
== 0)
444 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
450 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
452 struct nyd_info
*nip
= _nyd_infos
;
454 if (_nyd_curr
!= NELEM(_nyd_infos
))
460 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
461 nip
->ni_level
= ((act
== 0) ? _nyd_level
462 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
466 _nyd_oncrash(int signo
)
468 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
469 struct sigaction xact
;
473 struct nyd_info
*nip
;
475 LCTA(sizeof("./") -1 + sizeof(UAGENT
) -1 + sizeof(".dat") < PATH_MAX
);
477 xact
.sa_handler
= SIG_DFL
;
478 sigemptyset(&xact
.sa_mask
);
480 sigaction(signo
, &xact
, NULL
);
483 fnl
= sizeof(UAGENT
) -1;
485 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
486 (cp
= pathbuf
)[0] = '.';
489 memcpy(cp
= pathbuf
, tempdir
, i
);
490 cp
[i
++] = '/'; /* xxx pathsep */
491 memcpy(cp
+= i
, UAGENT
, fnl
);
493 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
494 fnl
= i
+ sizeof(".dat") -1;
496 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
500 # define _X(X) (X), sizeof(X) -1
501 write(fd
, _X("\n\nNYD: program dying due to signal "));
503 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
507 *--cp
= "0123456789"[i
% 10];
510 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
512 write(fd
, _X(":\n"));
514 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
515 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
516 _nyd_print(fd
, nip
++);
517 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
518 _nyd_print(fd
, nip
++);
520 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
522 if (fd
!= STDERR_FILENO
) {
523 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
524 write(STDERR_FILENO
, pathbuf
, fnl
);
525 write(STDERR_FILENO
, _X("\n"));
532 sigaddset(&xset
, signo
);
533 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
538 #endif /* HAVE_NYD */
541 touch(struct message
*mp
)
544 mp
->m_flag
|= MTOUCH
;
545 if (!(mp
->m_flag
& MREAD
))
546 mp
->m_flag
|= MREAD
| MSTATUS
;
551 is_dir(char const *name
)
558 if (!stat(name
, &sbuf
)) {
559 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
561 } else if (errno
!= EINTR
)
568 argcount(char **argv
)
573 for (ap
= argv
; *ap
++ != NULL
;)
576 return (int)PTR2SIZE(ap
- argv
- 1);
586 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
587 s
= scrnheight
- 2; /* XXX no magics */
593 get_pager(char const **env_addon
)
598 cp
= ok_vlook(PAGER
);
599 if (cp
== NULL
|| *cp
== '\0')
602 if (env_addon
!= NULL
) {
604 if (strstr(cp
, "less") != NULL
) {
605 if (!env_blook("LESS", TRU1
))
606 *env_addon
= "LESS=FRSXi";
607 } else if (strstr(cp
, "lv") != NULL
) {
608 if (!env_blook("LV", TRU1
))
609 *env_addon
= "LV=-c";
617 page_or_print(FILE *fp
, size_t lines
)
625 if ((options
& OPT_INTERACTIVE
) && (pstate
& PS_STARTED
) &&
626 (cp
= ok_vlook(crt
)) != NULL
) {
628 union {sl_i sli
; size_t rows
;} u
;
630 u
.sli
= strtol(cp
, &eptr
, 0);
631 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
632 ? (size_t)u
.sli
: (size_t)scrnheight
;
634 if (u
.rows
> 0 && lines
== 0) {
635 while ((c
= getc(fp
)) != EOF
)
636 if (c
== '\n' && ++lines
>= u
.rows
)
641 if (lines
>= u
.rows
) {
642 run_command(get_pager(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
643 NULL
, NULL
, NULL
, NULL
);
648 while ((c
= getc(fp
)) != EOF
)
655 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
661 enum protocol rv
= PROTO_UNKNOWN
;
664 temporary_protocol_ext
= NULL
;
666 if (name
[0] == '%' && name
[1] == ':')
668 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
672 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
673 if (!strncmp(name
, "pop3://", 7)) {
677 n_err(_("No POP3 support compiled in\n"));
679 } else if (!strncmp(name
, "pop3s://", 8)) {
680 #if defined HAVE_POP3 && defined HAVE_SSL
684 n_err(_("No POP3 support compiled in\n"));
687 n_err(_("No SSL support compiled in\n"));
694 /* TODO This is the de facto maildir code and thus belongs into there!
695 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
696 * TODO or (more likely) in addition to *newfolders*) */
699 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
700 memcpy(np
, name
, sz
+ 1);
701 if (!stat(name
, &st
)) {
702 if (S_ISDIR(st
.st_mode
) &&
703 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
704 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
705 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
708 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
709 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
710 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
711 temporary_protocol_ext
= cp
;
712 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
722 torek_hash(char const *name
)
724 /* Chris Torek's hash.
725 * NOTE: need to change *at least* mk-okey-map.pl when changing the
730 while (*name
!= '\0') {
739 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
746 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
747 if ((g
= h
& 0xf0000000) != 0) {
759 static ui32_t
const primes
[] = {
760 5, 11, 23, 47, 97, 157, 283,
761 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
762 131071, 262139, 524287, 1048573, 2097143, 4194301,
763 8388593, 16777213, 33554393, 67108859, 134217689,
764 268435399, 536870909, 1073741789, 2147483647
770 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
771 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
772 : NELEM(primes
) / 2));
774 if ((mprime
= primes
[i
]) > n
)
776 while (++i
< NELEM(primes
));
777 if (i
== NELEM(primes
) && mprime
< n
)
784 n_shell_expand_tilde(char const *s
, bool_t
*err_or_null
)
798 if (*(rp
= s
+ 1) == '/' || *rp
== '\0')
801 if ((rp
= strchr(s
+ 1, '/')) == NULL
)
802 rp
= (np
= UNCONST(s
)) + 1;
804 nl
= PTR2SIZE(rp
- s
);
805 np
= savestrbuf(s
, nl
);
808 if ((pwp
= getpwnam(np
)) == NULL
) {
817 rv
= salloc(nl
+ 1 + rl
+1);
820 memcpy(rv
+ nl
, rp
, rl
);
829 if (err_or_null
!= NULL
)
836 n_shell_expand_var(char const *s
, bool_t bsescape
, bool_t
*err_or_null
)
838 struct shvar_stack top
;
842 memset(&top
, 0, sizeof top
);
845 if ((top
.shs_err
= err_or_null
) != NULL
)
847 top
.shs_bsesc
= bsescape
;
848 rv
= _sh_exp_var(&top
);
854 n_shell_expand_escape(char const **s
, bool_t use_nail_extensions
)
862 if ((c
= *xs
& 0xFF) == '\0')
868 switch ((c
= *xs
& 0xFF)) {
869 case 'a': c
= '\a'; break;
870 case 'b': c
= '\b'; break;
871 case 'c': c
= PROMPT_STOP
; break;
872 case 'f': c
= '\f'; break;
873 case 'n': c
= '\n'; break;
874 case 'r': c
= '\r'; break;
875 case 't': c
= '\t'; break;
876 case 'v': c
= '\v'; break;
878 /* Hexadecimal TODO uses ASCII */
881 static ui8_t
const hexatoi
[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
884 hexatoi[(ui8_t)((n) - ((n) <= '9' ? 48 : ((n) <= 'F' ? 55 : 87)))]
892 if(options
& OPT_D_V
)
893 n_err(_("Invalid \"\\xNUMBER\" notation in \"%s\"\n"), xs
- 1);
907 /* octal, with optional 0 prefix */
917 for (c
= 0, n
= 3; n
-- > 0 && octalchar(*xs
); ++xs
) {
923 /* S-nail extension for nice (get)prompt(()) support */
928 if (use_nail_extensions
) {
930 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
931 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
932 case '$': c
= PROMPT_DOLLAR
; break;
933 case '@': c
= PROMPT_AT
; break;
940 /* A sole <backslash> at EOS is treated as-is! */
955 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
957 static char buf
[PROMPT_BUFFER_SIZE
];
960 char const *ccp_base
, *ccp
;
961 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
962 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
965 /* No other place to place this */
967 if (options
& OPT_INTERACTIVE
) {
968 if (!(pstate
& PS_ERRORS_NOTED
) && a_aux_err_head
!= NULL
) {
969 pstate
|= PS_ERRORS_NOTED
;
970 fprintf(stderr
, _("There are new messages in the error message ring "
971 "(denoted by \"#ERR#\")\n"
972 " The `errors' command manages this message ring\n"));
975 if ((trigger
= (a_aux_err_cnt_noted
!= a_aux_err_cnt
)))
976 a_aux_err_cnt_noted
= a_aux_err_cnt
;
982 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
992 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
994 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
996 dfmaxlen
= 0; /* keep CC happy */
1000 NATCH_CHAR( cclen
= cclen_base
; )
1001 maxlen
= sizeof(buf
) -1;
1009 #ifdef HAVE_NATCH_CHAR
1010 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
1019 } else if ((l
= c
) > 1) {
1029 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
1035 if (c
== 0 || c
== PROMPT_STOP
)
1039 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
1042 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
1062 nodename(int mayoverride
)
1064 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1069 # ifdef HAVE_GETADDRINFO
1070 struct addrinfo hints
, *res
;
1072 struct hostent
*hent
;
1077 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
1079 } else if ((hn
= sys_hostname
) == NULL
) {
1083 # ifdef HAVE_GETADDRINFO
1084 memset(&hints
, 0, sizeof hints
);
1085 hints
.ai_family
= AF_UNSPEC
;
1086 hints
.ai_flags
= AI_CANONNAME
;
1087 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
1088 if (res
->ai_canonname
!= NULL
) {
1089 size_t l
= strlen(res
->ai_canonname
) +1;
1092 memcpy(hn
, res
->ai_canonname
, l
);
1097 hent
= gethostbyname(hn
);
1102 sys_hostname
= sstrdup(hn
);
1103 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1104 if (hn
!= ut
.nodename
)
1110 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
1112 hostname
= sstrdup(hn
);
1118 getrandstring(size_t length
)
1125 #ifndef HAVE_POSIX_RANDOM
1130 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1131 * with PAD stripped is still longer than what the user requests, easy way */
1132 data
= ac_alloc(i
= length
+ 3);
1134 #ifndef HAVE_POSIX_RANDOM
1136 data
[i
] = (char)_rand_get8();
1141 union {ui32_t i4
; char c
[4];} r
;
1144 r
.i4
= (ui32_t
)arc4random();
1145 switch ((j
= i
& 3)) {
1146 case 0: cp
[3] = r
.c
[3]; j
= 4;
1147 case 3: cp
[2] = r
.c
[2];
1148 case 2: cp
[1] = r
.c
[1];
1149 default: cp
[0] = r
.c
[0]; break;
1157 b64_encode_buf(&b64
, data
, length
+ 3,
1158 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1161 assert(b64
.l
>= length
);
1162 b64
.s
[length
] = '\0';
1168 makedir(char const *name
)
1171 enum okay rv
= STOP
;
1174 if (!mkdir(name
, 0700))
1178 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1179 S_ISDIR(st
.st_mode
))
1188 cwget(struct cw
*cw
)
1190 enum okay rv
= STOP
;
1193 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1195 if (fchdir(cw
->cw_fd
) == -1) {
1206 cwret(struct cw
*cw
)
1208 enum okay rv
= STOP
;
1211 if (!fchdir(cw
->cw_fd
))
1218 cwrelse(struct cw
*cw
)
1225 #else /* !HAVE_FCHDIR */
1227 cwget(struct cw
*cw
)
1229 enum okay rv
= STOP
;
1232 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1239 cwret(struct cw
*cw
)
1241 enum okay rv
= STOP
;
1244 if (!chdir(cw
->cw_wd
))
1251 cwrelse(struct cw
*cw
)
1257 #endif /* !HAVE_FCHDIR */
1260 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1265 #ifdef HAVE_NATCH_CHAR
1266 maxlen
= MIN(maxlen
, blen
);
1267 for (rv
= 0; maxlen
> 0;) {
1268 int ml
= mblen(buf
, maxlen
);
1278 rv
= MIN(blen
, maxlen
);
1285 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1287 NATCH_CHAR( struct bidi_info bi
; )
1288 size_t rv
NATCH_CHAR( COMMA i
);
1295 #ifdef HAVE_NATCH_CHAR
1296 bidi_info_create(&bi
);
1297 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1302 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1307 if ((i
= bi
.bi_start
.l
) > 0) {
1308 memcpy(store
, bi
.bi_start
.s
, i
);
1314 while (maxlen
> 0) {
1315 int ml
= mblen(buf
, blen
);
1320 if (UICMP(z
, maxlen
, <, ml
))
1325 memcpy(store
, buf
, ml
);
1332 if ((i
= bi
.bi_end
.l
) > 0) {
1333 memcpy(store
, bi
.bi_end
.s
, i
);
1341 rv
= MIN(blen
, maxlen
);
1342 memcpy(store
, buf
, rv
);
1351 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1353 NATCH_CHAR( struct bidi_info bi
; )
1354 int col_orig
= col
, n
, sz
;
1355 bool_t isbidi
, isuni
, istab
, isrepl
;
1359 /* Bidi only on request and when there is 8-bit data */
1360 isbidi
= isuni
= FAL0
;
1361 #ifdef HAVE_NATCH_CHAR
1362 isuni
= ((options
& OPT_UNICODE
) != 0);
1363 bidi_info_create(&bi
);
1364 if (bi
.bi_start
.l
== 0)
1366 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1369 if ((size_t)col
>= bi
.bi_pad
)
1376 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1378 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1381 #ifdef HAVE_NATCH_CHAR
1383 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1384 np
+= bi
.bi_start
.l
;
1388 while (*cp
!= '\0') {
1390 #ifdef HAVE_C90AMEND1
1391 if (mb_cur_max
> 1) {
1396 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1398 else if (wc
== L
'\t') {
1399 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1402 } else if (iswprint(wc
)) {
1403 # ifndef HAVE_WCWIDTH
1404 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1406 if ((n
= wcwidth(wc
)) == -1)
1416 istab
= (*cp
== '\t');
1417 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1426 np
[0] = (char)0xEFu
;
1427 np
[1] = (char)0xBFu
;
1428 np
[2] = (char)0xBDu
;
1433 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1441 if (fill
&& col
!= 0) {
1443 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1444 memset(nb
, ' ', col
);
1446 memset(np
, ' ', col
);
1451 #ifdef HAVE_NATCH_CHAR
1453 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1459 if (cols_decr_used_or_null
!= NULL
)
1460 *cols_decr_used_or_null
-= col_orig
- col
;
1466 makeprint(struct str
const *in
, struct str
*out
)
1468 char const *inp
, *maxp
;
1473 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1477 #ifdef HAVE_NATCH_CHAR
1478 if (mb_cur_max
> 1) {
1479 char mbb
[MB_LEN_MAX
+ 1];
1482 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1485 while (inp
< maxp
) {
1487 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1493 /* FIXME Why mbtowc() resetting here?
1494 * FIXME what about ISO 2022-JP plus -- those
1495 * FIXME will loose shifts, then!
1496 * FIXME THUS - we'd need special "known points"
1497 * FIXME to do so - say, after a newline!!
1498 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1499 mbtowc(&wc
, NULL
, mb_cur_max
);
1500 wc
= isuni
? 0xFFFD : '?';
1505 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1507 if ((wc
& ~(wchar_t)037) == 0)
1508 wc
= isuni
? 0x2400 | wc
: '?';
1509 else if (wc
== 0177)
1510 wc
= isuni
? 0x2421 : '?';
1512 wc
= isuni
? 0x2426 : '?';
1513 }else if(isuni
){ /* TODO ctext */
1514 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1515 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1517 /* And some zero-width messes */
1518 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1520 /* Oh about the ISO C wide character interfaces, baby! */
1524 if ((n
= wctomb(mbb
, wc
)) <= 0)
1527 assert(out
->l
< msz
);
1528 for (i
= 0; i
< n
; ++i
)
1532 #endif /* NATCH_CHAR */
1535 while (inp
< maxp
) {
1537 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1543 out
->s
[out
->l
] = '\0';
1548 delctrl(char *cp
, size_t len
)
1553 for (x
= y
= 0; x
< len
; ++x
)
1554 if (!cntrlchar(cp
[x
]))
1562 prstr(char const *s
)
1570 makeprint(&in
, &out
);
1571 rp
= savestrbuf(out
.s
, out
.l
);
1578 prout(char const *s
, size_t sz
, FILE *fp
)
1586 makeprint(&in
, &out
);
1587 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1594 putuc(int u
, int c
, FILE *fp
)
1600 #ifdef HAVE_NATCH_CHAR
1601 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1602 char mbb
[MB_LEN_MAX
];
1605 if ((n
= wctomb(mbb
, u
)) > 0) {
1607 for (i
= 0; i
< n
; ++i
)
1608 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1613 rv
= (putc('\0', fp
) != EOF
);
1618 rv
= (putc(c
, fp
) != EOF
);
1624 bidi_info_needed(char const *bdat
, size_t blen
)
1629 #ifdef HAVE_NATCH_CHAR
1630 if (options
& OPT_UNICODE
)
1632 /* TODO Checking for BIDI character: use S-CText fromutf8
1633 * TODO plus isrighttoleft (or whatever there will be)! */
1634 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1641 /* (Very very fuzzy, awaiting S-CText for good) */
1642 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1643 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1644 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1645 (c
>= 0x10800 && c
<= 0x10C48) ||
1646 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1651 #endif /* HAVE_NATCH_CHAR */
1657 bidi_info_create(struct bidi_info
*bip
)
1659 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1660 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1661 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1662 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1663 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1664 NATCH_CHAR( char const *hb
; )
1667 memset(bip
, 0, sizeof *bip
);
1668 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1670 #ifdef HAVE_NATCH_CHAR
1671 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1677 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1683 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1684 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1687 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1694 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1701 assert(inlen
== 0 || inbuf
!= NULL
);
1703 if (inlen
== UIZ_MAX
)
1704 inlen
= strlen(inbuf
);
1707 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1709 if ((inlen
== 1 && *inbuf
== '1') ||
1710 !ascncasecmp(inbuf
, "true", inlen
) ||
1711 !ascncasecmp(inbuf
, "yes", inlen
) ||
1712 !ascncasecmp(inbuf
, "on", inlen
))
1714 else if ((inlen
== 1 && *inbuf
== '0') ||
1715 !ascncasecmp(inbuf
, "false", inlen
) ||
1716 !ascncasecmp(inbuf
, "no", inlen
) ||
1717 !ascncasecmp(inbuf
, "off", inlen
))
1720 dat
= ac_alloc(inlen
+1);
1721 memcpy(dat
, inbuf
, inlen
);
1724 sli
= strtol(dat
, &eptr
, 0);
1725 if (*dat
!= '\0' && *eptr
== '\0')
1738 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1743 assert(inlen
== 0 || inbuf
!= NULL
);
1745 if (inlen
== UIZ_MAX
)
1746 inlen
= strlen(inbuf
);
1749 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1750 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1751 !ascncasecmp(inbuf
, "ask-", 4) &&
1752 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1753 (options
& OPT_INTERACTIVE
))
1754 rv
= getapproval(prompt
, rv
);
1760 n_is_all_or_aster(char const *name
){
1764 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1772 #ifdef HAVE_CLOCK_GETTIME
1774 #elif defined HAVE_GETTIMEOFDAY
1780 #ifdef HAVE_CLOCK_GETTIME
1781 clock_gettime(CLOCK_REALTIME
, &ts
);
1782 rv
= (time_t)ts
.tv_sec
;
1783 #elif defined HAVE_GETTIMEOFDAY
1784 gettimeofday(&ts
, NULL
);
1785 rv
= (time_t)ts
.tv_sec
;
1794 time_current_update(struct time_current
*tc
, bool_t full_update
)
1797 tc
->tc_time
= n_time_epoch();
1799 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1800 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1801 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1807 n_msleep(uiz_t millis
, bool_t ignint
){
1811 #ifdef HAVE_NANOSLEEP
1813 struct timespec ts
, trem
;
1816 ts
.tv_sec
= millis
/ 1000;
1817 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1819 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1821 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1824 #elif defined HAVE_SLEEP
1825 if((millis
/= 1000) == 0)
1827 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1830 # error Configuration should have detected a function for sleeping.
1838 n_err(char const *format
, ...){
1842 va_start(ap
, format
);
1844 if(options
& OPT_INTERACTIVE
)
1849 if(a_aux_err_dirty
++ == 0)
1850 fputs(UAGENT
": ", stderr
);
1851 vfprintf(stderr
, format
, ap
);
1852 if(strchr(format
, '\n') != NULL
){ /* TODO */
1853 a_aux_err_dirty
= 0;
1862 n_verr(char const *format
, va_list ap
){
1863 /* Check use cases of PS_ERRORS_NOTED, too! */
1865 char buf
[LINESIZE
], *xbuf
;
1867 struct a_aux_err_node
*enp
;
1869 LCTA(ERRORS_MAX
> 3);
1873 if(a_aux_err_dirty
++ == 0)
1874 fputs(UAGENT
": ", stderr
);
1877 if(!(options
& OPT_INTERACTIVE
))
1880 vfprintf(stderr
, format
, ap
);
1888 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1891 if (UICMP(z
, l
, >=, lmax
)) {
1892 /* FIXME Cannot reuse va_list
1894 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1899 fwrite(xbuf
, 1, l
, stderr
);
1901 /* Link it into the `errors' message ring */
1902 if((enp
= a_aux_err_tail
) == NULL
){
1904 enp
= scalloc(1, sizeof *enp
);
1905 if(a_aux_err_tail
!= NULL
)
1906 a_aux_err_tail
->ae_next
= enp
;
1908 a_aux_err_head
= enp
;
1909 a_aux_err_tail
= enp
;
1911 }else if(enp
->ae_str
.l
> 0 && enp
->ae_str
.s
[enp
->ae_str
.l
- 1] == '\n'){
1912 if(a_aux_err_cnt
< ERRORS_MAX
)
1915 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1916 a_aux_err_tail
->ae_next
= enp
;
1917 a_aux_err_tail
= enp
;
1918 free(enp
->ae_str
.s
);
1919 memset(enp
, 0, sizeof *enp
);
1922 n_str_add_buf(&enp
->ae_str
, xbuf
, l
);
1926 #endif /* HAVE_ERRORS */
1929 /* If the format ends with newline, be clean again */
1931 size_t i
= strlen(format
);
1933 if(i
> 0 && format
[i
- 1] == '\n'){
1935 a_aux_err_dirty
= 0;
1942 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
1946 va_start(ap
, format
);
1947 vfprintf(stderr
, format
, ap
);
1953 n_perr(char const *msg
, int errval
){
1966 n_err(fmt
, msg
, strerror(errval
));
1971 n_alert(char const *format
, ...){
1975 n_err(a_aux_err_dirty
> 0 ? _("\nAlert: ") : _("Alert: "));
1977 va_start(ap
, format
);
1986 n_panic(char const *format
, ...){
1990 if(a_aux_err_dirty
> 0){
1992 a_aux_err_dirty
= 0;
1994 fprintf(stderr
, UAGENT
": Panic: ");
1996 va_start(ap
, format
);
1997 vfprintf(stderr
, format
, ap
);
2003 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2010 struct a_aux_err_node
*enp
;
2017 if(!asccasecmp(*argv
, "show"))
2019 if(!asccasecmp(*argv
, "clear"))
2022 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2026 return v
== NULL
? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
2032 if(a_aux_err_head
== NULL
){
2033 fprintf(stderr
, _("The error ring is empty\n"));
2037 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
2039 fprintf(stderr
, _("tmpfile"));
2044 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
2045 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2046 ++i
, enp
->ae_str
.l
, enp
->ae_str
.s
);
2047 /* We don't know wether last string ended with NL; be simple */
2050 page_or_print(fp
, 0);
2056 a_aux_err_tail
= NULL
;
2057 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
2058 while((enp
= a_aux_err_head
) != NULL
){
2059 a_aux_err_head
= enp
->ae_next
;
2060 free(enp
->ae_str
.s
);
2065 #endif /* HAVE_ERRORS */
2069 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2076 if ((rv
= malloc(s
)) == NULL
)
2077 n_panic(_("no memory"));
2083 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2092 else if ((rv
= realloc(v
, s
)) == NULL
)
2093 n_panic(_("no memory"));
2099 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2106 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2107 n_panic(_("no memory"));
2112 #else /* !HAVE_DEBUG */
2113 CTA(sizeof(char) == sizeof(ui8_t
));
2115 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2116 # define _HOPE_SET(C) \
2118 union mem_ptr __xl, __xu;\
2119 struct mem_chunk *__xc;\
2120 __xl.p_p = (C).p_p;\
2121 __xc = __xl.p_c - 1;\
2124 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2125 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2126 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2127 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2128 __xu.p_ui8p += __xc->mc_size - 8;\
2129 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2130 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2131 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2132 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2134 # define _HOPE_GET_TRACE(C,BAD) \
2140 # define _HOPE_GET(C,BAD) \
2142 union mem_ptr __xl, __xu;\
2143 struct mem_chunk *__xc;\
2145 __xl.p_p = (C).p_p;\
2147 (C).p_cp = __xl.p_cp;\
2148 __xc = __xl.p_c - 1;\
2151 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2152 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2153 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2154 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2155 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2156 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2157 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2158 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2161 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2162 __xl.p_p, __i, mdbg_file, mdbg_line);\
2165 __xu.p_ui8p += __xc->mc_size - 8;\
2167 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2168 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2169 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2170 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2171 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2172 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2173 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2174 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2177 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2178 __xl.p_p, __i, mdbg_file, mdbg_line);\
2181 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2182 __xc->mc_file, __xc->mc_line);\
2186 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2193 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2194 n_panic("smalloc(): allocation too large: %s, line %d",
2195 mdbg_file
, mdbg_line
);
2196 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2198 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2199 n_panic(_("no memory"));
2200 p
.p_c
->mc_prev
= NULL
;
2201 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2202 _mem_list
->mc_prev
= p
.p_c
;
2203 p
.p_c
->mc_file
= mdbg_file
;
2204 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2205 p
.p_c
->mc_isfree
= FAL0
;
2206 p
.p_c
->mc_size
= (ui32_t
)s
;
2208 _mem_list
= p
.p_c
++;
2213 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2216 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2222 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2228 if ((p
.p_p
= v
) == NULL
) {
2229 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2233 _HOPE_GET(p
, isbad
);
2235 if (p
.p_c
->mc_isfree
) {
2236 n_err("srealloc(): region freed! At %s, line %d\n"
2237 "\tLast seen: %s, line %" PRIu16
"\n",
2238 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2242 if (p
.p_c
== _mem_list
)
2243 _mem_list
= p
.p_c
->mc_next
;
2245 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2246 if (p
.p_c
->mc_next
!= NULL
)
2247 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2250 _mem_mcur
-= p
.p_c
->mc_size
;
2254 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2255 n_panic("srealloc(): allocation too large: %s, line %d",
2256 mdbg_file
, mdbg_line
);
2257 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2259 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2260 n_panic(_("no memory"));
2261 p
.p_c
->mc_prev
= NULL
;
2262 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2263 _mem_list
->mc_prev
= p
.p_c
;
2264 p
.p_c
->mc_file
= mdbg_file
;
2265 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2266 p
.p_c
->mc_isfree
= FAL0
;
2267 p
.p_c
->mc_size
= (ui32_t
)s
;
2268 _mem_list
= p
.p_c
++;
2273 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2276 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2283 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2292 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2293 n_panic("scalloc(): allocation size too large: %s, line %d",
2294 mdbg_file
, mdbg_line
);
2295 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2296 n_panic("scalloc(): allocation count too large: %s, line %d",
2297 mdbg_file
, mdbg_line
);
2300 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2302 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2303 n_panic(_("no memory"));
2304 memset(p
.p_p
, 0, size
);
2305 p
.p_c
->mc_prev
= NULL
;
2306 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2307 _mem_list
->mc_prev
= p
.p_c
;
2308 p
.p_c
->mc_file
= mdbg_file
;
2309 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2310 p
.p_c
->mc_isfree
= FAL0
;
2311 p
.p_c
->mc_size
= (ui32_t
)size
;
2312 _mem_list
= p
.p_c
++;
2317 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2320 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2326 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2332 if ((p
.p_p
= v
) == NULL
) {
2333 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2337 _HOPE_GET(p
, isbad
);
2339 if (p
.p_c
->mc_isfree
) {
2340 n_err("sfree(): double-free avoided at %s, line %d\n"
2341 "\tLast seen: %s, line %" PRIu16
"\n",
2342 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2346 if (p
.p_c
== _mem_list
)
2347 _mem_list
= p
.p_c
->mc_next
;
2349 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2350 if (p
.p_c
->mc_next
!= NULL
)
2351 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2352 p
.p_c
->mc_isfree
= TRU1
;
2353 /* Trash contents (also see [21c05f8]) */
2354 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2357 _mem_mcur
-= p
.p_c
->mc_size
;
2359 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2360 p
.p_c
->mc_next
= _mem_free
;
2372 size_t c
= 0, s
= 0;
2377 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2380 s
+= p
.p_c
->mc_size
;
2381 p
.p_c
= p
.p_c
->mc_next
;
2386 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2387 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2392 c_smemtrace(void *v
)
2394 /* For _HOPE_GET() */
2395 char const * const mdbg_file
= "smemtrace()";
2396 int const mdbg_line
= -1;
2398 union mem_ptr p
, xp
;
2404 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2405 n_perr("tmpfile", 0);
2409 fprintf(fp
, "Memory statistics:\n"
2410 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2411 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2412 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2414 fprintf(fp
, "Currently allocated memory chunks:\n");
2415 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2416 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2419 _HOPE_GET_TRACE(xp
, isbad
);
2420 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2421 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2422 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2426 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2427 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2428 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2431 _HOPE_GET_TRACE(xp
, isbad
);
2432 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2433 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2434 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2435 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2439 page_or_print(fp
, lines
);
2449 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2451 union mem_ptr p
, xp
;
2452 bool_t anybad
= FAL0
, isbad
;
2456 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2457 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2460 _HOPE_GET_TRACE(xp
, isbad
);
2464 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2465 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2466 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2470 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2471 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2474 _HOPE_GET_TRACE(xp
, isbad
);
2478 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2479 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2480 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2487 # endif /* HAVE_DEVEL */
2491 # undef _HOPE_GET_TRACE
2493 #endif /* HAVE_DEBUG */