Offer a simple coloured display..
[s-mailx.git] / auxlily.c
blob8d638baf6ac528599cfbbd21e4478d38778061d5
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 /* 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 exit(EXIT_ERR);
186 #ifdef HAVE_DEBUG
187 FL void
188 warn(char const *format, ...)
190 va_list ap;
192 fprintf(stderr, tr(1, "Panic: "));
194 va_start(ap, format);
195 vfprintf(stderr, format, ap);
196 va_end(ap);
198 fputs("\n", stderr);
199 fflush(stderr);
201 #endif
203 FL sighandler_type
204 safe_signal(int signum, sighandler_type handler)
206 struct sigaction nact, oact;
208 nact.sa_handler = handler;
209 sigemptyset(&nact.sa_mask);
210 nact.sa_flags = 0;
211 #ifdef SA_RESTART
212 nact.sa_flags |= SA_RESTART;
213 #endif
214 return ((sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler);
217 FL void
218 hold_all_sigs(void)
220 if (_alls_depth++ == 0) {
221 sigfillset(&_alls_nset);
222 sigdelset(&_alls_nset, SIGKILL);
223 sigdelset(&_alls_nset, SIGSTOP);
224 sigdelset(&_alls_nset, SIGCHLD);
225 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
229 FL void
230 rele_all_sigs(void)
232 if (--_alls_depth == 0)
233 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
236 FL void
237 hold_sigs(void)
239 if (_hold_sigdepth++ == 0) {
240 sigemptyset(&_hold_nset);
241 sigaddset(&_hold_nset, SIGHUP);
242 sigaddset(&_hold_nset, SIGINT);
243 sigaddset(&_hold_nset, SIGQUIT);
244 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
248 FL void
249 rele_sigs(void)
251 if (--_hold_sigdepth == 0)
252 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
256 * Touch the named message by setting its MTOUCH flag.
257 * Touched messages have the effect of not being sent
258 * back to the system mailbox on exit.
260 FL void
261 touch(struct message *mp)
264 mp->m_flag |= MTOUCH;
265 if ((mp->m_flag & MREAD) == 0)
266 mp->m_flag |= MREAD|MSTATUS;
270 * Test to see if the passed file name is a directory.
271 * Return true if it is.
273 FL int
274 is_dir(char const *name)
276 struct stat sbuf;
278 if (stat(name, &sbuf) < 0)
279 return(0);
280 return(S_ISDIR(sbuf.st_mode));
284 * Count the number of arguments in the given string raw list.
286 FL int
287 argcount(char **argv)
289 char **ap;
291 for (ap = argv; *ap++ != NULL;)
293 return ap - argv - 1;
296 FL char *
297 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
299 int col_orig = col, n, sz;
300 char *nb, *np;
302 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
303 while (*cp) {
304 #ifdef HAVE_WCWIDTH
305 if (mb_cur_max > 1) {
306 wchar_t wc;
308 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
309 n = sz = 1;
310 } else {
311 if ((n = wcwidth(wc)) < 0)
312 n = 1;
314 } else
315 #endif
317 n = sz = 1;
319 if (n > col)
320 break;
321 col -= n;
322 if (sz == 1 && spacechar(*cp)) {
323 *np++ = ' ';
324 cp++;
325 } else
326 while (sz--)
327 *np++ = *cp++;
330 if (fill && col != 0) {
331 if (fill > 0) {
332 memmove(nb + col, nb, (size_t)(np - nb));
333 memset(nb, ' ', col);
334 } else
335 memset(np, ' ', col);
336 np += col;
337 col = 0;
340 *np = '\0';
341 if (cols_decr_used_or_null != NULL)
342 *cols_decr_used_or_null -= col_orig - col;
343 return nb;
346 FL char const *
347 get_pager(void)
349 char const *cp;
351 cp = ok_vlook(PAGER);
352 if (cp == NULL || *cp == '\0')
353 cp = XPAGER;
354 return cp;
357 FL size_t
358 paging_seems_sensible(void)
360 size_t ret = 0;
361 char const *cp;
363 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
364 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
365 return ret;
368 FL void
369 page_or_print(FILE *fp, size_t lines)
371 size_t rows;
372 int c;
374 fflush_rewind(fp);
376 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
377 while ((c = getc(fp)) != EOF)
378 if (c == '\n' && ++lines > rows)
379 break;
380 rewind(fp);
383 if (rows != 0 && lines >= rows)
384 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
385 else
386 while ((c = getc(fp)) != EOF)
387 putchar(c);
390 FL enum protocol
391 which_protocol(const char *name)
393 register const char *cp;
394 char *np;
395 size_t sz;
396 struct stat st;
397 enum protocol p;
399 if (name[0] == '%' && name[1] == ':')
400 name += 2;
401 for (cp = name; *cp && *cp != ':'; cp++)
402 if (!alnumchar(*cp&0377))
403 goto file;
404 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
405 if (strncmp(name, "pop3://", 7) == 0)
406 #ifdef HAVE_POP3
407 return PROTO_POP3;
408 #else
409 fprintf(stderr,
410 tr(216, "No POP3 support compiled in.\n"));
411 #endif
412 if (strncmp(name, "pop3s://", 8) == 0)
413 #ifdef HAVE_SSL
414 return PROTO_POP3;
415 #else
416 fprintf(stderr,
417 tr(225, "No SSL support compiled in.\n"));
418 #endif
419 if (strncmp(name, "imap://", 7) == 0)
420 #ifdef HAVE_IMAP
421 return PROTO_IMAP;
422 #else
423 fprintf(stderr,
424 tr(269, "No IMAP support compiled in.\n"));
425 #endif
426 if (strncmp(name, "imaps://", 8) == 0)
427 #ifdef HAVE_SSL
428 return PROTO_IMAP;
429 #else
430 fprintf(stderr,
431 tr(225, "No SSL support compiled in.\n"));
432 #endif
433 return PROTO_UNKNOWN;
434 } else {
435 /* TODO This is the de facto maildir code and thus belongs
436 * TODO into maildir! */
437 file: p = PROTO_FILE;
438 np = ac_alloc((sz = strlen(name)) + 5);
439 memcpy(np, name, sz + 1);
440 if (stat(name, &st) == 0) {
441 if (S_ISDIR(st.st_mode)) {
442 strcpy(&np[sz], "/tmp");
443 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
444 strcpy(&np[sz], "/new");
445 if (stat(np, &st) == 0 &&
446 S_ISDIR(st.st_mode)) {
447 strcpy(&np[sz], "/cur");
448 if (stat(np, &st) == 0 &&
449 S_ISDIR(st.st_mode))
450 p = PROTO_MAILDIR;
454 } else {
455 strcpy(&np[sz], ".gz");
456 if (stat(np, &st) < 0) {
457 strcpy(&np[sz], ".bz2");
458 if (stat(np, &st) < 0) {
459 if ((cp = ok_vlook(newfolders)) != NULL &&
460 strcmp(cp, "maildir") == 0)
461 p = PROTO_MAILDIR;
465 ac_free(np);
466 return p;
470 FL ui32_t
471 torek_hash(char const *name)
473 /* Chris Torek's hash.
474 * NOTE: need to change *at least* create-okey-map.pl when changing the
475 * algorithm!! */
476 ui32_t h = 0;
478 while (*name != '\0') {
479 h *= 33;
480 h += *name++;
482 return h;
485 FL unsigned
486 pjw(const char *cp)
488 unsigned h = 0, g;
490 cp--;
491 while (*++cp) {
492 h = (h << 4 & 0xffffffff) + (*cp&0377);
493 if ((g = h & 0xf0000000) != 0) {
494 h = h ^ g >> 24;
495 h = h ^ g;
498 return h;
501 FL long
502 nextprime(long n)
504 const long primes[] = {
505 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
506 131071, 262139, 524287, 1048573, 2097143, 4194301,
507 8388593, 16777213, 33554393, 67108859, 134217689,
508 268435399, 536870909, 1073741789, 2147483647
510 long mprime = 7;
511 size_t i;
513 for (i = 0; i < sizeof primes / sizeof *primes; i++)
514 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
515 n < 262144 ? n*2 : n))
516 break;
517 if (i == sizeof primes / sizeof *primes)
518 mprime = n; /* not so prime, but better than failure */
519 return mprime;
522 FL int
523 expand_shell_escape(char const **s, bool_t use_nail_extensions)
525 char const *xs = *s;
526 int c, n;
528 if ((c = *xs & 0xFF) == '\0')
529 goto jleave;
530 ++xs;
531 if (c != '\\')
532 goto jleave;
534 switch ((c = *xs & 0xFF)) {
535 case '\\': break;
536 case 'a': c = '\a'; break;
537 case 'b': c = '\b'; break;
538 case 'c': c = PROMPT_STOP; break;
539 case 'f': c = '\f'; break;
540 case 'n': c = '\n'; break;
541 case 'r': c = '\r'; break;
542 case 't': c = '\t'; break;
543 case 'v': c = '\v'; break;
544 case '0':
545 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
546 c <<= 3;
547 c |= *xs - '0';
549 goto jleave;
550 /* S-nail extension for nice (get)prompt(()) support */
551 case '&':
552 case '?':
553 case '$':
554 case '@':
555 if (use_nail_extensions) {
556 switch (c) {
557 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
558 case '?': c = exec_last_comm_error ? '1' : '0'; break;
559 case '$': c = PROMPT_DOLLAR; break;
560 case '@': c = PROMPT_AT; break;
562 break;
564 /* FALLTHRU */
565 case '\0':
566 /* A sole <backslash> at EOS is treated as-is! */
567 /* FALLTHRU */
568 default:
569 c = '\\';
570 goto jleave;
572 ++xs;
573 jleave:
574 *s = xs;
575 return c;
578 FL char *
579 getprompt(void)
581 static char buf[PROMPT_BUFFER_SIZE];
583 char *cp = buf;
584 char const *ccp;
586 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
587 goto jleave;
589 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
590 char const *a;
591 size_t l;
592 int c = expand_shell_escape(&ccp, TRU1);
594 if (c > 0) {
595 *cp = (char)c;
596 continue;
598 if (c == 0 || c == PROMPT_STOP)
599 break;
601 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
602 if (a == NULL)
603 a = "";
604 l = strlen(a);
605 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
606 *cp++ = '?';
607 else {
608 memcpy(cp, a, l);
609 cp += --l;
612 jleave:
613 *cp = '\0';
614 return buf;
617 FL char *
618 nodename(int mayoverride)
620 static char *hostname;
621 struct utsname ut;
622 char *hn;
623 #ifdef HAVE_SOCKETS
624 # ifdef HAVE_IPV6
625 struct addrinfo hints, *res;
626 # else
627 struct hostent *hent;
628 # endif
629 #endif
631 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
632 if (hostname != NULL)
633 free(hostname);
634 hostname = sstrdup(hn);
635 } else if (hostname == NULL) {
636 uname(&ut);
637 hn = ut.nodename;
638 #ifdef HAVE_SOCKETS
639 # ifdef HAVE_IPV6
640 memset(&hints, 0, sizeof hints);
641 hints.ai_family = AF_UNSPEC;
642 hints.ai_socktype = SOCK_DGRAM; /* dummy */
643 hints.ai_flags = AI_CANONNAME;
644 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
645 if (res->ai_canonname != NULL) {
646 size_t l = strlen(res->ai_canonname);
647 hn = ac_alloc(l + 1);
648 memcpy(hn, res->ai_canonname, l + 1);
650 freeaddrinfo(res);
652 # else
653 hent = gethostbyname(hn);
654 if (hent != NULL) {
655 hn = hent->h_name;
657 # endif
658 #endif
659 hostname = sstrdup(hn);
660 #if defined HAVE_SOCKETS && defined HAVE_IPV6
661 if (hn != ut.nodename)
662 ac_free(hn);
663 #endif
665 return (hostname);
668 FL char *
669 lookup_password_for_token(char const *token)
671 size_t tl;
672 char *var, *cp;
674 tl = strlen(token);
675 var = ac_alloc(tl + 10);
677 memcpy(var, "password-", 9);
678 memcpy(var + 9, token, tl);
679 var[tl + 9] = '\0';
681 if ((cp = vok_vlook(var)) != NULL)
682 cp = savestr(cp);
683 ac_free(var);
684 return cp;
687 FL char *
688 getrandstring(size_t length)
690 static unsigned char nodedigest[16];
691 static pid_t pid;
692 struct str b64;
693 int fd = -1;
694 char *data, *cp;
695 size_t i;
696 #ifdef HAVE_MD5
697 md5_ctx ctx;
698 #else
699 size_t j;
700 #endif
702 data = ac_alloc(length);
703 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
704 length != (size_t)read(fd, data, length)) {
705 if (pid == 0) {
706 pid = getpid();
707 srand(pid);
708 cp = nodename(0);
709 #ifdef HAVE_MD5
710 md5_init(&ctx);
711 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
712 md5_final(nodedigest, &ctx);
713 #else
714 /* In that case it's only used for boundaries and
715 * Message-Id:s so that srand(3) should suffice */
716 j = strlen(cp) + 1;
717 for (i = 0; i < sizeof(nodedigest); ++i)
718 nodedigest[i] = (unsigned char)(
719 cp[i % j] ^ rand());
720 #endif
722 for (i = 0; i < length; i++)
723 data[i] = (char)(
724 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
725 nodedigest[i % sizeof nodedigest]);
727 if (fd >= 0)
728 close(fd);
730 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
731 ac_free(data);
732 assert(length < b64.l);
733 b64.s[length] = '\0';
734 return b64.s;
737 #ifdef HAVE_MD5
738 FL char *
739 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
741 char const *cp = vp;
742 size_t i, j;
744 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
745 j = i << 1;
746 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
747 hex[++j] = hexchar(cp[i] & 0x0f);
749 return hex;
752 FL char *
753 cram_md5_string(char const *user, char const *pass, char const *b64)
755 struct str in, out;
756 char digest[16], *cp;
757 size_t lu;
759 out.s = NULL;
760 in.s = UNCONST(b64);
761 in.l = strlen(in.s);
762 (void)b64_decode(&out, &in, NULL);
763 assert(out.s != NULL);
765 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
766 digest);
767 free(out.s);
768 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
770 lu = strlen(user);
771 in.l = lu + MD5TOHEX_SIZE +1;
772 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
773 memcpy(in.s, user, lu);
774 in.s[lu] = ' ';
775 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
776 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
777 ac_free(in.s);
778 return out.s;
780 #endif /* HAVE_MD5 */
782 FL enum okay
783 makedir(const char *name)
785 int e;
786 struct stat st;
788 if (mkdir(name, 0700) < 0) {
789 e = errno;
790 if ((e == EEXIST || e == ENOSYS) &&
791 stat(name, &st) == 0 &&
792 (st.st_mode&S_IFMT) == S_IFDIR)
793 return OKAY;
794 return STOP;
796 return OKAY;
799 #ifdef HAVE_FCHDIR
800 FL enum okay
801 cwget(struct cw *cw)
803 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
804 return STOP;
805 if (fchdir(cw->cw_fd) < 0) {
806 close(cw->cw_fd);
807 return STOP;
809 return OKAY;
812 FL enum okay
813 cwret(struct cw *cw)
815 if (fchdir(cw->cw_fd) < 0)
816 return STOP;
817 return OKAY;
820 FL void
821 cwrelse(struct cw *cw)
823 close(cw->cw_fd);
825 #else /* !HAVE_FCHDIR */
826 FL enum okay
827 cwget(struct cw *cw)
829 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
830 return STOP;
831 return OKAY;
834 FL enum okay
835 cwret(struct cw *cw)
837 if (chdir(cw->cw_wd) < 0)
838 return STOP;
839 return OKAY;
842 /*ARGSUSED*/
843 FL void
844 cwrelse(struct cw *cw)
846 (void)cw;
848 #endif /* !HAVE_FCHDIR */
850 FL void
851 makeprint(struct str const *in, struct str *out)
853 static int print_all_chars = -1;
854 char const *inp, *maxp;
855 char *outp;
856 size_t msz;
858 if (print_all_chars == -1)
859 print_all_chars = ok_blook(print_all_chars);
861 msz = in->l + 1;
862 out->s = outp = smalloc(msz);
863 inp = in->s;
864 maxp = inp + in->l;
866 if (print_all_chars) {
867 out->l = in->l;
868 memcpy(outp, inp, out->l);
869 goto jleave;
872 #ifdef HAVE_C90AMEND1
873 if (mb_cur_max > 1) {
874 char mbb[MB_LEN_MAX + 1];
875 wchar_t wc;
876 int i, n;
877 size_t dist;
879 out->l = 0;
880 while (inp < maxp) {
881 if (*inp & 0200)
882 n = mbtowc(&wc, inp, maxp - inp);
883 else {
884 wc = *inp;
885 n = 1;
887 if (n < 0) {
888 /* FIXME Why mbtowc() resetting here?
889 * FIXME what about ISO 2022-JP plus -- those
890 * FIXME will loose shifts, then!
891 * FIXME THUS - we'd need special "known points"
892 * FIXME to do so - say, after a newline!!
893 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
894 (void)mbtowc(&wc, NULL, mb_cur_max);
895 wc = utf8 ? 0xFFFD : '?';
896 n = 1;
897 } else if (n == 0)
898 n = 1;
899 inp += n;
900 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
901 wc != '\b' && wc != '\t') {
902 if ((wc & ~(wchar_t)037) == 0)
903 wc = utf8 ? 0x2400 | wc : '?';
904 else if (wc == 0177)
905 wc = utf8 ? 0x2421 : '?';
906 else
907 wc = utf8 ? 0x2426 : '?';
909 if ((n = wctomb(mbb, wc)) <= 0)
910 continue;
911 out->l += n;
912 if (out->l >= msz - 1) {
913 dist = outp - out->s;
914 out->s = srealloc(out->s, msz += 32);
915 outp = &out->s[dist];
917 for (i = 0; i < n; i++)
918 *outp++ = mbb[i];
920 } else
921 #endif /* C90AMEND1 */
923 int c;
924 while (inp < maxp) {
925 c = *inp++ & 0377;
926 if (!isprint(c) && c != '\n' && c != '\r' &&
927 c != '\b' && c != '\t')
928 c = '?';
929 *outp++ = c;
931 out->l = in->l;
933 jleave:
934 out->s[out->l] = '\0';
937 FL char *
938 prstr(const char *s)
940 struct str in, out;
941 char *rp;
943 in.s = UNCONST(s);
944 in.l = strlen(s);
945 makeprint(&in, &out);
946 rp = salloc(out.l + 1);
947 memcpy(rp, out.s, out.l);
948 rp[out.l] = '\0';
949 free(out.s);
950 return rp;
953 FL int
954 prout(const char *s, size_t sz, FILE *fp)
956 struct str in, out;
957 int n;
959 in.s = UNCONST(s);
960 in.l = sz;
961 makeprint(&in, &out);
962 n = fwrite(out.s, 1, out.l, fp);
963 free(out.s);
964 return n;
967 FL size_t
968 putuc(int u, int c, FILE *fp)
970 size_t rv;
971 UNUSED(u);
973 #ifdef HAVE_C90AMEND1
974 if (utf8 && (u & ~(wchar_t)0177)) {
975 char mbb[MB_LEN_MAX];
976 int i, n;
977 if ((n = wctomb(mbb, u)) > 0) {
978 rv = wcwidth(u);
979 for (i = 0; i < n; ++i)
980 if (putc(mbb[i] & 0377, fp) == EOF) {
981 rv = 0;
982 break;
984 } else if (n == 0)
985 rv = (putc('\0', fp) != EOF);
986 else
987 rv = 0;
988 } else
989 #endif
990 rv = (putc(c, fp) != EOF);
991 return rv;
994 #ifdef HAVE_COLOUR
995 FL void
996 colour_table_create(char const *pager_used)
998 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
999 size_t i;
1000 struct colour_table *ct;
1002 if (ok_blook(colour_disable))
1003 goto jleave;
1005 /* If pager, check wether it is allowed to use colour */
1006 if (pager_used != NULL) {
1007 char *pager;
1009 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1010 u.ccp = COLOUR_PAGERS;
1011 pager = savestr(u.cp);
1013 while ((u.cp = strcomma(&pager, TRU1)) != NULL)
1014 if (strstr(pager_used, u.cp) != NULL)
1015 goto jok;
1016 goto jleave;
1019 /* $TERM is different in that we default to false unless whitelisted */
1021 char *term, *okterms;
1023 /* Don't use getenv(), but force copy-in into our own tables.. */
1024 if ((term = _var_voklook("TERM")) == NULL)
1025 goto jleave;
1026 if ((okterms = ok_vlook(colour_terms)) == NULL)
1027 okterms = UNCONST(COLOUR_TERMS);
1028 okterms = savestr(okterms);
1030 i = strlen(term);
1031 while ((u.cp = strcomma(&okterms, TRU1)) != NULL)
1032 if (!strncmp(u.cp, term, i))
1033 goto jok;
1034 goto jleave;
1037 jok:
1038 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1039 { static struct {
1040 enum okeys okey;
1041 enum colourspec cspec;
1042 char const *defval;
1043 } const map[] = {
1044 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1045 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1046 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1047 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1048 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1051 for (i = 0; i < NELEM(map); ++i) {
1052 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1053 u.ccp = map[i].defval;
1054 u.cp = _colour_iso6429(u.ccp);
1055 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1056 ct->ct_csinfo[map[i].cspec].s = u.cp;
1059 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") - 1;
1060 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1062 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1063 u.ccp = COLOUR_USER_HEADERS;
1064 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1065 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1066 jleave:
1070 FL void
1071 colour_put(FILE *fp, enum colourspec cs)
1073 if (colour_table != NULL) {
1074 struct str const *cp = colour_get(cs);
1076 fwrite(cp->s, cp->l, 1, fp);
1080 FL void
1081 colour_put_header(FILE *fp, char const *name)
1083 enum colourspec cs = COLOURSPEC_HEADER;
1084 struct str const *uheads;
1085 char *cp, *cp_base, *x;
1086 size_t namelen;
1088 if (colour_table == NULL)
1089 goto j_leave;
1090 /* Normal header colours if there are no user headers */
1091 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1092 if (uheads->s == NULL)
1093 goto jleave;
1095 /* Iterate over all entries in the *colour-user-headers* list */
1096 cp = ac_alloc(uheads->l + 1);
1097 memcpy(cp, uheads->s, uheads->l + 1);
1098 cp_base = cp;
1099 namelen = strlen(name);
1100 while ((x = strcomma(&cp, TRU1)) != NULL) {
1101 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1102 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1103 cs = COLOURSPEC_UHEADER;
1104 break;
1107 ac_free(cp_base);
1108 jleave:
1109 colour_put(fp, cs);
1110 j_leave:
1114 FL void
1115 colour_reset(FILE *fp)
1117 if (colour_table != NULL)
1118 fwrite("\033[0m", 4, 1, fp);
1121 FL struct str const *
1122 colour_get(enum colourspec cs)
1124 struct str const *rv = NULL;
1126 if (colour_table != NULL)
1127 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1128 rv = NULL;
1129 return rv;
1131 #endif /* HAVE_COLOUR */
1133 FL void
1134 time_current_update(struct time_current *tc, bool_t full_update)
1136 tc->tc_time = time(NULL);
1137 if (full_update) {
1138 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1139 memcpy(&tc->tc_local, localtime(&tc->tc_time),
1140 sizeof tc->tc_local);
1141 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1145 static void
1146 _out_of_memory(void)
1148 panic("no memory");
1151 FL void *
1152 (smalloc_safe)(size_t s SMALLOC_DEBUG_ARGS)
1154 void *rv;
1156 hold_all_sigs();
1157 rv = (smalloc)(s SMALLOC_DEBUG_ARGSCALL);
1158 rele_all_sigs();
1159 return rv;
1162 FL void *
1163 (srealloc_safe)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1165 void *rv;
1167 hold_all_sigs();
1168 rv = (srealloc)(v, s SMALLOC_DEBUG_ARGSCALL);
1169 rele_all_sigs();
1170 return rv;
1173 #ifdef notyet
1174 FL void *
1175 (scalloc_safe)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1177 void *rv;
1179 hold_all_sigs();
1180 rv = (scalloc)(nmemb, size SMALLOC_DEBUG_ARGSCALL);
1181 rele_all_sigs();
1182 return rv;
1184 #endif
1186 #ifndef HAVE_DEBUG
1187 FL void *
1188 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1190 void *rv;
1192 if (s == 0)
1193 s = 1;
1194 if ((rv = malloc(s)) == NULL)
1195 _out_of_memory();
1196 return rv;
1199 FL void *
1200 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1202 void *rv;
1204 if (s == 0)
1205 s = 1;
1206 if (v == NULL)
1207 rv = smalloc(s);
1208 else if ((rv = realloc(v, s)) == NULL)
1209 _out_of_memory();
1210 return rv;
1213 FL void *
1214 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1216 void *rv;
1218 if (size == 0)
1219 size = 1;
1220 if ((rv = calloc(nmemb, size)) == NULL)
1221 _out_of_memory();
1222 return rv;
1225 #else /* !HAVE_DEBUG */
1226 CTA(sizeof(char) == sizeof(ui8_t));
1228 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1229 # define _HOPE_SET(C) \
1230 do {\
1231 union ptr __xl, __xu;\
1232 struct chunk *__xc;\
1233 __xl.p = (C).p;\
1234 __xc = __xl.c - 1;\
1235 __xu.p = __xc;\
1236 (C).cp += 8;\
1237 __xl.ui8p[0]=0xDE; __xl.ui8p[1]=0xAA; __xl.ui8p[2]=0x55; __xl.ui8p[3]=0xAD;\
1238 __xl.ui8p[4]=0xBE; __xl.ui8p[5]=0x55; __xl.ui8p[6]=0xAA; __xl.ui8p[7]=0xEF;\
1239 __xu.ui8p += __xc->size - 8;\
1240 __xu.ui8p[0]=0xDE; __xu.ui8p[1]=0xAA; __xu.ui8p[2]=0x55; __xu.ui8p[3]=0xAD;\
1241 __xu.ui8p[4]=0xBE; __xu.ui8p[5]=0x55; __xu.ui8p[6]=0xAA; __xu.ui8p[7]=0xEF;\
1242 } while (0)
1243 # define _HOPE_GET_TRACE(C,BAD) do {(C).cp += 8; _HOPE_GET(C, BAD);} while(0)
1244 # define _HOPE_GET(C,BAD) \
1245 do {\
1246 union ptr __xl, __xu;\
1247 struct chunk *__xc;\
1248 ui32_t __i;\
1249 __xl.p = (C).p;\
1250 __xl.cp -= 8;\
1251 (C).cp = __xl.cp;\
1252 __xc = __xl.c - 1;\
1253 (BAD) = FAL0;\
1254 __i = 0;\
1255 if (__xl.ui8p[0] != 0xDE) __i |= 1<<0;\
1256 if (__xl.ui8p[1] != 0xAA) __i |= 1<<1;\
1257 if (__xl.ui8p[2] != 0x55) __i |= 1<<2;\
1258 if (__xl.ui8p[3] != 0xAD) __i |= 1<<3;\
1259 if (__xl.ui8p[4] != 0xBE) __i |= 1<<4;\
1260 if (__xl.ui8p[5] != 0x55) __i |= 1<<5;\
1261 if (__xl.ui8p[6] != 0xAA) __i |= 1<<6;\
1262 if (__xl.ui8p[7] != 0xEF) __i |= 1<<7;\
1263 if (__i != 0) {\
1264 (BAD) = TRU1;\
1265 warn("%p: corrupted lower canary: 0x%02X: %s, line %u",\
1266 __xl.p, __i, mdbg_file, mdbg_line);\
1268 __xu.p = __xc;\
1269 __xu.ui8p += __xc->size - 8;\
1270 __i = 0;\
1271 if (__xu.ui8p[0] != 0xDE) __i |= 1<<0;\
1272 if (__xu.ui8p[1] != 0xAA) __i |= 1<<1;\
1273 if (__xu.ui8p[2] != 0x55) __i |= 1<<2;\
1274 if (__xu.ui8p[3] != 0xAD) __i |= 1<<3;\
1275 if (__xu.ui8p[4] != 0xBE) __i |= 1<<4;\
1276 if (__xu.ui8p[5] != 0x55) __i |= 1<<5;\
1277 if (__xu.ui8p[6] != 0xAA) __i |= 1<<6;\
1278 if (__xu.ui8p[7] != 0xEF) __i |= 1<<7;\
1279 if (__i != 0) {\
1280 (BAD) = TRU1;\
1281 warn("%p: corrupted upper canary: 0x%02X: %s, line %u",\
1282 __xl.p, __i, mdbg_file, mdbg_line);\
1284 if (BAD)\
1285 warn(" ..canary last seen: %s, line %u", __xc->file, __xc->line);\
1286 } while (0)
1288 struct chunk {
1289 struct chunk *prev;
1290 struct chunk *next;
1291 char const *file;
1292 ui16_t line;
1293 ui8_t isfree;
1294 ui8_t __dummy;
1295 ui32_t size;
1298 union ptr {
1299 void *p;
1300 struct chunk *c;
1301 char *cp;
1302 ui8_t *ui8p;
1305 struct chunk *_mlist, *_mfree;
1307 FL void *
1308 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1310 union ptr p;
1312 if (s == 0)
1313 s = 1;
1314 s += sizeof(struct chunk) + _HOPE_SIZE;
1316 if ((p.p = (malloc)(s)) == NULL)
1317 _out_of_memory();
1318 p.c->prev = NULL;
1319 if ((p.c->next = _mlist) != NULL)
1320 _mlist->prev = p.c;
1321 p.c->file = mdbg_file;
1322 p.c->line = (ui16_t)mdbg_line;
1323 p.c->isfree = FAL0;
1324 p.c->size = (ui32_t)s;
1325 _mlist = p.c++;
1326 _HOPE_SET(p);
1327 return p.p;
1330 FL void *
1331 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1333 union ptr p;
1334 bool_t isbad;
1336 if ((p.p = v) == NULL) {
1337 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1338 goto jleave;
1341 _HOPE_GET(p, isbad);
1342 --p.c;
1343 if (p.c->isfree) {
1344 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1345 "\tLast seen: %s, line %d\n",
1346 mdbg_file, mdbg_line, p.c->file, p.c->line);
1347 goto jforce;
1350 if (p.c == _mlist)
1351 _mlist = p.c->next;
1352 else
1353 p.c->prev->next = p.c->next;
1354 if (p.c->next != NULL)
1355 p.c->next->prev = p.c->prev;
1357 jforce:
1358 if (s == 0)
1359 s = 1;
1360 s += sizeof(struct chunk) + _HOPE_SIZE;
1362 if ((p.p = (realloc)(p.c, s)) == NULL)
1363 _out_of_memory();
1364 p.c->prev = NULL;
1365 if ((p.c->next = _mlist) != NULL)
1366 _mlist->prev = p.c;
1367 p.c->file = mdbg_file;
1368 p.c->line = (ui16_t)mdbg_line;
1369 p.c->isfree = FAL0;
1370 p.c->size = (ui32_t)s;
1371 _mlist = p.c++;
1372 _HOPE_SET(p);
1373 jleave:
1374 return p.p;
1377 FL void *
1378 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1380 union ptr p;
1382 if (size == 0)
1383 size = 1;
1384 if (nmemb == 0)
1385 nmemb = 1;
1386 size *= nmemb;
1387 size += sizeof(struct chunk) + _HOPE_SIZE;
1389 if ((p.p = (malloc)(size)) == NULL)
1390 _out_of_memory();
1391 memset(p.p, 0, size);
1392 p.c->prev = NULL;
1393 if ((p.c->next = _mlist) != NULL)
1394 _mlist->prev = p.c;
1395 p.c->file = mdbg_file;
1396 p.c->line = (ui16_t)mdbg_line;
1397 p.c->isfree = FAL0;
1398 p.c->size = (ui32_t)size;
1399 _mlist = p.c++;
1400 _HOPE_SET(p);
1401 return p.p;
1404 FL void
1405 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1407 union ptr p;
1408 bool_t isbad;
1410 if ((p.p = v) == NULL) {
1411 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1412 goto jleave;
1415 _HOPE_GET(p, isbad);
1416 --p.c;
1417 if (p.c->isfree) {
1418 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1419 "\tLast seen: %s, line %d\n",
1420 mdbg_file, mdbg_line, p.c->file, p.c->line);
1421 goto jleave;
1424 if (p.c == _mlist)
1425 _mlist = p.c->next;
1426 else
1427 p.c->prev->next = p.c->next;
1428 if (p.c->next != NULL)
1429 p.c->next->prev = p.c->prev;
1430 p.c->isfree = TRU1;
1432 if (options & OPT_DEBUG) {
1433 p.c->next = _mfree;
1434 _mfree = p.c;
1435 } else
1436 (free)(p.c);
1437 jleave:
1441 FL void
1442 smemreset(void)
1444 union ptr p;
1445 size_t c = 0, s = 0;
1447 for (p.c = _mfree; p.c != NULL;) {
1448 void *vp = p.c;
1449 ++c;
1450 s += p.c->size;
1451 p.c = p.c->next;
1452 (free)(vp);
1454 _mfree = NULL;
1456 if (options & OPT_DEBUG)
1457 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1458 c, s);
1461 FL int
1462 smemtrace(void *v)
1464 /* For _HOPE_GET() */
1465 char const * const mdbg_file = "smemtrace()";
1466 int const mdbg_line = -1;
1468 FILE *fp;
1469 char *cp;
1470 union ptr p, xp;
1471 bool_t isbad;
1472 size_t lines;
1474 v = (void*)0x1;
1475 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1476 perror("tmpfile");
1477 goto jleave;
1479 rm(cp);
1480 Ftfree(&cp);
1482 fprintf(fp, "Currently allocated memory chunks:\n");
1483 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1484 xp = p;
1485 ++xp.c;
1486 _HOPE_GET_TRACE(xp, isbad);
1487 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1488 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1489 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1492 if (options & OPT_DEBUG) {
1493 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1494 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1495 xp = p;
1496 ++xp.c;
1497 _HOPE_GET_TRACE(xp, isbad);
1498 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1499 (isbad ? "! CANARY ERROR: " : ""), xp.p,
1500 (size_t)(p.c->size - sizeof(struct chunk)), p.c->file, p.c->line);
1504 page_or_print(fp, lines);
1505 Fclose(fp);
1506 v = NULL;
1507 jleave:
1508 return (v != NULL);
1511 # ifdef MEMCHECK
1512 FL bool_t
1513 _smemcheck(char const *mdbg_file, int mdbg_line)
1515 union ptr p, xp;
1516 bool_t anybad = FAL0, isbad;
1517 size_t lines;
1519 for (lines = 0, p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next) {
1520 xp = p;
1521 ++xp.c;
1522 _HOPE_GET_TRACE(xp, isbad);
1523 if (isbad) {
1524 anybad = TRU1;
1525 fprintf(stderr,
1526 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1527 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1528 p.c->file, p.c->line);
1532 if (options & OPT_DEBUG) {
1533 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next) {
1534 xp = p;
1535 ++xp.c;
1536 _HOPE_GET_TRACE(xp, isbad);
1537 if (isbad) {
1538 anybad = TRU1;
1539 fprintf(stderr,
1540 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
1541 xp.p, (size_t)(p.c->size - sizeof(struct chunk)),
1542 p.c->file, p.c->line);
1546 return anybad;
1548 # endif /* MEMCHECK */
1549 #endif /* HAVE_DEBUG */
1551 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */