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) PART OF UI! */
956 { /* FIXME getprompt must mb->wc->mb+reset seq! */
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_width(char const *buf
, size_t blen
){
1265 blen
= (buf
== NULL
) ? 0 : strlen(buf
);
1266 assert(blen
== 0 || buf
!= NULL
);
1268 if((rv
= blen
) > 0){
1269 #ifdef HAVE_C90AMEND1
1273 memset(&mbs
, 0, sizeof mbs
);
1275 for(rv
= 0; blen
> 0;){
1276 size_t i
= mbrtowc(&wc
, buf
, blen
, &mbs
);
1289 # ifdef HAVE_WCWIDTH
1291 int w
= wcwidth(wc
);
1300 rv
+= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1307 #endif /* HAVE_C90AMEND1 */
1314 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1319 #ifdef HAVE_NATCH_CHAR
1320 maxlen
= MIN(maxlen
, blen
);
1321 for (rv
= 0; maxlen
> 0;) {
1322 int ml
= mblen(buf
, maxlen
);
1332 rv
= MIN(blen
, maxlen
);
1339 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1341 NATCH_CHAR( struct bidi_info bi
; )
1342 size_t rv
NATCH_CHAR( COMMA i
);
1349 #ifdef HAVE_NATCH_CHAR
1350 bidi_info_create(&bi
);
1351 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1356 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1361 if ((i
= bi
.bi_start
.l
) > 0) {
1362 memcpy(store
, bi
.bi_start
.s
, i
);
1368 while (maxlen
> 0) {
1369 int ml
= mblen(buf
, blen
);
1374 if (UICMP(z
, maxlen
, <, ml
))
1379 memcpy(store
, buf
, ml
);
1386 if ((i
= bi
.bi_end
.l
) > 0) {
1387 memcpy(store
, bi
.bi_end
.s
, i
);
1395 rv
= MIN(blen
, maxlen
);
1396 memcpy(store
, buf
, rv
);
1405 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1407 NATCH_CHAR( struct bidi_info bi
; )
1408 int col_orig
= col
, n
, sz
;
1409 bool_t isbidi
, isuni
, istab
, isrepl
;
1413 /* Bidi only on request and when there is 8-bit data */
1414 isbidi
= isuni
= FAL0
;
1415 #ifdef HAVE_NATCH_CHAR
1416 isuni
= ((options
& OPT_UNICODE
) != 0);
1417 bidi_info_create(&bi
);
1418 if (bi
.bi_start
.l
== 0)
1420 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1423 if ((size_t)col
>= bi
.bi_pad
)
1430 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1432 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1435 #ifdef HAVE_NATCH_CHAR
1437 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1438 np
+= bi
.bi_start
.l
;
1442 while (*cp
!= '\0') {
1444 #ifdef HAVE_C90AMEND1
1445 if (mb_cur_max
> 1) {
1450 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1452 else if (wc
== L
'\t') {
1453 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1456 } else if (iswprint(wc
)) {
1457 # ifndef HAVE_WCWIDTH
1458 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1460 if ((n
= wcwidth(wc
)) == -1)
1470 istab
= (*cp
== '\t');
1471 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1480 np
[0] = (char)0xEFu
;
1481 np
[1] = (char)0xBFu
;
1482 np
[2] = (char)0xBDu
;
1487 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1495 if (fill
&& col
!= 0) {
1497 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1498 memset(nb
, ' ', col
);
1500 memset(np
, ' ', col
);
1505 #ifdef HAVE_NATCH_CHAR
1507 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1513 if (cols_decr_used_or_null
!= NULL
)
1514 *cols_decr_used_or_null
-= col_orig
- col
;
1520 makeprint(struct str
const *in
, struct str
*out
)
1522 char const *inp
, *maxp
;
1527 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1531 #ifdef HAVE_NATCH_CHAR
1532 if (mb_cur_max
> 1) {
1533 char mbb
[MB_LEN_MAX
+ 1];
1536 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1539 while (inp
< maxp
) {
1541 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1547 /* FIXME Why mbtowc() resetting here?
1548 * FIXME what about ISO 2022-JP plus -- those
1549 * FIXME will loose shifts, then!
1550 * FIXME THUS - we'd need special "known points"
1551 * FIXME to do so - say, after a newline!!
1552 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1553 mbtowc(&wc
, NULL
, mb_cur_max
);
1554 wc
= isuni
? 0xFFFD : '?';
1559 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1561 if ((wc
& ~(wchar_t)037) == 0)
1562 wc
= isuni
? 0x2400 | wc
: '?';
1563 else if (wc
== 0177)
1564 wc
= isuni
? 0x2421 : '?';
1566 wc
= isuni
? 0x2426 : '?';
1567 }else if(isuni
){ /* TODO ctext */
1568 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1569 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1571 /* And some zero-width messes */
1572 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1574 /* Oh about the ISO C wide character interfaces, baby! */
1578 if ((n
= wctomb(mbb
, wc
)) <= 0)
1581 assert(out
->l
< msz
);
1582 for (i
= 0; i
< n
; ++i
)
1586 #endif /* NATCH_CHAR */
1589 while (inp
< maxp
) {
1591 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1597 out
->s
[out
->l
] = '\0';
1602 delctrl(char *cp
, size_t len
)
1607 for (x
= y
= 0; x
< len
; ++x
)
1608 if (!cntrlchar(cp
[x
]))
1616 prstr(char const *s
)
1624 makeprint(&in
, &out
);
1625 rp
= savestrbuf(out
.s
, out
.l
);
1632 prout(char const *s
, size_t sz
, FILE *fp
)
1640 makeprint(&in
, &out
);
1641 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1648 putuc(int u
, int c
, FILE *fp
)
1654 #ifdef HAVE_NATCH_CHAR
1655 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1656 char mbb
[MB_LEN_MAX
];
1659 if ((n
= wctomb(mbb
, u
)) > 0) {
1661 for (i
= 0; i
< n
; ++i
)
1662 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1667 rv
= (putc('\0', fp
) != EOF
);
1672 rv
= (putc(c
, fp
) != EOF
);
1678 bidi_info_needed(char const *bdat
, size_t blen
)
1683 #ifdef HAVE_NATCH_CHAR
1684 if (options
& OPT_UNICODE
)
1686 /* TODO Checking for BIDI character: use S-CText fromutf8
1687 * TODO plus isrighttoleft (or whatever there will be)! */
1688 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1695 /* (Very very fuzzy, awaiting S-CText for good) */
1696 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1697 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1698 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1699 (c
>= 0x10800 && c
<= 0x10C48) ||
1700 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1705 #endif /* HAVE_NATCH_CHAR */
1711 bidi_info_create(struct bidi_info
*bip
)
1713 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1714 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1715 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1716 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1717 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1718 NATCH_CHAR( char const *hb
; )
1721 memset(bip
, 0, sizeof *bip
);
1722 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1724 #ifdef HAVE_NATCH_CHAR
1725 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1731 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1737 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1738 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1741 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1748 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1755 assert(inlen
== 0 || inbuf
!= NULL
);
1757 if (inlen
== UIZ_MAX
)
1758 inlen
= strlen(inbuf
);
1761 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1763 if ((inlen
== 1 && *inbuf
== '1') ||
1764 !ascncasecmp(inbuf
, "true", inlen
) ||
1765 !ascncasecmp(inbuf
, "yes", inlen
) ||
1766 !ascncasecmp(inbuf
, "on", inlen
))
1768 else if ((inlen
== 1 && *inbuf
== '0') ||
1769 !ascncasecmp(inbuf
, "false", inlen
) ||
1770 !ascncasecmp(inbuf
, "no", inlen
) ||
1771 !ascncasecmp(inbuf
, "off", inlen
))
1774 dat
= ac_alloc(inlen
+1);
1775 memcpy(dat
, inbuf
, inlen
);
1778 sli
= strtol(dat
, &eptr
, 0);
1779 if (*dat
!= '\0' && *eptr
== '\0')
1792 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1797 assert(inlen
== 0 || inbuf
!= NULL
);
1799 if (inlen
== UIZ_MAX
)
1800 inlen
= strlen(inbuf
);
1803 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1804 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1805 !ascncasecmp(inbuf
, "ask-", 4) &&
1806 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1807 (options
& OPT_INTERACTIVE
))
1808 rv
= getapproval(prompt
, rv
);
1814 n_is_all_or_aster(char const *name
){
1818 rv
= ((name
[0] == '*' && name
[1] == '\0') || !asccasecmp(name
, "all"));
1826 #ifdef HAVE_CLOCK_GETTIME
1828 #elif defined HAVE_GETTIMEOFDAY
1834 #ifdef HAVE_CLOCK_GETTIME
1835 clock_gettime(CLOCK_REALTIME
, &ts
);
1836 rv
= (time_t)ts
.tv_sec
;
1837 #elif defined HAVE_GETTIMEOFDAY
1838 gettimeofday(&ts
, NULL
);
1839 rv
= (time_t)ts
.tv_sec
;
1848 time_current_update(struct time_current
*tc
, bool_t full_update
)
1851 tc
->tc_time
= n_time_epoch();
1853 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
1854 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
1855 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
1861 n_msleep(uiz_t millis
, bool_t ignint
){
1865 #ifdef HAVE_NANOSLEEP
1867 struct timespec ts
, trem
;
1870 ts
.tv_sec
= millis
/ 1000;
1871 ts
.tv_nsec
= (millis
%= 1000) * 1000 * 1000;
1873 while((i
= nanosleep(&ts
, &trem
)) != 0 && ignint
)
1875 rv
= (i
== 0) ? 0 : (trem
.tv_sec
* 1000) + (trem
.tv_nsec
/ (1000 * 1000));
1878 #elif defined HAVE_SLEEP
1879 if((millis
/= 1000) == 0)
1881 while((rv
= sleep((unsigned int)millis
)) != 0 && ignint
)
1884 # error Configuration should have detected a function for sleeping.
1892 n_err(char const *format
, ...){
1896 va_start(ap
, format
);
1898 if(options
& OPT_INTERACTIVE
)
1903 if(a_aux_err_dirty
++ == 0)
1904 fputs(UAGENT
": ", stderr
);
1905 vfprintf(stderr
, format
, ap
);
1906 if(strchr(format
, '\n') != NULL
){ /* TODO */
1907 a_aux_err_dirty
= 0;
1916 n_verr(char const *format
, va_list ap
){
1917 /* Check use cases of PS_ERRORS_NOTED, too! */
1919 char buf
[LINESIZE
], *xbuf
;
1921 struct a_aux_err_node
*enp
;
1923 LCTA(ERRORS_MAX
> 3);
1927 if(a_aux_err_dirty
++ == 0)
1928 fputs(UAGENT
": ", stderr
);
1931 if(!(options
& OPT_INTERACTIVE
))
1934 vfprintf(stderr
, format
, ap
);
1942 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
1945 if (UICMP(z
, l
, >=, lmax
)) {
1946 /* FIXME Cannot reuse va_list
1948 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1953 fwrite(xbuf
, 1, l
, stderr
);
1955 /* Link it into the `errors' message ring */
1956 if((enp
= a_aux_err_tail
) == NULL
){
1958 enp
= scalloc(1, sizeof *enp
);
1959 if(a_aux_err_tail
!= NULL
)
1960 a_aux_err_tail
->ae_next
= enp
;
1962 a_aux_err_head
= enp
;
1963 a_aux_err_tail
= enp
;
1965 }else if(enp
->ae_str
.l
> 0 && enp
->ae_str
.s
[enp
->ae_str
.l
- 1] == '\n'){
1966 if(a_aux_err_cnt
< ERRORS_MAX
)
1969 a_aux_err_head
= (enp
= a_aux_err_head
)->ae_next
;
1970 a_aux_err_tail
->ae_next
= enp
;
1971 a_aux_err_tail
= enp
;
1972 free(enp
->ae_str
.s
);
1973 memset(enp
, 0, sizeof *enp
);
1976 n_str_add_buf(&enp
->ae_str
, xbuf
, l
);
1980 #endif /* HAVE_ERRORS */
1983 /* If the format ends with newline, be clean again */
1985 size_t i
= strlen(format
);
1987 if(i
> 0 && format
[i
- 1] == '\n'){
1989 a_aux_err_dirty
= 0;
1996 n_err_sighdl(char const *format
, ...){ /* TODO sigsafe; obsolete! */
2000 va_start(ap
, format
);
2001 vfprintf(stderr
, format
, ap
);
2007 n_perr(char const *msg
, int errval
){
2020 n_err(fmt
, msg
, strerror(errval
));
2025 n_alert(char const *format
, ...){
2029 n_err(a_aux_err_dirty
> 0 ? _("\nAlert: ") : _("Alert: "));
2031 va_start(ap
, format
);
2040 n_panic(char const *format
, ...){
2044 if(a_aux_err_dirty
> 0){
2046 a_aux_err_dirty
= 0;
2048 fprintf(stderr
, UAGENT
": Panic: ");
2050 va_start(ap
, format
);
2051 vfprintf(stderr
, format
, ap
);
2057 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2064 struct a_aux_err_node
*enp
;
2071 if(!asccasecmp(*argv
, "show"))
2073 if(!asccasecmp(*argv
, "clear"))
2076 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2080 return v
== NULL
? !STOP
: !OKAY
; /* xxx 1:bad 0:good -- do some */
2086 if(a_aux_err_head
== NULL
){
2087 fprintf(stderr
, _("The error ring is empty\n"));
2091 if((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
2093 fprintf(stderr
, _("tmpfile"));
2098 for(i
= 0, enp
= a_aux_err_head
; enp
!= NULL
; enp
= enp
->ae_next
)
2099 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2100 ++i
, enp
->ae_str
.l
, enp
->ae_str
.s
);
2101 /* We don't know wether last string ended with NL; be simple */
2104 page_or_print(fp
, 0);
2110 a_aux_err_tail
= NULL
;
2111 a_aux_err_cnt
= a_aux_err_cnt_noted
= 0;
2112 while((enp
= a_aux_err_head
) != NULL
){
2113 a_aux_err_head
= enp
->ae_next
;
2114 free(enp
->ae_str
.s
);
2119 #endif /* HAVE_ERRORS */
2123 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2130 if ((rv
= malloc(s
)) == NULL
)
2131 n_panic(_("no memory"));
2137 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2146 else if ((rv
= realloc(v
, s
)) == NULL
)
2147 n_panic(_("no memory"));
2153 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2160 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2161 n_panic(_("no memory"));
2166 #else /* !HAVE_DEBUG */
2167 CTA(sizeof(char) == sizeof(ui8_t
));
2169 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2170 # define _HOPE_SET(C) \
2172 union mem_ptr __xl, __xu;\
2173 struct mem_chunk *__xc;\
2174 __xl.p_p = (C).p_p;\
2175 __xc = __xl.p_c - 1;\
2178 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2179 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2180 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2181 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2182 __xu.p_ui8p += __xc->mc_size - 8;\
2183 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2184 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2185 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2186 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2188 # define _HOPE_GET_TRACE(C,BAD) \
2194 # define _HOPE_GET(C,BAD) \
2196 union mem_ptr __xl, __xu;\
2197 struct mem_chunk *__xc;\
2199 __xl.p_p = (C).p_p;\
2201 (C).p_cp = __xl.p_cp;\
2202 __xc = __xl.p_c - 1;\
2205 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2206 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2207 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2208 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2209 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2210 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2211 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2212 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2215 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2216 __xl.p_p, __i, mdbg_file, mdbg_line);\
2219 __xu.p_ui8p += __xc->mc_size - 8;\
2221 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2222 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2223 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2224 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2225 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2226 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2227 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2228 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2231 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2232 __xl.p_p, __i, mdbg_file, mdbg_line);\
2235 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2236 __xc->mc_file, __xc->mc_line);\
2240 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2247 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2248 n_panic("smalloc(): allocation too large: %s, line %d",
2249 mdbg_file
, mdbg_line
);
2250 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2252 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2253 n_panic(_("no memory"));
2254 p
.p_c
->mc_prev
= NULL
;
2255 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2256 _mem_list
->mc_prev
= p
.p_c
;
2257 p
.p_c
->mc_file
= mdbg_file
;
2258 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2259 p
.p_c
->mc_isfree
= FAL0
;
2260 p
.p_c
->mc_size
= (ui32_t
)s
;
2262 _mem_list
= p
.p_c
++;
2267 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2270 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2276 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2282 if ((p
.p_p
= v
) == NULL
) {
2283 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2287 _HOPE_GET(p
, isbad
);
2289 if (p
.p_c
->mc_isfree
) {
2290 n_err("srealloc(): region freed! At %s, line %d\n"
2291 "\tLast seen: %s, line %" PRIu16
"\n",
2292 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2296 if (p
.p_c
== _mem_list
)
2297 _mem_list
= p
.p_c
->mc_next
;
2299 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2300 if (p
.p_c
->mc_next
!= NULL
)
2301 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2304 _mem_mcur
-= p
.p_c
->mc_size
;
2308 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2309 n_panic("srealloc(): allocation too large: %s, line %d",
2310 mdbg_file
, mdbg_line
);
2311 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2313 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2314 n_panic(_("no memory"));
2315 p
.p_c
->mc_prev
= NULL
;
2316 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2317 _mem_list
->mc_prev
= p
.p_c
;
2318 p
.p_c
->mc_file
= mdbg_file
;
2319 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2320 p
.p_c
->mc_isfree
= FAL0
;
2321 p
.p_c
->mc_size
= (ui32_t
)s
;
2322 _mem_list
= p
.p_c
++;
2327 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2330 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2337 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2346 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2347 n_panic("scalloc(): allocation size too large: %s, line %d",
2348 mdbg_file
, mdbg_line
);
2349 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2350 n_panic("scalloc(): allocation count too large: %s, line %d",
2351 mdbg_file
, mdbg_line
);
2354 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2356 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2357 n_panic(_("no memory"));
2358 memset(p
.p_p
, 0, size
);
2359 p
.p_c
->mc_prev
= NULL
;
2360 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2361 _mem_list
->mc_prev
= p
.p_c
;
2362 p
.p_c
->mc_file
= mdbg_file
;
2363 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2364 p
.p_c
->mc_isfree
= FAL0
;
2365 p
.p_c
->mc_size
= (ui32_t
)size
;
2366 _mem_list
= p
.p_c
++;
2371 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2374 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2380 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2386 if ((p
.p_p
= v
) == NULL
) {
2387 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2391 _HOPE_GET(p
, isbad
);
2393 if (p
.p_c
->mc_isfree
) {
2394 n_err("sfree(): double-free avoided at %s, line %d\n"
2395 "\tLast seen: %s, line %" PRIu16
"\n",
2396 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2400 if (p
.p_c
== _mem_list
)
2401 _mem_list
= p
.p_c
->mc_next
;
2403 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2404 if (p
.p_c
->mc_next
!= NULL
)
2405 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2406 p
.p_c
->mc_isfree
= TRU1
;
2407 /* Trash contents (also see [21c05f8]) */
2408 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2411 _mem_mcur
-= p
.p_c
->mc_size
;
2413 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2414 p
.p_c
->mc_next
= _mem_free
;
2426 size_t c
= 0, s
= 0;
2431 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2434 s
+= p
.p_c
->mc_size
;
2435 p
.p_c
= p
.p_c
->mc_next
;
2440 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2441 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2446 c_smemtrace(void *v
)
2448 /* For _HOPE_GET() */
2449 char const * const mdbg_file
= "smemtrace()";
2450 int const mdbg_line
= -1;
2452 union mem_ptr p
, xp
;
2458 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2459 n_perr("tmpfile", 0);
2463 fprintf(fp
, "Memory statistics:\n"
2464 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2465 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2466 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2468 fprintf(fp
, "Currently allocated memory chunks:\n");
2469 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2470 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2473 _HOPE_GET_TRACE(xp
, isbad
);
2474 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2475 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2476 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2480 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2481 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2482 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2485 _HOPE_GET_TRACE(xp
, isbad
);
2486 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2487 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2488 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2489 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2493 page_or_print(fp
, lines
);
2503 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2505 union mem_ptr p
, xp
;
2506 bool_t anybad
= FAL0
, isbad
;
2510 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2511 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2514 _HOPE_GET_TRACE(xp
, isbad
);
2518 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2519 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2520 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2524 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2525 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2528 _HOPE_GET_TRACE(xp
, isbad
);
2532 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2533 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2534 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2541 # endif /* HAVE_DEVEL */
2545 # undef _HOPE_GET_TRACE
2547 #endif /* HAVE_DEBUG */