NYD: lex.c
[s-mailx.git] / auxlily.c
blob1ff8437e3e076df7e3fa7c67d3ae926e2cf113e4
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_MD5
59 # include "md5.h"
60 #endif
62 #ifdef HAVE_DEBUG
63 struct nyd_info {
64 char const *ni_file;
65 char const *ni_fun;
66 ui32_t ni_chirp_line;
67 ui32_t ni_level;
69 #endif
71 /* NYD */
72 #ifdef HAVE_DEBUG
73 static ui32_t _nyd_curr;
74 static ui32_t _nyd_level;
75 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
76 #endif
78 /* {hold,rele}_all_sigs() */
79 static size_t _alls_depth;
80 static sigset_t _alls_nset, _alls_oset;
82 /* {hold,rele}_sigs() */
83 static size_t _hold_sigdepth;
84 static sigset_t _hold_nset, _hold_oset;
86 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
87 #ifdef HAVE_COLOUR
88 static char * _colour_iso6429(char const *wish);
89 #endif
91 #ifdef HAVE_DEBUG
92 static void _nyd_print(struct nyd_info *nip);
93 #endif
95 #ifdef HAVE_COLOUR
96 static char *
97 _colour_iso6429(char const *wish)
99 char const * const wish_orig = wish;
100 char *xwish, *cp, cfg[3] = {0, 0, 0};
101 NYD_ENTER;
103 /* Since we use salloc(), reuse the strcomma() buffer also for the return
104 * value, ensure we have enough room for that */
106 size_t i = strlen(wish) + 1;
107 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
108 memcpy(xwish, wish, i);
109 wish = xwish;
112 /* Iterate over the colour spec */
113 while ((cp = strcomma(&xwish, TRU1)) != NULL) {
114 char *y, *x = strchr(cp, '=');
115 if (x == NULL) {
116 jbail:
117 fprintf(stderr, tr(527,
118 "Invalid colour specification \"%s\": >>> %s <<<\n"),
119 wish_orig, cp);
120 continue;
122 *x++ = '\0';
124 /* TODO convert the ft/fg/bg parser into a table-based one! */
125 if (!asccasecmp(cp, "ft")) {
126 if (!asccasecmp(x, "bold"))
127 cfg[0] = '1';
128 else if (!asccasecmp(x, "inverse"))
129 cfg[0] = '7';
130 else if (!asccasecmp(x, "underline"))
131 cfg[0] = '4';
132 else
133 goto jbail;
134 } else if (!asccasecmp(cp, "fg")) {
135 y = cfg + 1;
136 goto jiter_colour;
137 } else if (!asccasecmp(cp, "bg")) {
138 y = cfg + 2;
139 jiter_colour:
140 if (!asccasecmp(x, "black"))
141 *y = '0';
142 else if (!asccasecmp(x, "blue"))
143 *y = '4';
144 else if (!asccasecmp(x, "green"))
145 *y = '2';
146 else if (!asccasecmp(x, "red"))
147 *y = '1';
148 else if (!asccasecmp(x, "brown"))
149 *y = '3';
150 else if (!asccasecmp(x, "magenta"))
151 *y = '5';
152 else if (!asccasecmp(x, "cyan"))
153 *y = '6';
154 else if (!asccasecmp(x, "white"))
155 *y = '7';
156 else
157 goto jbail;
158 } else
159 goto jbail;
162 /* Restore our salloc() buffer, create return value */
163 xwish = UNCONST(wish);
164 if (cfg[0] || cfg[1] || cfg[2]) {
165 xwish[0] = '\033';
166 xwish[1] = '[';
167 xwish += 2;
168 if (cfg[0])
169 *xwish++ = cfg[0];
170 if (cfg[1]) {
171 if (cfg[0])
172 *xwish++ = ';';
173 xwish[0] = '3';
174 xwish[1] = cfg[1];
175 xwish += 2;
177 if (cfg[2]) {
178 if (cfg[0] || cfg[1])
179 *xwish++ = ';';
180 xwish[0] = '4';
181 xwish[1] = cfg[2];
182 xwish += 2;
184 *xwish++ = 'm';
186 *xwish = '\0';
187 NYD_LEAVE;
188 return UNCONST(wish);
190 #endif /* HAVE_COLOUR */
192 #ifdef HAVE_DEBUG
193 static void
194 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
196 char buf[80];
197 union {int i; size_t z;} u;
199 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
200 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
201 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
202 if (u.i > 0) {
203 u.z = u.i;
204 if (u.z > sizeof buf)
205 u.z = sizeof buf - 1; /* (Skip \0) */
206 write(STDERR_FILENO, buf, u.z);
209 #endif
211 FL void
212 panic(char const *format, ...)
214 va_list ap;
215 NYD_ENTER;
217 fprintf(stderr, tr(1, "Panic: "));
219 va_start(ap, format);
220 vfprintf(stderr, format, ap);
221 va_end(ap);
223 fputs("\n", stderr);
224 fflush(stderr);
225 NYD_LEAVE;
226 abort(); /* Was exit(EXIT_ERR); for a while, but no */
229 FL void
230 alert(char const *format, ...)
232 va_list ap;
233 NYD_ENTER;
235 fprintf(stderr, tr(1, "Panic: "));
237 va_start(ap, format);
238 vfprintf(stderr, format, ap);
239 va_end(ap);
241 fputs("\n", stderr);
242 fflush(stderr);
243 NYD_LEAVE;
246 FL sighandler_type
247 safe_signal(int signum, sighandler_type handler)
249 struct sigaction nact, oact;
250 sighandler_type rv;
251 NYD_ENTER;
253 nact.sa_handler = handler;
254 sigemptyset(&nact.sa_mask);
255 nact.sa_flags = 0;
256 #ifdef SA_RESTART
257 nact.sa_flags |= SA_RESTART;
258 #endif
259 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
260 NYD_LEAVE;
261 return rv;
264 FL void
265 hold_all_sigs(void)
267 NYD_ENTER;
268 if (_alls_depth++ == 0) {
269 sigfillset(&_alls_nset);
270 sigdelset(&_alls_nset, SIGABRT);
271 #ifdef SIGBUS
272 sigdelset(&_alls_nset, SIGBUS);
273 #endif
274 sigdelset(&_alls_nset, SIGCHLD);
275 sigdelset(&_alls_nset, SIGFPE);
276 sigdelset(&_alls_nset, SIGILL);
277 sigdelset(&_alls_nset, SIGKILL);
278 sigdelset(&_alls_nset, SIGSEGV);
279 sigdelset(&_alls_nset, SIGSTOP);
280 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
282 NYD_LEAVE;
285 FL void
286 rele_all_sigs(void)
288 NYD_ENTER;
289 if (--_alls_depth == 0)
290 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
291 NYD_LEAVE;
294 FL void
295 hold_sigs(void)
297 NYD_ENTER;
298 if (_hold_sigdepth++ == 0) {
299 sigemptyset(&_hold_nset);
300 sigaddset(&_hold_nset, SIGHUP);
301 sigaddset(&_hold_nset, SIGINT);
302 sigaddset(&_hold_nset, SIGQUIT);
303 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
305 NYD_LEAVE;
308 FL void
309 rele_sigs(void)
311 NYD_ENTER;
312 if (--_hold_sigdepth == 0)
313 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
314 NYD_LEAVE;
317 #ifdef HAVE_DEBUG
318 FL void
319 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
321 struct nyd_info *nip = _nyd_infos;
323 if (_nyd_curr != NELEM(_nyd_infos))
324 nip += _nyd_curr++;
325 else
326 _nyd_curr = 1;
327 nip->ni_file = file;
328 nip->ni_fun = fun;
329 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
330 nip->ni_level = ((act == 0) ? _nyd_level
331 : (act == 1) ? ++_nyd_level : _nyd_level--);
334 FL void
335 _nyd_oncrash(int signo)
337 struct sigaction xact;
338 sigset_t xset;
339 struct nyd_info *nip;
340 size_t i;
342 xact.sa_handler = SIG_DFL;
343 sigemptyset(&xact.sa_mask);
344 xact.sa_flags = 0;
345 sigaction(signo, &xact, NULL);
347 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
348 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
349 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
350 _nyd_print(nip++);
351 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
352 _nyd_print(nip++);
354 sigemptyset(&xset);
355 sigaddset(&xset, signo);
356 sigprocmask(SIG_UNBLOCK, &xset, NULL);
357 kill(0, signo);
358 while (1)
359 exit(1);
361 #endif
363 FL void
364 touch(struct message *mp)
366 NYD_ENTER;
367 mp->m_flag |= MTOUCH;
368 if (!(mp->m_flag & MREAD))
369 mp->m_flag |= MREAD | MSTATUS;
370 NYD_LEAVE;
373 FL bool_t
374 is_dir(char const *name)
376 struct stat sbuf;
377 bool_t rv = FAL0;
378 NYD_ENTER;
380 if (!stat(name, &sbuf))
381 rv = !!S_ISDIR(sbuf.st_mode);
382 NYD_LEAVE;
383 return rv;
386 FL int
387 argcount(char **argv)
389 char **ap;
390 NYD_ENTER;
392 for (ap = argv; *ap++ != NULL;)
394 NYD_LEAVE;
395 return ap - argv - 1;
398 FL int
399 screensize(void)
401 int s;
402 char *cp;
403 NYD_ENTER;
405 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
406 s = scrnheight - 4;
407 NYD_LEAVE;
408 return s;
411 FL char const *
412 get_pager(void)
414 char const *cp;
415 NYD_ENTER;
417 cp = ok_vlook(PAGER);
418 if (cp == NULL || *cp == '\0')
419 cp = XPAGER;
420 NYD_LEAVE;
421 return cp;
424 FL size_t
425 paging_seems_sensible(void)
427 size_t rv = 0;
428 char const *cp;
429 NYD_ENTER;
431 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
432 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
433 NYD_LEAVE;
434 return rv;
437 FL void
438 page_or_print(FILE *fp, size_t lines)
440 size_t rows;
441 int c;
442 NYD_ENTER;
444 fflush_rewind(fp);
446 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
447 while ((c = getc(fp)) != EOF)
448 if (c == '\n' && ++lines > rows)
449 break;
450 rewind(fp);
453 if (rows != 0 && lines >= rows)
454 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
455 else
456 while ((c = getc(fp)) != EOF)
457 putchar(c);
458 NYD_LEAVE;
461 FL enum protocol
462 which_protocol(char const *name)
464 struct stat st;
465 char const *cp;
466 char *np;
467 size_t sz;
468 enum protocol rv = PROTO_UNKNOWN;
469 NYD_ENTER;
471 if (name[0] == '%' && name[1] == ':')
472 name += 2;
473 for (cp = name; *cp && *cp != ':'; cp++)
474 if (!alnumchar(*cp))
475 goto jfile;
477 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
478 if (strncmp(name, "pop3://", 7) == 0) {
479 #ifdef HAVE_POP3
480 rv = PROTO_POP3;
481 #else
482 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
483 #endif
484 goto jleave;
486 if (strncmp(name, "pop3s://", 8) == 0) {
487 #if defined HAVE_POP3 && defined HAVE_SSL
488 rv = PROTO_POP3;
489 #else
490 # ifndef HAVE_POP3
491 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
492 # endif
493 # ifndef HAVE_SSL
494 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
495 # endif
496 #endif
497 goto jleave;
499 if (strncmp(name, "imap://", 7) == 0) {
500 #ifdef HAVE_IMAP
501 rv = PROTO_IMAP;
502 #else
503 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
504 #endif
505 goto jleave;
507 if (strncmp(name, "imaps://", 8) == 0) {
508 #if defined HAVE_IMAP && defined HAVE_SSL
509 rv = PROTO_IMAP;
510 #else
511 # ifndef HAVE_IMAP
512 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
513 # endif
514 # ifndef HAVE_SSL
515 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
516 # endif
517 #endif
518 goto jleave;
520 } else {
521 /* TODO This is the de facto maildir code and thus belongs into there! */
522 jfile:
523 rv = PROTO_FILE;
524 np = ac_alloc((sz = strlen(name)) + 5);
525 memcpy(np, name, sz + 1);
526 if (stat(name, &st) == 0) {
527 if (S_ISDIR(st.st_mode)) {
528 strcpy(&np[sz], "/tmp");
529 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
530 strcpy(&np[sz], "/new");
531 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
532 strcpy(&np[sz], "/cur");
533 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode))
534 rv = PROTO_MAILDIR;
538 } else {
539 strcpy(&np[sz], ".gz");
540 if (stat(np, &st) < 0) {
541 strcpy(&np[sz], ".bz2");
542 if (stat(np, &st) < 0) {
543 if ((cp = ok_vlook(newfolders)) != NULL &&
544 strcmp(cp, "maildir") == 0)
545 rv = PROTO_MAILDIR;
549 ac_free(np);
551 jleave:
552 NYD_LEAVE;
553 return rv;
556 FL ui32_t
557 torek_hash(char const *name)
559 /* Chris Torek's hash.
560 * NOTE: need to change *at least* create-okey-map.pl when changing the
561 * algorithm!! */
562 ui32_t h = 0;
563 NYD_ENTER;
565 while (*name != '\0') {
566 h *= 33;
567 h += *name++;
569 NYD_LEAVE;
570 return h;
573 FL unsigned
574 pjw(char const *cp) /* XXX obsolete that -> torek_hash */
576 unsigned h = 0, g;
577 NYD_ENTER;
579 cp--;
580 while (*++cp) {
581 h = (h << 4 & 0xffffffff) + (*cp&0377);
582 if ((g = h & 0xf0000000) != 0) {
583 h = h ^ g >> 24;
584 h = h ^ g;
587 NYD_LEAVE;
588 return h;
591 FL ui32_t
592 nextprime(ui32_t n)
594 static ui32_t const primes[] = {
595 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
596 131071, 262139, 524287, 1048573, 2097143, 4194301,
597 8388593, 16777213, 33554393, 67108859, 134217689,
598 268435399, 536870909, 1073741789, 2147483647
601 ui32_t mprime = 7;
602 size_t i;
603 NYD_ENTER;
605 for (i = 0; i < NELEM(primes); i++)
606 if ((mprime = primes[i]) >= (n < 65536 ? n*4 : (n < 262144u ? n*2 : n)))
607 break;
608 if (i == NELEM(primes))
609 mprime = n; /* TODO not so prime, but better than failure */
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 = *s;
618 int c, n;
619 NYD_ENTER;
621 if ((c = *xs & 0xFF) == '\0')
622 goto jleave;
623 ++xs;
624 if (c != '\\')
625 goto jleave;
627 switch ((c = *xs & 0xFF)) {
628 case '\\': break;
629 case 'a': c = '\a'; break;
630 case 'b': c = '\b'; break;
631 case 'c': c = PROMPT_STOP; break;
632 case 'f': c = '\f'; break;
633 case 'n': c = '\n'; break;
634 case 'r': c = '\r'; break;
635 case 't': c = '\t'; break;
636 case 'v': c = '\v'; break;
637 case '0':
638 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
639 c <<= 3;
640 c |= *xs - '0';
642 goto jleave;
643 /* S-nail extension for nice (get)prompt(()) support */
644 case '&':
645 case '?':
646 case '$':
647 case '@':
648 if (use_nail_extensions) {
649 switch (c) {
650 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
651 case '?': c = exec_last_comm_error ? '1' : '0'; break;
652 case '$': c = PROMPT_DOLLAR; break;
653 case '@': c = PROMPT_AT; break;
655 break;
657 /* FALLTHRU */
658 case '\0':
659 /* A sole <backslash> at EOS is treated as-is! */
660 /* FALLTHRU */
661 default:
662 c = '\\';
663 goto jleave;
665 ++xs;
666 jleave:
667 *s = xs;
668 NYD_LEAVE;
669 return c;
672 FL char *
673 getprompt(void)
675 static char buf[PROMPT_BUFFER_SIZE];
677 char *cp = buf;
678 char const *ccp;
679 NYD_ENTER;
681 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
682 goto jleave;
684 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
685 char const *a;
686 size_t l;
687 int c = expand_shell_escape(&ccp, TRU1);
689 if (c > 0) {
690 *cp = (char)c;
691 continue;
693 if (c == 0 || c == PROMPT_STOP)
694 break;
696 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
697 if (a == NULL)
698 a = "";
699 l = strlen(a);
700 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
701 *cp++ = '?';
702 else {
703 memcpy(cp, a, l);
704 cp += --l;
707 jleave:
708 *cp = '\0';
709 NYD_LEAVE;
710 return buf;
713 FL char *
714 nodename(int mayoverride)
716 static char *hostname;
717 struct utsname ut;
718 char *hn;
719 #ifdef HAVE_SOCKETS
720 # ifdef HAVE_IPV6
721 struct addrinfo hints, *res;
722 # else
723 struct hostent *hent;
724 # endif
725 #endif
726 NYD_ENTER;
728 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
729 if (hostname != NULL)
730 free(hostname);
731 hostname = sstrdup(hn);
732 } else if (hostname == NULL) {
733 uname(&ut);
734 hn = ut.nodename;
735 #ifdef HAVE_SOCKETS
736 # ifdef HAVE_IPV6
737 memset(&hints, 0, sizeof hints);
738 hints.ai_family = AF_UNSPEC;
739 hints.ai_socktype = SOCK_DGRAM; /* dummy */
740 hints.ai_flags = AI_CANONNAME;
741 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
742 if (res->ai_canonname != NULL) {
743 size_t l = strlen(res->ai_canonname);
744 hn = ac_alloc(l + 1);
745 memcpy(hn, res->ai_canonname, l + 1);
747 freeaddrinfo(res);
749 # else
750 hent = gethostbyname(hn);
751 if (hent != NULL)
752 hn = hent->h_name;
753 # endif
754 #endif
755 hostname = sstrdup(hn);
756 #if defined HAVE_SOCKETS && defined HAVE_IPV6
757 if (hn != ut.nodename)
758 ac_free(hn);
759 #endif
761 NYD_LEAVE;
762 return hostname;
765 FL char *
766 lookup_password_for_token(char const *token)
768 size_t tl;
769 char *var, *cp;
770 NYD_ENTER;
772 tl = strlen(token);
773 var = ac_alloc(tl + 9 +1);
775 memcpy(var, "password-", 9);
776 memcpy(var + 9, token, tl);
777 var[tl + 9] = '\0';
779 if ((cp = vok_vlook(var)) != NULL)
780 cp = savestr(cp);
781 ac_free(var);
782 NYD_LEAVE;
783 return cp;
786 FL char *
787 getrandstring(size_t length)
789 static unsigned char nodedigest[16];
790 static pid_t pid;
792 struct str b64;
793 char *data, *cp;
794 size_t i;
795 int fd = -1;
796 #ifdef HAVE_MD5
797 md5_ctx ctx;
798 #else
799 size_t j;
800 #endif
801 NYD_ENTER;
803 data = ac_alloc(length);
804 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
805 length != (size_t)read(fd, data, length)) {
806 if (pid == 0) {
807 pid = getpid();
808 srand(pid);
809 cp = nodename(0);
810 #ifdef HAVE_MD5
811 md5_init(&ctx);
812 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
813 md5_final(nodedigest, &ctx);
814 #else
815 /* In that case it's only used for boundaries and Message-Id:s so that
816 * srand(3) should suffice */
817 j = strlen(cp) + 1;
818 for (i = 0; i < sizeof(nodedigest); ++i)
819 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
820 #endif
822 for (i = 0; i < length; i++)
823 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
824 nodedigest[i % sizeof nodedigest]);
826 if (fd >= 0)
827 close(fd);
829 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
830 ac_free(data);
831 assert(length < b64.l);
832 b64.s[length] = '\0';
833 NYD_LEAVE;
834 return b64.s;
837 #ifdef HAVE_MD5
838 FL char *
839 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
841 char const *cp = vp;
842 size_t i, j;
843 NYD_ENTER;
845 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
846 j = i << 1;
847 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
848 hex[++j] = hexchar(cp[i] & 0x0f);
850 NYD_LEAVE;
851 return hex;
854 FL char *
855 cram_md5_string(char const *user, char const *pass, char const *b64)
857 struct str in, out;
858 char digest[16], *cp;
859 size_t lu;
860 NYD_ENTER;
862 out.s = NULL;
863 in.s = UNCONST(b64);
864 in.l = strlen(in.s);
865 (void)b64_decode(&out, &in, NULL);
866 assert(out.s != NULL);
868 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
869 free(out.s);
870 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
872 lu = strlen(user);
873 in.l = lu + MD5TOHEX_SIZE +1;
874 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
875 memcpy(in.s, user, lu);
876 in.s[lu] = ' ';
877 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
878 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
879 ac_free(in.s);
880 NYD_LEAVE;
881 return out.s;
883 #endif /* HAVE_MD5 */
885 FL enum okay
886 makedir(char const *name)
888 struct stat st;
889 enum okay rv = STOP;
890 NYD_ENTER;
892 if (!mkdir(name, 0700))
893 rv = OKAY;
894 else {
895 int e = errno;
896 if ((e == EEXIST || e == ENOSYS) && stat(name, &st) == 0 &&
897 S_ISDIR(st.st_mode))
898 rv = OKAY;
900 NYD_LEAVE;
901 return rv;
904 #ifdef HAVE_FCHDIR
905 FL enum okay
906 cwget(struct cw *cw)
908 enum okay rv = STOP;
909 NYD_ENTER;
911 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
912 goto jleave;
913 if (fchdir(cw->cw_fd) == -1) {
914 close(cw->cw_fd);
915 goto jleave;
917 rv = OKAY;
918 jleave:
919 NYD_LEAVE;
920 return rv;
923 FL enum okay
924 cwret(struct cw *cw)
926 enum okay rv = STOP;
927 NYD_ENTER;
929 if (!fchdir(cw->cw_fd))
930 rv = OKAY;
931 NYD_LEAVE;
932 return rv;
935 FL void
936 cwrelse(struct cw *cw)
938 NYD_ENTER;
939 close(cw->cw_fd);
940 NYD_LEAVE;
943 #else /* !HAVE_FCHDIR */
944 FL enum okay
945 cwget(struct cw *cw)
947 enum okay rv = STOP;
948 NYD_ENTER;
950 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
951 rv = OKAY;
952 NYD_LEAVE;
953 return rv;
956 FL enum okay
957 cwret(struct cw *cw)
959 enum okay rv = STOP;
960 NYD_ENTER;
962 if (!chdir(cw->cw_wd))
963 rv = OKAY;
964 NYD_LEAVE;
965 return rv;
968 FL void
969 cwrelse(struct cw *cw)
971 NYD_ENTER;
972 UNUSED(cw);
973 NYD_LEAVE;
975 #endif /* !HAVE_FCHDIR */
977 FL char *
978 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
980 int col_orig = col, n, sz;
981 char *nb, *np;
982 NYD_ENTER;
984 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
985 while (*cp) {
986 #ifdef HAVE_WCWIDTH
987 if (mb_cur_max > 1) {
988 wchar_t wc;
990 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0)
991 n = sz = 1;
992 else if ((n = wcwidth(wc)) < 0)
993 n = 1;
994 } else
995 #endif
996 n = sz = 1;
997 if (n > col)
998 break;
999 col -= n;
1000 if (sz == 1 && spacechar(*cp)) {
1001 *np++ = ' ';
1002 cp++;
1003 } else
1004 while (sz--)
1005 *np++ = *cp++;
1008 if (fill && col != 0) {
1009 if (fill > 0) {
1010 memmove(nb + col, nb, (size_t)(np - nb));
1011 memset(nb, ' ', col);
1012 } else
1013 memset(np, ' ', col);
1014 np += col;
1015 col = 0;
1018 *np = '\0';
1019 if (cols_decr_used_or_null != NULL)
1020 *cols_decr_used_or_null -= col_orig - col;
1021 NYD_LEAVE;
1022 return nb;
1025 FL void
1026 makeprint(struct str const *in, struct str *out)
1028 static int print_all_chars = -1;
1030 char const *inp, *maxp;
1031 char *outp;
1032 size_t msz;
1033 NYD_ENTER;
1035 if (print_all_chars == -1)
1036 print_all_chars = ok_blook(print_all_chars);
1038 msz = in->l + 1;
1039 out->s = outp = smalloc(msz);
1040 inp = in->s;
1041 maxp = inp + in->l;
1043 if (print_all_chars) {
1044 out->l = in->l;
1045 memcpy(outp, inp, out->l);
1046 goto jleave;
1049 #ifdef HAVE_C90AMEND1
1050 if (mb_cur_max > 1) {
1051 char mbb[MB_LEN_MAX + 1];
1052 wchar_t wc;
1053 int i, n;
1054 size_t dist;
1056 out->l = 0;
1057 while (inp < maxp) {
1058 if (*inp & 0200)
1059 n = mbtowc(&wc, inp, maxp - inp);
1060 else {
1061 wc = *inp;
1062 n = 1;
1064 if (n < 0) {
1065 /* FIXME Why mbtowc() resetting here?
1066 * FIXME what about ISO 2022-JP plus -- those
1067 * FIXME will loose shifts, then!
1068 * FIXME THUS - we'd need special "known points"
1069 * FIXME to do so - say, after a newline!!
1070 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1071 (void)mbtowc(&wc, NULL, mb_cur_max);
1072 wc = utf8 ? 0xFFFD : '?';
1073 n = 1;
1074 } else if (n == 0)
1075 n = 1;
1076 inp += n;
1077 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1078 wc != '\t') {
1079 if ((wc & ~(wchar_t)037) == 0)
1080 wc = utf8 ? 0x2400 | wc : '?';
1081 else if (wc == 0177)
1082 wc = utf8 ? 0x2421 : '?';
1083 else
1084 wc = utf8 ? 0x2426 : '?';
1086 if ((n = wctomb(mbb, wc)) <= 0)
1087 continue;
1088 out->l += n;
1089 if (out->l >= msz - 1) {
1090 dist = outp - out->s;
1091 out->s = srealloc(out->s, msz += 32);
1092 outp = &out->s[dist];
1094 for (i = 0; i < n; i++)
1095 *outp++ = mbb[i];
1097 } else
1098 #endif /* C90AMEND1 */
1100 int c;
1101 while (inp < maxp) {
1102 c = *inp++ & 0377;
1103 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1104 c = '?';
1105 *outp++ = c;
1107 out->l = in->l;
1109 jleave:
1110 NYD_LEAVE;
1111 out->s[out->l] = '\0';
1114 FL char *
1115 prstr(char const *s)
1117 struct str in, out;
1118 char *rp;
1119 NYD_ENTER;
1121 in.s = UNCONST(s);
1122 in.l = strlen(s);
1123 makeprint(&in, &out);
1124 rp = savestrbuf(out.s, out.l);
1125 free(out.s);
1126 NYD_LEAVE;
1127 return rp;
1130 FL int
1131 prout(char const *s, size_t sz, FILE *fp)
1133 struct str in, out;
1134 int n;
1135 NYD_ENTER;
1137 in.s = UNCONST(s);
1138 in.l = sz;
1139 makeprint(&in, &out);
1140 n = fwrite(out.s, 1, out.l, fp);
1141 free(out.s);
1142 NYD_LEAVE;
1143 return n;
1146 FL size_t
1147 putuc(int u, int c, FILE *fp)
1149 size_t rv;
1150 UNUSED(u);
1151 NYD_ENTER;
1153 #ifdef HAVE_C90AMEND1
1154 if (utf8 && (u & ~(wchar_t)0177)) {
1155 char mbb[MB_LEN_MAX];
1156 int i, n;
1158 if ((n = wctomb(mbb, u)) > 0) {
1159 rv = wcwidth(u);
1160 for (i = 0; i < n; ++i)
1161 if (putc(mbb[i] & 0377, fp) == EOF) {
1162 rv = 0;
1163 break;
1165 } else if (n == 0)
1166 rv = (putc('\0', fp) != EOF);
1167 else
1168 rv = 0;
1169 } else
1170 #endif
1171 rv = (putc(c, fp) != EOF);
1172 NYD_LEAVE;
1173 return rv;
1176 #ifdef HAVE_COLOUR
1177 FL void
1178 colour_table_create(char const *pager_used)
1180 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1181 size_t i;
1182 struct colour_table *ct;
1183 NYD_ENTER;
1185 if (ok_blook(colour_disable))
1186 goto jleave;
1188 /* If pager, check wether it is allowed to use colour */
1189 if (pager_used != NULL) {
1190 char *pager;
1192 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1193 u.ccp = COLOUR_PAGERS;
1194 pager = savestr(u.cp);
1196 while ((u.cp = strcomma(&pager, TRU1)) != NULL)
1197 if (strstr(pager_used, u.cp) != NULL)
1198 goto jok;
1199 goto jleave;
1202 /* $TERM is different in that we default to false unless whitelisted */
1204 char *term, *okterms;
1206 /* Don't use getenv(), but force copy-in into our own tables.. */
1207 if ((term = _var_voklook("TERM")) == NULL)
1208 goto jleave;
1209 if ((okterms = ok_vlook(colour_terms)) == NULL)
1210 okterms = UNCONST(COLOUR_TERMS);
1211 okterms = savestr(okterms);
1213 i = strlen(term);
1214 while ((u.cp = strcomma(&okterms, TRU1)) != NULL)
1215 if (!strncmp(u.cp, term, i))
1216 goto jok;
1217 goto jleave;
1220 jok:
1221 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1222 { static struct {
1223 enum okeys okey;
1224 enum colourspec cspec;
1225 char const *defval;
1226 } const map[] = {
1227 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1228 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1229 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1230 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1231 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1234 for (i = 0; i < NELEM(map); ++i) {
1235 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1236 u.ccp = map[i].defval;
1237 u.cp = _colour_iso6429(u.ccp);
1238 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1239 ct->ct_csinfo[map[i].cspec].s = u.cp;
1242 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") - 1;
1243 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1245 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1246 u.ccp = COLOUR_USER_HEADERS;
1247 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1248 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1249 jleave:
1250 NYD_LEAVE;
1253 FL void
1254 colour_put(FILE *fp, enum colourspec cs)
1256 NYD_ENTER;
1257 if (colour_table != NULL) {
1258 struct str const *cp = colour_get(cs);
1260 fwrite(cp->s, cp->l, 1, fp);
1262 NYD_LEAVE;
1265 FL void
1266 colour_put_header(FILE *fp, char const *name)
1268 enum colourspec cs = COLOURSPEC_HEADER;
1269 struct str const *uheads;
1270 char *cp, *cp_base, *x;
1271 size_t namelen;
1272 NYD_ENTER;
1274 if (colour_table == NULL)
1275 goto j_leave;
1276 /* Normal header colours if there are no user headers */
1277 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1278 if (uheads->s == NULL)
1279 goto jleave;
1281 /* Iterate over all entries in the *colour-user-headers* list */
1282 cp = ac_alloc(uheads->l + 1);
1283 memcpy(cp, uheads->s, uheads->l + 1);
1284 cp_base = cp;
1285 namelen = strlen(name);
1286 while ((x = strcomma(&cp, TRU1)) != NULL) {
1287 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1288 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1289 cs = COLOURSPEC_UHEADER;
1290 break;
1293 ac_free(cp_base);
1294 jleave:
1295 colour_put(fp, cs);
1296 j_leave:
1297 NYD_LEAVE;
1300 FL void
1301 colour_reset(FILE *fp)
1303 NYD_ENTER;
1304 if (colour_table != NULL)
1305 fwrite("\033[0m", 4, 1, fp);
1306 NYD_LEAVE;
1309 FL struct str const *
1310 colour_get(enum colourspec cs)
1312 struct str const *rv = NULL;
1313 NYD_ENTER;
1315 if (colour_table != NULL)
1316 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1317 rv = NULL;
1318 NYD_LEAVE;
1319 return rv;
1321 #endif /* HAVE_COLOUR */
1323 FL void
1324 time_current_update(struct time_current *tc, bool_t full_update)
1326 NYD_ENTER;
1327 tc->tc_time = time(NULL);
1328 if (full_update) {
1329 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1330 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1331 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1333 NYD_LEAVE;
1336 static void
1337 _out_of_memory(void)
1339 panic("no memory");
1342 #ifndef HAVE_DEBUG
1343 FL void *
1344 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1346 void *rv;
1347 NYD_ENTER;
1349 if (s == 0)
1350 s = 1;
1351 if ((rv = malloc(s)) == NULL)
1352 _out_of_memory();
1353 NYD_LEAVE;
1354 return rv;
1357 FL void *
1358 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1360 void *rv;
1361 NYD_ENTER;
1363 if (s == 0)
1364 s = 1;
1365 if (v == NULL)
1366 rv = smalloc(s);
1367 else if ((rv = realloc(v, s)) == NULL)
1368 _out_of_memory();
1369 NYD_LEAVE;
1370 return rv;
1373 FL void *
1374 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1376 void *rv;
1377 NYD_ENTER;
1379 if (size == 0)
1380 size = 1;
1381 if ((rv = calloc(nmemb, size)) == NULL)
1382 _out_of_memory();
1383 NYD_LEAVE;
1384 return rv;
1387 #else /* !HAVE_DEBUG */
1388 CTA(sizeof(char) == sizeof(ui8_t));
1390 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1391 # define _HOPE_SET(C) \
1392 do {\
1393 union ptr __xl, __xu;\
1394 struct chunk *__xc;\
1395 __xl.p = (C).p;\
1396 __xc = __xl.c - 1;\
1397 __xu.p = __xc;\
1398 (C).cp += 8;\
1399 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1400 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1401 __xu.ui8p += __xc->size - 8;\
1402 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1403 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1404 } while (0)
1405 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1406 # define _HOPE_GET(C,BAD) \
1407 do {\
1408 union ptr __xl, __xu;\
1409 struct chunk *__xc;\
1410 ui32_t __i;\
1411 __xl.p = (C).p;\
1412 __xl.cp -= 8;\
1413 (C).cp = __xl.cp;\
1414 __xc = __xl.c - 1;\
1415 (BAD) = FAL0;\
1416 __i = 0;\
1417 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1418 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1419 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1420 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1421 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1422 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1423 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1424 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1425 if (__i != 0) {\
1426 (BAD) = TRU1;\
1427 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1428 __xl.p, __i, mdbg_file, mdbg_line);\
1430 __xu.p = __xc;\
1431 __xu.ui8p += __xc->size - 8;\
1432 __i = 0;\
1433 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1434 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1435 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1436 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1437 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1438 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1439 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1440 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1441 if (__i != 0) {\
1442 (BAD) = TRU1;\
1443 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1444 __xl.p, __i, mdbg_file, mdbg_line);\
1446 if (BAD)\
1447 alert(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1448 } while (0)
1450 struct chunk {
1451 struct chunk *prev;
1452 struct chunk *next;
1453 char const *file;
1454 ui16_t line;
1455 ui8_t isfree;
1456 ui8_t __dummy;
1457 ui32_t size;
1460 union ptr {
1461 void *p;
1462 struct chunk *c;
1463 char *cp;
1464 ui8_t *ui8p;
1467 struct chunk *_mlist, *_mfree;
1469 FL void *
1470 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1472 union ptr p;
1473 NYD_ENTER;
1475 if (s == 0)
1476 s = 1;
1477 s += sizeof(struct chunk) + _HOPE_SIZE;
1479 if ((p.p = (malloc)(s)) == NULL)
1480 _out_of_memory();
1481 p.c->prev = NULL;
1482 if ((p.c->next = _mlist) != NULL)
1483 _mlist->prev = p.c;
1484 p.c->file = mdbg_file;
1485 p.c->line = (ui16_t)mdbg_line;
1486 p.c->isfree = FAL0;
1487 p.c->size = (ui32_t)s;
1488 _mlist = p.c++;
1489 _HOPE_SET(p);
1490 NYD_LEAVE;
1491 return p.p;
1494 FL void *
1495 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1497 union ptr p;
1498 bool_t isbad;
1499 NYD_ENTER;
1501 if ((p.p = v) == NULL) {
1502 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1503 goto jleave;
1506 _HOPE_GET(p, isbad);
1507 --p.c;
1508 if (p.c->isfree) {
1509 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1510 "\tLast seen: %s, line %d\n",
1511 mdbg_file, mdbg_line, p.c->file, p.c->line);
1512 goto jforce;
1515 if (p.c == _mlist)
1516 _mlist = p.c->next;
1517 else
1518 p.c->prev->next = p.c->next;
1519 if (p.c->next != NULL)
1520 p.c->next->prev = p.c->prev;
1522 jforce:
1523 if (s == 0)
1524 s = 1;
1525 s += sizeof(struct chunk) + _HOPE_SIZE;
1527 if ((p.p = (realloc)(p.c, s)) == NULL)
1528 _out_of_memory();
1529 p.c->prev = NULL;
1530 if ((p.c->next = _mlist) != NULL)
1531 _mlist->prev = p.c;
1532 p.c->file = mdbg_file;
1533 p.c->line = (ui16_t)mdbg_line;
1534 p.c->isfree = FAL0;
1535 p.c->size = (ui32_t)s;
1536 _mlist = p.c++;
1537 _HOPE_SET(p);
1538 jleave:
1539 NYD_LEAVE;
1540 return p.p;
1543 FL void *
1544 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1546 union ptr p;
1547 NYD_ENTER;
1549 if (size == 0)
1550 size = 1;
1551 if (nmemb == 0)
1552 nmemb = 1;
1553 size *= nmemb;
1554 size += sizeof(struct chunk) + _HOPE_SIZE;
1556 if ((p.p = (malloc)(size)) == NULL)
1557 _out_of_memory();
1558 memset(p.p, 0, size);
1559 p.c->prev = NULL;
1560 if ((p.c->next = _mlist) != NULL)
1561 _mlist->prev = p.c;
1562 p.c->file = mdbg_file;
1563 p.c->line = (ui16_t)mdbg_line;
1564 p.c->isfree = FAL0;
1565 p.c->size = (ui32_t)size;
1566 _mlist = p.c++;
1567 _HOPE_SET(p);
1568 NYD_LEAVE;
1569 return p.p;
1572 FL void
1573 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1575 union ptr p;
1576 bool_t isbad;
1577 NYD_ENTER;
1579 if ((p.p = v) == NULL) {
1580 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1581 goto jleave;
1584 _HOPE_GET(p, isbad);
1585 --p.c;
1586 if (p.c->isfree) {
1587 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1588 "\tLast seen: %s, line %d\n",
1589 mdbg_file, mdbg_line, p.c->file, p.c->line);
1590 goto jleave;
1593 if (p.c == _mlist)
1594 _mlist = p.c->next;
1595 else
1596 p.c->prev->next = p.c->next;
1597 if (p.c->next != NULL)
1598 p.c->next->prev = p.c->prev;
1599 p.c->isfree = TRU1;
1601 if (options & OPT_DEBUG) {
1602 p.c->next = _mfree;
1603 _mfree = p.c;
1604 } else
1605 (free)(p.c);
1606 jleave:
1607 NYD_LEAVE;
1610 FL void
1611 smemreset(void)
1613 union ptr p;
1614 size_t c = 0, s = 0;
1615 NYD_ENTER;
1617 for (p.c = _mfree; p.c != NULL;) {
1618 void *vp = p.c;
1619 ++c;
1620 s += p.c->size;
1621 p.c = p.c->next;
1622 (free)(vp);
1624 _mfree = NULL;
1626 if (options & OPT_DEBUG)
1627 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1628 c, s);
1629 NYD_LEAVE;
1632 FL int
1633 c_smemtrace(void *v)
1635 /* For _HOPE_GET() */
1636 char const * const mdbg_file = "smemtrace()";
1637 int const mdbg_line = -1;
1638 FILE *fp;
1639 union ptr p, xp;
1640 bool_t isbad;
1641 size_t lines;
1642 NYD_ENTER;
1644 v = (void*)0x1;
1645 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1646 NULL) {
1647 perror("tmpfile");
1648 goto jleave;
1651 fprintf(fp, "Currently allocated memory chunks:\n");
1652 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1653 xp = p;
1654 ++xp.c;
1655 _HOPE_GET_TRACE(xp, isbad);
1656 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1657 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1658 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1661 if (options & OPT_DEBUG) {
1662 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1663 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1664 xp = p;
1665 ++xp.c;
1666 _HOPE_GET_TRACE(xp, isbad);
1667 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1668 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1669 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1673 page_or_print(fp, lines);
1674 Fclose(fp);
1675 v = NULL;
1676 jleave:
1677 NYD_LEAVE;
1678 return (v != NULL);
1681 # ifdef MEMCHECK
1682 FL bool_t
1683 _smemcheck(char const *mdbg_file, int mdbg_line)
1685 union ptr p, xp;
1686 bool_t anybad = FAL0, isbad;
1687 size_t lines;
1688 NYD_ENTER;
1690 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1691 xp = p;
1692 ++xp.c;
1693 _HOPE_GET_TRACE(xp, isbad);
1694 if (isbad) {
1695 anybad = TRU1;
1696 fprintf(stderr,
1697 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1698 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1699 p.c->file, p.c->line);
1703 if (options & OPT_DEBUG) {
1704 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1705 xp = p;
1706 ++xp.c;
1707 _HOPE_GET_TRACE(xp, isbad);
1708 if (isbad) {
1709 anybad = TRU1;
1710 fprintf(stderr,
1711 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1712 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1713 p.c->file, p.c->line);
1717 NYD_LEAVE;
1718 return anybad;
1720 # endif /* MEMCHECK */
1721 #endif /* HAVE_DEBUG */
1723 /* vim:set fenc=utf-8:s-it-mode */