Bump s-nail v14.6.1
[s-mailx.git] / auxlily.c
blob8de276a2f6e4c29304bb91a85cb8fb99d8a3f636
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 n_strsep() 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 = n_strsep(&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 for (;;)
355 _exit(EXIT_ERR);
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) != 0);
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 (int)PTR2SIZE(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; /* XXX no magics */
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)) {
475 #ifdef HAVE_POP3
476 rv = PROTO_POP3;
477 #else
478 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
479 #endif
480 } else if (!strncmp(name, "pop3s://", 8)) {
481 #if defined HAVE_POP3 && defined HAVE_SSL
482 rv = PROTO_POP3;
483 #else
484 # ifndef HAVE_POP3
485 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
486 # endif
487 # ifndef HAVE_SSL
488 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
489 # endif
490 #endif
491 } else if (!strncmp(name, "imap://", 7)) {
492 #ifdef HAVE_IMAP
493 rv = PROTO_IMAP;
494 #else
495 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
496 #endif
497 } else if (!strncmp(name, "imaps://", 8)) {
498 #if defined HAVE_IMAP && defined HAVE_SSL
499 rv = PROTO_IMAP;
500 #else
501 # ifndef HAVE_IMAP
502 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
503 # endif
504 # ifndef HAVE_SSL
505 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
506 # endif
507 #endif
509 goto jleave;
512 /* TODO This is the de facto maildir code and thus belongs into there!
513 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
514 * TODO or (more likely) in addition to *newfolders*) */
515 jfile:
516 rv = PROTO_FILE;
517 np = ac_alloc((sz = strlen(name)) + 4 +1);
518 memcpy(np, name, sz + 1);
519 if (!stat(name, &st)) {
520 if (S_ISDIR(st.st_mode) &&
521 (strcpy(&np[sz], "/tmp"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
522 (strcpy(&np[sz], "/new"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
523 (strcpy(&np[sz], "/cur"), !stat(np, &st) && S_ISDIR(st.st_mode)))
524 rv = PROTO_MAILDIR;
525 } else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
526 rv = PROTO_MAILDIR;
527 ac_free(np);
528 jleave:
529 NYD_LEAVE;
530 return rv;
533 FL ui32_t
534 torek_hash(char const *name)
536 /* Chris Torek's hash.
537 * NOTE: need to change *at least* create-okey-map.pl when changing the
538 * algorithm!! */
539 ui32_t h = 0;
540 NYD_ENTER;
542 while (*name != '\0') {
543 h *= 33;
544 h += *name++;
546 NYD_LEAVE;
547 return h;
550 FL unsigned
551 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
553 unsigned h = 0, g;
554 NYD_ENTER;
556 cp--;
557 while (*++cp) {
558 h = (h << 4 & 0xffffffff) + (*cp&0377);
559 if ((g = h & 0xf0000000) != 0) {
560 h = h ^ g >> 24;
561 h = h ^ g;
564 NYD_LEAVE;
565 return h;
568 FL ui32_t
569 nextprime(ui32_t n)
571 static ui32_t const primes[] = {
572 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
573 131071, 262139, 524287, 1048573, 2097143, 4194301,
574 8388593, 16777213, 33554393, 67108859, 134217689,
575 268435399, 536870909, 1073741789, 2147483647
578 ui32_t mprime = 7, cutlim;
579 size_t i;
580 NYD_ENTER;
582 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
584 for (i = 0; i < NELEM(primes); i++)
585 if ((mprime = primes[i]) >= cutlim)
586 break;
587 if (i == NELEM(primes) && mprime < n)
588 mprime = n;
589 NYD_LEAVE;
590 return mprime;
593 FL int
594 expand_shell_escape(char const **s, bool_t use_nail_extensions)
596 char const *xs;
597 int c, n;
598 NYD_ENTER;
600 xs = *s;
602 if ((c = *xs & 0xFF) == '\0')
603 goto jleave;
604 ++xs;
605 if (c != '\\')
606 goto jleave;
608 switch ((c = *xs & 0xFF)) {
609 case '\\': break;
610 case 'a': c = '\a'; break;
611 case 'b': c = '\b'; break;
612 case 'c': c = PROMPT_STOP; break;
613 case 'f': c = '\f'; break;
614 case 'n': c = '\n'; break;
615 case 'r': c = '\r'; break;
616 case 't': c = '\t'; break;
617 case 'v': c = '\v'; break;
618 case '0':
619 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
620 c <<= 3;
621 c |= *xs - '0';
623 goto jleave;
624 /* S-nail extension for nice (get)prompt(()) support */
625 case '&':
626 case '?':
627 case '$':
628 case '@':
629 if (use_nail_extensions) {
630 switch (c) {
631 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
632 case '?': c = exec_last_comm_error ? '1' : '0'; break;
633 case '$': c = PROMPT_DOLLAR; break;
634 case '@': c = PROMPT_AT; break;
636 break;
638 /* FALLTHRU */
639 case '\0':
640 /* A sole <backslash> at EOS is treated as-is! */
641 /* FALLTHRU */
642 default:
643 c = '\\';
644 goto jleave;
646 ++xs;
647 jleave:
648 *s = xs;
649 NYD_LEAVE;
650 return c;
653 FL char *
654 getprompt(void)
656 static char buf[PROMPT_BUFFER_SIZE];
658 char *cp = buf;
659 char const *ccp;
660 NYD_ENTER;
662 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
663 goto jleave;
665 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
666 char const *a;
667 size_t l;
668 int c = expand_shell_escape(&ccp, TRU1);
670 if (c > 0) {
671 *cp = (char)c;
672 continue;
674 if (c == 0 || c == PROMPT_STOP)
675 break;
677 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
678 if (a == NULL)
679 a = "";
680 l = strlen(a);
681 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
682 *cp++ = '?';
683 else {
684 memcpy(cp, a, l);
685 cp += --l;
688 jleave:
689 *cp = '\0';
690 NYD_LEAVE;
691 return buf;
694 FL char *
695 nodename(int mayoverride)
697 static char *hostname;
699 struct utsname ut;
700 char *hn;
701 #ifdef HAVE_SOCKETS
702 # ifdef HAVE_IPV6
703 struct addrinfo hints, *res;
704 # else
705 struct hostent *hent;
706 # endif
707 #endif
708 NYD_ENTER;
710 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
711 if (hostname != NULL)
712 free(hostname);
713 hostname = sstrdup(hn);
714 } else if (hostname == NULL) {
715 uname(&ut);
716 hn = ut.nodename;
717 #ifdef HAVE_SOCKETS
718 # ifdef HAVE_IPV6
719 memset(&hints, 0, sizeof hints);
720 hints.ai_family = AF_UNSPEC;
721 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
722 hints.ai_flags = AI_CANONNAME;
723 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
724 if (res->ai_canonname != NULL) {
725 size_t l = strlen(res->ai_canonname) +1;
726 hn = ac_alloc(l);
727 memcpy(hn, res->ai_canonname, l);
729 freeaddrinfo(res);
731 # else
732 hent = gethostbyname(hn);
733 if (hent != NULL)
734 hn = hent->h_name;
735 # endif
736 #endif
737 hostname = sstrdup(hn);
738 #if defined HAVE_SOCKETS && defined HAVE_IPV6
739 if (hn != ut.nodename)
740 ac_free(hn);
741 #endif
743 NYD_LEAVE;
744 return hostname;
747 FL char *
748 lookup_password_for_token(char const *token)
750 size_t tl;
751 char *var, *cp;
752 NYD_ENTER;
754 tl = strlen(token);
755 var = ac_alloc(tl + 9 +1);
757 memcpy(var, "password-", 9);
758 memcpy(var + 9, token, tl);
759 var[tl + 9] = '\0';
761 if ((cp = vok_vlook(var)) != NULL)
762 cp = savestr(cp);
763 ac_free(var);
764 NYD_LEAVE;
765 return cp;
768 FL char *
769 getrandstring(size_t length)
771 static unsigned char nodedigest[16];
772 static pid_t pid;
774 struct str b64;
775 char *data, *cp;
776 size_t i;
777 int fd = -1;
778 #ifdef HAVE_MD5
779 md5_ctx ctx;
780 #else
781 size_t j;
782 #endif
783 NYD_ENTER;
785 data = ac_alloc(length);
787 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
788 length != (size_t)read(fd, data, length)) {
789 if (pid == 0) {
790 pid = getpid();
791 srand(pid);
792 cp = nodename(0);
793 #ifdef HAVE_MD5
794 md5_init(&ctx);
795 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
796 md5_final(nodedigest, &ctx);
797 #else
798 /* In that case it's only used for boundaries and Message-Id:s so that
799 * srand(3) should suffice */
800 j = strlen(cp) + 1;
801 for (i = 0; i < sizeof(nodedigest); ++i)
802 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
803 #endif
805 for (i = 0; i < length; i++)
806 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
807 nodedigest[i % sizeof nodedigest]);
809 if (fd >= 0)
810 close(fd);
812 b64_encode_buf(&b64, data, length, B64_SALLOC);
813 ac_free(data);
814 assert(length < b64.l);
815 b64.s[length] = '\0';
816 NYD_LEAVE;
817 return b64.s;
820 #ifdef HAVE_MD5
821 FL char *
822 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
824 char const *cp = vp;
825 size_t i, j;
826 NYD_ENTER;
828 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
829 j = i << 1;
830 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
831 hex[++j] = hexchar(cp[i] & 0x0f);
833 NYD_LEAVE;
834 return hex;
837 FL char *
838 cram_md5_string(char const *user, char const *pass, char const *b64)
840 struct str in, out;
841 char digest[16], *cp;
842 size_t lu;
843 NYD_ENTER;
845 out.s = NULL;
846 in.s = UNCONST(b64);
847 in.l = strlen(in.s);
848 b64_decode(&out, &in, NULL);
849 assert(out.s != NULL);
851 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
852 free(out.s);
853 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
855 lu = strlen(user);
856 in.l = lu + MD5TOHEX_SIZE +1;
857 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
858 memcpy(in.s, user, lu);
859 in.s[lu++] = ' ';
860 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
861 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
862 ac_free(in.s);
863 NYD_LEAVE;
864 return out.s;
867 FL void
868 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
869 void *digest)
872 * This code is taken from
874 * Network Working Group H. Krawczyk
875 * Request for Comments: 2104 IBM
876 * Category: Informational M. Bellare
877 * UCSD
878 * R. Canetti
879 * IBM
880 * February 1997
883 * HMAC: Keyed-Hashing for Message Authentication
885 md5_ctx context;
886 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
887 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
888 unsigned char tk[16];
889 int i;
890 NYD_ENTER;
892 /* if key is longer than 64 bytes reset it to key=MD5(key) */
893 if (key_len > 64) {
894 md5_ctx tctx;
896 md5_init(&tctx);
897 md5_update(&tctx, key, key_len);
898 md5_final(tk, &tctx);
900 key = tk;
901 key_len = 16;
904 /* the HMAC_MD5 transform looks like:
906 * MD5(K XOR opad, MD5(K XOR ipad, text))
908 * where K is an n byte key
909 * ipad is the byte 0x36 repeated 64 times
910 * opad is the byte 0x5c repeated 64 times
911 * and text is the data being protected */
913 /* start out by storing key in pads */
914 memset(k_ipad, 0, sizeof k_ipad);
915 memset(k_opad, 0, sizeof k_opad);
916 memcpy(k_ipad, key, key_len);
917 memcpy(k_opad, key, key_len);
919 /* XOR key with ipad and opad values */
920 for (i=0; i<64; i++) {
921 k_ipad[i] ^= 0x36;
922 k_opad[i] ^= 0x5c;
925 /* perform inner MD5 */
926 md5_init(&context); /* init context for 1st pass */
927 md5_update(&context, k_ipad, 64); /* start with inner pad */
928 md5_update(&context, text, text_len); /* then text of datagram */
929 md5_final(digest, &context); /* finish up 1st pass */
931 /* perform outer MD5 */
932 md5_init(&context); /* init context for 2nd pass */
933 md5_update(&context, k_opad, 64); /* start with outer pad */
934 md5_update(&context, digest, 16); /* then results of 1st hash */
935 md5_final(digest, &context); /* finish up 2nd pass */
936 NYD_LEAVE;
938 #endif /* HAVE_MD5 */
940 FL enum okay
941 makedir(char const *name)
943 struct stat st;
944 enum okay rv = STOP;
945 NYD_ENTER;
947 if (!mkdir(name, 0700))
948 rv = OKAY;
949 else {
950 int e = errno;
951 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
952 S_ISDIR(st.st_mode))
953 rv = OKAY;
955 NYD_LEAVE;
956 return rv;
959 #ifdef HAVE_FCHDIR
960 FL enum okay
961 cwget(struct cw *cw)
963 enum okay rv = STOP;
964 NYD_ENTER;
966 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
967 goto jleave;
968 if (fchdir(cw->cw_fd) == -1) {
969 close(cw->cw_fd);
970 goto jleave;
972 rv = OKAY;
973 jleave:
974 NYD_LEAVE;
975 return rv;
978 FL enum okay
979 cwret(struct cw *cw)
981 enum okay rv = STOP;
982 NYD_ENTER;
984 if (!fchdir(cw->cw_fd))
985 rv = OKAY;
986 NYD_LEAVE;
987 return rv;
990 FL void
991 cwrelse(struct cw *cw)
993 NYD_ENTER;
994 close(cw->cw_fd);
995 NYD_LEAVE;
998 #else /* !HAVE_FCHDIR */
999 FL enum okay
1000 cwget(struct cw *cw)
1002 enum okay rv = STOP;
1003 NYD_ENTER;
1005 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1006 rv = OKAY;
1007 NYD_LEAVE;
1008 return rv;
1011 FL enum okay
1012 cwret(struct cw *cw)
1014 enum okay rv = STOP;
1015 NYD_ENTER;
1017 if (!chdir(cw->cw_wd))
1018 rv = OKAY;
1019 NYD_LEAVE;
1020 return rv;
1023 FL void
1024 cwrelse(struct cw *cw)
1026 NYD_ENTER;
1027 UNUSED(cw);
1028 NYD_LEAVE;
1030 #endif /* !HAVE_FCHDIR */
1032 FL char *
1033 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1035 int col_orig = col, n, sz;
1036 char *nb, *np;
1037 NYD_ENTER;
1039 np = nb = salloc(mb_cur_max * strlen(cp) + col +1);
1040 while (*cp) {
1041 #ifdef HAVE_WCWIDTH
1042 if (mb_cur_max > 1) {
1043 wchar_t wc;
1045 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1046 n = sz = 1;
1047 else if ((n = wcwidth(wc)) == -1)
1048 n = 1;
1049 } else
1050 #endif
1051 n = sz = 1;
1052 if (n > col)
1053 break;
1054 col -= n;
1055 if (sz == 1 && spacechar(*cp)) {
1056 *np++ = ' ';
1057 cp++;
1058 } else
1059 while (sz--)
1060 *np++ = *cp++;
1063 if (fill && col != 0) {
1064 if (fill > 0) {
1065 memmove(nb + col, nb, PTR2SIZE(np - nb));
1066 memset(nb, ' ', col);
1067 } else
1068 memset(np, ' ', col);
1069 np += col;
1070 col = 0;
1073 *np = '\0';
1074 if (cols_decr_used_or_null != NULL)
1075 *cols_decr_used_or_null -= col_orig - col;
1076 NYD_LEAVE;
1077 return nb;
1080 FL void
1081 makeprint(struct str const *in, struct str *out)
1083 static int print_all_chars = -1;
1085 char const *inp, *maxp;
1086 char *outp;
1087 size_t msz;
1088 NYD_ENTER;
1090 if (print_all_chars == -1)
1091 print_all_chars = ok_blook(print_all_chars);
1093 msz = in->l +1;
1094 out->s = outp = smalloc(msz);
1095 inp = in->s;
1096 maxp = inp + in->l;
1098 if (print_all_chars) {
1099 out->l = in->l;
1100 memcpy(outp, inp, out->l);
1101 goto jleave;
1104 #ifdef HAVE_C90AMEND1
1105 if (mb_cur_max > 1) {
1106 char mbb[MB_LEN_MAX + 1];
1107 wchar_t wc;
1108 int i, n;
1109 size_t dist;
1111 out->l = 0;
1112 while (inp < maxp) {
1113 if (*inp & 0200)
1114 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1115 else {
1116 wc = *inp;
1117 n = 1;
1119 if (n == -1) {
1120 /* FIXME Why mbtowc() resetting here?
1121 * FIXME what about ISO 2022-JP plus -- those
1122 * FIXME will loose shifts, then!
1123 * FIXME THUS - we'd need special "known points"
1124 * FIXME to do so - say, after a newline!!
1125 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1126 mbtowc(&wc, NULL, mb_cur_max);
1127 wc = utf8 ? 0xFFFD : '?';
1128 n = 1;
1129 } else if (n == 0)
1130 n = 1;
1131 inp += n;
1132 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1133 wc != '\t') {
1134 if ((wc & ~(wchar_t)037) == 0)
1135 wc = utf8 ? 0x2400 | wc : '?';
1136 else if (wc == 0177)
1137 wc = utf8 ? 0x2421 : '?';
1138 else
1139 wc = utf8 ? 0x2426 : '?';
1141 if ((n = wctomb(mbb, wc)) <= 0)
1142 continue;
1143 out->l += n;
1144 if (out->l >= msz - 1) {
1145 dist = outp - out->s;
1146 out->s = srealloc(out->s, msz += 32);
1147 outp = &out->s[dist];
1149 for (i = 0; i < n; ++i)
1150 *outp++ = mbb[i];
1152 } else
1153 #endif /* C90AMEND1 */
1155 int c;
1156 while (inp < maxp) {
1157 c = *inp++ & 0377;
1158 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1159 c = '?';
1160 *outp++ = c;
1162 out->l = in->l;
1164 jleave:
1165 out->s[out->l] = '\0';
1166 NYD_LEAVE;
1169 FL char *
1170 prstr(char const *s)
1172 struct str in, out;
1173 char *rp;
1174 NYD_ENTER;
1176 in.s = UNCONST(s);
1177 in.l = strlen(s);
1178 makeprint(&in, &out);
1179 rp = savestrbuf(out.s, out.l);
1180 free(out.s);
1181 NYD_LEAVE;
1182 return rp;
1185 FL int
1186 prout(char const *s, size_t sz, FILE *fp)
1188 struct str in, out;
1189 int n;
1190 NYD_ENTER;
1192 in.s = UNCONST(s);
1193 in.l = sz;
1194 makeprint(&in, &out);
1195 n = fwrite(out.s, 1, out.l, fp);
1196 free(out.s);
1197 NYD_LEAVE;
1198 return n;
1201 FL size_t
1202 putuc(int u, int c, FILE *fp)
1204 size_t rv;
1205 UNUSED(u);
1206 NYD_ENTER;
1208 #ifdef HAVE_C90AMEND1
1209 if (utf8 && (u & ~(wchar_t)0177)) {
1210 char mbb[MB_LEN_MAX];
1211 int i, n;
1213 if ((n = wctomb(mbb, u)) > 0) {
1214 rv = wcwidth(u);
1215 for (i = 0; i < n; ++i)
1216 if (putc(mbb[i] & 0377, fp) == EOF) {
1217 rv = 0;
1218 break;
1220 } else if (n == 0)
1221 rv = (putc('\0', fp) != EOF);
1222 else
1223 rv = 0;
1224 } else
1225 #endif
1226 rv = (putc(c, fp) != EOF);
1227 NYD_LEAVE;
1228 return rv;
1231 #ifdef HAVE_COLOUR
1232 FL void
1233 colour_table_create(char const *pager_used)
1235 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1236 size_t i;
1237 struct colour_table *ct;
1238 NYD_ENTER;
1240 if (ok_blook(colour_disable))
1241 goto jleave;
1243 /* If pager, check wether it is allowed to use colour */
1244 if (pager_used != NULL) {
1245 char *pager;
1247 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1248 u.ccp = COLOUR_PAGERS;
1249 pager = savestr(u.cp);
1251 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
1252 if (strstr(pager_used, u.cp) != NULL)
1253 goto jok;
1254 goto jleave;
1257 /* $TERM is different in that we default to false unless whitelisted */
1259 char *term, *okterms;
1261 /* Don't use getenv(), but force copy-in into our own tables.. */
1262 if ((term = _var_voklook("TERM")) == NULL)
1263 goto jleave;
1264 if ((okterms = ok_vlook(colour_terms)) == NULL)
1265 okterms = UNCONST(COLOUR_TERMS);
1266 okterms = savestr(okterms);
1268 i = strlen(term);
1269 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1270 if (!strncmp(u.cp, term, i))
1271 goto jok;
1272 goto jleave;
1275 jok:
1276 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1277 { static struct {
1278 enum okeys okey;
1279 enum colourspec cspec;
1280 char const *defval;
1281 } const map[] = {
1282 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1283 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1284 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1285 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1286 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1289 for (i = 0; i < NELEM(map); ++i) {
1290 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1291 u.ccp = map[i].defval;
1292 u.cp = _colour_iso6429(u.ccp);
1293 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1294 ct->ct_csinfo[map[i].cspec].s = u.cp;
1297 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1298 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1300 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1301 u.ccp = COLOUR_USER_HEADERS;
1302 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1303 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1304 jleave:
1305 NYD_LEAVE;
1308 FL void
1309 colour_put(FILE *fp, enum colourspec cs)
1311 NYD_ENTER;
1312 if (colour_table != NULL) {
1313 struct str const *cp = colour_get(cs);
1315 fwrite(cp->s, cp->l, 1, fp);
1317 NYD_LEAVE;
1320 FL void
1321 colour_put_header(FILE *fp, char const *name)
1323 enum colourspec cs = COLOURSPEC_HEADER;
1324 struct str const *uheads;
1325 char *cp, *cp_base, *x;
1326 size_t namelen;
1327 NYD_ENTER;
1329 if (colour_table == NULL)
1330 goto j_leave;
1331 /* Normal header colours if there are no user headers */
1332 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1333 if (uheads->s == NULL)
1334 goto jleave;
1336 /* Iterate over all entries in the *colour-user-headers* list */
1337 cp = ac_alloc(uheads->l +1);
1338 memcpy(cp, uheads->s, uheads->l +1);
1339 cp_base = cp;
1340 namelen = strlen(name);
1341 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1342 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1343 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1344 cs = COLOURSPEC_UHEADER;
1345 break;
1348 ac_free(cp_base);
1349 jleave:
1350 colour_put(fp, cs);
1351 j_leave:
1352 NYD_LEAVE;
1355 FL void
1356 colour_reset(FILE *fp)
1358 NYD_ENTER;
1359 if (colour_table != NULL)
1360 fwrite("\033[0m", 4, 1, fp);
1361 NYD_LEAVE;
1364 FL struct str const *
1365 colour_get(enum colourspec cs)
1367 struct str const *rv = NULL;
1368 NYD_ENTER;
1370 if (colour_table != NULL)
1371 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1372 rv = NULL;
1373 NYD_LEAVE;
1374 return rv;
1376 #endif /* HAVE_COLOUR */
1378 FL void
1379 time_current_update(struct time_current *tc, bool_t full_update)
1381 NYD_ENTER;
1382 tc->tc_time = time(NULL);
1383 if (full_update) {
1384 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1385 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1386 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1388 NYD_LEAVE;
1391 static void
1392 _out_of_memory(void)
1394 panic("no memory");
1397 #ifndef HAVE_DEBUG
1398 FL void *
1399 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1401 void *rv;
1402 NYD_ENTER;
1404 if (s == 0)
1405 s = 1;
1406 if ((rv = malloc(s)) == NULL)
1407 _out_of_memory();
1408 NYD_LEAVE;
1409 return rv;
1412 FL void *
1413 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1415 void *rv;
1416 NYD_ENTER;
1418 if (s == 0)
1419 s = 1;
1420 if (v == NULL)
1421 rv = smalloc(s);
1422 else if ((rv = realloc(v, s)) == NULL)
1423 _out_of_memory();
1424 NYD_LEAVE;
1425 return rv;
1428 FL void *
1429 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1431 void *rv;
1432 NYD_ENTER;
1434 if (size == 0)
1435 size = 1;
1436 if ((rv = calloc(nmemb, size)) == NULL)
1437 _out_of_memory();
1438 NYD_LEAVE;
1439 return rv;
1442 #else /* !HAVE_DEBUG */
1443 CTA(sizeof(char) == sizeof(ui8_t));
1445 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1446 # define _HOPE_SET(C) \
1447 do {\
1448 union ptr __xl, __xu;\
1449 struct chunk *__xc;\
1450 __xl.p = (C).p;\
1451 __xc = __xl.c - 1;\
1452 __xu.p = __xc;\
1453 (C).cp += 8;\
1454 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1455 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1456 __xu.ui8p += __xc->size - 8;\
1457 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1458 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1459 } while (0)
1460 # define _HOPE_GET_TRACE(C,BAD) \
1461 do {\
1462 (C).cp += 8;\
1463 _HOPE_GET(C, BAD);\
1464 (C).cp += 8;\
1465 } while(0)
1466 # define _HOPE_GET(C,BAD) \
1467 do {\
1468 union ptr __xl, __xu;\
1469 struct chunk *__xc;\
1470 ui32_t __i;\
1471 __xl.p = (C).p;\
1472 __xl.cp -= 8;\
1473 (C).cp = __xl.cp;\
1474 __xc = __xl.c - 1;\
1475 (BAD) = FAL0;\
1476 __i = 0;\
1477 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1478 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1479 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1480 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1481 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1482 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1483 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1484 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1485 if (__i != 0) {\
1486 (BAD) = TRU1;\
1487 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1488 __xl.p, __i, mdbg_file, mdbg_line);\
1490 __xu.p = __xc;\
1491 __xu.ui8p += __xc->size - 8;\
1492 __i = 0;\
1493 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1494 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1495 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1496 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1497 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1498 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1499 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1500 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1501 if (__i != 0) {\
1502 (BAD) = TRU1;\
1503 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1504 __xl.p, __i, mdbg_file, mdbg_line);\
1506 if (BAD)\
1507 alert(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1508 } while (0)
1510 struct chunk {
1511 struct chunk *prev;
1512 struct chunk *next;
1513 char const *file;
1514 ui16_t line;
1515 ui8_t isfree;
1516 ui8_t __dummy;
1517 ui32_t size;
1520 union ptr {
1521 void *p;
1522 struct chunk *c;
1523 char *cp;
1524 ui8_t *ui8p;
1527 struct chunk *_mlist, *_mfree;
1529 FL void *
1530 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1532 union ptr p;
1533 NYD_ENTER;
1535 if (s == 0)
1536 s = 1;
1537 s += sizeof(struct chunk) + _HOPE_SIZE;
1539 if ((p.p = (malloc)(s)) == NULL)
1540 _out_of_memory();
1541 p.c->prev = NULL;
1542 if ((p.c->next = _mlist) != NULL)
1543 _mlist->prev = p.c;
1544 p.c->file = mdbg_file;
1545 p.c->line = (ui16_t)mdbg_line;
1546 p.c->isfree = FAL0;
1547 p.c->size = (ui32_t)s;
1548 _mlist = p.c++;
1549 _HOPE_SET(p);
1550 NYD_LEAVE;
1551 return p.p;
1554 FL void *
1555 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1557 union ptr p;
1558 bool_t isbad;
1559 NYD_ENTER;
1561 if ((p.p = v) == NULL) {
1562 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1563 goto jleave;
1566 _HOPE_GET(p, isbad);
1567 --p.c;
1568 if (p.c->isfree) {
1569 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1570 "\tLast seen: %s, line %d\n",
1571 mdbg_file, mdbg_line, p.c->file, p.c->line);
1572 goto jforce;
1575 if (p.c == _mlist)
1576 _mlist = p.c->next;
1577 else
1578 p.c->prev->next = p.c->next;
1579 if (p.c->next != NULL)
1580 p.c->next->prev = p.c->prev;
1582 jforce:
1583 if (s == 0)
1584 s = 1;
1585 s += sizeof(struct chunk) + _HOPE_SIZE;
1587 if ((p.p = (realloc)(p.c, s)) == NULL)
1588 _out_of_memory();
1589 p.c->prev = NULL;
1590 if ((p.c->next = _mlist) != NULL)
1591 _mlist->prev = p.c;
1592 p.c->file = mdbg_file;
1593 p.c->line = (ui16_t)mdbg_line;
1594 p.c->isfree = FAL0;
1595 p.c->size = (ui32_t)s;
1596 _mlist = p.c++;
1597 _HOPE_SET(p);
1598 jleave:
1599 NYD_LEAVE;
1600 return p.p;
1603 FL void *
1604 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1606 union ptr p;
1607 NYD_ENTER;
1609 if (size == 0)
1610 size = 1;
1611 if (nmemb == 0)
1612 nmemb = 1;
1613 size *= nmemb;
1614 size += sizeof(struct chunk) + _HOPE_SIZE;
1616 if ((p.p = (malloc)(size)) == NULL)
1617 _out_of_memory();
1618 memset(p.p, 0, size);
1619 p.c->prev = NULL;
1620 if ((p.c->next = _mlist) != NULL)
1621 _mlist->prev = p.c;
1622 p.c->file = mdbg_file;
1623 p.c->line = (ui16_t)mdbg_line;
1624 p.c->isfree = FAL0;
1625 p.c->size = (ui32_t)size;
1626 _mlist = p.c++;
1627 _HOPE_SET(p);
1628 NYD_LEAVE;
1629 return p.p;
1632 FL void
1633 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1635 union ptr p;
1636 bool_t isbad;
1637 NYD_ENTER;
1639 if ((p.p = v) == NULL) {
1640 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1641 goto jleave;
1644 _HOPE_GET(p, isbad);
1645 --p.c;
1646 if (p.c->isfree) {
1647 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1648 "\tLast seen: %s, line %d\n",
1649 mdbg_file, mdbg_line, p.c->file, p.c->line);
1650 goto jleave;
1653 if (p.c == _mlist)
1654 _mlist = p.c->next;
1655 else
1656 p.c->prev->next = p.c->next;
1657 if (p.c->next != NULL)
1658 p.c->next->prev = p.c->prev;
1659 p.c->isfree = TRU1;
1661 if (options & OPT_DEBUG) {
1662 p.c->next = _mfree;
1663 _mfree = p.c;
1664 } else
1665 (free)(p.c);
1666 jleave:
1667 NYD_LEAVE;
1670 FL void
1671 smemreset(void)
1673 union ptr p;
1674 size_t c = 0, s = 0;
1675 NYD_ENTER;
1677 for (p.c = _mfree; p.c != NULL;) {
1678 void *vp = p.c;
1679 ++c;
1680 s += p.c->size;
1681 p.c = p.c->next;
1682 (free)(vp);
1684 _mfree = NULL;
1686 if (options & OPT_DEBUG)
1687 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1688 c, s);
1689 NYD_LEAVE;
1692 FL int
1693 c_smemtrace(void *v)
1695 /* For _HOPE_GET() */
1696 char const * const mdbg_file = "smemtrace()";
1697 int const mdbg_line = -1;
1698 FILE *fp;
1699 union ptr p, xp;
1700 bool_t isbad;
1701 size_t lines;
1702 NYD_ENTER;
1704 v = (void*)0x1;
1705 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1706 NULL) {
1707 perror("tmpfile");
1708 goto jleave;
1711 fprintf(fp, "Currently allocated memory chunks:\n");
1712 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1713 xp = p;
1714 ++xp.c;
1715 _HOPE_GET_TRACE(xp, isbad);
1716 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1717 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1718 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1721 if (options & OPT_DEBUG) {
1722 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1723 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1724 xp = p;
1725 ++xp.c;
1726 _HOPE_GET_TRACE(xp, isbad);
1727 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1728 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1729 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1733 page_or_print(fp, lines);
1734 Fclose(fp);
1735 v = NULL;
1736 jleave:
1737 NYD_LEAVE;
1738 return (v != NULL);
1741 # ifdef MEMCHECK
1742 FL bool_t
1743 _smemcheck(char const *mdbg_file, int mdbg_line)
1745 union ptr p, xp;
1746 bool_t anybad = FAL0, isbad;
1747 size_t lines;
1748 NYD_ENTER;
1750 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1751 xp = p;
1752 ++xp.c;
1753 _HOPE_GET_TRACE(xp, isbad);
1754 if (isbad) {
1755 anybad = TRU1;
1756 fprintf(stderr,
1757 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1758 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1759 p.c->file, p.c->line);
1763 if (options & OPT_DEBUG) {
1764 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1765 xp = p;
1766 ++xp.c;
1767 _HOPE_GET_TRACE(xp, isbad);
1768 if (isbad) {
1769 anybad = TRU1;
1770 fprintf(stderr,
1771 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1772 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1773 p.c->file, p.c->line);
1777 NYD_LEAVE;
1778 return anybad;
1780 # endif /* MEMCHECK */
1781 #endif /* HAVE_DEBUG */
1783 /* vim:set fenc=utf-8:s-it-mode */