NEWS: update for s-nail-14_5_2-mimeheader.patch
[s-mailx.git] / auxlily.c
blob747c581b68c4c744475a940f0e6b2a892b29b295
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 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
63 #ifdef HAVE_COLOUR
64 static char * _colour_iso6429(char const *wish);
65 #endif
67 /* {hold,rele}_all_sigs() */
68 static size_t _alls_depth;
69 static sigset_t _alls_nset, _alls_oset;
71 /* {hold,rele}_sigs() */
72 static size_t _hold_sigdepth;
73 static sigset_t _hold_nset, _hold_oset;
75 #ifdef HAVE_COLOUR
76 static char *
77 _colour_iso6429(char const *wish)
79 char const * const wish_orig = wish;
80 char *xwish, *cp, cfg[3] = {0, 0, 0};
82 /* Since we use salloc(), reuse the strcomma() buffer also for the return
83 * value, ensure we have enough room for that */
85 size_t i = strlen(wish) + 1;
86 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
87 memcpy(xwish, wish, i);
88 wish = xwish;
91 /* Iterate over the colour spec */
92 while ((cp = strcomma(&xwish, TRU1)) != NULL) {
93 char *y, *x = strchr(cp, '=');
94 if (x == NULL) {
95 jbail:
96 fprintf(stderr, tr(527,
97 "Invalid colour specification \"%s\": >>> %s <<<\n"),
98 wish_orig, cp);
99 continue;
101 *x++ = '\0';
103 /* TODO convert the ft/fg/bg parser into a table-based one! */
104 if (!asccasecmp(cp, "ft")) {
105 if (!asccasecmp(x, "bold"))
106 cfg[0] = '1';
107 else if (!asccasecmp(x, "inverse"))
108 cfg[0] = '7';
109 else if (!asccasecmp(x, "underline"))
110 cfg[0] = '4';
111 else
112 goto jbail;
113 } else if (!asccasecmp(cp, "fg")) {
114 y = cfg + 1;
115 goto jiter_colour;
116 } else if (!asccasecmp(cp, "bg")) {
117 y = cfg + 2;
118 jiter_colour:
119 if (!asccasecmp(x, "black"))
120 *y = '0';
121 else if (!asccasecmp(x, "blue"))
122 *y = '4';
123 else if (!asccasecmp(x, "green"))
124 *y = '2';
125 else if (!asccasecmp(x, "red"))
126 *y = '1';
127 else if (!asccasecmp(x, "brown"))
128 *y = '3';
129 else if (!asccasecmp(x, "magenta"))
130 *y = '5';
131 else if (!asccasecmp(x, "cyan"))
132 *y = '6';
133 else if (!asccasecmp(x, "white"))
134 *y = '7';
135 else
136 goto jbail;
137 } else
138 goto jbail;
141 /* Restore our salloc() buffer, create return value */
142 xwish = UNCONST(wish);
143 if (cfg[0] || cfg[1] || cfg[2]) {
144 xwish[0] = '\033';
145 xwish[1] = '[';
146 xwish += 2;
147 if (cfg[0])
148 *xwish++ = cfg[0];
149 if (cfg[1]) {
150 if (cfg[0])
151 *xwish++ = ';';
152 xwish[0] = '3';
153 xwish[1] = cfg[1];
154 xwish += 2;
156 if (cfg[2]) {
157 if (cfg[0] || cfg[1])
158 *xwish++ = ';';
159 xwish[0] = '4';
160 xwish[1] = cfg[2];
161 xwish += 2;
163 *xwish++ = 'm';
165 *xwish = '\0';
166 return UNCONST(wish);
168 #endif /* HAVE_COLOUR */
170 FL void
171 panic(char const *format, ...)
173 va_list ap;
175 fprintf(stderr, tr(1, "Panic: "));
177 va_start(ap, format);
178 vfprintf(stderr, format, ap);
179 va_end(ap);
181 fputs("\n", stderr);
182 fflush(stderr);
183 abort(); /* Was exit(EXIT_ERR); for a while, but no */
186 FL void
187 alert(char const *format, ...)
189 va_list ap;
191 fprintf(stderr, tr(1, "Panic: "));
193 va_start(ap, format);
194 vfprintf(stderr, format, ap);
195 va_end(ap);
197 fputs("\n", stderr);
198 fflush(stderr);
201 FL sighandler_type
202 safe_signal(int signum, sighandler_type handler)
204 struct sigaction nact, oact;
206 nact.sa_handler = handler;
207 sigemptyset(&nact.sa_mask);
208 nact.sa_flags = 0;
209 #ifdef SA_RESTART
210 nact.sa_flags |= SA_RESTART;
211 #endif
212 return ((sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler);
215 FL void
216 hold_all_sigs(void)
218 if (_alls_depth++ == 0) {
219 sigfillset(&_alls_nset);
220 sigdelset(&_alls_nset, SIGABRT);
221 #ifdef SIGBUS
222 sigdelset(&_alls_nset, SIGBUS);
223 #endif
224 sigdelset(&_alls_nset, SIGCHLD);
225 sigdelset(&_alls_nset, SIGFPE);
226 sigdelset(&_alls_nset, SIGILL);
227 sigdelset(&_alls_nset, SIGKILL);
228 sigdelset(&_alls_nset, SIGSEGV);
229 sigdelset(&_alls_nset, SIGSTOP);
230 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
234 FL void
235 rele_all_sigs(void)
237 if (--_alls_depth == 0)
238 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
241 FL void
242 hold_sigs(void)
244 if (_hold_sigdepth++ == 0) {
245 sigemptyset(&_hold_nset);
246 sigaddset(&_hold_nset, SIGHUP);
247 sigaddset(&_hold_nset, SIGINT);
248 sigaddset(&_hold_nset, SIGQUIT);
249 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
253 FL void
254 rele_sigs(void)
256 if (--_hold_sigdepth == 0)
257 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
261 * Touch the named message by setting its MTOUCH flag.
262 * Touched messages have the effect of not being sent
263 * back to the system mailbox on exit.
265 FL void
266 touch(struct message *mp)
269 mp->m_flag |= MTOUCH;
270 if ((mp->m_flag & MREAD) == 0)
271 mp->m_flag |= MREAD|MSTATUS;
275 * Test to see if the passed file name is a directory.
276 * Return true if it is.
278 FL int
279 is_dir(char const *name)
281 struct stat sbuf;
283 if (stat(name, &sbuf) < 0)
284 return(0);
285 return(S_ISDIR(sbuf.st_mode));
289 * Count the number of arguments in the given string raw list.
291 FL int
292 argcount(char **argv)
294 char **ap;
296 for (ap = argv; *ap++ != NULL;)
298 return ap - argv - 1;
301 FL char *
302 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
304 int col_orig = col, n, sz;
305 char *nb, *np;
307 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
308 while (*cp) {
309 #ifdef HAVE_WCWIDTH
310 if (mb_cur_max > 1) {
311 wchar_t wc;
313 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
314 n = sz = 1;
315 } else {
316 if ((n = wcwidth(wc)) < 0)
317 n = 1;
319 } else
320 #endif
322 n = sz = 1;
324 if (n > col)
325 break;
326 col -= n;
327 if (sz == 1 && spacechar(*cp)) {
328 *np++ = ' ';
329 cp++;
330 } else
331 while (sz--)
332 *np++ = *cp++;
335 if (fill && col != 0) {
336 if (fill > 0) {
337 memmove(nb + col, nb, (size_t)(np - nb));
338 memset(nb, ' ', col);
339 } else
340 memset(np, ' ', col);
341 np += col;
342 col = 0;
345 *np = '\0';
346 if (cols_decr_used_or_null != NULL)
347 *cols_decr_used_or_null -= col_orig - col;
348 return nb;
351 FL char const *
352 get_pager(void)
354 char const *cp;
356 cp = ok_vlook(PAGER);
357 if (cp == NULL || *cp == '\0')
358 cp = XPAGER;
359 return cp;
362 FL size_t
363 paging_seems_sensible(void)
365 size_t ret = 0;
366 char const *cp;
368 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
369 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
370 return ret;
373 FL void
374 page_or_print(FILE *fp, size_t lines)
376 size_t rows;
377 int c;
379 fflush_rewind(fp);
381 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
382 while ((c = getc(fp)) != EOF)
383 if (c == '\n' && ++lines > rows)
384 break;
385 rewind(fp);
388 if (rows != 0 && lines >= rows)
389 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
390 else
391 while ((c = getc(fp)) != EOF)
392 putchar(c);
395 FL enum protocol
396 which_protocol(const char *name)
398 register const char *cp;
399 char *np;
400 size_t sz;
401 struct stat st;
402 enum protocol p;
404 if (name[0] == '%' && name[1] == ':')
405 name += 2;
406 for (cp = name; *cp && *cp != ':'; cp++)
407 if (!alnumchar(*cp&0377))
408 goto file;
409 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
410 if (strncmp(name, "pop3://", 7) == 0)
411 #ifdef HAVE_POP3
412 return PROTO_POP3;
413 #else
414 fprintf(stderr,
415 tr(216, "No POP3 support compiled in.\n"));
416 #endif
417 if (strncmp(name, "pop3s://", 8) == 0)
418 #ifdef HAVE_SSL
419 return PROTO_POP3;
420 #else
421 fprintf(stderr,
422 tr(225, "No SSL support compiled in.\n"));
423 #endif
424 if (strncmp(name, "imap://", 7) == 0)
425 #ifdef HAVE_IMAP
426 return PROTO_IMAP;
427 #else
428 fprintf(stderr,
429 tr(269, "No IMAP support compiled in.\n"));
430 #endif
431 if (strncmp(name, "imaps://", 8) == 0)
432 #ifdef HAVE_SSL
433 return PROTO_IMAP;
434 #else
435 fprintf(stderr,
436 tr(225, "No SSL support compiled in.\n"));
437 #endif
438 return PROTO_UNKNOWN;
439 } else {
440 /* TODO This is the de facto maildir code and thus belongs
441 * TODO into maildir! */
442 file: p = PROTO_FILE;
443 np = ac_alloc((sz = strlen(name)) + 5);
444 memcpy(np, name, sz + 1);
445 if (stat(name, &st) == 0) {
446 if (S_ISDIR(st.st_mode)) {
447 strcpy(&np[sz], "/tmp");
448 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
449 strcpy(&np[sz], "/new");
450 if (stat(np, &st) == 0 &&
451 S_ISDIR(st.st_mode)) {
452 strcpy(&np[sz], "/cur");
453 if (stat(np, &st) == 0 &&
454 S_ISDIR(st.st_mode))
455 p = PROTO_MAILDIR;
459 } else {
460 strcpy(&np[sz], ".gz");
461 if (stat(np, &st) < 0) {
462 strcpy(&np[sz], ".bz2");
463 if (stat(np, &st) < 0) {
464 if ((cp = ok_vlook(newfolders)) != NULL &&
465 strcmp(cp, "maildir") == 0)
466 p = PROTO_MAILDIR;
470 ac_free(np);
471 return p;
475 FL ui32_t
476 torek_hash(char const *name)
478 /* Chris Torek's hash.
479 * NOTE: need to change *at least* create-okey-map.pl when changing the
480 * algorithm!! */
481 ui32_t h = 0;
483 while (*name != '\0') {
484 h *= 33;
485 h += *name++;
487 return h;
490 FL unsigned
491 pjw(const char *cp)
493 unsigned h = 0, g;
495 cp--;
496 while (*++cp) {
497 h = (h << 4 & 0xffffffff) + (*cp&0377);
498 if ((g = h & 0xf0000000) != 0) {
499 h = h ^ g >> 24;
500 h = h ^ g;
503 return h;
506 FL long
507 nextprime(long n)
509 const long primes[] = {
510 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
511 131071, 262139, 524287, 1048573, 2097143, 4194301,
512 8388593, 16777213, 33554393, 67108859, 134217689,
513 268435399, 536870909, 1073741789, 2147483647
515 long mprime = 7;
516 size_t i;
518 for (i = 0; i < sizeof primes / sizeof *primes; i++)
519 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
520 n < 262144 ? n*2 : n))
521 break;
522 if (i == sizeof primes / sizeof *primes)
523 mprime = n; /* not so prime, but better than failure */
524 return mprime;
527 FL int
528 expand_shell_escape(char const **s, bool_t use_nail_extensions)
530 char const *xs = *s;
531 int c, n;
533 if ((c = *xs & 0xFF) == '\0')
534 goto jleave;
535 ++xs;
536 if (c != '\\')
537 goto jleave;
539 switch ((c = *xs & 0xFF)) {
540 case '\\': break;
541 case 'a': c = '\a'; break;
542 case 'b': c = '\b'; break;
543 case 'c': c = PROMPT_STOP; break;
544 case 'f': c = '\f'; break;
545 case 'n': c = '\n'; break;
546 case 'r': c = '\r'; break;
547 case 't': c = '\t'; break;
548 case 'v': c = '\v'; break;
549 case '0':
550 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
551 c <<= 3;
552 c |= *xs - '0';
554 goto jleave;
555 /* S-nail extension for nice (get)prompt(()) support */
556 case '&':
557 case '?':
558 case '$':
559 case '@':
560 if (use_nail_extensions) {
561 switch (c) {
562 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
563 case '?': c = exec_last_comm_error ? '1' : '0'; break;
564 case '$': c = PROMPT_DOLLAR; break;
565 case '@': c = PROMPT_AT; break;
567 break;
569 /* FALLTHRU */
570 case '\0':
571 /* A sole <backslash> at EOS is treated as-is! */
572 /* FALLTHRU */
573 default:
574 c = '\\';
575 goto jleave;
577 ++xs;
578 jleave:
579 *s = xs;
580 return c;
583 FL char *
584 getprompt(void)
586 static char buf[PROMPT_BUFFER_SIZE];
588 char *cp = buf;
589 char const *ccp;
591 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
592 goto jleave;
594 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
595 char const *a;
596 size_t l;
597 int c = expand_shell_escape(&ccp, TRU1);
599 if (c > 0) {
600 *cp = (char)c;
601 continue;
603 if (c == 0 || c == PROMPT_STOP)
604 break;
606 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
607 if (a == NULL)
608 a = "";
609 l = strlen(a);
610 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
611 *cp++ = '?';
612 else {
613 memcpy(cp, a, l);
614 cp += --l;
617 jleave:
618 *cp = '\0';
619 return buf;
622 FL char *
623 nodename(int mayoverride)
625 static char *hostname;
626 struct utsname ut;
627 char *hn;
628 #ifdef HAVE_SOCKETS
629 # ifdef HAVE_IPV6
630 struct addrinfo hints, *res;
631 # else
632 struct hostent *hent;
633 # endif
634 #endif
636 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
637 if (hostname != NULL)
638 free(hostname);
639 hostname = sstrdup(hn);
640 } else if (hostname == NULL) {
641 uname(&ut);
642 hn = ut.nodename;
643 #ifdef HAVE_SOCKETS
644 # ifdef HAVE_IPV6
645 memset(&hints, 0, sizeof hints);
646 hints.ai_family = AF_UNSPEC;
647 hints.ai_socktype = SOCK_DGRAM; /* dummy */
648 hints.ai_flags = AI_CANONNAME;
649 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
650 if (res->ai_canonname != NULL) {
651 size_t l = strlen(res->ai_canonname);
652 hn = ac_alloc(l + 1);
653 memcpy(hn, res->ai_canonname, l + 1);
655 freeaddrinfo(res);
657 # else
658 hent = gethostbyname(hn);
659 if (hent != NULL) {
660 hn = hent->h_name;
662 # endif
663 #endif
664 hostname = sstrdup(hn);
665 #if defined HAVE_SOCKETS && defined HAVE_IPV6
666 if (hn != ut.nodename)
667 ac_free(hn);
668 #endif
670 return (hostname);
673 FL char *
674 lookup_password_for_token(char const *token)
676 size_t tl;
677 char *var, *cp;
679 tl = strlen(token);
680 var = ac_alloc(tl + 10);
682 memcpy(var, "password-", 9);
683 memcpy(var + 9, token, tl);
684 var[tl + 9] = '\0';
686 if ((cp = vok_vlook(var)) != NULL)
687 cp = savestr(cp);
688 ac_free(var);
689 return cp;
692 FL char *
693 getrandstring(size_t length)
695 static unsigned char nodedigest[16];
696 static pid_t pid;
697 struct str b64;
698 int fd = -1;
699 char *data, *cp;
700 size_t i;
701 #ifdef HAVE_MD5
702 md5_ctx ctx;
703 #else
704 size_t j;
705 #endif
707 data = ac_alloc(length);
708 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
709 length != (size_t)read(fd, data, length)) {
710 if (pid == 0) {
711 pid = getpid();
712 srand(pid);
713 cp = nodename(0);
714 #ifdef HAVE_MD5
715 md5_init(&ctx);
716 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
717 md5_final(nodedigest, &ctx);
718 #else
719 /* In that case it's only used for boundaries and
720 * Message-Id:s so that srand(3) should suffice */
721 j = strlen(cp) + 1;
722 for (i = 0; i < sizeof(nodedigest); ++i)
723 nodedigest[i] = (unsigned char)(
724 cp[i % j] ^ rand());
725 #endif
727 for (i = 0; i < length; i++)
728 data[i] = (char)(
729 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
730 nodedigest[i % sizeof nodedigest]);
732 if (fd >= 0)
733 close(fd);
735 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
736 ac_free(data);
737 assert(length < b64.l);
738 b64.s[length] = '\0';
739 return b64.s;
742 #ifdef HAVE_MD5
743 FL char *
744 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
746 char const *cp = vp;
747 size_t i, j;
749 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
750 j = i << 1;
751 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
752 hex[++j] = hexchar(cp[i] & 0x0f);
754 return hex;
757 FL char *
758 cram_md5_string(char const *user, char const *pass, char const *b64)
760 struct str in, out;
761 char digest[16], *cp;
762 size_t lu;
764 out.s = NULL;
765 in.s = UNCONST(b64);
766 in.l = strlen(in.s);
767 (void)b64_decode(&out, &in, NULL);
768 assert(out.s != NULL);
770 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
771 digest);
772 free(out.s);
773 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
775 lu = strlen(user);
776 in.l = lu + MD5TOHEX_SIZE +1;
777 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
778 memcpy(in.s, user, lu);
779 in.s[lu] = ' ';
780 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
781 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
782 ac_free(in.s);
783 return out.s;
785 #endif /* HAVE_MD5 */
787 FL enum okay
788 makedir(const char *name)
790 int e;
791 struct stat st;
793 if (mkdir(name, 0700) < 0) {
794 e = errno;
795 if ((e == EEXIST || e == ENOSYS) &&
796 stat(name, &st) == 0 &&
797 (st.st_mode&S_IFMT) == S_IFDIR)
798 return OKAY;
799 return STOP;
801 return OKAY;
804 #ifdef HAVE_FCHDIR
805 FL enum okay
806 cwget(struct cw *cw)
808 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
809 return STOP;
810 if (fchdir(cw->cw_fd) < 0) {
811 close(cw->cw_fd);
812 return STOP;
814 return OKAY;
817 FL enum okay
818 cwret(struct cw *cw)
820 if (fchdir(cw->cw_fd) < 0)
821 return STOP;
822 return OKAY;
825 FL void
826 cwrelse(struct cw *cw)
828 close(cw->cw_fd);
830 #else /* !HAVE_FCHDIR */
831 FL enum okay
832 cwget(struct cw *cw)
834 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
835 return STOP;
836 return OKAY;
839 FL enum okay
840 cwret(struct cw *cw)
842 if (chdir(cw->cw_wd) < 0)
843 return STOP;
844 return OKAY;
847 /*ARGSUSED*/
848 FL void
849 cwrelse(struct cw *cw)
851 (void)cw;
853 #endif /* !HAVE_FCHDIR */
855 FL void
856 makeprint(struct str const *in, struct str *out)
858 static int print_all_chars = -1;
859 char const *inp, *maxp;
860 char *outp;
861 size_t msz;
863 if (print_all_chars == -1)
864 print_all_chars = ok_blook(print_all_chars);
866 msz = in->l + 1;
867 out->s = outp = smalloc(msz);
868 inp = in->s;
869 maxp = inp + in->l;
871 if (print_all_chars) {
872 out->l = in->l;
873 memcpy(outp, inp, out->l);
874 goto jleave;
877 #ifdef HAVE_C90AMEND1
878 if (mb_cur_max > 1) {
879 char mbb[MB_LEN_MAX + 1];
880 wchar_t wc;
881 int i, n;
882 size_t dist;
884 out->l = 0;
885 while (inp < maxp) {
886 if (*inp & 0200)
887 n = mbtowc(&wc, inp, maxp - inp);
888 else {
889 wc = *inp;
890 n = 1;
892 if (n < 0) {
893 /* FIXME Why mbtowc() resetting here?
894 * FIXME what about ISO 2022-JP plus -- those
895 * FIXME will loose shifts, then!
896 * FIXME THUS - we'd need special "known points"
897 * FIXME to do so - say, after a newline!!
898 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
899 (void)mbtowc(&wc, NULL, mb_cur_max);
900 wc = utf8 ? 0xFFFD : '?';
901 n = 1;
902 } else if (n == 0)
903 n = 1;
904 inp += n;
905 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
906 wc != '\b' && wc != '\t') {
907 if ((wc & ~(wchar_t)037) == 0)
908 wc = utf8 ? 0x2400 | wc : '?';
909 else if (wc == 0177)
910 wc = utf8 ? 0x2421 : '?';
911 else
912 wc = utf8 ? 0x2426 : '?';
914 if ((n = wctomb(mbb, wc)) <= 0)
915 continue;
916 out->l += n;
917 if (out->l >= msz - 1) {
918 dist = outp - out->s;
919 out->s = srealloc(out->s, msz += 32);
920 outp = &out->s[dist];
922 for (i = 0; i < n; i++)
923 *outp++ = mbb[i];
925 } else
926 #endif /* C90AMEND1 */
928 int c;
929 while (inp < maxp) {
930 c = *inp++ & 0377;
931 if (!isprint(c) && c != '\n' && c != '\r' &&
932 c != '\b' && c != '\t')
933 c = '?';
934 *outp++ = c;
936 out->l = in->l;
938 jleave:
939 out->s[out->l] = '\0';
942 FL char *
943 prstr(const char *s)
945 struct str in, out;
946 char *rp;
948 in.s = UNCONST(s);
949 in.l = strlen(s);
950 makeprint(&in, &out);
951 rp = salloc(out.l + 1);
952 memcpy(rp, out.s, out.l);
953 rp[out.l] = '\0';
954 free(out.s);
955 return rp;
958 FL int
959 prout(const char *s, size_t sz, FILE *fp)
961 struct str in, out;
962 int n;
964 in.s = UNCONST(s);
965 in.l = sz;
966 makeprint(&in, &out);
967 n = fwrite(out.s, 1, out.l, fp);
968 free(out.s);
969 return n;
972 FL size_t
973 putuc(int u, int c, FILE *fp)
975 size_t rv;
976 UNUSED(u);
978 #ifdef HAVE_C90AMEND1
979 if (utf8 && (u & ~(wchar_t)0177)) {
980 char mbb[MB_LEN_MAX];
981 int i, n;
982 if ((n = wctomb(mbb, u)) > 0) {
983 rv = wcwidth(u);
984 for (i = 0; i < n; ++i)
985 if (putc(mbb[i] & 0377, fp) == EOF) {
986 rv = 0;
987 break;
989 } else if (n == 0)
990 rv = (putc('\0', fp) != EOF);
991 else
992 rv = 0;
993 } else
994 #endif
995 rv = (putc(c, fp) != EOF);
996 return rv;
999 #ifdef HAVE_COLOUR
1000 FL void
1001 colour_table_create(char const *pager_used)
1003 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1004 size_t i;
1005 struct colour_table *ct;
1007 if (ok_blook(colour_disable))
1008 goto jleave;
1010 /* If pager, check wether it is allowed to use colour */
1011 if (pager_used != NULL) {
1012 char *pager;
1014 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1015 u.ccp = COLOUR_PAGERS;
1016 pager = savestr(u.cp);
1018 while ((u.cp = strcomma(&pager, TRU1)) != NULL)
1019 if (strstr(pager_used, u.cp) != NULL)
1020 goto jok;
1021 goto jleave;
1024 /* $TERM is different in that we default to false unless whitelisted */
1026 char *term, *okterms;
1028 /* Don't use getenv(), but force copy-in into our own tables.. */
1029 if ((term = _var_voklook("TERM")) == NULL)
1030 goto jleave;
1031 if ((okterms = ok_vlook(colour_terms)) == NULL)
1032 okterms = UNCONST(COLOUR_TERMS);
1033 okterms = savestr(okterms);
1035 i = strlen(term);
1036 while ((u.cp = strcomma(&okterms, TRU1)) != NULL)
1037 if (!strncmp(u.cp, term, i))
1038 goto jok;
1039 goto jleave;
1042 jok:
1043 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1044 { static struct {
1045 enum okeys okey;
1046 enum colourspec cspec;
1047 char const *defval;
1048 } const map[] = {
1049 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1050 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1051 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1052 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1053 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1056 for (i = 0; i < NELEM(map); ++i) {
1057 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1058 u.ccp = map[i].defval;
1059 u.cp = _colour_iso6429(u.ccp);
1060 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1061 ct->ct_csinfo[map[i].cspec].s = u.cp;
1064 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") - 1;
1065 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1067 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1068 u.ccp = COLOUR_USER_HEADERS;
1069 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1070 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1071 jleave:
1075 FL void
1076 colour_put(FILE *fp, enum colourspec cs)
1078 if (colour_table != NULL) {
1079 struct str const *cp = colour_get(cs);
1081 fwrite(cp->s, cp->l, 1, fp);
1085 FL void
1086 colour_put_header(FILE *fp, char const *name)
1088 enum colourspec cs = COLOURSPEC_HEADER;
1089 struct str const *uheads;
1090 char *cp, *cp_base, *x;
1091 size_t namelen;
1093 if (colour_table == NULL)
1094 goto j_leave;
1095 /* Normal header colours if there are no user headers */
1096 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1097 if (uheads->s == NULL)
1098 goto jleave;
1100 /* Iterate over all entries in the *colour-user-headers* list */
1101 cp = ac_alloc(uheads->l + 1);
1102 memcpy(cp, uheads->s, uheads->l + 1);
1103 cp_base = cp;
1104 namelen = strlen(name);
1105 while ((x = strcomma(&cp, TRU1)) != NULL) {
1106 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1107 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1108 cs = COLOURSPEC_UHEADER;
1109 break;
1112 ac_free(cp_base);
1113 jleave:
1114 colour_put(fp, cs);
1115 j_leave:
1119 FL void
1120 colour_reset(FILE *fp)
1122 if (colour_table != NULL)
1123 fwrite("\033[0m", 4, 1, fp);
1126 FL struct str const *
1127 colour_get(enum colourspec cs)
1129 struct str const *rv = NULL;
1131 if (colour_table != NULL)
1132 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1133 rv = NULL;
1134 return rv;
1136 #endif /* HAVE_COLOUR */
1138 FL void
1139 time_current_update(struct time_current *tc, bool_t full_update)
1141 tc->tc_time = time(NULL);
1142 if (full_update) {
1143 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1144 memcpy(&tc->tc_local, localtime(&tc->tc_time),
1145 sizeof tc->tc_local);
1146 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1150 static void
1151 _out_of_memory(void)
1153 panic("no memory");
1156 #ifndef HAVE_DEBUG
1157 FL void *
1158 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1160 void *rv;
1162 if (s == 0)
1163 s = 1;
1164 if ((rv = malloc(s)) == NULL)
1165 _out_of_memory();
1166 return rv;
1169 FL void *
1170 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1172 void *rv;
1174 if (s == 0)
1175 s = 1;
1176 if (v == NULL)
1177 rv = smalloc(s);
1178 else if ((rv = realloc(v, s)) == NULL)
1179 _out_of_memory();
1180 return rv;
1183 FL void *
1184 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1186 void *rv;
1188 if (size == 0)
1189 size = 1;
1190 if ((rv = calloc(nmemb, size)) == NULL)
1191 _out_of_memory();
1192 return rv;
1195 #else /* !HAVE_DEBUG */
1196 CTA(sizeof(char) == sizeof(ui8_t));
1198 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1199 # define _HOPE_SET(C) \
1200 do {\
1201 union ptr __xl, __xu;\
1202 struct chunk *__xc;\
1203 __xl.p = (C).p;\
1204 __xc = __xl.c - 1;\
1205 __xu.p = __xc;\
1206 (C).cp += 8;\
1207 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1208 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1209 __xu.ui8p += __xc->size - 8;\
1210 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1211 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1212 } while (0)
1213 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1214 # define _HOPE_GET(C,BAD) \
1215 do {\
1216 union ptr __xl, __xu;\
1217 struct chunk *__xc;\
1218 ui32_t __i;\
1219 __xl.p = (C).p;\
1220 __xl.cp -= 8;\
1221 (C).cp = __xl.cp;\
1222 __xc = __xl.c - 1;\
1223 (BAD) = FAL0;\
1224 __i = 0;\
1225 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1226 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1227 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1228 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1229 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1230 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1231 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1232 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1233 if (__i != 0) {\
1234 (BAD) = TRU1;\
1235 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1236 __xl.p, __i, mdbg_file, mdbg_line);\
1238 __xu.p = __xc;\
1239 __xu.ui8p += __xc->size - 8;\
1240 __i = 0;\
1241 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1242 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1243 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1244 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1245 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1246 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1247 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1248 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1249 if (__i != 0) {\
1250 (BAD) = TRU1;\
1251 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1252 __xl.p, __i, mdbg_file, mdbg_line);\
1254 if (BAD)\
1255 alert(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1256 } while (0)
1258 struct chunk {
1259 struct chunk *prev;
1260 struct chunk *next;
1261 char const *file;
1262 ui16_t line;
1263 ui8_t isfree;
1264 ui8_t __dummy;
1265 ui32_t size;
1268 union ptr {
1269 void *p;
1270 struct chunk *c;
1271 char *cp;
1272 ui8_t *ui8p;
1275 struct chunk *_mlist, *_mfree;
1277 FL void *
1278 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1280 union ptr p;
1282 if (s == 0)
1283 s = 1;
1284 s += sizeof(struct chunk) + _HOPE_SIZE;
1286 if ((p.p = (malloc)(s)) == NULL)
1287 _out_of_memory();
1288 p.c->prev = NULL;
1289 if ((p.c->next = _mlist) != NULL)
1290 _mlist->prev = p.c;
1291 p.c->file = mdbg_file;
1292 p.c->line = (ui16_t)mdbg_line;
1293 p.c->isfree = FAL0;
1294 p.c->size = (ui32_t)s;
1295 _mlist = p.c++;
1296 _HOPE_SET(p);
1297 return p.p;
1300 FL void *
1301 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1303 union ptr p;
1304 bool_t isbad;
1306 if ((p.p = v) == NULL) {
1307 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1308 goto jleave;
1311 _HOPE_GET(p, isbad);
1312 --p.c;
1313 if (p.c->isfree) {
1314 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1315 "\tLast seen: %s, line %d\n",
1316 mdbg_file, mdbg_line, p.c->file, p.c->line);
1317 goto jforce;
1320 if (p.c == _mlist)
1321 _mlist = p.c->next;
1322 else
1323 p.c->prev->next = p.c->next;
1324 if (p.c->next != NULL)
1325 p.c->next->prev = p.c->prev;
1327 jforce:
1328 if (s == 0)
1329 s = 1;
1330 s += sizeof(struct chunk) + _HOPE_SIZE;
1332 if ((p.p = (realloc)(p.c, s)) == NULL)
1333 _out_of_memory();
1334 p.c->prev = NULL;
1335 if ((p.c->next = _mlist) != NULL)
1336 _mlist->prev = p.c;
1337 p.c->file = mdbg_file;
1338 p.c->line = (ui16_t)mdbg_line;
1339 p.c->isfree = FAL0;
1340 p.c->size = (ui32_t)s;
1341 _mlist = p.c++;
1342 _HOPE_SET(p);
1343 jleave:
1344 return p.p;
1347 FL void *
1348 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1350 union ptr p;
1352 if (size == 0)
1353 size = 1;
1354 if (nmemb == 0)
1355 nmemb = 1;
1356 size *= nmemb;
1357 size += sizeof(struct chunk) + _HOPE_SIZE;
1359 if ((p.p = (malloc)(size)) == NULL)
1360 _out_of_memory();
1361 memset(p.p, 0, size);
1362 p.c->prev = NULL;
1363 if ((p.c->next = _mlist) != NULL)
1364 _mlist->prev = p.c;
1365 p.c->file = mdbg_file;
1366 p.c->line = (ui16_t)mdbg_line;
1367 p.c->isfree = FAL0;
1368 p.c->size = (ui32_t)size;
1369 _mlist = p.c++;
1370 _HOPE_SET(p);
1371 return p.p;
1374 FL void
1375 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1377 union ptr p;
1378 bool_t isbad;
1380 if ((p.p = v) == NULL) {
1381 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1382 goto jleave;
1385 _HOPE_GET(p, isbad);
1386 --p.c;
1387 if (p.c->isfree) {
1388 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1389 "\tLast seen: %s, line %d\n",
1390 mdbg_file, mdbg_line, p.c->file, p.c->line);
1391 goto jleave;
1394 if (p.c == _mlist)
1395 _mlist = p.c->next;
1396 else
1397 p.c->prev->next = p.c->next;
1398 if (p.c->next != NULL)
1399 p.c->next->prev = p.c->prev;
1400 p.c->isfree = TRU1;
1402 if (options & OPT_DEBUG) {
1403 p.c->next = _mfree;
1404 _mfree = p.c;
1405 } else
1406 (free)(p.c);
1407 jleave:
1411 FL void
1412 smemreset(void)
1414 union ptr p;
1415 size_t c = 0, s = 0;
1417 for (p.c = _mfree; p.c != NULL;) {
1418 void *vp = p.c;
1419 ++c;
1420 s += p.c->size;
1421 p.c = p.c->next;
1422 (free)(vp);
1424 _mfree = NULL;
1426 if (options & OPT_DEBUG)
1427 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1428 c, s);
1431 FL int
1432 smemtrace(void *v)
1434 /* For _HOPE_GET() */
1435 char const * const mdbg_file = "smemtrace()";
1436 int const mdbg_line = -1;
1438 FILE *fp;
1439 char *cp;
1440 union ptr p, xp;
1441 bool_t isbad;
1442 size_t lines;
1444 v = (void*)0x1;
1445 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1446 perror("tmpfile");
1447 goto jleave;
1449 rm(cp);
1450 Ftfree(&cp);
1452 fprintf(fp, "Currently allocated memory chunks:\n");
1453 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1454 xp = p;
1455 ++xp.c;
1456 _HOPE_GET_TRACE(xp, isbad);
1457 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1458 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1459 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1462 if (options & OPT_DEBUG) {
1463 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1464 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1465 xp = p;
1466 ++xp.c;
1467 _HOPE_GET_TRACE(xp, isbad);
1468 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1469 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1470 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1474 page_or_print(fp, lines);
1475 Fclose(fp);
1476 v = NULL;
1477 jleave:
1478 return (v != NULL);
1481 # ifdef MEMCHECK
1482 FL bool_t
1483 _smemcheck(char const *mdbg_file, int mdbg_line)
1485 union ptr p, xp;
1486 bool_t anybad = FAL0, isbad;
1487 size_t lines;
1489 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1490 xp = p;
1491 ++xp.c;
1492 _HOPE_GET_TRACE(xp, isbad);
1493 if (isbad) {
1494 anybad = TRU1;
1495 fprintf(stderr,
1496 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1497 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1498 p.c->file, p.c->line);
1502 if (options & OPT_DEBUG) {
1503 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1504 xp = p;
1505 ++xp.c;
1506 _HOPE_GET_TRACE(xp, isbad);
1507 if (isbad) {
1508 anybad = TRU1;
1509 fprintf(stderr,
1510 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1511 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1512 p.c->file, p.c->line);
1516 return anybad;
1518 # endif /* MEMCHECK */
1519 #endif /* HAVE_DEBUG */
1521 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */