4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / usr.sbin / sendmail / src / util.c
index 082737f..2c40bf2 100644 (file)
@@ -1,16 +1,19 @@
-# include <stdio.h>
-# include <pwd.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <sysexits.h>
-# include <errno.h>
-# include <ctype.h>
-# include "sendmail.h"
-# include "conf.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
 
 
-SCCSID(@(#)util.c      3.37            %G%);
+#ifndef lint
+static char sccsid[] = "@(#)util.c     8.1 (Berkeley) %G%";
+#endif /* not lint */
 
 
-/*
+# include "sendmail.h"
+# include <sysexits.h>
+# include "conf.h"
+\f/*
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
 **     Runs through a string and strips off unquoted quote
 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
 **
 **     Runs through a string and strips off unquoted quote
@@ -18,8 +21,6 @@ SCCSID(@(#)util.c     3.37            %G%);
 **
 **     Parameters:
 **             s -- the string to strip.
 **
 **     Parameters:
 **             s -- the string to strip.
-**             qf -- if set, remove actual `` " '' characters
-**                     as well as the quote bits.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -31,9 +32,8 @@ SCCSID(@(#)util.c     3.37            %G%);
 **             deliver
 */
 
 **             deliver
 */
 
-stripquotes(s, qf)
+stripquotes(s)
        char *s;
        char *s;
-       bool qf;
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
@@ -42,75 +42,16 @@ stripquotes(s, qf)
        if (s == NULL)
                return;
 
        if (s == NULL)
                return;
 
-       for (p = q = s; (c = *p++) != '\0'; )
-       {
-               if (c != '"' || !qf)
-                       *q++ = c & 0177;
-       }
-       *q = '\0';
-}
-\f/*
-**  QSTRLEN -- give me the string length assuming 0200 bits add a char
-**
-**     Parameters:
-**             s -- the string to measure.
-**
-**     Reurns:
-**             The length of s, including space for backslash escapes.
-**
-**     Side Effects:
-**             none.
-*/
-
-qstrlen(s)
-       register char *s;
-{
-       register int l = 0;
-       register char c;
-
-       while ((c = *s++) != '\0')
-       {
-               if (bitset(0200, c))
-                       l++;
-               l++;
-       }
-       return (l);
-}
-\f/*
-**  CAPITALIZE -- return a copy of a string, properly capitalized.
-**
-**     Parameters:
-**             s -- the string to capitalize.
-**
-**     Returns:
-**             a pointer to a properly capitalized string.
-**
-**     Side Effects:
-**             none.
-*/
-
-char *
-capitalize(s)
-       register char *s;
-{
-       static char buf[50];
-       register char *p;
-
-       p = buf;
-
-       for (;;)
+       p = q = s;
+       do
        {
        {
-               while (!isalpha(*s) && *s != '\0')
-                       *p++ = *s++;
-               if (*s == '\0')
-                       break;
-               *p++ = toupper(*s++);
-               while (isalpha(*s))
-                       *p++ = *s++;
-       }
-
-       *p = '\0';
-       return (buf);
+               c = *p++;
+               if (c == '\\')
+                       c = *p++;
+               else if (c == '"')
+                       continue;
+               *q++ = c;
+       } while (c != '\0');
 }
 \f/*
 **  XALLOC -- Allocate memory and bitch wildly on failure.
 }
 \f/*
 **  XALLOC -- Allocate memory and bitch wildly on failure.
@@ -134,40 +75,16 @@ xalloc(sz)
 {
        register char *p;
 
 {
        register char *p;
 
-       p = malloc(sz);
+       p = malloc((unsigned) sz);
        if (p == NULL)
        {
                syserr("Out of memory!!");
        if (p == NULL)
        {
                syserr("Out of memory!!");
-               exit(EX_UNAVAILABLE);
+               abort();
+               /* exit(EX_UNAVAILABLE); */
        }
        return (p);
 }
 \f/*
        }
        return (p);
 }
 \f/*
-**  NEWSTR -- make copy of string.
-**
-**     Space is allocated for it using xalloc.
-**
-**     Parameters:
-**             string to copy.
-**
-**     Returns:
-**             pointer to new string.
-**
-**     Side Effects:
-**             none.
-*/
-
-char *
-newstr(s)
-       register char *s;
-{
-       register char *p;
-
-       p = xalloc(strlen(s) + 1);
-       (void) strcpy(p, s);
-       return (p);
-}
-\f/*
 **  COPYPLIST -- copy list of pointers.
 **
 **     This routine is the equivalent of newstr for lists of
 **  COPYPLIST -- copy list of pointers.
 **
 **     This routine is the equivalent of newstr for lists of
@@ -199,8 +116,8 @@ copyplist(list, copycont)
 
        vp++;
 
 
        vp++;
 
-       newvp = (char **) xalloc((vp - list) * sizeof *vp);
-       bmove((char *) list, (char *) newvp, (vp - list) * sizeof *vp);
+       newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
+       bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
 
        if (copycont)
        {
 
        if (copycont)
        {
@@ -211,6 +128,45 @@ copyplist(list, copycont)
        return (newvp);
 }
 \f/*
        return (newvp);
 }
 \f/*
+**  COPYQUEUE -- copy address queue.
+**
+**     This routine is the equivalent of newstr for address queues
+**     addresses marked with QDONTSEND aren't copied
+**
+**     Parameters:
+**             addr -- list of address structures to copy.
+**
+**     Returns:
+**             a copy of 'addr'.
+**
+**     Side Effects:
+**             none.
+*/
+
+ADDRESS *
+copyqueue(addr)
+       ADDRESS *addr;
+{
+       register ADDRESS *newaddr;
+       ADDRESS *ret;
+       register ADDRESS **tail = &ret;
+
+       while (addr != NULL)
+       {
+               if (!bitset(QDONTSEND, addr->q_flags))
+               {
+                       newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
+                       STRUCTCOPY(*addr, *newaddr);
+                       *tail = newaddr;
+                       tail = &newaddr->q_next;
+               }
+               addr = addr->q_next;
+       }
+       *tail = NULL;
+       
+       return ret;
+}
+\f/*
 **  PRINTAV -- print argument vector.
 **
 **     Parameters:
 **  PRINTAV -- print argument vector.
 **
 **     Parameters:
@@ -223,7 +179,6 @@ copyplist(list, copycont)
 **             prints av.
 */
 
 **             prints av.
 */
 
-# ifdef DEBUG
 printav(av)
        register char **av;
 {
 printav(av)
        register char **av;
 {
@@ -232,12 +187,11 @@ printav(av)
                if (tTd(0, 44))
                        printf("\n\t%08x=", *av);
                else
                if (tTd(0, 44))
                        printf("\n\t%08x=", *av);
                else
-                       putchar(' ');
+                       (void) putchar(' ');
                xputs(*av++);
        }
                xputs(*av++);
        }
-       putchar('\n');
+       (void) putchar('\n');
 }
 }
-# endif DEBUG
 \f/*
 **  LOWER -- turn letter into lower case.
 **
 \f/*
 **  LOWER -- turn letter into lower case.
 **
@@ -255,9 +209,7 @@ char
 lower(c)
        register char c;
 {
 lower(c)
        register char c;
 {
-       if (isascii(c) && isupper(c))
-               c = c - 'A' + 'a';
-       return (c);
+       return((isascii(c) && isupper(c)) ? tolower(c) : c);
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
 }
 \f/*
 **  XPUTS -- put string doing control escapes.
@@ -272,36 +224,72 @@ lower(c)
 **             output to stdout
 */
 
 **             output to stdout
 */
 
-# ifdef DEBUG
 xputs(s)
        register char *s;
 {
 xputs(s)
        register char *s;
 {
-       register char c;
+       register int c;
+       register struct metamac *mp;
+       extern struct metamac MetaMacros[];
 
        if (s == NULL)
        {
                printf("<null>");
                return;
        }
 
        if (s == NULL)
        {
                printf("<null>");
                return;
        }
-       putchar('"');
-       while ((c = *s++) != '\0')
+       while ((c = (*s++ & 0377)) != '\0')
        {
                if (!isascii(c))
                {
        {
                if (!isascii(c))
                {
-                       putchar('\\');
+                       if (c == MATCHREPL || c == MACROEXPAND)
+                       {
+                               putchar('$');
+                               continue;
+                       }
+                       for (mp = MetaMacros; mp->metaname != '\0'; mp++)
+                       {
+                               if ((mp->metaval & 0377) == c)
+                               {
+                                       printf("$%c", mp->metaname);
+                                       break;
+                               }
+                       }
+                       if (mp->metaname != '\0')
+                               continue;
+                       (void) putchar('\\');
                        c &= 0177;
                }
                        c &= 0177;
                }
-               if (iscntrl(c))
+               if (isprint(c))
+               {
+                       putchar(c);
+                       continue;
+               }
+
+               /* wasn't a meta-macro -- find another way to print it */
+               switch (c)
                {
                {
-                       putchar('^');
-                       c |= 0100;
+                 case '\0':
+                       continue;
+
+                 case '\n':
+                       c = 'n';
+                       break;
+
+                 case '\r':
+                       c = 'r';
+                       break;
+
+                 case '\t':
+                       c = 't';
+                       break;
+
+                 default:
+                       (void) putchar('^');
+                       (void) putchar(c ^ 0100);
+                       continue;
                }
                }
-               putchar(c);
        }
        }
-       putchar('"');
        (void) fflush(stdout);
 }
        (void) fflush(stdout);
 }
-# endif DEBUG
 \f/*
 **  MAKELOWER -- Translate a line into lower case
 **
 \f/*
 **  MAKELOWER -- Translate a line into lower case
 **
@@ -328,57 +316,7 @@ makelower(p)
                return;
        for (; (c = *p) != '\0'; p++)
                if (isascii(c) && isupper(c))
                return;
        for (; (c = *p) != '\0'; p++)
                if (isascii(c) && isupper(c))
-                       *p = c - 'A' + 'a';
-}
-\f/*
-**  SAMEWORD -- return TRUE if the words are the same
-**
-**     Ignores case.
-**
-**     Parameters:
-**             a, b -- the words to compare.
-**
-**     Returns:
-**             TRUE if a & b match exactly (modulo case)
-**             FALSE otherwise.
-**
-**     Side Effects:
-**             none.
-*/
-
-bool
-sameword(a, b)
-       register char *a, *b;
-{
-       while (lower(*a) == lower(*b))
-       {
-               if (*a == '\0')
-                       return (TRUE);
-               a++;
-               b++;
-       }
-       return (FALSE);
-}
-\f/*
-**  CLEAR -- clear a block of memory
-**
-**     Parameters:
-**             p -- location to clear.
-**             l -- number of bytes to clear.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             none.
-*/
-
-clear(p, l)
-       register char *p;
-       register int l;
-{
-       while (l-- > 0)
-               *p++ = 0;
+                       *p = tolower(c);
 }
 \f/*
 **  FULLNAME -- extract full name from a passwd file entry.
 }
 \f/*
 **  FULLNAME -- extract full name from a passwd file entry.
@@ -398,12 +336,26 @@ fullname(pw, buf)
        register struct passwd *pw;
        char *buf;
 {
        register struct passwd *pw;
        char *buf;
 {
+       register char *p;
        register char *bp = buf;
        register char *bp = buf;
+       int l;
        register char *p = pw->pw_gecos;
 
        register char *p = pw->pw_gecos;
 
-       if (*p == '*')
-               p++;
-       while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
+       if (*gecos == '*')
+               gecos++;
+
+       /* find length of final string */
+       l = 0;
+       for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+       {
+               if (*p == '&')
+                       l += strlen(login);
+               else
+                       l++;
+       }
+
+       /* now fill in buf */
+       for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
        {
                if (*p == '&')
                {
        {
                if (*p == '&')
                {
@@ -411,10 +363,9 @@ fullname(pw, buf)
                        *bp = toupper(*bp);
                        while (*bp != '\0')
                                bp++;
                        *bp = toupper(*bp);
                        while (*bp != '\0')
                                bp++;
-                       p++;
                }
                else
                }
                else
-                       *bp++ = *p++;
+                       *bp++ = *p;
        }
        *bp = '\0';
 }
        }
        *bp = '\0';
 }
@@ -427,25 +378,31 @@ fullname(pw, buf)
 **             mode -- mode bits that must match.
 **
 **     Returns:
 **             mode -- mode bits that must match.
 **
 **     Returns:
-**             TRUE if fn exists, is owned by uid, and matches mode.
-**             FALSE otherwise.
+**             0 if fn exists, is owned by uid, and matches mode.
+**             An errno otherwise.  The actual errno is cleared.
 **
 **     Side Effects:
 **             none.
 */
 
 **
 **     Side Effects:
 **             none.
 */
 
-bool
+int
 safefile(fn, uid, mode)
        char *fn;
 safefile(fn, uid, mode)
        char *fn;
-       int uid;
+       uid_t uid;
        int mode;
 {
        struct stat stbuf;
 
        int mode;
 {
        struct stat stbuf;
 
-       if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
-           (stbuf.st_mode & mode) == mode)
-               return (TRUE);
-       return (FALSE);
+       if (stat(fn, &stbuf) < 0)
+       {
+               int ret = errno;
+
+               errno = 0;
+               return ret;
+       }
+       if (stbuf.st_uid == uid && (stbuf.st_mode & mode) == mode)
+               return 0;
+       return EPERM;
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
 }
 \f/*
 **  FIXCRLF -- fix <CR><LF> in line.
@@ -472,35 +429,16 @@ fixcrlf(line, stripnl)
 {
        register char *p;
 
 {
        register char *p;
 
-       p = index(line, '\n');
+       p = strchr(line, '\n');
        if (p == NULL)
                return;
        if (p == NULL)
                return;
-       if (p[-1] == '\r')
+       if (p > line && p[-1] == '\r')
                p--;
        if (!stripnl)
                *p++ = '\n';
        *p = '\0';
 }
 \f/*
                p--;
        if (!stripnl)
                *p++ = '\n';
        *p = '\0';
 }
 \f/*
-**  SYSLOG -- fake entry to fool lint
-*/
-
-# ifdef LOG
-# ifdef lint
-
-/*VARARGS2*/
-syslog(pri, fmt, args)
-       int pri;
-       char *fmt;
-{
-       pri = *fmt;
-       args = pri;
-       pri = args;
-}
-
-# endif lint
-# endif LOG
-\f/*
 **  DFOPEN -- determined file open
 **
 **     This routine has the semantics of fopen, except that it will
 **  DFOPEN -- determined file open
 **
 **     This routine has the semantics of fopen, except that it will
@@ -509,25 +447,59 @@ syslog(pri, fmt, args)
 **     whatever), so this tries to get around it.
 */
 
 **     whatever), so this tries to get around it.
 */
 
+struct omodes
+{
+       int     mask;
+       int     mode;
+       char    *farg;
+} OpenModes[] =
+{
+       O_ACCMODE,              O_RDONLY,               "r",
+       O_ACCMODE|O_APPEND,     O_WRONLY,               "w",
+       O_ACCMODE|O_APPEND,     O_WRONLY|O_APPEND,      "a",
+       O_TRUNC,                0,                      "w+",
+       O_APPEND,               O_APPEND,               "a+",
+       0,                      0,                      "r+",
+};
+
 FILE *
 FILE *
-dfopen(filename, mode)
+dfopen(filename, omode, cmode)
        char *filename;
        char *filename;
-       char *mode;
+       int omode;
+       int cmode;
 {
        register int tries;
 {
        register int tries;
-       register FILE *fp;
+       int fd;
+       register struct omodes *om;
+       struct stat st;
+
+       for (om = OpenModes; om->mask != 0; om++)
+               if ((omode & om->mask) == om->mode)
+                       break;
 
        for (tries = 0; tries < 10; tries++)
        {
 
        for (tries = 0; tries < 10; tries++)
        {
-               sleep(10 * tries);
+               sleep((unsigned) (10 * tries));
                errno = 0;
                errno = 0;
-               fp = fopen(filename, mode);
-               if (fp != NULL)
+               fd = open(filename, omode, cmode);
+               if (fd >= 0)
                        break;
                if (errno != ENFILE && errno != EINTR)
                        break;
        }
                        break;
                if (errno != ENFILE && errno != EINTR)
                        break;
        }
-       return (fp);
+       if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
+       {
+               int locktype;
+
+               /* lock the file to avoid accidental conflicts */
+               if ((omode & O_ACCMODE) != O_RDONLY)
+                       locktype = LOCK_EX;
+               else
+                       locktype = LOCK_SH;
+               (void) lockfile(fd, filename, locktype);
+               errno = 0;
+       }
+       return fdopen(fd, om->farg);
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
 }
 \f/*
 **  PUTLINE -- put a line like fputs obeying SMTP conventions
@@ -538,7 +510,7 @@ dfopen(filename, mode)
 **     Parameters:
 **             l -- line to put.
 **             fp -- file to put it onto.
 **     Parameters:
 **             l -- line to put.
 **             fp -- file to put it onto.
-**             fullsmtp -- if set, obey strictest SMTP conventions.
+**             m -- the mailer used to control output.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -547,47 +519,53 @@ dfopen(filename, mode)
 **             output of l to fp.
 */
 
 **             output of l to fp.
 */
 
-# define SMTPLINELIM   990     /* maximum line length */
-
-putline(l, fp, fullsmtp)
+putline(l, fp, m)
        register char *l;
        FILE *fp;
        register char *l;
        FILE *fp;
-       bool fullsmtp;
+       MAILER *m;
 {
        register char *p;
 {
        register char *p;
-       char svchar;
+       register char svchar;
+
+       /* strip out 0200 bits -- these can look like TELNET protocol */
+       if (bitnset(M_7BITS, m->m_flags))
+       {
+               for (p = l; (svchar = *p) != '\0'; ++p)
+                       if (bitset(0200, svchar))
+                               *p = svchar &~ 0200;
+       }
 
        do
        {
                /* find the end of the line */
 
        do
        {
                /* find the end of the line */
-               p = index(l, '\n');
+               p = strchr(l, '\n');
                if (p == NULL)
                        p = &l[strlen(l)];
 
                /* check for line overflow */
                if (p == NULL)
                        p = &l[strlen(l)];
 
                /* check for line overflow */
-               while (fullsmtp && (p - l) > SMTPLINELIM)
+               while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
                {
                {
-                       register char *q = &l[SMTPLINELIM - 1];
+                       register char *q = &l[m->m_linelimit - 1];
 
                        svchar = *q;
                        *q = '\0';
 
                        svchar = *q;
                        *q = '\0';
+                       if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                               (void) putc('.', fp);
                        fputs(l, fp);
                        fputs(l, fp);
-                       fputs("!\r\n", fp);
+                       (void) putc('!', fp);
+                       fputs(m->m_eol, fp);
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
                        *q = svchar;
                        l = q;
                }
 
                /* output last part */
-               svchar = *p;
-               *p = '\0';
-               fputs(l, fp);
-               if (fullsmtp)
-                       fputc('\r', fp);
-               fputc('\n', fp);
-               *p = svchar;
-               l = p;
+               if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                       (void) putc('.', fp);
+               for ( ; l < p; ++l)
+                       (void) putc(*l, fp);
+               fputs(m->m_eol, fp);
                if (*l == '\n')
                if (*l == '\n')
-                       l++;
+                       ++l;
        } while (l[0] != '\0');
 }
 \f/*
        } while (l[0] != '\0');
 }
 \f/*
@@ -609,63 +587,121 @@ xunlink(f)
        register int i;
 
 # ifdef LOG
        register int i;
 
 # ifdef LOG
-       if (LogLevel > 20)
-               syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
-# endif LOG
+       if (LogLevel > 98)
+               syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
+# endif /* LOG */
 
        i = unlink(f);
 # ifdef LOG
 
        i = unlink(f);
 # ifdef LOG
-       if (i < 0 && LogLevel > 21)
+       if (i < 0 && LogLevel > 97)
                syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
                syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
-# endif LOG
+# endif /* LOG */
 }
 \f/*
 }
 \f/*
-**  SFGETS -- "safe" fgets -- times out.
+**  XFCLOSE -- close a file, doing logging as appropriate.
+**
+**     Parameters:
+**             fp -- file pointer for the file to close
+**             a, b -- miscellaneous crud to print for debugging
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             fp is closed.
+*/
+
+xfclose(fp, a, b)
+       FILE *fp;
+       char *a, *b;
+{
+       if (tTd(53, 99))
+               printf("xfclose(%x) %s %s\n", fp, a, b);
+       if (fclose(fp) < 0 && tTd(53, 99))
+               printf("xfclose FAILURE: %s\n", errstring(errno));
+}
+\f/*
+**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
 **
 **     Parameters:
 **             buf -- place to put the input line.
 **             siz -- size of buf.
 **             fp -- file to read from.
 **
 **     Parameters:
 **             buf -- place to put the input line.
 **             siz -- size of buf.
 **             fp -- file to read from.
+**             timeout -- the timeout before error occurs.
+**             during -- what we are trying to read (for error messages).
 **
 **     Returns:
 **
 **     Returns:
-**             NULL on error (including timeout).
+**             NULL on error (including timeout).  This will also leave
+**                     buf containing a null string.
 **             buf otherwise.
 **
 **     Side Effects:
 **             none.
 */
 
 **             buf otherwise.
 **
 **     Side Effects:
 **             none.
 */
 
-static bool    TimeoutFlag;
+static jmp_buf CtxReadTimeout;
 
 char *
 
 char *
-sfgets(buf, siz, fp)
+sfgets(buf, siz, fp, timeout, during)
        char *buf;
        int siz;
        FILE *fp;
        char *buf;
        int siz;
        FILE *fp;
+       time_t timeout;
+       char *during;
 {
        register EVENT *ev = NULL;
        register char *p;
 {
        register EVENT *ev = NULL;
        register char *p;
-       extern readtimeout();
+       static int readtimeout();
 
 
-       if (ReadTimeout != 0)
-               ev = setevent(ReadTimeout, readtimeout, 0);
-       TimeoutFlag = FALSE;
-       do
+       /* set the timeout */
+       if (timeout != 0)
+       {
+               if (setjmp(CtxReadTimeout) != 0)
+               {
+# ifdef LOG
+                       syslog(LOG_NOTICE,
+                           "timeout waiting for input from %s during %s\n",
+                           CurHostName? CurHostName: "local", during);
+# endif
+                       errno = 0;
+                       usrerr("451 timeout waiting for input during %s",
+                               during);
+                       buf[0] = '\0';
+                       return (NULL);
+               }
+               ev = setevent(timeout, readtimeout, 0);
+       }
+
+       /* try to read */
+       p = NULL;
+       while (p == NULL && !feof(fp) && !ferror(fp))
        {
                errno = 0;
                p = fgets(buf, siz, fp);
        {
                errno = 0;
                p = fgets(buf, siz, fp);
-       } while (!(p != NULL || TimeoutFlag || errno != EINTR));
+               if (errno == EINTR)
+                       clearerr(fp);
+       }
+
+       /* clear the event if it has not sprung */
        clrevent(ev);
        clrevent(ev);
+
+       /* clean up the books and exit */
        LineNumber++;
        LineNumber++;
-       if (TimeoutFlag)
-               syserr("sfgets: timeout on read (mailer may be hung)");
-       return (p);
+       if (p == NULL)
+       {
+               buf[0] = '\0';
+               return (NULL);
+       }
+       if (SevenBit)
+               for (p = buf; *p != '\0'; p++)
+                       *p &= ~0200;
+       return (buf);
 }
 
 static
 readtimeout()
 {
 }
 
 static
 readtimeout()
 {
-       TimeoutFlag = TRUE;
+       longjmp(CtxReadTimeout, 1);
 }
 \f/*
 **  FGETFOLDED -- like fgets, but know about folded lines.
 }
 \f/*
 **  FGETFOLDED -- like fgets, but know about folded lines.
@@ -676,7 +712,9 @@ readtimeout()
 **             f -- file to read from.
 **
 **     Returns:
 **             f -- file to read from.
 **
 **     Returns:
-**             buf on success, NULL on error or EOF.
+**             input line(s) on success, NULL on error or EOF.
+**             This will normally be buf -- unless the line is too
+**                     long, when it will be xalloc()ed.
 **
 **     Side Effects:
 **             buf gets lines from f, with continuation lines (lines
 **
 **     Side Effects:
 **             buf gets lines from f, with continuation lines (lines
@@ -691,24 +729,56 @@ fgetfolded(buf, n, f)
        FILE *f;
 {
        register char *p = buf;
        FILE *f;
 {
        register char *p = buf;
+       char *bp = buf;
        register int i;
 
        n--;
        register int i;
 
        n--;
-       while (fgets(p, n, f) != NULL)
+       while ((i = getc(f)) != EOF)
        {
        {
-               LineNumber++;
-               fixcrlf(p, TRUE);
-               i = fgetc(f);
-               if (i != EOF)
-                       ungetc(i, f);
-               if (i != ' ' && i != '\t')
-                       return (buf);
-               i = strlen(p);
-               p += i;
-               *p++ = '\n';
-               n -= i + 1;
+               if (i == '\r')
+               {
+                       i = getc(f);
+                       if (i != '\n')
+                       {
+                               if (i != EOF)
+                                       (void) ungetc(i, f);
+                               i = '\r';
+                       }
+               }
+               if (--n <= 0)
+               {
+                       /* allocate new space */
+                       char *nbp;
+                       int nn;
+
+                       nn = (p - bp);
+                       if (nn < MEMCHUNKSIZE)
+                               nn *= 2;
+                       else
+                               nn += MEMCHUNKSIZE;
+                       nbp = xalloc(nn);
+                       bcopy(bp, nbp, p - bp);
+                       p = &nbp[p - bp];
+                       if (bp != buf)
+                               free(bp);
+                       bp = nbp;
+                       n = nn - (p - bp);
+               }
+               *p++ = i;
+               if (i == '\n')
+               {
+                       LineNumber++;
+                       i = getc(f);
+                       if (i != EOF)
+                               (void) ungetc(i, f);
+                       if (i != ' ' && i != '\t')
+                               break;
+               }
        }
        }
-       return (NULL);
+       if (p == bp)
+               return (NULL);
+       *--p = '\0';
+       return (bp);
 }
 \f/*
 **  CURTIME -- return current time.
 }
 \f/*
 **  CURTIME -- return current time.
@@ -751,7 +821,7 @@ bool
 atobool(s)
        register char *s;
 {
 atobool(s)
        register char *s;
 {
-       if (*s == '\0' || index("tTyY", *s) != NULL)
+       if (*s == '\0' || strchr("tTyY", *s) != NULL)
                return (TRUE);
        return (FALSE);
 }
                return (TRUE);
        return (FALSE);
 }
@@ -808,22 +878,83 @@ waitfor(pid)
        return (st);
 }
 \f/*
        return (st);
 }
 \f/*
-**  CLOSEALL -- close all extraneous file descriptors
+**  BITINTERSECT -- tell if two bitmaps intersect
 **
 **     Parameters:
 **
 **     Parameters:
-**             none.
+**             a, b -- the bitmaps in question
 **
 **     Returns:
 **
 **     Returns:
+**             TRUE if they have a non-null intersection
+**             FALSE otherwise
+**
+**     Side Effects:
 **             none.
 **             none.
+*/
+
+bool
+bitintersect(a, b)
+       BITMAP a;
+       BITMAP b;
+{
+       int i;
+
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if ((a[i] & b[i]) != 0)
+                       return (TRUE);
+       return (FALSE);
+}
+\f/*
+**  BITZEROP -- tell if a bitmap is all zero
+**
+**     Parameters:
+**             map -- the bit map to check
+**
+**     Returns:
+**             TRUE if map is all zero.
+**             FALSE if there are any bits set in map.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             Closes all file descriptors except zero, one, and two.
+**             none.
 */
 
 */
 
-closeall()
+bool
+bitzerop(map)
+       BITMAP map;
 {
        int i;
 
 {
        int i;
 
-       for (i = 3; i < 50; i++)
-               (void) close(i);
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if (map[i] != 0)
+                       return (FALSE);
+       return (TRUE);
+}
+\f/*
+**  STRCONTAINEDIN -- tell if one string is contained in another
+**
+**     Parameters:
+**             a -- possible substring.
+**             b -- possible superstring.
+**
+**     Returns:
+**             TRUE if a is contained in b.
+**             FALSE otherwise.
+*/
+
+bool
+strcontainedin(a, b)
+       register char *a;
+       register char *b;
+{
+       int l;
+
+       l = strlen(a);
+       for (;;)
+       {
+               b = strchr(b, a[0]);
+               if (b == NULL)
+                       return FALSE;
+               if (strncmp(a, b, l) == 0)
+                       return TRUE;
+               b++;
+       }
 }
 }