Merge branch 'topic/srelax'
[s-mailx.git] / auxlily.c
blob79eaefabb3fe12c4ba16b7b9f69128ec6701ca10
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>
47 #include <pwd.h>
49 #ifdef HAVE_SOCKETS
50 # ifdef HAVE_IPV6
51 # include <sys/socket.h>
52 # endif
54 # include <netdb.h>
55 #endif
57 #ifdef HAVE_MD5
58 # include "md5.h"
59 #endif
61 /* {hold,rele}_all_sigs() */
62 static size_t _alls_depth;
63 static sigset_t _alls_nset, _alls_oset;
65 void
66 panic(char const *format, ...)
68 va_list ap;
70 fprintf(stderr, tr(1, "Panic: "));
72 va_start(ap, format);
73 vfprintf(stderr, format, ap);
74 va_end(ap);
76 fputs("\n", stderr);
77 fflush(stderr);
78 abort();
81 void
82 hold_all_sigs(void)
84 if (_alls_depth++ == 0) {
85 sigfillset(&_alls_nset);
86 sigdelset(&_alls_nset, SIGKILL);
87 sigdelset(&_alls_nset, SIGSTOP);
88 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
92 void
93 rele_all_sigs(void)
95 if (--_alls_depth == 0)
96 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
100 * Touch the named message by setting its MTOUCH flag.
101 * Touched messages have the effect of not being sent
102 * back to the system mailbox on exit.
104 void
105 touch(struct message *mp)
108 mp->m_flag |= MTOUCH;
109 if ((mp->m_flag & MREAD) == 0)
110 mp->m_flag |= MREAD|MSTATUS;
114 * Test to see if the passed file name is a directory.
115 * Return true if it is.
117 int
118 is_dir(char const *name)
120 struct stat sbuf;
122 if (stat(name, &sbuf) < 0)
123 return(0);
124 return(S_ISDIR(sbuf.st_mode));
128 * Count the number of arguments in the given string raw list.
130 int
131 argcount(char **argv)
133 char **ap;
135 for (ap = argv; *ap++ != NULL;)
137 return ap - argv - 1;
140 char *
141 colalign(const char *cp, int col, int fill, int *cols_decr_used_or_null)
143 int col_orig = col, n, sz;
144 char *nb, *np;
146 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
147 while (*cp) {
148 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
149 if (mb_cur_max > 1) {
150 wchar_t wc;
152 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
153 n = sz = 1;
154 } else {
155 if ((n = wcwidth(wc)) < 0)
156 n = 1;
158 } else
159 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
161 n = sz = 1;
163 if (n > col)
164 break;
165 col -= n;
166 if (sz == 1 && spacechar(*cp)) {
167 *np++ = ' ';
168 cp++;
169 } else
170 while (sz--)
171 *np++ = *cp++;
174 if (fill && col != 0) {
175 if (fill > 0) {
176 memmove(nb + col, nb, (size_t)(np - nb));
177 memset(nb, ' ', col);
178 } else
179 memset(np, ' ', col);
180 np += col;
181 col = 0;
184 *np = '\0';
185 if (cols_decr_used_or_null != NULL)
186 *cols_decr_used_or_null -= col_orig - col;
187 return nb;
190 size_t
191 paging_seems_sensible(void)
193 size_t ret = 0;
194 char const *cp;
196 if (IS_TTY_SESSION() && (cp = voption("crt")) != NULL)
197 ret = (*cp != '\0') ? (size_t)atol(cp) : (size_t)scrnheight;
198 return ret;
201 void
202 page_or_print(FILE *fp, size_t lines)
204 size_t rows;
205 int c;
207 fflush_rewind(fp);
209 if ((rows = paging_seems_sensible()) != 0 && lines == 0) {
210 while ((c = getc(fp)) != EOF)
211 if (c == '\n' && ++lines > rows)
212 break;
213 rewind(fp);
216 if (rows != 0 && lines >= rows)
217 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
218 else
219 while ((c = getc(fp)) != EOF)
220 putchar(c);
223 enum protocol
224 which_protocol(const char *name)
226 register const char *cp;
227 char *np;
228 size_t sz;
229 struct stat st;
230 enum protocol p;
232 if (name[0] == '%' && name[1] == ':')
233 name += 2;
234 for (cp = name; *cp && *cp != ':'; cp++)
235 if (!alnumchar(*cp&0377))
236 goto file;
237 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
238 if (strncmp(name, "pop3://", 7) == 0)
239 #ifdef HAVE_POP3
240 return PROTO_POP3;
241 #else
242 fprintf(stderr,
243 tr(216, "No POP3 support compiled in.\n"));
244 #endif
245 if (strncmp(name, "pop3s://", 8) == 0)
246 #ifdef HAVE_SSL
247 return PROTO_POP3;
248 #else
249 fprintf(stderr,
250 tr(225, "No SSL support compiled in.\n"));
251 #endif
252 if (strncmp(name, "imap://", 7) == 0)
253 #ifdef HAVE_IMAP
254 return PROTO_IMAP;
255 #else
256 fprintf(stderr,
257 tr(269, "No IMAP support compiled in.\n"));
258 #endif
259 if (strncmp(name, "imaps://", 8) == 0)
260 #ifdef HAVE_SSL
261 return PROTO_IMAP;
262 #else
263 fprintf(stderr,
264 tr(225, "No SSL support compiled in.\n"));
265 #endif
266 return PROTO_UNKNOWN;
267 } else {
268 /* TODO This is the de facto maildir code and thus belongs
269 * TODO into maildir! */
270 file: p = PROTO_FILE;
271 np = ac_alloc((sz = strlen(name)) + 5);
272 memcpy(np, name, sz + 1);
273 if (stat(name, &st) == 0) {
274 if (S_ISDIR(st.st_mode)) {
275 strcpy(&np[sz], "/tmp");
276 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
277 strcpy(&np[sz], "/new");
278 if (stat(np, &st) == 0 &&
279 S_ISDIR(st.st_mode)) {
280 strcpy(&np[sz], "/cur");
281 if (stat(np, &st) == 0 &&
282 S_ISDIR(st.st_mode))
283 p = PROTO_MAILDIR;
287 } else {
288 strcpy(&np[sz], ".gz");
289 if (stat(np, &st) < 0) {
290 strcpy(&np[sz], ".bz2");
291 if (stat(np, &st) < 0) {
292 if ((cp = value("newfolders")) != 0 &&
293 strcmp(cp, "maildir") == 0)
294 p = PROTO_MAILDIR;
298 ac_free(np);
299 return p;
303 unsigned
304 pjw(const char *cp)
306 unsigned h = 0, g;
308 cp--;
309 while (*++cp) {
310 h = (h << 4 & 0xffffffff) + (*cp&0377);
311 if ((g = h & 0xf0000000) != 0) {
312 h = h ^ g >> 24;
313 h = h ^ g;
316 return h;
319 long
320 nextprime(long n)
322 const long primes[] = {
323 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
324 131071, 262139, 524287, 1048573, 2097143, 4194301,
325 8388593, 16777213, 33554393, 67108859, 134217689,
326 268435399, 536870909, 1073741789, 2147483647
328 long mprime = 7;
329 size_t i;
331 for (i = 0; i < sizeof primes / sizeof *primes; i++)
332 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
333 n < 262144 ? n*2 : n))
334 break;
335 if (i == sizeof primes / sizeof *primes)
336 mprime = n; /* not so prime, but better than failure */
337 return mprime;
341 expand_shell_escape(char const **s, bool_t use_nail_extensions)
343 char const *xs = *s;
344 int c, n;
346 if ((c = *xs & 0xFF) == '\0')
347 goto jleave;
348 ++xs;
349 if (c != '\\')
350 goto jleave;
352 switch ((c = *xs & 0xFF)) {
353 case '\\': break;
354 case 'a': c = '\a'; break;
355 case 'b': c = '\b'; break;
356 case 'c': c = PROMPT_STOP;break;
357 case 'f': c = '\f'; break;
358 case 'n': c = '\n'; break;
359 case 'r': c = '\r'; break;
360 case 't': c = '\t'; break;
361 case 'v': c = '\v'; break;
362 case '0':
363 for (++xs, c = 0, n = 4; --n > 0 && octalchar(*xs); ++xs) {
364 c <<= 3;
365 c |= *xs - '0';
367 goto jleave;
368 /* S-nail extension for nice (get)prompt(()) support */
369 case '?':
370 case '$':
371 case '@':
372 if (use_nail_extensions) {
373 switch (c) {
374 case '?':
375 c = exec_last_comm_error ? '1' : '0';
376 break;
377 case '$':
378 c = PROMPT_DOLLAR;
379 break;
380 case '@':
381 c = PROMPT_AT;
382 break;
384 break;
386 /* FALLTHRU */
387 case '\0':
388 /* A sole <backslash> at EOS is treated as-is! */
389 /* FALLTHRU */
390 default:
391 c = '\\';
392 goto jleave;
394 ++xs;
395 jleave:
396 *s = xs;
397 return c;
400 char *
401 getprompt(void)
403 static char buf[PROMPT_BUFFER_SIZE];
405 char const *ccp;
407 if (options & OPT_NOPROMPT)
408 buf[0] = '\0';
409 else if ((ccp = value("prompt")) == NULL) {
410 buf[0] = value("bsdcompat") ? '&' : '?';
411 buf[1] = ' ';
412 buf[2] = '\0';
413 } else {
414 char *cp;
416 for (cp = buf; PTRCMP(cp, <, buf + sizeof(buf) - 1); ++cp) {
417 char const *a;
418 size_t l;
419 int c = expand_shell_escape(&ccp, TRU1);
420 if (c > 0) {
421 *cp = (char)c;
422 continue;
424 if (c == 0 || c == PROMPT_STOP)
425 break;
427 a = (c == PROMPT_DOLLAR) ? account_name : displayname;
428 if (a == NULL)
429 a = "";
430 l = strlen(a);
431 if (PTRCMP(cp + l, >=, buf + sizeof(buf) - 1))
432 *cp++ = '?';
433 else {
434 memcpy(cp, a, l);
435 cp += --l;
438 *cp = '\0';
440 return buf;
443 char *
444 getname(int uid)
446 struct passwd *pw = getpwuid(uid);
448 return pw == NULL ? NULL : pw->pw_name;
451 char *
452 username(void)
454 char *np;
455 uid_t uid;
457 if ((np = getenv("USER")) != NULL)
458 goto jleave;
459 if ((np = getname(uid = getuid())) != NULL)
460 goto jleave;
461 panic(tr(201, "Cannot associate a name with uid %d\n"), (int)uid);
462 jleave:
463 return (np);
466 char *
467 nodename(int mayoverride)
469 static char *hostname;
470 struct utsname ut;
471 char *hn;
472 #ifdef HAVE_SOCKETS
473 # ifdef HAVE_IPV6
474 struct addrinfo hints, *res;
475 # else
476 struct hostent *hent;
477 # endif
478 #endif
480 if (mayoverride && (hn = value("hostname")) != NULL && *hn != '\0') {
481 if (hostname != NULL)
482 free(hostname);
483 hostname = sstrdup(hn);
484 } else if (hostname == NULL) {
485 uname(&ut);
486 hn = ut.nodename;
487 #ifdef HAVE_SOCKETS
488 # ifdef HAVE_IPV6
489 memset(&hints, 0, sizeof hints);
490 hints.ai_family = AF_UNSPEC;
491 hints.ai_socktype = SOCK_DGRAM; /* dummy */
492 hints.ai_flags = AI_CANONNAME;
493 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
494 if (res->ai_canonname != NULL) {
495 size_t l = strlen(res->ai_canonname);
496 hn = ac_alloc(l + 1);
497 memcpy(hn, res->ai_canonname, l + 1);
499 freeaddrinfo(res);
501 # else
502 hent = gethostbyname(hn);
503 if (hent != NULL) {
504 hn = hent->h_name;
506 # endif
507 #endif
508 hostname = sstrdup(hn);
509 #if defined HAVE_SOCKETS && defined HAVE_IPV6
510 if (hn != ut.nodename)
511 ac_free(hn);
512 #endif
514 return (hostname);
517 char *
518 lookup_password_for_token(char const *token)
520 size_t tl;
521 char *var, *cp;
523 tl = strlen(token);
524 var = ac_alloc(tl + 10);
526 memcpy(var, "password-", 9);
527 memcpy(var + 9, token, tl);
528 var[tl + 9] = '\0';
530 if ((cp = value(var)) != NULL)
531 cp = savestr(cp);
532 ac_free(var);
533 return cp;
536 char *
537 getrandstring(size_t length)
539 static unsigned char nodedigest[16];
540 static pid_t pid;
541 struct str b64;
542 int fd = -1;
543 char *data, *cp;
544 size_t i;
545 #ifdef HAVE_MD5
546 MD5_CTX ctx;
547 #else
548 size_t j;
549 #endif
551 data = ac_alloc(length);
552 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
553 length != (size_t)read(fd, data, length)) {
554 if (pid == 0) {
555 pid = getpid();
556 srand(pid);
557 cp = nodename(0);
558 #ifdef HAVE_MD5
559 MD5Init(&ctx);
560 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
561 MD5Final(nodedigest, &ctx);
562 #else
563 /* In that case it's only used for boundaries and
564 * Message-Id:s so that srand(3) should suffice */
565 j = strlen(cp) + 1;
566 for (i = 0; i < sizeof(nodedigest); ++i)
567 nodedigest[i] = (unsigned char)(
568 cp[i % j] ^ rand());
569 #endif
571 for (i = 0; i < length; i++)
572 data[i] = (char)(
573 (int)(255 * (rand() / (RAND_MAX + 1.0))) ^
574 nodedigest[i % sizeof nodedigest]);
576 if (fd >= 0)
577 close(fd);
579 (void)b64_encode_buf(&b64, data, length, B64_SALLOC);
580 ac_free(data);
581 assert(length < b64.l);
582 b64.s[length] = '\0';
583 return b64.s;
586 #ifdef HAVE_MD5
587 char *
588 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
590 char const *cp = vp;
591 size_t i, j;
593 for (i = 0; i < MD5TOHEX_SIZE / 2; i++) {
594 j = i << 1;
595 hex[j] = hexchar((cp[i] & 0xf0) >> 4);
596 hex[++j] = hexchar(cp[i] & 0x0f);
598 hex[MD5TOHEX_SIZE] = '\0';
599 return hex;
602 char *
603 cram_md5_string(char const *user, char const *pass, char const *b64)
605 struct str in, out;
606 char digest[16], *cp;
607 size_t lu;
609 out.s = NULL;
610 in.s = UNCONST(b64);
611 in.l = strlen(in.s);
612 (void)b64_decode(&out, &in, NULL);
613 assert(out.s != NULL);
615 hmac_md5((unsigned char*)out.s, out.l, UNCONST(pass), strlen(pass),
616 digest);
617 free(out.s);
618 cp = md5tohex(salloc(MD5TOHEX_SIZE + 1), digest);
620 lu = strlen(user);
621 in.l = lu + MD5TOHEX_SIZE +1;
622 in.s = ac_alloc(lu + 1 + MD5TOHEX_SIZE +1);
623 memcpy(in.s, user, lu);
624 in.s[lu] = ' ';
625 memcpy(in.s + lu + 1, cp, MD5TOHEX_SIZE);
626 (void)b64_encode(&out, &in, B64_SALLOC|B64_CRLF);
627 ac_free(in.s);
628 return out.s;
630 #endif /* HAVE_MD5 */
632 enum okay
633 makedir(const char *name)
635 int e;
636 struct stat st;
638 if (mkdir(name, 0700) < 0) {
639 e = errno;
640 if ((e == EEXIST || e == ENOSYS) &&
641 stat(name, &st) == 0 &&
642 (st.st_mode&S_IFMT) == S_IFDIR)
643 return OKAY;
644 return STOP;
646 return OKAY;
649 #ifdef HAVE_FCHDIR
650 enum okay
651 cwget(struct cw *cw)
653 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
654 return STOP;
655 if (fchdir(cw->cw_fd) < 0) {
656 close(cw->cw_fd);
657 return STOP;
659 return OKAY;
662 enum okay
663 cwret(struct cw *cw)
665 if (fchdir(cw->cw_fd) < 0)
666 return STOP;
667 return OKAY;
670 void
671 cwrelse(struct cw *cw)
673 close(cw->cw_fd);
675 #else /* !HAVE_FCHDIR */
676 enum okay
677 cwget(struct cw *cw)
679 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
680 return STOP;
681 return OKAY;
684 enum okay
685 cwret(struct cw *cw)
687 if (chdir(cw->cw_wd) < 0)
688 return STOP;
689 return OKAY;
692 /*ARGSUSED*/
693 void
694 cwrelse(struct cw *cw)
696 (void)cw;
698 #endif /* !HAVE_FCHDIR */
700 void
701 makeprint(struct str const *in, struct str *out)
703 static int print_all_chars = -1;
704 char const *inp, *maxp;
705 char *outp;
706 size_t msz, dist;
708 if (print_all_chars == -1)
709 print_all_chars = (value("print-all-chars") != NULL);
711 msz = in->l + 1;
712 out->s = outp = smalloc(msz);
713 inp = in->s;
714 maxp = inp + in->l;
716 if (print_all_chars) {
717 out->l = in->l;
718 memcpy(outp, inp, out->l);
719 goto jleave;
722 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
723 if (mb_cur_max > 1) {
724 char mbb[MB_LEN_MAX + 1];
725 wchar_t wc;
726 int i, n;
727 out->l = 0;
728 while (inp < maxp) {
729 if (*inp & 0200)
730 n = mbtowc(&wc, inp, maxp - inp);
731 else {
732 wc = *inp;
733 n = 1;
735 if (n < 0) {
736 /* FIXME Why mbtowc() resetting here?
737 * FIXME what about ISO 2022-JP plus -- those
738 * FIXME will loose shifts, then!
739 * FIXME THUS - we'd need special "known points"
740 * FIXME to do so - say, after a newline!!
741 * FIXME WE NEED TO CHANGE ALL USES +MBLEN! */
742 (void)mbtowc(&wc, NULL, mb_cur_max);
743 wc = utf8 ? 0xFFFD : '?';
744 n = 1;
745 } else if (n == 0)
746 n = 1;
747 inp += n;
748 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
749 wc != '\b' && wc != '\t') {
750 if ((wc & ~(wchar_t)037) == 0)
751 wc = utf8 ? 0x2400 | wc : '?';
752 else if (wc == 0177)
753 wc = utf8 ? 0x2421 : '?';
754 else
755 wc = utf8 ? 0x2426 : '?';
757 if ((n = wctomb(mbb, wc)) <= 0)
758 continue;
759 out->l += n;
760 if (out->l >= msz - 1) {
761 dist = outp - out->s;
762 out->s = srealloc(out->s, msz += 32);
763 outp = &out->s[dist];
765 for (i = 0; i < n; i++)
766 *outp++ = mbb[i];
768 } else
769 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
771 int c;
772 while (inp < maxp) {
773 c = *inp++ & 0377;
774 if (!isprint(c) && c != '\n' && c != '\r' &&
775 c != '\b' && c != '\t')
776 c = '?';
777 *outp++ = c;
779 out->l = in->l;
781 jleave:
782 out->s[out->l] = '\0';
785 char *
786 prstr(const char *s)
788 struct str in, out;
789 char *rp;
791 in.s = UNCONST(s);
792 in.l = strlen(s);
793 makeprint(&in, &out);
794 rp = salloc(out.l + 1);
795 memcpy(rp, out.s, out.l);
796 rp[out.l] = '\0';
797 free(out.s);
798 return rp;
802 prout(const char *s, size_t sz, FILE *fp)
804 struct str in, out;
805 int n;
807 in.s = UNCONST(s);
808 in.l = sz;
809 makeprint(&in, &out);
810 n = fwrite(out.s, 1, out.l, fp);
811 free(out.s);
812 return n;
815 size_t
816 putuc(int u, int c, FILE *fp)
818 size_t rv;
820 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
821 if (utf8 && u & ~(wchar_t)0177) {
822 char mbb[MB_LEN_MAX];
823 int i, n;
824 if ((n = wctomb(mbb, u)) > 0) {
825 rv = wcwidth(u);
826 for (i = 0; i < n; ++i)
827 if (putc(mbb[i] & 0377, fp) == EOF) {
828 rv = 0;
829 break;
831 } else if (n == 0)
832 rv = (putc('\0', fp) != EOF);
833 else
834 rv = 0;
835 } else
836 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
837 rv = (putc(c, fp) != EOF);
838 return rv;
841 void
842 time_current_update(struct time_current *tc, bool_t full_update)
844 tc->tc_time = time(NULL);
845 if (full_update) {
846 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
847 memcpy(&tc->tc_local, localtime(&tc->tc_time),
848 sizeof tc->tc_local);
849 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
853 #ifndef HAVE_GETOPT
854 char *my_optarg;
855 int my_optind = 1, /*my_opterr = 1,*/ my_optopt;
858 my_getopt(int argc, char *const argv[], char const *optstring) /* XXX */
860 static char const *lastp;
861 int colon;
862 char const *curp;
864 if (optstring[0] == ':') {
865 colon = 1;
866 optstring++;
867 } else
868 colon = 0;
869 if (lastp) {
870 curp = lastp;
871 lastp = 0;
872 } else {
873 if (optind >= argc || argv[optind] == 0 ||
874 argv[optind][0] != '-' ||
875 argv[optind][1] == '\0')
876 return -1;
877 if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
878 optind++;
879 return -1;
881 curp = &argv[optind][1];
883 optopt = curp[0];
884 while (optstring[0]) {
885 if (optstring[0] == ':' || optstring[0] != optopt) {
886 optstring++;
887 continue;
889 if (optstring[1] == ':') {
890 if (curp[1] != '\0') {
891 optarg = UNCONST(&curp[1]);
892 optind++;
893 } else {
894 if ((optind += 2) > argc) {
895 if (!colon /*&& opterr*/) {
896 fprintf(stderr, tr(79,
897 "%s: option requires an argument -- %c\n"),
898 argv[0], (char)optopt);
900 return colon ? ':' : '?';
902 optarg = argv[optind - 1];
904 } else {
905 if (curp[1] != '\0')
906 lastp = &curp[1];
907 else
908 optind++;
909 optarg = 0;
911 return optopt;
913 if (!colon /*&& opterr*/)
914 fprintf(stderr, tr(78, "%s: illegal option -- %c\n"),
915 argv[0], optopt);
916 if (curp[1] != '\0')
917 lastp = &curp[1];
918 else
919 optind++;
920 optarg = 0;
921 return '?';
923 #endif /* HAVE_GETOPT */
925 static void
926 out_of_memory(void)
928 panic("no memory");
931 void *
932 (smalloc_safe)(size_t s SMALLOC_DEBUG_ARGS)
934 void *rv;
936 hold_all_sigs();
937 rv = (smalloc)(s SMALLOC_DEBUG_ARGSCALL);
938 rele_all_sigs();
939 return rv;
942 void *
943 (srealloc_safe)(void *v, size_t s SMALLOC_DEBUG_ARGS)
945 void *rv;
947 hold_all_sigs();
948 rv = (srealloc)(v, s SMALLOC_DEBUG_ARGSCALL);
949 rele_all_sigs();
950 return rv;
953 void *
954 (scalloc_safe)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
956 void *rv;
958 hold_all_sigs();
959 rv = (scalloc)(nmemb, size SMALLOC_DEBUG_ARGSCALL);
960 rele_all_sigs();
961 return rv;
964 #ifndef HAVE_ASSERTS
965 void *
966 smalloc(size_t s SMALLOC_DEBUG_ARGS)
968 void *p;
970 if (s == 0)
971 s = 1;
972 if ((p = malloc(s)) == NULL)
973 out_of_memory();
974 return p;
977 void *
978 srealloc(void *v, size_t s SMALLOC_DEBUG_ARGS)
980 void *r;
982 if (s == 0)
983 s = 1;
984 if (v == NULL)
985 return smalloc(s);
986 if ((r = realloc(v, s)) == NULL)
987 out_of_memory();
988 return r;
991 void *
992 scalloc(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
994 void *vp;
996 if (size == 0)
997 size = 1;
998 if ((vp = calloc(nmemb, size)) == NULL)
999 out_of_memory();
1000 return vp;
1003 #else /* !HAVE_ASSERTS */
1004 struct chunk {
1005 struct chunk *prev;
1006 struct chunk *next;
1007 char const *file;
1008 us_it line;
1009 uc_it isfree;
1010 sc_it __dummy;
1011 ui_it size;
1014 union ptr {
1015 struct chunk *c;
1016 void *p;
1019 struct chunk *_mlist, *_mfree;
1021 void *
1022 (smalloc)(size_t s SMALLOC_DEBUG_ARGS)
1024 union ptr p;
1026 if (s == 0)
1027 s = 1;
1028 s += sizeof(struct chunk);
1030 if ((p.p = malloc(s)) == NULL)
1031 out_of_memory();
1032 p.c->prev = NULL;
1033 if ((p.c->next = _mlist) != NULL)
1034 _mlist->prev = p.c;
1035 p.c->file = mdbg_file;
1036 p.c->line = (us_it)mdbg_line;
1037 p.c->isfree = 0;
1038 p.c->size = (ui_it)s;
1039 _mlist = p.c++;
1040 return (p.p);
1043 void *
1044 (srealloc)(void *v, size_t s SMALLOC_DEBUG_ARGS)
1046 union ptr p;
1048 if ((p.p = v) == NULL) {
1049 p.p = (smalloc)(s, mdbg_file, mdbg_line);
1050 goto jleave;
1053 --p.c;
1054 if (p.c->isfree) {
1055 fprintf(stderr, "srealloc(): region freed! At %s, line %d\n"
1056 "\tLast seen: %s, line %d\n",
1057 mdbg_file, mdbg_line, p.c->file, p.c->line);
1058 goto jforce;
1061 if (p.c == _mlist)
1062 _mlist = p.c->next;
1063 else
1064 p.c->prev->next = p.c->next;
1065 if (p.c->next != NULL)
1066 p.c->next->prev = p.c->prev;
1068 jforce:
1069 if (s == 0)
1070 s = 1;
1071 s += sizeof(struct chunk);
1073 if ((p.p = realloc(p.c, s)) == NULL)
1074 out_of_memory();
1075 p.c->prev = NULL;
1076 if ((p.c->next = _mlist) != NULL)
1077 _mlist->prev = p.c;
1078 p.c->file = mdbg_file;
1079 p.c->line = (us_it)mdbg_line;
1080 p.c->isfree = 0;
1081 p.c->size = (ui_it)s;
1082 _mlist = p.c++;
1083 jleave:
1084 return (p.p);
1087 void *
1088 (scalloc)(size_t nmemb, size_t size SMALLOC_DEBUG_ARGS)
1090 union ptr p;
1092 if (size == 0)
1093 size = 1;
1094 if (nmemb == 0)
1095 nmemb = 1;
1096 size *= nmemb;
1097 size += sizeof(struct chunk);
1099 if ((p.p = malloc(size)) == NULL)
1100 out_of_memory();
1101 memset(p.p, 0, size);
1102 p.c->prev = NULL;
1103 if ((p.c->next = _mlist) != NULL)
1104 _mlist->prev = p.c;
1105 p.c->file = mdbg_file;
1106 p.c->line = (us_it)mdbg_line;
1107 p.c->isfree = 0;
1108 p.c->size = (ui_it)size;
1109 _mlist = p.c++;
1110 return (p.p);
1113 void
1114 (sfree)(void *v SMALLOC_DEBUG_ARGS)
1116 union ptr p;
1118 if ((p.p = v) == NULL) {
1119 fprintf(stderr, "sfree(NULL) from %s, line %d\n",
1120 mdbg_file, mdbg_line);
1121 goto jleave;
1124 --p.c;
1125 if (p.c->isfree) {
1126 fprintf(stderr, "sfree(): double-free avoided at %s, line %d\n"
1127 "\tLast seen: %s, line %d\n",
1128 mdbg_file, mdbg_line, p.c->file, p.c->line);
1129 goto jleave;
1132 if (p.c == _mlist)
1133 _mlist = p.c->next;
1134 else
1135 p.c->prev->next = p.c->next;
1136 if (p.c->next != NULL)
1137 p.c->next->prev = p.c->prev;
1138 p.c->isfree = 1;
1140 if (options & OPT_DEBUG) {
1141 p.c->next = _mfree;
1142 _mfree = p.c;
1143 } else
1144 (free)(p.c);
1145 jleave: ;
1148 void
1149 smemreset(void)
1151 union ptr p;
1152 ul_it c = 0, s = 0;
1154 for (p.c = _mfree; p.c != NULL;) {
1155 void *vp = p.c;
1156 ++c;
1157 s += p.c->size;
1158 p.c = p.c->next;
1159 (free)(vp);
1161 _mfree = NULL;
1163 if (options & OPT_DEBUG)
1164 fprintf(stderr, "smemreset(): freed %lu regions/%lu bytes\n",
1165 c, s);
1169 (smemtrace)(void *v)
1171 FILE *fp;
1172 char *cp;
1173 union ptr p;
1174 size_t lines = 0;
1176 v = (void*)0x1;
1177 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1178 perror("tmpfile");
1179 goto jleave;
1181 rm(cp);
1182 Ftfree(&cp);
1184 fprintf(fp, "Currently allocated memory chunks:\n");
1185 for (p.c = _mlist; p.c != NULL; ++lines, p.c = p.c->next)
1186 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1187 (void*)(p.c + 1),
1188 (ui_it)(p.c->size - sizeof(struct chunk)),
1189 p.c->file, p.c->line);
1191 if (options & OPT_DEBUG) {
1192 fprintf(fp, "sfree()d memory chunks awaiting free():\n");
1193 for (p.c = _mfree; p.c != NULL; ++lines, p.c = p.c->next)
1194 fprintf(fp, "%p (%5u bytes): %s, line %hu\n",
1195 (void*)(p.c + 1),
1196 (ui_it)(p.c->size - sizeof(struct chunk)),
1197 p.c->file, p.c->line);
1200 page_or_print(fp, lines);
1201 Fclose(fp);
1202 v = NULL;
1203 jleave:
1204 return (v != NULL);
1206 #endif /* HAVE_ASSERTS */