nail.1: mention "portable character set", e.g., for `urlencode'
[s-mailx.git] / auxlily.c
blob2f2ab1f574dcaefb55e6e1482fbd59d0ca0bc14e
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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #include <ctype.h>
45 #include <pwd.h>
47 #ifdef HAVE_SOCKETS
48 # ifdef HAVE_GETADDRINFO
49 # include <sys/socket.h>
50 # endif
52 # include <netdb.h>
53 #endif
55 #ifndef HAVE_POSIX_RANDOM
56 union rand_state {
57 struct rand_arc4 {
58 ui8_t __pad[6];
59 ui8_t _i;
60 ui8_t _j;
61 ui8_t _dat[256];
62 } a;
63 ui8_t b8[sizeof(struct rand_arc4)];
64 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
66 #endif
68 #ifdef HAVE_NYD
69 struct nyd_info {
70 char const *ni_file;
71 char const *ni_fun;
72 ui32_t ni_chirp_line;
73 ui32_t ni_level;
75 #endif
77 struct shvar_stack {
78 struct shvar_stack *shs_next; /* Outer stack frame */
79 char const *shs_value; /* Remaining value to expand */
80 size_t shs_len; /* gth of .shs_dat this level */
81 char const *shs_dat; /* Result data of this level */
82 bool_t *shs_err; /* Or NULL */
83 bool_t shs_bsesc; /* Shall backslash escaping be performed */
86 #ifdef HAVE_ERRORS
87 struct err_node {
88 struct err_node *en_next;
89 struct str en_str;
91 #endif
93 #ifdef HAVE_DEBUG
94 struct mem_chunk {
95 struct mem_chunk *mc_prev;
96 struct mem_chunk *mc_next;
97 char const *mc_file;
98 ui16_t mc_line;
99 ui8_t mc_isfree;
100 ui8_t __dummy;
101 ui32_t mc_size;
104 union mem_ptr {
105 void *p_p;
106 struct mem_chunk *p_c;
107 char *p_cp;
108 ui8_t *p_ui8p;
110 #endif
112 #ifndef HAVE_POSIX_RANDOM
113 static union rand_state *_rand;
114 #endif
116 /* {hold,rele}_all_sigs() */
117 static size_t _alls_depth;
118 static sigset_t _alls_nset, _alls_oset;
120 /* {hold,rele}_sigs() */
121 static size_t _hold_sigdepth;
122 static sigset_t _hold_nset, _hold_oset;
124 /* NYD, memory pool debug */
125 #ifdef HAVE_NYD
126 static ui32_t _nyd_curr, _nyd_level;
127 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
128 #endif
130 /* Error ring, for `errors' */
131 #ifdef HAVE_ERRORS
132 static struct err_node *_err_head, *_err_tail;
133 static size_t _err_cnt, _err_cnt_noted;
134 #endif
136 #ifdef HAVE_DEBUG
137 static size_t _mem_aall, _mem_acur, _mem_amax,
138 _mem_mall, _mem_mcur, _mem_mmax;
140 static struct mem_chunk *_mem_list, *_mem_free;
141 #endif
143 /* Our ARC4 random generator with its completely unacademical pseudo
144 * initialization (shall /dev/urandom fail) */
145 #ifndef HAVE_POSIX_RANDOM
146 static void _rand_init(void);
147 static ui32_t _rand_weak(ui32_t seed);
148 SINLINE ui8_t _rand_get8(void);
149 #endif
151 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence */
152 #ifdef HAVE_COLOUR
153 static char * _colour_iso6429(char const *wish);
154 #endif
156 #ifdef HAVE_NYD
157 static void _nyd_print(int fd, struct nyd_info *nip);
158 #endif
160 /* Perform shell variable expansion */
161 static char * _sh_exp_var(struct shvar_stack *shsp);
163 #ifndef HAVE_POSIX_RANDOM
164 static void
165 _rand_init(void)
167 # ifdef HAVE_CLOCK_GETTIME
168 struct timespec ts;
169 # else
170 struct timeval ts;
171 # endif
172 union {int fd; size_t i;} u;
173 ui32_t seed, rnd;
174 NYD2_ENTER;
176 _rand = smalloc(sizeof *_rand);
178 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
179 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
181 close(u.fd);
182 if (ok)
183 goto jleave;
186 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
187 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
188 size_t t, k;
190 # ifdef HAVE_CLOCK_GETTIME
191 clock_gettime(CLOCK_REALTIME, &ts);
192 t = (ui32_t)ts.tv_nsec;
193 # else
194 gettimeofday(&ts, NULL);
195 t = (ui32_t)ts.tv_usec;
196 # endif
197 if (rnd & 1)
198 t = (t >> 16) | (t << 16);
199 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
200 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
201 if (rnd == 7 || rnd == 17)
202 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
203 k = _rand->b32[u.i] % NELEM(_rand->b32);
204 _rand->b32[k] ^= _rand->b32[u.i];
205 seed ^= _rand_weak(_rand->b32[k]);
206 if ((rnd & 3) == 3)
207 seed ^= nextprime(seed);
211 for (u.i = 11 * sizeof(_rand->b8); u.i != 0; --u.i)
212 _rand_get8();
213 jleave:
214 NYD2_LEAVE;
217 static ui32_t
218 _rand_weak(ui32_t seed)
220 /* From "Random number generators: good ones are hard to find",
221 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
222 * October 1988, p. 1195.
223 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
224 ui32_t hi;
226 if (seed == 0)
227 seed = 123459876;
228 hi = seed / 127773;
229 seed %= 127773;
230 seed = (seed * 16807) - (hi * 2836);
231 if ((si32_t)seed < 0)
232 seed += SI32_MAX;
233 return seed;
236 SINLINE ui8_t
237 _rand_get8(void)
239 ui8_t si, sj;
241 si = _rand->a._dat[++_rand->a._i];
242 sj = _rand->a._dat[_rand->a._j += si];
243 _rand->a._dat[_rand->a._i] = sj;
244 _rand->a._dat[_rand->a._j] = si;
245 return _rand->a._dat[(ui8_t)(si + sj)];
247 #endif /* HAVE_POSIX_RANDOM */
249 #ifdef HAVE_COLOUR
250 static char *
251 _colour_iso6429(char const *wish)
253 struct isodesc {
254 char id_name[15];
255 char id_modc;
256 } const fta[] = {
257 {"bold", '1'}, {"underline", '4'}, {"inverse", '7'}
258 }, ca[] = {
259 {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
260 {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
261 }, *idp;
262 char const * const wish_orig = wish;
263 char *xwish, *cp, cfg[3] = {0, 0, 0};
264 NYD_ENTER;
266 /* Since we use salloc(), reuse the n_strsep() buffer also for the return
267 * value, ensure we have enough room for that */
269 size_t i = strlen(wish) +1;
270 xwish = salloc(MAX(i, sizeof("\033[1;30;40m")));
271 memcpy(xwish, wish, i);
272 wish = xwish;
275 /* Iterate over the colour spec */
276 while ((cp = n_strsep(&xwish, ',', TRU1)) != NULL) {
277 char *y, *x = strchr(cp, '=');
278 if (x == NULL) {
279 jbail:
280 n_err(_("Invalid colour specification \"%s\": %s\n"), wish_orig, cp);
281 continue;
283 *x++ = '\0';
285 if (!asccasecmp(cp, "ft")) {
286 for (idp = fta;; ++idp)
287 if (idp == fta + NELEM(fta))
288 goto jbail;
289 else if (!asccasecmp(x, idp->id_name)) {
290 cfg[0] = idp->id_modc;
291 break;
293 } else if (!asccasecmp(cp, "fg")) {
294 y = cfg + 1;
295 goto jiter_colour;
296 } else if (!asccasecmp(cp, "bg")) {
297 y = cfg + 2;
298 jiter_colour:
299 for (idp = ca;; ++idp)
300 if (idp == ca + NELEM(ca))
301 goto jbail;
302 else if (!asccasecmp(x, idp->id_name)) {
303 *y = idp->id_modc;
304 break;
306 } else
307 goto jbail;
310 /* Restore our salloc() buffer, create return value */
311 xwish = UNCONST(wish);
312 if (cfg[0] || cfg[1] || cfg[2]) {
313 xwish[0] = '\033';
314 xwish[1] = '[';
315 xwish += 2;
316 if (cfg[0])
317 *xwish++ = cfg[0];
318 if (cfg[1]) {
319 if (cfg[0])
320 *xwish++ = ';';
321 xwish[0] = '3';
322 xwish[1] = cfg[1];
323 xwish += 2;
325 if (cfg[2]) {
326 if (cfg[0] || cfg[1])
327 *xwish++ = ';';
328 xwish[0] = '4';
329 xwish[1] = cfg[2];
330 xwish += 2;
332 *xwish++ = 'm';
334 *xwish = '\0';
335 NYD_LEAVE;
336 return UNCONST(wish);
338 #endif /* HAVE_COLOUR */
340 #ifdef HAVE_NYD
341 static void
342 _nyd_print(int fd, struct nyd_info *nip)
344 char buf[80];
345 union {int i; size_t z;} u;
347 u.i = snprintf(buf, sizeof buf,
348 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
349 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
350 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
351 if (u.i > 0) {
352 u.z = u.i;
353 if (u.z > sizeof buf)
354 u.z = sizeof buf - 1; /* (Skip \0) */
355 write(fd, buf, u.z);
358 #endif
360 static char *
361 _sh_exp_var(struct shvar_stack *shsp)
363 struct shvar_stack next, *np, *tmp;
364 char const *vp;
365 char lc, c, *cp, *rv;
366 size_t i;
367 NYD2_ENTER;
369 if (*(vp = shsp->shs_value) != '$') {
370 bool_t bsesc = shsp->shs_bsesc;
371 union {bool_t hadbs; char c;} u = {FAL0};
373 shsp->shs_dat = vp;
374 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
375 if (c == '$' && lc != '\\')
376 break;
377 if (!bsesc)
378 continue;
379 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
381 shsp->shs_len = i;
383 if (u.hadbs) {
384 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
386 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
387 if (u.c != '\\' || lc == '\\')
388 *rv++ = u.c;
389 lc = (lc == '\\') ? '\0' : u.c;
391 *rv = '\0';
393 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
395 } else {
396 if ((lc = (*++vp == '{')))
397 ++vp;
399 /* POSIX says
400 * Environment variable names used by the utilities in the Shell and
401 * Utilities volume of POSIX.1-2008 consist solely of uppercase
402 * letters, digits, and the <underscore> ('_') from the characters
403 * defined in Portable Character Set and do not begin with a digit.
404 * Other characters may be permitted by an implementation;
405 * applications shall tolerate the presence of such names. */
406 shsp->shs_dat = vp;
407 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
408 if (!alnumchar(c) && c != '_')
409 break;
411 if (lc) {
412 if (c != '}') {
413 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
414 shsp->shs_value);
415 shsp->shs_len = strlen(shsp->shs_value);
416 shsp->shs_dat = shsp->shs_value;
417 if (shsp->shs_err != NULL)
418 *shsp->shs_err = TRU1;
419 goto junroll;
421 c = *++vp;
424 shsp->shs_len = i;
425 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
426 shsp->shs_len = strlen(shsp->shs_dat = cp);
428 if (c != '\0')
429 goto jrecurse;
431 /* That level made the great and completed encoding. Build result */
432 junroll:
433 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
434 i += np->shs_len;
435 tmp = np->shs_next;
436 np->shs_next = shsp;
437 shsp = np;
438 np = tmp;
441 cp = rv = salloc(i +1);
442 while (shsp != NULL) {
443 np = shsp;
444 shsp = shsp->shs_next;
445 memcpy(cp, np->shs_dat, np->shs_len);
446 cp += np->shs_len;
448 *cp = '\0';
450 jleave:
451 NYD2_LEAVE;
452 return rv;
453 jrecurse:
454 memset(&next, 0, sizeof next);
455 next.shs_next = shsp;
456 next.shs_value = vp;
457 next.shs_err = shsp->shs_err;
458 next.shs_bsesc = shsp->shs_bsesc;
459 rv = _sh_exp_var(&next);
460 goto jleave;
463 FL void
464 n_raise(int signo)
466 NYD2_ENTER;
467 kill(getpid(), signo);
468 NYD2_LEAVE;
471 FL sighandler_type
472 safe_signal(int signum, sighandler_type handler)
474 struct sigaction nact, oact;
475 sighandler_type rv;
476 NYD2_ENTER;
478 nact.sa_handler = handler;
479 sigemptyset(&nact.sa_mask);
480 nact.sa_flags = 0;
481 #ifdef SA_RESTART
482 nact.sa_flags |= SA_RESTART;
483 #endif
484 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
485 NYD2_LEAVE;
486 return rv;
489 FL void
490 hold_all_sigs(void)
492 NYD2_ENTER;
493 if (_alls_depth++ == 0) {
494 sigfillset(&_alls_nset);
495 sigdelset(&_alls_nset, SIGABRT);
496 #ifdef SIGBUS
497 sigdelset(&_alls_nset, SIGBUS);
498 #endif
499 sigdelset(&_alls_nset, SIGCHLD);
500 sigdelset(&_alls_nset, SIGFPE);
501 sigdelset(&_alls_nset, SIGILL);
502 sigdelset(&_alls_nset, SIGKILL);
503 sigdelset(&_alls_nset, SIGSEGV);
504 sigdelset(&_alls_nset, SIGSTOP);
505 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
507 NYD2_LEAVE;
510 FL void
511 rele_all_sigs(void)
513 NYD2_ENTER;
514 if (--_alls_depth == 0)
515 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
516 NYD2_LEAVE;
519 FL void
520 hold_sigs(void)
522 NYD2_ENTER;
523 if (_hold_sigdepth++ == 0) {
524 sigemptyset(&_hold_nset);
525 sigaddset(&_hold_nset, SIGHUP);
526 sigaddset(&_hold_nset, SIGINT);
527 sigaddset(&_hold_nset, SIGQUIT);
528 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
530 NYD2_LEAVE;
533 FL void
534 rele_sigs(void)
536 NYD2_ENTER;
537 if (--_hold_sigdepth == 0)
538 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
539 NYD2_LEAVE;
542 #ifdef HAVE_NYD
543 FL void
544 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
546 struct nyd_info *nip = _nyd_infos;
548 if (_nyd_curr != NELEM(_nyd_infos))
549 nip += _nyd_curr++;
550 else
551 _nyd_curr = 1;
552 nip->ni_file = file;
553 nip->ni_fun = fun;
554 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
555 nip->ni_level = ((act == 0) ? _nyd_level
556 : (act == 1) ? ++_nyd_level : _nyd_level--);
559 FL void
560 _nyd_oncrash(int signo)
562 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
563 struct sigaction xact;
564 sigset_t xset;
565 size_t i, fnl;
566 int fd;
567 struct nyd_info *nip;
569 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
571 xact.sa_handler = SIG_DFL;
572 sigemptyset(&xact.sa_mask);
573 xact.sa_flags = 0;
574 sigaction(signo, &xact, NULL);
576 i = strlen(tempdir);
577 fnl = sizeof(UAGENT) -1;
579 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
580 (cp = pathbuf)[0] = '.';
581 i = 1;
582 } else
583 memcpy(cp = pathbuf, tempdir, i);
584 cp[i++] = '/'; /* xxx pathsep */
585 memcpy(cp += i, UAGENT, fnl);
586 i += fnl;
587 memcpy(cp += fnl, ".dat", sizeof(".dat"));
588 fnl = i + sizeof(".dat") -1;
590 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
591 fd = STDERR_FILENO;
593 # undef _X
594 # define _X(X) (X), sizeof(X) -1
595 write(fd, _X("\n\nNYD: program dying due to signal "));
597 cp = s2ibuf + sizeof(s2ibuf) -1;
598 *cp = '\0';
599 i = signo;
600 do {
601 *--cp = "0123456789"[i % 10];
602 i /= 10;
603 } while (i != 0);
604 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
606 write(fd, _X(":\n"));
608 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
609 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
610 _nyd_print(fd, nip++);
611 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
612 _nyd_print(fd, nip++);
614 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
616 if (fd != STDERR_FILENO) {
617 write(STDERR_FILENO, _X("Crash NYD listing written to "));
618 write(STDERR_FILENO, pathbuf, fnl);
619 write(STDERR_FILENO, _X("\n"));
620 # undef _X
622 close(fd);
625 sigemptyset(&xset);
626 sigaddset(&xset, signo);
627 sigprocmask(SIG_UNBLOCK, &xset, NULL);
628 n_raise(signo);
629 for (;;)
630 _exit(EXIT_ERR);
632 #endif /* HAVE_NYD */
634 FL void
635 touch(struct message *mp)
637 NYD_ENTER;
638 mp->m_flag |= MTOUCH;
639 if (!(mp->m_flag & MREAD))
640 mp->m_flag |= MREAD | MSTATUS;
641 NYD_LEAVE;
644 FL bool_t
645 is_dir(char const *name)
647 struct stat sbuf;
648 bool_t rv;
649 NYD_ENTER;
651 for (rv = FAL0;;)
652 if (!stat(name, &sbuf)) {
653 rv = (S_ISDIR(sbuf.st_mode) != 0);
654 break;
655 } else if (errno != EINTR)
656 break;
657 NYD_LEAVE;
658 return rv;
661 FL int
662 argcount(char **argv)
664 char **ap;
665 NYD_ENTER;
667 for (ap = argv; *ap++ != NULL;)
669 NYD_LEAVE;
670 return (int)PTR2SIZE(ap - argv - 1);
673 FL int
674 screensize(void)
676 int s;
677 char *cp;
678 NYD_ENTER;
680 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
681 s = scrnheight - 2; /* XXX no magics */
682 NYD_LEAVE;
683 return s;
686 FL char const *
687 get_pager(char const **env_addon)
689 char const *cp;
690 NYD_ENTER;
692 cp = ok_vlook(PAGER);
693 if (cp == NULL || *cp == '\0')
694 cp = XPAGER;
696 if (env_addon != NULL) {
697 *env_addon = NULL;
698 if (strstr(cp, "less") != NULL) {
699 if (!env_blook("LESS", TRU1))
700 *env_addon = "LESS=FRSXi";
701 } else if (strstr(cp, "lv") != NULL) {
702 if (!env_blook("LV", TRU1))
703 *env_addon = "LV=-c";
706 NYD_LEAVE;
707 return cp;
710 FL void
711 page_or_print(FILE *fp, size_t lines)
713 int c;
714 char const *cp;
715 NYD_ENTER;
717 fflush_rewind(fp);
719 if (IS_TTY_SESSION() && (cp = ok_vlook(crt)) != NULL) {
720 char *eptr;
721 union {sl_i sli; size_t rows;} u;
723 u.sli = strtol(cp, &eptr, 0);
724 u.rows = (*cp != '\0' && *eptr == '\0')
725 ? (size_t)u.sli : (size_t)scrnheight;
727 if (u.rows > 0 && lines == 0) {
728 while ((c = getc(fp)) != EOF)
729 if (c == '\n' && ++lines >= u.rows)
730 break;
731 really_rewind(fp);
734 if (lines >= u.rows) {
735 run_command(get_pager(NULL), 0, fileno(fp), COMMAND_FD_PASS,
736 NULL, NULL, NULL, NULL);
737 goto jleave;
741 while ((c = getc(fp)) != EOF)
742 putchar(c);
743 jleave:
744 NYD_LEAVE;
747 FL enum protocol
748 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
750 struct stat st;
751 char const *cp;
752 char *np;
753 size_t sz;
754 enum protocol rv = PROTO_UNKNOWN;
755 NYD_ENTER;
757 temporary_protocol_ext = NULL;
759 if (name[0] == '%' && name[1] == ':')
760 name += 2;
761 for (cp = name; *cp && *cp != ':'; cp++)
762 if (!alnumchar(*cp))
763 goto jfile;
765 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
766 if (!strncmp(name, "pop3://", 7)) {
767 #ifdef HAVE_POP3
768 rv = PROTO_POP3;
769 #else
770 n_err(_("No POP3 support compiled in\n"));
771 #endif
772 } else if (!strncmp(name, "pop3s://", 8)) {
773 #if defined HAVE_POP3 && defined HAVE_SSL
774 rv = PROTO_POP3;
775 #else
776 # ifndef HAVE_POP3
777 n_err(_("No POP3 support compiled in\n"));
778 # endif
779 # ifndef HAVE_SSL
780 n_err(_("No SSL support compiled in\n"));
781 # endif
782 #endif
783 } else if (!strncmp(name, "imap://", 7)) {
784 #ifdef HAVE_IMAP
785 rv = PROTO_IMAP;
786 #else
787 fprintf(stderr, _("No IMAP support compiled in.\n"));
788 #endif
789 } else if (!strncmp(name, "imaps://", 8)) {
790 #if defined HAVE_IMAP && defined HAVE_SSL
791 rv = PROTO_IMAP;
792 #else
793 # ifndef HAVE_IMAP
794 fprintf(stderr, _("No IMAP support compiled in.\n"));
795 # endif
796 # ifndef HAVE_SSL
797 fprintf(stderr, _("No SSL support compiled in.\n"));
798 # endif
799 #endif
801 goto jleave;
804 /* TODO This is the de facto maildir code and thus belongs into there!
805 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
806 * TODO or (more likely) in addition to *newfolders*) */
807 jfile:
808 rv = PROTO_FILE;
809 np = ac_alloc((sz = strlen(name)) + 4 +1);
810 memcpy(np, name, sz + 1);
811 if (!stat(name, &st)) {
812 if (S_ISDIR(st.st_mode) &&
813 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
814 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
815 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
816 rv = PROTO_MAILDIR;
817 } else {
818 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
819 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
820 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
821 temporary_protocol_ext = cp;
822 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
823 rv = PROTO_MAILDIR;
825 ac_free(np);
826 jleave:
827 NYD_LEAVE;
828 return rv;
831 FL ui32_t
832 torek_hash(char const *name)
834 /* Chris Torek's hash.
835 * NOTE: need to change *at least* create-okey-map.pl when changing the
836 * algorithm!! */
837 ui32_t h = 0;
838 NYD_ENTER;
840 while (*name != '\0') {
841 h *= 33;
842 h += *name++;
844 NYD_LEAVE;
845 return h;
848 FL unsigned
849 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
851 unsigned h = 0, g;
852 NYD_ENTER;
854 cp--;
855 while (*++cp) {
856 h = (h << 4 & 0xffffffff) + (*cp&0377);
857 if ((g = h & 0xf0000000) != 0) {
858 h = h ^ g >> 24;
859 h = h ^ g;
862 NYD_LEAVE;
863 return h;
866 FL ui32_t
867 nextprime(ui32_t n)
869 static ui32_t const primes[] = {
870 5, 11, 23, 47, 97, 157, 283,
871 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
872 131071, 262139, 524287, 1048573, 2097143, 4194301,
873 8388593, 16777213, 33554393, 67108859, 134217689,
874 268435399, 536870909, 1073741789, 2147483647
877 ui32_t i, mprime;
878 NYD_ENTER;
880 i = (n < primes[NELEM(primes) / 4] ? 0
881 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
882 : NELEM(primes) / 2));
884 if ((mprime = primes[i]) > n)
885 break;
886 while (++i < NELEM(primes));
887 if (i == NELEM(primes) && mprime < n)
888 mprime = n;
889 NYD_LEAVE;
890 return mprime;
893 FL char *
894 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
896 struct passwd *pwp;
897 size_t nl, rl;
898 char const *rp, *np;
899 char *rv;
900 bool_t err;
901 NYD2_ENTER;
903 err = FAL0;
905 if (s[0] != '~')
906 goto jasis;
908 if (*(rp = s + 1) == '/' || *rp == '\0')
909 np = homedir;
910 else {
911 if ((rp = strchr(s + 1, '/')) == NULL)
912 rp = (np = UNCONST(s)) + 1;
913 else {
914 nl = PTR2SIZE(rp - s);
915 np = savestrbuf(s, nl);
918 if ((pwp = getpwnam(np)) == NULL) {
919 err = TRU1;
920 goto jasis;
922 np = pwp->pw_name;
925 nl = strlen(np);
926 rl = strlen(rp);
927 rv = salloc(nl + 1 + rl +1);
928 memcpy(rv, np, nl);
929 if (rl > 0) {
930 memcpy(rv + nl, rp, rl);
931 nl += rl;
933 rv[nl] = '\0';
934 goto jleave;
936 jasis:
937 rv = savestr(s);
938 jleave:
939 if (err_or_null != NULL)
940 *err_or_null = err;
941 NYD2_LEAVE;
942 return rv;
945 FL char *
946 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
948 struct shvar_stack top;
949 char *rv;
950 NYD2_ENTER;
952 memset(&top, 0, sizeof top);
954 top.shs_value = s;
955 if ((top.shs_err = err_or_null) != NULL)
956 *err_or_null = FAL0;
957 top.shs_bsesc = bsescape;
958 rv = _sh_exp_var(&top);
959 NYD2_LEAVE;
960 return rv;
963 FL int
964 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
966 char const *xs;
967 int c, n;
968 NYD2_ENTER;
970 xs = *s;
972 if ((c = *xs & 0xFF) == '\0')
973 goto jleave;
974 ++xs;
975 if (c != '\\')
976 goto jleave;
978 switch ((c = *xs & 0xFF)) {
979 case '\\': break;
980 case 'a': c = '\a'; break;
981 case 'b': c = '\b'; break;
982 case 'c': c = PROMPT_STOP; break;
983 case 'f': c = '\f'; break;
984 case 'n': c = '\n'; break;
985 case 'r': c = '\r'; break;
986 case 't': c = '\t'; break;
987 case 'v': c = '\v'; break;
988 case '0':
989 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
990 c <<= 3;
991 c |= *xs - '0';
993 goto jleave;
994 /* S-nail extension for nice (get)prompt(()) support */
995 case '&':
996 case '?':
997 case '$':
998 case '@':
999 if (use_nail_extensions) {
1000 switch (c) {
1001 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
1002 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
1003 case '$': c = PROMPT_DOLLAR; break;
1004 case '@': c = PROMPT_AT; break;
1006 break;
1008 /* FALLTHRU */
1009 case '\0':
1010 /* A sole <backslash> at EOS is treated as-is! */
1011 /* FALLTHRU */
1012 default:
1013 c = '\\';
1014 goto jleave;
1016 ++xs;
1017 jleave:
1018 *s = xs;
1019 NYD2_LEAVE;
1020 return c;
1023 FL char *
1024 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
1026 static char buf[PROMPT_BUFFER_SIZE];
1028 char *cp;
1029 char const *ccp_base, *ccp;
1030 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
1031 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
1032 NYD_ENTER;
1034 /* No other place to place this */
1035 #ifdef HAVE_ERRORS
1036 if (options & OPT_INTERACTIVE) {
1037 if (!(pstate & PS_ERRORS_NOTED) && _err_head != NULL) {
1038 pstate |= PS_ERRORS_NOTED;
1039 fprintf(stderr, _("There are new messages in the error message ring "
1040 "(denoted by \"#ERR#\")\n"
1041 " The `errors' command manages this message ring\n"));
1044 if ((trigger = (_err_cnt_noted != _err_cnt)))
1045 _err_cnt_noted = _err_cnt;
1046 } else
1047 trigger = FAL0;
1048 #endif
1050 cp = buf;
1051 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
1052 #ifdef HAVE_ERRORS
1053 if (trigger)
1054 ccp_base = "";
1055 else
1056 #endif
1057 goto jleave;
1059 #ifdef HAVE_ERRORS
1060 if (trigger)
1061 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
1062 #endif
1063 NATCH_CHAR( cclen_base = strlen(ccp_base); )
1065 dfmaxlen = 0; /* keep CC happy */
1066 trigger = FAL0;
1067 jredo:
1068 ccp = ccp_base;
1069 NATCH_CHAR( cclen = cclen_base; )
1070 maxlen = sizeof(buf) -1;
1072 for (;;) {
1073 size_t l;
1074 int c;
1076 if (maxlen == 0)
1077 goto jleave;
1078 #ifdef HAVE_NATCH_CHAR
1079 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
1080 if (c <= 0) {
1081 mblen(NULL, 0);
1082 if (c < 0) {
1083 *buf = '?';
1084 cp = buf + 1;
1085 goto jleave;
1087 break;
1088 } else if ((l = c) > 1) {
1089 if (trigger) {
1090 memcpy(cp, ccp, l);
1091 cp += l;
1093 ccp += l;
1094 maxlen -= l;
1095 continue;
1096 } else
1097 #endif
1098 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
1099 if (trigger)
1100 *cp++ = (char)c;
1101 --maxlen;
1102 continue;
1104 if (c == 0 || c == PROMPT_STOP)
1105 break;
1107 if (trigger) {
1108 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
1109 if (a == NULL)
1110 a = "";
1111 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
1112 cp += l;
1113 maxlen -= l;
1114 dfmaxlen -= l;
1119 if (!trigger) {
1120 trigger = TRU1;
1121 dfmaxlen = maxlen;
1122 goto jredo;
1124 jleave:
1125 *cp = '\0';
1126 NYD_LEAVE;
1127 return buf;
1130 FL char *
1131 nodename(int mayoverride)
1133 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1135 struct utsname ut;
1136 char *hn;
1137 #ifdef HAVE_SOCKETS
1138 # ifdef HAVE_GETADDRINFO
1139 struct addrinfo hints, *res;
1140 # else
1141 struct hostent *hent;
1142 # endif
1143 #endif
1144 NYD_ENTER;
1146 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
1148 } else if ((hn = sys_hostname) == NULL) {
1149 uname(&ut);
1150 hn = ut.nodename;
1151 #ifdef HAVE_SOCKETS
1152 # ifdef HAVE_GETADDRINFO
1153 memset(&hints, 0, sizeof hints);
1154 hints.ai_family = AF_UNSPEC;
1155 hints.ai_flags = AI_CANONNAME;
1156 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
1157 if (res->ai_canonname != NULL) {
1158 size_t l = strlen(res->ai_canonname) +1;
1160 hn = ac_alloc(l);
1161 memcpy(hn, res->ai_canonname, l);
1163 freeaddrinfo(res);
1165 # else
1166 hent = gethostbyname(hn);
1167 if (hent != NULL)
1168 hn = hent->h_name;
1169 # endif
1170 #endif
1171 sys_hostname = sstrdup(hn);
1172 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1173 if (hn != ut.nodename)
1174 ac_free(hn);
1175 #endif
1176 hn = sys_hostname;
1179 if (hostname != NULL && hostname != sys_hostname)
1180 free(hostname);
1181 hostname = sstrdup(hn);
1182 NYD_LEAVE;
1183 return hostname;
1186 FL char *
1187 getrandstring(size_t length)
1189 struct str b64;
1190 char *data;
1191 size_t i;
1192 NYD_ENTER;
1194 #ifndef HAVE_POSIX_RANDOM
1195 if (_rand == NULL)
1196 _rand_init();
1197 #endif
1199 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1200 * with PAD stripped is still longer than what the user requests, easy way */
1201 data = ac_alloc(i = length + 3);
1203 #ifndef HAVE_POSIX_RANDOM
1204 while (i-- > 0)
1205 data[i] = (char)_rand_get8();
1206 #else
1207 { char *cp = data;
1209 while (i > 0) {
1210 union {ui32_t i4; char c[4];} r;
1211 size_t j;
1213 r.i4 = (ui32_t)arc4random();
1214 switch ((j = i & 3)) {
1215 case 0: cp[3] = r.c[3]; j = 4;
1216 case 3: cp[2] = r.c[2];
1217 case 2: cp[1] = r.c[1];
1218 default: cp[0] = r.c[0]; break;
1220 cp += j;
1221 i -= j;
1224 #endif
1226 b64_encode_buf(&b64, data, length + 3,
1227 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1228 ac_free(data);
1230 assert(b64.l >= length);
1231 b64.s[length] = '\0';
1232 NYD_LEAVE;
1233 return b64.s;
1236 FL enum okay
1237 makedir(char const *name)
1239 struct stat st;
1240 enum okay rv = STOP;
1241 NYD_ENTER;
1243 if (!mkdir(name, 0700))
1244 rv = OKAY;
1245 else {
1246 int e = errno;
1247 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1248 S_ISDIR(st.st_mode))
1249 rv = OKAY;
1251 NYD_LEAVE;
1252 return rv;
1255 #ifdef HAVE_FCHDIR
1256 FL enum okay
1257 cwget(struct cw *cw)
1259 enum okay rv = STOP;
1260 NYD_ENTER;
1262 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1263 goto jleave;
1264 if (fchdir(cw->cw_fd) == -1) {
1265 close(cw->cw_fd);
1266 goto jleave;
1268 rv = OKAY;
1269 jleave:
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 (!fchdir(cw->cw_fd))
1281 rv = OKAY;
1282 NYD_LEAVE;
1283 return rv;
1286 FL void
1287 cwrelse(struct cw *cw)
1289 NYD_ENTER;
1290 close(cw->cw_fd);
1291 NYD_LEAVE;
1294 #else /* !HAVE_FCHDIR */
1295 FL enum okay
1296 cwget(struct cw *cw)
1298 enum okay rv = STOP;
1299 NYD_ENTER;
1301 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1302 rv = OKAY;
1303 NYD_LEAVE;
1304 return rv;
1307 FL enum okay
1308 cwret(struct cw *cw)
1310 enum okay rv = STOP;
1311 NYD_ENTER;
1313 if (!chdir(cw->cw_wd))
1314 rv = OKAY;
1315 NYD_LEAVE;
1316 return rv;
1319 FL void
1320 cwrelse(struct cw *cw)
1322 NYD_ENTER;
1323 UNUSED(cw);
1324 NYD_LEAVE;
1326 #endif /* !HAVE_FCHDIR */
1328 FL size_t
1329 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1331 size_t rv;
1332 NYD_ENTER;
1334 #ifdef HAVE_NATCH_CHAR
1335 maxlen = MIN(maxlen, blen);
1336 for (rv = 0; maxlen > 0;) {
1337 int ml = mblen(buf, maxlen);
1338 if (ml <= 0) {
1339 mblen(NULL, 0);
1340 break;
1342 buf += ml;
1343 rv += ml;
1344 maxlen -= ml;
1346 #else
1347 rv = MIN(blen, maxlen);
1348 #endif
1349 NYD_LEAVE;
1350 return rv;
1353 FL size_t
1354 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1356 NATCH_CHAR( struct bidi_info bi; )
1357 size_t rv NATCH_CHAR( COMMA i );
1358 NYD_ENTER;
1360 rv = 0;
1361 if (maxlen-- == 0)
1362 goto j_leave;
1364 #ifdef HAVE_NATCH_CHAR
1365 bidi_info_create(&bi);
1366 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1367 bi.bi_end.l = 0;
1368 goto jnobidi;
1371 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1372 maxlen -= i;
1373 else
1374 goto jleave;
1376 if ((i = bi.bi_start.l) > 0) {
1377 memcpy(store, bi.bi_start.s, i);
1378 store += i;
1379 rv += i;
1382 jnobidi:
1383 while (maxlen > 0) {
1384 int ml = mblen(buf, blen);
1385 if (ml <= 0) {
1386 mblen(NULL, 0);
1387 break;
1389 if (UICMP(z, maxlen, <, ml))
1390 break;
1391 if (ml == 1)
1392 *store = *buf;
1393 else
1394 memcpy(store, buf, ml);
1395 store += ml;
1396 buf += ml;
1397 rv += ml;
1398 maxlen -= ml;
1401 if ((i = bi.bi_end.l) > 0) {
1402 memcpy(store, bi.bi_end.s, i);
1403 store += i;
1404 rv += i;
1406 jleave:
1407 *store = '\0';
1409 #else
1410 rv = MIN(blen, maxlen);
1411 memcpy(store, buf, rv);
1412 store[rv] = '\0';
1413 #endif
1414 j_leave:
1415 NYD_LEAVE;
1416 return rv;
1419 FL char *
1420 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1422 NATCH_CHAR( struct bidi_info bi; )
1423 int col_orig = col, n, sz;
1424 bool_t isbidi, isuni, istab, isrepl;
1425 char *nb, *np;
1426 NYD_ENTER;
1428 /* Bidi only on request and when there is 8-bit data */
1429 isbidi = isuni = FAL0;
1430 #ifdef HAVE_NATCH_CHAR
1431 isuni = ((options & OPT_UNICODE) != 0);
1432 bidi_info_create(&bi);
1433 if (bi.bi_start.l == 0)
1434 goto jnobidi;
1435 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1436 goto jnobidi;
1438 if ((size_t)col >= bi.bi_pad)
1439 col -= bi.bi_pad;
1440 else
1441 col = 0;
1442 jnobidi:
1443 #endif
1445 np = nb = salloc(mb_cur_max * strlen(cp) +
1446 ((fill ? col : 0)
1447 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1448 +1));
1450 #ifdef HAVE_NATCH_CHAR
1451 if (isbidi) {
1452 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1453 np += bi.bi_start.l;
1455 #endif
1457 while (*cp != '\0') {
1458 istab = FAL0;
1459 #ifdef HAVE_C90AMEND1
1460 if (mb_cur_max > 1) {
1461 wchar_t wc;
1463 n = 1;
1464 isrepl = TRU1;
1465 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1466 sz = 1;
1467 else if (wc == L'\t') {
1468 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1469 isrepl = FAL0;
1470 istab = TRU1;
1471 } else if (iswprint(wc)) {
1472 # ifndef HAVE_WCWIDTH
1473 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1474 # else
1475 if ((n = wcwidth(wc)) == -1)
1476 n = 1;
1477 else
1478 # endif
1479 isrepl = FAL0;
1481 } else
1482 #endif
1484 n = sz = 1;
1485 istab = (*cp == '\t');
1486 isrepl = !(istab || isprint((uc_i)*cp));
1489 if (n > col)
1490 break;
1491 col -= n;
1493 if (isrepl) {
1494 if (isuni) {
1495 np[0] = (char)0xEFu;
1496 np[1] = (char)0xBFu;
1497 np[2] = (char)0xBDu;
1498 np += 3;
1499 } else
1500 *np++ = '?';
1501 cp += sz;
1502 } else if (istab || (sz == 1 && spacechar(*cp))) {
1503 *np++ = ' ';
1504 ++cp;
1505 } else
1506 while (sz--)
1507 *np++ = *cp++;
1510 if (fill && col != 0) {
1511 if (fill > 0) {
1512 memmove(nb + col, nb, PTR2SIZE(np - nb));
1513 memset(nb, ' ', col);
1514 } else
1515 memset(np, ' ', col);
1516 np += col;
1517 col = 0;
1520 #ifdef HAVE_NATCH_CHAR
1521 if (isbidi) {
1522 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1523 np += bi.bi_end.l;
1525 #endif
1527 *np = '\0';
1528 if (cols_decr_used_or_null != NULL)
1529 *cols_decr_used_or_null -= col_orig - col;
1530 NYD_LEAVE;
1531 return nb;
1534 FL void
1535 makeprint(struct str const *in, struct str *out)
1537 static int print_all_chars = -1;
1539 char const *inp, *maxp;
1540 char *outp;
1541 DBG( size_t msz; )
1542 NYD_ENTER;
1544 if (print_all_chars == -1)
1545 print_all_chars = ok_blook(print_all_chars);
1547 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1548 inp = in->s;
1549 maxp = inp + in->l;
1551 if (print_all_chars) {
1552 out->l = in->l;
1553 memcpy(outp, inp, out->l);
1554 goto jleave;
1557 #ifdef HAVE_NATCH_CHAR
1558 if (mb_cur_max > 1) {
1559 char mbb[MB_LEN_MAX + 1];
1560 wchar_t wc;
1561 int i, n;
1562 bool_t isuni = ((options & OPT_UNICODE) != 0);
1564 out->l = 0;
1565 while (inp < maxp) {
1566 if (*inp & 0200)
1567 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1568 else {
1569 wc = *inp;
1570 n = 1;
1572 if (n == -1) {
1573 /* FIXME Why mbtowc() resetting here?
1574 * FIXME what about ISO 2022-JP plus -- those
1575 * FIXME will loose shifts, then!
1576 * FIXME THUS - we'd need special "known points"
1577 * FIXME to do so - say, after a newline!!
1578 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1579 mbtowc(&wc, NULL, mb_cur_max);
1580 wc = isuni ? 0xFFFD : '?';
1581 n = 1;
1582 } else if (n == 0)
1583 n = 1;
1584 inp += n;
1585 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1586 wc != '\t') {
1587 if ((wc & ~(wchar_t)037) == 0)
1588 wc = isuni ? 0x2400 | wc : '?';
1589 else if (wc == 0177)
1590 wc = isuni ? 0x2421 : '?';
1591 else
1592 wc = isuni ? 0x2426 : '?';
1593 }else if(isuni){ /* TODO ctext */
1594 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1595 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1596 continue;
1597 /* And some zero-width messes */
1598 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1599 continue;
1600 /* Oh about the ISO C wide character interfaces, baby! */
1601 if(wc == 0xFEFF)
1602 continue;
1604 if ((n = wctomb(mbb, wc)) <= 0)
1605 continue;
1606 out->l += n;
1607 assert(out->l < msz);
1608 for (i = 0; i < n; ++i)
1609 *outp++ = mbb[i];
1611 } else
1612 #endif /* NATCH_CHAR */
1614 int c;
1615 while (inp < maxp) {
1616 c = *inp++ & 0377;
1617 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1618 c = '?';
1619 *outp++ = c;
1621 out->l = in->l;
1623 jleave:
1624 out->s[out->l] = '\0';
1625 NYD_LEAVE;
1628 FL size_t
1629 delctrl(char *cp, size_t len)
1631 size_t x, y;
1632 NYD_ENTER;
1634 for (x = y = 0; x < len; ++x)
1635 if (!cntrlchar(cp[x]))
1636 cp[y++] = cp[x];
1637 cp[y] = '\0';
1638 NYD_LEAVE;
1639 return y;
1642 FL char *
1643 prstr(char const *s)
1645 struct str in, out;
1646 char *rp;
1647 NYD_ENTER;
1649 in.s = UNCONST(s);
1650 in.l = strlen(s);
1651 makeprint(&in, &out);
1652 rp = savestrbuf(out.s, out.l);
1653 free(out.s);
1654 NYD_LEAVE;
1655 return rp;
1658 FL int
1659 prout(char const *s, size_t sz, FILE *fp)
1661 struct str in, out;
1662 int n;
1663 NYD_ENTER;
1665 in.s = UNCONST(s);
1666 in.l = sz;
1667 makeprint(&in, &out);
1668 n = fwrite(out.s, 1, out.l, fp);
1669 free(out.s);
1670 NYD_LEAVE;
1671 return n;
1674 FL size_t
1675 putuc(int u, int c, FILE *fp)
1677 size_t rv;
1678 NYD_ENTER;
1679 UNUSED(u);
1681 #ifdef HAVE_NATCH_CHAR
1682 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1683 char mbb[MB_LEN_MAX];
1684 int i, n;
1686 if ((n = wctomb(mbb, u)) > 0) {
1687 rv = wcwidth(u);
1688 for (i = 0; i < n; ++i)
1689 if (putc(mbb[i] & 0377, fp) == EOF) {
1690 rv = 0;
1691 break;
1693 } else if (n == 0)
1694 rv = (putc('\0', fp) != EOF);
1695 else
1696 rv = 0;
1697 } else
1698 #endif
1699 rv = (putc(c, fp) != EOF);
1700 NYD_LEAVE;
1701 return rv;
1704 FL bool_t
1705 bidi_info_needed(char const *bdat, size_t blen)
1707 bool_t rv = FAL0;
1708 NYD_ENTER;
1710 #ifdef HAVE_NATCH_CHAR
1711 if (options & OPT_UNICODE)
1712 while (blen > 0) {
1713 /* TODO Checking for BIDI character: use S-CText fromutf8
1714 * TODO plus isrighttoleft (or whatever there will be)! */
1715 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1716 if (c == UI32_MAX)
1717 break;
1719 if (c <= 0x05BE)
1720 continue;
1722 /* (Very very fuzzy, awaiting S-CText for good) */
1723 if ((c >= 0x05BE && c <= 0x08E3) ||
1724 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1725 (c >= 0xFE70 && c <= 0xFEFC) ||
1726 (c >= 0x10800 && c <= 0x10C48) ||
1727 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1728 rv = TRU1;
1729 break;
1732 #endif /* HAVE_NATCH_CHAR */
1733 NYD_LEAVE;
1734 return rv;
1737 FL void
1738 bidi_info_create(struct bidi_info *bip)
1740 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1741 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1742 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1743 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1744 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1745 NATCH_CHAR( char const *hb; )
1746 NYD_ENTER;
1748 memset(bip, 0, sizeof *bip);
1749 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1751 #ifdef HAVE_NATCH_CHAR
1752 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1753 switch (*hb) {
1754 case '3':
1755 bip->bi_pad = 2;
1756 /* FALLTHRU */
1757 case '2':
1758 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1759 break;
1760 case '1':
1761 bip->bi_pad = 2;
1762 /* FALLTHRU */
1763 default:
1764 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1765 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1766 break;
1768 bip->bi_start.l = bip->bi_end.l = 3;
1770 #endif
1771 NYD_LEAVE;
1774 #ifdef HAVE_COLOUR
1775 FL void
1776 colour_table_create(bool_t pager_used)
1778 union {char *cp; char const *ccp; void *vp; struct colour_table *ctp;} u;
1779 size_t i;
1780 struct colour_table *ct;
1781 NYD_ENTER;
1783 if (ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
1784 goto jleave;
1785 else {
1786 char *term, *okterms;
1788 if ((term = env_vlook("TERM", FAL0)) == NULL)
1789 goto jleave;
1790 /* terminfo rocks: if we find "color", assume it's right */
1791 if (strstr(term, "color") != NULL)
1792 goto jok;
1793 if ((okterms = ok_vlook(colour_terms)) == NULL)
1794 okterms = UNCONST(COLOUR_TERMS);
1795 okterms = savestr(okterms);
1797 i = strlen(term);
1798 while ((u.cp = n_strsep(&okterms, ',', TRU1)) != NULL)
1799 if (!strncmp(u.cp, term, i))
1800 goto jok;
1801 goto jleave;
1804 jok:
1805 colour_table = ct = salloc(sizeof *ct); /* XXX lex.c yet resets (FILTER!) */
1806 { static struct {
1807 enum okeys okey;
1808 enum colourspec cspec;
1809 char const *defval;
1810 } const map[] = {
1811 {ok_v_colour_msginfo, COLOURSPEC_MSGINFO, COLOUR_MSGINFO},
1812 {ok_v_colour_partinfo, COLOURSPEC_PARTINFO, COLOUR_PARTINFO},
1813 {ok_v_colour_from_, COLOURSPEC_FROM_, COLOUR_FROM_},
1814 {ok_v_colour_header, COLOURSPEC_HEADER, COLOUR_HEADER},
1815 {ok_v_colour_uheader, COLOURSPEC_UHEADER, COLOUR_UHEADER}
1818 for (i = 0; i < NELEM(map); ++i) {
1819 if ((u.cp = _var_oklook(map[i].okey)) == NULL)
1820 u.ccp = map[i].defval;
1821 u.cp = _colour_iso6429(u.ccp);
1822 ct->ct_csinfo[map[i].cspec].l = strlen(u.cp);
1823 ct->ct_csinfo[map[i].cspec].s = u.cp;
1826 ct->ct_csinfo[COLOURSPEC_RESET].l = sizeof("\033[0m") -1;
1827 ct->ct_csinfo[COLOURSPEC_RESET].s = UNCONST("\033[0m");
1829 if ((u.cp = ok_vlook(colour_user_headers)) == NULL)
1830 u.ccp = COLOUR_USER_HEADERS;
1831 ct->ct_csinfo[COLOURSPEC_RESET + 1].l = i = strlen(u.ccp);
1832 ct->ct_csinfo[COLOURSPEC_RESET + 1].s = (i == 0) ? NULL : savestr(u.ccp);
1833 jleave:
1834 NYD_LEAVE;
1837 FL void
1838 colour_put(FILE *fp, enum colourspec cs)
1840 NYD_ENTER;
1841 if (colour_table != NULL) {
1842 struct str const *cp = colour_get(cs);
1844 fwrite(cp->s, cp->l, 1, fp);
1846 NYD_LEAVE;
1849 FL void
1850 colour_put_header(FILE *fp, char const *name)
1852 enum colourspec cs = COLOURSPEC_HEADER;
1853 struct str const *uheads;
1854 char *cp, *cp_base, *x;
1855 size_t namelen;
1856 NYD_ENTER;
1858 if (colour_table == NULL)
1859 goto j_leave;
1860 /* Normal header colours if there are no user headers */
1861 uheads = colour_table->ct_csinfo + COLOURSPEC_RESET + 1;
1862 if (uheads->s == NULL)
1863 goto jleave;
1865 /* Iterate over all entries in the *colour-user-headers* list */
1866 cp = ac_alloc(uheads->l +1);
1867 memcpy(cp, uheads->s, uheads->l +1);
1868 cp_base = cp;
1869 namelen = strlen(name);
1870 while ((x = n_strsep(&cp, ',', TRU1)) != NULL) {
1871 size_t l = (cp != NULL) ? PTR2SIZE(cp - x) - 1 : strlen(x);
1872 if (l == namelen && !ascncasecmp(x, name, namelen)) {
1873 cs = COLOURSPEC_UHEADER;
1874 break;
1877 ac_free(cp_base);
1878 jleave:
1879 colour_put(fp, cs);
1880 j_leave:
1881 NYD_LEAVE;
1884 FL void
1885 colour_reset(FILE *fp)
1887 NYD_ENTER;
1888 if (colour_table != NULL)
1889 fwrite("\033[0m", 4, 1, fp);
1890 NYD_LEAVE;
1893 FL struct str const *
1894 colour_get(enum colourspec cs)
1896 struct str const *rv = NULL;
1897 NYD_ENTER;
1899 if (colour_table != NULL)
1900 if ((rv = colour_table->ct_csinfo + cs)->s == NULL)
1901 rv = NULL;
1902 NYD_LEAVE;
1903 return rv;
1905 #endif /* HAVE_COLOUR */
1907 FL si8_t
1908 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1910 char *dat, *eptr;
1911 sl_i sli;
1912 si8_t rv;
1913 NYD_ENTER;
1915 assert(inlen == 0 || inbuf != NULL);
1917 if (inlen == UIZ_MAX)
1918 inlen = strlen(inbuf);
1920 if (inlen == 0)
1921 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1922 else {
1923 if ((inlen == 1 && *inbuf == '1') ||
1924 !ascncasecmp(inbuf, "true", inlen) ||
1925 !ascncasecmp(inbuf, "yes", inlen) ||
1926 !ascncasecmp(inbuf, "on", inlen))
1927 rv = 1;
1928 else if ((inlen == 1 && *inbuf == '0') ||
1929 !ascncasecmp(inbuf, "false", inlen) ||
1930 !ascncasecmp(inbuf, "no", inlen) ||
1931 !ascncasecmp(inbuf, "off", inlen))
1932 rv = 0;
1933 else {
1934 dat = ac_alloc(inlen +1);
1935 memcpy(dat, inbuf, inlen);
1936 dat[inlen] = '\0';
1938 sli = strtol(dat, &eptr, 0);
1939 if (*dat != '\0' && *eptr == '\0')
1940 rv = (sli != 0);
1941 else
1942 rv = -1;
1944 ac_free(dat);
1947 NYD_LEAVE;
1948 return rv;
1951 FL si8_t
1952 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1954 si8_t rv;
1955 NYD_ENTER;
1957 assert(inlen == 0 || inbuf != NULL);
1959 if (inlen == UIZ_MAX)
1960 inlen = strlen(inbuf);
1962 if (inlen == 0)
1963 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1964 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1965 !ascncasecmp(inbuf, "ask-", 4) &&
1966 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1967 (options & OPT_INTERACTIVE)) {
1968 if (prompt != NULL)
1969 fputs(prompt, stdout);
1970 rv = getapproval(NULL, rv);
1972 NYD_LEAVE;
1973 return rv;
1976 FL time_t
1977 n_time_epoch(void)
1979 #ifdef HAVE_CLOCK_GETTIME
1980 struct timespec ts;
1981 #elif defined HAVE_GETTIMEOFDAY
1982 struct timeval ts;
1983 #endif
1984 time_t rv;
1985 NYD2_ENTER;
1987 #ifdef HAVE_CLOCK_GETTIME
1988 clock_gettime(CLOCK_REALTIME, &ts);
1989 rv = (time_t)ts.tv_sec;
1990 #elif defined HAVE_GETTIMEOFDAY
1991 gettimeofday(&ts, NULL);
1992 rv = (time_t)ts.tv_sec;
1993 #else
1994 rv = time(NULL);
1995 #endif
1996 NYD2_LEAVE;
1997 return rv;
2000 FL void
2001 time_current_update(struct time_current *tc, bool_t full_update)
2003 NYD_ENTER;
2004 tc->tc_time = n_time_epoch();
2005 if (full_update) {
2006 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
2007 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
2008 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
2010 NYD_LEAVE;
2013 FL void
2014 n_err(char const *format, ...)
2016 va_list ap;
2017 NYD2_ENTER;
2019 va_start(ap, format);
2020 #ifdef HAVE_ERRORS
2021 if (options & OPT_INTERACTIVE)
2022 n_verr(format, ap);
2023 else
2024 #endif
2026 vfprintf(stderr, format, ap);
2027 if (strchr(format, '\n') != NULL) /* TODO */
2028 fflush(stderr);
2030 va_end(ap);
2031 NYD2_LEAVE;
2034 FL void
2035 n_verr(char const *format, va_list ap)
2037 /* Check use cases of PS_ERRORS_NOTED, too! */
2038 #ifdef HAVE_ERRORS
2039 char buf[LINESIZE], *xbuf;
2040 int lmax, l;
2041 struct err_node *enp;
2043 LCTA(ERRORS_MAX > 3);
2044 #endif
2045 NYD2_ENTER;
2047 #ifdef HAVE_ERRORS
2048 if (!(options & OPT_INTERACTIVE))
2049 #endif
2051 vfprintf(stderr, format, ap);
2052 goto jleave;
2055 #ifdef HAVE_ERRORS
2056 xbuf = buf;
2057 lmax = sizeof buf;
2058 jredo:
2059 l = vsnprintf(xbuf, lmax, format, ap);
2060 if (l <= 0)
2061 goto jleave;
2062 if (UICMP(z, l, >=, lmax)) {
2063 /* FIXME Cannot reuse va_list
2064 lmax = ++l;
2065 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
2066 goto jredo;
2070 fwrite(xbuf, 1, l, stderr);
2072 /* Link it into the `errors' message ring */
2073 if ((enp = _err_tail) == NULL) {
2074 jcreat:
2075 enp = scalloc(1, sizeof *enp);
2076 if (_err_tail != NULL)
2077 _err_tail->en_next = enp;
2078 else
2079 _err_head = enp;
2080 _err_tail = enp;
2081 ++_err_cnt;
2082 } else if (enp->en_str.l > 0 && enp->en_str.s[enp->en_str.l - 1] == '\n') {
2083 if (_err_cnt < ERRORS_MAX)
2084 goto jcreat;
2086 _err_head = (enp = _err_head)->en_next;
2087 _err_tail->en_next = enp;
2088 _err_tail = enp;
2089 free(enp->en_str.s);
2090 memset(enp, 0, sizeof *enp);
2093 n_str_add_buf(&enp->en_str, xbuf, l);
2095 if (xbuf != buf)
2096 free(xbuf);
2097 #endif /* HAVE_ERRORS */
2099 jleave:
2100 if (strchr(format, '\n') != NULL) /* TODO */
2101 fflush(stderr);
2102 NYD2_LEAVE;
2105 FL void
2106 n_err_sighdl(char const *format, ...) /* TODO sigsafe; obsolete! */
2108 va_list ap;
2109 NYD_X;
2111 va_start(ap, format);
2112 vfprintf(stderr, format, ap);
2113 va_end(ap);
2114 fflush(stderr);
2117 FL void
2118 n_perr(char const *msg, int errval)
2120 char const *fmt;
2121 NYD2_ENTER;
2123 if (msg == NULL) {
2124 fmt = "%s%s\n";
2125 msg = "";
2126 } else
2127 fmt = "%s: %s\n";
2129 if (errval == 0)
2130 errval = errno;
2132 n_err(fmt, msg, strerror(errval));
2133 NYD2_LEAVE;
2136 FL void
2137 n_alert(char const *format, ...)
2139 va_list ap;
2140 NYD2_ENTER;
2142 n_err(_("Alert: "));
2144 va_start(ap, format);
2145 n_verr(format, ap);
2146 va_end(ap);
2148 n_err("\n");
2149 NYD2_LEAVE;
2152 FL void
2153 n_panic(char const *format, ...)
2155 va_list ap;
2156 NYD2_ENTER;
2158 fprintf(stderr, _("Panic: "));
2160 va_start(ap, format);
2161 vfprintf(stderr, format, ap);
2162 va_end(ap);
2164 putc('\n', stderr);
2165 fflush(stderr);
2166 NYD2_LEAVE;
2167 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2170 #ifdef HAVE_ERRORS
2171 FL int
2172 c_errors(void *v)
2174 char **argv = v;
2175 struct err_node *enp;
2176 NYD_ENTER;
2178 if (*argv == NULL)
2179 goto jlist;
2180 if (argv[1] != NULL)
2181 goto jerr;
2182 if (!asccasecmp(*argv, "show"))
2183 goto jlist;
2184 if (!asccasecmp(*argv, "clear"))
2185 goto jclear;
2186 jerr:
2187 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2188 v = NULL;
2189 jleave:
2190 NYD_LEAVE;
2191 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
2193 jlist: {
2194 FILE *fp;
2195 size_t i;
2197 if (_err_head == NULL) {
2198 fprintf(stderr, _("The error ring is empty\n"));
2199 goto jleave;
2202 if ((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
2203 NULL) {
2204 fprintf(stderr, _("tmpfile"));
2205 v = NULL;
2206 goto jleave;
2209 for (i = 0, enp = _err_head; enp != NULL; enp = enp->en_next)
2210 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2211 ++i, enp->en_str.l, enp->en_str.s);
2212 /* We don't know wether last string ended with NL; be simple */
2213 putc('\n', fp);
2215 page_or_print(fp, 0);
2216 Fclose(fp);
2218 /* FALLTHRU */
2220 jclear:
2221 _err_tail = NULL;
2222 _err_cnt = _err_cnt_noted = 0;
2223 while ((enp = _err_head) != NULL) {
2224 _err_head = enp->en_next;
2225 free(enp->en_str.s);
2226 free(enp);
2228 goto jleave;
2230 #endif /* HAVE_ERRORS */
2232 #ifndef HAVE_DEBUG
2233 FL void *
2234 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2236 void *rv;
2237 NYD2_ENTER;
2239 if (s == 0)
2240 s = 1;
2241 if ((rv = malloc(s)) == NULL)
2242 n_panic(_("no memory"));
2243 NYD2_LEAVE;
2244 return rv;
2247 FL void *
2248 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2250 void *rv;
2251 NYD2_ENTER;
2253 if (s == 0)
2254 s = 1;
2255 if (v == NULL)
2256 rv = smalloc(s);
2257 else if ((rv = realloc(v, s)) == NULL)
2258 n_panic(_("no memory"));
2259 NYD2_LEAVE;
2260 return rv;
2263 FL void *
2264 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2266 void *rv;
2267 NYD2_ENTER;
2269 if (size == 0)
2270 size = 1;
2271 if ((rv = calloc(nmemb, size)) == NULL)
2272 n_panic(_("no memory"));
2273 NYD2_LEAVE;
2274 return rv;
2277 #else /* !HAVE_DEBUG */
2278 CTA(sizeof(char) == sizeof(ui8_t));
2280 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2281 # define _HOPE_SET(C) \
2282 do {\
2283 union mem_ptr __xl, __xu;\
2284 struct mem_chunk *__xc;\
2285 __xl.p_p = (C).p_p;\
2286 __xc = __xl.p_c - 1;\
2287 __xu.p_p = __xc;\
2288 (C).p_cp += 8;\
2289 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2290 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2291 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2292 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2293 __xu.p_ui8p += __xc->mc_size - 8;\
2294 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2295 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2296 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2297 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2298 } while (0)
2299 # define _HOPE_GET_TRACE(C,BAD) \
2300 do {\
2301 (C).p_cp += 8;\
2302 _HOPE_GET(C, BAD);\
2303 (C).p_cp += 8;\
2304 } while(0)
2305 # define _HOPE_GET(C,BAD) \
2306 do {\
2307 union mem_ptr __xl, __xu;\
2308 struct mem_chunk *__xc;\
2309 ui32_t __i;\
2310 __xl.p_p = (C).p_p;\
2311 __xl.p_cp -= 8;\
2312 (C).p_cp = __xl.p_cp;\
2313 __xc = __xl.p_c - 1;\
2314 (BAD) = FAL0;\
2315 __i = 0;\
2316 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2317 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2318 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2319 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2320 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2321 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2322 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2323 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2324 if (__i != 0) {\
2325 (BAD) = TRU1;\
2326 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2327 __xl.p_p, __i, mdbg_file, mdbg_line);\
2329 __xu.p_p = __xc;\
2330 __xu.p_ui8p += __xc->mc_size - 8;\
2331 __i = 0;\
2332 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2333 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2334 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2335 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2336 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2337 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2338 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2339 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2340 if (__i != 0) {\
2341 (BAD) = TRU1;\
2342 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2343 __xl.p_p, __i, mdbg_file, mdbg_line);\
2345 if (BAD)\
2346 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2347 __xc->mc_file, __xc->mc_line);\
2348 } while (0)
2350 FL void *
2351 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2353 union mem_ptr p;
2354 NYD2_ENTER;
2356 if (s == 0)
2357 s = 1;
2358 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2359 n_panic("smalloc(): allocation too large: %s, line %d",
2360 mdbg_file, mdbg_line);
2361 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2363 if ((p.p_p = (malloc)(s)) == NULL)
2364 n_panic(_("no memory"));
2365 p.p_c->mc_prev = NULL;
2366 if ((p.p_c->mc_next = _mem_list) != NULL)
2367 _mem_list->mc_prev = p.p_c;
2368 p.p_c->mc_file = mdbg_file;
2369 p.p_c->mc_line = (ui16_t)mdbg_line;
2370 p.p_c->mc_isfree = FAL0;
2371 p.p_c->mc_size = (ui32_t)s;
2373 _mem_list = p.p_c++;
2374 _HOPE_SET(p);
2376 ++_mem_aall;
2377 ++_mem_acur;
2378 _mem_amax = MAX(_mem_amax, _mem_acur);
2379 _mem_mall += s;
2380 _mem_mcur += s;
2381 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2382 NYD2_LEAVE;
2383 return p.p_p;
2386 FL void *
2387 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2389 union mem_ptr p;
2390 bool_t isbad;
2391 NYD2_ENTER;
2393 if ((p.p_p = v) == NULL) {
2394 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2395 goto jleave;
2398 _HOPE_GET(p, isbad);
2399 --p.p_c;
2400 if (p.p_c->mc_isfree) {
2401 n_err("srealloc(): region freed! At %s, line %d\n"
2402 "\tLast seen: %s, line %" PRIu16 "\n",
2403 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2404 goto jforce;
2407 if (p.p_c == _mem_list)
2408 _mem_list = p.p_c->mc_next;
2409 else
2410 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2411 if (p.p_c->mc_next != NULL)
2412 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2414 --_mem_acur;
2415 _mem_mcur -= p.p_c->mc_size;
2416 jforce:
2417 if (s == 0)
2418 s = 1;
2419 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2420 n_panic("srealloc(): allocation too large: %s, line %d",
2421 mdbg_file, mdbg_line);
2422 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2424 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2425 n_panic(_("no memory"));
2426 p.p_c->mc_prev = NULL;
2427 if ((p.p_c->mc_next = _mem_list) != NULL)
2428 _mem_list->mc_prev = p.p_c;
2429 p.p_c->mc_file = mdbg_file;
2430 p.p_c->mc_line = (ui16_t)mdbg_line;
2431 p.p_c->mc_isfree = FAL0;
2432 p.p_c->mc_size = (ui32_t)s;
2433 _mem_list = p.p_c++;
2434 _HOPE_SET(p);
2436 ++_mem_aall;
2437 ++_mem_acur;
2438 _mem_amax = MAX(_mem_amax, _mem_acur);
2439 _mem_mall += s;
2440 _mem_mcur += s;
2441 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2442 jleave:
2443 NYD2_LEAVE;
2444 return p.p_p;
2447 FL void *
2448 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2450 union mem_ptr p;
2451 NYD2_ENTER;
2453 if (size == 0)
2454 size = 1;
2455 if (nmemb == 0)
2456 nmemb = 1;
2457 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2458 n_panic("scalloc(): allocation size too large: %s, line %d",
2459 mdbg_file, mdbg_line);
2460 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2461 n_panic("scalloc(): allocation count too large: %s, line %d",
2462 mdbg_file, mdbg_line);
2464 size *= nmemb;
2465 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2467 if ((p.p_p = (malloc)(size)) == NULL)
2468 n_panic(_("no memory"));
2469 memset(p.p_p, 0, size);
2470 p.p_c->mc_prev = NULL;
2471 if ((p.p_c->mc_next = _mem_list) != NULL)
2472 _mem_list->mc_prev = p.p_c;
2473 p.p_c->mc_file = mdbg_file;
2474 p.p_c->mc_line = (ui16_t)mdbg_line;
2475 p.p_c->mc_isfree = FAL0;
2476 p.p_c->mc_size = (ui32_t)size;
2477 _mem_list = p.p_c++;
2478 _HOPE_SET(p);
2480 ++_mem_aall;
2481 ++_mem_acur;
2482 _mem_amax = MAX(_mem_amax, _mem_acur);
2483 _mem_mall += size;
2484 _mem_mcur += size;
2485 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2486 NYD2_LEAVE;
2487 return p.p_p;
2490 FL void
2491 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2493 union mem_ptr p;
2494 bool_t isbad;
2495 NYD2_ENTER;
2497 if ((p.p_p = v) == NULL) {
2498 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2499 goto jleave;
2502 _HOPE_GET(p, isbad);
2503 --p.p_c;
2504 if (p.p_c->mc_isfree) {
2505 n_err("sfree(): double-free avoided at %s, line %d\n"
2506 "\tLast seen: %s, line %" PRIu16 "\n",
2507 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2508 goto jleave;
2511 if (p.p_c == _mem_list)
2512 _mem_list = p.p_c->mc_next;
2513 else
2514 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2515 if (p.p_c->mc_next != NULL)
2516 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2517 p.p_c->mc_isfree = TRU1;
2518 /* Trash contents (also see [21c05f8]) */
2519 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2521 --_mem_acur;
2522 _mem_mcur -= p.p_c->mc_size;
2524 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2525 p.p_c->mc_next = _mem_free;
2526 _mem_free = p.p_c;
2527 } else
2528 (free)(p.p_c);
2529 jleave:
2530 NYD2_LEAVE;
2533 FL void
2534 smemreset(void)
2536 union mem_ptr p;
2537 size_t c = 0, s = 0;
2538 NYD_ENTER;
2540 smemcheck();
2542 for (p.p_c = _mem_free; p.p_c != NULL;) {
2543 void *vp = p.p_c;
2544 ++c;
2545 s += p.p_c->mc_size;
2546 p.p_c = p.p_c->mc_next;
2547 (free)(vp);
2549 _mem_free = NULL;
2551 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2552 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2553 NYD_LEAVE;
2556 FL int
2557 c_smemtrace(void *v)
2559 /* For _HOPE_GET() */
2560 char const * const mdbg_file = "smemtrace()";
2561 int const mdbg_line = -1;
2562 FILE *fp;
2563 union mem_ptr p, xp;
2564 bool_t isbad;
2565 size_t lines;
2566 NYD_ENTER;
2568 v = (void*)0x1;
2569 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2570 n_perr("tmpfile", 0);
2571 goto jleave;
2574 fprintf(fp, "Memory statistics:\n"
2575 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2576 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2577 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2579 fprintf(fp, "Currently allocated memory chunks:\n");
2580 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2581 ++lines, p.p_c = p.p_c->mc_next) {
2582 xp = p;
2583 ++xp.p_c;
2584 _HOPE_GET_TRACE(xp, isbad);
2585 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2586 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2587 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2588 p.p_c->mc_line);
2591 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2592 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2593 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2594 xp = p;
2595 ++xp.p_c;
2596 _HOPE_GET_TRACE(xp, isbad);
2597 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2598 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2599 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2600 p.p_c->mc_file, p.p_c->mc_line);
2604 page_or_print(fp, lines);
2605 Fclose(fp);
2606 v = NULL;
2607 jleave:
2608 NYD_LEAVE;
2609 return (v != NULL);
2612 # ifdef HAVE_DEVEL
2613 FL bool_t
2614 _smemcheck(char const *mdbg_file, int mdbg_line)
2616 union mem_ptr p, xp;
2617 bool_t anybad = FAL0, isbad;
2618 size_t lines;
2619 NYD_ENTER;
2621 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2622 ++lines, p.p_c = p.p_c->mc_next) {
2623 xp = p;
2624 ++xp.p_c;
2625 _HOPE_GET_TRACE(xp, isbad);
2626 if (isbad) {
2627 anybad = TRU1;
2628 n_err(
2629 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2630 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2631 p.p_c->mc_file, p.p_c->mc_line);
2635 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2636 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2637 xp = p;
2638 ++xp.p_c;
2639 _HOPE_GET_TRACE(xp, isbad);
2640 if (isbad) {
2641 anybad = TRU1;
2642 n_err(
2643 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2644 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2645 p.p_c->mc_file, p.p_c->mc_line);
2649 NYD_LEAVE;
2650 return anybad;
2652 # endif /* HAVE_DEVEL */
2654 # undef _HOPE_SIZE
2655 # undef _HOPE_SET
2656 # undef _HOPE_GET_TRACE
2657 # undef _HOPE_GET
2658 #endif /* HAVE_DEBUG */
2660 /* s-it-mode */