nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / auxlily.c
blobddb5d2d3c3096516532d73ba73c3904809ad1696
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;
65 #endif
67 /* NYD */
68 #ifdef HAVE_DEBUG
69 static ui32_t _nyd_curr;
70 static ui32_t _nyd_level;
71 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
72 #endif
74 /* {hold,rele}_all_sigs() */
75 static size_t _alls_depth;
76 static sigset_t _alls_nset, _alls_oset;
78 /* {hold,rele}_sigs() */
79 static size_t _hold_sigdepth;
80 static sigset_t _hold_nset, _hold_oset;
82 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
83 #ifdef HAVE_COLOUR
84 static char * _colour_iso6429(char const *wish);
85 #endif
87 #ifdef HAVE_DEBUG
88 static void _nyd_print(struct nyd_info *nip);
89 #endif
91 #ifdef HAVE_COLOUR
92 static char *
93 _colour_iso6429(char const *wish)
95 char const * const wish_orig = wish;
96 char *xwish, *cp, cfg[3] = {0, 0, 0};
97 NYD_ENTER;
99 /* Since we use salloc(), reuse the strcomma() buffer also for the return
100 * value, ensure we have enough room for that */
102 size_t i = strlen(wish) + 1;
103 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
104 memcpy(xwish, wish, i);
105 wish = xwish;
108 /* Iterate over the colour spec */
109 while ((cp = strcomma(&xwish, TRU1)) != NULL) {
110 char *y, *x = strchr(cp, '=');
111 if (x == NULL) {
112 jbail:
113 fprintf(stderr, tr(527,
114 "Invalid colour specification \"%s\": >>> %s <<<\n"),
115 wish_orig, cp);
116 continue;
118 *x++ = '\0';
120 /* TODO convert the ft/fg/bg parser into a table-based one! */
121 if (!asccasecmp(cp, "ft")) {
122 if (!asccasecmp(x, "bold"))
123 cfg[0] = '1';
124 else if (!asccasecmp(x, "inverse"))
125 cfg[0] = '7';
126 else if (!asccasecmp(x, "underline"))
127 cfg[0] = '4';
128 else
129 goto jbail;
130 } else if (!asccasecmp(cp, "fg")) {
131 y = cfg + 1;
132 goto jiter_colour;
133 } else if (!asccasecmp(cp, "bg")) {
134 y = cfg + 2;
135 jiter_colour:
136 if (!asccasecmp(x, "black"))
137 *y = '0';
138 else if (!asccasecmp(x, "blue"))
139 *y = '4';
140 else if (!asccasecmp(x, "green"))
141 *y = '2';
142 else if (!asccasecmp(x, "red"))
143 *y = '1';
144 else if (!asccasecmp(x, "brown"))
145 *y = '3';
146 else if (!asccasecmp(x, "magenta"))
147 *y = '5';
148 else if (!asccasecmp(x, "cyan"))
149 *y = '6';
150 else if (!asccasecmp(x, "white"))
151 *y = '7';
152 else
153 goto jbail;
154 } else
155 goto jbail;
158 /* Restore our salloc() buffer, create return value */
159 xwish = UNCONST(wish);
160 if (cfg[0] || cfg[1] || cfg[2]) {
161 xwish[0] = '\033';
162 xwish[1] = '[';
163 xwish += 2;
164 if (cfg[0])
165 *xwish++ = cfg[0];
166 if (cfg[1]) {
167 if (cfg[0])
168 *xwish++ = ';';
169 xwish[0] = '3';
170 xwish[1] = cfg[1];
171 xwish += 2;
173 if (cfg[2]) {
174 if (cfg[0] || cfg[1])
175 *xwish++ = ';';
176 xwish[0] = '4';
177 xwish[1] = cfg[2];
178 xwish += 2;
180 *xwish++ = 'm';
182 *xwish = '\0';
183 NYD_LEAVE;
184 return UNCONST(wish);
186 #endif /* HAVE_COLOUR */
188 #ifdef HAVE_DEBUG
189 static void
190 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
192 char buf[80];
193 union {int i; size_t z;} u;
195 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
196 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
197 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
198 if (u.i > 0) {
199 u.z = u.i;
200 if (u.z > sizeof buf)
201 u.z = sizeof buf - 1; /* (Skip \0) */
202 write(STDERR_FILENO, buf, u.z);
205 #endif
207 FL void
208 panic(char const *format, ...)
210 va_list ap;
211 NYD_ENTER;
213 fprintf(stderr, tr(1, "Panic: "));
215 va_start(ap, format);
216 vfprintf(stderr, format, ap);
217 va_end(ap);
219 fputs("\n", stderr);
220 fflush(stderr);
221 NYD_LEAVE;
222 abort(); /* Was exit(EXIT_ERR); for a while, but no */
225 FL void
226 alert(char const *format, ...)
228 va_list ap;
229 NYD_ENTER;
231 fprintf(stderr, tr(1, "Panic: "));
233 va_start(ap, format);
234 vfprintf(stderr, format, ap);
235 va_end(ap);
237 fputs("\n", stderr);
238 fflush(stderr);
239 NYD_LEAVE;
242 FL sighandler_type
243 safe_signal(int signum, sighandler_type handler)
245 struct sigaction nact, oact;
246 sighandler_type rv;
247 NYD_ENTER;
249 nact.sa_handler = handler;
250 sigemptyset(&nact.sa_mask);
251 nact.sa_flags = 0;
252 #ifdef SA_RESTART
253 nact.sa_flags |= SA_RESTART;
254 #endif
255 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
256 NYD_LEAVE;
257 return rv;
260 FL void
261 hold_all_sigs(void)
263 NYD_ENTER;
264 if (_alls_depth++ == 0) {
265 sigfillset(&_alls_nset);
266 sigdelset(&_alls_nset, SIGABRT);
267 #ifdef SIGBUS
268 sigdelset(&_alls_nset, SIGBUS);
269 #endif
270 sigdelset(&_alls_nset, SIGCHLD);
271 sigdelset(&_alls_nset, SIGFPE);
272 sigdelset(&_alls_nset, SIGILL);
273 sigdelset(&_alls_nset, SIGKILL);
274 sigdelset(&_alls_nset, SIGSEGV);
275 sigdelset(&_alls_nset, SIGSTOP);
276 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
278 NYD_LEAVE;
281 FL void
282 rele_all_sigs(void)
284 NYD_ENTER;
285 if (--_alls_depth == 0)
286 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
287 NYD_LEAVE;
290 FL void
291 hold_sigs(void)
293 NYD_ENTER;
294 if (_hold_sigdepth++ == 0) {
295 sigemptyset(&_hold_nset);
296 sigaddset(&_hold_nset, SIGHUP);
297 sigaddset(&_hold_nset, SIGINT);
298 sigaddset(&_hold_nset, SIGQUIT);
299 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
301 NYD_LEAVE;
304 FL void
305 rele_sigs(void)
307 NYD_ENTER;
308 if (--_hold_sigdepth == 0)
309 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
310 NYD_LEAVE;
313 #ifdef HAVE_DEBUG
314 FL void
315 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
317 struct nyd_info *nip = _nyd_infos;
319 if (_nyd_curr != NELEM(_nyd_infos))
320 nip += _nyd_curr++;
321 else
322 _nyd_curr = 1;
323 nip->ni_file = file;
324 nip->ni_fun = fun;
325 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
326 nip->ni_level = ((act == 0) ? _nyd_level
327 : (act == 1) ? ++_nyd_level : _nyd_level--);
330 FL void
331 _nyd_oncrash(int signo)
333 struct sigaction xact;
334 sigset_t xset;
335 struct nyd_info *nip;
336 size_t i;
338 xact.sa_handler = SIG_DFL;
339 sigemptyset(&xact.sa_mask);
340 xact.sa_flags = 0;
341 sigaction(signo, &xact, NULL);
343 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
344 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
345 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
346 _nyd_print(nip++);
347 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
348 _nyd_print(nip++);
350 sigemptyset(&xset);
351 sigaddset(&xset, signo);
352 sigprocmask(SIG_UNBLOCK, &xset, NULL);
353 kill(0, signo);
354 while (1)
355 exit(1);
357 #endif
359 FL void
360 touch(struct message *mp)
362 NYD_ENTER;
363 mp->m_flag |= MTOUCH;
364 if (!(mp->m_flag & MREAD))
365 mp->m_flag |= MREAD | MSTATUS;
366 NYD_LEAVE;
369 FL bool_t
370 is_dir(char const *name)
372 struct stat sbuf;
373 bool_t rv = FAL0;
374 NYD_ENTER;
376 if (!stat(name, &sbuf))
377 rv = !!S_ISDIR(sbuf.st_mode);
378 NYD_LEAVE;
379 return rv;
382 FL int
383 argcount(char **argv)
385 char **ap;
386 NYD_ENTER;
388 for (ap = argv; *ap++ != NULL;)
390 NYD_LEAVE;
391 return ap - argv - 1;
394 FL int
395 screensize(void)
397 int s;
398 char *cp;
399 NYD_ENTER;
401 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
402 s = scrnheight - 4;
403 NYD_LEAVE;
404 return s;
407 FL char const *
408 get_pager(void)
410 char const *cp;
411 NYD_ENTER;
413 cp = ok_vlook(PAGER);
414 if (cp == NULL || *cp == '\0')
415 cp = XPAGER;
416 NYD_LEAVE;
417 return cp;
420 FL size_t
421 paging_seems_sensible(void)
423 size_t rv = 0;
424 char const *cp;
425 NYD_ENTER;
427 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
428 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
429 NYD_LEAVE;
430 return rv;
433 FL void
434 page_or_print(FILE *fp, size_t lines)
436 size_t rows;
437 int c;
438 NYD_ENTER;
440 fflush_rewind(fp);
442 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
443 while ((c = getc(fp)) != EOF)
444 if (c == '\n' && ++lines > rows)
445 break;
446 rewind(fp);
449 if (rows != 0 && lines >= rows)
450 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
451 else
452 while ((c = getc(fp)) != EOF)
453 putchar(c);
454 NYD_LEAVE;
457 FL enum protocol
458 which_protocol(char const *name)
460 struct stat st;
461 char const *cp;
462 char *np;
463 size_t sz;
464 enum protocol rv = PROTO_UNKNOWN;
465 NYD_ENTER;
467 if (name[0] == '%' && name[1] == ':')
468 name += 2;
469 for (cp = name; *cp && *cp != ':'; cp++)
470 if (!alnumchar(*cp))
471 goto jfile;
473 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
474 if (strncmp(name, "pop3://", 7) == 0) {
475 #ifdef HAVE_POP3
476 rv = PROTO_POP3;
477 #else
478 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
479 #endif
480 goto jleave;
482 if (strncmp(name, "pop3s://", 8) == 0) {
483 #if defined HAVE_POP3 && defined HAVE_SSL
484 rv = PROTO_POP3;
485 #else
486 # ifndef HAVE_POP3
487 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
488 # endif
489 # ifndef HAVE_SSL
490 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
491 # endif
492 #endif
493 goto jleave;
495 if (strncmp(name, "imap://", 7) == 0) {
496 #ifdef HAVE_IMAP
497 rv = PROTO_IMAP;
498 #else
499 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
500 #endif
501 goto jleave;
503 if (strncmp(name, "imaps://", 8) == 0) {
504 #if defined HAVE_IMAP && defined HAVE_SSL
505 rv = PROTO_IMAP;
506 #else
507 # ifndef HAVE_IMAP
508 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
509 # endif
510 # ifndef HAVE_SSL
511 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
512 # endif
513 #endif
514 goto jleave;
516 } else {
517 /* TODO This is the de facto maildir code and thus belongs into there! */
518 jfile:
519 rv = PROTO_FILE;
520 np = ac_alloc((sz = strlen(name)) + 5);
521 memcpy(np, name, sz + 1);
522 if (stat(name, &st) == 0) {
523 if (S_ISDIR(st.st_mode)) {
524 strcpy(&np[sz], "/tmp");
525 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
526 strcpy(&np[sz], "/new");
527 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
528 strcpy(&np[sz], "/cur");
529 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode))
530 rv = PROTO_MAILDIR;
534 } else {
535 strcpy(&np[sz], ".gz");
536 if (stat(np, &st) < 0) {
537 strcpy(&np[sz], ".bz2");
538 if (stat(np, &st) < 0) {
539 if ((cp = ok_vlook(newfolders)) != NULL &&
540 strcmp(cp, "maildir") == 0)
541 rv = PROTO_MAILDIR;
545 ac_free(np);
547 jleave:
548 NYD_LEAVE;
549 return rv;
552 FL ui32_t
553 torek_hash(char const *name)
555 /* Chris Torek's hash.
556 * NOTE: need to change *at least* create-okey-map.pl when changing the
557 * algorithm!! */
558 ui32_t h = 0;
559 NYD_ENTER;
561 while (*name != '\0') {
562 h *= 33;
563 h += *name++;
565 NYD_LEAVE;
566 return h;
569 FL unsigned
570 pjw(char const *cp) /* XXX obsolete that -> torek_hash */
572 unsigned h = 0, g;
573 NYD_ENTER;
575 cp--;
576 while (*++cp) {
577 h = (h << 4 & 0xffffffff) + (*cp&0377);
578 if ((g = h & 0xf0000000) != 0) {
579 h = h ^ g >> 24;
580 h = h ^ g;
583 NYD_LEAVE;
584 return h;
587 FL ui32_t
588 nextprime(ui32_t n)
590 static ui32_t const primes[] = {
591 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
592 131071, 262139, 524287, 1048573, 2097143, 4194301,
593 8388593, 16777213, 33554393, 67108859, 134217689,
594 268435399, 536870909, 1073741789, 2147483647
597 ui32_t mprime = 7;
598 size_t i;
599 NYD_ENTER;
601 for (i = 0; i < NELEM(primes); i++)
602 if ((mprime = primes[i]) >= (n < 65536 ? n*4 : (n < 262144u ? n*2 : n)))
603 break;
604 if (i == NELEM(primes))
605 mprime = n; /* TODO not so prime, but better than failure */
606 NYD_LEAVE;
607 return mprime;
610 FL int
611 expand_shell_escape(char const **s, bool_t use_nail_extensions)
613 char const *xs = *s;
614 int c, n;
615 NYD_ENTER;
617 if ((c = *xs & 0xFF) == '\0')
618 goto jleave;
619 ++xs;
620 if (c != '\\')
621 goto jleave;
623 switch ((c = *xs & 0xFF)) {
624 case '\\': break;
625 case 'a': c = '\a'; break;
626 case 'b': c = '\b'; break;
627 case 'c': c = PROMPT_STOP; break;
628 case 'f': c = '\f'; break;
629 case 'n': c = '\n'; break;
630 case 'r': c = '\r'; break;
631 case 't': c = '\t'; break;
632 case 'v': c = '\v'; break;
633 case '0':
634 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
635 c <<= 3;
636 c |= *xs - '0';
638 goto jleave;
639 /* S-nail extension for nice (get)prompt(()) support */
640 case '&':
641 case '?':
642 case '$':
643 case '@':
644 if (use_nail_extensions) {
645 switch (c) {
646 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
647 case '?': c = exec_last_comm_error ? '1' : '0'; break;
648 case '$': c = PROMPT_DOLLAR; break;
649 case '@': c = PROMPT_AT; break;
651 break;
653 /* FALLTHRU */
654 case '\0':
655 /* A sole <backslash> at EOS is treated as-is! */
656 /* FALLTHRU */
657 default:
658 c = '\\';
659 goto jleave;
661 ++xs;
662 jleave:
663 *s = xs;
664 NYD_LEAVE;
665 return c;
668 FL char *
669 getprompt(void)
671 static char buf[PROMPT_BUFFER_SIZE];
673 char *cp = buf;
674 char const *ccp;
675 NYD_ENTER;
677 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
678 goto jleave;
680 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
681 char const *a;
682 size_t l;
683 int c = expand_shell_escape(&ccp, TRU1);
685 if (c > 0) {
686 *cp = (char)c;
687 continue;
689 if (c == 0 || c == PROMPT_STOP)
690 break;
692 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
693 if (a == NULL)
694 a = "";
695 l = strlen(a);
696 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
697 *cp++ = '?';
698 else {
699 memcpy(cp, a, l);
700 cp += --l;
703 jleave:
704 *cp = '\0';
705 NYD_LEAVE;
706 return buf;
709 FL char *
710 nodename(int mayoverride)
712 static char *hostname;
713 struct utsname ut;
714 char *hn;
715 #ifdef HAVE_SOCKETS
716 # ifdef HAVE_IPV6
717 struct addrinfo hints, *res;
718 # else
719 struct hostent *hent;
720 # endif
721 #endif
722 NYD_ENTER;
724 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
725 if (hostname != NULL)
726 free(hostname);
727 hostname = sstrdup(hn);
728 } else if (hostname == NULL) {
729 uname(&ut);
730 hn = ut.nodename;
731 #ifdef HAVE_SOCKETS
732 # ifdef HAVE_IPV6
733 memset(&hints, 0, sizeof hints);
734 hints.ai_family = AF_UNSPEC;
735 hints.ai_socktype = SOCK_DGRAM; /* dummy */
736 hints.ai_flags = AI_CANONNAME;
737 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
738 if (res->ai_canonname != NULL) {
739 size_t l = strlen(res->ai_canonname);
740 hn = ac_alloc(l + 1);
741 memcpy(hn, res->ai_canonname, l + 1);
743 freeaddrinfo(res);
745 # else
746 hent = gethostbyname(hn);
747 if (hent != NULL)
748 hn = hent->h_name;
749 # endif
750 #endif
751 hostname = sstrdup(hn);
752 #if defined HAVE_SOCKETS && defined HAVE_IPV6
753 if (hn != ut.nodename)
754 ac_free(hn);
755 #endif
757 NYD_LEAVE;
758 return hostname;
761 FL char *
762 lookup_password_for_token(char const *token)
764 size_t tl;
765 char *var, *cp;
766 NYD_ENTER;
768 tl = strlen(token);
769 var = ac_alloc(tl + 9 +1);
771 memcpy(var, "password-", 9);
772 memcpy(var + 9, token, tl);
773 var[tl + 9] = '\0';
775 if ((cp = vok_vlook(var)) != NULL)
776 cp = savestr(cp);
777 ac_free(var);
778 NYD_LEAVE;
779 return cp;
782 FL char *
783 getrandstring(size_t length)
785 static unsigned char nodedigest[16];
786 static pid_t pid;
788 struct str b64;
789 char *data, *cp;
790 size_t i;
791 int fd = -1;
792 #ifdef HAVE_MD5
793 md5_ctx ctx;
794 #else
795 size_t j;
796 #endif
797 NYD_ENTER;
799 data = ac_alloc(length);
800 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
801 length != (size_t)read(fd, data, length)) {
802 if (pid == 0) {
803 pid = getpid();
804 srand(pid);
805 cp = nodename(0);
806 #ifdef HAVE_MD5
807 md5_init(&ctx);
808 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
809 md5_final(nodedigest, &ctx);
810 #else
811 /* In that case it's only used for boundaries and Message-Id:s so that
812 * srand(3) should suffice */
813 j = strlen(cp) + 1;
814 for (i = 0; i < sizeof(nodedigest); ++i)
815 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
816 #endif
818 for (i = 0; i < length; i++)
819 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
820 nodedigest[i % sizeof nodedigest]);
822 if (fd >= 0)
823 close(fd);
825 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
826 ac_free(data);
827 assert(length < b64.l);
828 b64.s[length] = '\0';
829 NYD_LEAVE;
830 return b64.s;
833 #ifdef HAVE_MD5
834 FL char *
835 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
837 char const *cp = vp;
838 size_t i, j;
839 NYD_ENTER;
841 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
842 j = i << 1;
843 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
844 hex[++j] = hexchar(cp[i] & 0x0f);
846 NYD_LEAVE;
847 return hex;
850 FL char *
851 cram_md5_string(char const *user, char const *pass, char const *b64)
853 struct str in, out;
854 char digest[16], *cp;
855 size_t lu;
856 NYD_ENTER;
858 out.s = NULL;
859 in.s = UNCONST(b64);
860 in.l = strlen(in.s);
861 (void)b64_decode(&out, &in, NULL);
862 assert(out.s != NULL);
864 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
865 free(out.s);
866 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
868 lu = strlen(user);
869 in.l = lu + MD5TOHEX_SIZE +1;
870 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
871 memcpy(in.s, user, lu);
872 in.s[lu] = ' ';
873 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
874 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
875 ac_free(in.s);
876 NYD_LEAVE;
877 return out.s;
880 FL void
881 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
882 void *digest)
885 * This code is taken from
887 * Network Working Group H. Krawczyk
888 * Request for Comments: 2104 IBM
889 * Category: Informational M. Bellare
890 * UCSD
891 * R. Canetti
892 * IBM
893 * February 1997
896 * HMAC: Keyed-Hashing for Message Authentication
898 md5_ctx context;
899 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
900 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
901 unsigned char tk[16];
902 int i;
903 NYD_ENTER;
905 /* if key is longer than 64 bytes reset it to key=MD5(key) */
906 if (key_len > 64) {
907 md5_ctx tctx;
909 md5_init(&tctx);
910 md5_update(&tctx, key, key_len);
911 md5_final(tk, &tctx);
913 key = tk;
914 key_len = 16;
917 /* the HMAC_MD5 transform looks like:
919 * MD5(K XOR opad, MD5(K XOR ipad, text))
921 * where K is an n byte key
922 * ipad is the byte 0x36 repeated 64 times
923 * opad is the byte 0x5c repeated 64 times
924 * and text is the data being protected */
926 /* start out by storing key in pads */
927 memset(k_ipad, 0, sizeof k_ipad);
928 memset(k_opad, 0, sizeof k_opad);
929 memcpy(k_ipad, key, key_len);
930 memcpy(k_opad, key, key_len);
932 /* XOR key with ipad and opad values */
933 for (i=0; i<64; i++) {
934 k_ipad[i] ^= 0x36;
935 k_opad[i] ^= 0x5c;
938 /* perform inner MD5 */
939 md5_init(&context); /* init context for 1st pass */
940 md5_update(&context, k_ipad, 64); /* start with inner pad */
941 md5_update(&context, text, text_len); /* then text of datagram */
942 md5_final(digest, &context); /* finish up 1st pass */
944 /* perform outer MD5 */
945 md5_init(&context); /* init context for 2nd pass */
946 md5_update(&context, k_opad, 64); /* start with outer pad */
947 md5_update(&context, digest, 16); /* then results of 1st hash */
948 md5_final(digest, &context); /* finish up 2nd pass */
949 NYD_LEAVE;
951 #endif /* HAVE_MD5 */
953 FL enum okay
954 makedir(char const *name)
956 struct stat st;
957 enum okay rv = STOP;
958 NYD_ENTER;
960 if (!mkdir(name, 0700))
961 rv = OKAY;
962 else {
963 int e = errno;
964 if ((e == EEXIST || e == ENOSYS) && stat(name, &st) == 0 &&
965 S_ISDIR(st.st_mode))
966 rv = OKAY;
968 NYD_LEAVE;
969 return rv;
972 #ifdef HAVE_FCHDIR
973 FL enum okay
974 cwget(struct cw *cw)
976 enum okay rv = STOP;
977 NYD_ENTER;
979 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
980 goto jleave;
981 if (fchdir(cw->cw_fd) == -1) {
982 close(cw->cw_fd);
983 goto jleave;
985 rv = OKAY;
986 jleave:
987 NYD_LEAVE;
988 return rv;
991 FL enum okay
992 cwret(struct cw *cw)
994 enum okay rv = STOP;
995 NYD_ENTER;
997 if (!fchdir(cw->cw_fd))
998 rv = OKAY;
999 NYD_LEAVE;
1000 return rv;
1003 FL void
1004 cwrelse(struct cw *cw)
1006 NYD_ENTER;
1007 close(cw->cw_fd);
1008 NYD_LEAVE;
1011 #else /* !HAVE_FCHDIR */
1012 FL enum okay
1013 cwget(struct cw *cw)
1015 enum okay rv = STOP;
1016 NYD_ENTER;
1018 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1019 rv = OKAY;
1020 NYD_LEAVE;
1021 return rv;
1024 FL enum okay
1025 cwret(struct cw *cw)
1027 enum okay rv = STOP;
1028 NYD_ENTER;
1030 if (!chdir(cw->cw_wd))
1031 rv = OKAY;
1032 NYD_LEAVE;
1033 return rv;
1036 FL void
1037 cwrelse(struct cw *cw)
1039 NYD_ENTER;
1040 UNUSED(cw);
1041 NYD_LEAVE;
1043 #endif /* !HAVE_FCHDIR */
1045 FL char *
1046 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1048 int col_orig = col, n, sz;
1049 char *nb, *np;
1050 NYD_ENTER;
1052 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
1053 while (*cp) {
1054 #ifdef HAVE_WCWIDTH
1055 if (mb_cur_max > 1) {
1056 wchar_t wc;
1058 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0)
1059 n = sz = 1;
1060 else if ((n = wcwidth(wc)) < 0)
1061 n = 1;
1062 } else
1063 #endif
1064 n = sz = 1;
1065 if (n > col)
1066 break;
1067 col -= n;
1068 if (sz == 1 && spacechar(*cp)) {
1069 *np++ = ' ';
1070 cp++;
1071 } else
1072 while (sz--)
1073 *np++ = *cp++;
1076 if (fill && col != 0) {
1077 if (fill > 0) {
1078 memmove(nb + col, nb, (size_t)(np - nb));
1079 memset(nb, ' ', col);
1080 } else
1081 memset(np, ' ', col);
1082 np += col;
1083 col = 0;
1086 *np = '\0';
1087 if (cols_decr_used_or_null != NULL)
1088 *cols_decr_used_or_null -= col_orig - col;
1089 NYD_LEAVE;
1090 return nb;
1093 FL void
1094 makeprint(struct str const *in, struct str *out)
1096 static int print_all_chars = -1;
1098 char const *inp, *maxp;
1099 char *outp;
1100 size_t msz;
1101 NYD_ENTER;
1103 if (print_all_chars == -1)
1104 print_all_chars = ok_blook(print_all_chars);
1106 msz = in->l + 1;
1107 out->s = outp = smalloc(msz);
1108 inp = in->s;
1109 maxp = inp + in->l;
1111 if (print_all_chars) {
1112 out->l = in->l;
1113 memcpy(outp, inp, out->l);
1114 goto jleave;
1117 #ifdef HAVE_C90AMEND1
1118 if (mb_cur_max > 1) {
1119 char mbb[MB_LEN_MAX + 1];
1120 wchar_t wc;
1121 int i, n;
1122 size_t dist;
1124 out->l = 0;
1125 while (inp < maxp) {
1126 if (*inp & 0200)
1127 n = mbtowc(&wc, inp, maxp - inp);
1128 else {
1129 wc = *inp;
1130 n = 1;
1132 if (n < 0) {
1133 /* FIXME Why mbtowc() resetting here?
1134 * FIXME what about ISO 2022-JP plus -- those
1135 * FIXME will loose shifts, then!
1136 * FIXME THUS - we'd need special "known points"
1137 * FIXME to do so - say, after a newline!!
1138 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1139 (void)mbtowc(&wc, NULL, mb_cur_max);
1140 wc = utf8 ? 0xFFFD : '?';
1141 n = 1;
1142 } else if (n == 0)
1143 n = 1;
1144 inp += n;
1145 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1146 wc != '\t') {
1147 if ((wc & ~(wchar_t)037) == 0)
1148 wc = utf8 ? 0x2400 | wc : '?';
1149 else if (wc == 0177)
1150 wc = utf8 ? 0x2421 : '?';
1151 else
1152 wc = utf8 ? 0x2426 : '?';
1154 if ((n = wctomb(mbb, wc)) <= 0)
1155 continue;
1156 out->l += n;
1157 if (out->l >= msz - 1) {
1158 dist = outp - out->s;
1159 out->s = srealloc(out->s, msz += 32);
1160 outp = &out->s[dist];
1162 for (i = 0; i < n; i++)
1163 *outp++ = mbb[i];
1165 } else
1166 #endif /* C90AMEND1 */
1168 int c;
1169 while (inp < maxp) {
1170 c = *inp++ & 0377;
1171 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1172 c = '?';
1173 *outp++ = c;
1175 out->l = in->l;
1177 jleave:
1178 NYD_LEAVE;
1179 out->s[out->l] = '\0';
1182 FL char *
1183 prstr(char const *s)
1185 struct str in, out;
1186 char *rp;
1187 NYD_ENTER;
1189 in.s = UNCONST(s);
1190 in.l = strlen(s);
1191 makeprint(&in, &out);
1192 rp = savestrbuf(out.s, out.l);
1193 free(out.s);
1194 NYD_LEAVE;
1195 return rp;
1198 FL int
1199 prout(char const *s, size_t sz, FILE *fp)
1201 struct str in, out;
1202 int n;
1203 NYD_ENTER;
1205 in.s = UNCONST(s);
1206 in.l = sz;
1207 makeprint(&in, &out);
1208 n = fwrite(out.s, 1, out.l, fp);
1209 free(out.s);
1210 NYD_LEAVE;
1211 return n;
1214 FL size_t
1215 putuc(int u, int c, FILE *fp)
1217 size_t rv;
1218 UNUSED(u);
1219 NYD_ENTER;
1221 #ifdef HAVE_C90AMEND1
1222 if (utf8 && (u & ~(wchar_t)0177)) {
1223 char mbb[MB_LEN_MAX];
1224 int i, n;
1226 if ((n = wctomb(mbb, u)) > 0) {
1227 rv = wcwidth(u);
1228 for (i = 0; i < n; ++i)
1229 if (putc(mbb[i] & 0377, fp) == EOF) {
1230 rv = 0;
1231 break;
1233 } else if (n == 0)
1234 rv = (putc('\0', fp) != EOF);
1235 else
1236 rv = 0;
1237 } else
1238 #endif
1239 rv = (putc(c, fp) != EOF);
1240 NYD_LEAVE;
1241 return rv;
1244 #ifdef HAVE_COLOUR
1245 FL void
1246 colour_table_create(char const *pager_used)
1248 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1249 size_t i;
1250 struct colour_table *ct;
1251 NYD_ENTER;
1253 if (ok_blook(colour_disable))
1254 goto jleave;
1256 /* If pager, check wether it is allowed to use colour */
1257 if (pager_used != NULL) {
1258 char *pager;
1260 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1261 u.ccp = COLOUR_PAGERS;
1262 pager = savestr(u.cp);
1264 while ((u.cp = strcomma(&pager, TRU1)) != NULL)
1265 if (strstr(pager_used, u.cp) != NULL)
1266 goto jok;
1267 goto jleave;
1270 /* $TERM is different in that we default to false unless whitelisted */
1272 char *term, *okterms;
1274 /* Don't use getenv(), but force copy-in into our own tables.. */
1275 if ((term = _var_voklook("TERM")) == NULL)
1276 goto jleave;
1277 if ((okterms = ok_vlook(colour_terms)) == NULL)
1278 okterms = UNCONST(COLOUR_TERMS);
1279 okterms = savestr(okterms);
1281 i = strlen(term);
1282 while ((u.cp = strcomma(&okterms, TRU1)) != NULL)
1283 if (!strncmp(u.cp, term, i))
1284 goto jok;
1285 goto jleave;
1288 jok:
1289 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1290 { static struct {
1291 enum okeys okey;
1292 enum colourspec cspec;
1293 char const *defval;
1294 } const map[] = {
1295 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1296 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1297 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1298 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1299 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1302 for (i = 0; i < NELEM(map); ++i) {
1303 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1304 u.ccp = map[i].defval;
1305 u.cp = _colour_iso6429(u.ccp);
1306 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1307 ct->ct_csinfo[map[i].cspec].s = u.cp;
1310 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") - 1;
1311 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1313 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1314 u.ccp = COLOUR_USER_HEADERS;
1315 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1316 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1317 jleave:
1318 NYD_LEAVE;
1321 FL void
1322 colour_put(FILE *fp, enum colourspec cs)
1324 NYD_ENTER;
1325 if (colour_table != NULL) {
1326 struct str const *cp = colour_get(cs);
1328 fwrite(cp->s, cp->l, 1, fp);
1330 NYD_LEAVE;
1333 FL void
1334 colour_put_header(FILE *fp, char const *name)
1336 enum colourspec cs = COLOURSPEC_HEADER;
1337 struct str const *uheads;
1338 char *cp, *cp_base, *x;
1339 size_t namelen;
1340 NYD_ENTER;
1342 if (colour_table == NULL)
1343 goto j_leave;
1344 /* Normal header colours if there are no user headers */
1345 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1346 if (uheads->s == NULL)
1347 goto jleave;
1349 /* Iterate over all entries in the *colour-user-headers* list */
1350 cp = ac_alloc(uheads->l + 1);
1351 memcpy(cp, uheads->s, uheads->l + 1);
1352 cp_base = cp;
1353 namelen = strlen(name);
1354 while ((x = strcomma(&cp, TRU1)) != NULL) {
1355 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1356 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1357 cs = COLOURSPEC_UHEADER;
1358 break;
1361 ac_free(cp_base);
1362 jleave:
1363 colour_put(fp, cs);
1364 j_leave:
1365 NYD_LEAVE;
1368 FL void
1369 colour_reset(FILE *fp)
1371 NYD_ENTER;
1372 if (colour_table != NULL)
1373 fwrite("\033[0m", 4, 1, fp);
1374 NYD_LEAVE;
1377 FL struct str const *
1378 colour_get(enum colourspec cs)
1380 struct str const *rv = NULL;
1381 NYD_ENTER;
1383 if (colour_table != NULL)
1384 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1385 rv = NULL;
1386 NYD_LEAVE;
1387 return rv;
1389 #endif /* HAVE_COLOUR */
1391 FL void
1392 time_current_update(struct time_current *tc, bool_t full_update)
1394 NYD_ENTER;
1395 tc->tc_time = time(NULL);
1396 if (full_update) {
1397 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1398 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1399 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1401 NYD_LEAVE;
1404 static void
1405 _out_of_memory(void)
1407 panic("no memory");
1410 #ifndef HAVE_DEBUG
1411 FL void *
1412 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1414 void *rv;
1415 NYD_ENTER;
1417 if (s == 0)
1418 s = 1;
1419 if ((rv = malloc(s)) == NULL)
1420 _out_of_memory();
1421 NYD_LEAVE;
1422 return rv;
1425 FL void *
1426 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1428 void *rv;
1429 NYD_ENTER;
1431 if (s == 0)
1432 s = 1;
1433 if (v == NULL)
1434 rv = smalloc(s);
1435 else if ((rv = realloc(v, s)) == NULL)
1436 _out_of_memory();
1437 NYD_LEAVE;
1438 return rv;
1441 FL void *
1442 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1444 void *rv;
1445 NYD_ENTER;
1447 if (size == 0)
1448 size = 1;
1449 if ((rv = calloc(nmemb, size)) == NULL)
1450 _out_of_memory();
1451 NYD_LEAVE;
1452 return rv;
1455 #else /* !HAVE_DEBUG */
1456 CTA(sizeof(char) == sizeof(ui8_t));
1458 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1459 # define _HOPE_SET(C) \
1460 do {\
1461 union ptr __xl, __xu;\
1462 struct chunk *__xc;\
1463 __xl.p = (C).p;\
1464 __xc = __xl.c - 1;\
1465 __xu.p = __xc;\
1466 (C).cp += 8;\
1467 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1468 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1469 __xu.ui8p += __xc->size - 8;\
1470 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1471 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1472 } while (0)
1473 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1474 # define _HOPE_GET(C,BAD) \
1475 do {\
1476 union ptr __xl, __xu;\
1477 struct chunk *__xc;\
1478 ui32_t __i;\
1479 __xl.p = (C).p;\
1480 __xl.cp -= 8;\
1481 (C).cp = __xl.cp;\
1482 __xc = __xl.c - 1;\
1483 (BAD) = FAL0;\
1484 __i = 0;\
1485 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1486 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1487 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1488 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1489 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1490 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1491 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1492 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1493 if (__i != 0) {\
1494 (BAD) = TRU1;\
1495 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1496 __xl.p, __i, mdbg_file, mdbg_line);\
1498 __xu.p = __xc;\
1499 __xu.ui8p += __xc->size - 8;\
1500 __i = 0;\
1501 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1502 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1503 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1504 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1505 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1506 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1507 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1508 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1509 if (__i != 0) {\
1510 (BAD) = TRU1;\
1511 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1512 __xl.p, __i, mdbg_file, mdbg_line);\
1514 if (BAD)\
1515 alert(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1516 } while (0)
1518 struct chunk {
1519 struct chunk *prev;
1520 struct chunk *next;
1521 char const *file;
1522 ui16_t line;
1523 ui8_t isfree;
1524 ui8_t __dummy;
1525 ui32_t size;
1528 union ptr {
1529 void *p;
1530 struct chunk *c;
1531 char *cp;
1532 ui8_t *ui8p;
1535 struct chunk *_mlist, *_mfree;
1537 FL void *
1538 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1540 union ptr p;
1541 NYD_ENTER;
1543 if (s == 0)
1544 s = 1;
1545 s += sizeof(struct chunk) + _HOPE_SIZE;
1547 if ((p.p = (malloc)(s)) == NULL)
1548 _out_of_memory();
1549 p.c->prev = NULL;
1550 if ((p.c->next = _mlist) != NULL)
1551 _mlist->prev = p.c;
1552 p.c->file = mdbg_file;
1553 p.c->line = (ui16_t)mdbg_line;
1554 p.c->isfree = FAL0;
1555 p.c->size = (ui32_t)s;
1556 _mlist = p.c++;
1557 _HOPE_SET(p);
1558 NYD_LEAVE;
1559 return p.p;
1562 FL void *
1563 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1565 union ptr p;
1566 bool_t isbad;
1567 NYD_ENTER;
1569 if ((p.p = v) == NULL) {
1570 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1571 goto jleave;
1574 _HOPE_GET(p, isbad);
1575 --p.c;
1576 if (p.c->isfree) {
1577 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1578 "\tLast seen: %s, line %d\n",
1579 mdbg_file, mdbg_line, p.c->file, p.c->line);
1580 goto jforce;
1583 if (p.c == _mlist)
1584 _mlist = p.c->next;
1585 else
1586 p.c->prev->next = p.c->next;
1587 if (p.c->next != NULL)
1588 p.c->next->prev = p.c->prev;
1590 jforce:
1591 if (s == 0)
1592 s = 1;
1593 s += sizeof(struct chunk) + _HOPE_SIZE;
1595 if ((p.p = (realloc)(p.c, s)) == NULL)
1596 _out_of_memory();
1597 p.c->prev = NULL;
1598 if ((p.c->next = _mlist) != NULL)
1599 _mlist->prev = p.c;
1600 p.c->file = mdbg_file;
1601 p.c->line = (ui16_t)mdbg_line;
1602 p.c->isfree = FAL0;
1603 p.c->size = (ui32_t)s;
1604 _mlist = p.c++;
1605 _HOPE_SET(p);
1606 jleave:
1607 NYD_LEAVE;
1608 return p.p;
1611 FL void *
1612 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1614 union ptr p;
1615 NYD_ENTER;
1617 if (size == 0)
1618 size = 1;
1619 if (nmemb == 0)
1620 nmemb = 1;
1621 size *= nmemb;
1622 size += sizeof(struct chunk) + _HOPE_SIZE;
1624 if ((p.p = (malloc)(size)) == NULL)
1625 _out_of_memory();
1626 memset(p.p, 0, size);
1627 p.c->prev = NULL;
1628 if ((p.c->next = _mlist) != NULL)
1629 _mlist->prev = p.c;
1630 p.c->file = mdbg_file;
1631 p.c->line = (ui16_t)mdbg_line;
1632 p.c->isfree = FAL0;
1633 p.c->size = (ui32_t)size;
1634 _mlist = p.c++;
1635 _HOPE_SET(p);
1636 NYD_LEAVE;
1637 return p.p;
1640 FL void
1641 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1643 union ptr p;
1644 bool_t isbad;
1645 NYD_ENTER;
1647 if ((p.p = v) == NULL) {
1648 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1649 goto jleave;
1652 _HOPE_GET(p, isbad);
1653 --p.c;
1654 if (p.c->isfree) {
1655 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1656 "\tLast seen: %s, line %d\n",
1657 mdbg_file, mdbg_line, p.c->file, p.c->line);
1658 goto jleave;
1661 if (p.c == _mlist)
1662 _mlist = p.c->next;
1663 else
1664 p.c->prev->next = p.c->next;
1665 if (p.c->next != NULL)
1666 p.c->next->prev = p.c->prev;
1667 p.c->isfree = TRU1;
1669 if (options & OPT_DEBUG) {
1670 p.c->next = _mfree;
1671 _mfree = p.c;
1672 } else
1673 (free)(p.c);
1674 jleave:
1675 NYD_LEAVE;
1678 FL void
1679 smemreset(void)
1681 union ptr p;
1682 size_t c = 0, s = 0;
1683 NYD_ENTER;
1685 for (p.c = _mfree; p.c != NULL;) {
1686 void *vp = p.c;
1687 ++c;
1688 s += p.c->size;
1689 p.c = p.c->next;
1690 (free)(vp);
1692 _mfree = NULL;
1694 if (options & OPT_DEBUG)
1695 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1696 c, s);
1697 NYD_LEAVE;
1700 FL int
1701 c_smemtrace(void *v)
1703 /* For _HOPE_GET() */
1704 char const * const mdbg_file = "smemtrace()";
1705 int const mdbg_line = -1;
1706 FILE *fp;
1707 union ptr p, xp;
1708 bool_t isbad;
1709 size_t lines;
1710 NYD_ENTER;
1712 v = (void*)0x1;
1713 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1714 NULL) {
1715 perror("tmpfile");
1716 goto jleave;
1719 fprintf(fp, "Currently allocated memory chunks:\n");
1720 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1721 xp = p;
1722 ++xp.c;
1723 _HOPE_GET_TRACE(xp, isbad);
1724 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1725 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1726 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1729 if (options & OPT_DEBUG) {
1730 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1731 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1732 xp = p;
1733 ++xp.c;
1734 _HOPE_GET_TRACE(xp, isbad);
1735 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1736 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1737 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1741 page_or_print(fp, lines);
1742 Fclose(fp);
1743 v = NULL;
1744 jleave:
1745 NYD_LEAVE;
1746 return (v != NULL);
1749 # ifdef MEMCHECK
1750 FL bool_t
1751 _smemcheck(char const *mdbg_file, int mdbg_line)
1753 union ptr p, xp;
1754 bool_t anybad = FAL0, isbad;
1755 size_t lines;
1756 NYD_ENTER;
1758 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1759 xp = p;
1760 ++xp.c;
1761 _HOPE_GET_TRACE(xp, isbad);
1762 if (isbad) {
1763 anybad = TRU1;
1764 fprintf(stderr,
1765 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1766 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1767 p.c->file, p.c->line);
1771 if (options & OPT_DEBUG) {
1772 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1773 xp = p;
1774 ++xp.c;
1775 _HOPE_GET_TRACE(xp, isbad);
1776 if (isbad) {
1777 anybad = TRU1;
1778 fprintf(stderr,
1779 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1780 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1781 p.c->file, p.c->line);
1785 NYD_LEAVE;
1786 return anybad;
1788 # endif /* MEMCHECK */
1789 #endif /* HAVE_DEBUG */
1791 /* vim:set fenc=utf-8:s-it-mode */