Add `customhdr' (Sergey Matveev)..
[s-mailx.git] / auxlily.c
blob5430852af6af7454a72b1126c997bb76b3150581
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 a_aux_err_node{
88 struct a_aux_err_node *ae_next;
89 struct str ae_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 a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
133 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
134 #endif
135 static size_t a_aux_err_dirty;
137 #ifdef HAVE_DEBUG
138 static size_t _mem_aall, _mem_acur, _mem_amax,
139 _mem_mall, _mem_mcur, _mem_mmax;
141 static struct mem_chunk *_mem_list, *_mem_free;
142 #endif
144 /* Our ARC4 random generator with its completely unacademical pseudo
145 * initialization (shall /dev/urandom fail) */
146 #ifndef HAVE_POSIX_RANDOM
147 static void _rand_init(void);
148 static ui32_t _rand_weak(ui32_t seed);
149 SINLINE ui8_t _rand_get8(void);
150 #endif
152 #ifdef HAVE_NYD
153 static void _nyd_print(int fd, struct nyd_info *nip);
154 #endif
156 /* Perform shell variable expansion */
157 static char * _sh_exp_var(struct shvar_stack *shsp);
159 #ifndef HAVE_POSIX_RANDOM
160 static void
161 _rand_init(void)
163 # ifdef HAVE_CLOCK_GETTIME
164 struct timespec ts;
165 # else
166 struct timeval ts;
167 # endif
168 union {int fd; size_t i;} u;
169 ui32_t seed, rnd;
170 NYD2_ENTER;
172 _rand = smalloc(sizeof *_rand);
174 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
175 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
177 close(u.fd);
178 if (ok)
179 goto jleave;
182 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
183 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
184 size_t t, k;
186 # ifdef HAVE_CLOCK_GETTIME
187 clock_gettime(CLOCK_REALTIME, &ts);
188 t = (ui32_t)ts.tv_nsec;
189 # else
190 gettimeofday(&ts, NULL);
191 t = (ui32_t)ts.tv_usec;
192 # endif
193 if (rnd & 1)
194 t = (t >> 16) | (t << 16);
195 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
196 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
197 if (rnd == 7 || rnd == 17)
198 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
199 k = _rand->b32[u.i] % NELEM(_rand->b32);
200 _rand->b32[k] ^= _rand->b32[u.i];
201 seed ^= _rand_weak(_rand->b32[k]);
202 if ((rnd & 3) == 3)
203 seed ^= nextprime(seed);
207 for (u.i = 11 * sizeof(_rand->b8); u.i != 0; --u.i)
208 _rand_get8();
209 jleave:
210 NYD2_LEAVE;
213 static ui32_t
214 _rand_weak(ui32_t seed)
216 /* From "Random number generators: good ones are hard to find",
217 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
218 * October 1988, p. 1195.
219 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
220 ui32_t hi;
222 if (seed == 0)
223 seed = 123459876;
224 hi = seed / 127773;
225 seed %= 127773;
226 seed = (seed * 16807) - (hi * 2836);
227 if ((si32_t)seed < 0)
228 seed += SI32_MAX;
229 return seed;
232 SINLINE ui8_t
233 _rand_get8(void)
235 ui8_t si, sj;
237 si = _rand->a._dat[++_rand->a._i];
238 sj = _rand->a._dat[_rand->a._j += si];
239 _rand->a._dat[_rand->a._i] = sj;
240 _rand->a._dat[_rand->a._j] = si;
241 return _rand->a._dat[(ui8_t)(si + sj)];
243 #endif /* HAVE_POSIX_RANDOM */
245 #ifdef HAVE_NYD
246 static void
247 _nyd_print(int fd, struct nyd_info *nip)
249 char buf[80];
250 union {int i; size_t z;} u;
252 u.i = snprintf(buf, sizeof buf,
253 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
254 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
255 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
256 if (u.i > 0) {
257 u.z = u.i;
258 if (u.z > sizeof buf)
259 u.z = sizeof buf - 1; /* (Skip \0) */
260 write(fd, buf, u.z);
263 #endif
265 static char *
266 _sh_exp_var(struct shvar_stack *shsp)
268 struct shvar_stack next, *np, *tmp;
269 char const *vp;
270 char lc, c, *cp, *rv;
271 size_t i;
272 NYD2_ENTER;
274 if (*(vp = shsp->shs_value) != '$') {
275 bool_t bsesc = shsp->shs_bsesc;
276 union {bool_t hadbs; char c;} u = {FAL0};
278 shsp->shs_dat = vp;
279 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
280 if (c == '$' && lc != '\\')
281 break;
282 if (!bsesc)
283 continue;
284 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
286 shsp->shs_len = i;
288 if (u.hadbs) {
289 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
291 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
292 if (u.c != '\\' || lc == '\\')
293 *rv++ = u.c;
294 lc = (lc == '\\') ? '\0' : u.c;
296 *rv = '\0';
298 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
300 } else {
301 if ((lc = (*++vp == '{')))
302 ++vp;
304 /* POSIX says
305 * Environment variable names used by the utilities in the Shell and
306 * Utilities volume of POSIX.1-2008 consist solely of uppercase
307 * letters, digits, and the <underscore> ('_') from the characters
308 * defined in Portable Character Set and do not begin with a digit.
309 * Other characters may be permitted by an implementation;
310 * applications shall tolerate the presence of such names. */
311 shsp->shs_dat = vp;
312 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
313 if (!alnumchar(c) && c != '_')
314 break;
316 if (lc) {
317 if (c != '}') {
318 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
319 shsp->shs_value);
320 shsp->shs_len = strlen(shsp->shs_value);
321 shsp->shs_dat = shsp->shs_value;
322 if (shsp->shs_err != NULL)
323 *shsp->shs_err = TRU1;
324 goto junroll;
326 c = *++vp;
329 shsp->shs_len = i;
330 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
331 shsp->shs_len = strlen(shsp->shs_dat = cp);
333 if (c != '\0')
334 goto jrecurse;
336 /* That level made the great and completed encoding. Build result */
337 junroll:
338 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
339 i += np->shs_len;
340 tmp = np->shs_next;
341 np->shs_next = shsp;
342 shsp = np;
343 np = tmp;
346 cp = rv = salloc(i +1);
347 while (shsp != NULL) {
348 np = shsp;
349 shsp = shsp->shs_next;
350 memcpy(cp, np->shs_dat, np->shs_len);
351 cp += np->shs_len;
353 *cp = '\0';
355 jleave:
356 NYD2_LEAVE;
357 return rv;
358 jrecurse:
359 memset(&next, 0, sizeof next);
360 next.shs_next = shsp;
361 next.shs_value = vp;
362 next.shs_err = shsp->shs_err;
363 next.shs_bsesc = shsp->shs_bsesc;
364 rv = _sh_exp_var(&next);
365 goto jleave;
368 FL void
369 n_raise(int signo)
371 NYD2_ENTER;
372 kill(getpid(), signo);
373 NYD2_LEAVE;
376 FL sighandler_type
377 safe_signal(int signum, sighandler_type handler)
379 struct sigaction nact, oact;
380 sighandler_type rv;
381 NYD2_ENTER;
383 nact.sa_handler = handler;
384 sigemptyset(&nact.sa_mask);
385 nact.sa_flags = 0;
386 #ifdef SA_RESTART
387 nact.sa_flags |= SA_RESTART;
388 #endif
389 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
390 NYD2_LEAVE;
391 return rv;
394 FL void
395 hold_all_sigs(void)
397 NYD2_ENTER;
398 if (_alls_depth++ == 0) {
399 sigfillset(&_alls_nset);
400 sigdelset(&_alls_nset, SIGABRT);
401 #ifdef SIGBUS
402 sigdelset(&_alls_nset, SIGBUS);
403 #endif
404 sigdelset(&_alls_nset, SIGCHLD);
405 sigdelset(&_alls_nset, SIGFPE);
406 sigdelset(&_alls_nset, SIGILL);
407 sigdelset(&_alls_nset, SIGKILL);
408 sigdelset(&_alls_nset, SIGSEGV);
409 sigdelset(&_alls_nset, SIGSTOP);
410 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
412 NYD2_LEAVE;
415 FL void
416 rele_all_sigs(void)
418 NYD2_ENTER;
419 if (--_alls_depth == 0)
420 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
421 NYD2_LEAVE;
424 FL void
425 hold_sigs(void)
427 NYD2_ENTER;
428 if (_hold_sigdepth++ == 0) {
429 sigemptyset(&_hold_nset);
430 sigaddset(&_hold_nset, SIGHUP);
431 sigaddset(&_hold_nset, SIGINT);
432 sigaddset(&_hold_nset, SIGQUIT);
433 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
435 NYD2_LEAVE;
438 FL void
439 rele_sigs(void)
441 NYD2_ENTER;
442 if (--_hold_sigdepth == 0)
443 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
444 NYD2_LEAVE;
447 #ifdef HAVE_NYD
448 FL void
449 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
451 struct nyd_info *nip = _nyd_infos;
453 if (_nyd_curr != NELEM(_nyd_infos))
454 nip += _nyd_curr++;
455 else
456 _nyd_curr = 1;
457 nip->ni_file = file;
458 nip->ni_fun = fun;
459 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
460 nip->ni_level = ((act == 0) ? _nyd_level
461 : (act == 1) ? ++_nyd_level : _nyd_level--);
464 FL void
465 _nyd_oncrash(int signo)
467 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
468 struct sigaction xact;
469 sigset_t xset;
470 size_t i, fnl;
471 int fd;
472 struct nyd_info *nip;
474 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
476 xact.sa_handler = SIG_DFL;
477 sigemptyset(&xact.sa_mask);
478 xact.sa_flags = 0;
479 sigaction(signo, &xact, NULL);
481 i = strlen(tempdir);
482 fnl = sizeof(UAGENT) -1;
484 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
485 (cp = pathbuf)[0] = '.';
486 i = 1;
487 } else
488 memcpy(cp = pathbuf, tempdir, i);
489 cp[i++] = '/'; /* xxx pathsep */
490 memcpy(cp += i, UAGENT, fnl);
491 i += fnl;
492 memcpy(cp += fnl, ".dat", sizeof(".dat"));
493 fnl = i + sizeof(".dat") -1;
495 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
496 fd = STDERR_FILENO;
498 # undef _X
499 # define _X(X) (X), sizeof(X) -1
500 write(fd, _X("\n\nNYD: program dying due to signal "));
502 cp = s2ibuf + sizeof(s2ibuf) -1;
503 *cp = '\0';
504 i = signo;
505 do {
506 *--cp = "0123456789"[i % 10];
507 i /= 10;
508 } while (i != 0);
509 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
511 write(fd, _X(":\n"));
513 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
514 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
515 _nyd_print(fd, nip++);
516 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
517 _nyd_print(fd, nip++);
519 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
521 if (fd != STDERR_FILENO) {
522 write(STDERR_FILENO, _X("Crash NYD listing written to "));
523 write(STDERR_FILENO, pathbuf, fnl);
524 write(STDERR_FILENO, _X("\n"));
525 # undef _X
527 close(fd);
530 sigemptyset(&xset);
531 sigaddset(&xset, signo);
532 sigprocmask(SIG_UNBLOCK, &xset, NULL);
533 n_raise(signo);
534 for (;;)
535 _exit(EXIT_ERR);
537 #endif /* HAVE_NYD */
539 FL void
540 touch(struct message *mp)
542 NYD_ENTER;
543 mp->m_flag |= MTOUCH;
544 if (!(mp->m_flag & MREAD))
545 mp->m_flag |= MREAD | MSTATUS;
546 NYD_LEAVE;
549 FL bool_t
550 is_dir(char const *name)
552 struct stat sbuf;
553 bool_t rv;
554 NYD_ENTER;
556 for (rv = FAL0;;)
557 if (!stat(name, &sbuf)) {
558 rv = (S_ISDIR(sbuf.st_mode) != 0);
559 break;
560 } else if (errno != EINTR)
561 break;
562 NYD_LEAVE;
563 return rv;
566 FL int
567 argcount(char **argv)
569 char **ap;
570 NYD_ENTER;
572 for (ap = argv; *ap++ != NULL;)
574 NYD_LEAVE;
575 return (int)PTR2SIZE(ap - argv - 1);
578 FL int
579 screensize(void)
581 int s;
582 char *cp;
583 NYD_ENTER;
585 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
586 s = scrnheight - 2; /* XXX no magics */
587 NYD_LEAVE;
588 return s;
591 FL char const *
592 get_pager(char const **env_addon)
594 char const *cp;
595 NYD_ENTER;
597 cp = ok_vlook(PAGER);
598 if (cp == NULL || *cp == '\0')
599 cp = XPAGER;
601 if (env_addon != NULL) {
602 *env_addon = NULL;
603 if (strstr(cp, "less") != NULL) {
604 if (!env_blook("LESS", TRU1))
605 *env_addon = "LESS=FRSXi";
606 } else if (strstr(cp, "lv") != NULL) {
607 if (!env_blook("LV", TRU1))
608 *env_addon = "LV=-c";
611 NYD_LEAVE;
612 return cp;
615 FL void
616 page_or_print(FILE *fp, size_t lines)
618 int c;
619 char const *cp;
620 NYD_ENTER;
622 fflush_rewind(fp);
624 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
625 char *eptr;
626 union {sl_i sli; size_t rows;} u;
628 u.sli = strtol(cp, &eptr, 0);
629 u.rows = (*cp != '\0' && *eptr == '\0')
630 ? (size_t)u.sli : (size_t)scrnheight;
632 if (u.rows > 0 && lines == 0) {
633 while ((c = getc(fp)) != EOF)
634 if (c == '\n' && ++lines >= u.rows)
635 break;
636 really_rewind(fp);
639 if (lines >= u.rows) {
640 run_command(get_pager(NULL), 0, fileno(fp), COMMAND_FD_PASS,
641 NULL, NULL, NULL, NULL);
642 goto jleave;
646 while ((c = getc(fp)) != EOF)
647 putchar(c);
648 jleave:
649 NYD_LEAVE;
652 FL enum protocol
653 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
655 struct stat st;
656 char const *cp;
657 char *np;
658 size_t sz;
659 enum protocol rv = PROTO_UNKNOWN;
660 NYD_ENTER;
662 temporary_protocol_ext = NULL;
664 if (name[0] == '%' && name[1] == ':')
665 name += 2;
666 for (cp = name; *cp && *cp != ':'; cp++)
667 if (!alnumchar(*cp))
668 goto jfile;
670 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
671 if (!strncmp(name, "pop3://", 7)) {
672 #ifdef HAVE_POP3
673 rv = PROTO_POP3;
674 #else
675 n_err(_("No POP3 support compiled in\n"));
676 #endif
677 } else if (!strncmp(name, "pop3s://", 8)) {
678 #if defined HAVE_POP3 && defined HAVE_SSL
679 rv = PROTO_POP3;
680 #else
681 # ifndef HAVE_POP3
682 n_err(_("No POP3 support compiled in\n"));
683 # endif
684 # ifndef HAVE_SSL
685 n_err(_("No SSL support compiled in\n"));
686 # endif
687 #endif
689 goto jleave;
692 /* TODO This is the de facto maildir code and thus belongs into there!
693 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
694 * TODO or (more likely) in addition to *newfolders*) */
695 jfile:
696 rv = PROTO_FILE;
697 np = ac_alloc((sz = strlen(name)) + 4 +1);
698 memcpy(np, name, sz + 1);
699 if (!stat(name, &st)) {
700 if (S_ISDIR(st.st_mode) &&
701 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
702 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
703 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
704 rv = PROTO_MAILDIR;
705 } else {
706 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
707 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
708 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
709 temporary_protocol_ext = cp;
710 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
711 rv = PROTO_MAILDIR;
713 ac_free(np);
714 jleave:
715 NYD_LEAVE;
716 return rv;
719 FL ui32_t
720 torek_hash(char const *name)
722 /* Chris Torek's hash.
723 * NOTE: need to change *at least* create-okey-map.pl when changing the
724 * algorithm!! */
725 ui32_t h = 0;
726 NYD_ENTER;
728 while (*name != '\0') {
729 h *= 33;
730 h += *name++;
732 NYD_LEAVE;
733 return h;
736 FL unsigned
737 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
739 unsigned h = 0, g;
740 NYD_ENTER;
742 cp--;
743 while (*++cp) {
744 h = (h << 4 & 0xffffffff) + (*cp&0377);
745 if ((g = h & 0xf0000000) != 0) {
746 h = h ^ g >> 24;
747 h = h ^ g;
750 NYD_LEAVE;
751 return h;
754 FL ui32_t
755 nextprime(ui32_t n)
757 static ui32_t const primes[] = {
758 5, 11, 23, 47, 97, 157, 283,
759 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
760 131071, 262139, 524287, 1048573, 2097143, 4194301,
761 8388593, 16777213, 33554393, 67108859, 134217689,
762 268435399, 536870909, 1073741789, 2147483647
765 ui32_t i, mprime;
766 NYD_ENTER;
768 i = (n < primes[NELEM(primes) / 4] ? 0
769 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
770 : NELEM(primes) / 2));
772 if ((mprime = primes[i]) > n)
773 break;
774 while (++i < NELEM(primes));
775 if (i == NELEM(primes) && mprime < n)
776 mprime = n;
777 NYD_LEAVE;
778 return mprime;
781 FL char *
782 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
784 struct passwd *pwp;
785 size_t nl, rl;
786 char const *rp, *np;
787 char *rv;
788 bool_t err;
789 NYD2_ENTER;
791 err = FAL0;
793 if (s[0] != '~')
794 goto jasis;
796 if (*(rp = s + 1) == '/' || *rp == '\0')
797 np = homedir;
798 else {
799 if ((rp = strchr(s + 1, '/')) == NULL)
800 rp = (np = UNCONST(s)) + 1;
801 else {
802 nl = PTR2SIZE(rp - s);
803 np = savestrbuf(s, nl);
806 if ((pwp = getpwnam(np)) == NULL) {
807 err = TRU1;
808 goto jasis;
810 np = pwp->pw_name;
813 nl = strlen(np);
814 rl = strlen(rp);
815 rv = salloc(nl + 1 + rl +1);
816 memcpy(rv, np, nl);
817 if (rl > 0) {
818 memcpy(rv + nl, rp, rl);
819 nl += rl;
821 rv[nl] = '\0';
822 goto jleave;
824 jasis:
825 rv = savestr(s);
826 jleave:
827 if (err_or_null != NULL)
828 *err_or_null = err;
829 NYD2_LEAVE;
830 return rv;
833 FL char *
834 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
836 struct shvar_stack top;
837 char *rv;
838 NYD2_ENTER;
840 memset(&top, 0, sizeof top);
842 top.shs_value = s;
843 if ((top.shs_err = err_or_null) != NULL)
844 *err_or_null = FAL0;
845 top.shs_bsesc = bsescape;
846 rv = _sh_exp_var(&top);
847 NYD2_LEAVE;
848 return rv;
851 FL int
852 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
854 char const *xs;
855 int c, n;
856 NYD2_ENTER;
858 xs = *s;
860 if ((c = *xs & 0xFF) == '\0')
861 goto jleave;
862 ++xs;
863 if (c != '\\')
864 goto jleave;
866 switch ((c = *xs & 0xFF)) {
867 case '\\': break;
868 case 'a': c = '\a'; break;
869 case 'b': c = '\b'; break;
870 case 'c': c = PROMPT_STOP; break;
871 case 'f': c = '\f'; break;
872 case 'n': c = '\n'; break;
873 case 'r': c = '\r'; break;
874 case 't': c = '\t'; break;
875 case 'v': c = '\v'; break;
876 case '0':
877 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
878 c <<= 3;
879 c |= *xs - '0';
881 goto jleave;
882 /* S-nail extension for nice (get)prompt(()) support */
883 case '&':
884 case '?':
885 case '$':
886 case '@':
887 if (use_nail_extensions) {
888 switch (c) {
889 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
890 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
891 case '$': c = PROMPT_DOLLAR; break;
892 case '@': c = PROMPT_AT; break;
894 break;
896 /* FALLTHRU */
897 case '\0':
898 /* A sole <backslash> at EOS is treated as-is! */
899 /* FALLTHRU */
900 default:
901 c = '\\';
902 goto jleave;
904 ++xs;
905 jleave:
906 *s = xs;
907 NYD2_LEAVE;
908 return c;
911 FL char *
912 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
914 static char buf[PROMPT_BUFFER_SIZE];
916 char *cp;
917 char const *ccp_base, *ccp;
918 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
919 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
920 NYD_ENTER;
922 /* No other place to place this */
923 #ifdef HAVE_ERRORS
924 if (options & OPT_INTERACTIVE) {
925 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
926 pstate |= PS_ERRORS_NOTED;
927 fprintf(stderr, _("There are new messages in the error message ring "
928 "(denoted by \"#ERR#\")\n"
929 " The `errors' command manages this message ring\n"));
932 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
933 a_aux_err_cnt_noted = a_aux_err_cnt;
934 } else
935 trigger = FAL0;
936 #endif
938 cp = buf;
939 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
940 #ifdef HAVE_ERRORS
941 if (trigger)
942 ccp_base = "";
943 else
944 #endif
945 goto jleave;
947 #ifdef HAVE_ERRORS
948 if (trigger)
949 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
950 #endif
951 NATCH_CHAR( cclen_base = strlen(ccp_base); )
953 dfmaxlen = 0; /* keep CC happy */
954 trigger = FAL0;
955 jredo:
956 ccp = ccp_base;
957 NATCH_CHAR( cclen = cclen_base; )
958 maxlen = sizeof(buf) -1;
960 for (;;) {
961 size_t l;
962 int c;
964 if (maxlen == 0)
965 goto jleave;
966 #ifdef HAVE_NATCH_CHAR
967 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
968 if (c <= 0) {
969 mblen(NULL, 0);
970 if (c < 0) {
971 *buf = '?';
972 cp = buf + 1;
973 goto jleave;
975 break;
976 } else if ((l = c) > 1) {
977 if (trigger) {
978 memcpy(cp, ccp, l);
979 cp += l;
981 ccp += l;
982 maxlen -= l;
983 continue;
984 } else
985 #endif
986 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
987 if (trigger)
988 *cp++ = (char)c;
989 --maxlen;
990 continue;
992 if (c == 0 || c == PROMPT_STOP)
993 break;
995 if (trigger) {
996 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
997 if (a == NULL)
998 a = "";
999 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
1000 cp += l;
1001 maxlen -= l;
1002 dfmaxlen -= l;
1007 if (!trigger) {
1008 trigger = TRU1;
1009 dfmaxlen = maxlen;
1010 goto jredo;
1012 jleave:
1013 *cp = '\0';
1014 NYD_LEAVE;
1015 return buf;
1018 FL char *
1019 nodename(int mayoverride)
1021 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1023 struct utsname ut;
1024 char *hn;
1025 #ifdef HAVE_SOCKETS
1026 # ifdef HAVE_GETADDRINFO
1027 struct addrinfo hints, *res;
1028 # else
1029 struct hostent *hent;
1030 # endif
1031 #endif
1032 NYD_ENTER;
1034 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
1036 } else if ((hn = sys_hostname) == NULL) {
1037 uname(&ut);
1038 hn = ut.nodename;
1039 #ifdef HAVE_SOCKETS
1040 # ifdef HAVE_GETADDRINFO
1041 memset(&hints, 0, sizeof hints);
1042 hints.ai_family = AF_UNSPEC;
1043 hints.ai_flags = AI_CANONNAME;
1044 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
1045 if (res->ai_canonname != NULL) {
1046 size_t l = strlen(res->ai_canonname) +1;
1048 hn = ac_alloc(l);
1049 memcpy(hn, res->ai_canonname, l);
1051 freeaddrinfo(res);
1053 # else
1054 hent = gethostbyname(hn);
1055 if (hent != NULL)
1056 hn = hent->h_name;
1057 # endif
1058 #endif
1059 sys_hostname = sstrdup(hn);
1060 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1061 if (hn != ut.nodename)
1062 ac_free(hn);
1063 #endif
1064 hn = sys_hostname;
1067 if (hostname != NULL && hostname != sys_hostname)
1068 free(hostname);
1069 hostname = sstrdup(hn);
1070 NYD_LEAVE;
1071 return hostname;
1074 FL char *
1075 getrandstring(size_t length)
1077 struct str b64;
1078 char *data;
1079 size_t i;
1080 NYD_ENTER;
1082 #ifndef HAVE_POSIX_RANDOM
1083 if (_rand == NULL)
1084 _rand_init();
1085 #endif
1087 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1088 * with PAD stripped is still longer than what the user requests, easy way */
1089 data = ac_alloc(i = length + 3);
1091 #ifndef HAVE_POSIX_RANDOM
1092 while (i-- > 0)
1093 data[i] = (char)_rand_get8();
1094 #else
1095 { char *cp = data;
1097 while (i > 0) {
1098 union {ui32_t i4; char c[4];} r;
1099 size_t j;
1101 r.i4 = (ui32_t)arc4random();
1102 switch ((j = i & 3)) {
1103 case 0: cp[3] = r.c[3]; j = 4;
1104 case 3: cp[2] = r.c[2];
1105 case 2: cp[1] = r.c[1];
1106 default: cp[0] = r.c[0]; break;
1108 cp += j;
1109 i -= j;
1112 #endif
1114 b64_encode_buf(&b64, data, length + 3,
1115 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1116 ac_free(data);
1118 assert(b64.l >= length);
1119 b64.s[length] = '\0';
1120 NYD_LEAVE;
1121 return b64.s;
1124 FL enum okay
1125 makedir(char const *name)
1127 struct stat st;
1128 enum okay rv = STOP;
1129 NYD_ENTER;
1131 if (!mkdir(name, 0700))
1132 rv = OKAY;
1133 else {
1134 int e = errno;
1135 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1136 S_ISDIR(st.st_mode))
1137 rv = OKAY;
1139 NYD_LEAVE;
1140 return rv;
1143 #ifdef HAVE_FCHDIR
1144 FL enum okay
1145 cwget(struct cw *cw)
1147 enum okay rv = STOP;
1148 NYD_ENTER;
1150 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1151 goto jleave;
1152 if (fchdir(cw->cw_fd) == -1) {
1153 close(cw->cw_fd);
1154 goto jleave;
1156 rv = OKAY;
1157 jleave:
1158 NYD_LEAVE;
1159 return rv;
1162 FL enum okay
1163 cwret(struct cw *cw)
1165 enum okay rv = STOP;
1166 NYD_ENTER;
1168 if (!fchdir(cw->cw_fd))
1169 rv = OKAY;
1170 NYD_LEAVE;
1171 return rv;
1174 FL void
1175 cwrelse(struct cw *cw)
1177 NYD_ENTER;
1178 close(cw->cw_fd);
1179 NYD_LEAVE;
1182 #else /* !HAVE_FCHDIR */
1183 FL enum okay
1184 cwget(struct cw *cw)
1186 enum okay rv = STOP;
1187 NYD_ENTER;
1189 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1190 rv = OKAY;
1191 NYD_LEAVE;
1192 return rv;
1195 FL enum okay
1196 cwret(struct cw *cw)
1198 enum okay rv = STOP;
1199 NYD_ENTER;
1201 if (!chdir(cw->cw_wd))
1202 rv = OKAY;
1203 NYD_LEAVE;
1204 return rv;
1207 FL void
1208 cwrelse(struct cw *cw)
1210 NYD_ENTER;
1211 UNUSED(cw);
1212 NYD_LEAVE;
1214 #endif /* !HAVE_FCHDIR */
1216 FL size_t
1217 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1219 size_t rv;
1220 NYD_ENTER;
1222 #ifdef HAVE_NATCH_CHAR
1223 maxlen = MIN(maxlen, blen);
1224 for (rv = 0; maxlen > 0;) {
1225 int ml = mblen(buf, maxlen);
1226 if (ml <= 0) {
1227 mblen(NULL, 0);
1228 break;
1230 buf += ml;
1231 rv += ml;
1232 maxlen -= ml;
1234 #else
1235 rv = MIN(blen, maxlen);
1236 #endif
1237 NYD_LEAVE;
1238 return rv;
1241 FL size_t
1242 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1244 NATCH_CHAR( struct bidi_info bi; )
1245 size_t rv NATCH_CHAR( COMMA i );
1246 NYD_ENTER;
1248 rv = 0;
1249 if (maxlen-- == 0)
1250 goto j_leave;
1252 #ifdef HAVE_NATCH_CHAR
1253 bidi_info_create(&bi);
1254 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1255 bi.bi_end.l = 0;
1256 goto jnobidi;
1259 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1260 maxlen -= i;
1261 else
1262 goto jleave;
1264 if ((i = bi.bi_start.l) > 0) {
1265 memcpy(store, bi.bi_start.s, i);
1266 store += i;
1267 rv += i;
1270 jnobidi:
1271 while (maxlen > 0) {
1272 int ml = mblen(buf, blen);
1273 if (ml <= 0) {
1274 mblen(NULL, 0);
1275 break;
1277 if (UICMP(z, maxlen, <, ml))
1278 break;
1279 if (ml == 1)
1280 *store = *buf;
1281 else
1282 memcpy(store, buf, ml);
1283 store += ml;
1284 buf += ml;
1285 rv += ml;
1286 maxlen -= ml;
1289 if ((i = bi.bi_end.l) > 0) {
1290 memcpy(store, bi.bi_end.s, i);
1291 store += i;
1292 rv += i;
1294 jleave:
1295 *store = '\0';
1297 #else
1298 rv = MIN(blen, maxlen);
1299 memcpy(store, buf, rv);
1300 store[rv] = '\0';
1301 #endif
1302 j_leave:
1303 NYD_LEAVE;
1304 return rv;
1307 FL char *
1308 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1310 NATCH_CHAR( struct bidi_info bi; )
1311 int col_orig = col, n, sz;
1312 bool_t isbidi, isuni, istab, isrepl;
1313 char *nb, *np;
1314 NYD_ENTER;
1316 /* Bidi only on request and when there is 8-bit data */
1317 isbidi = isuni = FAL0;
1318 #ifdef HAVE_NATCH_CHAR
1319 isuni = ((options & OPT_UNICODE) != 0);
1320 bidi_info_create(&bi);
1321 if (bi.bi_start.l == 0)
1322 goto jnobidi;
1323 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1324 goto jnobidi;
1326 if ((size_t)col >= bi.bi_pad)
1327 col -= bi.bi_pad;
1328 else
1329 col = 0;
1330 jnobidi:
1331 #endif
1333 np = nb = salloc(mb_cur_max * strlen(cp) +
1334 ((fill ? col : 0)
1335 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1336 +1));
1338 #ifdef HAVE_NATCH_CHAR
1339 if (isbidi) {
1340 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1341 np += bi.bi_start.l;
1343 #endif
1345 while (*cp != '\0') {
1346 istab = FAL0;
1347 #ifdef HAVE_C90AMEND1
1348 if (mb_cur_max > 1) {
1349 wchar_t wc;
1351 n = 1;
1352 isrepl = TRU1;
1353 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1354 sz = 1;
1355 else if (wc == L'\t') {
1356 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1357 isrepl = FAL0;
1358 istab = TRU1;
1359 } else if (iswprint(wc)) {
1360 # ifndef HAVE_WCWIDTH
1361 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1362 # else
1363 if ((n = wcwidth(wc)) == -1)
1364 n = 1;
1365 else
1366 # endif
1367 isrepl = FAL0;
1369 } else
1370 #endif
1372 n = sz = 1;
1373 istab = (*cp == '\t');
1374 isrepl = !(istab || isprint((uc_i)*cp));
1377 if (n > col)
1378 break;
1379 col -= n;
1381 if (isrepl) {
1382 if (isuni) {
1383 np[0] = (char)0xEFu;
1384 np[1] = (char)0xBFu;
1385 np[2] = (char)0xBDu;
1386 np += 3;
1387 } else
1388 *np++ = '?';
1389 cp += sz;
1390 } else if (istab || (sz == 1 && spacechar(*cp))) {
1391 *np++ = ' ';
1392 ++cp;
1393 } else
1394 while (sz--)
1395 *np++ = *cp++;
1398 if (fill && col != 0) {
1399 if (fill > 0) {
1400 memmove(nb + col, nb, PTR2SIZE(np - nb));
1401 memset(nb, ' ', col);
1402 } else
1403 memset(np, ' ', col);
1404 np += col;
1405 col = 0;
1408 #ifdef HAVE_NATCH_CHAR
1409 if (isbidi) {
1410 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1411 np += bi.bi_end.l;
1413 #endif
1415 *np = '\0';
1416 if (cols_decr_used_or_null != NULL)
1417 *cols_decr_used_or_null -= col_orig - col;
1418 NYD_LEAVE;
1419 return nb;
1422 FL void
1423 makeprint(struct str const *in, struct str *out)
1425 static int print_all_chars = -1;
1427 char const *inp, *maxp;
1428 char *outp;
1429 DBG( size_t msz; )
1430 NYD_ENTER;
1432 if (print_all_chars == -1)
1433 print_all_chars = ok_blook(print_all_chars);
1435 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1436 inp = in->s;
1437 maxp = inp + in->l;
1439 if (print_all_chars) {
1440 out->l = in->l;
1441 memcpy(outp, inp, out->l);
1442 goto jleave;
1445 #ifdef HAVE_NATCH_CHAR
1446 if (mb_cur_max > 1) {
1447 char mbb[MB_LEN_MAX + 1];
1448 wchar_t wc;
1449 int i, n;
1450 bool_t isuni = ((options & OPT_UNICODE) != 0);
1452 out->l = 0;
1453 while (inp < maxp) {
1454 if (*inp & 0200)
1455 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1456 else {
1457 wc = *inp;
1458 n = 1;
1460 if (n == -1) {
1461 /* FIXME Why mbtowc() resetting here?
1462 * FIXME what about ISO 2022-JP plus -- those
1463 * FIXME will loose shifts, then!
1464 * FIXME THUS - we'd need special "known points"
1465 * FIXME to do so - say, after a newline!!
1466 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1467 mbtowc(&wc, NULL, mb_cur_max);
1468 wc = isuni ? 0xFFFD : '?';
1469 n = 1;
1470 } else if (n == 0)
1471 n = 1;
1472 inp += n;
1473 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1474 wc != '\t') {
1475 if ((wc & ~(wchar_t)037) == 0)
1476 wc = isuni ? 0x2400 | wc : '?';
1477 else if (wc == 0177)
1478 wc = isuni ? 0x2421 : '?';
1479 else
1480 wc = isuni ? 0x2426 : '?';
1481 }else if(isuni){ /* TODO ctext */
1482 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1483 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1484 continue;
1485 /* And some zero-width messes */
1486 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1487 continue;
1488 /* Oh about the ISO C wide character interfaces, baby! */
1489 if(wc == 0xFEFF)
1490 continue;
1492 if ((n = wctomb(mbb, wc)) <= 0)
1493 continue;
1494 out->l += n;
1495 assert(out->l < msz);
1496 for (i = 0; i < n; ++i)
1497 *outp++ = mbb[i];
1499 } else
1500 #endif /* NATCH_CHAR */
1502 int c;
1503 while (inp < maxp) {
1504 c = *inp++ & 0377;
1505 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1506 c = '?';
1507 *outp++ = c;
1509 out->l = in->l;
1511 jleave:
1512 out->s[out->l] = '\0';
1513 NYD_LEAVE;
1516 FL size_t
1517 delctrl(char *cp, size_t len)
1519 size_t x, y;
1520 NYD_ENTER;
1522 for (x = y = 0; x < len; ++x)
1523 if (!cntrlchar(cp[x]))
1524 cp[y++] = cp[x];
1525 cp[y] = '\0';
1526 NYD_LEAVE;
1527 return y;
1530 FL char *
1531 prstr(char const *s)
1533 struct str in, out;
1534 char *rp;
1535 NYD_ENTER;
1537 in.s = UNCONST(s);
1538 in.l = strlen(s);
1539 makeprint(&in, &out);
1540 rp = savestrbuf(out.s, out.l);
1541 free(out.s);
1542 NYD_LEAVE;
1543 return rp;
1546 FL int
1547 prout(char const *s, size_t sz, FILE *fp)
1549 struct str in, out;
1550 int n;
1551 NYD_ENTER;
1553 in.s = UNCONST(s);
1554 in.l = sz;
1555 makeprint(&in, &out);
1556 n = fwrite(out.s, 1, out.l, fp);
1557 free(out.s);
1558 NYD_LEAVE;
1559 return n;
1562 FL size_t
1563 putuc(int u, int c, FILE *fp)
1565 size_t rv;
1566 NYD_ENTER;
1567 UNUSED(u);
1569 #ifdef HAVE_NATCH_CHAR
1570 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1571 char mbb[MB_LEN_MAX];
1572 int i, n;
1574 if ((n = wctomb(mbb, u)) > 0) {
1575 rv = wcwidth(u);
1576 for (i = 0; i < n; ++i)
1577 if (putc(mbb[i] & 0377, fp) == EOF) {
1578 rv = 0;
1579 break;
1581 } else if (n == 0)
1582 rv = (putc('\0', fp) != EOF);
1583 else
1584 rv = 0;
1585 } else
1586 #endif
1587 rv = (putc(c, fp) != EOF);
1588 NYD_LEAVE;
1589 return rv;
1592 FL bool_t
1593 bidi_info_needed(char const *bdat, size_t blen)
1595 bool_t rv = FAL0;
1596 NYD_ENTER;
1598 #ifdef HAVE_NATCH_CHAR
1599 if (options & OPT_UNICODE)
1600 while (blen > 0) {
1601 /* TODO Checking for BIDI character: use S-CText fromutf8
1602 * TODO plus isrighttoleft (or whatever there will be)! */
1603 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1604 if (c == UI32_MAX)
1605 break;
1607 if (c <= 0x05BE)
1608 continue;
1610 /* (Very very fuzzy, awaiting S-CText for good) */
1611 if ((c >= 0x05BE && c <= 0x08E3) ||
1612 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1613 (c >= 0xFE70 && c <= 0xFEFC) ||
1614 (c >= 0x10800 && c <= 0x10C48) ||
1615 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1616 rv = TRU1;
1617 break;
1620 #endif /* HAVE_NATCH_CHAR */
1621 NYD_LEAVE;
1622 return rv;
1625 FL void
1626 bidi_info_create(struct bidi_info *bip)
1628 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1629 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1630 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1631 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1632 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1633 NATCH_CHAR( char const *hb; )
1634 NYD_ENTER;
1636 memset(bip, 0, sizeof *bip);
1637 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1639 #ifdef HAVE_NATCH_CHAR
1640 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1641 switch (*hb) {
1642 case '3':
1643 bip->bi_pad = 2;
1644 /* FALLTHRU */
1645 case '2':
1646 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1647 break;
1648 case '1':
1649 bip->bi_pad = 2;
1650 /* FALLTHRU */
1651 default:
1652 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1653 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1654 break;
1656 bip->bi_start.l = bip->bi_end.l = 3;
1658 #endif
1659 NYD_LEAVE;
1662 FL si8_t
1663 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1665 char *dat, *eptr;
1666 sl_i sli;
1667 si8_t rv;
1668 NYD_ENTER;
1670 assert(inlen == 0 || inbuf != NULL);
1672 if (inlen == UIZ_MAX)
1673 inlen = strlen(inbuf);
1675 if (inlen == 0)
1676 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1677 else {
1678 if ((inlen == 1 && *inbuf == '1') ||
1679 !ascncasecmp(inbuf, "true", inlen) ||
1680 !ascncasecmp(inbuf, "yes", inlen) ||
1681 !ascncasecmp(inbuf, "on", inlen))
1682 rv = 1;
1683 else if ((inlen == 1 && *inbuf == '0') ||
1684 !ascncasecmp(inbuf, "false", inlen) ||
1685 !ascncasecmp(inbuf, "no", inlen) ||
1686 !ascncasecmp(inbuf, "off", inlen))
1687 rv = 0;
1688 else {
1689 dat = ac_alloc(inlen +1);
1690 memcpy(dat, inbuf, inlen);
1691 dat[inlen] = '\0';
1693 sli = strtol(dat, &eptr, 0);
1694 if (*dat != '\0' && *eptr == '\0')
1695 rv = (sli != 0);
1696 else
1697 rv = -1;
1699 ac_free(dat);
1702 NYD_LEAVE;
1703 return rv;
1706 FL si8_t
1707 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1709 si8_t rv;
1710 NYD_ENTER;
1712 assert(inlen == 0 || inbuf != NULL);
1714 if (inlen == UIZ_MAX)
1715 inlen = strlen(inbuf);
1717 if (inlen == 0)
1718 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1719 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1720 !ascncasecmp(inbuf, "ask-", 4) &&
1721 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1722 (options & OPT_INTERACTIVE)) {
1723 if (prompt != NULL)
1724 fputs(prompt, stdout);
1725 rv = getapproval(NULL, rv);
1727 NYD_LEAVE;
1728 return rv;
1731 FL time_t
1732 n_time_epoch(void)
1734 #ifdef HAVE_CLOCK_GETTIME
1735 struct timespec ts;
1736 #elif defined HAVE_GETTIMEOFDAY
1737 struct timeval ts;
1738 #endif
1739 time_t rv;
1740 NYD2_ENTER;
1742 #ifdef HAVE_CLOCK_GETTIME
1743 clock_gettime(CLOCK_REALTIME, &ts);
1744 rv = (time_t)ts.tv_sec;
1745 #elif defined HAVE_GETTIMEOFDAY
1746 gettimeofday(&ts, NULL);
1747 rv = (time_t)ts.tv_sec;
1748 #else
1749 rv = time(NULL);
1750 #endif
1751 NYD2_LEAVE;
1752 return rv;
1755 FL void
1756 time_current_update(struct time_current *tc, bool_t full_update)
1758 NYD_ENTER;
1759 tc->tc_time = n_time_epoch();
1760 if (full_update) {
1761 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1762 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1763 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1765 NYD_LEAVE;
1768 FL void
1769 n_err(char const *format, ...){
1770 va_list ap;
1771 NYD2_ENTER;
1773 va_start(ap, format);
1774 #ifdef HAVE_ERRORS
1775 if(options & OPT_INTERACTIVE)
1776 n_verr(format, ap);
1777 else
1778 #endif
1780 if(a_aux_err_dirty++ == 0)
1781 fputs(UAGENT ": ", stderr);
1782 vfprintf(stderr, format, ap);
1783 if(strchr(format, '\n') != NULL){ /* TODO */
1784 a_aux_err_dirty = 0;
1785 fflush(stderr);
1788 va_end(ap);
1789 NYD2_LEAVE;
1792 FL void
1793 n_verr(char const *format, va_list ap){
1794 /* Check use cases of PS_ERRORS_NOTED, too! */
1795 #ifdef HAVE_ERRORS
1796 char buf[LINESIZE], *xbuf;
1797 int lmax, l;
1798 struct a_aux_err_node *enp;
1800 LCTA(ERRORS_MAX > 3);
1801 #endif
1802 NYD2_ENTER;
1804 if(a_aux_err_dirty++ == 0)
1805 fputs(UAGENT ": ", stderr);
1807 #ifdef HAVE_ERRORS
1808 if(!(options & OPT_INTERACTIVE))
1809 #endif
1811 vfprintf(stderr, format, ap);
1812 goto jleave;
1815 #ifdef HAVE_ERRORS
1816 xbuf = buf;
1817 lmax = sizeof buf;
1818 jredo:
1819 l = vsnprintf(xbuf, lmax, format, ap);
1820 if (l <= 0)
1821 goto jleave;
1822 if (UICMP(z, l, >=, lmax)) {
1823 /* FIXME Cannot reuse va_list
1824 lmax = ++l;
1825 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1826 goto jredo;
1830 fwrite(xbuf, 1, l, stderr);
1832 /* Link it into the `errors' message ring */
1833 if((enp = a_aux_err_tail) == NULL){
1834 jcreat:
1835 enp = scalloc(1, sizeof *enp);
1836 if(a_aux_err_tail != NULL)
1837 a_aux_err_tail->ae_next = enp;
1838 else
1839 a_aux_err_head = enp;
1840 a_aux_err_tail = enp;
1841 ++a_aux_err_cnt;
1842 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1843 if(a_aux_err_cnt < ERRORS_MAX)
1844 goto jcreat;
1846 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1847 a_aux_err_tail->ae_next = enp;
1848 a_aux_err_tail = enp;
1849 free(enp->ae_str.s);
1850 memset(enp, 0, sizeof *enp);
1853 n_str_add_buf(&enp->ae_str, xbuf, l);
1855 if(xbuf != buf)
1856 free(xbuf);
1857 #endif /* HAVE_ERRORS */
1859 jleave:
1860 /* If the format ends with newline, be clean again */
1861 /* C99 */{
1862 size_t i = strlen(format);
1864 if(i > 0 && format[i - 1] == '\n'){
1865 fflush(stderr);
1866 a_aux_err_dirty = 0;
1869 NYD2_LEAVE;
1872 FL void
1873 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1874 va_list ap;
1875 NYD_X;
1877 va_start(ap, format);
1878 vfprintf(stderr, format, ap);
1879 va_end(ap);
1880 fflush(stderr);
1883 FL void
1884 n_perr(char const *msg, int errval){
1885 char const *fmt;
1886 NYD2_ENTER;
1888 if(msg == NULL){
1889 fmt = "%s%s\n";
1890 msg = "";
1891 }else
1892 fmt = "%s: %s\n";
1894 if(errval == 0)
1895 errval = errno;
1897 n_err(fmt, msg, strerror(errval));
1898 NYD2_LEAVE;
1901 FL void
1902 n_alert(char const *format, ...){
1903 va_list ap;
1904 NYD2_ENTER;
1906 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
1908 va_start(ap, format);
1909 n_verr(format, ap);
1910 va_end(ap);
1912 n_err("\n");
1913 NYD2_LEAVE;
1916 FL void
1917 n_panic(char const *format, ...){
1918 va_list ap;
1919 NYD2_ENTER;
1921 if(a_aux_err_dirty > 0){
1922 putc('\n', stderr);
1923 a_aux_err_dirty = 0;
1925 fprintf(stderr, UAGENT ": Panic: ");
1927 va_start(ap, format);
1928 vfprintf(stderr, format, ap);
1929 va_end(ap);
1931 putc('\n', stderr);
1932 fflush(stderr);
1933 NYD2_LEAVE;
1934 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1937 #ifdef HAVE_ERRORS
1938 FL int
1939 c_errors(void *v){
1940 char **argv = v;
1941 struct a_aux_err_node *enp;
1942 NYD_ENTER;
1944 if(*argv == NULL)
1945 goto jlist;
1946 if(argv[1] != NULL)
1947 goto jerr;
1948 if(!asccasecmp(*argv, "show"))
1949 goto jlist;
1950 if(!asccasecmp(*argv, "clear"))
1951 goto jclear;
1952 jerr:
1953 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1954 v = NULL;
1955 jleave:
1956 NYD_LEAVE;
1957 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1959 jlist:{
1960 FILE *fp;
1961 size_t i;
1963 if(a_aux_err_head == NULL){
1964 fprintf(stderr, _("The error ring is empty\n"));
1965 goto jleave;
1968 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1969 NULL){
1970 fprintf(stderr, _("tmpfile"));
1971 v = NULL;
1972 goto jleave;
1975 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1976 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1977 ++i, enp->ae_str.l, enp->ae_str.s);
1978 /* We don't know wether last string ended with NL; be simple */
1979 putc('\n', fp);
1981 page_or_print(fp, 0);
1982 Fclose(fp);
1984 /* FALLTHRU */
1986 jclear:
1987 a_aux_err_tail = NULL;
1988 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1989 while((enp = a_aux_err_head) != NULL){
1990 a_aux_err_head = enp->ae_next;
1991 free(enp->ae_str.s);
1992 free(enp);
1994 goto jleave;
1996 #endif /* HAVE_ERRORS */
1998 #ifndef HAVE_DEBUG
1999 FL void *
2000 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2002 void *rv;
2003 NYD2_ENTER;
2005 if (s == 0)
2006 s = 1;
2007 if ((rv = malloc(s)) == NULL)
2008 n_panic(_("no memory"));
2009 NYD2_LEAVE;
2010 return rv;
2013 FL void *
2014 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2016 void *rv;
2017 NYD2_ENTER;
2019 if (s == 0)
2020 s = 1;
2021 if (v == NULL)
2022 rv = smalloc(s);
2023 else if ((rv = realloc(v, s)) == NULL)
2024 n_panic(_("no memory"));
2025 NYD2_LEAVE;
2026 return rv;
2029 FL void *
2030 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2032 void *rv;
2033 NYD2_ENTER;
2035 if (size == 0)
2036 size = 1;
2037 if ((rv = calloc(nmemb, size)) == NULL)
2038 n_panic(_("no memory"));
2039 NYD2_LEAVE;
2040 return rv;
2043 #else /* !HAVE_DEBUG */
2044 CTA(sizeof(char) == sizeof(ui8_t));
2046 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2047 # define _HOPE_SET(C) \
2048 do {\
2049 union mem_ptr __xl, __xu;\
2050 struct mem_chunk *__xc;\
2051 __xl.p_p = (C).p_p;\
2052 __xc = __xl.p_c - 1;\
2053 __xu.p_p = __xc;\
2054 (C).p_cp += 8;\
2055 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2056 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2057 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2058 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2059 __xu.p_ui8p += __xc->mc_size - 8;\
2060 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2061 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2062 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2063 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2064 } while (0)
2065 # define _HOPE_GET_TRACE(C,BAD) \
2066 do {\
2067 (C).p_cp += 8;\
2068 _HOPE_GET(C, BAD);\
2069 (C).p_cp += 8;\
2070 } while(0)
2071 # define _HOPE_GET(C,BAD) \
2072 do {\
2073 union mem_ptr __xl, __xu;\
2074 struct mem_chunk *__xc;\
2075 ui32_t __i;\
2076 __xl.p_p = (C).p_p;\
2077 __xl.p_cp -= 8;\
2078 (C).p_cp = __xl.p_cp;\
2079 __xc = __xl.p_c - 1;\
2080 (BAD) = FAL0;\
2081 __i = 0;\
2082 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2083 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2084 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2085 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2086 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2087 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2088 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2089 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2090 if (__i != 0) {\
2091 (BAD) = TRU1;\
2092 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2093 __xl.p_p, __i, mdbg_file, mdbg_line);\
2095 __xu.p_p = __xc;\
2096 __xu.p_ui8p += __xc->mc_size - 8;\
2097 __i = 0;\
2098 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2099 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2100 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2101 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2102 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2103 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2104 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2105 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2106 if (__i != 0) {\
2107 (BAD) = TRU1;\
2108 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2109 __xl.p_p, __i, mdbg_file, mdbg_line);\
2111 if (BAD)\
2112 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2113 __xc->mc_file, __xc->mc_line);\
2114 } while (0)
2116 FL void *
2117 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2119 union mem_ptr p;
2120 NYD2_ENTER;
2122 if (s == 0)
2123 s = 1;
2124 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2125 n_panic("smalloc(): allocation too large: %s, line %d",
2126 mdbg_file, mdbg_line);
2127 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2129 if ((p.p_p = (malloc)(s)) == NULL)
2130 n_panic(_("no memory"));
2131 p.p_c->mc_prev = NULL;
2132 if ((p.p_c->mc_next = _mem_list) != NULL)
2133 _mem_list->mc_prev = p.p_c;
2134 p.p_c->mc_file = mdbg_file;
2135 p.p_c->mc_line = (ui16_t)mdbg_line;
2136 p.p_c->mc_isfree = FAL0;
2137 p.p_c->mc_size = (ui32_t)s;
2139 _mem_list = p.p_c++;
2140 _HOPE_SET(p);
2142 ++_mem_aall;
2143 ++_mem_acur;
2144 _mem_amax = MAX(_mem_amax, _mem_acur);
2145 _mem_mall += s;
2146 _mem_mcur += s;
2147 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2148 NYD2_LEAVE;
2149 return p.p_p;
2152 FL void *
2153 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2155 union mem_ptr p;
2156 bool_t isbad;
2157 NYD2_ENTER;
2159 if ((p.p_p = v) == NULL) {
2160 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2161 goto jleave;
2164 _HOPE_GET(p, isbad);
2165 --p.p_c;
2166 if (p.p_c->mc_isfree) {
2167 n_err("srealloc(): region freed! At %s, line %d\n"
2168 "\tLast seen: %s, line %" PRIu16 "\n",
2169 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2170 goto jforce;
2173 if (p.p_c == _mem_list)
2174 _mem_list = p.p_c->mc_next;
2175 else
2176 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2177 if (p.p_c->mc_next != NULL)
2178 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2180 --_mem_acur;
2181 _mem_mcur -= p.p_c->mc_size;
2182 jforce:
2183 if (s == 0)
2184 s = 1;
2185 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2186 n_panic("srealloc(): allocation too large: %s, line %d",
2187 mdbg_file, mdbg_line);
2188 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2190 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2191 n_panic(_("no memory"));
2192 p.p_c->mc_prev = NULL;
2193 if ((p.p_c->mc_next = _mem_list) != NULL)
2194 _mem_list->mc_prev = p.p_c;
2195 p.p_c->mc_file = mdbg_file;
2196 p.p_c->mc_line = (ui16_t)mdbg_line;
2197 p.p_c->mc_isfree = FAL0;
2198 p.p_c->mc_size = (ui32_t)s;
2199 _mem_list = p.p_c++;
2200 _HOPE_SET(p);
2202 ++_mem_aall;
2203 ++_mem_acur;
2204 _mem_amax = MAX(_mem_amax, _mem_acur);
2205 _mem_mall += s;
2206 _mem_mcur += s;
2207 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2208 jleave:
2209 NYD2_LEAVE;
2210 return p.p_p;
2213 FL void *
2214 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2216 union mem_ptr p;
2217 NYD2_ENTER;
2219 if (size == 0)
2220 size = 1;
2221 if (nmemb == 0)
2222 nmemb = 1;
2223 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2224 n_panic("scalloc(): allocation size too large: %s, line %d",
2225 mdbg_file, mdbg_line);
2226 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2227 n_panic("scalloc(): allocation count too large: %s, line %d",
2228 mdbg_file, mdbg_line);
2230 size *= nmemb;
2231 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2233 if ((p.p_p = (malloc)(size)) == NULL)
2234 n_panic(_("no memory"));
2235 memset(p.p_p, 0, size);
2236 p.p_c->mc_prev = NULL;
2237 if ((p.p_c->mc_next = _mem_list) != NULL)
2238 _mem_list->mc_prev = p.p_c;
2239 p.p_c->mc_file = mdbg_file;
2240 p.p_c->mc_line = (ui16_t)mdbg_line;
2241 p.p_c->mc_isfree = FAL0;
2242 p.p_c->mc_size = (ui32_t)size;
2243 _mem_list = p.p_c++;
2244 _HOPE_SET(p);
2246 ++_mem_aall;
2247 ++_mem_acur;
2248 _mem_amax = MAX(_mem_amax, _mem_acur);
2249 _mem_mall += size;
2250 _mem_mcur += size;
2251 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2252 NYD2_LEAVE;
2253 return p.p_p;
2256 FL void
2257 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2259 union mem_ptr p;
2260 bool_t isbad;
2261 NYD2_ENTER;
2263 if ((p.p_p = v) == NULL) {
2264 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2265 goto jleave;
2268 _HOPE_GET(p, isbad);
2269 --p.p_c;
2270 if (p.p_c->mc_isfree) {
2271 n_err("sfree(): double-free avoided at %s, line %d\n"
2272 "\tLast seen: %s, line %" PRIu16 "\n",
2273 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2274 goto jleave;
2277 if (p.p_c == _mem_list)
2278 _mem_list = p.p_c->mc_next;
2279 else
2280 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2281 if (p.p_c->mc_next != NULL)
2282 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2283 p.p_c->mc_isfree = TRU1;
2284 /* Trash contents (also see [21c05f8]) */
2285 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2287 --_mem_acur;
2288 _mem_mcur -= p.p_c->mc_size;
2290 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2291 p.p_c->mc_next = _mem_free;
2292 _mem_free = p.p_c;
2293 } else
2294 (free)(p.p_c);
2295 jleave:
2296 NYD2_LEAVE;
2299 FL void
2300 smemreset(void)
2302 union mem_ptr p;
2303 size_t c = 0, s = 0;
2304 NYD_ENTER;
2306 smemcheck();
2308 for (p.p_c = _mem_free; p.p_c != NULL;) {
2309 void *vp = p.p_c;
2310 ++c;
2311 s += p.p_c->mc_size;
2312 p.p_c = p.p_c->mc_next;
2313 (free)(vp);
2315 _mem_free = NULL;
2317 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2318 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2319 NYD_LEAVE;
2322 FL int
2323 c_smemtrace(void *v)
2325 /* For _HOPE_GET() */
2326 char const * const mdbg_file = "smemtrace()";
2327 int const mdbg_line = -1;
2328 FILE *fp;
2329 union mem_ptr p, xp;
2330 bool_t isbad;
2331 size_t lines;
2332 NYD_ENTER;
2334 v = (void*)0x1;
2335 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2336 n_perr("tmpfile", 0);
2337 goto jleave;
2340 fprintf(fp, "Memory statistics:\n"
2341 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2342 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2343 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2345 fprintf(fp, "Currently allocated memory chunks:\n");
2346 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2347 ++lines, p.p_c = p.p_c->mc_next) {
2348 xp = p;
2349 ++xp.p_c;
2350 _HOPE_GET_TRACE(xp, isbad);
2351 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2352 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2353 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2354 p.p_c->mc_line);
2357 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2358 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2359 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2360 xp = p;
2361 ++xp.p_c;
2362 _HOPE_GET_TRACE(xp, isbad);
2363 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2364 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2365 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2366 p.p_c->mc_file, p.p_c->mc_line);
2370 page_or_print(fp, lines);
2371 Fclose(fp);
2372 v = NULL;
2373 jleave:
2374 NYD_LEAVE;
2375 return (v != NULL);
2378 # ifdef HAVE_DEVEL
2379 FL bool_t
2380 _smemcheck(char const *mdbg_file, int mdbg_line)
2382 union mem_ptr p, xp;
2383 bool_t anybad = FAL0, isbad;
2384 size_t lines;
2385 NYD_ENTER;
2387 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2388 ++lines, p.p_c = p.p_c->mc_next) {
2389 xp = p;
2390 ++xp.p_c;
2391 _HOPE_GET_TRACE(xp, isbad);
2392 if (isbad) {
2393 anybad = TRU1;
2394 n_err(
2395 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2396 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2397 p.p_c->mc_file, p.p_c->mc_line);
2401 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2402 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2403 xp = p;
2404 ++xp.p_c;
2405 _HOPE_GET_TRACE(xp, isbad);
2406 if (isbad) {
2407 anybad = TRU1;
2408 n_err(
2409 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2410 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2411 p.p_c->mc_file, p.p_c->mc_line);
2415 NYD_LEAVE;
2416 return anybad;
2418 # endif /* HAVE_DEVEL */
2420 # undef _HOPE_SIZE
2421 # undef _HOPE_SET
2422 # undef _HOPE_GET_TRACE
2423 # undef _HOPE_GET
2424 #endif /* HAVE_DEBUG */
2426 /* s-it-mode */