Add *sendmail-no-default-arguments* (Claudio Cappelli)..
[s-mailx.git] / auxlily.c
blob098a9a3dfdba1a77fd47bf587593f8fdf0a7d40e
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>.
6 */
7 /*
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
13 * are met:
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
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #include <ctype.h>
45 #include <dirent.h>
47 #ifdef HAVE_SOCKETS
48 # ifdef HAVE_GETADDRINFO
49 # include <sys/socket.h>
50 # endif
52 # include <netdb.h>
53 #endif
55 #ifndef HAVE_POSIX_RANDOM
56 union rand_state {
57 struct rand_arc4 {
58 ui8_t __pad[6];
59 ui8_t _i;
60 ui8_t _j;
61 ui8_t _dat[256];
62 } a;
63 ui8_t b8[sizeof(struct rand_arc4)];
64 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
66 #endif
68 #ifdef HAVE_NYD
69 struct nyd_info {
70 char const *ni_file;
71 char const *ni_fun;
72 ui32_t ni_chirp_line;
73 ui32_t ni_level;
75 #endif
77 #ifdef HAVE_DEBUG
78 struct mem_chunk {
79 struct mem_chunk *mc_prev;
80 struct mem_chunk *mc_next;
81 char const *mc_file;
82 ui16_t mc_line;
83 ui8_t mc_isfree;
84 ui8_t __dummy;
85 ui32_t mc_size;
88 union mem_ptr {
89 void *p_p;
90 struct mem_chunk *p_c;
91 char *p_cp;
92 ui8_t *p_ui8p;
94 #endif
96 #ifdef HAVE_ERRORS
97 struct err_node {
98 struct err_node *en_next;
99 struct str en_str;
101 #endif
103 #ifndef HAVE_POSIX_RANDOM
104 static union rand_state *_rand;
105 #endif
107 /* {hold,rele}_all_sigs() */
108 static size_t _alls_depth;
109 static sigset_t _alls_nset, _alls_oset;
111 /* {hold,rele}_sigs() */
112 static size_t _hold_sigdepth;
113 static sigset_t _hold_nset, _hold_oset;
115 /* NYD, memory pool debug */
116 #ifdef HAVE_NYD
117 static ui32_t _nyd_curr, _nyd_level;
118 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
119 #endif
121 /* Error ring, for `errors' */
122 #ifdef HAVE_ERRORS
123 static struct err_node *_err_head, *_err_tail;
124 static size_t _err_cnt, _err_cnt_noted;
125 #endif
127 #ifdef HAVE_DEBUG
128 static size_t _mem_aall, _mem_acur, _mem_amax,
129 _mem_mall, _mem_mcur, _mem_mmax;
131 static struct mem_chunk *_mem_list, *_mem_free;
132 #endif
134 /* Our ARC4 random generator with its completely unacademical pseudo
135 * initialization (shall /dev/urandom fail) */
136 #ifndef HAVE_POSIX_RANDOM
137 static void _rand_init(void);
138 static ui32_t _rand_weak(ui32_t seed);
139 SINLINE ui8_t _rand_get8(void);
140 #endif
142 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
143 #ifdef HAVE_COLOUR
144 static char * _colour_iso6429(char const *wish);
145 #endif
147 #ifdef HAVE_NYD
148 static void _nyd_print(int fd, struct nyd_info *nip);
149 #endif
151 #ifndef HAVE_POSIX_RANDOM
152 static void
153 _rand_init(void)
155 # ifdef HAVE_CLOCK_GETTIME
156 struct timespec ts;
157 # else
158 struct timeval ts;
159 # endif
160 union {int fd; size_t i;} u;
161 ui32_t seed, rnd;
162 NYD2_ENTER;
164 _rand = smalloc(sizeof *_rand);
166 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
167 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
169 close(u.fd);
170 if (ok)
171 goto jleave;
174 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
175 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
176 size_t t, k;
178 # ifdef HAVE_CLOCK_GETTIME
179 clock_gettime(CLOCK_REALTIME, &ts);
180 t = (ui32_t)ts.tv_nsec;
181 # else
182 gettimeofday(&ts, NULL);
183 t = (ui32_t)ts.tv_usec;
184 # endif
185 if (rnd & 1)
186 t = (t >> 16) | (t << 16);
187 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
188 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
189 if (rnd == 7 || rnd == 17)
190 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
191 k = _rand->b32[u.i] % NELEM(_rand->b32);
192 _rand->b32[k] ^= _rand->b32[u.i];
193 seed ^= _rand_weak(_rand->b32[k]);
194 if ((rnd & 3) == 3)
195 seed ^= nextprime(seed);
199 for (u.i = 11 * sizeof(_rand->b8); u.i != 0; --u.i)
200 _rand_get8();
201 jleave:
202 NYD2_LEAVE;
205 static ui32_t
206 _rand_weak(ui32_t seed)
208 /* From "Random number generators: good ones are hard to find",
209 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
210 * October 1988, p. 1195.
211 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
212 ui32_t hi;
214 if (seed == 0)
215 seed = 123459876;
216 hi = seed / 127773;
217 seed %= 127773;
218 seed = (seed * 16807) - (hi * 2836);
219 if ((si32_t)seed < 0)
220 seed += SI32_MAX;
221 return seed;
224 SINLINE ui8_t
225 _rand_get8(void)
227 ui8_t si, sj;
229 si = _rand->a._dat[++_rand->a._i];
230 sj = _rand->a._dat[_rand->a._j += si];
231 _rand->a._dat[_rand->a._i] = sj;
232 _rand->a._dat[_rand->a._j] = si;
233 return _rand->a._dat[(ui8_t)(si + sj)];
235 #endif /* HAVE_POSIX_RANDOM */
237 #ifdef HAVE_COLOUR
238 static char *
239 _colour_iso6429(char const *wish)
241 struct isodesc {
242 char id_name[15];
243 char id_modc;
244 } const fta[] = {
245 {"bold", '1'}, {"underline", '4'}, {"inverse", '7'}
246 }, ca[] = {
247 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
248 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
249 }, *idp;
250 char const * const wish_orig = wish;
251 char *xwish, *cp, cfg[3] = {0, 0, 0};
252 NYD_ENTER;
254 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
255 * value, ensure we have enough room for that */
257 size_t i = strlen(wish) +1;
258 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
259 memcpy(xwish, wish, i);
260 wish = xwish;
263 /* Iterate over the colour spec */
264 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
265 char *y, *x = strchr(cp, '=');
266 if (x == NULL) {
267 jbail:
268 n_err(_("Invalid colour specification \"%s\": %s\n"), wish_orig, cp);
269 continue;
271 *x++ = '\0';
273 if (!asccasecmp(cp, "ft")) {
274 for (idp = fta;; ++idp)
275 if (idp == fta + NELEM(fta))
276 goto jbail;
277 else if (!asccasecmp(x, idp->id_name)) {
278 cfg[0] = idp->id_modc;
279 break;
281 } else if (!asccasecmp(cp, "fg")) {
282 y = cfg + 1;
283 goto jiter_colour;
284 } else if (!asccasecmp(cp, "bg")) {
285 y = cfg + 2;
286 jiter_colour:
287 for (idp = ca;; ++idp)
288 if (idp == ca + NELEM(ca))
289 goto jbail;
290 else if (!asccasecmp(x, idp->id_name)) {
291 *y = idp->id_modc;
292 break;
294 } else
295 goto jbail;
298 /* Restore our salloc() buffer, create return value */
299 xwish = UNCONST(wish);
300 if (cfg[0] || cfg[1] || cfg[2]) {
301 xwish[0] = '\033';
302 xwish[1] = '[';
303 xwish += 2;
304 if (cfg[0])
305 *xwish++ = cfg[0];
306 if (cfg[1]) {
307 if (cfg[0])
308 *xwish++ = ';';
309 xwish[0] = '3';
310 xwish[1] = cfg[1];
311 xwish += 2;
313 if (cfg[2]) {
314 if (cfg[0] || cfg[1])
315 *xwish++ = ';';
316 xwish[0] = '4';
317 xwish[1] = cfg[2];
318 xwish += 2;
320 *xwish++ = 'm';
322 *xwish = '\0';
323 NYD_LEAVE;
324 return UNCONST(wish);
326 #endif /* HAVE_COLOUR */
328 #ifdef HAVE_NYD
329 static void
330 _nyd_print(int fd, struct nyd_info *nip)
332 char buf[80];
333 union {int i; size_t z;} u;
335 u.i = snprintf(buf, sizeof buf,
336 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
337 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
338 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
339 if (u.i > 0) {
340 u.z = u.i;
341 if (u.z > sizeof buf)
342 u.z = sizeof buf - 1; /* (Skip \0) */
343 write(fd, buf, u.z);
346 #endif
348 FL void
349 n_raise(int signo)
351 NYD2_ENTER;
352 kill(getpid(), signo);
353 NYD2_LEAVE;
356 FL sighandler_type
357 safe_signal(int signum, sighandler_type handler)
359 struct sigaction nact, oact;
360 sighandler_type rv;
361 NYD2_ENTER;
363 nact.sa_handler = handler;
364 sigemptyset(&nact.sa_mask);
365 nact.sa_flags = 0;
366 #ifdef SA_RESTART
367 nact.sa_flags |= SA_RESTART;
368 #endif
369 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
370 NYD2_LEAVE;
371 return rv;
374 FL void
375 hold_all_sigs(void)
377 NYD2_ENTER;
378 if (_alls_depth++ == 0) {
379 sigfillset(&_alls_nset);
380 sigdelset(&_alls_nset, SIGABRT);
381 #ifdef SIGBUS
382 sigdelset(&_alls_nset, SIGBUS);
383 #endif
384 sigdelset(&_alls_nset, SIGCHLD);
385 sigdelset(&_alls_nset, SIGFPE);
386 sigdelset(&_alls_nset, SIGILL);
387 sigdelset(&_alls_nset, SIGKILL);
388 sigdelset(&_alls_nset, SIGSEGV);
389 sigdelset(&_alls_nset, SIGSTOP);
390 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
392 NYD2_LEAVE;
395 FL void
396 rele_all_sigs(void)
398 NYD2_ENTER;
399 if (--_alls_depth == 0)
400 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
401 NYD2_LEAVE;
404 FL void
405 hold_sigs(void)
407 NYD2_ENTER;
408 if (_hold_sigdepth++ == 0) {
409 sigemptyset(&_hold_nset);
410 sigaddset(&_hold_nset, SIGHUP);
411 sigaddset(&_hold_nset, SIGINT);
412 sigaddset(&_hold_nset, SIGQUIT);
413 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
415 NYD2_LEAVE;
418 FL void
419 rele_sigs(void)
421 NYD2_ENTER;
422 if (--_hold_sigdepth == 0)
423 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
424 NYD2_LEAVE;
427 #ifdef HAVE_NYD
428 FL void
429 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
431 struct nyd_info *nip = _nyd_infos;
433 if (_nyd_curr != NELEM(_nyd_infos))
434 nip += _nyd_curr++;
435 else
436 _nyd_curr = 1;
437 nip->ni_file = file;
438 nip->ni_fun = fun;
439 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
440 nip->ni_level = ((act == 0) ? _nyd_level
441 : (act == 1) ? ++_nyd_level : _nyd_level--);
444 FL void
445 _nyd_oncrash(int signo)
447 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
448 struct sigaction xact;
449 sigset_t xset;
450 size_t i, fnl;
451 int fd;
452 struct nyd_info *nip;
454 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
456 xact.sa_handler = SIG_DFL;
457 sigemptyset(&xact.sa_mask);
458 xact.sa_flags = 0;
459 sigaction(signo, &xact, NULL);
461 i = strlen(tempdir);
462 fnl = sizeof(UAGENT) -1;
464 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
465 (cp = pathbuf)[0] = '.';
466 i = 1;
467 } else
468 memcpy(cp = pathbuf, tempdir, i);
469 cp[i++] = '/'; /* xxx pathsep */
470 memcpy(cp += i, UAGENT, fnl);
471 i += fnl;
472 memcpy(cp += fnl, ".dat", sizeof(".dat"));
473 fnl = i + sizeof(".dat") -1;
475 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
476 fd = STDERR_FILENO;
478 # undef _X
479 # define _X(X) (X), sizeof(X) -1
480 write(fd, _X("\n\nNYD: program dying due to signal "));
482 cp = s2ibuf + sizeof(s2ibuf) -1;
483 *cp = '\0';
484 i = signo;
485 do {
486 *--cp = "0123456789"[i % 10];
487 i /= 10;
488 } while (i != 0);
489 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
491 write(fd, _X(":\n"));
493 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
494 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
495 _nyd_print(fd, nip++);
496 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
497 _nyd_print(fd, nip++);
499 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
501 if (fd != STDERR_FILENO) {
502 write(STDERR_FILENO, _X("Crash NYD listing written to "));
503 write(STDERR_FILENO, pathbuf, fnl);
504 write(STDERR_FILENO, _X("\n"));
505 # undef _X
507 close(fd);
510 sigemptyset(&xset);
511 sigaddset(&xset, signo);
512 sigprocmask(SIG_UNBLOCK, &xset, NULL);
513 n_raise(signo);
514 for (;;)
515 _exit(EXIT_ERR);
517 #endif /* HAVE_NYD */
519 FL void
520 touch(struct message *mp)
522 NYD_ENTER;
523 mp->m_flag |= MTOUCH;
524 if (!(mp->m_flag & MREAD))
525 mp->m_flag |= MREAD | MSTATUS;
526 NYD_LEAVE;
529 FL bool_t
530 is_dir(char const *name)
532 struct stat sbuf;
533 bool_t rv;
534 NYD_ENTER;
536 for (rv = FAL0;;)
537 if (!stat(name, &sbuf)) {
538 rv = (S_ISDIR(sbuf.st_mode) != 0);
539 break;
540 } else if (errno != EINTR)
541 break;
542 NYD_LEAVE;
543 return rv;
546 FL int
547 argcount(char **argv)
549 char **ap;
550 NYD_ENTER;
552 for (ap = argv; *ap++ != NULL;)
554 NYD_LEAVE;
555 return (int)PTR2SIZE(ap - argv - 1);
558 FL int
559 screensize(void)
561 int s;
562 char *cp;
563 NYD_ENTER;
565 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
566 s = scrnheight - 4; /* XXX no magics */
567 NYD_LEAVE;
568 return s;
571 FL char const *
572 get_pager(char const **env_addon)
574 char const *cp;
575 NYD_ENTER;
577 cp = ok_vlook(PAGER);
578 if (cp == NULL || *cp == '\0')
579 cp = XPAGER;
581 if (env_addon != NULL) {
582 *env_addon = NULL;
583 if (strstr(cp, "less") != NULL) {
584 if (!env_blook("LESS", TRU1))
585 *env_addon = "LESS=FRSXi";
586 } else if (strstr(cp, "lv") != NULL) {
587 if (!env_blook("LV", TRU1))
588 *env_addon = "LV=-c";
591 NYD_LEAVE;
592 return cp;
595 FL void
596 page_or_print(FILE *fp, size_t lines)
598 int c;
599 char const *cp;
600 NYD_ENTER;
602 fflush_rewind(fp);
604 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
605 char *eptr;
606 union {sl_i sli; size_t rows;} u;
608 u.sli = strtol(cp, &eptr, 0);
609 u.rows = (*cp != '\0' && *eptr == '\0')
610 ? (size_t)u.sli : (size_t)scrnheight;
612 if (u.rows > 0 && lines == 0) {
613 while ((c = getc(fp)) != EOF)
614 if (c == '\n' && ++lines >= u.rows)
615 break;
616 really_rewind(fp);
619 if (lines >= u.rows) {
620 run_command(get_pager(NULL), 0, fileno(fp), -1, NULL, NULL, NULL);
621 goto jleave;
625 while ((c = getc(fp)) != EOF)
626 putchar(c);
627 jleave:
628 NYD_LEAVE;
631 FL enum protocol
632 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
634 struct stat st;
635 char const *cp;
636 char *np;
637 size_t sz;
638 enum protocol rv = PROTO_UNKNOWN;
639 NYD_ENTER;
641 temporary_protocol_ext = NULL;
643 if (name[0] == '%' && name[1] == ':')
644 name += 2;
645 for (cp = name; *cp && *cp != ':'; cp++)
646 if (!alnumchar(*cp))
647 goto jfile;
649 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
650 if (!strncmp(name, "pop3://", 7)) {
651 #ifdef HAVE_POP3
652 rv = PROTO_POP3;
653 #else
654 n_err(_("No POP3 support compiled in\n"));
655 #endif
656 } else if (!strncmp(name, "pop3s://", 8)) {
657 #if defined HAVE_POP3 && defined HAVE_SSL
658 rv = PROTO_POP3;
659 #else
660 # ifndef HAVE_POP3
661 n_err(_("No POP3 support compiled in\n"));
662 # endif
663 # ifndef HAVE_SSL
664 n_err(_("No SSL support compiled in\n"));
665 # endif
666 #endif
667 } else if (!strncmp(name, "imap://", 7)) {
668 #ifdef HAVE_IMAP
669 rv = PROTO_IMAP;
670 #else
671 fprintf(stderr, _("No IMAP support compiled in.\n"));
672 #endif
673 } else if (!strncmp(name, "imaps://", 8)) {
674 #if defined HAVE_IMAP && defined HAVE_SSL
675 rv = PROTO_IMAP;
676 #else
677 # ifndef HAVE_IMAP
678 fprintf(stderr, _("No IMAP support compiled in.\n"));
679 # endif
680 # ifndef HAVE_SSL
681 fprintf(stderr, _("No SSL support compiled in.\n"));
682 # endif
683 #endif
685 goto jleave;
688 /* TODO This is the de facto maildir code and thus belongs into there!
689 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
690 * TODO or (more likely) in addition to *newfolders*) */
691 jfile:
692 rv = PROTO_FILE;
693 np = ac_alloc((sz = strlen(name)) + 4 +1);
694 memcpy(np, name, sz + 1);
695 if (!stat(name, &st)) {
696 if (S_ISDIR(st.st_mode) &&
697 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
698 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
699 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
700 rv = PROTO_MAILDIR;
701 } else {
702 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
703 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
704 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
705 temporary_protocol_ext = cp;
706 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
707 rv = PROTO_MAILDIR;
709 ac_free(np);
710 jleave:
711 NYD_LEAVE;
712 return rv;
715 FL ui32_t
716 torek_hash(char const *name)
718 /* Chris Torek's hash.
719 * NOTE: need to change *at least* create-okey-map.pl when changing the
720 * algorithm!! */
721 ui32_t h = 0;
722 NYD_ENTER;
724 while (*name != '\0') {
725 h *= 33;
726 h += *name++;
728 NYD_LEAVE;
729 return h;
732 FL unsigned
733 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
735 unsigned h = 0, g;
736 NYD_ENTER;
738 cp--;
739 while (*++cp) {
740 h = (h << 4 & 0xffffffff) + (*cp&0377);
741 if ((g = h & 0xf0000000) != 0) {
742 h = h ^ g >> 24;
743 h = h ^ g;
746 NYD_LEAVE;
747 return h;
750 FL ui32_t
751 nextprime(ui32_t n)
753 static ui32_t const primes[] = {
754 5, 11, 23, 47, 97, 157, 283,
755 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
756 131071, 262139, 524287, 1048573, 2097143, 4194301,
757 8388593, 16777213, 33554393, 67108859, 134217689,
758 268435399, 536870909, 1073741789, 2147483647
761 ui32_t i, mprime;
762 NYD_ENTER;
764 i = (n < primes[NELEM(primes) / 4] ? 0
765 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
766 : NELEM(primes) / 2));
768 if ((mprime = primes[i]) > n)
769 break;
770 while (++i < NELEM(primes));
771 if (i == NELEM(primes) && mprime < n)
772 mprime = n;
773 NYD_LEAVE;
774 return mprime;
777 FL int
778 expand_shell_escape(char const **s, bool_t use_nail_extensions)
780 char const *xs;
781 int c, n;
782 NYD2_ENTER;
784 xs = *s;
786 if ((c = *xs & 0xFF) == '\0')
787 goto jleave;
788 ++xs;
789 if (c != '\\')
790 goto jleave;
792 switch ((c = *xs & 0xFF)) {
793 case '\\': break;
794 case 'a': c = '\a'; break;
795 case 'b': c = '\b'; break;
796 case 'c': c = PROMPT_STOP; break;
797 case 'f': c = '\f'; break;
798 case 'n': c = '\n'; break;
799 case 'r': c = '\r'; break;
800 case 't': c = '\t'; break;
801 case 'v': c = '\v'; break;
802 case '0':
803 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
804 c <<= 3;
805 c |= *xs - '0';
807 goto jleave;
808 /* S-nail extension for nice (get)prompt(()) support */
809 case '&':
810 case '?':
811 case '$':
812 case '@':
813 if (use_nail_extensions) {
814 switch (c) {
815 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
816 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
817 case '$': c = PROMPT_DOLLAR; break;
818 case '@': c = PROMPT_AT; break;
820 break;
822 /* FALLTHRU */
823 case '\0':
824 /* A sole <backslash> at EOS is treated as-is! */
825 /* FALLTHRU */
826 default:
827 c = '\\';
828 goto jleave;
830 ++xs;
831 jleave:
832 *s = xs;
833 NYD2_LEAVE;
834 return c;
837 FL char *
838 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
840 static char buf[PROMPT_BUFFER_SIZE];
842 char *cp;
843 char const *ccp_base, *ccp;
844 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
845 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
846 NYD_ENTER;
848 /* No other place to place this */
849 #ifdef HAVE_ERRORS
850 if (options & OPT_INTERACTIVE) {
851 if (!(pstate & PS_ERRORS_NOTED) && _err_head != NULL) {
852 pstate |= PS_ERRORS_NOTED;
853 fprintf(stderr, _("There are new messages in the error message ring "
854 "(denoted by \"#ERR#\")\n"
855 " The `errors' command manages this message ring\n"));
858 if ((trigger = (_err_cnt_noted != _err_cnt)))
859 _err_cnt_noted = _err_cnt;
860 } else
861 trigger = FAL0;
862 #endif
864 cp = buf;
865 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
866 #ifdef HAVE_ERRORS
867 if (trigger)
868 ccp_base = "";
869 else
870 #endif
871 goto jleave;
873 #ifdef HAVE_ERRORS
874 if (trigger)
875 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
876 #endif
877 NATCH_CHAR( cclen_base = strlen(ccp_base); )
879 dfmaxlen = 0; /* keep CC happy */
880 trigger = FAL0;
881 jredo:
882 ccp = ccp_base;
883 NATCH_CHAR( cclen = cclen_base; )
884 maxlen = sizeof(buf) -1;
886 for (;;) {
887 size_t l;
888 int c;
890 if (maxlen == 0)
891 goto jleave;
892 #ifdef HAVE_NATCH_CHAR
893 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
894 if (c <= 0) {
895 mblen(NULL, 0);
896 if (c < 0) {
897 *buf = '?';
898 cp = buf + 1;
899 goto jleave;
901 break;
902 } else if ((l = c) > 1) {
903 if (trigger) {
904 memcpy(cp, ccp, l);
905 cp += l;
907 ccp += l;
908 maxlen -= l;
909 continue;
910 } else
911 #endif
912 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
913 if (trigger)
914 *cp++ = (char)c;
915 --maxlen;
916 continue;
918 if (c == 0 || c == PROMPT_STOP)
919 break;
921 if (trigger) {
922 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
923 if (a == NULL)
924 a = "";
925 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
926 cp += l;
927 maxlen -= l;
928 dfmaxlen -= l;
933 if (!trigger) {
934 trigger = TRU1;
935 dfmaxlen = maxlen;
936 goto jredo;
938 jleave:
939 *cp = '\0';
940 NYD_LEAVE;
941 return buf;
944 FL char *
945 nodename(int mayoverride)
947 static char *sys_hostname, *hostname; /* XXX free-at-exit */
949 struct utsname ut;
950 char *hn;
951 #ifdef HAVE_SOCKETS
952 # ifdef HAVE_GETADDRINFO
953 struct addrinfo hints, *res;
954 # else
955 struct hostent *hent;
956 # endif
957 #endif
958 NYD_ENTER;
960 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
962 } else if ((hn = sys_hostname) == NULL) {
963 uname(&ut);
964 hn = ut.nodename;
965 #ifdef HAVE_SOCKETS
966 # ifdef HAVE_GETADDRINFO
967 memset(&hints, 0, sizeof hints);
968 hints.ai_family = AF_UNSPEC;
969 hints.ai_flags = AI_CANONNAME;
970 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
971 if (res->ai_canonname != NULL) {
972 size_t l = strlen(res->ai_canonname) +1;
974 hn = ac_alloc(l);
975 memcpy(hn, res->ai_canonname, l);
977 freeaddrinfo(res);
979 # else
980 hent = gethostbyname(hn);
981 if (hent != NULL)
982 hn = hent->h_name;
983 # endif
984 #endif
985 sys_hostname = sstrdup(hn);
986 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
987 if (hn != ut.nodename)
988 ac_free(hn);
989 #endif
990 hn = sys_hostname;
993 if (hostname != NULL && hostname != sys_hostname)
994 free(hostname);
995 hostname = sstrdup(hn);
996 NYD_LEAVE;
997 return hostname;
1000 FL char *
1001 getrandstring(size_t length)
1003 struct str b64;
1004 char *data;
1005 size_t i;
1006 NYD_ENTER;
1008 #ifndef HAVE_POSIX_RANDOM
1009 if (_rand == NULL)
1010 _rand_init();
1011 #endif
1013 data = ac_alloc(length);
1015 #ifndef HAVE_POSIX_RANDOM
1016 for (i = length; i-- > 0;)
1017 data[i] = (char)_rand_get8();
1018 #else
1019 { char *cp = data;
1021 for (i = length; i > 0;) {
1022 union {ui32_t i4; char c[4];} r;
1023 size_t j;
1025 r.i4 = (ui32_t)arc4random();
1026 switch ((j = i & 3)) {
1027 case 0: cp[3] = r.c[3]; j = 4;
1028 case 3: cp[2] = r.c[2];
1029 case 2: cp[1] = r.c[1];
1030 default: cp[0] = r.c[0]; break;
1032 cp += j;
1033 i -= j;
1036 #endif
1038 b64_encode_buf(&b64, data, length, B64_SALLOC | B64_RFC4648URL);
1039 ac_free(data);
1041 b64.s[length] = '\0';
1042 NYD_LEAVE;
1043 return b64.s;
1046 FL enum okay
1047 makedir(char const *name)
1049 struct stat st;
1050 enum okay rv = STOP;
1051 NYD_ENTER;
1053 if (!mkdir(name, 0700))
1054 rv = OKAY;
1055 else {
1056 int e = errno;
1057 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1058 S_ISDIR(st.st_mode))
1059 rv = OKAY;
1061 NYD_LEAVE;
1062 return rv;
1065 #ifdef HAVE_FCHDIR
1066 FL enum okay
1067 cwget(struct cw *cw)
1069 enum okay rv = STOP;
1070 NYD_ENTER;
1072 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1073 goto jleave;
1074 if (fchdir(cw->cw_fd) == -1) {
1075 close(cw->cw_fd);
1076 goto jleave;
1078 rv = OKAY;
1079 jleave:
1080 NYD_LEAVE;
1081 return rv;
1084 FL enum okay
1085 cwret(struct cw *cw)
1087 enum okay rv = STOP;
1088 NYD_ENTER;
1090 if (!fchdir(cw->cw_fd))
1091 rv = OKAY;
1092 NYD_LEAVE;
1093 return rv;
1096 FL void
1097 cwrelse(struct cw *cw)
1099 NYD_ENTER;
1100 close(cw->cw_fd);
1101 NYD_LEAVE;
1104 #else /* !HAVE_FCHDIR */
1105 FL enum okay
1106 cwget(struct cw *cw)
1108 enum okay rv = STOP;
1109 NYD_ENTER;
1111 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1112 rv = OKAY;
1113 NYD_LEAVE;
1114 return rv;
1117 FL enum okay
1118 cwret(struct cw *cw)
1120 enum okay rv = STOP;
1121 NYD_ENTER;
1123 if (!chdir(cw->cw_wd))
1124 rv = OKAY;
1125 NYD_LEAVE;
1126 return rv;
1129 FL void
1130 cwrelse(struct cw *cw)
1132 NYD_ENTER;
1133 UNUSED(cw);
1134 NYD_LEAVE;
1136 #endif /* !HAVE_FCHDIR */
1138 FL size_t
1139 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1141 size_t rv;
1142 NYD_ENTER;
1144 #ifdef HAVE_NATCH_CHAR
1145 maxlen = MIN(maxlen, blen);
1146 for (rv = 0; maxlen > 0;) {
1147 int ml = mblen(buf, maxlen);
1148 if (ml <= 0) {
1149 mblen(NULL, 0);
1150 break;
1152 buf += ml;
1153 rv += ml;
1154 maxlen -= ml;
1156 #else
1157 rv = MIN(blen, maxlen);
1158 #endif
1159 NYD_LEAVE;
1160 return rv;
1163 FL size_t
1164 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1166 NATCH_CHAR( struct bidi_info bi; )
1167 size_t rv NATCH_CHAR( COMMA i );
1168 NYD_ENTER;
1170 rv = 0;
1171 if (maxlen-- == 0)
1172 goto j_leave;
1174 #ifdef HAVE_NATCH_CHAR
1175 bidi_info_create(&bi);
1176 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1177 bi.bi_end.l = 0;
1178 goto jnobidi;
1181 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1182 maxlen -= i;
1183 else
1184 goto jleave;
1186 if ((i = bi.bi_start.l) > 0) {
1187 memcpy(store, bi.bi_start.s, i);
1188 store += i;
1189 rv += i;
1192 jnobidi:
1193 while (maxlen > 0) {
1194 int ml = mblen(buf, blen);
1195 if (ml <= 0) {
1196 mblen(NULL, 0);
1197 break;
1199 if (UICMP(z, maxlen, <, ml))
1200 break;
1201 if (ml == 1)
1202 *store = *buf;
1203 else
1204 memcpy(store, buf, ml);
1205 store += ml;
1206 buf += ml;
1207 rv += ml;
1208 maxlen -= ml;
1211 if ((i = bi.bi_end.l) > 0) {
1212 memcpy(store, bi.bi_end.s, i);
1213 store += i;
1214 rv += i;
1216 jleave:
1217 *store = '\0';
1219 #else
1220 rv = MIN(blen, maxlen);
1221 memcpy(store, buf, rv);
1222 store[rv] = '\0';
1223 #endif
1224 j_leave:
1225 NYD_LEAVE;
1226 return rv;
1229 FL char *
1230 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1232 NATCH_CHAR( struct bidi_info bi; )
1233 int col_orig = col, n, sz;
1234 bool_t isbidi, isuni, istab, isrepl;
1235 char *nb, *np;
1236 NYD_ENTER;
1238 /* Bidi only on request and when there is 8-bit data */
1239 isbidi = isuni = FAL0;
1240 #ifdef HAVE_NATCH_CHAR
1241 isuni = ((options & OPT_UNICODE) != 0);
1242 bidi_info_create(&bi);
1243 if (bi.bi_start.l == 0)
1244 goto jnobidi;
1245 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1246 goto jnobidi;
1248 if ((size_t)col >= bi.bi_pad)
1249 col -= bi.bi_pad;
1250 else
1251 col = 0;
1252 jnobidi:
1253 #endif
1255 np = nb = salloc(mb_cur_max * strlen(cp) +
1256 ((fill ? col : 0)
1257 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1258 +1));
1260 #ifdef HAVE_NATCH_CHAR
1261 if (isbidi) {
1262 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1263 np += bi.bi_start.l;
1265 #endif
1267 while (*cp != '\0') {
1268 istab = FAL0;
1269 #ifdef HAVE_C90AMEND1
1270 if (mb_cur_max > 1) {
1271 wchar_t wc;
1273 n = 1;
1274 isrepl = TRU1;
1275 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1276 sz = 1;
1277 else if (wc == L'\t') {
1278 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1279 isrepl = FAL0;
1280 istab = TRU1;
1281 } else if (iswprint(wc)) {
1282 # ifndef HAVE_WCWIDTH
1283 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1284 # else
1285 if ((n = wcwidth(wc)) == -1)
1286 n = 1;
1287 else
1288 # endif
1289 isrepl = FAL0;
1291 } else
1292 #endif
1294 n = sz = 1;
1295 istab = (*cp == '\t');
1296 isrepl = !(istab || isprint((uc_i)*cp));
1299 if (n > col)
1300 break;
1301 col -= n;
1303 if (isrepl) {
1304 if (isuni) {
1305 np[0] = (char)0xEFu;
1306 np[1] = (char)0xBFu;
1307 np[2] = (char)0xBDu;
1308 np += 3;
1309 } else
1310 *np++ = '?';
1311 cp += sz;
1312 } else if (istab || (sz == 1 && spacechar(*cp))) {
1313 *np++ = ' ';
1314 ++cp;
1315 } else
1316 while (sz--)
1317 *np++ = *cp++;
1320 if (fill && col != 0) {
1321 if (fill > 0) {
1322 memmove(nb + col, nb, PTR2SIZE(np - nb));
1323 memset(nb, ' ', col);
1324 } else
1325 memset(np, ' ', col);
1326 np += col;
1327 col = 0;
1330 #ifdef HAVE_NATCH_CHAR
1331 if (isbidi) {
1332 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1333 np += bi.bi_end.l;
1335 #endif
1337 *np = '\0';
1338 if (cols_decr_used_or_null != NULL)
1339 *cols_decr_used_or_null -= col_orig - col;
1340 NYD_LEAVE;
1341 return nb;
1344 FL void
1345 makeprint(struct str const *in, struct str *out)
1347 static int print_all_chars = -1;
1349 char const *inp, *maxp;
1350 char *outp;
1351 DBG( size_t msz; )
1352 NYD_ENTER;
1354 if (print_all_chars == -1)
1355 print_all_chars = ok_blook(print_all_chars);
1357 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1358 inp = in->s;
1359 maxp = inp + in->l;
1361 if (print_all_chars) {
1362 out->l = in->l;
1363 memcpy(outp, inp, out->l);
1364 goto jleave;
1367 #ifdef HAVE_NATCH_CHAR
1368 if (mb_cur_max > 1) {
1369 char mbb[MB_LEN_MAX + 1];
1370 wchar_t wc;
1371 int i, n;
1372 bool_t isuni = ((options & OPT_UNICODE) != 0);
1374 out->l = 0;
1375 while (inp < maxp) {
1376 if (*inp & 0200)
1377 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1378 else {
1379 wc = *inp;
1380 n = 1;
1382 if (n == -1) {
1383 /* FIXME Why mbtowc() resetting here?
1384 * FIXME what about ISO 2022-JP plus -- those
1385 * FIXME will loose shifts, then!
1386 * FIXME THUS - we'd need special "known points"
1387 * FIXME to do so - say, after a newline!!
1388 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1389 mbtowc(&wc, NULL, mb_cur_max);
1390 wc = isuni ? 0xFFFD : '?';
1391 n = 1;
1392 } else if (n == 0)
1393 n = 1;
1394 inp += n;
1395 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1396 wc != '\t') {
1397 if ((wc & ~(wchar_t)037) == 0)
1398 wc = isuni ? 0x2400 | wc : '?';
1399 else if (wc == 0177)
1400 wc = isuni ? 0x2421 : '?';
1401 else
1402 wc = isuni ? 0x2426 : '?';
1404 if ((n = wctomb(mbb, wc)) <= 0)
1405 continue;
1406 out->l += n;
1407 assert(out->l < msz);
1408 for (i = 0; i < n; ++i)
1409 *outp++ = mbb[i];
1411 } else
1412 #endif /* NATCH_CHAR */
1414 int c;
1415 while (inp < maxp) {
1416 c = *inp++ & 0377;
1417 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1418 c = '?';
1419 *outp++ = c;
1421 out->l = in->l;
1423 jleave:
1424 out->s[out->l] = '\0';
1425 NYD_LEAVE;
1428 FL size_t
1429 delctrl(char *cp, size_t len)
1431 size_t x, y;
1432 NYD_ENTER;
1434 for (x = y = 0; x < len; ++x)
1435 if (!cntrlchar(cp[x]))
1436 cp[y++] = cp[x];
1437 cp[y] = '\0';
1438 NYD_LEAVE;
1439 return y;
1442 FL char *
1443 prstr(char const *s)
1445 struct str in, out;
1446 char *rp;
1447 NYD_ENTER;
1449 in.s = UNCONST(s);
1450 in.l = strlen(s);
1451 makeprint(&in, &out);
1452 rp = savestrbuf(out.s, out.l);
1453 free(out.s);
1454 NYD_LEAVE;
1455 return rp;
1458 FL int
1459 prout(char const *s, size_t sz, FILE *fp)
1461 struct str in, out;
1462 int n;
1463 NYD_ENTER;
1465 in.s = UNCONST(s);
1466 in.l = sz;
1467 makeprint(&in, &out);
1468 n = fwrite(out.s, 1, out.l, fp);
1469 free(out.s);
1470 NYD_LEAVE;
1471 return n;
1474 FL size_t
1475 putuc(int u, int c, FILE *fp)
1477 size_t rv;
1478 NYD_ENTER;
1479 UNUSED(u);
1481 #ifdef HAVE_NATCH_CHAR
1482 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1483 char mbb[MB_LEN_MAX];
1484 int i, n;
1486 if ((n = wctomb(mbb, u)) > 0) {
1487 rv = wcwidth(u);
1488 for (i = 0; i < n; ++i)
1489 if (putc(mbb[i] & 0377, fp) == EOF) {
1490 rv = 0;
1491 break;
1493 } else if (n == 0)
1494 rv = (putc('\0', fp) != EOF);
1495 else
1496 rv = 0;
1497 } else
1498 #endif
1499 rv = (putc(c, fp) != EOF);
1500 NYD_LEAVE;
1501 return rv;
1504 FL bool_t
1505 bidi_info_needed(char const *bdat, size_t blen)
1507 bool_t rv = FAL0;
1508 NYD_ENTER;
1510 #ifdef HAVE_NATCH_CHAR
1511 if (options & OPT_UNICODE)
1512 while (blen > 0) {
1513 /* TODO Checking for BIDI character: use S-CText fromutf8
1514 * TODO plus isrighttoleft (or whatever there will be)! */
1515 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1516 if (c == UI32_MAX)
1517 break;
1519 if (c <= 0x05BE)
1520 continue;
1522 /* (Very very fuzzy, awaiting S-CText for good) */
1523 if ((c >= 0x05BE && c <= 0x08E3) ||
1524 (c >= 0xFB1D && c <= 0xFEFC) ||
1525 (c >= 0x10800 && c <= 0x10C48) ||
1526 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1527 rv = TRU1;
1528 break;
1531 #endif /* HAVE_NATCH_CHAR */
1532 NYD_LEAVE;
1533 return rv;
1536 FL void
1537 bidi_info_create(struct bidi_info *bip)
1539 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1540 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1541 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1542 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1543 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1544 NATCH_CHAR( char const *hb; )
1545 NYD_ENTER;
1547 memset(bip, 0, sizeof *bip);
1548 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1550 #ifdef HAVE_NATCH_CHAR
1551 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1552 switch (*hb) {
1553 case '3':
1554 bip->bi_pad = 2;
1555 /* FALLTHRU */
1556 case '2':
1557 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1558 break;
1559 case '1':
1560 bip->bi_pad = 2;
1561 /* FALLTHRU */
1562 default:
1563 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1564 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1565 break;
1567 bip->bi_start.l = bip->bi_end.l = 3;
1569 #endif
1570 NYD_LEAVE;
1573 #ifdef HAVE_COLOUR
1574 FL void
1575 colour_table_create(bool_t pager_used)
1577 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1578 size_t i;
1579 struct colour_table *ct;
1580 NYD_ENTER;
1582 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1583 goto jleave;
1584 else {
1585 char *term, *okterms;
1587 if ((term = env_vlook("TERM", FAL0)) == NULL)
1588 goto jleave;
1589 /* terminfo rocks: if we find "color", assume it's right */
1590 if (strstr(term, "color") != NULL)
1591 goto jok;
1592 if ((okterms = ok_vlook(colour_terms)) == NULL)
1593 okterms = UNCONST(COLOUR_TERMS);
1594 okterms = savestr(okterms);
1596 i = strlen(term);
1597 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1598 if (!strncmp(u.cp, term, i))
1599 goto jok;
1600 goto jleave;
1603 jok:
1604 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1605 { static struct {
1606 enum okeys okey;
1607 enum colourspec cspec;
1608 char const *defval;
1609 } const map[] = {
1610 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1611 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1612 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1613 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1614 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1617 for (i = 0; i < NELEM(map); ++i) {
1618 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1619 u.ccp = map[i].defval;
1620 u.cp = _colour_iso6429(u.ccp);
1621 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1622 ct->ct_csinfo[map[i].cspec].s = u.cp;
1625 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1626 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1628 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1629 u.ccp = COLOUR_USER_HEADERS;
1630 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1631 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1632 jleave:
1633 NYD_LEAVE;
1636 FL void
1637 colour_put(FILE *fp, enum colourspec cs)
1639 NYD_ENTER;
1640 if (colour_table != NULL) {
1641 struct str const *cp = colour_get(cs);
1643 fwrite(cp->s, cp->l, 1, fp);
1645 NYD_LEAVE;
1648 FL void
1649 colour_put_header(FILE *fp, char const *name)
1651 enum colourspec cs = COLOURSPEC_HEADER;
1652 struct str const *uheads;
1653 char *cp, *cp_base, *x;
1654 size_t namelen;
1655 NYD_ENTER;
1657 if (colour_table == NULL)
1658 goto j_leave;
1659 /* Normal header colours if there are no user headers */
1660 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1661 if (uheads->s == NULL)
1662 goto jleave;
1664 /* Iterate over all entries in the *colour-user-headers* list */
1665 cp = ac_alloc(uheads->l +1);
1666 memcpy(cp, uheads->s, uheads->l +1);
1667 cp_base = cp;
1668 namelen = strlen(name);
1669 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1670 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1671 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1672 cs = COLOURSPEC_UHEADER;
1673 break;
1676 ac_free(cp_base);
1677 jleave:
1678 colour_put(fp, cs);
1679 j_leave:
1680 NYD_LEAVE;
1683 FL void
1684 colour_reset(FILE *fp)
1686 NYD_ENTER;
1687 if (colour_table != NULL)
1688 fwrite("\033[0m", 4, 1, fp);
1689 NYD_LEAVE;
1692 FL struct str const *
1693 colour_get(enum colourspec cs)
1695 struct str const *rv = NULL;
1696 NYD_ENTER;
1698 if (colour_table != NULL)
1699 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1700 rv = NULL;
1701 NYD_LEAVE;
1702 return rv;
1704 #endif /* HAVE_COLOUR */
1706 FL si8_t
1707 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1709 char *dat, *eptr;
1710 sl_i sli;
1711 si8_t rv;
1712 NYD_ENTER;
1714 assert(inlen == 0 || inbuf != NULL);
1716 if (inlen == UIZ_MAX)
1717 inlen = strlen(inbuf);
1719 if (inlen == 0)
1720 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1721 else {
1722 if ((inlen == 1 && *inbuf == '1') ||
1723 !ascncasecmp(inbuf, "true", inlen) ||
1724 !ascncasecmp(inbuf, "yes", inlen) ||
1725 !ascncasecmp(inbuf, "on", inlen))
1726 rv = 1;
1727 else if ((inlen == 1 && *inbuf == '0') ||
1728 !ascncasecmp(inbuf, "false", inlen) ||
1729 !ascncasecmp(inbuf, "no", inlen) ||
1730 !ascncasecmp(inbuf, "off", inlen))
1731 rv = 0;
1732 else {
1733 dat = ac_alloc(inlen +1);
1734 memcpy(dat, inbuf, inlen);
1735 dat[inlen] = '\0';
1737 sli = strtol(dat, &eptr, 0);
1738 if (*dat != '\0' && *eptr == '\0')
1739 rv = (sli != 0);
1740 else
1741 rv = -1;
1743 ac_free(dat);
1746 NYD_LEAVE;
1747 return rv;
1750 FL si8_t
1751 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1753 si8_t rv;
1754 NYD_ENTER;
1756 assert(inlen == 0 || inbuf != NULL);
1758 if (inlen == UIZ_MAX)
1759 inlen = strlen(inbuf);
1761 if (inlen == 0)
1762 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1763 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1764 !ascncasecmp(inbuf, "ask-", 4) &&
1765 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1766 (options & OPT_INTERACTIVE)) {
1767 if (prompt != NULL)
1768 fputs(prompt, stdout);
1769 rv = getapproval(NULL, rv);
1771 NYD_LEAVE;
1772 return rv;
1775 FL void
1776 time_current_update(struct time_current *tc, bool_t full_update)
1778 NYD_ENTER;
1779 tc->tc_time = time(NULL);
1780 if (full_update) {
1781 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1782 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1783 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1785 NYD_LEAVE;
1788 FL void
1789 n_err(char const *format, ...)
1791 va_list ap;
1792 NYD2_ENTER;
1794 va_start(ap, format);
1795 #ifdef HAVE_ERRORS
1796 if (options & OPT_INTERACTIVE)
1797 n_verr(format, ap);
1798 else
1799 #endif
1801 vfprintf(stderr, format, ap);
1802 if (strchr(format, '\n') != NULL) /* TODO */
1803 fflush(stderr);
1805 va_end(ap);
1806 NYD2_LEAVE;
1809 FL void
1810 n_verr(char const *format, va_list ap)
1812 /* Check use cases of PS_ERRORS_NOTED, too! */
1813 #ifdef HAVE_ERRORS
1814 char buf[LINESIZE], *xbuf = buf;
1815 int l = sizeof(buf) -1;
1816 struct err_node *enp;
1818 LCTA(ERRORS_MAX > 3);
1819 #endif
1820 NYD2_ENTER;
1822 #ifdef HAVE_ERRORS
1823 if (!(options & OPT_INTERACTIVE))
1824 #endif
1826 vfprintf(stderr, format, ap);
1827 goto jleave;
1830 #ifdef HAVE_ERRORS
1831 jredo:
1832 l = vsnprintf(xbuf, l +1, format, ap);
1833 if (l == 0) {
1834 assert(xbuf == buf);
1835 goto jleave;
1837 if (UICMP(z, l, >=, sizeof buf)) {
1838 if (xbuf == buf) {
1839 xbuf = smalloc(l +1);
1840 goto jredo;
1842 l = sizeof(buf) -1;
1844 xbuf[l] = '\0';
1846 fwrite(xbuf, 1, l, stderr);
1848 /* Link it into the `errors' message ring */
1849 if ((enp = _err_tail) == NULL) {
1850 jcreat:
1851 enp = scalloc(1, sizeof *enp);
1852 if (_err_tail != NULL)
1853 _err_tail->en_next = enp;
1854 else
1855 _err_head = enp;
1856 _err_tail = enp;
1857 ++_err_cnt;
1858 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
1859 if (_err_cnt < ERRORS_MAX)
1860 goto jcreat;
1862 _err_head = (enp = _err_head)->en_next;
1863 _err_tail->en_next = enp;
1864 _err_tail = enp;
1865 free(enp->en_str.s);
1866 memset(enp, 0, sizeof *enp);
1869 n_str_add_buf(&enp->en_str, xbuf, l);
1871 if (xbuf != buf)
1872 free(xbuf);
1873 #endif /* HAVE_ERRORS */
1875 jleave:
1876 if (strchr(format, '\n') != NULL) /* TODO */
1877 fflush(stderr);
1878 NYD2_LEAVE;
1881 FL void
1882 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
1884 va_list ap;
1885 NYD_X;
1887 va_start(ap, format);
1888 vfprintf(stderr, format, ap);
1889 va_end(ap);
1890 fflush(stderr);
1893 FL void
1894 n_perr(char const *msg, int errval)
1896 char const *fmt;
1897 NYD2_ENTER;
1899 if (msg == NULL) {
1900 fmt = "%s%s\n";
1901 msg = "";
1902 } else
1903 fmt = "%s: %s\n";
1905 if (errval == 0)
1906 errval = errno;
1908 n_err(fmt, msg, strerror(errval));
1909 NYD2_LEAVE;
1912 FL void
1913 n_alert(char const *format, ...)
1915 va_list ap;
1916 NYD2_ENTER;
1918 n_err(_("Alert: "));
1920 va_start(ap, format);
1921 n_verr(format, ap);
1922 va_end(ap);
1924 n_err("\n");
1925 NYD2_LEAVE;
1928 FL void
1929 n_panic(char const *format, ...)
1931 va_list ap;
1932 NYD2_ENTER;
1934 fprintf(stderr, _("Panic: "));
1936 va_start(ap, format);
1937 vfprintf(stderr, format, ap);
1938 va_end(ap);
1940 putc('\n', stderr);
1941 fflush(stderr);
1942 NYD2_LEAVE;
1943 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1946 #ifdef HAVE_ERRORS
1947 FL int
1948 c_errors(void *v)
1950 char **argv = v;
1951 struct err_node *enp;
1952 NYD_ENTER;
1954 if (*argv == NULL)
1955 goto jlist;
1956 if (argv[1] != NULL)
1957 goto jerr;
1958 if (!asccasecmp(*argv, "show"))
1959 goto jlist;
1960 if (!asccasecmp(*argv, "clear"))
1961 goto jclear;
1962 jerr:
1963 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1964 v = NULL;
1965 jleave:
1966 NYD_LEAVE;
1967 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1969 jlist: {
1970 FILE *fp;
1971 size_t i;
1973 if (_err_head == NULL) {
1974 fprintf(stderr, _("The error ring is empty\n"));
1975 goto jleave;
1978 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)
1979 ) == NULL) {
1980 fprintf(stderr, _("tmpfile"));
1981 v = NULL;
1982 goto jleave;
1985 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
1986 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1987 ++i, enp->en_str.l, enp->en_str.s);
1988 /* We don't know wether last string ended with NL; be simple */
1989 putc('\n', fp);
1991 page_or_print(fp, 0);
1992 Fclose(fp);
1994 /* FALLTHRU */
1996 jclear:
1997 _err_tail = NULL;
1998 _err_cnt = _err_cnt_noted = 0;
1999 while ((enp = _err_head) != NULL) {
2000 _err_head = enp->en_next;
2001 free(enp->en_str.s);
2002 free(enp);
2004 goto jleave;
2006 #endif /* HAVE_ERRORS */
2008 #ifndef HAVE_DEBUG
2009 FL void *
2010 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2012 void *rv;
2013 NYD2_ENTER;
2015 if (s == 0)
2016 s = 1;
2017 if ((rv = malloc(s)) == NULL)
2018 n_panic(_("no memory"));
2019 NYD2_LEAVE;
2020 return rv;
2023 FL void *
2024 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2026 void *rv;
2027 NYD2_ENTER;
2029 if (s == 0)
2030 s = 1;
2031 if (v == NULL)
2032 rv = smalloc(s);
2033 else if ((rv = realloc(v, s)) == NULL)
2034 n_panic(_("no memory"));
2035 NYD2_LEAVE;
2036 return rv;
2039 FL void *
2040 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2042 void *rv;
2043 NYD2_ENTER;
2045 if (size == 0)
2046 size = 1;
2047 if ((rv = calloc(nmemb, size)) == NULL)
2048 n_panic(_("no memory"));
2049 NYD2_LEAVE;
2050 return rv;
2053 #else /* !HAVE_DEBUG */
2054 CTA(sizeof(char) == sizeof(ui8_t));
2056 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2057 # define _HOPE_SET(C) \
2058 do {\
2059 union mem_ptr __xl, __xu;\
2060 struct mem_chunk *__xc;\
2061 __xl.p_p = (C).p_p;\
2062 __xc = __xl.p_c - 1;\
2063 __xu.p_p = __xc;\
2064 (C).p_cp += 8;\
2065 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2066 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2067 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2068 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2069 __xu.p_ui8p += __xc->mc_size - 8;\
2070 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2071 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2072 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2073 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2074 } while (0)
2075 # define _HOPE_GET_TRACE(C,BAD) \
2076 do {\
2077 (C).p_cp += 8;\
2078 _HOPE_GET(C, BAD);\
2079 (C).p_cp += 8;\
2080 } while(0)
2081 # define _HOPE_GET(C,BAD) \
2082 do {\
2083 union mem_ptr __xl, __xu;\
2084 struct mem_chunk *__xc;\
2085 ui32_t __i;\
2086 __xl.p_p = (C).p_p;\
2087 __xl.p_cp -= 8;\
2088 (C).p_cp = __xl.p_cp;\
2089 __xc = __xl.p_c - 1;\
2090 (BAD) = FAL0;\
2091 __i = 0;\
2092 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2093 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2094 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2095 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2096 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2097 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2098 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2099 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2100 if (__i != 0) {\
2101 (BAD) = TRU1;\
2102 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2103 __xl.p_p, __i, mdbg_file, mdbg_line);\
2105 __xu.p_p = __xc;\
2106 __xu.p_ui8p += __xc->mc_size - 8;\
2107 __i = 0;\
2108 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2109 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2110 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2111 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2112 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2113 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2114 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2115 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2116 if (__i != 0) {\
2117 (BAD) = TRU1;\
2118 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2119 __xl.p_p, __i, mdbg_file, mdbg_line);\
2121 if (BAD)\
2122 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2123 __xc->mc_file, __xc->mc_line);\
2124 } while (0)
2126 FL void *
2127 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2129 union mem_ptr p;
2130 NYD2_ENTER;
2132 if (s == 0)
2133 s = 1;
2134 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2135 n_panic("smalloc(): allocation too large: %s, line %d",
2136 mdbg_file, mdbg_line);
2137 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2139 if ((p.p_p = (malloc)(s)) == NULL)
2140 n_panic(_("no memory"));
2141 p.p_c->mc_prev = NULL;
2142 if ((p.p_c->mc_next = _mem_list) != NULL)
2143 _mem_list->mc_prev = p.p_c;
2144 p.p_c->mc_file = mdbg_file;
2145 p.p_c->mc_line = (ui16_t)mdbg_line;
2146 p.p_c->mc_isfree = FAL0;
2147 p.p_c->mc_size = (ui32_t)s;
2149 _mem_list = p.p_c++;
2150 _HOPE_SET(p);
2152 ++_mem_aall;
2153 ++_mem_acur;
2154 _mem_amax = MAX(_mem_amax, _mem_acur);
2155 _mem_mall += s;
2156 _mem_mcur += s;
2157 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2158 NYD2_LEAVE;
2159 return p.p_p;
2162 FL void *
2163 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2165 union mem_ptr p;
2166 bool_t isbad;
2167 NYD2_ENTER;
2169 if ((p.p_p = v) == NULL) {
2170 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2171 goto jleave;
2174 _HOPE_GET(p, isbad);
2175 --p.p_c;
2176 if (p.p_c->mc_isfree) {
2177 n_err("srealloc(): region freed! At %s, line %d\n"
2178 "\tLast seen: %s, line %" PRIu16 "\n",
2179 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2180 goto jforce;
2183 if (p.p_c == _mem_list)
2184 _mem_list = p.p_c->mc_next;
2185 else
2186 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2187 if (p.p_c->mc_next != NULL)
2188 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2190 --_mem_acur;
2191 _mem_mcur -= p.p_c->mc_size;
2192 jforce:
2193 if (s == 0)
2194 s = 1;
2195 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2196 n_panic("srealloc(): allocation too large: %s, line %d",
2197 mdbg_file, mdbg_line);
2198 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2200 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2201 n_panic(_("no memory"));
2202 p.p_c->mc_prev = NULL;
2203 if ((p.p_c->mc_next = _mem_list) != NULL)
2204 _mem_list->mc_prev = p.p_c;
2205 p.p_c->mc_file = mdbg_file;
2206 p.p_c->mc_line = (ui16_t)mdbg_line;
2207 p.p_c->mc_isfree = FAL0;
2208 p.p_c->mc_size = (ui32_t)s;
2209 _mem_list = p.p_c++;
2210 _HOPE_SET(p);
2212 ++_mem_aall;
2213 ++_mem_acur;
2214 _mem_amax = MAX(_mem_amax, _mem_acur);
2215 _mem_mall += s;
2216 _mem_mcur += s;
2217 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2218 jleave:
2219 NYD2_LEAVE;
2220 return p.p_p;
2223 FL void *
2224 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2226 union mem_ptr p;
2227 NYD2_ENTER;
2229 if (size == 0)
2230 size = 1;
2231 if (nmemb == 0)
2232 nmemb = 1;
2233 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2234 n_panic("scalloc(): allocation size too large: %s, line %d",
2235 mdbg_file, mdbg_line);
2236 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2237 n_panic("scalloc(): allocation count too large: %s, line %d",
2238 mdbg_file, mdbg_line);
2240 size *= nmemb;
2241 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2243 if ((p.p_p = (malloc)(size)) == NULL)
2244 n_panic(_("no memory"));
2245 memset(p.p_p, 0, size);
2246 p.p_c->mc_prev = NULL;
2247 if ((p.p_c->mc_next = _mem_list) != NULL)
2248 _mem_list->mc_prev = p.p_c;
2249 p.p_c->mc_file = mdbg_file;
2250 p.p_c->mc_line = (ui16_t)mdbg_line;
2251 p.p_c->mc_isfree = FAL0;
2252 p.p_c->mc_size = (ui32_t)size;
2253 _mem_list = p.p_c++;
2254 _HOPE_SET(p);
2256 ++_mem_aall;
2257 ++_mem_acur;
2258 _mem_amax = MAX(_mem_amax, _mem_acur);
2259 _mem_mall += size;
2260 _mem_mcur += size;
2261 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2262 NYD2_LEAVE;
2263 return p.p_p;
2266 FL void
2267 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2269 union mem_ptr p;
2270 bool_t isbad;
2271 NYD2_ENTER;
2273 if ((p.p_p = v) == NULL) {
2274 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2275 goto jleave;
2278 _HOPE_GET(p, isbad);
2279 --p.p_c;
2280 if (p.p_c->mc_isfree) {
2281 n_err("sfree(): double-free avoided at %s, line %d\n"
2282 "\tLast seen: %s, line %" PRIu16 "\n",
2283 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2284 goto jleave;
2287 if (p.p_c == _mem_list)
2288 _mem_list = p.p_c->mc_next;
2289 else
2290 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2291 if (p.p_c->mc_next != NULL)
2292 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2293 p.p_c->mc_isfree = TRU1;
2294 /* Trash contents (also see [21c05f8]) */
2295 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2297 --_mem_acur;
2298 _mem_mcur -= p.p_c->mc_size;
2300 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2301 p.p_c->mc_next = _mem_free;
2302 _mem_free = p.p_c;
2303 } else
2304 (free)(p.p_c);
2305 jleave:
2306 NYD2_LEAVE;
2309 FL void
2310 smemreset(void)
2312 union mem_ptr p;
2313 size_t c = 0, s = 0;
2314 NYD_ENTER;
2316 smemcheck();
2318 for (p.p_c = _mem_free; p.p_c != NULL;) {
2319 void *vp = p.p_c;
2320 ++c;
2321 s += p.p_c->mc_size;
2322 p.p_c = p.p_c->mc_next;
2323 (free)(vp);
2325 _mem_free = NULL;
2327 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2328 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2329 NYD_LEAVE;
2332 FL int
2333 c_smemtrace(void *v)
2335 /* For _HOPE_GET() */
2336 char const * const mdbg_file = "smemtrace()";
2337 int const mdbg_line = -1;
2338 FILE *fp;
2339 union mem_ptr p, xp;
2340 bool_t isbad;
2341 size_t lines;
2342 NYD_ENTER;
2344 v = (void*)0x1;
2345 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2346 NULL) {
2347 n_perr("tmpfile", 0);
2348 goto jleave;
2351 fprintf(fp, "Memory statistics:\n"
2352 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2353 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2354 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2356 fprintf(fp, "Currently allocated memory chunks:\n");
2357 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2358 ++lines, p.p_c = p.p_c->mc_next) {
2359 xp = p;
2360 ++xp.p_c;
2361 _HOPE_GET_TRACE(xp, isbad);
2362 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2363 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2364 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2365 p.p_c->mc_line);
2368 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2369 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2370 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2371 xp = p;
2372 ++xp.p_c;
2373 _HOPE_GET_TRACE(xp, isbad);
2374 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2375 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2376 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2377 p.p_c->mc_file, p.p_c->mc_line);
2381 page_or_print(fp, lines);
2382 Fclose(fp);
2383 v = NULL;
2384 jleave:
2385 NYD_LEAVE;
2386 return (v != NULL);
2389 # ifdef HAVE_DEVEL
2390 FL bool_t
2391 _smemcheck(char const *mdbg_file, int mdbg_line)
2393 union mem_ptr p, xp;
2394 bool_t anybad = FAL0, isbad;
2395 size_t lines;
2396 NYD_ENTER;
2398 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2399 ++lines, p.p_c = p.p_c->mc_next) {
2400 xp = p;
2401 ++xp.p_c;
2402 _HOPE_GET_TRACE(xp, isbad);
2403 if (isbad) {
2404 anybad = TRU1;
2405 n_err(
2406 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2407 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2408 p.p_c->mc_file, p.p_c->mc_line);
2412 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2413 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2414 xp = p;
2415 ++xp.p_c;
2416 _HOPE_GET_TRACE(xp, isbad);
2417 if (isbad) {
2418 anybad = TRU1;
2419 n_err(
2420 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2421 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2422 p.p_c->mc_file, p.p_c->mc_line);
2426 NYD_LEAVE;
2427 return anybad;
2429 # endif /* HAVE_DEVEL */
2431 # undef _HOPE_SIZE
2432 # undef _HOPE_SET
2433 # undef _HOPE_GET_TRACE
2434 # undef _HOPE_GET
2435 #endif /* HAVE_DEBUG */
2437 /* s-it-mode */