Add bidi_info_create(), struct bidi_info
[s-mailx.git] / auxlily.c
blob24aa4bda3bc2f67c1a940484da747d5bcb59bf5c
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_DEBUG
59 struct nyd_info {
60 char const *ni_file;
61 char const *ni_fun;
62 ui32_t ni_chirp_line;
63 ui32_t ni_level;
66 struct mem_chunk {
67 struct mem_chunk *mc_prev;
68 struct mem_chunk *mc_next;
69 char const *mc_file;
70 ui16_t mc_line;
71 ui8_t mc_isfree;
72 ui8_t __dummy;
73 ui32_t mc_size;
76 union mem_ptr {
77 void *p_p;
78 struct mem_chunk *p_c;
79 char *p_cp;
80 ui8_t *p_ui8p;
82 #endif /* HAVE_DEBUG */
84 /* NYD, memory pool debug */
85 #ifdef HAVE_DEBUG
86 static ui32_t _nyd_curr, _nyd_level;
87 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
89 static size_t _mem_aall, _mem_acur, _mem_amax,
90 _mem_mall, _mem_mcur, _mem_mmax;
92 static struct mem_chunk *_mem_list, *_mem_free;
93 #endif
95 /* {hold,rele}_all_sigs() */
96 static size_t _alls_depth;
97 static sigset_t _alls_nset, _alls_oset;
99 /* {hold,rele}_sigs() */
100 static size_t _hold_sigdepth;
101 static sigset_t _hold_nset, _hold_oset;
103 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
104 #ifdef HAVE_COLOUR
105 static char * _colour_iso6429(char const *wish);
106 #endif
108 #ifdef HAVE_DEBUG
109 static void _nyd_print(struct nyd_info *nip);
110 #endif
112 #ifdef HAVE_COLOUR
113 static char *
114 _colour_iso6429(char const *wish)
116 char const * const wish_orig = wish;
117 char *xwish, *cp, cfg[3] = {0, 0, 0};
118 NYD_ENTER;
120 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
121 * value, ensure we have enough room for that */
123 size_t i = strlen(wish) +1;
124 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
125 memcpy(xwish, wish, i);
126 wish = xwish;
129 /* Iterate over the colour spec */
130 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
131 char *y, *x = strchr(cp, '=');
132 if (x == NULL) {
133 jbail:
134 fprintf(stderr, tr(527,
135 "Invalid colour specification \"%s\": >>> %s <<<\n"),
136 wish_orig, cp);
137 continue;
139 *x++ = '\0';
141 /* TODO convert the ft/fg/bg parser into a table-based one! */
142 if (!asccasecmp(cp, "ft")) {
143 if (!asccasecmp(x, "bold"))
144 cfg[0] = '1';
145 else if (!asccasecmp(x, "inverse"))
146 cfg[0] = '7';
147 else if (!asccasecmp(x, "underline"))
148 cfg[0] = '4';
149 else
150 goto jbail;
151 } else if (!asccasecmp(cp, "fg")) {
152 y = cfg + 1;
153 goto jiter_colour;
154 } else if (!asccasecmp(cp, "bg")) {
155 y = cfg + 2;
156 jiter_colour:
157 if (!asccasecmp(x, "black"))
158 *y = '0';
159 else if (!asccasecmp(x, "blue"))
160 *y = '4';
161 else if (!asccasecmp(x, "green"))
162 *y = '2';
163 else if (!asccasecmp(x, "red"))
164 *y = '1';
165 else if (!asccasecmp(x, "brown"))
166 *y = '3';
167 else if (!asccasecmp(x, "magenta"))
168 *y = '5';
169 else if (!asccasecmp(x, "cyan"))
170 *y = '6';
171 else if (!asccasecmp(x, "white"))
172 *y = '7';
173 else
174 goto jbail;
175 } else
176 goto jbail;
179 /* Restore our salloc() buffer, create return value */
180 xwish = UNCONST(wish);
181 if (cfg[0] || cfg[1] || cfg[2]) {
182 xwish[0] = '\033';
183 xwish[1] = '[';
184 xwish += 2;
185 if (cfg[0])
186 *xwish++ = cfg[0];
187 if (cfg[1]) {
188 if (cfg[0])
189 *xwish++ = ';';
190 xwish[0] = '3';
191 xwish[1] = cfg[1];
192 xwish += 2;
194 if (cfg[2]) {
195 if (cfg[0] || cfg[1])
196 *xwish++ = ';';
197 xwish[0] = '4';
198 xwish[1] = cfg[2];
199 xwish += 2;
201 *xwish++ = 'm';
203 *xwish = '\0';
204 NYD_LEAVE;
205 return UNCONST(wish);
207 #endif /* HAVE_COLOUR */
209 #ifdef HAVE_DEBUG
210 static void
211 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
213 char buf[80];
214 union {int i; size_t z;} u;
216 u.i = snprintf(buf, sizeof buf, "%c [%2u] %-25.25s %.16s:%-5u\n",
217 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
218 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
219 if (u.i > 0) {
220 u.z = u.i;
221 if (u.z > sizeof buf)
222 u.z = sizeof buf - 1; /* (Skip \0) */
223 write(STDERR_FILENO, buf, u.z);
226 #endif
228 FL void
229 panic(char const *format, ...)
231 va_list ap;
232 NYD_ENTER;
234 fprintf(stderr, tr(1, "Panic: "));
236 va_start(ap, format);
237 vfprintf(stderr, format, ap);
238 va_end(ap);
240 fputs("\n", stderr);
241 fflush(stderr);
242 NYD_LEAVE;
243 abort(); /* Was exit(EXIT_ERR); for a while, but no */
246 FL void
247 alert(char const *format, ...)
249 va_list ap;
250 NYD_ENTER;
252 fprintf(stderr, tr(1, "Panic: "));
254 va_start(ap, format);
255 vfprintf(stderr, format, ap);
256 va_end(ap);
258 fputs("\n", stderr);
259 fflush(stderr);
260 NYD_LEAVE;
263 FL sighandler_type
264 safe_signal(int signum, sighandler_type handler)
266 struct sigaction nact, oact;
267 sighandler_type rv;
268 NYD_ENTER;
270 nact.sa_handler = handler;
271 sigemptyset(&nact.sa_mask);
272 nact.sa_flags = 0;
273 #ifdef SA_RESTART
274 nact.sa_flags |= SA_RESTART;
275 #endif
276 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
277 NYD_LEAVE;
278 return rv;
281 FL void
282 hold_all_sigs(void)
284 NYD_ENTER;
285 if (_alls_depth++ == 0) {
286 sigfillset(&_alls_nset);
287 sigdelset(&_alls_nset, SIGABRT);
288 #ifdef SIGBUS
289 sigdelset(&_alls_nset, SIGBUS);
290 #endif
291 sigdelset(&_alls_nset, SIGCHLD);
292 sigdelset(&_alls_nset, SIGFPE);
293 sigdelset(&_alls_nset, SIGILL);
294 sigdelset(&_alls_nset, SIGKILL);
295 sigdelset(&_alls_nset, SIGSEGV);
296 sigdelset(&_alls_nset, SIGSTOP);
297 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
299 NYD_LEAVE;
302 FL void
303 rele_all_sigs(void)
305 NYD_ENTER;
306 if (--_alls_depth == 0)
307 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
308 NYD_LEAVE;
311 FL void
312 hold_sigs(void)
314 NYD_ENTER;
315 if (_hold_sigdepth++ == 0) {
316 sigemptyset(&_hold_nset);
317 sigaddset(&_hold_nset, SIGHUP);
318 sigaddset(&_hold_nset, SIGINT);
319 sigaddset(&_hold_nset, SIGQUIT);
320 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
322 NYD_LEAVE;
325 FL void
326 rele_sigs(void)
328 NYD_ENTER;
329 if (--_hold_sigdepth == 0)
330 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
331 NYD_LEAVE;
334 #ifdef HAVE_DEBUG
335 FL void
336 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
338 struct nyd_info *nip = _nyd_infos;
340 if (_nyd_curr != NELEM(_nyd_infos))
341 nip += _nyd_curr++;
342 else
343 _nyd_curr = 1;
344 nip->ni_file = file;
345 nip->ni_fun = fun;
346 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
347 nip->ni_level = ((act == 0) ? _nyd_level
348 : (act == 1) ? ++_nyd_level : _nyd_level--);
351 FL void
352 _nyd_oncrash(int signo)
354 struct sigaction xact;
355 sigset_t xset;
356 struct nyd_info *nip;
357 size_t i;
359 xact.sa_handler = SIG_DFL;
360 sigemptyset(&xact.sa_mask);
361 xact.sa_flags = 0;
362 sigaction(signo, &xact, NULL);
364 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
365 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
366 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
367 _nyd_print(nip++);
368 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
369 _nyd_print(nip++);
371 sigemptyset(&xset);
372 sigaddset(&xset, signo);
373 sigprocmask(SIG_UNBLOCK, &xset, NULL);
374 kill(0, signo);
375 for (;;)
376 _exit(EXIT_ERR);
378 #endif
380 FL void
381 touch(struct message *mp)
383 NYD_ENTER;
384 mp->m_flag |= MTOUCH;
385 if (!(mp->m_flag & MREAD))
386 mp->m_flag |= MREAD | MSTATUS;
387 NYD_LEAVE;
390 FL bool_t
391 is_dir(char const *name)
393 struct stat sbuf;
394 bool_t rv = FAL0;
395 NYD_ENTER;
397 if (!stat(name, &sbuf))
398 rv = (S_ISDIR(sbuf.st_mode) != 0);
399 NYD_LEAVE;
400 return rv;
403 FL int
404 argcount(char **argv)
406 char **ap;
407 NYD_ENTER;
409 for (ap = argv; *ap++ != NULL;)
411 NYD_LEAVE;
412 return (int)PTR2SIZE(ap - argv - 1);
415 FL int
416 screensize(void)
418 int s;
419 char *cp;
420 NYD_ENTER;
422 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
423 s = scrnheight - 4; /* XXX no magics */
424 NYD_LEAVE;
425 return s;
428 FL char const *
429 get_pager(void)
431 char const *cp;
432 NYD_ENTER;
434 cp = ok_vlook(PAGER);
435 if (cp == NULL || *cp == '\0')
436 cp = XPAGER;
437 NYD_LEAVE;
438 return cp;
441 FL size_t
442 paging_seems_sensible(void)
444 size_t rv = 0;
445 char const *cp;
446 NYD_ENTER;
448 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
449 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
450 NYD_LEAVE;
451 return rv;
454 FL void
455 page_or_print(FILE *fp, size_t lines)
457 size_t rows;
458 int c;
459 NYD_ENTER;
461 fflush_rewind(fp);
463 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
464 while ((c = getc(fp)) != EOF)
465 if (c == '\n' && ++lines > rows)
466 break;
467 really_rewind(fp);
470 if (rows != 0 && lines >= rows)
471 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
472 else
473 while ((c = getc(fp)) != EOF)
474 putchar(c);
475 NYD_LEAVE;
478 FL enum protocol
479 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
481 struct stat st;
482 char const *cp;
483 char *np;
484 size_t sz;
485 enum protocol rv = PROTO_UNKNOWN;
486 NYD_ENTER;
488 if (name[0] == '%' && name[1] == ':')
489 name += 2;
490 for (cp = name; *cp && *cp != ':'; cp++)
491 if (!alnumchar(*cp))
492 goto jfile;
494 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
495 if (!strncmp(name, "pop3://", 7)) {
496 #ifdef HAVE_POP3
497 rv = PROTO_POP3;
498 #else
499 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
500 #endif
501 } else if (!strncmp(name, "pop3s://", 8)) {
502 #if defined HAVE_POP3 && defined HAVE_SSL
503 rv = PROTO_POP3;
504 #else
505 # ifndef HAVE_POP3
506 fprintf(stderr, tr(216, "No POP3 support compiled in.\n"));
507 # endif
508 # ifndef HAVE_SSL
509 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
510 # endif
511 #endif
512 } else if (!strncmp(name, "imap://", 7)) {
513 #ifdef HAVE_IMAP
514 rv = PROTO_IMAP;
515 #else
516 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
517 #endif
518 } else if (!strncmp(name, "imaps://", 8)) {
519 #if defined HAVE_IMAP && defined HAVE_SSL
520 rv = PROTO_IMAP;
521 #else
522 # ifndef HAVE_IMAP
523 fprintf(stderr, tr(269, "No IMAP support compiled in.\n"));
524 # endif
525 # ifndef HAVE_SSL
526 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
527 # endif
528 #endif
530 goto jleave;
533 /* TODO This is the de facto maildir code and thus belongs into there!
534 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
535 * TODO or (more likely) in addition to *newfolders*) */
536 jfile:
537 rv = PROTO_FILE;
538 np = ac_alloc((sz = strlen(name)) + 4 +1);
539 memcpy(np, name, sz + 1);
540 if (!stat(name, &st)) {
541 if (S_ISDIR(st.st_mode) &&
542 (memcpy(np+sz, "/tmp", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
543 (memcpy(np+sz, "/new", 4), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
544 (memcpy(np+sz, "/cur", 4), !stat(np, &st) && S_ISDIR(st.st_mode)))
545 rv = PROTO_MAILDIR;
546 } else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
547 rv = PROTO_MAILDIR;
548 ac_free(np);
549 jleave:
550 NYD_LEAVE;
551 return rv;
554 FL ui32_t
555 torek_hash(char const *name)
557 /* Chris Torek's hash.
558 * NOTE: need to change *at least* create-okey-map.pl when changing the
559 * algorithm!! */
560 ui32_t h = 0;
561 NYD_ENTER;
563 while (*name != '\0') {
564 h *= 33;
565 h += *name++;
567 NYD_LEAVE;
568 return h;
571 FL unsigned
572 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
574 unsigned h = 0, g;
575 NYD_ENTER;
577 cp--;
578 while (*++cp) {
579 h = (h << 4 & 0xffffffff) + (*cp&0377);
580 if ((g = h & 0xf0000000) != 0) {
581 h = h ^ g >> 24;
582 h = h ^ g;
585 NYD_LEAVE;
586 return h;
589 FL ui32_t
590 nextprime(ui32_t n)
592 static ui32_t const primes[] = {
593 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
594 131071, 262139, 524287, 1048573, 2097143, 4194301,
595 8388593, 16777213, 33554393, 67108859, 134217689,
596 268435399, 536870909, 1073741789, 2147483647
599 ui32_t mprime = 7, cutlim;
600 size_t i;
601 NYD_ENTER;
603 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
605 for (i = 0; i < NELEM(primes); i++)
606 if ((mprime = primes[i]) >= cutlim)
607 break;
608 if (i == NELEM(primes) && mprime < n)
609 mprime = n;
610 NYD_LEAVE;
611 return mprime;
614 FL int
615 expand_shell_escape(char const **s, bool_t use_nail_extensions)
617 char const *xs;
618 int c, n;
619 NYD_ENTER;
621 xs = *s;
623 if ((c = *xs & 0xFF) == '\0')
624 goto jleave;
625 ++xs;
626 if (c != '\\')
627 goto jleave;
629 switch ((c = *xs & 0xFF)) {
630 case '\\': break;
631 case 'a': c = '\a'; break;
632 case 'b': c = '\b'; break;
633 case 'c': c = PROMPT_STOP; break;
634 case 'f': c = '\f'; break;
635 case 'n': c = '\n'; break;
636 case 'r': c = '\r'; break;
637 case 't': c = '\t'; break;
638 case 'v': c = '\v'; break;
639 case '0':
640 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
641 c <<= 3;
642 c |= *xs - '0';
644 goto jleave;
645 /* S-nail extension for nice (get)prompt(()) support */
646 case '&':
647 case '?':
648 case '$':
649 case '@':
650 if (use_nail_extensions) {
651 switch (c) {
652 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
653 case '?': c = exec_last_comm_error ? '1' : '0'; break;
654 case '$': c = PROMPT_DOLLAR; break;
655 case '@': c = PROMPT_AT; break;
657 break;
659 /* FALLTHRU */
660 case '\0':
661 /* A sole <backslash> at EOS is treated as-is! */
662 /* FALLTHRU */
663 default:
664 c = '\\';
665 goto jleave;
667 ++xs;
668 jleave:
669 *s = xs;
670 NYD_LEAVE;
671 return c;
674 FL char *
675 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
677 static char buf[PROMPT_BUFFER_SIZE];
679 char *cp;
680 char const *ccp_base, *ccp;
681 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
682 bool_t run2;
683 NYD_ENTER;
685 cp = buf;
686 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0')
687 goto jleave;
688 NATCH_CHAR( cclen_base = strlen(ccp_base); )
690 dfmaxlen = 0; /* keep CC happy */
691 run2 = FAL0;
692 jredo:
693 ccp = ccp_base;
694 NATCH_CHAR( cclen = cclen_base; )
695 maxlen = sizeof(buf) -1;
697 for (;;) {
698 size_t l;
699 int c;
701 if (maxlen == 0)
702 goto jleave;
703 #ifdef HAVE_NATCH_CHAR
704 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
705 if (c <= 0) {
706 mblen(NULL, 0);
707 if (c < 0) {
708 *buf = '?';
709 cp = buf + 1;
710 goto jleave;
712 break;
713 } else if ((l = c) > 1) {
714 if (run2) {
715 memcpy(cp, ccp, l);
716 cp += l;
718 ccp += l;
719 maxlen -= l;
720 continue;
721 } else
722 #endif
723 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
724 if (run2)
725 *cp++ = (char)c;
726 --maxlen;
727 continue;
729 if (c == 0 || c == PROMPT_STOP)
730 break;
732 if (run2) {
733 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
734 if (a == NULL)
735 a = "";
736 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
737 cp += l;
738 maxlen -= l;
739 dfmaxlen -= l;
744 if (!run2) {
745 run2 = TRU1;
746 dfmaxlen = maxlen;
747 goto jredo;
749 jleave:
750 *cp = '\0';
751 NYD_LEAVE;
752 return buf;
755 FL char *
756 nodename(int mayoverride)
758 static char *sys_hostname, *hostname; /* XXX free-at-exit */
760 struct utsname ut;
761 char *hn;
762 #ifdef HAVE_SOCKETS
763 # ifdef HAVE_IPV6
764 struct addrinfo hints, *res;
765 # else
766 struct hostent *hent;
767 # endif
768 #endif
769 NYD_ENTER;
771 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
773 } else if ((hn = sys_hostname) == NULL) {
774 uname(&ut);
775 hn = ut.nodename;
776 #ifdef HAVE_SOCKETS
777 # ifdef HAVE_IPV6
778 memset(&hints, 0, sizeof hints);
779 hints.ai_family = AF_UNSPEC;
780 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
781 hints.ai_flags = AI_CANONNAME;
782 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
783 if (res->ai_canonname != NULL) {
784 size_t l = strlen(res->ai_canonname) +1;
785 hn = ac_alloc(l);
786 memcpy(hn, res->ai_canonname, l);
788 freeaddrinfo(res);
790 # else
791 hent = gethostbyname(hn);
792 if (hent != NULL)
793 hn = hent->h_name;
794 # endif
795 #endif
796 sys_hostname = sstrdup(hn);
797 #if defined HAVE_SOCKETS && defined HAVE_IPV6
798 if (hn != ut.nodename)
799 ac_free(hn);
800 #endif
801 hn = sys_hostname;
804 if (hostname != NULL && hostname != sys_hostname)
805 free(hostname);
806 hostname = sstrdup(hn);
807 NYD_LEAVE;
808 return hostname;
811 FL bool_t
812 url_parse(struct url *urlp, enum cproto cproto, char const *data)
814 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
815 # define __ALLPROTO
816 #endif
817 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
818 # define __ANYPROTO
819 char *cp, *x;
820 #endif
821 bool_t rv = FAL0;
822 NYD_ENTER;
823 UNUSED(data);
825 memset(urlp, 0, sizeof *urlp);
826 urlp->url_input = data;
827 urlp->url_cproto = cproto;
829 /* Network protocol */
830 #define _protox(X,Y) \
831 urlp->url_portno = Y;\
832 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
833 urlp->url_proto[sizeof(X) -1] = '\0';\
834 urlp->url_proto_len = sizeof(X) -1;\
835 urlp->url_proto_xlen = sizeof(X "://") -1
836 #define __if(X,Y,Z) \
837 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
838 _protox(X, Y);\
839 data += sizeof(X "://") -1;\
840 do { Z; } while (0);\
841 goto juser;\
843 #define _if(X,Y) __if(X, Y, (void)0)
844 #ifdef HAVE_SSL
845 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
846 #else
847 # define _ifs(X,Y) goto jeproto;
848 #endif
850 switch (cproto) {
851 case CPROTO_SMTP:
852 #ifdef HAVE_SMTP
853 _if ("smtp", 25)
854 _if ("submission", 587)
855 _ifs ("smtps", 465)
856 _protox("smtp", 25);
857 break;
858 #else
859 goto jeproto;
860 #endif
861 case CPROTO_POP3:
862 #ifdef HAVE_POP3
863 _if ("pop3", 110)
864 _ifs ("pop3s", 995)
865 _protox("pop3", 110);
866 break;
867 #else
868 goto jeproto;
869 #endif
870 case CPROTO_IMAP:
871 #ifdef HAVE_IMAP
872 _if ("imap", 143)
873 _ifs ("imaps", 993)
874 _protox("imap", 143);
875 break;
876 #else
877 goto jeproto;
878 #endif
881 #undef _ifs
882 #undef _if
883 #undef __if
884 #undef _protox
886 if (strstr(data, "://") != NULL) {
887 #if !defined __ALLPROTO || !defined HAVE_SSL
888 jeproto:
889 #endif
890 fprintf(stderr, tr(574, "URL `proto://' prefix invalid: `%s'\n"),
891 urlp->url_input);
892 goto jleave;
894 #ifdef __ANYPROTO
896 /* User and password, I */
897 juser:
898 if ((cp = UNCONST(last_at_before_slash(data))) == NULL)
899 goto jserver;
901 urlp->url_had_user = TRU1;
902 urlp->url_user.s = savestrbuf(data, urlp->url_user.l = PTR2SIZE(cp - data));
903 data = ++cp;
905 /* And also have a password? */
906 if ((cp = strchr(urlp->url_user.s, ':')) != NULL) {
907 x = urlp->url_user.s;
908 urlp->url_user.s = savestrbuf(x, urlp->url_user.l = PTR2SIZE(cp - x));
909 urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(++cp));
910 urlp->url_pass_enc.l = strlen(
911 urlp->url_pass_enc.s = urlxenc(urlp->url_pass.s));
914 /* Servername and port -- and possible path suffix */
915 jserver:
916 if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */
917 char *eptr;
918 long l;
920 urlp->url_port = x = savestr(x = cp + 1);
921 if ((x = strchr(x, '/')) != NULL)
922 *x = '\0';
923 l = strtol(urlp->url_port, &eptr, 10);
924 if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) {
925 fprintf(stderr, tr(573, "URL with invalid port number: `%s'\n"),
926 urlp->url_input);
927 goto jleave;
929 urlp->url_portno = (ui16_t)l;
930 } else {
931 if ((x = strchr(data, '/')) != NULL)
932 data = savestrbuf(data, PTR2SIZE(x - data));
933 cp = UNCONST(data + strlen(data));
936 /* A (non-empty) path may only occur with IMAP */
937 if (x != NULL && x[1] != '\0') {
938 if (cproto != CPROTO_IMAP) {
939 fprintf(stderr, tr(575, "URL protocol doesn't support paths: `%s'\n"),
940 urlp->url_input);
941 goto jleave;
943 urlp->url_path.l = strlen(++x);
944 urlp->url_path.s = savestrbuf(x, urlp->url_path.l);
947 urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
948 { size_t i;
949 for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
950 *cp = lowerconv(*cp);
953 /* HOST:PORT */
954 { size_t i;
955 struct str *s = &urlp->url_hp;
957 s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1);
958 memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
959 if (urlp->url_port != NULL) {
960 size_t j = strlen(urlp->url_port);
961 s->s[i++] = ':';
962 memcpy(s->s + i, urlp->url_port, j);
963 i += j;
965 s->s[i] = '\0';
966 s->l = i;
969 /* User, II
970 * If there was no user in the URL, do we have *user-HOST* or *user*? */
971 if (!urlp->url_had_user) {
972 char const usrstr[] = "user-";
973 size_t const usrlen = sizeof(usrstr) -1;
975 cp = ac_alloc(usrlen + urlp->url_hp.l +1);
976 memcpy(cp, usrstr, usrlen);
977 memcpy(cp + usrlen, urlp->url_hp.s, urlp->url_hp.l +1);
978 if ((urlp->url_user.s = vok_vlook(cp)) == NULL) {
979 cp[usrlen - 1] = '\0';
980 if ((urlp->url_user.s = vok_vlook(cp)) == NULL)
981 urlp->url_user.s = UNCONST(myname);
983 ac_free(cp);
986 urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(urlp->url_user.s));
987 urlp->url_user_enc.l = strlen(
988 urlp->url_user_enc.s = urlxenc(urlp->url_user.s));
990 /* USER@HOST:PORT */
991 if (urlp->url_user_enc.l == 0)
992 urlp->url_uhp = urlp->url_hp;
993 else {
994 struct str *s = &urlp->url_uhp;
995 size_t i = urlp->url_user_enc.l;
997 s->s = salloc(i + 1 + urlp->url_hp.l +1);
998 if (i > 0) {
999 memcpy(s->s, urlp->url_user_enc.s, i);
1000 s->s[i++] = '@';
1002 memcpy(s->s + i, urlp->url_hp.s, urlp->url_hp.l +1);
1003 i += urlp->url_hp.l;
1004 s->l = i;
1007 /* USER@HOST
1008 * For SMTP we apply ridiculously complicated *v15-compat* plus
1009 * *smtp-hostname* / *hostname* dependent rules */
1010 { struct str h;
1012 if (cproto == CPROTO_SMTP && ok_blook(v15_compat) &&
1013 (cp = ok_vlook(smtp_hostname)) != NULL) {
1014 if (*cp == '\0')
1015 cp = nodename(1);
1016 h.s = savestrbuf(cp, h.l = strlen(cp));
1017 } else
1018 h = urlp->url_host;
1020 if (urlp->url_user_enc.l == 0)
1021 urlp->url_uh = h;
1022 else {
1023 struct str *s = &urlp->url_uh;
1024 size_t i = urlp->url_user_enc.l;
1026 s->s = salloc(i + 1 + h.l +1);
1027 if (i > 0) {
1028 memcpy(s->s, urlp->url_user_enc.s, i);
1029 s->s[i++] = '@';
1031 memcpy(s->s + i, h.s, h.l +1);
1032 i += h.l;
1033 s->l = i;
1037 /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */
1038 { size_t i;
1039 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_uhp.l) +
1040 1 + urlp->url_path.l +1);
1042 urlp->url_proto[urlp->url_proto_len] = ':';
1043 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_uhp.s, urlp->url_uhp.l +1);
1044 urlp->url_proto[urlp->url_proto_len] = '\0';
1046 if (urlp->url_path.l == 0)
1047 urlp->url_puhp = urlp->url_puhpp = ud;
1048 else {
1049 urlp->url_puhp = savestrbuf(ud, i);
1050 urlp->url_puhpp = ud;
1051 ud += i;
1052 *ud++ = '/';
1053 memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
1057 rv = TRU1;
1058 #endif /* __ANYPROTO */
1059 jleave:
1060 NYD_LEAVE;
1061 return rv;
1062 #undef __ANYPROTO
1063 #undef __ALLPROTO
1066 FL bool_t
1067 ccred_lookup_old(struct ccred *ccp, enum cproto cproto, char const *addr)
1069 char const *pname, *pxstr, *authdef;
1070 size_t pxlen, addrlen, i;
1071 char *vbuf, *s;
1072 ui8_t authmask;
1073 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1074 ware = NONE;
1075 bool_t addr_is_nuser = FAL0; /* XXX v15.0 legacy! v15_compat */
1076 NYD_ENTER;
1078 memset(ccp, 0, sizeof *ccp);
1080 switch (cproto) {
1081 default:
1082 case CPROTO_SMTP:
1083 pname = "SMTP";
1084 pxstr = "smtp-auth";
1085 pxlen = sizeof("smtp-auth") -1;
1086 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1087 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1088 authdef = "none";
1089 addr_is_nuser = TRU1;
1090 break;
1091 case CPROTO_POP3:
1092 pname = "POP3";
1093 pxstr = "pop3-auth";
1094 pxlen = sizeof("pop3-auth") -1;
1095 authmask = AUTHTYPE_PLAIN;
1096 authdef = "plain";
1097 break;
1098 case CPROTO_IMAP:
1099 pname = "IMAP";
1100 pxstr = "imap-auth";
1101 pxlen = sizeof("imap-auth") -1;
1102 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1103 authdef = "login";
1104 break;
1107 ccp->cc_cproto = cproto;
1108 addrlen = strlen(addr);
1109 vbuf = ac_alloc(pxlen + addrlen + sizeof("-password-")-1 +1);
1110 memcpy(vbuf, pxstr, pxlen);
1112 /* Authentication type */
1113 vbuf[pxlen] = '-';
1114 memcpy(vbuf + pxlen + 1, addr, addrlen +1);
1115 if ((s = vok_vlook(vbuf)) == NULL) {
1116 vbuf[pxlen] = '\0';
1117 if ((s = vok_vlook(vbuf)) == NULL)
1118 s = UNCONST(authdef);
1121 if (!asccasecmp(s, "none")) {
1122 ccp->cc_auth = "NONE";
1123 ccp->cc_authtype = AUTHTYPE_NONE;
1124 /*ware = NONE;*/
1125 } else if (!asccasecmp(s, "plain")) {
1126 ccp->cc_auth = "PLAIN";
1127 ccp->cc_authtype = AUTHTYPE_PLAIN;
1128 ware = REQ_PASS | REQ_USER;
1129 } else if (!asccasecmp(s, "login")) {
1130 ccp->cc_auth = "LOGIN";
1131 ccp->cc_authtype = AUTHTYPE_LOGIN;
1132 ware = REQ_PASS | REQ_USER;
1133 } else if (!asccasecmp(s, "cram-md5")) {
1134 ccp->cc_auth = "CRAM-MD5";
1135 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1136 ware = REQ_PASS | REQ_USER;
1137 } else if (!asccasecmp(s, "gssapi")) {
1138 ccp->cc_auth = "GSS-API";
1139 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1140 ware = REQ_USER;
1141 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1142 } else if (!asccasecmp(cp, "sasl")) {
1143 ware = REQ_USER | WANT_PASS;
1144 #endif
1147 /* Verify method */
1148 if (!(ccp->cc_authtype & authmask)) {
1149 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1150 pname, s);
1151 ccp = NULL;
1152 goto jleave;
1154 #ifndef HAVE_MD5
1155 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1156 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1157 ccp = NULL;
1158 goto jleave;
1160 #endif
1161 #ifndef HAVE_GSSAPI
1162 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1163 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1164 ccp = NULL;
1165 goto jleave;
1167 #endif
1169 /* User name */
1170 if (!(ware & (WANT_USER | REQ_USER)))
1171 goto jpass;
1173 if (!addr_is_nuser) {
1174 if ((s = UNCONST(last_at_before_slash(addr))) != NULL) {
1175 ccp->cc_user.s = urlxdec(savestrbuf(addr, PTR2SIZE(s - addr)));
1176 ccp->cc_user.l = strlen(ccp->cc_user.s);
1177 } else if (ware & REQ_USER)
1178 goto jgetuser;
1179 goto jpass;
1182 memcpy(vbuf + pxlen, "-user-", i = sizeof("-user-") -1);
1183 i += pxlen;
1184 memcpy(vbuf + i, addr, addrlen +1);
1185 if ((s = vok_vlook(vbuf)) == NULL) {
1186 vbuf[--i] = '\0';
1187 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_USER)) {
1188 if ((s = getuser(NULL)) != NULL)
1189 s = savestr(s);
1190 else {
1191 jgetuser: /* TODO v15.0: today we simply bail, but we should call getuser().
1192 * TODO even better: introduce `PROTO-user' and `PROTO-pass' and
1193 * TODO check that first, then! change control flow, grow `vbuf' */
1194 fprintf(stderr, tr(274,
1195 "A user is necessary for %s authentication.\n"), pname);
1196 ccp = NULL;
1197 goto jleave;
1201 ccp->cc_user.l = strlen(ccp->cc_user.s = s);
1203 /* Password */
1204 jpass:
1205 if (!(ware & (WANT_PASS | REQ_PASS)))
1206 goto jleave;
1208 if (!addr_is_nuser) {
1209 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1210 } else {
1211 memcpy(vbuf + pxlen, "-password-", i = sizeof("-password-") -1);
1212 i += pxlen;
1214 memcpy(vbuf + i, addr, addrlen +1);
1215 if ((s = vok_vlook(vbuf)) == NULL) {
1216 vbuf[--i] = '\0';
1217 if ((!addr_is_nuser || (s = vok_vlook(vbuf)) == NULL) &&
1218 (ware & REQ_PASS)) {
1219 if ((s = getpassword(NULL)) == NULL) {
1220 fprintf(stderr, tr(275,
1221 "A password is necessary for %s authentication.\n"), pname);
1222 ccp = NULL;
1223 goto jleave;
1227 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1229 jleave:
1230 ac_free(vbuf);
1231 NYD_LEAVE;
1232 return (ccp != NULL);
1235 FL bool_t
1236 ccred_lookup(struct ccred *ccp, struct url *urlp)
1238 char const *pstr, *authdef;
1239 size_t plen, i;
1240 char *vbuf, *s;
1241 ui8_t authmask;
1242 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1243 ware = NONE;
1244 NYD_ENTER;
1246 memset(ccp, 0, sizeof *ccp);
1247 ccp->cc_user = urlp->url_user;
1249 switch ((ccp->cc_cproto = urlp->url_cproto)) {
1250 default:
1251 case CPROTO_SMTP:
1252 pstr = "smtp";
1253 plen = sizeof("smtp") -1;
1254 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1255 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1256 authdef = "none";
1257 break;
1258 case CPROTO_POP3:
1259 pstr = "pop3";
1260 plen = sizeof("pop3") -1;
1261 authmask = AUTHTYPE_PLAIN;
1262 authdef = "plain";
1263 break;
1264 case CPROTO_IMAP:
1265 pstr = "imap";
1266 plen = sizeof("imap") -1;
1267 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1268 authdef = "login";
1269 break;
1272 /* Note: "password-" is longer than "-auth-", ditto .url_uhp and .url_hp */
1273 vbuf = ac_alloc(plen + sizeof("password-")-1 + urlp->url_uhp.l +1);
1274 memcpy(vbuf, pstr, plen);
1276 /* Authentication type */
1277 memcpy(vbuf + plen, "-auth-", i = sizeof("-auth-") -1);
1278 i += plen;
1279 /* -USER@HOST, -HOST, '' */
1280 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1281 if ((s = vok_vlook(vbuf)) == NULL) {
1282 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1283 if ((s = vok_vlook(vbuf)) == NULL) {
1284 vbuf[plen + sizeof("-auth") -1] = '\0';
1285 if ((s = vok_vlook(vbuf)) == NULL)
1286 s = UNCONST(authdef);
1290 if (!asccasecmp(s, "none")) {
1291 ccp->cc_auth = "NONE";
1292 ccp->cc_authtype = AUTHTYPE_NONE;
1293 /*ware = NONE;*/
1294 } else if (!asccasecmp(s, "plain")) {
1295 ccp->cc_auth = "PLAIN";
1296 ccp->cc_authtype = AUTHTYPE_PLAIN;
1297 ware = REQ_PASS | REQ_USER;
1298 } else if (!asccasecmp(s, "login")) {
1299 ccp->cc_auth = "LOGIN";
1300 ccp->cc_authtype = AUTHTYPE_LOGIN;
1301 ware = REQ_PASS | REQ_USER;
1302 } else if (!asccasecmp(s, "cram-md5")) {
1303 ccp->cc_auth = "CRAM-MD5";
1304 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1305 ware = REQ_PASS | REQ_USER;
1306 } else if (!asccasecmp(s, "gssapi")) {
1307 ccp->cc_auth = "GSS-API";
1308 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1309 ware = REQ_USER;
1310 #if 0 /* TODO SASL (homebrew `auto'matic indeed) */
1311 } else if (!asccasecmp(cp, "sasl")) {
1312 ware = REQ_USER | WANT_PASS;
1313 #endif
1316 /* Verify method */
1317 if (!(ccp->cc_authtype & authmask)) {
1318 fprintf(stderr, tr(273, "Unsupported %s authentication method: %s\n"),
1319 pstr, s);
1320 ccp = NULL;
1321 goto jleave;
1323 #ifndef HAVE_MD5
1324 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1325 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
1326 ccp = NULL;
1327 goto jleave;
1329 #endif
1330 #ifndef HAVE_GSSAPI
1331 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1332 fprintf(stderr, tr(272, "No GSS-API support compiled in.\n"));
1333 ccp = NULL;
1334 goto jleave;
1336 #endif
1338 /* Password */
1339 if ((ccp->cc_pass = urlp->url_pass).s != NULL)
1340 goto jleave;
1342 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1343 /* -USER@HOST, -HOST, '' */
1344 memcpy(vbuf + i, urlp->url_uhp.s, urlp->url_uhp.l +1);
1345 if ((s = vok_vlook(vbuf)) == NULL) {
1346 memcpy(vbuf + i, urlp->url_hp.s, urlp->url_hp.l +1);
1347 if ((s = vok_vlook(vbuf)) == NULL) {
1348 vbuf[--i] = '\0';
1349 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_PASS)) {
1350 if ((s = getpassword(NULL)) != NULL)
1351 s = savestr(s);
1352 else {
1353 fprintf(stderr, tr(275,
1354 "A password is necessary for %s authentication.\n"), pstr);
1355 ccp = NULL;
1356 goto jleave;
1361 ccp->cc_pass.l = strlen(ccp->cc_pass.s = urlxdec(s));
1363 jleave:
1364 ac_free(vbuf);
1365 NYD_LEAVE;
1366 return (ccp != NULL);
1369 FL char *
1370 getrandstring(size_t length)
1372 static unsigned char nodedigest[16];
1373 static pid_t pid;
1375 struct str b64;
1376 char *data, *cp;
1377 size_t i;
1378 int fd = -1;
1379 #ifdef HAVE_MD5
1380 md5_ctx ctx;
1381 #else
1382 size_t j;
1383 #endif
1384 NYD_ENTER;
1386 data = ac_alloc(length);
1388 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
1389 length != (size_t)read(fd, data, length)) {
1390 if (pid == 0) {
1391 pid = getpid();
1392 srand(pid);
1393 cp = nodename(0);
1394 #ifdef HAVE_MD5
1395 md5_init(&ctx);
1396 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
1397 md5_final(nodedigest, &ctx);
1398 #else
1399 /* In that case it's only used for boundaries and Message-Id:s so that
1400 * srand(3) should suffice */
1401 j = strlen(cp) + 1;
1402 for (i = 0; i < sizeof(nodedigest); ++i)
1403 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
1404 #endif
1406 for (i = 0; i < length; i++)
1407 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
1408 nodedigest[i % sizeof nodedigest]);
1410 if (fd >= 0)
1411 close(fd);
1413 b64_encode_buf(&b64, data, length, B64_SALLOC);
1414 ac_free(data);
1415 assert(length < b64.l);
1416 b64.s[length] = '\0';
1417 NYD_LEAVE;
1418 return b64.s;
1421 #ifdef HAVE_MD5
1422 FL char *
1423 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
1425 char const *cp = vp;
1426 size_t i, j;
1427 NYD_ENTER;
1429 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
1430 j = i << 1;
1431 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
1432 hex[++j] = hexchar(cp[i] & 0x0f);
1434 NYD_LEAVE;
1435 return hex;
1438 FL char *
1439 cram_md5_string(char const *user, char const *pass, char const *b64)
1441 struct str in, out;
1442 char digest[16], *cp;
1443 size_t lu;
1444 NYD_ENTER;
1446 out.s = NULL;
1447 in.s = UNCONST(b64);
1448 in.l = strlen(in.s);
1449 b64_decode(&out, &in, NULL);
1450 assert(out.s != NULL);
1452 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
1453 free(out.s);
1454 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
1456 lu = strlen(user);
1457 in.l = lu + MD5TOHEX_SIZE +1;
1458 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
1459 memcpy(in.s, user, lu);
1460 in.s[lu++] = ' ';
1461 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
1462 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
1463 ac_free(in.s);
1464 NYD_LEAVE;
1465 return out.s;
1468 FL void
1469 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
1470 void *digest)
1473 * This code is taken from
1475 * Network Working Group H. Krawczyk
1476 * Request for Comments: 2104 IBM
1477 * Category: Informational M. Bellare
1478 * UCSD
1479 * R. Canetti
1480 * IBM
1481 * February 1997
1484 * HMAC: Keyed-Hashing for Message Authentication
1486 md5_ctx context;
1487 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
1488 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
1489 unsigned char tk[16];
1490 int i;
1491 NYD_ENTER;
1493 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1494 if (key_len > 64) {
1495 md5_ctx tctx;
1497 md5_init(&tctx);
1498 md5_update(&tctx, key, key_len);
1499 md5_final(tk, &tctx);
1501 key = tk;
1502 key_len = 16;
1505 /* the HMAC_MD5 transform looks like:
1507 * MD5(K XOR opad, MD5(K XOR ipad, text))
1509 * where K is an n byte key
1510 * ipad is the byte 0x36 repeated 64 times
1511 * opad is the byte 0x5c repeated 64 times
1512 * and text is the data being protected */
1514 /* start out by storing key in pads */
1515 memset(k_ipad, 0, sizeof k_ipad);
1516 memset(k_opad, 0, sizeof k_opad);
1517 memcpy(k_ipad, key, key_len);
1518 memcpy(k_opad, key, key_len);
1520 /* XOR key with ipad and opad values */
1521 for (i=0; i<64; i++) {
1522 k_ipad[i] ^= 0x36;
1523 k_opad[i] ^= 0x5c;
1526 /* perform inner MD5 */
1527 md5_init(&context); /* init context for 1st pass */
1528 md5_update(&context, k_ipad, 64); /* start with inner pad */
1529 md5_update(&context, text, text_len); /* then text of datagram */
1530 md5_final(digest, &context); /* finish up 1st pass */
1532 /* perform outer MD5 */
1533 md5_init(&context); /* init context for 2nd pass */
1534 md5_update(&context, k_opad, 64); /* start with outer pad */
1535 md5_update(&context, digest, 16); /* then results of 1st hash */
1536 md5_final(digest, &context); /* finish up 2nd pass */
1537 NYD_LEAVE;
1539 #endif /* HAVE_MD5 */
1541 FL enum okay
1542 makedir(char const *name)
1544 struct stat st;
1545 enum okay rv = STOP;
1546 NYD_ENTER;
1548 if (!mkdir(name, 0700))
1549 rv = OKAY;
1550 else {
1551 int e = errno;
1552 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1553 S_ISDIR(st.st_mode))
1554 rv = OKAY;
1556 NYD_LEAVE;
1557 return rv;
1560 #ifdef HAVE_FCHDIR
1561 FL enum okay
1562 cwget(struct cw *cw)
1564 enum okay rv = STOP;
1565 NYD_ENTER;
1567 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1568 goto jleave;
1569 if (fchdir(cw->cw_fd) == -1) {
1570 close(cw->cw_fd);
1571 goto jleave;
1573 rv = OKAY;
1574 jleave:
1575 NYD_LEAVE;
1576 return rv;
1579 FL enum okay
1580 cwret(struct cw *cw)
1582 enum okay rv = STOP;
1583 NYD_ENTER;
1585 if (!fchdir(cw->cw_fd))
1586 rv = OKAY;
1587 NYD_LEAVE;
1588 return rv;
1591 FL void
1592 cwrelse(struct cw *cw)
1594 NYD_ENTER;
1595 close(cw->cw_fd);
1596 NYD_LEAVE;
1599 #else /* !HAVE_FCHDIR */
1600 FL enum okay
1601 cwget(struct cw *cw)
1603 enum okay rv = STOP;
1604 NYD_ENTER;
1606 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1607 rv = OKAY;
1608 NYD_LEAVE;
1609 return rv;
1612 FL enum okay
1613 cwret(struct cw *cw)
1615 enum okay rv = STOP;
1616 NYD_ENTER;
1618 if (!chdir(cw->cw_wd))
1619 rv = OKAY;
1620 NYD_LEAVE;
1621 return rv;
1624 FL void
1625 cwrelse(struct cw *cw)
1627 NYD_ENTER;
1628 UNUSED(cw);
1629 NYD_LEAVE;
1631 #endif /* !HAVE_FCHDIR */
1633 FL size_t
1634 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1636 size_t rv;
1637 NYD_ENTER;
1639 #ifdef HAVE_NATCH_CHAR
1640 maxlen = MIN(maxlen, blen);
1641 for (rv = 0; maxlen > 0;) {
1642 int ml = mblen(buf, maxlen);
1643 if (ml <= 0) {
1644 mblen(NULL, 0);
1645 break;
1647 buf += ml;
1648 rv += ml;
1649 maxlen -= ml;
1651 #else
1652 rv = MIN(blen, maxlen);
1653 #endif
1654 NYD_LEAVE;
1655 return rv;
1658 FL char *
1659 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1661 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1662 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1663 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1664 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE */
1665 #ifdef HAVE_NATCH_CHAR
1666 char const _bire[2][6] = { "\xE2\x81\xA8" "\xE2\x81\xA9",
1667 "\xE2\x80\x8E" "\xE2\x80\x8E"
1668 /* worse results: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1669 }, *birep = NULL /* old gcc warning */;
1670 #endif
1671 int col_orig = col, n, sz;
1672 bool_t isbidi, isuni, isrepl;
1673 char *nb, *np;
1674 NYD_ENTER;
1676 /* Bidi only on request and when there is 8-bit data */
1677 isbidi = isuni = FAL0;
1678 #ifdef HAVE_NATCH_CHAR
1679 if ((isuni = ((options & OPT_UNICODE) != 0))) {
1680 if ((nb = ok_vlook(headline_bidi)) == NULL)
1681 goto jnobidi;
1682 for (birep = cp;; ++birep) {
1683 if (*birep == '\0')
1684 goto jnobidi;
1685 else if (*birep & (char)0x80) {
1686 /* TODO Checking for BIDI character: use S-CText fromutf8
1687 * TODO plus isrighttoleft (or whatever there will be)! */
1688 ui32_t c, x = (ui8_t)*birep;
1689 if ((x & 0xE0) == 0xC0) {
1690 c = x & ~0xC0;
1691 if ((x = (ui8_t)*++birep) == '\0')
1692 goto jnobidi;
1693 } else if ((x & 0xF0) == 0xE0) {
1694 c = x & ~0xE0;
1695 if ((x = (ui8_t)*++birep) == '\0' || *++birep == '\0')
1696 goto jnobidi;
1697 c <<= 6;
1698 c |= x & 0x7F;
1699 x = (ui8_t)*birep;
1700 } else {
1701 c = x & ~0xF0;
1702 if ((x = (ui8_t)*++birep) == '\0' ||
1703 *++birep == '\0' || birep[1] == '\0')
1704 goto jnobidi;
1705 c <<= 6;
1706 c |= x & 0x7F;
1707 c <<= 6;
1708 c |= (x = (ui8_t)*birep) & 0x7F;
1709 x = (ui8_t)*++birep;
1711 c <<= 6;
1712 c |= x & 0x7F;
1713 /* (Very very fuzzy, awaiting S-CText for good) */
1714 if ((c >= 0x05BE && c <= 0x08E3) ||
1715 (c >= 0xFB1D && c <= 0xFEFC) ||
1716 (c >= 0x10800 && c <= 0x10C48) ||
1717 (c >= 0x1EE00 && c <= 0x1EEF1))
1718 break;
1722 isbidi = TRU1;
1723 birep = _bire[0];
1724 switch (*nb) {
1725 case '3': col -= 2;
1726 case '2': birep = _bire[1];
1727 break;
1728 case '1': col -= 2;
1729 default: break;
1731 if (col < 0)
1732 col = 0;
1734 jnobidi:
1735 #endif /* HAVE_NATCH_CHAR */
1737 np = nb = salloc(mb_cur_max * strlen(cp) +
1738 ((fill ? col : 0) + (isbidi ? sizeof(_bire[0]) : 0) +1));
1740 #ifdef HAVE_NATCH_CHAR
1741 if (isbidi) {
1742 np[0] = birep[0];
1743 np[1] = birep[1];
1744 np[2] = birep[2];
1745 np += 3;
1747 #endif
1749 while (*cp != '\0') {
1750 #ifdef HAVE_C90AMEND1
1751 if (mb_cur_max > 1) {
1752 wchar_t wc;
1754 n = 1;
1755 isrepl = TRU1;
1756 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1757 sz = 1;
1758 else if (iswprint(wc)) {
1759 # ifndef HAVE_WCWIDTH
1760 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1761 # else
1762 if ((n = wcwidth(wc)) == -1)
1763 n = 1;
1764 else
1765 # endif
1766 isrepl = FAL0;
1768 } else
1769 #endif
1770 n = sz = 1,
1771 isrepl = !isprint(*cp);
1773 if (n > col)
1774 break;
1775 col -= n;
1777 if (isrepl) {
1778 if (isuni) {
1779 np[0] = (char)0xEFu;
1780 np[1] = (char)0xBFu;
1781 np[2] = (char)0xBDu;
1782 np += 3;
1783 } else
1784 *np++ = '?';
1785 cp += sz;
1786 } else if (sz == 1 && spacechar(*cp)) {
1787 *np++ = ' ';
1788 ++cp;
1789 } else
1790 while (sz--)
1791 *np++ = *cp++;
1794 if (fill && col != 0) {
1795 if (fill > 0) {
1796 memmove(nb + col, nb, PTR2SIZE(np - nb));
1797 memset(nb, ' ', col);
1798 } else
1799 memset(np, ' ', col);
1800 np += col;
1801 col = 0;
1804 #ifdef HAVE_NATCH_CHAR
1805 if (isbidi) {
1806 np[0] = birep[3];
1807 np[1] = birep[4];
1808 np[2] = birep[5];
1809 np += 3;
1811 #endif
1813 *np = '\0';
1814 if (cols_decr_used_or_null != NULL)
1815 *cols_decr_used_or_null -= col_orig - col;
1816 NYD_LEAVE;
1817 return nb;
1820 FL void
1821 makeprint(struct str const *in, struct str *out)
1823 static int print_all_chars = -1;
1825 char const *inp, *maxp;
1826 char *outp;
1827 DBG( size_t msz; )
1828 NYD_ENTER;
1830 if (print_all_chars == -1)
1831 print_all_chars = ok_blook(print_all_chars);
1833 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1834 inp = in->s;
1835 maxp = inp + in->l;
1837 if (print_all_chars) {
1838 out->l = in->l;
1839 memcpy(outp, inp, out->l);
1840 goto jleave;
1843 #ifdef HAVE_NATCH_CHAR
1844 if (mb_cur_max > 1) {
1845 char mbb[MB_LEN_MAX + 1];
1846 wchar_t wc;
1847 int i, n;
1848 bool_t isuni = ((options & OPT_UNICODE) != 0);
1850 out->l = 0;
1851 while (inp < maxp) {
1852 if (*inp & 0200)
1853 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1854 else {
1855 wc = *inp;
1856 n = 1;
1858 if (n == -1) {
1859 /* FIXME Why mbtowc() resetting here?
1860 * FIXME what about ISO 2022-JP plus -- those
1861 * FIXME will loose shifts, then!
1862 * FIXME THUS - we'd need special "known points"
1863 * FIXME to do so - say, after a newline!!
1864 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1865 mbtowc(&wc, NULL, mb_cur_max);
1866 wc = isuni ? 0xFFFD : '?';
1867 n = 1;
1868 } else if (n == 0)
1869 n = 1;
1870 inp += n;
1871 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1872 wc != '\t') {
1873 if ((wc & ~(wchar_t)037) == 0)
1874 wc = isuni ? 0x2400 | wc : '?';
1875 else if (wc == 0177)
1876 wc = isuni ? 0x2421 : '?';
1877 else
1878 wc = isuni ? 0x2426 : '?';
1880 if ((n = wctomb(mbb, wc)) <= 0)
1881 continue;
1882 out->l += n;
1883 assert(out->l < msz);
1884 for (i = 0; i < n; ++i)
1885 *outp++ = mbb[i];
1887 } else
1888 #endif /* NATCH_CHAR */
1890 int c;
1891 while (inp < maxp) {
1892 c = *inp++ & 0377;
1893 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1894 c = '?';
1895 *outp++ = c;
1897 out->l = in->l;
1899 jleave:
1900 out->s[out->l] = '\0';
1901 NYD_LEAVE;
1904 FL char *
1905 prstr(char const *s)
1907 struct str in, out;
1908 char *rp;
1909 NYD_ENTER;
1911 in.s = UNCONST(s);
1912 in.l = strlen(s);
1913 makeprint(&in, &out);
1914 rp = savestrbuf(out.s, out.l);
1915 free(out.s);
1916 NYD_LEAVE;
1917 return rp;
1920 FL int
1921 prout(char const *s, size_t sz, FILE *fp)
1923 struct str in, out;
1924 int n;
1925 NYD_ENTER;
1927 in.s = UNCONST(s);
1928 in.l = sz;
1929 makeprint(&in, &out);
1930 n = fwrite(out.s, 1, out.l, fp);
1931 free(out.s);
1932 NYD_LEAVE;
1933 return n;
1936 FL size_t
1937 putuc(int u, int c, FILE *fp)
1939 size_t rv;
1940 NYD_ENTER;
1941 UNUSED(u);
1943 #ifdef HAVE_NATCH_CHAR
1944 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1945 char mbb[MB_LEN_MAX];
1946 int i, n;
1948 if ((n = wctomb(mbb, u)) > 0) {
1949 rv = wcwidth(u);
1950 for (i = 0; i < n; ++i)
1951 if (putc(mbb[i] & 0377, fp) == EOF) {
1952 rv = 0;
1953 break;
1955 } else if (n == 0)
1956 rv = (putc('\0', fp) != EOF);
1957 else
1958 rv = 0;
1959 } else
1960 #endif
1961 rv = (putc(c, fp) != EOF);
1962 NYD_LEAVE;
1963 return rv;
1966 FL void
1967 bidi_info_create(struct bidi_info *bip)
1969 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1970 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1971 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1972 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1973 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1974 NATCH_CHAR( char const *hb; )
1975 NYD_ENTER;
1977 memset(bip, 0, sizeof *bip);
1978 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1980 #ifdef HAVE_NATCH_CHAR
1981 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1982 switch (*hb) {
1983 case '3':
1984 bip->bi_pad = 2;
1985 /* FALLTHRU */
1986 case '2':
1987 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1988 break;
1989 case '1':
1990 bip->bi_pad = 2;
1991 /* FALLTHRU */
1992 default:
1993 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1994 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1995 break;
1997 bip->bi_start.l = bip->bi_end.l = 3;
1999 #endif
2000 NYD_LEAVE;
2003 #ifdef HAVE_COLOUR
2004 FL void
2005 colour_table_create(char const *pager_used)
2007 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
2008 size_t i;
2009 struct colour_table *ct;
2010 NYD_ENTER;
2012 if (ok_blook(colour_disable))
2013 goto jleave;
2015 /* If pager, check wether it is allowed to use colour */
2016 if (pager_used != NULL) {
2017 char *pager;
2019 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
2020 u.ccp = COLOUR_PAGERS;
2021 pager = savestr(u.cp);
2023 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
2024 if (strstr(pager_used, u.cp) != NULL)
2025 goto jok;
2026 goto jleave;
2029 /* $TERM is different in that we default to false unless whitelisted */
2031 char *term, *okterms;
2033 /* Don't use getenv(), but force copy-in into our own tables.. */
2034 if ((term = _var_voklook("TERM")) == NULL)
2035 goto jleave;
2036 if ((okterms = ok_vlook(colour_terms)) == NULL)
2037 okterms = UNCONST(COLOUR_TERMS);
2038 okterms = savestr(okterms);
2040 i = strlen(term);
2041 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
2042 if (!strncmp(u.cp, term, i))
2043 goto jok;
2044 goto jleave;
2047 jok:
2048 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
2049 { static struct {
2050 enum okeys okey;
2051 enum colourspec cspec;
2052 char const *defval;
2053 } const map[] = {
2054 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
2055 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
2056 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
2057 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
2058 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
2061 for (i = 0; i < NELEM(map); ++i) {
2062 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
2063 u.ccp = map[i].defval;
2064 u.cp = _colour_iso6429(u.ccp);
2065 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
2066 ct->ct_csinfo[map[i].cspec].s = u.cp;
2069 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
2070 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
2072 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
2073 u.ccp = COLOUR_USER_HEADERS;
2074 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
2075 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
2076 jleave:
2077 NYD_LEAVE;
2080 FL void
2081 colour_put(FILE *fp, enum colourspec cs)
2083 NYD_ENTER;
2084 if (colour_table != NULL) {
2085 struct str const *cp = colour_get(cs);
2087 fwrite(cp->s, cp->l, 1, fp);
2089 NYD_LEAVE;
2092 FL void
2093 colour_put_header(FILE *fp, char const *name)
2095 enum colourspec cs = COLOURSPEC_HEADER;
2096 struct str const *uheads;
2097 char *cp, *cp_base, *x;
2098 size_t namelen;
2099 NYD_ENTER;
2101 if (colour_table == NULL)
2102 goto j_leave;
2103 /* Normal header colours if there are no user headers */
2104 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
2105 if (uheads->s == NULL)
2106 goto jleave;
2108 /* Iterate over all entries in the *colour-user-headers* list */
2109 cp = ac_alloc(uheads->l +1);
2110 memcpy(cp, uheads->s, uheads->l +1);
2111 cp_base = cp;
2112 namelen = strlen(name);
2113 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
2114 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
2115 if (l == namelen && !ascncasecmp(x, name, namelen)) {
2116 cs = COLOURSPEC_UHEADER;
2117 break;
2120 ac_free(cp_base);
2121 jleave:
2122 colour_put(fp, cs);
2123 j_leave:
2124 NYD_LEAVE;
2127 FL void
2128 colour_reset(FILE *fp)
2130 NYD_ENTER;
2131 if (colour_table != NULL)
2132 fwrite("\033[0m", 4, 1, fp);
2133 NYD_LEAVE;
2136 FL struct str const *
2137 colour_get(enum colourspec cs)
2139 struct str const *rv = NULL;
2140 NYD_ENTER;
2142 if (colour_table != NULL)
2143 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
2144 rv = NULL;
2145 NYD_LEAVE;
2146 return rv;
2148 #endif /* HAVE_COLOUR */
2150 FL void
2151 time_current_update(struct time_current *tc, bool_t full_update)
2153 NYD_ENTER;
2154 tc->tc_time = time(NULL);
2155 if (full_update) {
2156 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
2157 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
2158 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
2160 NYD_LEAVE;
2163 static void
2164 _out_of_memory(void)
2166 panic("no memory");
2169 #ifndef HAVE_DEBUG
2170 FL void *
2171 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2173 void *rv;
2174 NYD_ENTER;
2176 if (s == 0)
2177 s = 1;
2178 if ((rv = malloc(s)) == NULL)
2179 _out_of_memory();
2180 NYD_LEAVE;
2181 return rv;
2184 FL void *
2185 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2187 void *rv;
2188 NYD_ENTER;
2190 if (s == 0)
2191 s = 1;
2192 if (v == NULL)
2193 rv = smalloc(s);
2194 else if ((rv = realloc(v, s)) == NULL)
2195 _out_of_memory();
2196 NYD_LEAVE;
2197 return rv;
2200 FL void *
2201 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2203 void *rv;
2204 NYD_ENTER;
2206 if (size == 0)
2207 size = 1;
2208 if ((rv = calloc(nmemb, size)) == NULL)
2209 _out_of_memory();
2210 NYD_LEAVE;
2211 return rv;
2214 #else /* !HAVE_DEBUG */
2215 CTA(sizeof(char) == sizeof(ui8_t));
2217 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2218 # define _HOPE_SET(C) \
2219 do {\
2220 union mem_ptr __xl, __xu;\
2221 struct mem_chunk *__xc;\
2222 __xl.p_p = (C).p_p;\
2223 __xc = __xl.p_c - 1;\
2224 __xu.p_p = __xc;\
2225 (C).p_cp += 8;\
2226 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2227 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2228 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2229 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2230 __xu.p_ui8p += __xc->mc_size - 8;\
2231 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2232 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2233 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2234 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2235 } while (0)
2236 # define _HOPE_GET_TRACE(C,BAD) \
2237 do {\
2238 (C).p_cp += 8;\
2239 _HOPE_GET(C, BAD);\
2240 (C).p_cp += 8;\
2241 } while(0)
2242 # define _HOPE_GET(C,BAD) \
2243 do {\
2244 union mem_ptr __xl, __xu;\
2245 struct mem_chunk *__xc;\
2246 ui32_t __i;\
2247 __xl.p_p = (C).p_p;\
2248 __xl.p_cp -= 8;\
2249 (C).p_cp = __xl.p_cp;\
2250 __xc = __xl.p_c - 1;\
2251 (BAD) = FAL0;\
2252 __i = 0;\
2253 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2254 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2255 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2256 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2257 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2258 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2259 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2260 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2261 if (__i != 0) {\
2262 (BAD) = TRU1;\
2263 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
2264 __xl.p_p, __i, mdbg_file, mdbg_line);\
2266 __xu.p_p = __xc;\
2267 __xu.p_ui8p += __xc->mc_size - 8;\
2268 __i = 0;\
2269 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2270 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2271 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2272 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2273 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2274 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2275 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2276 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2277 if (__i != 0) {\
2278 (BAD) = TRU1;\
2279 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
2280 __xl.p_p, __i, mdbg_file, mdbg_line);\
2282 if (BAD)\
2283 alert(" ..canary last seen: %s, line %u",\
2284 __xc->mc_file, __xc->mc_line);\
2285 } while (0)
2287 FL void *
2288 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2290 union mem_ptr p;
2291 NYD_ENTER;
2293 if (s == 0)
2294 s = 1;
2295 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2297 if ((p.p_p = (malloc)(s)) == NULL)
2298 _out_of_memory();
2299 p.p_c->mc_prev = NULL;
2300 if ((p.p_c->mc_next = _mem_list) != NULL)
2301 _mem_list->mc_prev = p.p_c;
2302 p.p_c->mc_file = mdbg_file;
2303 p.p_c->mc_line = (ui16_t)mdbg_line;
2304 p.p_c->mc_isfree = FAL0;
2305 p.p_c->mc_size = (ui32_t)s;
2306 _mem_list = p.p_c++;
2307 _HOPE_SET(p);
2309 ++_mem_aall;
2310 ++_mem_acur;
2311 _mem_amax = MAX(_mem_amax, _mem_acur);
2312 _mem_mall += s;
2313 _mem_mcur += s;
2314 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2315 NYD_LEAVE;
2316 return p.p_p;
2319 FL void *
2320 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2322 union mem_ptr p;
2323 bool_t isbad;
2324 NYD_ENTER;
2326 if ((p.p_p = v) == NULL) {
2327 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2328 goto jleave;
2331 _HOPE_GET(p, isbad);
2332 --p.p_c;
2333 if (p.p_c->mc_isfree) {
2334 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
2335 "\tLast seen: %s, line %d\n",
2336 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2337 goto jforce;
2340 if (p.p_c == _mem_list)
2341 _mem_list = p.p_c->mc_next;
2342 else
2343 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2344 if (p.p_c->mc_next != NULL)
2345 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2347 --_mem_acur;
2348 _mem_mcur -= p.p_c->mc_size;
2349 jforce:
2350 if (s == 0)
2351 s = 1;
2352 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2354 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2355 _out_of_memory();
2356 p.p_c->mc_prev = NULL;
2357 if ((p.p_c->mc_next = _mem_list) != NULL)
2358 _mem_list->mc_prev = p.p_c;
2359 p.p_c->mc_file = mdbg_file;
2360 p.p_c->mc_line = (ui16_t)mdbg_line;
2361 p.p_c->mc_isfree = FAL0;
2362 p.p_c->mc_size = (ui32_t)s;
2363 _mem_list = p.p_c++;
2364 _HOPE_SET(p);
2366 ++_mem_aall;
2367 ++_mem_acur;
2368 _mem_amax = MAX(_mem_amax, _mem_acur);
2369 _mem_mall += s;
2370 _mem_mcur += s;
2371 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2372 jleave:
2373 NYD_LEAVE;
2374 return p.p_p;
2377 FL void *
2378 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2380 union mem_ptr p;
2381 NYD_ENTER;
2383 if (size == 0)
2384 size = 1;
2385 if (nmemb == 0)
2386 nmemb = 1;
2387 size *= nmemb;
2388 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2390 if ((p.p_p = (malloc)(size)) == NULL)
2391 _out_of_memory();
2392 memset(p.p_p, 0, size);
2393 p.p_c->mc_prev = NULL;
2394 if ((p.p_c->mc_next = _mem_list) != NULL)
2395 _mem_list->mc_prev = p.p_c;
2396 p.p_c->mc_file = mdbg_file;
2397 p.p_c->mc_line = (ui16_t)mdbg_line;
2398 p.p_c->mc_isfree = FAL0;
2399 p.p_c->mc_size = (ui32_t)size;
2400 _mem_list = p.p_c++;
2401 _HOPE_SET(p);
2403 ++_mem_aall;
2404 ++_mem_acur;
2405 _mem_amax = MAX(_mem_amax, _mem_acur);
2406 _mem_mall += size;
2407 _mem_mcur += size;
2408 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2409 NYD_LEAVE;
2410 return p.p_p;
2413 FL void
2414 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2416 union mem_ptr p;
2417 bool_t isbad;
2418 NYD_ENTER;
2420 if ((p.p_p = v) == NULL) {
2421 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2422 goto jleave;
2425 _HOPE_GET(p, isbad);
2426 --p.p_c;
2427 if (p.p_c->mc_isfree) {
2428 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
2429 "\tLast seen: %s, line %d\n",
2430 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2431 goto jleave;
2434 if (p.p_c == _mem_list)
2435 _mem_list = p.p_c->mc_next;
2436 else
2437 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2438 if (p.p_c->mc_next != NULL)
2439 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2440 p.p_c->mc_isfree = TRU1;
2442 --_mem_acur;
2443 _mem_mcur -= p.p_c->mc_size;
2445 if (options & OPT_DEBUG) {
2446 p.p_c->mc_next = _mem_free;
2447 _mem_free = p.p_c;
2448 } else
2449 (free)(p.p_c);
2450 jleave:
2451 NYD_LEAVE;
2454 FL void
2455 smemreset(void)
2457 union mem_ptr p;
2458 size_t c = 0, s = 0;
2459 NYD_ENTER;
2461 for (p.p_c = _mem_free; p.p_c != NULL;) {
2462 void *vp = p.p_c;
2463 ++c;
2464 s += p.p_c->mc_size;
2465 p.p_c = p.p_c->mc_next;
2466 (free)(vp);
2468 _mem_free = NULL;
2470 if (options & OPT_DEBUG)
2471 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
2472 c, s);
2473 NYD_LEAVE;
2476 FL int
2477 c_smemtrace(void *v)
2479 /* For _HOPE_GET() */
2480 char const * const mdbg_file = "smemtrace()";
2481 int const mdbg_line = -1;
2482 FILE *fp;
2483 union mem_ptr p, xp;
2484 bool_t isbad;
2485 size_t lines;
2486 NYD_ENTER;
2488 v = (void*)0x1;
2489 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2490 NULL) {
2491 perror("tmpfile");
2492 goto jleave;
2495 fprintf(fp, "Memory statistics:\n"
2496 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
2497 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
2498 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2500 fprintf(fp, "Currently allocated memory chunks:\n");
2501 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2502 ++lines, p.p_c = p.p_c->mc_next) {
2503 xp = p;
2504 ++xp.p_c;
2505 _HOPE_GET_TRACE(xp, isbad);
2506 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2507 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2508 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2509 p.p_c->mc_line);
2512 if (options & OPT_DEBUG) {
2513 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2514 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2515 xp = p;
2516 ++xp.p_c;
2517 _HOPE_GET_TRACE(xp, isbad);
2518 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2519 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2520 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2521 p.p_c->mc_file, p.p_c->mc_line);
2525 page_or_print(fp, lines);
2526 Fclose(fp);
2527 v = NULL;
2528 jleave:
2529 NYD_LEAVE;
2530 return (v != NULL);
2533 # ifdef _HAVE_MEMCHECK
2534 FL bool_t
2535 _smemcheck(char const *mdbg_file, int mdbg_line)
2537 union mem_ptr p, xp;
2538 bool_t anybad = FAL0, isbad;
2539 size_t lines;
2540 NYD_ENTER;
2542 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2543 ++lines, p.p_c = p.p_c->mc_next) {
2544 xp = p;
2545 ++xp.p_c;
2546 _HOPE_GET_TRACE(xp, isbad);
2547 if (isbad) {
2548 anybad = TRU1;
2549 fprintf(stderr,
2550 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2551 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2552 p.p_c->mc_file, p.p_c->mc_line);
2556 if (options & OPT_DEBUG) {
2557 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2558 xp = p;
2559 ++xp.p_c;
2560 _HOPE_GET_TRACE(xp, isbad);
2561 if (isbad) {
2562 anybad = TRU1;
2563 fprintf(stderr,
2564 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2565 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2566 p.p_c->mc_file, p.p_c->mc_line);
2570 NYD_LEAVE;
2571 return anybad;
2573 # endif /* _HAVE_MEMCHECK */
2575 # undef _HOPE_SIZE
2576 # undef _HOPE_SET
2577 # undef _HOPE_GET_TRACE
2578 # undef _HOPE_GET
2579 #endif /* HAVE_DEBUG */
2581 /* vim:set fenc=utf-8:s-it-mode */