Add struct url and url_parse()
[s-mailx.git] / auxlily.c
blob964f70b2bb27a4b3649dd40d8da1a32e2395b9aa
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)
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 (strcpy(&np[sz], "/tmp"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
543 (strcpy(&np[sz], "/new"), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
544 (strcpy(&np[sz], "/cur"), !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)
677 static char buf[PROMPT_BUFFER_SIZE];
679 char *cp = buf;
680 char const *ccp;
681 NYD_ENTER;
683 if ((ccp = ok_vlook(prompt)) == NULL || *ccp == '\0')
684 goto jleave;
686 for (; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
687 char const *a;
688 size_t l;
689 int c = expand_shell_escape(&ccp, TRU1);
691 if (c > 0) {
692 *cp = (char)c;
693 continue;
695 if (c == 0 || c == PROMPT_STOP)
696 break;
698 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
699 if (a == NULL)
700 a = "";
701 l = strlen(a);
702 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
703 *cp++ = '?';
704 else {
705 memcpy(cp, a, l);
706 cp += --l;
709 jleave:
710 *cp = '\0';
711 NYD_LEAVE;
712 return buf;
715 FL char *
716 nodename(int mayoverride)
718 static char *hostname;
720 struct utsname ut;
721 char *hn;
722 #ifdef HAVE_SOCKETS
723 # ifdef HAVE_IPV6
724 struct addrinfo hints, *res;
725 # else
726 struct hostent *hent;
727 # endif
728 #endif
729 NYD_ENTER;
731 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
732 if (hostname != NULL)
733 free(hostname);
734 hostname = sstrdup(hn);
735 } else if (hostname == NULL) {
736 uname(&ut);
737 hn = ut.nodename;
738 #ifdef HAVE_SOCKETS
739 # ifdef HAVE_IPV6
740 memset(&hints, 0, sizeof hints);
741 hints.ai_family = AF_UNSPEC;
742 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
743 hints.ai_flags = AI_CANONNAME;
744 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
745 if (res->ai_canonname != NULL) {
746 size_t l = strlen(res->ai_canonname) +1;
747 hn = ac_alloc(l);
748 memcpy(hn, res->ai_canonname, l);
750 freeaddrinfo(res);
752 # else
753 hent = gethostbyname(hn);
754 if (hent != NULL)
755 hn = hent->h_name;
756 # endif
757 #endif
758 hostname = sstrdup(hn);
759 #if defined HAVE_SOCKETS && defined HAVE_IPV6
760 if (hn != ut.nodename)
761 ac_free(hn);
762 #endif
764 NYD_LEAVE;
765 return hostname;
768 FL bool_t
769 url_parse(struct url *urlp, enum cproto cproto, char const *data)
771 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
772 # define __ALLPROTO
773 #endif
774 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
775 # define __ANYPROTO
776 char *cp, *x;
777 #endif
778 bool_t rv = FAL0;
779 NYD_ENTER;
780 UNUSED(data);
782 memset(urlp, 0, sizeof *urlp);
783 urlp->url_input = data;
784 urlp->url_cproto = cproto;
786 /* Network protocol */
787 #define _protox(X,Y) \
788 urlp->url_portno = Y;\
789 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
790 urlp->url_proto[sizeof(X) -1] = '\0';\
791 urlp->url_proto_len = sizeof(X) -1;\
792 urlp->url_proto_xlen = sizeof(X "://") -1
793 #define __if(X,Y,Z) \
794 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
795 _protox(X, Y);\
796 data += sizeof(X "://") -1;\
797 do { Z; } while (0);\
798 goto juser;\
800 #define _if(X,Y) __if(X, Y, (void)0)
801 #ifdef HAVE_SSL
802 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
803 #else
804 # define _ifs(X,Y) goto jeproto;
805 #endif
807 switch (cproto) {
808 case CPROTO_SMTP:
809 #ifdef HAVE_SMTP
810 _if ("smtp", 25)
811 _if ("submission", 587)
812 _ifs ("smtps", 465)
813 _protox("smtp", 25);
814 break;
815 #else
816 goto jeproto;
817 #endif
818 case CPROTO_POP3:
819 #ifdef HAVE_POP3
820 _if ("pop3", 110)
821 _ifs ("pop3s", 995)
822 _protox("pop3", 110);
823 break;
824 #else
825 goto jeproto;
826 #endif
827 case CPROTO_IMAP:
828 #ifdef HAVE_IMAP
829 _if ("imap", 143)
830 _ifs ("imaps", 993)
831 _protox("imap", 143);
832 break;
833 #else
834 goto jeproto;
835 #endif
838 #undef _ifs
839 #undef _if
840 #undef __if
841 #undef _protox
843 if (strstr(data, "://") != NULL) {
844 #if !defined __ALLPROTO || !defined HAVE_SSL
845 jeproto:
846 #endif
847 fprintf(stderr, tr(574, "URL `proto://' prefix invalid: `%s'\n"),
848 urlp->url_input);
849 goto jleave;
851 #ifdef __ANYPROTO
853 /* User and password, I */
854 juser:
855 if ((cp = UNCONST(last_at_before_slash(data))) == NULL)
856 goto jserver;
858 urlp->url_had_user = TRU1;
859 urlp->url_user.s = savestrbuf(data, urlp->url_user.l = PTR2SIZE(cp - data));
860 data = ++cp;
862 /* And also have a password? */
863 if ((cp = strchr(urlp->url_user.s, ':')) != NULL) {
864 x = urlp->url_user.s;
865 urlp->url_user.s = savestrbuf(x, urlp->url_user.l = PTR2SIZE(cp - x));
866 urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(++cp));
867 urlp->url_pass_enc.l = strlen(
868 urlp->url_pass_enc.s = urlxenc(urlp->url_pass.s));
871 /* Servername and port -- and possible path suffix */
872 jserver:
873 if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */
874 char *eptr;
875 long l;
877 urlp->url_port = x = savestr(x = cp + 1);
878 if ((x = strchr(x, '/')) != NULL)
879 *x = '\0';
880 l = strtol(urlp->url_port, &eptr, 10);
881 if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) {
882 fprintf(stderr, tr(573, "URL with invalid port number: `%s'\n"),
883 urlp->url_input);
884 goto jleave;
886 urlp->url_portno = (ui16_t)l;
887 } else {
888 if ((x = strchr(data, '/')) != NULL)
889 data = savestrbuf(data, PTR2SIZE(x - data));
890 cp = UNCONST(data + strlen(data));
893 /* A (non-empty) path may only occur with IMAP */
894 if (x != NULL && x[1] != '\0') {
895 if (cproto != CPROTO_IMAP) {
896 fprintf(stderr, tr(575, "URL protocol doesn't support paths: `%s'\n"),
897 urlp->url_input);
898 goto jleave;
900 urlp->url_path.l = strlen(++x);
901 urlp->url_path.s = savestrbuf(x, urlp->url_path.l);
904 urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
905 { size_t i;
906 for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
907 *cp = lowerconv(*cp);
910 /* HOST:PORT */
911 { size_t i;
912 struct str *s = &urlp->url_hp;
914 s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1);
915 memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
916 if (urlp->url_port != NULL) {
917 size_t j = strlen(urlp->url_port);
918 s->s[i++] = ':';
919 memcpy(s->s + i, urlp->url_port, j);
920 i += j;
922 s->s[i] = '\0';
923 s->l = i;
926 /* User, II
927 * If there was no user in the URL, do we have *user-HOST* or *user*? */
928 if (!urlp->url_had_user) {
929 char const usrstr[] = "user-";
930 size_t const usrlen = sizeof(usrstr) -1;
932 cp = ac_alloc(usrlen + urlp->url_hp.l +1);
933 memcpy(cp, usrstr, usrlen);
934 memcpy(cp + usrlen, urlp->url_hp.s, urlp->url_hp.l +1);
935 if ((urlp->url_user.s = vok_vlook(cp)) == NULL) {
936 cp[usrlen - 1] = '\0';
937 if ((urlp->url_user.s = vok_vlook(cp)) == NULL)
938 urlp->url_user.s = UNCONST(myname);
940 ac_free(cp);
943 urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(urlp->url_user.s));
944 urlp->url_user_enc.l = strlen(
945 urlp->url_user_enc.s = urlxenc(urlp->url_user.s));
947 /* USER@HOST:PORT */
948 if (urlp->url_user_enc.l == 0)
949 urlp->url_uhp = urlp->url_hp;
950 else {
951 struct str *s = &urlp->url_uhp;
952 size_t i = urlp->url_user_enc.l;
954 s->s = salloc(i + 1 + urlp->url_hp.l +1);
955 if (i > 0) {
956 memcpy(s->s, urlp->url_user_enc.s, i);
957 s->s[i++] = '@';
959 memcpy(s->s + i, urlp->url_hp.s, urlp->url_hp.l +1);
960 i += urlp->url_hp.l;
961 s->l = i;
964 /* USER@HOST */
965 if (urlp->url_user_enc.l == 0)
966 urlp->url_uh = urlp->url_host;
967 else {
968 struct str *s = &urlp->url_uh;
969 size_t i = urlp->url_user_enc.l;
971 s->s = salloc(i + 1 + urlp->url_host.l +1);
972 if (i > 0) {
973 memcpy(s->s, urlp->url_user_enc.s, i);
974 s->s[i++] = '@';
976 memcpy(s->s + i, urlp->url_host.s, urlp->url_host.l +1);
977 i += urlp->url_host.l;
978 s->l = i;
981 /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */
982 { size_t i;
983 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_uhp.l) +
984 1 + urlp->url_path.l +1);
986 urlp->url_proto[urlp->url_proto_len] = ':';
987 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_uhp.s, urlp->url_uhp.l +1);
988 urlp->url_proto[urlp->url_proto_len] = '\0';
990 if (urlp->url_path.l == 0)
991 urlp->url_puhp = urlp->url_puhpp = ud;
992 else {
993 urlp->url_puhp = savestrbuf(ud, i);
994 urlp->url_puhpp = ud;
995 ud += i;
996 *ud++ = '/';
997 memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
1001 rv = TRU1;
1002 #endif /* __ANYPROTO */
1003 jleave:
1004 NYD_LEAVE;
1005 return rv;
1006 #undef __ANYPROTO
1007 #undef __ALLPROTO
1010 FL char *
1011 lookup_password_for_token(char const *token)
1013 size_t tl;
1014 char *var, *cp;
1015 NYD_ENTER;
1017 tl = strlen(token);
1018 var = ac_alloc(tl + 9 +1);
1020 memcpy(var, "password-", 9);
1021 memcpy(var + 9, token, tl);
1022 var[tl + 9] = '\0';
1024 if ((cp = vok_vlook(var)) != NULL)
1025 cp = savestr(cp);
1026 ac_free(var);
1027 NYD_LEAVE;
1028 return cp;
1031 FL char *
1032 getrandstring(size_t length)
1034 static unsigned char nodedigest[16];
1035 static pid_t pid;
1037 struct str b64;
1038 char *data, *cp;
1039 size_t i;
1040 int fd = -1;
1041 #ifdef HAVE_MD5
1042 md5_ctx ctx;
1043 #else
1044 size_t j;
1045 #endif
1046 NYD_ENTER;
1048 data = ac_alloc(length);
1050 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
1051 length != (size_t)read(fd, data, length)) {
1052 if (pid == 0) {
1053 pid = getpid();
1054 srand(pid);
1055 cp = nodename(0);
1056 #ifdef HAVE_MD5
1057 md5_init(&ctx);
1058 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
1059 md5_final(nodedigest, &ctx);
1060 #else
1061 /* In that case it's only used for boundaries and Message-Id:s so that
1062 * srand(3) should suffice */
1063 j = strlen(cp) + 1;
1064 for (i = 0; i < sizeof(nodedigest); ++i)
1065 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
1066 #endif
1068 for (i = 0; i < length; i++)
1069 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
1070 nodedigest[i % sizeof nodedigest]);
1072 if (fd >= 0)
1073 close(fd);
1075 b64_encode_buf(&b64, data, length, B64_SALLOC);
1076 ac_free(data);
1077 assert(length < b64.l);
1078 b64.s[length] = '\0';
1079 NYD_LEAVE;
1080 return b64.s;
1083 #ifdef HAVE_MD5
1084 FL char *
1085 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
1087 char const *cp = vp;
1088 size_t i, j;
1089 NYD_ENTER;
1091 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
1092 j = i << 1;
1093 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
1094 hex[++j] = hexchar(cp[i] & 0x0f);
1096 NYD_LEAVE;
1097 return hex;
1100 FL char *
1101 cram_md5_string(char const *user, char const *pass, char const *b64)
1103 struct str in, out;
1104 char digest[16], *cp;
1105 size_t lu;
1106 NYD_ENTER;
1108 out.s = NULL;
1109 in.s = UNCONST(b64);
1110 in.l = strlen(in.s);
1111 b64_decode(&out, &in, NULL);
1112 assert(out.s != NULL);
1114 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass), digest);
1115 free(out.s);
1116 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
1118 lu = strlen(user);
1119 in.l = lu + MD5TOHEX_SIZE +1;
1120 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
1121 memcpy(in.s, user, lu);
1122 in.s[lu++] = ' ';
1123 memcpy(in.s + lu, cp, MD5TOHEX_SIZE);
1124 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
1125 ac_free(in.s);
1126 NYD_LEAVE;
1127 return out.s;
1130 FL void
1131 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
1132 void *digest)
1135 * This code is taken from
1137 * Network Working Group H. Krawczyk
1138 * Request for Comments: 2104 IBM
1139 * Category: Informational M. Bellare
1140 * UCSD
1141 * R. Canetti
1142 * IBM
1143 * February 1997
1146 * HMAC: Keyed-Hashing for Message Authentication
1148 md5_ctx context;
1149 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
1150 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
1151 unsigned char tk[16];
1152 int i;
1153 NYD_ENTER;
1155 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1156 if (key_len > 64) {
1157 md5_ctx tctx;
1159 md5_init(&tctx);
1160 md5_update(&tctx, key, key_len);
1161 md5_final(tk, &tctx);
1163 key = tk;
1164 key_len = 16;
1167 /* the HMAC_MD5 transform looks like:
1169 * MD5(K XOR opad, MD5(K XOR ipad, text))
1171 * where K is an n byte key
1172 * ipad is the byte 0x36 repeated 64 times
1173 * opad is the byte 0x5c repeated 64 times
1174 * and text is the data being protected */
1176 /* start out by storing key in pads */
1177 memset(k_ipad, 0, sizeof k_ipad);
1178 memset(k_opad, 0, sizeof k_opad);
1179 memcpy(k_ipad, key, key_len);
1180 memcpy(k_opad, key, key_len);
1182 /* XOR key with ipad and opad values */
1183 for (i=0; i<64; i++) {
1184 k_ipad[i] ^= 0x36;
1185 k_opad[i] ^= 0x5c;
1188 /* perform inner MD5 */
1189 md5_init(&context); /* init context for 1st pass */
1190 md5_update(&context, k_ipad, 64); /* start with inner pad */
1191 md5_update(&context, text, text_len); /* then text of datagram */
1192 md5_final(digest, &context); /* finish up 1st pass */
1194 /* perform outer MD5 */
1195 md5_init(&context); /* init context for 2nd pass */
1196 md5_update(&context, k_opad, 64); /* start with outer pad */
1197 md5_update(&context, digest, 16); /* then results of 1st hash */
1198 md5_final(digest, &context); /* finish up 2nd pass */
1199 NYD_LEAVE;
1201 #endif /* HAVE_MD5 */
1203 FL enum okay
1204 makedir(char const *name)
1206 struct stat st;
1207 enum okay rv = STOP;
1208 NYD_ENTER;
1210 if (!mkdir(name, 0700))
1211 rv = OKAY;
1212 else {
1213 int e = errno;
1214 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1215 S_ISDIR(st.st_mode))
1216 rv = OKAY;
1218 NYD_LEAVE;
1219 return rv;
1222 #ifdef HAVE_FCHDIR
1223 FL enum okay
1224 cwget(struct cw *cw)
1226 enum okay rv = STOP;
1227 NYD_ENTER;
1229 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1230 goto jleave;
1231 if (fchdir(cw->cw_fd) == -1) {
1232 close(cw->cw_fd);
1233 goto jleave;
1235 rv = OKAY;
1236 jleave:
1237 NYD_LEAVE;
1238 return rv;
1241 FL enum okay
1242 cwret(struct cw *cw)
1244 enum okay rv = STOP;
1245 NYD_ENTER;
1247 if (!fchdir(cw->cw_fd))
1248 rv = OKAY;
1249 NYD_LEAVE;
1250 return rv;
1253 FL void
1254 cwrelse(struct cw *cw)
1256 NYD_ENTER;
1257 close(cw->cw_fd);
1258 NYD_LEAVE;
1261 #else /* !HAVE_FCHDIR */
1262 FL enum okay
1263 cwget(struct cw *cw)
1265 enum okay rv = STOP;
1266 NYD_ENTER;
1268 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1269 rv = OKAY;
1270 NYD_LEAVE;
1271 return rv;
1274 FL enum okay
1275 cwret(struct cw *cw)
1277 enum okay rv = STOP;
1278 NYD_ENTER;
1280 if (!chdir(cw->cw_wd))
1281 rv = OKAY;
1282 NYD_LEAVE;
1283 return rv;
1286 FL void
1287 cwrelse(struct cw *cw)
1289 NYD_ENTER;
1290 UNUSED(cw);
1291 NYD_LEAVE;
1293 #endif /* !HAVE_FCHDIR */
1295 FL char *
1296 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1298 int col_orig = col, n, sz;
1299 char *nb, *np;
1300 NYD_ENTER;
1302 np = nb = salloc(mb_cur_max * strlen(cp) + col +1);
1303 while (*cp) {
1304 #ifdef HAVE_WCWIDTH
1305 if (mb_cur_max > 1) {
1306 wchar_t wc;
1308 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1309 n = sz = 1;
1310 else if ((n = wcwidth(wc)) == -1)
1311 n = 1;
1312 } else
1313 #endif
1314 n = sz = 1;
1315 if (n > col)
1316 break;
1317 col -= n;
1318 if (sz == 1 && spacechar(*cp)) {
1319 *np++ = ' ';
1320 cp++;
1321 } else
1322 while (sz--)
1323 *np++ = *cp++;
1326 if (fill && col != 0) {
1327 if (fill > 0) {
1328 memmove(nb + col, nb, PTR2SIZE(np - nb));
1329 memset(nb, ' ', col);
1330 } else
1331 memset(np, ' ', col);
1332 np += col;
1333 col = 0;
1336 *np = '\0';
1337 if (cols_decr_used_or_null != NULL)
1338 *cols_decr_used_or_null -= col_orig - col;
1339 NYD_LEAVE;
1340 return nb;
1343 FL void
1344 makeprint(struct str const *in, struct str *out)
1346 static int print_all_chars = -1;
1348 char const *inp, *maxp;
1349 char *outp;
1350 size_t msz;
1351 NYD_ENTER;
1353 if (print_all_chars == -1)
1354 print_all_chars = ok_blook(print_all_chars);
1356 msz = in->l +1;
1357 out->s = outp = smalloc(msz);
1358 inp = in->s;
1359 maxp = inp + in->l;
1361 if (print_all_chars) {
1362 out->l = in->l;
1363 memcpy(outp, inp, out->l);
1364 goto jleave;
1367 #ifdef HAVE_C90AMEND1
1368 if (mb_cur_max > 1) {
1369 char mbb[MB_LEN_MAX + 1];
1370 wchar_t wc;
1371 int i, n;
1372 size_t dist;
1374 out->l = 0;
1375 while (inp < maxp) {
1376 if (*inp & 0200)
1377 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1378 else {
1379 wc = *inp;
1380 n = 1;
1382 if (n == -1) {
1383 /* FIXME Why mbtowc() resetting here?
1384 * FIXME what about ISO 2022-JP plus -- those
1385 * FIXME will loose shifts, then!
1386 * FIXME THUS - we'd need special "known points"
1387 * FIXME to do so - say, after a newline!!
1388 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1389 mbtowc(&wc, NULL, mb_cur_max);
1390 wc = utf8 ? 0xFFFD : '?';
1391 n = 1;
1392 } else if (n == 0)
1393 n = 1;
1394 inp += n;
1395 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1396 wc != '\t') {
1397 if ((wc & ~(wchar_t)037) == 0)
1398 wc = utf8 ? 0x2400 | wc : '?';
1399 else if (wc == 0177)
1400 wc = utf8 ? 0x2421 : '?';
1401 else
1402 wc = utf8 ? 0x2426 : '?';
1404 if ((n = wctomb(mbb, wc)) <= 0)
1405 continue;
1406 out->l += n;
1407 if (out->l >= msz - 1) {
1408 dist = outp - out->s;
1409 out->s = srealloc(out->s, msz += 32);
1410 outp = &out->s[dist];
1412 for (i = 0; i < n; ++i)
1413 *outp++ = mbb[i];
1415 } else
1416 #endif /* C90AMEND1 */
1418 int c;
1419 while (inp < maxp) {
1420 c = *inp++ & 0377;
1421 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1422 c = '?';
1423 *outp++ = c;
1425 out->l = in->l;
1427 jleave:
1428 out->s[out->l] = '\0';
1429 NYD_LEAVE;
1432 FL char *
1433 prstr(char const *s)
1435 struct str in, out;
1436 char *rp;
1437 NYD_ENTER;
1439 in.s = UNCONST(s);
1440 in.l = strlen(s);
1441 makeprint(&in, &out);
1442 rp = savestrbuf(out.s, out.l);
1443 free(out.s);
1444 NYD_LEAVE;
1445 return rp;
1448 FL int
1449 prout(char const *s, size_t sz, FILE *fp)
1451 struct str in, out;
1452 int n;
1453 NYD_ENTER;
1455 in.s = UNCONST(s);
1456 in.l = sz;
1457 makeprint(&in, &out);
1458 n = fwrite(out.s, 1, out.l, fp);
1459 free(out.s);
1460 NYD_LEAVE;
1461 return n;
1464 FL size_t
1465 putuc(int u, int c, FILE *fp)
1467 size_t rv;
1468 UNUSED(u);
1469 NYD_ENTER;
1471 #ifdef HAVE_C90AMEND1
1472 if (utf8 && (u & ~(wchar_t)0177)) {
1473 char mbb[MB_LEN_MAX];
1474 int i, n;
1476 if ((n = wctomb(mbb, u)) > 0) {
1477 rv = wcwidth(u);
1478 for (i = 0; i < n; ++i)
1479 if (putc(mbb[i] & 0377, fp) == EOF) {
1480 rv = 0;
1481 break;
1483 } else if (n == 0)
1484 rv = (putc('\0', fp) != EOF);
1485 else
1486 rv = 0;
1487 } else
1488 #endif
1489 rv = (putc(c, fp) != EOF);
1490 NYD_LEAVE;
1491 return rv;
1494 #ifdef HAVE_COLOUR
1495 FL void
1496 colour_table_create(char const *pager_used)
1498 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1499 size_t i;
1500 struct colour_table *ct;
1501 NYD_ENTER;
1503 if (ok_blook(colour_disable))
1504 goto jleave;
1506 /* If pager, check wether it is allowed to use colour */
1507 if (pager_used != NULL) {
1508 char *pager;
1510 if ((u.cp = ok_vlook(colour_pagers)) == NULL)
1511 u.ccp = COLOUR_PAGERS;
1512 pager = savestr(u.cp);
1514 while ((u.cp = n_strsep(&pager, ',', TRU1)) != NULL)
1515 if (strstr(pager_used, u.cp) != NULL)
1516 goto jok;
1517 goto jleave;
1520 /* $TERM is different in that we default to false unless whitelisted */
1522 char *term, *okterms;
1524 /* Don't use getenv(), but force copy-in into our own tables.. */
1525 if ((term = _var_voklook("TERM")) == NULL)
1526 goto jleave;
1527 if ((okterms = ok_vlook(colour_terms)) == NULL)
1528 okterms = UNCONST(COLOUR_TERMS);
1529 okterms = savestr(okterms);
1531 i = strlen(term);
1532 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1533 if (!strncmp(u.cp, term, i))
1534 goto jok;
1535 goto jleave;
1538 jok:
1539 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1540 { static struct {
1541 enum okeys okey;
1542 enum colourspec cspec;
1543 char const *defval;
1544 } const map[] = {
1545 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1546 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1547 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1548 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1549 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1552 for (i = 0; i < NELEM(map); ++i) {
1553 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1554 u.ccp = map[i].defval;
1555 u.cp = _colour_iso6429(u.ccp);
1556 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1557 ct->ct_csinfo[map[i].cspec].s = u.cp;
1560 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1561 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1563 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1564 u.ccp = COLOUR_USER_HEADERS;
1565 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1566 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1567 jleave:
1568 NYD_LEAVE;
1571 FL void
1572 colour_put(FILE *fp, enum colourspec cs)
1574 NYD_ENTER;
1575 if (colour_table != NULL) {
1576 struct str const *cp = colour_get(cs);
1578 fwrite(cp->s, cp->l, 1, fp);
1580 NYD_LEAVE;
1583 FL void
1584 colour_put_header(FILE *fp, char const *name)
1586 enum colourspec cs = COLOURSPEC_HEADER;
1587 struct str const *uheads;
1588 char *cp, *cp_base, *x;
1589 size_t namelen;
1590 NYD_ENTER;
1592 if (colour_table == NULL)
1593 goto j_leave;
1594 /* Normal header colours if there are no user headers */
1595 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1596 if (uheads->s == NULL)
1597 goto jleave;
1599 /* Iterate over all entries in the *colour-user-headers* list */
1600 cp = ac_alloc(uheads->l +1);
1601 memcpy(cp, uheads->s, uheads->l +1);
1602 cp_base = cp;
1603 namelen = strlen(name);
1604 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1605 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1606 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1607 cs = COLOURSPEC_UHEADER;
1608 break;
1611 ac_free(cp_base);
1612 jleave:
1613 colour_put(fp, cs);
1614 j_leave:
1615 NYD_LEAVE;
1618 FL void
1619 colour_reset(FILE *fp)
1621 NYD_ENTER;
1622 if (colour_table != NULL)
1623 fwrite("\033[0m", 4, 1, fp);
1624 NYD_LEAVE;
1627 FL struct str const *
1628 colour_get(enum colourspec cs)
1630 struct str const *rv = NULL;
1631 NYD_ENTER;
1633 if (colour_table != NULL)
1634 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1635 rv = NULL;
1636 NYD_LEAVE;
1637 return rv;
1639 #endif /* HAVE_COLOUR */
1641 FL void
1642 time_current_update(struct time_current *tc, bool_t full_update)
1644 NYD_ENTER;
1645 tc->tc_time = time(NULL);
1646 if (full_update) {
1647 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1648 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1649 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1651 NYD_LEAVE;
1654 static void
1655 _out_of_memory(void)
1657 panic("no memory");
1660 #ifndef HAVE_DEBUG
1661 FL void *
1662 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1664 void *rv;
1665 NYD_ENTER;
1667 if (s == 0)
1668 s = 1;
1669 if ((rv = malloc(s)) == NULL)
1670 _out_of_memory();
1671 NYD_LEAVE;
1672 return rv;
1675 FL void *
1676 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1678 void *rv;
1679 NYD_ENTER;
1681 if (s == 0)
1682 s = 1;
1683 if (v == NULL)
1684 rv = smalloc(s);
1685 else if ((rv = realloc(v, s)) == NULL)
1686 _out_of_memory();
1687 NYD_LEAVE;
1688 return rv;
1691 FL void *
1692 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1694 void *rv;
1695 NYD_ENTER;
1697 if (size == 0)
1698 size = 1;
1699 if ((rv = calloc(nmemb, size)) == NULL)
1700 _out_of_memory();
1701 NYD_LEAVE;
1702 return rv;
1705 #else /* !HAVE_DEBUG */
1706 CTA(sizeof(char) == sizeof(ui8_t));
1708 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1709 # define _HOPE_SET(C) \
1710 do {\
1711 union mem_ptr __xl, __xu;\
1712 struct mem_chunk *__xc;\
1713 __xl.p_p = (C).p_p;\
1714 __xc = __xl.p_c - 1;\
1715 __xu.p_p = __xc;\
1716 (C).p_cp += 8;\
1717 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1718 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1719 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1720 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1721 __xu.p_ui8p += __xc->mc_size - 8;\
1722 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1723 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1724 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1725 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1726 } while (0)
1727 # define _HOPE_GET_TRACE(C,BAD) \
1728 do {\
1729 (C).p_cp += 8;\
1730 _HOPE_GET(C, BAD);\
1731 (C).p_cp += 8;\
1732 } while(0)
1733 # define _HOPE_GET(C,BAD) \
1734 do {\
1735 union mem_ptr __xl, __xu;\
1736 struct mem_chunk *__xc;\
1737 ui32_t __i;\
1738 __xl.p_p = (C).p_p;\
1739 __xl.p_cp -= 8;\
1740 (C).p_cp = __xl.p_cp;\
1741 __xc = __xl.p_c - 1;\
1742 (BAD) = FAL0;\
1743 __i = 0;\
1744 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1745 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1746 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1747 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1748 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1749 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1750 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1751 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1752 if (__i != 0) {\
1753 (BAD) = TRU1;\
1754 alert("%p: corrupt lower canary: 0x%02X: %s, line %u",\
1755 __xl.p_p, __i, mdbg_file, mdbg_line);\
1757 __xu.p_p = __xc;\
1758 __xu.p_ui8p += __xc->mc_size - 8;\
1759 __i = 0;\
1760 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1761 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1762 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1763 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1764 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1765 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1766 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1767 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1768 if (__i != 0) {\
1769 (BAD) = TRU1;\
1770 alert("%p: corrupt upper canary: 0x%02X: %s, line %u",\
1771 __xl.p_p, __i, mdbg_file, mdbg_line);\
1773 if (BAD)\
1774 alert(" ..canary last seen: %s, line %u",\
1775 __xc->mc_file, __xc->mc_line);\
1776 } while (0)
1778 FL void *
1779 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1781 union mem_ptr p;
1782 NYD_ENTER;
1784 if (s == 0)
1785 s = 1;
1786 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1788 if ((p.p_p = (malloc)(s)) == NULL)
1789 _out_of_memory();
1790 p.p_c->mc_prev = NULL;
1791 if ((p.p_c->mc_next = _mem_list) != NULL)
1792 _mem_list->mc_prev = p.p_c;
1793 p.p_c->mc_file = mdbg_file;
1794 p.p_c->mc_line = (ui16_t)mdbg_line;
1795 p.p_c->mc_isfree = FAL0;
1796 p.p_c->mc_size = (ui32_t)s;
1797 _mem_list = p.p_c++;
1798 _HOPE_SET(p);
1800 ++_mem_aall;
1801 ++_mem_acur;
1802 _mem_amax = MAX(_mem_amax, _mem_acur);
1803 _mem_mall += s;
1804 _mem_mcur += s;
1805 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1806 NYD_LEAVE;
1807 return p.p_p;
1810 FL void *
1811 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1813 union mem_ptr p;
1814 bool_t isbad;
1815 NYD_ENTER;
1817 if ((p.p_p = v) == NULL) {
1818 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
1819 goto jleave;
1822 _HOPE_GET(p, isbad);
1823 --p.p_c;
1824 if (p.p_c->mc_isfree) {
1825 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1826 "\tLast seen: %s, line %d\n",
1827 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1828 goto jforce;
1831 if (p.p_c == _mem_list)
1832 _mem_list = p.p_c->mc_next;
1833 else
1834 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1835 if (p.p_c->mc_next != NULL)
1836 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1838 --_mem_acur;
1839 _mem_mcur -= p.p_c->mc_size;
1840 jforce:
1841 if (s == 0)
1842 s = 1;
1843 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1845 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
1846 _out_of_memory();
1847 p.p_c->mc_prev = NULL;
1848 if ((p.p_c->mc_next = _mem_list) != NULL)
1849 _mem_list->mc_prev = p.p_c;
1850 p.p_c->mc_file = mdbg_file;
1851 p.p_c->mc_line = (ui16_t)mdbg_line;
1852 p.p_c->mc_isfree = FAL0;
1853 p.p_c->mc_size = (ui32_t)s;
1854 _mem_list = p.p_c++;
1855 _HOPE_SET(p);
1857 ++_mem_aall;
1858 ++_mem_acur;
1859 _mem_amax = MAX(_mem_amax, _mem_acur);
1860 _mem_mall += s;
1861 _mem_mcur += s;
1862 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1863 jleave:
1864 NYD_LEAVE;
1865 return p.p_p;
1868 FL void *
1869 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1871 union mem_ptr p;
1872 NYD_ENTER;
1874 if (size == 0)
1875 size = 1;
1876 if (nmemb == 0)
1877 nmemb = 1;
1878 size *= nmemb;
1879 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
1881 if ((p.p_p = (malloc)(size)) == NULL)
1882 _out_of_memory();
1883 memset(p.p_p, 0, size);
1884 p.p_c->mc_prev = NULL;
1885 if ((p.p_c->mc_next = _mem_list) != NULL)
1886 _mem_list->mc_prev = p.p_c;
1887 p.p_c->mc_file = mdbg_file;
1888 p.p_c->mc_line = (ui16_t)mdbg_line;
1889 p.p_c->mc_isfree = FAL0;
1890 p.p_c->mc_size = (ui32_t)size;
1891 _mem_list = p.p_c++;
1892 _HOPE_SET(p);
1894 ++_mem_aall;
1895 ++_mem_acur;
1896 _mem_amax = MAX(_mem_amax, _mem_acur);
1897 _mem_mall += size;
1898 _mem_mcur += size;
1899 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1900 NYD_LEAVE;
1901 return p.p_p;
1904 FL void
1905 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1907 union mem_ptr p;
1908 bool_t isbad;
1909 NYD_ENTER;
1911 if ((p.p_p = v) == NULL) {
1912 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1913 goto jleave;
1916 _HOPE_GET(p, isbad);
1917 --p.p_c;
1918 if (p.p_c->mc_isfree) {
1919 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1920 "\tLast seen: %s, line %d\n",
1921 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1922 goto jleave;
1925 if (p.p_c == _mem_list)
1926 _mem_list = p.p_c->mc_next;
1927 else
1928 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1929 if (p.p_c->mc_next != NULL)
1930 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1931 p.p_c->mc_isfree = TRU1;
1933 --_mem_acur;
1934 _mem_mcur -= p.p_c->mc_size;
1936 if (options & OPT_DEBUG) {
1937 p.p_c->mc_next = _mem_free;
1938 _mem_free = p.p_c;
1939 } else
1940 (free)(p.p_c);
1941 jleave:
1942 NYD_LEAVE;
1945 FL void
1946 smemreset(void)
1948 union mem_ptr p;
1949 size_t c = 0, s = 0;
1950 NYD_ENTER;
1952 for (p.p_c = _mem_free; p.p_c != NULL;) {
1953 void *vp = p.p_c;
1954 ++c;
1955 s += p.p_c->mc_size;
1956 p.p_c = p.p_c->mc_next;
1957 (free)(vp);
1959 _mem_free = NULL;
1961 if (options & OPT_DEBUG)
1962 fprintf(stderr, "smemreset(): freed %" ZFMT " chunks/%" ZFMT " bytes\n",
1963 c, s);
1964 NYD_LEAVE;
1967 FL int
1968 c_smemtrace(void *v)
1970 /* For _HOPE_GET() */
1971 char const * const mdbg_file = "smemtrace()";
1972 int const mdbg_line = -1;
1973 FILE *fp;
1974 union mem_ptr p, xp;
1975 bool_t isbad;
1976 size_t lines;
1977 NYD_ENTER;
1979 v = (void*)0x1;
1980 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1981 NULL) {
1982 perror("tmpfile");
1983 goto jleave;
1986 fprintf(fp, "Memory statistics:\n"
1987 " Count cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n"
1988 " Bytes cur/peek/all: %7" ZFMT "/%7" ZFMT "/%10" ZFMT "\n\n",
1989 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
1991 fprintf(fp, "Currently allocated memory chunks:\n");
1992 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1993 ++lines, p.p_c = p.p_c->mc_next) {
1994 xp = p;
1995 ++xp.p_c;
1996 _HOPE_GET_TRACE(xp, isbad);
1997 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
1998 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1999 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2000 p.p_c->mc_line);
2003 if (options & OPT_DEBUG) {
2004 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2005 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2006 xp = p;
2007 ++xp.p_c;
2008 _HOPE_GET_TRACE(xp, isbad);
2009 fprintf(fp, "%s%p (%5" ZFMT " bytes): %s, line %u\n",
2010 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2011 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2012 p.p_c->mc_file, p.p_c->mc_line);
2016 page_or_print(fp, lines);
2017 Fclose(fp);
2018 v = NULL;
2019 jleave:
2020 NYD_LEAVE;
2021 return (v != NULL);
2024 # ifdef _HAVE_MEMCHECK
2025 FL bool_t
2026 _smemcheck(char const *mdbg_file, int mdbg_line)
2028 union mem_ptr p, xp;
2029 bool_t anybad = FAL0, isbad;
2030 size_t lines;
2031 NYD_ENTER;
2033 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2034 ++lines, p.p_c = p.p_c->mc_next) {
2035 xp = p;
2036 ++xp.p_c;
2037 _HOPE_GET_TRACE(xp, isbad);
2038 if (isbad) {
2039 anybad = TRU1;
2040 fprintf(stderr,
2041 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2042 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2043 p.p_c->mc_file, p.p_c->mc_line);
2047 if (options & OPT_DEBUG) {
2048 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2049 xp = p;
2050 ++xp.p_c;
2051 _HOPE_GET_TRACE(xp, isbad);
2052 if (isbad) {
2053 anybad = TRU1;
2054 fprintf(stderr,
2055 "! CANARY ERROR: %p (%5" ZFMT " bytes): %s, line %u\n",
2056 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2057 p.p_c->mc_file, p.p_c->mc_line);
2061 NYD_LEAVE;
2062 return anybad;
2064 # endif /* _HAVE_MEMCHECK */
2066 # undef _HOPE_SIZE
2067 # undef _HOPE_SET
2068 # undef _HOPE_GET_TRACE
2069 # undef _HOPE_GET
2070 #endif /* HAVE_DEBUG */
2072 /* vim:set fenc=utf-8:s-it-mode */