nail.1: remove that folder-loss paragraph again; too rude
[s-mailx.git] / auxlily.c
blobf60e167e8ec792803c572be79432f1ff47badaee
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 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/utsname.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <fcntl.h>
50 #ifdef HAVE_SOCKETS
51 # ifdef HAVE_IPV6
52 # include <sys/socket.h>
53 # endif
55 # include <netdb.h>
56 #endif
58 #ifdef HAVE_DEBUG
59 struct nyd_info {
60 char const *ni_file;
61 char const *ni_fun;
62 ui32_t ni_chirp_line;
63 ui32_t ni_level;
66 struct mem_chunk {
67 struct mem_chunk *mc_prev;
68 struct mem_chunk *mc_next;
69 char const *mc_file;
70 ui16_t mc_line;
71 ui8_t mc_isfree;
72 ui8_t __dummy;
73 ui32_t mc_size;
76 union mem_ptr {
77 void *p_p;
78 struct mem_chunk *p_c;
79 char *p_cp;
80 ui8_t *p_ui8p;
82 #endif /* HAVE_DEBUG */
84 /* NYD, memory pool debug */
85 #ifdef HAVE_DEBUG
86 static ui32_t _nyd_curr, _nyd_level;
87 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
89 static size_t _mem_aall, _mem_acur, _mem_amax,
90 _mem_mall, _mem_mcur, _mem_mmax;
92 static struct mem_chunk *_mem_list, *_mem_free;
93 #endif
95 /* {hold,rele}_all_sigs() */
96 static size_t _alls_depth;
97 static sigset_t _alls_nset, _alls_oset;
99 /* {hold,rele}_sigs() */
100 static size_t _hold_sigdepth;
101 static sigset_t _hold_nset, _hold_oset;
103 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
104 #ifdef HAVE_COLOUR
105 static char * _colour_iso6429(char const *wish);
106 #endif
108 #ifdef HAVE_DEBUG
109 static void _nyd_print(struct nyd_info *nip);
110 #endif
112 #ifdef HAVE_COLOUR
113 static char *
114 _colour_iso6429(char const *wish)
116 char const * const wish_orig = wish;
117 char *xwish, *cp, cfg[3] = {0, 0, 0};
118 NYD_ENTER;
120 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
121 * value, ensure we have enough room for that */
123 size_t i = strlen(wish) +1;
124 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
125 memcpy(xwish, wish, i);
126 wish = xwish;
129 /* Iterate over the colour spec */
130 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
131 char *y, *x = strchr(cp, '=');
132 if (x == NULL) {
133 jbail:
134 fprintf(stderr, tr(527,
135 "Invalid colour specification \"%s\": >>> %s <<<\n"),
136 wish_orig, cp);
137 continue;
139 *x++ = '\0';
141 /* TODO convert the ft/fg/bg parser into a table-based one! */
142 if (!asccasecmp(cp, "ft")) {
143 if (!asccasecmp(x, "bold"))
144 cfg[0] = '1';
145 else if (!asccasecmp(x, "inverse"))
146 cfg[0] = '7';
147 else if (!asccasecmp(x, "underline"))
148 cfg[0] = '4';
149 else
150 goto jbail;
151 } else if (!asccasecmp(cp, "fg")) {
152 y = cfg + 1;
153 goto jiter_colour;
154 } else if (!asccasecmp(cp, "bg")) {
155 y = cfg + 2;
156 jiter_colour:
157 if (!asccasecmp(x, "black"))
158 *y = '0';
159 else if (!asccasecmp(x, "blue"))
160 *y = '4';
161 else if (!asccasecmp(x, "green"))
162 *y = '2';
163 else if (!asccasecmp(x, "red"))
164 *y = '1';
165 else if (!asccasecmp(x, "brown"))
166 *y = '3';
167 else if (!asccasecmp(x, "magenta"))
168 *y = '5';
169 else if (!asccasecmp(x, "cyan"))
170 *y = '6';
171 else if (!asccasecmp(x, "white"))
172 *y = '7';
173 else
174 goto jbail;
175 } else
176 goto jbail;
179 /* Restore our salloc() buffer, create return value */
180 xwish = UNCONST(wish);
181 if (cfg[0] || cfg[1] || cfg[2]) {
182 xwish[0] = '\033';
183 xwish[1] = '[';
184 xwish += 2;
185 if (cfg[0])
186 *xwish++ = cfg[0];
187 if (cfg[1]) {
188 if (cfg[0])
189 *xwish++ = ';';
190 xwish[0] = '3';
191 xwish[1] = cfg[1];
192 xwish += 2;
194 if (cfg[2]) {
195 if (cfg[0] || cfg[1])
196 *xwish++ = ';';
197 xwish[0] = '4';
198 xwish[1] = cfg[2];
199 xwish += 2;
201 *xwish++ = 'm';
203 *xwish = '\0';
204 NYD_LEAVE;
205 return UNCONST(wish);
207 #endif /* HAVE_COLOUR */
209 #ifdef HAVE_DEBUG
210 static void
211 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
213 char buf[80];
214 union {int i; size_t z;} u;
216 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
217 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
218 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
219 if (u.i > 0) {
220 u.z = u.i;
221 if (u.z > sizeof buf)
222 u.z = sizeof buf - 1; /* (Skip \0) */
223 write(STDERR_FILENO, buf, u.z);
226 #endif
228 FL void
229 panic(char const *format, ...)
231 va_list ap;
232 NYD_ENTER;
234 fprintf(stderr, tr(1, "Panic: "));
236 va_start(ap, format);
237 vfprintf(stderr, format, ap);
238 va_end(ap);
240 fputs("\n", stderr);
241 fflush(stderr);
242 NYD_LEAVE;
243 abort(); /* Was exit(EXIT_ERR); for a while, but no */
246 FL void
247 alert(char const *format, ...)
249 va_list ap;
250 NYD_ENTER;
252 fprintf(stderr, tr(1, "Panic: "));
254 va_start(ap, format);
255 vfprintf(stderr, format, ap);
256 va_end(ap);
258 fputs("\n", stderr);
259 fflush(stderr);
260 NYD_LEAVE;
263 FL sighandler_type
264 safe_signal(int signum, sighandler_type handler)
266 struct sigaction nact, oact;
267 sighandler_type rv;
268 NYD_ENTER;
270 nact.sa_handler = handler;
271 sigemptyset(&nact.sa_mask);
272 nact.sa_flags = 0;
273 #ifdef SA_RESTART
274 nact.sa_flags |= SA_RESTART;
275 #endif
276 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
277 NYD_LEAVE;
278 return rv;
281 FL void
282 hold_all_sigs(void)
284 NYD_ENTER;
285 if (_alls_depth++ == 0) {
286 sigfillset(&_alls_nset);
287 sigdelset(&_alls_nset, SIGABRT);
288 #ifdef SIGBUS
289 sigdelset(&_alls_nset, SIGBUS);
290 #endif
291 sigdelset(&_alls_nset, SIGCHLD);
292 sigdelset(&_alls_nset, SIGFPE);
293 sigdelset(&_alls_nset, SIGILL);
294 sigdelset(&_alls_nset, SIGKILL);
295 sigdelset(&_alls_nset, SIGSEGV);
296 sigdelset(&_alls_nset, SIGSTOP);
297 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
299 NYD_LEAVE;
302 FL void
303 rele_all_sigs(void)
305 NYD_ENTER;
306 if (--_alls_depth == 0)
307 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
308 NYD_LEAVE;
311 FL void
312 hold_sigs(void)
314 NYD_ENTER;
315 if (_hold_sigdepth++ == 0) {
316 sigemptyset(&_hold_nset);
317 sigaddset(&_hold_nset, SIGHUP);
318 sigaddset(&_hold_nset, SIGINT);
319 sigaddset(&_hold_nset, SIGQUIT);
320 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
322 NYD_LEAVE;
325 FL void
326 rele_sigs(void)
328 NYD_ENTER;
329 if (--_hold_sigdepth == 0)
330 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
331 NYD_LEAVE;
334 #ifdef HAVE_DEBUG
335 FL void
336 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
338 struct nyd_info *nip = _nyd_infos;
340 if (_nyd_curr != NELEM(_nyd_infos))
341 nip += _nyd_curr++;
342 else
343 _nyd_curr = 1;
344 nip->ni_file = file;
345 nip->ni_fun = fun;
346 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
347 nip->ni_level = ((act == 0) ? _nyd_level
348 : (act == 1) ? ++_nyd_level : _nyd_level--);
351 FL void
352 _nyd_oncrash(int signo)
354 struct sigaction xact;
355 sigset_t xset;
356 struct nyd_info *nip;
357 size_t i;
359 xact.sa_handler = SIG_DFL;
360 sigemptyset(&xact.sa_mask);
361 xact.sa_flags = 0;
362 sigaction(signo, &xact, NULL);
364 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
365 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
366 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
367 _nyd_print(nip++);
368 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
369 _nyd_print(nip++);
371 sigemptyset(&xset);
372 sigaddset(&xset, signo);
373 sigprocmask(SIG_UNBLOCK, &xset, NULL);
374 kill(0, signo);
375 for (;;)
376 _exit(EXIT_ERR);
378 #endif
380 FL void
381 touch(struct message *mp)
383 NYD_ENTER;
384 mp->m_flag |= MTOUCH;
385 if (!(mp->m_flag & MREAD))
386 mp->m_flag |= MREAD | MSTATUS;
387 NYD_LEAVE;
390 FL bool_t
391 is_dir(char const *name)
393 struct stat sbuf;
394 bool_t rv = FAL0;
395 NYD_ENTER;
397 if (!stat(name, &sbuf))
398 rv = (S_ISDIR(sbuf.st_mode) != 0);
399 NYD_LEAVE;
400 return rv;
403 FL int
404 argcount(char **argv)
406 char **ap;
407 NYD_ENTER;
409 for (ap = argv; *ap++ != NULL;)
411 NYD_LEAVE;
412 return (int)PTR2SIZE(ap - argv - 1);
415 FL int
416 screensize(void)
418 int s;
419 char *cp;
420 NYD_ENTER;
422 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
423 s = scrnheight - 4; /* XXX no magics */
424 NYD_LEAVE;
425 return s;
428 FL char const *
429 get_pager(void)
431 char const *cp;
432 NYD_ENTER;
434 cp = ok_vlook(PAGER);
435 if (cp == NULL || *cp == '\0')
436 cp = XPAGER;
437 NYD_LEAVE;
438 return cp;
441 FL size_t
442 paging_seems_sensible(void)
444 size_t rv = 0;
445 char const *cp;
446 NYD_ENTER;
448 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
449 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
450 NYD_LEAVE;
451 return rv;
454 FL void
455 page_or_print(FILE *fp, size_t lines)
457 size_t rows;
458 int c;
459 NYD_ENTER;
461 fflush_rewind(fp);
463 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
464 while ((c = getc(fp)) != EOF)
465 if (c == '\n' && ++lines > rows)
466 break;
467 really_rewind(fp);
470 if (rows != 0 && lines >= rows)
471 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
472 else
473 while ((c = getc(fp)) != EOF)
474 putchar(c);
475 NYD_LEAVE;
478 FL enum protocol
479 which_protocol(char const *name)
481 struct stat st;
482 char const *cp;
483 char *np;
484 size_t sz;
485 enum protocol rv = PROTO_UNKNOWN;
486 NYD_ENTER;
488 if (name[0] == '%' && name[1] == ':')
489 name += 2;
490 for (cp = name; *cp && *cp != ':'; cp++)
491 if (!alnumchar(*cp))
492 goto jfile;
494 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
495 if (!strncmp(name, "pop3://", 7)) {
496 #ifdef HAVE_POP3
497 rv = PROTO_POP3;
498 #else
499 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
500 #endif
501 } else if (!strncmp(name, "pop3s://", 8)) {
502 #if defined HAVE_POP3 && defined HAVE_SSL
503 rv = PROTO_POP3;
504 #else
505 # ifndef HAVE_POP3
506 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
507 # endif
508 # ifndef HAVE_SSL
509 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
510 # endif
511 #endif
512 } else if (!strncmp(name, "imap://", 7)) {
513 #ifdef HAVE_IMAP
514 rv = PROTO_IMAP;
515 #else
516 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
517 #endif
518 } else if (!strncmp(name, "imaps://", 8)) {
519 #if defined HAVE_IMAP && defined HAVE_SSL
520 rv = PROTO_IMAP;
521 #else
522 # ifndef HAVE_IMAP
523 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
524 # endif
525 # ifndef HAVE_SSL
526 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
527 # endif
528 #endif
530 goto jleave;
533 /* TODO This is the de facto maildir code and thus belongs into there!
534 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
535 * TODO or (more likely) in addition to *newfolders*) */
536 jfile:
537 rv = PROTO_FILE;
538 np = ac_alloc((sz = strlen(name)) + 4 +1);
539 memcpy(np, name, sz + 1);
540 if (!stat(name, &st)) {
541 if (S_ISDIR(st.st_mode) &&
542 (strcpy(&np[sz], "/tmp"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
543 (strcpy(&np[sz], "/new"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
544 (strcpy(&np[sz], "/cur"), !stat(np, &st) && S_ISDIR(st.st_mode)))
545 rv = PROTO_MAILDIR;
546 } else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
547 rv = PROTO_MAILDIR;
548 ac_free(np);
549 jleave:
550 NYD_LEAVE;
551 return rv;
554 FL ui32_t
555 torek_hash(char const *name)
557 /* Chris Torek's hash.
558 * NOTE: need to change *at least* create-okey-map.pl when changing the
559 * algorithm!! */
560 ui32_t h = 0;
561 NYD_ENTER;
563 while (*name != '\0') {
564 h *= 33;
565 h += *name++;
567 NYD_LEAVE;
568 return h;
571 FL unsigned
572 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
574 unsigned h = 0, g;
575 NYD_ENTER;
577 cp--;
578 while (*++cp) {
579 h = (h << 4 & 0xffffffff) + (*cp&0377);
580 if ((g = h & 0xf0000000) != 0) {
581 h = h ^ g >> 24;
582 h = h ^ g;
585 NYD_LEAVE;
586 return h;
589 FL ui32_t
590 nextprime(ui32_t n)
592 static ui32_t const primes[] = {
593 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
594 131071, 262139, 524287, 1048573, 2097143, 4194301,
595 8388593, 16777213, 33554393, 67108859, 134217689,
596 268435399, 536870909, 1073741789, 2147483647
599 ui32_t mprime = 7, cutlim;
600 size_t i;
601 NYD_ENTER;
603 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
605 for (i = 0; i < NELEM(primes); i++)
606 if ((mprime = primes[i]) >= cutlim)
607 break;
608 if (i == NELEM(primes) && mprime < n)
609 mprime = n;
610 NYD_LEAVE;
611 return mprime;
614 FL int
615 expand_shell_escape(char const **s, bool_t use_nail_extensions)
617 char const *xs;
618 int c, n;
619 NYD_ENTER;
621 xs = *s;
623 if ((c = *xs & 0xFF) == '\0')
624 goto jleave;
625 ++xs;
626 if (c != '\\')
627 goto jleave;
629 switch ((c = *xs & 0xFF)) {
630 case '\\': break;
631 case 'a': c = '\a'; break;
632 case 'b': c = '\b'; break;
633 case 'c': c = PROMPT_STOP; break;
634 case 'f': c = '\f'; break;
635 case 'n': c = '\n'; break;
636 case 'r': c = '\r'; break;
637 case 't': c = '\t'; break;
638 case 'v': c = '\v'; break;
639 case '0':
640 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
641 c <<= 3;
642 c |= *xs - '0';
644 goto jleave;
645 /* S-nail extension for nice (get)prompt(()) support */
646 case '&':
647 case '?':
648 case '$':
649 case '@':
650 if (use_nail_extensions) {
651 switch (c) {
652 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
653 case '?': c = exec_last_comm_error ? '1' : '0'; break;
654 case '$': c = PROMPT_DOLLAR; break;
655 case '@': c = PROMPT_AT; break;
657 break;
659 /* FALLTHRU */
660 case '\0':
661 /* A sole <backslash> at EOS is treated as-is! */
662 /* FALLTHRU */
663 default:
664 c = '\\';
665 goto jleave;
667 ++xs;
668 jleave:
669 *s = xs;
670 NYD_LEAVE;
671 return c;
674 FL char *
675 getprompt(void)
677 static char buf[PROMPT_BUFFER_SIZE];
679 char *cp = buf;
680 char const *ccp;
681 NYD_ENTER;
683 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
684 goto jleave;
686 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
687 char const *a;
688 size_t l;
689 int c = expand_shell_escape(&ccp, TRU1);
691 if (c > 0) {
692 *cp = (char)c;
693 continue;
695 if (c == 0 || c == PROMPT_STOP)
696 break;
698 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
699 if (a == NULL)
700 a = "";
701 l = strlen(a);
702 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
703 *cp++ = '?';
704 else {
705 memcpy(cp, a, l);
706 cp += --l;
709 jleave:
710 *cp = '\0';
711 NYD_LEAVE;
712 return buf;
715 FL char *
716 nodename(int mayoverride)
718 static char *hostname;
720 struct utsname ut;
721 char *hn;
722 #ifdef HAVE_SOCKETS
723 # ifdef HAVE_IPV6
724 struct addrinfo hints, *res;
725 # else
726 struct hostent *hent;
727 # endif
728 #endif
729 NYD_ENTER;
731 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
732 if (hostname != NULL)
733 free(hostname);
734 hostname = sstrdup(hn);
735 } else if (hostname == NULL) {
736 uname(&ut);
737 hn = ut.nodename;
738 #ifdef HAVE_SOCKETS
739 # ifdef HAVE_IPV6
740 memset(&hints, 0, sizeof hints);
741 hints.ai_family = AF_UNSPEC;
742 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
743 hints.ai_flags = AI_CANONNAME;
744 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
745 if (res->ai_canonname != NULL) {
746 size_t l = strlen(res->ai_canonname) +1;
747 hn = ac_alloc(l);
748 memcpy(hn, res->ai_canonname, l);
750 freeaddrinfo(res);
752 # else
753 hent = gethostbyname(hn);
754 if (hent != NULL)
755 hn = hent->h_name;
756 # endif
757 #endif
758 hostname = sstrdup(hn);
759 #if defined HAVE_SOCKETS && defined HAVE_IPV6
760 if (hn != ut.nodename)
761 ac_free(hn);
762 #endif
764 NYD_LEAVE;
765 return hostname;
768 FL char *
769 lookup_password_for_token(char const *token)
771 size_t tl;
772 char *var, *cp;
773 NYD_ENTER;
775 tl = strlen(token);
776 var = ac_alloc(tl + 9 +1);
778 memcpy(var, "password-", 9);
779 memcpy(var + 9, token, tl);
780 var[tl + 9] = '\0';
782 if ((cp = vok_vlook(var)) != NULL)
783 cp = savestr(cp);
784 ac_free(var);
785 NYD_LEAVE;
786 return cp;
789 FL char *
790 getrandstring(size_t length)
792 static unsigned char nodedigest[16];
793 static pid_t pid;
795 struct str b64;
796 char *data, *cp;
797 size_t i;
798 int fd = -1;
799 #ifdef HAVE_MD5
800 md5_ctx ctx;
801 #else
802 size_t j;
803 #endif
804 NYD_ENTER;
806 data = ac_alloc(length);
808 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
809 length != (size_t)read(fd, data, length)) {
810 if (pid == 0) {
811 pid = getpid();
812 srand(pid);
813 cp = nodename(0);
814 #ifdef HAVE_MD5
815 md5_init(&ctx);
816 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
817 md5_final(nodedigest, &ctx);
818 #else
819 /* In that case it's only used for boundaries and Message-Id:s so that
820 * srand(3) should suffice */
821 j = strlen(cp) + 1;
822 for (i = 0; i < sizeof(nodedigest); ++i)
823 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
824 #endif
826 for (i = 0; i < length; i++)
827 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
828 nodedigest[i % sizeof nodedigest]);
830 if (fd >= 0)
831 close(fd);
833 b64_encode_buf(&b64, data, length, B64_SALLOC);
834 ac_free(data);
835 assert(length < b64.l);
836 b64.s[length] = '\0';
837 NYD_LEAVE;
838 return b64.s;
841 #ifdef HAVE_MD5
842 FL char *
843 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
845 char const *cp = vp;
846 size_t i, j;
847 NYD_ENTER;
849 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
850 j = i << 1;
851 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
852 hex[++j] = hexchar(cp[i] & 0x0f);
854 NYD_LEAVE;
855 return hex;
858 FL char *
859 cram_md5_string(char const *user, char const *pass, char const *b64)
861 struct str in, out;
862 char digest[16], *cp;
863 size_t lu;
864 NYD_ENTER;
866 out.s = NULL;
867 in.s = UNCONST(b64);
868 in.l = strlen(in.s);
869 b64_decode(&out, &in, NULL);
870 assert(out.s != NULL);
872 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
873 free(out.s);
874 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
876 lu = strlen(user);
877 in.l = lu + MD5TOHEX_SIZE +1;
878 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
879 memcpy(in.s, user, lu);
880 in.s[lu++] = ' ';
881 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
882 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
883 ac_free(in.s);
884 NYD_LEAVE;
885 return out.s;
888 FL void
889 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
890 void *digest)
893 * This code is taken from
895 * Network Working Group H. Krawczyk
896 * Request for Comments: 2104 IBM
897 * Category: Informational M. Bellare
898 * UCSD
899 * R. Canetti
900 * IBM
901 * February 1997
904 * HMAC: Keyed-Hashing for Message Authentication
906 md5_ctx context;
907 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
908 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
909 unsigned char tk[16];
910 int i;
911 NYD_ENTER;
913 /* if key is longer than 64 bytes reset it to key=MD5(key) */
914 if (key_len > 64) {
915 md5_ctx tctx;
917 md5_init(&tctx);
918 md5_update(&tctx, key, key_len);
919 md5_final(tk, &tctx);
921 key = tk;
922 key_len = 16;
925 /* the HMAC_MD5 transform looks like:
927 * MD5(K XOR opad, MD5(K XOR ipad, text))
929 * where K is an n byte key
930 * ipad is the byte 0x36 repeated 64 times
931 * opad is the byte 0x5c repeated 64 times
932 * and text is the data being protected */
934 /* start out by storing key in pads */
935 memset(k_ipad, 0, sizeof k_ipad);
936 memset(k_opad, 0, sizeof k_opad);
937 memcpy(k_ipad, key, key_len);
938 memcpy(k_opad, key, key_len);
940 /* XOR key with ipad and opad values */
941 for (i=0; i<64; i++) {
942 k_ipad[i] ^= 0x36;
943 k_opad[i] ^= 0x5c;
946 /* perform inner MD5 */
947 md5_init(&context); /* init context for 1st pass */
948 md5_update(&context, k_ipad, 64); /* start with inner pad */
949 md5_update(&context, text, text_len); /* then text of datagram */
950 md5_final(digest, &context); /* finish up 1st pass */
952 /* perform outer MD5 */
953 md5_init(&context); /* init context for 2nd pass */
954 md5_update(&context, k_opad, 64); /* start with outer pad */
955 md5_update(&context, digest, 16); /* then results of 1st hash */
956 md5_final(digest, &context); /* finish up 2nd pass */
957 NYD_LEAVE;
959 #endif /* HAVE_MD5 */
961 FL enum okay
962 makedir(char const *name)
964 struct stat st;
965 enum okay rv = STOP;
966 NYD_ENTER;
968 if (!mkdir(name, 0700))
969 rv = OKAY;
970 else {
971 int e = errno;
972 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
973 S_ISDIR(st.st_mode))
974 rv = OKAY;
976 NYD_LEAVE;
977 return rv;
980 #ifdef HAVE_FCHDIR
981 FL enum okay
982 cwget(struct cw *cw)
984 enum okay rv = STOP;
985 NYD_ENTER;
987 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
988 goto jleave;
989 if (fchdir(cw->cw_fd) == -1) {
990 close(cw->cw_fd);
991 goto jleave;
993 rv = OKAY;
994 jleave:
995 NYD_LEAVE;
996 return rv;
999 FL enum okay
1000 cwret(struct cw *cw)
1002 enum okay rv = STOP;
1003 NYD_ENTER;
1005 if (!fchdir(cw->cw_fd))
1006 rv = OKAY;
1007 NYD_LEAVE;
1008 return rv;
1011 FL void
1012 cwrelse(struct cw *cw)
1014 NYD_ENTER;
1015 close(cw->cw_fd);
1016 NYD_LEAVE;
1019 #else /* !HAVE_FCHDIR */
1020 FL enum okay
1021 cwget(struct cw *cw)
1023 enum okay rv = STOP;
1024 NYD_ENTER;
1026 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1027 rv = OKAY;
1028 NYD_LEAVE;
1029 return rv;
1032 FL enum okay
1033 cwret(struct cw *cw)
1035 enum okay rv = STOP;
1036 NYD_ENTER;
1038 if (!chdir(cw->cw_wd))
1039 rv = OKAY;
1040 NYD_LEAVE;
1041 return rv;
1044 FL void
1045 cwrelse(struct cw *cw)
1047 NYD_ENTER;
1048 UNUSED(cw);
1049 NYD_LEAVE;
1051 #endif /* !HAVE_FCHDIR */
1053 FL char *
1054 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1056 int col_orig = col, n, sz;
1057 char *nb, *np;
1058 NYD_ENTER;
1060 np = nb = salloc(mb_cur_max * strlen(cp) + col +1);
1061 while (*cp) {
1062 #ifdef HAVE_WCWIDTH
1063 if (mb_cur_max > 1) {
1064 wchar_t wc;
1066 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1067 n = sz = 1;
1068 else if ((n = wcwidth(wc)) == -1)
1069 n = 1;
1070 } else
1071 #endif
1072 n = sz = 1;
1073 if (n > col)
1074 break;
1075 col -= n;
1076 if (sz == 1 && spacechar(*cp)) {
1077 *np++ = ' ';
1078 cp++;
1079 } else
1080 while (sz--)
1081 *np++ = *cp++;
1084 if (fill && col != 0) {
1085 if (fill > 0) {
1086 memmove(nb + col, nb, PTR2SIZE(np - nb));
1087 memset(nb, ' ', col);
1088 } else
1089 memset(np, ' ', col);
1090 np += col;
1091 col = 0;
1094 *np = '\0';
1095 if (cols_decr_used_or_null != NULL)
1096 *cols_decr_used_or_null -= col_orig - col;
1097 NYD_LEAVE;
1098 return nb;
1101 FL void
1102 makeprint(struct str const *in, struct str *out)
1104 static int print_all_chars = -1;
1106 char const *inp, *maxp;
1107 char *outp;
1108 size_t msz;
1109 NYD_ENTER;
1111 if (print_all_chars == -1)
1112 print_all_chars = ok_blook(print_all_chars);
1114 msz = in->l +1;
1115 out->s = outp = smalloc(msz);
1116 inp = in->s;
1117 maxp = inp + in->l;
1119 if (print_all_chars) {
1120 out->l = in->l;
1121 memcpy(outp, inp, out->l);
1122 goto jleave;
1125 #ifdef HAVE_C90AMEND1
1126 if (mb_cur_max > 1) {
1127 char mbb[MB_LEN_MAX + 1];
1128 wchar_t wc;
1129 int i, n;
1130 size_t dist;
1132 out->l = 0;
1133 while (inp < maxp) {
1134 if (*inp & 0200)
1135 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1136 else {
1137 wc = *inp;
1138 n = 1;
1140 if (n == -1) {
1141 /* FIXME Why mbtowc() resetting here?
1142 * FIXME what about ISO 2022-JP plus -- those
1143 * FIXME will loose shifts, then!
1144 * FIXME THUS - we'd need special "known points"
1145 * FIXME to do so - say, after a newline!!
1146 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1147 mbtowc(&wc, NULL, mb_cur_max);
1148 wc = utf8 ? 0xFFFD : '?';
1149 n = 1;
1150 } else if (n == 0)
1151 n = 1;
1152 inp += n;
1153 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1154 wc != '\t') {
1155 if ((wc & ~(wchar_t)037) == 0)
1156 wc = utf8 ? 0x2400 | wc : '?';
1157 else if (wc == 0177)
1158 wc = utf8 ? 0x2421 : '?';
1159 else
1160 wc = utf8 ? 0x2426 : '?';
1162 if ((n = wctomb(mbb, wc)) <= 0)
1163 continue;
1164 out->l += n;
1165 if (out->l >= msz - 1) {
1166 dist = outp - out->s;
1167 out->s = srealloc(out->s, msz += 32);
1168 outp = &out->s[dist];
1170 for (i = 0; i < n; ++i)
1171 *outp++ = mbb[i];
1173 } else
1174 #endif /* C90AMEND1 */
1176 int c;
1177 while (inp < maxp) {
1178 c = *inp++ & 0377;
1179 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1180 c = '?';
1181 *outp++ = c;
1183 out->l = in->l;
1185 jleave:
1186 out->s[out->l] = '\0';
1187 NYD_LEAVE;
1190 FL char *
1191 prstr(char const *s)
1193 struct str in, out;
1194 char *rp;
1195 NYD_ENTER;
1197 in.s = UNCONST(s);
1198 in.l = strlen(s);
1199 makeprint(&in, &out);
1200 rp = savestrbuf(out.s, out.l);
1201 free(out.s);
1202 NYD_LEAVE;
1203 return rp;
1206 FL int
1207 prout(char const *s, size_t sz, FILE *fp)
1209 struct str in, out;
1210 int n;
1211 NYD_ENTER;
1213 in.s = UNCONST(s);
1214 in.l = sz;
1215 makeprint(&in, &out);
1216 n = fwrite(out.s, 1, out.l, fp);
1217 free(out.s);
1218 NYD_LEAVE;
1219 return n;
1222 FL size_t
1223 putuc(int u, int c, FILE *fp)
1225 size_t rv;
1226 UNUSED(u);
1227 NYD_ENTER;
1229 #ifdef HAVE_C90AMEND1
1230 if (utf8 && (u & ~(wchar_t)0177)) {
1231 char mbb[MB_LEN_MAX];
1232 int i, n;
1234 if ((n = wctomb(mbb, u)) > 0) {
1235 rv = wcwidth(u);
1236 for (i = 0; i < n; ++i)
1237 if (putc(mbb[i] & 0377, fp) == EOF) {
1238 rv = 0;
1239 break;
1241 } else if (n == 0)
1242 rv = (putc('\0', fp) != EOF);
1243 else
1244 rv = 0;
1245 } else
1246 #endif
1247 rv = (putc(c, fp) != EOF);
1248 NYD_LEAVE;
1249 return rv;
1252 #ifdef HAVE_COLOUR
1253 FL void
1254 colour_table_create(char const *pager_used)
1256 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1257 size_t i;
1258 struct colour_table *ct;
1259 NYD_ENTER;
1261 if (ok_blook(colour_disable))
1262 goto jleave;
1264 /* If pager, check wether it is allowed to use colour */
1265 if (pager_used != NULL) {
1266 char *pager;
1268 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1269 u.ccp = COLOUR_PAGERS;
1270 pager = savestr(u.cp);
1272 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
1273 if (strstr(pager_used, u.cp) != NULL)
1274 goto jok;
1275 goto jleave;
1278 /* $TERM is different in that we default to false unless whitelisted */
1280 char *term, *okterms;
1282 /* Don't use getenv(), but force copy-in into our own tables.. */
1283 if ((term = _var_voklook("TERM")) == NULL)
1284 goto jleave;
1285 if ((okterms = ok_vlook(colour_terms)) == NULL)
1286 okterms = UNCONST(COLOUR_TERMS);
1287 okterms = savestr(okterms);
1289 i = strlen(term);
1290 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1291 if (!strncmp(u.cp, term, i))
1292 goto jok;
1293 goto jleave;
1296 jok:
1297 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1298 { static struct {
1299 enum okeys okey;
1300 enum colourspec cspec;
1301 char const *defval;
1302 } const map[] = {
1303 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1304 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1305 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1306 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1307 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1310 for (i = 0; i < NELEM(map); ++i) {
1311 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1312 u.ccp = map[i].defval;
1313 u.cp = _colour_iso6429(u.ccp);
1314 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1315 ct->ct_csinfo[map[i].cspec].s = u.cp;
1318 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1319 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1321 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1322 u.ccp = COLOUR_USER_HEADERS;
1323 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1324 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1325 jleave:
1326 NYD_LEAVE;
1329 FL void
1330 colour_put(FILE *fp, enum colourspec cs)
1332 NYD_ENTER;
1333 if (colour_table != NULL) {
1334 struct str const *cp = colour_get(cs);
1336 fwrite(cp->s, cp->l, 1, fp);
1338 NYD_LEAVE;
1341 FL void
1342 colour_put_header(FILE *fp, char const *name)
1344 enum colourspec cs = COLOURSPEC_HEADER;
1345 struct str const *uheads;
1346 char *cp, *cp_base, *x;
1347 size_t namelen;
1348 NYD_ENTER;
1350 if (colour_table == NULL)
1351 goto j_leave;
1352 /* Normal header colours if there are no user headers */
1353 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1354 if (uheads->s == NULL)
1355 goto jleave;
1357 /* Iterate over all entries in the *colour-user-headers* list */
1358 cp = ac_alloc(uheads->l +1);
1359 memcpy(cp, uheads->s, uheads->l +1);
1360 cp_base = cp;
1361 namelen = strlen(name);
1362 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1363 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1364 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1365 cs = COLOURSPEC_UHEADER;
1366 break;
1369 ac_free(cp_base);
1370 jleave:
1371 colour_put(fp, cs);
1372 j_leave:
1373 NYD_LEAVE;
1376 FL void
1377 colour_reset(FILE *fp)
1379 NYD_ENTER;
1380 if (colour_table != NULL)
1381 fwrite("\033[0m", 4, 1, fp);
1382 NYD_LEAVE;
1385 FL struct str const *
1386 colour_get(enum colourspec cs)
1388 struct str const *rv = NULL;
1389 NYD_ENTER;
1391 if (colour_table != NULL)
1392 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1393 rv = NULL;
1394 NYD_LEAVE;
1395 return rv;
1397 #endif /* HAVE_COLOUR */
1399 FL void
1400 time_current_update(struct time_current *tc, bool_t full_update)
1402 NYD_ENTER;
1403 tc->tc_time = time(NULL);
1404 if (full_update) {
1405 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1406 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1407 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1409 NYD_LEAVE;
1412 static void
1413 _out_of_memory(void)
1415 panic("no memory");
1418 #ifndef HAVE_DEBUG
1419 FL void *
1420 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1422 void *rv;
1423 NYD_ENTER;
1425 if (s == 0)
1426 s = 1;
1427 if ((rv = malloc(s)) == NULL)
1428 _out_of_memory();
1429 NYD_LEAVE;
1430 return rv;
1433 FL void *
1434 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1436 void *rv;
1437 NYD_ENTER;
1439 if (s == 0)
1440 s = 1;
1441 if (v == NULL)
1442 rv = smalloc(s);
1443 else if ((rv = realloc(v, s)) == NULL)
1444 _out_of_memory();
1445 NYD_LEAVE;
1446 return rv;
1449 FL void *
1450 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1452 void *rv;
1453 NYD_ENTER;
1455 if (size == 0)
1456 size = 1;
1457 if ((rv = calloc(nmemb, size)) == NULL)
1458 _out_of_memory();
1459 NYD_LEAVE;
1460 return rv;
1463 #else /* !HAVE_DEBUG */
1464 CTA(sizeof(char) == sizeof(ui8_t));
1466 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1467 # define _HOPE_SET(C) \
1468 do {\
1469 union mem_ptr __xl, __xu;\
1470 struct mem_chunk *__xc;\
1471 __xl.p_p = (C).p_p;\
1472 __xc = __xl.p_c - 1;\
1473 __xu.p_p = __xc;\
1474 (C).p_cp += 8;\
1475 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1476 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1477 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1478 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1479 __xu.p_ui8p += __xc->mc_size - 8;\
1480 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1481 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1482 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1483 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1484 } while (0)
1485 # define _HOPE_GET_TRACE(C,BAD) \
1486 do {\
1487 (C).p_cp += 8;\
1488 _HOPE_GET(C, BAD);\
1489 (C).p_cp += 8;\
1490 } while(0)
1491 # define _HOPE_GET(C,BAD) \
1492 do {\
1493 union mem_ptr __xl, __xu;\
1494 struct mem_chunk *__xc;\
1495 ui32_t __i;\
1496 __xl.p_p = (C).p_p;\
1497 __xl.p_cp -= 8;\
1498 (C).p_cp = __xl.p_cp;\
1499 __xc = __xl.p_c - 1;\
1500 (BAD) = FAL0;\
1501 __i = 0;\
1502 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1503 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1504 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1505 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1506 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1507 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1508 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1509 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1510 if (__i != 0) {\
1511 (BAD) = TRU1;\
1512 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1513 __xl.p_p, __i, mdbg_file, mdbg_line);\
1515 __xu.p_p = __xc;\
1516 __xu.p_ui8p += __xc->mc_size - 8;\
1517 __i = 0;\
1518 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1519 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1520 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1521 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1522 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1523 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1524 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1525 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1526 if (__i != 0) {\
1527 (BAD) = TRU1;\
1528 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1529 __xl.p_p, __i, mdbg_file, mdbg_line);\
1531 if (BAD)\
1532 alert(" ..canary last seen: %s, line %u",\
1533 __xc->mc_file, __xc->mc_line);\
1534 } while (0)
1536 FL void *
1537 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1539 union mem_ptr p;
1540 NYD_ENTER;
1542 if (s == 0)
1543 s = 1;
1544 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1546 if ((p.p_p = (malloc)(s)) == NULL)
1547 _out_of_memory();
1548 p.p_c->mc_prev = NULL;
1549 if ((p.p_c->mc_next = _mem_list) != NULL)
1550 _mem_list->mc_prev = p.p_c;
1551 p.p_c->mc_file = mdbg_file;
1552 p.p_c->mc_line = (ui16_t)mdbg_line;
1553 p.p_c->mc_isfree = FAL0;
1554 p.p_c->mc_size = (ui32_t)s;
1555 _mem_list = p.p_c++;
1556 _HOPE_SET(p);
1558 ++_mem_aall;
1559 ++_mem_acur;
1560 _mem_amax = MAX(_mem_amax, _mem_acur);
1561 _mem_mall += s;
1562 _mem_mcur += s;
1563 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1564 NYD_LEAVE;
1565 return p.p_p;
1568 FL void *
1569 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1571 union mem_ptr p;
1572 bool_t isbad;
1573 NYD_ENTER;
1575 if ((p.p_p = v) == NULL) {
1576 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
1577 goto jleave;
1580 _HOPE_GET(p, isbad);
1581 --p.p_c;
1582 if (p.p_c->mc_isfree) {
1583 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1584 "\tLast seen: %s, line %d\n",
1585 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1586 goto jforce;
1589 if (p.p_c == _mem_list)
1590 _mem_list = p.p_c->mc_next;
1591 else
1592 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1593 if (p.p_c->mc_next != NULL)
1594 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1596 --_mem_acur;
1597 _mem_mcur -= p.p_c->mc_size;
1598 jforce:
1599 if (s == 0)
1600 s = 1;
1601 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1603 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
1604 _out_of_memory();
1605 p.p_c->mc_prev = NULL;
1606 if ((p.p_c->mc_next = _mem_list) != NULL)
1607 _mem_list->mc_prev = p.p_c;
1608 p.p_c->mc_file = mdbg_file;
1609 p.p_c->mc_line = (ui16_t)mdbg_line;
1610 p.p_c->mc_isfree = FAL0;
1611 p.p_c->mc_size = (ui32_t)s;
1612 _mem_list = p.p_c++;
1613 _HOPE_SET(p);
1615 ++_mem_aall;
1616 ++_mem_acur;
1617 _mem_amax = MAX(_mem_amax, _mem_acur);
1618 _mem_mall += s;
1619 _mem_mcur += s;
1620 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1621 jleave:
1622 NYD_LEAVE;
1623 return p.p_p;
1626 FL void *
1627 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1629 union mem_ptr p;
1630 NYD_ENTER;
1632 if (size == 0)
1633 size = 1;
1634 if (nmemb == 0)
1635 nmemb = 1;
1636 size *= nmemb;
1637 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
1639 if ((p.p_p = (malloc)(size)) == NULL)
1640 _out_of_memory();
1641 memset(p.p_p, 0, size);
1642 p.p_c->mc_prev = NULL;
1643 if ((p.p_c->mc_next = _mem_list) != NULL)
1644 _mem_list->mc_prev = p.p_c;
1645 p.p_c->mc_file = mdbg_file;
1646 p.p_c->mc_line = (ui16_t)mdbg_line;
1647 p.p_c->mc_isfree = FAL0;
1648 p.p_c->mc_size = (ui32_t)size;
1649 _mem_list = p.p_c++;
1650 _HOPE_SET(p);
1652 ++_mem_aall;
1653 ++_mem_acur;
1654 _mem_amax = MAX(_mem_amax, _mem_acur);
1655 _mem_mall += size;
1656 _mem_mcur += size;
1657 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1658 NYD_LEAVE;
1659 return p.p_p;
1662 FL void
1663 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1665 union mem_ptr p;
1666 bool_t isbad;
1667 NYD_ENTER;
1669 if ((p.p_p = v) == NULL) {
1670 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1671 goto jleave;
1674 _HOPE_GET(p, isbad);
1675 --p.p_c;
1676 if (p.p_c->mc_isfree) {
1677 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1678 "\tLast seen: %s, line %d\n",
1679 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1680 goto jleave;
1683 if (p.p_c == _mem_list)
1684 _mem_list = p.p_c->mc_next;
1685 else
1686 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1687 if (p.p_c->mc_next != NULL)
1688 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1689 p.p_c->mc_isfree = TRU1;
1691 --_mem_acur;
1692 _mem_mcur -= p.p_c->mc_size;
1694 if (options & OPT_DEBUG) {
1695 p.p_c->mc_next = _mem_free;
1696 _mem_free = p.p_c;
1697 } else
1698 (free)(p.p_c);
1699 jleave:
1700 NYD_LEAVE;
1703 FL void
1704 smemreset(void)
1706 union mem_ptr p;
1707 size_t c = 0, s = 0;
1708 NYD_ENTER;
1710 for (p.p_c = _mem_free; p.p_c != NULL;) {
1711 void *vp = p.p_c;
1712 ++c;
1713 s += p.p_c->mc_size;
1714 p.p_c = p.p_c->mc_next;
1715 (free)(vp);
1717 _mem_free = NULL;
1719 if (options & OPT_DEBUG)
1720 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1721 c, s);
1722 NYD_LEAVE;
1725 FL int
1726 c_smemtrace(void *v)
1728 /* For _HOPE_GET() */
1729 char const * const mdbg_file = "smemtrace()";
1730 int const mdbg_line = -1;
1731 FILE *fp;
1732 union mem_ptr p, xp;
1733 bool_t isbad;
1734 size_t lines;
1735 NYD_ENTER;
1737 v = (void*)0x1;
1738 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1739 NULL) {
1740 perror("tmpfile");
1741 goto jleave;
1744 fprintf(fp, "Memory statistics:\n"
1745 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
1746 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
1747 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
1749 fprintf(fp, "Currently allocated memory chunks:\n");
1750 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1751 ++lines, p.p_c = p.p_c->mc_next) {
1752 xp = p;
1753 ++xp.p_c;
1754 _HOPE_GET_TRACE(xp, isbad);
1755 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1756 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1757 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
1758 p.p_c->mc_line);
1761 if (options & OPT_DEBUG) {
1762 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1763 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1764 xp = p;
1765 ++xp.p_c;
1766 _HOPE_GET_TRACE(xp, isbad);
1767 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1768 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1769 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1770 p.p_c->mc_file, p.p_c->mc_line);
1774 page_or_print(fp, lines);
1775 Fclose(fp);
1776 v = NULL;
1777 jleave:
1778 NYD_LEAVE;
1779 return (v != NULL);
1782 # ifdef _HAVE_MEMCHECK
1783 FL bool_t
1784 _smemcheck(char const *mdbg_file, int mdbg_line)
1786 union mem_ptr p, xp;
1787 bool_t anybad = FAL0, isbad;
1788 size_t lines;
1789 NYD_ENTER;
1791 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1792 ++lines, p.p_c = p.p_c->mc_next) {
1793 xp = p;
1794 ++xp.p_c;
1795 _HOPE_GET_TRACE(xp, isbad);
1796 if (isbad) {
1797 anybad = TRU1;
1798 fprintf(stderr,
1799 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1800 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1801 p.p_c->mc_file, p.p_c->mc_line);
1805 if (options & OPT_DEBUG) {
1806 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1807 xp = p;
1808 ++xp.p_c;
1809 _HOPE_GET_TRACE(xp, isbad);
1810 if (isbad) {
1811 anybad = TRU1;
1812 fprintf(stderr,
1813 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1814 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1815 p.p_c->mc_file, p.p_c->mc_line);
1819 NYD_LEAVE;
1820 return anybad;
1822 # endif /* _HAVE_MEMCHECK */
1824 # undef _HOPE_SIZE
1825 # undef _HOPE_SET
1826 # undef _HOPE_GET_TRACE
1827 # undef _HOPE_GET
1828 #endif /* HAVE_DEBUG */
1830 /* vim:set fenc=utf-8:s-it-mode */