Merge branch 'topic/colour.2'
[s-mailx.git] / auxlily.c
bloba8659e49d2b1e884ee4612e3d4327c09899a173a
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 <pwd.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 struct shvar_stack {
78 struct shvar_stack *shs_next; /* Outer stack frame */
79 char const *shs_value; /* Remaining value to expand */
80 size_t shs_len; /* gth of .shs_dat this level */
81 char const *shs_dat; /* Result data of this level */
82 bool_t *shs_err; /* Or NULL */
83 bool_t shs_bsesc; /* Shall backslash escaping be performed */
86 #ifdef HAVE_ERRORS
87 struct err_node {
88 struct err_node *en_next;
89 struct str en_str;
91 #endif
93 #ifdef HAVE_DEBUG
94 struct mem_chunk {
95 struct mem_chunk *mc_prev;
96 struct mem_chunk *mc_next;
97 char const *mc_file;
98 ui16_t mc_line;
99 ui8_t mc_isfree;
100 ui8_t __dummy;
101 ui32_t mc_size;
104 union mem_ptr {
105 void *p_p;
106 struct mem_chunk *p_c;
107 char *p_cp;
108 ui8_t *p_ui8p;
110 #endif
112 #ifndef HAVE_POSIX_RANDOM
113 static union rand_state *_rand;
114 #endif
116 /* {hold,rele}_all_sigs() */
117 static size_t _alls_depth;
118 static sigset_t _alls_nset, _alls_oset;
120 /* {hold,rele}_sigs() */
121 static size_t _hold_sigdepth;
122 static sigset_t _hold_nset, _hold_oset;
124 /* NYD, memory pool debug */
125 #ifdef HAVE_NYD
126 static ui32_t _nyd_curr, _nyd_level;
127 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
128 #endif
130 /* Error ring, for `errors' */
131 #ifdef HAVE_ERRORS
132 static struct err_node *_err_head, *_err_tail;
133 static size_t _err_cnt, _err_cnt_noted;
134 #endif
136 #ifdef HAVE_DEBUG
137 static size_t _mem_aall, _mem_acur, _mem_amax,
138 _mem_mall, _mem_mcur, _mem_mmax;
140 static struct mem_chunk *_mem_list, *_mem_free;
141 #endif
143 /* Our ARC4 random generator with its completely unacademical pseudo
144 * initialization (shall /dev/urandom fail) */
145 #ifndef HAVE_POSIX_RANDOM
146 static void _rand_init(void);
147 static ui32_t _rand_weak(ui32_t seed);
148 SINLINE ui8_t _rand_get8(void);
149 #endif
151 #ifdef HAVE_NYD
152 static void _nyd_print(int fd, struct nyd_info *nip);
153 #endif
155 /* Perform shell variable expansion */
156 static char * _sh_exp_var(struct shvar_stack *shsp);
158 #ifndef HAVE_POSIX_RANDOM
159 static void
160 _rand_init(void)
162 # ifdef HAVE_CLOCK_GETTIME
163 struct timespec ts;
164 # else
165 struct timeval ts;
166 # endif
167 union {int fd; size_t i;} u;
168 ui32_t seed, rnd;
169 NYD2_ENTER;
171 _rand = smalloc(sizeof *_rand);
173 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
174 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
176 close(u.fd);
177 if (ok)
178 goto jleave;
181 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
182 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
183 size_t t, k;
185 # ifdef HAVE_CLOCK_GETTIME
186 clock_gettime(CLOCK_REALTIME, &ts);
187 t = (ui32_t)ts.tv_nsec;
188 # else
189 gettimeofday(&ts, NULL);
190 t = (ui32_t)ts.tv_usec;
191 # endif
192 if (rnd & 1)
193 t = (t >> 16) | (t << 16);
194 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
195 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
196 if (rnd == 7 || rnd == 17)
197 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
198 k = _rand->b32[u.i] % NELEM(_rand->b32);
199 _rand->b32[k] ^= _rand->b32[u.i];
200 seed ^= _rand_weak(_rand->b32[k]);
201 if ((rnd & 3) == 3)
202 seed ^= nextprime(seed);
206 for (u.i = 11 * sizeof(_rand->b8); u.i != 0; --u.i)
207 _rand_get8();
208 jleave:
209 NYD2_LEAVE;
212 static ui32_t
213 _rand_weak(ui32_t seed)
215 /* From "Random number generators: good ones are hard to find",
216 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
217 * October 1988, p. 1195.
218 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
219 ui32_t hi;
221 if (seed == 0)
222 seed = 123459876;
223 hi = seed / 127773;
224 seed %= 127773;
225 seed = (seed * 16807) - (hi * 2836);
226 if ((si32_t)seed < 0)
227 seed += SI32_MAX;
228 return seed;
231 SINLINE ui8_t
232 _rand_get8(void)
234 ui8_t si, sj;
236 si = _rand->a._dat[++_rand->a._i];
237 sj = _rand->a._dat[_rand->a._j += si];
238 _rand->a._dat[_rand->a._i] = sj;
239 _rand->a._dat[_rand->a._j] = si;
240 return _rand->a._dat[(ui8_t)(si + sj)];
242 #endif /* HAVE_POSIX_RANDOM */
244 #ifdef HAVE_NYD
245 static void
246 _nyd_print(int fd, struct nyd_info *nip)
248 char buf[80];
249 union {int i; size_t z;} u;
251 u.i = snprintf(buf, sizeof buf,
252 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
253 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
254 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
255 if (u.i > 0) {
256 u.z = u.i;
257 if (u.z > sizeof buf)
258 u.z = sizeof buf - 1; /* (Skip \0) */
259 write(fd, buf, u.z);
262 #endif
264 static char *
265 _sh_exp_var(struct shvar_stack *shsp)
267 struct shvar_stack next, *np, *tmp;
268 char const *vp;
269 char lc, c, *cp, *rv;
270 size_t i;
271 NYD2_ENTER;
273 if (*(vp = shsp->shs_value) != '$') {
274 bool_t bsesc = shsp->shs_bsesc;
275 union {bool_t hadbs; char c;} u = {FAL0};
277 shsp->shs_dat = vp;
278 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
279 if (c == '$' && lc != '\\')
280 break;
281 if (!bsesc)
282 continue;
283 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
285 shsp->shs_len = i;
287 if (u.hadbs) {
288 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
290 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
291 if (u.c != '\\' || lc == '\\')
292 *rv++ = u.c;
293 lc = (lc == '\\') ? '\0' : u.c;
295 *rv = '\0';
297 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
299 } else {
300 if ((lc = (*++vp == '{')))
301 ++vp;
303 /* POSIX says
304 * Environment variable names used by the utilities in the Shell and
305 * Utilities volume of POSIX.1-2008 consist solely of uppercase
306 * letters, digits, and the <underscore> ('_') from the characters
307 * defined in Portable Character Set and do not begin with a digit.
308 * Other characters may be permitted by an implementation;
309 * applications shall tolerate the presence of such names. */
310 shsp->shs_dat = vp;
311 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
312 if (!alnumchar(c) && c != '_')
313 break;
315 if (lc) {
316 if (c != '}') {
317 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
318 shsp->shs_value);
319 shsp->shs_len = strlen(shsp->shs_value);
320 shsp->shs_dat = shsp->shs_value;
321 if (shsp->shs_err != NULL)
322 *shsp->shs_err = TRU1;
323 goto junroll;
325 c = *++vp;
328 shsp->shs_len = i;
329 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
330 shsp->shs_len = strlen(shsp->shs_dat = cp);
332 if (c != '\0')
333 goto jrecurse;
335 /* That level made the great and completed encoding. Build result */
336 junroll:
337 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
338 i += np->shs_len;
339 tmp = np->shs_next;
340 np->shs_next = shsp;
341 shsp = np;
342 np = tmp;
345 cp = rv = salloc(i +1);
346 while (shsp != NULL) {
347 np = shsp;
348 shsp = shsp->shs_next;
349 memcpy(cp, np->shs_dat, np->shs_len);
350 cp += np->shs_len;
352 *cp = '\0';
354 jleave:
355 NYD2_LEAVE;
356 return rv;
357 jrecurse:
358 memset(&next, 0, sizeof next);
359 next.shs_next = shsp;
360 next.shs_value = vp;
361 next.shs_err = shsp->shs_err;
362 next.shs_bsesc = shsp->shs_bsesc;
363 rv = _sh_exp_var(&next);
364 goto jleave;
367 FL void
368 n_raise(int signo)
370 NYD2_ENTER;
371 kill(getpid(), signo);
372 NYD2_LEAVE;
375 FL sighandler_type
376 safe_signal(int signum, sighandler_type handler)
378 struct sigaction nact, oact;
379 sighandler_type rv;
380 NYD2_ENTER;
382 nact.sa_handler = handler;
383 sigemptyset(&nact.sa_mask);
384 nact.sa_flags = 0;
385 #ifdef SA_RESTART
386 nact.sa_flags |= SA_RESTART;
387 #endif
388 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
389 NYD2_LEAVE;
390 return rv;
393 FL void
394 hold_all_sigs(void)
396 NYD2_ENTER;
397 if (_alls_depth++ == 0) {
398 sigfillset(&_alls_nset);
399 sigdelset(&_alls_nset, SIGABRT);
400 #ifdef SIGBUS
401 sigdelset(&_alls_nset, SIGBUS);
402 #endif
403 sigdelset(&_alls_nset, SIGCHLD);
404 sigdelset(&_alls_nset, SIGFPE);
405 sigdelset(&_alls_nset, SIGILL);
406 sigdelset(&_alls_nset, SIGKILL);
407 sigdelset(&_alls_nset, SIGSEGV);
408 sigdelset(&_alls_nset, SIGSTOP);
409 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
411 NYD2_LEAVE;
414 FL void
415 rele_all_sigs(void)
417 NYD2_ENTER;
418 if (--_alls_depth == 0)
419 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
420 NYD2_LEAVE;
423 FL void
424 hold_sigs(void)
426 NYD2_ENTER;
427 if (_hold_sigdepth++ == 0) {
428 sigemptyset(&_hold_nset);
429 sigaddset(&_hold_nset, SIGHUP);
430 sigaddset(&_hold_nset, SIGINT);
431 sigaddset(&_hold_nset, SIGQUIT);
432 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
434 NYD2_LEAVE;
437 FL void
438 rele_sigs(void)
440 NYD2_ENTER;
441 if (--_hold_sigdepth == 0)
442 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
443 NYD2_LEAVE;
446 #ifdef HAVE_NYD
447 FL void
448 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
450 struct nyd_info *nip = _nyd_infos;
452 if (_nyd_curr != NELEM(_nyd_infos))
453 nip += _nyd_curr++;
454 else
455 _nyd_curr = 1;
456 nip->ni_file = file;
457 nip->ni_fun = fun;
458 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
459 nip->ni_level = ((act == 0) ? _nyd_level
460 : (act == 1) ? ++_nyd_level : _nyd_level--);
463 FL void
464 _nyd_oncrash(int signo)
466 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
467 struct sigaction xact;
468 sigset_t xset;
469 size_t i, fnl;
470 int fd;
471 struct nyd_info *nip;
473 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
475 xact.sa_handler = SIG_DFL;
476 sigemptyset(&xact.sa_mask);
477 xact.sa_flags = 0;
478 sigaction(signo, &xact, NULL);
480 i = strlen(tempdir);
481 fnl = sizeof(UAGENT) -1;
483 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
484 (cp = pathbuf)[0] = '.';
485 i = 1;
486 } else
487 memcpy(cp = pathbuf, tempdir, i);
488 cp[i++] = '/'; /* xxx pathsep */
489 memcpy(cp += i, UAGENT, fnl);
490 i += fnl;
491 memcpy(cp += fnl, ".dat", sizeof(".dat"));
492 fnl = i + sizeof(".dat") -1;
494 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
495 fd = STDERR_FILENO;
497 # undef _X
498 # define _X(X) (X), sizeof(X) -1
499 write(fd, _X("\n\nNYD: program dying due to signal "));
501 cp = s2ibuf + sizeof(s2ibuf) -1;
502 *cp = '\0';
503 i = signo;
504 do {
505 *--cp = "0123456789"[i % 10];
506 i /= 10;
507 } while (i != 0);
508 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
510 write(fd, _X(":\n"));
512 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
513 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
514 _nyd_print(fd, nip++);
515 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
516 _nyd_print(fd, nip++);
518 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
520 if (fd != STDERR_FILENO) {
521 write(STDERR_FILENO, _X("Crash NYD listing written to "));
522 write(STDERR_FILENO, pathbuf, fnl);
523 write(STDERR_FILENO, _X("\n"));
524 # undef _X
526 close(fd);
529 sigemptyset(&xset);
530 sigaddset(&xset, signo);
531 sigprocmask(SIG_UNBLOCK, &xset, NULL);
532 n_raise(signo);
533 for (;;)
534 _exit(EXIT_ERR);
536 #endif /* HAVE_NYD */
538 FL void
539 touch(struct message *mp)
541 NYD_ENTER;
542 mp->m_flag |= MTOUCH;
543 if (!(mp->m_flag & MREAD))
544 mp->m_flag |= MREAD | MSTATUS;
545 NYD_LEAVE;
548 FL bool_t
549 is_dir(char const *name)
551 struct stat sbuf;
552 bool_t rv;
553 NYD_ENTER;
555 for (rv = FAL0;;)
556 if (!stat(name, &sbuf)) {
557 rv = (S_ISDIR(sbuf.st_mode) != 0);
558 break;
559 } else if (errno != EINTR)
560 break;
561 NYD_LEAVE;
562 return rv;
565 FL int
566 argcount(char **argv)
568 char **ap;
569 NYD_ENTER;
571 for (ap = argv; *ap++ != NULL;)
573 NYD_LEAVE;
574 return (int)PTR2SIZE(ap - argv - 1);
577 FL int
578 screensize(void)
580 int s;
581 char *cp;
582 NYD_ENTER;
584 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
585 s = scrnheight - 2; /* XXX no magics */
586 NYD_LEAVE;
587 return s;
590 FL char const *
591 get_pager(char const **env_addon)
593 char const *cp;
594 NYD_ENTER;
596 cp = ok_vlook(PAGER);
597 if (cp == NULL || *cp == '\0')
598 cp = XPAGER;
600 if (env_addon != NULL) {
601 *env_addon = NULL;
602 if (strstr(cp, "less") != NULL) {
603 if (!env_blook("LESS", TRU1))
604 *env_addon = "LESS=FRSXi";
605 } else if (strstr(cp, "lv") != NULL) {
606 if (!env_blook("LV", TRU1))
607 *env_addon = "LV=-c";
610 NYD_LEAVE;
611 return cp;
614 FL void
615 page_or_print(FILE *fp, size_t lines)
617 int c;
618 char const *cp;
619 NYD_ENTER;
621 fflush_rewind(fp);
623 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
624 char *eptr;
625 union {sl_i sli; size_t rows;} u;
627 u.sli = strtol(cp, &eptr, 0);
628 u.rows = (*cp != '\0' && *eptr == '\0')
629 ? (size_t)u.sli : (size_t)scrnheight;
631 if (u.rows > 0 && lines == 0) {
632 while ((c = getc(fp)) != EOF)
633 if (c == '\n' && ++lines >= u.rows)
634 break;
635 really_rewind(fp);
638 if (lines >= u.rows) {
639 run_command(get_pager(NULL), 0, fileno(fp), COMMAND_FD_PASS,
640 NULL, NULL, NULL, NULL);
641 goto jleave;
645 while ((c = getc(fp)) != EOF)
646 putchar(c);
647 jleave:
648 NYD_LEAVE;
651 FL enum protocol
652 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
654 struct stat st;
655 char const *cp;
656 char *np;
657 size_t sz;
658 enum protocol rv = PROTO_UNKNOWN;
659 NYD_ENTER;
661 temporary_protocol_ext = NULL;
663 if (name[0] == '%' && name[1] == ':')
664 name += 2;
665 for (cp = name; *cp && *cp != ':'; cp++)
666 if (!alnumchar(*cp))
667 goto jfile;
669 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
670 if (!strncmp(name, "pop3://", 7)) {
671 #ifdef HAVE_POP3
672 rv = PROTO_POP3;
673 #else
674 n_err(_("No POP3 support compiled in\n"));
675 #endif
676 } else if (!strncmp(name, "pop3s://", 8)) {
677 #if defined HAVE_POP3 && defined HAVE_SSL
678 rv = PROTO_POP3;
679 #else
680 # ifndef HAVE_POP3
681 n_err(_("No POP3 support compiled in\n"));
682 # endif
683 # ifndef HAVE_SSL
684 n_err(_("No SSL support compiled in\n"));
685 # endif
686 #endif
687 } else if (!strncmp(name, "imap://", 7)) {
688 #ifdef HAVE_IMAP
689 rv = PROTO_IMAP;
690 #else
691 fprintf(stderr, _("No IMAP support compiled in.\n"));
692 #endif
693 } else if (!strncmp(name, "imaps://", 8)) {
694 #if defined HAVE_IMAP && defined HAVE_SSL
695 rv = PROTO_IMAP;
696 #else
697 # ifndef HAVE_IMAP
698 fprintf(stderr, _("No IMAP support compiled in.\n"));
699 # endif
700 # ifndef HAVE_SSL
701 fprintf(stderr, _("No SSL support compiled in.\n"));
702 # endif
703 #endif
705 goto jleave;
708 /* TODO This is the de facto maildir code and thus belongs into there!
709 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
710 * TODO or (more likely) in addition to *newfolders*) */
711 jfile:
712 rv = PROTO_FILE;
713 np = ac_alloc((sz = strlen(name)) + 4 +1);
714 memcpy(np, name, sz + 1);
715 if (!stat(name, &st)) {
716 if (S_ISDIR(st.st_mode) &&
717 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
718 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
719 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
720 rv = PROTO_MAILDIR;
721 } else {
722 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
723 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
724 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
725 temporary_protocol_ext = cp;
726 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
727 rv = PROTO_MAILDIR;
729 ac_free(np);
730 jleave:
731 NYD_LEAVE;
732 return rv;
735 FL ui32_t
736 torek_hash(char const *name)
738 /* Chris Torek's hash.
739 * NOTE: need to change *at least* create-okey-map.pl when changing the
740 * algorithm!! */
741 ui32_t h = 0;
742 NYD_ENTER;
744 while (*name != '\0') {
745 h *= 33;
746 h += *name++;
748 NYD_LEAVE;
749 return h;
752 FL unsigned
753 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
755 unsigned h = 0, g;
756 NYD_ENTER;
758 cp--;
759 while (*++cp) {
760 h = (h << 4 & 0xffffffff) + (*cp&0377);
761 if ((g = h & 0xf0000000) != 0) {
762 h = h ^ g >> 24;
763 h = h ^ g;
766 NYD_LEAVE;
767 return h;
770 FL ui32_t
771 nextprime(ui32_t n)
773 static ui32_t const primes[] = {
774 5, 11, 23, 47, 97, 157, 283,
775 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
776 131071, 262139, 524287, 1048573, 2097143, 4194301,
777 8388593, 16777213, 33554393, 67108859, 134217689,
778 268435399, 536870909, 1073741789, 2147483647
781 ui32_t i, mprime;
782 NYD_ENTER;
784 i = (n < primes[NELEM(primes) / 4] ? 0
785 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
786 : NELEM(primes) / 2));
788 if ((mprime = primes[i]) > n)
789 break;
790 while (++i < NELEM(primes));
791 if (i == NELEM(primes) && mprime < n)
792 mprime = n;
793 NYD_LEAVE;
794 return mprime;
797 FL char *
798 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
800 struct passwd *pwp;
801 size_t nl, rl;
802 char const *rp, *np;
803 char *rv;
804 bool_t err;
805 NYD2_ENTER;
807 err = FAL0;
809 if (s[0] != '~')
810 goto jasis;
812 if (*(rp = s + 1) == '/' || *rp == '\0')
813 np = homedir;
814 else {
815 if ((rp = strchr(s + 1, '/')) == NULL)
816 rp = (np = UNCONST(s)) + 1;
817 else {
818 nl = PTR2SIZE(rp - s);
819 np = savestrbuf(s, nl);
822 if ((pwp = getpwnam(np)) == NULL) {
823 err = TRU1;
824 goto jasis;
826 np = pwp->pw_name;
829 nl = strlen(np);
830 rl = strlen(rp);
831 rv = salloc(nl + 1 + rl +1);
832 memcpy(rv, np, nl);
833 if (rl > 0) {
834 memcpy(rv + nl, rp, rl);
835 nl += rl;
837 rv[nl] = '\0';
838 goto jleave;
840 jasis:
841 rv = savestr(s);
842 jleave:
843 if (err_or_null != NULL)
844 *err_or_null = err;
845 NYD2_LEAVE;
846 return rv;
849 FL char *
850 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
852 struct shvar_stack top;
853 char *rv;
854 NYD2_ENTER;
856 memset(&top, 0, sizeof top);
858 top.shs_value = s;
859 if ((top.shs_err = err_or_null) != NULL)
860 *err_or_null = FAL0;
861 top.shs_bsesc = bsescape;
862 rv = _sh_exp_var(&top);
863 NYD2_LEAVE;
864 return rv;
867 FL int
868 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
870 char const *xs;
871 int c, n;
872 NYD2_ENTER;
874 xs = *s;
876 if ((c = *xs & 0xFF) == '\0')
877 goto jleave;
878 ++xs;
879 if (c != '\\')
880 goto jleave;
882 switch ((c = *xs & 0xFF)) {
883 case '\\': break;
884 case 'a': c = '\a'; break;
885 case 'b': c = '\b'; break;
886 case 'c': c = PROMPT_STOP; break;
887 case 'f': c = '\f'; break;
888 case 'n': c = '\n'; break;
889 case 'r': c = '\r'; break;
890 case 't': c = '\t'; break;
891 case 'v': c = '\v'; break;
892 case '0':
893 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
894 c <<= 3;
895 c |= *xs - '0';
897 goto jleave;
898 /* S-nail extension for nice (get)prompt(()) support */
899 case '&':
900 case '?':
901 case '$':
902 case '@':
903 if (use_nail_extensions) {
904 switch (c) {
905 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
906 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
907 case '$': c = PROMPT_DOLLAR; break;
908 case '@': c = PROMPT_AT; break;
910 break;
912 /* FALLTHRU */
913 case '\0':
914 /* A sole <backslash> at EOS is treated as-is! */
915 /* FALLTHRU */
916 default:
917 c = '\\';
918 goto jleave;
920 ++xs;
921 jleave:
922 *s = xs;
923 NYD2_LEAVE;
924 return c;
927 FL char *
928 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
930 static char buf[PROMPT_BUFFER_SIZE];
932 char *cp;
933 char const *ccp_base, *ccp;
934 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
935 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
936 NYD_ENTER;
938 /* No other place to place this */
939 #ifdef HAVE_ERRORS
940 if (options & OPT_INTERACTIVE) {
941 if (!(pstate & PS_ERRORS_NOTED) && _err_head != NULL) {
942 pstate |= PS_ERRORS_NOTED;
943 fprintf(stderr, _("There are new messages in the error message ring "
944 "(denoted by \"#ERR#\")\n"
945 " The `errors' command manages this message ring\n"));
948 if ((trigger = (_err_cnt_noted != _err_cnt)))
949 _err_cnt_noted = _err_cnt;
950 } else
951 trigger = FAL0;
952 #endif
954 cp = buf;
955 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
956 #ifdef HAVE_ERRORS
957 if (trigger)
958 ccp_base = "";
959 else
960 #endif
961 goto jleave;
963 #ifdef HAVE_ERRORS
964 if (trigger)
965 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
966 #endif
967 NATCH_CHAR( cclen_base = strlen(ccp_base); )
969 dfmaxlen = 0; /* keep CC happy */
970 trigger = FAL0;
971 jredo:
972 ccp = ccp_base;
973 NATCH_CHAR( cclen = cclen_base; )
974 maxlen = sizeof(buf) -1;
976 for (;;) {
977 size_t l;
978 int c;
980 if (maxlen == 0)
981 goto jleave;
982 #ifdef HAVE_NATCH_CHAR
983 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
984 if (c <= 0) {
985 mblen(NULL, 0);
986 if (c < 0) {
987 *buf = '?';
988 cp = buf + 1;
989 goto jleave;
991 break;
992 } else if ((l = c) > 1) {
993 if (trigger) {
994 memcpy(cp, ccp, l);
995 cp += l;
997 ccp += l;
998 maxlen -= l;
999 continue;
1000 } else
1001 #endif
1002 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
1003 if (trigger)
1004 *cp++ = (char)c;
1005 --maxlen;
1006 continue;
1008 if (c == 0 || c == PROMPT_STOP)
1009 break;
1011 if (trigger) {
1012 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
1013 if (a == NULL)
1014 a = "";
1015 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
1016 cp += l;
1017 maxlen -= l;
1018 dfmaxlen -= l;
1023 if (!trigger) {
1024 trigger = TRU1;
1025 dfmaxlen = maxlen;
1026 goto jredo;
1028 jleave:
1029 *cp = '\0';
1030 NYD_LEAVE;
1031 return buf;
1034 FL char *
1035 nodename(int mayoverride)
1037 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1039 struct utsname ut;
1040 char *hn;
1041 #ifdef HAVE_SOCKETS
1042 # ifdef HAVE_GETADDRINFO
1043 struct addrinfo hints, *res;
1044 # else
1045 struct hostent *hent;
1046 # endif
1047 #endif
1048 NYD_ENTER;
1050 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
1052 } else if ((hn = sys_hostname) == NULL) {
1053 uname(&ut);
1054 hn = ut.nodename;
1055 #ifdef HAVE_SOCKETS
1056 # ifdef HAVE_GETADDRINFO
1057 memset(&hints, 0, sizeof hints);
1058 hints.ai_family = AF_UNSPEC;
1059 hints.ai_flags = AI_CANONNAME;
1060 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
1061 if (res->ai_canonname != NULL) {
1062 size_t l = strlen(res->ai_canonname) +1;
1064 hn = ac_alloc(l);
1065 memcpy(hn, res->ai_canonname, l);
1067 freeaddrinfo(res);
1069 # else
1070 hent = gethostbyname(hn);
1071 if (hent != NULL)
1072 hn = hent->h_name;
1073 # endif
1074 #endif
1075 sys_hostname = sstrdup(hn);
1076 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1077 if (hn != ut.nodename)
1078 ac_free(hn);
1079 #endif
1080 hn = sys_hostname;
1083 if (hostname != NULL && hostname != sys_hostname)
1084 free(hostname);
1085 hostname = sstrdup(hn);
1086 NYD_LEAVE;
1087 return hostname;
1090 FL char *
1091 getrandstring(size_t length)
1093 struct str b64;
1094 char *data;
1095 size_t i;
1096 NYD_ENTER;
1098 #ifndef HAVE_POSIX_RANDOM
1099 if (_rand == NULL)
1100 _rand_init();
1101 #endif
1103 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1104 * with PAD stripped is still longer than what the user requests, easy way */
1105 data = ac_alloc(i = length + 3);
1107 #ifndef HAVE_POSIX_RANDOM
1108 while (i-- > 0)
1109 data[i] = (char)_rand_get8();
1110 #else
1111 { char *cp = data;
1113 while (i > 0) {
1114 union {ui32_t i4; char c[4];} r;
1115 size_t j;
1117 r.i4 = (ui32_t)arc4random();
1118 switch ((j = i & 3)) {
1119 case 0: cp[3] = r.c[3]; j = 4;
1120 case 3: cp[2] = r.c[2];
1121 case 2: cp[1] = r.c[1];
1122 default: cp[0] = r.c[0]; break;
1124 cp += j;
1125 i -= j;
1128 #endif
1130 b64_encode_buf(&b64, data, length + 3,
1131 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1132 ac_free(data);
1134 assert(b64.l >= length);
1135 b64.s[length] = '\0';
1136 NYD_LEAVE;
1137 return b64.s;
1140 FL enum okay
1141 makedir(char const *name)
1143 struct stat st;
1144 enum okay rv = STOP;
1145 NYD_ENTER;
1147 if (!mkdir(name, 0700))
1148 rv = OKAY;
1149 else {
1150 int e = errno;
1151 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1152 S_ISDIR(st.st_mode))
1153 rv = OKAY;
1155 NYD_LEAVE;
1156 return rv;
1159 #ifdef HAVE_FCHDIR
1160 FL enum okay
1161 cwget(struct cw *cw)
1163 enum okay rv = STOP;
1164 NYD_ENTER;
1166 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1167 goto jleave;
1168 if (fchdir(cw->cw_fd) == -1) {
1169 close(cw->cw_fd);
1170 goto jleave;
1172 rv = OKAY;
1173 jleave:
1174 NYD_LEAVE;
1175 return rv;
1178 FL enum okay
1179 cwret(struct cw *cw)
1181 enum okay rv = STOP;
1182 NYD_ENTER;
1184 if (!fchdir(cw->cw_fd))
1185 rv = OKAY;
1186 NYD_LEAVE;
1187 return rv;
1190 FL void
1191 cwrelse(struct cw *cw)
1193 NYD_ENTER;
1194 close(cw->cw_fd);
1195 NYD_LEAVE;
1198 #else /* !HAVE_FCHDIR */
1199 FL enum okay
1200 cwget(struct cw *cw)
1202 enum okay rv = STOP;
1203 NYD_ENTER;
1205 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1206 rv = OKAY;
1207 NYD_LEAVE;
1208 return rv;
1211 FL enum okay
1212 cwret(struct cw *cw)
1214 enum okay rv = STOP;
1215 NYD_ENTER;
1217 if (!chdir(cw->cw_wd))
1218 rv = OKAY;
1219 NYD_LEAVE;
1220 return rv;
1223 FL void
1224 cwrelse(struct cw *cw)
1226 NYD_ENTER;
1227 UNUSED(cw);
1228 NYD_LEAVE;
1230 #endif /* !HAVE_FCHDIR */
1232 FL size_t
1233 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1235 size_t rv;
1236 NYD_ENTER;
1238 #ifdef HAVE_NATCH_CHAR
1239 maxlen = MIN(maxlen, blen);
1240 for (rv = 0; maxlen > 0;) {
1241 int ml = mblen(buf, maxlen);
1242 if (ml <= 0) {
1243 mblen(NULL, 0);
1244 break;
1246 buf += ml;
1247 rv += ml;
1248 maxlen -= ml;
1250 #else
1251 rv = MIN(blen, maxlen);
1252 #endif
1253 NYD_LEAVE;
1254 return rv;
1257 FL size_t
1258 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1260 NATCH_CHAR( struct bidi_info bi; )
1261 size_t rv NATCH_CHAR( COMMA i );
1262 NYD_ENTER;
1264 rv = 0;
1265 if (maxlen-- == 0)
1266 goto j_leave;
1268 #ifdef HAVE_NATCH_CHAR
1269 bidi_info_create(&bi);
1270 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1271 bi.bi_end.l = 0;
1272 goto jnobidi;
1275 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1276 maxlen -= i;
1277 else
1278 goto jleave;
1280 if ((i = bi.bi_start.l) > 0) {
1281 memcpy(store, bi.bi_start.s, i);
1282 store += i;
1283 rv += i;
1286 jnobidi:
1287 while (maxlen > 0) {
1288 int ml = mblen(buf, blen);
1289 if (ml <= 0) {
1290 mblen(NULL, 0);
1291 break;
1293 if (UICMP(z, maxlen, <, ml))
1294 break;
1295 if (ml == 1)
1296 *store = *buf;
1297 else
1298 memcpy(store, buf, ml);
1299 store += ml;
1300 buf += ml;
1301 rv += ml;
1302 maxlen -= ml;
1305 if ((i = bi.bi_end.l) > 0) {
1306 memcpy(store, bi.bi_end.s, i);
1307 store += i;
1308 rv += i;
1310 jleave:
1311 *store = '\0';
1313 #else
1314 rv = MIN(blen, maxlen);
1315 memcpy(store, buf, rv);
1316 store[rv] = '\0';
1317 #endif
1318 j_leave:
1319 NYD_LEAVE;
1320 return rv;
1323 FL char *
1324 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1326 NATCH_CHAR( struct bidi_info bi; )
1327 int col_orig = col, n, sz;
1328 bool_t isbidi, isuni, istab, isrepl;
1329 char *nb, *np;
1330 NYD_ENTER;
1332 /* Bidi only on request and when there is 8-bit data */
1333 isbidi = isuni = FAL0;
1334 #ifdef HAVE_NATCH_CHAR
1335 isuni = ((options & OPT_UNICODE) != 0);
1336 bidi_info_create(&bi);
1337 if (bi.bi_start.l == 0)
1338 goto jnobidi;
1339 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1340 goto jnobidi;
1342 if ((size_t)col >= bi.bi_pad)
1343 col -= bi.bi_pad;
1344 else
1345 col = 0;
1346 jnobidi:
1347 #endif
1349 np = nb = salloc(mb_cur_max * strlen(cp) +
1350 ((fill ? col : 0)
1351 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1352 +1));
1354 #ifdef HAVE_NATCH_CHAR
1355 if (isbidi) {
1356 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1357 np += bi.bi_start.l;
1359 #endif
1361 while (*cp != '\0') {
1362 istab = FAL0;
1363 #ifdef HAVE_C90AMEND1
1364 if (mb_cur_max > 1) {
1365 wchar_t wc;
1367 n = 1;
1368 isrepl = TRU1;
1369 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1370 sz = 1;
1371 else if (wc == L'\t') {
1372 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1373 isrepl = FAL0;
1374 istab = TRU1;
1375 } else if (iswprint(wc)) {
1376 # ifndef HAVE_WCWIDTH
1377 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1378 # else
1379 if ((n = wcwidth(wc)) == -1)
1380 n = 1;
1381 else
1382 # endif
1383 isrepl = FAL0;
1385 } else
1386 #endif
1388 n = sz = 1;
1389 istab = (*cp == '\t');
1390 isrepl = !(istab || isprint((uc_i)*cp));
1393 if (n > col)
1394 break;
1395 col -= n;
1397 if (isrepl) {
1398 if (isuni) {
1399 np[0] = (char)0xEFu;
1400 np[1] = (char)0xBFu;
1401 np[2] = (char)0xBDu;
1402 np += 3;
1403 } else
1404 *np++ = '?';
1405 cp += sz;
1406 } else if (istab || (sz == 1 && spacechar(*cp))) {
1407 *np++ = ' ';
1408 ++cp;
1409 } else
1410 while (sz--)
1411 *np++ = *cp++;
1414 if (fill && col != 0) {
1415 if (fill > 0) {
1416 memmove(nb + col, nb, PTR2SIZE(np - nb));
1417 memset(nb, ' ', col);
1418 } else
1419 memset(np, ' ', col);
1420 np += col;
1421 col = 0;
1424 #ifdef HAVE_NATCH_CHAR
1425 if (isbidi) {
1426 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1427 np += bi.bi_end.l;
1429 #endif
1431 *np = '\0';
1432 if (cols_decr_used_or_null != NULL)
1433 *cols_decr_used_or_null -= col_orig - col;
1434 NYD_LEAVE;
1435 return nb;
1438 FL void
1439 makeprint(struct str const *in, struct str *out)
1441 static int print_all_chars = -1;
1443 char const *inp, *maxp;
1444 char *outp;
1445 DBG( size_t msz; )
1446 NYD_ENTER;
1448 if (print_all_chars == -1)
1449 print_all_chars = ok_blook(print_all_chars);
1451 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1452 inp = in->s;
1453 maxp = inp + in->l;
1455 if (print_all_chars) {
1456 out->l = in->l;
1457 memcpy(outp, inp, out->l);
1458 goto jleave;
1461 #ifdef HAVE_NATCH_CHAR
1462 if (mb_cur_max > 1) {
1463 char mbb[MB_LEN_MAX + 1];
1464 wchar_t wc;
1465 int i, n;
1466 bool_t isuni = ((options & OPT_UNICODE) != 0);
1468 out->l = 0;
1469 while (inp < maxp) {
1470 if (*inp & 0200)
1471 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1472 else {
1473 wc = *inp;
1474 n = 1;
1476 if (n == -1) {
1477 /* FIXME Why mbtowc() resetting here?
1478 * FIXME what about ISO 2022-JP plus -- those
1479 * FIXME will loose shifts, then!
1480 * FIXME THUS - we'd need special "known points"
1481 * FIXME to do so - say, after a newline!!
1482 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1483 mbtowc(&wc, NULL, mb_cur_max);
1484 wc = isuni ? 0xFFFD : '?';
1485 n = 1;
1486 } else if (n == 0)
1487 n = 1;
1488 inp += n;
1489 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1490 wc != '\t') {
1491 if ((wc & ~(wchar_t)037) == 0)
1492 wc = isuni ? 0x2400 | wc : '?';
1493 else if (wc == 0177)
1494 wc = isuni ? 0x2421 : '?';
1495 else
1496 wc = isuni ? 0x2426 : '?';
1497 }else if(isuni){ /* TODO ctext */
1498 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1499 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1500 continue;
1501 /* And some zero-width messes */
1502 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1503 continue;
1504 /* Oh about the ISO C wide character interfaces, baby! */
1505 if(wc == 0xFEFF)
1506 continue;
1508 if ((n = wctomb(mbb, wc)) <= 0)
1509 continue;
1510 out->l += n;
1511 assert(out->l < msz);
1512 for (i = 0; i < n; ++i)
1513 *outp++ = mbb[i];
1515 } else
1516 #endif /* NATCH_CHAR */
1518 int c;
1519 while (inp < maxp) {
1520 c = *inp++ & 0377;
1521 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1522 c = '?';
1523 *outp++ = c;
1525 out->l = in->l;
1527 jleave:
1528 out->s[out->l] = '\0';
1529 NYD_LEAVE;
1532 FL size_t
1533 delctrl(char *cp, size_t len)
1535 size_t x, y;
1536 NYD_ENTER;
1538 for (x = y = 0; x < len; ++x)
1539 if (!cntrlchar(cp[x]))
1540 cp[y++] = cp[x];
1541 cp[y] = '\0';
1542 NYD_LEAVE;
1543 return y;
1546 FL char *
1547 prstr(char const *s)
1549 struct str in, out;
1550 char *rp;
1551 NYD_ENTER;
1553 in.s = UNCONST(s);
1554 in.l = strlen(s);
1555 makeprint(&in, &out);
1556 rp = savestrbuf(out.s, out.l);
1557 free(out.s);
1558 NYD_LEAVE;
1559 return rp;
1562 FL int
1563 prout(char const *s, size_t sz, FILE *fp)
1565 struct str in, out;
1566 int n;
1567 NYD_ENTER;
1569 in.s = UNCONST(s);
1570 in.l = sz;
1571 makeprint(&in, &out);
1572 n = fwrite(out.s, 1, out.l, fp);
1573 free(out.s);
1574 NYD_LEAVE;
1575 return n;
1578 FL size_t
1579 putuc(int u, int c, FILE *fp)
1581 size_t rv;
1582 NYD_ENTER;
1583 UNUSED(u);
1585 #ifdef HAVE_NATCH_CHAR
1586 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1587 char mbb[MB_LEN_MAX];
1588 int i, n;
1590 if ((n = wctomb(mbb, u)) > 0) {
1591 rv = wcwidth(u);
1592 for (i = 0; i < n; ++i)
1593 if (putc(mbb[i] & 0377, fp) == EOF) {
1594 rv = 0;
1595 break;
1597 } else if (n == 0)
1598 rv = (putc('\0', fp) != EOF);
1599 else
1600 rv = 0;
1601 } else
1602 #endif
1603 rv = (putc(c, fp) != EOF);
1604 NYD_LEAVE;
1605 return rv;
1608 FL bool_t
1609 bidi_info_needed(char const *bdat, size_t blen)
1611 bool_t rv = FAL0;
1612 NYD_ENTER;
1614 #ifdef HAVE_NATCH_CHAR
1615 if (options & OPT_UNICODE)
1616 while (blen > 0) {
1617 /* TODO Checking for BIDI character: use S-CText fromutf8
1618 * TODO plus isrighttoleft (or whatever there will be)! */
1619 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1620 if (c == UI32_MAX)
1621 break;
1623 if (c <= 0x05BE)
1624 continue;
1626 /* (Very very fuzzy, awaiting S-CText for good) */
1627 if ((c >= 0x05BE && c <= 0x08E3) ||
1628 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1629 (c >= 0xFE70 && c <= 0xFEFC) ||
1630 (c >= 0x10800 && c <= 0x10C48) ||
1631 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1632 rv = TRU1;
1633 break;
1636 #endif /* HAVE_NATCH_CHAR */
1637 NYD_LEAVE;
1638 return rv;
1641 FL void
1642 bidi_info_create(struct bidi_info *bip)
1644 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1645 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1646 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1647 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1648 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1649 NATCH_CHAR( char const *hb; )
1650 NYD_ENTER;
1652 memset(bip, 0, sizeof *bip);
1653 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1655 #ifdef HAVE_NATCH_CHAR
1656 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1657 switch (*hb) {
1658 case '3':
1659 bip->bi_pad = 2;
1660 /* FALLTHRU */
1661 case '2':
1662 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1663 break;
1664 case '1':
1665 bip->bi_pad = 2;
1666 /* FALLTHRU */
1667 default:
1668 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1669 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1670 break;
1672 bip->bi_start.l = bip->bi_end.l = 3;
1674 #endif
1675 NYD_LEAVE;
1678 FL si8_t
1679 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1681 char *dat, *eptr;
1682 sl_i sli;
1683 si8_t rv;
1684 NYD_ENTER;
1686 assert(inlen == 0 || inbuf != NULL);
1688 if (inlen == UIZ_MAX)
1689 inlen = strlen(inbuf);
1691 if (inlen == 0)
1692 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1693 else {
1694 if ((inlen == 1 && *inbuf == '1') ||
1695 !ascncasecmp(inbuf, "true", inlen) ||
1696 !ascncasecmp(inbuf, "yes", inlen) ||
1697 !ascncasecmp(inbuf, "on", inlen))
1698 rv = 1;
1699 else if ((inlen == 1 && *inbuf == '0') ||
1700 !ascncasecmp(inbuf, "false", inlen) ||
1701 !ascncasecmp(inbuf, "no", inlen) ||
1702 !ascncasecmp(inbuf, "off", inlen))
1703 rv = 0;
1704 else {
1705 dat = ac_alloc(inlen +1);
1706 memcpy(dat, inbuf, inlen);
1707 dat[inlen] = '\0';
1709 sli = strtol(dat, &eptr, 0);
1710 if (*dat != '\0' && *eptr == '\0')
1711 rv = (sli != 0);
1712 else
1713 rv = -1;
1715 ac_free(dat);
1718 NYD_LEAVE;
1719 return rv;
1722 FL si8_t
1723 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1725 si8_t rv;
1726 NYD_ENTER;
1728 assert(inlen == 0 || inbuf != NULL);
1730 if (inlen == UIZ_MAX)
1731 inlen = strlen(inbuf);
1733 if (inlen == 0)
1734 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1735 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1736 !ascncasecmp(inbuf, "ask-", 4) &&
1737 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1738 (options & OPT_INTERACTIVE)) {
1739 if (prompt != NULL)
1740 fputs(prompt, stdout);
1741 rv = getapproval(NULL, rv);
1743 NYD_LEAVE;
1744 return rv;
1747 FL time_t
1748 n_time_epoch(void)
1750 #ifdef HAVE_CLOCK_GETTIME
1751 struct timespec ts;
1752 #elif defined HAVE_GETTIMEOFDAY
1753 struct timeval ts;
1754 #endif
1755 time_t rv;
1756 NYD2_ENTER;
1758 #ifdef HAVE_CLOCK_GETTIME
1759 clock_gettime(CLOCK_REALTIME, &ts);
1760 rv = (time_t)ts.tv_sec;
1761 #elif defined HAVE_GETTIMEOFDAY
1762 gettimeofday(&ts, NULL);
1763 rv = (time_t)ts.tv_sec;
1764 #else
1765 rv = time(NULL);
1766 #endif
1767 NYD2_LEAVE;
1768 return rv;
1771 FL void
1772 time_current_update(struct time_current *tc, bool_t full_update)
1774 NYD_ENTER;
1775 tc->tc_time = n_time_epoch();
1776 if (full_update) {
1777 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1778 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1779 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1781 NYD_LEAVE;
1784 FL void
1785 n_err(char const *format, ...)
1787 va_list ap;
1788 NYD2_ENTER;
1790 va_start(ap, format);
1791 #ifdef HAVE_ERRORS
1792 if (options & OPT_INTERACTIVE)
1793 n_verr(format, ap);
1794 else
1795 #endif
1797 vfprintf(stderr, format, ap);
1798 if (strchr(format, '\n') != NULL) /* TODO */
1799 fflush(stderr);
1801 va_end(ap);
1802 NYD2_LEAVE;
1805 FL void
1806 n_verr(char const *format, va_list ap)
1808 /* Check use cases of PS_ERRORS_NOTED, too! */
1809 #ifdef HAVE_ERRORS
1810 char buf[LINESIZE], *xbuf;
1811 int lmax, l;
1812 struct err_node *enp;
1814 LCTA(ERRORS_MAX > 3);
1815 #endif
1816 NYD2_ENTER;
1818 #ifdef HAVE_ERRORS
1819 if (!(options & OPT_INTERACTIVE))
1820 #endif
1822 vfprintf(stderr, format, ap);
1823 goto jleave;
1826 #ifdef HAVE_ERRORS
1827 xbuf = buf;
1828 lmax = sizeof buf;
1829 jredo:
1830 l = vsnprintf(xbuf, lmax, format, ap);
1831 if (l <= 0)
1832 goto jleave;
1833 if (UICMP(z, l, >=, lmax)) {
1834 /* FIXME Cannot reuse va_list
1835 lmax = ++l;
1836 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1837 goto jredo;
1841 fwrite(xbuf, 1, l, stderr);
1843 /* Link it into the `errors' message ring */
1844 if ((enp = _err_tail) == NULL) {
1845 jcreat:
1846 enp = scalloc(1, sizeof *enp);
1847 if (_err_tail != NULL)
1848 _err_tail->en_next = enp;
1849 else
1850 _err_head = enp;
1851 _err_tail = enp;
1852 ++_err_cnt;
1853 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
1854 if (_err_cnt < ERRORS_MAX)
1855 goto jcreat;
1857 _err_head = (enp = _err_head)->en_next;
1858 _err_tail->en_next = enp;
1859 _err_tail = enp;
1860 free(enp->en_str.s);
1861 memset(enp, 0, sizeof *enp);
1864 n_str_add_buf(&enp->en_str, xbuf, l);
1866 if (xbuf != buf)
1867 free(xbuf);
1868 #endif /* HAVE_ERRORS */
1870 jleave:
1871 if (strchr(format, '\n') != NULL) /* TODO */
1872 fflush(stderr);
1873 NYD2_LEAVE;
1876 FL void
1877 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
1879 va_list ap;
1880 NYD_X;
1882 va_start(ap, format);
1883 vfprintf(stderr, format, ap);
1884 va_end(ap);
1885 fflush(stderr);
1888 FL void
1889 n_perr(char const *msg, int errval)
1891 char const *fmt;
1892 NYD2_ENTER;
1894 if (msg == NULL) {
1895 fmt = "%s%s\n";
1896 msg = "";
1897 } else
1898 fmt = "%s: %s\n";
1900 if (errval == 0)
1901 errval = errno;
1903 n_err(fmt, msg, strerror(errval));
1904 NYD2_LEAVE;
1907 FL void
1908 n_alert(char const *format, ...)
1910 va_list ap;
1911 NYD2_ENTER;
1913 n_err(_("Alert: "));
1915 va_start(ap, format);
1916 n_verr(format, ap);
1917 va_end(ap);
1919 n_err("\n");
1920 NYD2_LEAVE;
1923 FL void
1924 n_panic(char const *format, ...)
1926 va_list ap;
1927 NYD2_ENTER;
1929 fprintf(stderr, _("Panic: "));
1931 va_start(ap, format);
1932 vfprintf(stderr, format, ap);
1933 va_end(ap);
1935 putc('\n', stderr);
1936 fflush(stderr);
1937 NYD2_LEAVE;
1938 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1941 #ifdef HAVE_ERRORS
1942 FL int
1943 c_errors(void *v)
1945 char **argv = v;
1946 struct err_node *enp;
1947 NYD_ENTER;
1949 if (*argv == NULL)
1950 goto jlist;
1951 if (argv[1] != NULL)
1952 goto jerr;
1953 if (!asccasecmp(*argv, "show"))
1954 goto jlist;
1955 if (!asccasecmp(*argv, "clear"))
1956 goto jclear;
1957 jerr:
1958 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1959 v = NULL;
1960 jleave:
1961 NYD_LEAVE;
1962 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1964 jlist: {
1965 FILE *fp;
1966 size_t i;
1968 if (_err_head == NULL) {
1969 fprintf(stderr, _("The error ring is empty\n"));
1970 goto jleave;
1973 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1974 NULL) {
1975 fprintf(stderr, _("tmpfile"));
1976 v = NULL;
1977 goto jleave;
1980 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
1981 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1982 ++i, enp->en_str.l, enp->en_str.s);
1983 /* We don't know wether last string ended with NL; be simple */
1984 putc('\n', fp);
1986 page_or_print(fp, 0);
1987 Fclose(fp);
1989 /* FALLTHRU */
1991 jclear:
1992 _err_tail = NULL;
1993 _err_cnt = _err_cnt_noted = 0;
1994 while ((enp = _err_head) != NULL) {
1995 _err_head = enp->en_next;
1996 free(enp->en_str.s);
1997 free(enp);
1999 goto jleave;
2001 #endif /* HAVE_ERRORS */
2003 #ifndef HAVE_DEBUG
2004 FL void *
2005 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2007 void *rv;
2008 NYD2_ENTER;
2010 if (s == 0)
2011 s = 1;
2012 if ((rv = malloc(s)) == NULL)
2013 n_panic(_("no memory"));
2014 NYD2_LEAVE;
2015 return rv;
2018 FL void *
2019 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2021 void *rv;
2022 NYD2_ENTER;
2024 if (s == 0)
2025 s = 1;
2026 if (v == NULL)
2027 rv = smalloc(s);
2028 else if ((rv = realloc(v, s)) == NULL)
2029 n_panic(_("no memory"));
2030 NYD2_LEAVE;
2031 return rv;
2034 FL void *
2035 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2037 void *rv;
2038 NYD2_ENTER;
2040 if (size == 0)
2041 size = 1;
2042 if ((rv = calloc(nmemb, size)) == NULL)
2043 n_panic(_("no memory"));
2044 NYD2_LEAVE;
2045 return rv;
2048 #else /* !HAVE_DEBUG */
2049 CTA(sizeof(char) == sizeof(ui8_t));
2051 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2052 # define _HOPE_SET(C) \
2053 do {\
2054 union mem_ptr __xl, __xu;\
2055 struct mem_chunk *__xc;\
2056 __xl.p_p = (C).p_p;\
2057 __xc = __xl.p_c - 1;\
2058 __xu.p_p = __xc;\
2059 (C).p_cp += 8;\
2060 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2061 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2062 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2063 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2064 __xu.p_ui8p += __xc->mc_size - 8;\
2065 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2066 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2067 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2068 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2069 } while (0)
2070 # define _HOPE_GET_TRACE(C,BAD) \
2071 do {\
2072 (C).p_cp += 8;\
2073 _HOPE_GET(C, BAD);\
2074 (C).p_cp += 8;\
2075 } while(0)
2076 # define _HOPE_GET(C,BAD) \
2077 do {\
2078 union mem_ptr __xl, __xu;\
2079 struct mem_chunk *__xc;\
2080 ui32_t __i;\
2081 __xl.p_p = (C).p_p;\
2082 __xl.p_cp -= 8;\
2083 (C).p_cp = __xl.p_cp;\
2084 __xc = __xl.p_c - 1;\
2085 (BAD) = FAL0;\
2086 __i = 0;\
2087 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2088 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2089 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2090 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2091 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2092 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2093 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2094 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2095 if (__i != 0) {\
2096 (BAD) = TRU1;\
2097 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2098 __xl.p_p, __i, mdbg_file, mdbg_line);\
2100 __xu.p_p = __xc;\
2101 __xu.p_ui8p += __xc->mc_size - 8;\
2102 __i = 0;\
2103 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2104 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2105 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2106 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2107 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2108 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2109 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2110 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2111 if (__i != 0) {\
2112 (BAD) = TRU1;\
2113 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2114 __xl.p_p, __i, mdbg_file, mdbg_line);\
2116 if (BAD)\
2117 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2118 __xc->mc_file, __xc->mc_line);\
2119 } while (0)
2121 FL void *
2122 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2124 union mem_ptr p;
2125 NYD2_ENTER;
2127 if (s == 0)
2128 s = 1;
2129 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2130 n_panic("smalloc(): allocation too large: %s, line %d",
2131 mdbg_file, mdbg_line);
2132 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2134 if ((p.p_p = (malloc)(s)) == NULL)
2135 n_panic(_("no memory"));
2136 p.p_c->mc_prev = NULL;
2137 if ((p.p_c->mc_next = _mem_list) != NULL)
2138 _mem_list->mc_prev = p.p_c;
2139 p.p_c->mc_file = mdbg_file;
2140 p.p_c->mc_line = (ui16_t)mdbg_line;
2141 p.p_c->mc_isfree = FAL0;
2142 p.p_c->mc_size = (ui32_t)s;
2144 _mem_list = p.p_c++;
2145 _HOPE_SET(p);
2147 ++_mem_aall;
2148 ++_mem_acur;
2149 _mem_amax = MAX(_mem_amax, _mem_acur);
2150 _mem_mall += s;
2151 _mem_mcur += s;
2152 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2153 NYD2_LEAVE;
2154 return p.p_p;
2157 FL void *
2158 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2160 union mem_ptr p;
2161 bool_t isbad;
2162 NYD2_ENTER;
2164 if ((p.p_p = v) == NULL) {
2165 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2166 goto jleave;
2169 _HOPE_GET(p, isbad);
2170 --p.p_c;
2171 if (p.p_c->mc_isfree) {
2172 n_err("srealloc(): region freed! At %s, line %d\n"
2173 "\tLast seen: %s, line %" PRIu16 "\n",
2174 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2175 goto jforce;
2178 if (p.p_c == _mem_list)
2179 _mem_list = p.p_c->mc_next;
2180 else
2181 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2182 if (p.p_c->mc_next != NULL)
2183 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2185 --_mem_acur;
2186 _mem_mcur -= p.p_c->mc_size;
2187 jforce:
2188 if (s == 0)
2189 s = 1;
2190 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2191 n_panic("srealloc(): allocation too large: %s, line %d",
2192 mdbg_file, mdbg_line);
2193 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2195 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2196 n_panic(_("no memory"));
2197 p.p_c->mc_prev = NULL;
2198 if ((p.p_c->mc_next = _mem_list) != NULL)
2199 _mem_list->mc_prev = p.p_c;
2200 p.p_c->mc_file = mdbg_file;
2201 p.p_c->mc_line = (ui16_t)mdbg_line;
2202 p.p_c->mc_isfree = FAL0;
2203 p.p_c->mc_size = (ui32_t)s;
2204 _mem_list = p.p_c++;
2205 _HOPE_SET(p);
2207 ++_mem_aall;
2208 ++_mem_acur;
2209 _mem_amax = MAX(_mem_amax, _mem_acur);
2210 _mem_mall += s;
2211 _mem_mcur += s;
2212 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2213 jleave:
2214 NYD2_LEAVE;
2215 return p.p_p;
2218 FL void *
2219 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2221 union mem_ptr p;
2222 NYD2_ENTER;
2224 if (size == 0)
2225 size = 1;
2226 if (nmemb == 0)
2227 nmemb = 1;
2228 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2229 n_panic("scalloc(): allocation size too large: %s, line %d",
2230 mdbg_file, mdbg_line);
2231 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2232 n_panic("scalloc(): allocation count too large: %s, line %d",
2233 mdbg_file, mdbg_line);
2235 size *= nmemb;
2236 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2238 if ((p.p_p = (malloc)(size)) == NULL)
2239 n_panic(_("no memory"));
2240 memset(p.p_p, 0, size);
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)size;
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 += size;
2255 _mem_mcur += size;
2256 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2257 NYD2_LEAVE;
2258 return p.p_p;
2261 FL void
2262 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2264 union mem_ptr p;
2265 bool_t isbad;
2266 NYD2_ENTER;
2268 if ((p.p_p = v) == NULL) {
2269 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2270 goto jleave;
2273 _HOPE_GET(p, isbad);
2274 --p.p_c;
2275 if (p.p_c->mc_isfree) {
2276 n_err("sfree(): double-free avoided at %s, line %d\n"
2277 "\tLast seen: %s, line %" PRIu16 "\n",
2278 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2279 goto jleave;
2282 if (p.p_c == _mem_list)
2283 _mem_list = p.p_c->mc_next;
2284 else
2285 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2286 if (p.p_c->mc_next != NULL)
2287 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2288 p.p_c->mc_isfree = TRU1;
2289 /* Trash contents (also see [21c05f8]) */
2290 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2292 --_mem_acur;
2293 _mem_mcur -= p.p_c->mc_size;
2295 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2296 p.p_c->mc_next = _mem_free;
2297 _mem_free = p.p_c;
2298 } else
2299 (free)(p.p_c);
2300 jleave:
2301 NYD2_LEAVE;
2304 FL void
2305 smemreset(void)
2307 union mem_ptr p;
2308 size_t c = 0, s = 0;
2309 NYD_ENTER;
2311 smemcheck();
2313 for (p.p_c = _mem_free; p.p_c != NULL;) {
2314 void *vp = p.p_c;
2315 ++c;
2316 s += p.p_c->mc_size;
2317 p.p_c = p.p_c->mc_next;
2318 (free)(vp);
2320 _mem_free = NULL;
2322 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2323 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2324 NYD_LEAVE;
2327 FL int
2328 c_smemtrace(void *v)
2330 /* For _HOPE_GET() */
2331 char const * const mdbg_file = "smemtrace()";
2332 int const mdbg_line = -1;
2333 FILE *fp;
2334 union mem_ptr p, xp;
2335 bool_t isbad;
2336 size_t lines;
2337 NYD_ENTER;
2339 v = (void*)0x1;
2340 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2341 n_perr("tmpfile", 0);
2342 goto jleave;
2345 fprintf(fp, "Memory statistics:\n"
2346 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2347 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2348 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2350 fprintf(fp, "Currently allocated memory chunks:\n");
2351 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2352 ++lines, p.p_c = p.p_c->mc_next) {
2353 xp = p;
2354 ++xp.p_c;
2355 _HOPE_GET_TRACE(xp, isbad);
2356 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2357 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2358 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2359 p.p_c->mc_line);
2362 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2363 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2364 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2365 xp = p;
2366 ++xp.p_c;
2367 _HOPE_GET_TRACE(xp, isbad);
2368 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2369 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2370 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2371 p.p_c->mc_file, p.p_c->mc_line);
2375 page_or_print(fp, lines);
2376 Fclose(fp);
2377 v = NULL;
2378 jleave:
2379 NYD_LEAVE;
2380 return (v != NULL);
2383 # ifdef HAVE_DEVEL
2384 FL bool_t
2385 _smemcheck(char const *mdbg_file, int mdbg_line)
2387 union mem_ptr p, xp;
2388 bool_t anybad = FAL0, isbad;
2389 size_t lines;
2390 NYD_ENTER;
2392 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2393 ++lines, p.p_c = p.p_c->mc_next) {
2394 xp = p;
2395 ++xp.p_c;
2396 _HOPE_GET_TRACE(xp, isbad);
2397 if (isbad) {
2398 anybad = TRU1;
2399 n_err(
2400 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2401 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2402 p.p_c->mc_file, p.p_c->mc_line);
2406 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2407 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2408 xp = p;
2409 ++xp.p_c;
2410 _HOPE_GET_TRACE(xp, isbad);
2411 if (isbad) {
2412 anybad = TRU1;
2413 n_err(
2414 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2415 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2416 p.p_c->mc_file, p.p_c->mc_line);
2420 NYD_LEAVE;
2421 return anybad;
2423 # endif /* HAVE_DEVEL */
2425 # undef _HOPE_SIZE
2426 # undef _HOPE_SET
2427 # undef _HOPE_GET_TRACE
2428 # undef _HOPE_GET
2429 #endif /* HAVE_DEBUG */
2431 /* s-it-mode */