BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index 739df98..2ee24e0 100644 (file)
@@ -1,9 +1,45 @@
-# include "sendmail.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parseaddr.c        5.13 (Berkeley) 6/1/90";
+#endif /* not lint */
 
 
-static char    SccsId[] = "@(#)parseaddr.c     3.31    %G%";
+# include "sendmail.h"
 
 /*
 
 /*
-**  PARSE -- Parse an address
+**  PARSEADDR -- Parse an address
 **
 **     Parses an address and breaks it up into three parts: a
 **     net to transmit the message on, the host to transmit it
 **
 **     Parses an address and breaks it up into three parts: a
 **     net to transmit the message on, the host to transmit it
@@ -28,6 +64,8 @@ static char   SccsId[] = "@(#)parseaddr.c     3.31    %G%";
 **                     0 -- copy out the parsed user & host, but
 **                             don't copy the printname.
 **                     +1 -- copy everything.
 **                     0 -- copy out the parsed user & host, but
 **                             don't copy the printname.
 **                     +1 -- copy everything.
+**             delim -- the character to terminate the address, passed
+**                     to prescan.
 **
 **     Returns:
 **             A pointer to the address descriptor header (`a' if
 **
 **     Returns:
 **             A pointer to the address descriptor header (`a' if
@@ -38,16 +76,19 @@ static char SccsId[] = "@(#)parseaddr.c     3.31    %G%";
 **             none
 */
 
 **             none
 */
 
-# define DELIMCHARS    "$()<>,;\\\"\r\n"       /* word delimiters */
+/* following delimiters are inherent to the internal algorithms */
+# define DELIMCHARS    "\001()<>,;\\\"\r\n"    /* word delimiters */
 
 ADDRESS *
 
 ADDRESS *
-parse(addr, a, copyf)
+parseaddr(addr, a, copyf, delim)
        char *addr;
        register ADDRESS *a;
        int copyf;
        char *addr;
        register ADDRESS *a;
        int copyf;
+       char delim;
 {
        register char **pvp;
        register struct mailer *m;
 {
        register char **pvp;
        register struct mailer *m;
+       char pvpbuf[PSBUFSIZE];
        extern char **prescan();
        extern ADDRESS *buildaddr();
 
        extern char **prescan();
        extern ADDRESS *buildaddr();
 
@@ -55,20 +96,20 @@ parse(addr, a, copyf)
        **  Initialize and prescan address.
        */
 
        **  Initialize and prescan address.
        */
 
-       To = addr;
-# ifdef DEBUG
-       if (Debug)
-               printf("\n--parse(%s)\n", addr);
-# endif DEBUG
+       CurEnv->e_to = addr;
+       if (tTd(20, 1))
+               printf("\n--parseaddr(%s)\n", addr);
 
 
-       pvp = prescan(addr, '\0');
+       pvp = prescan(addr, delim, pvpbuf);
        if (pvp == NULL)
                return (NULL);
 
        /*
        **  Apply rewriting rules.
        if (pvp == NULL)
                return (NULL);
 
        /*
        **  Apply rewriting rules.
+       **      Ruleset 0 does basic parsing.  It must resolve.
        */
 
        */
 
+       rewrite(pvp, 3);
        rewrite(pvp, 0);
 
        /*
        rewrite(pvp, 0);
 
        /*
@@ -97,51 +138,75 @@ parse(addr, a, copyf)
        */
 
        if (copyf > 0)
        */
 
        if (copyf > 0)
+       {
+               extern char *DelimChar;
+               char savec = *DelimChar;
+
+               *DelimChar = '\0';
                a->q_paddr = newstr(addr);
                a->q_paddr = newstr(addr);
+               *DelimChar = savec;
+       }
        else
                a->q_paddr = addr;
 
        else
                a->q_paddr = addr;
 
+       if (a->q_user == NULL)
+               a->q_user = "";
+       if (a->q_host == NULL)
+               a->q_host = "";
+
        if (copyf >= 0)
        {
        if (copyf >= 0)
        {
-               if (a->q_host != NULL)
-                       a->q_host = newstr(a->q_host);
-               else
-                       a->q_host = "";
+               a->q_host = newstr(a->q_host);
                if (a->q_user != a->q_paddr)
                        a->q_user = newstr(a->q_user);
        }
 
        /*
                if (a->q_user != a->q_paddr)
                        a->q_user = newstr(a->q_user);
        }
 
        /*
-       **  Do UPPER->lower case mapping unless inhibited.
+       **  Convert host name to lower case if requested.
+       **      User name will be done later.
        */
 
        */
 
-       if (!bitset(M_HST_UPPER, m->m_flags))
+       if (!bitnset(M_HST_UPPER, m->m_flags))
                makelower(a->q_host);
                makelower(a->q_host);
-       if (!bitset(M_USR_UPPER, m->m_flags))
-               makelower(a->q_user);
 
        /*
        **  Compute return value.
        */
 
 
        /*
        **  Compute return value.
        */
 
-# ifdef DEBUG
-       if (Debug)
+       if (tTd(20, 1))
        {
        {
-               printf("parse-->");
+               printf("parseaddr-->");
                printaddr(a, FALSE);
        }
                printaddr(a, FALSE);
        }
-# endif DEBUG
 
        return (a);
 }
 \f/*
 
        return (a);
 }
 \f/*
+**  LOWERADDR -- map UPPER->lower case on addresses as requested.
+**
+**     Parameters:
+**             a -- address to be mapped.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+loweraddr(a)
+       register ADDRESS *a;
+{
+       register MAILER *m = a->q_mailer;
+
+       if (!bitnset(M_USR_UPPER, m->m_flags))
+               makelower(a->q_user);
+}
+\f/*
 **  PRESCAN -- Prescan name and make it canonical
 **
 **  PRESCAN -- Prescan name and make it canonical
 **
-**     Scans a name and turns it into canonical form.  This involves
-**     deleting blanks, comments (in parentheses), and turning the
-**     word "at" into an at-sign ("@").  The name is copied as this
-**     is done; it is legal to copy a name onto itself, since this
-**     process can only make things smaller.
+**     Scans a name and turns it into a set of tokens.  This process
+**     deletes blanks and comments (in parentheses).
 **
 **     This routine knows about quoted strings and angle brackets.
 **
 **
 **     This routine knows about quoted strings and angle brackets.
 **
@@ -157,270 +222,208 @@ parse(addr, a, copyf)
 **             addr -- the name to chomp.
 **             delim -- the delimiter for the address, normally
 **                     '\0' or ','; \0 is accepted in any case.
 **             addr -- the name to chomp.
 **             delim -- the delimiter for the address, normally
 **                     '\0' or ','; \0 is accepted in any case.
-**                     are moving in place; set buflim to high core.
+**                     If '\t' then we are reading the .cf file.
+**             pvpbuf -- place to put the saved text -- note that
+**                     the pointers are static.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
 **
 **     Side Effects:
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
 **
 **     Side Effects:
-**             none.
+**             sets DelimChar to point to the character matching 'delim'.
 */
 
 */
 
-# define OPER          1
-# define ATOM          2
-# define EOTOK         3
-# define QSTRING       4
-# define SPACE         5
-# define DOLLAR                6
-# define GETONE                7
-# define MACRO         8
+/* states and character types */
+# define OPR           0       /* operator */
+# define ATM           1       /* atom */
+# define QST           2       /* in quoted string */
+# define SPC           3       /* chewing up spaces */
+# define ONE           4       /* pick up one character */
+
+# define NSTATES       5       /* number of states */
+# define TYPE          017     /* mask to select state type */
+
+/* meta bits for table */
+# define M             020     /* meta character; don't pass through */
+# define B             040     /* cause a break */
+# define MB            M|B     /* meta-break */
+
+static short StateTab[NSTATES][NSTATES] =
+{
+   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
+       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,
+       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,
+       /*QST*/         QST,    QST,    OPR,    QST,    QST,
+       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,
+       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
+};
+
+# define NOCHAR                -1      /* signal nothing in lookahead token */
+
+char   *DelimChar;             /* set to point to the delimiter */
 
 char **
 
 char **
-prescan(addr, delim)
+prescan(addr, delim, pvpbuf)
        char *addr;
        char delim;
        char *addr;
        char delim;
+       char pvpbuf[];
 {
        register char *p;
 {
        register char *p;
-       static char buf[MAXNAME+MAXATOM];
-       static char *av[MAXATOM+1];
+       register char *q;
+       register int c;
        char **avp;
        bool bslashmode;
        int cmntcnt;
        char **avp;
        bool bslashmode;
        int cmntcnt;
-       int brccnt;
-       register char c;
+       int anglecnt;
        char *tok;
        char *tok;
-       register char *q;
-       register int state;
-       int nstate;
-       extern char lower();
+       int state;
+       int newstate;
+       static char *av[MAXATOM+1];
+       extern int errno;
+
+       /* make sure error messages don't have garbage on them */
+       errno = 0;
 
 
-       q = buf;
+       q = pvpbuf;
        bslashmode = FALSE;
        bslashmode = FALSE;
-       cmntcnt = brccnt = 0;
+       cmntcnt = 0;
+       anglecnt = 0;
        avp = av;
        avp = av;
-       state = OPER;
-       for (p = addr; *p != '\0' && *p != delim; )
+       state = OPR;
+       c = NOCHAR;
+       p = addr;
+       if (tTd(22, 45))
+       {
+               printf("prescan: ");
+               xputs(p);
+               (void) putchar('\n');
+       }
+
+       do
        {
                /* read a token */
                tok = q;
        {
                /* read a token */
                tok = q;
-               while ((c = *p++) != '\0' && c != delim)
+               for (;;)
                {
                {
-                       /* chew up special characters */
+                       /* store away any old lookahead character */
+                       if (c != NOCHAR)
+                       {
+                               /* see if there is room */
+                               if (q >= &pvpbuf[PSBUFSIZE - 5])
+                               {
+                                       usrerr("Address too long");
+                                       DelimChar = p;
+                                       return (NULL);
+                               }
+
+                               /* squirrel it away */
+                               *q++ = c;
+                       }
+
+                       /* read a new input character */
+                       c = *p++;
+                       if (c == '\0')
+                               break;
                        c &= ~0200;
                        c &= ~0200;
+
+                       if (tTd(22, 101))
+                               printf("c=%c, s=%d; ", c, state);
+
+                       /* chew up special characters */
                        *q = '\0';
                        if (bslashmode)
                        {
                        *q = '\0';
                        if (bslashmode)
                        {
-                               c |= 0200;
+                               /* kludge \! for naive users */
+                               if (c != '!')
+                                       c |= 0200;
                                bslashmode = FALSE;
                        }
                        else if (c == '\\')
                        {
                                bslashmode = TRUE;
                                bslashmode = FALSE;
                        }
                        else if (c == '\\')
                        {
                                bslashmode = TRUE;
-                               continue;
+                               c = NOCHAR;
                        }
                        }
-                       else if (c == '"')
+                       else if (state == QST)
                        {
                        {
-                               if (state == QSTRING)
-                                       state = OPER;
-                               else
-                                       state = QSTRING;
-                               break;
+                               /* do nothing, just avoid next clauses */
                        }
                        }
-
-                       if (c == '$' && delim == '\t')
-                               nstate = DOLLAR;
-                       else
-                               nstate = toktype(c);
-                       switch (state)
+                       else if (c == '(')
                        {
                        {
-                         case QSTRING:         /* in quoted string */
-                               break;
-
-                         case ATOM:            /* regular atom */
-                               if (nstate != ATOM)
-                               {
-                                       state = EOTOK;
-                                       p--;
-                               }
-                               break;
-
-                         case GETONE:          /* grab one character */
-                               state = OPER;
-                               break;
-
-                         case EOTOK:           /* after atom or q-string */
-                               state = nstate;
-                               if (state == SPACE)
-                                       continue;
-                               break;
-
-                         case SPACE:           /* linear white space */
-                               state = nstate;
-                               break;
-
-                         case OPER:            /* operator */
-                               if (nstate == SPACE)
-                                       continue;
-                               state = nstate;
-                               break;
-
-                         case DOLLAR:          /* $- etc. */
-                               state = OPER;
-                               if (isascii(c) && isdigit(c))
+                               cmntcnt++;
+                               c = NOCHAR;
+                       }
+                       else if (c == ')')
+                       {
+                               if (cmntcnt <= 0)
                                {
                                {
-                                       /* replacement */
-                                       c = MATCHREPL;
-                                       state = GETONE;
-                                       p--;
-                                       break;
+                                       usrerr("Unbalanced ')'");
+                                       DelimChar = p;
+                                       return (NULL);
                                }
                                }
-                               switch (c)
+                               else
+                                       cmntcnt--;
+                       }
+                       else if (cmntcnt > 0)
+                               c = NOCHAR;
+                       else if (c == '<')
+                               anglecnt++;
+                       else if (c == '>')
+                       {
+                               if (anglecnt <= 0)
                                {
                                {
-                                 case '$':             /* literal $ */
-                                       break;
-
-                                 case '+':             /* match anything */
-                                       c = MATCHANY;
-                                       break;
-
-                                 case '-':             /* match one token */
-                                       c = MATCHONE;
-                                       break;
-
-                                 case '=':             /* match one token of class */
-                                       c = MATCHCLASS;
-                                       state = GETONE;
-                                       break;
-
-                                 case '#':             /* canonical net name */
-                                       c = CANONNET;
-                                       break;
-
-                                 case '@':             /* canonical host name */
-                                       c = CANONHOST;
-                                       break;
-
-                                 case ':':             /* canonical user name */
-                                       c = CANONUSER;
-                                       break;
-
-                                 default:
-                                       state = MACRO;
-                                       break;
+                                       usrerr("Unbalanced '>'");
+                                       DelimChar = p;
+                                       return (NULL);
                                }
                                }
-                               break;
-
-                         default:
-                               syserr("prescan: unknown state %d", state);
+                               anglecnt--;
                        }
                        }
+                       else if (delim == ' ' && isspace(c))
+                               c = ' ';
 
 
-                       if (state == EOTOK || state == SPACE)
-                               break;
-                       if (state == DOLLAR)
+                       if (c == NOCHAR)
                                continue;
 
                                continue;
 
-                       /* squirrel it away */
-                       if (q >= &buf[sizeof buf - 5])
-                       {
-                               usrerr("Address too long");
-                               return (NULL);
-                       }
-                       if (state == MACRO)
-                       {
-                               char mbuf[3];
-
-                               mbuf[0] = '$';
-                               mbuf[1] = c;
-                               mbuf[2] = '\0';
-                               (void) expand(mbuf, q, &buf[sizeof buf - 5]);
-                               q += strlen(q);
-                               state = EOTOK;
+                       /* see if this is end of input */
+                       if (c == delim && anglecnt <= 0 && state != QST)
                                break;
                                break;
-                       }
-                       *q++ = c;
 
 
-                       /* decide whether this represents end of token */
-                       if (state == OPER)
+                       newstate = StateTab[state][toktype(c)];
+                       if (tTd(22, 101))
+                               printf("ns=%02o\n", newstate);
+                       state = newstate & TYPE;
+                       if (bitset(M, newstate))
+                               c = NOCHAR;
+                       if (bitset(B, newstate))
                                break;
                }
                                break;
                }
-               if (c == '\0' || c == delim)
-                       p--;
 
                /* new token */
 
                /* new token */
-               if (tok == q)
-                       continue;
-               *q++ = '\0';
-
-               c = tok[0];
-               if (c == '(')
+               if (tok != q)
                {
                {
-                       cmntcnt++;
-                       continue;
-               }
-               else if (c == ')')
-               {
-                       if (cmntcnt <= 0)
-                       {
-                               usrerr("Unbalanced ')'");
-                               return (NULL);
-                       }
-                       else
-                       {
-                               cmntcnt--;
-                               continue;
-                       }
-               }
-               else if (cmntcnt > 0)
-                       continue;
-
-               /* we prefer <> specs */
-               if (c == '<')
-               {
-                       if (brccnt < 0)
-                       {
-                               usrerr("multiple < spec");
-                               return (NULL);
-                       }
-                       brccnt++;
-                       if (brccnt == 1)
+                       *q++ = '\0';
+                       if (tTd(22, 36))
                        {
                        {
-                               /* we prefer using machine readable name */
-                               q = buf;
-                               *q = '\0';
-                               avp = av;
-                               continue;
+                               printf("tok=");
+                               xputs(tok);
+                               (void) putchar('\n');
                        }
                        }
-               }
-               else if (c == '>')
-               {
-                       if (brccnt <= 0)
+                       if (avp >= &av[MAXATOM])
                        {
                        {
-                               usrerr("Unbalanced `>'");
+                               syserr("prescan: too many tokens");
+                               DelimChar = p;
                                return (NULL);
                        }
                                return (NULL);
                        }
-                       else
-                               brccnt--;
-                       if (brccnt <= 0)
-                       {
-                               brccnt = -1;
-                               continue;
-                       }
+                       *avp++ = tok;
                }
                }
-
-               if (avp >= &av[MAXATOM])
-               {
-                       syserr("prescan: too many tokens");
-                       return (NULL);
-               }
-               *avp++ = tok;
-       }
+       } while (c != '\0' && (c != delim || anglecnt > 0));
        *avp = NULL;
        *avp = NULL;
+       DelimChar = --p;
        if (cmntcnt > 0)
                usrerr("Unbalanced '('");
        if (cmntcnt > 0)
                usrerr("Unbalanced '('");
-       else if (brccnt > 0)
+       else if (anglecnt > 0)
                usrerr("Unbalanced '<'");
                usrerr("Unbalanced '<'");
-       else if (state == QSTRING)
+       else if (state == QST)
                usrerr("Unbalanced '\"'");
        else if (av[0] != NULL)
                return (av);
                usrerr("Unbalanced '\"'");
        else if (av[0] != NULL)
                return (av);
@@ -448,16 +451,20 @@ toktype(c)
        if (firstime)
        {
                firstime = FALSE;
        if (firstime)
        {
                firstime = FALSE;
-               (void) expand("$o", buf, &buf[sizeof buf - 1]);
-               strcat(buf, DELIMCHARS);
+               expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
+               (void) strcat(buf, DELIMCHARS);
        }
        }
+       if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
+               return (ONE);
+       if (c == '"')
+               return (QST);
        if (!isascii(c))
        if (!isascii(c))
-               return (ATOM);
-       if (isspace(c))
-               return (SPACE);
+               return (ATM);
+       if (isspace(c) || c == ')')
+               return (SPC);
        if (iscntrl(c) || index(buf, c) != NULL)
        if (iscntrl(c) || index(buf, c) != NULL)
-               return (OPER);
-       return (ATOM);
+               return (OPR);
+       return (ATM);
 }
 \f/*
 **  REWRITE -- apply rewrite rules to token vector.
 }
 \f/*
 **  REWRITE -- apply rewrite rules to token vector.
@@ -468,14 +475,15 @@ toktype(c)
 **
 **     For each rewrite rule, 'avp' points the address vector we
 **     are trying to match against, and 'pvp' points to the pattern.
 **
 **     For each rewrite rule, 'avp' points the address vector we
 **     are trying to match against, and 'pvp' points to the pattern.
-**     If pvp points to a special match value (MATCHANY, MATCHONE,
-**     MATCHCLASS) then the address in avp matched is saved away
-**     in the match vector (pointed to by 'mvp').
+**     If pvp points to a special match value (MATCHZANY, MATCHANY,
+**     MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
+**     matched is saved away in the match vector (pointed to by 'mvp').
 **
 **     When a match between avp & pvp does not match, we try to
 **
 **     When a match between avp & pvp does not match, we try to
-**     back out.  If we back up over a MATCHONE or a MATCHCLASS
+**     back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
 **     we must also back out the match in mvp.  If we reach a
 **     we must also back out the match in mvp.  If we reach a
-**     MATCHANY we just extend the match and start over again.
+**     MATCHANY or MATCHZANY we just extend the match and start
+**     over again.
 **
 **     When we finally match, we rewrite the address vector
 **     and try over again.
 **
 **     When we finally match, we rewrite the address vector
 **     and try over again.
@@ -507,19 +515,18 @@ rewrite(pvp, ruleset)
        register char *rp;              /* rewrite pointer */
        register char **avp;            /* address vector pointer */
        register char **rvp;            /* rewrite vector pointer */
        register char *rp;              /* rewrite pointer */
        register char **avp;            /* address vector pointer */
        register char **rvp;            /* rewrite vector pointer */
-       struct rewrite *rwr;            /* pointer to current rewrite rule */
+       register struct match *mlp;     /* cur ptr into mlist */
+       register struct rewrite *rwr;   /* pointer to current rewrite rule */
        struct match mlist[MAXMATCH];   /* stores match on LHS */
        struct match mlist[MAXMATCH];   /* stores match on LHS */
-       struct match *mlp;              /* cur ptr into mlist */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
-       extern bool sameword();
 
 
-# ifdef DEBUG
-       if (Debug > 9)
+       if (OpMode == MD_TEST || tTd(21, 2))
        {
        {
-               printf("rewrite: original pvp:\n");
+               printf("rewrite: ruleset %2d   input:", ruleset);
                printav(pvp);
        }
                printav(pvp);
        }
-# endif DEBUG
+       if (pvp == NULL)
+               return;
 
        /*
        **  Run through the list of rewrite rules, applying
 
        /*
        **  Run through the list of rewrite rules, applying
@@ -528,58 +535,75 @@ rewrite(pvp, ruleset)
 
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
 
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
-# ifdef DEBUG
-               if (Debug > 10)
+               if (tTd(21, 12))
                {
                {
-                       printf("-----trying rule:\n");
+                       printf("-----trying rule:");
                        printav(rwr->r_lhs);
                }
                        printav(rwr->r_lhs);
                }
-# endif DEBUG
 
                /* try to match on this rule */
                mlp = mlist;
 
                /* try to match on this rule */
                mlp = mlist;
-               for (rvp = rwr->r_lhs, avp = pvp; *avp != NULL; )
+               rvp = rwr->r_lhs;
+               avp = pvp;
+               while ((ap = *avp) != NULL || *rvp != NULL)
                {
                {
-                       ap = *avp;
                        rp = *rvp;
                        rp = *rvp;
-
+                       if (tTd(21, 35))
+                       {
+                               printf("ap=");
+                               xputs(ap);
+                               printf(", rp=");
+                               xputs(rp);
+                               printf("\n");
+                       }
                        if (rp == NULL)
                        {
                                /* end-of-pattern before end-of-address */
                        if (rp == NULL)
                        {
                                /* end-of-pattern before end-of-address */
-                               goto fail;
+                               goto backup;
+                       }
+                       if (ap == NULL && *rp != MATCHZANY)
+                       {
+                               /* end-of-input */
+                               break;
                        }
 
                        switch (*rp)
                        {
                                register STAB *s;
                        }
 
                        switch (*rp)
                        {
                                register STAB *s;
-                               register int class;
 
                          case MATCHCLASS:
 
                          case MATCHCLASS:
-                               /* match any token in a class */
-                               class = rp[1];
-                               if (!isalpha(class))
-                                       goto fail;
-                               if (isupper(class))
-                                       class -= 'A';
-                               else
-                                       class -= 'a';
+                         case MATCHNCLASS:
+                               /* match any token in (not in) a class */
                                s = stab(ap, ST_CLASS, ST_FIND);
                                s = stab(ap, ST_CLASS, ST_FIND);
-                               if (s == NULL || (s->s_class & (1 << class)) == 0)
-                                       goto fail;
+                               if (s == NULL || !bitnset(rp[1], s->s_class))
+                               {
+                                       if (*rp == MATCHCLASS)
+                                               goto backup;
+                               }
+                               else if (*rp == MATCHNCLASS)
+                                       goto backup;
 
                                /* explicit fall-through */
 
                          case MATCHONE:
                          case MATCHANY:
                                /* match exactly one token */
 
                                /* explicit fall-through */
 
                          case MATCHONE:
                          case MATCHANY:
                                /* match exactly one token */
-                               mlp->first = mlp->last = avp++;
+                               mlp->first = avp;
+                               mlp->last = avp++;
+                               mlp++;
+                               break;
+
+                         case MATCHZANY:
+                               /* match zero or more tokens */
+                               mlp->first = avp;
+                               mlp->last = avp - 1;
                                mlp++;
                                break;
 
                          default:
                                /* must have exact match */
                                mlp++;
                                break;
 
                          default:
                                /* must have exact match */
-                               if (!sameword(rp, ap))
-                                       goto fail;
+                               if (strcasecmp(rp, ap))
+                                       goto backup;
                                avp++;
                                break;
                        }
                                avp++;
                                break;
                        }
@@ -588,20 +612,22 @@ rewrite(pvp, ruleset)
                        rvp++;
                        continue;
 
                        rvp++;
                        continue;
 
-                 fail:
+                 backup:
                        /* match failed -- back up */
                        while (--rvp >= rwr->r_lhs)
                        {
                                rp = *rvp;
                        /* match failed -- back up */
                        while (--rvp >= rwr->r_lhs)
                        {
                                rp = *rvp;
-                               if (*rp == MATCHANY)
+                               if (*rp == MATCHANY || *rp == MATCHZANY)
                                {
                                        /* extend binding and continue */
                                {
                                        /* extend binding and continue */
-                                       mlp[-1].last = avp++;
+                                       avp = ++mlp[-1].last;
+                                       avp++;
                                        rvp++;
                                        break;
                                }
                                avp--;
                                        rvp++;
                                        break;
                                }
                                avp--;
-                               if (*rp == MATCHONE || *rp == MATCHCLASS)
+                               if (*rp == MATCHONE || *rp == MATCHCLASS ||
+                                   *rp == MATCHNCLASS)
                                {
                                        /* back out binding */
                                        mlp--;
                                {
                                        /* back out binding */
                                        mlp--;
@@ -619,90 +645,183 @@ rewrite(pvp, ruleset)
                **  See if we successfully matched
                */
 
                **  See if we successfully matched
                */
 
-               if (rvp >= rwr->r_lhs && *rvp == NULL)
+               if (rvp < rwr->r_lhs || *rvp != NULL)
                {
                {
-# ifdef DEBUG
-                       if (Debug > 10)
-                       {
-                               printf("-----rule matches:\n");
-                               printav(rwr->r_rhs);
-                       }
-# endif DEBUG
+                       if (tTd(21, 10))
+                               printf("----- rule fails\n");
+                       rwr = rwr->r_next;
+                       continue;
+               }
+
+               rvp = rwr->r_rhs;
+               if (tTd(21, 12))
+               {
+                       printf("-----rule matches:");
+                       printav(rvp);
+               }
 
 
-                       /* substitute */
-                       for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++)
+               rp = *rvp;
+               if (*rp == CANONUSER)
+               {
+                       rvp++;
+                       rwr = rwr->r_next;
+               }
+               else if (*rp == CANONHOST)
+               {
+                       rvp++;
+                       rwr = NULL;
+               }
+               else if (*rp == CANONNET)
+                       rwr = NULL;
+
+               /* substitute */
+               for (avp = npvp; *rvp != NULL; rvp++)
+               {
+                       register struct match *m;
+                       register char **pp;
+
+                       rp = *rvp;
+                       if (*rp == MATCHREPL)
                        {
                        {
-                               rp = *rvp;
-                               if (*rp == MATCHREPL)
+                               /* substitute from LHS */
+                               m = &mlist[rp[1] - '1'];
+                               if (m >= mlp)
                                {
                                {
-                                       register struct match *m;
-                                       register char **pp;
-
-                                       m = &mlist[rp[1] - '1'];
-# ifdef DEBUG
-                                       if (Debug > 13)
-                                       {
-                                               printf("$%c:", rp[1]);
-                                               pp = m->first;
-                                               do
-                                               {
-                                                       printf(" %x=\"", *pp);
-                                                       fflush(stdout);
-                                                       printf("%s\"", *pp);
-                                               } while (pp++ != m->last);
-                                               printf("\n");
-                                       }
-# endif DEBUG
+                                       syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
+                                       return;
+                               }
+                               if (tTd(21, 15))
+                               {
+                                       printf("$%c:", rp[1]);
                                        pp = m->first;
                                        pp = m->first;
-                                       do
+                                       while (pp <= m->last)
                                        {
                                        {
-                                               if (avp >= &npvp[MAXATOM])
-                                               {
-                                                       syserr("rewrite: expansion too long");
-                                                       return;
-                                               }
-                                               *avp++ = *pp;
-                                       } while (pp++ != m->last);
+                                               printf(" %x=\"", *pp);
+                                               (void) fflush(stdout);
+                                               printf("%s\"", *pp++);
+                                       }
+                                       printf("\n");
                                }
                                }
-                               else
+                               pp = m->first;
+                               while (pp <= m->last)
                                {
                                        if (avp >= &npvp[MAXATOM])
                                        {
                                                syserr("rewrite: expansion too long");
                                                return;
                                        }
                                {
                                        if (avp >= &npvp[MAXATOM])
                                        {
                                                syserr("rewrite: expansion too long");
                                                return;
                                        }
-                                       *avp++ = rp;
+                                       *avp++ = *pp++;
                                }
                        }
                                }
                        }
-                       *avp++ = NULL;
-                       bmove((char *) npvp, (char *) pvp, (avp - npvp) * sizeof *avp);
-# ifdef DEBUG
-                       if (Debug > 3)
+                       else
                        {
                        {
-                               char **vp;
-
-                               printf("rewritten as `");
-                               for (vp = pvp; *vp != NULL; vp++)
+                               /* vanilla replacement */
+                               if (avp >= &npvp[MAXATOM])
                                {
                                {
-                                       if (vp != pvp)
-                                               printf("_");
-                                       xputs(*vp);
+       toolong:
+                                       syserr("rewrite: expansion too long");
+                                       return;
                                }
                                }
-                               printf("'\n");
+                               *avp++ = rp;
                        }
                        }
-# endif DEBUG
-                       if (pvp[0][0] == CANONNET)
-                               break;
+               }
+               *avp++ = NULL;
+
+               /*
+               **  Check for any hostname lookups.
+               */
+
+               for (rvp = npvp; *rvp != NULL; rvp++)
+               {
+                       char **hbrvp;
+                       char **xpvp;
+                       int trsize;
+                       char *olddelimchar;
+                       char buf[MAXNAME + 1];
+                       char *pvpb1[MAXATOM + 1];
+                       char pvpbuf[PSBUFSIZE];
+                       extern char *DelimChar;
+
+                       if (**rvp != HOSTBEGIN)
+                               continue;
+
+                       /*
+                       **  Got a hostname lookup.
+                       **
+                       **      This could be optimized fairly easily.
+                       */
+
+                       hbrvp = rvp;
+
+                       /* extract the match part */
+                       while (*++rvp != NULL && **rvp != HOSTEND)
+                               continue;
+                       if (*rvp != NULL)
+                               *rvp++ = NULL;
+
+                       /* save the remainder of the input string */
+                       trsize = (int) (avp - rvp + 1) * sizeof *rvp;
+                       bcopy((char *) rvp, (char *) pvpb1, trsize);
+
+                       /* look it up */
+                       cataddr(++hbrvp, buf, sizeof buf);
+                       maphostname(buf, sizeof buf);
+
+                       /* scan the new host name */
+                       olddelimchar = DelimChar;
+                       xpvp = prescan(buf, '\0', pvpbuf);
+                       DelimChar = olddelimchar;
+                       if (xpvp == NULL)
+                       {
+                               syserr("rewrite: cannot prescan canonical hostname: %s", buf);
+                               return;
+                       }
+
+                       /* append it to the token list */
+                       for (avp = --hbrvp; *xpvp != NULL; xpvp++)
+                       {
+                               *avp++ = newstr(*xpvp);
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+                       }
+
+                       /* restore the old trailing information */
+                       for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+
+                       break;
+               }
+
+               /*
+               **  Check for subroutine calls.
+               */
+
+               if (*npvp != NULL && **npvp == CALLSUBR)
+               {
+                       bcopy((char *) &npvp[2], (char *) pvp,
+                               (int) (avp - npvp - 2) * sizeof *avp);
+                       if (tTd(21, 3))
+                               printf("-----callsubr %s\n", npvp[1]);
+                       rewrite(pvp, atoi(npvp[1]));
                }
                else
                {
                }
                else
                {
-# ifdef DEBUG
-                       if (Debug > 10)
-                               printf("----- rule fails\n");
-# endif DEBUG
-                       rwr = rwr->r_next;
+                       bcopy((char *) npvp, (char *) pvp,
+                               (int) (avp - npvp) * sizeof *avp);
+               }
+               if (tTd(21, 4))
+               {
+                       printf("rewritten as:");
+                       printav(pvp);
                }
        }
                }
        }
+
+       if (OpMode == MD_TEST || tTd(21, 2))
+       {
+               printf("rewrite: ruleset %2d returns:", ruleset);
+               printav(pvp);
+       }
 }
 \f/*
 **  BUILDADDR -- build address from token vector.
 }
 \f/*
 **  BUILDADDR -- build address from token vector.
@@ -725,15 +844,13 @@ buildaddr(tv, a)
        register char **tv;
        register ADDRESS *a;
 {
        register char **tv;
        register ADDRESS *a;
 {
-       register int i;
        static char buf[MAXNAME];
        struct mailer **mp;
        register struct mailer *m;
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
        static char buf[MAXNAME];
        struct mailer **mp;
        register struct mailer *m;
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
-       a->q_flags = 0;
-       a->q_home = NULL;
+       bzero((char *) a, sizeof *a);
 
        /* figure out what net/mailer to use */
        if (**tv != CANONNET)
 
        /* figure out what net/mailer to use */
        if (**tv != CANONNET)
@@ -742,55 +859,69 @@ buildaddr(tv, a)
                return (NULL);
        }
        tv++;
                return (NULL);
        }
        tv++;
-       if (strcmp(*tv, "error") == 0)
+       if (!strcasecmp(*tv, "error"))
        {
        {
-               if (**++tv != CANONUSER)
+               if (**++tv == CANONHOST)
+               {
+                       setstat(atoi(*++tv));
+                       tv++;
+               }
+               if (**tv != CANONUSER)
                        syserr("buildaddr: error: no user");
                buf[0] = '\0';
                while (*++tv != NULL)
                {
                        if (buf[0] != '\0')
                        syserr("buildaddr: error: no user");
                buf[0] = '\0';
                while (*++tv != NULL)
                {
                        if (buf[0] != '\0')
-                               strcat(buf, " ");
-                       strcat(buf, *tv);
+                               (void) strcat(buf, " ");
+                       (void) strcat(buf, *tv);
                }
                usrerr(buf);
                return (NULL);
        }
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
                }
                usrerr(buf);
                return (NULL);
        }
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (strcmp(m->m_name, *tv) == 0)
+               if (!strcasecmp(m->m_name, *tv))
                        break;
        }
        if (m == NULL)
        {
                        break;
        }
        if (m == NULL)
        {
-               syserr("buildaddr: unknown net %s", *tv);
+               syserr("buildaddr: unknown mailer %s", *tv);
                return (NULL);
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
        tv++;
                return (NULL);
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
        tv++;
-       if (!bitset(M_LOCAL, m->m_flags))
+       if (!bitnset(M_LOCAL, m->m_flags))
        {
        {
-               if (**tv != CANONHOST)
+               if (**tv++ != CANONHOST)
                {
                        syserr("buildaddr: no host");
                        return (NULL);
                }
                {
                        syserr("buildaddr: no host");
                        return (NULL);
                }
-               tv++;
-               a->q_host = *tv;
-               tv++;
+               buf[0] = '\0';
+               while (*tv != NULL && **tv != CANONUSER)
+                       (void) strcat(buf, *tv++);
+               a->q_host = newstr(buf);
        }
        else
                a->q_host = NULL;
 
        /* figure out the user */
        }
        else
                a->q_host = NULL;
 
        /* figure out the user */
-       if (**tv != CANONUSER)
+       if (*tv == NULL || **tv != CANONUSER)
        {
                syserr("buildaddr: no user");
                return (NULL);
        }
        {
                syserr("buildaddr: no user");
                return (NULL);
        }
-       cataddr(++tv, buf, sizeof buf);
+
+       /* rewrite according recipient mailer rewriting rules */
+       rewrite(++tv, 2);
+       if (m->m_r_rwset > 0)
+               rewrite(tv, m->m_r_rwset);
+       rewrite(tv, 4);
+
+       /* save the result for the command line/RCPT argument */
+       cataddr(tv, buf, sizeof buf);
        a->q_user = buf;
 
        return (a);
        a->q_user = buf;
 
        return (a);
@@ -820,17 +951,22 @@ cataddr(pvp, buf, sz)
        register int i;
        register char *p;
 
        register int i;
        register char *p;
 
+       if (pvp == NULL)
+       {
+               (void) strcpy(buf, "");
+               return;
+       }
        p = buf;
        p = buf;
-       sz--;
+       sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
-               natomtok = (toktype(**pvp) == ATOM);
+               natomtok = (toktype(**pvp) == ATM);
                if (oatomtok && natomtok)
                if (oatomtok && natomtok)
-                       *p++ = SPACESUB;
+                       *p++ = SpaceSub;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
-               sz -= i;
+               sz -= i + 1;
                pvp++;
        }
        *p = '\0';
                pvp++;
        }
        *p = '\0';
@@ -843,8 +979,6 @@ cataddr(pvp, buf, sz)
 **
 **     Parameters:
 **             a, b -- pointers to the internal forms to compare.
 **
 **     Parameters:
 **             a, b -- pointers to the internal forms to compare.
-**             wildflg -- if TRUE, 'a' may have no user specified,
-**                     in which case it is to match anything.
 **
 **     Returns:
 **             TRUE -- they represent the same mailbox.
 **
 **     Returns:
 **             TRUE -- they represent the same mailbox.
@@ -855,21 +989,20 @@ cataddr(pvp, buf, sz)
 */
 
 bool
 */
 
 bool
-sameaddr(a, b, wildflg)
+sameaddr(a, b)
        register ADDRESS *a;
        register ADDRESS *b;
        register ADDRESS *a;
        register ADDRESS *b;
-       bool wildflg;
 {
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
 {
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
-       if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0)
+       if (strcmp(a->q_user, b->q_user) != 0)
                return (FALSE);
 
        /* if the mailer ignores hosts, we have succeeded! */
                return (FALSE);
 
        /* if the mailer ignores hosts, we have succeeded! */
-       if (bitset(M_LOCAL, a->q_mailer->m_flags))
+       if (bitnset(M_LOCAL, a->q_mailer->m_flags))
                return (TRUE);
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
                return (TRUE);
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
@@ -894,27 +1027,161 @@ sameaddr(a, b, wildflg)
 **             none.
 */
 
 **             none.
 */
 
-# ifdef DEBUG
-
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
+       bool first = TRUE;
+
        while (a != NULL)
        {
        while (a != NULL)
        {
+               first = FALSE;
                printf("%x=", a);
                (void) fflush(stdout);
                printf("%x=", a);
                (void) fflush(stdout);
-               printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
-                      a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host, a->q_user);
-               printf("\tnext=%x, flags=%o, rmailer %d\n", a->q_next,
-                      a->q_flags, a->q_rmailer);
+               printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n",
+                      a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name,
+                      a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>");
+               printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
+                      a->q_alias);
+               printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
+                      a->q_fullname);
 
                if (!follow)
                        return;
                a = a->q_next;
        }
 
                if (!follow)
                        return;
                a = a->q_next;
        }
-       if (!follow)
+       if (first)
                printf("[NULL]\n");
 }
 
                printf("[NULL]\n");
 }
 
-# endif DEBUG
+\f/*
+**  REMOTENAME -- return the name relative to the current mailer
+**
+**     Parameters:
+**             name -- the name to translate.
+**             m -- the mailer that we want to do rewriting relative
+**                     to.
+**             senderaddress -- if set, uses the sender rewriting rules
+**                     rather than the recipient rewriting rules.
+**             canonical -- if set, strip out any comment information,
+**                     etc.
+**
+**     Returns:
+**             the text string representing this address relative to
+**                     the receiving mailer.
+**
+**     Side Effects:
+**             none.
+**
+**     Warnings:
+**             The text string returned is tucked away locally;
+**                     copy it if you intend to save it.
+*/
+
+char *
+remotename(name, m, senderaddress, canonical)
+       char *name;
+       struct mailer *m;
+       bool senderaddress;
+       bool canonical;
+{
+       register char **pvp;
+       char *fancy;
+       extern char *macvalue();
+       char *oldg = macvalue('g', CurEnv);
+       static char buf[MAXNAME];
+       char lbuf[MAXNAME];
+       char pvpbuf[PSBUFSIZE];
+       extern char **prescan();
+       extern char *crackaddr();
+
+       if (tTd(12, 1))
+               printf("remotename(%s)\n", name);
+
+       /* don't do anything if we are tagging it as special */
+       if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
+               return (name);
+
+       /*
+       **  Do a heuristic crack of this name to extract any comment info.
+       **      This will leave the name as a comment and a $g macro.
+       */
+
+       if (canonical)
+               fancy = "\001g";
+       else
+               fancy = crackaddr(name);
+
+       /*
+       **  Turn the name into canonical form.
+       **      Normally this will be RFC 822 style, i.e., "user@domain".
+       **      If this only resolves to "user", and the "C" flag is
+       **      specified in the sending mailer, then the sender's
+       **      domain will be appended.
+       */
+
+       pvp = prescan(name, '\0', pvpbuf);
+       if (pvp == NULL)
+               return (name);
+       rewrite(pvp, 3);
+       if (CurEnv->e_fromdomain != NULL)
+       {
+               /* append from domain to this address */
+               register char **pxp = pvp;
+
+               /* see if there is an "@domain" in the current name */
+               while (*pxp != NULL && strcmp(*pxp, "@") != 0)
+                       pxp++;
+               if (*pxp == NULL)
+               {
+                       /* no.... append the "@domain" from the sender */
+                       register char **qxq = CurEnv->e_fromdomain;
+
+                       while ((*pxp++ = *qxq++) != NULL)
+                               continue;
+                       rewrite(pvp, 3);
+               }
+       }
+
+       /*
+       **  Do more specific rewriting.
+       **      Rewrite using ruleset 1 or 2 depending on whether this is
+       **              a sender address or not.
+       **      Then run it through any receiving-mailer-specific rulesets.
+       */
+
+       if (senderaddress)
+       {
+               rewrite(pvp, 1);
+               if (m->m_s_rwset > 0)
+                       rewrite(pvp, m->m_s_rwset);
+       }
+       else
+       {
+               rewrite(pvp, 2);
+               if (m->m_r_rwset > 0)
+                       rewrite(pvp, m->m_r_rwset);
+       }
+
+       /*
+       **  Do any final sanitation the address may require.
+       **      This will normally be used to turn internal forms
+       **      (e.g., user@host.LOCAL) into external form.  This
+       **      may be used as a default to the above rules.
+       */
+
+       rewrite(pvp, 4);
+
+       /*
+       **  Now restore the comment information we had at the beginning.
+       */
+
+       cataddr(pvp, lbuf, sizeof lbuf);
+       define('g', lbuf, CurEnv);
+       expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
+       define('g', oldg, CurEnv);
+
+       if (tTd(12, 1))
+               printf("remotename => `%s'\n", buf);
+       return (buf);
+}