FIX "," message specification (since [1c4b8c9], v14.8.4)..
[s-mailx.git] / auxlily.c
blob336eea52f05142858bb9bcf0c28f11ee22fc3986
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 : '?';
1407 }else if(isuni){ /* TODO ctext */
1408 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1409 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1410 continue;
1411 /* And some zero-width messes */
1412 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1413 continue;
1414 /* Oh about the ISO C wide character interfaces, baby! */
1415 if(wc == 0xFEFF)
1416 continue;
1418 if ((n = wctomb(mbb, wc)) <= 0)
1419 continue;
1420 out->l += n;
1421 assert(out->l < msz);
1422 for (i = 0; i < n; ++i)
1423 *outp++ = mbb[i];
1425 } else
1426 #endif /* NATCH_CHAR */
1428 int c;
1429 while (inp < maxp) {
1430 c = *inp++ & 0377;
1431 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1432 c = '?';
1433 *outp++ = c;
1435 out->l = in->l;
1437 jleave:
1438 out->s[out->l] = '\0';
1439 NYD_LEAVE;
1442 FL size_t
1443 delctrl(char *cp, size_t len)
1445 size_t x, y;
1446 NYD_ENTER;
1448 for (x = y = 0; x < len; ++x)
1449 if (!cntrlchar(cp[x]))
1450 cp[y++] = cp[x];
1451 cp[y] = '\0';
1452 NYD_LEAVE;
1453 return y;
1456 FL char *
1457 prstr(char const *s)
1459 struct str in, out;
1460 char *rp;
1461 NYD_ENTER;
1463 in.s = UNCONST(s);
1464 in.l = strlen(s);
1465 makeprint(&in, &out);
1466 rp = savestrbuf(out.s, out.l);
1467 free(out.s);
1468 NYD_LEAVE;
1469 return rp;
1472 FL int
1473 prout(char const *s, size_t sz, FILE *fp)
1475 struct str in, out;
1476 int n;
1477 NYD_ENTER;
1479 in.s = UNCONST(s);
1480 in.l = sz;
1481 makeprint(&in, &out);
1482 n = fwrite(out.s, 1, out.l, fp);
1483 free(out.s);
1484 NYD_LEAVE;
1485 return n;
1488 FL size_t
1489 putuc(int u, int c, FILE *fp)
1491 size_t rv;
1492 NYD_ENTER;
1493 UNUSED(u);
1495 #ifdef HAVE_NATCH_CHAR
1496 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1497 char mbb[MB_LEN_MAX];
1498 int i, n;
1500 if ((n = wctomb(mbb, u)) > 0) {
1501 rv = wcwidth(u);
1502 for (i = 0; i < n; ++i)
1503 if (putc(mbb[i] & 0377, fp) == EOF) {
1504 rv = 0;
1505 break;
1507 } else if (n == 0)
1508 rv = (putc('\0', fp) != EOF);
1509 else
1510 rv = 0;
1511 } else
1512 #endif
1513 rv = (putc(c, fp) != EOF);
1514 NYD_LEAVE;
1515 return rv;
1518 FL bool_t
1519 bidi_info_needed(char const *bdat, size_t blen)
1521 bool_t rv = FAL0;
1522 NYD_ENTER;
1524 #ifdef HAVE_NATCH_CHAR
1525 if (options & OPT_UNICODE)
1526 while (blen > 0) {
1527 /* TODO Checking for BIDI character: use S-CText fromutf8
1528 * TODO plus isrighttoleft (or whatever there will be)! */
1529 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1530 if (c == UI32_MAX)
1531 break;
1533 if (c <= 0x05BE)
1534 continue;
1536 /* (Very very fuzzy, awaiting S-CText for good) */
1537 if ((c >= 0x05BE && c <= 0x08E3) ||
1538 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1539 (c >= 0xFE70 && c <= 0xFEFC) ||
1540 (c >= 0x10800 && c <= 0x10C48) ||
1541 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1542 rv = TRU1;
1543 break;
1546 #endif /* HAVE_NATCH_CHAR */
1547 NYD_LEAVE;
1548 return rv;
1551 FL void
1552 bidi_info_create(struct bidi_info *bip)
1554 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1555 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1556 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1557 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1558 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1559 NATCH_CHAR( char const *hb; )
1560 NYD_ENTER;
1562 memset(bip, 0, sizeof *bip);
1563 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1565 #ifdef HAVE_NATCH_CHAR
1566 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1567 switch (*hb) {
1568 case '3':
1569 bip->bi_pad = 2;
1570 /* FALLTHRU */
1571 case '2':
1572 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1573 break;
1574 case '1':
1575 bip->bi_pad = 2;
1576 /* FALLTHRU */
1577 default:
1578 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1579 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1580 break;
1582 bip->bi_start.l = bip->bi_end.l = 3;
1584 #endif
1585 NYD_LEAVE;
1588 #ifdef HAVE_COLOUR
1589 FL void
1590 colour_table_create(bool_t pager_used)
1592 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1593 size_t i;
1594 struct colour_table *ct;
1595 NYD_ENTER;
1597 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1598 goto jleave;
1599 else {
1600 char *term, *okterms;
1602 if ((term = env_vlook("TERM", FAL0)) == NULL)
1603 goto jleave;
1604 /* terminfo rocks: if we find "color", assume it's right */
1605 if (strstr(term, "color") != NULL)
1606 goto jok;
1607 if ((okterms = ok_vlook(colour_terms)) == NULL)
1608 okterms = UNCONST(COLOUR_TERMS);
1609 okterms = savestr(okterms);
1611 i = strlen(term);
1612 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1613 if (!strncmp(u.cp, term, i))
1614 goto jok;
1615 goto jleave;
1618 jok:
1619 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1620 { static struct {
1621 enum okeys okey;
1622 enum colourspec cspec;
1623 char const *defval;
1624 } const map[] = {
1625 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1626 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1627 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1628 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1629 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1632 for (i = 0; i < NELEM(map); ++i) {
1633 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1634 u.ccp = map[i].defval;
1635 u.cp = _colour_iso6429(u.ccp);
1636 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1637 ct->ct_csinfo[map[i].cspec].s = u.cp;
1640 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1641 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1643 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1644 u.ccp = COLOUR_USER_HEADERS;
1645 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1646 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1647 jleave:
1648 NYD_LEAVE;
1651 FL void
1652 colour_put(FILE *fp, enum colourspec cs)
1654 NYD_ENTER;
1655 if (colour_table != NULL) {
1656 struct str const *cp = colour_get(cs);
1658 fwrite(cp->s, cp->l, 1, fp);
1660 NYD_LEAVE;
1663 FL void
1664 colour_put_header(FILE *fp, char const *name)
1666 enum colourspec cs = COLOURSPEC_HEADER;
1667 struct str const *uheads;
1668 char *cp, *cp_base, *x;
1669 size_t namelen;
1670 NYD_ENTER;
1672 if (colour_table == NULL)
1673 goto j_leave;
1674 /* Normal header colours if there are no user headers */
1675 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1676 if (uheads->s == NULL)
1677 goto jleave;
1679 /* Iterate over all entries in the *colour-user-headers* list */
1680 cp = ac_alloc(uheads->l +1);
1681 memcpy(cp, uheads->s, uheads->l +1);
1682 cp_base = cp;
1683 namelen = strlen(name);
1684 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1685 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1686 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1687 cs = COLOURSPEC_UHEADER;
1688 break;
1691 ac_free(cp_base);
1692 jleave:
1693 colour_put(fp, cs);
1694 j_leave:
1695 NYD_LEAVE;
1698 FL void
1699 colour_reset(FILE *fp)
1701 NYD_ENTER;
1702 if (colour_table != NULL)
1703 fwrite("\033[0m", 4, 1, fp);
1704 NYD_LEAVE;
1707 FL struct str const *
1708 colour_get(enum colourspec cs)
1710 struct str const *rv = NULL;
1711 NYD_ENTER;
1713 if (colour_table != NULL)
1714 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1715 rv = NULL;
1716 NYD_LEAVE;
1717 return rv;
1719 #endif /* HAVE_COLOUR */
1721 FL si8_t
1722 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1724 char *dat, *eptr;
1725 sl_i sli;
1726 si8_t rv;
1727 NYD_ENTER;
1729 assert(inlen == 0 || inbuf != NULL);
1731 if (inlen == UIZ_MAX)
1732 inlen = strlen(inbuf);
1734 if (inlen == 0)
1735 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1736 else {
1737 if ((inlen == 1 && *inbuf == '1') ||
1738 !ascncasecmp(inbuf, "true", inlen) ||
1739 !ascncasecmp(inbuf, "yes", inlen) ||
1740 !ascncasecmp(inbuf, "on", inlen))
1741 rv = 1;
1742 else if ((inlen == 1 && *inbuf == '0') ||
1743 !ascncasecmp(inbuf, "false", inlen) ||
1744 !ascncasecmp(inbuf, "no", inlen) ||
1745 !ascncasecmp(inbuf, "off", inlen))
1746 rv = 0;
1747 else {
1748 dat = ac_alloc(inlen +1);
1749 memcpy(dat, inbuf, inlen);
1750 dat[inlen] = '\0';
1752 sli = strtol(dat, &eptr, 0);
1753 if (*dat != '\0' && *eptr == '\0')
1754 rv = (sli != 0);
1755 else
1756 rv = -1;
1758 ac_free(dat);
1761 NYD_LEAVE;
1762 return rv;
1765 FL si8_t
1766 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1768 si8_t rv;
1769 NYD_ENTER;
1771 assert(inlen == 0 || inbuf != NULL);
1773 if (inlen == UIZ_MAX)
1774 inlen = strlen(inbuf);
1776 if (inlen == 0)
1777 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1778 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1779 !ascncasecmp(inbuf, "ask-", 4) &&
1780 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1781 (options & OPT_INTERACTIVE)) {
1782 if (prompt != NULL)
1783 fputs(prompt, stdout);
1784 rv = getapproval(NULL, rv);
1786 NYD_LEAVE;
1787 return rv;
1790 FL time_t
1791 n_time_epoch(void)
1793 #ifdef HAVE_CLOCK_GETTIME
1794 struct timespec ts;
1795 #elif defined HAVE_GETTIMEOFDAY
1796 struct timeval ts;
1797 #endif
1798 time_t rv;
1799 NYD2_ENTER;
1801 #ifdef HAVE_CLOCK_GETTIME
1802 clock_gettime(CLOCK_REALTIME, &ts);
1803 rv = (time_t)ts.tv_sec;
1804 #elif defined HAVE_GETTIMEOFDAY
1805 gettimeofday(&ts, NULL);
1806 rv = (time_t)ts.tv_sec;
1807 #else
1808 rv = time(NULL);
1809 #endif
1810 NYD2_LEAVE;
1811 return rv;
1814 FL void
1815 time_current_update(struct time_current *tc, bool_t full_update)
1817 NYD_ENTER;
1818 tc->tc_time = n_time_epoch();
1819 if (full_update) {
1820 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1821 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1822 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1824 NYD_LEAVE;
1827 FL void
1828 n_err(char const *format, ...)
1830 va_list ap;
1831 NYD2_ENTER;
1833 va_start(ap, format);
1834 #ifdef HAVE_ERRORS
1835 if (options & OPT_INTERACTIVE)
1836 n_verr(format, ap);
1837 else
1838 #endif
1840 vfprintf(stderr, format, ap);
1841 if (strchr(format, '\n') != NULL) /* TODO */
1842 fflush(stderr);
1844 va_end(ap);
1845 NYD2_LEAVE;
1848 FL void
1849 n_verr(char const *format, va_list ap)
1851 /* Check use cases of PS_ERRORS_NOTED, too! */
1852 #ifdef HAVE_ERRORS
1853 char buf[LINESIZE], *xbuf;
1854 int lmax, l;
1855 struct err_node *enp;
1857 LCTA(ERRORS_MAX > 3);
1858 #endif
1859 NYD2_ENTER;
1861 #ifdef HAVE_ERRORS
1862 if (!(options & OPT_INTERACTIVE))
1863 #endif
1865 vfprintf(stderr, format, ap);
1866 goto jleave;
1869 #ifdef HAVE_ERRORS
1870 xbuf = buf;
1871 lmax = sizeof buf;
1872 jredo:
1873 l = vsnprintf(xbuf, lmax, format, ap);
1874 if (l <= 0)
1875 goto jleave;
1876 if (UICMP(z, l, >=, lmax)) {
1877 /* FIXME Cannot reuse va_list
1878 lmax = ++l;
1879 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1880 goto jredo;
1884 fwrite(xbuf, 1, l, stderr);
1886 /* Link it into the `errors' message ring */
1887 if ((enp = _err_tail) == NULL) {
1888 jcreat:
1889 enp = scalloc(1, sizeof *enp);
1890 if (_err_tail != NULL)
1891 _err_tail->en_next = enp;
1892 else
1893 _err_head = enp;
1894 _err_tail = enp;
1895 ++_err_cnt;
1896 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
1897 if (_err_cnt < ERRORS_MAX)
1898 goto jcreat;
1900 _err_head = (enp = _err_head)->en_next;
1901 _err_tail->en_next = enp;
1902 _err_tail = enp;
1903 free(enp->en_str.s);
1904 memset(enp, 0, sizeof *enp);
1907 n_str_add_buf(&enp->en_str, xbuf, l);
1909 if (xbuf != buf)
1910 free(xbuf);
1911 #endif /* HAVE_ERRORS */
1913 jleave:
1914 if (strchr(format, '\n') != NULL) /* TODO */
1915 fflush(stderr);
1916 NYD2_LEAVE;
1919 FL void
1920 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
1922 va_list ap;
1923 NYD_X;
1925 va_start(ap, format);
1926 vfprintf(stderr, format, ap);
1927 va_end(ap);
1928 fflush(stderr);
1931 FL void
1932 n_perr(char const *msg, int errval)
1934 char const *fmt;
1935 NYD2_ENTER;
1937 if (msg == NULL) {
1938 fmt = "%s%s\n";
1939 msg = "";
1940 } else
1941 fmt = "%s: %s\n";
1943 if (errval == 0)
1944 errval = errno;
1946 n_err(fmt, msg, strerror(errval));
1947 NYD2_LEAVE;
1950 FL void
1951 n_alert(char const *format, ...)
1953 va_list ap;
1954 NYD2_ENTER;
1956 n_err(_("Alert: "));
1958 va_start(ap, format);
1959 n_verr(format, ap);
1960 va_end(ap);
1962 n_err("\n");
1963 NYD2_LEAVE;
1966 FL void
1967 n_panic(char const *format, ...)
1969 va_list ap;
1970 NYD2_ENTER;
1972 fprintf(stderr, _("Panic: "));
1974 va_start(ap, format);
1975 vfprintf(stderr, format, ap);
1976 va_end(ap);
1978 putc('\n', stderr);
1979 fflush(stderr);
1980 NYD2_LEAVE;
1981 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1984 #ifdef HAVE_ERRORS
1985 FL int
1986 c_errors(void *v)
1988 char **argv = v;
1989 struct err_node *enp;
1990 NYD_ENTER;
1992 if (*argv == NULL)
1993 goto jlist;
1994 if (argv[1] != NULL)
1995 goto jerr;
1996 if (!asccasecmp(*argv, "show"))
1997 goto jlist;
1998 if (!asccasecmp(*argv, "clear"))
1999 goto jclear;
2000 jerr:
2001 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2002 v = NULL;
2003 jleave:
2004 NYD_LEAVE;
2005 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
2007 jlist: {
2008 FILE *fp;
2009 size_t i;
2011 if (_err_head == NULL) {
2012 fprintf(stderr, _("The error ring is empty\n"));
2013 goto jleave;
2016 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)
2017 ) == NULL) {
2018 fprintf(stderr, _("tmpfile"));
2019 v = NULL;
2020 goto jleave;
2023 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
2024 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2025 ++i, enp->en_str.l, enp->en_str.s);
2026 /* We don't know wether last string ended with NL; be simple */
2027 putc('\n', fp);
2029 page_or_print(fp, 0);
2030 Fclose(fp);
2032 /* FALLTHRU */
2034 jclear:
2035 _err_tail = NULL;
2036 _err_cnt = _err_cnt_noted = 0;
2037 while ((enp = _err_head) != NULL) {
2038 _err_head = enp->en_next;
2039 free(enp->en_str.s);
2040 free(enp);
2042 goto jleave;
2044 #endif /* HAVE_ERRORS */
2046 #ifndef HAVE_DEBUG
2047 FL void *
2048 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2050 void *rv;
2051 NYD2_ENTER;
2053 if (s == 0)
2054 s = 1;
2055 if ((rv = malloc(s)) == NULL)
2056 n_panic(_("no memory"));
2057 NYD2_LEAVE;
2058 return rv;
2061 FL void *
2062 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2064 void *rv;
2065 NYD2_ENTER;
2067 if (s == 0)
2068 s = 1;
2069 if (v == NULL)
2070 rv = smalloc(s);
2071 else if ((rv = realloc(v, s)) == NULL)
2072 n_panic(_("no memory"));
2073 NYD2_LEAVE;
2074 return rv;
2077 FL void *
2078 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2080 void *rv;
2081 NYD2_ENTER;
2083 if (size == 0)
2084 size = 1;
2085 if ((rv = calloc(nmemb, size)) == NULL)
2086 n_panic(_("no memory"));
2087 NYD2_LEAVE;
2088 return rv;
2091 #else /* !HAVE_DEBUG */
2092 CTA(sizeof(char) == sizeof(ui8_t));
2094 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2095 # define _HOPE_SET(C) \
2096 do {\
2097 union mem_ptr __xl, __xu;\
2098 struct mem_chunk *__xc;\
2099 __xl.p_p = (C).p_p;\
2100 __xc = __xl.p_c - 1;\
2101 __xu.p_p = __xc;\
2102 (C).p_cp += 8;\
2103 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2104 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2105 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2106 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2107 __xu.p_ui8p += __xc->mc_size - 8;\
2108 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2109 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2110 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2111 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2112 } while (0)
2113 # define _HOPE_GET_TRACE(C,BAD) \
2114 do {\
2115 (C).p_cp += 8;\
2116 _HOPE_GET(C, BAD);\
2117 (C).p_cp += 8;\
2118 } while(0)
2119 # define _HOPE_GET(C,BAD) \
2120 do {\
2121 union mem_ptr __xl, __xu;\
2122 struct mem_chunk *__xc;\
2123 ui32_t __i;\
2124 __xl.p_p = (C).p_p;\
2125 __xl.p_cp -= 8;\
2126 (C).p_cp = __xl.p_cp;\
2127 __xc = __xl.p_c - 1;\
2128 (BAD) = FAL0;\
2129 __i = 0;\
2130 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2131 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2132 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2133 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2134 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2135 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2136 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2137 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2138 if (__i != 0) {\
2139 (BAD) = TRU1;\
2140 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2141 __xl.p_p, __i, mdbg_file, mdbg_line);\
2143 __xu.p_p = __xc;\
2144 __xu.p_ui8p += __xc->mc_size - 8;\
2145 __i = 0;\
2146 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2147 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2148 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2149 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2150 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2151 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2152 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2153 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2154 if (__i != 0) {\
2155 (BAD) = TRU1;\
2156 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2157 __xl.p_p, __i, mdbg_file, mdbg_line);\
2159 if (BAD)\
2160 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2161 __xc->mc_file, __xc->mc_line);\
2162 } while (0)
2164 FL void *
2165 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2167 union mem_ptr p;
2168 NYD2_ENTER;
2170 if (s == 0)
2171 s = 1;
2172 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2173 n_panic("smalloc(): allocation too large: %s, line %d",
2174 mdbg_file, mdbg_line);
2175 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2177 if ((p.p_p = (malloc)(s)) == NULL)
2178 n_panic(_("no memory"));
2179 p.p_c->mc_prev = NULL;
2180 if ((p.p_c->mc_next = _mem_list) != NULL)
2181 _mem_list->mc_prev = p.p_c;
2182 p.p_c->mc_file = mdbg_file;
2183 p.p_c->mc_line = (ui16_t)mdbg_line;
2184 p.p_c->mc_isfree = FAL0;
2185 p.p_c->mc_size = (ui32_t)s;
2187 _mem_list = p.p_c++;
2188 _HOPE_SET(p);
2190 ++_mem_aall;
2191 ++_mem_acur;
2192 _mem_amax = MAX(_mem_amax, _mem_acur);
2193 _mem_mall += s;
2194 _mem_mcur += s;
2195 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2196 NYD2_LEAVE;
2197 return p.p_p;
2200 FL void *
2201 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2203 union mem_ptr p;
2204 bool_t isbad;
2205 NYD2_ENTER;
2207 if ((p.p_p = v) == NULL) {
2208 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2209 goto jleave;
2212 _HOPE_GET(p, isbad);
2213 --p.p_c;
2214 if (p.p_c->mc_isfree) {
2215 n_err("srealloc(): region freed! At %s, line %d\n"
2216 "\tLast seen: %s, line %" PRIu16 "\n",
2217 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2218 goto jforce;
2221 if (p.p_c == _mem_list)
2222 _mem_list = p.p_c->mc_next;
2223 else
2224 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2225 if (p.p_c->mc_next != NULL)
2226 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2228 --_mem_acur;
2229 _mem_mcur -= p.p_c->mc_size;
2230 jforce:
2231 if (s == 0)
2232 s = 1;
2233 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2234 n_panic("srealloc(): allocation too large: %s, line %d",
2235 mdbg_file, mdbg_line);
2236 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2238 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2239 n_panic(_("no memory"));
2240 p.p_c->mc_prev = NULL;
2241 if ((p.p_c->mc_next = _mem_list) != NULL)
2242 _mem_list->mc_prev = p.p_c;
2243 p.p_c->mc_file = mdbg_file;
2244 p.p_c->mc_line = (ui16_t)mdbg_line;
2245 p.p_c->mc_isfree = FAL0;
2246 p.p_c->mc_size = (ui32_t)s;
2247 _mem_list = p.p_c++;
2248 _HOPE_SET(p);
2250 ++_mem_aall;
2251 ++_mem_acur;
2252 _mem_amax = MAX(_mem_amax, _mem_acur);
2253 _mem_mall += s;
2254 _mem_mcur += s;
2255 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2256 jleave:
2257 NYD2_LEAVE;
2258 return p.p_p;
2261 FL void *
2262 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2264 union mem_ptr p;
2265 NYD2_ENTER;
2267 if (size == 0)
2268 size = 1;
2269 if (nmemb == 0)
2270 nmemb = 1;
2271 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2272 n_panic("scalloc(): allocation size too large: %s, line %d",
2273 mdbg_file, mdbg_line);
2274 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2275 n_panic("scalloc(): allocation count too large: %s, line %d",
2276 mdbg_file, mdbg_line);
2278 size *= nmemb;
2279 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2281 if ((p.p_p = (malloc)(size)) == NULL)
2282 n_panic(_("no memory"));
2283 memset(p.p_p, 0, size);
2284 p.p_c->mc_prev = NULL;
2285 if ((p.p_c->mc_next = _mem_list) != NULL)
2286 _mem_list->mc_prev = p.p_c;
2287 p.p_c->mc_file = mdbg_file;
2288 p.p_c->mc_line = (ui16_t)mdbg_line;
2289 p.p_c->mc_isfree = FAL0;
2290 p.p_c->mc_size = (ui32_t)size;
2291 _mem_list = p.p_c++;
2292 _HOPE_SET(p);
2294 ++_mem_aall;
2295 ++_mem_acur;
2296 _mem_amax = MAX(_mem_amax, _mem_acur);
2297 _mem_mall += size;
2298 _mem_mcur += size;
2299 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2300 NYD2_LEAVE;
2301 return p.p_p;
2304 FL void
2305 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2307 union mem_ptr p;
2308 bool_t isbad;
2309 NYD2_ENTER;
2311 if ((p.p_p = v) == NULL) {
2312 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2313 goto jleave;
2316 _HOPE_GET(p, isbad);
2317 --p.p_c;
2318 if (p.p_c->mc_isfree) {
2319 n_err("sfree(): double-free avoided at %s, line %d\n"
2320 "\tLast seen: %s, line %" PRIu16 "\n",
2321 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2322 goto jleave;
2325 if (p.p_c == _mem_list)
2326 _mem_list = p.p_c->mc_next;
2327 else
2328 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2329 if (p.p_c->mc_next != NULL)
2330 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2331 p.p_c->mc_isfree = TRU1;
2332 /* Trash contents (also see [21c05f8]) */
2333 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2335 --_mem_acur;
2336 _mem_mcur -= p.p_c->mc_size;
2338 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2339 p.p_c->mc_next = _mem_free;
2340 _mem_free = p.p_c;
2341 } else
2342 (free)(p.p_c);
2343 jleave:
2344 NYD2_LEAVE;
2347 FL void
2348 smemreset(void)
2350 union mem_ptr p;
2351 size_t c = 0, s = 0;
2352 NYD_ENTER;
2354 smemcheck();
2356 for (p.p_c = _mem_free; p.p_c != NULL;) {
2357 void *vp = p.p_c;
2358 ++c;
2359 s += p.p_c->mc_size;
2360 p.p_c = p.p_c->mc_next;
2361 (free)(vp);
2363 _mem_free = NULL;
2365 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2366 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2367 NYD_LEAVE;
2370 FL int
2371 c_smemtrace(void *v)
2373 /* For _HOPE_GET() */
2374 char const * const mdbg_file = "smemtrace()";
2375 int const mdbg_line = -1;
2376 FILE *fp;
2377 union mem_ptr p, xp;
2378 bool_t isbad;
2379 size_t lines;
2380 NYD_ENTER;
2382 v = (void*)0x1;
2383 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2384 NULL) {
2385 n_perr("tmpfile", 0);
2386 goto jleave;
2389 fprintf(fp, "Memory statistics:\n"
2390 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2391 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2392 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2394 fprintf(fp, "Currently allocated memory chunks:\n");
2395 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2396 ++lines, p.p_c = p.p_c->mc_next) {
2397 xp = p;
2398 ++xp.p_c;
2399 _HOPE_GET_TRACE(xp, isbad);
2400 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2401 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2402 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2403 p.p_c->mc_line);
2406 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2407 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2408 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2409 xp = p;
2410 ++xp.p_c;
2411 _HOPE_GET_TRACE(xp, isbad);
2412 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2413 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2414 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2415 p.p_c->mc_file, p.p_c->mc_line);
2419 page_or_print(fp, lines);
2420 Fclose(fp);
2421 v = NULL;
2422 jleave:
2423 NYD_LEAVE;
2424 return (v != NULL);
2427 # ifdef HAVE_DEVEL
2428 FL bool_t
2429 _smemcheck(char const *mdbg_file, int mdbg_line)
2431 union mem_ptr p, xp;
2432 bool_t anybad = FAL0, isbad;
2433 size_t lines;
2434 NYD_ENTER;
2436 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2437 ++lines, p.p_c = p.p_c->mc_next) {
2438 xp = p;
2439 ++xp.p_c;
2440 _HOPE_GET_TRACE(xp, isbad);
2441 if (isbad) {
2442 anybad = TRU1;
2443 n_err(
2444 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2445 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2446 p.p_c->mc_file, p.p_c->mc_line);
2450 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2451 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2452 xp = p;
2453 ++xp.p_c;
2454 _HOPE_GET_TRACE(xp, isbad);
2455 if (isbad) {
2456 anybad = TRU1;
2457 n_err(
2458 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2459 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2460 p.p_c->mc_file, p.p_c->mc_line);
2464 NYD_LEAVE;
2465 return anybad;
2467 # endif /* HAVE_DEVEL */
2469 # undef _HOPE_SIZE
2470 # undef _HOPE_SET
2471 # undef _HOPE_GET_TRACE
2472 # undef _HOPE_GET
2473 #endif /* HAVE_DEBUG */
2475 /* s-it-mode */