Typo (harmless): Message-Id: -> Message-ID:
[s-mailx.git] / auxlily.c
blob4b39708b519e68018f8e04a084a36c8a65255a17
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 shsp->shs_dat = vp;
312 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
313 if (!alnumchar(c) && c != '_')
314 break;
316 if (lc) {
317 if (c != '}') {
318 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
319 shsp->shs_value);
320 shsp->shs_len = strlen(shsp->shs_value);
321 shsp->shs_dat = shsp->shs_value;
322 if (shsp->shs_err != NULL)
323 *shsp->shs_err = TRU1;
324 goto junroll;
326 c = *++vp;
329 shsp->shs_len = i;
330 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
331 shsp->shs_len = strlen(shsp->shs_dat = cp);
333 if (c != '\0')
334 goto jrecurse;
336 /* That level made the great and completed encoding. Build result */
337 junroll:
338 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
339 i += np->shs_len;
340 tmp = np->shs_next;
341 np->shs_next = shsp;
342 shsp = np;
343 np = tmp;
346 cp = rv = salloc(i +1);
347 while (shsp != NULL) {
348 np = shsp;
349 shsp = shsp->shs_next;
350 memcpy(cp, np->shs_dat, np->shs_len);
351 cp += np->shs_len;
353 *cp = '\0';
355 jleave:
356 NYD2_LEAVE;
357 return rv;
358 jrecurse:
359 memset(&next, 0, sizeof next);
360 next.shs_next = shsp;
361 next.shs_value = vp;
362 next.shs_err = shsp->shs_err;
363 next.shs_bsesc = shsp->shs_bsesc;
364 rv = _sh_exp_var(&next);
365 goto jleave;
368 FL void
369 n_raise(int signo)
371 NYD2_ENTER;
372 kill(getpid(), signo);
373 NYD2_LEAVE;
376 FL sighandler_type
377 safe_signal(int signum, sighandler_type handler)
379 struct sigaction nact, oact;
380 sighandler_type rv;
381 NYD2_ENTER;
383 nact.sa_handler = handler;
384 sigemptyset(&nact.sa_mask);
385 nact.sa_flags = 0;
386 #ifdef SA_RESTART
387 nact.sa_flags |= SA_RESTART;
388 #endif
389 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
390 NYD2_LEAVE;
391 return rv;
394 FL void
395 hold_all_sigs(void)
397 NYD2_ENTER;
398 if (_alls_depth++ == 0) {
399 sigfillset(&_alls_nset);
400 sigdelset(&_alls_nset, SIGABRT);
401 #ifdef SIGBUS
402 sigdelset(&_alls_nset, SIGBUS);
403 #endif
404 sigdelset(&_alls_nset, SIGCHLD);
405 sigdelset(&_alls_nset, SIGFPE);
406 sigdelset(&_alls_nset, SIGILL);
407 sigdelset(&_alls_nset, SIGKILL);
408 sigdelset(&_alls_nset, SIGSEGV);
409 sigdelset(&_alls_nset, SIGSTOP);
410 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
412 NYD2_LEAVE;
415 FL void
416 rele_all_sigs(void)
418 NYD2_ENTER;
419 if (--_alls_depth == 0)
420 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
421 NYD2_LEAVE;
424 FL void
425 hold_sigs(void)
427 NYD2_ENTER;
428 if (_hold_sigdepth++ == 0) {
429 sigemptyset(&_hold_nset);
430 sigaddset(&_hold_nset, SIGHUP);
431 sigaddset(&_hold_nset, SIGINT);
432 sigaddset(&_hold_nset, SIGQUIT);
433 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
435 NYD2_LEAVE;
438 FL void
439 rele_sigs(void)
441 NYD2_ENTER;
442 if (--_hold_sigdepth == 0)
443 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
444 NYD2_LEAVE;
447 #ifdef HAVE_NYD
448 FL void
449 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
451 struct nyd_info *nip = _nyd_infos;
453 if (_nyd_curr != NELEM(_nyd_infos))
454 nip += _nyd_curr++;
455 else
456 _nyd_curr = 1;
457 nip->ni_file = file;
458 nip->ni_fun = fun;
459 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
460 nip->ni_level = ((act == 0) ? _nyd_level
461 : (act == 1) ? ++_nyd_level : _nyd_level--);
464 FL void
465 _nyd_oncrash(int signo)
467 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
468 struct sigaction xact;
469 sigset_t xset;
470 size_t i, fnl;
471 int fd;
472 struct nyd_info *nip;
474 LCTA(sizeof("./") -1 + sizeof(UAGENT) -1 + sizeof(".dat") < PATH_MAX);
476 xact.sa_handler = SIG_DFL;
477 sigemptyset(&xact.sa_mask);
478 xact.sa_flags = 0;
479 sigaction(signo, &xact, NULL);
481 i = strlen(tempdir);
482 fnl = sizeof(UAGENT) -1;
484 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
485 (cp = pathbuf)[0] = '.';
486 i = 1;
487 } else
488 memcpy(cp = pathbuf, tempdir, i);
489 cp[i++] = '/'; /* xxx pathsep */
490 memcpy(cp += i, UAGENT, fnl);
491 i += fnl;
492 memcpy(cp += fnl, ".dat", sizeof(".dat"));
493 fnl = i + sizeof(".dat") -1;
495 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
496 fd = STDERR_FILENO;
498 # undef _X
499 # define _X(X) (X), sizeof(X) -1
500 write(fd, _X("\n\nNYD: program dying due to signal "));
502 cp = s2ibuf + sizeof(s2ibuf) -1;
503 *cp = '\0';
504 i = signo;
505 do {
506 *--cp = "0123456789"[i % 10];
507 i /= 10;
508 } while (i != 0);
509 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
511 write(fd, _X(":\n"));
513 if (_nyd_infos[NELEM(_nyd_infos) - 1].ni_file != NULL)
514 for (i = _nyd_curr, nip = _nyd_infos + i; i < NELEM(_nyd_infos); ++i)
515 _nyd_print(fd, nip++);
516 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
517 _nyd_print(fd, nip++);
519 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
521 if (fd != STDERR_FILENO) {
522 write(STDERR_FILENO, _X("Crash NYD listing written to "));
523 write(STDERR_FILENO, pathbuf, fnl);
524 write(STDERR_FILENO, _X("\n"));
525 # undef _X
527 close(fd);
530 sigemptyset(&xset);
531 sigaddset(&xset, signo);
532 sigprocmask(SIG_UNBLOCK, &xset, NULL);
533 n_raise(signo);
534 for (;;)
535 _exit(EXIT_ERR);
537 #endif /* HAVE_NYD */
539 FL void
540 touch(struct message *mp)
542 NYD_ENTER;
543 mp->m_flag |= MTOUCH;
544 if (!(mp->m_flag & MREAD))
545 mp->m_flag |= MREAD | MSTATUS;
546 NYD_LEAVE;
549 FL bool_t
550 is_dir(char const *name)
552 struct stat sbuf;
553 bool_t rv;
554 NYD_ENTER;
556 for (rv = FAL0;;)
557 if (!stat(name, &sbuf)) {
558 rv = (S_ISDIR(sbuf.st_mode) != 0);
559 break;
560 } else if (errno != EINTR)
561 break;
562 NYD_LEAVE;
563 return rv;
566 FL int
567 argcount(char **argv)
569 char **ap;
570 NYD_ENTER;
572 for (ap = argv; *ap++ != NULL;)
574 NYD_LEAVE;
575 return (int)PTR2SIZE(ap - argv - 1);
578 FL int
579 screensize(void)
581 int s;
582 char *cp;
583 NYD_ENTER;
585 if ((cp = ok_vlook(screen)) == NULL || (s = atoi(cp)) <= 0)
586 s = scrnheight - 2; /* XXX no magics */
587 NYD_LEAVE;
588 return s;
591 FL char const *
592 get_pager(char const **env_addon)
594 char const *cp;
595 NYD_ENTER;
597 cp = ok_vlook(PAGER);
598 if (cp == NULL || *cp == '\0')
599 cp = XPAGER;
601 if (env_addon != NULL) {
602 *env_addon = NULL;
603 if (strstr(cp, "less") != NULL) {
604 if (!env_blook("LESS", TRU1))
605 *env_addon = "LESS=FRSXi";
606 } else if (strstr(cp, "lv") != NULL) {
607 if (!env_blook("LV", TRU1))
608 *env_addon = "LV=-c";
611 NYD_LEAVE;
612 return cp;
615 FL void
616 page_or_print(FILE *fp, size_t lines)
618 int c;
619 char const *cp;
620 NYD_ENTER;
622 fflush_rewind(fp);
624 if ((options & OPT_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL) {
625 char *eptr;
626 union {sl_i sli; size_t rows;} u;
628 u.sli = strtol(cp, &eptr, 0);
629 u.rows = (*cp != '\0' && *eptr == '\0')
630 ? (size_t)u.sli : (size_t)scrnheight;
632 if (u.rows > 0 && lines == 0) {
633 while ((c = getc(fp)) != EOF)
634 if (c == '\n' && ++lines >= u.rows)
635 break;
636 really_rewind(fp);
639 if (lines >= u.rows) {
640 run_command(get_pager(NULL), 0, fileno(fp), COMMAND_FD_PASS,
641 NULL, NULL, NULL, NULL);
642 goto jleave;
646 while ((c = getc(fp)) != EOF)
647 putchar(c);
648 jleave:
649 NYD_LEAVE;
652 FL enum protocol
653 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
655 struct stat st;
656 char const *cp;
657 char *np;
658 size_t sz;
659 enum protocol rv = PROTO_UNKNOWN;
660 NYD_ENTER;
662 temporary_protocol_ext = NULL;
664 if (name[0] == '%' && name[1] == ':')
665 name += 2;
666 for (cp = name; *cp && *cp != ':'; cp++)
667 if (!alnumchar(*cp))
668 goto jfile;
670 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
671 if (!strncmp(name, "pop3://", 7)) {
672 #ifdef HAVE_POP3
673 rv = PROTO_POP3;
674 #else
675 n_err(_("No POP3 support compiled in\n"));
676 #endif
677 } else if (!strncmp(name, "pop3s://", 8)) {
678 #if defined HAVE_POP3 && defined HAVE_SSL
679 rv = PROTO_POP3;
680 #else
681 # ifndef HAVE_POP3
682 n_err(_("No POP3 support compiled in\n"));
683 # endif
684 # ifndef HAVE_SSL
685 n_err(_("No SSL support compiled in\n"));
686 # endif
687 #endif
689 goto jleave;
692 /* TODO This is the de facto maildir code and thus belongs into there!
693 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
694 * TODO or (more likely) in addition to *newfolders*) */
695 jfile:
696 rv = PROTO_FILE;
697 np = ac_alloc((sz = strlen(name)) + 4 +1);
698 memcpy(np, name, sz + 1);
699 if (!stat(name, &st)) {
700 if (S_ISDIR(st.st_mode) &&
701 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
702 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
703 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
704 rv = PROTO_MAILDIR;
705 } else {
706 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
707 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
708 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
709 temporary_protocol_ext = cp;
710 else if ((cp = ok_vlook(newfolders)) != NULL && !strcmp(cp, "maildir"))
711 rv = PROTO_MAILDIR;
713 ac_free(np);
714 jleave:
715 NYD_LEAVE;
716 return rv;
719 FL ui32_t
720 torek_hash(char const *name)
722 /* Chris Torek's hash.
723 * NOTE: need to change *at least* create-okey-map.pl when changing the
724 * algorithm!! */
725 ui32_t h = 0;
726 NYD_ENTER;
728 while (*name != '\0') {
729 h *= 33;
730 h += *name++;
732 NYD_LEAVE;
733 return h;
736 FL unsigned
737 pjw(char const *cp) /* TODO obsolete that -> torek_hash */
739 unsigned h = 0, g;
740 NYD_ENTER;
742 cp--;
743 while (*++cp) {
744 h = (h << 4 & 0xffffffff) + (*cp&0377);
745 if ((g = h & 0xf0000000) != 0) {
746 h = h ^ g >> 24;
747 h = h ^ g;
750 NYD_LEAVE;
751 return h;
754 FL ui32_t
755 nextprime(ui32_t n)
757 static ui32_t const primes[] = {
758 5, 11, 23, 47, 97, 157, 283,
759 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
760 131071, 262139, 524287, 1048573, 2097143, 4194301,
761 8388593, 16777213, 33554393, 67108859, 134217689,
762 268435399, 536870909, 1073741789, 2147483647
765 ui32_t i, mprime;
766 NYD_ENTER;
768 i = (n < primes[NELEM(primes) / 4] ? 0
769 : (n < primes[NELEM(primes) / 2] ? NELEM(primes) / 4
770 : NELEM(primes) / 2));
772 if ((mprime = primes[i]) > n)
773 break;
774 while (++i < NELEM(primes));
775 if (i == NELEM(primes) && mprime < n)
776 mprime = n;
777 NYD_LEAVE;
778 return mprime;
781 FL char *
782 n_shell_expand_tilde(char const *s, bool_t *err_or_null)
784 struct passwd *pwp;
785 size_t nl, rl;
786 char const *rp, *np;
787 char *rv;
788 bool_t err;
789 NYD2_ENTER;
791 err = FAL0;
793 if (s[0] != '~')
794 goto jasis;
796 if (*(rp = s + 1) == '/' || *rp == '\0')
797 np = homedir;
798 else {
799 if ((rp = strchr(s + 1, '/')) == NULL)
800 rp = (np = UNCONST(s)) + 1;
801 else {
802 nl = PTR2SIZE(rp - s);
803 np = savestrbuf(s, nl);
806 if ((pwp = getpwnam(np)) == NULL) {
807 err = TRU1;
808 goto jasis;
810 np = pwp->pw_name;
813 nl = strlen(np);
814 rl = strlen(rp);
815 rv = salloc(nl + 1 + rl +1);
816 memcpy(rv, np, nl);
817 if (rl > 0) {
818 memcpy(rv + nl, rp, rl);
819 nl += rl;
821 rv[nl] = '\0';
822 goto jleave;
824 jasis:
825 rv = savestr(s);
826 jleave:
827 if (err_or_null != NULL)
828 *err_or_null = err;
829 NYD2_LEAVE;
830 return rv;
833 FL char *
834 n_shell_expand_var(char const *s, bool_t bsescape, bool_t *err_or_null)
836 struct shvar_stack top;
837 char *rv;
838 NYD2_ENTER;
840 memset(&top, 0, sizeof top);
842 top.shs_value = s;
843 if ((top.shs_err = err_or_null) != NULL)
844 *err_or_null = FAL0;
845 top.shs_bsesc = bsescape;
846 rv = _sh_exp_var(&top);
847 NYD2_LEAVE;
848 return rv;
851 FL int
852 n_shell_expand_escape(char const **s, bool_t use_nail_extensions)
854 char const *xs;
855 int c, n;
856 NYD2_ENTER;
858 xs = *s;
860 if ((c = *xs & 0xFF) == '\0')
861 goto jleave;
862 ++xs;
863 if (c != '\\')
864 goto jleave;
866 switch ((c = *xs & 0xFF)) {
867 case '\\': break;
868 case 'a': c = '\a'; break;
869 case 'b': c = '\b'; break;
870 case 'c': c = PROMPT_STOP; break;
871 case 'f': c = '\f'; break;
872 case 'n': c = '\n'; break;
873 case 'r': c = '\r'; break;
874 case 't': c = '\t'; break;
875 case 'v': c = '\v'; break;
876 case '0':
877 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
878 c <<= 3;
879 c |= *xs - '0';
881 goto jleave;
882 /* S-nail extension for nice (get)prompt(()) support */
883 case '&':
884 case '?':
885 case '$':
886 case '@':
887 if (use_nail_extensions) {
888 switch (c) {
889 case '&': c = ok_blook(bsdcompat) ? '&' : '?'; break;
890 case '?': c = (pstate & PS_EVAL_ERROR) ? '1' : '0'; break;
891 case '$': c = PROMPT_DOLLAR; break;
892 case '@': c = PROMPT_AT; break;
894 break;
896 /* FALLTHRU */
897 case '\0':
898 /* A sole <backslash> at EOS is treated as-is! */
899 /* FALLTHRU */
900 default:
901 c = '\\';
902 goto jleave;
904 ++xs;
905 jleave:
906 *s = xs;
907 NYD2_LEAVE;
908 return c;
911 FL char *
912 getprompt(void) /* TODO evaluate only as necessary (needs a bit) */
914 static char buf[PROMPT_BUFFER_SIZE];
916 char *cp;
917 char const *ccp_base, *ccp;
918 size_t NATCH_CHAR( cclen_base COMMA cclen COMMA ) maxlen, dfmaxlen;
919 bool_t trigger; /* 1.: `errors' ring note? 2.: first loop tick done */
920 NYD_ENTER;
922 /* No other place to place this */
923 #ifdef HAVE_ERRORS
924 if (options & OPT_INTERACTIVE) {
925 if (!(pstate & PS_ERRORS_NOTED) && a_aux_err_head != NULL) {
926 pstate |= PS_ERRORS_NOTED;
927 fprintf(stderr, _("There are new messages in the error message ring "
928 "(denoted by \"#ERR#\")\n"
929 " The `errors' command manages this message ring\n"));
932 if ((trigger = (a_aux_err_cnt_noted != a_aux_err_cnt)))
933 a_aux_err_cnt_noted = a_aux_err_cnt;
934 } else
935 trigger = FAL0;
936 #endif
938 cp = buf;
939 if ((ccp_base = ok_vlook(prompt)) == NULL || *ccp_base == '\0') {
940 #ifdef HAVE_ERRORS
941 if (trigger)
942 ccp_base = "";
943 else
944 #endif
945 goto jleave;
947 #ifdef HAVE_ERRORS
948 if (trigger)
949 ccp_base = savecatsep(_("#ERR#"), '\0', ccp_base);
950 #endif
951 NATCH_CHAR( cclen_base = strlen(ccp_base); )
953 dfmaxlen = 0; /* keep CC happy */
954 trigger = FAL0;
955 jredo:
956 ccp = ccp_base;
957 NATCH_CHAR( cclen = cclen_base; )
958 maxlen = sizeof(buf) -1;
960 for (;;) {
961 size_t l;
962 int c;
964 if (maxlen == 0)
965 goto jleave;
966 #ifdef HAVE_NATCH_CHAR
967 c = mblen(ccp, cclen); /* TODO use mbrtowc() */
968 if (c <= 0) {
969 mblen(NULL, 0);
970 if (c < 0) {
971 *buf = '?';
972 cp = buf + 1;
973 goto jleave;
975 break;
976 } else if ((l = c) > 1) {
977 if (trigger) {
978 memcpy(cp, ccp, l);
979 cp += l;
981 ccp += l;
982 maxlen -= l;
983 continue;
984 } else
985 #endif
986 if ((c = n_shell_expand_escape(&ccp, TRU1)) > 0) {
987 if (trigger)
988 *cp++ = (char)c;
989 --maxlen;
990 continue;
992 if (c == 0 || c == PROMPT_STOP)
993 break;
995 if (trigger) {
996 char const *a = (c == PROMPT_DOLLAR) ? account_name : displayname;
997 if (a == NULL)
998 a = "";
999 if ((l = field_put_bidi_clip(cp, dfmaxlen, a, strlen(a))) > 0) {
1000 cp += l;
1001 maxlen -= l;
1002 dfmaxlen -= l;
1007 if (!trigger) {
1008 trigger = TRU1;
1009 dfmaxlen = maxlen;
1010 goto jredo;
1012 jleave:
1013 *cp = '\0';
1014 NYD_LEAVE;
1015 return buf;
1018 FL char *
1019 nodename(int mayoverride)
1021 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1023 struct utsname ut;
1024 char *hn;
1025 #ifdef HAVE_SOCKETS
1026 # ifdef HAVE_GETADDRINFO
1027 struct addrinfo hints, *res;
1028 # else
1029 struct hostent *hent;
1030 # endif
1031 #endif
1032 NYD_ENTER;
1034 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
1036 } else if ((hn = sys_hostname) == NULL) {
1037 uname(&ut);
1038 hn = ut.nodename;
1039 #ifdef HAVE_SOCKETS
1040 # ifdef HAVE_GETADDRINFO
1041 memset(&hints, 0, sizeof hints);
1042 hints.ai_family = AF_UNSPEC;
1043 hints.ai_flags = AI_CANONNAME;
1044 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
1045 if (res->ai_canonname != NULL) {
1046 size_t l = strlen(res->ai_canonname) +1;
1048 hn = ac_alloc(l);
1049 memcpy(hn, res->ai_canonname, l);
1051 freeaddrinfo(res);
1053 # else
1054 hent = gethostbyname(hn);
1055 if (hent != NULL)
1056 hn = hent->h_name;
1057 # endif
1058 #endif
1059 sys_hostname = sstrdup(hn);
1060 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
1061 if (hn != ut.nodename)
1062 ac_free(hn);
1063 #endif
1064 hn = sys_hostname;
1067 if (hostname != NULL && hostname != sys_hostname)
1068 free(hostname);
1069 hostname = sstrdup(hn);
1070 NYD_LEAVE;
1071 return hostname;
1074 FL char *
1075 getrandstring(size_t length)
1077 struct str b64;
1078 char *data;
1079 size_t i;
1080 NYD_ENTER;
1082 #ifndef HAVE_POSIX_RANDOM
1083 if (_rand == NULL)
1084 _rand_init();
1085 #endif
1087 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1088 * with PAD stripped is still longer than what the user requests, easy way */
1089 data = ac_alloc(i = length + 3);
1091 #ifndef HAVE_POSIX_RANDOM
1092 while (i-- > 0)
1093 data[i] = (char)_rand_get8();
1094 #else
1095 { char *cp = data;
1097 while (i > 0) {
1098 union {ui32_t i4; char c[4];} r;
1099 size_t j;
1101 r.i4 = (ui32_t)arc4random();
1102 switch ((j = i & 3)) {
1103 case 0: cp[3] = r.c[3]; j = 4;
1104 case 3: cp[2] = r.c[2];
1105 case 2: cp[1] = r.c[1];
1106 default: cp[0] = r.c[0]; break;
1108 cp += j;
1109 i -= j;
1112 #endif
1114 b64_encode_buf(&b64, data, length + 3,
1115 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
1116 ac_free(data);
1118 assert(b64.l >= length);
1119 b64.s[length] = '\0';
1120 NYD_LEAVE;
1121 return b64.s;
1124 FL enum okay
1125 makedir(char const *name)
1127 struct stat st;
1128 enum okay rv = STOP;
1129 NYD_ENTER;
1131 if (!mkdir(name, 0700))
1132 rv = OKAY;
1133 else {
1134 int e = errno;
1135 if ((e == EEXIST || e == ENOSYS) && !stat(name, &st) &&
1136 S_ISDIR(st.st_mode))
1137 rv = OKAY;
1139 NYD_LEAVE;
1140 return rv;
1143 #ifdef HAVE_FCHDIR
1144 FL enum okay
1145 cwget(struct cw *cw)
1147 enum okay rv = STOP;
1148 NYD_ENTER;
1150 if ((cw->cw_fd = open(".", O_RDONLY)) == -1)
1151 goto jleave;
1152 if (fchdir(cw->cw_fd) == -1) {
1153 close(cw->cw_fd);
1154 goto jleave;
1156 rv = OKAY;
1157 jleave:
1158 NYD_LEAVE;
1159 return rv;
1162 FL enum okay
1163 cwret(struct cw *cw)
1165 enum okay rv = STOP;
1166 NYD_ENTER;
1168 if (!fchdir(cw->cw_fd))
1169 rv = OKAY;
1170 NYD_LEAVE;
1171 return rv;
1174 FL void
1175 cwrelse(struct cw *cw)
1177 NYD_ENTER;
1178 close(cw->cw_fd);
1179 NYD_LEAVE;
1182 #else /* !HAVE_FCHDIR */
1183 FL enum okay
1184 cwget(struct cw *cw)
1186 enum okay rv = STOP;
1187 NYD_ENTER;
1189 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) != NULL && !chdir(cw->cw_wd))
1190 rv = OKAY;
1191 NYD_LEAVE;
1192 return rv;
1195 FL enum okay
1196 cwret(struct cw *cw)
1198 enum okay rv = STOP;
1199 NYD_ENTER;
1201 if (!chdir(cw->cw_wd))
1202 rv = OKAY;
1203 NYD_LEAVE;
1204 return rv;
1207 FL void
1208 cwrelse(struct cw *cw)
1210 NYD_ENTER;
1211 UNUSED(cw);
1212 NYD_LEAVE;
1214 #endif /* !HAVE_FCHDIR */
1216 FL size_t
1217 field_detect_clip(size_t maxlen, char const *buf, size_t blen)/*TODO mbrtowc()*/
1219 size_t rv;
1220 NYD_ENTER;
1222 #ifdef HAVE_NATCH_CHAR
1223 maxlen = MIN(maxlen, blen);
1224 for (rv = 0; maxlen > 0;) {
1225 int ml = mblen(buf, maxlen);
1226 if (ml <= 0) {
1227 mblen(NULL, 0);
1228 break;
1230 buf += ml;
1231 rv += ml;
1232 maxlen -= ml;
1234 #else
1235 rv = MIN(blen, maxlen);
1236 #endif
1237 NYD_LEAVE;
1238 return rv;
1241 FL size_t
1242 field_put_bidi_clip(char *store, size_t maxlen, char const *buf, size_t blen)
1244 NATCH_CHAR( struct bidi_info bi; )
1245 size_t rv NATCH_CHAR( COMMA i );
1246 NYD_ENTER;
1248 rv = 0;
1249 if (maxlen-- == 0)
1250 goto j_leave;
1252 #ifdef HAVE_NATCH_CHAR
1253 bidi_info_create(&bi);
1254 if (bi.bi_start.l == 0 || !bidi_info_needed(buf, blen)) {
1255 bi.bi_end.l = 0;
1256 goto jnobidi;
1259 if (maxlen >= (i = bi.bi_pad + bi.bi_end.l + bi.bi_start.l))
1260 maxlen -= i;
1261 else
1262 goto jleave;
1264 if ((i = bi.bi_start.l) > 0) {
1265 memcpy(store, bi.bi_start.s, i);
1266 store += i;
1267 rv += i;
1270 jnobidi:
1271 while (maxlen > 0) {
1272 int ml = mblen(buf, blen);
1273 if (ml <= 0) {
1274 mblen(NULL, 0);
1275 break;
1277 if (UICMP(z, maxlen, <, ml))
1278 break;
1279 if (ml == 1)
1280 *store = *buf;
1281 else
1282 memcpy(store, buf, ml);
1283 store += ml;
1284 buf += ml;
1285 rv += ml;
1286 maxlen -= ml;
1289 if ((i = bi.bi_end.l) > 0) {
1290 memcpy(store, bi.bi_end.s, i);
1291 store += i;
1292 rv += i;
1294 jleave:
1295 *store = '\0';
1297 #else
1298 rv = MIN(blen, maxlen);
1299 memcpy(store, buf, rv);
1300 store[rv] = '\0';
1301 #endif
1302 j_leave:
1303 NYD_LEAVE;
1304 return rv;
1307 FL char *
1308 colalign(char const *cp, int col, int fill, int *cols_decr_used_or_null)
1310 NATCH_CHAR( struct bidi_info bi; )
1311 int col_orig = col, n, sz;
1312 bool_t isbidi, isuni, istab, isrepl;
1313 char *nb, *np;
1314 NYD_ENTER;
1316 /* Bidi only on request and when there is 8-bit data */
1317 isbidi = isuni = FAL0;
1318 #ifdef HAVE_NATCH_CHAR
1319 isuni = ((options & OPT_UNICODE) != 0);
1320 bidi_info_create(&bi);
1321 if (bi.bi_start.l == 0)
1322 goto jnobidi;
1323 if (!(isbidi = bidi_info_needed(cp, strlen(cp))))
1324 goto jnobidi;
1326 if ((size_t)col >= bi.bi_pad)
1327 col -= bi.bi_pad;
1328 else
1329 col = 0;
1330 jnobidi:
1331 #endif
1333 np = nb = salloc(mb_cur_max * strlen(cp) +
1334 ((fill ? col : 0)
1335 NATCH_CHAR( + (isbidi ? bi.bi_start.l + bi.bi_end.l : 0) )
1336 +1));
1338 #ifdef HAVE_NATCH_CHAR
1339 if (isbidi) {
1340 memcpy(np, bi.bi_start.s, bi.bi_start.l);
1341 np += bi.bi_start.l;
1343 #endif
1345 while (*cp != '\0') {
1346 istab = FAL0;
1347 #ifdef HAVE_C90AMEND1
1348 if (mb_cur_max > 1) {
1349 wchar_t wc;
1351 n = 1;
1352 isrepl = TRU1;
1353 if ((sz = mbtowc(&wc, cp, mb_cur_max)) == -1)
1354 sz = 1;
1355 else if (wc == L'\t') {
1356 cp += sz - 1; /* Silly, no such charset known (.. until S-Ctext) */
1357 isrepl = FAL0;
1358 istab = TRU1;
1359 } else if (iswprint(wc)) {
1360 # ifndef HAVE_WCWIDTH
1361 n = 1 + (wc >= 0x1100u); /* TODO use S-CText isfullwidth() */
1362 # else
1363 if ((n = wcwidth(wc)) == -1)
1364 n = 1;
1365 else
1366 # endif
1367 isrepl = FAL0;
1369 } else
1370 #endif
1372 n = sz = 1;
1373 istab = (*cp == '\t');
1374 isrepl = !(istab || isprint((uc_i)*cp));
1377 if (n > col)
1378 break;
1379 col -= n;
1381 if (isrepl) {
1382 if (isuni) {
1383 np[0] = (char)0xEFu;
1384 np[1] = (char)0xBFu;
1385 np[2] = (char)0xBDu;
1386 np += 3;
1387 } else
1388 *np++ = '?';
1389 cp += sz;
1390 } else if (istab || (sz == 1 && spacechar(*cp))) {
1391 *np++ = ' ';
1392 ++cp;
1393 } else
1394 while (sz--)
1395 *np++ = *cp++;
1398 if (fill && col != 0) {
1399 if (fill > 0) {
1400 memmove(nb + col, nb, PTR2SIZE(np - nb));
1401 memset(nb, ' ', col);
1402 } else
1403 memset(np, ' ', col);
1404 np += col;
1405 col = 0;
1408 #ifdef HAVE_NATCH_CHAR
1409 if (isbidi) {
1410 memcpy(np, bi.bi_end.s, bi.bi_end.l);
1411 np += bi.bi_end.l;
1413 #endif
1415 *np = '\0';
1416 if (cols_decr_used_or_null != NULL)
1417 *cols_decr_used_or_null -= col_orig - col;
1418 NYD_LEAVE;
1419 return nb;
1422 FL void
1423 makeprint(struct str const *in, struct str *out)
1425 char const *inp, *maxp;
1426 char *outp;
1427 DBG( size_t msz; )
1428 NYD_ENTER;
1430 out->s = outp = smalloc(DBG( msz = ) in->l*mb_cur_max + 2u*mb_cur_max +1);
1431 inp = in->s;
1432 maxp = inp + in->l;
1434 #ifdef HAVE_NATCH_CHAR
1435 if (mb_cur_max > 1) {
1436 char mbb[MB_LEN_MAX + 1];
1437 wchar_t wc;
1438 int i, n;
1439 bool_t isuni = ((options & OPT_UNICODE) != 0);
1441 out->l = 0;
1442 while (inp < maxp) {
1443 if (*inp & 0200)
1444 n = mbtowc(&wc, inp, PTR2SIZE(maxp - inp));
1445 else {
1446 wc = *inp;
1447 n = 1;
1449 if (n == -1) {
1450 /* FIXME Why mbtowc() resetting here?
1451 * FIXME what about ISO 2022-JP plus -- those
1452 * FIXME will loose shifts, then!
1453 * FIXME THUS - we'd need special "known points"
1454 * FIXME to do so - say, after a newline!!
1455 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
1456 mbtowc(&wc, NULL, mb_cur_max);
1457 wc = isuni ? 0xFFFD : '?';
1458 n = 1;
1459 } else if (n == 0)
1460 n = 1;
1461 inp += n;
1462 if (!iswprint(wc) && wc != '\n' && wc != '\r' && wc != '\b' &&
1463 wc != '\t') {
1464 if ((wc & ~(wchar_t)037) == 0)
1465 wc = isuni ? 0x2400 | wc : '?';
1466 else if (wc == 0177)
1467 wc = isuni ? 0x2421 : '?';
1468 else
1469 wc = isuni ? 0x2426 : '?';
1470 }else if(isuni){ /* TODO ctext */
1471 /* We need to actively filter out L-TO-R and R-TO-R marks TODO ctext */
1472 if(wc == 0x200E || wc == 0x200F || (wc >= 0x202A && wc <= 0x202E))
1473 continue;
1474 /* And some zero-width messes */
1475 if(wc == 0x00AD || (wc >= 0x200B && wc <= 0x200D))
1476 continue;
1477 /* Oh about the ISO C wide character interfaces, baby! */
1478 if(wc == 0xFEFF)
1479 continue;
1481 if ((n = wctomb(mbb, wc)) <= 0)
1482 continue;
1483 out->l += n;
1484 assert(out->l < msz);
1485 for (i = 0; i < n; ++i)
1486 *outp++ = mbb[i];
1488 } else
1489 #endif /* NATCH_CHAR */
1491 int c;
1492 while (inp < maxp) {
1493 c = *inp++ & 0377;
1494 if (!isprint(c) && c != '\n' && c != '\r' && c != '\b' && c != '\t')
1495 c = '?';
1496 *outp++ = c;
1498 out->l = in->l;
1500 out->s[out->l] = '\0';
1501 NYD_LEAVE;
1504 FL size_t
1505 delctrl(char *cp, size_t len)
1507 size_t x, y;
1508 NYD_ENTER;
1510 for (x = y = 0; x < len; ++x)
1511 if (!cntrlchar(cp[x]))
1512 cp[y++] = cp[x];
1513 cp[y] = '\0';
1514 NYD_LEAVE;
1515 return y;
1518 FL char *
1519 prstr(char const *s)
1521 struct str in, out;
1522 char *rp;
1523 NYD_ENTER;
1525 in.s = UNCONST(s);
1526 in.l = strlen(s);
1527 makeprint(&in, &out);
1528 rp = savestrbuf(out.s, out.l);
1529 free(out.s);
1530 NYD_LEAVE;
1531 return rp;
1534 FL int
1535 prout(char const *s, size_t sz, FILE *fp)
1537 struct str in, out;
1538 int n;
1539 NYD_ENTER;
1541 in.s = UNCONST(s);
1542 in.l = sz;
1543 makeprint(&in, &out);
1544 n = fwrite(out.s, 1, out.l, fp);
1545 free(out.s);
1546 NYD_LEAVE;
1547 return n;
1550 FL size_t
1551 putuc(int u, int c, FILE *fp)
1553 size_t rv;
1554 NYD_ENTER;
1555 UNUSED(u);
1557 #ifdef HAVE_NATCH_CHAR
1558 if ((options & OPT_UNICODE) && (u & ~(wchar_t)0177)) {
1559 char mbb[MB_LEN_MAX];
1560 int i, n;
1562 if ((n = wctomb(mbb, u)) > 0) {
1563 rv = wcwidth(u);
1564 for (i = 0; i < n; ++i)
1565 if (putc(mbb[i] & 0377, fp) == EOF) {
1566 rv = 0;
1567 break;
1569 } else if (n == 0)
1570 rv = (putc('\0', fp) != EOF);
1571 else
1572 rv = 0;
1573 } else
1574 #endif
1575 rv = (putc(c, fp) != EOF);
1576 NYD_LEAVE;
1577 return rv;
1580 FL bool_t
1581 bidi_info_needed(char const *bdat, size_t blen)
1583 bool_t rv = FAL0;
1584 NYD_ENTER;
1586 #ifdef HAVE_NATCH_CHAR
1587 if (options & OPT_UNICODE)
1588 while (blen > 0) {
1589 /* TODO Checking for BIDI character: use S-CText fromutf8
1590 * TODO plus isrighttoleft (or whatever there will be)! */
1591 ui32_t c = n_utf8_to_utf32(&bdat, &blen);
1592 if (c == UI32_MAX)
1593 break;
1595 if (c <= 0x05BE)
1596 continue;
1598 /* (Very very fuzzy, awaiting S-CText for good) */
1599 if ((c >= 0x05BE && c <= 0x08E3) ||
1600 (c >= 0xFB1D && c <= 0xFE00) /* No: variation selectors */ ||
1601 (c >= 0xFE70 && c <= 0xFEFC) ||
1602 (c >= 0x10800 && c <= 0x10C48) ||
1603 (c >= 0x1EE00 && c <= 0x1EEF1)) {
1604 rv = TRU1;
1605 break;
1608 #endif /* HAVE_NATCH_CHAR */
1609 NYD_LEAVE;
1610 return rv;
1613 FL void
1614 bidi_info_create(struct bidi_info *bip)
1616 /* Unicode: how to isolate RIGHT-TO-LEFT scripts via *headline-bidi*
1617 * 1.1 (Jun 1993): U+200E (E2 80 8E) LEFT-TO-RIGHT MARK
1618 * 6.3 (Sep 2013): U+2068 (E2 81 A8) FIRST STRONG ISOLATE,
1619 * U+2069 (E2 81 A9) POP DIRECTIONAL ISOLATE
1620 * Worse results seen for: U+202D "\xE2\x80\xAD" U+202C "\xE2\x80\xAC" */
1621 NATCH_CHAR( char const *hb; )
1622 NYD_ENTER;
1624 memset(bip, 0, sizeof *bip);
1625 bip->bi_start.s = bip->bi_end.s = UNCONST("");
1627 #ifdef HAVE_NATCH_CHAR
1628 if ((options & OPT_UNICODE) && (hb = ok_vlook(headline_bidi)) != NULL) {
1629 switch (*hb) {
1630 case '3':
1631 bip->bi_pad = 2;
1632 /* FALLTHRU */
1633 case '2':
1634 bip->bi_start.s = bip->bi_end.s = UNCONST("\xE2\x80\x8E");
1635 break;
1636 case '1':
1637 bip->bi_pad = 2;
1638 /* FALLTHRU */
1639 default:
1640 bip->bi_start.s = UNCONST("\xE2\x81\xA8");
1641 bip->bi_end.s = UNCONST("\xE2\x81\xA9");
1642 break;
1644 bip->bi_start.l = bip->bi_end.l = 3;
1646 #endif
1647 NYD_LEAVE;
1650 FL si8_t
1651 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
1653 char *dat, *eptr;
1654 sl_i sli;
1655 si8_t rv;
1656 NYD_ENTER;
1658 assert(inlen == 0 || inbuf != NULL);
1660 if (inlen == UIZ_MAX)
1661 inlen = strlen(inbuf);
1663 if (inlen == 0)
1664 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1665 else {
1666 if ((inlen == 1 && *inbuf == '1') ||
1667 !ascncasecmp(inbuf, "true", inlen) ||
1668 !ascncasecmp(inbuf, "yes", inlen) ||
1669 !ascncasecmp(inbuf, "on", inlen))
1670 rv = 1;
1671 else if ((inlen == 1 && *inbuf == '0') ||
1672 !ascncasecmp(inbuf, "false", inlen) ||
1673 !ascncasecmp(inbuf, "no", inlen) ||
1674 !ascncasecmp(inbuf, "off", inlen))
1675 rv = 0;
1676 else {
1677 dat = ac_alloc(inlen +1);
1678 memcpy(dat, inbuf, inlen);
1679 dat[inlen] = '\0';
1681 sli = strtol(dat, &eptr, 0);
1682 if (*dat != '\0' && *eptr == '\0')
1683 rv = (sli != 0);
1684 else
1685 rv = -1;
1687 ac_free(dat);
1690 NYD_LEAVE;
1691 return rv;
1694 FL si8_t
1695 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
1697 si8_t rv;
1698 NYD_ENTER;
1700 assert(inlen == 0 || inbuf != NULL);
1702 if (inlen == UIZ_MAX)
1703 inlen = strlen(inbuf);
1705 if (inlen == 0)
1706 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
1707 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
1708 !ascncasecmp(inbuf, "ask-", 4) &&
1709 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
1710 (options & OPT_INTERACTIVE))
1711 rv = getapproval(prompt, rv);
1712 NYD_LEAVE;
1713 return rv;
1716 FL time_t
1717 n_time_epoch(void)
1719 #ifdef HAVE_CLOCK_GETTIME
1720 struct timespec ts;
1721 #elif defined HAVE_GETTIMEOFDAY
1722 struct timeval ts;
1723 #endif
1724 time_t rv;
1725 NYD2_ENTER;
1727 #ifdef HAVE_CLOCK_GETTIME
1728 clock_gettime(CLOCK_REALTIME, &ts);
1729 rv = (time_t)ts.tv_sec;
1730 #elif defined HAVE_GETTIMEOFDAY
1731 gettimeofday(&ts, NULL);
1732 rv = (time_t)ts.tv_sec;
1733 #else
1734 rv = time(NULL);
1735 #endif
1736 NYD2_LEAVE;
1737 return rv;
1740 FL void
1741 time_current_update(struct time_current *tc, bool_t full_update)
1743 NYD_ENTER;
1744 tc->tc_time = n_time_epoch();
1745 if (full_update) {
1746 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
1747 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
1748 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
1750 NYD_LEAVE;
1753 FL uiz_t
1754 n_msleep(uiz_t millis, bool_t ignint){
1755 uiz_t rv;
1756 NYD2_ENTER;
1758 #ifdef HAVE_NANOSLEEP
1759 /* C99 */{
1760 struct timespec ts, trem;
1761 int i;
1763 ts.tv_sec = millis / 1000;
1764 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1766 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1767 ts = trem;
1768 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1771 #elif defined HAVE_SLEEP
1772 if((millis /= 1000) == 0)
1773 millis = 1;
1774 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1775 millis = rv;
1776 #else
1777 # error Configuration should have detected a function for sleeping.
1778 #endif
1780 NYD2_LEAVE;
1781 return rv;
1784 FL void
1785 n_err(char const *format, ...){
1786 va_list ap;
1787 NYD2_ENTER;
1789 va_start(ap, format);
1790 #ifdef HAVE_ERRORS
1791 if(options & OPT_INTERACTIVE)
1792 n_verr(format, ap);
1793 else
1794 #endif
1796 if(a_aux_err_dirty++ == 0)
1797 fputs(UAGENT ": ", stderr);
1798 vfprintf(stderr, format, ap);
1799 if(strchr(format, '\n') != NULL){ /* TODO */
1800 a_aux_err_dirty = 0;
1801 fflush(stderr);
1804 va_end(ap);
1805 NYD2_LEAVE;
1808 FL void
1809 n_verr(char const *format, va_list ap){
1810 /* Check use cases of PS_ERRORS_NOTED, too! */
1811 #ifdef HAVE_ERRORS
1812 char buf[LINESIZE], *xbuf;
1813 int lmax, l;
1814 struct a_aux_err_node *enp;
1816 LCTA(ERRORS_MAX > 3);
1817 #endif
1818 NYD2_ENTER;
1820 if(a_aux_err_dirty++ == 0)
1821 fputs(UAGENT ": ", stderr);
1823 #ifdef HAVE_ERRORS
1824 if(!(options & OPT_INTERACTIVE))
1825 #endif
1827 vfprintf(stderr, format, ap);
1828 goto jleave;
1831 #ifdef HAVE_ERRORS
1832 xbuf = buf;
1833 lmax = sizeof buf;
1834 jredo:
1835 l = vsnprintf(xbuf, lmax, format, ap);
1836 if (l <= 0)
1837 goto jleave;
1838 if (UICMP(z, l, >=, lmax)) {
1839 /* FIXME Cannot reuse va_list
1840 lmax = ++l;
1841 xbuf = srealloc((xbuf == buf ? NULL : xbuf), lmax);
1842 goto jredo;
1846 fwrite(xbuf, 1, l, stderr);
1848 /* Link it into the `errors' message ring */
1849 if((enp = a_aux_err_tail) == NULL){
1850 jcreat:
1851 enp = scalloc(1, sizeof *enp);
1852 if(a_aux_err_tail != NULL)
1853 a_aux_err_tail->ae_next = enp;
1854 else
1855 a_aux_err_head = enp;
1856 a_aux_err_tail = enp;
1857 ++a_aux_err_cnt;
1858 }else if(enp->ae_str.l > 0 && enp->ae_str.s[enp->ae_str.l - 1] == '\n'){
1859 if(a_aux_err_cnt < ERRORS_MAX)
1860 goto jcreat;
1862 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1863 a_aux_err_tail->ae_next = enp;
1864 a_aux_err_tail = enp;
1865 free(enp->ae_str.s);
1866 memset(enp, 0, sizeof *enp);
1869 n_str_add_buf(&enp->ae_str, xbuf, l);
1871 if(xbuf != buf)
1872 free(xbuf);
1873 #endif /* HAVE_ERRORS */
1875 jleave:
1876 /* If the format ends with newline, be clean again */
1877 /* C99 */{
1878 size_t i = strlen(format);
1880 if(i > 0 && format[i - 1] == '\n'){
1881 fflush(stderr);
1882 a_aux_err_dirty = 0;
1885 NYD2_LEAVE;
1888 FL void
1889 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1890 va_list ap;
1891 NYD_X;
1893 va_start(ap, format);
1894 vfprintf(stderr, format, ap);
1895 va_end(ap);
1896 fflush(stderr);
1899 FL void
1900 n_perr(char const *msg, int errval){
1901 char const *fmt;
1902 NYD2_ENTER;
1904 if(msg == NULL){
1905 fmt = "%s%s\n";
1906 msg = "";
1907 }else
1908 fmt = "%s: %s\n";
1910 if(errval == 0)
1911 errval = errno;
1913 n_err(fmt, msg, strerror(errval));
1914 NYD2_LEAVE;
1917 FL void
1918 n_alert(char const *format, ...){
1919 va_list ap;
1920 NYD2_ENTER;
1922 n_err(a_aux_err_dirty > 0 ? _("\nAlert: ") : _("Alert: "));
1924 va_start(ap, format);
1925 n_verr(format, ap);
1926 va_end(ap);
1928 n_err("\n");
1929 NYD2_LEAVE;
1932 FL void
1933 n_panic(char const *format, ...){
1934 va_list ap;
1935 NYD2_ENTER;
1937 if(a_aux_err_dirty > 0){
1938 putc('\n', stderr);
1939 a_aux_err_dirty = 0;
1941 fprintf(stderr, UAGENT ": Panic: ");
1943 va_start(ap, format);
1944 vfprintf(stderr, format, ap);
1945 va_end(ap);
1947 putc('\n', stderr);
1948 fflush(stderr);
1949 NYD2_LEAVE;
1950 abort(); /* Was exit(EXIT_ERR); for a while, but no */
1953 #ifdef HAVE_ERRORS
1954 FL int
1955 c_errors(void *v){
1956 char **argv = v;
1957 struct a_aux_err_node *enp;
1958 NYD_ENTER;
1960 if(*argv == NULL)
1961 goto jlist;
1962 if(argv[1] != NULL)
1963 goto jerr;
1964 if(!asccasecmp(*argv, "show"))
1965 goto jlist;
1966 if(!asccasecmp(*argv, "clear"))
1967 goto jclear;
1968 jerr:
1969 fprintf(stderr, _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1970 v = NULL;
1971 jleave:
1972 NYD_LEAVE;
1973 return v == NULL ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1975 jlist:{
1976 FILE *fp;
1977 size_t i;
1979 if(a_aux_err_head == NULL){
1980 fprintf(stderr, _("The error ring is empty\n"));
1981 goto jleave;
1984 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1985 NULL){
1986 fprintf(stderr, _("tmpfile"));
1987 v = NULL;
1988 goto jleave;
1991 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1992 fprintf(fp, "- %4" PRIuZ ". %" PRIuZ " bytes: %s",
1993 ++i, enp->ae_str.l, enp->ae_str.s);
1994 /* We don't know wether last string ended with NL; be simple */
1995 putc('\n', fp);
1997 page_or_print(fp, 0);
1998 Fclose(fp);
2000 /* FALLTHRU */
2002 jclear:
2003 a_aux_err_tail = NULL;
2004 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
2005 while((enp = a_aux_err_head) != NULL){
2006 a_aux_err_head = enp->ae_next;
2007 free(enp->ae_str.s);
2008 free(enp);
2010 goto jleave;
2012 #endif /* HAVE_ERRORS */
2014 #ifndef HAVE_DEBUG
2015 FL void *
2016 smalloc(size_t s SMALLOC_DEBUG_ARGS)
2018 void *rv;
2019 NYD2_ENTER;
2021 if (s == 0)
2022 s = 1;
2023 if ((rv = malloc(s)) == NULL)
2024 n_panic(_("no memory"));
2025 NYD2_LEAVE;
2026 return rv;
2029 FL void *
2030 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
2032 void *rv;
2033 NYD2_ENTER;
2035 if (s == 0)
2036 s = 1;
2037 if (v == NULL)
2038 rv = smalloc(s);
2039 else if ((rv = realloc(v, s)) == NULL)
2040 n_panic(_("no memory"));
2041 NYD2_LEAVE;
2042 return rv;
2045 FL void *
2046 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2048 void *rv;
2049 NYD2_ENTER;
2051 if (size == 0)
2052 size = 1;
2053 if ((rv = calloc(nmemb, size)) == NULL)
2054 n_panic(_("no memory"));
2055 NYD2_LEAVE;
2056 return rv;
2059 #else /* !HAVE_DEBUG */
2060 CTA(sizeof(char) == sizeof(ui8_t));
2062 # define _HOPE_SIZE (2 * 8 * sizeof(char))
2063 # define _HOPE_SET(C) \
2064 do {\
2065 union mem_ptr __xl, __xu;\
2066 struct mem_chunk *__xc;\
2067 __xl.p_p = (C).p_p;\
2068 __xc = __xl.p_c - 1;\
2069 __xu.p_p = __xc;\
2070 (C).p_cp += 8;\
2071 __xl.p_ui8p[0]=0xDE; __xl.p_ui8p[1]=0xAA;\
2072 __xl.p_ui8p[2]=0x55; __xl.p_ui8p[3]=0xAD;\
2073 __xl.p_ui8p[4]=0xBE; __xl.p_ui8p[5]=0x55;\
2074 __xl.p_ui8p[6]=0xAA; __xl.p_ui8p[7]=0xEF;\
2075 __xu.p_ui8p += __xc->mc_size - 8;\
2076 __xu.p_ui8p[0]=0xDE; __xu.p_ui8p[1]=0xAA;\
2077 __xu.p_ui8p[2]=0x55; __xu.p_ui8p[3]=0xAD;\
2078 __xu.p_ui8p[4]=0xBE; __xu.p_ui8p[5]=0x55;\
2079 __xu.p_ui8p[6]=0xAA; __xu.p_ui8p[7]=0xEF;\
2080 } while (0)
2081 # define _HOPE_GET_TRACE(C,BAD) \
2082 do {\
2083 (C).p_cp += 8;\
2084 _HOPE_GET(C, BAD);\
2085 (C).p_cp += 8;\
2086 } while(0)
2087 # define _HOPE_GET(C,BAD) \
2088 do {\
2089 union mem_ptr __xl, __xu;\
2090 struct mem_chunk *__xc;\
2091 ui32_t __i;\
2092 __xl.p_p = (C).p_p;\
2093 __xl.p_cp -= 8;\
2094 (C).p_cp = __xl.p_cp;\
2095 __xc = __xl.p_c - 1;\
2096 (BAD) = FAL0;\
2097 __i = 0;\
2098 if (__xl.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2099 if (__xl.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2100 if (__xl.p_ui8p[2] != 0x55) __i |= 1<<2;\
2101 if (__xl.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2102 if (__xl.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2103 if (__xl.p_ui8p[5] != 0x55) __i |= 1<<5;\
2104 if (__xl.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2105 if (__xl.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2106 if (__i != 0) {\
2107 (BAD) = TRU1;\
2108 n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
2109 __xl.p_p, __i, mdbg_file, mdbg_line);\
2111 __xu.p_p = __xc;\
2112 __xu.p_ui8p += __xc->mc_size - 8;\
2113 __i = 0;\
2114 if (__xu.p_ui8p[0] != 0xDE) __i |= 1<<0;\
2115 if (__xu.p_ui8p[1] != 0xAA) __i |= 1<<1;\
2116 if (__xu.p_ui8p[2] != 0x55) __i |= 1<<2;\
2117 if (__xu.p_ui8p[3] != 0xAD) __i |= 1<<3;\
2118 if (__xu.p_ui8p[4] != 0xBE) __i |= 1<<4;\
2119 if (__xu.p_ui8p[5] != 0x55) __i |= 1<<5;\
2120 if (__xu.p_ui8p[6] != 0xAA) __i |= 1<<6;\
2121 if (__xu.p_ui8p[7] != 0xEF) __i |= 1<<7;\
2122 if (__i != 0) {\
2123 (BAD) = TRU1;\
2124 n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
2125 __xl.p_p, __i, mdbg_file, mdbg_line);\
2127 if (BAD)\
2128 n_alert(" ..canary last seen: %s, line %" PRIu16 "",\
2129 __xc->mc_file, __xc->mc_line);\
2130 } while (0)
2132 FL void *
2133 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
2135 union mem_ptr p;
2136 NYD2_ENTER;
2138 if (s == 0)
2139 s = 1;
2140 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2141 n_panic("smalloc(): allocation too large: %s, line %d",
2142 mdbg_file, mdbg_line);
2143 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2145 if ((p.p_p = (malloc)(s)) == NULL)
2146 n_panic(_("no memory"));
2147 p.p_c->mc_prev = NULL;
2148 if ((p.p_c->mc_next = _mem_list) != NULL)
2149 _mem_list->mc_prev = p.p_c;
2150 p.p_c->mc_file = mdbg_file;
2151 p.p_c->mc_line = (ui16_t)mdbg_line;
2152 p.p_c->mc_isfree = FAL0;
2153 p.p_c->mc_size = (ui32_t)s;
2155 _mem_list = p.p_c++;
2156 _HOPE_SET(p);
2158 ++_mem_aall;
2159 ++_mem_acur;
2160 _mem_amax = MAX(_mem_amax, _mem_acur);
2161 _mem_mall += s;
2162 _mem_mcur += s;
2163 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2164 NYD2_LEAVE;
2165 return p.p_p;
2168 FL void *
2169 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
2171 union mem_ptr p;
2172 bool_t isbad;
2173 NYD2_ENTER;
2175 if ((p.p_p = v) == NULL) {
2176 p.p_p = (smalloc)(s, mdbg_file, mdbg_line);
2177 goto jleave;
2180 _HOPE_GET(p, isbad);
2181 --p.p_c;
2182 if (p.p_c->mc_isfree) {
2183 n_err("srealloc(): region freed! At %s, line %d\n"
2184 "\tLast seen: %s, line %" PRIu16 "\n",
2185 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2186 goto jforce;
2189 if (p.p_c == _mem_list)
2190 _mem_list = p.p_c->mc_next;
2191 else
2192 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2193 if (p.p_c->mc_next != NULL)
2194 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2196 --_mem_acur;
2197 _mem_mcur -= p.p_c->mc_size;
2198 jforce:
2199 if (s == 0)
2200 s = 1;
2201 if (s > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2202 n_panic("srealloc(): allocation too large: %s, line %d",
2203 mdbg_file, mdbg_line);
2204 s += sizeof(struct mem_chunk) + _HOPE_SIZE;
2206 if ((p.p_p = (realloc)(p.p_c, s)) == NULL)
2207 n_panic(_("no memory"));
2208 p.p_c->mc_prev = NULL;
2209 if ((p.p_c->mc_next = _mem_list) != NULL)
2210 _mem_list->mc_prev = p.p_c;
2211 p.p_c->mc_file = mdbg_file;
2212 p.p_c->mc_line = (ui16_t)mdbg_line;
2213 p.p_c->mc_isfree = FAL0;
2214 p.p_c->mc_size = (ui32_t)s;
2215 _mem_list = p.p_c++;
2216 _HOPE_SET(p);
2218 ++_mem_aall;
2219 ++_mem_acur;
2220 _mem_amax = MAX(_mem_amax, _mem_acur);
2221 _mem_mall += s;
2222 _mem_mcur += s;
2223 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2224 jleave:
2225 NYD2_LEAVE;
2226 return p.p_p;
2229 FL void *
2230 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
2232 union mem_ptr p;
2233 NYD2_ENTER;
2235 if (size == 0)
2236 size = 1;
2237 if (nmemb == 0)
2238 nmemb = 1;
2239 if (size > UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE)
2240 n_panic("scalloc(): allocation size too large: %s, line %d",
2241 mdbg_file, mdbg_line);
2242 if ((UI32_MAX - sizeof(struct mem_chunk) - _HOPE_SIZE) / nmemb < size)
2243 n_panic("scalloc(): allocation count too large: %s, line %d",
2244 mdbg_file, mdbg_line);
2246 size *= nmemb;
2247 size += sizeof(struct mem_chunk) + _HOPE_SIZE;
2249 if ((p.p_p = (malloc)(size)) == NULL)
2250 n_panic(_("no memory"));
2251 memset(p.p_p, 0, size);
2252 p.p_c->mc_prev = NULL;
2253 if ((p.p_c->mc_next = _mem_list) != NULL)
2254 _mem_list->mc_prev = p.p_c;
2255 p.p_c->mc_file = mdbg_file;
2256 p.p_c->mc_line = (ui16_t)mdbg_line;
2257 p.p_c->mc_isfree = FAL0;
2258 p.p_c->mc_size = (ui32_t)size;
2259 _mem_list = p.p_c++;
2260 _HOPE_SET(p);
2262 ++_mem_aall;
2263 ++_mem_acur;
2264 _mem_amax = MAX(_mem_amax, _mem_acur);
2265 _mem_mall += size;
2266 _mem_mcur += size;
2267 _mem_mmax = MAX(_mem_mmax, _mem_mcur);
2268 NYD2_LEAVE;
2269 return p.p_p;
2272 FL void
2273 (sfree)(void *v SMALLOC_DEBUG_ARGS)
2275 union mem_ptr p;
2276 bool_t isbad;
2277 NYD2_ENTER;
2279 if ((p.p_p = v) == NULL) {
2280 n_err("sfree(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
2281 goto jleave;
2284 _HOPE_GET(p, isbad);
2285 --p.p_c;
2286 if (p.p_c->mc_isfree) {
2287 n_err("sfree(): double-free avoided at %s, line %d\n"
2288 "\tLast seen: %s, line %" PRIu16 "\n",
2289 mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
2290 goto jleave;
2293 if (p.p_c == _mem_list)
2294 _mem_list = p.p_c->mc_next;
2295 else
2296 p.p_c->mc_prev->mc_next = p.p_c->mc_next;
2297 if (p.p_c->mc_next != NULL)
2298 p.p_c->mc_next->mc_prev = p.p_c->mc_prev;
2299 p.p_c->mc_isfree = TRU1;
2300 /* Trash contents (also see [21c05f8]) */
2301 memset(v, 0377, p.p_c->mc_size - sizeof(struct mem_chunk) - _HOPE_SIZE);
2303 --_mem_acur;
2304 _mem_mcur -= p.p_c->mc_size;
2306 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2307 p.p_c->mc_next = _mem_free;
2308 _mem_free = p.p_c;
2309 } else
2310 (free)(p.p_c);
2311 jleave:
2312 NYD2_LEAVE;
2315 FL void
2316 smemreset(void)
2318 union mem_ptr p;
2319 size_t c = 0, s = 0;
2320 NYD_ENTER;
2322 smemcheck();
2324 for (p.p_c = _mem_free; p.p_c != NULL;) {
2325 void *vp = p.p_c;
2326 ++c;
2327 s += p.p_c->mc_size;
2328 p.p_c = p.p_c->mc_next;
2329 (free)(vp);
2331 _mem_free = NULL;
2333 if (options & (OPT_DEBUG | OPT_MEMDEBUG))
2334 n_err("smemreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
2335 NYD_LEAVE;
2338 FL int
2339 c_smemtrace(void *v)
2341 /* For _HOPE_GET() */
2342 char const * const mdbg_file = "smemtrace()";
2343 int const mdbg_line = -1;
2344 FILE *fp;
2345 union mem_ptr p, xp;
2346 bool_t isbad;
2347 size_t lines;
2348 NYD_ENTER;
2350 v = (void*)0x1;
2351 if ((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL) {
2352 n_perr("tmpfile", 0);
2353 goto jleave;
2356 fprintf(fp, "Memory statistics:\n"
2357 " Count cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
2358 " Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
2359 _mem_acur, _mem_amax, _mem_aall, _mem_mcur, _mem_mmax, _mem_mall);
2361 fprintf(fp, "Currently allocated memory chunks:\n");
2362 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2363 ++lines, p.p_c = p.p_c->mc_next) {
2364 xp = p;
2365 ++xp.p_c;
2366 _HOPE_GET_TRACE(xp, isbad);
2367 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2368 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2369 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)), p.p_c->mc_file,
2370 p.p_c->mc_line);
2373 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2374 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
2375 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2376 xp = p;
2377 ++xp.p_c;
2378 _HOPE_GET_TRACE(xp, isbad);
2379 fprintf(fp, "%s%p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2380 (isbad ? "! CANARY ERROR: " : ""), xp.p_p,
2381 (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2382 p.p_c->mc_file, p.p_c->mc_line);
2386 page_or_print(fp, lines);
2387 Fclose(fp);
2388 v = NULL;
2389 jleave:
2390 NYD_LEAVE;
2391 return (v != NULL);
2394 # ifdef HAVE_DEVEL
2395 FL bool_t
2396 _smemcheck(char const *mdbg_file, int mdbg_line)
2398 union mem_ptr p, xp;
2399 bool_t anybad = FAL0, isbad;
2400 size_t lines;
2401 NYD_ENTER;
2403 for (lines = 0, p.p_c = _mem_list; p.p_c != NULL;
2404 ++lines, p.p_c = p.p_c->mc_next) {
2405 xp = p;
2406 ++xp.p_c;
2407 _HOPE_GET_TRACE(xp, isbad);
2408 if (isbad) {
2409 anybad = TRU1;
2410 n_err(
2411 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2412 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2413 p.p_c->mc_file, p.p_c->mc_line);
2417 if (options & (OPT_DEBUG | OPT_MEMDEBUG)) {
2418 for (p.p_c = _mem_free; p.p_c != NULL; ++lines, p.p_c = p.p_c->mc_next) {
2419 xp = p;
2420 ++xp.p_c;
2421 _HOPE_GET_TRACE(xp, isbad);
2422 if (isbad) {
2423 anybad = TRU1;
2424 n_err(
2425 "! CANARY ERROR: %p (%5" PRIuZ " bytes): %s, line %" PRIu16 "\n",
2426 xp.p_p, (size_t)(p.p_c->mc_size - sizeof(struct mem_chunk)),
2427 p.p_c->mc_file, p.p_c->mc_line);
2431 NYD_LEAVE;
2432 return anybad;
2434 # endif /* HAVE_DEVEL */
2436 # undef _HOPE_SIZE
2437 # undef _HOPE_SET
2438 # undef _HOPE_GET_TRACE
2439 # undef _HOPE_GET
2440 #endif /* HAVE_DEBUG */
2442 /* s-it-mode */