Fix -u, change meaning of $USER to be EQ -u etc..
[s-mailx.git] / auxlily.c
bloba42066e66fd9232413ff7e5d0d41a99bb1982ce1
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 "nail.h"
42 #include <sys/utsname.h>
44 #include <ctype.h>
45 #include <dirent.h>
46 #include <fcntl.h>
48 #ifdef HAVE_SOCKETS
49 # ifdef HAVE_IPV6
50 # include <sys/socket.h>
51 # endif
53 # include <netdb.h>
54 #endif
56 #ifdef HAVE_MD5
57 # include "md5.h"
58 #endif
60 /* {hold,rele}_all_sigs() */
61 static size_t _alls_depth;
62 static sigset_t _alls_nset, _alls_oset;
64 void
65 panic(char const *format, ...)
67 va_list ap;
69 fprintf(stderr, tr(1, "Panic: "));
71 va_start(ap, format);
72 vfprintf(stderr, format, ap);
73 va_end(ap);
75 fputs("\n", stderr);
76 fflush(stderr);
77 exit(EXIT_ERR);
80 void
81 hold_all_sigs(void)
83 if (_alls_depth++ == 0) {
84 sigfillset(&_alls_nset);
85 sigdelset(&_alls_nset, SIGKILL);
86 sigdelset(&_alls_nset, SIGSTOP);
87 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
91 void
92 rele_all_sigs(void)
94 if (--_alls_depth == 0)
95 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
99 * Touch the named message by setting its MTOUCH flag.
100 * Touched messages have the effect of not being sent
101 * back to the system mailbox on exit.
103 void
104 touch(struct message *mp)
107 mp->m_flag |= MTOUCH;
108 if ((mp->m_flag & MREAD) == 0)
109 mp->m_flag |= MREAD|MSTATUS;
113 * Test to see if the passed file name is a directory.
114 * Return true if it is.
116 int
117 is_dir(char const *name)
119 struct stat sbuf;
121 if (stat(name, &sbuf) < 0)
122 return(0);
123 return(S_ISDIR(sbuf.st_mode));
127 * Count the number of arguments in the given string raw list.
129 int
130 argcount(char **argv)
132 char **ap;
134 for (ap = argv; *ap++ != NULL;)
136 return ap - argv - 1;
139 char *
140 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
142 int col_orig = col, n, sz;
143 char *nb, *np;
145 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
146 while (*cp) {
147 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
148 if (mb_cur_max > 1) {
149 wchar_t wc;
151 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
152 n = sz = 1;
153 } else {
154 if ((n = wcwidth(wc)) < 0)
155 n = 1;
157 } else
158 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
160 n = sz = 1;
162 if (n > col)
163 break;
164 col -= n;
165 if (sz == 1 && spacechar(*cp)) {
166 *np++ = ' ';
167 cp++;
168 } else
169 while (sz--)
170 *np++ = *cp++;
173 if (fill && col != 0) {
174 if (fill > 0) {
175 memmove(nb + col, nb, (size_t)(np - nb));
176 memset(nb, ' ', col);
177 } else
178 memset(np, ' ', col);
179 np += col;
180 col = 0;
183 *np = '\0';
184 if (cols_decr_used_or_null != NULL)
185 *cols_decr_used_or_null -= col_orig - col;
186 return nb;
189 size_t
190 paging_seems_sensible(void)
192 size_t ret = 0;
193 char const *cp;
195 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL)
196 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
197 return ret;
200 void
201 page_or_print(FILE *fp, size_t lines)
203 size_t rows;
204 int c;
206 fflush_rewind(fp);
208 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
209 while ((c = getc(fp)) != EOF)
210 if (c == '\n' && ++lines > rows)
211 break;
212 rewind(fp);
215 if (rows != 0 && lines >= rows)
216 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
217 else
218 while ((c = getc(fp)) != EOF)
219 putchar(c);
222 enum protocol
223 which_protocol(const char *name)
225 register const char *cp;
226 char *np;
227 size_t sz;
228 struct stat st;
229 enum protocol p;
231 if (name[0] == '%' && name[1] == ':')
232 name += 2;
233 for (cp = name; *cp && *cp != ':'; cp++)
234 if (!alnumchar(*cp&0377))
235 goto file;
236 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
237 if (strncmp(name, "pop3://", 7) == 0)
238 #ifdef HAVE_POP3
239 return PROTO_POP3;
240 #else
241 fprintf(stderr,
242 tr(216, "No POP3 support compiled in.\n"));
243 #endif
244 if (strncmp(name, "pop3s://", 8) == 0)
245 #ifdef HAVE_SSL
246 return PROTO_POP3;
247 #else
248 fprintf(stderr,
249 tr(225, "No SSL support compiled in.\n"));
250 #endif
251 if (strncmp(name, "imap://", 7) == 0)
252 #ifdef HAVE_IMAP
253 return PROTO_IMAP;
254 #else
255 fprintf(stderr,
256 tr(269, "No IMAP support compiled in.\n"));
257 #endif
258 if (strncmp(name, "imaps://", 8) == 0)
259 #ifdef HAVE_SSL
260 return PROTO_IMAP;
261 #else
262 fprintf(stderr,
263 tr(225, "No SSL support compiled in.\n"));
264 #endif
265 return PROTO_UNKNOWN;
266 } else {
267 /* TODO This is the de facto maildir code and thus belongs
268 * TODO into maildir! */
269 file: p = PROTO_FILE;
270 np = ac_alloc((sz = strlen(name)) + 5);
271 memcpy(np, name, sz + 1);
272 if (stat(name, &st) == 0) {
273 if (S_ISDIR(st.st_mode)) {
274 strcpy(&np[sz], "/tmp");
275 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
276 strcpy(&np[sz], "/new");
277 if (stat(np, &st) == 0 &&
278 S_ISDIR(st.st_mode)) {
279 strcpy(&np[sz], "/cur");
280 if (stat(np, &st) == 0 &&
281 S_ISDIR(st.st_mode))
282 p = PROTO_MAILDIR;
286 } else {
287 strcpy(&np[sz], ".gz");
288 if (stat(np, &st) < 0) {
289 strcpy(&np[sz], ".bz2");
290 if (stat(np, &st) < 0) {
291 if ((cp = value("newfolders")) != 0 &&
292 strcmp(cp, "maildir") == 0)
293 p = PROTO_MAILDIR;
297 ac_free(np);
298 return p;
302 unsigned
303 pjw(const char *cp)
305 unsigned h = 0, g;
307 cp--;
308 while (*++cp) {
309 h = (h << 4 & 0xffffffff) + (*cp&0377);
310 if ((g = h & 0xf0000000) != 0) {
311 h = h ^ g >> 24;
312 h = h ^ g;
315 return h;
318 long
319 nextprime(long n)
321 const long primes[] = {
322 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
323 131071, 262139, 524287, 1048573, 2097143, 4194301,
324 8388593, 16777213, 33554393, 67108859, 134217689,
325 268435399, 536870909, 1073741789, 2147483647
327 long mprime = 7;
328 size_t i;
330 for (i = 0; i < sizeof primes / sizeof *primes; i++)
331 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
332 n < 262144 ? n*2 : n))
333 break;
334 if (i == sizeof primes / sizeof *primes)
335 mprime = n; /* not so prime, but better than failure */
336 return mprime;
340 expand_shell_escape(char const **s, bool_t use_nail_extensions)
342 char const *xs = *s;
343 int c, n;
345 if ((c = *xs & 0xFF) == '\0')
346 goto jleave;
347 ++xs;
348 if (c != '\\')
349 goto jleave;
351 switch ((c = *xs & 0xFF)) {
352 case '\\': break;
353 case 'a': c = '\a'; break;
354 case 'b': c = '\b'; break;
355 case 'c': c = PROMPT_STOP;break;
356 case 'f': c = '\f'; break;
357 case 'n': c = '\n'; break;
358 case 'r': c = '\r'; break;
359 case 't': c = '\t'; break;
360 case 'v': c = '\v'; break;
361 case '0':
362 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
363 c <<= 3;
364 c |= *xs - '0';
366 goto jleave;
367 /* S-nail extension for nice (get)prompt(()) support */
368 case '?':
369 case '$':
370 case '@':
371 if (use_nail_extensions) {
372 switch (c) {
373 case '?':
374 c = exec_last_comm_error ? '1' : '0';
375 break;
376 case '$':
377 c = PROMPT_DOLLAR;
378 break;
379 case '@':
380 c = PROMPT_AT;
381 break;
383 break;
385 /* FALLTHRU */
386 case '\0':
387 /* A sole <backslash> at EOS is treated as-is! */
388 /* FALLTHRU */
389 default:
390 c = '\\';
391 goto jleave;
393 ++xs;
394 jleave:
395 *s = xs;
396 return c;
399 char *
400 getprompt(void)
402 static char buf[PROMPT_BUFFER_SIZE];
404 char const *ccp;
406 if (options & OPT_NOPROMPT)
407 buf[0] = '\0';
408 else if ((ccp = value("prompt")) == NULL) {
409 buf[0] = value("bsdcompat") ? '&' : '?';
410 buf[1] = ' ';
411 buf[2] = '\0';
412 } else {
413 char *cp;
415 for (cp = buf; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
416 char const *a;
417 size_t l;
418 int c = expand_shell_escape(&ccp, TRU1);
419 if (c > 0) {
420 *cp = (char)c;
421 continue;
423 if (c == 0 || c == PROMPT_STOP)
424 break;
426 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
427 if (a == NULL)
428 a = "";
429 l = strlen(a);
430 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
431 *cp++ = '?';
432 else {
433 memcpy(cp, a, l);
434 cp += --l;
437 *cp = '\0';
439 return buf;
442 char *
443 nodename(int mayoverride)
445 static char *hostname;
446 struct utsname ut;
447 char *hn;
448 #ifdef HAVE_SOCKETS
449 # ifdef HAVE_IPV6
450 struct addrinfo hints, *res;
451 # else
452 struct hostent *hent;
453 # endif
454 #endif
456 if (mayoverride && (hn = value("hostname")) != NULL && *hn != '\0') {
457 if (hostname != NULL)
458 free(hostname);
459 hostname = sstrdup(hn);
460 } else if (hostname == NULL) {
461 uname(&ut);
462 hn = ut.nodename;
463 #ifdef HAVE_SOCKETS
464 # ifdef HAVE_IPV6
465 memset(&hints, 0, sizeof hints);
466 hints.ai_family = AF_UNSPEC;
467 hints.ai_socktype = SOCK_DGRAM; /* dummy */
468 hints.ai_flags = AI_CANONNAME;
469 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
470 if (res->ai_canonname != NULL) {
471 size_t l = strlen(res->ai_canonname);
472 hn = ac_alloc(l + 1);
473 memcpy(hn, res->ai_canonname, l + 1);
475 freeaddrinfo(res);
477 # else
478 hent = gethostbyname(hn);
479 if (hent != NULL) {
480 hn = hent->h_name;
482 # endif
483 #endif
484 hostname = sstrdup(hn);
485 #if defined HAVE_SOCKETS && defined HAVE_IPV6
486 if (hn != ut.nodename)
487 ac_free(hn);
488 #endif
490 return (hostname);
493 char *
494 lookup_password_for_token(char const *token)
496 size_t tl;
497 char *var, *cp;
499 tl = strlen(token);
500 var = ac_alloc(tl + 10);
502 memcpy(var, "password-", 9);
503 memcpy(var + 9, token, tl);
504 var[tl + 9] = '\0';
506 if ((cp = value(var)) != NULL)
507 cp = savestr(cp);
508 ac_free(var);
509 return cp;
512 char *
513 getrandstring(size_t length)
515 static unsigned char nodedigest[16];
516 static pid_t pid;
517 struct str b64;
518 int fd = -1;
519 char *data, *cp;
520 size_t i;
521 #ifdef HAVE_MD5
522 MD5_CTX ctx;
523 #else
524 size_t j;
525 #endif
527 data = ac_alloc(length);
528 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
529 length != (size_t)read(fd, data, length)) {
530 if (pid == 0) {
531 pid = getpid();
532 srand(pid);
533 cp = nodename(0);
534 #ifdef HAVE_MD5
535 MD5Init(&ctx);
536 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
537 MD5Final(nodedigest, &ctx);
538 #else
539 /* In that case it's only used for boundaries and
540 * Message-Id:s so that srand(3) should suffice */
541 j = strlen(cp) + 1;
542 for (i = 0; i < sizeof(nodedigest); ++i)
543 nodedigest[i] = (unsigned char)(
544 cp[i % j] ^ rand());
545 #endif
547 for (i = 0; i < length; i++)
548 data[i] = (char)(
549 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
550 nodedigest[i % sizeof nodedigest]);
552 if (fd >= 0)
553 close(fd);
555 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
556 ac_free(data);
557 assert(length < b64.l);
558 b64.s[length] = '\0';
559 return b64.s;
562 #ifdef HAVE_MD5
563 char *
564 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
566 char const *cp = vp;
567 size_t i, j;
569 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
570 j = i << 1;
571 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
572 hex[++j] = hexchar(cp[i] & 0x0f);
574 hex[MD5TOHEX_SIZE] = '\0';
575 return hex;
578 char *
579 cram_md5_string(char const *user, char const *pass, char const *b64)
581 struct str in, out;
582 char digest[16], *cp;
583 size_t lu;
585 out.s = NULL;
586 in.s = UNCONST(b64);
587 in.l = strlen(in.s);
588 (void)b64_decode(&out, &in, NULL);
589 assert(out.s != NULL);
591 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
592 digest);
593 free(out.s);
594 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
596 lu = strlen(user);
597 in.l = lu + MD5TOHEX_SIZE +1;
598 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
599 memcpy(in.s, user, lu);
600 in.s[lu] = ' ';
601 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
602 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
603 ac_free(in.s);
604 return out.s;
606 #endif /* HAVE_MD5 */
608 enum okay
609 makedir(const char *name)
611 int e;
612 struct stat st;
614 if (mkdir(name, 0700) < 0) {
615 e = errno;
616 if ((e == EEXIST || e == ENOSYS) &&
617 stat(name, &st) == 0 &&
618 (st.st_mode&S_IFMT) == S_IFDIR)
619 return OKAY;
620 return STOP;
622 return OKAY;
625 #ifdef HAVE_FCHDIR
626 enum okay
627 cwget(struct cw *cw)
629 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
630 return STOP;
631 if (fchdir(cw->cw_fd) < 0) {
632 close(cw->cw_fd);
633 return STOP;
635 return OKAY;
638 enum okay
639 cwret(struct cw *cw)
641 if (fchdir(cw->cw_fd) < 0)
642 return STOP;
643 return OKAY;
646 void
647 cwrelse(struct cw *cw)
649 close(cw->cw_fd);
651 #else /* !HAVE_FCHDIR */
652 enum okay
653 cwget(struct cw *cw)
655 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
656 return STOP;
657 return OKAY;
660 enum okay
661 cwret(struct cw *cw)
663 if (chdir(cw->cw_wd) < 0)
664 return STOP;
665 return OKAY;
668 /*ARGSUSED*/
669 void
670 cwrelse(struct cw *cw)
672 (void)cw;
674 #endif /* !HAVE_FCHDIR */
676 void
677 makeprint(struct str const *in, struct str *out)
679 static int print_all_chars = -1;
680 char const *inp, *maxp;
681 char *outp;
682 size_t msz, dist;
684 if (print_all_chars == -1)
685 print_all_chars = (value("print-all-chars") != NULL);
687 msz = in->l + 1;
688 out->s = outp = smalloc(msz);
689 inp = in->s;
690 maxp = inp + in->l;
692 if (print_all_chars) {
693 out->l = in->l;
694 memcpy(outp, inp, out->l);
695 goto jleave;
698 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
699 if (mb_cur_max > 1) {
700 char mbb[MB_LEN_MAX + 1];
701 wchar_t wc;
702 int i, n;
703 out->l = 0;
704 while (inp < maxp) {
705 if (*inp & 0200)
706 n = mbtowc(&wc, inp, maxp - inp);
707 else {
708 wc = *inp;
709 n = 1;
711 if (n < 0) {
712 /* FIXME Why mbtowc() resetting here?
713 * FIXME what about ISO 2022-JP plus -- those
714 * FIXME will loose shifts, then!
715 * FIXME THUS - we'd need special "known points"
716 * FIXME to do so - say, after a newline!!
717 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
718 (void)mbtowc(&wc, NULL, mb_cur_max);
719 wc = utf8 ? 0xFFFD : '?';
720 n = 1;
721 } else if (n == 0)
722 n = 1;
723 inp += n;
724 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
725 wc != '\b' && wc != '\t') {
726 if ((wc & ~(wchar_t)037) == 0)
727 wc = utf8 ? 0x2400 | wc : '?';
728 else if (wc == 0177)
729 wc = utf8 ? 0x2421 : '?';
730 else
731 wc = utf8 ? 0x2426 : '?';
733 if ((n = wctomb(mbb, wc)) <= 0)
734 continue;
735 out->l += n;
736 if (out->l >= msz - 1) {
737 dist = outp - out->s;
738 out->s = srealloc(out->s, msz += 32);
739 outp = &out->s[dist];
741 for (i = 0; i < n; i++)
742 *outp++ = mbb[i];
744 } else
745 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
747 int c;
748 while (inp < maxp) {
749 c = *inp++ & 0377;
750 if (!isprint(c) && c != '\n' && c != '\r' &&
751 c != '\b' && c != '\t')
752 c = '?';
753 *outp++ = c;
755 out->l = in->l;
757 jleave:
758 out->s[out->l] = '\0';
761 char *
762 prstr(const char *s)
764 struct str in, out;
765 char *rp;
767 in.s = UNCONST(s);
768 in.l = strlen(s);
769 makeprint(&in, &out);
770 rp = salloc(out.l + 1);
771 memcpy(rp, out.s, out.l);
772 rp[out.l] = '\0';
773 free(out.s);
774 return rp;
778 prout(const char *s, size_t sz, FILE *fp)
780 struct str in, out;
781 int n;
783 in.s = UNCONST(s);
784 in.l = sz;
785 makeprint(&in, &out);
786 n = fwrite(out.s, 1, out.l, fp);
787 free(out.s);
788 return n;
791 size_t
792 putuc(int u, int c, FILE *fp)
794 size_t rv;
796 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
797 if (utf8 && u & ~(wchar_t)0177) {
798 char mbb[MB_LEN_MAX];
799 int i, n;
800 if ((n = wctomb(mbb, u)) > 0) {
801 rv = wcwidth(u);
802 for (i = 0; i < n; ++i)
803 if (putc(mbb[i] & 0377, fp) == EOF) {
804 rv = 0;
805 break;
807 } else if (n == 0)
808 rv = (putc('\0', fp) != EOF);
809 else
810 rv = 0;
811 } else
812 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
813 rv = (putc(c, fp) != EOF);
814 return rv;
817 void
818 time_current_update(struct time_current *tc, bool_t full_update)
820 tc->tc_time = time(NULL);
821 if (full_update) {
822 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
823 memcpy(&tc->tc_local, localtime(&tc->tc_time),
824 sizeof tc->tc_local);
825 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
829 #ifndef HAVE_GETOPT
830 char *my_optarg;
831 int my_optind = 1, /*my_opterr = 1,*/ my_optopt;
834 my_getopt(int argc, char *const argv[], char const *optstring) /* XXX */
836 static char const *lastp;
837 int colon;
838 char const *curp;
840 if (optstring[0] == ':') {
841 colon = 1;
842 optstring++;
843 } else
844 colon = 0;
845 if (lastp) {
846 curp = lastp;
847 lastp = 0;
848 } else {
849 if (optind >= argc || argv[optind] == 0 ||
850 argv[optind][0] != '-' ||
851 argv[optind][1] == '\0')
852 return -1;
853 if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
854 optind++;
855 return -1;
857 curp = &argv[optind][1];
859 optopt = curp[0];
860 while (optstring[0]) {
861 if (optstring[0] == ':' || optstring[0] != optopt) {
862 optstring++;
863 continue;
865 if (optstring[1] == ':') {
866 if (curp[1] != '\0') {
867 optarg = UNCONST(&curp[1]);
868 optind++;
869 } else {
870 if ((optind += 2) > argc) {
871 if (!colon /*&& opterr*/) {
872 fprintf(stderr, tr(79,
873 "%s: option requires an argument -- %c\n"),
874 argv[0], (char)optopt);
876 return colon ? ':' : '?';
878 optarg = argv[optind - 1];
880 } else {
881 if (curp[1] != '\0')
882 lastp = &curp[1];
883 else
884 optind++;
885 optarg = 0;
887 return optopt;
889 if (!colon /*&& opterr*/)
890 fprintf(stderr, tr(78, "%s: illegal option -- %c\n"),
891 argv[0], optopt);
892 if (curp[1] != '\0')
893 lastp = &curp[1];
894 else
895 optind++;
896 optarg = 0;
897 return '?';
899 #endif /* HAVE_GETOPT */
901 static void
902 out_of_memory(void)
904 panic("no memory");
907 void *
908 (smalloc_safe)(size_t s SMALLOC_DEBUG_ARGS)
910 void *rv;
912 hold_all_sigs();
913 rv = (smalloc)(s SMALLOC_DEBUG_ARGSCALL);
914 rele_all_sigs();
915 return rv;
918 void *
919 (srealloc_safe)(void *v, size_t s SMALLOC_DEBUG_ARGS)
921 void *rv;
923 hold_all_sigs();
924 rv = (srealloc)(v, s SMALLOC_DEBUG_ARGSCALL);
925 rele_all_sigs();
926 return rv;
929 void *
930 (scalloc_safe)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
932 void *rv;
934 hold_all_sigs();
935 rv = (scalloc)(nmemb, size SMALLOC_DEBUG_ARGSCALL);
936 rele_all_sigs();
937 return rv;
940 #ifndef HAVE_ASSERTS
941 void *
942 smalloc(size_t s SMALLOC_DEBUG_ARGS)
944 void *p;
946 if (s == 0)
947 s = 1;
948 if ((p = malloc(s)) == NULL)
949 out_of_memory();
950 return p;
953 void *
954 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
956 void *r;
958 if (s == 0)
959 s = 1;
960 if (v == NULL)
961 return smalloc(s);
962 if ((r = realloc(v, s)) == NULL)
963 out_of_memory();
964 return r;
967 void *
968 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
970 void *vp;
972 if (size == 0)
973 size = 1;
974 if ((vp = calloc(nmemb, size)) == NULL)
975 out_of_memory();
976 return vp;
979 #else /* !HAVE_ASSERTS */
980 struct chunk {
981 struct chunk *prev;
982 struct chunk *next;
983 char const *file;
984 us_it line;
985 uc_it isfree;
986 sc_it __dummy;
987 ui_it size;
990 union ptr {
991 struct chunk *c;
992 void *p;
995 struct chunk *_mlist, *_mfree;
997 void *
998 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1000 union ptr p;
1002 if (s == 0)
1003 s = 1;
1004 s += sizeof(struct chunk);
1006 if ((p.p = malloc(s)) == NULL)
1007 out_of_memory();
1008 p.c->prev = NULL;
1009 if ((p.c->next = _mlist) != NULL)
1010 _mlist->prev = p.c;
1011 p.c->file = mdbg_file;
1012 p.c->line = (us_it)mdbg_line;
1013 p.c->isfree = 0;
1014 p.c->size = (ui_it)s;
1015 _mlist = p.c++;
1016 return (p.p);
1019 void *
1020 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1022 union ptr p;
1024 if ((p.p = v) == NULL) {
1025 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1026 goto jleave;
1029 --p.c;
1030 if (p.c->isfree) {
1031 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1032 "\tLast seen: %s, line %d\n",
1033 mdbg_file, mdbg_line, p.c->file, p.c->line);
1034 goto jforce;
1037 if (p.c == _mlist)
1038 _mlist = p.c->next;
1039 else
1040 p.c->prev->next = p.c->next;
1041 if (p.c->next != NULL)
1042 p.c->next->prev = p.c->prev;
1044 jforce:
1045 if (s == 0)
1046 s = 1;
1047 s += sizeof(struct chunk);
1049 if ((p.p = realloc(p.c, s)) == NULL)
1050 out_of_memory();
1051 p.c->prev = NULL;
1052 if ((p.c->next = _mlist) != NULL)
1053 _mlist->prev = p.c;
1054 p.c->file = mdbg_file;
1055 p.c->line = (us_it)mdbg_line;
1056 p.c->isfree = 0;
1057 p.c->size = (ui_it)s;
1058 _mlist = p.c++;
1059 jleave:
1060 return (p.p);
1063 void *
1064 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1066 union ptr p;
1068 if (size == 0)
1069 size = 1;
1070 if (nmemb == 0)
1071 nmemb = 1;
1072 size *= nmemb;
1073 size += sizeof(struct chunk);
1075 if ((p.p = malloc(size)) == NULL)
1076 out_of_memory();
1077 memset(p.p, 0, size);
1078 p.c->prev = NULL;
1079 if ((p.c->next = _mlist) != NULL)
1080 _mlist->prev = p.c;
1081 p.c->file = mdbg_file;
1082 p.c->line = (us_it)mdbg_line;
1083 p.c->isfree = 0;
1084 p.c->size = (ui_it)size;
1085 _mlist = p.c++;
1086 return (p.p);
1089 void
1090 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1092 union ptr p;
1094 if ((p.p = v) == NULL) {
1095 fprintf(stderr, "sfree(NULL) from %s, line %d\n",
1096 mdbg_file, mdbg_line);
1097 goto jleave;
1100 --p.c;
1101 if (p.c->isfree) {
1102 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1103 "\tLast seen: %s, line %d\n",
1104 mdbg_file, mdbg_line, p.c->file, p.c->line);
1105 goto jleave;
1108 if (p.c == _mlist)
1109 _mlist = p.c->next;
1110 else
1111 p.c->prev->next = p.c->next;
1112 if (p.c->next != NULL)
1113 p.c->next->prev = p.c->prev;
1114 p.c->isfree = 1;
1116 if (options & OPT_DEBUG) {
1117 p.c->next = _mfree;
1118 _mfree = p.c;
1119 } else
1120 (free)(p.c);
1121 jleave: ;
1124 void
1125 smemreset(void)
1127 union ptr p;
1128 ul_it c = 0, s = 0;
1130 for (p.c = _mfree; p.c != NULL;) {
1131 void *vp = p.c;
1132 ++c;
1133 s += p.c->size;
1134 p.c = p.c->next;
1135 (free)(vp);
1137 _mfree = NULL;
1139 if (options & OPT_DEBUG)
1140 fprintf(stderr, "smemreset(): freed %lu regions/%lu bytes\n",
1141 c, s);
1145 (smemtrace)(void *v)
1147 FILE *fp;
1148 char *cp;
1149 union ptr p;
1150 size_t lines = 0;
1152 v = (void*)0x1;
1153 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1154 perror("tmpfile");
1155 goto jleave;
1157 rm(cp);
1158 Ftfree(&cp);
1160 fprintf(fp, "Currently allocated memory chunks:\n");
1161 for (p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next)
1162 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1163 (void*)(p.c + 1),
1164 (ui_it)(p.c->size - sizeof(struct chunk)),
1165 p.c->file, p.c->line);
1167 if (options & OPT_DEBUG) {
1168 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1169 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next)
1170 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1171 (void*)(p.c + 1),
1172 (ui_it)(p.c->size - sizeof(struct chunk)),
1173 p.c->file, p.c->line);
1176 page_or_print(fp, lines);
1177 Fclose(fp);
1178 v = NULL;
1179 jleave:
1180 return (v != NULL);
1182 #endif /* HAVE_ASSERTS */