don't pass through NOTIFY= unless it was given in the SMTP RCPT
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index 56628d3..0315056 100644 (file)
@@ -1,17 +1,19 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 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%
  */
 
 #ifndef lint
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parseaddr.c        6.49 (Berkeley) %G%";
+static char sccsid[] = "@(#)parseaddr.c        8.60 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "sendmail.h"
 
 #endif /* not lint */
 
 #include "sendmail.h"
 
+static int     callsubr __P((char **, int, ENVELOPE *));
+
 #ifdef CC_WONT_PROMOTE
 static int toktype __P((char));
 #else  /* !CC_WONT_PROMOTE */
 #ifdef CC_WONT_PROMOTE
 static int toktype __P((char));
 #else  /* !CC_WONT_PROMOTE */
@@ -42,14 +44,8 @@ char *DelimChar;             /* set to point to the delimiter */
 **             addr -- the address to parse.
 **             a -- a pointer to the address descriptor buffer.
 **                     If NULL, a header will be created.
 **             addr -- the address to parse.
 **             a -- a pointer to the address descriptor buffer.
 **                     If NULL, a header will be created.
-**             copyf -- determines what shall be copied:
-**                     -1 -- don't copy anything.  The printname
-**                             (q_paddr) is just addr, and the
-**                             user & host are allocated internally
-**                             to parse.
-**                     0 -- copy out the parsed user & host, but
-**                             don't copy the printname.
-**                     +1 -- copy everything.
+**             flags -- describe detail for parsing.  See RF_ definitions
+**                     in sendmail.h.
 **             delim -- the character to terminate the address, passed
 **                     to prescan.
 **             delimptr -- if non-NULL, set to the location of the
 **             delim -- the character to terminate the address, passed
 **                     to prescan.
 **             delimptr -- if non-NULL, set to the location of the
@@ -69,10 +65,10 @@ char        *DelimChar;             /* set to point to the delimiter */
 # define DELIMCHARS    "()<>,;\r\n"    /* default word delimiters */
 
 ADDRESS *
 # define DELIMCHARS    "()<>,;\r\n"    /* default word delimiters */
 
 ADDRESS *
-parseaddr(addr, a, copyf, delim, delimptr, e)
+parseaddr(addr, a, flags, delim, delimptr, e)
        char *addr;
        register ADDRESS *a;
        char *addr;
        register ADDRESS *a;
-       int copyf;
+       int flags;
        int delim;
        char **delimptr;
        register ENVELOPE *e;
        int delim;
        char **delimptr;
        register ENVELOPE *e;
@@ -90,13 +86,6 @@ parseaddr(addr, a, copyf, delim, delimptr, e)
        if (tTd(20, 1))
                printf("\n--parseaddr(%s)\n", addr);
 
        if (tTd(20, 1))
                printf("\n--parseaddr(%s)\n", addr);
 
-       if (invalidaddr(addr))
-       {
-               if (tTd(20, 1))
-                       printf("parseaddr-->bad address\n");
-               return NULL;
-       }
-
        {
                extern char *DelimChar;         /* parseaddr.c */
                char savec;
        {
                extern char *DelimChar;         /* parseaddr.c */
                char savec;
@@ -116,7 +105,7 @@ parseaddr(addr, a, copyf, delim, delimptr, e)
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
-       pvp = prescan(addr, delim, pvpbuf, delimptr);
+       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr);
        if (pvp == NULL)
        {
                if (tTd(20, 1))
        if (pvp == NULL)
        {
                if (tTd(20, 1))
@@ -124,42 +113,58 @@ parseaddr(addr, a, copyf, delim, delimptr, e)
                return (NULL);
        }
 
                return (NULL);
        }
 
+       if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
+       {
+               if (tTd(20, 1))
+                       printf("parseaddr-->bad address\n");
+               return NULL;
+       }
+
+       /*
+       **  Save addr if we are going to have to.
+       **
+       **      We have to do this early because there is a chance that
+       **      the map lookups in the rewriting rules could clobber
+       **      static memory somewhere.
+       */
+
+       if (bitset(RF_COPYPADDR, flags) && addr != NULL)
+       {
+               char savec = **delimptr;
+
+               if (savec != '\0')
+                       **delimptr = '\0';
+               e->e_to = addr = newstr(addr);
+               if (savec != '\0')
+                       **delimptr = savec;
+       }
+
        /*
        **  Apply rewriting rules.
        **      Ruleset 0 does basic parsing.  It must resolve.
        */
 
        queueup = FALSE;
        /*
        **  Apply rewriting rules.
        **      Ruleset 0 does basic parsing.  It must resolve.
        */
 
        queueup = FALSE;
-       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+       if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
                queueup = TRUE;
                queueup = TRUE;
-       if (rewrite(pvp, 0, e) == EX_TEMPFAIL)
+       if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
                queueup = TRUE;
 
                queueup = TRUE;
 
-       /*
-       **  See if we resolved to a real mailer.
-       */
-
-       if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
-       {
-               setstat(EX_USAGE);
-               syserr("554 cannot resolve name");
-               return (NULL);
-       }
 
        /*
        **  Build canonical address from pvp.
        */
 
 
        /*
        **  Build canonical address from pvp.
        */
 
-       a = buildaddr(pvp, a, e);
-       if (a == NULL)
-               return (NULL);
+       a = buildaddr(pvp, a, flags, e);
 
        /*
        **  Make local copies of the host & user and then
        **  transport them out.
        */
 
 
        /*
        **  Make local copies of the host & user and then
        **  transport them out.
        */
 
-       allocaddr(a, copyf, addr, *delimptr);
+       allocaddr(a, flags, addr);
+       if (bitset(QBADADDR, a->q_flags))
+               return a;
 
        /*
        **  If there was a parsing failure, mark it for queueing.
 
        /*
        **  If there was a parsing failure, mark it for queueing.
@@ -173,8 +178,9 @@ parseaddr(addr, a, copyf, delim, delimptr, e)
                        printf("parseaddr: queuing message\n");
                message(msg);
                if (e->e_message == NULL)
                        printf("parseaddr: queuing message\n");
                message(msg);
                if (e->e_message == NULL)
-                       e->e_message = msg;
+                       e->e_message = newstr(msg);
                a->q_flags |= QQUEUEUP;
                a->q_flags |= QQUEUEUP;
+               a->q_status = "4.4.3";
        }
 
        /*
        }
 
        /*
@@ -201,18 +207,43 @@ parseaddr(addr, a, copyf, delim, delimptr, e)
 */
 
 bool
 */
 
 bool
-invalidaddr(addr)
+invalidaddr(addr, delimptr)
        register char *addr;
        register char *addr;
+       char *delimptr;
 {
 {
+       char savedelim = '\0';
+
+       if (delimptr != NULL)
+       {
+               savedelim = *delimptr;
+               if (savedelim != '\0')
+                       *delimptr = '\0';
+       }
+#if 0
+       /* for testing.... */
+       if (strcmp(addr, "INvalidADDR") == 0)
+       {
+               usrerr("553 INvalid ADDRess");
+               goto addrfailure;
+       }
+#endif
        for (; *addr != '\0'; addr++)
        {
        for (; *addr != '\0'; addr++)
        {
-               if ((*addr & 0340) != 0200)
-                       continue;
-               setstat(EX_USAGE);
-               usrerr("553 Address contained invalid control characters");
-               return TRUE;
+               if ((*addr & 0340) == 0200)
+                       break;
        }
        }
-       return FALSE;
+       if (*addr == '\0')
+       {
+               if (delimptr != NULL && savedelim != '\0')
+                       *delimptr = savedelim;
+               return FALSE;
+       }
+       setstat(EX_USAGE);
+       usrerr("553 Address contained invalid control characters");
+  addrfailure:
+       if (delimptr != NULL && savedelim != '\0')
+               *delimptr = savedelim;
+       return TRUE;
 }
 \f/*
 **  ALLOCADDR -- do local allocations of address on demand.
 }
 \f/*
 **  ALLOCADDR -- do local allocations of address on demand.
@@ -221,9 +252,9 @@ invalidaddr(addr)
 **
 **     Parameters:
 **             a -- the address to reallocate.
 **
 **     Parameters:
 **             a -- the address to reallocate.
-**             copyf -- the copy flag (see parseaddr for description).
+**             flags -- the copy flag (see RF_ definitions in sendmail.h
+**                     for a description).
 **             paddr -- the printname of the address.
 **             paddr -- the printname of the address.
-**             delimptr -- a pointer to the address delimiter.  Must be set.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -232,34 +263,22 @@ invalidaddr(addr)
 **             Copies portions of a into local buffers as requested.
 */
 
 **             Copies portions of a into local buffers as requested.
 */
 
-allocaddr(a, copyf, paddr, delimptr)
+allocaddr(a, flags, paddr)
        register ADDRESS *a;
        register ADDRESS *a;
-       int copyf;
+       int flags;
        char *paddr;
        char *paddr;
-       char *delimptr;
 {
 {
-       register MAILER *m = a->q_mailer;
-
        if (tTd(24, 4))
        if (tTd(24, 4))
-               printf("allocaddr(copyf=%d, paddr=%s)\n", copyf, paddr);
+               printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
 
 
-       if (copyf > 0 && paddr != NULL)
-       {
-               char savec = *delimptr;
-
-               *delimptr = '\0';
-               a->q_paddr = newstr(paddr);
-               *delimptr = savec;
-       }
-       else
-               a->q_paddr = paddr;
+       a->q_paddr = paddr;
 
        if (a->q_user == NULL)
                a->q_user = "";
        if (a->q_host == NULL)
                a->q_host = "";
 
 
        if (a->q_user == NULL)
                a->q_user = "";
        if (a->q_host == NULL)
                a->q_host = "";
 
-       if (copyf >= 0)
+       if (bitset(RF_COPYPARSE, flags))
        {
                a->q_host = newstr(a->q_host);
                if (a->q_user != a->q_paddr)
        {
                a->q_host = newstr(a->q_host);
                if (a->q_user != a->q_paddr)
@@ -331,6 +350,7 @@ invalidaddr(addr)
 **                     If '\t' then we are reading the .cf file.
 **             pvpbuf -- place to put the saved text -- note that
 **                     the pointers are static.
 **                     If '\t' then we are reading the .cf file.
 **             pvpbuf -- place to put the saved text -- note that
 **                     the pointers are static.
+**             pvpbsize -- size of pvpbuf.
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
 **
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
 **
@@ -364,12 +384,36 @@ static short StateTab[NSTATES][NSTATES] =
        /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
 };
 
        /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
 };
 
+/* token type table -- it gets modified with $o characters */
+static 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,
+};
+
+#define toktype(c)     ((int) TokTypeTab[(c) & 0xff])
+
+
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
-prescan(addr, delim, pvpbuf, delimptr)
+prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
        char *addr;
        char *addr;
-       char delim;
+       int delim;
        char pvpbuf[];
        char **delimptr;
 {
        char pvpbuf[];
        char **delimptr;
 {
@@ -383,7 +427,24 @@ prescan(addr, delim, pvpbuf, delimptr)
        char *tok;
        int state;
        int newstate;
        char *tok;
        int state;
        int newstate;
+       char *saveto = CurEnv->e_to;
        static char *av[MAXATOM+1];
        static char *av[MAXATOM+1];
+       static char firsttime = TRUE;
+
+       if (firsttime)
+       {
+               /* initialize the token type table */
+               char obuf[50];
+
+               firsttime = FALSE;
+               expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
+               strcat(obuf, DELIMCHARS);
+               for (p = obuf; *p != '\0'; p++)
+               {
+                       if (TokTypeTab[*p & 0xff] == ATM)
+                               TokTypeTab[*p & 0xff] = OPR;
+               }
+       }
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
@@ -396,6 +457,7 @@ prescan(addr, delim, pvpbuf, delimptr)
        state = ATM;
        c = NOCHAR;
        p = addr;
        state = ATM;
        c = NOCHAR;
        p = addr;
+       CurEnv->e_to = p;
        if (tTd(22, 11))
        {
                printf("prescan: ");
        if (tTd(22, 11))
        {
                printf("prescan: ");
@@ -413,11 +475,15 @@ prescan(addr, delim, pvpbuf, delimptr)
                        if (c != NOCHAR && !bslashmode)
                        {
                                /* see if there is room */
                        if (c != NOCHAR && !bslashmode)
                        {
                                /* see if there is room */
-                               if (q >= &pvpbuf[PSBUFSIZE - 5])
+                               if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
                                {
                                        usrerr("553 Address too long");
+                                       if (strlen(addr) > MAXNAME)
+                                               addr[MAXNAME] = '\0';
+       returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
                                        if (delimptr != NULL)
                                                *delimptr = p;
+                                       CurEnv->e_to = saveto;
                                        return (NULL);
                                }
 
                                        return (NULL);
                                }
 
@@ -432,18 +498,18 @@ prescan(addr, delim, pvpbuf, delimptr)
                                /* diagnose and patch up bad syntax */
                                if (state == QST)
                                {
                                /* diagnose and patch up bad syntax */
                                if (state == QST)
                                {
-                                       usrerr("553 Unbalanced '\"'");
+                                       usrerr("653 Unbalanced '\"'");
                                        c = '"';
                                }
                                else if (cmntcnt > 0)
                                {
                                        c = '"';
                                }
                                else if (cmntcnt > 0)
                                {
-                                       usrerr("553 Unbalanced '('");
+                                       usrerr("653 Unbalanced '('");
                                        c = ')';
                                }
                                else if (anglecnt > 0)
                                {
                                        c = '>';
                                        c = ')';
                                }
                                else if (anglecnt > 0)
                                {
                                        c = '>';
-                                       usrerr("553 Unbalanced '<'");
+                                       usrerr("653 Unbalanced '<'");
                                }
                                else
                                        break;
                                }
                                else
                                        break;
@@ -493,10 +559,8 @@ prescan(addr, delim, pvpbuf, delimptr)
                        {
                                if (cmntcnt <= 0)
                                {
                        {
                                if (cmntcnt <= 0)
                                {
-                                       usrerr("553 Unbalanced ')'");
-                                       if (delimptr != NULL)
-                                               *delimptr = p;
-                                       return (NULL);
+                                       usrerr("653 Unbalanced ')'");
+                                       c = NOCHAR;
                                }
                                else
                                        cmntcnt--;
                                }
                                else
                                        cmntcnt--;
@@ -509,12 +573,11 @@ prescan(addr, delim, pvpbuf, delimptr)
                        {
                                if (anglecnt <= 0)
                                {
                        {
                                if (anglecnt <= 0)
                                {
-                                       usrerr("553 Unbalanced '>'");
-                                       if (delimptr != NULL)
-                                               *delimptr = p;
-                                       return (NULL);
+                                       usrerr("653 Unbalanced '>'");
+                                       c = NOCHAR;
                                }
                                }
-                               anglecnt--;
+                               else
+                                       anglecnt--;
                        }
                        else if (delim == ' ' && isascii(c) && isspace(c))
                                c = ' ';
                        }
                        else if (delim == ' ' && isascii(c) && isspace(c))
                                c = ' ';
@@ -563,9 +626,12 @@ prescan(addr, delim, pvpbuf, delimptr)
                        if (avp >= &av[MAXATOM])
                        {
                                syserr("553 prescan: too many tokens");
                        if (avp >= &av[MAXATOM])
                        {
                                syserr("553 prescan: too many tokens");
-                               if (delimptr != NULL)
-                                       *delimptr = p;
-                               return (NULL);
+                               goto returnnull;
+                       }
+                       if (q - tok > MAXNAME)
+                       {
+                               syserr("553 prescan: token too long");
+                               goto returnnull;
                        }
                        *avp++ = tok;
                }
                        }
                        *avp++ = tok;
                }
@@ -579,56 +645,14 @@ prescan(addr, delim, pvpbuf, delimptr)
                printf("prescan==>");
                printav(av);
        }
                printf("prescan==>");
                printav(av);
        }
+       CurEnv->e_to = saveto;
        if (av[0] == NULL)
        if (av[0] == NULL)
-               return (NULL);
-       return (av);
-}
-\f/*
-**  TOKTYPE -- return token type
-**
-**     Parameters:
-**             c -- the character in question.
-**
-**     Returns:
-**             Its type.
-**
-**     Side Effects:
-**             none.
-*/
-
-static int
-toktype(c)
-       register int c;
-{
-       static char buf[50];
-       static bool firstime = TRUE;
-
-       if (firstime)
        {
        {
-               firstime = FALSE;
-               expand("\201o", buf, &buf[sizeof buf - 1], CurEnv);
-               (void) strcat(buf, DELIMCHARS);
+               if (tTd(22, 1))
+                       printf("prescan: null leading token\n");
+               return (NULL);
        }
        }
-       c &= 0377;
-       if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
-               return (ONE);
-       if (c == MACRODEXPAND)
-               return (ONE);
-#ifdef MACVALUE
-       if (c == MACVALUE)
-               return (ONE);
-#endif /* MACVALUE */
-       if (c == '"')
-               return (QST);
-       if ((c & 0340) == 0200)
-               return (OPR);
-       if (!isascii(c))
-               return (ATM);
-       if (isspace(c) || c == ')')
-               return (SPC);
-       if (strchr(buf, c) != NULL)
-               return (OPR);
-       return (ATM);
+       return (av);
 }
 \f/*
 **  REWRITE -- apply rewrite rules to token vector.
 }
 \f/*
 **  REWRITE -- apply rewrite rules to token vector.
@@ -655,6 +679,7 @@ toktype(c)
 **     Parameters:
 **             pvp -- pointer to token vector.
 **             ruleset -- the ruleset to use for rewriting.
 **     Parameters:
 **             pvp -- pointer to token vector.
 **             ruleset -- the ruleset to use for rewriting.
+**             reclevel -- recursion level (to catch loops).
 **             e -- the current envelope.
 **
 **     Returns:
 **             e -- the current envelope.
 **
 **     Returns:
@@ -682,8 +707,12 @@ struct match
 # define MAXMATCH      9       /* max params per rewrite */
 # define MAX_CONTROL ' '
 
 # define MAXMATCH      9       /* max params per rewrite */
 # define MAX_CONTROL ' '
 
+# ifndef MAXRULERECURSION
+#  define MAXRULERECURSION     50      /* max recursion depth */
+# endif
 static char control_opts[MAX_CONTROL];
 
 static char control_opts[MAX_CONTROL];
 
+
 int
 static char control_init_data[] = { 
        MATCHZANY,      OP_VARLEN,
 int
 static char control_init_data[] = { 
        MATCHZANY,      OP_VARLEN,
@@ -699,9 +728,10 @@ static char control_init_data[] = {
 static int nrw;
 
 void
 static int nrw;
 
 void
-rewrite(pvp, ruleset, e)
+rewrite(pvp, ruleset, reclevel, e)
        char **pvp;
        int ruleset;
        char **pvp;
        int ruleset;
+       int reclevel;
        register ENVELOPE *e;
 {
        nrw = 0;
        register ENVELOPE *e;
 {
        nrw = 0;
@@ -721,10 +751,10 @@ _rewrite(pvp, ruleset)
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
        int ruleno;                     /* current rule number */
        int rstat = EX_OK;              /* return status */
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
        int ruleno;                     /* current rule number */
        int rstat = EX_OK;              /* return status */
+       int loopcount;
        int subr;                       /* subroutine number if >= 0 */
        bool dolookup;                  /* do host aliasing */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
        int subr;                       /* subroutine number if >= 0 */
        bool dolookup;                  /* do host aliasing */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
-       extern char *macvalue();
        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 */
        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 */
@@ -732,7 +762,7 @@ _rewrite(pvp, ruleset)
        struct match *old_mlp;          /* to save our place */
        bool extend_match;      /* extend existing match during backup */
 
        struct match *old_mlp;          /* to save our place */
        bool extend_match;      /* extend existing match during backup */
 
-       if (OpMode == MD_TEST || tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d   input:", ruleset);
                printcav(pvp);
        {
                printf("rewrite: ruleset %2d   input:", ruleset);
                printcav(pvp);
@@ -742,6 +772,11 @@ _rewrite(pvp, ruleset)
                syserr("554 rewrite: illegal ruleset number %d", ruleset);
                return EX_CONFIG;
        }
                syserr("554 rewrite: illegal ruleset number %d", ruleset);
                return EX_CONFIG;
        }
+       if (reclevel++ > MAXRULERECURSION)
+       {
+               syserr("rewrite: infinite recursion, ruleset %d", ruleset);
+               return EX_CONFIG;
+       }
        if (pvp == NULL)
                return EX_USAGE;
 
        if (pvp == NULL)
                return EX_USAGE;
 
@@ -764,10 +799,9 @@ _rewrite(pvp, ruleset)
        */
 
        ruleno = 1;
        */
 
        ruleno = 1;
+       loopcount = 0;
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
-               int loopcount = 0;
-
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
@@ -947,7 +981,7 @@ _rewrite(pvp, ruleset)
 
                                if (ap == NULL)
                                        break;
 
                                if (ap == NULL)
                                        break;
-                               while (*ap != NULL)
+                               while (*ap != '\0')
                                {
                                        if (*avp == NULL ||
                                            strncasecmp(ap, *avp, strlen(*avp)) != 0)
                                {
                                        if (*avp == NULL ||
                                            strncasecmp(ap, *avp, strlen(*avp)) != 0)
@@ -1155,6 +1189,7 @@ backup:
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
                        ruleno++;
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
                        ruleno++;
+                       loopcount = 0;
                        nmatches = 0;
                        continue;
                }
                        nmatches = 0;
                        continue;
                }
@@ -1181,6 +1216,7 @@ backup:
                        rvp++;
                        rwr = rwr->r_next;
                        ruleno++;
                        rvp++;
                        rwr = rwr->r_next;
                        ruleno++;
+                       loopcount = 0;
                        nmatches = 0;
                }
                else if ((*rp & 0377) == CANONHOST)
                        nmatches = 0;
                }
                else if ((*rp & 0377) == CANONHOST)
@@ -1268,7 +1304,6 @@ backup:
                        char **hbrvp, **ubrvp;
                        char **xpvp;
                        int trsize;
                        char **hbrvp, **ubrvp;
                        char **xpvp;
                        int trsize;
-                       char *olddelimchar;
                        char *replac;
                        int endtoken;
                        STAB *map;
                        char *replac;
                        int endtoken;
                        STAB *map;
@@ -1280,6 +1315,7 @@ backup:
                        char *pvpb1[MAXATOM + 1];
                        char *argvect[10];
                        char pvpbuf[PSBUFSIZE];
                        char *pvpb1[MAXATOM + 1];
                        char *argvect[10];
                        char pvpbuf[PSBUFSIZE];
+                       char *nullpvp[1];
                        bool match, defaultpart;
                        char begintype;
                        char db = '\0';
                        bool match, defaultpart;
                        char begintype;
                        char db = '\0';
@@ -1399,11 +1435,6 @@ backup:
                **  Then copy vector back into original space.
                */
 
                **  Then copy vector back into original space.
                */
 
-               callsubr(npvp);
-
-               for (avp = npvp; *avp++ != NULL;);
-                       subr = atoi(*++rvp);
-                       rvp++;
 
                else
                        subr = -1;
 
                else
                        subr = -1;
@@ -1440,7 +1471,7 @@ backup:
                }
        }
 
                }
        }
 
-       if (OpMode == MD_TEST || tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d returns:", ruleset);
                printcav(pvp);
        {
                printf("rewrite: ruleset %2d returns:", ruleset);
                printcav(pvp);
@@ -1461,6 +1492,70 @@ backup:
 **             pvp is modified.
 */
 
 **             pvp is modified.
 */
 
+static int
+callsubr(pvp, reclevel, e)
+       char **pvp;
+       int reclevel;
+       ENVELOPE *e;
+{
+       char **rvp;
+       int subr;
+       int stat;
+       STAB *s;
+
+       for (; *pvp != NULL; pvp++)
+       {
+               if ((**pvp & 0377) == CALLSUBR && pvp[1] != NULL)
+                       break;
+       }
+       if (*pvp == NULL)
+               return EX_OK;
+
+       if (tTd(21, 3))
+               printf("-----callsubr %s\n", pvp[1]);
+
+       s = stab(pvp[1], ST_RULESET, ST_FIND);
+       if (s == NULL)
+               subr = atoi(pvp[1]);
+       else
+               subr = s->s_ruleset;
+
+       /*
+       **  Take care of possible inner calls.
+       */
+
+       stat = callsubr(&pvp[2], reclevel, e);
+       if (stat != EX_OK)
+               return stat;
+
+       /*
+       **  Move vector up over calling opcode.
+       */
+
+       for (rvp = &pvp[2]; *rvp != NULL; rvp++)
+               rvp[-2] = rvp[0];
+       rvp[-2] = NULL;
+
+       /*
+       **  Call inferior ruleset.
+       */
+
+       stat = rewrite(pvp, subr, reclevel, e);
+       return stat;
+}
+\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;
 static void
 callsubr(pvp)
        char **pvp;
@@ -1503,6 +1598,8 @@ callsubr(pvp)
 **             tv -- token vector.
 **             a -- pointer to address descriptor to fill.
 **                     If NULL, one will be allocated.
 **             tv -- token vector.
 **             a -- pointer to address descriptor to fill.
 **                     If NULL, one will be allocated.
+**             flags -- info regarding whether this is a sender or
+**                     a recipient.
 **             e -- the current envelope.
 **
 **     Returns:
 **             e -- the current envelope.
 **
 **     Returns:
@@ -1533,26 +1630,48 @@ struct errcodes
 };
 
 static ADDRESS *
 };
 
 static ADDRESS *
-buildaddr(tv, a, e)
+buildaddr(tv, a, flags, e)
        register char **tv;
        register ADDRESS *a;
        register char **tv;
        register ADDRESS *a;
+       int flags;
        register ENVELOPE *e;
 {
        struct mailer **mp;
        register struct mailer *m;
        char *bp;
        int spaceleft;
        register ENVELOPE *e;
 {
        struct mailer **mp;
        register struct mailer *m;
        char *bp;
        int spaceleft;
-       static char buf[MAXNAME];
+       static MAILER errormailer;
+       static char *errorargv[] = { "ERROR", NULL };
+       static char buf[MAXNAME + 1];
+
+       if (tTd(24, 5))
+       {
+               printf("buildaddr, flags=%x, tv=", flags);
+               printav(tv);
+       }
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
        clear((char *) a, sizeof *a);
 
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
        clear((char *) a, sizeof *a);
 
+       /* set up default error return flags */
+       a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
+
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
                syserr("554 buildaddr: no net");
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
                syserr("554 buildaddr: no net");
-               return (NULL);
+badaddr:
+               a->q_flags |= QBADADDR;
+               a->q_mailer = &errormailer;
+               if (errormailer.m_name == NULL)
+               {
+                       /* initialize the bogus mailer */
+                       errormailer.m_name = "*error*";
+                       errormailer.m_mailer = "ERROR";
+                       errormailer.m_argv = errorargv;
+               }
+               return a;
        }
        tv++;
        if (strcasecmp(*tv, "error") == 0)
        }
        tv++;
        if (strcasecmp(*tv, "error") == 0)
@@ -1574,6 +1693,8 @@ buildaddr(tv, a, e)
                        }
                        tv++;
                }
                        }
                        tv++;
                }
+               else
+                       setstat(EX_UNAVAILABLE);
                buf[0] = '\0';
                for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
                {
                buf[0] = '\0';
                for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
                {
@@ -1590,8 +1711,34 @@ buildaddr(tv, a, e)
                        syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
                                CurEnv->e_id, buf);
 #endif /* LOG */
                        syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
                                CurEnv->e_id, buf);
 #endif /* LOG */
-               usrerr(buf);
-               return (NULL);
+               if (isascii(buf[0]) && isdigit(buf[0]) &&
+                   isascii(buf[1]) && isdigit(buf[1]) &&
+                   isascii(buf[2]) && isdigit(buf[2]) &&
+                   buf[3] == ' ')
+               {
+                       char fmt[10];
+
+                       strncpy(fmt, buf, 3);
+                       strcpy(&fmt[3], " %s");
+                       usrerr(fmt, buf + 4);
+
+                       /*
+                       **  If this is a 4xx code and we aren't running
+                       **  SMTP on our input, bounce this message;
+                       **  otherwise it disappears without a trace.
+                       */
+
+                       if (fmt[0] == '4' && OpMode != MD_SMTP &&
+                           OpMode != MD_DAEMON)
+                       {
+                               e->e_flags |= EF_FATALERRS;
+                       }
+               }
+               else
+               {
+                       usrerr("553 %s", buf);
+               }
+               goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
@@ -1602,7 +1749,7 @@ buildaddr(tv, a, e)
        if (m == NULL)
        {
                syserr("554 buildaddr: unknown mailer %s", *tv);
        if (m == NULL)
        {
                syserr("554 buildaddr: unknown mailer %s", *tv);
-               return (NULL);
+               goto badaddr;
        }
        a->q_mailer = m;
 
        }
        a->q_mailer = m;
 
@@ -1627,22 +1774,29 @@ buildaddr(tv, a, e)
        if (*tv == NULL || (**tv & 0377) != CANONUSER)
        {
                syserr("554 buildaddr: no user");
        if (*tv == NULL || (**tv & 0377) != CANONUSER)
        {
                syserr("554 buildaddr: no user");
-               return (NULL);
+               goto badaddr;
        }
        tv++;
 
        }
        tv++;
 
+       if (bitnset(M_CHECKUDB, m->m_flags) && *tv != NULL &&
+           strcmp(*tv, "@") == 0)
+       {
+               tv++;
+               a->q_flags |= QNOTREMOTE;
+       }
+
        /* do special mapping for local mailer */
        /* do special mapping for local mailer */
-       if (m == LocalMailer && *tv != NULL)
+       if (*tv != NULL)
        {
                register char *p = *tv;
 
                if (*p == '"')
                        p++;
        {
                register char *p = *tv;
 
                if (*p == '"')
                        p++;
-               if (*p == '|')
+               if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
                        a->q_mailer = m = ProgMailer;
                        a->q_mailer = m = ProgMailer;
-               else if (*p == '/')
+               else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
                        a->q_mailer = m = FileMailer;
                        a->q_mailer = m = FileMailer;
-               else if (*p == ':')
+               else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
                {
                        /* may be :include: */
                        cataddr(tv, NULL, buf, sizeof buf, '\0');
                {
                        /* may be :include: */
                        cataddr(tv, NULL, buf, sizeof buf, '\0');
@@ -1657,15 +1811,9 @@ buildaddr(tv, a, e)
                }
        }
 
                }
        }
 
-       if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0)
-       {
-               tv++;
-               a->q_flags |= QNOTREMOTE;
-       }
-
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
-       (void) rewrite(tv, 4, e);
+       (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
        cataddr(tv, NULL, buf, sizeof buf, '\0');
 
        /* save the result for the command line/RCPT argument */
        cataddr(tv, NULL, buf, sizeof buf, '\0');
@@ -1760,6 +1908,8 @@ sameaddr(a, b)
        register ADDRESS *a;
        register ADDRESS *b;
 {
        register ADDRESS *a;
        register ADDRESS *b;
 {
+       register ADDRESS *ca, *cb;
+
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
@@ -1768,9 +1918,16 @@ sameaddr(a, b)
        if (strcasecmp(a->q_user, b->q_user))
                return (FALSE);
 
        if (strcasecmp(a->q_user, b->q_user))
                return (FALSE);
 
-       /* if we have good uids for both but the differ, these are different */
-       if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid)
-               return (FALSE);
+       /* if we have good uids for both but they differ, these are different */
+       if (a->q_mailer == ProgMailer)
+       {
+               ca = getctladdr(a);
+               cb = getctladdr(b);
+               if (ca != NULL && cb != NULL &&
+                   bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
+                   ca->q_uid != cb->q_uid)
+                       return (FALSE);
+       }
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
        if (a->q_host == b->q_host)
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
        if (a->q_host == b->q_host)
@@ -1802,21 +1959,56 @@ 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,
+       "QREPORT",              QREPORT,
+       "QBOGUSSHELL",          QBOGUSSHELL,
+       "QUNSAFEADDR",          QUNSAFEADDR,
+       "QPINGONSUCCESS",       QPINGONSUCCESS,
+       "QPINGONFAILURE",       QPINGONFAILURE,
+       "QPINGONDELAY",         QPINGONDELAY,
+       "QHASNOTIFY",           QHASNOTIFY,
+       "QRELAYED",             QRELAYED,
+       NULL
+};
+
+void
 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;
+
+       if (a == NULL)
+       {
+               printf("[NULL]\n");
+               return;
+       }
 
        static int indent;
        register int i;
 
        while (a != NULL)
        {
 
        static int indent;
        register int i;
 
        while (a != NULL)
        {
-               first = FALSE;
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("%x=", a);
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("%x=", a);
@@ -1846,10 +2038,25 @@ printaddr(a, follow)
                indent--;
                a = a->q_sibling;
        }
                indent--;
                a = a->q_sibling;
        }
-       if (first)
-               printf("[NULL]\n");
 }
 }
+\f/*
+**  EMPTYADDR -- return TRUE if this address is empty (``<>'')
+**
+**     Parameters:
+**             a -- pointer to the address
+**
+**     Returns:
+**             TRUE -- if this address is "empty" (i.e., no one should
+**                     ever generate replies to it.
+**             FALSE -- if it is a "regular" (read: replyable) address.
+*/
 
 
+bool
+emptyaddr(a)
+       register ADDRESS *a;
+{
+       return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
+}
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
@@ -1885,8 +2092,8 @@ 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];
 
        if (tTd(12, 1))
        char pvpbuf[PSBUFSIZE];
 
        if (tTd(12, 1))
@@ -1914,10 +2121,10 @@ remotename(name, m, flags, pstat, e)
        **      domain will be appended.
        */
 
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0', pvpbuf, NULL);
+       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
        if (pvp == NULL)
                return (name);
        if (pvp == NULL)
                return (name);
-       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+       if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
                *pstat = EX_TEMPFAIL;
        if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
        {
                *pstat = EX_TEMPFAIL;
        if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
        {
@@ -1934,7 +2141,7 @@ remotename(name, m, flags, pstat, e)
 
                        while ((*pxp++ = *qxq++) != NULL)
                                continue;
 
                        while ((*pxp++ = *qxq++) != NULL)
                                continue;
-                       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+                       if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
                                *pstat = EX_TEMPFAIL;
                }
        }
                                *pstat = EX_TEMPFAIL;
                }
        }
@@ -1951,7 +2158,7 @@ remotename(name, m, flags, pstat, e)
        {
        if (rwset > 0)
        {
        {
        if (rwset > 0)
        {
-               if (rewrite(pvp, rwset, e) == EX_TEMPFAIL)
+               if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
                        *pstat = EX_TEMPFAIL;
        }
 
                        *pstat = EX_TEMPFAIL;
        }
 
@@ -1962,7 +2169,7 @@ remotename(name, m, flags, pstat, e)
        **      may be used as a default to the above rules.
        */
 
        **      may be used as a default to the above rules.
        */
 
-       if (rewrite(pvp, 4, e) == EX_TEMPFAIL)
+       if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
                *pstat = EX_TEMPFAIL;
 
        /*
                *pstat = EX_TEMPFAIL;
 
        /*
@@ -1971,7 +2178,13 @@ remotename(name, m, flags, pstat, e)
 
        cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
        define('g', lbuf, e);
 
        cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
        define('g', lbuf, e);
-       expand(fancy, buf, &buf[sizeof buf - 1], e);
+
+       /* need to make sure route-addrs have <angle brackets> */
+       if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
+               expand("<\201g>", buf, sizeof buf, e);
+       else
+               expand(fancy, buf, sizeof buf, e);
+
        define('g', oldg, e);
 
        if (tTd(12, 1))
        define('g', oldg, e);
 
        if (tTd(12, 1))
@@ -2055,14 +2268,17 @@ uurelativize(from, to, pvp)
 **             a -- the address to map (but just the user name part).
 **             sendq -- the sendq in which to install any replacement
 **                     addresses.
 **             a -- the address to map (but just the user name part).
 **             sendq -- the sendq in which to install any replacement
 **                     addresses.
+**             aliaslevel -- the alias nesting depth.
+**             e -- the envelope.
 **
 **     Returns:
 **             none.
 */
 
 **
 **     Returns:
 **             none.
 */
 
-maplocaluser(a, sendq, e)
+maplocaluser(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        ENVELOPE *e;
 {
        register char **pvp;
        ENVELOPE *e;
 {
        register char **pvp;
@@ -2075,16 +2291,16 @@ maplocaluser(a, sendq, e)
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
-       pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr);
+       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
        if (pvp == NULL)
                return;
 
        if (pvp == NULL)
                return;
 
-       (void) rewrite(pvp, 5, e);
+       (void) rewrite(pvp, 5, 0, e);
        if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
                return;
 
        /* if non-null, mailer destination specified -- has it changed? */
        if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
                return;
 
        /* if non-null, mailer destination specified -- has it changed? */
-       a1 = buildaddr(pvp, NULL, e);
+       a1 = buildaddr(pvp, NULL, 0, e);
        if (a1 == NULL || sameaddr(a, a1))
                return;
 
        if (a1 == NULL || sameaddr(a, a1))
                return;
 
@@ -2096,8 +2312,8 @@ maplocaluser(a, sendq, e)
                printaddr(a, FALSE);
        }
        a1->q_alias = a;
                printaddr(a, FALSE);
        }
        a1->q_alias = a;
-       allocaddr(a1, 1, NULL, delimptr);
-       (void) recipient(a1, sendq, e);
+       allocaddr(a1, RF_COPYALL, NULL);
+       (void) recipient(a1, sendq, aliaslevel, e);
 }
 \f/*
 **  DEQUOTE_INIT -- initialize dequote map
 }
 \f/*
 **  DEQUOTE_INIT -- initialize dequote map
@@ -2106,7 +2322,6 @@ maplocaluser(a, sendq, e)
 **
 **     Parameters:
 **             map -- the internal map structure.
 **
 **     Parameters:
 **             map -- the internal map structure.
-**             mapname -- the name of the mapl.
 **             args -- arguments.
 **
 **     Returns:
 **             args -- arguments.
 **
 **     Returns:
@@ -2114,9 +2329,8 @@ maplocaluser(a, sendq, e)
 */
 
 bool
 */
 
 bool
-dequote_init(map, mapname, args)
+dequote_init(map, args)
        MAP *map;
        MAP *map;
-       char *mapname;
        char *args;
 {
        register char *p = args;
        char *args;
 {
        register char *p = args;
@@ -2132,6 +2346,10 @@ dequote_init(map, mapname, args)
                  case 'a':
                        map->map_app = ++p;
                        break;
                  case 'a':
                        map->map_app = ++p;
                        break;
+
+                 case 's':
+                       map->map_coldelim = *++p;
+                       break;
                }
                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                        p++;
                }
                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                        p++;
@@ -2148,8 +2366,7 @@ dequote_init(map, mapname, args)
 **
 **     Parameters:
 **             map -- the internal map structure (ignored).
 **
 **     Parameters:
 **             map -- the internal map structure (ignored).
-**             buf -- the buffer to dequote.
-**             bufsiz -- the size of that buffer.
+**             name -- the name to dequote.
 **             av -- arguments (ignored).
 **             statp -- pointer to status out-parameter.
 **
 **             av -- arguments (ignored).
 **             statp -- pointer to status out-parameter.
 **
@@ -2160,31 +2377,24 @@ dequote_init(map, mapname, args)
 */
 
 char *
 */
 
 char *
-dequote_map(map, buf, bufsiz, av, statp)
+dequote_map(map, name, av, statp)
        MAP *map;
        MAP *map;
-       char buf[];
-       int bufsiz;
+       char *name;
        char **av;
        int *statp;
 {
        register char *p;
        register char *q;
        register char c;
        char **av;
        int *statp;
 {
        register char *p;
        register char *q;
        register char c;
-       int anglecnt;
-       int cmntcnt;
-       int quotecnt;
-       int spacecnt;
-       bool quotemode;
-       bool bslashmode;
-
-       anglecnt = 0;
-       cmntcnt = 0;
-       quotecnt = 0;
-       spacecnt = 0;
-       quotemode = FALSE;
-       bslashmode = FALSE;
-
-       for (p = q = buf; (c = *p++) != '\0'; )
+       int anglecnt = 0;
+       int cmntcnt = 0;
+       int quotecnt = 0;
+       int spacecnt = 0;
+       bool quotemode = FALSE;
+       bool bslashmode = FALSE;
+       char spacesub = map->map_coldelim;
+
+       for (p = q = name; (c = *p++) != '\0'; )
        {
                if (bslashmode)
                {
        {
                if (bslashmode)
                {
@@ -2193,6 +2403,9 @@ dequote_map(map, buf, bufsiz, av, statp)
                        continue;
                }
 
                        continue;
                }
 
+               if (c == ' ' && spacesub != '\0')
+                       c = spacesub;
+
                switch (c)
                {
                  case '\\':
                switch (c)
                {
                  case '\\':
@@ -2242,5 +2455,5 @@ dequote_map(map, buf, bufsiz, av, statp)
            quotemode || quotecnt <= 0 || spacecnt != 0)
                return NULL;
        *q++ = '\0';
            quotemode || quotecnt <= 0 || spacecnt != 0)
                return NULL;
        *q++ = '\0';
-       return buf;
+       return name;
 }
 }