BSD 4_4_Lite2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index 9ef19f9..405486e 100644 (file)
@@ -1,28 +1,42 @@
 /*
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
- * %sccs.include.redist.c%
+ * 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
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parseaddr.c        8.44 (Berkeley) %G%";
+static char sccsid[] = "@(#)parseaddr.c        8.73 (Berkeley) 6/19/95";
 #endif /* not lint */
 
 #endif /* not lint */
 
-#include "sendmail.h"
-
-#ifdef CC_WONT_PROMOTE
-static int toktype __P((char));
-#else  /* !CC_WONT_PROMOTE */
-static int toktype __P((int));                         /* char -> int */
-#endif /* CC_WONT_PROMOTE */
-static void _rewrite __P((char **, int));
-static void callsubr __P((char **));
-static ADDRESS * buildaddr __P((char **, ADDRESS *));
-static void uurelativize __P((const char *, const char *, char **));
-
-char   *DelimChar;             /* set to point to the delimiter */
+# include "sendmail.h"
 
 /*
 **  PARSEADDR -- Parse an address
 
 /*
 **  PARSEADDR -- Parse an address
@@ -75,6 +89,9 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        auto char *delimptrbuf;
        bool queueup;
        char pvpbuf[PSBUFSIZE];
        auto char *delimptrbuf;
        bool queueup;
        char pvpbuf[PSBUFSIZE];
+       extern ADDRESS *buildaddr();
+       extern bool invalidaddr();
+       extern void allocaddr __P((ADDRESS *, int, char *));
 
        /*
        **  Initialize and prescan address.
 
        /*
        **  Initialize and prescan address.
@@ -84,26 +101,10 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        if (tTd(20, 1))
                printf("\n--parseaddr(%s)\n", addr);
 
        if (tTd(20, 1))
                printf("\n--parseaddr(%s)\n", addr);
 
-       {
-               extern char *DelimChar;         /* parseaddr.c */
-               char savec;
-               bool invalid;
-               extern char *finddelim();
-               extern bool invalidaddr();
-
-               DelimChar = finddelim(addr, delim);
-               savec = *DelimChar;
-               *DelimChar = '\0';
-               invalid = invalidaddr(addr);
-               *DelimChar = savec;
-               if (invalid)
-                       return (NULL);
-       }
-
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
-       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr);
+       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
        if (pvp == NULL)
        {
                if (tTd(20, 1))
        if (pvp == NULL)
        {
                if (tTd(20, 1))
@@ -178,6 +179,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
                if (e->e_message == NULL)
                        e->e_message = newstr(msg);
                a->q_flags |= QQUEUEUP;
                if (e->e_message == NULL)
                        e->e_message = newstr(msg);
                a->q_flags |= QQUEUEUP;
+               a->q_status = "4.4.3";
        }
 
        /*
        }
 
        /*
@@ -208,7 +210,7 @@ invalidaddr(addr, delimptr)
        register char *addr;
        char *delimptr;
 {
        register char *addr;
        char *delimptr;
 {
-       char savedelim;
+       char savedelim = '\0';
 
        if (delimptr != NULL)
        {
 
        if (delimptr != NULL)
        {
@@ -231,14 +233,14 @@ invalidaddr(addr, delimptr)
        }
        if (*addr == '\0')
        {
        }
        if (*addr == '\0')
        {
-               if (savedelim != '\0' && delimptr != NULL)
+               if (delimptr != NULL && savedelim != '\0')
                        *delimptr = savedelim;
                return FALSE;
        }
        setstat(EX_USAGE);
        usrerr("553 Address contained invalid control characters");
   addrfailure:
                        *delimptr = savedelim;
                return FALSE;
        }
        setstat(EX_USAGE);
        usrerr("553 Address contained invalid control characters");
   addrfailure:
-       if (savedelim != '\0' && delimptr != NULL)
+       if (delimptr != NULL && savedelim != '\0')
                *delimptr = savedelim;
        return TRUE;
 }
                *delimptr = savedelim;
        return TRUE;
 }
@@ -260,6 +262,7 @@ invalidaddr(addr, delimptr)
 **             Copies portions of a into local buffers as requested.
 */
 
 **             Copies portions of a into local buffers as requested.
 */
 
+void
 allocaddr(a, flags, paddr)
        register ADDRESS *a;
        int flags;
 allocaddr(a, flags, paddr)
        register ADDRESS *a;
        int flags;
@@ -286,45 +289,6 @@ allocaddr(a, flags, paddr)
                a->q_paddr = a->q_user;
 }
 \f/*
                a->q_paddr = a->q_user;
 }
 \f/*
-**  INVALIDADDR -- check an address string for invalid control characters.
-**
-**     Parameters:
-**             addr -- address string to be checked.
-**
-**     Returns:
-**             TRUE if address string could cause problems, FALSE o/w.
-**
-**     Side Effects:
-**             ExitStat may be changed and an error message generated.
-*/
-
-bool
-invalidaddr(addr)
-       const char *addr;
-{
-       register const char *cp;
-
-       /* make sure error messages don't have garbage on them */
-       errno = 0;
-
-       /*
-       ** Sendmail reserves characters 020 - 036 for rewriting rules
-       ** which can cause havoc (e.g. infinite rewriting loops) if
-       ** one shows up at the wrong time.  If any of these characters
-       ** appear in an address, the address is deemed "invalid" and
-       ** an error message is generated.
-       */
-
-       for (cp = addr; *cp; cp++)
-               if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001')
-               {
-                       setstat(EX_USAGE);
-                       usrerr("address contained invalid control char(s)");
-                       return (TRUE);
-               }
-       return (FALSE);
-}
-\f/*
 **  PRESCAN -- Prescan name and make it canonical
 **
 **     Scans a name and turns it into a set of tokens.  This process
 **  PRESCAN -- Prescan name and make it canonical
 **
 **     Scans a name and turns it into a set of tokens.  This process
@@ -350,6 +314,8 @@ invalidaddr(addr)
 **             pvpbsize -- size of pvpbuf.
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
 **             pvpbsize -- size of pvpbuf.
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
+**             toktab -- if set, a token table to use for parsing.
+**                     If NULL, use the default table.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
@@ -362,8 +328,9 @@ invalidaddr(addr)
 # define QST           2       /* in quoted string */
 # define SPC           3       /* chewing up spaces */
 # define ONE           4       /* pick up one character */
 # define QST           2       /* in quoted string */
 # define SPC           3       /* chewing up spaces */
 # define ONE           4       /* pick up one character */
+# define ILL           5       /* illegal character */
 
 
-# define NSTATES       5       /* number of states */
+# define NSTATES       6       /* number of states */
 # define TYPE          017     /* mask to select state type */
 
 /* meta bits for table */
 # define TYPE          017     /* mask to select state type */
 
 /* meta bits for table */
@@ -373,46 +340,101 @@ invalidaddr(addr)
 
 static short StateTab[NSTATES][NSTATES] =
 {
 
 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,
+   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     ILL     */
+       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|MB,
+       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,  ILL|MB,
+       /*QST*/         QST,    QST,    OPR,    QST,    QST,    QST,
+       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,    ILL|MB,
+       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,    ILL|MB,
+       /*ILL*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|M,
 };
 
 /* token type table -- it gets modified with $o characters */
 };
 
 /* token type table -- it gets modified with $o characters */
-static TokTypeTab[256] =
+static u_char  TokTypeTab[256] =
 {
 {
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM,ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 };
 
 };
 
-#define toktype(c)     ((int) TokTypeTab[(c) & 0xff])
+/* token type table for MIME parsing */
+u_char MimeTokenTab[256] =
+{
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+};
 
 
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
 
 
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
-prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
+prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
        char *addr;
        char *addr;
-       char delim;
+       int delim;
        char pvpbuf[];
        char **delimptr;
        char pvpbuf[];
        char **delimptr;
+       u_char *toktab;
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
@@ -427,6 +449,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
        char *saveto = CurEnv->e_to;
        static char *av[MAXATOM+1];
        static char firsttime = TRUE;
        char *saveto = CurEnv->e_to;
        static char *av[MAXATOM+1];
        static char firsttime = TRUE;
+       extern int errno;
 
        if (firsttime)
        {
 
        if (firsttime)
        {
@@ -434,7 +457,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                char obuf[50];
 
                firsttime = FALSE;
                char obuf[50];
 
                firsttime = FALSE;
-               expand("\201o", obuf, &obuf[sizeof obuf - sizeof DELIMCHARS], CurEnv);
+               expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
                strcat(obuf, DELIMCHARS);
                for (p = obuf; *p != '\0'; p++)
                {
                strcat(obuf, DELIMCHARS);
                for (p = obuf; *p != '\0'; p++)
                {
@@ -442,6 +465,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                TokTypeTab[*p & 0xff] = OPR;
                }
        }
                                TokTypeTab[*p & 0xff] = OPR;
                }
        }
+       if (toktab == NULL)
+               toktab = TokTypeTab;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
@@ -475,6 +500,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
                                if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
+                                       if (strlen(addr) > MAXNAME)
+                                               addr[MAXNAME] = '\0';
        returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
        returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
@@ -541,7 +568,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                        {
                                bslashmode = TRUE;
                        }
                        {
                                bslashmode = TRUE;
                        }
-                       if (state == QST)
+                       else if (state == QST)
                        {
                                /* do nothing, just avoid next clauses */
                        }
                        {
                                /* do nothing, just avoid next clauses */
                        }
@@ -576,20 +603,6 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                        }
                        else if (delim == ' ' && isascii(c) && isspace(c))
                                c = ' ';
                        }
                        else if (delim == ' ' && isascii(c) && isspace(c))
                                c = ' ';
-                       else if (c == ':' && !CurEnv->e_oldstyle)
-                       {
-                               /* consume characters until a semicolon */
-                               while (*p != '\0' && *p != ';')
-                                       p++;
-                               if (*p == '\0')
-                                       usrerr("Unbalanced ':...;' group spec");
-                               else
-                                       p++;
-                               c = ' ';
-                       }
-
-                       else if (c == ';') /* semicolons are not tokens */
-                               c = NOCHAR;
 
                        if (c == NOCHAR)
                                continue;
 
                        if (c == NOCHAR)
                                continue;
@@ -598,10 +611,17 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                        if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
                        if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
-                       newstate = StateTab[state][toktype(c)];
+                       newstate = StateTab[state][toktab[c & 0xff]];
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
                        state = newstate & TYPE;
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
                        state = newstate & TYPE;
+                       if (state == ILL)
+                       {
+                               if (isascii(c) && isprint(c))
+                                       usrerr("653 Illegal character %c", c);
+                               else
+                                       usrerr("653 Illegal character 0x%02x", c);
+                       }
                        if (bitset(M, newstate))
                                c = NOCHAR;
                        if (bitset(B, newstate))
                        if (bitset(M, newstate))
                                c = NOCHAR;
                        if (bitset(B, newstate))
@@ -685,58 +705,26 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
 **             pvp is modified.
 */
 
 **             pvp is modified.
 */
 
-# define OP_NONZLEN    00001
-# define OP_VARLEN     00002
-# define OP_CLASS      00004
-# define OP_EXACT      00010
-
 struct match
 {
        char    **first;        /* first token matched */
        char    **last;         /* last token matched */
        char    **pattern;      /* pointer to pattern */
 struct match
 {
        char    **first;        /* first token matched */
        char    **last;         /* last token matched */
        char    **pattern;      /* pointer to pattern */
-       char    **source;       /* left hand source operand */
-       char    flags;          /* attributes of this operator */
 };
 
 # define MAXMATCH      9       /* max params per rewrite */
 };
 
 # define MAXMATCH      9       /* max params per rewrite */
-# define MAX_CONTROL ' '
 
 # ifndef MAXRULERECURSION
 #  define MAXRULERECURSION     50      /* max recursion depth */
 # endif
 
 # ifndef MAXRULERECURSION
 #  define MAXRULERECURSION     50      /* max recursion depth */
 # endif
-static char control_opts[MAX_CONTROL];
 
 
 int
 
 
 int
-static char control_init_data[] = { 
-       MATCHZANY,      OP_VARLEN,
-       MATCHONE,       OP_NONZLEN,
-       MATCHANY,       OP_VARLEN|OP_NONZLEN,
-#ifdef MACVALUE
-       MACVALUE,       OP_EXACT,
-#endif /* MACVALUE */
-       MATCHNCLASS,    OP_NONZLEN,
-       MATCHCLASS,     OP_NONZLEN|OP_VARLEN|OP_CLASS,
-};
-
-static int nrw;
-
-void
 rewrite(pvp, ruleset, reclevel, e)
        char **pvp;
        int ruleset;
        int reclevel;
        register ENVELOPE *e;
 rewrite(pvp, ruleset, reclevel, e)
        char **pvp;
        int ruleset;
        int reclevel;
        register ENVELOPE *e;
-{
-       nrw = 0;
-       _rewrite(pvp, ruleset);
-}
-
-static void
-_rewrite(pvp, ruleset)
-       char **pvp;
-       int ruleset;
 {
        register char *ap;              /* address pointer */
        register char *rp;              /* rewrite pointer */
 {
        register char *ap;              /* address pointer */
        register char *rp;              /* rewrite pointer */
@@ -747,20 +735,13 @@ _rewrite(pvp, ruleset)
        int ruleno;                     /* current rule number */
        int rstat = EX_OK;              /* return status */
        int loopcount;
        int ruleno;                     /* current rule number */
        int rstat = EX_OK;              /* return status */
        int loopcount;
-       int subr;                       /* subroutine number if >= 0 */
-       bool dolookup;                  /* do host aliasing */
+       struct match mlist[MAXMATCH];   /* stores match on LHS */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
-       char tokbuf[MAXNAME+1];         /* for concatenated class tokens */
-       int nloops, nmatches = 0;       /* for looping rule checks */
-       struct rewrite *prev_rwr;       /* pointer to previous rewrite rule */
-       struct match mlist[MAXMATCH+1]; /* stores match on LHS */
-       struct match *old_mlp;          /* to save our place */
-       bool extend_match;      /* extend existing match during backup */
 
        if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d   input:", ruleset);
 
        if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d   input:", ruleset);
-               printcav(pvp);
+               printav(pvp);
        }
        if (ruleset < 0 || ruleset >= MAXRWSETS)
        {
        }
        if (ruleset < 0 || ruleset >= MAXRWSETS)
        {
@@ -775,22 +756,9 @@ _rewrite(pvp, ruleset)
        if (pvp == NULL)
                return EX_USAGE;
 
        if (pvp == NULL)
                return EX_USAGE;
 
-       if (++nrw > 100)
-       {
-               char buf[MAXLINE];
-
-               buf[0] = buf[MAXLINE-1] = 0;
-               while (*pvp)
-                       (void) strncat(buf, *pvp++, sizeof buf);
-               syserr("address causes rewrite loop: <%s>", buf);
-               return;
-       }
-
-       /* Be sure to recognize first rule as new */
-       prev_rwr = NULL;
-
        /*
        /*
-       **  Run through the list of rewrite rules, applying any that match.
+       **  Run through the list of rewrite rules, applying
+       **      any that match.
        */
 
        ruleno = 1;
        */
 
        ruleno = 1;
@@ -800,57 +768,15 @@ _rewrite(pvp, ruleset)
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
-                       printcav(rwr->r_lhs);
-               }
-
-               /*
-               **  Set up the match list.  This is done once for each
-               **  rule.  If a rule is used repeatedly, the list need not
-               **  be set up the next time.
-               */
-
-               if (rwr != prev_rwr)
-               {
-                       prev_rwr = rwr;
-                       for (rvp = rwr->r_lhs, mlp = mlist;
-                                *rvp && (mlp < &mlist[MAXMATCH]); rvp++)
-                       {
-                               mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ?
-                                                               0 : control_opts[**rvp] ;
-                               if (mlp->flags)
-                               {
-                                       mlp->source = rvp;
-                                       mlp++;
-                               }
-                       }
-                       if (*rvp)
-                       {
-                               syserr("Too many variables on LHS in ruleset %d", ruleset);
-                               return;
-                       }
-                       mlp->source = rvp;
-
-                       /* Make sure end marker is initialized */
-                       mlp->flags = 0;
+                       printav(rwr->r_lhs);
                }
 
                /* try to match on this rule */
                mlp = mlist;
                }
 
                /* try to match on this rule */
                mlp = mlist;
-
                rvp = rwr->r_lhs;
                avp = pvp;
                rvp = rwr->r_lhs;
                avp = pvp;
-               nloops = 0;
-               extend_match = FALSE;
-
                if (++loopcount > 100)
                {
                if (++loopcount > 100)
                {
-                       if (nloops++ > 400)
-                       {
-                               syserr("Looping on ruleset %d, rule %d",
-                                       ruleset, rwr-RewriteRules[ruleset]);
-                               mlp = mlist - 1; /* force rule failure */
-                               break;
-                       }
                        syserr("554 Infinite loop in ruleset %d, rule %d",
                                ruleset, ruleno);
                        if (tTd(21, 1))
                        syserr("554 Infinite loop in ruleset %d, rule %d",
                                ruleset, ruleno);
                        if (tTd(21, 1))
@@ -864,7 +790,6 @@ _rewrite(pvp, ruleset)
                while ((ap = *avp) != NULL || *rvp != NULL)
                {
                        rp = *rvp;
                while ((ap = *avp) != NULL || *rvp != NULL)
                {
                        rp = *rvp;
-
                        if (tTd(21, 35))
                        {
                                printf("ADVANCE rp=");
                        if (tTd(21, 35))
                        {
                                printf("ADVANCE rp=");
@@ -873,90 +798,76 @@ _rewrite(pvp, ruleset)
                                xputs(ap);
                                printf("\n");
                        }
                                xputs(ap);
                                printf("\n");
                        }
-
-                       if (extend_match)
-                               extend_match = FALSE;
-                       else
-                       {
-                               mlp->first = avp;
-                               mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ?
-                                                       avp + 1 : avp;
-                       }
-
                        if (rp == NULL)
                        if (rp == NULL)
+                       {
                                /* end-of-pattern before end-of-address */
                                goto backup;
                                /* end-of-pattern before end-of-address */
                                goto backup;
-
-                       /* Premature end of address */
-                       if (ap == NULL && avp != mlp->last)
-                               goto backup;
-
-                       /*
-                       **  Simplest case - exact token comparison between
-                       **  pattern and address.  Such a match is not saved
-                       **  in mlp.
-                       */
-
-                       if (rvp < mlp->source)
+                       }
+                       if (ap == NULL && (*rp & 0377) != MATCHZANY &&
+                           (*rp & 0377) != MATCHZERO)
                        {
                        {
-                               if (ap == NULL || strcasecmp(ap, rp))
-                                       goto backup;
-                               rvp++;
-                               avp++;
-                               continue;
+                               /* end-of-input with patterns left */
+                               goto backup;
                        }
 
                        }
 
-#ifdef MACVALUE
-                       /*
-                       **  This is special case handled.  The match is exact,
-                       **  but might span zero or more address tokens.  The
-                       **  result is saved in mlp.
-                       */
-
-                       if (*rp == MACVALUE)
+                       switch (*rp & 0377)
                        {
                        {
-                               int len;
-                               rp = macvalue(rp[1], CurEnv);
+                               char buf[MAXLINE];
 
 
-                               if (rp)
-                                       while (*rp)
+                         case MATCHCLASS:
+                               /* match any phrase in a class */
+                               mlp->pattern = rvp;
+                               mlp->first = avp;
+       extendclass:
+                               ap = *avp;
+                               if (ap == NULL)
+                                       goto backup;
+                               mlp->last = avp++;
+                               cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
+                               if (!wordinclass(buf, rp[1]))
+                               {
+                                       if (tTd(21, 36))
                                        {
                                        {
-                                               if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp)))
-                                                       goto backup;
-                                               rp += len;
-                                               avp++;
+                                               printf("EXTEND  rp=");
+                                               xputs(rp);
+                                               printf(", ap=");
+                                               xputs(ap);
+                                               printf("\n");
                                        }
                                        }
-                               mlp->last = avp;
-                               rvp++;
+                                       goto extendclass;
+                               }
+                               if (tTd(21, 36))
+                                       printf("CLMATCH\n");
                                mlp++;
                                mlp++;
-                               continue;
-                       }
-#endif /* MACVALUE */
+                               break;
 
 
-                       /*
-                       **  All other matches are saved in mlp.  Initially
-                       **  assume they match at the shortest possible length
-                       **  for this pattern.  Variable patterns will be
-                       **  extended later as needed.
-                       */
+                         case MATCHNCLASS:
+                               /* match any token not in a class */
+                               if (wordinclass(ap, rp[1]))
+                                       goto backup;
 
 
-                       /* Fixed length first */
-                       if (!(mlp->flags & OP_VARLEN))
-                       {
-                               switch (*rp)
-                               {
-                                       break;
-                               }
+                               /* fall through */
+
+                         case MATCHONE:
+                         case MATCHANY:
+                               /* match exactly one token */
+                               mlp->pattern = rvp;
+                               mlp->first = avp;
+                               mlp->last = avp++;
+                               mlp++;
+                               break;
 
 
-                               avp = mlp->last;
-                               rvp++;
+                         case MATCHZANY:
+                               /* match zero or more tokens */
+                               mlp->pattern = rvp;
+                               mlp->first = avp;
+                               mlp->last = avp - 1;
                                mlp++;
                                break;
 
                          case MATCHZERO:
                                /* match zero tokens */
                                mlp++;
                                break;
 
                          case MATCHZERO:
                                /* match zero tokens */
-                               continue;
-                       }
+                               break;
 
                          case MACRODEXPAND:
                                /*
 
                          case MACRODEXPAND:
                                /*
@@ -991,210 +902,76 @@ _rewrite(pvp, ruleset)
                                /* match */
                                break;
 
                                /* match */
                                break;
 
-                       /*
-                       **  We now have a variable length item.  It could
-                       **  be $+ or $* in which case no special checking
-                       **  is needed.  But a class match such as $=x must
-                       **  be verified.
-                       **
-                       **  As a speedup, if a variable length item is
-                       **  followed by a plain character token, we initially
-                       **  extend the match to the first such token we find.
-                       **  If the required character token cannot be found,
-                       **  we fail the match at this point.
-                       */
-
-                       avp = mlp->last;
-
-                       /* If next token is char token */
-                       if (&rvp[1] < mlp[1].source)
-                       {
-                               while (*avp && strcasecmp(*avp, rvp[1]))
-                                       avp++;
-
-                               /*
-                               **  If we can't find the proper ending token,
-                               **  leave avp point to NULL.  This indicates
-                               **  we have run out of address tokens.  It is
-                               **  pointless to advance the beginning of this
-                               **  match and retry.
-                               */
-
-                               if (*avp == NULL)
+                         default:
+                               /* must have exact match */
+                               if (strcasecmp(rp, ap))
                                        goto backup;
                                        goto backup;
-                               mlp->last = avp;
-                       }
-                       else if (rvp[1] == NULL)
-                       /* next token is end of address */
-                       {
-                               while (*avp)
-                                       avp++;
-                               mlp->last = avp;
-                       }
-
-                       if (mlp->flags & OP_CLASS)
-                       {
-                               register char *cp = tokbuf;
-
-                               avp = mlp->first;
-                               strcpy(cp, *avp);
                                avp++;
                                avp++;
-                               for (;;)
-                               {
-                                       while (avp < mlp->last)
-                                       {
-                                               while (*cp)
-                                                       cp++;
-                                               strcpy(cp, *avp);
-                                               avp++;
-                                       }
-                                       switch (*rp)
-                                       {
-                                               register STAB *s;
-
-                                           case MATCHCLASS:
-                                               s = stab(tokbuf, ST_CLASS, ST_FIND);
-                                               if (s != NULL && bitnset(rp[1], s->s_class))
-                                                       goto have_match;
-                                               break;
-                                       }
-
-                                       /*
-                                       **  Class match initially failed.
-                                       **  Extend the tentative match.
-                                       **  Again, if followed by a character
-                                       **  token, extend all the way to that
-                                       **  token before checking.
-                                       */
-
-                                       if (*avp)
-                                       {
-                                               (mlp->last)++;
-                                               if (&rvp[1] < mlp[1].source)
-                                               {
-                                                       while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1]))
-                                                               (mlp->last)++;
-                                                       if (*(mlp->last) == NULL)
-                                                               avp = mlp->last;
-                                               }
-                                       }
-                                       if (*avp == NULL)
-                                       {
-                                               /*
-                                               **  We could not find the
-                                               **  ending token.  But we had
-                                               **  found ending tokens before.
-                                               **  A match is still plausible
-                                               **  if the start of the
-                                               **  tentative match is advanced.
-                                               **  Hence we must not leave avp
-                                               **  pointing to NULL.
-                                               */
-                                               avp = mlp->first;
-                                               goto backup;
-                                       }
-                               }
+                               break;
                        }
 
                        }
 
- have_match:
+                       /* successful match on this token */
                        rvp++;
                        rvp++;
-                       mlp++;
                        continue;
 
                        continue;
 
-backup:
-                       /* We failed to match.  mlp marks point of failure */
-
-                       /*
-                       **  There is a special case when we have exhausted
-                       **  the address, but have not exhausted the pattern.
-                       **  Under normal circumstances we could consider the
-                       **  failure permanent, since extending the number of
-                       **  address tokens matched by a '$+' or a '$*' will
-                       **  only worsen the situation.
-                       **
-                       **  There is an exception, however.  It is possible
-                       **  that we have matched a class token, say '$=x',
-                       **  with three or more tokens.  Extending a '$+' say,
-                       **  which precedes the '$=x' will move the beginning
-                       **  of the '$=x' match to the right, but it might match
-                       **  a smaller number of tokens then, possibly
-                       **  correcting the mismatch.
-                       **
-                       **  Thus in this case we initially back up to the
-                       **  $=x which matches three or more tokens.
-                       */
-
-                       if (*avp == NULL)
+         backup:
+                       /* match failed -- back up */
+                       while (--mlp >= mlist)
                        {
                                rvp = mlp->pattern;
                        {
                                rvp = mlp->pattern;
-                               while (--mlp > mlist)
+                               rp = *rvp;
+                               avp = mlp->last + 1;
+                               ap = *avp;
+
+                               if (tTd(21, 36))
                                {
                                {
-                                       if ((mlp->flags & OP_CLASS) &&
-                                           mlp->last > 2 + mlp->first)
-                                               break;
+                                       printf("BACKUP  rp=");
+                                       xputs(rp);
+                                       printf(", ap=");
+                                       xputs(ap);
+                                       printf("\n");
                                }
                                }
-                       }
-
-                       /*
-                       **  Now backup till we find a match with a pattern
-                       **  whose length is extendable, and extend that.
-                       */
 
 
-                       mlp--;
-                       while (mlp >= mlist && !(mlp->flags & OP_VARLEN))
-                               mlp--;
+                               if (ap == NULL)
+                               {
+                                       /* run off the end -- back up again */
+                                       continue;
+                               }
+                               if ((*rp & 0377) == MATCHANY ||
+                                   (*rp & 0377) == MATCHZANY)
+                               {
+                                       /* extend binding and continue */
+                                       mlp->last = avp++;
+                                       rvp++;
+                                       mlp++;
+                                       break;
+                               }
+                               if ((*rp & 0377) == MATCHCLASS)
+                               {
+                                       /* extend binding and try again */
+                                       mlp->last = avp;
+                                       goto extendclass;
+                               }
+                       }
 
 
-                       /* Total failure to match */
                        if (mlp < mlist)
                        if (mlp < mlist)
-                               break;
-
-                       avp = ++(mlp->last);
-                       rvp = mlp->source;
-
-                       /*
-                       **  We have found a backup point.  Normally we would
-                       **  increase the matched amount by one token, and
-                       **  continue from the next item in the pattern.  But
-                       **  there are two special cases.  If this is a
-                       **  class-type match (OP_CLASS), we must test the
-                       **  validity of the extended match.  If this pattern
-                       **  item is directly followed by a character token, it
-                       **  is worth going back and locating the next such
-                       **  character token before we continue on.
-                       */
-                       if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source))
-                       {
-                               avp = mlp->first;
-                               extend_match = TRUE;
-                       }
-                       else
                        {
                        {
-                               mlp++;
-                               rvp++;
+                               /* total failure to match */
+                               break;
                        }
                }
 
                /*
                        }
                }
 
                /*
-               **  See if we successfully matched.
+               **  See if we successfully matched
                */
 
                */
 
-               if (mlp < mlist)
+               if (mlp < mlist || *rvp != NULL)
                {
                        if (tTd(21, 10))
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
                        ruleno++;
                        loopcount = 0;
                {
                        if (tTd(21, 10))
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
                        ruleno++;
                        loopcount = 0;
-                       nmatches = 0;
-                       continue;
-               }
-
-               if (nmatches++ > 200)
-               {
-                       syserr("Loop in ruleset %d, rule %d (too many matches)",
-                               ruleset, rwr - RewriteRules[ruleset]);
-                       rwr = rwr->r_next;
-                       nmatches = 0;
                        continue;
                }
 
                        continue;
                }
 
@@ -1202,7 +979,7 @@ backup:
                if (tTd(21, 12))
                {
                        printf("-----rule matches:");
                if (tTd(21, 12))
                {
                        printf("-----rule matches:");
-                       printcav(rvp);
+                       printav(rvp);
                }
 
                rp = *rvp;
                }
 
                rp = *rvp;
@@ -1212,7 +989,6 @@ backup:
                        rwr = rwr->r_next;
                        ruleno++;
                        loopcount = 0;
                        rwr = rwr->r_next;
                        ruleno++;
                        loopcount = 0;
-                       nmatches = 0;
                }
                else if ((*rp & 0377) == CANONHOST)
                {
                }
                else if ((*rp & 0377) == CANONHOST)
                {
@@ -1223,27 +999,19 @@ backup:
                        rwr = NULL;
 
                /* substitute */
                        rwr = NULL;
 
                /* substitute */
-               dolookup = FALSE;
                for (avp = npvp; *rvp != NULL; rvp++)
                {
                        register struct match *m;
                        register char **pp;
 
                        rp = *rvp;
                for (avp = npvp; *rvp != NULL; rvp++)
                {
                        register struct match *m;
                        register char **pp;
 
                        rp = *rvp;
-
-                       /* check to see if we should do a lookup */
-                       if (*rp == MATCHLOOKUP)
-                               dolookup = TRUE;
-
-                       /* see if there is substitution here */
-                       if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9')
+                       if ((*rp & 0377) == MATCHREPL)
                        {
                                /* substitute from LHS */
                                m = &mlist[rp[1] - '1'];
                                if (m < mlist || m >= mlp)
                                {
                        {
                                /* substitute from LHS */
                                m = &mlist[rp[1] - '1'];
                                if (m < mlist || m >= mlp)
                                {
-                                 toolong:
-                                       syserr("rewrite: ruleset %d: replacement #%c out of bounds",
+                                       syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
                                                ruleset, rp[1]);
                                        return EX_CONFIG;
                                }
                                                ruleset, rp[1]);
                                        return EX_CONFIG;
                                }
@@ -1251,7 +1019,7 @@ backup:
                                {
                                        printf("$%c:", rp[1]);
                                        pp = m->first;
                                {
                                        printf("$%c:", rp[1]);
                                        pp = m->first;
-                                       while (pp < m->last)
+                                       while (pp <= m->last)
                                        {
                                                printf(" %x=\"", *pp);
                                                (void) fflush(stdout);
                                        {
                                                printf(" %x=\"", *pp);
                                                (void) fflush(stdout);
@@ -1260,10 +1028,13 @@ backup:
                                        printf("\n");
                                }
                                pp = m->first;
                                        printf("\n");
                                }
                                pp = m->first;
-                               while (pp < m->last)
+                               while (pp <= m->last)
                                {
                                        if (avp >= &npvp[MAXATOM])
                                {
                                        if (avp >= &npvp[MAXATOM])
-                                               goto toolong;
+                                       {
+                                               syserr("554 rewrite: expansion too long");
+                                               return EX_DATAERR;
+                                       }
                                        *avp++ = *pp++;
                                }
                        }
                                        *avp++ = *pp++;
                                }
                        }
@@ -1271,21 +1042,23 @@ backup:
                        {
                                /* vanilla replacement */
                                if (avp >= &npvp[MAXATOM])
                        {
                                /* vanilla replacement */
                                if (avp >= &npvp[MAXATOM])
-                                       goto toolong;
-#ifdef MACVALUE
-                               if (*rp == MACVALUE)
                                {
                                {
-                                       char *p = macvalue(rp[1], CurEnv);
-
-                                       if (tTd(21, 2))
-                                               printf("expanding runtime macro '%c' to \"%s\"\n",
-                                                   rp[1], p ? p : "(null)");
-                                       if (p)
-                                               *avp++ = p;
+       toolong:
+                                       syserr("554 rewrite: expansion too long");
+                                       return EX_DATAERR;
                                }
                                }
-                               else
-#endif /* MACVALUE */
+                               if ((*rp & 0377) != MACRODEXPAND)
                                        *avp++ = rp;
                                        *avp++ = rp;
+                               else
+                               {
+                                       *avp = macvalue(rp[1], e);
+                                       if (tTd(21, 2))
+                                               printf("rewrite: RHS $&%c => \"%s\"\n",
+                                                       rp[1],
+                                                       *avp == NULL ? "(NULL)" : *avp);
+                                       if (*avp != NULL)
+                                               avp++;
+                               }
                        }
                }
                *avp++ = NULL;
                        }
                }
                *avp++ = NULL;
@@ -1296,7 +1069,7 @@ backup:
 
                for (rvp = npvp; *rvp != NULL; rvp++)
                {
 
                for (rvp = npvp; *rvp != NULL; rvp++)
                {
-                       char **hbrvp, **ubrvp;
+                       char **hbrvp;
                        char **xpvp;
                        int trsize;
                        char *replac;
                        char **xpvp;
                        int trsize;
                        char *replac;
@@ -1306,14 +1079,11 @@ backup:
                        char **key_rvp;
                        char **arg_rvp;
                        char **default_rvp;
                        char **key_rvp;
                        char **arg_rvp;
                        char **default_rvp;
-                       char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1];
+                       char buf[MAXNAME + 1];
                        char *pvpb1[MAXATOM + 1];
                        char *argvect[10];
                        char pvpbuf[PSBUFSIZE];
                        char *nullpvp[1];
                        char *pvpb1[MAXATOM + 1];
                        char *argvect[10];
                        char pvpbuf[PSBUFSIZE];
                        char *nullpvp[1];
-                       bool match, defaultpart;
-                       char begintype;
-                       char db = '\0';
 
                        if ((**rvp & 0377) != HOSTBEGIN &&
                            (**rvp & 0377) != LOOKUPBEGIN)
 
                        if ((**rvp & 0377) != HOSTBEGIN &&
                            (**rvp & 0377) != LOOKUPBEGIN)
@@ -1325,9 +1095,7 @@ backup:
                        **      This could be optimized fairly easily.
                        */
 
                        **      This could be optimized fairly easily.
                        */
 
-                       begintype = **rvp;
                        hbrvp = rvp;
                        hbrvp = rvp;
-                       ubrvp = NULL;
                        if ((**rvp & 0377) == HOSTBEGIN)
                        {
                                endtoken = HOSTEND;
                        if ((**rvp & 0377) == HOSTBEGIN)
                        {
                                endtoken = HOSTEND;
@@ -1395,139 +1163,146 @@ backup:
                        trsize = (int) (avp - rvp + 1) * sizeof *rvp;
                        bcopy((char *) rvp, (char *) pvpb1, trsize);
 
                        trsize = (int) (avp - rvp + 1) * sizeof *rvp;
                        bcopy((char *) rvp, (char *) pvpb1, trsize);
 
-                       /* append it to the token list */
-                               for (avp = hbrvp; *xpvp != NULL; xpvp++)
+                       /* look it up */
+                       cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
+                       argvect[0] = buf;
+                       if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags))
+                       {
+                               auto int stat = EX_OK;
+
+                               if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
+                                       stripquotes(buf);
+
+                               /* XXX should try to auto-open the map here */
+
+                               if (tTd(60, 1))
+                                       printf("map_lookup(%s, %s) => ",
+                                               mapname, buf);
+                               replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
+                                               buf, argvect, &stat);
+                               if (tTd(60, 1))
+                                       printf("%s (%d)\n",
+                                               replac ? replac : "NOT FOUND",
+                                               stat);
+
+                               /* should recover if stat == EX_TEMPFAIL */
+                               if (stat == EX_TEMPFAIL)
                                {
                                {
-                               *avp++ = newstr(*xpvp);
-                               if (avp >= &npvp[MAXATOM])
-                                       goto toolong;
+                                       rstat = EX_TEMPFAIL;
+                                       if (tTd(60, 1))
+                                               printf("map_lookup(%s, %s) failed (stat = %d)\n",
+                                                       mapname, buf, stat);
+                                       if (e->e_message == NULL)
+                                       {
+                                               char mbuf[300];
+
+                                               sprintf(mbuf, "map %s: lookup (%s) failed",
+                                                       mapname, buf);
+                                               e->e_message = newstr(mbuf);
+                                       }
                                }
                        }
                        else
                                }
                        }
                        else
-                               avp = hbrvp;
+                               replac = NULL;
 
 
-                       /* restore the old trailing information */
-                       rvp = avp - 1;
-                       for (xpvp = pvpb1; *xpvp != NULL; xpvp++)
+                       /* if no replacement, use default */
+                       if (replac == NULL && default_rvp != NULL)
                        {
                        {
-                               if (defaultpart && **xpvp == HOSTEND)
+                               /* create the default */
+                               cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
+                               replac = buf;
+                       }
+
+                       if (replac == NULL)
+                       {
+                               xpvp = key_rvp;
+                       }
+                       else if (*replac == '\0')
+                       {
+                               /* null replacement */
+                               nullpvp[0] = NULL;
+                               xpvp = nullpvp;
+                       }
+                       else
+                       {
+                               /* scan the new replacement */
+                               xpvp = prescan(replac, '\0', pvpbuf,
+                                              sizeof pvpbuf, NULL, NULL);
+                               if (xpvp == NULL)
                                {
                                {
-                                       defaultpart = FALSE;
-                                       rvp = avp - 1;
+                                       /* prescan already printed error */
+                                       return EX_DATAERR;
                                }
                                }
-                               else if (!defaultpart || !match)
-                                       *avp++ = *xpvp;
+                       }
+
+                       /* append it to the token list */
+                       for (avp = hbrvp; *xpvp != NULL; xpvp++)
+                       {
+                               *avp++ = newstr(*xpvp);
                                if (avp >= &npvp[MAXATOM])
                                        goto toolong;
                        }
                                if (avp >= &npvp[MAXATOM])
                                        goto toolong;
                        }
-                       *avp++ = NULL;
 
 
-                       /*break;*/
+                       /* restore the old trailing information */
+                       for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+
+                       break;
                }
 
                /*
                **  Check for subroutine calls.
                }
 
                /*
                **  Check for subroutine calls.
-               **  Then copy vector back into original space.
                */
 
                */
 
-               callsubr(npvp);
-
-               for (avp = npvp; *avp++ != NULL;);
-                       subr = atoi(*++rvp);
-                       rvp++;
+               if (*npvp != NULL && (**npvp & 0377) == CALLSUBR)
+               {
+                       int stat;
 
 
+                       if (npvp[1] == NULL)
+                       {
+                               syserr("parseaddr: NULL subroutine call in ruleset %d, rule %d",
+                                       ruleset, ruleno);
+                               *pvp = NULL;
+                       }
+                       else
+                       {
+                               int ruleset;
+                               STAB *s;
+
+                               bcopy((char *) &npvp[2], (char *) pvp,
+                                       (int) (avp - npvp - 2) * sizeof *avp);
+                               if (tTd(21, 3))
+                                       printf("-----callsubr %s\n", npvp[1]);
+                               ruleset = strtorwset(npvp[1], NULL, ST_FIND);
+                               stat = rewrite(pvp, ruleset, reclevel, e);
+                               if (rstat == EX_OK || stat == EX_TEMPFAIL)
+                                       rstat = stat;
+                               if (*pvp != NULL && (**pvp & 0377) == CANONNET)
+                               rwr = NULL;
+                       }
+               }
                else
                else
-                       subr = -1;
-
-               /*
-               **  Copy result back to original string.
-               */
-
-               for (avp = pvp; *rvp != NULL; rvp++)
-                       *avp++ = *rvp;
-               *avp = NULL;
-
-               /*
-               **  If this specified a subroutine, call it.
-               */
-
-               if (subr >= 0)
                {
                {
-# ifdef DEBUG
-                       if (tTd(21, 3))
-                               printf("-----callsubr %s\n", subr);
-# endif DEBUG
-                       rewrite(pvp, subr);
+                       bcopy((char *) npvp, (char *) pvp,
+                               (int) (avp - npvp) * sizeof *avp);
                }
                }
-
-               /*
-               **  Done with rewriting this pass.
-               */
-
                if (tTd(21, 4))
                {
                        printf("rewritten as:");
                if (tTd(21, 4))
                {
                        printf("rewritten as:");
-                       printcav(pvp);
+                       printav(pvp);
                }
        }
 
        if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d returns:", ruleset);
                }
        }
 
        if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d returns:", ruleset);
-               printcav(pvp);
+               printav(pvp);
        }
 
        return rstat;
 }
 \f/*
        }
 
        return rstat;
 }
 \f/*
-**  CALLSUBR -- call subroutines in rewrite vector
-**
-**     Parameters:
-**             pvp -- pointer to token vector.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             pvp is modified.
-*/
-
-static void
-callsubr(pvp)
-       char **pvp;
-{
-       char **rvp;
-       int subr;
-
-       for (; *pvp != NULL; pvp++)
-               if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0]))
-               {
-                       subr = atoi(pvp[1]);
-
-                       if (tTd(21, 3))
-                               printf("-----callsubr %d\n", subr);
-
-                       /*
-                       **  Take care of possible inner calls.
-                       */
-                       callsubr(pvp+2);
-
-                       /*
-                       **  Move vector up over calling opcode.
-                       */
-                       for (rvp = pvp+2; *rvp != NULL; rvp++)
-                               rvp[-2] = rvp[0];
-                       rvp[-2] = NULL;
-
-                       /*
-                       **  Call inferior ruleset.
-                       */
-                       _rewrite(pvp, subr);
-
-                       break;
-               }
-}
-\f/*
 **  BUILDADDR -- build address from token vector.
 **
 **     Parameters:
 **  BUILDADDR -- build address from token vector.
 **
 **     Parameters:
@@ -1565,7 +1340,7 @@ struct errcodes
        NULL,           EX_UNAVAILABLE,
 };
 
        NULL,           EX_UNAVAILABLE,
 };
 
-static ADDRESS *
+ADDRESS *
 buildaddr(tv, a, flags, e)
        register char **tv;
        register ADDRESS *a;
 buildaddr(tv, a, flags, e)
        register char **tv;
        register ADDRESS *a;
@@ -1574,11 +1349,13 @@ buildaddr(tv, a, flags, e)
 {
        struct mailer **mp;
        register struct mailer *m;
 {
        struct mailer **mp;
        register struct mailer *m;
-       char *bp;
-       int spaceleft;
+       register char *p;
+       char *mname;
+       char **hostp;
+       char hbuf[MAXNAME + 1];
        static MAILER errormailer;
        static char *errorargv[] = { "ERROR", NULL };
        static MAILER errormailer;
        static char *errorargv[] = { "ERROR", NULL };
-       static char buf[MAXNAME];
+       static char ubuf[MAXNAME + 1];
 
        if (tTd(24, 5))
        {
 
        if (tTd(24, 5))
        {
@@ -1588,13 +1365,13 @@ buildaddr(tv, a, flags, e)
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
-       clear((char *) a, sizeof *a);
+       bzero((char *) a, sizeof *a);
 
        /* set up default error return flags */
        a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
 
        /* figure out what net/mailer to use */
 
        /* set up default error return flags */
        a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
 
        /* figure out what net/mailer to use */
-       if (*tv == NULL || **tv != CANONNET)
+       if (*tv == NULL || (**tv & 0377) != CANONNET)
        {
                syserr("554 buildaddr: no net");
 badaddr:
        {
                syserr("554 buildaddr: no net");
 badaddr:
@@ -1609,54 +1386,61 @@ badaddr:
                }
                return a;
        }
                }
                return a;
        }
-       tv++;
-       if (strcasecmp(*tv, "error") == 0)
+       mname = *++tv;
+
+       /* extract host and user portions */
+       if ((**++tv & 0377) == CANONHOST)
+               hostp = ++tv;
+       else
+               hostp = NULL;
+       while (*tv != NULL && (**tv & 0377) != CANONUSER)
+               tv++;
+       if (*tv == NULL)
+       {
+               syserr("554 buildaddr: no user");
+               goto badaddr;
+       }
+       if (hostp != NULL)
+               cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
+       cataddr(++tv, NULL, ubuf, sizeof ubuf, '\0');
+
+       /* save away the host name */
+       if (strcasecmp(mname, "error") == 0)
        {
        {
-               if ((**++tv & 0377) == CANONHOST)
+               if (hostp != NULL)
                {
                        register struct errcodes *ep;
 
                {
                        register struct errcodes *ep;
 
-                       if (isascii(**++tv) && isdigit(**tv))
+                       if (strchr(hbuf, '.') != NULL)
+                       {
+                               a->q_status = newstr(hbuf);
+                               setstat(dsntoexitstat(hbuf));
+                       }
+                       else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
                        {
                        {
-                               setstat(atoi(*tv));
+                               setstat(atoi(hbuf));
                        }
                        else
                        {
                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
                        }
                        else
                        {
                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
-                                       if (strcasecmp(ep->ec_name, *tv) == 0)
+                                       if (strcasecmp(ep->ec_name, hbuf) == 0)
                                                break;
                                setstat(ep->ec_code);
                        }
                                                break;
                                setstat(ep->ec_code);
                        }
-                       tv++;
                }
                else
                        setstat(EX_UNAVAILABLE);
                }
                else
                        setstat(EX_UNAVAILABLE);
-               buf[0] = '\0';
-               for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
-               {
-                       if (buf[0] != '\0')
-                               (void) strcat(buf, " ");
-                       (void) strcat(buf, *tv);
-               }
-               if ((**tv & 0377) != CANONUSER)
-                       syserr("554 buildaddr: error: no user");
-               cataddr(++tv, NULL, buf, sizeof buf, ' ');
-               stripquotes(buf);
-#ifdef LOG
-               if (LogLevel > 8)
-                       syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
-                               CurEnv->e_id, buf);
-#endif /* LOG */
-               if (isascii(buf[0]) && isdigit(buf[0]) &&
-                   isascii(buf[1]) && isdigit(buf[1]) &&
-                   isascii(buf[2]) && isdigit(buf[2]) &&
-                   buf[3] == ' ')
+               stripquotes(ubuf);
+               if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
+                   isascii(ubuf[1]) && isdigit(ubuf[1]) &&
+                   isascii(ubuf[2]) && isdigit(ubuf[2]) &&
+                   ubuf[3] == ' ')
                {
                        char fmt[10];
 
                {
                        char fmt[10];
 
-                       strncpy(fmt, buf, 3);
+                       strncpy(fmt, ubuf, 3);
                        strcpy(&fmt[3], " %s");
                        strcpy(&fmt[3], " %s");
-                       usrerr(fmt, buf + 4);
+                       usrerr(fmt, ubuf + 4);
 
                        /*
                        **  If this is a 4xx code and we aren't running
 
                        /*
                        **  If this is a 4xx code and we aren't running
@@ -1672,88 +1456,79 @@ badaddr:
                }
                else
                {
                }
                else
                {
-                       usrerr("553 %s", buf);
+                       usrerr("553 %s", ubuf);
                }
                goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
                }
                goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (strcasecmp(m->m_name, *tv) == 0)
+               if (strcasecmp(m->m_name, mname) == 0)
                        break;
        }
        if (m == NULL)
        {
                        break;
        }
        if (m == NULL)
        {
-               syserr("554 buildaddr: unknown mailer %s", *tv);
+               syserr("554 buildaddr: unknown mailer %s", mname);
                goto badaddr;
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
                goto badaddr;
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
-       if (**++tv != CANONHOST)
+       if (hostp == NULL)
        {
        {
+               if (!bitnset(M_LOCALMAILER, m->m_flags))
+               {
+                       syserr("554 buildaddr: no host");
+                       goto badaddr;
+               }
                a->q_host = NULL;
        }
        else
                a->q_host = NULL;
        }
        else
-       {
-               else
-                       a->q_host = NULL;
-       }
-       else
-       {
-               while (*++tv != NULL && **tv != CANONUSER)
-                       (void) strcat(buf, *tv);
-               a->q_host = newstr(buf);
-       }
+               a->q_host = newstr(hbuf);
 
        /* figure out the user */
 
        /* figure out the user */
-       if (*tv == NULL || (**tv & 0377) != CANONUSER)
+       p = ubuf;
+       if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
        {
        {
-               syserr("554 buildaddr: no user");
-               goto badaddr;
+               p++;
+               tv++;
+               a->q_flags |= QNOTREMOTE;
        }
        }
-       tv++;
 
        /* do special mapping for local mailer */
 
        /* do special mapping for local mailer */
-       if (*tv != NULL)
+       if (*p == '"')
+               p++;
+       if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
+               a->q_mailer = m = ProgMailer;
+       else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
+               a->q_mailer = m = FileMailer;
+       else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
        {
        {
-               register char *p = *tv;
-
-               if (*p == '"')
-                       p++;
-               if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
-                       a->q_mailer = m = ProgMailer;
-               else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
-                       a->q_mailer = m = FileMailer;
-               else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
+               /* may be :include: */
+               stripquotes(ubuf);
+               if (strncasecmp(ubuf, ":include:", 9) == 0)
                {
                {
-                       /* may be :include: */
-                       cataddr(tv, NULL, buf, sizeof buf, '\0');
-                       stripquotes(buf);
-                       if (strncasecmp(buf, ":include:", 9) == 0)
-                       {
-                               /* if :include:, don't need further rewriting */
-                               a->q_mailer = m = InclMailer;
-                               a->q_user = &buf[9];
-                               return (a);
-                       }
+                       /* if :include:, don't need further rewriting */
+                       a->q_mailer = m = InclMailer;
+                       a->q_user = newstr(&ubuf[9]);
+                       return a;
                }
        }
 
                }
        }
 
-       if (bitnset(M_CHECKUDB, m->m_flags) && *tv != NULL &&
-           strcmp(*tv, "@") == 0)
+       /* rewrite according recipient mailer rewriting rules */
+       define('h', a->q_host, e);
+       if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
        {
        {
-               tv++;
-               a->q_flags |= QNOTREMOTE;
+               /* sender addresses done later */
+               (void) rewrite(tv, 2, 0, e);
+               if (m->m_re_rwset > 0)
+                      (void) rewrite(tv, m->m_re_rwset, 0, e);
        }
        }
-
-       if (m->m_r_rwset > 0)
-               rewrite(tv, m->m_r_rwset);
        (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
        (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
-       cataddr(tv, NULL, buf, sizeof buf, '\0');
-       a->q_user = buf;
+       cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
+       a->q_user = ubuf;
 
        /*
        **  Do mapping to lower case as requested by mailer
 
        /*
        **  Do mapping to lower case as requested by mailer
@@ -1764,7 +1539,7 @@ badaddr:
        if (!bitnset(M_USR_UPPER, m->m_flags))
                makelower(a->q_user);
 
        if (!bitnset(M_USR_UPPER, m->m_flags))
                makelower(a->q_user);
 
-       return (a);
+       return a;
 }
 \f/*
 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
 }
 \f/*
 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
@@ -1791,10 +1566,10 @@ cataddr(pvp, evp, buf, sz, spacesub)
        char **evp;
        char *buf;
        register int sz;
        char **evp;
        char *buf;
        register int sz;
-       char spacesub;
+       int spacesub;
 {
        bool oatomtok = FALSE;
 {
        bool oatomtok = FALSE;
-       bool natomtok;
+       bool natomtok = FALSE;
        register int i;
        register char *p;
 
        register int i;
        register char *p;
 
@@ -1810,7 +1585,7 @@ cataddr(pvp, evp, buf, sz, spacesub)
        sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
        sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
-               natomtok = (toktype(**pvp) == ATM);
+               natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
                if (oatomtok && natomtok)
                        *p++ = spacesub;
                (void) strcpy(p, *pvp);
                if (oatomtok && natomtok)
                        *p++ = spacesub;
                (void) strcpy(p, *pvp);
@@ -1851,7 +1626,7 @@ sameaddr(a, b)
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
-       if (strcasecmp(a->q_user, b->q_user))
+       if (strcmp(a->q_user, b->q_user) != 0)
                return (FALSE);
 
        /* if we have good uids for both but they differ, these are different */
                return (FALSE);
 
        /* if we have good uids for both but they differ, these are different */
@@ -1876,7 +1651,7 @@ sameaddr(a, b)
                /* only one is a null pointer */
                return (FALSE);
        }
                /* only one is a null pointer */
                return (FALSE);
        }
-       if (strcasecmp(a->q_host, b->q_host))
+       if (strcmp(a->q_host, b->q_host) != 0)
                return (FALSE);
 
        return (TRUE);
                return (FALSE);
 
        return (TRUE);
@@ -1895,23 +1670,55 @@ sameaddr(a, b)
 **             none.
 */
 
 **             none.
 */
 
+struct qflags
+{
+       char    *qf_name;
+       u_long  qf_bit;
+};
+
+struct qflags  AddressFlags[] =
+{
+       "QDONTSEND",            QDONTSEND,
+       "QBADADDR",             QBADADDR,
+       "QGOODUID",             QGOODUID,
+       "QPRIMARY",             QPRIMARY,
+       "QQUEUEUP",             QQUEUEUP,
+       "QSENT",                QSENT,
+       "QNOTREMOTE",           QNOTREMOTE,
+       "QSELFREF",             QSELFREF,
+       "QVERIFIED",            QVERIFIED,
+       "QBOGUSSHELL",          QBOGUSSHELL,
+       "QUNSAFEADDR",          QUNSAFEADDR,
+       "QPINGONSUCCESS",       QPINGONSUCCESS,
+       "QPINGONFAILURE",       QPINGONFAILURE,
+       "QPINGONDELAY",         QPINGONDELAY,
+       "QHASNOTIFY",           QHASNOTIFY,
+       "QRELAYED",             QRELAYED,
+       "QEXPANDED",            QEXPANDED,
+       "QDELIVERED",           QDELIVERED,
+       "QDELAYED",             QDELAYED,
+       "QTHISPASS",            QTHISPASS,
+       NULL
+};
+
 void
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
 void
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
-       bool first = TRUE;
        register MAILER *m;
        MAILER pseudomailer;
        register MAILER *m;
        MAILER pseudomailer;
+       register struct qflags *qfp;
+       bool firstone;
 
 
-       static int indent;
-       register int i;
+       if (a == NULL)
+       {
+               printf("[NULL]\n");
+               return;
+       }
 
        while (a != NULL)
        {
 
        while (a != NULL)
        {
-               first = FALSE;
-               for (i = indent; i > 0; i--)
-                       printf("\t");
                printf("%x=", a);
                (void) fflush(stdout);
 
                printf("%x=", a);
                (void) fflush(stdout);
 
@@ -1924,23 +1731,39 @@ printaddr(a, follow)
                        m->m_name = "NULL";
                }
 
                        m->m_name = "NULL";
                }
 
-               for (i = indent; i > 0; i--)
-                       printf("\t");
-               printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
-                      a->q_next, a->q_flags, a->q_rmailer, a->q_alias,
-                      a->q_sibling, a->q_child);
-               
-               /* follow the chain if appropriate */
+               printf("%s:\n\tmailer %d (%s), host `%s'\n",
+                      a->q_paddr, m->m_mno, m->m_name,
+                      a->q_host == NULL ? "<null>" : a->q_host);
+               printf("\tuser `%s', ruser `%s'\n",
+                      a->q_user,
+                      a->q_ruser == NULL ? "<null>" : a->q_ruser);
+               printf("\tnext=%x, alias %x, uid %d, gid %d\n",
+                      a->q_next, a->q_alias, a->q_uid, a->q_gid);
+               printf("\tflags=%lx<", a->q_flags);
+               firstone = TRUE;
+               for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
+               {
+                       if (!bitset(qfp->qf_bit, a->q_flags))
+                               continue;
+                       if (!firstone)
+                               printf(",");
+                       firstone = FALSE;
+                       printf("%s", qfp->qf_name);
+               }
+               printf(">\n");
+               printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
+                      a->q_owner == NULL ? "(none)" : a->q_owner,
+                      a->q_home == NULL ? "(none)" : a->q_home,
+                      a->q_fullname == NULL ? "(none)" : a->q_fullname);
+               printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n",
+                      a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
+                      a->q_statmta == NULL ? "(none)" : a->q_statmta,
+                      a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
+
                if (!follow)
                        return;
                if (!follow)
                        return;
-               
-               indent++;
-               printaddr(a->q_child, TRUE);
-               indent--;
-               a = a->q_sibling;
+               a = a->q_next;
        }
        }
-       if (first)
-               printf("[NULL]\n");
 }
 \f/*
 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
 }
 \f/*
 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
@@ -1986,7 +1809,7 @@ emptyaddr(a)
 char *
 remotename(name, m, flags, pstat, e)
        char *name;
 char *
 remotename(name, m, flags, pstat, e)
        char *name;
-       MAILER *m;
+       struct mailer *m;
        int flags;
        int *pstat;
        register ENVELOPE *e;
        int flags;
        int *pstat;
        register ENVELOPE *e;
@@ -1995,15 +1818,22 @@ remotename(name, m, flags, pstat, e)
        char *fancy;
        char *oldg = macvalue('g', e);
        int rwset;
        char *fancy;
        char *oldg = macvalue('g', e);
        int rwset;
-       static char buf[MAXNAME];
-       char lbuf[MAXNAME];
+       static char buf[MAXNAME + 1];
+       char lbuf[MAXNAME + 1];
        char pvpbuf[PSBUFSIZE];
        char pvpbuf[PSBUFSIZE];
+       extern char *crackaddr();
 
        if (tTd(12, 1))
                printf("remotename(%s)\n", name);
 
        /* don't do anything if we are tagging it as special */
 
        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)
+       if (bitset(RF_SENDERADDR, flags))
+               rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
+                                                    : m->m_se_rwset;
+       else
+               rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
+                                                    : m->m_re_rwset;
+       if (rwset < 0)
                return (name);
 
        /*
                return (name);
 
        /*
@@ -2024,7 +1854,7 @@ remotename(name, m, flags, pstat, e)
        **      domain will be appended.
        */
 
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
+       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
        if (pvp == NULL)
                return (name);
        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
        if (pvp == NULL)
                return (name);
        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
@@ -2051,14 +1881,21 @@ remotename(name, m, flags, pstat, e)
 
        /*
        **  Do more specific rewriting.
 
        /*
        **  Do more specific rewriting.
-       **      Rewrite using ruleset 1 or 2 for envelope addresses and
-       **      5 or 6 for header addresses depending on whether this
-       **      is a sender address or not.
+       **      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.
        */
 
        **      Then run it through any receiving-mailer-specific rulesets.
        */
 
+       if (bitset(RF_SENDERADDR, flags))
+       {
+               if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
+                       *pstat = EX_TEMPFAIL;
+       }
        else
        {
        else
        {
+               if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
+                       *pstat = EX_TEMPFAIL;
+       }
        if (rwset > 0)
        {
                if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
        if (rwset > 0)
        {
                if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
@@ -2084,9 +1921,9 @@ remotename(name, m, flags, pstat, e)
 
        /* need to make sure route-addrs have <angle brackets> */
        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
 
        /* need to make sure route-addrs have <angle brackets> */
        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
-               expand("<\201g>", buf, &buf[sizeof buf - 1], e);
+               expand("<\201g>", buf, sizeof buf, e);
        else
        else
-               expand(fancy, buf, &buf[sizeof buf - 1], e);
+               expand(fancy, buf, sizeof buf, e);
 
        define('g', oldg, e);
 
 
        define('g', oldg, e);
 
@@ -2095,76 +1932,6 @@ remotename(name, m, flags, pstat, e)
        return (buf);
 }
 \f/*
        return (buf);
 }
 \f/*
-**  UURELATIVIZE -- Make an address !-relative to recipient/sender nodes
-**
-**     Parameters:
-**             from -- the sending node (usually "$k" or "$w")
-**             to -- the receiving node (usually "$h")
-**             pvp -- address vector
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             The pvp is rewritten to be relative the "to" node
-**             wrt the "from" node.  In other words, if the pvp
-**             is headed by "to!" that part is stripped; otherwise
-**             "from!" is prepended.  Exception: "to!user" addresses
-**             with no '!'s in the user part are sent as is.
-**
-**     Bugs:
-**             The pvp may overflow, but we don't catch it.
-*/
-
-static void
-uurelativize(from, to, pvp)
-       const char *from, *to;
-       char **pvp;
-{
-       register char **pxp = pvp;
-       char expfrom[MAXNAME], expto[MAXNAME];
-
-       expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv);
-       expand(to, expto, &expto[sizeof expto - 1], CurEnv);
-
-       /*
-        * supposing that we've got something, should
-        * we add "from!" or remove "to!"?
-        */
-       if (pvp[0] != NULL)
-               if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
-                   /*strcasecmp?*/ strcmp(pvp[0], expto) != 0)
-               {
-                       /* either local name, no UUCP address, */
-                       /* or not to "to!" ==> prepend address with "from!" */
-
-                       /* already there? */
-                       if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
-                           /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0)
-                       {
-
-                               /* no, put it there */
-                               while (*pxp != NULL)
-                                       pxp++;
-                               do
-                                       pxp[2] = *pxp;
-                               while (pxp-- != pvp);
-                               pvp[0] = newstr(expfrom);
-                               pvp[1] = "!";
-                       }
-               }
-               else
-               {
-                       /* address is to "to!" -- remove if not "to!user" */
-                       for (pxp = &pvp[2];
-                            *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++)
-                               ;
-                       if (*pxp != NULL)
-                               for (pxp = pvp; *pxp != NULL; pxp++)
-                                       *pxp = pxp[2];
-               }
-}
-\f/*
 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
 **
 **     Parameters:
 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
 **
 **     Parameters:
@@ -2178,6 +1945,7 @@ uurelativize(from, to, pvp)
 **             none.
 */
 
 **             none.
 */
 
+void
 maplocaluser(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
 maplocaluser(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
@@ -2194,7 +1962,7 @@ maplocaluser(a, sendq, aliaslevel, e)
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
-       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
+       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
        if (pvp == NULL)
                return;
 
        if (pvp == NULL)
                return;
 
@@ -2238,6 +2006,7 @@ dequote_init(map, args)
 {
        register char *p = args;
 
 {
        register char *p = args;
 
+       map->map_mflags |= MF_KEEPQUOTES;
        for (;;)
        {
                while (isascii(*p) && isspace(*p))
        for (;;)
        {
                while (isascii(*p) && isspace(*p))