Turn NCL into M(ailx) L(ine) E(ditor)..
[s-mailx.git] / auxlily.c
blob9e83ab6fe97457635d0af6555db5ceeb9fe56836
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) PART OF UI! */
956 { /* FIXME getprompt must mb->wc->mb+reset seq! */
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_width(char const *buf, size_t blen){
1261 size_t rv;
1262 NYD2_ENTER;
1264 if(blen == UIZ_MAX)
1265 blen = (buf == NULL) ? 0 : strlen(buf);
1266 assert(blen == 0 || buf != NULL);
1268 if((rv = blen) > 0){
1269 #ifdef HAVE_C90AMEND1
1270 mbstate_t mbs;
1271 wchar_t wc;
1273 memset(&mbs, 0, sizeof mbs);
1275 for(rv = 0; blen > 0;){
1276 size_t i = mbrtowc(&wc, buf, blen, &mbs);
1278 switch(i){
1279 case (size_t)-2:
1280 case (size_t)-1:
1281 rv = (size_t)-1;
1282 /* FALLTHRU */
1283 case 0:
1284 blen = 0;
1285 break;
1286 default:
1287 buf += i;
1288 blen -= i;
1289 # ifdef HAVE_WCWIDTH
1290 /* C99 */{
1291 int w = wcwidth(wc);
1293 if(w > 0)
1294 rv += w;
1295 else if(wc == '\t')
1296 ++rv;
1298 # else
1299 if(iswprint(wc))
1300 rv += 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1301 else if(wc == '\t')
1302 ++rv;
1303 # endif
1304 break;
1307 #endif /* HAVE_C90AMEND1 */
1309 NYD2_LEAVE;
1310 return rv;
1313 FL size_t
1314 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1316 size_t rv;
1317 NYD_ENTER;
1319 #ifdef HAVE_NATCH_CHAR
1320 maxlen = MIN(maxlen, blen);
1321 for (rv = 0; maxlen > 0;) {
1322 int ml = mblen(buf, maxlen);
1323 if (ml <= 0) {
1324 mblen(NULL, 0);
1325 break;
1327 buf += ml;
1328 rv += ml;
1329 maxlen -= ml;
1331 #else
1332 rv = MIN(blen, maxlen);
1333 #endif
1334 NYD_LEAVE;
1335 return rv;
1338 FL size_t
1339 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1341 NATCH_CHAR( struct bidi_info bi; )
1342 size_t rv NATCH_CHAR( COMMA i );
1343 NYD_ENTER;
1345 rv = 0;
1346 if (maxlen-- == 0)
1347 goto j_leave;
1349 #ifdef HAVE_NATCH_CHAR
1350 bidi_info_create(&bi);
1351 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1352 bi.bi_end.l = 0;
1353 goto jnobidi;
1356 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1357 maxlen -= i;
1358 else
1359 goto jleave;
1361 if ((i = bi.bi_start.l) > 0) {
1362 memcpy(store, bi.bi_start.s, i);
1363 store += i;
1364 rv += i;
1367 jnobidi:
1368 while (maxlen > 0) {
1369 int ml = mblen(buf, blen);
1370 if (ml <= 0) {
1371 mblen(NULL, 0);
1372 break;
1374 if (UICMP(z, maxlen, <, ml))
1375 break;
1376 if (ml == 1)
1377 *store = *buf;
1378 else
1379 memcpy(store, buf, ml);
1380 store += ml;
1381 buf += ml;
1382 rv += ml;
1383 maxlen -= ml;
1386 if ((i = bi.bi_end.l) > 0) {
1387 memcpy(store, bi.bi_end.s, i);
1388 store += i;
1389 rv += i;
1391 jleave:
1392 *store = '\0';
1394 #else
1395 rv = MIN(blen, maxlen);
1396 memcpy(store, buf, rv);
1397 store[rv] = '\0';
1398 #endif
1399 j_leave:
1400 NYD_LEAVE;
1401 return rv;
1404 FL char *
1405 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1407 NATCH_CHAR( struct bidi_info bi; )
1408 int col_orig = col, n, sz;
1409 bool_t isbidi, isuni, istab, isrepl;
1410 char *nb, *np;
1411 NYD_ENTER;
1413 /* Bidi only on request and when there is 8-bit data */
1414 isbidi = isuni = FAL0;
1415 #ifdef HAVE_NATCH_CHAR
1416 isuni = ((options & OPT_UNICODE) != 0);
1417 bidi_info_create(&bi);
1418 if (bi.bi_start.l == 0)
1419 goto jnobidi;
1420 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1421 goto jnobidi;
1423 if ((size_t)col >= bi.bi_pad)
1424 col -= bi.bi_pad;
1425 else
1426 col = 0;
1427 jnobidi:
1428 #endif
1430 np = nb = salloc(mb_cur_max * strlen(cp) +
1431 ((fill ? col : 0)
1432 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1433 +1));
1435 #ifdef HAVE_NATCH_CHAR
1436 if (isbidi) {
1437 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1438 np += bi.bi_start.l;
1440 #endif
1442 while (*cp != '\0') {
1443 istab = FAL0;
1444 #ifdef HAVE_C90AMEND1
1445 if (mb_cur_max > 1) {
1446 wchar_t wc;
1448 n = 1;
1449 isrepl = TRU1;
1450 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1451 sz = 1;
1452 else if (wc == L'\t') {
1453 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1454 isrepl = FAL0;
1455 istab = TRU1;
1456 } else if (iswprint(wc)) {
1457 # ifndef HAVE_WCWIDTH
1458 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1459 # else
1460 if ((n = wcwidth(wc)) == -1)
1461 n = 1;
1462 else
1463 # endif
1464 isrepl = FAL0;
1466 } else
1467 #endif
1469 n = sz = 1;
1470 istab = (*cp == '\t');
1471 isrepl = !(istab || isprint((uc_i)*cp));
1474 if (n > col)
1475 break;
1476 col -= n;
1478 if (isrepl) {
1479 if (isuni) {
1480 np[0] = (char)0xEFu;
1481 np[1] = (char)0xBFu;
1482 np[2] = (char)0xBDu;
1483 np += 3;
1484 } else
1485 *np++ = '?';
1486 cp += sz;
1487 } else if (istab || (sz == 1 && spacechar(*cp))) {
1488 *np++ = ' ';
1489 ++cp;
1490 } else
1491 while (sz--)
1492 *np++ = *cp++;
1495 if (fill && col != 0) {
1496 if (fill > 0) {
1497 memmove(nb + col, nb, PTR2SIZE(np - nb));
1498 memset(nb, ' ', col);
1499 } else
1500 memset(np, ' ', col);
1501 np += col;
1502 col = 0;
1505 #ifdef HAVE_NATCH_CHAR
1506 if (isbidi) {
1507 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1508 np += bi.bi_end.l;
1510 #endif
1512 *np = '\0';
1513 if (cols_decr_used_or_null != NULL)
1514 *cols_decr_used_or_null -= col_orig - col;
1515 NYD_LEAVE;
1516 return nb;
1519 FL void
1520 makeprint(struct str const *in, struct str *out)
1522 char const *inp, *maxp;
1523 char *outp;
1524 DBG( size_t msz; )
1525 NYD_ENTER;
1527 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1528 inp = in->s;
1529 maxp = inp + in->l;
1531 #ifdef HAVE_NATCH_CHAR
1532 if (mb_cur_max > 1) {
1533 char mbb[MB_LEN_MAX + 1];
1534 wchar_t wc;
1535 int i, n;
1536 bool_t isuni = ((options & OPT_UNICODE) != 0);
1538 out->l = 0;
1539 while (inp < maxp) {
1540 if (*inp & 0200)
1541 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1542 else {
1543 wc = *inp;
1544 n = 1;
1546 if (n == -1) {
1547 /* FIXME Why mbtowc() resetting here?
1548 * FIXME what about ISO 2022-JP plus -- those
1549 * FIXME will loose shifts, then!
1550 * FIXME THUS - we'd need special "known points"
1551 * FIXME to do so - say, after a newline!!
1552 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1553 mbtowc(&wc, NULL, mb_cur_max);
1554 wc = isuni ? 0xFFFD : '?';
1555 n = 1;
1556 } else if (n == 0)
1557 n = 1;
1558 inp += n;
1559 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1560 wc != '\t') {
1561 if ((wc & ~(wchar_t)037) == 0)
1562 wc = isuni ? 0x2400 | wc : '?';
1563 else if (wc == 0177)
1564 wc = isuni ? 0x2421 : '?';
1565 else
1566 wc = isuni ? 0x2426 : '?';
1567 }else if(isuni){ /* TODO ctext */
1568 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1569 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1570 continue;
1571 /* And some zero-width messes */
1572 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1573 continue;
1574 /* Oh about the ISO C wide character interfaces, baby! */
1575 if(wc == 0xFEFF)
1576 continue;
1578 if ((n = wctomb(mbb, wc)) <= 0)
1579 continue;
1580 out->l += n;
1581 assert(out->l < msz);
1582 for (i = 0; i < n; ++i)
1583 *outp++ = mbb[i];
1585 } else
1586 #endif /* NATCH_CHAR */
1588 int c;
1589 while (inp < maxp) {
1590 c = *inp++ & 0377;
1591 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1592 c = '?';
1593 *outp++ = c;
1595 out->l = in->l;
1597 out->s[out->l] = '\0';
1598 NYD_LEAVE;
1601 FL size_t
1602 delctrl(char *cp, size_t len)
1604 size_t x, y;
1605 NYD_ENTER;
1607 for (x = y = 0; x < len; ++x)
1608 if (!cntrlchar(cp[x]))
1609 cp[y++] = cp[x];
1610 cp[y] = '\0';
1611 NYD_LEAVE;
1612 return y;
1615 FL char *
1616 prstr(char const *s)
1618 struct str in, out;
1619 char *rp;
1620 NYD_ENTER;
1622 in.s = UNCONST(s);
1623 in.l = strlen(s);
1624 makeprint(&in, &out);
1625 rp = savestrbuf(out.s, out.l);
1626 free(out.s);
1627 NYD_LEAVE;
1628 return rp;
1631 FL int
1632 prout(char const *s, size_t sz, FILE *fp)
1634 struct str in, out;
1635 int n;
1636 NYD_ENTER;
1638 in.s = UNCONST(s);
1639 in.l = sz;
1640 makeprint(&in, &out);
1641 n = fwrite(out.s, 1, out.l, fp);
1642 free(out.s);
1643 NYD_LEAVE;
1644 return n;
1647 FL size_t
1648 putuc(int u, int c, FILE *fp)
1650 size_t rv;
1651 NYD_ENTER;
1652 UNUSED(u);
1654 #ifdef HAVE_NATCH_CHAR
1655 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1656 char mbb[MB_LEN_MAX];
1657 int i, n;
1659 if ((n = wctomb(mbb, u)) > 0) {
1660 rv = wcwidth(u);
1661 for (i = 0; i < n; ++i)
1662 if (putc(mbb[i] & 0377, fp) == EOF) {
1663 rv = 0;
1664 break;
1666 } else if (n == 0)
1667 rv = (putc('\0', fp) != EOF);
1668 else
1669 rv = 0;
1670 } else
1671 #endif
1672 rv = (putc(c, fp) != EOF);
1673 NYD_LEAVE;
1674 return rv;
1677 FL bool_t
1678 bidi_info_needed(char const *bdat, size_t blen)
1680 bool_t rv = FAL0;
1681 NYD_ENTER;
1683 #ifdef HAVE_NATCH_CHAR
1684 if (options & OPT_UNICODE)
1685 while (blen > 0) {
1686 /* TODO Checking for BIDI character: use S-CText fromutf8
1687 * TODO plus isrighttoleft (or whatever there will be)! */
1688 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1689 if (c == UI32_MAX)
1690 break;
1692 if (c <= 0x05BE)
1693 continue;
1695 /* (Very very fuzzy, awaiting S-CText for good) */
1696 if ((c >= 0x05BE && c <= 0x08E3) ||
1697 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1698 (c >= 0xFE70 && c <= 0xFEFC) ||
1699 (c >= 0x10800 && c <= 0x10C48) ||
1700 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1701 rv = TRU1;
1702 break;
1705 #endif /* HAVE_NATCH_CHAR */
1706 NYD_LEAVE;
1707 return rv;
1710 FL void
1711 bidi_info_create(struct bidi_info *bip)
1713 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1714 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1715 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1716 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1717 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1718 NATCH_CHAR( char const *hb; )
1719 NYD_ENTER;
1721 memset(bip, 0, sizeof *bip);
1722 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1724 #ifdef HAVE_NATCH_CHAR
1725 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1726 switch (*hb) {
1727 case '3':
1728 bip->bi_pad = 2;
1729 /* FALLTHRU */
1730 case '2':
1731 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1732 break;
1733 case '1':
1734 bip->bi_pad = 2;
1735 /* FALLTHRU */
1736 default:
1737 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1738 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1739 break;
1741 bip->bi_start.l = bip->bi_end.l = 3;
1743 #endif
1744 NYD_LEAVE;
1747 FL si8_t
1748 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1750 char *dat, *eptr;
1751 sl_i sli;
1752 si8_t rv;
1753 NYD_ENTER;
1755 assert(inlen == 0 || inbuf != NULL);
1757 if (inlen == UIZ_MAX)
1758 inlen = strlen(inbuf);
1760 if (inlen == 0)
1761 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1762 else {
1763 if ((inlen == 1 && *inbuf == '1') ||
1764 !ascncasecmp(inbuf, "true", inlen) ||
1765 !ascncasecmp(inbuf, "yes", inlen) ||
1766 !ascncasecmp(inbuf, "on", inlen))
1767 rv = 1;
1768 else if ((inlen == 1 && *inbuf == '0') ||
1769 !ascncasecmp(inbuf, "false", inlen) ||
1770 !ascncasecmp(inbuf, "no", inlen) ||
1771 !ascncasecmp(inbuf, "off", inlen))
1772 rv = 0;
1773 else {
1774 dat = ac_alloc(inlen +1);
1775 memcpy(dat, inbuf, inlen);
1776 dat[inlen] = '\0';
1778 sli = strtol(dat, &eptr, 0);
1779 if (*dat != '\0' && *eptr == '\0')
1780 rv = (sli != 0);
1781 else
1782 rv = -1;
1784 ac_free(dat);
1787 NYD_LEAVE;
1788 return rv;
1791 FL si8_t
1792 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1794 si8_t rv;
1795 NYD_ENTER;
1797 assert(inlen == 0 || inbuf != NULL);
1799 if (inlen == UIZ_MAX)
1800 inlen = strlen(inbuf);
1802 if (inlen == 0)
1803 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1804 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1805 !ascncasecmp(inbuf, "ask-", 4) &&
1806 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1807 (options & OPT_INTERACTIVE))
1808 rv = getapproval(prompt, rv);
1809 NYD_LEAVE;
1810 return rv;
1813 FL bool_t
1814 n_is_all_or_aster(char const *name){
1815 bool_t rv;
1816 NYD_ENTER;
1818 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1819 NYD_LEAVE;
1820 return rv;
1823 FL time_t
1824 n_time_epoch(void)
1826 #ifdef HAVE_CLOCK_GETTIME
1827 struct timespec ts;
1828 #elif defined HAVE_GETTIMEOFDAY
1829 struct timeval ts;
1830 #endif
1831 time_t rv;
1832 NYD2_ENTER;
1834 #ifdef HAVE_CLOCK_GETTIME
1835 clock_gettime(CLOCK_REALTIME, &ts);
1836 rv = (time_t)ts.tv_sec;
1837 #elif defined HAVE_GETTIMEOFDAY
1838 gettimeofday(&ts, NULL);
1839 rv = (time_t)ts.tv_sec;
1840 #else
1841 rv = time(NULL);
1842 #endif
1843 NYD2_LEAVE;
1844 return rv;
1847 FL void
1848 time_current_update(struct time_current *tc, bool_t full_update)
1850 NYD_ENTER;
1851 tc->tc_time = n_time_epoch();
1852 if (full_update) {
1853 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1854 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1855 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1857 NYD_LEAVE;
1860 FL uiz_t
1861 n_msleep(uiz_t millis, bool_t ignint){
1862 uiz_t rv;
1863 NYD2_ENTER;
1865 #ifdef HAVE_NANOSLEEP
1866 /* C99 */{
1867 struct timespec ts, trem;
1868 int i;
1870 ts.tv_sec = millis / 1000;
1871 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1873 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1874 ts = trem;
1875 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1878 #elif defined HAVE_SLEEP
1879 if((millis /= 1000) == 0)
1880 millis = 1;
1881 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1882 millis = rv;
1883 #else
1884 # error Configuration should have detected a function for sleeping.
1885 #endif
1887 NYD2_LEAVE;
1888 return rv;
1891 FL void
1892 n_err(char const *format, ...){
1893 va_list ap;
1894 NYD2_ENTER;
1896 va_start(ap, format);
1897 #ifdef HAVE_ERRORS
1898 if(options & OPT_INTERACTIVE)
1899 n_verr(format, ap);
1900 else
1901 #endif
1903 if(a_aux_err_dirty++ == 0)
1904 fputs(UAGENT ": ", stderr);
1905 vfprintf(stderr, format, ap);
1906 if(strchr(format, '\n') != NULL){ /* TODO */
1907 a_aux_err_dirty = 0;
1908 fflush(stderr);
1911 va_end(ap);
1912 NYD2_LEAVE;
1915 FL void
1916 n_verr(char const *format, va_list ap){
1917 /* Check use cases of PS_ERRORS_NOTED, too! */
1918 #ifdef HAVE_ERRORS
1919 char buf[LINESIZE], *xbuf;
1920 int lmax, l;
1921 struct a_aux_err_node *enp;
1923 LCTA(ERRORS_MAX > 3);
1924 #endif
1925 NYD2_ENTER;
1927 if(a_aux_err_dirty++ == 0)
1928 fputs(UAGENT ": ", stderr);
1930 #ifdef HAVE_ERRORS
1931 if(!(options & OPT_INTERACTIVE))
1932 #endif
1934 vfprintf(stderr, format, ap);
1935 goto jleave;
1938 #ifdef HAVE_ERRORS
1939 xbuf = buf;
1940 lmax = sizeof buf;
1941 jredo:
1942 l = vsnprintf(xbuf, lmax, format, ap);
1943 if (l <= 0)
1944 goto jleave;
1945 if (UICMP(z, l, >=, lmax)) {
1946 /* FIXME Cannot reuse va_list
1947 lmax = ++l;
1948 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1949 goto jredo;
1953 fwrite(xbuf, 1, l, stderr);
1955 /* Link it into the `errors' message ring */
1956 if((enp = a_aux_err_tail) == NULL){
1957 jcreat:
1958 enp = scalloc(1, sizeof *enp);
1959 if(a_aux_err_tail != NULL)
1960 a_aux_err_tail->ae_next = enp;
1961 else
1962 a_aux_err_head = enp;
1963 a_aux_err_tail = enp;
1964 ++a_aux_err_cnt;
1965 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1966 if(a_aux_err_cnt < ERRORS_MAX)
1967 goto jcreat;
1969 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1970 a_aux_err_tail->ae_next = enp;
1971 a_aux_err_tail = enp;
1972 free(enp->ae_str.s);
1973 memset(enp, 0, sizeof *enp);
1976 n_str_add_buf(&enp->ae_str, xbuf, l);
1978 if(xbuf != buf)
1979 free(xbuf);
1980 #endif /* HAVE_ERRORS */
1982 jleave:
1983 /* If the format ends with newline, be clean again */
1984 /* C99 */{
1985 size_t i = strlen(format);
1987 if(i > 0 && format[i - 1] == '\n'){
1988 fflush(stderr);
1989 a_aux_err_dirty = 0;
1992 NYD2_LEAVE;
1995 FL void
1996 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1997 va_list ap;
1998 NYD_X;
2000 va_start(ap, format);
2001 vfprintf(stderr, format, ap);
2002 va_end(ap);
2003 fflush(stderr);
2006 FL void
2007 n_perr(char const *msg, int errval){
2008 char const *fmt;
2009 NYD2_ENTER;
2011 if(msg == NULL){
2012 fmt = "%s%s\n";
2013 msg = "";
2014 }else
2015 fmt = "%s: %s\n";
2017 if(errval == 0)
2018 errval = errno;
2020 n_err(fmt, msg, strerror(errval));
2021 NYD2_LEAVE;
2024 FL void
2025 n_alert(char const *format, ...){
2026 va_list ap;
2027 NYD2_ENTER;
2029 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
2031 va_start(ap, format);
2032 n_verr(format, ap);
2033 va_end(ap);
2035 n_err("\n");
2036 NYD2_LEAVE;
2039 FL void
2040 n_panic(char const *format, ...){
2041 va_list ap;
2042 NYD2_ENTER;
2044 if(a_aux_err_dirty > 0){
2045 putc('\n', stderr);
2046 a_aux_err_dirty = 0;
2048 fprintf(stderr, UAGENT ": Panic: ");
2050 va_start(ap, format);
2051 vfprintf(stderr, format, ap);
2052 va_end(ap);
2054 putc('\n', stderr);
2055 fflush(stderr);
2056 NYD2_LEAVE;
2057 abort(); /* Was exit(EXIT_ERR); for a while, but no */
2060 #ifdef HAVE_ERRORS
2061 FL int
2062 c_errors(void *v){
2063 char **argv = v;
2064 struct a_aux_err_node *enp;
2065 NYD_ENTER;
2067 if(*argv == NULL)
2068 goto jlist;
2069 if(argv[1] != NULL)
2070 goto jerr;
2071 if(!asccasecmp(*argv, "show"))
2072 goto jlist;
2073 if(!asccasecmp(*argv, "clear"))
2074 goto jclear;
2075 jerr:
2076 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
2077 v = NULL;
2078 jleave:
2079 NYD_LEAVE;
2080 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
2082 jlist:{
2083 FILE *fp;
2084 size_t i;
2086 if(a_aux_err_head == NULL){
2087 fprintf(stderr, _("The error ring is empty\n"));
2088 goto jleave;
2091 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
2092 NULL){
2093 fprintf(stderr, _("tmpfile"));
2094 v = NULL;
2095 goto jleave;
2098 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
2099 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
2100 ++i, enp->ae_str.l, enp->ae_str.s);
2101 /* We don't know wether last string ended with NL; be simple */
2102 putc('\n', fp);
2104 page_or_print(fp, 0);
2105 Fclose(fp);
2107 /* FALLTHRU */
2109 jclear:
2110 a_aux_err_tail = NULL;
2111 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
2112 while((enp = a_aux_err_head) != NULL){
2113 a_aux_err_head = enp->ae_next;
2114 free(enp->ae_str.s);
2115 free(enp);
2117 goto jleave;
2119 #endif /* HAVE_ERRORS */
2121 #ifndef HAVE_DEBUG
2122 FL void *
2123 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2125 void *rv;
2126 NYD2_ENTER;
2128 if (s == 0)
2129 s = 1;
2130 if ((rv = malloc(s)) == NULL)
2131 n_panic(_("no memory"));
2132 NYD2_LEAVE;
2133 return rv;
2136 FL void *
2137 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2139 void *rv;
2140 NYD2_ENTER;
2142 if (s == 0)
2143 s = 1;
2144 if (v == NULL)
2145 rv = smalloc(s);
2146 else if ((rv = realloc(v, s)) == NULL)
2147 n_panic(_("no memory"));
2148 NYD2_LEAVE;
2149 return rv;
2152 FL void *
2153 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2155 void *rv;
2156 NYD2_ENTER;
2158 if (size == 0)
2159 size = 1;
2160 if ((rv = calloc(nmemb, size)) == NULL)
2161 n_panic(_("no memory"));
2162 NYD2_LEAVE;
2163 return rv;
2166 #else /* !HAVE_DEBUG */
2167 CTA(sizeof(char) == sizeof(ui8_t));
2169 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2170 # define _HOPE_SET(C) \
2171 do {\
2172 union mem_ptr __xl, __xu;\
2173 struct mem_chunk *__xc;\
2174 __xl.p_p = (C).p_p;\
2175 __xc = __xl.p_c - 1;\
2176 __xu.p_p = __xc;\
2177 (C).p_cp += 8;\
2178 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2179 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2180 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2181 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2182 __xu.p_ui8p += __xc->mc_size - 8;\
2183 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2184 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2185 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2186 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2187 } while (0)
2188 # define _HOPE_GET_TRACE(C,BAD) \
2189 do {\
2190 (C).p_cp += 8;\
2191 _HOPE_GET(C, BAD);\
2192 (C).p_cp += 8;\
2193 } while(0)
2194 # define _HOPE_GET(C,BAD) \
2195 do {\
2196 union mem_ptr __xl, __xu;\
2197 struct mem_chunk *__xc;\
2198 ui32_t __i;\
2199 __xl.p_p = (C).p_p;\
2200 __xl.p_cp -= 8;\
2201 (C).p_cp = __xl.p_cp;\
2202 __xc = __xl.p_c - 1;\
2203 (BAD) = FAL0;\
2204 __i = 0;\
2205 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2206 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2207 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2208 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2209 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2210 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2211 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2212 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2213 if (__i != 0) {\
2214 (BAD) = TRU1;\
2215 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2216 __xl.p_p, __i, mdbg_file, mdbg_line);\
2218 __xu.p_p = __xc;\
2219 __xu.p_ui8p += __xc->mc_size - 8;\
2220 __i = 0;\
2221 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2222 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2223 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2224 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2225 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2226 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2227 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2228 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2229 if (__i != 0) {\
2230 (BAD) = TRU1;\
2231 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2232 __xl.p_p, __i, mdbg_file, mdbg_line);\
2234 if (BAD)\
2235 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2236 __xc->mc_file, __xc->mc_line);\
2237 } while (0)
2239 FL void *
2240 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2242 union mem_ptr p;
2243 NYD2_ENTER;
2245 if (s == 0)
2246 s = 1;
2247 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2248 n_panic("smalloc(): allocation too large: %s, line %d",
2249 mdbg_file, mdbg_line);
2250 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2252 if ((p.p_p = (malloc)(s)) == NULL)
2253 n_panic(_("no memory"));
2254 p.p_c->mc_prev = NULL;
2255 if ((p.p_c->mc_next = _mem_list) != NULL)
2256 _mem_list->mc_prev = p.p_c;
2257 p.p_c->mc_file = mdbg_file;
2258 p.p_c->mc_line = (ui16_t)mdbg_line;
2259 p.p_c->mc_isfree = FAL0;
2260 p.p_c->mc_size = (ui32_t)s;
2262 _mem_list = p.p_c++;
2263 _HOPE_SET(p);
2265 ++_mem_aall;
2266 ++_mem_acur;
2267 _mem_amax = MAX(_mem_amax, _mem_acur);
2268 _mem_mall += s;
2269 _mem_mcur += s;
2270 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2271 NYD2_LEAVE;
2272 return p.p_p;
2275 FL void *
2276 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2278 union mem_ptr p;
2279 bool_t isbad;
2280 NYD2_ENTER;
2282 if ((p.p_p = v) == NULL) {
2283 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2284 goto jleave;
2287 _HOPE_GET(p, isbad);
2288 --p.p_c;
2289 if (p.p_c->mc_isfree) {
2290 n_err("srealloc(): region freed! At %s, line %d\n"
2291 "\tLast seen: %s, line %" PRIu16 "\n",
2292 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2293 goto jforce;
2296 if (p.p_c == _mem_list)
2297 _mem_list = p.p_c->mc_next;
2298 else
2299 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2300 if (p.p_c->mc_next != NULL)
2301 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2303 --_mem_acur;
2304 _mem_mcur -= p.p_c->mc_size;
2305 jforce:
2306 if (s == 0)
2307 s = 1;
2308 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2309 n_panic("srealloc(): allocation too large: %s, line %d",
2310 mdbg_file, mdbg_line);
2311 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2313 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2314 n_panic(_("no memory"));
2315 p.p_c->mc_prev = NULL;
2316 if ((p.p_c->mc_next = _mem_list) != NULL)
2317 _mem_list->mc_prev = p.p_c;
2318 p.p_c->mc_file = mdbg_file;
2319 p.p_c->mc_line = (ui16_t)mdbg_line;
2320 p.p_c->mc_isfree = FAL0;
2321 p.p_c->mc_size = (ui32_t)s;
2322 _mem_list = p.p_c++;
2323 _HOPE_SET(p);
2325 ++_mem_aall;
2326 ++_mem_acur;
2327 _mem_amax = MAX(_mem_amax, _mem_acur);
2328 _mem_mall += s;
2329 _mem_mcur += s;
2330 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2331 jleave:
2332 NYD2_LEAVE;
2333 return p.p_p;
2336 FL void *
2337 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2339 union mem_ptr p;
2340 NYD2_ENTER;
2342 if (size == 0)
2343 size = 1;
2344 if (nmemb == 0)
2345 nmemb = 1;
2346 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2347 n_panic("scalloc(): allocation size too large: %s, line %d",
2348 mdbg_file, mdbg_line);
2349 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2350 n_panic("scalloc(): allocation count too large: %s, line %d",
2351 mdbg_file, mdbg_line);
2353 size *= nmemb;
2354 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2356 if ((p.p_p = (malloc)(size)) == NULL)
2357 n_panic(_("no memory"));
2358 memset(p.p_p, 0, size);
2359 p.p_c->mc_prev = NULL;
2360 if ((p.p_c->mc_next = _mem_list) != NULL)
2361 _mem_list->mc_prev = p.p_c;
2362 p.p_c->mc_file = mdbg_file;
2363 p.p_c->mc_line = (ui16_t)mdbg_line;
2364 p.p_c->mc_isfree = FAL0;
2365 p.p_c->mc_size = (ui32_t)size;
2366 _mem_list = p.p_c++;
2367 _HOPE_SET(p);
2369 ++_mem_aall;
2370 ++_mem_acur;
2371 _mem_amax = MAX(_mem_amax, _mem_acur);
2372 _mem_mall += size;
2373 _mem_mcur += size;
2374 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2375 NYD2_LEAVE;
2376 return p.p_p;
2379 FL void
2380 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2382 union mem_ptr p;
2383 bool_t isbad;
2384 NYD2_ENTER;
2386 if ((p.p_p = v) == NULL) {
2387 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2388 goto jleave;
2391 _HOPE_GET(p, isbad);
2392 --p.p_c;
2393 if (p.p_c->mc_isfree) {
2394 n_err("sfree(): double-free avoided at %s, line %d\n"
2395 "\tLast seen: %s, line %" PRIu16 "\n",
2396 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2397 goto jleave;
2400 if (p.p_c == _mem_list)
2401 _mem_list = p.p_c->mc_next;
2402 else
2403 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2404 if (p.p_c->mc_next != NULL)
2405 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2406 p.p_c->mc_isfree = TRU1;
2407 /* Trash contents (also see [21c05f8]) */
2408 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2410 --_mem_acur;
2411 _mem_mcur -= p.p_c->mc_size;
2413 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2414 p.p_c->mc_next = _mem_free;
2415 _mem_free = p.p_c;
2416 } else
2417 (free)(p.p_c);
2418 jleave:
2419 NYD2_LEAVE;
2422 FL void
2423 smemreset(void)
2425 union mem_ptr p;
2426 size_t c = 0, s = 0;
2427 NYD_ENTER;
2429 smemcheck();
2431 for (p.p_c = _mem_free; p.p_c != NULL;) {
2432 void *vp = p.p_c;
2433 ++c;
2434 s += p.p_c->mc_size;
2435 p.p_c = p.p_c->mc_next;
2436 (free)(vp);
2438 _mem_free = NULL;
2440 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2441 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2442 NYD_LEAVE;
2445 FL int
2446 c_smemtrace(void *v)
2448 /* For _HOPE_GET() */
2449 char const * const mdbg_file = "smemtrace()";
2450 int const mdbg_line = -1;
2451 FILE *fp;
2452 union mem_ptr p, xp;
2453 bool_t isbad;
2454 size_t lines;
2455 NYD_ENTER;
2457 v = (void*)0x1;
2458 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2459 n_perr("tmpfile", 0);
2460 goto jleave;
2463 fprintf(fp, "Memory statistics:\n"
2464 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2465 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2466 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2468 fprintf(fp, "Currently allocated memory chunks:\n");
2469 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2470 ++lines, p.p_c = p.p_c->mc_next) {
2471 xp = p;
2472 ++xp.p_c;
2473 _HOPE_GET_TRACE(xp, isbad);
2474 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2475 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2476 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2477 p.p_c->mc_line);
2480 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2481 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2482 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2483 xp = p;
2484 ++xp.p_c;
2485 _HOPE_GET_TRACE(xp, isbad);
2486 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2487 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2488 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2489 p.p_c->mc_file, p.p_c->mc_line);
2493 page_or_print(fp, lines);
2494 Fclose(fp);
2495 v = NULL;
2496 jleave:
2497 NYD_LEAVE;
2498 return (v != NULL);
2501 # ifdef HAVE_DEVEL
2502 FL bool_t
2503 _smemcheck(char const *mdbg_file, int mdbg_line)
2505 union mem_ptr p, xp;
2506 bool_t anybad = FAL0, isbad;
2507 size_t lines;
2508 NYD_ENTER;
2510 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2511 ++lines, p.p_c = p.p_c->mc_next) {
2512 xp = p;
2513 ++xp.p_c;
2514 _HOPE_GET_TRACE(xp, isbad);
2515 if (isbad) {
2516 anybad = TRU1;
2517 n_err(
2518 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2519 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2520 p.p_c->mc_file, p.p_c->mc_line);
2524 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2525 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2526 xp = p;
2527 ++xp.p_c;
2528 _HOPE_GET_TRACE(xp, isbad);
2529 if (isbad) {
2530 anybad = TRU1;
2531 n_err(
2532 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2533 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2534 p.p_c->mc_file, p.p_c->mc_line);
2538 NYD_LEAVE;
2539 return anybad;
2541 # endif /* HAVE_DEVEL */
2543 # undef _HOPE_SIZE
2544 # undef _HOPE_SET
2545 # undef _HOPE_GET_TRACE
2546 # undef _HOPE_GET
2547 #endif /* HAVE_DEBUG */
2549 /* s-it-mode */