Move get_pager() cmd1.c -> auxlily.c
[s-mailx.git] / auxlily.c
blob1d603382ab5ac69ad72a9f732b6b043e5a30d657
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 /* {hold,rele}_sigs() */
67 static size_t _hold_sigdepth;
68 static sigset_t _hold_nset, _hold_oset;
70 FL void
71 panic(char const *format, ...)
73 va_list ap;
75 fprintf(stderr, tr(1, "Panic: "));
77 va_start(ap, format);
78 vfprintf(stderr, format, ap);
79 va_end(ap);
81 fputs("\n", stderr);
82 fflush(stderr);
83 exit(EXIT_ERR);
86 #ifdef HAVE_DEBUG
87 FL void
88 warn(char const *format, ...)
90 va_list ap;
92 fprintf(stderr, tr(1, "Panic: "));
94 va_start(ap, format);
95 vfprintf(stderr, format, ap);
96 va_end(ap);
98 fputs("\n", stderr);
99 fflush(stderr);
101 #endif
103 FL sighandler_type
104 safe_signal(int signum, sighandler_type handler)
106 struct sigaction nact, oact;
108 nact.sa_handler = handler;
109 sigemptyset(&nact.sa_mask);
110 nact.sa_flags = 0;
111 #ifdef SA_RESTART
112 nact.sa_flags |= SA_RESTART;
113 #endif
114 return ((sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler);
117 FL void
118 hold_all_sigs(void)
120 if (_alls_depth++ == 0) {
121 sigfillset(&_alls_nset);
122 sigdelset(&_alls_nset, SIGKILL);
123 sigdelset(&_alls_nset, SIGSTOP);
124 sigdelset(&_alls_nset, SIGCHLD);
125 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
129 FL void
130 rele_all_sigs(void)
132 if (--_alls_depth == 0)
133 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
136 FL void
137 hold_sigs(void)
139 if (_hold_sigdepth++ == 0) {
140 sigemptyset(&_hold_nset);
141 sigaddset(&_hold_nset, SIGHUP);
142 sigaddset(&_hold_nset, SIGINT);
143 sigaddset(&_hold_nset, SIGQUIT);
144 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
148 FL void
149 rele_sigs(void)
151 if (--_hold_sigdepth == 0)
152 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
156 * Touch the named message by setting its MTOUCH flag.
157 * Touched messages have the effect of not being sent
158 * back to the system mailbox on exit.
160 FL void
161 touch(struct message *mp)
164 mp->m_flag |= MTOUCH;
165 if ((mp->m_flag & MREAD) == 0)
166 mp->m_flag |= MREAD|MSTATUS;
170 * Test to see if the passed file name is a directory.
171 * Return true if it is.
173 FL int
174 is_dir(char const *name)
176 struct stat sbuf;
178 if (stat(name, &sbuf) < 0)
179 return(0);
180 return(S_ISDIR(sbuf.st_mode));
184 * Count the number of arguments in the given string raw list.
186 FL int
187 argcount(char **argv)
189 char **ap;
191 for (ap = argv; *ap++ != NULL;)
193 return ap - argv - 1;
196 FL char *
197 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
199 int col_orig = col, n, sz;
200 char *nb, *np;
202 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
203 while (*cp) {
204 #ifdef HAVE_WCWIDTH
205 if (mb_cur_max > 1) {
206 wchar_t wc;
208 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
209 n = sz = 1;
210 } else {
211 if ((n = wcwidth(wc)) < 0)
212 n = 1;
214 } else
215 #endif
217 n = sz = 1;
219 if (n > col)
220 break;
221 col -= n;
222 if (sz == 1 && spacechar(*cp)) {
223 *np++ = ' ';
224 cp++;
225 } else
226 while (sz--)
227 *np++ = *cp++;
230 if (fill && col != 0) {
231 if (fill > 0) {
232 memmove(nb + col, nb, (size_t)(np - nb));
233 memset(nb, ' ', col);
234 } else
235 memset(np, ' ', col);
236 np += col;
237 col = 0;
240 *np = '\0';
241 if (cols_decr_used_or_null != NULL)
242 *cols_decr_used_or_null -= col_orig - col;
243 return nb;
246 FL char const *
247 get_pager(void)
249 char const *cp;
251 cp = ok_vlook(PAGER);
252 if (cp == NULL || *cp == '\0')
253 cp = XPAGER;
254 return cp;
257 FL size_t
258 paging_seems_sensible(void)
260 size_t ret = 0;
261 char const *cp;
263 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
264 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
265 return ret;
268 FL void
269 page_or_print(FILE *fp, size_t lines)
271 size_t rows;
272 int c;
274 fflush_rewind(fp);
276 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
277 while ((c = getc(fp)) != EOF)
278 if (c == '\n' && ++lines > rows)
279 break;
280 rewind(fp);
283 if (rows != 0 && lines >= rows)
284 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
285 else
286 while ((c = getc(fp)) != EOF)
287 putchar(c);
290 FL enum protocol
291 which_protocol(const char *name)
293 register const char *cp;
294 char *np;
295 size_t sz;
296 struct stat st;
297 enum protocol p;
299 if (name[0] == '%' && name[1] == ':')
300 name += 2;
301 for (cp = name; *cp && *cp != ':'; cp++)
302 if (!alnumchar(*cp&0377))
303 goto file;
304 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
305 if (strncmp(name, "pop3://", 7) == 0)
306 #ifdef HAVE_POP3
307 return PROTO_POP3;
308 #else
309 fprintf(stderr,
310 tr(216, "No POP3 support compiled in.\n"));
311 #endif
312 if (strncmp(name, "pop3s://", 8) == 0)
313 #ifdef HAVE_SSL
314 return PROTO_POP3;
315 #else
316 fprintf(stderr,
317 tr(225, "No SSL support compiled in.\n"));
318 #endif
319 if (strncmp(name, "imap://", 7) == 0)
320 #ifdef HAVE_IMAP
321 return PROTO_IMAP;
322 #else
323 fprintf(stderr,
324 tr(269, "No IMAP support compiled in.\n"));
325 #endif
326 if (strncmp(name, "imaps://", 8) == 0)
327 #ifdef HAVE_SSL
328 return PROTO_IMAP;
329 #else
330 fprintf(stderr,
331 tr(225, "No SSL support compiled in.\n"));
332 #endif
333 return PROTO_UNKNOWN;
334 } else {
335 /* TODO This is the de facto maildir code and thus belongs
336 * TODO into maildir! */
337 file: p = PROTO_FILE;
338 np = ac_alloc((sz = strlen(name)) + 5);
339 memcpy(np, name, sz + 1);
340 if (stat(name, &st) == 0) {
341 if (S_ISDIR(st.st_mode)) {
342 strcpy(&np[sz], "/tmp");
343 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
344 strcpy(&np[sz], "/new");
345 if (stat(np, &st) == 0 &&
346 S_ISDIR(st.st_mode)) {
347 strcpy(&np[sz], "/cur");
348 if (stat(np, &st) == 0 &&
349 S_ISDIR(st.st_mode))
350 p = PROTO_MAILDIR;
354 } else {
355 strcpy(&np[sz], ".gz");
356 if (stat(np, &st) < 0) {
357 strcpy(&np[sz], ".bz2");
358 if (stat(np, &st) < 0) {
359 if ((cp = ok_vlook(newfolders)) != NULL &&
360 strcmp(cp, "maildir") == 0)
361 p = PROTO_MAILDIR;
365 ac_free(np);
366 return p;
370 FL ui32_t
371 torek_hash(char const *name)
373 /* Chris Torek's hash.
374 * NOTE: need to change *at least* create-okey-map.pl when changing the
375 * algorithm!! */
376 ui32_t h = 0;
378 while (*name != '\0') {
379 h *= 33;
380 h += *name++;
382 return h;
385 FL unsigned
386 pjw(const char *cp)
388 unsigned h = 0, g;
390 cp--;
391 while (*++cp) {
392 h = (h << 4 & 0xffffffff) + (*cp&0377);
393 if ((g = h & 0xf0000000) != 0) {
394 h = h ^ g >> 24;
395 h = h ^ g;
398 return h;
401 FL long
402 nextprime(long n)
404 const long primes[] = {
405 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
406 131071, 262139, 524287, 1048573, 2097143, 4194301,
407 8388593, 16777213, 33554393, 67108859, 134217689,
408 268435399, 536870909, 1073741789, 2147483647
410 long mprime = 7;
411 size_t i;
413 for (i = 0; i < sizeof primes / sizeof *primes; i++)
414 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
415 n < 262144 ? n*2 : n))
416 break;
417 if (i == sizeof primes / sizeof *primes)
418 mprime = n; /* not so prime, but better than failure */
419 return mprime;
422 FL int
423 expand_shell_escape(char const **s, bool_t use_nail_extensions)
425 char const *xs = *s;
426 int c, n;
428 if ((c = *xs & 0xFF) == '\0')
429 goto jleave;
430 ++xs;
431 if (c != '\\')
432 goto jleave;
434 switch ((c = *xs & 0xFF)) {
435 case '\\': break;
436 case 'a': c = '\a'; break;
437 case 'b': c = '\b'; break;
438 case 'c': c = PROMPT_STOP; break;
439 case 'f': c = '\f'; break;
440 case 'n': c = '\n'; break;
441 case 'r': c = '\r'; break;
442 case 't': c = '\t'; break;
443 case 'v': c = '\v'; break;
444 case '0':
445 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
446 c <<= 3;
447 c |= *xs - '0';
449 goto jleave;
450 /* S-nail extension for nice (get)prompt(()) support */
451 case '&':
452 case '?':
453 case '$':
454 case '@':
455 if (use_nail_extensions) {
456 switch (c) {
457 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
458 case '?': c = exec_last_comm_error ? '1' : '0'; break;
459 case '$': c = PROMPT_DOLLAR; break;
460 case '@': c = PROMPT_AT; break;
462 break;
464 /* FALLTHRU */
465 case '\0':
466 /* A sole <backslash> at EOS is treated as-is! */
467 /* FALLTHRU */
468 default:
469 c = '\\';
470 goto jleave;
472 ++xs;
473 jleave:
474 *s = xs;
475 return c;
478 FL char *
479 getprompt(void)
481 static char buf[PROMPT_BUFFER_SIZE];
483 char *cp = buf;
484 char const *ccp;
486 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
487 goto jleave;
489 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
490 char const *a;
491 size_t l;
492 int c = expand_shell_escape(&ccp, TRU1);
494 if (c > 0) {
495 *cp = (char)c;
496 continue;
498 if (c == 0 || c == PROMPT_STOP)
499 break;
501 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
502 if (a == NULL)
503 a = "";
504 l = strlen(a);
505 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
506 *cp++ = '?';
507 else {
508 memcpy(cp, a, l);
509 cp += --l;
512 jleave:
513 *cp = '\0';
514 return buf;
517 FL char *
518 nodename(int mayoverride)
520 static char *hostname;
521 struct utsname ut;
522 char *hn;
523 #ifdef HAVE_SOCKETS
524 # ifdef HAVE_IPV6
525 struct addrinfo hints, *res;
526 # else
527 struct hostent *hent;
528 # endif
529 #endif
531 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
532 if (hostname != NULL)
533 free(hostname);
534 hostname = sstrdup(hn);
535 } else if (hostname == NULL) {
536 uname(&ut);
537 hn = ut.nodename;
538 #ifdef HAVE_SOCKETS
539 # ifdef HAVE_IPV6
540 memset(&hints, 0, sizeof hints);
541 hints.ai_family = AF_UNSPEC;
542 hints.ai_socktype = SOCK_DGRAM; /* dummy */
543 hints.ai_flags = AI_CANONNAME;
544 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
545 if (res->ai_canonname != NULL) {
546 size_t l = strlen(res->ai_canonname);
547 hn = ac_alloc(l + 1);
548 memcpy(hn, res->ai_canonname, l + 1);
550 freeaddrinfo(res);
552 # else
553 hent = gethostbyname(hn);
554 if (hent != NULL) {
555 hn = hent->h_name;
557 # endif
558 #endif
559 hostname = sstrdup(hn);
560 #if defined HAVE_SOCKETS && defined HAVE_IPV6
561 if (hn != ut.nodename)
562 ac_free(hn);
563 #endif
565 return (hostname);
568 FL char *
569 lookup_password_for_token(char const *token)
571 size_t tl;
572 char *var, *cp;
574 tl = strlen(token);
575 var = ac_alloc(tl + 10);
577 memcpy(var, "password-", 9);
578 memcpy(var + 9, token, tl);
579 var[tl + 9] = '\0';
581 if ((cp = vok_vlook(var)) != NULL)
582 cp = savestr(cp);
583 ac_free(var);
584 return cp;
587 FL char *
588 getrandstring(size_t length)
590 static unsigned char nodedigest[16];
591 static pid_t pid;
592 struct str b64;
593 int fd = -1;
594 char *data, *cp;
595 size_t i;
596 #ifdef HAVE_MD5
597 md5_ctx ctx;
598 #else
599 size_t j;
600 #endif
602 data = ac_alloc(length);
603 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
604 length != (size_t)read(fd, data, length)) {
605 if (pid == 0) {
606 pid = getpid();
607 srand(pid);
608 cp = nodename(0);
609 #ifdef HAVE_MD5
610 md5_init(&ctx);
611 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
612 md5_final(nodedigest, &ctx);
613 #else
614 /* In that case it's only used for boundaries and
615 * Message-Id:s so that srand(3) should suffice */
616 j = strlen(cp) + 1;
617 for (i = 0; i < sizeof(nodedigest); ++i)
618 nodedigest[i] = (unsigned char)(
619 cp[i % j] ^ rand());
620 #endif
622 for (i = 0; i < length; i++)
623 data[i] = (char)(
624 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
625 nodedigest[i % sizeof nodedigest]);
627 if (fd >= 0)
628 close(fd);
630 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
631 ac_free(data);
632 assert(length < b64.l);
633 b64.s[length] = '\0';
634 return b64.s;
637 #ifdef HAVE_MD5
638 FL char *
639 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
641 char const *cp = vp;
642 size_t i, j;
644 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
645 j = i << 1;
646 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
647 hex[++j] = hexchar(cp[i] & 0x0f);
649 return hex;
652 FL char *
653 cram_md5_string(char const *user, char const *pass, char const *b64)
655 struct str in, out;
656 char digest[16], *cp;
657 size_t lu;
659 out.s = NULL;
660 in.s = UNCONST(b64);
661 in.l = strlen(in.s);
662 (void)b64_decode(&out, &in, NULL);
663 assert(out.s != NULL);
665 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
666 digest);
667 free(out.s);
668 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
670 lu = strlen(user);
671 in.l = lu + MD5TOHEX_SIZE +1;
672 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
673 memcpy(in.s, user, lu);
674 in.s[lu] = ' ';
675 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
676 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
677 ac_free(in.s);
678 return out.s;
680 #endif /* HAVE_MD5 */
682 FL enum okay
683 makedir(const char *name)
685 int e;
686 struct stat st;
688 if (mkdir(name, 0700) < 0) {
689 e = errno;
690 if ((e == EEXIST || e == ENOSYS) &&
691 stat(name, &st) == 0 &&
692 (st.st_mode&S_IFMT) == S_IFDIR)
693 return OKAY;
694 return STOP;
696 return OKAY;
699 #ifdef HAVE_FCHDIR
700 FL enum okay
701 cwget(struct cw *cw)
703 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
704 return STOP;
705 if (fchdir(cw->cw_fd) < 0) {
706 close(cw->cw_fd);
707 return STOP;
709 return OKAY;
712 FL enum okay
713 cwret(struct cw *cw)
715 if (fchdir(cw->cw_fd) < 0)
716 return STOP;
717 return OKAY;
720 FL void
721 cwrelse(struct cw *cw)
723 close(cw->cw_fd);
725 #else /* !HAVE_FCHDIR */
726 FL enum okay
727 cwget(struct cw *cw)
729 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
730 return STOP;
731 return OKAY;
734 FL enum okay
735 cwret(struct cw *cw)
737 if (chdir(cw->cw_wd) < 0)
738 return STOP;
739 return OKAY;
742 /*ARGSUSED*/
743 FL void
744 cwrelse(struct cw *cw)
746 (void)cw;
748 #endif /* !HAVE_FCHDIR */
750 FL void
751 makeprint(struct str const *in, struct str *out)
753 static int print_all_chars = -1;
754 char const *inp, *maxp;
755 char *outp;
756 size_t msz;
758 if (print_all_chars == -1)
759 print_all_chars = ok_blook(print_all_chars);
761 msz = in->l + 1;
762 out->s = outp = smalloc(msz);
763 inp = in->s;
764 maxp = inp + in->l;
766 if (print_all_chars) {
767 out->l = in->l;
768 memcpy(outp, inp, out->l);
769 goto jleave;
772 #ifdef HAVE_C90AMEND1
773 if (mb_cur_max > 1) {
774 char mbb[MB_LEN_MAX + 1];
775 wchar_t wc;
776 int i, n;
777 size_t dist;
779 out->l = 0;
780 while (inp < maxp) {
781 if (*inp & 0200)
782 n = mbtowc(&wc, inp, maxp - inp);
783 else {
784 wc = *inp;
785 n = 1;
787 if (n < 0) {
788 /* FIXME Why mbtowc() resetting here?
789 * FIXME what about ISO 2022-JP plus -- those
790 * FIXME will loose shifts, then!
791 * FIXME THUS - we'd need special "known points"
792 * FIXME to do so - say, after a newline!!
793 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
794 (void)mbtowc(&wc, NULL, mb_cur_max);
795 wc = utf8 ? 0xFFFD : '?';
796 n = 1;
797 } else if (n == 0)
798 n = 1;
799 inp += n;
800 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
801 wc != '\b' && wc != '\t') {
802 if ((wc & ~(wchar_t)037) == 0)
803 wc = utf8 ? 0x2400 | wc : '?';
804 else if (wc == 0177)
805 wc = utf8 ? 0x2421 : '?';
806 else
807 wc = utf8 ? 0x2426 : '?';
809 if ((n = wctomb(mbb, wc)) <= 0)
810 continue;
811 out->l += n;
812 if (out->l >= msz - 1) {
813 dist = outp - out->s;
814 out->s = srealloc(out->s, msz += 32);
815 outp = &out->s[dist];
817 for (i = 0; i < n; i++)
818 *outp++ = mbb[i];
820 } else
821 #endif /* C90AMEND1 */
823 int c;
824 while (inp < maxp) {
825 c = *inp++ & 0377;
826 if (!isprint(c) && c != '\n' && c != '\r' &&
827 c != '\b' && c != '\t')
828 c = '?';
829 *outp++ = c;
831 out->l = in->l;
833 jleave:
834 out->s[out->l] = '\0';
837 FL char *
838 prstr(const char *s)
840 struct str in, out;
841 char *rp;
843 in.s = UNCONST(s);
844 in.l = strlen(s);
845 makeprint(&in, &out);
846 rp = salloc(out.l + 1);
847 memcpy(rp, out.s, out.l);
848 rp[out.l] = '\0';
849 free(out.s);
850 return rp;
853 FL int
854 prout(const char *s, size_t sz, FILE *fp)
856 struct str in, out;
857 int n;
859 in.s = UNCONST(s);
860 in.l = sz;
861 makeprint(&in, &out);
862 n = fwrite(out.s, 1, out.l, fp);
863 free(out.s);
864 return n;
867 FL size_t
868 putuc(int u, int c, FILE *fp)
870 size_t rv;
871 UNUSED(u);
873 #ifdef HAVE_C90AMEND1
874 if (utf8 && (u & ~(wchar_t)0177)) {
875 char mbb[MB_LEN_MAX];
876 int i, n;
877 if ((n = wctomb(mbb, u)) > 0) {
878 rv = wcwidth(u);
879 for (i = 0; i < n; ++i)
880 if (putc(mbb[i] & 0377, fp) == EOF) {
881 rv = 0;
882 break;
884 } else if (n == 0)
885 rv = (putc('\0', fp) != EOF);
886 else
887 rv = 0;
888 } else
889 #endif
890 rv = (putc(c, fp) != EOF);
891 return rv;
894 FL void
895 time_current_update(struct time_current *tc, bool_t full_update)
897 tc->tc_time = time(NULL);
898 if (full_update) {
899 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
900 memcpy(&tc->tc_local, localtime(&tc->tc_time),
901 sizeof tc->tc_local);
902 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
906 static void
907 _out_of_memory(void)
909 panic("no memory");
912 FL void *
913 (smalloc_safe)(size_t s SMALLOC_DEBUG_ARGS)
915 void *rv;
917 hold_all_sigs();
918 rv = (smalloc)(s SMALLOC_DEBUG_ARGSCALL);
919 rele_all_sigs();
920 return rv;
923 FL void *
924 (srealloc_safe)(void *v, size_t s SMALLOC_DEBUG_ARGS)
926 void *rv;
928 hold_all_sigs();
929 rv = (srealloc)(v, s SMALLOC_DEBUG_ARGSCALL);
930 rele_all_sigs();
931 return rv;
934 #ifdef notyet
935 FL void *
936 (scalloc_safe)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
938 void *rv;
940 hold_all_sigs();
941 rv = (scalloc)(nmemb, size SMALLOC_DEBUG_ARGSCALL);
942 rele_all_sigs();
943 return rv;
945 #endif
947 #ifndef HAVE_DEBUG
948 FL void *
949 smalloc(size_t s SMALLOC_DEBUG_ARGS)
951 void *rv;
953 if (s == 0)
954 s = 1;
955 if ((rv = malloc(s)) == NULL)
956 _out_of_memory();
957 return rv;
960 FL void *
961 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
963 void *rv;
965 if (s == 0)
966 s = 1;
967 if (v == NULL)
968 rv = smalloc(s);
969 else if ((rv = realloc(v, s)) == NULL)
970 _out_of_memory();
971 return rv;
974 FL void *
975 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
977 void *rv;
979 if (size == 0)
980 size = 1;
981 if ((rv = calloc(nmemb, size)) == NULL)
982 _out_of_memory();
983 return rv;
986 #else /* !HAVE_DEBUG */
987 CTA(sizeof(char) == sizeof(ui8_t));
989 # define _HOPE_SIZE (2 * 8 * sizeof(char))
990 # define _HOPE_SET(C) \
991 do {\
992 union ptr __xl, __xu;\
993 struct chunk *__xc;\
994 __xl.p = (C).p;\
995 __xc = __xl.c - 1;\
996 __xu.p = __xc;\
997 (C).cp += 8;\
998 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
999 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1000 __xu.ui8p += __xc->size - 8;\
1001 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1002 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1003 } while (0)
1004 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1005 # define _HOPE_GET(C,BAD) \
1006 do {\
1007 union ptr __xl, __xu;\
1008 struct chunk *__xc;\
1009 ui32_t __i;\
1010 __xl.p = (C).p;\
1011 __xl.cp -= 8;\
1012 (C).cp = __xl.cp;\
1013 __xc = __xl.c - 1;\
1014 (BAD) = FAL0;\
1015 __i = 0;\
1016 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1017 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1018 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1019 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1020 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1021 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1022 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1023 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1024 if (__i != 0) {\
1025 (BAD) = TRU1;\
1026 warn("%p: corrupted lower canary: 0x%02X: %s, line %u",\
1027 __xl.p, __i, mdbg_file, mdbg_line);\
1029 __xu.p = __xc;\
1030 __xu.ui8p += __xc->size - 8;\
1031 __i = 0;\
1032 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1033 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1034 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1035 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1036 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1037 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1038 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1039 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1040 if (__i != 0) {\
1041 (BAD) = TRU1;\
1042 warn("%p: corrupted upper canary: 0x%02X: %s, line %u",\
1043 __xl.p, __i, mdbg_file, mdbg_line);\
1045 if (BAD)\
1046 warn(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1047 } while (0)
1049 struct chunk {
1050 struct chunk *prev;
1051 struct chunk *next;
1052 char const *file;
1053 ui16_t line;
1054 ui8_t isfree;
1055 ui8_t __dummy;
1056 ui32_t size;
1059 union ptr {
1060 void *p;
1061 struct chunk *c;
1062 char *cp;
1063 ui8_t *ui8p;
1066 struct chunk *_mlist, *_mfree;
1068 FL void *
1069 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1071 union ptr p;
1073 if (s == 0)
1074 s = 1;
1075 s += sizeof(struct chunk) + _HOPE_SIZE;
1077 if ((p.p = (malloc)(s)) == NULL)
1078 _out_of_memory();
1079 p.c->prev = NULL;
1080 if ((p.c->next = _mlist) != NULL)
1081 _mlist->prev = p.c;
1082 p.c->file = mdbg_file;
1083 p.c->line = (ui16_t)mdbg_line;
1084 p.c->isfree = FAL0;
1085 p.c->size = (ui32_t)s;
1086 _mlist = p.c++;
1087 _HOPE_SET(p);
1088 return p.p;
1091 FL void *
1092 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1094 union ptr p;
1095 bool_t isbad;
1097 if ((p.p = v) == NULL) {
1098 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1099 goto jleave;
1102 _HOPE_GET(p, isbad);
1103 --p.c;
1104 if (p.c->isfree) {
1105 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1106 "\tLast seen: %s, line %d\n",
1107 mdbg_file, mdbg_line, p.c->file, p.c->line);
1108 goto jforce;
1111 if (p.c == _mlist)
1112 _mlist = p.c->next;
1113 else
1114 p.c->prev->next = p.c->next;
1115 if (p.c->next != NULL)
1116 p.c->next->prev = p.c->prev;
1118 jforce:
1119 if (s == 0)
1120 s = 1;
1121 s += sizeof(struct chunk) + _HOPE_SIZE;
1123 if ((p.p = (realloc)(p.c, s)) == NULL)
1124 _out_of_memory();
1125 p.c->prev = NULL;
1126 if ((p.c->next = _mlist) != NULL)
1127 _mlist->prev = p.c;
1128 p.c->file = mdbg_file;
1129 p.c->line = (ui16_t)mdbg_line;
1130 p.c->isfree = FAL0;
1131 p.c->size = (ui32_t)s;
1132 _mlist = p.c++;
1133 _HOPE_SET(p);
1134 jleave:
1135 return p.p;
1138 FL void *
1139 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1141 union ptr p;
1143 if (size == 0)
1144 size = 1;
1145 if (nmemb == 0)
1146 nmemb = 1;
1147 size *= nmemb;
1148 size += sizeof(struct chunk) + _HOPE_SIZE;
1150 if ((p.p = (malloc)(size)) == NULL)
1151 _out_of_memory();
1152 memset(p.p, 0, size);
1153 p.c->prev = NULL;
1154 if ((p.c->next = _mlist) != NULL)
1155 _mlist->prev = p.c;
1156 p.c->file = mdbg_file;
1157 p.c->line = (ui16_t)mdbg_line;
1158 p.c->isfree = FAL0;
1159 p.c->size = (ui32_t)size;
1160 _mlist = p.c++;
1161 _HOPE_SET(p);
1162 return p.p;
1165 FL void
1166 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1168 union ptr p;
1169 bool_t isbad;
1171 if ((p.p = v) == NULL) {
1172 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1173 goto jleave;
1176 _HOPE_GET(p, isbad);
1177 --p.c;
1178 if (p.c->isfree) {
1179 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1180 "\tLast seen: %s, line %d\n",
1181 mdbg_file, mdbg_line, p.c->file, p.c->line);
1182 goto jleave;
1185 if (p.c == _mlist)
1186 _mlist = p.c->next;
1187 else
1188 p.c->prev->next = p.c->next;
1189 if (p.c->next != NULL)
1190 p.c->next->prev = p.c->prev;
1191 p.c->isfree = TRU1;
1193 if (options & OPT_DEBUG) {
1194 p.c->next = _mfree;
1195 _mfree = p.c;
1196 } else
1197 (free)(p.c);
1198 jleave:
1202 FL void
1203 smemreset(void)
1205 union ptr p;
1206 size_t c = 0, s = 0;
1208 for (p.c = _mfree; p.c != NULL;) {
1209 void *vp = p.c;
1210 ++c;
1211 s += p.c->size;
1212 p.c = p.c->next;
1213 (free)(vp);
1215 _mfree = NULL;
1217 if (options & OPT_DEBUG)
1218 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1219 c, s);
1222 FL int
1223 smemtrace(void *v)
1225 /* For _HOPE_GET() */
1226 char const * const mdbg_file = "smemtrace()";
1227 int const mdbg_line = -1;
1229 FILE *fp;
1230 char *cp;
1231 union ptr p, xp;
1232 bool_t isbad;
1233 size_t lines;
1235 v = (void*)0x1;
1236 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1237 perror("tmpfile");
1238 goto jleave;
1240 rm(cp);
1241 Ftfree(&cp);
1243 fprintf(fp, "Currently allocated memory chunks:\n");
1244 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1245 xp = p;
1246 ++xp.c;
1247 _HOPE_GET_TRACE(xp, isbad);
1248 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1249 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1250 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1253 if (options & OPT_DEBUG) {
1254 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1255 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1256 xp = p;
1257 ++xp.c;
1258 _HOPE_GET_TRACE(xp, isbad);
1259 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1260 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1261 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1265 page_or_print(fp, lines);
1266 Fclose(fp);
1267 v = NULL;
1268 jleave:
1269 return (v != NULL);
1272 # ifdef MEMCHECK
1273 FL bool_t
1274 _smemcheck(char const *mdbg_file, int mdbg_line)
1276 union ptr p, xp;
1277 bool_t anybad = FAL0, isbad;
1278 size_t lines;
1280 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1281 xp = p;
1282 ++xp.c;
1283 _HOPE_GET_TRACE(xp, isbad);
1284 if (isbad) {
1285 anybad = TRU1;
1286 fprintf(stderr,
1287 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1288 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1289 p.c->file, p.c->line);
1293 if (options & OPT_DEBUG) {
1294 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1295 xp = p;
1296 ++xp.c;
1297 _HOPE_GET_TRACE(xp, isbad);
1298 if (isbad) {
1299 anybad = TRU1;
1300 fprintf(stderr,
1301 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1302 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1303 p.c->file, p.c->line);
1307 return anybad;
1309 # endif /* MEMCHECK */
1310 #endif /* HAVE_DEBUG */
1312 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */