nail.1: drop of -h, correct docu for -O plus..
[s-mailx.git] / aux.c
blobd86533a9f318c865ac6d0735b43119dc07e2c507
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 * Return new string copy of a non-terminated argument.
88 char *
89 savestrbuf(const char *sbuf, size_t sbuf_len)
91 char *news;
93 if ((news = salloc(sbuf_len + 1)) != NULL) {
94 memcpy(news, sbuf, sbuf_len);
95 news[sbuf_len] = 0;
97 return (news);
101 * Make a copy of new argument incorporating old one.
103 char *
104 save2str(const char *str, const char *old)
106 char *new;
107 int newsize = strlen(str) + 1;
108 int oldsize = old ? strlen(old) + 1 : 0;
110 if ((new = salloc(newsize + oldsize)) != NULL) {
111 if (oldsize) {
112 memcpy(new, old, oldsize);
113 new[oldsize - 1] = ' ';
115 memcpy(new + oldsize, str, newsize);
117 return new;
120 char *
121 savecat(const char *s1, const char *s2)
123 const char *cp;
124 char *ns, *np;
126 np = ns = salloc(strlen(s1) + strlen(s2) + 1);
127 for (cp = s1; *cp; cp++)
128 *np++ = *cp;
129 for (cp = s2; *cp; cp++)
130 *np++ = *cp;
131 *np = '\0';
132 return ns;
135 #include <stdarg.h>
137 #ifndef HAVE_SNPRINTF
139 * Lazy vsprintf wrapper.
142 snprintf(char *str, size_t size, const char *format, ...)
144 va_list ap;
145 int ret;
147 va_start(ap, format);
148 ret = vsprintf(str, format, ap);
149 va_end(ap);
150 return ret;
152 #endif /* !HAVE_SNPRINTF */
155 * Announce a fatal error and die.
157 void
158 panic(const char *format, ...)
160 va_list ap;
162 va_start(ap, format);
163 fprintf(stderr, catgets(catd, CATSET, 1, "panic: "));
164 vfprintf(stderr, format, ap);
165 va_end(ap);
166 fprintf(stderr, catgets(catd, CATSET, 2, "\n"));
167 fflush(stderr);
168 abort();
171 void
172 holdint(void)
174 sigset_t set;
176 sigemptyset(&set);
177 sigaddset(&set, SIGINT);
178 sigprocmask(SIG_BLOCK, &set, NULL);
181 void
182 relseint(void)
184 sigset_t set;
186 sigemptyset(&set);
187 sigaddset(&set, SIGINT);
188 sigprocmask(SIG_UNBLOCK, &set, NULL);
192 * Touch the named message by setting its MTOUCH flag.
193 * Touched messages have the effect of not being sent
194 * back to the system mailbox on exit.
196 void
197 touch(struct message *mp)
200 mp->m_flag |= MTOUCH;
201 if ((mp->m_flag & MREAD) == 0)
202 mp->m_flag |= MREAD|MSTATUS;
206 * Test to see if the passed file name is a directory.
207 * Return true if it is.
209 int
210 is_dir(char *name)
212 struct stat sbuf;
214 if (stat(name, &sbuf) < 0)
215 return(0);
216 return(S_ISDIR(sbuf.st_mode));
220 * Count the number of arguments in the given string raw list.
222 int
223 argcount(char **argv)
225 char **ap;
227 for (ap = argv; *ap++ != NULL;)
229 return ap - argv - 1;
233 * Copy a string, lowercasing it as we go.
235 void
236 i_strcpy(char *dest, const char *src, int size)
238 char *max;
240 max=dest+size-1;
241 while (dest<=max) {
242 *dest++ = lowerconv(*src & 0377);
243 if (*src++ == '\0')
244 break;
248 char *
249 i_strdup(const char *src)
251 int sz;
252 char *dest;
254 sz = strlen(src) + 1;
255 dest = salloc(sz);
256 i_strcpy(dest, src, sz);
257 return dest;
261 * Convert a string to lowercase, in-place and with multibyte-aware.
263 void
264 makelow(char *cp)
266 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
267 if (mb_cur_max > 1) {
268 char *tp = cp;
269 wchar_t wc;
270 int len;
272 while (*cp) {
273 len = mbtowc(&wc, cp, mb_cur_max);
274 if (len < 0)
275 *tp++ = *cp++;
276 else {
277 wc = towlower(wc);
278 if (wctomb(tp, wc) == len)
279 tp += len, cp += len;
280 else
281 *tp++ = *cp++;
284 } else
285 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
288 *cp = tolower(*cp & 0377);
289 while (*cp++);
293 int
294 substr(const char *str, const char *sub)
296 const char *cp, *backup;
298 cp = sub;
299 backup = str;
300 while (*str && *cp) {
301 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
302 if (mb_cur_max > 1) {
303 wchar_t c, c2;
304 int sz;
306 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
307 goto singlebyte;
308 cp += sz;
309 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
310 goto singlebyte;
311 str += sz;
312 c = towupper(c);
313 c2 = towupper(c2);
314 if (c != c2) {
315 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
316 backup += sz;
317 str = backup;
318 } else
319 str = ++backup;
320 cp = sub;
322 } else
323 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
325 int c, c2;
327 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
328 singlebyte:
329 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
330 c = *cp++ & 0377;
331 if (islower(c))
332 c = toupper(c);
333 c2 = *str++ & 0377;
334 if (islower(c2))
335 c2 = toupper(c2);
336 if (c != c2) {
337 str = ++backup;
338 cp = sub;
342 return *cp == '\0';
345 char *
346 colalign(const char *cp, int col, int fill)
348 int n, sz;
349 char *nb, *np;
351 np = nb = salloc(mb_cur_max * strlen(cp) + col + 1);
352 while (*cp) {
353 #if defined (HAVE_MBTOWC) && defined (HAVE_WCWIDTH)
354 if (mb_cur_max > 1) {
355 wchar_t wc;
357 if ((sz = mbtowc(&wc, cp, mb_cur_max)) < 0) {
358 n = sz = 1;
359 } else {
360 if ((n = wcwidth(wc)) < 0)
361 n = 1;
363 } else
364 #endif /* HAVE_MBTOWC && HAVE_WCWIDTH */
366 n = sz = 1;
368 if (n > col)
369 break;
370 col -= n;
371 if (sz == 1 && spacechar(*cp&0377)) {
372 *np++ = ' ';
373 cp++;
374 } else
375 while (sz--)
376 *np++ = *cp++;
378 if (fill)
379 while (col-- > 0)
380 *np++ = ' ';
381 *np = '\0';
382 return nb;
385 void
386 try_pager(FILE *fp)
388 long lines = 0;
389 int c;
390 char *cp;
392 fflush(fp);
393 rewind(fp);
394 while ((c = getc(fp)) != EOF)
395 if (c == '\n')
396 lines++;
397 rewind(fp);
398 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL &&
399 lines > (*cp ? atol(cp) : scrnheight))
400 run_command(get_pager(), 0, fileno(fp), -1, NULL, NULL, NULL);
401 else
402 while ((c = getc(fp)) != EOF)
403 putchar(c);
407 * The following code deals with input stacking to do source
408 * commands. All but the current file pointer are saved on
409 * the stack.
412 static int ssp; /* Top of file stack */
413 struct sstack {
414 FILE *s_file; /* File we were in. */
415 enum condition s_cond; /* Saved state of conditionals */
416 int s_loading; /* Loading .mailrc, etc. */
417 #define SSTACK 20
418 } sstack[SSTACK];
421 * Pushdown current input file and switch to a new one.
422 * Set the global flag "sourcing" so that others will realize
423 * that they are no longer reading from a tty (in all probability).
425 int
426 source(void *v)
428 char **arglist = v;
429 FILE *fi;
430 char *cp;
432 if ((cp = expand(*arglist)) == NULL)
433 return(1);
434 if ((fi = Fopen(cp, "r")) == NULL) {
435 perror(cp);
436 return(1);
438 if (ssp >= SSTACK - 1) {
439 printf(catgets(catd, CATSET, 3,
440 "Too much \"sourcing\" going on.\n"));
441 Fclose(fi);
442 return(1);
444 sstack[ssp].s_file = input;
445 sstack[ssp].s_cond = cond;
446 sstack[ssp].s_loading = loading;
447 ssp++;
448 loading = 0;
449 cond = CANY;
450 input = fi;
451 sourcing++;
452 return(0);
456 * Pop the current input back to the previous level.
457 * Update the "sourcing" flag as appropriate.
459 int
460 unstack(void)
462 if (ssp <= 0) {
463 printf(catgets(catd, CATSET, 4,
464 "\"Source\" stack over-pop.\n"));
465 sourcing = 0;
466 return(1);
468 Fclose(input);
469 if (cond != CANY)
470 printf(catgets(catd, CATSET, 5, "Unmatched \"if\"\n"));
471 ssp--;
472 cond = sstack[ssp].s_cond;
473 loading = sstack[ssp].s_loading;
474 input = sstack[ssp].s_file;
475 if (ssp == 0)
476 sourcing = loading;
477 return(0);
481 * Touch the indicated file.
482 * This is nifty for the shell.
484 void
485 alter(char *name)
487 struct stat sb;
488 struct utimbuf utb;
490 if (stat(name, &sb))
491 return;
492 utb.actime = time((time_t *)0) + 1;
493 utb.modtime = sb.st_mtime;
494 utime(name, &utb);
498 * Examine the passed line buffer and
499 * return true if it is all blanks and tabs.
501 int
502 blankline(char *linebuf)
504 char *cp;
506 for (cp = linebuf; *cp; cp++)
507 if (!blankchar(*cp & 0377))
508 return(0);
509 return(1);
513 * Are any of the characters in the two strings the same?
515 int
516 anyof(char *s1, char *s2)
519 while (*s1)
520 if (strchr(s2, *s1++))
521 return 1;
522 return 0;
526 * Determine if as1 is a valid prefix of as2.
527 * Return true if yep.
529 int
530 is_prefix(const char *as1, const char *as2)
532 const char *s1, *s2;
534 s1 = as1;
535 s2 = as2;
536 while (*s1++ == *s2)
537 if (*s2++ == '\0')
538 return(1);
539 return(*--s1 == '\0');
542 char *
543 last_at_before_slash(const char *sp)
545 const char *cp;
547 for (cp = sp; *cp; cp++)
548 if (*cp == '/')
549 break;
550 while (cp > sp && *--cp != '@');
551 return *cp == '@' ? (char *)cp : NULL;
554 enum protocol
555 which_protocol(const char *name)
557 register const char *cp;
558 char *np;
559 size_t sz;
560 struct stat st;
561 enum protocol p;
563 if (name[0] == '%' && name[1] == ':')
564 name += 2;
565 for (cp = name; *cp && *cp != ':'; cp++)
566 if (!alnumchar(*cp&0377))
567 goto file;
568 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
569 if (strncmp(name, "pop3://", 7) == 0)
570 #ifdef USE_POP3
571 return PROTO_POP3;
572 #else
573 fprintf(stderr, catgets(catd, CATSET, 216,
574 "No POP3 support compiled in.\n"));
575 #endif
576 if (strncmp(name, "pop3s://", 8) == 0)
577 #ifdef USE_SSL
578 return PROTO_POP3;
579 #else /* !USE_SSL */
580 fprintf(stderr, catgets(catd, CATSET, 225,
581 "No SSL support compiled in.\n"));
582 #endif /* !USE_SSL */
583 if (strncmp(name, "imap://", 7) == 0)
584 #ifdef USE_IMAP
585 return PROTO_IMAP;
586 #else
587 fprintf(stderr, catgets(catd, CATSET, 269,
588 "No IMAP support compiled in.\n"));
589 #endif
590 if (strncmp(name, "imaps://", 8) == 0)
591 #ifdef USE_SSL
592 return PROTO_IMAP;
593 #else /* !USE_SSL */
594 fprintf(stderr, catgets(catd, CATSET, 225,
595 "No SSL support compiled in.\n"));
596 #endif /* !USE_SSL */
597 return PROTO_UNKNOWN;
598 } else {
599 file: p = PROTO_FILE;
600 np = ac_alloc((sz = strlen(name)) + 5);
601 strcpy(np, name);
602 if (stat(name, &st) == 0) {
603 if (S_ISDIR(st.st_mode)) {
604 strcpy(&np[sz], "/tmp");
605 if (stat(np, &st) == 0 && S_ISDIR(st.st_mode)) {
606 strcpy(&np[sz], "/new");
607 if (stat(np, &st) == 0 &&
608 S_ISDIR(st.st_mode)) {
609 strcpy(&np[sz], "/cur");
610 if (stat(np, &st) == 0 &&
611 S_ISDIR(st.st_mode))
612 p = PROTO_MAILDIR;
616 } else {
617 strcpy(&np[sz], ".gz");
618 if (stat(np, &st) < 0) {
619 strcpy(&np[sz], ".bz2");
620 if (stat(np, &st) < 0) {
621 if ((cp = value("newfolders")) != 0 &&
622 strcmp(cp, "maildir") == 0)
623 p = PROTO_MAILDIR;
627 ac_free(np);
628 return p;
632 const char *
633 protfile(const char *xcp)
635 const char *cp = xcp;
636 int state = 0;
638 while (*cp) {
639 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
640 cp += 3;
641 state = 1;
643 if (cp[0] == '/' && state == 1)
644 return &cp[1];
645 if (cp[0] == '/')
646 return xcp;
647 cp++;
649 return cp;
652 char *
653 protbase(const char *cp)
655 char *n = salloc(strlen(cp) + 1);
656 char *np = n;
658 while (*cp) {
659 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
660 *np++ = *cp++;
661 *np++ = *cp++;
662 *np++ = *cp++;
663 } else if (cp[0] == '/')
664 break;
665 else
666 *np++ = *cp++;
668 *np = '\0';
669 return n;
672 int
673 disconnected(const char *file)
675 char *cp, *cq, *vp;
676 int vs, r;
678 if (value("disconnected"))
679 return 1;
680 cp = protbase(file);
681 if (strncmp(cp, "imap://", 7) == 0)
682 cp += 7;
683 else if (strncmp(cp, "imaps://", 8) == 0)
684 cp += 8;
685 else
686 return 0;
687 if ((cq = strchr(cp, ':')) != NULL)
688 *cq = '\0';
689 vp = ac_alloc(vs = strlen(cp) + 14);
690 snprintf(vp, vs, "disconnected-%s", cp);
691 r = value(vp) != NULL;
692 ac_free(vp);
693 return r;
696 unsigned
697 pjw(const char *cp)
699 unsigned h = 0, g;
701 cp--;
702 while (*++cp) {
703 h = (h << 4 & 0xffffffff) + (*cp&0377);
704 if ((g = h & 0xf0000000) != 0) {
705 h = h ^ g >> 24;
706 h = h ^ g;
709 return h;
712 long
713 nextprime(long n)
715 const long primes[] = {
716 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
717 131071, 262139, 524287, 1048573, 2097143, 4194301,
718 8388593, 16777213, 33554393, 67108859, 134217689,
719 268435399, 536870909, 1073741789, 2147483647
721 long mprime = 7;
722 size_t i;
724 for (i = 0; i < sizeof primes / sizeof *primes; i++)
725 if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
726 n < 262144 ? n*2 : n))
727 break;
728 if (i == sizeof primes / sizeof *primes)
729 mprime = n; /* not so prime, but better than failure */
730 return mprime;
733 #define Hexchar(n) ((n)>9 ? (n)-10+'A' : (n)+'0')
734 #define hexchar(n) ((n)>9 ? (n)-10+'a' : (n)+'0')
736 char *
737 strenc(const char *cp)
739 char *n, *np;
741 np = n = salloc(strlen(cp) * 3 + 1);
742 while (*cp) {
743 if (alnumchar(*cp&0377) || *cp == '_' || *cp == '@' ||
744 (np > n && (*cp == '.' || *cp == '-' ||
745 *cp == ':')))
746 *np++ = *cp;
747 else {
748 *np++ = '%';
749 *np++ = Hexchar((*cp&0xf0) >> 4);
750 *np++ = Hexchar(*cp&0x0f);
752 cp++;
754 *np = '\0';
755 return n;
758 char *
759 strdec(const char *cp)
761 char *n, *np;
763 np = n = salloc(strlen(cp) + 1);
764 while (*cp) {
765 if (cp[0] == '%' && cp[1] && cp[2]) {
766 *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4;
767 *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0';
768 cp += 3;
769 } else
770 *np++ = *cp++;
772 *np = '\0';
773 return n;
776 #ifdef USE_MD5
777 char *
778 md5tohex(const void *vp)
780 char *hex;
781 const char *cp = vp;
782 int i;
784 hex = salloc(33);
785 for (i = 0; i < 16; i++) {
786 hex[2*i] = hexchar((cp[i]&0xf0) >> 4);
787 hex[2*i+1] = hexchar(cp[i]&0x0f);
789 hex[32] = '\0';
790 return hex;
793 char *
794 cram_md5_string(const char *user, const char *pass, const char *b64)
796 struct str in, out;
797 char digest[16], *cp, *sp, *rp, *xp;
798 int ss, rs;
800 in.s = (char *)b64;
801 in.l = strlen(in.s);
802 mime_fromb64(&in, &out, 0);
803 hmac_md5((unsigned char *)out.s, out.l,
804 (unsigned char *)pass, strlen(pass),
805 digest);
806 free(out.s);
807 xp = md5tohex(digest);
808 sp = ac_alloc(ss = strlen(user) + strlen(xp) + 2);
809 snprintf(sp, ss, "%s %s", user, xp);
810 cp = strtob64(sp);
811 ac_free(sp);
812 rp = salloc(rs = strlen(cp) + 3);
813 snprintf(rp, rs, "%s\r\n", cp);
814 free(cp);
815 return rp;
817 #endif /* USE_MD5 */
819 char *
820 getuser(void)
822 char *line = NULL, *user;
823 size_t linesize = 0;
825 if (is_a_tty[0]) {
826 fputs("User: ", stdout);
827 fflush(stdout);
829 if (readline(stdin, &line, &linesize) == 0) {
830 if (line)
831 free(line);
832 return NULL;
834 user = savestr(line);
835 free(line);
836 return user;
839 char *
840 getpassword(struct termios *otio, int *reset_tio, const char *query)
842 struct termios tio;
843 char *line = NULL, *pass;
844 size_t linesize = 0;
845 int i;
847 if (is_a_tty[0]) {
848 fputs(query ? query : "Password:", stdout);
849 fflush(stdout);
850 tcgetattr(0, &tio);
851 *otio = tio;
852 tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
853 *reset_tio = 1;
854 tcsetattr(0, TCSAFLUSH, &tio);
856 i = readline(stdin, &line, &linesize);
857 if (is_a_tty[0]) {
858 fputc('\n', stdout);
859 tcsetattr(0, TCSADRAIN, otio);
861 *reset_tio = 0;
862 if (i < 0) {
863 if (line)
864 free(line);
865 return NULL;
867 pass = savestr(line);
868 free(line);
869 return pass;
872 void
873 transflags(struct message *omessage, long omsgCount, int transparent)
875 struct message *omp, *nmp, *newdot, *newprevdot;
876 int hf;
878 omp = omessage;
879 nmp = message;
880 newdot = message;
881 newprevdot = NULL;
882 while (omp < &omessage[omsgCount] &&
883 nmp < &message[msgCount]) {
884 if (dot && nmp->m_uid == dot->m_uid)
885 newdot = nmp;
886 if (prevdot && nmp->m_uid == prevdot->m_uid)
887 newprevdot = nmp;
888 if (omp->m_uid == nmp->m_uid) {
889 hf = nmp->m_flag & MHIDDEN;
890 if (transparent && mb.mb_type == MB_IMAP)
891 omp->m_flag &= ~MHIDDEN;
892 *nmp++ = *omp++;
893 if (transparent && mb.mb_type == MB_CACHE)
894 nmp[-1].m_flag |= hf;
895 } else if (omp->m_uid < nmp->m_uid)
896 omp++;
897 else
898 nmp++;
900 dot = newdot;
901 setdot(newdot);
902 prevdot = newprevdot;
903 free(omessage);
906 char *
907 getrandstring(size_t length)
909 static unsigned char nodedigest[16];
910 static pid_t pid;
911 int fd = -1;
912 char *data;
913 char *cp, *rp;
914 size_t i;
915 #ifdef USE_MD5
916 MD5_CTX ctx;
917 #else
918 size_t j;
919 #endif
921 data = salloc(length);
922 if ((fd = open("/dev/urandom", O_RDONLY)) < 0 ||
923 length != (size_t)read(fd, data, length)) {
924 if (pid == 0) {
925 pid = getpid();
926 srand(pid);
927 cp = nodename(0);
928 #ifdef USE_MD5
929 MD5Init(&ctx);
930 MD5Update(&ctx, (unsigned char *)cp, strlen(cp));
931 MD5Final(nodedigest, &ctx);
932 #else
933 /* In that case it's only used for boundaries and
934 * Message-Id:s so that srand(3) should suffice */
935 j = strlen(cp) + 1;
936 for (i = 0; i < sizeof(nodedigest); ++i)
937 nodedigest[i] = (unsigned char)(
938 cp[i % j] ^ rand());
939 #endif
941 for (i = 0; i < length; i++)
942 data[i] = (char)
943 ((int)(255 * (rand() / (RAND_MAX + 1.0))) ^
944 nodedigest[i % sizeof nodedigest]);
946 if (fd > 0)
947 close(fd);
948 cp = memtob64(data, length);
949 rp = salloc(length+1);
950 strncpy(rp, cp, length)[length] = '\0';
951 free(cp);
952 return rp;
955 void
956 out_of_memory(void)
958 panic("no memory");
961 void *
962 smalloc(size_t s)
964 void *p;
966 if (s == 0)
967 s = 1;
968 if ((p = malloc(s)) == NULL)
969 out_of_memory();
970 return p;
973 void *
974 srealloc(void *v, size_t s)
976 void *r;
978 if (s == 0)
979 s = 1;
980 if (v == NULL)
981 return smalloc(s);
982 if ((r = realloc(v, s)) == NULL)
983 out_of_memory();
984 return r;
987 void *
988 scalloc(size_t nmemb, size_t size)
990 void *vp;
992 if (size == 0)
993 size = 1;
994 if ((vp = calloc(nmemb, size)) == NULL)
995 out_of_memory();
996 return vp;
999 char *
1000 sstpcpy(char *dst, const char *src)
1002 while ((*dst = *src++) != '\0')
1003 dst++;
1004 return dst;
1007 char *
1008 sstrdup(const char *cp)
1010 char *dp;
1012 if (cp) {
1013 dp = smalloc(strlen(cp) + 1);
1014 strcpy(dp, cp);
1015 return dp;
1016 } else
1017 return NULL;
1020 enum okay
1021 makedir(const char *name)
1023 int e;
1024 struct stat st;
1026 if (mkdir(name, 0700) < 0) {
1027 e = errno;
1028 if ((e == EEXIST || e == ENOSYS) &&
1029 stat(name, &st) == 0 &&
1030 (st.st_mode&S_IFMT) == S_IFDIR)
1031 return OKAY;
1032 return STOP;
1034 return OKAY;
1037 #ifdef HAVE_FCHDIR
1038 enum okay
1039 cwget(struct cw *cw)
1041 if ((cw->cw_fd = open(".", O_RDONLY)) < 0)
1042 return STOP;
1043 if (fchdir(cw->cw_fd) < 0) {
1044 close(cw->cw_fd);
1045 return STOP;
1047 return OKAY;
1050 enum okay
1051 cwret(struct cw *cw)
1053 if (fchdir(cw->cw_fd) < 0)
1054 return STOP;
1055 return OKAY;
1058 void
1059 cwrelse(struct cw *cw)
1061 close(cw->cw_fd);
1063 #else /* !HAVE_FCHDIR */
1064 enum okay
1065 cwget(struct cw *cw)
1067 if (getcwd(cw->cw_wd, sizeof cw->cw_wd) == NULL || chdir(cw->cw_wd) < 0)
1068 return STOP;
1069 return OKAY;
1072 enum okay
1073 cwret(struct cw *cw)
1075 if (chdir(cw->cw_wd) < 0)
1076 return STOP;
1077 return OKAY;
1080 /*ARGSUSED*/
1081 void
1082 cwrelse(struct cw *cw)
1085 #endif /* !HAVE_FCHDIR */
1087 void
1088 makeprint(struct str *in, struct str *out)
1090 static int print_all_chars = -1;
1091 char *inp, *outp;
1092 size_t msz, dist;
1094 out->s = smalloc(msz = in->l + 1);
1095 if (print_all_chars == -1)
1096 print_all_chars = value("print-all-chars") != NULL;
1097 if (print_all_chars) {
1098 memcpy(out->s, in->s, in->l);
1099 out->l = in->l;
1100 out->s[out->l] = '\0';
1101 return;
1103 inp = in->s;
1104 outp = out->s;
1105 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1106 if (mb_cur_max > 1) {
1107 wchar_t wc;
1108 char mb[MB_LEN_MAX+1];
1109 int i, n;
1110 out->l = 0;
1111 while (inp < &in->s[in->l]) {
1112 if (*inp & 0200)
1113 n = mbtowc(&wc, inp, &in->s[in->l] - inp);
1114 else {
1115 wc = *inp;
1116 n = 1;
1118 if (n < 0) {
1119 mbtowc(&wc, NULL, mb_cur_max);
1120 wc = utf8 ? 0xFFFD : '?';
1121 n = 1;
1122 } else if (n == 0)
1123 n = 1;
1124 inp += n;
1125 if (!iswprint(wc) && wc != '\n' && wc != '\r' &&
1126 wc != '\b' && wc != '\t') {
1127 if ((wc & ~(wchar_t)037) == 0)
1128 wc = utf8 ? 0x2400 | wc : '?';
1129 else if (wc == 0177)
1130 wc = utf8 ? 0x2421 : '?';
1131 else
1132 wc = utf8 ? 0x2426 : '?';
1134 if ((n = wctomb(mb, wc)) <= 0)
1135 continue;
1136 out->l += n;
1137 if (out->l >= msz - 1) {
1138 dist = outp - out->s;
1139 out->s = srealloc(out->s, msz += 32);
1140 outp = &out->s[dist];
1142 for (i = 0; i < n; i++)
1143 *outp++ = mb[i];
1145 } else
1146 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1148 int c;
1149 while (inp < &in->s[in->l]) {
1150 c = *inp++ & 0377;
1151 if (!isprint(c) && c != '\n' && c != '\r' &&
1152 c != '\b' && c != '\t')
1153 c = '?';
1154 *outp++ = c;
1156 out->l = in->l;
1158 out->s[out->l] = '\0';
1161 char *
1162 prstr(const char *s)
1164 struct str in, out;
1165 char *rp;
1167 in.s = (char *)s;
1168 in.l = strlen(s);
1169 makeprint(&in, &out);
1170 rp = salloc(out.l + 1);
1171 memcpy(rp, out.s, out.l);
1172 rp[out.l] = '\0';
1173 free(out.s);
1174 return rp;
1178 prout(const char *s, size_t sz, FILE *fp)
1180 struct str in, out;
1181 int n;
1183 in.s = (char *)s;
1184 in.l = sz;
1185 makeprint(&in, &out);
1186 n = fwrite(out.s, 1, out.l, fp);
1187 free(out.s);
1188 return n;
1192 * Print out a Unicode character or a substitute for it.
1195 putuc(int u, int c, FILE *fp)
1197 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
1198 if (utf8 && u & ~(wchar_t)0177) {
1199 char mb[MB_LEN_MAX];
1200 int i, n, r = 0;
1201 if ((n = wctomb(mb, u)) > 0) {
1202 for (i = 0; i < n; i++)
1203 r += putc(mb[i] & 0377, fp) != EOF;
1204 return r;
1205 } else if (n == 0)
1206 return putc('\0', fp) != EOF;
1207 else
1208 return 0;
1209 } else
1210 #endif /* HAVE_MBTOWC && HAVE_WCTYPE_H */
1211 return putc(c, fp) != EOF;
1215 * Locale-independent character class functions.
1217 int
1218 asccasecmp(const char *s1, const char *s2)
1220 register int cmp;
1223 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1224 return cmp;
1225 while (*s1++ != '\0' && *s2++ != '\0');
1226 return 0;
1230 ascncasecmp(const char *s1, const char *s2, size_t sz)
1232 register int cmp;
1233 size_t i = 1;
1235 if (sz == 0)
1236 return 0;
1238 if ((cmp = lowerconv(*s1 & 0377) - lowerconv(*s2 & 0377)) != 0)
1239 return cmp;
1240 while (i++ < sz && *s1++ != '\0' && *s2++ != '\0');
1241 return 0;
1244 char *
1245 asccasestr(const char *haystack, const char *xneedle)
1247 char *needle, *NEEDLE;
1248 int i, sz;
1250 sz = strlen(xneedle);
1251 if (sz == 0)
1252 return (char *)haystack;
1253 needle = ac_alloc(sz);
1254 NEEDLE = ac_alloc(sz);
1255 for (i = 0; i < sz; i++) {
1256 needle[i] = lowerconv(xneedle[i]&0377);
1257 NEEDLE[i] = upperconv(xneedle[i]&0377);
1259 while (*haystack) {
1260 if (*haystack == *needle || *haystack == *NEEDLE) {
1261 for (i = 1; i < sz; i++)
1262 if (haystack[i] != needle[i] &&
1263 haystack[i] != NEEDLE[i])
1264 break;
1265 if (i == sz)
1266 return (char *)haystack;
1268 haystack++;
1270 return NULL;
1273 const unsigned char class_char[] = {
1274 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
1275 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1276 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
1277 C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL,
1278 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
1279 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1280 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
1281 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
1282 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
1283 C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1284 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
1285 C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1286 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
1287 C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,
1288 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
1289 C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1290 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
1291 C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1292 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
1293 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1294 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
1295 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
1296 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
1297 C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
1298 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
1299 C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1300 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
1301 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1302 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
1303 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
1304 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
1305 C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL