Do not terminate MTA args with -- if *sendmail-no-default-arguments* is used
[s-mailx.git] / auxlily.c
blobeb880f1f0abb1b8068fb8e9cb6ed716faca59ea0
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 - 2; /* 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 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1014 * with PAD stripped is still longer than what the user requests, easy way */
1015 data = ac_alloc(i = length + 3);
1017 #ifndef HAVE_POSIX_RANDOM
1018 while (i-- > 0)
1019 data[i] = (char)_rand_get8();
1020 #else
1021 { char *cp = data;
1023 while (i > 0) {
1024 union {ui32_t i4; char c[4];} r;
1025 size_t j;
1027 r.i4 = (ui32_t)arc4random();
1028 switch ((j = i & 3)) {
1029 case 0: cp[3] = r.c[3]; j = 4;
1030 case 3: cp[2] = r.c[2];
1031 case 2: cp[1] = r.c[1];
1032 default: cp[0] = r.c[0]; break;
1034 cp += j;
1035 i -= j;
1038 #endif
1040 b64_encode_buf(&b64, data, length + 3,
1041 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1042 ac_free(data);
1044 assert(b64.l >= length);
1045 b64.s[length] = '\0';
1046 NYD_LEAVE;
1047 return b64.s;
1050 FL enum okay
1051 makedir(char const *name)
1053 struct stat st;
1054 enum okay rv = STOP;
1055 NYD_ENTER;
1057 if (!mkdir(name, 0700))
1058 rv = OKAY;
1059 else {
1060 int e = errno;
1061 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1062 S_ISDIR(st.st_mode))
1063 rv = OKAY;
1065 NYD_LEAVE;
1066 return rv;
1069 #ifdef HAVE_FCHDIR
1070 FL enum okay
1071 cwget(struct cw *cw)
1073 enum okay rv = STOP;
1074 NYD_ENTER;
1076 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1077 goto jleave;
1078 if (fchdir(cw->cw_fd) == -1) {
1079 close(cw->cw_fd);
1080 goto jleave;
1082 rv = OKAY;
1083 jleave:
1084 NYD_LEAVE;
1085 return rv;
1088 FL enum okay
1089 cwret(struct cw *cw)
1091 enum okay rv = STOP;
1092 NYD_ENTER;
1094 if (!fchdir(cw->cw_fd))
1095 rv = OKAY;
1096 NYD_LEAVE;
1097 return rv;
1100 FL void
1101 cwrelse(struct cw *cw)
1103 NYD_ENTER;
1104 close(cw->cw_fd);
1105 NYD_LEAVE;
1108 #else /* !HAVE_FCHDIR */
1109 FL enum okay
1110 cwget(struct cw *cw)
1112 enum okay rv = STOP;
1113 NYD_ENTER;
1115 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1116 rv = OKAY;
1117 NYD_LEAVE;
1118 return rv;
1121 FL enum okay
1122 cwret(struct cw *cw)
1124 enum okay rv = STOP;
1125 NYD_ENTER;
1127 if (!chdir(cw->cw_wd))
1128 rv = OKAY;
1129 NYD_LEAVE;
1130 return rv;
1133 FL void
1134 cwrelse(struct cw *cw)
1136 NYD_ENTER;
1137 UNUSED(cw);
1138 NYD_LEAVE;
1140 #endif /* !HAVE_FCHDIR */
1142 FL size_t
1143 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1145 size_t rv;
1146 NYD_ENTER;
1148 #ifdef HAVE_NATCH_CHAR
1149 maxlen = MIN(maxlen, blen);
1150 for (rv = 0; maxlen > 0;) {
1151 int ml = mblen(buf, maxlen);
1152 if (ml <= 0) {
1153 mblen(NULL, 0);
1154 break;
1156 buf += ml;
1157 rv += ml;
1158 maxlen -= ml;
1160 #else
1161 rv = MIN(blen, maxlen);
1162 #endif
1163 NYD_LEAVE;
1164 return rv;
1167 FL size_t
1168 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1170 NATCH_CHAR( struct bidi_info bi; )
1171 size_t rv NATCH_CHAR( COMMA i );
1172 NYD_ENTER;
1174 rv = 0;
1175 if (maxlen-- == 0)
1176 goto j_leave;
1178 #ifdef HAVE_NATCH_CHAR
1179 bidi_info_create(&bi);
1180 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1181 bi.bi_end.l = 0;
1182 goto jnobidi;
1185 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1186 maxlen -= i;
1187 else
1188 goto jleave;
1190 if ((i = bi.bi_start.l) > 0) {
1191 memcpy(store, bi.bi_start.s, i);
1192 store += i;
1193 rv += i;
1196 jnobidi:
1197 while (maxlen > 0) {
1198 int ml = mblen(buf, blen);
1199 if (ml <= 0) {
1200 mblen(NULL, 0);
1201 break;
1203 if (UICMP(z, maxlen, <, ml))
1204 break;
1205 if (ml == 1)
1206 *store = *buf;
1207 else
1208 memcpy(store, buf, ml);
1209 store += ml;
1210 buf += ml;
1211 rv += ml;
1212 maxlen -= ml;
1215 if ((i = bi.bi_end.l) > 0) {
1216 memcpy(store, bi.bi_end.s, i);
1217 store += i;
1218 rv += i;
1220 jleave:
1221 *store = '\0';
1223 #else
1224 rv = MIN(blen, maxlen);
1225 memcpy(store, buf, rv);
1226 store[rv] = '\0';
1227 #endif
1228 j_leave:
1229 NYD_LEAVE;
1230 return rv;
1233 FL char *
1234 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1236 NATCH_CHAR( struct bidi_info bi; )
1237 int col_orig = col, n, sz;
1238 bool_t isbidi, isuni, istab, isrepl;
1239 char *nb, *np;
1240 NYD_ENTER;
1242 /* Bidi only on request and when there is 8-bit data */
1243 isbidi = isuni = FAL0;
1244 #ifdef HAVE_NATCH_CHAR
1245 isuni = ((options & OPT_UNICODE) != 0);
1246 bidi_info_create(&bi);
1247 if (bi.bi_start.l == 0)
1248 goto jnobidi;
1249 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1250 goto jnobidi;
1252 if ((size_t)col >= bi.bi_pad)
1253 col -= bi.bi_pad;
1254 else
1255 col = 0;
1256 jnobidi:
1257 #endif
1259 np = nb = salloc(mb_cur_max * strlen(cp) +
1260 ((fill ? col : 0)
1261 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1262 +1));
1264 #ifdef HAVE_NATCH_CHAR
1265 if (isbidi) {
1266 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1267 np += bi.bi_start.l;
1269 #endif
1271 while (*cp != '\0') {
1272 istab = FAL0;
1273 #ifdef HAVE_C90AMEND1
1274 if (mb_cur_max > 1) {
1275 wchar_t wc;
1277 n = 1;
1278 isrepl = TRU1;
1279 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1280 sz = 1;
1281 else if (wc == L'\t') {
1282 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1283 isrepl = FAL0;
1284 istab = TRU1;
1285 } else if (iswprint(wc)) {
1286 # ifndef HAVE_WCWIDTH
1287 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1288 # else
1289 if ((n = wcwidth(wc)) == -1)
1290 n = 1;
1291 else
1292 # endif
1293 isrepl = FAL0;
1295 } else
1296 #endif
1298 n = sz = 1;
1299 istab = (*cp == '\t');
1300 isrepl = !(istab || isprint((uc_i)*cp));
1303 if (n > col)
1304 break;
1305 col -= n;
1307 if (isrepl) {
1308 if (isuni) {
1309 np[0] = (char)0xEFu;
1310 np[1] = (char)0xBFu;
1311 np[2] = (char)0xBDu;
1312 np += 3;
1313 } else
1314 *np++ = '?';
1315 cp += sz;
1316 } else if (istab || (sz == 1 && spacechar(*cp))) {
1317 *np++ = ' ';
1318 ++cp;
1319 } else
1320 while (sz--)
1321 *np++ = *cp++;
1324 if (fill && col != 0) {
1325 if (fill > 0) {
1326 memmove(nb + col, nb, PTR2SIZE(np - nb));
1327 memset(nb, ' ', col);
1328 } else
1329 memset(np, ' ', col);
1330 np += col;
1331 col = 0;
1334 #ifdef HAVE_NATCH_CHAR
1335 if (isbidi) {
1336 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1337 np += bi.bi_end.l;
1339 #endif
1341 *np = '\0';
1342 if (cols_decr_used_or_null != NULL)
1343 *cols_decr_used_or_null -= col_orig - col;
1344 NYD_LEAVE;
1345 return nb;
1348 FL void
1349 makeprint(struct str const *in, struct str *out)
1351 static int print_all_chars = -1;
1353 char const *inp, *maxp;
1354 char *outp;
1355 DBG( size_t msz; )
1356 NYD_ENTER;
1358 if (print_all_chars == -1)
1359 print_all_chars = ok_blook(print_all_chars);
1361 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1362 inp = in->s;
1363 maxp = inp + in->l;
1365 if (print_all_chars) {
1366 out->l = in->l;
1367 memcpy(outp, inp, out->l);
1368 goto jleave;
1371 #ifdef HAVE_NATCH_CHAR
1372 if (mb_cur_max > 1) {
1373 char mbb[MB_LEN_MAX + 1];
1374 wchar_t wc;
1375 int i, n;
1376 bool_t isuni = ((options & OPT_UNICODE) != 0);
1378 out->l = 0;
1379 while (inp < maxp) {
1380 if (*inp & 0200)
1381 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1382 else {
1383 wc = *inp;
1384 n = 1;
1386 if (n == -1) {
1387 /* FIXME Why mbtowc() resetting here?
1388 * FIXME what about ISO 2022-JP plus -- those
1389 * FIXME will loose shifts, then!
1390 * FIXME THUS - we'd need special "known points"
1391 * FIXME to do so - say, after a newline!!
1392 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1393 mbtowc(&wc, NULL, mb_cur_max);
1394 wc = isuni ? 0xFFFD : '?';
1395 n = 1;
1396 } else if (n == 0)
1397 n = 1;
1398 inp += n;
1399 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1400 wc != '\t') {
1401 if ((wc & ~(wchar_t)037) == 0)
1402 wc = isuni ? 0x2400 | wc : '?';
1403 else if (wc == 0177)
1404 wc = isuni ? 0x2421 : '?';
1405 else
1406 wc = isuni ? 0x2426 : '?';
1408 if ((n = wctomb(mbb, wc)) <= 0)
1409 continue;
1410 out->l += n;
1411 assert(out->l < msz);
1412 for (i = 0; i < n; ++i)
1413 *outp++ = mbb[i];
1415 } else
1416 #endif /* NATCH_CHAR */
1418 int c;
1419 while (inp < maxp) {
1420 c = *inp++ & 0377;
1421 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1422 c = '?';
1423 *outp++ = c;
1425 out->l = in->l;
1427 jleave:
1428 out->s[out->l] = '\0';
1429 NYD_LEAVE;
1432 FL size_t
1433 delctrl(char *cp, size_t len)
1435 size_t x, y;
1436 NYD_ENTER;
1438 for (x = y = 0; x < len; ++x)
1439 if (!cntrlchar(cp[x]))
1440 cp[y++] = cp[x];
1441 cp[y] = '\0';
1442 NYD_LEAVE;
1443 return y;
1446 FL char *
1447 prstr(char const *s)
1449 struct str in, out;
1450 char *rp;
1451 NYD_ENTER;
1453 in.s = UNCONST(s);
1454 in.l = strlen(s);
1455 makeprint(&in, &out);
1456 rp = savestrbuf(out.s, out.l);
1457 free(out.s);
1458 NYD_LEAVE;
1459 return rp;
1462 FL int
1463 prout(char const *s, size_t sz, FILE *fp)
1465 struct str in, out;
1466 int n;
1467 NYD_ENTER;
1469 in.s = UNCONST(s);
1470 in.l = sz;
1471 makeprint(&in, &out);
1472 n = fwrite(out.s, 1, out.l, fp);
1473 free(out.s);
1474 NYD_LEAVE;
1475 return n;
1478 FL size_t
1479 putuc(int u, int c, FILE *fp)
1481 size_t rv;
1482 NYD_ENTER;
1483 UNUSED(u);
1485 #ifdef HAVE_NATCH_CHAR
1486 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1487 char mbb[MB_LEN_MAX];
1488 int i, n;
1490 if ((n = wctomb(mbb, u)) > 0) {
1491 rv = wcwidth(u);
1492 for (i = 0; i < n; ++i)
1493 if (putc(mbb[i] & 0377, fp) == EOF) {
1494 rv = 0;
1495 break;
1497 } else if (n == 0)
1498 rv = (putc('\0', fp) != EOF);
1499 else
1500 rv = 0;
1501 } else
1502 #endif
1503 rv = (putc(c, fp) != EOF);
1504 NYD_LEAVE;
1505 return rv;
1508 FL bool_t
1509 bidi_info_needed(char const *bdat, size_t blen)
1511 bool_t rv = FAL0;
1512 NYD_ENTER;
1514 #ifdef HAVE_NATCH_CHAR
1515 if (options & OPT_UNICODE)
1516 while (blen > 0) {
1517 /* TODO Checking for BIDI character: use S-CText fromutf8
1518 * TODO plus isrighttoleft (or whatever there will be)! */
1519 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1520 if (c == UI32_MAX)
1521 break;
1523 if (c <= 0x05BE)
1524 continue;
1526 /* (Very very fuzzy, awaiting S-CText for good) */
1527 if ((c >= 0x05BE && c <= 0x08E3) ||
1528 (c >= 0xFB1D && c <= 0xFEFC) ||
1529 (c >= 0x10800 && c <= 0x10C48) ||
1530 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1531 rv = TRU1;
1532 break;
1535 #endif /* HAVE_NATCH_CHAR */
1536 NYD_LEAVE;
1537 return rv;
1540 FL void
1541 bidi_info_create(struct bidi_info *bip)
1543 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1544 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1545 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1546 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1547 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1548 NATCH_CHAR( char const *hb; )
1549 NYD_ENTER;
1551 memset(bip, 0, sizeof *bip);
1552 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1554 #ifdef HAVE_NATCH_CHAR
1555 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1556 switch (*hb) {
1557 case '3':
1558 bip->bi_pad = 2;
1559 /* FALLTHRU */
1560 case '2':
1561 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1562 break;
1563 case '1':
1564 bip->bi_pad = 2;
1565 /* FALLTHRU */
1566 default:
1567 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1568 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1569 break;
1571 bip->bi_start.l = bip->bi_end.l = 3;
1573 #endif
1574 NYD_LEAVE;
1577 #ifdef HAVE_COLOUR
1578 FL void
1579 colour_table_create(bool_t pager_used)
1581 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1582 size_t i;
1583 struct colour_table *ct;
1584 NYD_ENTER;
1586 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1587 goto jleave;
1588 else {
1589 char *term, *okterms;
1591 if ((term = env_vlook("TERM", FAL0)) == NULL)
1592 goto jleave;
1593 /* terminfo rocks: if we find "color", assume it's right */
1594 if (strstr(term, "color") != NULL)
1595 goto jok;
1596 if ((okterms = ok_vlook(colour_terms)) == NULL)
1597 okterms = UNCONST(COLOUR_TERMS);
1598 okterms = savestr(okterms);
1600 i = strlen(term);
1601 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1602 if (!strncmp(u.cp, term, i))
1603 goto jok;
1604 goto jleave;
1607 jok:
1608 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1609 { static struct {
1610 enum okeys okey;
1611 enum colourspec cspec;
1612 char const *defval;
1613 } const map[] = {
1614 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1615 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1616 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1617 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1618 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1621 for (i = 0; i < NELEM(map); ++i) {
1622 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1623 u.ccp = map[i].defval;
1624 u.cp = _colour_iso6429(u.ccp);
1625 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1626 ct->ct_csinfo[map[i].cspec].s = u.cp;
1629 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1630 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1632 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1633 u.ccp = COLOUR_USER_HEADERS;
1634 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1635 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1636 jleave:
1637 NYD_LEAVE;
1640 FL void
1641 colour_put(FILE *fp, enum colourspec cs)
1643 NYD_ENTER;
1644 if (colour_table != NULL) {
1645 struct str const *cp = colour_get(cs);
1647 fwrite(cp->s, cp->l, 1, fp);
1649 NYD_LEAVE;
1652 FL void
1653 colour_put_header(FILE *fp, char const *name)
1655 enum colourspec cs = COLOURSPEC_HEADER;
1656 struct str const *uheads;
1657 char *cp, *cp_base, *x;
1658 size_t namelen;
1659 NYD_ENTER;
1661 if (colour_table == NULL)
1662 goto j_leave;
1663 /* Normal header colours if there are no user headers */
1664 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1665 if (uheads->s == NULL)
1666 goto jleave;
1668 /* Iterate over all entries in the *colour-user-headers* list */
1669 cp = ac_alloc(uheads->l +1);
1670 memcpy(cp, uheads->s, uheads->l +1);
1671 cp_base = cp;
1672 namelen = strlen(name);
1673 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1674 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1675 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1676 cs = COLOURSPEC_UHEADER;
1677 break;
1680 ac_free(cp_base);
1681 jleave:
1682 colour_put(fp, cs);
1683 j_leave:
1684 NYD_LEAVE;
1687 FL void
1688 colour_reset(FILE *fp)
1690 NYD_ENTER;
1691 if (colour_table != NULL)
1692 fwrite("\033[0m", 4, 1, fp);
1693 NYD_LEAVE;
1696 FL struct str const *
1697 colour_get(enum colourspec cs)
1699 struct str const *rv = NULL;
1700 NYD_ENTER;
1702 if (colour_table != NULL)
1703 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1704 rv = NULL;
1705 NYD_LEAVE;
1706 return rv;
1708 #endif /* HAVE_COLOUR */
1710 FL si8_t
1711 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1713 char *dat, *eptr;
1714 sl_i sli;
1715 si8_t rv;
1716 NYD_ENTER;
1718 assert(inlen == 0 || inbuf != NULL);
1720 if (inlen == UIZ_MAX)
1721 inlen = strlen(inbuf);
1723 if (inlen == 0)
1724 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1725 else {
1726 if ((inlen == 1 && *inbuf == '1') ||
1727 !ascncasecmp(inbuf, "true", inlen) ||
1728 !ascncasecmp(inbuf, "yes", inlen) ||
1729 !ascncasecmp(inbuf, "on", inlen))
1730 rv = 1;
1731 else if ((inlen == 1 && *inbuf == '0') ||
1732 !ascncasecmp(inbuf, "false", inlen) ||
1733 !ascncasecmp(inbuf, "no", inlen) ||
1734 !ascncasecmp(inbuf, "off", inlen))
1735 rv = 0;
1736 else {
1737 dat = ac_alloc(inlen +1);
1738 memcpy(dat, inbuf, inlen);
1739 dat[inlen] = '\0';
1741 sli = strtol(dat, &eptr, 0);
1742 if (*dat != '\0' && *eptr == '\0')
1743 rv = (sli != 0);
1744 else
1745 rv = -1;
1747 ac_free(dat);
1750 NYD_LEAVE;
1751 return rv;
1754 FL si8_t
1755 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1757 si8_t rv;
1758 NYD_ENTER;
1760 assert(inlen == 0 || inbuf != NULL);
1762 if (inlen == UIZ_MAX)
1763 inlen = strlen(inbuf);
1765 if (inlen == 0)
1766 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1767 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1768 !ascncasecmp(inbuf, "ask-", 4) &&
1769 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1770 (options & OPT_INTERACTIVE)) {
1771 if (prompt != NULL)
1772 fputs(prompt, stdout);
1773 rv = getapproval(NULL, rv);
1775 NYD_LEAVE;
1776 return rv;
1779 FL time_t
1780 n_time_epoch(void)
1782 #ifdef HAVE_CLOCK_GETTIME
1783 struct timespec ts;
1784 #elif defined HAVE_GETTIMEOFDAY
1785 struct timeval ts;
1786 #endif
1787 time_t rv;
1788 NYD2_ENTER;
1790 #ifdef HAVE_CLOCK_GETTIME
1791 clock_gettime(CLOCK_REALTIME, &ts);
1792 rv = (time_t)ts.tv_sec;
1793 #elif defined HAVE_GETTIMEOFDAY
1794 gettimeofday(&ts, NULL);
1795 rv = (time_t)ts.tv_sec;
1796 #else
1797 rv = time(NULL);
1798 #endif
1799 NYD2_LEAVE;
1800 return rv;
1803 FL void
1804 time_current_update(struct time_current *tc, bool_t full_update)
1806 NYD_ENTER;
1807 tc->tc_time = n_time_epoch();
1808 if (full_update) {
1809 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1810 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1811 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1813 NYD_LEAVE;
1816 FL void
1817 n_err(char const *format, ...)
1819 va_list ap;
1820 NYD2_ENTER;
1822 va_start(ap, format);
1823 #ifdef HAVE_ERRORS
1824 if (options & OPT_INTERACTIVE)
1825 n_verr(format, ap);
1826 else
1827 #endif
1829 vfprintf(stderr, format, ap);
1830 if (strchr(format, '\n') != NULL) /* TODO */
1831 fflush(stderr);
1833 va_end(ap);
1834 NYD2_LEAVE;
1837 FL void
1838 n_verr(char const *format, va_list ap)
1840 /* Check use cases of PS_ERRORS_NOTED, too! */
1841 #ifdef HAVE_ERRORS
1842 char buf[LINESIZE], *xbuf = buf;
1843 int l = sizeof(buf) -1;
1844 struct err_node *enp;
1846 LCTA(ERRORS_MAX > 3);
1847 #endif
1848 NYD2_ENTER;
1850 #ifdef HAVE_ERRORS
1851 if (!(options & OPT_INTERACTIVE))
1852 #endif
1854 vfprintf(stderr, format, ap);
1855 goto jleave;
1858 #ifdef HAVE_ERRORS
1859 jredo:
1860 l = vsnprintf(xbuf, l +1, format, ap);
1861 if (l == 0) {
1862 assert(xbuf == buf);
1863 goto jleave;
1865 if (UICMP(z, l, >=, sizeof buf)) {
1866 if (xbuf == buf) {
1867 xbuf = smalloc(l +1);
1868 goto jredo;
1870 l = sizeof(buf) -1;
1872 xbuf[l] = '\0';
1874 fwrite(xbuf, 1, l, stderr);
1876 /* Link it into the `errors' message ring */
1877 if ((enp = _err_tail) == NULL) {
1878 jcreat:
1879 enp = scalloc(1, sizeof *enp);
1880 if (_err_tail != NULL)
1881 _err_tail->en_next = enp;
1882 else
1883 _err_head = enp;
1884 _err_tail = enp;
1885 ++_err_cnt;
1886 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
1887 if (_err_cnt < ERRORS_MAX)
1888 goto jcreat;
1890 _err_head = (enp = _err_head)->en_next;
1891 _err_tail->en_next = enp;
1892 _err_tail = enp;
1893 free(enp->en_str.s);
1894 memset(enp, 0, sizeof *enp);
1897 n_str_add_buf(&enp->en_str, xbuf, l);
1899 if (xbuf != buf)
1900 free(xbuf);
1901 #endif /* HAVE_ERRORS */
1903 jleave:
1904 if (strchr(format, '\n') != NULL) /* TODO */
1905 fflush(stderr);
1906 NYD2_LEAVE;
1909 FL void
1910 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
1912 va_list ap;
1913 NYD_X;
1915 va_start(ap, format);
1916 vfprintf(stderr, format, ap);
1917 va_end(ap);
1918 fflush(stderr);
1921 FL void
1922 n_perr(char const *msg, int errval)
1924 char const *fmt;
1925 NYD2_ENTER;
1927 if (msg == NULL) {
1928 fmt = "%s%s\n";
1929 msg = "";
1930 } else
1931 fmt = "%s: %s\n";
1933 if (errval == 0)
1934 errval = errno;
1936 n_err(fmt, msg, strerror(errval));
1937 NYD2_LEAVE;
1940 FL void
1941 n_alert(char const *format, ...)
1943 va_list ap;
1944 NYD2_ENTER;
1946 n_err(_("Alert: "));
1948 va_start(ap, format);
1949 n_verr(format, ap);
1950 va_end(ap);
1952 n_err("\n");
1953 NYD2_LEAVE;
1956 FL void
1957 n_panic(char const *format, ...)
1959 va_list ap;
1960 NYD2_ENTER;
1962 fprintf(stderr, _("Panic: "));
1964 va_start(ap, format);
1965 vfprintf(stderr, format, ap);
1966 va_end(ap);
1968 putc('\n', stderr);
1969 fflush(stderr);
1970 NYD2_LEAVE;
1971 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1974 #ifdef HAVE_ERRORS
1975 FL int
1976 c_errors(void *v)
1978 char **argv = v;
1979 struct err_node *enp;
1980 NYD_ENTER;
1982 if (*argv == NULL)
1983 goto jlist;
1984 if (argv[1] != NULL)
1985 goto jerr;
1986 if (!asccasecmp(*argv, "show"))
1987 goto jlist;
1988 if (!asccasecmp(*argv, "clear"))
1989 goto jclear;
1990 jerr:
1991 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1992 v = NULL;
1993 jleave:
1994 NYD_LEAVE;
1995 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1997 jlist: {
1998 FILE *fp;
1999 size_t i;
2001 if (_err_head == NULL) {
2002 fprintf(stderr, _("The error ring is empty\n"));
2003 goto jleave;
2006 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)
2007 ) == NULL) {
2008 fprintf(stderr, _("tmpfile"));
2009 v = NULL;
2010 goto jleave;
2013 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
2014 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2015 ++i, enp->en_str.l, enp->en_str.s);
2016 /* We don't know wether last string ended with NL; be simple */
2017 putc('\n', fp);
2019 page_or_print(fp, 0);
2020 Fclose(fp);
2022 /* FALLTHRU */
2024 jclear:
2025 _err_tail = NULL;
2026 _err_cnt = _err_cnt_noted = 0;
2027 while ((enp = _err_head) != NULL) {
2028 _err_head = enp->en_next;
2029 free(enp->en_str.s);
2030 free(enp);
2032 goto jleave;
2034 #endif /* HAVE_ERRORS */
2036 #ifndef HAVE_DEBUG
2037 FL void *
2038 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2040 void *rv;
2041 NYD2_ENTER;
2043 if (s == 0)
2044 s = 1;
2045 if ((rv = malloc(s)) == NULL)
2046 n_panic(_("no memory"));
2047 NYD2_LEAVE;
2048 return rv;
2051 FL void *
2052 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2054 void *rv;
2055 NYD2_ENTER;
2057 if (s == 0)
2058 s = 1;
2059 if (v == NULL)
2060 rv = smalloc(s);
2061 else if ((rv = realloc(v, s)) == NULL)
2062 n_panic(_("no memory"));
2063 NYD2_LEAVE;
2064 return rv;
2067 FL void *
2068 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2070 void *rv;
2071 NYD2_ENTER;
2073 if (size == 0)
2074 size = 1;
2075 if ((rv = calloc(nmemb, size)) == NULL)
2076 n_panic(_("no memory"));
2077 NYD2_LEAVE;
2078 return rv;
2081 #else /* !HAVE_DEBUG */
2082 CTA(sizeof(char) == sizeof(ui8_t));
2084 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2085 # define _HOPE_SET(C) \
2086 do {\
2087 union mem_ptr __xl, __xu;\
2088 struct mem_chunk *__xc;\
2089 __xl.p_p = (C).p_p;\
2090 __xc = __xl.p_c - 1;\
2091 __xu.p_p = __xc;\
2092 (C).p_cp += 8;\
2093 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2094 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2095 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2096 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2097 __xu.p_ui8p += __xc->mc_size - 8;\
2098 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2099 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2100 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2101 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2102 } while (0)
2103 # define _HOPE_GET_TRACE(C,BAD) \
2104 do {\
2105 (C).p_cp += 8;\
2106 _HOPE_GET(C, BAD);\
2107 (C).p_cp += 8;\
2108 } while(0)
2109 # define _HOPE_GET(C,BAD) \
2110 do {\
2111 union mem_ptr __xl, __xu;\
2112 struct mem_chunk *__xc;\
2113 ui32_t __i;\
2114 __xl.p_p = (C).p_p;\
2115 __xl.p_cp -= 8;\
2116 (C).p_cp = __xl.p_cp;\
2117 __xc = __xl.p_c - 1;\
2118 (BAD) = FAL0;\
2119 __i = 0;\
2120 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2121 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2122 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2123 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2124 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2125 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2126 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2127 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2128 if (__i != 0) {\
2129 (BAD) = TRU1;\
2130 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2131 __xl.p_p, __i, mdbg_file, mdbg_line);\
2133 __xu.p_p = __xc;\
2134 __xu.p_ui8p += __xc->mc_size - 8;\
2135 __i = 0;\
2136 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2137 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2138 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2139 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2140 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2141 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2142 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2143 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2144 if (__i != 0) {\
2145 (BAD) = TRU1;\
2146 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2147 __xl.p_p, __i, mdbg_file, mdbg_line);\
2149 if (BAD)\
2150 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2151 __xc->mc_file, __xc->mc_line);\
2152 } while (0)
2154 FL void *
2155 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2157 union mem_ptr p;
2158 NYD2_ENTER;
2160 if (s == 0)
2161 s = 1;
2162 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2163 n_panic("smalloc(): allocation too large: %s, line %d",
2164 mdbg_file, mdbg_line);
2165 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2167 if ((p.p_p = (malloc)(s)) == NULL)
2168 n_panic(_("no memory"));
2169 p.p_c->mc_prev = NULL;
2170 if ((p.p_c->mc_next = _mem_list) != NULL)
2171 _mem_list->mc_prev = p.p_c;
2172 p.p_c->mc_file = mdbg_file;
2173 p.p_c->mc_line = (ui16_t)mdbg_line;
2174 p.p_c->mc_isfree = FAL0;
2175 p.p_c->mc_size = (ui32_t)s;
2177 _mem_list = p.p_c++;
2178 _HOPE_SET(p);
2180 ++_mem_aall;
2181 ++_mem_acur;
2182 _mem_amax = MAX(_mem_amax, _mem_acur);
2183 _mem_mall += s;
2184 _mem_mcur += s;
2185 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2186 NYD2_LEAVE;
2187 return p.p_p;
2190 FL void *
2191 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2193 union mem_ptr p;
2194 bool_t isbad;
2195 NYD2_ENTER;
2197 if ((p.p_p = v) == NULL) {
2198 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2199 goto jleave;
2202 _HOPE_GET(p, isbad);
2203 --p.p_c;
2204 if (p.p_c->mc_isfree) {
2205 n_err("srealloc(): region freed! At %s, line %d\n"
2206 "\tLast seen: %s, line %" PRIu16 "\n",
2207 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2208 goto jforce;
2211 if (p.p_c == _mem_list)
2212 _mem_list = p.p_c->mc_next;
2213 else
2214 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2215 if (p.p_c->mc_next != NULL)
2216 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2218 --_mem_acur;
2219 _mem_mcur -= p.p_c->mc_size;
2220 jforce:
2221 if (s == 0)
2222 s = 1;
2223 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2224 n_panic("srealloc(): allocation too large: %s, line %d",
2225 mdbg_file, mdbg_line);
2226 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2228 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2229 n_panic(_("no memory"));
2230 p.p_c->mc_prev = NULL;
2231 if ((p.p_c->mc_next = _mem_list) != NULL)
2232 _mem_list->mc_prev = p.p_c;
2233 p.p_c->mc_file = mdbg_file;
2234 p.p_c->mc_line = (ui16_t)mdbg_line;
2235 p.p_c->mc_isfree = FAL0;
2236 p.p_c->mc_size = (ui32_t)s;
2237 _mem_list = p.p_c++;
2238 _HOPE_SET(p);
2240 ++_mem_aall;
2241 ++_mem_acur;
2242 _mem_amax = MAX(_mem_amax, _mem_acur);
2243 _mem_mall += s;
2244 _mem_mcur += s;
2245 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2246 jleave:
2247 NYD2_LEAVE;
2248 return p.p_p;
2251 FL void *
2252 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2254 union mem_ptr p;
2255 NYD2_ENTER;
2257 if (size == 0)
2258 size = 1;
2259 if (nmemb == 0)
2260 nmemb = 1;
2261 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2262 n_panic("scalloc(): allocation size too large: %s, line %d",
2263 mdbg_file, mdbg_line);
2264 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2265 n_panic("scalloc(): allocation count too large: %s, line %d",
2266 mdbg_file, mdbg_line);
2268 size *= nmemb;
2269 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2271 if ((p.p_p = (malloc)(size)) == NULL)
2272 n_panic(_("no memory"));
2273 memset(p.p_p, 0, size);
2274 p.p_c->mc_prev = NULL;
2275 if ((p.p_c->mc_next = _mem_list) != NULL)
2276 _mem_list->mc_prev = p.p_c;
2277 p.p_c->mc_file = mdbg_file;
2278 p.p_c->mc_line = (ui16_t)mdbg_line;
2279 p.p_c->mc_isfree = FAL0;
2280 p.p_c->mc_size = (ui32_t)size;
2281 _mem_list = p.p_c++;
2282 _HOPE_SET(p);
2284 ++_mem_aall;
2285 ++_mem_acur;
2286 _mem_amax = MAX(_mem_amax, _mem_acur);
2287 _mem_mall += size;
2288 _mem_mcur += size;
2289 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2290 NYD2_LEAVE;
2291 return p.p_p;
2294 FL void
2295 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2297 union mem_ptr p;
2298 bool_t isbad;
2299 NYD2_ENTER;
2301 if ((p.p_p = v) == NULL) {
2302 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2303 goto jleave;
2306 _HOPE_GET(p, isbad);
2307 --p.p_c;
2308 if (p.p_c->mc_isfree) {
2309 n_err("sfree(): double-free avoided at %s, line %d\n"
2310 "\tLast seen: %s, line %" PRIu16 "\n",
2311 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2312 goto jleave;
2315 if (p.p_c == _mem_list)
2316 _mem_list = p.p_c->mc_next;
2317 else
2318 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2319 if (p.p_c->mc_next != NULL)
2320 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2321 p.p_c->mc_isfree = TRU1;
2322 /* Trash contents (also see [21c05f8]) */
2323 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2325 --_mem_acur;
2326 _mem_mcur -= p.p_c->mc_size;
2328 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2329 p.p_c->mc_next = _mem_free;
2330 _mem_free = p.p_c;
2331 } else
2332 (free)(p.p_c);
2333 jleave:
2334 NYD2_LEAVE;
2337 FL void
2338 smemreset(void)
2340 union mem_ptr p;
2341 size_t c = 0, s = 0;
2342 NYD_ENTER;
2344 smemcheck();
2346 for (p.p_c = _mem_free; p.p_c != NULL;) {
2347 void *vp = p.p_c;
2348 ++c;
2349 s += p.p_c->mc_size;
2350 p.p_c = p.p_c->mc_next;
2351 (free)(vp);
2353 _mem_free = NULL;
2355 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2356 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2357 NYD_LEAVE;
2360 FL int
2361 c_smemtrace(void *v)
2363 /* For _HOPE_GET() */
2364 char const * const mdbg_file = "smemtrace()";
2365 int const mdbg_line = -1;
2366 FILE *fp;
2367 union mem_ptr p, xp;
2368 bool_t isbad;
2369 size_t lines;
2370 NYD_ENTER;
2372 v = (void*)0x1;
2373 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2374 NULL) {
2375 n_perr("tmpfile", 0);
2376 goto jleave;
2379 fprintf(fp, "Memory statistics:\n"
2380 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2381 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2382 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2384 fprintf(fp, "Currently allocated memory chunks:\n");
2385 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2386 ++lines, p.p_c = p.p_c->mc_next) {
2387 xp = p;
2388 ++xp.p_c;
2389 _HOPE_GET_TRACE(xp, isbad);
2390 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2391 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2392 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2393 p.p_c->mc_line);
2396 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2397 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2398 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2399 xp = p;
2400 ++xp.p_c;
2401 _HOPE_GET_TRACE(xp, isbad);
2402 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2403 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2404 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2405 p.p_c->mc_file, p.p_c->mc_line);
2409 page_or_print(fp, lines);
2410 Fclose(fp);
2411 v = NULL;
2412 jleave:
2413 NYD_LEAVE;
2414 return (v != NULL);
2417 # ifdef HAVE_DEVEL
2418 FL bool_t
2419 _smemcheck(char const *mdbg_file, int mdbg_line)
2421 union mem_ptr p, xp;
2422 bool_t anybad = FAL0, isbad;
2423 size_t lines;
2424 NYD_ENTER;
2426 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2427 ++lines, p.p_c = p.p_c->mc_next) {
2428 xp = p;
2429 ++xp.p_c;
2430 _HOPE_GET_TRACE(xp, isbad);
2431 if (isbad) {
2432 anybad = TRU1;
2433 n_err(
2434 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2435 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2436 p.p_c->mc_file, p.p_c->mc_line);
2440 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2441 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2442 xp = p;
2443 ++xp.p_c;
2444 _HOPE_GET_TRACE(xp, isbad);
2445 if (isbad) {
2446 anybad = TRU1;
2447 n_err(
2448 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2449 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2450 p.p_c->mc_file, p.p_c->mc_line);
2454 NYD_LEAVE;
2455 return anybad;
2457 # endif /* HAVE_DEVEL */
2459 # undef _HOPE_SIZE
2460 # undef _HOPE_SET
2461 # undef _HOPE_GET_TRACE
2462 # undef _HOPE_GET
2463 #endif /* HAVE_DEBUG */
2465 /* s-it-mode */