1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
42 #include <sys/utsname.h>
48 # ifdef HAVE_GETADDRINFO
49 # include <sys/socket.h>
55 #ifndef HAVE_POSIX_RANDOM
63 ui8_t b8
[sizeof(struct rand_arc4
)];
64 ui32_t b32
[sizeof(struct rand_arc4
) / sizeof(ui32_t
)];
78 struct shvar_stack
*shs_next
; /* Outer stack frame */
79 char const *shs_value
; /* Remaining value to expand */
80 size_t shs_len
; /* gth of .shs_dat this level */
81 char const *shs_dat
; /* Result data of this level */
82 bool_t
*shs_err
; /* Or NULL */
83 bool_t shs_bsesc
; /* Shall backslash escaping be performed */
88 struct err_node
*en_next
;
95 struct mem_chunk
*mc_prev
;
96 struct mem_chunk
*mc_next
;
106 struct mem_chunk
*p_c
;
112 #ifndef HAVE_POSIX_RANDOM
113 static union rand_state
*_rand
;
116 /* {hold,rele}_all_sigs() */
117 static size_t _alls_depth
;
118 static sigset_t _alls_nset
, _alls_oset
;
120 /* {hold,rele}_sigs() */
121 static size_t _hold_sigdepth
;
122 static sigset_t _hold_nset
, _hold_oset
;
124 /* NYD, memory pool debug */
126 static ui32_t _nyd_curr
, _nyd_level
;
127 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
130 /* Error ring, for `errors' */
132 static struct err_node
*_err_head
, *_err_tail
;
133 static size_t _err_cnt
, _err_cnt_noted
;
137 static size_t _mem_aall
, _mem_acur
, _mem_amax
,
138 _mem_mall
, _mem_mcur
, _mem_mmax
;
140 static struct mem_chunk
*_mem_list
, *_mem_free
;
143 /* Our ARC4 random generator with its completely unacademical pseudo
144 * initialization (shall /dev/urandom fail) */
145 #ifndef HAVE_POSIX_RANDOM
146 static void _rand_init(void);
147 static ui32_t
_rand_weak(ui32_t seed
);
148 SINLINE ui8_t
_rand_get8(void);
151 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
153 static char * _colour_iso6429(char const *wish
);
157 static void _nyd_print(int fd
, struct nyd_info
*nip
);
160 /* Perform shell variable expansion */
161 static char * _sh_exp_var(struct shvar_stack
*shsp
);
163 #ifndef HAVE_POSIX_RANDOM
167 # ifdef HAVE_CLOCK_GETTIME
172 union {int fd
; size_t i
;} u
;
176 _rand
= smalloc(sizeof *_rand
);
178 if ((u
.fd
= open("/dev/urandom", O_RDONLY
)) != -1) {
179 bool_t ok
= (sizeof *_rand
== (size_t)read(u
.fd
, _rand
, sizeof *_rand
));
186 for (seed
= (uintptr_t)_rand
& UI32_MAX
, rnd
= 21; rnd
!= 0; --rnd
) {
187 for (u
.i
= NELEM(_rand
->b32
); u
.i
-- != 0;) {
190 # ifdef HAVE_CLOCK_GETTIME
191 clock_gettime(CLOCK_REALTIME
, &ts
);
192 t
= (ui32_t
)ts
.tv_nsec
;
194 gettimeofday(&ts
, NULL
);
195 t
= (ui32_t
)ts
.tv_usec
;
198 t
= (t
>> 16) | (t
<< 16);
199 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ t
);
200 _rand
->b32
[t
% NELEM(_rand
->b32
)] ^= seed
;
201 if (rnd
== 7 || rnd
== 17)
202 _rand
->b32
[u
.i
] ^= _rand_weak(seed
^ (ui32_t
)ts
.tv_sec
);
203 k
= _rand
->b32
[u
.i
] % NELEM(_rand
->b32
);
204 _rand
->b32
[k
] ^= _rand
->b32
[u
.i
];
205 seed
^= _rand_weak(_rand
->b32
[k
]);
207 seed
^= nextprime(seed
);
211 for (u
.i
= 11 * sizeof(_rand
->b8
); u
.i
!= 0; --u
.i
)
218 _rand_weak(ui32_t seed
)
220 /* From "Random number generators: good ones are hard to find",
221 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
222 * October 1988, p. 1195.
223 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
230 seed
= (seed
* 16807) - (hi
* 2836);
231 if ((si32_t
)seed
< 0)
241 si
= _rand
->a
._dat
[++_rand
->a
._i
];
242 sj
= _rand
->a
._dat
[_rand
->a
._j
+= si
];
243 _rand
->a
._dat
[_rand
->a
._i
] = sj
;
244 _rand
->a
._dat
[_rand
->a
._j
] = si
;
245 return _rand
->a
._dat
[(ui8_t
)(si
+ sj
)];
247 #endif /* HAVE_POSIX_RANDOM */
251 _colour_iso6429(char const *wish
)
257 {"bold", '1'}, {"underline", '4'}, {"inverse", '7'}
259 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
260 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
262 char const * const wish_orig
= wish
;
263 char *xwish
, *cp
, cfg
[3] = {0, 0, 0};
266 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
267 * value, ensure we have enough room for that */
269 size_t i
= strlen(wish
) +1;
270 xwish
= salloc(MAX(i
, sizeof("\033[1;30;40m")));
271 memcpy(xwish
, wish
, i
);
275 /* Iterate over the colour spec */
276 while ((cp
= n_strsep(&xwish
, ',', TRU1
)) != NULL
) {
277 char *y
, *x
= strchr(cp
, '=');
280 n_err(_("Invalid colour specification \"%s\": %s\n"), wish_orig
, cp
);
285 if (!asccasecmp(cp
, "ft")) {
286 for (idp
= fta
;; ++idp
)
287 if (idp
== fta
+ NELEM(fta
))
289 else if (!asccasecmp(x
, idp
->id_name
)) {
290 cfg
[0] = idp
->id_modc
;
293 } else if (!asccasecmp(cp
, "fg")) {
296 } else if (!asccasecmp(cp
, "bg")) {
299 for (idp
= ca
;; ++idp
)
300 if (idp
== ca
+ NELEM(ca
))
302 else if (!asccasecmp(x
, idp
->id_name
)) {
310 /* Restore our salloc() buffer, create return value */
311 xwish
= UNCONST(wish
);
312 if (cfg
[0] || cfg
[1] || cfg
[2]) {
326 if (cfg
[0] || cfg
[1])
336 return UNCONST(wish
);
338 #endif /* HAVE_COLOUR */
342 _nyd_print(int fd
, struct nyd_info
*nip
)
345 union {int i
; size_t z
;} u
;
347 u
.i
= snprintf(buf
, sizeof buf
,
348 "%c [%2" PRIu32
"] %.25s (%.16s:%" PRIu32
")\n",
349 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
350 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
353 if (u
.z
> sizeof buf
)
354 u
.z
= sizeof buf
- 1; /* (Skip \0) */
361 _sh_exp_var(struct shvar_stack
*shsp
)
363 struct shvar_stack next
, *np
, *tmp
;
365 char lc
, c
, *cp
, *rv
;
369 if (*(vp
= shsp
->shs_value
) != '$') {
370 bool_t bsesc
= shsp
->shs_bsesc
;
371 union {bool_t hadbs
; char c
;} u
= {FAL0
};
374 for (lc
= '\0', i
= 0; ((c
= *vp
) != '\0'); ++i
, ++vp
) {
375 if (c
== '$' && lc
!= '\\')
379 lc
= (lc
== '\\') ? (u
.hadbs
= TRU1
, '\0') : c
;
384 shsp
->shs_dat
= cp
= savestrbuf(shsp
->shs_dat
, i
);
386 for (lc
= '\0', rv
= cp
; (u
.c
= *cp
++) != '\0';) {
387 if (u
.c
!= '\\' || lc
== '\\')
389 lc
= (lc
== '\\') ? '\0' : u
.c
;
393 shsp
->shs_len
= PTR2SIZE(rv
- shsp
->shs_dat
);
396 if ((lc
= (*++vp
== '{')))
400 * Environment variable names used by the utilities in the Shell and
401 * Utilities volume of POSIX.1-2008 consist solely of uppercase
402 * letters, digits, and the <underscore> ('_') from the characters
403 * defined in Portable Character Set and do not begin with a digit.
404 * Other characters may be permitted by an implementation;
405 * applications shall tolerate the presence of such names. */
407 for (i
= 0; (c
= *vp
) != '\0'; ++i
, ++vp
)
408 if (!alnumchar(c
) && c
!= '_')
413 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
415 shsp
->shs_len
= strlen(shsp
->shs_value
);
416 shsp
->shs_dat
= shsp
->shs_value
;
417 if (shsp
->shs_err
!= NULL
)
418 *shsp
->shs_err
= TRU1
;
425 if ((cp
= vok_vlook(savestrbuf(shsp
->shs_dat
, i
))) != NULL
)
426 shsp
->shs_len
= strlen(shsp
->shs_dat
= cp
);
431 /* That level made the great and completed encoding. Build result */
433 for (i
= 0, np
= shsp
, shsp
= NULL
; np
!= NULL
;) {
441 cp
= rv
= salloc(i
+1);
442 while (shsp
!= NULL
) {
444 shsp
= shsp
->shs_next
;
445 memcpy(cp
, np
->shs_dat
, np
->shs_len
);
454 memset(&next
, 0, sizeof next
);
455 next
.shs_next
= shsp
;
457 next
.shs_err
= shsp
->shs_err
;
458 next
.shs_bsesc
= shsp
->shs_bsesc
;
459 rv
= _sh_exp_var(&next
);
467 kill(getpid(), signo
);
472 safe_signal(int signum
, sighandler_type handler
)
474 struct sigaction nact
, oact
;
478 nact
.sa_handler
= handler
;
479 sigemptyset(&nact
.sa_mask
);
482 nact
.sa_flags
|= SA_RESTART
;
484 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
493 if (_alls_depth
++ == 0) {
494 sigfillset(&_alls_nset
);
495 sigdelset(&_alls_nset
, SIGABRT
);
497 sigdelset(&_alls_nset
, SIGBUS
);
499 sigdelset(&_alls_nset
, SIGCHLD
);
500 sigdelset(&_alls_nset
, SIGFPE
);
501 sigdelset(&_alls_nset
, SIGILL
);
502 sigdelset(&_alls_nset
, SIGKILL
);
503 sigdelset(&_alls_nset
, SIGSEGV
);
504 sigdelset(&_alls_nset
, SIGSTOP
);
505 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
514 if (--_alls_depth
== 0)
515 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
523 if (_hold_sigdepth
++ == 0) {
524 sigemptyset(&_hold_nset
);
525 sigaddset(&_hold_nset
, SIGHUP
);
526 sigaddset(&_hold_nset
, SIGINT
);
527 sigaddset(&_hold_nset
, SIGQUIT
);
528 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
537 if (--_hold_sigdepth
== 0)
538 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
544 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
546 struct nyd_info
*nip
= _nyd_infos
;
548 if (_nyd_curr
!= NELEM(_nyd_infos
))
554 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
555 nip
->ni_level
= ((act
== 0) ? _nyd_level
556 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
560 _nyd_oncrash(int signo
)
562 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
563 struct sigaction xact
;
567 struct nyd_info
*nip
;
569 LCTA(sizeof("./") -1 + sizeof(UAGENT
) -1 + sizeof(".dat") < PATH_MAX
);
571 xact
.sa_handler
= SIG_DFL
;
572 sigemptyset(&xact
.sa_mask
);
574 sigaction(signo
, &xact
, NULL
);
577 fnl
= sizeof(UAGENT
) -1;
579 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
580 (cp
= pathbuf
)[0] = '.';
583 memcpy(cp
= pathbuf
, tempdir
, i
);
584 cp
[i
++] = '/'; /* xxx pathsep */
585 memcpy(cp
+= i
, UAGENT
, fnl
);
587 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
588 fnl
= i
+ sizeof(".dat") -1;
590 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
594 # define _X(X) (X), sizeof(X) -1
595 write(fd
, _X("\n\nNYD: program dying due to signal "));
597 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
601 *--cp
= "0123456789"[i
% 10];
604 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
606 write(fd
, _X(":\n"));
608 if (_nyd_infos
[NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
609 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< NELEM(_nyd_infos
); ++i
)
610 _nyd_print(fd
, nip
++);
611 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
612 _nyd_print(fd
, nip
++);
614 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
616 if (fd
!= STDERR_FILENO
) {
617 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
618 write(STDERR_FILENO
, pathbuf
, fnl
);
619 write(STDERR_FILENO
, _X("\n"));
626 sigaddset(&xset
, signo
);
627 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
632 #endif /* HAVE_NYD */
635 touch(struct message
*mp
)
638 mp
->m_flag
|= MTOUCH
;
639 if (!(mp
->m_flag
& MREAD
))
640 mp
->m_flag
|= MREAD
| MSTATUS
;
645 is_dir(char const *name
)
652 if (!stat(name
, &sbuf
)) {
653 rv
= (S_ISDIR(sbuf
.st_mode
) != 0);
655 } else if (errno
!= EINTR
)
662 argcount(char **argv
)
667 for (ap
= argv
; *ap
++ != NULL
;)
670 return (int)PTR2SIZE(ap
- argv
- 1);
680 if ((cp
= ok_vlook(screen
)) == NULL
|| (s
= atoi(cp
)) <= 0)
681 s
= scrnheight
- 2; /* XXX no magics */
687 get_pager(char const **env_addon
)
692 cp
= ok_vlook(PAGER
);
693 if (cp
== NULL
|| *cp
== '\0')
696 if (env_addon
!= NULL
) {
698 if (strstr(cp
, "less") != NULL
) {
699 if (!env_blook("LESS", TRU1
))
700 *env_addon
= "LESS=FRSXi";
701 } else if (strstr(cp
, "lv") != NULL
) {
702 if (!env_blook("LV", TRU1
))
703 *env_addon
= "LV=-c";
711 page_or_print(FILE *fp
, size_t lines
)
719 if (IS_TTY_SESSION() && (cp
= ok_vlook(crt
)) != NULL
) {
721 union {sl_i sli
; size_t rows
;} u
;
723 u
.sli
= strtol(cp
, &eptr
, 0);
724 u
.rows
= (*cp
!= '\0' && *eptr
== '\0')
725 ? (size_t)u
.sli
: (size_t)scrnheight
;
727 if (u
.rows
> 0 && lines
== 0) {
728 while ((c
= getc(fp
)) != EOF
)
729 if (c
== '\n' && ++lines
>= u
.rows
)
734 if (lines
>= u
.rows
) {
735 run_command(get_pager(NULL
), 0, fileno(fp
), COMMAND_FD_PASS
,
736 NULL
, NULL
, NULL
, NULL
);
741 while ((c
= getc(fp
)) != EOF
)
748 which_protocol(char const *name
) /* XXX (->URL (yet auxlily.c)) */
754 enum protocol rv
= PROTO_UNKNOWN
;
757 temporary_protocol_ext
= NULL
;
759 if (name
[0] == '%' && name
[1] == ':')
761 for (cp
= name
; *cp
&& *cp
!= ':'; cp
++)
765 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
766 if (!strncmp(name
, "pop3://", 7)) {
770 n_err(_("No POP3 support compiled in\n"));
772 } else if (!strncmp(name
, "pop3s://", 8)) {
773 #if defined HAVE_POP3 && defined HAVE_SSL
777 n_err(_("No POP3 support compiled in\n"));
780 n_err(_("No SSL support compiled in\n"));
783 } else if (!strncmp(name
, "imap://", 7)) {
787 fprintf(stderr
, _("No IMAP support compiled in.\n"));
789 } else if (!strncmp(name
, "imaps://", 8)) {
790 #if defined HAVE_IMAP && defined HAVE_SSL
794 fprintf(stderr
, _("No IMAP support compiled in.\n"));
797 fprintf(stderr
, _("No SSL support compiled in.\n"));
804 /* TODO This is the de facto maildir code and thus belongs into there!
805 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
806 * TODO or (more likely) in addition to *newfolders*) */
809 np
= ac_alloc((sz
= strlen(name
)) + 4 +1);
810 memcpy(np
, name
, sz
+ 1);
811 if (!stat(name
, &st
)) {
812 if (S_ISDIR(st
.st_mode
) &&
813 (memcpy(np
+sz
, "/tmp", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
814 (memcpy(np
+sz
, "/new", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)) &&
815 (memcpy(np
+sz
, "/cur", 5), !stat(np
, &st
) && S_ISDIR(st
.st_mode
)))
818 if ((memcpy(np
+sz
, cp
=".gz", 4), !stat(np
, &st
) && S_ISREG(st
.st_mode
)) ||
819 (memcpy(np
+sz
, cp
=".xz",4), !stat(np
,&st
) && S_ISREG(st
.st_mode
)) ||
820 (memcpy(np
+sz
, cp
=".bz2",5), !stat(np
, &st
) && S_ISREG(st
.st_mode
)))
821 temporary_protocol_ext
= cp
;
822 else if ((cp
= ok_vlook(newfolders
)) != NULL
&& !strcmp(cp
, "maildir"))
832 torek_hash(char const *name
)
834 /* Chris Torek's hash.
835 * NOTE: need to change *at least* create-okey-map.pl when changing the
840 while (*name
!= '\0') {
849 pjw(char const *cp
) /* TODO obsolete that -> torek_hash */
856 h
= (h
<< 4 & 0xffffffff) + (*cp
&0377);
857 if ((g
= h
& 0xf0000000) != 0) {
869 static ui32_t
const primes
[] = {
870 5, 11, 23, 47, 97, 157, 283,
871 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
872 131071, 262139, 524287, 1048573, 2097143, 4194301,
873 8388593, 16777213, 33554393, 67108859, 134217689,
874 268435399, 536870909, 1073741789, 2147483647
880 i
= (n
< primes
[NELEM(primes
) / 4] ? 0
881 : (n
< primes
[NELEM(primes
) / 2] ? NELEM(primes
) / 4
882 : NELEM(primes
) / 2));
884 if ((mprime
= primes
[i
]) > n
)
886 while (++i
< NELEM(primes
));
887 if (i
== NELEM(primes
) && mprime
< n
)
894 n_shell_expand_tilde(char const *s
, bool_t
*err_or_null
)
908 if (*(rp
= s
+ 1) == '/' || *rp
== '\0')
911 if ((rp
= strchr(s
+ 1, '/')) == NULL
)
912 rp
= (np
= UNCONST(s
)) + 1;
914 nl
= PTR2SIZE(rp
- s
);
915 np
= savestrbuf(s
, nl
);
918 if ((pwp
= getpwnam(np
)) == NULL
) {
927 rv
= salloc(nl
+ 1 + rl
+1);
930 memcpy(rv
+ nl
, rp
, rl
);
939 if (err_or_null
!= NULL
)
946 n_shell_expand_var(char const *s
, bool_t bsescape
, bool_t
*err_or_null
)
948 struct shvar_stack top
;
952 memset(&top
, 0, sizeof top
);
955 if ((top
.shs_err
= err_or_null
) != NULL
)
957 top
.shs_bsesc
= bsescape
;
958 rv
= _sh_exp_var(&top
);
964 n_shell_expand_escape(char const **s
, bool_t use_nail_extensions
)
972 if ((c
= *xs
& 0xFF) == '\0')
978 switch ((c
= *xs
& 0xFF)) {
980 case 'a': c
= '\a'; break;
981 case 'b': c
= '\b'; break;
982 case 'c': c
= PROMPT_STOP
; break;
983 case 'f': c
= '\f'; break;
984 case 'n': c
= '\n'; break;
985 case 'r': c
= '\r'; break;
986 case 't': c
= '\t'; break;
987 case 'v': c
= '\v'; break;
989 for (++xs
, c
= 0, n
= 4; --n
> 0 && octalchar(*xs
); ++xs
) {
994 /* S-nail extension for nice (get)prompt(()) support */
999 if (use_nail_extensions
) {
1001 case '&': c
= ok_blook(bsdcompat
) ? '&' : '?'; break;
1002 case '?': c
= (pstate
& PS_EVAL_ERROR
) ? '1' : '0'; break;
1003 case '$': c
= PROMPT_DOLLAR
; break;
1004 case '@': c
= PROMPT_AT
; break;
1010 /* A sole <backslash> at EOS is treated as-is! */
1024 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
1026 static char buf
[PROMPT_BUFFER_SIZE
];
1029 char const *ccp_base
, *ccp
;
1030 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA
) maxlen
, dfmaxlen
;
1031 bool_t trigger
; /* 1.: `errors' ring note? 2.: first loop tick done */
1034 /* No other place to place this */
1036 if (options
& OPT_INTERACTIVE
) {
1037 if (!(pstate
& PS_ERRORS_NOTED
) && _err_head
!= NULL
) {
1038 pstate
|= PS_ERRORS_NOTED
;
1039 fprintf(stderr
, _("There are new messages in the error message ring "
1040 "(denoted by \"#ERR#\")\n"
1041 " The `errors' command manages this message ring\n"));
1044 if ((trigger
= (_err_cnt_noted
!= _err_cnt
)))
1045 _err_cnt_noted
= _err_cnt
;
1051 if ((ccp_base
= ok_vlook(prompt
)) == NULL
|| *ccp_base
== '\0') {
1061 ccp_base
= savecatsep(_("#ERR#"), '\0', ccp_base
);
1063 NATCH_CHAR( cclen_base
= strlen(ccp_base
); )
1065 dfmaxlen
= 0; /* keep CC happy */
1069 NATCH_CHAR( cclen
= cclen_base
; )
1070 maxlen
= sizeof(buf
) -1;
1078 #ifdef HAVE_NATCH_CHAR
1079 c
= mblen(ccp
, cclen
); /* TODO use mbrtowc() */
1088 } else if ((l
= c
) > 1) {
1098 if ((c
= n_shell_expand_escape(&ccp
, TRU1
)) > 0) {
1104 if (c
== 0 || c
== PROMPT_STOP
)
1108 char const *a
= (c
== PROMPT_DOLLAR
) ? account_name
: displayname
;
1111 if ((l
= field_put_bidi_clip(cp
, dfmaxlen
, a
, strlen(a
))) > 0) {
1131 nodename(int mayoverride
)
1133 static char *sys_hostname
, *hostname
; /* XXX free-at-exit */
1138 # ifdef HAVE_GETADDRINFO
1139 struct addrinfo hints
, *res
;
1141 struct hostent
*hent
;
1146 if (mayoverride
&& (hn
= ok_vlook(hostname
)) != NULL
&& *hn
!= '\0') {
1148 } else if ((hn
= sys_hostname
) == NULL
) {
1152 # ifdef HAVE_GETADDRINFO
1153 memset(&hints
, 0, sizeof hints
);
1154 hints
.ai_family
= AF_UNSPEC
;
1155 hints
.ai_flags
= AI_CANONNAME
;
1156 if (getaddrinfo(hn
, NULL
, &hints
, &res
) == 0) {
1157 if (res
->ai_canonname
!= NULL
) {
1158 size_t l
= strlen(res
->ai_canonname
) +1;
1161 memcpy(hn
, res
->ai_canonname
, l
);
1166 hent
= gethostbyname(hn
);
1171 sys_hostname
= sstrdup(hn
);
1172 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1173 if (hn
!= ut
.nodename
)
1179 if (hostname
!= NULL
&& hostname
!= sys_hostname
)
1181 hostname
= sstrdup(hn
);
1187 getrandstring(size_t length
)
1194 #ifndef HAVE_POSIX_RANDOM
1199 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1200 * with PAD stripped is still longer than what the user requests, easy way */
1201 data
= ac_alloc(i
= length
+ 3);
1203 #ifndef HAVE_POSIX_RANDOM
1205 data
[i
] = (char)_rand_get8();
1210 union {ui32_t i4
; char c
[4];} r
;
1213 r
.i4
= (ui32_t
)arc4random();
1214 switch ((j
= i
& 3)) {
1215 case 0: cp
[3] = r
.c
[3]; j
= 4;
1216 case 3: cp
[2] = r
.c
[2];
1217 case 2: cp
[1] = r
.c
[1];
1218 default: cp
[0] = r
.c
[0]; break;
1226 b64_encode_buf(&b64
, data
, length
+ 3,
1227 B64_SALLOC
| B64_RFC4648URL
| B64_NOPAD
);
1230 assert(b64
.l
>= length
);
1231 b64
.s
[length
] = '\0';
1237 makedir(char const *name
)
1240 enum okay rv
= STOP
;
1243 if (!mkdir(name
, 0700))
1247 if ((e
== EEXIST
|| e
== ENOSYS
) && !stat(name
, &st
) &&
1248 S_ISDIR(st
.st_mode
))
1257 cwget(struct cw
*cw
)
1259 enum okay rv
= STOP
;
1262 if ((cw
->cw_fd
= open(".", O_RDONLY
)) == -1)
1264 if (fchdir(cw
->cw_fd
) == -1) {
1275 cwret(struct cw
*cw
)
1277 enum okay rv
= STOP
;
1280 if (!fchdir(cw
->cw_fd
))
1287 cwrelse(struct cw
*cw
)
1294 #else /* !HAVE_FCHDIR */
1296 cwget(struct cw
*cw
)
1298 enum okay rv
= STOP
;
1301 if (getcwd(cw
->cw_wd
, sizeof cw
->cw_wd
) != NULL
&& !chdir(cw
->cw_wd
))
1308 cwret(struct cw
*cw
)
1310 enum okay rv
= STOP
;
1313 if (!chdir(cw
->cw_wd
))
1320 cwrelse(struct cw
*cw
)
1326 #endif /* !HAVE_FCHDIR */
1329 field_detect_clip(size_t maxlen
, char const *buf
, size_t blen
)/*TODO mbrtowc()*/
1334 #ifdef HAVE_NATCH_CHAR
1335 maxlen
= MIN(maxlen
, blen
);
1336 for (rv
= 0; maxlen
> 0;) {
1337 int ml
= mblen(buf
, maxlen
);
1347 rv
= MIN(blen
, maxlen
);
1354 field_put_bidi_clip(char *store
, size_t maxlen
, char const *buf
, size_t blen
)
1356 NATCH_CHAR( struct bidi_info bi
; )
1357 size_t rv
NATCH_CHAR( COMMA i
);
1364 #ifdef HAVE_NATCH_CHAR
1365 bidi_info_create(&bi
);
1366 if (bi
.bi_start
.l
== 0 || !bidi_info_needed(buf
, blen
)) {
1371 if (maxlen
>= (i
= bi
.bi_pad
+ bi
.bi_end
.l
+ bi
.bi_start
.l
))
1376 if ((i
= bi
.bi_start
.l
) > 0) {
1377 memcpy(store
, bi
.bi_start
.s
, i
);
1383 while (maxlen
> 0) {
1384 int ml
= mblen(buf
, blen
);
1389 if (UICMP(z
, maxlen
, <, ml
))
1394 memcpy(store
, buf
, ml
);
1401 if ((i
= bi
.bi_end
.l
) > 0) {
1402 memcpy(store
, bi
.bi_end
.s
, i
);
1410 rv
= MIN(blen
, maxlen
);
1411 memcpy(store
, buf
, rv
);
1420 colalign(char const *cp
, int col
, int fill
, int *cols_decr_used_or_null
)
1422 NATCH_CHAR( struct bidi_info bi
; )
1423 int col_orig
= col
, n
, sz
;
1424 bool_t isbidi
, isuni
, istab
, isrepl
;
1428 /* Bidi only on request and when there is 8-bit data */
1429 isbidi
= isuni
= FAL0
;
1430 #ifdef HAVE_NATCH_CHAR
1431 isuni
= ((options
& OPT_UNICODE
) != 0);
1432 bidi_info_create(&bi
);
1433 if (bi
.bi_start
.l
== 0)
1435 if (!(isbidi
= bidi_info_needed(cp
, strlen(cp
))))
1438 if ((size_t)col
>= bi
.bi_pad
)
1445 np
= nb
= salloc(mb_cur_max
* strlen(cp
) +
1447 NATCH_CHAR( + (isbidi
? bi
.bi_start
.l
+ bi
.bi_end
.l
: 0) )
1450 #ifdef HAVE_NATCH_CHAR
1452 memcpy(np
, bi
.bi_start
.s
, bi
.bi_start
.l
);
1453 np
+= bi
.bi_start
.l
;
1457 while (*cp
!= '\0') {
1459 #ifdef HAVE_C90AMEND1
1460 if (mb_cur_max
> 1) {
1465 if ((sz
= mbtowc(&wc
, cp
, mb_cur_max
)) == -1)
1467 else if (wc
== L
'\t') {
1468 cp
+= sz
- 1; /* Silly, no such charset known (.. until S-Ctext) */
1471 } else if (iswprint(wc
)) {
1472 # ifndef HAVE_WCWIDTH
1473 n
= 1 + (wc
>= 0x1100u
); /* TODO use S-CText isfullwidth() */
1475 if ((n
= wcwidth(wc
)) == -1)
1485 istab
= (*cp
== '\t');
1486 isrepl
= !(istab
|| isprint((uc_i
)*cp
));
1495 np
[0] = (char)0xEFu
;
1496 np
[1] = (char)0xBFu
;
1497 np
[2] = (char)0xBDu
;
1502 } else if (istab
|| (sz
== 1 && spacechar(*cp
))) {
1510 if (fill
&& col
!= 0) {
1512 memmove(nb
+ col
, nb
, PTR2SIZE(np
- nb
));
1513 memset(nb
, ' ', col
);
1515 memset(np
, ' ', col
);
1520 #ifdef HAVE_NATCH_CHAR
1522 memcpy(np
, bi
.bi_end
.s
, bi
.bi_end
.l
);
1528 if (cols_decr_used_or_null
!= NULL
)
1529 *cols_decr_used_or_null
-= col_orig
- col
;
1535 makeprint(struct str
const *in
, struct str
*out
)
1537 static int print_all_chars
= -1;
1539 char const *inp
, *maxp
;
1544 if (print_all_chars
== -1)
1545 print_all_chars
= ok_blook(print_all_chars
);
1547 out
->s
= outp
= smalloc(DBG( msz
= ) in
->l
*mb_cur_max
+ 2u*mb_cur_max
+1);
1551 if (print_all_chars
) {
1553 memcpy(outp
, inp
, out
->l
);
1557 #ifdef HAVE_NATCH_CHAR
1558 if (mb_cur_max
> 1) {
1559 char mbb
[MB_LEN_MAX
+ 1];
1562 bool_t isuni
= ((options
& OPT_UNICODE
) != 0);
1565 while (inp
< maxp
) {
1567 n
= mbtowc(&wc
, inp
, PTR2SIZE(maxp
- inp
));
1573 /* FIXME Why mbtowc() resetting here?
1574 * FIXME what about ISO 2022-JP plus -- those
1575 * FIXME will loose shifts, then!
1576 * FIXME THUS - we'd need special "known points"
1577 * FIXME to do so - say, after a newline!!
1578 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1579 mbtowc(&wc
, NULL
, mb_cur_max
);
1580 wc
= isuni
? 0xFFFD : '?';
1585 if (!iswprint(wc
) && wc
!= '\n' && wc
!= '\r' && wc
!= '\b' &&
1587 if ((wc
& ~(wchar_t)037) == 0)
1588 wc
= isuni
? 0x2400 | wc
: '?';
1589 else if (wc
== 0177)
1590 wc
= isuni
? 0x2421 : '?';
1592 wc
= isuni
? 0x2426 : '?';
1593 }else if(isuni
){ /* TODO ctext */
1594 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1595 if(wc
== 0x200E || wc
== 0x200F || (wc
>= 0x202A && wc
<= 0x202E))
1597 /* And some zero-width messes */
1598 if(wc
== 0x00AD || (wc
>= 0x200B && wc
<= 0x200D))
1600 /* Oh about the ISO C wide character interfaces, baby! */
1604 if ((n
= wctomb(mbb
, wc
)) <= 0)
1607 assert(out
->l
< msz
);
1608 for (i
= 0; i
< n
; ++i
)
1612 #endif /* NATCH_CHAR */
1615 while (inp
< maxp
) {
1617 if (!isprint(c
) && c
!= '\n' && c
!= '\r' && c
!= '\b' && c
!= '\t')
1624 out
->s
[out
->l
] = '\0';
1629 delctrl(char *cp
, size_t len
)
1634 for (x
= y
= 0; x
< len
; ++x
)
1635 if (!cntrlchar(cp
[x
]))
1643 prstr(char const *s
)
1651 makeprint(&in
, &out
);
1652 rp
= savestrbuf(out
.s
, out
.l
);
1659 prout(char const *s
, size_t sz
, FILE *fp
)
1667 makeprint(&in
, &out
);
1668 n
= fwrite(out
.s
, 1, out
.l
, fp
);
1675 putuc(int u
, int c
, FILE *fp
)
1681 #ifdef HAVE_NATCH_CHAR
1682 if ((options
& OPT_UNICODE
) && (u
& ~(wchar_t)0177)) {
1683 char mbb
[MB_LEN_MAX
];
1686 if ((n
= wctomb(mbb
, u
)) > 0) {
1688 for (i
= 0; i
< n
; ++i
)
1689 if (putc(mbb
[i
] & 0377, fp
) == EOF
) {
1694 rv
= (putc('\0', fp
) != EOF
);
1699 rv
= (putc(c
, fp
) != EOF
);
1705 bidi_info_needed(char const *bdat
, size_t blen
)
1710 #ifdef HAVE_NATCH_CHAR
1711 if (options
& OPT_UNICODE
)
1713 /* TODO Checking for BIDI character: use S-CText fromutf8
1714 * TODO plus isrighttoleft (or whatever there will be)! */
1715 ui32_t c
= n_utf8_to_utf32(&bdat
, &blen
);
1722 /* (Very very fuzzy, awaiting S-CText for good) */
1723 if ((c
>= 0x05BE && c
<= 0x08E3) ||
1724 (c
>= 0xFB1D && c
<= 0xFE00) /* No: variation selectors */ ||
1725 (c
>= 0xFE70 && c
<= 0xFEFC) ||
1726 (c
>= 0x10800 && c
<= 0x10C48) ||
1727 (c
>= 0x1EE00 && c
<= 0x1EEF1)) {
1732 #endif /* HAVE_NATCH_CHAR */
1738 bidi_info_create(struct bidi_info
*bip
)
1740 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1741 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1742 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1743 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1744 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1745 NATCH_CHAR( char const *hb
; )
1748 memset(bip
, 0, sizeof *bip
);
1749 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("");
1751 #ifdef HAVE_NATCH_CHAR
1752 if ((options
& OPT_UNICODE
) && (hb
= ok_vlook(headline_bidi
)) != NULL
) {
1758 bip
->bi_start
.s
= bip
->bi_end
.s
= UNCONST("\xE2\x80\x8E");
1764 bip
->bi_start
.s
= UNCONST("\xE2\x81\xA8");
1765 bip
->bi_end
.s
= UNCONST("\xE2\x81\xA9");
1768 bip
->bi_start
.l
= bip
->bi_end
.l
= 3;
1776 colour_table_create(bool_t pager_used
)
1778 union {char *cp
; char const *ccp
; void *vp
; struct colour_table
*ctp
;} u
;
1780 struct colour_table
*ct
;
1783 if (ok_blook(colour_disable
) || (pager_used
&& !ok_blook(colour_pager
)))
1786 char *term
, *okterms
;
1788 if ((term
= env_vlook("TERM", FAL0
)) == NULL
)
1790 /* terminfo rocks: if we find "color", assume it's right */
1791 if (strstr(term
, "color") != NULL
)
1793 if ((okterms
= ok_vlook(colour_terms
)) == NULL
)
1794 okterms
= UNCONST(COLOUR_TERMS
);
1795 okterms
= savestr(okterms
);
1798 while ((u
.cp
= n_strsep(&okterms
, ',', TRU1
)) != NULL
)
1799 if (!strncmp(u
.cp
, term
, i
))
1805 colour_table
= ct
= salloc(sizeof *ct
); /* XXX lex.c yet resets (FILTER!) */
1808 enum colourspec cspec
;
1811 {ok_v_colour_msginfo
, COLOURSPEC_MSGINFO
, COLOUR_MSGINFO
},
1812 {ok_v_colour_partinfo
, COLOURSPEC_PARTINFO
, COLOUR_PARTINFO
},
1813 {ok_v_colour_from_
, COLOURSPEC_FROM_
, COLOUR_FROM_
},
1814 {ok_v_colour_header
, COLOURSPEC_HEADER
, COLOUR_HEADER
},
1815 {ok_v_colour_uheader
, COLOURSPEC_UHEADER
, COLOUR_UHEADER
}
1818 for (i
= 0; i
< NELEM(map
); ++i
) {
1819 if ((u
.cp
= _var_oklook(map
[i
].okey
)) == NULL
)
1820 u
.ccp
= map
[i
].defval
;
1821 u
.cp
= _colour_iso6429(u
.ccp
);
1822 ct
->ct_csinfo
[map
[i
].cspec
].l
= strlen(u
.cp
);
1823 ct
->ct_csinfo
[map
[i
].cspec
].s
= u
.cp
;
1826 ct
->ct_csinfo
[COLOURSPEC_RESET
].l
= sizeof("\033[0m") -1;
1827 ct
->ct_csinfo
[COLOURSPEC_RESET
].s
= UNCONST("\033[0m");
1829 if ((u
.cp
= ok_vlook(colour_user_headers
)) == NULL
)
1830 u
.ccp
= COLOUR_USER_HEADERS
;
1831 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].l
= i
= strlen(u
.ccp
);
1832 ct
->ct_csinfo
[COLOURSPEC_RESET
+ 1].s
= (i
== 0) ? NULL
: savestr(u
.ccp
);
1838 colour_put(FILE *fp
, enum colourspec cs
)
1841 if (colour_table
!= NULL
) {
1842 struct str
const *cp
= colour_get(cs
);
1844 fwrite(cp
->s
, cp
->l
, 1, fp
);
1850 colour_put_header(FILE *fp
, char const *name
)
1852 enum colourspec cs
= COLOURSPEC_HEADER
;
1853 struct str
const *uheads
;
1854 char *cp
, *cp_base
, *x
;
1858 if (colour_table
== NULL
)
1860 /* Normal header colours if there are no user headers */
1861 uheads
= colour_table
->ct_csinfo
+ COLOURSPEC_RESET
+ 1;
1862 if (uheads
->s
== NULL
)
1865 /* Iterate over all entries in the *colour-user-headers* list */
1866 cp
= ac_alloc(uheads
->l
+1);
1867 memcpy(cp
, uheads
->s
, uheads
->l
+1);
1869 namelen
= strlen(name
);
1870 while ((x
= n_strsep(&cp
, ',', TRU1
)) != NULL
) {
1871 size_t l
= (cp
!= NULL
) ? PTR2SIZE(cp
- x
) - 1 : strlen(x
);
1872 if (l
== namelen
&& !ascncasecmp(x
, name
, namelen
)) {
1873 cs
= COLOURSPEC_UHEADER
;
1885 colour_reset(FILE *fp
)
1888 if (colour_table
!= NULL
)
1889 fwrite("\033[0m", 4, 1, fp
);
1893 FL
struct str
const *
1894 colour_get(enum colourspec cs
)
1896 struct str
const *rv
= NULL
;
1899 if (colour_table
!= NULL
)
1900 if ((rv
= colour_table
->ct_csinfo
+ cs
)->s
== NULL
)
1905 #endif /* HAVE_COLOUR */
1908 boolify(char const *inbuf
, uiz_t inlen
, si8_t emptyrv
)
1915 assert(inlen
== 0 || inbuf
!= NULL
);
1917 if (inlen
== UIZ_MAX
)
1918 inlen
= strlen(inbuf
);
1921 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1923 if ((inlen
== 1 && *inbuf
== '1') ||
1924 !ascncasecmp(inbuf
, "true", inlen
) ||
1925 !ascncasecmp(inbuf
, "yes", inlen
) ||
1926 !ascncasecmp(inbuf
, "on", inlen
))
1928 else if ((inlen
== 1 && *inbuf
== '0') ||
1929 !ascncasecmp(inbuf
, "false", inlen
) ||
1930 !ascncasecmp(inbuf
, "no", inlen
) ||
1931 !ascncasecmp(inbuf
, "off", inlen
))
1934 dat
= ac_alloc(inlen
+1);
1935 memcpy(dat
, inbuf
, inlen
);
1938 sli
= strtol(dat
, &eptr
, 0);
1939 if (*dat
!= '\0' && *eptr
== '\0')
1952 quadify(char const *inbuf
, uiz_t inlen
, char const *prompt
, si8_t emptyrv
)
1957 assert(inlen
== 0 || inbuf
!= NULL
);
1959 if (inlen
== UIZ_MAX
)
1960 inlen
= strlen(inbuf
);
1963 rv
= (emptyrv
>= 0) ? (emptyrv
== 0 ? 0 : 1) : -1;
1964 else if ((rv
= boolify(inbuf
, inlen
, -1)) < 0 &&
1965 !ascncasecmp(inbuf
, "ask-", 4) &&
1966 (rv
= boolify(inbuf
+ 4, inlen
- 4, -1)) >= 0 &&
1967 (options
& OPT_INTERACTIVE
)) {
1969 fputs(prompt
, stdout
);
1970 rv
= getapproval(NULL
, rv
);
1979 #ifdef HAVE_CLOCK_GETTIME
1981 #elif defined HAVE_GETTIMEOFDAY
1987 #ifdef HAVE_CLOCK_GETTIME
1988 clock_gettime(CLOCK_REALTIME
, &ts
);
1989 rv
= (time_t)ts
.tv_sec
;
1990 #elif defined HAVE_GETTIMEOFDAY
1991 gettimeofday(&ts
, NULL
);
1992 rv
= (time_t)ts
.tv_sec
;
2001 time_current_update(struct time_current
*tc
, bool_t full_update
)
2004 tc
->tc_time
= n_time_epoch();
2006 memcpy(&tc
->tc_gm
, gmtime(&tc
->tc_time
), sizeof tc
->tc_gm
);
2007 memcpy(&tc
->tc_local
, localtime(&tc
->tc_time
), sizeof tc
->tc_local
);
2008 sstpcpy(tc
->tc_ctime
, ctime(&tc
->tc_time
));
2014 n_err(char const *format
, ...)
2019 va_start(ap
, format
);
2021 if (options
& OPT_INTERACTIVE
)
2026 vfprintf(stderr
, format
, ap
);
2027 if (strchr(format
, '\n') != NULL
) /* TODO */
2035 n_verr(char const *format
, va_list ap
)
2037 /* Check use cases of PS_ERRORS_NOTED, too! */
2039 char buf
[LINESIZE
], *xbuf
;
2041 struct err_node
*enp
;
2043 LCTA(ERRORS_MAX
> 3);
2048 if (!(options
& OPT_INTERACTIVE
))
2051 vfprintf(stderr
, format
, ap
);
2059 l
= vsnprintf(xbuf
, lmax
, format
, ap
);
2062 if (UICMP(z
, l
, >=, lmax
)) {
2063 /* FIXME Cannot reuse va_list
2065 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
2070 fwrite(xbuf
, 1, l
, stderr
);
2072 /* Link it into the `errors' message ring */
2073 if ((enp
= _err_tail
) == NULL
) {
2075 enp
= scalloc(1, sizeof *enp
);
2076 if (_err_tail
!= NULL
)
2077 _err_tail
->en_next
= enp
;
2082 } else if (enp
->en_str
.l
> 0 && enp
->en_str
.s
[enp
->en_str
.l
- 1] == '\n') {
2083 if (_err_cnt
< ERRORS_MAX
)
2086 _err_head
= (enp
= _err_head
)->en_next
;
2087 _err_tail
->en_next
= enp
;
2089 free(enp
->en_str
.s
);
2090 memset(enp
, 0, sizeof *enp
);
2093 n_str_add_buf(&enp
->en_str
, xbuf
, l
);
2097 #endif /* HAVE_ERRORS */
2100 if (strchr(format
, '\n') != NULL
) /* TODO */
2106 n_err_sighdl(char const *format
, ...) /* TODO sigsafe; obsolete! */
2111 va_start(ap
, format
);
2112 vfprintf(stderr
, format
, ap
);
2118 n_perr(char const *msg
, int errval
)
2132 n_err(fmt
, msg
, strerror(errval
));
2137 n_alert(char const *format
, ...)
2142 n_err(_("Alert: "));
2144 va_start(ap
, format
);
2153 n_panic(char const *format
, ...)
2158 fprintf(stderr
, _("Panic: "));
2160 va_start(ap
, format
);
2161 vfprintf(stderr
, format
, ap
);
2167 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2175 struct err_node
*enp
;
2180 if (argv
[1] != NULL
)
2182 if (!asccasecmp(*argv
, "show"))
2184 if (!asccasecmp(*argv
, "clear"))
2187 fprintf(stderr
, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2191 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
2197 if (_err_head
== NULL
) {
2198 fprintf(stderr
, _("The error ring is empty\n"));
2202 if ((fp
= Ftmp(NULL
, "errors", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
2204 fprintf(stderr
, _("tmpfile"));
2209 for (i
= 0, enp
= _err_head
; enp
!= NULL
; enp
= enp
->en_next
)
2210 fprintf(fp
, "- %4" PRIuZ
". %" PRIuZ
" bytes: %s",
2211 ++i
, enp
->en_str
.l
, enp
->en_str
.s
);
2212 /* We don't know wether last string ended with NL; be simple */
2215 page_or_print(fp
, 0);
2222 _err_cnt
= _err_cnt_noted
= 0;
2223 while ((enp
= _err_head
) != NULL
) {
2224 _err_head
= enp
->en_next
;
2225 free(enp
->en_str
.s
);
2230 #endif /* HAVE_ERRORS */
2234 smalloc(size_t s SMALLOC_DEBUG_ARGS
)
2241 if ((rv
= malloc(s
)) == NULL
)
2242 n_panic(_("no memory"));
2248 srealloc(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2257 else if ((rv
= realloc(v
, s
)) == NULL
)
2258 n_panic(_("no memory"));
2264 scalloc(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2271 if ((rv
= calloc(nmemb
, size
)) == NULL
)
2272 n_panic(_("no memory"));
2277 #else /* !HAVE_DEBUG */
2278 CTA(sizeof(char) == sizeof(ui8_t
));
2280 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2281 # define _HOPE_SET(C) \
2283 union mem_ptr __xl, __xu;\
2284 struct mem_chunk *__xc;\
2285 __xl.p_p = (C).p_p;\
2286 __xc = __xl.p_c - 1;\
2289 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2290 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2291 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2292 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2293 __xu.p_ui8p += __xc->mc_size - 8;\
2294 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2295 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2296 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2297 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2299 # define _HOPE_GET_TRACE(C,BAD) \
2305 # define _HOPE_GET(C,BAD) \
2307 union mem_ptr __xl, __xu;\
2308 struct mem_chunk *__xc;\
2310 __xl.p_p = (C).p_p;\
2312 (C).p_cp = __xl.p_cp;\
2313 __xc = __xl.p_c - 1;\
2316 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2317 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2318 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2319 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2320 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2321 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2322 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2323 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2326 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2327 __xl.p_p, __i, mdbg_file, mdbg_line);\
2330 __xu.p_ui8p += __xc->mc_size - 8;\
2332 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2333 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2334 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2335 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2336 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2337 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2338 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2339 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2342 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2343 __xl.p_p, __i, mdbg_file, mdbg_line);\
2346 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2347 __xc->mc_file, __xc->mc_line);\
2351 (smalloc
)(size_t s SMALLOC_DEBUG_ARGS
)
2358 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2359 n_panic("smalloc(): allocation too large: %s, line %d",
2360 mdbg_file
, mdbg_line
);
2361 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2363 if ((p
.p_p
= (malloc
)(s
)) == NULL
)
2364 n_panic(_("no memory"));
2365 p
.p_c
->mc_prev
= NULL
;
2366 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2367 _mem_list
->mc_prev
= p
.p_c
;
2368 p
.p_c
->mc_file
= mdbg_file
;
2369 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2370 p
.p_c
->mc_isfree
= FAL0
;
2371 p
.p_c
->mc_size
= (ui32_t
)s
;
2373 _mem_list
= p
.p_c
++;
2378 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2381 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2387 (srealloc
)(void *v
, size_t s SMALLOC_DEBUG_ARGS
)
2393 if ((p
.p_p
= v
) == NULL
) {
2394 p
.p_p
= (smalloc
)(s
, mdbg_file
, mdbg_line
);
2398 _HOPE_GET(p
, isbad
);
2400 if (p
.p_c
->mc_isfree
) {
2401 n_err("srealloc(): region freed! At %s, line %d\n"
2402 "\tLast seen: %s, line %" PRIu16
"\n",
2403 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2407 if (p
.p_c
== _mem_list
)
2408 _mem_list
= p
.p_c
->mc_next
;
2410 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2411 if (p
.p_c
->mc_next
!= NULL
)
2412 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2415 _mem_mcur
-= p
.p_c
->mc_size
;
2419 if (s
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2420 n_panic("srealloc(): allocation too large: %s, line %d",
2421 mdbg_file
, mdbg_line
);
2422 s
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2424 if ((p
.p_p
= (realloc
)(p
.p_c
, s
)) == NULL
)
2425 n_panic(_("no memory"));
2426 p
.p_c
->mc_prev
= NULL
;
2427 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2428 _mem_list
->mc_prev
= p
.p_c
;
2429 p
.p_c
->mc_file
= mdbg_file
;
2430 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2431 p
.p_c
->mc_isfree
= FAL0
;
2432 p
.p_c
->mc_size
= (ui32_t
)s
;
2433 _mem_list
= p
.p_c
++;
2438 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2441 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2448 (scalloc
)(size_t nmemb
, size_t size SMALLOC_DEBUG_ARGS
)
2457 if (size
> UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
)
2458 n_panic("scalloc(): allocation size too large: %s, line %d",
2459 mdbg_file
, mdbg_line
);
2460 if ((UI32_MAX
- sizeof(struct mem_chunk
) - _HOPE_SIZE
) / nmemb
< size
)
2461 n_panic("scalloc(): allocation count too large: %s, line %d",
2462 mdbg_file
, mdbg_line
);
2465 size
+= sizeof(struct mem_chunk
) + _HOPE_SIZE
;
2467 if ((p
.p_p
= (malloc
)(size
)) == NULL
)
2468 n_panic(_("no memory"));
2469 memset(p
.p_p
, 0, size
);
2470 p
.p_c
->mc_prev
= NULL
;
2471 if ((p
.p_c
->mc_next
= _mem_list
) != NULL
)
2472 _mem_list
->mc_prev
= p
.p_c
;
2473 p
.p_c
->mc_file
= mdbg_file
;
2474 p
.p_c
->mc_line
= (ui16_t
)mdbg_line
;
2475 p
.p_c
->mc_isfree
= FAL0
;
2476 p
.p_c
->mc_size
= (ui32_t
)size
;
2477 _mem_list
= p
.p_c
++;
2482 _mem_amax
= MAX(_mem_amax
, _mem_acur
);
2485 _mem_mmax
= MAX(_mem_mmax
, _mem_mcur
);
2491 (sfree
)(void *v SMALLOC_DEBUG_ARGS
)
2497 if ((p
.p_p
= v
) == NULL
) {
2498 n_err("sfree(NULL) from %s, line %d\n", mdbg_file
, mdbg_line
);
2502 _HOPE_GET(p
, isbad
);
2504 if (p
.p_c
->mc_isfree
) {
2505 n_err("sfree(): double-free avoided at %s, line %d\n"
2506 "\tLast seen: %s, line %" PRIu16
"\n",
2507 mdbg_file
, mdbg_line
, p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2511 if (p
.p_c
== _mem_list
)
2512 _mem_list
= p
.p_c
->mc_next
;
2514 p
.p_c
->mc_prev
->mc_next
= p
.p_c
->mc_next
;
2515 if (p
.p_c
->mc_next
!= NULL
)
2516 p
.p_c
->mc_next
->mc_prev
= p
.p_c
->mc_prev
;
2517 p
.p_c
->mc_isfree
= TRU1
;
2518 /* Trash contents (also see [21c05f8]) */
2519 memset(v
, 0377, p
.p_c
->mc_size
- sizeof(struct mem_chunk
) - _HOPE_SIZE
);
2522 _mem_mcur
-= p
.p_c
->mc_size
;
2524 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2525 p
.p_c
->mc_next
= _mem_free
;
2537 size_t c
= 0, s
= 0;
2542 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
;) {
2545 s
+= p
.p_c
->mc_size
;
2546 p
.p_c
= p
.p_c
->mc_next
;
2551 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
))
2552 n_err("smemreset: freed %" PRIuZ
" chunks/%" PRIuZ
" bytes\n", c
, s
);
2557 c_smemtrace(void *v
)
2559 /* For _HOPE_GET() */
2560 char const * const mdbg_file
= "smemtrace()";
2561 int const mdbg_line
= -1;
2563 union mem_ptr p
, xp
;
2569 if ((fp
= Ftmp(NULL
, "memtr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
) {
2570 n_perr("tmpfile", 0);
2574 fprintf(fp
, "Memory statistics:\n"
2575 " Count cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n"
2576 " Bytes cur/peek/all: %7" PRIuZ
"/%7" PRIuZ
"/%10" PRIuZ
"\n\n",
2577 _mem_acur
, _mem_amax
, _mem_aall
, _mem_mcur
, _mem_mmax
, _mem_mall
);
2579 fprintf(fp
, "Currently allocated memory chunks:\n");
2580 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2581 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2584 _HOPE_GET_TRACE(xp
, isbad
);
2585 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2586 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2587 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)), p
.p_c
->mc_file
,
2591 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2592 fprintf(fp
, "sfree()d memory chunks awaiting free():\n");
2593 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2596 _HOPE_GET_TRACE(xp
, isbad
);
2597 fprintf(fp
, "%s%p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2598 (isbad
? "! CANARY ERROR: " : ""), xp
.p_p
,
2599 (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2600 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2604 page_or_print(fp
, lines
);
2614 _smemcheck(char const *mdbg_file
, int mdbg_line
)
2616 union mem_ptr p
, xp
;
2617 bool_t anybad
= FAL0
, isbad
;
2621 for (lines
= 0, p
.p_c
= _mem_list
; p
.p_c
!= NULL
;
2622 ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2625 _HOPE_GET_TRACE(xp
, isbad
);
2629 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2630 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2631 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2635 if (options
& (OPT_DEBUG
| OPT_MEMDEBUG
)) {
2636 for (p
.p_c
= _mem_free
; p
.p_c
!= NULL
; ++lines
, p
.p_c
= p
.p_c
->mc_next
) {
2639 _HOPE_GET_TRACE(xp
, isbad
);
2643 "! CANARY ERROR: %p (%5" PRIuZ
" bytes): %s, line %" PRIu16
"\n",
2644 xp
.p_p
, (size_t)(p
.p_c
->mc_size
- sizeof(struct mem_chunk
)),
2645 p
.p_c
->mc_file
, p
.p_c
->mc_line
);
2652 # endif /* HAVE_DEVEL */
2656 # undef _HOPE_GET_TRACE
2658 #endif /* HAVE_DEBUG */