mime.c:mime_write_tohdr(): complete rewrite (Peter Hofmann)..
[s-mailx.git] / auxlily.c
blob3b9c4481e335b80aef086dcb19152fe4190a564e
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_NYD
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;
65 #endif
67 #ifdef HAVE_DEBUG
68 struct mem_chunk {
69 struct mem_chunk *mc_prev;
70 struct mem_chunk *mc_next;
71 char const *mc_file;
72 ui16_t mc_line;
73 ui8_t mc_isfree;
74 ui8_t __dummy;
75 ui32_t mc_size;
78 union mem_ptr {
79 void *p_p;
80 struct mem_chunk *p_c;
81 char *p_cp;
82 ui8_t *p_ui8p;
84 #endif
86 /* NYD, memory pool debug */
87 #ifdef HAVE_NYD
88 static ui32_t _nyd_curr, _nyd_level;
89 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
90 #endif
92 #ifdef HAVE_DEBUG
93 static size_t _mem_aall, _mem_acur, _mem_amax,
94 _mem_mall, _mem_mcur, _mem_mmax;
96 static struct mem_chunk *_mem_list, *_mem_free;
97 #endif
99 /* {hold,rele}_all_sigs() */
100 static size_t _alls_depth;
101 static sigset_t _alls_nset, _alls_oset;
103 /* {hold,rele}_sigs() */
104 static size_t _hold_sigdepth;
105 static sigset_t _hold_nset, _hold_oset;
107 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
108 #ifdef HAVE_COLOUR
109 static char * _colour_iso6429(char const *wish);
110 #endif
112 #ifdef HAVE_NYD
113 static void _nyd_print(struct nyd_info *nip);
114 #endif
116 #ifdef HAVE_COLOUR
117 static char *
118 _colour_iso6429(char const *wish)
120 char const * const wish_orig = wish;
121 char *xwish, *cp, cfg[3] = {0, 0, 0};
122 NYD_ENTER;
124 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
125 * value, ensure we have enough room for that */
127 size_t i = strlen(wish) +1;
128 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
129 memcpy(xwish, wish, i);
130 wish = xwish;
133 /* Iterate over the colour spec */
134 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
135 char *y, *x = strchr(cp, '=');
136 if (x == NULL) {
137 jbail:
138 fprintf(stderr, _(
139 "Invalid colour specification \"%s\": >>> %s <<<\n"),
140 wish_orig, cp);
141 continue;
143 *x++ = '\0';
145 /* TODO convert the ft/fg/bg parser into a table-based one! */
146 if (!asccasecmp(cp, "ft")) {
147 if (!asccasecmp(x, "bold"))
148 cfg[0] = '1';
149 else if (!asccasecmp(x, "inverse"))
150 cfg[0] = '7';
151 else if (!asccasecmp(x, "underline"))
152 cfg[0] = '4';
153 else
154 goto jbail;
155 } else if (!asccasecmp(cp, "fg")) {
156 y = cfg + 1;
157 goto jiter_colour;
158 } else if (!asccasecmp(cp, "bg")) {
159 y = cfg + 2;
160 jiter_colour:
161 if (!asccasecmp(x, "black"))
162 *y = '0';
163 else if (!asccasecmp(x, "blue"))
164 *y = '4';
165 else if (!asccasecmp(x, "green"))
166 *y = '2';
167 else if (!asccasecmp(x, "red"))
168 *y = '1';
169 else if (!asccasecmp(x, "brown"))
170 *y = '3';
171 else if (!asccasecmp(x, "magenta"))
172 *y = '5';
173 else if (!asccasecmp(x, "cyan"))
174 *y = '6';
175 else if (!asccasecmp(x, "white"))
176 *y = '7';
177 else
178 goto jbail;
179 } else
180 goto jbail;
183 /* Restore our salloc() buffer, create return value */
184 xwish = UNCONST(wish);
185 if (cfg[0] || cfg[1] || cfg[2]) {
186 xwish[0] = '\033';
187 xwish[1] = '[';
188 xwish += 2;
189 if (cfg[0])
190 *xwish++ = cfg[0];
191 if (cfg[1]) {
192 if (cfg[0])
193 *xwish++ = ';';
194 xwish[0] = '3';
195 xwish[1] = cfg[1];
196 xwish += 2;
198 if (cfg[2]) {
199 if (cfg[0] || cfg[1])
200 *xwish++ = ';';
201 xwish[0] = '4';
202 xwish[1] = cfg[2];
203 xwish += 2;
205 *xwish++ = 'm';
207 *xwish = '\0';
208 NYD_LEAVE;
209 return UNCONST(wish);
211 #endif /* HAVE_COLOUR */
213 #ifdef HAVE_NYD
214 static void
215 _nyd_print(struct nyd_info *nip) /* XXX like SFSYS;no magics;jumps:lvl wrong */
217 char buf[80];
218 union {int i; size_t z;} u;
220 u.i = snprintf(buf, sizeof buf,
221 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
222 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
223 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
224 if (u.i > 0) {
225 u.z = u.i;
226 if (u.z > sizeof buf)
227 u.z = sizeof buf - 1; /* (Skip \0) */
228 write(STDERR_FILENO, buf, u.z);
231 #endif
233 FL void
234 panic(char const *format, ...)
236 va_list ap;
237 NYD2_ENTER;
239 fprintf(stderr, _("Panic: "));
241 va_start(ap, format);
242 vfprintf(stderr, format, ap);
243 va_end(ap);
245 fputs("\n", stderr);
246 fflush(stderr);
247 NYD2_LEAVE;
248 abort(); /* Was exit(EXIT_ERR); for a while, but no */
251 FL void
252 alert(char const *format, ...)
254 va_list ap;
255 NYD2_ENTER;
257 fprintf(stderr, _("Panic: "));
259 va_start(ap, format);
260 vfprintf(stderr, format, ap);
261 va_end(ap);
263 fputs("\n", stderr);
264 fflush(stderr);
265 NYD2_LEAVE;
268 FL sighandler_type
269 safe_signal(int signum, sighandler_type handler)
271 struct sigaction nact, oact;
272 sighandler_type rv;
273 NYD2_ENTER;
275 nact.sa_handler = handler;
276 sigemptyset(&nact.sa_mask);
277 nact.sa_flags = 0;
278 #ifdef SA_RESTART
279 nact.sa_flags |= SA_RESTART;
280 #endif
281 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
282 NYD2_LEAVE;
283 return rv;
286 FL void
287 hold_all_sigs(void)
289 NYD2_ENTER;
290 if (_alls_depth++ == 0) {
291 sigfillset(&_alls_nset);
292 sigdelset(&_alls_nset, SIGABRT);
293 #ifdef SIGBUS
294 sigdelset(&_alls_nset, SIGBUS);
295 #endif
296 sigdelset(&_alls_nset, SIGCHLD);
297 sigdelset(&_alls_nset, SIGFPE);
298 sigdelset(&_alls_nset, SIGILL);
299 sigdelset(&_alls_nset, SIGKILL);
300 sigdelset(&_alls_nset, SIGSEGV);
301 sigdelset(&_alls_nset, SIGSTOP);
302 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
304 NYD2_LEAVE;
307 FL void
308 rele_all_sigs(void)
310 NYD2_ENTER;
311 if (--_alls_depth == 0)
312 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
313 NYD2_LEAVE;
316 FL void
317 hold_sigs(void)
319 NYD2_ENTER;
320 if (_hold_sigdepth++ == 0) {
321 sigemptyset(&_hold_nset);
322 sigaddset(&_hold_nset, SIGHUP);
323 sigaddset(&_hold_nset, SIGINT);
324 sigaddset(&_hold_nset, SIGQUIT);
325 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
327 NYD2_LEAVE;
330 FL void
331 rele_sigs(void)
333 NYD2_ENTER;
334 if (--_hold_sigdepth == 0)
335 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
336 NYD2_LEAVE;
339 #ifdef HAVE_NYD
340 FL void
341 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
343 struct nyd_info *nip = _nyd_infos;
345 if (_nyd_curr != NELEM(_nyd_infos))
346 nip += _nyd_curr++;
347 else
348 _nyd_curr = 1;
349 nip->ni_file = file;
350 nip->ni_fun = fun;
351 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
352 nip->ni_level = ((act == 0) ? _nyd_level
353 : (act == 1) ? ++_nyd_level : _nyd_level--);
356 FL void
357 _nyd_oncrash(int signo)
359 struct sigaction xact;
360 sigset_t xset;
361 struct nyd_info *nip;
362 size_t i;
364 xact.sa_handler = SIG_DFL;
365 sigemptyset(&xact.sa_mask);
366 xact.sa_flags = 0;
367 sigaction(signo, &xact, NULL);
369 fprintf(stderr, "\n\nNYD: program dying due to signal %d:\n", signo);
370 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
371 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
372 _nyd_print(nip++);
373 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
374 _nyd_print(nip++);
376 sigemptyset(&xset);
377 sigaddset(&xset, signo);
378 sigprocmask(SIG_UNBLOCK, &xset, NULL);
379 kill(0, signo);
380 for (;;)
381 _exit(EXIT_ERR);
383 #endif /* HAVE_NYD */
385 FL void
386 touch(struct message *mp)
388 NYD_ENTER;
389 mp->m_flag |= MTOUCH;
390 if (!(mp->m_flag & MREAD))
391 mp->m_flag |= MREAD | MSTATUS;
392 NYD_LEAVE;
395 FL bool_t
396 is_dir(char const *name)
398 struct stat sbuf;
399 bool_t rv = FAL0;
400 NYD_ENTER;
402 if (!stat(name, &sbuf))
403 rv = (S_ISDIR(sbuf.st_mode) != 0);
404 NYD_LEAVE;
405 return rv;
408 FL int
409 argcount(char **argv)
411 char **ap;
412 NYD_ENTER;
414 for (ap = argv; *ap++ != NULL;)
416 NYD_LEAVE;
417 return (int)PTR2SIZE(ap - argv - 1);
420 FL int
421 screensize(void)
423 int s;
424 char *cp;
425 NYD_ENTER;
427 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
428 s = scrnheight - 4; /* XXX no magics */
429 NYD_LEAVE;
430 return s;
433 FL char const *
434 get_pager(char const **env_addon)
436 char const *cp;
437 NYD_ENTER;
439 cp = ok_vlook(PAGER);
440 if (cp == NULL || *cp == '\0')
441 cp = XPAGER;
443 if (env_addon != NULL) {
444 *env_addon = NULL;
445 if (strstr(cp, "less") != NULL) {
446 if (getenv("LESS") == NULL)
447 *env_addon = "LESS=FRSXi";
448 } else if (strstr(cp, "lv") != NULL) {
449 if (getenv("LV") == NULL)
450 *env_addon = "LV=-c";
453 NYD_LEAVE;
454 return cp;
457 FL size_t
458 paging_seems_sensible(void)
460 size_t rv = 0;
461 char const *cp;
462 NYD_ENTER;
464 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL)
465 rv = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
466 NYD_LEAVE;
467 return rv;
470 FL void
471 page_or_print(FILE *fp, size_t lines)
473 size_t rows;
474 int c;
475 NYD_ENTER;
477 fflush_rewind(fp);
479 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
480 while ((c = getc(fp)) != EOF)
481 if (c == '\n' && ++lines > rows)
482 break;
483 really_rewind(fp);
486 if (rows != 0 && lines >= rows)
487 run_command(get_pager(NULL), 0, fileno(fp), -1, NULL, NULL, NULL);
488 else
489 while ((c = getc(fp)) != EOF)
490 putchar(c);
491 NYD_LEAVE;
494 FL enum protocol
495 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
497 struct stat st;
498 char const *cp;
499 char *np;
500 size_t sz;
501 enum protocol rv = PROTO_UNKNOWN;
502 NYD_ENTER;
504 temporary_protocol_ext = NULL;
506 if (name[0] == '%' && name[1] == ':')
507 name += 2;
508 for (cp = name; *cp && *cp != ':'; cp++)
509 if (!alnumchar(*cp))
510 goto jfile;
512 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
513 if (!strncmp(name, "pop3://", 7)) {
514 #ifdef HAVE_POP3
515 rv = PROTO_POP3;
516 #else
517 fprintf(stderr, _("No POP3 support compiled in.\n"));
518 #endif
519 } else if (!strncmp(name, "pop3s://", 8)) {
520 #if defined HAVE_POP3 && defined HAVE_SSL
521 rv = PROTO_POP3;
522 #else
523 # ifndef HAVE_POP3
524 fprintf(stderr, _("No POP3 support compiled in.\n"));
525 # endif
526 # ifndef HAVE_SSL
527 fprintf(stderr, _("No SSL support compiled in.\n"));
528 # endif
529 #endif
530 } else if (!strncmp(name, "imap://", 7)) {
531 #ifdef HAVE_IMAP
532 rv = PROTO_IMAP;
533 #else
534 fprintf(stderr, _("No IMAP support compiled in.\n"));
535 #endif
536 } else if (!strncmp(name, "imaps://", 8)) {
537 #if defined HAVE_IMAP && defined HAVE_SSL
538 rv = PROTO_IMAP;
539 #else
540 # ifndef HAVE_IMAP
541 fprintf(stderr, _("No IMAP support compiled in.\n"));
542 # endif
543 # ifndef HAVE_SSL
544 fprintf(stderr, _("No SSL support compiled in.\n"));
545 # endif
546 #endif
548 goto jleave;
551 /* TODO This is the de facto maildir code and thus belongs into there!
552 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
553 * TODO or (more likely) in addition to *newfolders*) */
554 jfile:
555 rv = PROTO_FILE;
556 np = ac_alloc((sz = strlen(name)) + 4 +1);
557 memcpy(np, name, sz + 1);
558 if (!stat(name, &st)) {
559 if (S_ISDIR(st.st_mode) &&
560 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
561 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
562 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
563 rv = PROTO_MAILDIR;
564 } else {
565 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
566 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
567 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
568 temporary_protocol_ext = cp;
569 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
570 rv = PROTO_MAILDIR;
572 ac_free(np);
573 jleave:
574 NYD_LEAVE;
575 return rv;
578 FL ui32_t
579 torek_hash(char const *name)
581 /* Chris Torek's hash.
582 * NOTE: need to change *at least* create-okey-map.pl when changing the
583 * algorithm!! */
584 ui32_t h = 0;
585 NYD_ENTER;
587 while (*name != '\0') {
588 h *= 33;
589 h += *name++;
591 NYD_LEAVE;
592 return h;
595 FL unsigned
596 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
598 unsigned h = 0, g;
599 NYD_ENTER;
601 cp--;
602 while (*++cp) {
603 h = (h << 4 & 0xffffffff) + (*cp&0377);
604 if ((g = h & 0xf0000000) != 0) {
605 h = h ^ g >> 24;
606 h = h ^ g;
609 NYD_LEAVE;
610 return h;
613 FL ui32_t
614 nextprime(ui32_t n)
616 static ui32_t const primes[] = {
617 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
618 131071, 262139, 524287, 1048573, 2097143, 4194301,
619 8388593, 16777213, 33554393, 67108859, 134217689,
620 268435399, 536870909, 1073741789, 2147483647
623 ui32_t mprime = 7, cutlim;
624 size_t i;
625 NYD_ENTER;
627 cutlim = (n < 65536 ? n << 2 : (n < 262144 ? n << 1 : n));
629 for (i = 0; i < NELEM(primes); i++)
630 if ((mprime = primes[i]) >= cutlim)
631 break;
632 if (i == NELEM(primes) && mprime < n)
633 mprime = n;
634 NYD_LEAVE;
635 return mprime;
638 FL int
639 expand_shell_escape(char const **s, bool_t use_nail_extensions)
641 char const *xs;
642 int c, n;
643 NYD2_ENTER;
645 xs = *s;
647 if ((c = *xs & 0xFF) == '\0')
648 goto jleave;
649 ++xs;
650 if (c != '\\')
651 goto jleave;
653 switch ((c = *xs & 0xFF)) {
654 case '\\': break;
655 case 'a': c = '\a'; break;
656 case 'b': c = '\b'; break;
657 case 'c': c = PROMPT_STOP; break;
658 case 'f': c = '\f'; break;
659 case 'n': c = '\n'; break;
660 case 'r': c = '\r'; break;
661 case 't': c = '\t'; break;
662 case 'v': c = '\v'; break;
663 case '0':
664 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
665 c <<= 3;
666 c |= *xs - '0';
668 goto jleave;
669 /* S-nail extension for nice (get)prompt(()) support */
670 case '&':
671 case '?':
672 case '$':
673 case '@':
674 if (use_nail_extensions) {
675 switch (c) {
676 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
677 case '?': c = exec_last_comm_error ? '1' : '0'; break;
678 case '$': c = PROMPT_DOLLAR; break;
679 case '@': c = PROMPT_AT; break;
681 break;
683 /* FALLTHRU */
684 case '\0':
685 /* A sole <backslash> at EOS is treated as-is! */
686 /* FALLTHRU */
687 default:
688 c = '\\';
689 goto jleave;
691 ++xs;
692 jleave:
693 *s = xs;
694 NYD2_LEAVE;
695 return c;
698 FL char *
699 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
701 static char buf[PROMPT_BUFFER_SIZE];
703 char *cp;
704 char const *ccp_base, *ccp;
705 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
706 bool_t run2;
707 NYD_ENTER;
709 cp = buf;
710 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0')
711 goto jleave;
712 NATCH_CHAR( cclen_base = strlen(ccp_base); )
714 dfmaxlen = 0; /* keep CC happy */
715 run2 = FAL0;
716 jredo:
717 ccp = ccp_base;
718 NATCH_CHAR( cclen = cclen_base; )
719 maxlen = sizeof(buf) -1;
721 for (;;) {
722 size_t l;
723 int c;
725 if (maxlen == 0)
726 goto jleave;
727 #ifdef HAVE_NATCH_CHAR
728 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
729 if (c <= 0) {
730 mblen(NULL, 0);
731 if (c < 0) {
732 *buf = '?';
733 cp = buf + 1;
734 goto jleave;
736 break;
737 } else if ((l = c) > 1) {
738 if (run2) {
739 memcpy(cp, ccp, l);
740 cp += l;
742 ccp += l;
743 maxlen -= l;
744 continue;
745 } else
746 #endif
747 if ((c = expand_shell_escape(&ccp, TRU1)) > 0) {
748 if (run2)
749 *cp++ = (char)c;
750 --maxlen;
751 continue;
753 if (c == 0 || c == PROMPT_STOP)
754 break;
756 if (run2) {
757 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
758 if (a == NULL)
759 a = "";
760 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
761 cp += l;
762 maxlen -= l;
763 dfmaxlen -= l;
768 if (!run2) {
769 run2 = TRU1;
770 dfmaxlen = maxlen;
771 goto jredo;
773 jleave:
774 *cp = '\0';
775 NYD_LEAVE;
776 return buf;
779 FL char *
780 nodename(int mayoverride)
782 static char *sys_hostname, *hostname; /* XXX free-at-exit */
784 struct utsname ut;
785 char *hn;
786 #ifdef HAVE_SOCKETS
787 # ifdef HAVE_IPV6
788 struct addrinfo hints, *res;
789 # else
790 struct hostent *hent;
791 # endif
792 #endif
793 NYD_ENTER;
795 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
797 } else if ((hn = sys_hostname) == NULL) {
798 uname(&ut);
799 hn = ut.nodename;
800 #ifdef HAVE_SOCKETS
801 # ifdef HAVE_IPV6
802 memset(&hints, 0, sizeof hints);
803 hints.ai_family = AF_UNSPEC;
804 hints.ai_socktype = SOCK_DGRAM; /* (dummy) */
805 hints.ai_flags = AI_CANONNAME;
806 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
807 if (res->ai_canonname != NULL) {
808 size_t l = strlen(res->ai_canonname) +1;
809 hn = ac_alloc(l);
810 memcpy(hn, res->ai_canonname, l);
812 freeaddrinfo(res);
814 # else
815 hent = gethostbyname(hn);
816 if (hent != NULL)
817 hn = hent->h_name;
818 # endif
819 #endif
820 sys_hostname = sstrdup(hn);
821 #if defined HAVE_SOCKETS && defined HAVE_IPV6
822 if (hn != ut.nodename)
823 ac_free(hn);
824 #endif
825 hn = sys_hostname;
828 if (hostname != NULL && hostname != sys_hostname)
829 free(hostname);
830 hostname = sstrdup(hn);
831 NYD_LEAVE;
832 return hostname;
835 FL char *
836 getrandstring(size_t length)
838 static unsigned char nodedigest[16];
839 static pid_t pid;
841 struct str b64;
842 char *data, *cp;
843 size_t i;
844 int fd = -1;
845 #ifdef HAVE_MD5
846 md5_ctx ctx;
847 #else
848 size_t j;
849 #endif
850 NYD_ENTER;
852 data = ac_alloc(length);
854 if ((fd = open("/dev/urandom", O_RDONLY)) == -1 ||
855 length != (size_t)read(fd, data, length)) {
856 if (pid == 0) {
857 pid = getpid();
858 srand(pid);
859 cp = nodename(0);
860 #ifdef HAVE_MD5
861 md5_init(&ctx);
862 md5_update(&ctx, (unsigned char*)cp, strlen(cp));
863 md5_final(nodedigest, &ctx);
864 #else
865 /* In that case it's only used for boundaries and Message-Id:s so that
866 * srand(3) should suffice */
867 j = strlen(cp) + 1;
868 for (i = 0; i < sizeof(nodedigest); ++i)
869 nodedigest[i] = (unsigned char)(cp[i % j] ^ rand());
870 #endif
872 for (i = 0; i < length; i++)
873 data[i] = (char)((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
874 nodedigest[i % sizeof nodedigest]);
876 if (fd >= 0)
877 close(fd);
879 b64_encode_buf(&b64, data, length, B64_SALLOC);
880 ac_free(data);
881 assert(length < b64.l);
882 b64.s[length] = '\0';
884 /* Base64 includes + and /, replace them with _ and - */
885 for (data = b64.s; length-- > 0; ++data)
886 if (*data == '+')
887 *data = '_';
888 else if (*data == '/')
889 *data = '-';
890 NYD_LEAVE;
891 return b64.s;
894 FL enum okay
895 makedir(char const *name)
897 struct stat st;
898 enum okay rv = STOP;
899 NYD_ENTER;
901 if (!mkdir(name, 0700))
902 rv = OKAY;
903 else {
904 int e = errno;
905 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
906 S_ISDIR(st.st_mode))
907 rv = OKAY;
909 NYD_LEAVE;
910 return rv;
913 #ifdef HAVE_FCHDIR
914 FL enum okay
915 cwget(struct cw *cw)
917 enum okay rv = STOP;
918 NYD_ENTER;
920 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
921 goto jleave;
922 if (fchdir(cw->cw_fd) == -1) {
923 close(cw->cw_fd);
924 goto jleave;
926 rv = OKAY;
927 jleave:
928 NYD_LEAVE;
929 return rv;
932 FL enum okay
933 cwret(struct cw *cw)
935 enum okay rv = STOP;
936 NYD_ENTER;
938 if (!fchdir(cw->cw_fd))
939 rv = OKAY;
940 NYD_LEAVE;
941 return rv;
944 FL void
945 cwrelse(struct cw *cw)
947 NYD_ENTER;
948 close(cw->cw_fd);
949 NYD_LEAVE;
952 #else /* !HAVE_FCHDIR */
953 FL enum okay
954 cwget(struct cw *cw)
956 enum okay rv = STOP;
957 NYD_ENTER;
959 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
960 rv = OKAY;
961 NYD_LEAVE;
962 return rv;
965 FL enum okay
966 cwret(struct cw *cw)
968 enum okay rv = STOP;
969 NYD_ENTER;
971 if (!chdir(cw->cw_wd))
972 rv = OKAY;
973 NYD_LEAVE;
974 return rv;
977 FL void
978 cwrelse(struct cw *cw)
980 NYD_ENTER;
981 UNUSED(cw);
982 NYD_LEAVE;
984 #endif /* !HAVE_FCHDIR */
986 FL size_t
987 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
989 size_t rv;
990 NYD_ENTER;
992 #ifdef HAVE_NATCH_CHAR
993 maxlen = MIN(maxlen, blen);
994 for (rv = 0; maxlen > 0;) {
995 int ml = mblen(buf, maxlen);
996 if (ml <= 0) {
997 mblen(NULL, 0);
998 break;
1000 buf += ml;
1001 rv += ml;
1002 maxlen -= ml;
1004 #else
1005 rv = MIN(blen, maxlen);
1006 #endif
1007 NYD_LEAVE;
1008 return rv;
1011 FL size_t
1012 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1014 NATCH_CHAR( struct bidi_info bi; )
1015 size_t rv NATCH_CHAR( COMMA i );
1016 NYD_ENTER;
1018 rv = 0;
1019 if (maxlen-- == 0)
1020 goto j_leave;
1022 #ifdef HAVE_NATCH_CHAR
1023 bidi_info_create(&bi);
1024 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1025 bi.bi_end.l = 0;
1026 goto jnobidi;
1029 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1030 maxlen -= i;
1031 else
1032 goto jleave;
1034 if ((i = bi.bi_start.l) > 0) {
1035 memcpy(store, bi.bi_start.s, i);
1036 store += i;
1037 rv += i;
1040 jnobidi:
1041 while (maxlen > 0) {
1042 int ml = mblen(buf, blen);
1043 if (ml <= 0) {
1044 mblen(NULL, 0);
1045 break;
1047 if (UICMP(z, maxlen, <, ml))
1048 break;
1049 if (ml == 1)
1050 *store = *buf;
1051 else
1052 memcpy(store, buf, ml);
1053 store += ml;
1054 buf += ml;
1055 rv += ml;
1056 maxlen -= ml;
1059 if ((i = bi.bi_end.l) > 0) {
1060 memcpy(store, bi.bi_end.s, i);
1061 store += i;
1062 rv += i;
1064 jleave:
1065 *store = '\0';
1067 #else
1068 rv = MIN(blen, maxlen);
1069 memcpy(store, buf, rv);
1070 store[rv] = '\0';
1071 #endif
1072 j_leave:
1073 NYD_LEAVE;
1074 return rv;
1077 FL char *
1078 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1080 NATCH_CHAR( struct bidi_info bi; )
1081 int col_orig = col, n, sz;
1082 bool_t isbidi, isuni, istab, isrepl;
1083 char *nb, *np;
1084 NYD_ENTER;
1086 /* Bidi only on request and when there is 8-bit data */
1087 isbidi = isuni = FAL0;
1088 #ifdef HAVE_NATCH_CHAR
1089 isuni = ((options & OPT_UNICODE) != 0);
1090 bidi_info_create(&bi);
1091 if (bi.bi_start.l == 0)
1092 goto jnobidi;
1093 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1094 goto jnobidi;
1096 if ((size_t)col >= bi.bi_pad)
1097 col -= bi.bi_pad;
1098 else
1099 col = 0;
1100 jnobidi:
1101 #endif
1103 np = nb = salloc(mb_cur_max * strlen(cp) +
1104 ((fill ? col : 0)
1105 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1106 +1));
1108 #ifdef HAVE_NATCH_CHAR
1109 if (isbidi) {
1110 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1111 np += bi.bi_start.l;
1113 #endif
1115 while (*cp != '\0') {
1116 istab = FAL0;
1117 #ifdef HAVE_C90AMEND1
1118 if (mb_cur_max > 1) {
1119 wchar_t wc;
1121 n = 1;
1122 isrepl = TRU1;
1123 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1124 sz = 1;
1125 else if (wc == L'\t') {
1126 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1127 isrepl = FAL0;
1128 istab = TRU1;
1129 } else if (iswprint(wc)) {
1130 # ifndef HAVE_WCWIDTH
1131 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1132 # else
1133 if ((n = wcwidth(wc)) == -1)
1134 n = 1;
1135 else
1136 # endif
1137 isrepl = FAL0;
1139 } else
1140 #endif
1142 n = sz = 1;
1143 istab = (*cp == '\t');
1144 isrepl = !(istab || isprint((uc_i)*cp));
1147 if (n > col)
1148 break;
1149 col -= n;
1151 if (isrepl) {
1152 if (isuni) {
1153 np[0] = (char)0xEFu;
1154 np[1] = (char)0xBFu;
1155 np[2] = (char)0xBDu;
1156 np += 3;
1157 } else
1158 *np++ = '?';
1159 cp += sz;
1160 } else if (istab || (sz == 1 && spacechar(*cp))) {
1161 *np++ = ' ';
1162 ++cp;
1163 } else
1164 while (sz--)
1165 *np++ = *cp++;
1168 if (fill && col != 0) {
1169 if (fill > 0) {
1170 memmove(nb + col, nb, PTR2SIZE(np - nb));
1171 memset(nb, ' ', col);
1172 } else
1173 memset(np, ' ', col);
1174 np += col;
1175 col = 0;
1178 #ifdef HAVE_NATCH_CHAR
1179 if (isbidi) {
1180 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1181 np += bi.bi_end.l;
1183 #endif
1185 *np = '\0';
1186 if (cols_decr_used_or_null != NULL)
1187 *cols_decr_used_or_null -= col_orig - col;
1188 NYD_LEAVE;
1189 return nb;
1192 FL void
1193 makeprint(struct str const *in, struct str *out)
1195 static int print_all_chars = -1;
1197 char const *inp, *maxp;
1198 char *outp;
1199 DBG( size_t msz; )
1200 NYD_ENTER;
1202 if (print_all_chars == -1)
1203 print_all_chars = ok_blook(print_all_chars);
1205 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max);
1206 inp = in->s;
1207 maxp = inp + in->l;
1209 if (print_all_chars) {
1210 out->l = in->l;
1211 memcpy(outp, inp, out->l);
1212 goto jleave;
1215 #ifdef HAVE_NATCH_CHAR
1216 if (mb_cur_max > 1) {
1217 char mbb[MB_LEN_MAX + 1];
1218 wchar_t wc;
1219 int i, n;
1220 bool_t isuni = ((options & OPT_UNICODE) != 0);
1222 out->l = 0;
1223 while (inp < maxp) {
1224 if (*inp & 0200)
1225 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1226 else {
1227 wc = *inp;
1228 n = 1;
1230 if (n == -1) {
1231 /* FIXME Why mbtowc() resetting here?
1232 * FIXME what about ISO 2022-JP plus -- those
1233 * FIXME will loose shifts, then!
1234 * FIXME THUS - we'd need special "known points"
1235 * FIXME to do so - say, after a newline!!
1236 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1237 mbtowc(&wc, NULL, mb_cur_max);
1238 wc = isuni ? 0xFFFD : '?';
1239 n = 1;
1240 } else if (n == 0)
1241 n = 1;
1242 inp += n;
1243 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1244 wc != '\t') {
1245 if ((wc & ~(wchar_t)037) == 0)
1246 wc = isuni ? 0x2400 | wc : '?';
1247 else if (wc == 0177)
1248 wc = isuni ? 0x2421 : '?';
1249 else
1250 wc = isuni ? 0x2426 : '?';
1252 if ((n = wctomb(mbb, wc)) <= 0)
1253 continue;
1254 out->l += n;
1255 assert(out->l < msz);
1256 for (i = 0; i < n; ++i)
1257 *outp++ = mbb[i];
1259 } else
1260 #endif /* NATCH_CHAR */
1262 int c;
1263 while (inp < maxp) {
1264 c = *inp++ & 0377;
1265 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1266 c = '?';
1267 *outp++ = c;
1269 out->l = in->l;
1271 jleave:
1272 out->s[out->l] = '\0';
1273 NYD_LEAVE;
1276 FL char *
1277 prstr(char const *s)
1279 struct str in, out;
1280 char *rp;
1281 NYD_ENTER;
1283 in.s = UNCONST(s);
1284 in.l = strlen(s);
1285 makeprint(&in, &out);
1286 rp = savestrbuf(out.s, out.l);
1287 free(out.s);
1288 NYD_LEAVE;
1289 return rp;
1292 FL int
1293 prout(char const *s, size_t sz, FILE *fp)
1295 struct str in, out;
1296 int n;
1297 NYD_ENTER;
1299 in.s = UNCONST(s);
1300 in.l = sz;
1301 makeprint(&in, &out);
1302 n = fwrite(out.s, 1, out.l, fp);
1303 free(out.s);
1304 NYD_LEAVE;
1305 return n;
1308 FL size_t
1309 putuc(int u, int c, FILE *fp)
1311 size_t rv;
1312 NYD_ENTER;
1313 UNUSED(u);
1315 #ifdef HAVE_NATCH_CHAR
1316 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1317 char mbb[MB_LEN_MAX];
1318 int i, n;
1320 if ((n = wctomb(mbb, u)) > 0) {
1321 rv = wcwidth(u);
1322 for (i = 0; i < n; ++i)
1323 if (putc(mbb[i] & 0377, fp) == EOF) {
1324 rv = 0;
1325 break;
1327 } else if (n == 0)
1328 rv = (putc('\0', fp) != EOF);
1329 else
1330 rv = 0;
1331 } else
1332 #endif
1333 rv = (putc(c, fp) != EOF);
1334 NYD_LEAVE;
1335 return rv;
1338 FL bool_t
1339 bidi_info_needed(char const *bdat, size_t blen)
1341 bool_t rv = FAL0;
1342 NYD_ENTER;
1344 #ifdef HAVE_NATCH_CHAR
1345 if (options & OPT_UNICODE)
1346 for (; blen > 0; ++bdat, --blen) {
1347 if ((ui8_t)*bdat > 0x7F) {
1348 /* TODO Checking for BIDI character: use S-CText fromutf8
1349 * TODO plus isrighttoleft (or whatever there will be)! */
1350 ui32_t c, x = (ui8_t)*bdat;
1352 if ((x & 0xE0) == 0xC0) {
1353 if (blen < 2)
1354 break;
1355 blen -= 1;
1356 c = x & ~0xC0;
1357 } else if ((x & 0xF0) == 0xE0) {
1358 if (blen < 3)
1359 break;
1360 blen -= 2;
1361 c = x & ~0xE0;
1362 c <<= 6;
1363 x = (ui8_t)*++bdat;
1364 c |= x & 0x7F;
1365 } else {
1366 if (blen < 4)
1367 break;
1368 blen -= 3;
1369 c = x & ~0xF0;
1370 c <<= 6;
1371 x = (ui8_t)*++bdat;
1372 c |= x & 0x7F;
1373 c <<= 6;
1374 x = (ui8_t)*++bdat;
1375 c |= x & 0x7F;
1377 c <<= 6;
1378 x = (ui8_t)*++bdat;
1379 c |= x & 0x7F;
1381 /* (Very very fuzzy, awaiting S-CText for good) */
1382 if ((c >= 0x05BE && c <= 0x08E3) ||
1383 (c >= 0xFB1D && c <= 0xFEFC) ||
1384 (c >= 0x10800 && c <= 0x10C48) ||
1385 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1386 rv = TRU1;
1387 break;
1391 #endif /* HAVE_NATCH_CHAR */
1392 NYD_LEAVE;
1393 return rv;
1396 FL void
1397 bidi_info_create(struct bidi_info *bip)
1399 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1400 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1401 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1402 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1403 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1404 NATCH_CHAR( char const *hb; )
1405 NYD_ENTER;
1407 memset(bip, 0, sizeof *bip);
1408 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1410 #ifdef HAVE_NATCH_CHAR
1411 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1412 switch (*hb) {
1413 case '3':
1414 bip->bi_pad = 2;
1415 /* FALLTHRU */
1416 case '2':
1417 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1418 break;
1419 case '1':
1420 bip->bi_pad = 2;
1421 /* FALLTHRU */
1422 default:
1423 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1424 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1425 break;
1427 bip->bi_start.l = bip->bi_end.l = 3;
1429 #endif
1430 NYD_LEAVE;
1433 #ifdef HAVE_COLOUR
1434 FL void
1435 colour_table_create(bool_t pager_used)
1437 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1438 size_t i;
1439 struct colour_table *ct;
1440 NYD_ENTER;
1442 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1443 goto jleave;
1444 else {
1445 char *term, *okterms;
1447 /* Don't use getenv(), but force copy-in into our own tables.. */
1448 if ((term = _var_voklook("TERM")) == NULL)
1449 goto jleave;
1450 /* terminfo rocks: if we find "color", assume it's right */
1451 if (strstr(term, "color") != NULL)
1452 goto jok;
1453 if ((okterms = ok_vlook(colour_terms)) == NULL)
1454 okterms = UNCONST(COLOUR_TERMS);
1455 okterms = savestr(okterms);
1457 i = strlen(term);
1458 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1459 if (!strncmp(u.cp, term, i))
1460 goto jok;
1461 goto jleave;
1464 jok:
1465 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1466 { static struct {
1467 enum okeys okey;
1468 enum colourspec cspec;
1469 char const *defval;
1470 } const map[] = {
1471 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1472 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1473 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1474 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1475 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1478 for (i = 0; i < NELEM(map); ++i) {
1479 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1480 u.ccp = map[i].defval;
1481 u.cp = _colour_iso6429(u.ccp);
1482 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1483 ct->ct_csinfo[map[i].cspec].s = u.cp;
1486 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1487 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1489 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1490 u.ccp = COLOUR_USER_HEADERS;
1491 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1492 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1493 jleave:
1494 NYD_LEAVE;
1497 FL void
1498 colour_put(FILE *fp, enum colourspec cs)
1500 NYD_ENTER;
1501 if (colour_table != NULL) {
1502 struct str const *cp = colour_get(cs);
1504 fwrite(cp->s, cp->l, 1, fp);
1506 NYD_LEAVE;
1509 FL void
1510 colour_put_header(FILE *fp, char const *name)
1512 enum colourspec cs = COLOURSPEC_HEADER;
1513 struct str const *uheads;
1514 char *cp, *cp_base, *x;
1515 size_t namelen;
1516 NYD_ENTER;
1518 if (colour_table == NULL)
1519 goto j_leave;
1520 /* Normal header colours if there are no user headers */
1521 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1522 if (uheads->s == NULL)
1523 goto jleave;
1525 /* Iterate over all entries in the *colour-user-headers* list */
1526 cp = ac_alloc(uheads->l +1);
1527 memcpy(cp, uheads->s, uheads->l +1);
1528 cp_base = cp;
1529 namelen = strlen(name);
1530 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1531 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1532 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1533 cs = COLOURSPEC_UHEADER;
1534 break;
1537 ac_free(cp_base);
1538 jleave:
1539 colour_put(fp, cs);
1540 j_leave:
1541 NYD_LEAVE;
1544 FL void
1545 colour_reset(FILE *fp)
1547 NYD_ENTER;
1548 if (colour_table != NULL)
1549 fwrite("\033[0m", 4, 1, fp);
1550 NYD_LEAVE;
1553 FL struct str const *
1554 colour_get(enum colourspec cs)
1556 struct str const *rv = NULL;
1557 NYD_ENTER;
1559 if (colour_table != NULL)
1560 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1561 rv = NULL;
1562 NYD_LEAVE;
1563 return rv;
1565 #endif /* HAVE_COLOUR */
1567 FL void
1568 time_current_update(struct time_current *tc, bool_t full_update)
1570 NYD_ENTER;
1571 tc->tc_time = time(NULL);
1572 if (full_update) {
1573 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1574 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1575 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1577 NYD_LEAVE;
1580 static void
1581 _out_of_memory(void)
1583 panic("no memory");
1586 #ifndef HAVE_DEBUG
1587 FL void *
1588 smalloc(size_t s SMALLOC_DEBUG_ARGS)
1590 void *rv;
1591 NYD2_ENTER;
1593 if (s == 0)
1594 s = 1;
1595 if ((rv = malloc(s)) == NULL)
1596 _out_of_memory();
1597 NYD2_LEAVE;
1598 return rv;
1601 FL void *
1602 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
1604 void *rv;
1605 NYD2_ENTER;
1607 if (s == 0)
1608 s = 1;
1609 if (v == NULL)
1610 rv = smalloc(s);
1611 else if ((rv = realloc(v, s)) == NULL)
1612 _out_of_memory();
1613 NYD2_LEAVE;
1614 return rv;
1617 FL void *
1618 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1620 void *rv;
1621 NYD2_ENTER;
1623 if (size == 0)
1624 size = 1;
1625 if ((rv = calloc(nmemb, size)) == NULL)
1626 _out_of_memory();
1627 NYD2_LEAVE;
1628 return rv;
1631 #else /* !HAVE_DEBUG */
1632 CTA(sizeof(char) == sizeof(ui8_t));
1634 # define _HOPE_SIZE (2 * 8 * sizeof(char))
1635 # define _HOPE_SET(C) \
1636 do {\
1637 union mem_ptr __xl, __xu;\
1638 struct mem_chunk *__xc;\
1639 __xl.p_p = (C).p_p;\
1640 __xc = __xl.p_c - 1;\
1641 __xu.p_p = __xc;\
1642 (C).p_cp += 8;\
1643 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
1644 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
1645 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
1646 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
1647 __xu.p_ui8p += __xc->mc_size - 8;\
1648 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
1649 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
1650 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
1651 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
1652 } while (0)
1653 # define _HOPE_GET_TRACE(C,BAD) \
1654 do {\
1655 (C).p_cp += 8;\
1656 _HOPE_GET(C, BAD);\
1657 (C).p_cp += 8;\
1658 } while(0)
1659 # define _HOPE_GET(C,BAD) \
1660 do {\
1661 union mem_ptr __xl, __xu;\
1662 struct mem_chunk *__xc;\
1663 ui32_t __i;\
1664 __xl.p_p = (C).p_p;\
1665 __xl.p_cp -= 8;\
1666 (C).p_cp = __xl.p_cp;\
1667 __xc = __xl.p_c - 1;\
1668 (BAD) = FAL0;\
1669 __i = 0;\
1670 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1671 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1672 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
1673 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1674 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1675 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
1676 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1677 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1678 if (__i != 0) {\
1679 (BAD) = TRU1;\
1680 alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
1681 __xl.p_p, __i, mdbg_file, mdbg_line);\
1683 __xu.p_p = __xc;\
1684 __xu.p_ui8p += __xc->mc_size - 8;\
1685 __i = 0;\
1686 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
1687 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
1688 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
1689 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
1690 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
1691 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
1692 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
1693 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
1694 if (__i != 0) {\
1695 (BAD) = TRU1;\
1696 alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
1697 __xl.p_p, __i, mdbg_file, mdbg_line);\
1699 if (BAD)\
1700 alert(" ..canary last seen: %s, line %" PRIu16 "",\
1701 __xc->mc_file, __xc->mc_line);\
1702 } while (0)
1704 FL void *
1705 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1707 union mem_ptr p;
1708 NYD2_ENTER;
1710 if (s == 0)
1711 s = 1;
1712 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1714 if ((p.p_p = (malloc)(s)) == NULL)
1715 _out_of_memory();
1716 p.p_c->mc_prev = NULL;
1717 if ((p.p_c->mc_next = _mem_list) != NULL)
1718 _mem_list->mc_prev = p.p_c;
1719 p.p_c->mc_file = mdbg_file;
1720 p.p_c->mc_line = (ui16_t)mdbg_line;
1721 p.p_c->mc_isfree = FAL0;
1722 p.p_c->mc_size = (ui32_t)s;
1723 _mem_list = p.p_c++;
1724 _HOPE_SET(p);
1726 ++_mem_aall;
1727 ++_mem_acur;
1728 _mem_amax = MAX(_mem_amax, _mem_acur);
1729 _mem_mall += s;
1730 _mem_mcur += s;
1731 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1732 NYD2_LEAVE;
1733 return p.p_p;
1736 FL void *
1737 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1739 union mem_ptr p;
1740 bool_t isbad;
1741 NYD2_ENTER;
1743 if ((p.p_p = v) == NULL) {
1744 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
1745 goto jleave;
1748 _HOPE_GET(p, isbad);
1749 --p.p_c;
1750 if (p.p_c->mc_isfree) {
1751 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1752 "\tLast seen: %s, line %" PRIu16 "\n",
1753 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1754 goto jforce;
1757 if (p.p_c == _mem_list)
1758 _mem_list = p.p_c->mc_next;
1759 else
1760 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1761 if (p.p_c->mc_next != NULL)
1762 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1764 --_mem_acur;
1765 _mem_mcur -= p.p_c->mc_size;
1766 jforce:
1767 if (s == 0)
1768 s = 1;
1769 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
1771 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
1772 _out_of_memory();
1773 p.p_c->mc_prev = NULL;
1774 if ((p.p_c->mc_next = _mem_list) != NULL)
1775 _mem_list->mc_prev = p.p_c;
1776 p.p_c->mc_file = mdbg_file;
1777 p.p_c->mc_line = (ui16_t)mdbg_line;
1778 p.p_c->mc_isfree = FAL0;
1779 p.p_c->mc_size = (ui32_t)s;
1780 _mem_list = p.p_c++;
1781 _HOPE_SET(p);
1783 ++_mem_aall;
1784 ++_mem_acur;
1785 _mem_amax = MAX(_mem_amax, _mem_acur);
1786 _mem_mall += s;
1787 _mem_mcur += s;
1788 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1789 jleave:
1790 NYD2_LEAVE;
1791 return p.p_p;
1794 FL void *
1795 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1797 union mem_ptr p;
1798 NYD2_ENTER;
1800 if (size == 0)
1801 size = 1;
1802 if (nmemb == 0)
1803 nmemb = 1;
1804 size *= nmemb;
1805 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
1807 if ((p.p_p = (malloc)(size)) == NULL)
1808 _out_of_memory();
1809 memset(p.p_p, 0, size);
1810 p.p_c->mc_prev = NULL;
1811 if ((p.p_c->mc_next = _mem_list) != NULL)
1812 _mem_list->mc_prev = p.p_c;
1813 p.p_c->mc_file = mdbg_file;
1814 p.p_c->mc_line = (ui16_t)mdbg_line;
1815 p.p_c->mc_isfree = FAL0;
1816 p.p_c->mc_size = (ui32_t)size;
1817 _mem_list = p.p_c++;
1818 _HOPE_SET(p);
1820 ++_mem_aall;
1821 ++_mem_acur;
1822 _mem_amax = MAX(_mem_amax, _mem_acur);
1823 _mem_mall += size;
1824 _mem_mcur += size;
1825 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
1826 NYD2_LEAVE;
1827 return p.p_p;
1830 FL void
1831 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1833 union mem_ptr p;
1834 bool_t isbad;
1835 NYD2_ENTER;
1837 if ((p.p_p = v) == NULL) {
1838 fprintf(stderr, "sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
1839 goto jleave;
1842 _HOPE_GET(p, isbad);
1843 --p.p_c;
1844 if (p.p_c->mc_isfree) {
1845 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1846 "\tLast seen: %s, line %" PRIu16 "\n",
1847 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
1848 goto jleave;
1851 if (p.p_c == _mem_list)
1852 _mem_list = p.p_c->mc_next;
1853 else
1854 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
1855 if (p.p_c->mc_next != NULL)
1856 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
1857 p.p_c->mc_isfree = TRU1;
1859 --_mem_acur;
1860 _mem_mcur -= p.p_c->mc_size;
1862 if (options & OPT_DEBUG) {
1863 p.p_c->mc_next = _mem_free;
1864 _mem_free = p.p_c;
1865 } else
1866 (free)(p.p_c);
1867 jleave:
1868 NYD2_LEAVE;
1871 FL void
1872 smemreset(void)
1874 union mem_ptr p;
1875 size_t c = 0, s = 0;
1876 NYD_ENTER;
1878 for (p.p_c = _mem_free; p.p_c != NULL;) {
1879 void *vp = p.p_c;
1880 ++c;
1881 s += p.p_c->mc_size;
1882 p.p_c = p.p_c->mc_next;
1883 (free)(vp);
1885 _mem_free = NULL;
1887 if (options & OPT_DEBUG)
1888 fprintf(stderr, "smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n",
1889 c, s);
1890 NYD_LEAVE;
1893 FL int
1894 c_smemtrace(void *v)
1896 /* For _HOPE_GET() */
1897 char const * const mdbg_file = "smemtrace()";
1898 int const mdbg_line = -1;
1899 FILE *fp;
1900 union mem_ptr p, xp;
1901 bool_t isbad;
1902 size_t lines;
1903 NYD_ENTER;
1905 v = (void*)0x1;
1906 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1907 NULL) {
1908 perror("tmpfile");
1909 goto jleave;
1912 fprintf(fp, "Memory statistics:\n"
1913 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
1914 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
1915 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
1917 fprintf(fp, "Currently allocated memory chunks:\n");
1918 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1919 ++lines, p.p_c = p.p_c->mc_next) {
1920 xp = p;
1921 ++xp.p_c;
1922 _HOPE_GET_TRACE(xp, isbad);
1923 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
1924 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1925 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
1926 p.p_c->mc_line);
1929 if (options & OPT_DEBUG) {
1930 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1931 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1932 xp = p;
1933 ++xp.p_c;
1934 _HOPE_GET_TRACE(xp, isbad);
1935 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
1936 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
1937 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1938 p.p_c->mc_file, p.p_c->mc_line);
1942 page_or_print(fp, lines);
1943 Fclose(fp);
1944 v = NULL;
1945 jleave:
1946 NYD_LEAVE;
1947 return (v != NULL);
1950 # ifdef _HAVE_MEMCHECK
1951 FL bool_t
1952 _smemcheck(char const *mdbg_file, int mdbg_line)
1954 union mem_ptr p, xp;
1955 bool_t anybad = FAL0, isbad;
1956 size_t lines;
1957 NYD_ENTER;
1959 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
1960 ++lines, p.p_c = p.p_c->mc_next) {
1961 xp = p;
1962 ++xp.p_c;
1963 _HOPE_GET_TRACE(xp, isbad);
1964 if (isbad) {
1965 anybad = TRU1;
1966 fprintf(stderr,
1967 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
1968 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1969 p.p_c->mc_file, p.p_c->mc_line);
1973 if (options & OPT_DEBUG) {
1974 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
1975 xp = p;
1976 ++xp.p_c;
1977 _HOPE_GET_TRACE(xp, isbad);
1978 if (isbad) {
1979 anybad = TRU1;
1980 fprintf(stderr,
1981 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
1982 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
1983 p.p_c->mc_file, p.p_c->mc_line);
1987 NYD_LEAVE;
1988 return anybad;
1990 # endif /* _HAVE_MEMCHECK */
1992 # undef _HOPE_SIZE
1993 # undef _HOPE_SET
1994 # undef _HOPE_GET_TRACE
1995 # undef _HOPE_GET
1996 #endif /* HAVE_DEBUG */
1998 /* s-it-mode */