Try improve doc of *hostname*/Message-Id: relation
[s-mailx.git] / aux.c
blob13e45be715b302e1b0051cce5c01d31b09a2c0f5
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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"
41 #include "extern.h"
42 #include <sys/stat.h>
43 #include <utime.h>
44 #include <time.h>
45 #include <termios.h>
46 #include <ctype.h>
47 #ifdef HAVE_WCTYPE_H
48 #include <wctype.h>
49 #endif /* HAVE_WCTYPE_H */
50 #ifdef HAVE_WCWIDTH
51 #include <wchar.h>
52 #endif /* HAVE_WCWIDTH */
53 #include <errno.h>
54 #include <sys/stat.h>
55 #include <unistd.h>
56 #include <time.h>
57 #include <dirent.h>
58 #include <fcntl.h>
59 #include <limits.h>
61 #ifdef USE_MD5
62 # include "md5.h"
63 #endif
66 * Mail -- a mail program
68 * Auxiliary functions.
72 * Return a pointer to a dynamic copy of the argument.
74 char *
75 savestr(const char *str)
77 char *new;
78 int size = strlen(str) + 1;
80 if ((new = salloc(size)) != NULL)
81 memcpy(new, str, size);
82 return new;
86 * Make a copy of new argument incorporating old one.
88 char *
89 save2str(const char *str, const char *old)
91 char *new;
92 int newsize = strlen(str) + 1;
93 int oldsize = old ? strlen(old) + 1 : 0;
95 if ((new = salloc(newsize + oldsize)) != NULL) {
96 if (oldsize) {
97 memcpy(new, old, oldsize);
98 new[oldsize - 1] = ' ';
100 memcpy(new + oldsize, str, newsize);
102 return new;
105 char *
106 savecat(const char *s1, const char *s2)
108 const char *cp;
109 char *ns, *np;
111 np = ns = salloc(strlen(s1) + strlen(s2) + 1);
112 for (cp = s1; *cp; cp++)
113 *np++ = *cp;
114 for (cp = s2; *cp; cp++)
115 *np++ = *cp;
116 *np = '\0';
117 return ns;
120 #include <stdarg.h>
122 #ifndef HAVE_SNPRINTF
124 * Lazy vsprintf wrapper.
127 snprintf(char *str, size_t size, const char *format, ...)
129 va_list ap;
130 int ret;
132 va_start(ap, format);
133 ret = vsprintf(str, format, ap);
134 va_end(ap);
135 return ret;
137 #endif /* !HAVE_SNPRINTF */
140 * Announce a fatal error and die.
142 void
143 panic(const char *format, ...)
145 va_list ap;
147 va_start(ap, format);
148 fprintf(stderr, catgets(catd, CATSET, 1, "panic: "));
149 vfprintf(stderr, format, ap);
150 va_end(ap);
151 fprintf(stderr, catgets(catd, CATSET, 2, "\n"));
152 fflush(stderr);
153 abort();
156 void
157 holdint(void)
159 sigset_t set;
161 sigemptyset(&set);
162 sigaddset(&set, SIGINT);
163 sigprocmask(SIG_BLOCK, &set, NULL);
166 void
167 relseint(void)
169 sigset_t set;
171 sigemptyset(&set);
172 sigaddset(&set, SIGINT);
173 sigprocmask(SIG_UNBLOCK, &set, NULL);
177 * Touch the named message by setting its MTOUCH flag.
178 * Touched messages have the effect of not being sent
179 * back to the system mailbox on exit.
181 void
182 touch(struct message *mp)
185 mp->m_flag |= MTOUCH;
186 if ((mp->m_flag & MREAD) == 0)
187 mp->m_flag |= MREAD|MSTATUS;
191 * Test to see if the passed file name is a directory.
192 * Return true if it is.
194 int
195 is_dir(char *name)
197 struct stat sbuf;
199 if (stat(name, &sbuf) < 0)
200 return(0);
201 return(S_ISDIR(sbuf.st_mode));
205 * Count the number of arguments in the given string raw list.
207 int
208 argcount(char **argv)
210 char **ap;
212 for (ap = argv; *ap++ != NULL;)
214 return ap - argv - 1;
218 * Copy a string, lowercasing it as we go.
220 void
221 i_strcpy(char *dest, const char *src, int size)
223 char *max;
225 max=dest+size-1;
226 while (dest<=max) {
227 *dest++ = lowerconv(*src & 0377);
228 if (*src++ == '\0')
229 break;
233 char *
234 i_strdup(const char *src)
236 int sz;
237 char *dest;
239 sz = strlen(src) + 1;
240 dest = salloc(sz);
241 i_strcpy(dest, src, sz);
242 return dest;
246 * Convert a string to lowercase, in-place and with multibyte-aware.
248 void
249 makelow(char *cp)
251 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
252 if (mb_cur_max > 1) {
253 char *tp = cp;
254 wchar_t wc;
255 int len;
257 while (*cp) {
258 len = mbtowc(&wc, cp, mb_cur_max);
259 if (len < 0)
260 *tp++ = *cp++;
261 else {
262 wc = towlower(wc);
263 if (wctomb(tp, wc) == len)
264 tp += len, cp += len;
265 else
266 *tp++ = *cp++;
269 } else
270 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
273 *cp = tolower(*cp & 0377);
274 while (*cp++);
278 int
279 substr(const char *str, const char *sub)
281 const char *cp, *backup;
283 cp = sub;
284 backup = str;
285 while (*str && *cp) {
286 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
287 if (mb_cur_max > 1) {
288 wchar_t c, c2;
289 int sz;
291 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
292 goto singlebyte;
293 cp += sz;
294 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
295 goto singlebyte;
296 str += sz;
297 c = towupper(c);
298 c2 = towupper(c2);
299 if (c != c2) {
300 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
301 backup += sz;
302 str = backup;
303 } else
304 str = ++backup;
305 cp = sub;
307 } else
308 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
310 int c, c2;
312 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
313 singlebyte:
314 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
315 c = *cp++ & 0377;
316 if (islower(c))
317 c = toupper(c);
318 c2 = *str++ & 0377;
319 if (islower(c2))
320 c2 = toupper(c2);
321 if (c != c2) {
322 str = ++backup;
323 cp = sub;
327 return *cp == '\0';
330 char *
331 colalign(const char *cp, int col, int fill)
333 int n, sz;
334 char *nb, *np;
336 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
337 while (*cp) {
338 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
339 if (mb_cur_max > 1) {
340 wchar_t wc;
342 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
343 n = sz = 1;
344 } else {
345 if ((n = wcwidth(wc)) < 0)
346 n = 1;
348 } else
349 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
351 n = sz = 1;
353 if (n > col)
354 break;
355 col -= n;
356 if (sz == 1 && spacechar(*cp&0377)) {
357 *np++ = ' ';
358 cp++;
359 } else
360 while (sz--)
361 *np++ = *cp++;
363 if (fill)
364 while (col-- > 0)
365 *np++ = ' ';
366 *np = '\0';
367 return nb;
370 void
371 try_pager(FILE *fp)
373 long lines = 0;
374 int c;
375 char *cp;
377 fflush(fp);
378 rewind(fp);
379 while ((c = getc(fp)) != EOF)
380 if (c == '\n')
381 lines++;
382 rewind(fp);
383 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL &&
384 lines > (*cp ? atol(cp) : scrnheight))
385 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
386 else
387 while ((c = getc(fp)) != EOF)
388 putchar(c);
392 * The following code deals with input stacking to do source
393 * commands. All but the current file pointer are saved on
394 * the stack.
397 static int ssp; /* Top of file stack */
398 struct sstack {
399 FILE *s_file; /* File we were in. */
400 enum condition s_cond; /* Saved state of conditionals */
401 int s_loading; /* Loading .mailrc, etc. */
402 #define SSTACK 20
403 } sstack[SSTACK];
406 * Pushdown current input file and switch to a new one.
407 * Set the global flag "sourcing" so that others will realize
408 * that they are no longer reading from a tty (in all probability).
410 int
411 source(void *v)
413 char **arglist = v;
414 FILE *fi;
415 char *cp;
417 if ((cp = expand(*arglist)) == NULL)
418 return(1);
419 if ((fi = Fopen(cp, "r")) == NULL) {
420 perror(cp);
421 return(1);
423 if (ssp >= SSTACK - 1) {
424 printf(catgets(catd, CATSET, 3,
425 "Too much \"sourcing\" going on.\n"));
426 Fclose(fi);
427 return(1);
429 sstack[ssp].s_file = input;
430 sstack[ssp].s_cond = cond;
431 sstack[ssp].s_loading = loading;
432 ssp++;
433 loading = 0;
434 cond = CANY;
435 input = fi;
436 sourcing++;
437 return(0);
441 * Pop the current input back to the previous level.
442 * Update the "sourcing" flag as appropriate.
444 int
445 unstack(void)
447 if (ssp <= 0) {
448 printf(catgets(catd, CATSET, 4,
449 "\"Source\" stack over-pop.\n"));
450 sourcing = 0;
451 return(1);
453 Fclose(input);
454 if (cond != CANY)
455 printf(catgets(catd, CATSET, 5, "Unmatched \"if\"\n"));
456 ssp--;
457 cond = sstack[ssp].s_cond;
458 loading = sstack[ssp].s_loading;
459 input = sstack[ssp].s_file;
460 if (ssp == 0)
461 sourcing = loading;
462 return(0);
466 * Touch the indicated file.
467 * This is nifty for the shell.
469 void
470 alter(char *name)
472 struct stat sb;
473 struct utimbuf utb;
475 if (stat(name, &sb))
476 return;
477 utb.actime = time((time_t *)0) + 1;
478 utb.modtime = sb.st_mtime;
479 utime(name, &utb);
483 * Examine the passed line buffer and
484 * return true if it is all blanks and tabs.
486 int
487 blankline(char *linebuf)
489 char *cp;
491 for (cp = linebuf; *cp; cp++)
492 if (!blankchar(*cp & 0377))
493 return(0);
494 return(1);
498 * Are any of the characters in the two strings the same?
500 int
501 anyof(char *s1, char *s2)
504 while (*s1)
505 if (strchr(s2, *s1++))
506 return 1;
507 return 0;
511 * Determine if as1 is a valid prefix of as2.
512 * Return true if yep.
514 int
515 is_prefix(const char *as1, const char *as2)
517 const char *s1, *s2;
519 s1 = as1;
520 s2 = as2;
521 while (*s1++ == *s2)
522 if (*s2++ == '\0')
523 return(1);
524 return(*--s1 == '\0');
527 char *
528 last_at_before_slash(const char *sp)
530 const char *cp;
532 for (cp = sp; *cp; cp++)
533 if (*cp == '/')
534 break;
535 while (cp > sp && *--cp != '@');
536 return *cp == '@' ? (char *)cp : NULL;
539 enum protocol
540 which_protocol(const char *name)
542 register const char *cp;
543 char *np;
544 size_t sz;
545 struct stat st;
546 enum protocol p;
548 if (name[0] == '%' && name[1] == ':')
549 name += 2;
550 for (cp = name; *cp && *cp != ':'; cp++)
551 if (!alnumchar(*cp&0377))
552 goto file;
553 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
554 if (strncmp(name, "pop3://", 7) == 0)
555 #ifdef USE_POP3
556 return PROTO_POP3;
557 #else
558 fprintf(stderr, catgets(catd, CATSET, 216,
559 "No POP3 support compiled in.\n"));
560 #endif
561 if (strncmp(name, "pop3s://", 8) == 0)
562 #ifdef USE_SSL
563 return PROTO_POP3;
564 #else /* !USE_SSL */
565 fprintf(stderr, catgets(catd, CATSET, 225,
566 "No SSL support compiled in.\n"));
567 #endif /* !USE_SSL */
568 if (strncmp(name, "imap://", 7) == 0)
569 #ifdef USE_IMAP
570 return PROTO_IMAP;
571 #else
572 fprintf(stderr, catgets(catd, CATSET, 269,
573 "No IMAP support compiled in.\n"));
574 #endif
575 if (strncmp(name, "imaps://", 8) == 0)
576 #ifdef USE_SSL
577 return PROTO_IMAP;
578 #else /* !USE_SSL */
579 fprintf(stderr, catgets(catd, CATSET, 225,
580 "No SSL support compiled in.\n"));
581 #endif /* !USE_SSL */
582 return PROTO_UNKNOWN;
583 } else {
584 file: p = PROTO_FILE;
585 np = ac_alloc((sz = strlen(name)) + 5);
586 strcpy(np, name);
587 if (stat(name, &st) == 0) {
588 if (S_ISDIR(st.st_mode)) {
589 strcpy(&np[sz], "/tmp");
590 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
591 strcpy(&np[sz], "/new");
592 if (stat(np, &st) == 0 &&
593 S_ISDIR(st.st_mode)) {
594 strcpy(&np[sz], "/cur");
595 if (stat(np, &st) == 0 &&
596 S_ISDIR(st.st_mode))
597 p = PROTO_MAILDIR;
601 } else {
602 strcpy(&np[sz], ".gz");
603 if (stat(np, &st) < 0) {
604 strcpy(&np[sz], ".bz2");
605 if (stat(np, &st) < 0) {
606 if ((cp = value("newfolders")) != 0 &&
607 strcmp(cp, "maildir") == 0)
608 p = PROTO_MAILDIR;
612 ac_free(np);
613 return p;
617 const char *
618 protfile(const char *xcp)
620 const char *cp = xcp;
621 int state = 0;
623 while (*cp) {
624 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
625 cp += 3;
626 state = 1;
628 if (cp[0] == '/' && state == 1)
629 return &cp[1];
630 if (cp[0] == '/')
631 return xcp;
632 cp++;
634 return cp;
637 char *
638 protbase(const char *cp)
640 char *n = salloc(strlen(cp) + 1);
641 char *np = n;
643 while (*cp) {
644 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
645 *np++ = *cp++;
646 *np++ = *cp++;
647 *np++ = *cp++;
648 } else if (cp[0] == '/')
649 break;
650 else
651 *np++ = *cp++;
653 *np = '\0';
654 return n;
657 int
658 disconnected(const char *file)
660 char *cp, *cq, *vp;
661 int vs, r;
663 if (value("disconnected"))
664 return 1;
665 cp = protbase(file);
666 if (strncmp(cp, "imap://", 7) == 0)
667 cp += 7;
668 else if (strncmp(cp, "imaps://", 8) == 0)
669 cp += 8;
670 else
671 return 0;
672 if ((cq = strchr(cp, ':')) != NULL)
673 *cq = '\0';
674 vp = ac_alloc(vs = strlen(cp) + 14);
675 snprintf(vp, vs, "disconnected-%s", cp);
676 r = value(vp) != NULL;
677 ac_free(vp);
678 return r;
681 unsigned
682 pjw(const char *cp)
684 unsigned h = 0, g;
686 cp--;
687 while (*++cp) {
688 h = (h << 4 & 0xffffffff) + (*cp&0377);
689 if ((g = h & 0xf0000000) != 0) {
690 h = h ^ g >> 24;
691 h = h ^ g;
694 return h;
697 long
698 nextprime(long n)
700 const long primes[] = {
701 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
702 131071, 262139, 524287, 1048573, 2097143, 4194301,
703 8388593, 16777213, 33554393, 67108859, 134217689,
704 268435399, 536870909, 1073741789, 2147483647
706 long mprime = 7;
707 size_t i;
709 for (i = 0; i < sizeof primes / sizeof *primes; i++)
710 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
711 n < 262144 ? n*2 : n))
712 break;
713 if (i == sizeof primes / sizeof *primes)
714 mprime = n; /* not so prime, but better than failure */
715 return mprime;
718 #define Hexchar(n) ((n)>9 ? (n)-10+'A' : (n)+'0')
719 #define hexchar(n) ((n)>9 ? (n)-10+'a' : (n)+'0')
721 char *
722 strenc(const char *cp)
724 char *n, *np;
726 np = n = salloc(strlen(cp) * 3 + 1);
727 while (*cp) {
728 if (alnumchar(*cp&0377) || *cp == '_' || *cp == '@' ||
729 (np > n && (*cp == '.' || *cp == '-' ||
730 *cp == ':')))
731 *np++ = *cp;
732 else {
733 *np++ = '%';
734 *np++ = Hexchar((*cp&0xf0) >> 4);
735 *np++ = Hexchar(*cp&0x0f);
737 cp++;
739 *np = '\0';
740 return n;
743 char *
744 strdec(const char *cp)
746 char *n, *np;
748 np = n = salloc(strlen(cp) + 1);
749 while (*cp) {
750 if (cp[0] == '%' && cp[1] && cp[2]) {
751 *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4;
752 *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0';
753 cp += 3;
754 } else
755 *np++ = *cp++;
757 *np = '\0';
758 return n;
761 #ifdef USE_MD5
762 char *
763 md5tohex(const void *vp)
765 char *hex;
766 const char *cp = vp;
767 int i;
769 hex = salloc(33);
770 for (i = 0; i < 16; i++) {
771 hex[2*i] = hexchar((cp[i]&0xf0) >> 4);
772 hex[2*i+1] = hexchar(cp[i]&0x0f);
774 hex[32] = '\0';
775 return hex;
778 char *
779 cram_md5_string(const char *user, const char *pass, const char *b64)
781 struct str in, out;
782 char digest[16], *cp, *sp, *rp, *xp;
783 int ss, rs;
785 in.s = (char *)b64;
786 in.l = strlen(in.s);
787 mime_fromb64(&in, &out, 0);
788 hmac_md5((unsigned char *)out.s, out.l,
789 (unsigned char *)pass, strlen(pass),
790 digest);
791 free(out.s);
792 xp = md5tohex(digest);
793 sp = ac_alloc(ss = strlen(user) + strlen(xp) + 2);
794 snprintf(sp, ss, "%s %s", user, xp);
795 cp = strtob64(sp);
796 ac_free(sp);
797 rp = salloc(rs = strlen(cp) + 3);
798 snprintf(rp, rs, "%s\r\n", cp);
799 free(cp);
800 return rp;
802 #endif /* USE_MD5 */
804 char *
805 getuser(void)
807 char *line = NULL, *user;
808 size_t linesize = 0;
810 if (is_a_tty[0]) {
811 fputs("User: ", stdout);
812 fflush(stdout);
814 if (readline(stdin, &line, &linesize) == 0) {
815 if (line)
816 free(line);
817 return NULL;
819 user = savestr(line);
820 free(line);
821 return user;
824 char *
825 getpassword(struct termios *otio, int *reset_tio, const char *query)
827 struct termios tio;
828 char *line = NULL, *pass;
829 size_t linesize = 0;
830 int i;
832 if (is_a_tty[0]) {
833 fputs(query ? query : "Password:", stdout);
834 fflush(stdout);
835 tcgetattr(0, &tio);
836 *otio = tio;
837 tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
838 *reset_tio = 1;
839 tcsetattr(0, TCSAFLUSH, &tio);
841 i = readline(stdin, &line, &linesize);
842 if (is_a_tty[0]) {
843 fputc('\n', stdout);
844 tcsetattr(0, TCSADRAIN, otio);
846 *reset_tio = 0;
847 if (i < 0) {
848 if (line)
849 free(line);
850 return NULL;
852 pass = savestr(line);
853 free(line);
854 return pass;
857 void
858 transflags(struct message *omessage, long omsgCount, int transparent)
860 struct message *omp, *nmp, *newdot, *newprevdot;
861 int hf;
863 omp = omessage;
864 nmp = message;
865 newdot = message;
866 newprevdot = NULL;
867 while (omp < &omessage[omsgCount] &&
868 nmp < &message[msgCount]) {
869 if (dot && nmp->m_uid == dot->m_uid)
870 newdot = nmp;
871 if (prevdot && nmp->m_uid == prevdot->m_uid)
872 newprevdot = nmp;
873 if (omp->m_uid == nmp->m_uid) {
874 hf = nmp->m_flag & MHIDDEN;
875 if (transparent && mb.mb_type == MB_IMAP)
876 omp->m_flag &= ~MHIDDEN;
877 *nmp++ = *omp++;
878 if (transparent && mb.mb_type == MB_CACHE)
879 nmp[-1].m_flag |= hf;
880 } else if (omp->m_uid < nmp->m_uid)
881 omp++;
882 else
883 nmp++;
885 dot = newdot;
886 setdot(newdot);
887 prevdot = newprevdot;
888 free(omessage);
891 char *
892 getrandstring(size_t length)
894 static unsigned char nodedigest[16];
895 static pid_t pid;
896 int fd = -1;
897 char *data;
898 char *cp, *rp;
899 size_t i;
900 #ifdef USE_MD5
901 MD5_CTX ctx;
902 #else
903 size_t j;
904 #endif
906 data = salloc(length);
907 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
908 length != (size_t)read(fd, data, length)) {
909 if (pid == 0) {
910 pid = getpid();
911 srand(pid);
912 cp = nodename(0);
913 #ifdef USE_MD5
914 MD5Init(&ctx);
915 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
916 MD5Final(nodedigest, &ctx);
917 #else
918 /* In that case it's only used for boundaries and
919 * Message-Id:s so that srand(3) should suffice */
920 j = strlen(cp) + 1;
921 for (i = 0; i < sizeof(nodedigest); ++i)
922 nodedigest[i] = (unsigned char)(
923 cp[i % j] ^ rand());
924 #endif
926 for (i = 0; i < length; i++)
927 data[i] = (char)
928 ((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
929 nodedigest[i % sizeof nodedigest]);
931 if (fd > 0)
932 close(fd);
933 cp = memtob64(data, length);
934 rp = salloc(length+1);
935 strncpy(rp, cp, length)[length] = '\0';
936 free(cp);
937 return rp;
940 void
941 out_of_memory(void)
943 panic("no memory");
946 void *
947 smalloc(size_t s)
949 void *p;
951 if (s == 0)
952 s = 1;
953 if ((p = malloc(s)) == NULL)
954 out_of_memory();
955 return p;
958 void *
959 srealloc(void *v, size_t s)
961 void *r;
963 if (s == 0)
964 s = 1;
965 if (v == NULL)
966 return smalloc(s);
967 if ((r = realloc(v, s)) == NULL)
968 out_of_memory();
969 return r;
972 void *
973 scalloc(size_t nmemb, size_t size)
975 void *vp;
977 if (size == 0)
978 size = 1;
979 if ((vp = calloc(nmemb, size)) == NULL)
980 out_of_memory();
981 return vp;
984 char *
985 sstpcpy(char *dst, const char *src)
987 while ((*dst = *src++) != '\0')
988 dst++;
989 return dst;
992 char *
993 sstrdup(const char *cp)
995 char *dp;
997 if (cp) {
998 dp = smalloc(strlen(cp) + 1);
999 strcpy(dp, cp);
1000 return dp;
1001 } else
1002 return NULL;
1005 enum okay
1006 makedir(const char *name)
1008 int e;
1009 struct stat st;
1011 if (mkdir(name, 0700) < 0) {
1012 e = errno;
1013 if ((e == EEXIST || e == ENOSYS) &&
1014 stat(name, &st) == 0 &&
1015 (st.st_mode&S_IFMT) == S_IFDIR)
1016 return OKAY;
1017 return STOP;
1019 return OKAY;
1022 #ifdef HAVE_FCHDIR
1023 enum okay
1024 cwget(struct cw *cw)
1026 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
1027 return STOP;
1028 if (fchdir(cw->cw_fd) < 0) {
1029 close(cw->cw_fd);
1030 return STOP;
1032 return OKAY;
1035 enum okay
1036 cwret(struct cw *cw)
1038 if (fchdir(cw->cw_fd) < 0)
1039 return STOP;
1040 return OKAY;
1043 void
1044 cwrelse(struct cw *cw)
1046 close(cw->cw_fd);
1048 #else /* !HAVE_FCHDIR */
1049 enum okay
1050 cwget(struct cw *cw)
1052 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
1053 return STOP;
1054 return OKAY;
1057 enum okay
1058 cwret(struct cw *cw)
1060 if (chdir(cw->cw_wd) < 0)
1061 return STOP;
1062 return OKAY;
1065 /*ARGSUSED*/
1066 void
1067 cwrelse(struct cw *cw)
1070 #endif /* !HAVE_FCHDIR */
1072 void
1073 makeprint(struct str *in, struct str *out)
1075 static int print_all_chars = -1;
1076 char *inp, *outp;
1077 size_t msz, dist;
1079 out->s = smalloc(msz = in->l + 1);
1080 if (print_all_chars == -1)
1081 print_all_chars = value("print-all-chars") != NULL;
1082 if (print_all_chars) {
1083 memcpy(out->s, in->s, in->l);
1084 out->l = in->l;
1085 out->s[out->l] = '\0';
1086 return;
1088 inp = in->s;
1089 outp = out->s;
1090 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1091 if (mb_cur_max > 1) {
1092 wchar_t wc;
1093 char mb[MB_LEN_MAX+1];
1094 int i, n;
1095 out->l = 0;
1096 while (inp < &in->s[in->l]) {
1097 if (*inp & 0200)
1098 n = mbtowc(&wc, inp, &in->s[in->l] - inp);
1099 else {
1100 wc = *inp;
1101 n = 1;
1103 if (n < 0) {
1104 mbtowc(&wc, NULL, mb_cur_max);
1105 wc = utf8 ? 0xFFFD : '?';
1106 n = 1;
1107 } else if (n == 0)
1108 n = 1;
1109 inp += n;
1110 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
1111 wc != '\b' && wc != '\t') {
1112 if ((wc & ~(wchar_t)037) == 0)
1113 wc = utf8 ? 0x2400 | wc : '?';
1114 else if (wc == 0177)
1115 wc = utf8 ? 0x2421 : '?';
1116 else
1117 wc = utf8 ? 0x2426 : '?';
1119 if ((n = wctomb(mb, wc)) <= 0)
1120 continue;
1121 out->l += n;
1122 if (out->l >= msz - 1) {
1123 dist = outp - out->s;
1124 out->s = srealloc(out->s, msz += 32);
1125 outp = &out->s[dist];
1127 for (i = 0; i < n; i++)
1128 *outp++ = mb[i];
1130 } else
1131 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1133 int c;
1134 while (inp < &in->s[in->l]) {
1135 c = *inp++ & 0377;
1136 if (!isprint(c) && c != '\n' && c != '\r' &&
1137 c != '\b' && c != '\t')
1138 c = '?';
1139 *outp++ = c;
1141 out->l = in->l;
1143 out->s[out->l] = '\0';
1146 char *
1147 prstr(const char *s)
1149 struct str in, out;
1150 char *rp;
1152 in.s = (char *)s;
1153 in.l = strlen(s);
1154 makeprint(&in, &out);
1155 rp = salloc(out.l + 1);
1156 memcpy(rp, out.s, out.l);
1157 rp[out.l] = '\0';
1158 free(out.s);
1159 return rp;
1163 prout(const char *s, size_t sz, FILE *fp)
1165 struct str in, out;
1166 int n;
1168 in.s = (char *)s;
1169 in.l = sz;
1170 makeprint(&in, &out);
1171 n = fwrite(out.s, 1, out.l, fp);
1172 free(out.s);
1173 return n;
1177 * Print out a Unicode character or a substitute for it.
1180 putuc(int u, int c, FILE *fp)
1182 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1183 if (utf8 && u & ~(wchar_t)0177) {
1184 char mb[MB_LEN_MAX];
1185 int i, n, r = 0;
1186 if ((n = wctomb(mb, u)) > 0) {
1187 for (i = 0; i < n; i++)
1188 r += putc(mb[i] & 0377, fp) != EOF;
1189 return r;
1190 } else if (n == 0)
1191 return putc('\0', fp) != EOF;
1192 else
1193 return 0;
1194 } else
1195 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1196 return putc(c, fp) != EOF;
1200 * Locale-independent character class functions.
1202 int
1203 asccasecmp(const char *s1, const char *s2)
1205 register int cmp;
1208 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1209 return cmp;
1210 while (*s1++ != '\0' && *s2++ != '\0');
1211 return 0;
1215 ascncasecmp(const char *s1, const char *s2, size_t sz)
1217 register int cmp;
1218 size_t i = 1;
1220 if (sz == 0)
1221 return 0;
1223 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1224 return cmp;
1225 while (i++ < sz && *s1++ != '\0' && *s2++ != '\0');
1226 return 0;
1229 char *
1230 asccasestr(const char *haystack, const char *xneedle)
1232 char *needle, *NEEDLE;
1233 int i, sz;
1235 sz = strlen(xneedle);
1236 if (sz == 0)
1237 return (char *)haystack;
1238 needle = ac_alloc(sz);
1239 NEEDLE = ac_alloc(sz);
1240 for (i = 0; i < sz; i++) {
1241 needle[i] = lowerconv(xneedle[i]&0377);
1242 NEEDLE[i] = upperconv(xneedle[i]&0377);
1244 while (*haystack) {
1245 if (*haystack == *needle || *haystack == *NEEDLE) {
1246 for (i = 1; i < sz; i++)
1247 if (haystack[i] != needle[i] &&
1248 haystack[i] != NEEDLE[i])
1249 break;
1250 if (i == sz)
1251 return (char *)haystack;
1253 haystack++;
1255 return NULL;
1258 const unsigned char class_char[] = {
1259 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
1260 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1261 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
1262 C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL,
1263 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
1264 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1265 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
1266 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1267 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
1268 C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1269 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
1270 C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1271 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
1272 C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,
1273 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
1274 C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1275 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
1276 C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1277 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
1278 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1279 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
1280 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1281 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
1282 C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1283 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
1284 C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1285 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
1286 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1287 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
1288 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1289 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
1290 C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL