cc-test.sh: add --check-only mode
[s-mailx.git] / auxlily.c
blob970ed15f5b3dea9591e0d94b599d1e3b6941b2d3
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 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "rcv.h"
42 #include <sys/stat.h>
43 #include <sys/utsname.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <dirent.h>
47 #include <fcntl.h>
48 #include <pwd.h>
49 #include <stdarg.h>
50 #include <termios.h>
51 #include <unistd.h>
52 #ifdef HAVE_WCTYPE_H
53 # include <wctype.h>
54 #endif
55 #ifdef HAVE_WCWIDTH
56 # include <wchar.h>
57 #endif
59 #ifdef HAVE_SOCKETS
60 # ifdef HAVE_IPV6
61 # include <sys/socket.h>
62 # endif
63 # include <netdb.h>
64 #endif
66 #include "extern.h"
67 #ifdef HAVE_MD5
68 # include "md5.h"
69 #endif
71 /* {hold,rele}_all_sigs() */
72 static size_t _alls_depth;
73 static sigset_t _alls_nset, _alls_oset;
75 void
76 panic(char const *format, ...)
78 va_list ap;
80 fprintf(stderr, tr(1, "Panic: "));
82 va_start(ap, format);
83 vfprintf(stderr, format, ap);
84 va_end(ap);
86 fputs("\n", stderr);
87 fflush(stderr);
88 abort();
91 void
92 hold_all_sigs(void)
94 if (_alls_depth++ == 0) {
95 sigfillset(&_alls_nset);
96 sigdelset(&_alls_nset, SIGKILL);
97 sigdelset(&_alls_nset, SIGSTOP);
98 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
102 void
103 rele_all_sigs(void)
105 if (--_alls_depth == 0)
106 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
110 * Touch the named message by setting its MTOUCH flag.
111 * Touched messages have the effect of not being sent
112 * back to the system mailbox on exit.
114 void
115 touch(struct message *mp)
118 mp->m_flag |= MTOUCH;
119 if ((mp->m_flag & MREAD) == 0)
120 mp->m_flag |= MREAD|MSTATUS;
124 * Test to see if the passed file name is a directory.
125 * Return true if it is.
127 int
128 is_dir(char const *name)
130 struct stat sbuf;
132 if (stat(name, &sbuf) < 0)
133 return(0);
134 return(S_ISDIR(sbuf.st_mode));
138 * Count the number of arguments in the given string raw list.
140 int
141 argcount(char **argv)
143 char **ap;
145 for (ap = argv; *ap++ != NULL;)
147 return ap - argv - 1;
150 char *
151 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
153 int col_orig = col, n, sz;
154 char *nb, *np;
156 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
157 while (*cp) {
158 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
159 if (mb_cur_max > 1) {
160 wchar_t wc;
162 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
163 n = sz = 1;
164 } else {
165 if ((n = wcwidth(wc)) < 0)
166 n = 1;
168 } else
169 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
171 n = sz = 1;
173 if (n > col)
174 break;
175 col -= n;
176 if (sz == 1 && spacechar(*cp)) {
177 *np++ = ' ';
178 cp++;
179 } else
180 while (sz--)
181 *np++ = *cp++;
184 if (fill && col != 0) {
185 if (fill > 0) {
186 memmove(nb + col, nb, (size_t)(np - nb));
187 memset(nb, ' ', col);
188 } else
189 memset(np, ' ', col);
190 np += col;
191 col = 0;
194 *np = '\0';
195 if (cols_decr_used_or_null != NULL)
196 *cols_decr_used_or_null -= col_orig - col;
197 return nb;
200 size_t
201 paging_seems_sensible(void)
203 size_t ret = 0;
204 char const *cp;
206 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL)
207 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
208 return ret;
211 void
212 page_or_print(FILE *fp, size_t lines)
214 size_t rows;
215 int c;
217 fflush_rewind(fp);
219 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
220 while ((c = getc(fp)) != EOF)
221 if (c == '\n' && ++lines > rows)
222 break;
223 rewind(fp);
226 if (rows != 0 && lines >= rows)
227 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
228 else
229 while ((c = getc(fp)) != EOF)
230 putchar(c);
233 enum protocol
234 which_protocol(const char *name)
236 register const char *cp;
237 char *np;
238 size_t sz;
239 struct stat st;
240 enum protocol p;
242 if (name[0] == '%' && name[1] == ':')
243 name += 2;
244 for (cp = name; *cp && *cp != ':'; cp++)
245 if (!alnumchar(*cp&0377))
246 goto file;
247 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
248 if (strncmp(name, "pop3://", 7) == 0)
249 #ifdef HAVE_POP3
250 return PROTO_POP3;
251 #else
252 fprintf(stderr,
253 tr(216, "No POP3 support compiled in.\n"));
254 #endif
255 if (strncmp(name, "pop3s://", 8) == 0)
256 #ifdef HAVE_SSL
257 return PROTO_POP3;
258 #else
259 fprintf(stderr,
260 tr(225, "No SSL support compiled in.\n"));
261 #endif
262 if (strncmp(name, "imap://", 7) == 0)
263 #ifdef HAVE_IMAP
264 return PROTO_IMAP;
265 #else
266 fprintf(stderr,
267 tr(269, "No IMAP support compiled in.\n"));
268 #endif
269 if (strncmp(name, "imaps://", 8) == 0)
270 #ifdef HAVE_SSL
271 return PROTO_IMAP;
272 #else
273 fprintf(stderr,
274 tr(225, "No SSL support compiled in.\n"));
275 #endif
276 return PROTO_UNKNOWN;
277 } else {
278 /* TODO This is the de facto maildir code and thus belongs
279 * TODO into maildir! */
280 file: p = PROTO_FILE;
281 np = ac_alloc((sz = strlen(name)) + 5);
282 memcpy(np, name, sz + 1);
283 if (stat(name, &st) == 0) {
284 if (S_ISDIR(st.st_mode)) {
285 strcpy(&np[sz], "/tmp");
286 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
287 strcpy(&np[sz], "/new");
288 if (stat(np, &st) == 0 &&
289 S_ISDIR(st.st_mode)) {
290 strcpy(&np[sz], "/cur");
291 if (stat(np, &st) == 0 &&
292 S_ISDIR(st.st_mode))
293 p = PROTO_MAILDIR;
297 } else {
298 strcpy(&np[sz], ".gz");
299 if (stat(np, &st) < 0) {
300 strcpy(&np[sz], ".bz2");
301 if (stat(np, &st) < 0) {
302 if ((cp = value("newfolders")) != 0 &&
303 strcmp(cp, "maildir") == 0)
304 p = PROTO_MAILDIR;
308 ac_free(np);
309 return p;
313 unsigned
314 pjw(const char *cp)
316 unsigned h = 0, g;
318 cp--;
319 while (*++cp) {
320 h = (h << 4 & 0xffffffff) + (*cp&0377);
321 if ((g = h & 0xf0000000) != 0) {
322 h = h ^ g >> 24;
323 h = h ^ g;
326 return h;
329 long
330 nextprime(long n)
332 const long primes[] = {
333 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
334 131071, 262139, 524287, 1048573, 2097143, 4194301,
335 8388593, 16777213, 33554393, 67108859, 134217689,
336 268435399, 536870909, 1073741789, 2147483647
338 long mprime = 7;
339 size_t i;
341 for (i = 0; i < sizeof primes / sizeof *primes; i++)
342 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
343 n < 262144 ? n*2 : n))
344 break;
345 if (i == sizeof primes / sizeof *primes)
346 mprime = n; /* not so prime, but better than failure */
347 return mprime;
351 expand_shell_escape(char const **s, bool_t use_nail_extensions)
353 char const *xs = *s;
354 int c, n;
356 if ((c = *xs & 0xFF) == '\0')
357 goto jleave;
358 ++xs;
359 if (c != '\\')
360 goto jleave;
362 switch ((c = *xs & 0xFF)) {
363 case '\\': break;
364 case 'a': c = '\a'; break;
365 case 'b': c = '\b'; break;
366 case 'c': c = -1; break;
367 case 'f': c = '\f'; break;
368 case 'n': c = '\n'; break;
369 case 'r': c = '\r'; break;
370 case 't': c = '\t'; break;
371 case 'v': c = '\v'; break;
372 case '0':
373 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
374 c <<= 3;
375 c |= *xs - '0';
377 goto jleave;
378 /* S-nail extension for nice prompt support */
379 case '?':
380 if (use_nail_extensions) {
381 c = exec_last_comm_error ? '1' : '0';
382 break;
384 /* FALLTHRU */
385 case '\0':
386 /* A sole <backslash> at EOS is treated as-is! */
387 /* FALLTHRU */
388 default:
389 c = '\\';
390 goto jleave;
392 ++xs;
393 jleave:
394 *s = xs;
395 return c;
398 char *
399 getprompt(void)
401 static char buf[64];
402 char const *ccp;
404 if ((ccp = value("prompt")) == NULL) {
405 buf[0] = value("bsdcompat") ? '&' : '?';
406 buf[1] = ' ';
407 buf[2] = '\0';
408 } else {
409 char *cp;
411 for (cp = buf; cp < buf + sizeof(buf) - 1; ++cp) {
412 int c = expand_shell_escape(&ccp, TRU1);
413 if (c <= 0)
414 break;
415 *cp = (char)c;
417 *cp = '\0';
419 return buf;
422 char *
423 getname(int uid)
425 struct passwd *pw = getpwuid(uid);
427 return pw == NULL ? NULL : pw->pw_name;
430 char *
431 username(void)
433 char *np;
434 uid_t uid;
436 if ((np = getenv("USER")) != NULL)
437 goto jleave;
438 if ((np = getname(uid = getuid())) != NULL)
439 goto jleave;
440 panic(tr(201, "Cannot associate a name with uid %d\n"), (int)uid);
441 jleave:
442 return (np);
445 char *
446 nodename(int mayoverride)
448 static char *hostname;
449 struct utsname ut;
450 char *hn;
451 #ifdef HAVE_SOCKETS
452 # ifdef HAVE_IPV6
453 struct addrinfo hints, *res;
454 # else
455 struct hostent *hent;
456 # endif
457 #endif
459 if (mayoverride && (hn = value("hostname")) != NULL && *hn != '\0') {
460 if (hostname != NULL)
461 free(hostname);
462 hostname = sstrdup(hn);
463 } else if (hostname == NULL) {
464 uname(&ut);
465 hn = ut.nodename;
466 #ifdef HAVE_SOCKETS
467 # ifdef HAVE_IPV6
468 memset(&hints, 0, sizeof hints);
469 hints.ai_family = AF_UNSPEC;
470 hints.ai_socktype = SOCK_DGRAM; /* dummy */
471 hints.ai_flags = AI_CANONNAME;
472 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
473 if (res->ai_canonname != NULL) {
474 size_t l = strlen(res->ai_canonname);
475 hn = ac_alloc(l + 1);
476 memcpy(hn, res->ai_canonname, l + 1);
478 freeaddrinfo(res);
480 # else
481 hent = gethostbyname(hn);
482 if (hent != NULL) {
483 hn = hent->h_name;
485 # endif
486 #endif
487 hostname = sstrdup(hn);
488 #if defined HAVE_SOCKETS && defined HAVE_IPV6
489 if (hn != ut.nodename)
490 ac_free(hn);
491 #endif
493 return (hostname);
496 char *
497 lookup_password_for_token(char const *token)
499 size_t tl;
500 char *var, *cp;
502 tl = strlen(token);
503 var = ac_alloc(tl + 10);
505 memcpy(var, "password-", 9);
506 memcpy(var + 9, token, tl);
507 var[tl + 9] = '\0';
509 if ((cp = value(var)) != NULL)
510 cp = savestr(cp);
511 ac_free(var);
512 return cp;
515 char *
516 getrandstring(size_t length)
518 static unsigned char nodedigest[16];
519 static pid_t pid;
520 struct str b64;
521 int fd = -1;
522 char *data, *cp;
523 size_t i;
524 #ifdef HAVE_MD5
525 MD5_CTX ctx;
526 #else
527 size_t j;
528 #endif
530 data = ac_alloc(length);
531 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
532 length != (size_t)read(fd, data, length)) {
533 if (pid == 0) {
534 pid = getpid();
535 srand(pid);
536 cp = nodename(0);
537 #ifdef HAVE_MD5
538 MD5Init(&ctx);
539 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
540 MD5Final(nodedigest, &ctx);
541 #else
542 /* In that case it's only used for boundaries and
543 * Message-Id:s so that srand(3) should suffice */
544 j = strlen(cp) + 1;
545 for (i = 0; i < sizeof(nodedigest); ++i)
546 nodedigest[i] = (unsigned char)(
547 cp[i % j] ^ rand());
548 #endif
550 for (i = 0; i < length; i++)
551 data[i] = (char)(
552 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
553 nodedigest[i % sizeof nodedigest]);
555 if (fd >= 0)
556 close(fd);
558 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
559 ac_free(data);
560 assert(length < b64.l);
561 b64.s[length] = '\0';
562 return b64.s;
565 #ifdef HAVE_MD5
566 char *
567 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
569 char const *cp = vp;
570 size_t i, j;
572 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
573 j = i << 1;
574 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
575 hex[++j] = hexchar(cp[i] & 0x0f);
577 hex[MD5TOHEX_SIZE] = '\0';
578 return hex;
581 char *
582 cram_md5_string(char const *user, char const *pass, char const *b64)
584 struct str in, out;
585 char digest[16], *cp;
586 size_t lu;
588 out.s = NULL;
589 in.s = UNCONST(b64);
590 in.l = strlen(in.s);
591 (void)b64_decode(&out, &in, NULL);
592 assert(out.s != NULL);
594 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
595 digest);
596 free(out.s);
597 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
599 lu = strlen(user);
600 in.l = lu + MD5TOHEX_SIZE +1;
601 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
602 memcpy(in.s, user, lu);
603 in.s[lu] = ' ';
604 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
605 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
606 ac_free(in.s);
607 return out.s;
609 #endif /* HAVE_MD5 */
611 enum okay
612 makedir(const char *name)
614 int e;
615 struct stat st;
617 if (mkdir(name, 0700) < 0) {
618 e = errno;
619 if ((e == EEXIST || e == ENOSYS) &&
620 stat(name, &st) == 0 &&
621 (st.st_mode&S_IFMT) == S_IFDIR)
622 return OKAY;
623 return STOP;
625 return OKAY;
628 #ifdef HAVE_FCHDIR
629 enum okay
630 cwget(struct cw *cw)
632 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
633 return STOP;
634 if (fchdir(cw->cw_fd) < 0) {
635 close(cw->cw_fd);
636 return STOP;
638 return OKAY;
641 enum okay
642 cwret(struct cw *cw)
644 if (fchdir(cw->cw_fd) < 0)
645 return STOP;
646 return OKAY;
649 void
650 cwrelse(struct cw *cw)
652 close(cw->cw_fd);
654 #else /* !HAVE_FCHDIR */
655 enum okay
656 cwget(struct cw *cw)
658 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
659 return STOP;
660 return OKAY;
663 enum okay
664 cwret(struct cw *cw)
666 if (chdir(cw->cw_wd) < 0)
667 return STOP;
668 return OKAY;
671 /*ARGSUSED*/
672 void
673 cwrelse(struct cw *cw)
675 (void)cw;
677 #endif /* !HAVE_FCHDIR */
679 void
680 makeprint(struct str const *in, struct str *out)
682 static int print_all_chars = -1;
683 char const *inp, *maxp;
684 char *outp;
685 size_t msz, dist;
687 if (print_all_chars == -1)
688 print_all_chars = (value("print-all-chars") != NULL);
690 msz = in->l + 1;
691 out->s = outp = smalloc(msz);
692 inp = in->s;
693 maxp = inp + in->l;
695 if (print_all_chars) {
696 out->l = in->l;
697 memcpy(outp, inp, out->l);
698 goto jleave;
701 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
702 if (mb_cur_max > 1) {
703 char mbb[MB_LEN_MAX + 1];
704 wchar_t wc;
705 int i, n;
706 out->l = 0;
707 while (inp < maxp) {
708 if (*inp & 0200)
709 n = mbtowc(&wc, inp, maxp - inp);
710 else {
711 wc = *inp;
712 n = 1;
714 if (n < 0) {
715 /* FIXME Why mbtowc() resetting here?
716 * FIXME what about ISO 2022-JP plus -- those
717 * FIXME will loose shifts, then!
718 * FIXME THUS - we'd need special "known points"
719 * FIXME to do so - say, after a newline!!
720 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
721 (void)mbtowc(&wc, NULL, mb_cur_max);
722 wc = utf8 ? 0xFFFD : '?';
723 n = 1;
724 } else if (n == 0)
725 n = 1;
726 inp += n;
727 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
728 wc != '\b' && wc != '\t') {
729 if ((wc & ~(wchar_t)037) == 0)
730 wc = utf8 ? 0x2400 | wc : '?';
731 else if (wc == 0177)
732 wc = utf8 ? 0x2421 : '?';
733 else
734 wc = utf8 ? 0x2426 : '?';
736 if ((n = wctomb(mbb, wc)) <= 0)
737 continue;
738 out->l += n;
739 if (out->l >= msz - 1) {
740 dist = outp - out->s;
741 out->s = srealloc(out->s, msz += 32);
742 outp = &out->s[dist];
744 for (i = 0; i < n; i++)
745 *outp++ = mbb[i];
747 } else
748 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
750 int c;
751 while (inp < maxp) {
752 c = *inp++ & 0377;
753 if (!isprint(c) && c != '\n' && c != '\r' &&
754 c != '\b' && c != '\t')
755 c = '?';
756 *outp++ = c;
758 out->l = in->l;
760 jleave:
761 out->s[out->l] = '\0';
764 char *
765 prstr(const char *s)
767 struct str in, out;
768 char *rp;
770 in.s = UNCONST(s);
771 in.l = strlen(s);
772 makeprint(&in, &out);
773 rp = salloc(out.l + 1);
774 memcpy(rp, out.s, out.l);
775 rp[out.l] = '\0';
776 free(out.s);
777 return rp;
781 prout(const char *s, size_t sz, FILE *fp)
783 struct str in, out;
784 int n;
786 in.s = UNCONST(s);
787 in.l = sz;
788 makeprint(&in, &out);
789 n = fwrite(out.s, 1, out.l, fp);
790 free(out.s);
791 return n;
794 size_t
795 putuc(int u, int c, FILE *fp)
797 size_t rv;
799 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
800 if (utf8 && u & ~(wchar_t)0177) {
801 char mbb[MB_LEN_MAX];
802 int i, n;
803 if ((n = wctomb(mbb, u)) > 0) {
804 rv = wcwidth(u);
805 for (i = 0; i < n; ++i)
806 if (putc(mbb[i] & 0377, fp) == EOF) {
807 rv = 0;
808 break;
810 } else if (n == 0)
811 rv = (putc('\0', fp) != EOF);
812 else
813 rv = 0;
814 } else
815 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
816 rv = (putc(c, fp) != EOF);
817 return rv;
820 void
821 time_current_update(struct time_current *tc, bool_t full_update)
823 tc->tc_time = time(NULL);
824 if (full_update) {
825 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
826 memcpy(&tc->tc_local, localtime(&tc->tc_time),
827 sizeof tc->tc_local);
828 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
832 #ifndef HAVE_GETOPT
833 char *my_optarg;
834 int my_optind = 1, /*my_opterr = 1,*/ my_optopt;
837 my_getopt(int argc, char *const argv[], char const *optstring) /* XXX */
839 static char const *lastp;
840 int colon;
841 char const *curp;
843 if (optstring[0] == ':') {
844 colon = 1;
845 optstring++;
846 } else
847 colon = 0;
848 if (lastp) {
849 curp = lastp;
850 lastp = 0;
851 } else {
852 if (optind >= argc || argv[optind] == 0 ||
853 argv[optind][0] != '-' ||
854 argv[optind][1] == '\0')
855 return -1;
856 if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
857 optind++;
858 return -1;
860 curp = &argv[optind][1];
862 optopt = curp[0];
863 while (optstring[0]) {
864 if (optstring[0] == ':' || optstring[0] != optopt) {
865 optstring++;
866 continue;
868 if (optstring[1] == ':') {
869 if (curp[1] != '\0') {
870 optarg = UNCONST(&curp[1]);
871 optind++;
872 } else {
873 if ((optind += 2) > argc) {
874 if (!colon /*&& opterr*/) {
875 fprintf(stderr, tr(79,
876 "%s: option requires an argument -- %c\n"),
877 argv[0], (char)optopt);
879 return colon ? ':' : '?';
881 optarg = argv[optind - 1];
883 } else {
884 if (curp[1] != '\0')
885 lastp = &curp[1];
886 else
887 optind++;
888 optarg = 0;
890 return optopt;
892 if (!colon /*&& opterr*/)
893 fprintf(stderr, tr(78, "%s: illegal option -- %c\n"),
894 argv[0], optopt);
895 if (curp[1] != '\0')
896 lastp = &curp[1];
897 else
898 optind++;
899 optarg = 0;
900 return '?';
902 #endif /* HAVE_GETOPT */
904 static void
905 out_of_memory(void)
907 panic("no memory");
910 #ifndef HAVE_ASSERTS
911 void *
912 smalloc(size_t s SMALLOC_DEBUG_ARGS)
914 void *p;
916 if (s == 0)
917 s = 1;
918 if ((p = malloc(s)) == NULL)
919 out_of_memory();
920 return p;
923 void *
924 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
926 void *r;
928 if (s == 0)
929 s = 1;
930 if (v == NULL)
931 return smalloc(s);
932 if ((r = realloc(v, s)) == NULL)
933 out_of_memory();
934 return r;
937 void *
938 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
940 void *vp;
942 if (size == 0)
943 size = 1;
944 if ((vp = calloc(nmemb, size)) == NULL)
945 out_of_memory();
946 return vp;
949 #else /* !HAVE_ASSERTS */
950 struct chunk {
951 struct chunk *prev;
952 struct chunk *next;
953 char const *file;
954 us_it line;
955 uc_it isfree;
956 sc_it __dummy;
957 ui_it size;
960 union ptr {
961 struct chunk *c;
962 void *p;
965 struct chunk *_mlist, *_mfree;
967 void *
968 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
970 union ptr p;
972 if (s == 0)
973 s = 1;
974 s += sizeof(struct chunk);
976 if ((p.p = malloc(s)) == NULL)
977 out_of_memory();
978 p.c->prev = NULL;
979 if ((p.c->next = _mlist) != NULL)
980 _mlist->prev = p.c;
981 p.c->file = mdbg_file;
982 p.c->line = (us_it)mdbg_line;
983 p.c->isfree = 0;
984 p.c->size = (ui_it)s;
985 _mlist = p.c++;
986 return (p.p);
989 void *
990 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
992 union ptr p;
994 if ((p.p = v) == NULL) {
995 p.p = (smalloc)(s, mdbg_file, mdbg_line);
996 goto jleave;
999 --p.c;
1000 if (p.c->isfree) {
1001 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1002 "\tLast seen: %s, line %d\n",
1003 mdbg_file, mdbg_line, p.c->file, p.c->line);
1004 goto jforce;
1007 if (p.c == _mlist)
1008 _mlist = p.c->next;
1009 else
1010 p.c->prev->next = p.c->next;
1011 if (p.c->next != NULL)
1012 p.c->next->prev = p.c->prev;
1014 jforce:
1015 if (s == 0)
1016 s = 1;
1017 s += sizeof(struct chunk);
1019 if ((p.p = realloc(p.c, s)) == NULL)
1020 out_of_memory();
1021 p.c->prev = NULL;
1022 if ((p.c->next = _mlist) != NULL)
1023 _mlist->prev = p.c;
1024 p.c->file = mdbg_file;
1025 p.c->line = (us_it)mdbg_line;
1026 p.c->isfree = 0;
1027 p.c->size = (ui_it)s;
1028 _mlist = p.c++;
1029 jleave:
1030 return (p.p);
1033 void *
1034 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1036 union ptr p;
1038 if (size == 0)
1039 size = 1;
1040 if (nmemb == 0)
1041 nmemb = 1;
1042 size *= nmemb;
1043 size += sizeof(struct chunk);
1045 if ((p.p = malloc(size)) == NULL)
1046 out_of_memory();
1047 memset(p.p, 0, size);
1048 p.c->prev = NULL;
1049 if ((p.c->next = _mlist) != NULL)
1050 _mlist->prev = p.c;
1051 p.c->file = mdbg_file;
1052 p.c->line = (us_it)mdbg_line;
1053 p.c->isfree = 0;
1054 p.c->size = (ui_it)size;
1055 _mlist = p.c++;
1056 return (p.p);
1059 void
1060 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1062 union ptr p;
1064 if ((p.p = v) == NULL) {
1065 fprintf(stderr, "sfree(NULL) from %s, line %d\n",
1066 mdbg_file, mdbg_line);
1067 goto jleave;
1070 --p.c;
1071 if (p.c->isfree) {
1072 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1073 "\tLast seen: %s, line %d\n",
1074 mdbg_file, mdbg_line, p.c->file, p.c->line);
1075 goto jleave;
1078 if (p.c == _mlist)
1079 _mlist = p.c->next;
1080 else
1081 p.c->prev->next = p.c->next;
1082 if (p.c->next != NULL)
1083 p.c->next->prev = p.c->prev;
1084 p.c->isfree = 1;
1086 if (options & OPT_DEBUG) {
1087 p.c->next = _mfree;
1088 _mfree = p.c;
1089 } else
1090 (free)(p.c);
1091 jleave: ;
1094 void
1095 smemreset(void)
1097 union ptr p;
1098 ul_it c = 0, s = 0;
1100 for (p.c = _mfree; p.c != NULL;) {
1101 void *vp = p.c;
1102 ++c;
1103 s += p.c->size;
1104 p.c = p.c->next;
1105 (free)(vp);
1107 _mfree = NULL;
1109 if (options & OPT_DEBUG)
1110 fprintf(stderr, "smemreset(): freed %lu regions/%lu bytes\n",
1111 c, s);
1115 (smemtrace)(void *v)
1117 FILE *fp;
1118 char *cp;
1119 union ptr p;
1120 size_t lines = 0;
1122 v = (void*)0x1;
1123 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1124 perror("tmpfile");
1125 goto jleave;
1127 rm(cp);
1128 Ftfree(&cp);
1130 fprintf(fp, "Currently allocated memory chunks:\n");
1131 for (p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next)
1132 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1133 (void*)(p.c + 1),
1134 (ui_it)(p.c->size - sizeof(struct chunk)),
1135 p.c->file, p.c->line);
1137 if (options & OPT_DEBUG) {
1138 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1139 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next)
1140 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1141 (void*)(p.c + 1),
1142 (ui_it)(p.c->size - sizeof(struct chunk)),
1143 p.c->file, p.c->line);
1146 page_or_print(fp, lines);
1147 Fclose(fp);
1148 v = NULL;
1149 jleave:
1150 return (v != NULL);
1152 #endif /* HAVE_ASSERTS */