*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / auxlily.c
bloba5e322d14693108f3244a99570e401987829e652
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() && (pstate & PS_STARTED) &&
605 (cp = ok_vlook(crt)) != NULL) {
606 char *eptr;
607 union {sl_i sli; size_t rows;} u;
609 u.sli = strtol(cp, &eptr, 0);
610 u.rows = (*cp != '\0' && *eptr == '\0')
611 ? (size_t)u.sli : (size_t)scrnheight;
613 if (u.rows > 0 && lines == 0) {
614 while ((c = getc(fp)) != EOF)
615 if (c == '\n' && ++lines >= u.rows)
616 break;
617 really_rewind(fp);
620 if (lines >= u.rows) {
621 run_command(get_pager(NULL), 0, fileno(fp), -1, NULL, NULL, NULL);
622 goto jleave;
626 while ((c = getc(fp)) != EOF)
627 putchar(c);
628 jleave:
629 NYD_LEAVE;
632 FL enum protocol
633 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
635 struct stat st;
636 char const *cp;
637 char *np;
638 size_t sz;
639 enum protocol rv = PROTO_UNKNOWN;
640 NYD_ENTER;
642 temporary_protocol_ext = NULL;
644 if (name[0] == '%' && name[1] == ':')
645 name += 2;
646 for (cp = name; *cp && *cp != ':'; cp++)
647 if (!alnumchar(*cp))
648 goto jfile;
650 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
651 if (!strncmp(name, "pop3://", 7)) {
652 #ifdef HAVE_POP3
653 rv = PROTO_POP3;
654 #else
655 n_err(_("No POP3 support compiled in\n"));
656 #endif
657 } else if (!strncmp(name, "pop3s://", 8)) {
658 #if defined HAVE_POP3 && defined HAVE_SSL
659 rv = PROTO_POP3;
660 #else
661 # ifndef HAVE_POP3
662 n_err(_("No POP3 support compiled in\n"));
663 # endif
664 # ifndef HAVE_SSL
665 n_err(_("No SSL support compiled in\n"));
666 # endif
667 #endif
668 } else if (!strncmp(name, "imap://", 7)) {
669 #ifdef HAVE_IMAP
670 rv = PROTO_IMAP;
671 #else
672 fprintf(stderr, _("No IMAP support compiled in.\n"));
673 #endif
674 } else if (!strncmp(name, "imaps://", 8)) {
675 #if defined HAVE_IMAP && defined HAVE_SSL
676 rv = PROTO_IMAP;
677 #else
678 # ifndef HAVE_IMAP
679 fprintf(stderr, _("No IMAP support compiled in.\n"));
680 # endif
681 # ifndef HAVE_SSL
682 fprintf(stderr, _("No SSL support compiled in.\n"));
683 # endif
684 #endif
686 goto jleave;
689 /* TODO This is the de facto maildir code and thus belongs into there!
690 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
691 * TODO or (more likely) in addition to *newfolders*) */
692 jfile:
693 rv = PROTO_FILE;
694 np = ac_alloc((sz = strlen(name)) + 4 +1);
695 memcpy(np, name, sz + 1);
696 if (!stat(name, &st)) {
697 if (S_ISDIR(st.st_mode) &&
698 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
699 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
700 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
701 rv = PROTO_MAILDIR;
702 } else {
703 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
704 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
705 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
706 temporary_protocol_ext = cp;
707 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
708 rv = PROTO_MAILDIR;
710 ac_free(np);
711 jleave:
712 NYD_LEAVE;
713 return rv;
716 FL ui32_t
717 torek_hash(char const *name)
719 /* Chris Torek's hash.
720 * NOTE: need to change *at least* create-okey-map.pl when changing the
721 * algorithm!! */
722 ui32_t h = 0;
723 NYD_ENTER;
725 while (*name != '\0') {
726 h *= 33;
727 h += *name++;
729 NYD_LEAVE;
730 return h;
733 FL unsigned
734 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
736 unsigned h = 0, g;
737 NYD_ENTER;
739 cp--;
740 while (*++cp) {
741 h = (h << 4 & 0xffffffff) + (*cp&0377);
742 if ((g = h & 0xf0000000) != 0) {
743 h = h ^ g >> 24;
744 h = h ^ g;
747 NYD_LEAVE;
748 return h;
751 FL ui32_t
752 nextprime(ui32_t n)
754 static ui32_t const primes[] = {
755 5, 11, 23, 47, 97, 157, 283,
756 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
757 131071, 262139, 524287, 1048573, 2097143, 4194301,
758 8388593, 16777213, 33554393, 67108859, 134217689,
759 268435399, 536870909, 1073741789, 2147483647
762 ui32_t i, mprime;
763 NYD_ENTER;
765 i = (n < primes[NELEM(primes) / 4] ? 0
766 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
767 : NELEM(primes) / 2));
769 if ((mprime = primes[i]) > n)
770 break;
771 while (++i < NELEM(primes));
772 if (i == NELEM(primes) && mprime < n)
773 mprime = n;
774 NYD_LEAVE;
775 return mprime;
778 FL int
779 expand_shell_escape(char const **s, bool_t use_nail_extensions)
781 char const *xs;
782 int c, n;
783 NYD2_ENTER;
785 xs = *s;
787 if ((c = *xs & 0xFF) == '\0')
788 goto jleave;
789 ++xs;
790 if (c != '\\')
791 goto jleave;
793 switch ((c = *xs & 0xFF)) {
794 case '\\': break;
795 case 'a': c = '\a'; break;
796 case 'b': c = '\b'; break;
797 case 'c': c = PROMPT_STOP; break;
798 case 'f': c = '\f'; break;
799 case 'n': c = '\n'; break;
800 case 'r': c = '\r'; break;
801 case 't': c = '\t'; break;
802 case 'v': c = '\v'; break;
803 case '0':
804 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
805 c <<= 3;
806 c |= *xs - '0';
808 goto jleave;
809 /* S-nail extension for nice (get)prompt(()) support */
810 case '&':
811 case '?':
812 case '$':
813 case '@':
814 if (use_nail_extensions) {
815 switch (c) {
816 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
817 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
818 case '$': c = PROMPT_DOLLAR; break;
819 case '@': c = PROMPT_AT; break;
821 break;
823 /* FALLTHRU */
824 case '\0':
825 /* A sole <backslash> at EOS is treated as-is! */
826 /* FALLTHRU */
827 default:
828 c = '\\';
829 goto jleave;
831 ++xs;
832 jleave:
833 *s = xs;
834 NYD2_LEAVE;
835 return c;
838 FL char *
839 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
841 static char buf[PROMPT_BUFFER_SIZE];
843 char *cp;
844 char const *ccp_base, *ccp;
845 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
846 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
847 NYD_ENTER;
849 /* No other place to place this */
850 #ifdef HAVE_ERRORS
851 if (options & OPT_INTERACTIVE) {
852 if (!(pstate & PS_ERRORS_NOTED) && _err_head != NULL) {
853 pstate |= PS_ERRORS_NOTED;
854 fprintf(stderr, _("There are new messages in the error message ring "
855 "(denoted by \"#ERR#\")\n"
856 " The `errors' command manages this message ring\n"));
859 if ((trigger = (_err_cnt_noted != _err_cnt)))
860 _err_cnt_noted = _err_cnt;
861 } else
862 trigger = FAL0;
863 #endif
865 cp = buf;
866 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
867 #ifdef HAVE_ERRORS
868 if (trigger)
869 ccp_base = "";
870 else
871 #endif
872 goto jleave;
874 #ifdef HAVE_ERRORS
875 if (trigger)
876 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
877 #endif
878 NATCH_CHAR( cclen_base = strlen(ccp_base); )
880 dfmaxlen = 0; /* keep CC happy */
881 trigger = FAL0;
882 jredo:
883 ccp = ccp_base;
884 NATCH_CHAR( cclen = cclen_base; )
885 maxlen = sizeof(buf) -1;
887 for (;;) {
888 size_t l;
889 int c;
891 if (maxlen == 0)
892 goto jleave;
893 #ifdef HAVE_NATCH_CHAR
894 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
895 if (c <= 0) {
896 mblen(NULL, 0);
897 if (c < 0) {
898 *buf = '?';
899 cp = buf + 1;
900 goto jleave;
902 break;
903 } else if ((l = c) > 1) {
904 if (trigger) {
905 memcpy(cp, ccp, l);
906 cp += l;
908 ccp += l;
909 maxlen -= l;
910 continue;
911 } else
912 #endif
913 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
914 if (trigger)
915 *cp++ = (char)c;
916 --maxlen;
917 continue;
919 if (c == 0 || c == PROMPT_STOP)
920 break;
922 if (trigger) {
923 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
924 if (a == NULL)
925 a = "";
926 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
927 cp += l;
928 maxlen -= l;
929 dfmaxlen -= l;
934 if (!trigger) {
935 trigger = TRU1;
936 dfmaxlen = maxlen;
937 goto jredo;
939 jleave:
940 *cp = '\0';
941 NYD_LEAVE;
942 return buf;
945 FL char *
946 nodename(int mayoverride)
948 static char *sys_hostname, *hostname; /* XXX free-at-exit */
950 struct utsname ut;
951 char *hn;
952 #ifdef HAVE_SOCKETS
953 # ifdef HAVE_GETADDRINFO
954 struct addrinfo hints, *res;
955 # else
956 struct hostent *hent;
957 # endif
958 #endif
959 NYD_ENTER;
961 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
963 } else if ((hn = sys_hostname) == NULL) {
964 uname(&ut);
965 hn = ut.nodename;
966 #ifdef HAVE_SOCKETS
967 # ifdef HAVE_GETADDRINFO
968 memset(&hints, 0, sizeof hints);
969 hints.ai_family = AF_UNSPEC;
970 hints.ai_flags = AI_CANONNAME;
971 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
972 if (res->ai_canonname != NULL) {
973 size_t l = strlen(res->ai_canonname) +1;
975 hn = ac_alloc(l);
976 memcpy(hn, res->ai_canonname, l);
978 freeaddrinfo(res);
980 # else
981 hent = gethostbyname(hn);
982 if (hent != NULL)
983 hn = hent->h_name;
984 # endif
985 #endif
986 sys_hostname = sstrdup(hn);
987 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
988 if (hn != ut.nodename)
989 ac_free(hn);
990 #endif
991 hn = sys_hostname;
994 if (hostname != NULL && hostname != sys_hostname)
995 free(hostname);
996 hostname = sstrdup(hn);
997 NYD_LEAVE;
998 return hostname;
1001 FL char *
1002 getrandstring(size_t length)
1004 struct str b64;
1005 char *data;
1006 size_t i;
1007 NYD_ENTER;
1009 #ifndef HAVE_POSIX_RANDOM
1010 if (_rand == NULL)
1011 _rand_init();
1012 #endif
1014 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1015 * with PAD stripped is still longer than what the user requests, easy way */
1016 data = ac_alloc(i = length + 3);
1018 #ifndef HAVE_POSIX_RANDOM
1019 while (i-- > 0)
1020 data[i] = (char)_rand_get8();
1021 #else
1022 { char *cp = data;
1024 while (i > 0) {
1025 union {ui32_t i4; char c[4];} r;
1026 size_t j;
1028 r.i4 = (ui32_t)arc4random();
1029 switch ((j = i & 3)) {
1030 case 0: cp[3] = r.c[3]; j = 4;
1031 case 3: cp[2] = r.c[2];
1032 case 2: cp[1] = r.c[1];
1033 default: cp[0] = r.c[0]; break;
1035 cp += j;
1036 i -= j;
1039 #endif
1041 b64_encode_buf(&b64, data, length + 3,
1042 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1043 ac_free(data);
1045 assert(b64.l >= length);
1046 b64.s[length] = '\0';
1047 NYD_LEAVE;
1048 return b64.s;
1051 FL enum okay
1052 makedir(char const *name)
1054 struct stat st;
1055 enum okay rv = STOP;
1056 NYD_ENTER;
1058 if (!mkdir(name, 0700))
1059 rv = OKAY;
1060 else {
1061 int e = errno;
1062 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1063 S_ISDIR(st.st_mode))
1064 rv = OKAY;
1066 NYD_LEAVE;
1067 return rv;
1070 #ifdef HAVE_FCHDIR
1071 FL enum okay
1072 cwget(struct cw *cw)
1074 enum okay rv = STOP;
1075 NYD_ENTER;
1077 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1078 goto jleave;
1079 if (fchdir(cw->cw_fd) == -1) {
1080 close(cw->cw_fd);
1081 goto jleave;
1083 rv = OKAY;
1084 jleave:
1085 NYD_LEAVE;
1086 return rv;
1089 FL enum okay
1090 cwret(struct cw *cw)
1092 enum okay rv = STOP;
1093 NYD_ENTER;
1095 if (!fchdir(cw->cw_fd))
1096 rv = OKAY;
1097 NYD_LEAVE;
1098 return rv;
1101 FL void
1102 cwrelse(struct cw *cw)
1104 NYD_ENTER;
1105 close(cw->cw_fd);
1106 NYD_LEAVE;
1109 #else /* !HAVE_FCHDIR */
1110 FL enum okay
1111 cwget(struct cw *cw)
1113 enum okay rv = STOP;
1114 NYD_ENTER;
1116 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1117 rv = OKAY;
1118 NYD_LEAVE;
1119 return rv;
1122 FL enum okay
1123 cwret(struct cw *cw)
1125 enum okay rv = STOP;
1126 NYD_ENTER;
1128 if (!chdir(cw->cw_wd))
1129 rv = OKAY;
1130 NYD_LEAVE;
1131 return rv;
1134 FL void
1135 cwrelse(struct cw *cw)
1137 NYD_ENTER;
1138 UNUSED(cw);
1139 NYD_LEAVE;
1141 #endif /* !HAVE_FCHDIR */
1143 FL size_t
1144 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1146 size_t rv;
1147 NYD_ENTER;
1149 #ifdef HAVE_NATCH_CHAR
1150 maxlen = MIN(maxlen, blen);
1151 for (rv = 0; maxlen > 0;) {
1152 int ml = mblen(buf, maxlen);
1153 if (ml <= 0) {
1154 mblen(NULL, 0);
1155 break;
1157 buf += ml;
1158 rv += ml;
1159 maxlen -= ml;
1161 #else
1162 rv = MIN(blen, maxlen);
1163 #endif
1164 NYD_LEAVE;
1165 return rv;
1168 FL size_t
1169 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1171 NATCH_CHAR( struct bidi_info bi; )
1172 size_t rv NATCH_CHAR( COMMA i );
1173 NYD_ENTER;
1175 rv = 0;
1176 if (maxlen-- == 0)
1177 goto j_leave;
1179 #ifdef HAVE_NATCH_CHAR
1180 bidi_info_create(&bi);
1181 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1182 bi.bi_end.l = 0;
1183 goto jnobidi;
1186 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1187 maxlen -= i;
1188 else
1189 goto jleave;
1191 if ((i = bi.bi_start.l) > 0) {
1192 memcpy(store, bi.bi_start.s, i);
1193 store += i;
1194 rv += i;
1197 jnobidi:
1198 while (maxlen > 0) {
1199 int ml = mblen(buf, blen);
1200 if (ml <= 0) {
1201 mblen(NULL, 0);
1202 break;
1204 if (UICMP(z, maxlen, <, ml))
1205 break;
1206 if (ml == 1)
1207 *store = *buf;
1208 else
1209 memcpy(store, buf, ml);
1210 store += ml;
1211 buf += ml;
1212 rv += ml;
1213 maxlen -= ml;
1216 if ((i = bi.bi_end.l) > 0) {
1217 memcpy(store, bi.bi_end.s, i);
1218 store += i;
1219 rv += i;
1221 jleave:
1222 *store = '\0';
1224 #else
1225 rv = MIN(blen, maxlen);
1226 memcpy(store, buf, rv);
1227 store[rv] = '\0';
1228 #endif
1229 j_leave:
1230 NYD_LEAVE;
1231 return rv;
1234 FL char *
1235 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1237 NATCH_CHAR( struct bidi_info bi; )
1238 int col_orig = col, n, sz;
1239 bool_t isbidi, isuni, istab, isrepl;
1240 char *nb, *np;
1241 NYD_ENTER;
1243 /* Bidi only on request and when there is 8-bit data */
1244 isbidi = isuni = FAL0;
1245 #ifdef HAVE_NATCH_CHAR
1246 isuni = ((options & OPT_UNICODE) != 0);
1247 bidi_info_create(&bi);
1248 if (bi.bi_start.l == 0)
1249 goto jnobidi;
1250 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1251 goto jnobidi;
1253 if ((size_t)col >= bi.bi_pad)
1254 col -= bi.bi_pad;
1255 else
1256 col = 0;
1257 jnobidi:
1258 #endif
1260 np = nb = salloc(mb_cur_max * strlen(cp) +
1261 ((fill ? col : 0)
1262 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1263 +1));
1265 #ifdef HAVE_NATCH_CHAR
1266 if (isbidi) {
1267 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1268 np += bi.bi_start.l;
1270 #endif
1272 while (*cp != '\0') {
1273 istab = FAL0;
1274 #ifdef HAVE_C90AMEND1
1275 if (mb_cur_max > 1) {
1276 wchar_t wc;
1278 n = 1;
1279 isrepl = TRU1;
1280 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1281 sz = 1;
1282 else if (wc == L'\t') {
1283 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1284 isrepl = FAL0;
1285 istab = TRU1;
1286 } else if (iswprint(wc)) {
1287 # ifndef HAVE_WCWIDTH
1288 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1289 # else
1290 if ((n = wcwidth(wc)) == -1)
1291 n = 1;
1292 else
1293 # endif
1294 isrepl = FAL0;
1296 } else
1297 #endif
1299 n = sz = 1;
1300 istab = (*cp == '\t');
1301 isrepl = !(istab || isprint((uc_i)*cp));
1304 if (n > col)
1305 break;
1306 col -= n;
1308 if (isrepl) {
1309 if (isuni) {
1310 np[0] = (char)0xEFu;
1311 np[1] = (char)0xBFu;
1312 np[2] = (char)0xBDu;
1313 np += 3;
1314 } else
1315 *np++ = '?';
1316 cp += sz;
1317 } else if (istab || (sz == 1 && spacechar(*cp))) {
1318 *np++ = ' ';
1319 ++cp;
1320 } else
1321 while (sz--)
1322 *np++ = *cp++;
1325 if (fill && col != 0) {
1326 if (fill > 0) {
1327 memmove(nb + col, nb, PTR2SIZE(np - nb));
1328 memset(nb, ' ', col);
1329 } else
1330 memset(np, ' ', col);
1331 np += col;
1332 col = 0;
1335 #ifdef HAVE_NATCH_CHAR
1336 if (isbidi) {
1337 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1338 np += bi.bi_end.l;
1340 #endif
1342 *np = '\0';
1343 if (cols_decr_used_or_null != NULL)
1344 *cols_decr_used_or_null -= col_orig - col;
1345 NYD_LEAVE;
1346 return nb;
1349 FL void
1350 makeprint(struct str const *in, struct str *out)
1352 static int print_all_chars = -1;
1354 char const *inp, *maxp;
1355 char *outp;
1356 DBG( size_t msz; )
1357 NYD_ENTER;
1359 if (print_all_chars == -1)
1360 print_all_chars = ok_blook(print_all_chars);
1362 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1363 inp = in->s;
1364 maxp = inp + in->l;
1366 if (print_all_chars) {
1367 out->l = in->l;
1368 memcpy(outp, inp, out->l);
1369 goto jleave;
1372 #ifdef HAVE_NATCH_CHAR
1373 if (mb_cur_max > 1) {
1374 char mbb[MB_LEN_MAX + 1];
1375 wchar_t wc;
1376 int i, n;
1377 bool_t isuni = ((options & OPT_UNICODE) != 0);
1379 out->l = 0;
1380 while (inp < maxp) {
1381 if (*inp & 0200)
1382 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1383 else {
1384 wc = *inp;
1385 n = 1;
1387 if (n == -1) {
1388 /* FIXME Why mbtowc() resetting here?
1389 * FIXME what about ISO 2022-JP plus -- those
1390 * FIXME will loose shifts, then!
1391 * FIXME THUS - we'd need special "known points"
1392 * FIXME to do so - say, after a newline!!
1393 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1394 mbtowc(&wc, NULL, mb_cur_max);
1395 wc = isuni ? 0xFFFD : '?';
1396 n = 1;
1397 } else if (n == 0)
1398 n = 1;
1399 inp += n;
1400 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1401 wc != '\t') {
1402 if ((wc & ~(wchar_t)037) == 0)
1403 wc = isuni ? 0x2400 | wc : '?';
1404 else if (wc == 0177)
1405 wc = isuni ? 0x2421 : '?';
1406 else
1407 wc = isuni ? 0x2426 : '?';
1408 }else if(isuni){ /* TODO ctext */
1409 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1410 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1411 continue;
1412 /* And some zero-width messes */
1413 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1414 continue;
1415 /* Oh about the ISO C wide character interfaces, baby! */
1416 if(wc == 0xFEFF)
1417 continue;
1419 if ((n = wctomb(mbb, wc)) <= 0)
1420 continue;
1421 out->l += n;
1422 assert(out->l < msz);
1423 for (i = 0; i < n; ++i)
1424 *outp++ = mbb[i];
1426 } else
1427 #endif /* NATCH_CHAR */
1429 int c;
1430 while (inp < maxp) {
1431 c = *inp++ & 0377;
1432 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1433 c = '?';
1434 *outp++ = c;
1436 out->l = in->l;
1438 jleave:
1439 out->s[out->l] = '\0';
1440 NYD_LEAVE;
1443 FL size_t
1444 delctrl(char *cp, size_t len)
1446 size_t x, y;
1447 NYD_ENTER;
1449 for (x = y = 0; x < len; ++x)
1450 if (!cntrlchar(cp[x]))
1451 cp[y++] = cp[x];
1452 cp[y] = '\0';
1453 NYD_LEAVE;
1454 return y;
1457 FL char *
1458 prstr(char const *s)
1460 struct str in, out;
1461 char *rp;
1462 NYD_ENTER;
1464 in.s = UNCONST(s);
1465 in.l = strlen(s);
1466 makeprint(&in, &out);
1467 rp = savestrbuf(out.s, out.l);
1468 free(out.s);
1469 NYD_LEAVE;
1470 return rp;
1473 FL int
1474 prout(char const *s, size_t sz, FILE *fp)
1476 struct str in, out;
1477 int n;
1478 NYD_ENTER;
1480 in.s = UNCONST(s);
1481 in.l = sz;
1482 makeprint(&in, &out);
1483 n = fwrite(out.s, 1, out.l, fp);
1484 free(out.s);
1485 NYD_LEAVE;
1486 return n;
1489 FL size_t
1490 putuc(int u, int c, FILE *fp)
1492 size_t rv;
1493 NYD_ENTER;
1494 UNUSED(u);
1496 #ifdef HAVE_NATCH_CHAR
1497 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1498 char mbb[MB_LEN_MAX];
1499 int i, n;
1501 if ((n = wctomb(mbb, u)) > 0) {
1502 rv = wcwidth(u);
1503 for (i = 0; i < n; ++i)
1504 if (putc(mbb[i] & 0377, fp) == EOF) {
1505 rv = 0;
1506 break;
1508 } else if (n == 0)
1509 rv = (putc('\0', fp) != EOF);
1510 else
1511 rv = 0;
1512 } else
1513 #endif
1514 rv = (putc(c, fp) != EOF);
1515 NYD_LEAVE;
1516 return rv;
1519 FL bool_t
1520 bidi_info_needed(char const *bdat, size_t blen)
1522 bool_t rv = FAL0;
1523 NYD_ENTER;
1525 #ifdef HAVE_NATCH_CHAR
1526 if (options & OPT_UNICODE)
1527 while (blen > 0) {
1528 /* TODO Checking for BIDI character: use S-CText fromutf8
1529 * TODO plus isrighttoleft (or whatever there will be)! */
1530 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1531 if (c == UI32_MAX)
1532 break;
1534 if (c <= 0x05BE)
1535 continue;
1537 /* (Very very fuzzy, awaiting S-CText for good) */
1538 if ((c >= 0x05BE && c <= 0x08E3) ||
1539 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1540 (c >= 0xFE70 && c <= 0xFEFC) ||
1541 (c >= 0x10800 && c <= 0x10C48) ||
1542 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1543 rv = TRU1;
1544 break;
1547 #endif /* HAVE_NATCH_CHAR */
1548 NYD_LEAVE;
1549 return rv;
1552 FL void
1553 bidi_info_create(struct bidi_info *bip)
1555 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1556 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1557 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1558 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1559 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1560 NATCH_CHAR( char const *hb; )
1561 NYD_ENTER;
1563 memset(bip, 0, sizeof *bip);
1564 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1566 #ifdef HAVE_NATCH_CHAR
1567 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1568 switch (*hb) {
1569 case '3':
1570 bip->bi_pad = 2;
1571 /* FALLTHRU */
1572 case '2':
1573 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1574 break;
1575 case '1':
1576 bip->bi_pad = 2;
1577 /* FALLTHRU */
1578 default:
1579 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1580 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1581 break;
1583 bip->bi_start.l = bip->bi_end.l = 3;
1585 #endif
1586 NYD_LEAVE;
1589 #ifdef HAVE_COLOUR
1590 FL void
1591 colour_table_create(bool_t pager_used)
1593 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1594 size_t i;
1595 struct colour_table *ct;
1596 NYD_ENTER;
1598 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1599 goto jleave;
1600 else {
1601 char *term, *okterms;
1603 if ((term = env_vlook("TERM", FAL0)) == NULL)
1604 goto jleave;
1605 /* terminfo rocks: if we find "color", assume it's right */
1606 if (strstr(term, "color") != NULL)
1607 goto jok;
1608 if ((okterms = ok_vlook(colour_terms)) == NULL)
1609 okterms = UNCONST(COLOUR_TERMS);
1610 okterms = savestr(okterms);
1612 i = strlen(term);
1613 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1614 if (!strncmp(u.cp, term, i))
1615 goto jok;
1616 goto jleave;
1619 jok:
1620 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1621 { static struct {
1622 enum okeys okey;
1623 enum colourspec cspec;
1624 char const *defval;
1625 } const map[] = {
1626 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1627 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1628 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1629 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1630 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1633 for (i = 0; i < NELEM(map); ++i) {
1634 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1635 u.ccp = map[i].defval;
1636 u.cp = _colour_iso6429(u.ccp);
1637 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1638 ct->ct_csinfo[map[i].cspec].s = u.cp;
1641 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1642 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1644 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1645 u.ccp = COLOUR_USER_HEADERS;
1646 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1647 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1648 jleave:
1649 NYD_LEAVE;
1652 FL void
1653 colour_put(FILE *fp, enum colourspec cs)
1655 NYD_ENTER;
1656 if (colour_table != NULL) {
1657 struct str const *cp = colour_get(cs);
1659 fwrite(cp->s, cp->l, 1, fp);
1661 NYD_LEAVE;
1664 FL void
1665 colour_put_header(FILE *fp, char const *name)
1667 enum colourspec cs = COLOURSPEC_HEADER;
1668 struct str const *uheads;
1669 char *cp, *cp_base, *x;
1670 size_t namelen;
1671 NYD_ENTER;
1673 if (colour_table == NULL)
1674 goto j_leave;
1675 /* Normal header colours if there are no user headers */
1676 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1677 if (uheads->s == NULL)
1678 goto jleave;
1680 /* Iterate over all entries in the *colour-user-headers* list */
1681 cp = ac_alloc(uheads->l +1);
1682 memcpy(cp, uheads->s, uheads->l +1);
1683 cp_base = cp;
1684 namelen = strlen(name);
1685 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1686 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1687 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1688 cs = COLOURSPEC_UHEADER;
1689 break;
1692 ac_free(cp_base);
1693 jleave:
1694 colour_put(fp, cs);
1695 j_leave:
1696 NYD_LEAVE;
1699 FL void
1700 colour_reset(FILE *fp)
1702 NYD_ENTER;
1703 if (colour_table != NULL)
1704 fwrite("\033[0m", 4, 1, fp);
1705 NYD_LEAVE;
1708 FL struct str const *
1709 colour_get(enum colourspec cs)
1711 struct str const *rv = NULL;
1712 NYD_ENTER;
1714 if (colour_table != NULL)
1715 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1716 rv = NULL;
1717 NYD_LEAVE;
1718 return rv;
1720 #endif /* HAVE_COLOUR */
1722 FL si8_t
1723 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1725 char *dat, *eptr;
1726 sl_i sli;
1727 si8_t rv;
1728 NYD_ENTER;
1730 assert(inlen == 0 || inbuf != NULL);
1732 if (inlen == UIZ_MAX)
1733 inlen = strlen(inbuf);
1735 if (inlen == 0)
1736 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1737 else {
1738 if ((inlen == 1 && *inbuf == '1') ||
1739 !ascncasecmp(inbuf, "true", inlen) ||
1740 !ascncasecmp(inbuf, "yes", inlen) ||
1741 !ascncasecmp(inbuf, "on", inlen))
1742 rv = 1;
1743 else if ((inlen == 1 && *inbuf == '0') ||
1744 !ascncasecmp(inbuf, "false", inlen) ||
1745 !ascncasecmp(inbuf, "no", inlen) ||
1746 !ascncasecmp(inbuf, "off", inlen))
1747 rv = 0;
1748 else {
1749 dat = ac_alloc(inlen +1);
1750 memcpy(dat, inbuf, inlen);
1751 dat[inlen] = '\0';
1753 sli = strtol(dat, &eptr, 0);
1754 if (*dat != '\0' && *eptr == '\0')
1755 rv = (sli != 0);
1756 else
1757 rv = -1;
1759 ac_free(dat);
1762 NYD_LEAVE;
1763 return rv;
1766 FL si8_t
1767 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1769 si8_t rv;
1770 NYD_ENTER;
1772 assert(inlen == 0 || inbuf != NULL);
1774 if (inlen == UIZ_MAX)
1775 inlen = strlen(inbuf);
1777 if (inlen == 0)
1778 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1779 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1780 !ascncasecmp(inbuf, "ask-", 4) &&
1781 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1782 (options & OPT_INTERACTIVE)) {
1783 if (prompt != NULL)
1784 fputs(prompt, stdout);
1785 rv = getapproval(NULL, rv);
1787 NYD_LEAVE;
1788 return rv;
1791 FL time_t
1792 n_time_epoch(void)
1794 #ifdef HAVE_CLOCK_GETTIME
1795 struct timespec ts;
1796 #elif defined HAVE_GETTIMEOFDAY
1797 struct timeval ts;
1798 #endif
1799 time_t rv;
1800 NYD2_ENTER;
1802 #ifdef HAVE_CLOCK_GETTIME
1803 clock_gettime(CLOCK_REALTIME, &ts);
1804 rv = (time_t)ts.tv_sec;
1805 #elif defined HAVE_GETTIMEOFDAY
1806 gettimeofday(&ts, NULL);
1807 rv = (time_t)ts.tv_sec;
1808 #else
1809 rv = time(NULL);
1810 #endif
1811 NYD2_LEAVE;
1812 return rv;
1815 FL void
1816 time_current_update(struct time_current *tc, bool_t full_update)
1818 NYD_ENTER;
1819 tc->tc_time = n_time_epoch();
1820 if (full_update) {
1821 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1822 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1823 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1825 NYD_LEAVE;
1828 FL void
1829 n_err(char const *format, ...)
1831 va_list ap;
1832 NYD2_ENTER;
1834 va_start(ap, format);
1835 #ifdef HAVE_ERRORS
1836 if (options & OPT_INTERACTIVE)
1837 n_verr(format, ap);
1838 else
1839 #endif
1841 vfprintf(stderr, format, ap);
1842 if (strchr(format, '\n') != NULL) /* TODO */
1843 fflush(stderr);
1845 va_end(ap);
1846 NYD2_LEAVE;
1849 FL void
1850 n_verr(char const *format, va_list ap)
1852 /* Check use cases of PS_ERRORS_NOTED, too! */
1853 #ifdef HAVE_ERRORS
1854 char buf[LINESIZE], *xbuf;
1855 int lmax, l;
1856 struct err_node *enp;
1858 LCTA(ERRORS_MAX > 3);
1859 #endif
1860 NYD2_ENTER;
1862 #ifdef HAVE_ERRORS
1863 if (!(options & OPT_INTERACTIVE))
1864 #endif
1866 vfprintf(stderr, format, ap);
1867 goto jleave;
1870 #ifdef HAVE_ERRORS
1871 xbuf = buf;
1872 lmax = sizeof buf;
1873 jredo:
1874 l = vsnprintf(xbuf, lmax, format, ap);
1875 if (l <= 0)
1876 goto jleave;
1877 if (UICMP(z, l, >=, lmax)) {
1878 /* FIXME Cannot reuse va_list
1879 lmax = ++l;
1880 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1881 goto jredo;
1885 fwrite(xbuf, 1, l, stderr);
1887 /* Link it into the `errors' message ring */
1888 if ((enp = _err_tail) == NULL) {
1889 jcreat:
1890 enp = scalloc(1, sizeof *enp);
1891 if (_err_tail != NULL)
1892 _err_tail->en_next = enp;
1893 else
1894 _err_head = enp;
1895 _err_tail = enp;
1896 ++_err_cnt;
1897 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
1898 if (_err_cnt < ERRORS_MAX)
1899 goto jcreat;
1901 _err_head = (enp = _err_head)->en_next;
1902 _err_tail->en_next = enp;
1903 _err_tail = enp;
1904 free(enp->en_str.s);
1905 memset(enp, 0, sizeof *enp);
1908 n_str_add_buf(&enp->en_str, xbuf, l);
1910 if (xbuf != buf)
1911 free(xbuf);
1912 #endif /* HAVE_ERRORS */
1914 jleave:
1915 if (strchr(format, '\n') != NULL) /* TODO */
1916 fflush(stderr);
1917 NYD2_LEAVE;
1920 FL void
1921 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
1923 va_list ap;
1924 NYD_X;
1926 va_start(ap, format);
1927 vfprintf(stderr, format, ap);
1928 va_end(ap);
1929 fflush(stderr);
1932 FL void
1933 n_perr(char const *msg, int errval)
1935 char const *fmt;
1936 NYD2_ENTER;
1938 if (msg == NULL) {
1939 fmt = "%s%s\n";
1940 msg = "";
1941 } else
1942 fmt = "%s: %s\n";
1944 if (errval == 0)
1945 errval = errno;
1947 n_err(fmt, msg, strerror(errval));
1948 NYD2_LEAVE;
1951 FL void
1952 n_alert(char const *format, ...)
1954 va_list ap;
1955 NYD2_ENTER;
1957 n_err(_("Alert: "));
1959 va_start(ap, format);
1960 n_verr(format, ap);
1961 va_end(ap);
1963 n_err("\n");
1964 NYD2_LEAVE;
1967 FL void
1968 n_panic(char const *format, ...)
1970 va_list ap;
1971 NYD2_ENTER;
1973 fprintf(stderr, _("Panic: "));
1975 va_start(ap, format);
1976 vfprintf(stderr, format, ap);
1977 va_end(ap);
1979 putc('\n', stderr);
1980 fflush(stderr);
1981 NYD2_LEAVE;
1982 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1985 #ifdef HAVE_ERRORS
1986 FL int
1987 c_errors(void *v)
1989 char **argv = v;
1990 struct err_node *enp;
1991 NYD_ENTER;
1993 if (*argv == NULL)
1994 goto jlist;
1995 if (argv[1] != NULL)
1996 goto jerr;
1997 if (!asccasecmp(*argv, "show"))
1998 goto jlist;
1999 if (!asccasecmp(*argv, "clear"))
2000 goto jclear;
2001 jerr:
2002 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2003 v = NULL;
2004 jleave:
2005 NYD_LEAVE;
2006 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
2008 jlist: {
2009 FILE *fp;
2010 size_t i;
2012 if (_err_head == NULL) {
2013 fprintf(stderr, _("The error ring is empty\n"));
2014 goto jleave;
2017 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)
2018 ) == NULL) {
2019 fprintf(stderr, _("tmpfile"));
2020 v = NULL;
2021 goto jleave;
2024 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
2025 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2026 ++i, enp->en_str.l, enp->en_str.s);
2027 /* We don't know wether last string ended with NL; be simple */
2028 putc('\n', fp);
2030 page_or_print(fp, 0);
2031 Fclose(fp);
2033 /* FALLTHRU */
2035 jclear:
2036 _err_tail = NULL;
2037 _err_cnt = _err_cnt_noted = 0;
2038 while ((enp = _err_head) != NULL) {
2039 _err_head = enp->en_next;
2040 free(enp->en_str.s);
2041 free(enp);
2043 goto jleave;
2045 #endif /* HAVE_ERRORS */
2047 #ifndef HAVE_DEBUG
2048 FL void *
2049 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2051 void *rv;
2052 NYD2_ENTER;
2054 if (s == 0)
2055 s = 1;
2056 if ((rv = malloc(s)) == NULL)
2057 n_panic(_("no memory"));
2058 NYD2_LEAVE;
2059 return rv;
2062 FL void *
2063 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2065 void *rv;
2066 NYD2_ENTER;
2068 if (s == 0)
2069 s = 1;
2070 if (v == NULL)
2071 rv = smalloc(s);
2072 else if ((rv = realloc(v, s)) == NULL)
2073 n_panic(_("no memory"));
2074 NYD2_LEAVE;
2075 return rv;
2078 FL void *
2079 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2081 void *rv;
2082 NYD2_ENTER;
2084 if (size == 0)
2085 size = 1;
2086 if ((rv = calloc(nmemb, size)) == NULL)
2087 n_panic(_("no memory"));
2088 NYD2_LEAVE;
2089 return rv;
2092 #else /* !HAVE_DEBUG */
2093 CTA(sizeof(char) == sizeof(ui8_t));
2095 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2096 # define _HOPE_SET(C) \
2097 do {\
2098 union mem_ptr __xl, __xu;\
2099 struct mem_chunk *__xc;\
2100 __xl.p_p = (C).p_p;\
2101 __xc = __xl.p_c - 1;\
2102 __xu.p_p = __xc;\
2103 (C).p_cp += 8;\
2104 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2105 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2106 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2107 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2108 __xu.p_ui8p += __xc->mc_size - 8;\
2109 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2110 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2111 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2112 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2113 } while (0)
2114 # define _HOPE_GET_TRACE(C,BAD) \
2115 do {\
2116 (C).p_cp += 8;\
2117 _HOPE_GET(C, BAD);\
2118 (C).p_cp += 8;\
2119 } while(0)
2120 # define _HOPE_GET(C,BAD) \
2121 do {\
2122 union mem_ptr __xl, __xu;\
2123 struct mem_chunk *__xc;\
2124 ui32_t __i;\
2125 __xl.p_p = (C).p_p;\
2126 __xl.p_cp -= 8;\
2127 (C).p_cp = __xl.p_cp;\
2128 __xc = __xl.p_c - 1;\
2129 (BAD) = FAL0;\
2130 __i = 0;\
2131 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2132 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2133 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2134 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2135 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2136 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2137 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2138 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2139 if (__i != 0) {\
2140 (BAD) = TRU1;\
2141 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2142 __xl.p_p, __i, mdbg_file, mdbg_line);\
2144 __xu.p_p = __xc;\
2145 __xu.p_ui8p += __xc->mc_size - 8;\
2146 __i = 0;\
2147 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2148 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2149 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2150 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2151 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2152 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2153 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2154 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2155 if (__i != 0) {\
2156 (BAD) = TRU1;\
2157 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2158 __xl.p_p, __i, mdbg_file, mdbg_line);\
2160 if (BAD)\
2161 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2162 __xc->mc_file, __xc->mc_line);\
2163 } while (0)
2165 FL void *
2166 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2168 union mem_ptr p;
2169 NYD2_ENTER;
2171 if (s == 0)
2172 s = 1;
2173 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2174 n_panic("smalloc(): allocation too large: %s, line %d",
2175 mdbg_file, mdbg_line);
2176 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2178 if ((p.p_p = (malloc)(s)) == NULL)
2179 n_panic(_("no memory"));
2180 p.p_c->mc_prev = NULL;
2181 if ((p.p_c->mc_next = _mem_list) != NULL)
2182 _mem_list->mc_prev = p.p_c;
2183 p.p_c->mc_file = mdbg_file;
2184 p.p_c->mc_line = (ui16_t)mdbg_line;
2185 p.p_c->mc_isfree = FAL0;
2186 p.p_c->mc_size = (ui32_t)s;
2188 _mem_list = p.p_c++;
2189 _HOPE_SET(p);
2191 ++_mem_aall;
2192 ++_mem_acur;
2193 _mem_amax = MAX(_mem_amax, _mem_acur);
2194 _mem_mall += s;
2195 _mem_mcur += s;
2196 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2197 NYD2_LEAVE;
2198 return p.p_p;
2201 FL void *
2202 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2204 union mem_ptr p;
2205 bool_t isbad;
2206 NYD2_ENTER;
2208 if ((p.p_p = v) == NULL) {
2209 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2210 goto jleave;
2213 _HOPE_GET(p, isbad);
2214 --p.p_c;
2215 if (p.p_c->mc_isfree) {
2216 n_err("srealloc(): region freed! At %s, line %d\n"
2217 "\tLast seen: %s, line %" PRIu16 "\n",
2218 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2219 goto jforce;
2222 if (p.p_c == _mem_list)
2223 _mem_list = p.p_c->mc_next;
2224 else
2225 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2226 if (p.p_c->mc_next != NULL)
2227 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2229 --_mem_acur;
2230 _mem_mcur -= p.p_c->mc_size;
2231 jforce:
2232 if (s == 0)
2233 s = 1;
2234 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2235 n_panic("srealloc(): allocation too large: %s, line %d",
2236 mdbg_file, mdbg_line);
2237 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2239 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2240 n_panic(_("no memory"));
2241 p.p_c->mc_prev = NULL;
2242 if ((p.p_c->mc_next = _mem_list) != NULL)
2243 _mem_list->mc_prev = p.p_c;
2244 p.p_c->mc_file = mdbg_file;
2245 p.p_c->mc_line = (ui16_t)mdbg_line;
2246 p.p_c->mc_isfree = FAL0;
2247 p.p_c->mc_size = (ui32_t)s;
2248 _mem_list = p.p_c++;
2249 _HOPE_SET(p);
2251 ++_mem_aall;
2252 ++_mem_acur;
2253 _mem_amax = MAX(_mem_amax, _mem_acur);
2254 _mem_mall += s;
2255 _mem_mcur += s;
2256 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2257 jleave:
2258 NYD2_LEAVE;
2259 return p.p_p;
2262 FL void *
2263 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2265 union mem_ptr p;
2266 NYD2_ENTER;
2268 if (size == 0)
2269 size = 1;
2270 if (nmemb == 0)
2271 nmemb = 1;
2272 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2273 n_panic("scalloc(): allocation size too large: %s, line %d",
2274 mdbg_file, mdbg_line);
2275 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2276 n_panic("scalloc(): allocation count too large: %s, line %d",
2277 mdbg_file, mdbg_line);
2279 size *= nmemb;
2280 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2282 if ((p.p_p = (malloc)(size)) == NULL)
2283 n_panic(_("no memory"));
2284 memset(p.p_p, 0, size);
2285 p.p_c->mc_prev = NULL;
2286 if ((p.p_c->mc_next = _mem_list) != NULL)
2287 _mem_list->mc_prev = p.p_c;
2288 p.p_c->mc_file = mdbg_file;
2289 p.p_c->mc_line = (ui16_t)mdbg_line;
2290 p.p_c->mc_isfree = FAL0;
2291 p.p_c->mc_size = (ui32_t)size;
2292 _mem_list = p.p_c++;
2293 _HOPE_SET(p);
2295 ++_mem_aall;
2296 ++_mem_acur;
2297 _mem_amax = MAX(_mem_amax, _mem_acur);
2298 _mem_mall += size;
2299 _mem_mcur += size;
2300 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2301 NYD2_LEAVE;
2302 return p.p_p;
2305 FL void
2306 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2308 union mem_ptr p;
2309 bool_t isbad;
2310 NYD2_ENTER;
2312 if ((p.p_p = v) == NULL) {
2313 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2314 goto jleave;
2317 _HOPE_GET(p, isbad);
2318 --p.p_c;
2319 if (p.p_c->mc_isfree) {
2320 n_err("sfree(): double-free avoided at %s, line %d\n"
2321 "\tLast seen: %s, line %" PRIu16 "\n",
2322 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2323 goto jleave;
2326 if (p.p_c == _mem_list)
2327 _mem_list = p.p_c->mc_next;
2328 else
2329 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2330 if (p.p_c->mc_next != NULL)
2331 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2332 p.p_c->mc_isfree = TRU1;
2333 /* Trash contents (also see [21c05f8]) */
2334 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2336 --_mem_acur;
2337 _mem_mcur -= p.p_c->mc_size;
2339 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2340 p.p_c->mc_next = _mem_free;
2341 _mem_free = p.p_c;
2342 } else
2343 (free)(p.p_c);
2344 jleave:
2345 NYD2_LEAVE;
2348 FL void
2349 smemreset(void)
2351 union mem_ptr p;
2352 size_t c = 0, s = 0;
2353 NYD_ENTER;
2355 smemcheck();
2357 for (p.p_c = _mem_free; p.p_c != NULL;) {
2358 void *vp = p.p_c;
2359 ++c;
2360 s += p.p_c->mc_size;
2361 p.p_c = p.p_c->mc_next;
2362 (free)(vp);
2364 _mem_free = NULL;
2366 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2367 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2368 NYD_LEAVE;
2371 FL int
2372 c_smemtrace(void *v)
2374 /* For _HOPE_GET() */
2375 char const * const mdbg_file = "smemtrace()";
2376 int const mdbg_line = -1;
2377 FILE *fp;
2378 union mem_ptr p, xp;
2379 bool_t isbad;
2380 size_t lines;
2381 NYD_ENTER;
2383 v = (void*)0x1;
2384 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2385 NULL) {
2386 n_perr("tmpfile", 0);
2387 goto jleave;
2390 fprintf(fp, "Memory statistics:\n"
2391 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2392 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2393 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2395 fprintf(fp, "Currently allocated memory chunks:\n");
2396 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2397 ++lines, p.p_c = p.p_c->mc_next) {
2398 xp = p;
2399 ++xp.p_c;
2400 _HOPE_GET_TRACE(xp, isbad);
2401 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2402 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2403 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2404 p.p_c->mc_line);
2407 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2408 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2409 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2410 xp = p;
2411 ++xp.p_c;
2412 _HOPE_GET_TRACE(xp, isbad);
2413 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2414 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2415 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2416 p.p_c->mc_file, p.p_c->mc_line);
2420 page_or_print(fp, lines);
2421 Fclose(fp);
2422 v = NULL;
2423 jleave:
2424 NYD_LEAVE;
2425 return (v != NULL);
2428 # ifdef HAVE_DEVEL
2429 FL bool_t
2430 _smemcheck(char const *mdbg_file, int mdbg_line)
2432 union mem_ptr p, xp;
2433 bool_t anybad = FAL0, isbad;
2434 size_t lines;
2435 NYD_ENTER;
2437 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2438 ++lines, p.p_c = p.p_c->mc_next) {
2439 xp = p;
2440 ++xp.p_c;
2441 _HOPE_GET_TRACE(xp, isbad);
2442 if (isbad) {
2443 anybad = TRU1;
2444 n_err(
2445 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2446 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2447 p.p_c->mc_file, p.p_c->mc_line);
2451 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2452 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2453 xp = p;
2454 ++xp.p_c;
2455 _HOPE_GET_TRACE(xp, isbad);
2456 if (isbad) {
2457 anybad = TRU1;
2458 n_err(
2459 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2460 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2461 p.p_c->mc_file, p.p_c->mc_line);
2465 NYD_LEAVE;
2466 return anybad;
2468 # endif /* HAVE_DEVEL */
2470 # undef _HOPE_SIZE
2471 # undef _HOPE_SET
2472 # undef _HOPE_GET_TRACE
2473 # undef _HOPE_GET
2474 #endif /* HAVE_DEBUG */
2476 /* s-it-mode */