make.rc: enable WANT_TERMCAP by default
[s-mailx.git] / auxlily.c
blob0ebe8ef6d3ab8fe413abb1381fa1472276513df0
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 a_aux_err_node{
88 struct a_aux_err_node *ae_next;
89 struct str ae_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 a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
133 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
134 #endif
135 static size_t a_aux_err_dirty;
137 #ifdef HAVE_DEBUG
138 static size_t _mem_aall, _mem_acur, _mem_amax,
139 _mem_mall, _mem_mcur, _mem_mmax;
141 static struct mem_chunk *_mem_list, *_mem_free;
142 #endif
144 /* Our ARC4 random generator with its completely unacademical pseudo
145 * initialization (shall /dev/urandom fail) */
146 #ifndef HAVE_POSIX_RANDOM
147 static void _rand_init(void);
148 static ui32_t _rand_weak(ui32_t seed);
149 SINLINE ui8_t _rand_get8(void);
150 #endif
152 #ifdef HAVE_NYD
153 static void _nyd_print(int fd, struct nyd_info *nip);
154 #endif
156 /* Perform shell variable expansion */
157 static char * _sh_exp_var(struct shvar_stack *shsp);
159 #ifndef HAVE_POSIX_RANDOM
160 static void
161 _rand_init(void)
163 # ifdef HAVE_CLOCK_GETTIME
164 struct timespec ts;
165 # else
166 struct timeval ts;
167 # endif
168 union {int fd; size_t i;} u;
169 ui32_t seed, rnd;
170 NYD2_ENTER;
172 _rand = smalloc(sizeof *_rand);
174 if ((u.fd = open("/dev/urandom", O_RDONLY)) != -1) {
175 bool_t ok = (sizeof *_rand == (size_t)read(u.fd, _rand, sizeof *_rand));
177 close(u.fd);
178 if (ok)
179 goto jleave;
182 for (seed = (uintptr_t)_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd) {
183 for (u.i = NELEM(_rand->b32); u.i-- != 0;) {
184 size_t t, k;
186 # ifdef HAVE_CLOCK_GETTIME
187 clock_gettime(CLOCK_REALTIME, &ts);
188 t = (ui32_t)ts.tv_nsec;
189 # else
190 gettimeofday(&ts, NULL);
191 t = (ui32_t)ts.tv_usec;
192 # endif
193 if (rnd & 1)
194 t = (t >> 16) | (t << 16);
195 _rand->b32[u.i] ^= _rand_weak(seed ^ t);
196 _rand->b32[t % NELEM(_rand->b32)] ^= seed;
197 if (rnd == 7 || rnd == 17)
198 _rand->b32[u.i] ^= _rand_weak(seed ^ (ui32_t)ts.tv_sec);
199 k = _rand->b32[u.i] % NELEM(_rand->b32);
200 _rand->b32[k] ^= _rand->b32[u.i];
201 seed ^= _rand_weak(_rand->b32[k]);
202 if ((rnd & 3) == 3)
203 seed ^= nextprime(seed);
207 for (u.i = 11 * sizeof(_rand->b8); u.i != 0; --u.i)
208 _rand_get8();
209 jleave:
210 NYD2_LEAVE;
213 static ui32_t
214 _rand_weak(ui32_t seed)
216 /* From "Random number generators: good ones are hard to find",
217 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
218 * October 1988, p. 1195.
219 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
220 ui32_t hi;
222 if (seed == 0)
223 seed = 123459876;
224 hi = seed / 127773;
225 seed %= 127773;
226 seed = (seed * 16807) - (hi * 2836);
227 if ((si32_t)seed < 0)
228 seed += SI32_MAX;
229 return seed;
232 SINLINE ui8_t
233 _rand_get8(void)
235 ui8_t si, sj;
237 si = _rand->a._dat[++_rand->a._i];
238 sj = _rand->a._dat[_rand->a._j += si];
239 _rand->a._dat[_rand->a._i] = sj;
240 _rand->a._dat[_rand->a._j] = si;
241 return _rand->a._dat[(ui8_t)(si + sj)];
243 #endif /* HAVE_POSIX_RANDOM */
245 #ifdef HAVE_NYD
246 static void
247 _nyd_print(int fd, struct nyd_info *nip)
249 char buf[80];
250 union {int i; size_t z;} u;
252 u.i = snprintf(buf, sizeof buf,
253 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
254 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
255 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
256 if (u.i > 0) {
257 u.z = u.i;
258 if (u.z > sizeof buf)
259 u.z = sizeof buf - 1; /* (Skip \0) */
260 write(fd, buf, u.z);
263 #endif
265 static char *
266 _sh_exp_var(struct shvar_stack *shsp)
268 struct shvar_stack next, *np, *tmp;
269 char const *vp;
270 char lc, c, *cp, *rv;
271 size_t i;
272 NYD2_ENTER;
274 if (*(vp = shsp->shs_value) != '$') {
275 bool_t bsesc = shsp->shs_bsesc;
276 union {bool_t hadbs; char c;} u = {FAL0};
278 shsp->shs_dat = vp;
279 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
280 if (c == '$' && lc != '\\')
281 break;
282 if (!bsesc)
283 continue;
284 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
286 shsp->shs_len = i;
288 if (u.hadbs) {
289 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
291 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
292 if (u.c != '\\' || lc == '\\')
293 *rv++ = u.c;
294 lc = (lc == '\\') ? '\0' : u.c;
296 *rv = '\0';
298 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
300 } else {
301 if ((lc = (*++vp == '{')))
302 ++vp;
304 /* POSIX says
305 * Environment variable names used by the utilities in the Shell and
306 * Utilities volume of POSIX.1-2008 consist solely of uppercase
307 * letters, digits, and the <underscore> ('_') from the characters
308 * defined in Portable Character Set and do not begin with a digit.
309 * Other characters may be permitted by an implementation;
310 * applications shall tolerate the presence of such names.
311 * We do support the hyphen "-" because it is common for mailx. */
312 shsp->shs_dat = vp;
313 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
314 if (!alnumchar(c) && c != '_' && c != '-')
315 break;
317 if (lc) {
318 if (c != '}') {
319 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
320 shsp->shs_value);
321 shsp->shs_len = strlen(shsp->shs_value);
322 shsp->shs_dat = shsp->shs_value;
323 if (shsp->shs_err != NULL)
324 *shsp->shs_err = TRU1;
325 goto junroll;
327 c = *++vp;
330 shsp->shs_len = i;
331 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
332 shsp->shs_len = strlen(shsp->shs_dat = cp);
334 if (c != '\0')
335 goto jrecurse;
337 /* That level made the great and completed encoding. Build result */
338 junroll:
339 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
340 i += np->shs_len;
341 tmp = np->shs_next;
342 np->shs_next = shsp;
343 shsp = np;
344 np = tmp;
347 cp = rv = salloc(i +1);
348 while (shsp != NULL) {
349 np = shsp;
350 shsp = shsp->shs_next;
351 memcpy(cp, np->shs_dat, np->shs_len);
352 cp += np->shs_len;
354 *cp = '\0';
356 jleave:
357 NYD2_LEAVE;
358 return rv;
359 jrecurse:
360 memset(&next, 0, sizeof next);
361 next.shs_next = shsp;
362 next.shs_value = vp;
363 next.shs_err = shsp->shs_err;
364 next.shs_bsesc = shsp->shs_bsesc;
365 rv = _sh_exp_var(&next);
366 goto jleave;
369 FL void
370 n_raise(int signo)
372 NYD2_ENTER;
373 kill(getpid(), signo);
374 NYD2_LEAVE;
377 FL sighandler_type
378 safe_signal(int signum, sighandler_type handler)
380 struct sigaction nact, oact;
381 sighandler_type rv;
382 NYD2_ENTER;
384 nact.sa_handler = handler;
385 sigemptyset(&nact.sa_mask);
386 nact.sa_flags = 0;
387 #ifdef SA_RESTART
388 nact.sa_flags |= SA_RESTART;
389 #endif
390 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
391 NYD2_LEAVE;
392 return rv;
395 FL void
396 hold_all_sigs(void)
398 NYD2_ENTER;
399 if (_alls_depth++ == 0) {
400 sigfillset(&_alls_nset);
401 sigdelset(&_alls_nset, SIGABRT);
402 #ifdef SIGBUS
403 sigdelset(&_alls_nset, SIGBUS);
404 #endif
405 sigdelset(&_alls_nset, SIGCHLD);
406 sigdelset(&_alls_nset, SIGFPE);
407 sigdelset(&_alls_nset, SIGILL);
408 sigdelset(&_alls_nset, SIGKILL);
409 sigdelset(&_alls_nset, SIGSEGV);
410 sigdelset(&_alls_nset, SIGSTOP);
411 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
413 NYD2_LEAVE;
416 FL void
417 rele_all_sigs(void)
419 NYD2_ENTER;
420 if (--_alls_depth == 0)
421 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
422 NYD2_LEAVE;
425 FL void
426 hold_sigs(void)
428 NYD2_ENTER;
429 if (_hold_sigdepth++ == 0) {
430 sigemptyset(&_hold_nset);
431 sigaddset(&_hold_nset, SIGHUP);
432 sigaddset(&_hold_nset, SIGINT);
433 sigaddset(&_hold_nset, SIGQUIT);
434 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
436 NYD2_LEAVE;
439 FL void
440 rele_sigs(void)
442 NYD2_ENTER;
443 if (--_hold_sigdepth == 0)
444 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
445 NYD2_LEAVE;
448 #ifdef HAVE_NYD
449 FL void
450 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
452 struct nyd_info *nip = _nyd_infos;
454 if (_nyd_curr != NELEM(_nyd_infos))
455 nip += _nyd_curr++;
456 else
457 _nyd_curr = 1;
458 nip->ni_file = file;
459 nip->ni_fun = fun;
460 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
461 nip->ni_level = ((act == 0) ? _nyd_level
462 : (act == 1) ? ++_nyd_level : _nyd_level--);
465 FL void
466 _nyd_oncrash(int signo)
468 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
469 struct sigaction xact;
470 sigset_t xset;
471 size_t i, fnl;
472 int fd;
473 struct nyd_info *nip;
475 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
477 xact.sa_handler = SIG_DFL;
478 sigemptyset(&xact.sa_mask);
479 xact.sa_flags = 0;
480 sigaction(signo, &xact, NULL);
482 i = strlen(tempdir);
483 fnl = sizeof(UAGENT) -1;
485 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
486 (cp = pathbuf)[0] = '.';
487 i = 1;
488 } else
489 memcpy(cp = pathbuf, tempdir, i);
490 cp[i++] = '/'; /* xxx pathsep */
491 memcpy(cp += i, UAGENT, fnl);
492 i += fnl;
493 memcpy(cp += fnl, ".dat", sizeof(".dat"));
494 fnl = i + sizeof(".dat") -1;
496 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
497 fd = STDERR_FILENO;
499 # undef _X
500 # define _X(X) (X), sizeof(X) -1
501 write(fd, _X("\n\nNYD: program dying due to signal "));
503 cp = s2ibuf + sizeof(s2ibuf) -1;
504 *cp = '\0';
505 i = signo;
506 do {
507 *--cp = "0123456789"[i % 10];
508 i /= 10;
509 } while (i != 0);
510 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
512 write(fd, _X(":\n"));
514 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
515 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
516 _nyd_print(fd, nip++);
517 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
518 _nyd_print(fd, nip++);
520 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
522 if (fd != STDERR_FILENO) {
523 write(STDERR_FILENO, _X("Crash NYD listing written to "));
524 write(STDERR_FILENO, pathbuf, fnl);
525 write(STDERR_FILENO, _X("\n"));
526 # undef _X
528 close(fd);
531 sigemptyset(&xset);
532 sigaddset(&xset, signo);
533 sigprocmask(SIG_UNBLOCK, &xset, NULL);
534 n_raise(signo);
535 for (;;)
536 _exit(EXIT_ERR);
538 #endif /* HAVE_NYD */
540 FL void
541 touch(struct message *mp)
543 NYD_ENTER;
544 mp->m_flag |= MTOUCH;
545 if (!(mp->m_flag & MREAD))
546 mp->m_flag |= MREAD | MSTATUS;
547 NYD_LEAVE;
550 FL bool_t
551 is_dir(char const *name)
553 struct stat sbuf;
554 bool_t rv;
555 NYD_ENTER;
557 for (rv = FAL0;;)
558 if (!stat(name, &sbuf)) {
559 rv = (S_ISDIR(sbuf.st_mode) != 0);
560 break;
561 } else if (errno != EINTR)
562 break;
563 NYD_LEAVE;
564 return rv;
567 FL int
568 argcount(char **argv)
570 char **ap;
571 NYD_ENTER;
573 for (ap = argv; *ap++ != NULL;)
575 NYD_LEAVE;
576 return (int)PTR2SIZE(ap - argv - 1);
579 FL int
580 screensize(void)
582 int s;
583 char *cp;
584 NYD_ENTER;
586 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
587 s = scrnheight - 2; /* XXX no magics */
588 NYD_LEAVE;
589 return s;
592 FL char const *
593 get_pager(char const **env_addon)
595 char const *cp;
596 NYD_ENTER;
598 cp = ok_vlook(PAGER);
599 if (cp == NULL || *cp == '\0')
600 cp = XPAGER;
602 if (env_addon != NULL) {
603 *env_addon = NULL;
604 if (strstr(cp, "less") != NULL) {
605 if (!env_blook("LESS", TRU1))
606 *env_addon = "LESS=FRSXi";
607 } else if (strstr(cp, "lv") != NULL) {
608 if (!env_blook("LV", TRU1))
609 *env_addon = "LV=-c";
612 NYD_LEAVE;
613 return cp;
616 FL void
617 page_or_print(FILE *fp, size_t lines)
619 int c;
620 char const *cp;
621 NYD_ENTER;
623 fflush_rewind(fp);
625 if ((options & OPT_INTERACTIVE) && (pstate & PS_STARTED) &&
626 (cp = ok_vlook(crt)) != NULL) {
627 char *eptr;
628 union {sl_i sli; size_t rows;} u;
630 u.sli = strtol(cp, &eptr, 0);
631 u.rows = (*cp != '\0' && *eptr == '\0')
632 ? (size_t)u.sli : (size_t)scrnheight;
634 if (u.rows > 0 && lines == 0) {
635 while ((c = getc(fp)) != EOF)
636 if (c == '\n' && ++lines >= u.rows)
637 break;
638 really_rewind(fp);
641 if (lines >= u.rows) {
642 run_command(get_pager(NULL), 0, fileno(fp), COMMAND_FD_PASS,
643 NULL, NULL, NULL, NULL);
644 goto jleave;
648 while ((c = getc(fp)) != EOF)
649 putchar(c);
650 jleave:
651 NYD_LEAVE;
654 FL enum protocol
655 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
657 struct stat st;
658 char const *cp;
659 char *np;
660 size_t sz;
661 enum protocol rv = PROTO_UNKNOWN;
662 NYD_ENTER;
664 temporary_protocol_ext = NULL;
666 if (name[0] == '%' && name[1] == ':')
667 name += 2;
668 for (cp = name; *cp && *cp != ':'; cp++)
669 if (!alnumchar(*cp))
670 goto jfile;
672 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
673 if (!strncmp(name, "pop3://", 7)) {
674 #ifdef HAVE_POP3
675 rv = PROTO_POP3;
676 #else
677 n_err(_("No POP3 support compiled in\n"));
678 #endif
679 } else if (!strncmp(name, "pop3s://", 8)) {
680 #if defined HAVE_POP3 && defined HAVE_SSL
681 rv = PROTO_POP3;
682 #else
683 # ifndef HAVE_POP3
684 n_err(_("No POP3 support compiled in\n"));
685 # endif
686 # ifndef HAVE_SSL
687 n_err(_("No SSL support compiled in\n"));
688 # endif
689 #endif
691 goto jleave;
694 /* TODO This is the de facto maildir code and thus belongs into there!
695 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
696 * TODO or (more likely) in addition to *newfolders*) */
697 jfile:
698 rv = PROTO_FILE;
699 np = ac_alloc((sz = strlen(name)) + 4 +1);
700 memcpy(np, name, sz + 1);
701 if (!stat(name, &st)) {
702 if (S_ISDIR(st.st_mode) &&
703 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
704 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
705 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
706 rv = PROTO_MAILDIR;
707 } else {
708 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
709 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
710 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
711 temporary_protocol_ext = cp;
712 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
713 rv = PROTO_MAILDIR;
715 ac_free(np);
716 jleave:
717 NYD_LEAVE;
718 return rv;
721 FL ui32_t
722 torek_hash(char const *name)
724 /* Chris Torek's hash.
725 * NOTE: need to change *at least* mk-okey-map.pl when changing the
726 * algorithm!! */
727 ui32_t h = 0;
728 NYD_ENTER;
730 while (*name != '\0') {
731 h *= 33;
732 h += *name++;
734 NYD_LEAVE;
735 return h;
738 FL unsigned
739 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
741 unsigned h = 0, g;
742 NYD_ENTER;
744 cp--;
745 while (*++cp) {
746 h = (h << 4 & 0xffffffff) + (*cp&0377);
747 if ((g = h & 0xf0000000) != 0) {
748 h = h ^ g >> 24;
749 h = h ^ g;
752 NYD_LEAVE;
753 return h;
756 FL ui32_t
757 nextprime(ui32_t n)
759 static ui32_t const primes[] = {
760 5, 11, 23, 47, 97, 157, 283,
761 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
762 131071, 262139, 524287, 1048573, 2097143, 4194301,
763 8388593, 16777213, 33554393, 67108859, 134217689,
764 268435399, 536870909, 1073741789, 2147483647
767 ui32_t i, mprime;
768 NYD_ENTER;
770 i = (n < primes[NELEM(primes) / 4] ? 0
771 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
772 : NELEM(primes) / 2));
774 if ((mprime = primes[i]) > n)
775 break;
776 while (++i < NELEM(primes));
777 if (i == NELEM(primes) && mprime < n)
778 mprime = n;
779 NYD_LEAVE;
780 return mprime;
783 FL char *
784 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
786 struct passwd *pwp;
787 size_t nl, rl;
788 char const *rp, *np;
789 char *rv;
790 bool_t err;
791 NYD2_ENTER;
793 err = FAL0;
795 if (s[0] != '~')
796 goto jasis;
798 if (*(rp = s + 1) == '/' || *rp == '\0')
799 np = homedir;
800 else {
801 if ((rp = strchr(s + 1, '/')) == NULL)
802 rp = (np = UNCONST(s)) + 1;
803 else {
804 nl = PTR2SIZE(rp - s);
805 np = savestrbuf(s, nl);
808 if ((pwp = getpwnam(np)) == NULL) {
809 err = TRU1;
810 goto jasis;
812 np = pwp->pw_name;
815 nl = strlen(np);
816 rl = strlen(rp);
817 rv = salloc(nl + 1 + rl +1);
818 memcpy(rv, np, nl);
819 if (rl > 0) {
820 memcpy(rv + nl, rp, rl);
821 nl += rl;
823 rv[nl] = '\0';
824 goto jleave;
826 jasis:
827 rv = savestr(s);
828 jleave:
829 if (err_or_null != NULL)
830 *err_or_null = err;
831 NYD2_LEAVE;
832 return rv;
835 FL char *
836 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
838 struct shvar_stack top;
839 char *rv;
840 NYD2_ENTER;
842 memset(&top, 0, sizeof top);
844 top.shs_value = s;
845 if ((top.shs_err = err_or_null) != NULL)
846 *err_or_null = FAL0;
847 top.shs_bsesc = bsescape;
848 rv = _sh_exp_var(&top);
849 NYD2_LEAVE;
850 return rv;
853 FL int
854 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
856 char const *xs;
857 int c, n;
858 NYD2_ENTER;
860 xs = *s;
862 if ((c = *xs & 0xFF) == '\0')
863 goto jleave;
864 ++xs;
865 if (c != '\\')
866 goto jleave;
868 switch ((c = *xs & 0xFF)) {
869 case 'a': c = '\a'; break;
870 case 'b': c = '\b'; break;
871 case 'c': c = PROMPT_STOP; break;
872 case 'f': c = '\f'; break;
873 case 'n': c = '\n'; break;
874 case 'r': c = '\r'; break;
875 case 't': c = '\t'; break;
876 case 'v': c = '\v'; break;
878 /* Hexadecimal TODO uses ASCII */
879 case 'X':
880 case 'x': {
881 static ui8_t const hexatoi[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
882 #undef a_HEX
883 #define a_HEX(n) \
884 hexatoi[(ui8_t)((n) - ((n) <= '9' ? 48 : ((n) <= 'F' ? 55 : 87)))]
886 c = 0;
887 ++xs;
888 if(hexchar(*xs))
889 c = a_HEX(*xs);
890 else{
891 --xs;
892 if(options & OPT_D_V)
893 n_err(_("Invalid \"\\xNUMBER\" notation in \"%s\"\n"), xs - 1);
894 c = '\\';
895 goto jleave;
897 ++xs;
898 if(hexchar(*xs)){
899 c <<= 4;
900 c += a_HEX(*xs);
901 ++xs;
903 goto jleave;
905 #undef a_HEX
907 /* octal, with optional 0 prefix */
908 case '0':
909 ++xs;
910 if(0){
911 default:
912 if(*xs == '\0'){
913 c = '\\';
914 break;
917 for (c = 0, n = 3; n-- > 0 && octalchar(*xs); ++xs) {
918 c <<= 3;
919 c |= *xs - '0';
921 goto jleave;
923 /* S-nail extension for nice (get)prompt(()) support */
924 case '&':
925 case '?':
926 case '$':
927 case '@':
928 if (use_nail_extensions) {
929 switch (c) {
930 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
931 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
932 case '$': c = PROMPT_DOLLAR; break;
933 case '@': c = PROMPT_AT; break;
935 break;
938 /* FALLTHRU */
939 case '\0':
940 /* A sole <backslash> at EOS is treated as-is! */
941 c = '\\';
942 /* FALLTHRU */
943 case '\\':
944 break;
947 ++xs;
948 jleave:
949 *s = xs;
950 NYD2_LEAVE;
951 return c;
954 FL char *
955 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
957 static char buf[PROMPT_BUFFER_SIZE];
959 char *cp;
960 char const *ccp_base, *ccp;
961 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
962 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
963 NYD_ENTER;
965 /* No other place to place this */
966 #ifdef HAVE_ERRORS
967 if (options & OPT_INTERACTIVE) {
968 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
969 pstate |= PS_ERRORS_NOTED;
970 fprintf(stderr, _("There are new messages in the error message ring "
971 "(denoted by \"#ERR#\")\n"
972 " The `errors' command manages this message ring\n"));
975 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
976 a_aux_err_cnt_noted = a_aux_err_cnt;
977 } else
978 trigger = FAL0;
979 #endif
981 cp = buf;
982 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
983 #ifdef HAVE_ERRORS
984 if (trigger)
985 ccp_base = "";
986 else
987 #endif
988 goto jleave;
990 #ifdef HAVE_ERRORS
991 if (trigger)
992 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
993 #endif
994 NATCH_CHAR( cclen_base = strlen(ccp_base); )
996 dfmaxlen = 0; /* keep CC happy */
997 trigger = FAL0;
998 jredo:
999 ccp = ccp_base;
1000 NATCH_CHAR( cclen = cclen_base; )
1001 maxlen = sizeof(buf) -1;
1003 for (;;) {
1004 size_t l;
1005 int c;
1007 if (maxlen == 0)
1008 goto jleave;
1009 #ifdef HAVE_NATCH_CHAR
1010 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
1011 if (c <= 0) {
1012 mblen(NULL, 0);
1013 if (c < 0) {
1014 *buf = '?';
1015 cp = buf + 1;
1016 goto jleave;
1018 break;
1019 } else if ((l = c) > 1) {
1020 if (trigger) {
1021 memcpy(cp, ccp, l);
1022 cp += l;
1024 ccp += l;
1025 maxlen -= l;
1026 continue;
1027 } else
1028 #endif
1029 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
1030 if (trigger)
1031 *cp++ = (char)c;
1032 --maxlen;
1033 continue;
1035 if (c == 0 || c == PROMPT_STOP)
1036 break;
1038 if (trigger) {
1039 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
1040 if (a == NULL)
1041 a = "";
1042 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
1043 cp += l;
1044 maxlen -= l;
1045 dfmaxlen -= l;
1050 if (!trigger) {
1051 trigger = TRU1;
1052 dfmaxlen = maxlen;
1053 goto jredo;
1055 jleave:
1056 *cp = '\0';
1057 NYD_LEAVE;
1058 return buf;
1061 FL char *
1062 nodename(int mayoverride)
1064 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1066 struct utsname ut;
1067 char *hn;
1068 #ifdef HAVE_SOCKETS
1069 # ifdef HAVE_GETADDRINFO
1070 struct addrinfo hints, *res;
1071 # else
1072 struct hostent *hent;
1073 # endif
1074 #endif
1075 NYD_ENTER;
1077 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
1079 } else if ((hn = sys_hostname) == NULL) {
1080 uname(&ut);
1081 hn = ut.nodename;
1082 #ifdef HAVE_SOCKETS
1083 # ifdef HAVE_GETADDRINFO
1084 memset(&hints, 0, sizeof hints);
1085 hints.ai_family = AF_UNSPEC;
1086 hints.ai_flags = AI_CANONNAME;
1087 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
1088 if (res->ai_canonname != NULL) {
1089 size_t l = strlen(res->ai_canonname) +1;
1091 hn = ac_alloc(l);
1092 memcpy(hn, res->ai_canonname, l);
1094 freeaddrinfo(res);
1096 # else
1097 hent = gethostbyname(hn);
1098 if (hent != NULL)
1099 hn = hent->h_name;
1100 # endif
1101 #endif
1102 sys_hostname = sstrdup(hn);
1103 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1104 if (hn != ut.nodename)
1105 ac_free(hn);
1106 #endif
1107 hn = sys_hostname;
1110 if (hostname != NULL && hostname != sys_hostname)
1111 free(hostname);
1112 hostname = sstrdup(hn);
1113 NYD_LEAVE;
1114 return hostname;
1117 FL char *
1118 getrandstring(size_t length)
1120 struct str b64;
1121 char *data;
1122 size_t i;
1123 NYD_ENTER;
1125 #ifndef HAVE_POSIX_RANDOM
1126 if (_rand == NULL)
1127 _rand_init();
1128 #endif
1130 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1131 * with PAD stripped is still longer than what the user requests, easy way */
1132 data = ac_alloc(i = length + 3);
1134 #ifndef HAVE_POSIX_RANDOM
1135 while (i-- > 0)
1136 data[i] = (char)_rand_get8();
1137 #else
1138 { char *cp = data;
1140 while (i > 0) {
1141 union {ui32_t i4; char c[4];} r;
1142 size_t j;
1144 r.i4 = (ui32_t)arc4random();
1145 switch ((j = i & 3)) {
1146 case 0: cp[3] = r.c[3]; j = 4;
1147 case 3: cp[2] = r.c[2];
1148 case 2: cp[1] = r.c[1];
1149 default: cp[0] = r.c[0]; break;
1151 cp += j;
1152 i -= j;
1155 #endif
1157 b64_encode_buf(&b64, data, length + 3,
1158 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1159 ac_free(data);
1161 assert(b64.l >= length);
1162 b64.s[length] = '\0';
1163 NYD_LEAVE;
1164 return b64.s;
1167 FL enum okay
1168 makedir(char const *name)
1170 struct stat st;
1171 enum okay rv = STOP;
1172 NYD_ENTER;
1174 if (!mkdir(name, 0700))
1175 rv = OKAY;
1176 else {
1177 int e = errno;
1178 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1179 S_ISDIR(st.st_mode))
1180 rv = OKAY;
1182 NYD_LEAVE;
1183 return rv;
1186 #ifdef HAVE_FCHDIR
1187 FL enum okay
1188 cwget(struct cw *cw)
1190 enum okay rv = STOP;
1191 NYD_ENTER;
1193 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1194 goto jleave;
1195 if (fchdir(cw->cw_fd) == -1) {
1196 close(cw->cw_fd);
1197 goto jleave;
1199 rv = OKAY;
1200 jleave:
1201 NYD_LEAVE;
1202 return rv;
1205 FL enum okay
1206 cwret(struct cw *cw)
1208 enum okay rv = STOP;
1209 NYD_ENTER;
1211 if (!fchdir(cw->cw_fd))
1212 rv = OKAY;
1213 NYD_LEAVE;
1214 return rv;
1217 FL void
1218 cwrelse(struct cw *cw)
1220 NYD_ENTER;
1221 close(cw->cw_fd);
1222 NYD_LEAVE;
1225 #else /* !HAVE_FCHDIR */
1226 FL enum okay
1227 cwget(struct cw *cw)
1229 enum okay rv = STOP;
1230 NYD_ENTER;
1232 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1233 rv = OKAY;
1234 NYD_LEAVE;
1235 return rv;
1238 FL enum okay
1239 cwret(struct cw *cw)
1241 enum okay rv = STOP;
1242 NYD_ENTER;
1244 if (!chdir(cw->cw_wd))
1245 rv = OKAY;
1246 NYD_LEAVE;
1247 return rv;
1250 FL void
1251 cwrelse(struct cw *cw)
1253 NYD_ENTER;
1254 UNUSED(cw);
1255 NYD_LEAVE;
1257 #endif /* !HAVE_FCHDIR */
1259 FL size_t
1260 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1262 size_t rv;
1263 NYD_ENTER;
1265 #ifdef HAVE_NATCH_CHAR
1266 maxlen = MIN(maxlen, blen);
1267 for (rv = 0; maxlen > 0;) {
1268 int ml = mblen(buf, maxlen);
1269 if (ml <= 0) {
1270 mblen(NULL, 0);
1271 break;
1273 buf += ml;
1274 rv += ml;
1275 maxlen -= ml;
1277 #else
1278 rv = MIN(blen, maxlen);
1279 #endif
1280 NYD_LEAVE;
1281 return rv;
1284 FL size_t
1285 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1287 NATCH_CHAR( struct bidi_info bi; )
1288 size_t rv NATCH_CHAR( COMMA i );
1289 NYD_ENTER;
1291 rv = 0;
1292 if (maxlen-- == 0)
1293 goto j_leave;
1295 #ifdef HAVE_NATCH_CHAR
1296 bidi_info_create(&bi);
1297 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1298 bi.bi_end.l = 0;
1299 goto jnobidi;
1302 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1303 maxlen -= i;
1304 else
1305 goto jleave;
1307 if ((i = bi.bi_start.l) > 0) {
1308 memcpy(store, bi.bi_start.s, i);
1309 store += i;
1310 rv += i;
1313 jnobidi:
1314 while (maxlen > 0) {
1315 int ml = mblen(buf, blen);
1316 if (ml <= 0) {
1317 mblen(NULL, 0);
1318 break;
1320 if (UICMP(z, maxlen, <, ml))
1321 break;
1322 if (ml == 1)
1323 *store = *buf;
1324 else
1325 memcpy(store, buf, ml);
1326 store += ml;
1327 buf += ml;
1328 rv += ml;
1329 maxlen -= ml;
1332 if ((i = bi.bi_end.l) > 0) {
1333 memcpy(store, bi.bi_end.s, i);
1334 store += i;
1335 rv += i;
1337 jleave:
1338 *store = '\0';
1340 #else
1341 rv = MIN(blen, maxlen);
1342 memcpy(store, buf, rv);
1343 store[rv] = '\0';
1344 #endif
1345 j_leave:
1346 NYD_LEAVE;
1347 return rv;
1350 FL char *
1351 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1353 NATCH_CHAR( struct bidi_info bi; )
1354 int col_orig = col, n, sz;
1355 bool_t isbidi, isuni, istab, isrepl;
1356 char *nb, *np;
1357 NYD_ENTER;
1359 /* Bidi only on request and when there is 8-bit data */
1360 isbidi = isuni = FAL0;
1361 #ifdef HAVE_NATCH_CHAR
1362 isuni = ((options & OPT_UNICODE) != 0);
1363 bidi_info_create(&bi);
1364 if (bi.bi_start.l == 0)
1365 goto jnobidi;
1366 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1367 goto jnobidi;
1369 if ((size_t)col >= bi.bi_pad)
1370 col -= bi.bi_pad;
1371 else
1372 col = 0;
1373 jnobidi:
1374 #endif
1376 np = nb = salloc(mb_cur_max * strlen(cp) +
1377 ((fill ? col : 0)
1378 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1379 +1));
1381 #ifdef HAVE_NATCH_CHAR
1382 if (isbidi) {
1383 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1384 np += bi.bi_start.l;
1386 #endif
1388 while (*cp != '\0') {
1389 istab = FAL0;
1390 #ifdef HAVE_C90AMEND1
1391 if (mb_cur_max > 1) {
1392 wchar_t wc;
1394 n = 1;
1395 isrepl = TRU1;
1396 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1397 sz = 1;
1398 else if (wc == L'\t') {
1399 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1400 isrepl = FAL0;
1401 istab = TRU1;
1402 } else if (iswprint(wc)) {
1403 # ifndef HAVE_WCWIDTH
1404 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1405 # else
1406 if ((n = wcwidth(wc)) == -1)
1407 n = 1;
1408 else
1409 # endif
1410 isrepl = FAL0;
1412 } else
1413 #endif
1415 n = sz = 1;
1416 istab = (*cp == '\t');
1417 isrepl = !(istab || isprint((uc_i)*cp));
1420 if (n > col)
1421 break;
1422 col -= n;
1424 if (isrepl) {
1425 if (isuni) {
1426 np[0] = (char)0xEFu;
1427 np[1] = (char)0xBFu;
1428 np[2] = (char)0xBDu;
1429 np += 3;
1430 } else
1431 *np++ = '?';
1432 cp += sz;
1433 } else if (istab || (sz == 1 && spacechar(*cp))) {
1434 *np++ = ' ';
1435 ++cp;
1436 } else
1437 while (sz--)
1438 *np++ = *cp++;
1441 if (fill && col != 0) {
1442 if (fill > 0) {
1443 memmove(nb + col, nb, PTR2SIZE(np - nb));
1444 memset(nb, ' ', col);
1445 } else
1446 memset(np, ' ', col);
1447 np += col;
1448 col = 0;
1451 #ifdef HAVE_NATCH_CHAR
1452 if (isbidi) {
1453 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1454 np += bi.bi_end.l;
1456 #endif
1458 *np = '\0';
1459 if (cols_decr_used_or_null != NULL)
1460 *cols_decr_used_or_null -= col_orig - col;
1461 NYD_LEAVE;
1462 return nb;
1465 FL void
1466 makeprint(struct str const *in, struct str *out)
1468 char const *inp, *maxp;
1469 char *outp;
1470 DBG( size_t msz; )
1471 NYD_ENTER;
1473 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1474 inp = in->s;
1475 maxp = inp + in->l;
1477 #ifdef HAVE_NATCH_CHAR
1478 if (mb_cur_max > 1) {
1479 char mbb[MB_LEN_MAX + 1];
1480 wchar_t wc;
1481 int i, n;
1482 bool_t isuni = ((options & OPT_UNICODE) != 0);
1484 out->l = 0;
1485 while (inp < maxp) {
1486 if (*inp & 0200)
1487 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1488 else {
1489 wc = *inp;
1490 n = 1;
1492 if (n == -1) {
1493 /* FIXME Why mbtowc() resetting here?
1494 * FIXME what about ISO 2022-JP plus -- those
1495 * FIXME will loose shifts, then!
1496 * FIXME THUS - we'd need special "known points"
1497 * FIXME to do so - say, after a newline!!
1498 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1499 mbtowc(&wc, NULL, mb_cur_max);
1500 wc = isuni ? 0xFFFD : '?';
1501 n = 1;
1502 } else if (n == 0)
1503 n = 1;
1504 inp += n;
1505 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1506 wc != '\t') {
1507 if ((wc & ~(wchar_t)037) == 0)
1508 wc = isuni ? 0x2400 | wc : '?';
1509 else if (wc == 0177)
1510 wc = isuni ? 0x2421 : '?';
1511 else
1512 wc = isuni ? 0x2426 : '?';
1513 }else if(isuni){ /* TODO ctext */
1514 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1515 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1516 continue;
1517 /* And some zero-width messes */
1518 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1519 continue;
1520 /* Oh about the ISO C wide character interfaces, baby! */
1521 if(wc == 0xFEFF)
1522 continue;
1524 if ((n = wctomb(mbb, wc)) <= 0)
1525 continue;
1526 out->l += n;
1527 assert(out->l < msz);
1528 for (i = 0; i < n; ++i)
1529 *outp++ = mbb[i];
1531 } else
1532 #endif /* NATCH_CHAR */
1534 int c;
1535 while (inp < maxp) {
1536 c = *inp++ & 0377;
1537 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1538 c = '?';
1539 *outp++ = c;
1541 out->l = in->l;
1543 out->s[out->l] = '\0';
1544 NYD_LEAVE;
1547 FL size_t
1548 delctrl(char *cp, size_t len)
1550 size_t x, y;
1551 NYD_ENTER;
1553 for (x = y = 0; x < len; ++x)
1554 if (!cntrlchar(cp[x]))
1555 cp[y++] = cp[x];
1556 cp[y] = '\0';
1557 NYD_LEAVE;
1558 return y;
1561 FL char *
1562 prstr(char const *s)
1564 struct str in, out;
1565 char *rp;
1566 NYD_ENTER;
1568 in.s = UNCONST(s);
1569 in.l = strlen(s);
1570 makeprint(&in, &out);
1571 rp = savestrbuf(out.s, out.l);
1572 free(out.s);
1573 NYD_LEAVE;
1574 return rp;
1577 FL int
1578 prout(char const *s, size_t sz, FILE *fp)
1580 struct str in, out;
1581 int n;
1582 NYD_ENTER;
1584 in.s = UNCONST(s);
1585 in.l = sz;
1586 makeprint(&in, &out);
1587 n = fwrite(out.s, 1, out.l, fp);
1588 free(out.s);
1589 NYD_LEAVE;
1590 return n;
1593 FL size_t
1594 putuc(int u, int c, FILE *fp)
1596 size_t rv;
1597 NYD_ENTER;
1598 UNUSED(u);
1600 #ifdef HAVE_NATCH_CHAR
1601 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1602 char mbb[MB_LEN_MAX];
1603 int i, n;
1605 if ((n = wctomb(mbb, u)) > 0) {
1606 rv = wcwidth(u);
1607 for (i = 0; i < n; ++i)
1608 if (putc(mbb[i] & 0377, fp) == EOF) {
1609 rv = 0;
1610 break;
1612 } else if (n == 0)
1613 rv = (putc('\0', fp) != EOF);
1614 else
1615 rv = 0;
1616 } else
1617 #endif
1618 rv = (putc(c, fp) != EOF);
1619 NYD_LEAVE;
1620 return rv;
1623 FL bool_t
1624 bidi_info_needed(char const *bdat, size_t blen)
1626 bool_t rv = FAL0;
1627 NYD_ENTER;
1629 #ifdef HAVE_NATCH_CHAR
1630 if (options & OPT_UNICODE)
1631 while (blen > 0) {
1632 /* TODO Checking for BIDI character: use S-CText fromutf8
1633 * TODO plus isrighttoleft (or whatever there will be)! */
1634 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1635 if (c == UI32_MAX)
1636 break;
1638 if (c <= 0x05BE)
1639 continue;
1641 /* (Very very fuzzy, awaiting S-CText for good) */
1642 if ((c >= 0x05BE && c <= 0x08E3) ||
1643 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1644 (c >= 0xFE70 && c <= 0xFEFC) ||
1645 (c >= 0x10800 && c <= 0x10C48) ||
1646 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1647 rv = TRU1;
1648 break;
1651 #endif /* HAVE_NATCH_CHAR */
1652 NYD_LEAVE;
1653 return rv;
1656 FL void
1657 bidi_info_create(struct bidi_info *bip)
1659 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1660 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1661 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1662 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1663 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1664 NATCH_CHAR( char const *hb; )
1665 NYD_ENTER;
1667 memset(bip, 0, sizeof *bip);
1668 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1670 #ifdef HAVE_NATCH_CHAR
1671 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1672 switch (*hb) {
1673 case '3':
1674 bip->bi_pad = 2;
1675 /* FALLTHRU */
1676 case '2':
1677 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1678 break;
1679 case '1':
1680 bip->bi_pad = 2;
1681 /* FALLTHRU */
1682 default:
1683 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1684 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1685 break;
1687 bip->bi_start.l = bip->bi_end.l = 3;
1689 #endif
1690 NYD_LEAVE;
1693 FL si8_t
1694 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1696 char *dat, *eptr;
1697 sl_i sli;
1698 si8_t rv;
1699 NYD_ENTER;
1701 assert(inlen == 0 || inbuf != NULL);
1703 if (inlen == UIZ_MAX)
1704 inlen = strlen(inbuf);
1706 if (inlen == 0)
1707 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1708 else {
1709 if ((inlen == 1 && *inbuf == '1') ||
1710 !ascncasecmp(inbuf, "true", inlen) ||
1711 !ascncasecmp(inbuf, "yes", inlen) ||
1712 !ascncasecmp(inbuf, "on", inlen))
1713 rv = 1;
1714 else if ((inlen == 1 && *inbuf == '0') ||
1715 !ascncasecmp(inbuf, "false", inlen) ||
1716 !ascncasecmp(inbuf, "no", inlen) ||
1717 !ascncasecmp(inbuf, "off", inlen))
1718 rv = 0;
1719 else {
1720 dat = ac_alloc(inlen +1);
1721 memcpy(dat, inbuf, inlen);
1722 dat[inlen] = '\0';
1724 sli = strtol(dat, &eptr, 0);
1725 if (*dat != '\0' && *eptr == '\0')
1726 rv = (sli != 0);
1727 else
1728 rv = -1;
1730 ac_free(dat);
1733 NYD_LEAVE;
1734 return rv;
1737 FL si8_t
1738 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1740 si8_t rv;
1741 NYD_ENTER;
1743 assert(inlen == 0 || inbuf != NULL);
1745 if (inlen == UIZ_MAX)
1746 inlen = strlen(inbuf);
1748 if (inlen == 0)
1749 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1750 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1751 !ascncasecmp(inbuf, "ask-", 4) &&
1752 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1753 (options & OPT_INTERACTIVE))
1754 rv = getapproval(prompt, rv);
1755 NYD_LEAVE;
1756 return rv;
1759 FL bool_t
1760 n_is_all_or_aster(char const *name){
1761 bool_t rv;
1762 NYD_ENTER;
1764 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1765 NYD_LEAVE;
1766 return rv;
1769 FL time_t
1770 n_time_epoch(void)
1772 #ifdef HAVE_CLOCK_GETTIME
1773 struct timespec ts;
1774 #elif defined HAVE_GETTIMEOFDAY
1775 struct timeval ts;
1776 #endif
1777 time_t rv;
1778 NYD2_ENTER;
1780 #ifdef HAVE_CLOCK_GETTIME
1781 clock_gettime(CLOCK_REALTIME, &ts);
1782 rv = (time_t)ts.tv_sec;
1783 #elif defined HAVE_GETTIMEOFDAY
1784 gettimeofday(&ts, NULL);
1785 rv = (time_t)ts.tv_sec;
1786 #else
1787 rv = time(NULL);
1788 #endif
1789 NYD2_LEAVE;
1790 return rv;
1793 FL void
1794 time_current_update(struct time_current *tc, bool_t full_update)
1796 NYD_ENTER;
1797 tc->tc_time = n_time_epoch();
1798 if (full_update) {
1799 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1800 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1801 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1803 NYD_LEAVE;
1806 FL uiz_t
1807 n_msleep(uiz_t millis, bool_t ignint){
1808 uiz_t rv;
1809 NYD2_ENTER;
1811 #ifdef HAVE_NANOSLEEP
1812 /* C99 */{
1813 struct timespec ts, trem;
1814 int i;
1816 ts.tv_sec = millis / 1000;
1817 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1819 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1820 ts = trem;
1821 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1824 #elif defined HAVE_SLEEP
1825 if((millis /= 1000) == 0)
1826 millis = 1;
1827 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1828 millis = rv;
1829 #else
1830 # error Configuration should have detected a function for sleeping.
1831 #endif
1833 NYD2_LEAVE;
1834 return rv;
1837 FL void
1838 n_err(char const *format, ...){
1839 va_list ap;
1840 NYD2_ENTER;
1842 va_start(ap, format);
1843 #ifdef HAVE_ERRORS
1844 if(options & OPT_INTERACTIVE)
1845 n_verr(format, ap);
1846 else
1847 #endif
1849 if(a_aux_err_dirty++ == 0)
1850 fputs(UAGENT ": ", stderr);
1851 vfprintf(stderr, format, ap);
1852 if(strchr(format, '\n') != NULL){ /* TODO */
1853 a_aux_err_dirty = 0;
1854 fflush(stderr);
1857 va_end(ap);
1858 NYD2_LEAVE;
1861 FL void
1862 n_verr(char const *format, va_list ap){
1863 /* Check use cases of PS_ERRORS_NOTED, too! */
1864 #ifdef HAVE_ERRORS
1865 char buf[LINESIZE], *xbuf;
1866 int lmax, l;
1867 struct a_aux_err_node *enp;
1869 LCTA(ERRORS_MAX > 3);
1870 #endif
1871 NYD2_ENTER;
1873 if(a_aux_err_dirty++ == 0)
1874 fputs(UAGENT ": ", stderr);
1876 #ifdef HAVE_ERRORS
1877 if(!(options & OPT_INTERACTIVE))
1878 #endif
1880 vfprintf(stderr, format, ap);
1881 goto jleave;
1884 #ifdef HAVE_ERRORS
1885 xbuf = buf;
1886 lmax = sizeof buf;
1887 jredo:
1888 l = vsnprintf(xbuf, lmax, format, ap);
1889 if (l <= 0)
1890 goto jleave;
1891 if (UICMP(z, l, >=, lmax)) {
1892 /* FIXME Cannot reuse va_list
1893 lmax = ++l;
1894 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1895 goto jredo;
1899 fwrite(xbuf, 1, l, stderr);
1901 /* Link it into the `errors' message ring */
1902 if((enp = a_aux_err_tail) == NULL){
1903 jcreat:
1904 enp = scalloc(1, sizeof *enp);
1905 if(a_aux_err_tail != NULL)
1906 a_aux_err_tail->ae_next = enp;
1907 else
1908 a_aux_err_head = enp;
1909 a_aux_err_tail = enp;
1910 ++a_aux_err_cnt;
1911 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1912 if(a_aux_err_cnt < ERRORS_MAX)
1913 goto jcreat;
1915 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1916 a_aux_err_tail->ae_next = enp;
1917 a_aux_err_tail = enp;
1918 free(enp->ae_str.s);
1919 memset(enp, 0, sizeof *enp);
1922 n_str_add_buf(&enp->ae_str, xbuf, l);
1924 if(xbuf != buf)
1925 free(xbuf);
1926 #endif /* HAVE_ERRORS */
1928 jleave:
1929 /* If the format ends with newline, be clean again */
1930 /* C99 */{
1931 size_t i = strlen(format);
1933 if(i > 0 && format[i - 1] == '\n'){
1934 fflush(stderr);
1935 a_aux_err_dirty = 0;
1938 NYD2_LEAVE;
1941 FL void
1942 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1943 va_list ap;
1944 NYD_X;
1946 va_start(ap, format);
1947 vfprintf(stderr, format, ap);
1948 va_end(ap);
1949 fflush(stderr);
1952 FL void
1953 n_perr(char const *msg, int errval){
1954 char const *fmt;
1955 NYD2_ENTER;
1957 if(msg == NULL){
1958 fmt = "%s%s\n";
1959 msg = "";
1960 }else
1961 fmt = "%s: %s\n";
1963 if(errval == 0)
1964 errval = errno;
1966 n_err(fmt, msg, strerror(errval));
1967 NYD2_LEAVE;
1970 FL void
1971 n_alert(char const *format, ...){
1972 va_list ap;
1973 NYD2_ENTER;
1975 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
1977 va_start(ap, format);
1978 n_verr(format, ap);
1979 va_end(ap);
1981 n_err("\n");
1982 NYD2_LEAVE;
1985 FL void
1986 n_panic(char const *format, ...){
1987 va_list ap;
1988 NYD2_ENTER;
1990 if(a_aux_err_dirty > 0){
1991 putc('\n', stderr);
1992 a_aux_err_dirty = 0;
1994 fprintf(stderr, UAGENT ": Panic: ");
1996 va_start(ap, format);
1997 vfprintf(stderr, format, ap);
1998 va_end(ap);
2000 putc('\n', stderr);
2001 fflush(stderr);
2002 NYD2_LEAVE;
2003 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2006 #ifdef HAVE_ERRORS
2007 FL int
2008 c_errors(void *v){
2009 char **argv = v;
2010 struct a_aux_err_node *enp;
2011 NYD_ENTER;
2013 if(*argv == NULL)
2014 goto jlist;
2015 if(argv[1] != NULL)
2016 goto jerr;
2017 if(!asccasecmp(*argv, "show"))
2018 goto jlist;
2019 if(!asccasecmp(*argv, "clear"))
2020 goto jclear;
2021 jerr:
2022 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2023 v = NULL;
2024 jleave:
2025 NYD_LEAVE;
2026 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
2028 jlist:{
2029 FILE *fp;
2030 size_t i;
2032 if(a_aux_err_head == NULL){
2033 fprintf(stderr, _("The error ring is empty\n"));
2034 goto jleave;
2037 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
2038 NULL){
2039 fprintf(stderr, _("tmpfile"));
2040 v = NULL;
2041 goto jleave;
2044 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
2045 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2046 ++i, enp->ae_str.l, enp->ae_str.s);
2047 /* We don't know wether last string ended with NL; be simple */
2048 putc('\n', fp);
2050 page_or_print(fp, 0);
2051 Fclose(fp);
2053 /* FALLTHRU */
2055 jclear:
2056 a_aux_err_tail = NULL;
2057 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
2058 while((enp = a_aux_err_head) != NULL){
2059 a_aux_err_head = enp->ae_next;
2060 free(enp->ae_str.s);
2061 free(enp);
2063 goto jleave;
2065 #endif /* HAVE_ERRORS */
2067 #ifndef HAVE_DEBUG
2068 FL void *
2069 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2071 void *rv;
2072 NYD2_ENTER;
2074 if (s == 0)
2075 s = 1;
2076 if ((rv = malloc(s)) == NULL)
2077 n_panic(_("no memory"));
2078 NYD2_LEAVE;
2079 return rv;
2082 FL void *
2083 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2085 void *rv;
2086 NYD2_ENTER;
2088 if (s == 0)
2089 s = 1;
2090 if (v == NULL)
2091 rv = smalloc(s);
2092 else if ((rv = realloc(v, s)) == NULL)
2093 n_panic(_("no memory"));
2094 NYD2_LEAVE;
2095 return rv;
2098 FL void *
2099 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2101 void *rv;
2102 NYD2_ENTER;
2104 if (size == 0)
2105 size = 1;
2106 if ((rv = calloc(nmemb, size)) == NULL)
2107 n_panic(_("no memory"));
2108 NYD2_LEAVE;
2109 return rv;
2112 #else /* !HAVE_DEBUG */
2113 CTA(sizeof(char) == sizeof(ui8_t));
2115 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2116 # define _HOPE_SET(C) \
2117 do {\
2118 union mem_ptr __xl, __xu;\
2119 struct mem_chunk *__xc;\
2120 __xl.p_p = (C).p_p;\
2121 __xc = __xl.p_c - 1;\
2122 __xu.p_p = __xc;\
2123 (C).p_cp += 8;\
2124 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2125 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2126 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2127 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2128 __xu.p_ui8p += __xc->mc_size - 8;\
2129 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2130 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2131 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2132 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2133 } while (0)
2134 # define _HOPE_GET_TRACE(C,BAD) \
2135 do {\
2136 (C).p_cp += 8;\
2137 _HOPE_GET(C, BAD);\
2138 (C).p_cp += 8;\
2139 } while(0)
2140 # define _HOPE_GET(C,BAD) \
2141 do {\
2142 union mem_ptr __xl, __xu;\
2143 struct mem_chunk *__xc;\
2144 ui32_t __i;\
2145 __xl.p_p = (C).p_p;\
2146 __xl.p_cp -= 8;\
2147 (C).p_cp = __xl.p_cp;\
2148 __xc = __xl.p_c - 1;\
2149 (BAD) = FAL0;\
2150 __i = 0;\
2151 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2152 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2153 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2154 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2155 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2156 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2157 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2158 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2159 if (__i != 0) {\
2160 (BAD) = TRU1;\
2161 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2162 __xl.p_p, __i, mdbg_file, mdbg_line);\
2164 __xu.p_p = __xc;\
2165 __xu.p_ui8p += __xc->mc_size - 8;\
2166 __i = 0;\
2167 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2168 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2169 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2170 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2171 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2172 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2173 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2174 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2175 if (__i != 0) {\
2176 (BAD) = TRU1;\
2177 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2178 __xl.p_p, __i, mdbg_file, mdbg_line);\
2180 if (BAD)\
2181 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2182 __xc->mc_file, __xc->mc_line);\
2183 } while (0)
2185 FL void *
2186 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2188 union mem_ptr p;
2189 NYD2_ENTER;
2191 if (s == 0)
2192 s = 1;
2193 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2194 n_panic("smalloc(): allocation too large: %s, line %d",
2195 mdbg_file, mdbg_line);
2196 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2198 if ((p.p_p = (malloc)(s)) == NULL)
2199 n_panic(_("no memory"));
2200 p.p_c->mc_prev = NULL;
2201 if ((p.p_c->mc_next = _mem_list) != NULL)
2202 _mem_list->mc_prev = p.p_c;
2203 p.p_c->mc_file = mdbg_file;
2204 p.p_c->mc_line = (ui16_t)mdbg_line;
2205 p.p_c->mc_isfree = FAL0;
2206 p.p_c->mc_size = (ui32_t)s;
2208 _mem_list = p.p_c++;
2209 _HOPE_SET(p);
2211 ++_mem_aall;
2212 ++_mem_acur;
2213 _mem_amax = MAX(_mem_amax, _mem_acur);
2214 _mem_mall += s;
2215 _mem_mcur += s;
2216 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2217 NYD2_LEAVE;
2218 return p.p_p;
2221 FL void *
2222 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2224 union mem_ptr p;
2225 bool_t isbad;
2226 NYD2_ENTER;
2228 if ((p.p_p = v) == NULL) {
2229 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2230 goto jleave;
2233 _HOPE_GET(p, isbad);
2234 --p.p_c;
2235 if (p.p_c->mc_isfree) {
2236 n_err("srealloc(): region freed! At %s, line %d\n"
2237 "\tLast seen: %s, line %" PRIu16 "\n",
2238 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2239 goto jforce;
2242 if (p.p_c == _mem_list)
2243 _mem_list = p.p_c->mc_next;
2244 else
2245 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2246 if (p.p_c->mc_next != NULL)
2247 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2249 --_mem_acur;
2250 _mem_mcur -= p.p_c->mc_size;
2251 jforce:
2252 if (s == 0)
2253 s = 1;
2254 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2255 n_panic("srealloc(): allocation too large: %s, line %d",
2256 mdbg_file, mdbg_line);
2257 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2259 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2260 n_panic(_("no memory"));
2261 p.p_c->mc_prev = NULL;
2262 if ((p.p_c->mc_next = _mem_list) != NULL)
2263 _mem_list->mc_prev = p.p_c;
2264 p.p_c->mc_file = mdbg_file;
2265 p.p_c->mc_line = (ui16_t)mdbg_line;
2266 p.p_c->mc_isfree = FAL0;
2267 p.p_c->mc_size = (ui32_t)s;
2268 _mem_list = p.p_c++;
2269 _HOPE_SET(p);
2271 ++_mem_aall;
2272 ++_mem_acur;
2273 _mem_amax = MAX(_mem_amax, _mem_acur);
2274 _mem_mall += s;
2275 _mem_mcur += s;
2276 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2277 jleave:
2278 NYD2_LEAVE;
2279 return p.p_p;
2282 FL void *
2283 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2285 union mem_ptr p;
2286 NYD2_ENTER;
2288 if (size == 0)
2289 size = 1;
2290 if (nmemb == 0)
2291 nmemb = 1;
2292 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2293 n_panic("scalloc(): allocation size too large: %s, line %d",
2294 mdbg_file, mdbg_line);
2295 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2296 n_panic("scalloc(): allocation count too large: %s, line %d",
2297 mdbg_file, mdbg_line);
2299 size *= nmemb;
2300 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2302 if ((p.p_p = (malloc)(size)) == NULL)
2303 n_panic(_("no memory"));
2304 memset(p.p_p, 0, size);
2305 p.p_c->mc_prev = NULL;
2306 if ((p.p_c->mc_next = _mem_list) != NULL)
2307 _mem_list->mc_prev = p.p_c;
2308 p.p_c->mc_file = mdbg_file;
2309 p.p_c->mc_line = (ui16_t)mdbg_line;
2310 p.p_c->mc_isfree = FAL0;
2311 p.p_c->mc_size = (ui32_t)size;
2312 _mem_list = p.p_c++;
2313 _HOPE_SET(p);
2315 ++_mem_aall;
2316 ++_mem_acur;
2317 _mem_amax = MAX(_mem_amax, _mem_acur);
2318 _mem_mall += size;
2319 _mem_mcur += size;
2320 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2321 NYD2_LEAVE;
2322 return p.p_p;
2325 FL void
2326 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2328 union mem_ptr p;
2329 bool_t isbad;
2330 NYD2_ENTER;
2332 if ((p.p_p = v) == NULL) {
2333 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2334 goto jleave;
2337 _HOPE_GET(p, isbad);
2338 --p.p_c;
2339 if (p.p_c->mc_isfree) {
2340 n_err("sfree(): double-free avoided at %s, line %d\n"
2341 "\tLast seen: %s, line %" PRIu16 "\n",
2342 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2343 goto jleave;
2346 if (p.p_c == _mem_list)
2347 _mem_list = p.p_c->mc_next;
2348 else
2349 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2350 if (p.p_c->mc_next != NULL)
2351 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2352 p.p_c->mc_isfree = TRU1;
2353 /* Trash contents (also see [21c05f8]) */
2354 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2356 --_mem_acur;
2357 _mem_mcur -= p.p_c->mc_size;
2359 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2360 p.p_c->mc_next = _mem_free;
2361 _mem_free = p.p_c;
2362 } else
2363 (free)(p.p_c);
2364 jleave:
2365 NYD2_LEAVE;
2368 FL void
2369 smemreset(void)
2371 union mem_ptr p;
2372 size_t c = 0, s = 0;
2373 NYD_ENTER;
2375 smemcheck();
2377 for (p.p_c = _mem_free; p.p_c != NULL;) {
2378 void *vp = p.p_c;
2379 ++c;
2380 s += p.p_c->mc_size;
2381 p.p_c = p.p_c->mc_next;
2382 (free)(vp);
2384 _mem_free = NULL;
2386 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2387 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2388 NYD_LEAVE;
2391 FL int
2392 c_smemtrace(void *v)
2394 /* For _HOPE_GET() */
2395 char const * const mdbg_file = "smemtrace()";
2396 int const mdbg_line = -1;
2397 FILE *fp;
2398 union mem_ptr p, xp;
2399 bool_t isbad;
2400 size_t lines;
2401 NYD_ENTER;
2403 v = (void*)0x1;
2404 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2405 n_perr("tmpfile", 0);
2406 goto jleave;
2409 fprintf(fp, "Memory statistics:\n"
2410 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2411 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2412 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2414 fprintf(fp, "Currently allocated memory chunks:\n");
2415 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2416 ++lines, p.p_c = p.p_c->mc_next) {
2417 xp = p;
2418 ++xp.p_c;
2419 _HOPE_GET_TRACE(xp, isbad);
2420 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2421 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2422 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2423 p.p_c->mc_line);
2426 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2427 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2428 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2429 xp = p;
2430 ++xp.p_c;
2431 _HOPE_GET_TRACE(xp, isbad);
2432 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2433 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2434 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2435 p.p_c->mc_file, p.p_c->mc_line);
2439 page_or_print(fp, lines);
2440 Fclose(fp);
2441 v = NULL;
2442 jleave:
2443 NYD_LEAVE;
2444 return (v != NULL);
2447 # ifdef HAVE_DEVEL
2448 FL bool_t
2449 _smemcheck(char const *mdbg_file, int mdbg_line)
2451 union mem_ptr p, xp;
2452 bool_t anybad = FAL0, isbad;
2453 size_t lines;
2454 NYD_ENTER;
2456 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2457 ++lines, p.p_c = p.p_c->mc_next) {
2458 xp = p;
2459 ++xp.p_c;
2460 _HOPE_GET_TRACE(xp, isbad);
2461 if (isbad) {
2462 anybad = TRU1;
2463 n_err(
2464 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2465 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2466 p.p_c->mc_file, p.p_c->mc_line);
2470 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2471 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2472 xp = p;
2473 ++xp.p_c;
2474 _HOPE_GET_TRACE(xp, isbad);
2475 if (isbad) {
2476 anybad = TRU1;
2477 n_err(
2478 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2479 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2480 p.p_c->mc_file, p.p_c->mc_line);
2484 NYD_LEAVE;
2485 return anybad;
2487 # endif /* HAVE_DEVEL */
2489 # undef _HOPE_SIZE
2490 # undef _HOPE_SET
2491 # undef _HOPE_GET_TRACE
2492 # undef _HOPE_GET
2493 #endif /* HAVE_DEBUG */
2495 /* s-it-mode */