cc-test.sh: add test for -q
[s-mailx.git] / auxlily.c
blob52365c19bf7384bc2a178c99386df4b63b65a467
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 - 2013 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 /* {hold,rele}_all_sigs() */
63 static size_t _alls_depth;
64 static sigset_t _alls_nset, _alls_oset;
66 FL void
67 panic(char const *format, ...)
69 va_list ap;
71 fprintf(stderr, tr(1, "Panic: "));
73 va_start(ap, format);
74 vfprintf(stderr, format, ap);
75 va_end(ap);
77 fputs("\n", stderr);
78 fflush(stderr);
79 exit(EXIT_ERR);
82 #ifdef HAVE_DEBUG
83 FL void
84 warn(char const *format, ...)
86 va_list ap;
88 fprintf(stderr, tr(1, "Panic: "));
90 va_start(ap, format);
91 vfprintf(stderr, format, ap);
92 va_end(ap);
94 fputs("\n", stderr);
95 fflush(stderr);
97 #endif
99 FL void
100 hold_all_sigs(void)
102 if (_alls_depth++ == 0) {
103 sigfillset(&_alls_nset);
104 sigdelset(&_alls_nset, SIGKILL);
105 sigdelset(&_alls_nset, SIGSTOP);
106 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
110 FL void
111 rele_all_sigs(void)
113 if (--_alls_depth == 0)
114 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
118 * Touch the named message by setting its MTOUCH flag.
119 * Touched messages have the effect of not being sent
120 * back to the system mailbox on exit.
122 FL void
123 touch(struct message *mp)
126 mp->m_flag |= MTOUCH;
127 if ((mp->m_flag & MREAD) == 0)
128 mp->m_flag |= MREAD|MSTATUS;
132 * Test to see if the passed file name is a directory.
133 * Return true if it is.
135 FL int
136 is_dir(char const *name)
138 struct stat sbuf;
140 if (stat(name, &sbuf) < 0)
141 return(0);
142 return(S_ISDIR(sbuf.st_mode));
146 * Count the number of arguments in the given string raw list.
148 FL int
149 argcount(char **argv)
151 char **ap;
153 for (ap = argv; *ap++ != NULL;)
155 return ap - argv - 1;
158 FL char *
159 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
161 int col_orig = col, n, sz;
162 char *nb, *np;
164 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
165 while (*cp) {
166 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
167 if (mb_cur_max > 1) {
168 wchar_t wc;
170 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
171 n = sz = 1;
172 } else {
173 if ((n = wcwidth(wc)) < 0)
174 n = 1;
176 } else
177 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
179 n = sz = 1;
181 if (n > col)
182 break;
183 col -= n;
184 if (sz == 1 && spacechar(*cp)) {
185 *np++ = ' ';
186 cp++;
187 } else
188 while (sz--)
189 *np++ = *cp++;
192 if (fill && col != 0) {
193 if (fill > 0) {
194 memmove(nb + col, nb, (size_t)(np - nb));
195 memset(nb, ' ', col);
196 } else
197 memset(np, ' ', col);
198 np += col;
199 col = 0;
202 *np = '\0';
203 if (cols_decr_used_or_null != NULL)
204 *cols_decr_used_or_null -= col_orig - col;
205 return nb;
208 FL size_t
209 paging_seems_sensible(void)
211 size_t ret = 0;
212 char const *cp;
214 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL)
215 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
216 return ret;
219 FL void
220 page_or_print(FILE *fp, size_t lines)
222 size_t rows;
223 int c;
225 fflush_rewind(fp);
227 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
228 while ((c = getc(fp)) != EOF)
229 if (c == '\n' && ++lines > rows)
230 break;
231 rewind(fp);
234 if (rows != 0 && lines >= rows)
235 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
236 else
237 while ((c = getc(fp)) != EOF)
238 putchar(c);
241 FL enum protocol
242 which_protocol(const char *name)
244 register const char *cp;
245 char *np;
246 size_t sz;
247 struct stat st;
248 enum protocol p;
250 if (name[0] == '%' && name[1] == ':')
251 name += 2;
252 for (cp = name; *cp && *cp != ':'; cp++)
253 if (!alnumchar(*cp&0377))
254 goto file;
255 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
256 if (strncmp(name, "pop3://", 7) == 0)
257 #ifdef HAVE_POP3
258 return PROTO_POP3;
259 #else
260 fprintf(stderr,
261 tr(216, "No POP3 support compiled in.\n"));
262 #endif
263 if (strncmp(name, "pop3s://", 8) == 0)
264 #ifdef HAVE_SSL
265 return PROTO_POP3;
266 #else
267 fprintf(stderr,
268 tr(225, "No SSL support compiled in.\n"));
269 #endif
270 if (strncmp(name, "imap://", 7) == 0)
271 #ifdef HAVE_IMAP
272 return PROTO_IMAP;
273 #else
274 fprintf(stderr,
275 tr(269, "No IMAP support compiled in.\n"));
276 #endif
277 if (strncmp(name, "imaps://", 8) == 0)
278 #ifdef HAVE_SSL
279 return PROTO_IMAP;
280 #else
281 fprintf(stderr,
282 tr(225, "No SSL support compiled in.\n"));
283 #endif
284 return PROTO_UNKNOWN;
285 } else {
286 /* TODO This is the de facto maildir code and thus belongs
287 * TODO into maildir! */
288 file: p = PROTO_FILE;
289 np = ac_alloc((sz = strlen(name)) + 5);
290 memcpy(np, name, sz + 1);
291 if (stat(name, &st) == 0) {
292 if (S_ISDIR(st.st_mode)) {
293 strcpy(&np[sz], "/tmp");
294 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
295 strcpy(&np[sz], "/new");
296 if (stat(np, &st) == 0 &&
297 S_ISDIR(st.st_mode)) {
298 strcpy(&np[sz], "/cur");
299 if (stat(np, &st) == 0 &&
300 S_ISDIR(st.st_mode))
301 p = PROTO_MAILDIR;
305 } else {
306 strcpy(&np[sz], ".gz");
307 if (stat(np, &st) < 0) {
308 strcpy(&np[sz], ".bz2");
309 if (stat(np, &st) < 0) {
310 if ((cp = value("newfolders")) != 0 &&
311 strcmp(cp, "maildir") == 0)
312 p = PROTO_MAILDIR;
316 ac_free(np);
317 return p;
321 FL unsigned
322 pjw(const char *cp)
324 unsigned h = 0, g;
326 cp--;
327 while (*++cp) {
328 h = (h << 4 & 0xffffffff) + (*cp&0377);
329 if ((g = h & 0xf0000000) != 0) {
330 h = h ^ g >> 24;
331 h = h ^ g;
334 return h;
337 FL long
338 nextprime(long n)
340 const long primes[] = {
341 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
342 131071, 262139, 524287, 1048573, 2097143, 4194301,
343 8388593, 16777213, 33554393, 67108859, 134217689,
344 268435399, 536870909, 1073741789, 2147483647
346 long mprime = 7;
347 size_t i;
349 for (i = 0; i < sizeof primes / sizeof *primes; i++)
350 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
351 n < 262144 ? n*2 : n))
352 break;
353 if (i == sizeof primes / sizeof *primes)
354 mprime = n; /* not so prime, but better than failure */
355 return mprime;
358 FL int
359 expand_shell_escape(char const **s, bool_t use_nail_extensions)
361 char const *xs = *s;
362 int c, n;
364 if ((c = *xs & 0xFF) == '\0')
365 goto jleave;
366 ++xs;
367 if (c != '\\')
368 goto jleave;
370 switch ((c = *xs & 0xFF)) {
371 case '\\': break;
372 case 'a': c = '\a'; break;
373 case 'b': c = '\b'; break;
374 case 'c': c = PROMPT_STOP;break;
375 case 'f': c = '\f'; break;
376 case 'n': c = '\n'; break;
377 case 'r': c = '\r'; break;
378 case 't': c = '\t'; break;
379 case 'v': c = '\v'; break;
380 case '0':
381 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
382 c <<= 3;
383 c |= *xs - '0';
385 goto jleave;
386 /* S-nail extension for nice (get)prompt(()) support */
387 case '?':
388 case '$':
389 case '@':
390 if (use_nail_extensions) {
391 switch (c) {
392 case '?':
393 c = exec_last_comm_error ? '1' : '0';
394 break;
395 case '$':
396 c = PROMPT_DOLLAR;
397 break;
398 case '@':
399 c = PROMPT_AT;
400 break;
402 break;
404 /* FALLTHRU */
405 case '\0':
406 /* A sole <backslash> at EOS is treated as-is! */
407 /* FALLTHRU */
408 default:
409 c = '\\';
410 goto jleave;
412 ++xs;
413 jleave:
414 *s = xs;
415 return c;
418 FL char *
419 getprompt(void)
421 static char buf[PROMPT_BUFFER_SIZE];
423 char const *ccp;
425 if (options & OPT_NOPROMPT)
426 buf[0] = '\0';
427 else if ((ccp = value("prompt")) == NULL) {
428 buf[0] = value("bsdcompat") ? '&' : '?';
429 buf[1] = ' ';
430 buf[2] = '\0';
431 } else {
432 char *cp;
434 for (cp = buf; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
435 char const *a;
436 size_t l;
437 int c = expand_shell_escape(&ccp, TRU1);
438 if (c > 0) {
439 *cp = (char)c;
440 continue;
442 if (c == 0 || c == PROMPT_STOP)
443 break;
445 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
446 if (a == NULL)
447 a = "";
448 l = strlen(a);
449 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
450 *cp++ = '?';
451 else {
452 memcpy(cp, a, l);
453 cp += --l;
456 *cp = '\0';
458 return buf;
461 FL char *
462 nodename(int mayoverride)
464 static char *hostname;
465 struct utsname ut;
466 char *hn;
467 #ifdef HAVE_SOCKETS
468 # ifdef HAVE_IPV6
469 struct addrinfo hints, *res;
470 # else
471 struct hostent *hent;
472 # endif
473 #endif
475 if (mayoverride && (hn = value("hostname")) != NULL && *hn != '\0') {
476 if (hostname != NULL)
477 free(hostname);
478 hostname = sstrdup(hn);
479 } else if (hostname == NULL) {
480 uname(&ut);
481 hn = ut.nodename;
482 #ifdef HAVE_SOCKETS
483 # ifdef HAVE_IPV6
484 memset(&hints, 0, sizeof hints);
485 hints.ai_family = AF_UNSPEC;
486 hints.ai_socktype = SOCK_DGRAM; /* dummy */
487 hints.ai_flags = AI_CANONNAME;
488 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
489 if (res->ai_canonname != NULL) {
490 size_t l = strlen(res->ai_canonname);
491 hn = ac_alloc(l + 1);
492 memcpy(hn, res->ai_canonname, l + 1);
494 freeaddrinfo(res);
496 # else
497 hent = gethostbyname(hn);
498 if (hent != NULL) {
499 hn = hent->h_name;
501 # endif
502 #endif
503 hostname = sstrdup(hn);
504 #if defined HAVE_SOCKETS && defined HAVE_IPV6
505 if (hn != ut.nodename)
506 ac_free(hn);
507 #endif
509 return (hostname);
512 FL char *
513 lookup_password_for_token(char const *token)
515 size_t tl;
516 char *var, *cp;
518 tl = strlen(token);
519 var = ac_alloc(tl + 10);
521 memcpy(var, "password-", 9);
522 memcpy(var + 9, token, tl);
523 var[tl + 9] = '\0';
525 if ((cp = value(var)) != NULL)
526 cp = savestr(cp);
527 ac_free(var);
528 return cp;
531 FL char *
532 getrandstring(size_t length)
534 static unsigned char nodedigest[16];
535 static pid_t pid;
536 struct str b64;
537 int fd = -1;
538 char *data, *cp;
539 size_t i;
540 #ifdef HAVE_MD5
541 md5_ctx ctx;
542 #else
543 size_t j;
544 #endif
546 data = ac_alloc(length);
547 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
548 length != (size_t)read(fd, data, length)) {
549 if (pid == 0) {
550 pid = getpid();
551 srand(pid);
552 cp = nodename(0);
553 #ifdef HAVE_MD5
554 md5_init(&ctx);
555 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
556 md5_final(nodedigest, &ctx);
557 #else
558 /* In that case it's only used for boundaries and
559 * Message-Id:s so that srand(3) should suffice */
560 j = strlen(cp) + 1;
561 for (i = 0; i < sizeof(nodedigest); ++i)
562 nodedigest[i] = (unsigned char)(
563 cp[i % j] ^ rand());
564 #endif
566 for (i = 0; i < length; i++)
567 data[i] = (char)(
568 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
569 nodedigest[i % sizeof nodedigest]);
571 if (fd >= 0)
572 close(fd);
574 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
575 ac_free(data);
576 assert(length < b64.l);
577 b64.s[length] = '\0';
578 return b64.s;
581 #ifdef HAVE_MD5
582 FL char *
583 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
585 char const *cp = vp;
586 size_t i, j;
588 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
589 j = i << 1;
590 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
591 hex[++j] = hexchar(cp[i] & 0x0f);
593 return hex;
596 FL char *
597 cram_md5_string(char const *user, char const *pass, char const *b64)
599 struct str in, out;
600 char digest[16], *cp;
601 size_t lu;
603 out.s = NULL;
604 in.s = UNCONST(b64);
605 in.l = strlen(in.s);
606 (void)b64_decode(&out, &in, NULL);
607 assert(out.s != NULL);
609 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
610 digest);
611 free(out.s);
612 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
614 lu = strlen(user);
615 in.l = lu + MD5TOHEX_SIZE +1;
616 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
617 memcpy(in.s, user, lu);
618 in.s[lu] = ' ';
619 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
620 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
621 ac_free(in.s);
622 return out.s;
624 #endif /* HAVE_MD5 */
626 FL enum okay
627 makedir(const char *name)
629 int e;
630 struct stat st;
632 if (mkdir(name, 0700) < 0) {
633 e = errno;
634 if ((e == EEXIST || e == ENOSYS) &&
635 stat(name, &st) == 0 &&
636 (st.st_mode&S_IFMT) == S_IFDIR)
637 return OKAY;
638 return STOP;
640 return OKAY;
643 #ifdef HAVE_FCHDIR
644 FL enum okay
645 cwget(struct cw *cw)
647 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
648 return STOP;
649 if (fchdir(cw->cw_fd) < 0) {
650 close(cw->cw_fd);
651 return STOP;
653 return OKAY;
656 FL enum okay
657 cwret(struct cw *cw)
659 if (fchdir(cw->cw_fd) < 0)
660 return STOP;
661 return OKAY;
664 FL void
665 cwrelse(struct cw *cw)
667 close(cw->cw_fd);
669 #else /* !HAVE_FCHDIR */
670 FL enum okay
671 cwget(struct cw *cw)
673 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
674 return STOP;
675 return OKAY;
678 FL enum okay
679 cwret(struct cw *cw)
681 if (chdir(cw->cw_wd) < 0)
682 return STOP;
683 return OKAY;
686 /*ARGSUSED*/
687 FL void
688 cwrelse(struct cw *cw)
690 (void)cw;
692 #endif /* !HAVE_FCHDIR */
694 FL void
695 makeprint(struct str const *in, struct str *out)
697 static int print_all_chars = -1;
698 char const *inp, *maxp;
699 char *outp;
700 size_t msz, dist;
702 if (print_all_chars == -1)
703 print_all_chars = (value("print-all-chars") != NULL);
705 msz = in->l + 1;
706 out->s = outp = smalloc(msz);
707 inp = in->s;
708 maxp = inp + in->l;
710 if (print_all_chars) {
711 out->l = in->l;
712 memcpy(outp, inp, out->l);
713 goto jleave;
716 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
717 if (mb_cur_max > 1) {
718 char mbb[MB_LEN_MAX + 1];
719 wchar_t wc;
720 int i, n;
721 out->l = 0;
722 while (inp < maxp) {
723 if (*inp & 0200)
724 n = mbtowc(&wc, inp, maxp - inp);
725 else {
726 wc = *inp;
727 n = 1;
729 if (n < 0) {
730 /* FIXME Why mbtowc() resetting here?
731 * FIXME what about ISO 2022-JP plus -- those
732 * FIXME will loose shifts, then!
733 * FIXME THUS - we'd need special "known points"
734 * FIXME to do so - say, after a newline!!
735 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
736 (void)mbtowc(&wc, NULL, mb_cur_max);
737 wc = utf8 ? 0xFFFD : '?';
738 n = 1;
739 } else if (n == 0)
740 n = 1;
741 inp += n;
742 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
743 wc != '\b' && wc != '\t') {
744 if ((wc & ~(wchar_t)037) == 0)
745 wc = utf8 ? 0x2400 | wc : '?';
746 else if (wc == 0177)
747 wc = utf8 ? 0x2421 : '?';
748 else
749 wc = utf8 ? 0x2426 : '?';
751 if ((n = wctomb(mbb, wc)) <= 0)
752 continue;
753 out->l += n;
754 if (out->l >= msz - 1) {
755 dist = outp - out->s;
756 out->s = srealloc(out->s, msz += 32);
757 outp = &out->s[dist];
759 for (i = 0; i < n; i++)
760 *outp++ = mbb[i];
762 } else
763 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
765 int c;
766 while (inp < maxp) {
767 c = *inp++ & 0377;
768 if (!isprint(c) && c != '\n' && c != '\r' &&
769 c != '\b' && c != '\t')
770 c = '?';
771 *outp++ = c;
773 out->l = in->l;
775 jleave:
776 out->s[out->l] = '\0';
779 FL char *
780 prstr(const char *s)
782 struct str in, out;
783 char *rp;
785 in.s = UNCONST(s);
786 in.l = strlen(s);
787 makeprint(&in, &out);
788 rp = salloc(out.l + 1);
789 memcpy(rp, out.s, out.l);
790 rp[out.l] = '\0';
791 free(out.s);
792 return rp;
795 FL int
796 prout(const char *s, size_t sz, FILE *fp)
798 struct str in, out;
799 int n;
801 in.s = UNCONST(s);
802 in.l = sz;
803 makeprint(&in, &out);
804 n = fwrite(out.s, 1, out.l, fp);
805 free(out.s);
806 return n;
809 FL size_t
810 putuc(int u, int c, FILE *fp)
812 size_t rv;
814 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
815 if (utf8 && u & ~(wchar_t)0177) {
816 char mbb[MB_LEN_MAX];
817 int i, n;
818 if ((n = wctomb(mbb, u)) > 0) {
819 rv = wcwidth(u);
820 for (i = 0; i < n; ++i)
821 if (putc(mbb[i] & 0377, fp) == EOF) {
822 rv = 0;
823 break;
825 } else if (n == 0)
826 rv = (putc('\0', fp) != EOF);
827 else
828 rv = 0;
829 } else
830 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
831 rv = (putc(c, fp) != EOF);
832 return rv;
835 FL void
836 time_current_update(struct time_current *tc, bool_t full_update)
838 tc->tc_time = time(NULL);
839 if (full_update) {
840 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
841 memcpy(&tc->tc_local, localtime(&tc->tc_time),
842 sizeof tc->tc_local);
843 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
847 static void
848 _out_of_memory(void)
850 panic("no memory");
853 FL void *
854 (smalloc_safe)(size_t s SMALLOC_DEBUG_ARGS)
856 void *rv;
858 hold_all_sigs();
859 rv = (smalloc)(s SMALLOC_DEBUG_ARGSCALL);
860 rele_all_sigs();
861 return rv;
864 FL void *
865 (srealloc_safe)(void *v, size_t s SMALLOC_DEBUG_ARGS)
867 void *rv;
869 hold_all_sigs();
870 rv = (srealloc)(v, s SMALLOC_DEBUG_ARGSCALL);
871 rele_all_sigs();
872 return rv;
875 #ifdef notyet
876 FL void *
877 (scalloc_safe)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
879 void *rv;
881 hold_all_sigs();
882 rv = (scalloc)(nmemb, size SMALLOC_DEBUG_ARGSCALL);
883 rele_all_sigs();
884 return rv;
886 #endif
888 #ifndef HAVE_DEBUG
889 FL void *
890 smalloc(size_t s SMALLOC_DEBUG_ARGS)
892 void *rv;
894 if (s == 0)
895 s = 1;
896 if ((rv = malloc(s)) == NULL)
897 _out_of_memory();
898 return rv;
901 FL void *
902 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
904 void *rv;
906 if (s == 0)
907 s = 1;
908 if (v == NULL)
909 rv = smalloc(s);
910 else if ((rv = realloc(v, s)) == NULL)
911 _out_of_memory();
912 return rv;
915 FL void *
916 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
918 void *rv;
920 if (size == 0)
921 size = 1;
922 if ((rv = calloc(nmemb, size)) == NULL)
923 _out_of_memory();
924 return rv;
927 #else /* !HAVE_DEBUG */
928 CTA(sizeof(char) == sizeof(ui8_t));
930 # define _HOPE_SIZE (2 * 8 * sizeof(char))
931 # define _HOPE_SET(C) \
932 do {\
933 union ptr __xl, __xu;\
934 struct chunk *__xc;\
935 __xl.p = (C).p;\
936 __xc = __xl.c - 1;\
937 __xu.p = __xc;\
938 (C).cp += 8;\
939 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
940 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
941 __xu.ui8p += __xc->size - 8;\
942 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
943 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
944 } while (0)
945 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
946 # define _HOPE_GET(C,BAD) \
947 do {\
948 union ptr __xl, __xu;\
949 struct chunk *__xc;\
950 ui32_t __i;\
951 __xl.p = (C).p;\
952 __xl.cp -= 8;\
953 (C).cp = __xl.cp;\
954 __xc = __xl.c - 1;\
955 (BAD) = FAL0;\
956 __i = 0;\
957 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
958 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
959 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
960 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
961 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
962 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
963 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
964 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
965 if (__i != 0) {\
966 (BAD) = TRU1;\
967 warn("%p: corrupted lower canary: 0x%02X: %s, line %u",\
968 __xl.p, __i, mdbg_file, mdbg_line);\
970 __xu.p = __xc;\
971 __xu.ui8p += __xc->size - 8;\
972 __i = 0;\
973 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
974 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
975 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
976 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
977 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
978 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
979 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
980 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
981 if (__i != 0) {\
982 (BAD) = TRU1;\
983 warn("%p: corrupted upper canary: 0x%02X: %s, line %u",\
984 __xl.p, __i, mdbg_file, mdbg_line);\
986 if (BAD)\
987 warn(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
988 } while (0)
990 struct chunk {
991 struct chunk *prev;
992 struct chunk *next;
993 char const *file;
994 ui16_t line;
995 ui8_t isfree;
996 ui8_t __dummy;
997 ui32_t size;
1000 union ptr {
1001 void *p;
1002 struct chunk *c;
1003 char *cp;
1004 ui8_t *ui8p;
1007 struct chunk *_mlist, *_mfree;
1009 FL void *
1010 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1012 union ptr p;
1014 if (s == 0)
1015 s = 1;
1016 s += sizeof(struct chunk) + _HOPE_SIZE;
1018 if ((p.p = (malloc)(s)) == NULL)
1019 _out_of_memory();
1020 p.c->prev = NULL;
1021 if ((p.c->next = _mlist) != NULL)
1022 _mlist->prev = p.c;
1023 p.c->file = mdbg_file;
1024 p.c->line = (ui16_t)mdbg_line;
1025 p.c->isfree = FAL0;
1026 p.c->size = (ui32_t)s;
1027 _mlist = p.c++;
1028 _HOPE_SET(p);
1029 return p.p;
1032 FL void *
1033 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1035 union ptr p;
1036 bool_t isbad;
1038 if ((p.p = v) == NULL) {
1039 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1040 goto jleave;
1043 _HOPE_GET(p, isbad);
1044 --p.c;
1045 if (p.c->isfree) {
1046 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1047 "\tLast seen: %s, line %d\n",
1048 mdbg_file, mdbg_line, p.c->file, p.c->line);
1049 goto jforce;
1052 if (p.c == _mlist)
1053 _mlist = p.c->next;
1054 else
1055 p.c->prev->next = p.c->next;
1056 if (p.c->next != NULL)
1057 p.c->next->prev = p.c->prev;
1059 jforce:
1060 if (s == 0)
1061 s = 1;
1062 s += sizeof(struct chunk) + _HOPE_SIZE;
1064 if ((p.p = (realloc)(p.c, s)) == NULL)
1065 _out_of_memory();
1066 p.c->prev = NULL;
1067 if ((p.c->next = _mlist) != NULL)
1068 _mlist->prev = p.c;
1069 p.c->file = mdbg_file;
1070 p.c->line = (ui16_t)mdbg_line;
1071 p.c->isfree = FAL0;
1072 p.c->size = (ui32_t)s;
1073 _mlist = p.c++;
1074 _HOPE_SET(p);
1075 jleave:
1076 return p.p;
1079 FL void *
1080 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1082 union ptr p;
1084 if (size == 0)
1085 size = 1;
1086 if (nmemb == 0)
1087 nmemb = 1;
1088 size *= nmemb;
1089 size += sizeof(struct chunk) + _HOPE_SIZE;
1091 if ((p.p = (malloc)(size)) == NULL)
1092 _out_of_memory();
1093 memset(p.p, 0, size);
1094 p.c->prev = NULL;
1095 if ((p.c->next = _mlist) != NULL)
1096 _mlist->prev = p.c;
1097 p.c->file = mdbg_file;
1098 p.c->line = (ui16_t)mdbg_line;
1099 p.c->isfree = FAL0;
1100 p.c->size = (ui32_t)size;
1101 _mlist = p.c++;
1102 _HOPE_SET(p);
1103 return p.p;
1106 FL void
1107 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1109 union ptr p;
1110 bool_t isbad;
1112 if ((p.p = v) == NULL) {
1113 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1114 goto jleave;
1117 _HOPE_GET(p, isbad);
1118 --p.c;
1119 if (p.c->isfree) {
1120 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1121 "\tLast seen: %s, line %d\n",
1122 mdbg_file, mdbg_line, p.c->file, p.c->line);
1123 goto jleave;
1126 if (p.c == _mlist)
1127 _mlist = p.c->next;
1128 else
1129 p.c->prev->next = p.c->next;
1130 if (p.c->next != NULL)
1131 p.c->next->prev = p.c->prev;
1132 p.c->isfree = TRU1;
1134 if (options & OPT_DEBUG) {
1135 p.c->next = _mfree;
1136 _mfree = p.c;
1137 } else
1138 (free)(p.c);
1139 jleave:
1143 FL void
1144 smemreset(void)
1146 union ptr p;
1147 size_t c = 0, s = 0;
1149 for (p.c = _mfree; p.c != NULL;) {
1150 void *vp = p.c;
1151 ++c;
1152 s += p.c->size;
1153 p.c = p.c->next;
1154 (free)(vp);
1156 _mfree = NULL;
1158 if (options & OPT_DEBUG)
1159 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1160 c, s);
1163 FL int
1164 smemtrace(void *v)
1166 /* For _HOPE_GET() */
1167 char const * const mdbg_file = "smemtrace()";
1168 int const mdbg_line = -1;
1170 FILE *fp;
1171 char *cp;
1172 union ptr p, xp;
1173 bool_t isbad;
1174 size_t lines;
1176 v = (void*)0x1;
1177 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1178 perror("tmpfile");
1179 goto jleave;
1181 rm(cp);
1182 Ftfree(&cp);
1184 fprintf(fp, "Currently allocated memory chunks:\n");
1185 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1186 xp = p;
1187 ++xp.c;
1188 _HOPE_GET_TRACE(xp, isbad);
1189 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1190 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1191 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1194 if (options & OPT_DEBUG) {
1195 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1196 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1197 xp = p;
1198 ++xp.c;
1199 _HOPE_GET_TRACE(xp, isbad);
1200 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1201 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1202 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1206 page_or_print(fp, lines);
1207 Fclose(fp);
1208 v = NULL;
1209 jleave:
1210 return (v != NULL);
1213 # ifdef MEMCHECK
1214 FL bool_t
1215 _smemcheck(char const *mdbg_file, int mdbg_line)
1217 union ptr p, xp;
1218 bool_t anybad = FAL0, isbad;
1219 size_t lines;
1221 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1222 xp = p;
1223 ++xp.c;
1224 _HOPE_GET_TRACE(xp, isbad);
1225 if (isbad) {
1226 anybad = TRU1;
1227 fprintf(stderr,
1228 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1229 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1230 p.c->file, p.c->line);
1234 if (options & OPT_DEBUG) {
1235 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1236 xp = p;
1237 ++xp.c;
1238 _HOPE_GET_TRACE(xp, isbad);
1239 if (isbad) {
1240 anybad = TRU1;
1241 fprintf(stderr,
1242 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1243 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1244 p.c->file, p.c->line);
1248 return anybad;
1250 # endif /* MEMCHECK */
1251 #endif /* HAVE_DEBUG */
1253 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */