122976Smiriam /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333730Sbostic  * Copyright (c) 1988 Regents of the University of California.
433730Sbostic  * All rights reserved.
533730Sbostic  *
642828Sbostic  * %sccs.include.redist.c%
733730Sbostic  */
822976Smiriam 
922976Smiriam #ifndef lint
10*56326Seric static char sccsid[] = "@(#)parseaddr.c	5.13.1.1 (Berkeley) 09/22/92";
1133730Sbostic #endif /* not lint */
1222976Smiriam 
13*56326Seric #include "sendmail.h"
14297Seric 
15*56326Seric #ifdef	CC_WONT_PROMOTE
16*56326Seric static int toktype __P((char));
17*56326Seric #else	/* !CC_WONT_PROMOTE */
18*56326Seric static int toktype __P((int));				/* char -> int */
19*56326Seric #endif	/* CC_WONT_PROMOTE */
20*56326Seric static void _rewrite __P((char **, int));
21*56326Seric static void callsubr __P((char **));
22*56326Seric static ADDRESS * buildaddr __P((char **, ADDRESS *));
23*56326Seric static void uurelativize __P((const char *, const char *, char **));
24*56326Seric 
25*56326Seric char	*DelimChar;		/* set to point to the delimiter */
26*56326Seric 
27297Seric /*
289888Seric **  PARSEADDR -- Parse an address
29297Seric **
30297Seric **	Parses an address and breaks it up into three parts: a
31297Seric **	net to transmit the message on, the host to transmit it
32297Seric **	to, and a user on that host.  These are loaded into an
332973Seric **	ADDRESS header with the values squirreled away if necessary.
34297Seric **	The "user" part may not be a real user; the process may
35297Seric **	just reoccur on that machine.  For example, on a machine
36297Seric **	with an arpanet connection, the address
37297Seric **		csvax.bill@berkeley
38297Seric **	will break up to a "user" of 'csvax.bill' and a host
39297Seric **	of 'berkeley' -- to be transmitted over the arpanet.
40297Seric **
41297Seric **	Parameters:
42297Seric **		addr -- the address to parse.
43297Seric **		a -- a pointer to the address descriptor buffer.
44297Seric **			If NULL, a header will be created.
45297Seric **		copyf -- determines what shall be copied:
46297Seric **			-1 -- don't copy anything.  The printname
47297Seric **				(q_paddr) is just addr, and the
48297Seric **				user & host are allocated internally
49297Seric **				to parse.
50297Seric **			0 -- copy out the parsed user & host, but
51297Seric **				don't copy the printname.
52297Seric **			+1 -- copy everything.
5311445Seric **		delim -- the character to terminate the address, passed
5411445Seric **			to prescan.
55297Seric **
56297Seric **	Returns:
57297Seric **		A pointer to the address descriptor header (`a' if
58297Seric **			`a' is non-NULL).
59297Seric **		NULL on error.
60297Seric **
61297Seric **	Side Effects:
62297Seric **		none
63297Seric */
64297Seric 
659374Seric /* following delimiters are inherent to the internal algorithms */
6616155Seric # define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
672091Seric 
682973Seric ADDRESS *
69*56326Seric parseaddr(addr, a, copyf, delim)
70297Seric 	char *addr;
712973Seric 	register ADDRESS *a;
72297Seric 	int copyf;
7311445Seric 	char delim;
74297Seric {
753149Seric 	register char **pvp;
76*56326Seric 	register struct mailer *m;
7716914Seric 	char pvpbuf[PSBUFSIZE];
78297Seric 
79297Seric 	/*
80297Seric 	**  Initialize and prescan address.
81297Seric 	*/
82297Seric 
83*56326Seric 	CurEnv->e_to = (char *)addr;
847675Seric 	if (tTd(20, 1))
859888Seric 		printf("\n--parseaddr(%s)\n", addr);
863188Seric 
87*56326Seric 	{
88*56326Seric 		extern char *DelimChar;		/* parseaddr.c */
89*56326Seric 		char savec;
90*56326Seric 		bool invalid;
91*56326Seric 
92*56326Seric 		DelimChar = finddelim(addr, delim);
93*56326Seric 		savec = *DelimChar;
94*56326Seric 		*DelimChar = '\0';
95*56326Seric 		invalid = invalidaddr(addr);
96*56326Seric 		*DelimChar = savec;
97*56326Seric 		if (invalid)
98*56326Seric 			return (NULL);
99*56326Seric 	}
100*56326Seric 
10116914Seric 	pvp = prescan(addr, delim, pvpbuf);
1023149Seric 	if (pvp == NULL)
103297Seric 		return (NULL);
104297Seric 
105297Seric 	/*
1063149Seric 	**  Apply rewriting rules.
1077889Seric 	**	Ruleset 0 does basic parsing.  It must resolve.
108297Seric 	*/
109297Seric 
1108181Seric 	rewrite(pvp, 3);
1114070Seric 	rewrite(pvp, 0);
112297Seric 
1133149Seric 	/*
1143149Seric 	**  See if we resolved to a real mailer.
1153149Seric 	*/
116297Seric 
1173149Seric 	if (pvp[0][0] != CANONNET)
1183149Seric 	{
1193149Seric 		setstat(EX_USAGE);
1203149Seric 		usrerr("cannot resolve name");
1213149Seric 		return (NULL);
122297Seric 	}
123297Seric 
124297Seric 	/*
1253149Seric 	**  Build canonical address from pvp.
126297Seric 	*/
127297Seric 
1283149Seric 	a = buildaddr(pvp, a);
1294279Seric 	if (a == NULL)
1304279Seric 		return (NULL);
131*56326Seric 	m = a->q_mailer;
132297Seric 
133297Seric 	/*
1343149Seric 	**  Make local copies of the host & user and then
1353149Seric 	**  transport them out.
136297Seric 	*/
137297Seric 
138*56326Seric 	if (copyf > 0)
1398078Seric 	{
1408078Seric 		char savec = *DelimChar;
1418078Seric 
1428078Seric 		*DelimChar = '\0';
143*56326Seric 		a->q_paddr = newstr(addr);
1448078Seric 		*DelimChar = savec;
1458078Seric 	}
146297Seric 	else
147*56326Seric 		a->q_paddr = addr;
14824944Seric 
14924944Seric 	if (a->q_user == NULL)
15024944Seric 		a->q_user = "";
15124944Seric 	if (a->q_host == NULL)
15224944Seric 		a->q_host = "";
15324944Seric 
1543149Seric 	if (copyf >= 0)
155297Seric 	{
15624944Seric 		a->q_host = newstr(a->q_host);
1573149Seric 		if (a->q_user != a->q_paddr)
1583149Seric 			a->q_user = newstr(a->q_user);
159297Seric 	}
160297Seric 
161297Seric 	/*
16216202Seric 	**  Convert host name to lower case if requested.
16316202Seric 	**	User name will be done later.
16416202Seric 	*/
16516202Seric 
16616202Seric 	if (!bitnset(M_HST_UPPER, m->m_flags))
16716202Seric 		makelower(a->q_host);
168*56326Seric 
169*56326Seric 	/*
170*56326Seric 	**  Compute return value.
171*56326Seric 	*/
172*56326Seric 
173*56326Seric 	if (tTd(20, 1))
174*56326Seric 	{
175*56326Seric 		printf("parseaddr-->");
176*56326Seric 		printaddr(a, FALSE);
177*56326Seric 	}
178*56326Seric 
179*56326Seric 	return (a);
180297Seric }
181297Seric /*
18216162Seric **  LOWERADDR -- map UPPER->lower case on addresses as requested.
18316162Seric **
18416162Seric **	Parameters:
18516162Seric **		a -- address to be mapped.
18616162Seric **
18716162Seric **	Returns:
18816162Seric **		none.
18916162Seric **
19016162Seric **	Side Effects:
19116162Seric **		none.
19216162Seric */
19316162Seric 
194*56326Seric void
19516162Seric loweraddr(a)
19616162Seric 	register ADDRESS *a;
19716162Seric {
19816162Seric 	register MAILER *m = a->q_mailer;
19916162Seric 
20016162Seric 	if (!bitnset(M_USR_UPPER, m->m_flags))
20116162Seric 		makelower(a->q_user);
20216162Seric }
20316162Seric /*
204*56326Seric **  INVALIDADDR -- check an address string for invalid control characters.
205*56326Seric **
206*56326Seric **	Parameters:
207*56326Seric **		addr -- address string to be checked.
208*56326Seric **
209*56326Seric **	Returns:
210*56326Seric **		TRUE if address string could cause problems, FALSE o/w.
211*56326Seric **
212*56326Seric **	Side Effects:
213*56326Seric **		ExitStat may be changed and an error message generated.
214*56326Seric */
215*56326Seric 
216*56326Seric bool
217*56326Seric invalidaddr(addr)
218*56326Seric 	const char *addr;
219*56326Seric {
220*56326Seric 	register const char *cp;
221*56326Seric 
222*56326Seric 	/* make sure error messages don't have garbage on them */
223*56326Seric 	errno = 0;
224*56326Seric 
225*56326Seric 	/*
226*56326Seric 	** Sendmail reserves characters 020 - 036 for rewriting rules
227*56326Seric 	** which can cause havoc (e.g. infinite rewriting loops) if
228*56326Seric 	** one shows up at the wrong time.  If any of these characters
229*56326Seric 	** appear in an address, the address is deemed "invalid" and
230*56326Seric 	** an error message is generated.
231*56326Seric 	*/
232*56326Seric 
233*56326Seric 	for (cp = addr; *cp; cp++)
234*56326Seric 		if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001')
235*56326Seric 		{
236*56326Seric 			setstat(EX_USAGE);
237*56326Seric 			usrerr("address contained invalid control char(s)");
238*56326Seric 			return (TRUE);
239*56326Seric 		}
240*56326Seric 	return (FALSE);
241*56326Seric }
242*56326Seric /*
243297Seric **  PRESCAN -- Prescan name and make it canonical
244297Seric **
2459374Seric **	Scans a name and turns it into a set of tokens.  This process
2469374Seric **	deletes blanks and comments (in parentheses).
247297Seric **
248297Seric **	This routine knows about quoted strings and angle brackets.
249297Seric **
250297Seric **	There are certain subtleties to this routine.  The one that
251297Seric **	comes to mind now is that backslashes on the ends of names
252297Seric **	are silently stripped off; this is intentional.  The problem
253297Seric **	is that some versions of sndmsg (like at LBL) set the kill
254297Seric **	character to something other than @ when reading addresses;
255297Seric **	so people type "csvax.eric\@berkeley" -- which screws up the
256297Seric **	berknet mailer.
257297Seric **
258297Seric **	Parameters:
259297Seric **		addr -- the name to chomp.
260297Seric **		delim -- the delimiter for the address, normally
261297Seric **			'\0' or ','; \0 is accepted in any case.
26215284Seric **			If '\t' then we are reading the .cf file.
26316914Seric **		pvpbuf -- place to put the saved text -- note that
26416914Seric **			the pointers are static.
265297Seric **
266297Seric **	Returns:
2673149Seric **		A pointer to a vector of tokens.
268297Seric **		NULL on error.
269297Seric **
270297Seric **	Side Effects:
27125279Seric **		sets DelimChar to point to the character matching 'delim'.
272297Seric */
273297Seric 
2748078Seric /* states and character types */
2758078Seric # define OPR		0	/* operator */
2768078Seric # define ATM		1	/* atom */
2778078Seric # define QST		2	/* in quoted string */
2788078Seric # define SPC		3	/* chewing up spaces */
2798078Seric # define ONE		4	/* pick up one character */
2803149Seric 
2818078Seric # define NSTATES	5	/* number of states */
2828078Seric # define TYPE		017	/* mask to select state type */
2838078Seric 
2848078Seric /* meta bits for table */
2858078Seric # define M		020	/* meta character; don't pass through */
2868078Seric # define B		040	/* cause a break */
2878078Seric # define MB		M|B	/* meta-break */
2888078Seric 
2898078Seric static short StateTab[NSTATES][NSTATES] =
2908078Seric {
2918087Seric    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
2929051Seric 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
2939051Seric 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
2949051Seric 	/*QST*/		QST,	QST,	OPR,	QST,	QST,
2958078Seric 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
2968078Seric 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
2978078Seric };
2988078Seric 
2998078Seric # define NOCHAR		-1	/* signal nothing in lookahead token */
3008078Seric 
3013149Seric char **
30216914Seric prescan(addr, delim, pvpbuf)
303297Seric 	char *addr;
304297Seric 	char delim;
30516914Seric 	char pvpbuf[];
306297Seric {
307297Seric 	register char *p;
3088078Seric 	register char *q;
3099346Seric 	register int c;
3103149Seric 	char **avp;
311297Seric 	bool bslashmode;
312297Seric 	int cmntcnt;
3138423Seric 	int anglecnt;
3143149Seric 	char *tok;
3158078Seric 	int state;
3168078Seric 	int newstate;
3178078Seric 	static char *av[MAXATOM+1];
318297Seric 
31915253Seric 	/* make sure error messages don't have garbage on them */
32015253Seric 	errno = 0;
32115253Seric 
32216914Seric 	q = pvpbuf;
3233149Seric 	bslashmode = FALSE;
3247800Seric 	cmntcnt = 0;
3258423Seric 	anglecnt = 0;
3263149Seric 	avp = av;
327*56326Seric 	state = OPR;
3288078Seric 	c = NOCHAR;
3298078Seric 	p = addr;
3308078Seric 	if (tTd(22, 45))
331297Seric 	{
3328078Seric 		printf("prescan: ");
3338078Seric 		xputs(p);
33423109Seric 		(void) putchar('\n');
3358078Seric 	}
3368078Seric 
3378078Seric 	do
3388078Seric 	{
3393149Seric 		/* read a token */
3403149Seric 		tok = q;
3418078Seric 		for (;;)
342297Seric 		{
3438078Seric 			/* store away any old lookahead character */
3448078Seric 			if (c != NOCHAR)
3458078Seric 			{
34615284Seric 				/* see if there is room */
34716914Seric 				if (q >= &pvpbuf[PSBUFSIZE - 5])
3488078Seric 				{
3498078Seric 					usrerr("Address too long");
3508078Seric 					DelimChar = p;
3518078Seric 					return (NULL);
3528078Seric 				}
35315284Seric 
35415284Seric 				/* squirrel it away */
3558078Seric 				*q++ = c;
3568078Seric 			}
3578078Seric 
3588078Seric 			/* read a new input character */
3598078Seric 			c = *p++;
3608078Seric 			if (c == '\0')
3618078Seric 				break;
362*56326Seric 			c &= 0177;
36315284Seric 
3648078Seric 			if (tTd(22, 101))
3658078Seric 				printf("c=%c, s=%d; ", c, state);
3668078Seric 
3673149Seric 			/* chew up special characters */
3683149Seric 			*q = '\0';
3693149Seric 			if (bslashmode)
3703149Seric 			{
37124944Seric 				/* kludge \! for naive users */
37224944Seric 				if (c != '!')
373*56326Seric 					c |= 0200;
3743149Seric 				bslashmode = FALSE;
375*56326Seric 				if (cmntcnt > 0)
376*56326Seric 					c = NOCHAR;
3773149Seric 			}
378*56326Seric 			else if (c == '\\')
3793149Seric 			{
3803149Seric 				bslashmode = TRUE;
3818078Seric 				c = NOCHAR;
3823149Seric 			}
383*56326Seric 			if (state == QST)
3848514Seric 			{
3858514Seric 				/* do nothing, just avoid next clauses */
3868514Seric 			}
3878078Seric 			else if (c == '(')
3884100Seric 			{
3898078Seric 				cmntcnt++;
3908078Seric 				c = NOCHAR;
3914100Seric 			}
3928078Seric 			else if (c == ')')
3933149Seric 			{
3948078Seric 				if (cmntcnt <= 0)
3953149Seric 				{
3968078Seric 					usrerr("Unbalanced ')'");
3978078Seric 					DelimChar = p;
3988078Seric 					return (NULL);
3993149Seric 				}
4008078Seric 				else
4018078Seric 					cmntcnt--;
4028078Seric 			}
4038078Seric 			else if (cmntcnt > 0)
4048078Seric 				c = NOCHAR;
4058423Seric 			else if (c == '<')
4068423Seric 				anglecnt++;
4078423Seric 			else if (c == '>')
4088423Seric 			{
4098423Seric 				if (anglecnt <= 0)
4108423Seric 				{
4118423Seric 					usrerr("Unbalanced '>'");
4128423Seric 					DelimChar = p;
4138423Seric 					return (NULL);
4148423Seric 				}
4158423Seric 				anglecnt--;
4168423Seric 			}
41711423Seric 			else if (delim == ' ' && isspace(c))
41811423Seric 				c = ' ';
4193149Seric 
420*56326Seric 			else if (c == ';') /* semicolons are not tokens */
421*56326Seric 				c = NOCHAR;
422*56326Seric 
4238078Seric 			if (c == NOCHAR)
4248078Seric 				continue;
4253149Seric 
4268078Seric 			/* see if this is end of input */
42711405Seric 			if (c == delim && anglecnt <= 0 && state != QST)
4283149Seric 				break;
4293149Seric 
4308078Seric 			newstate = StateTab[state][toktype(c)];
4318078Seric 			if (tTd(22, 101))
4328078Seric 				printf("ns=%02o\n", newstate);
4338078Seric 			state = newstate & TYPE;
4348078Seric 			if (bitset(M, newstate))
4358078Seric 				c = NOCHAR;
4368078Seric 			if (bitset(B, newstate))
4374228Seric 				break;
438297Seric 		}
4393149Seric 
4403149Seric 		/* new token */
4418078Seric 		if (tok != q)
4421378Seric 		{
4438078Seric 			*q++ = '\0';
4448078Seric 			if (tTd(22, 36))
445297Seric 			{
4468078Seric 				printf("tok=");
4478078Seric 				xputs(tok);
44823109Seric 				(void) putchar('\n');
449297Seric 			}
4508078Seric 			if (avp >= &av[MAXATOM])
451297Seric 			{
4528078Seric 				syserr("prescan: too many tokens");
4538078Seric 				DelimChar = p;
4548078Seric 				return (NULL);
455297Seric 			}
4568078Seric 			*avp++ = tok;
457297Seric 		}
4588423Seric 	} while (c != '\0' && (c != delim || anglecnt > 0));
4593149Seric 	*avp = NULL;
4608078Seric 	DelimChar = --p;
4613149Seric 	if (cmntcnt > 0)
4623149Seric 		usrerr("Unbalanced '('");
4638423Seric 	else if (anglecnt > 0)
4648423Seric 		usrerr("Unbalanced '<'");
4658078Seric 	else if (state == QST)
4663149Seric 		usrerr("Unbalanced '\"'");
4673149Seric 	else if (av[0] != NULL)
4683149Seric 		return (av);
4693149Seric 	return (NULL);
4703149Seric }
4713149Seric /*
4723149Seric **  TOKTYPE -- return token type
4733149Seric **
4743149Seric **	Parameters:
4753149Seric **		c -- the character in question.
4763149Seric **
4773149Seric **	Returns:
4783149Seric **		Its type.
4793149Seric **
4803149Seric **	Side Effects:
4813149Seric **		none.
4823149Seric */
483297Seric 
484*56326Seric static int
4853149Seric toktype(c)
4863149Seric 	register char c;
4873149Seric {
4883380Seric 	static char buf[50];
4893382Seric 	static bool firstime = TRUE;
4903380Seric 
4913382Seric 	if (firstime)
4923380Seric 	{
4933382Seric 		firstime = FALSE;
49416155Seric 		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
4957005Seric 		(void) strcat(buf, DELIMCHARS);
4963380Seric 	}
497*56326Seric 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS ||
498*56326Seric 	    c == MATCHMAP   || c == MATCHNMAP)
4998078Seric 		return (ONE);
500*56326Seric #ifdef MACVALUE
501*56326Seric 	if (c == MACVALUE)
502*56326Seric 		return (ONE);
503*56326Seric #endif /* MACVALUE */
5048078Seric 	if (c == '"')
5058078Seric 		return (QST);
5064100Seric 	if (!isascii(c))
5078078Seric 		return (ATM);
5088078Seric 	if (isspace(c) || c == ')')
5098078Seric 		return (SPC);
5103380Seric 	if (iscntrl(c) || index(buf, c) != NULL)
5118078Seric 		return (OPR);
5128078Seric 	return (ATM);
5133149Seric }
5143149Seric /*
5153149Seric **  REWRITE -- apply rewrite rules to token vector.
5163149Seric **
5174476Seric **	This routine is an ordered production system.  Each rewrite
5184476Seric **	rule has a LHS (called the pattern) and a RHS (called the
5194476Seric **	rewrite); 'rwr' points the the current rewrite rule.
5204476Seric **
5214476Seric **	For each rewrite rule, 'avp' points the address vector we
5224476Seric **	are trying to match against, and 'pvp' points to the pattern.
5238058Seric **	If pvp points to a special match value (MATCHZANY, MATCHANY,
5249585Seric **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
5259585Seric **	matched is saved away in the match vector (pointed to by 'mvp').
5264476Seric **
5274476Seric **	When a match between avp & pvp does not match, we try to
5289585Seric **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
5294476Seric **	we must also back out the match in mvp.  If we reach a
5308058Seric **	MATCHANY or MATCHZANY we just extend the match and start
5318058Seric **	over again.
5324476Seric **
5334476Seric **	When we finally match, we rewrite the address vector
5344476Seric **	and try over again.
5354476Seric **
5363149Seric **	Parameters:
5373149Seric **		pvp -- pointer to token vector.
5383149Seric **
5393149Seric **	Returns:
5403149Seric **		none.
5413149Seric **
5423149Seric **	Side Effects:
5433149Seric **		pvp is modified.
5443149Seric */
5452091Seric 
546*56326Seric # define OP_NONZLEN	00001
547*56326Seric # define OP_VARLEN	00002
548*56326Seric # define OP_CLASS	00004
549*56326Seric # define OP_EXACT	00010
550*56326Seric 
5513149Seric struct match
5523149Seric {
5534468Seric 	char	**first;	/* first token matched */
5544468Seric 	char	**last;		/* last token matched */
555*56326Seric 	char	**source;	/* left hand source operand */
556*56326Seric 	char	flags;		/* attributes of this operator */
5573149Seric };
5583149Seric 
5594468Seric # define MAXMATCH	9	/* max params per rewrite */
560*56326Seric # define MAX_CONTROL ' '
5613149Seric 
562*56326Seric static char control_opts[MAX_CONTROL];
5633149Seric 
564*56326Seric static char control_init_data[] = {
565*56326Seric 	MATCHZANY,	OP_VARLEN,
566*56326Seric 	MATCHONE,	OP_NONZLEN,
567*56326Seric 	MATCHANY,	OP_VARLEN|OP_NONZLEN,
568*56326Seric #ifdef MACVALUE
569*56326Seric 	MACVALUE,	OP_EXACT,
570*56326Seric #endif /* MACVALUE */
571*56326Seric 	MATCHNCLASS,	OP_NONZLEN,
572*56326Seric 	MATCHCLASS,	OP_NONZLEN|OP_VARLEN|OP_CLASS,
573*56326Seric 	MATCHNMAP,	OP_NONZLEN,
574*56326Seric 	MATCHMAP,	OP_NONZLEN|OP_VARLEN|OP_CLASS
575*56326Seric };
576*56326Seric 
577*56326Seric static int nrw;
578*56326Seric 
579*56326Seric void
5804070Seric rewrite(pvp, ruleset)
5813149Seric 	char **pvp;
5824070Seric 	int ruleset;
5833149Seric {
584*56326Seric 	nrw = 0;
585*56326Seric 	_rewrite(pvp, ruleset);
586*56326Seric }
587*56326Seric 
588*56326Seric static void
589*56326Seric _rewrite(pvp, ruleset)
590*56326Seric 	char **pvp;
591*56326Seric 	int ruleset;
592*56326Seric {
5933149Seric 	register char *ap;		/* address pointer */
5943149Seric 	register char *rp;		/* rewrite pointer */
5953149Seric 	register char **avp;		/* address vector pointer */
5963149Seric 	register char **rvp;		/* rewrite vector pointer */
5978058Seric 	register struct match *mlp;	/* cur ptr into mlist */
5988058Seric 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
5993149Seric 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
600*56326Seric 	char tokbuf[MAXNAME+1];		/* for concatenated class tokens */
601*56326Seric  	int nloops, nmatches = 0;	/* for looping rule checks */
602*56326Seric 	struct rewrite *prev_rwr;	/* pointer to previous rewrite rule */
603*56326Seric 	struct match mlist[MAXMATCH+1];	/* stores match on LHS */
604*56326Seric 	struct match *old_mlp;		/* to save our place */
605*56326Seric 	bool extend_match;	/* extend existing match during backup */
6063149Seric 
607*56326Seric 	/* test entry which should be nonzero */
608*56326Seric 	if (control_opts[MATCHMAP] == 0)
609*56326Seric 	{
610*56326Seric 		/* First time through, initialize table */
611*56326Seric 		int	c, i;
612*56326Seric 
613*56326Seric 		for (i = 0; i < sizeof control_init_data; )
614*56326Seric 		{
615*56326Seric 			c = control_init_data[i++];
616*56326Seric 			control_opts[c] = control_init_data[i++];
617*56326Seric 		}
618*56326Seric 	}
619*56326Seric 
6209279Seric 	if (OpMode == MD_TEST || tTd(21, 2))
6213149Seric 	{
6228959Seric 		printf("rewrite: ruleset %2d   input:", ruleset);
623*56326Seric 		printcav(pvp);
6243149Seric 	}
6258423Seric 	if (pvp == NULL)
6268423Seric 		return;
6273149Seric 
628*56326Seric 	if (++nrw > 100)
629*56326Seric 	{
630*56326Seric 		char buf[MAXLINE];
631*56326Seric 
632*56326Seric 		buf[0] = buf[MAXLINE-1] = 0;
633*56326Seric 		while (*pvp)
634*56326Seric 			(void) strncat(buf, *pvp++, sizeof buf);
635*56326Seric 		syserr("address causes rewrite loop: <%s>", buf);
636*56326Seric 		return;
637*56326Seric 	}
638*56326Seric 
639*56326Seric 	/* Be sure to recognize first rule as new */
640*56326Seric 	prev_rwr = NULL;
641*56326Seric 
6423149Seric 	/*
643*56326Seric 	**  Run through the list of rewrite rules, applying any that match.
6443149Seric 	*/
6453149Seric 
6464070Seric 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
6473149Seric 	{
6487675Seric 		if (tTd(21, 12))
649297Seric 		{
6508069Seric 			printf("-----trying rule:");
651*56326Seric 			printcav(rwr->r_lhs);
6523149Seric 		}
6533149Seric 
654*56326Seric 		/*
655*56326Seric 		**  Set up the match list.  This is done once for each
656*56326Seric 		**  rule.  If a rule is used repeatedly, the list need not
657*56326Seric 		**  be set up the next time.
658*56326Seric 		*/
659*56326Seric 
660*56326Seric 		if (rwr != prev_rwr)
661*56326Seric 		{
662*56326Seric 			prev_rwr = rwr;
663*56326Seric 			for (rvp = rwr->r_lhs, mlp = mlist;
664*56326Seric 				 *rvp && (mlp < &mlist[MAXMATCH]); rvp++)
665*56326Seric 			{
666*56326Seric 				mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ?
667*56326Seric 								0 : control_opts[**rvp] ;
668*56326Seric 				if (mlp->flags)
669*56326Seric 				{
670*56326Seric 					mlp->source = rvp;
671*56326Seric 					mlp++;
672*56326Seric 				}
673*56326Seric 			}
674*56326Seric 			if (*rvp)
675*56326Seric 			{
676*56326Seric 				syserr("Too many variables on LHS in ruleset %d", ruleset);
677*56326Seric 				return;
678*56326Seric 			}
679*56326Seric 			mlp->source = rvp;
680*56326Seric 
681*56326Seric 			/* Make sure end marker is initialized */
682*56326Seric 			mlp->flags = 0;
683*56326Seric 		}
684*56326Seric 
6853149Seric 		/* try to match on this rule */
6864468Seric 		mlp = mlist;
687*56326Seric 
6888058Seric 		rvp = rwr->r_lhs;
6898058Seric 		avp = pvp;
690*56326Seric 		nloops = 0;
691*56326Seric 		extend_match = FALSE;
692*56326Seric 
6938058Seric 		while ((ap = *avp) != NULL || *rvp != NULL)
6943149Seric 		{
695*56326Seric 			if (nloops++ > 400)
69652637Seric 			{
697*56326Seric 				syserr("Looping on ruleset %d, rule %d",
698*56326Seric 					ruleset, rwr-RewriteRules[ruleset]);
699*56326Seric 				mlp = mlist - 1; /* force rule failure */
70052637Seric 				break;
70152637Seric 			}
7023149Seric 			rp = *rvp;
703*56326Seric 
7048058Seric 			if (tTd(21, 35))
7058058Seric 			{
706*56326Seric 				printf("ap=");
7078058Seric 				xputs(ap);
708*56326Seric 				printf(", rp=");
7098058Seric 				xputs(rp);
7108069Seric 				printf("\n");
7118058Seric 			}
712*56326Seric 
713*56326Seric 			if (extend_match)
714*56326Seric 				extend_match = FALSE;
715*56326Seric 			else
716*56326Seric 			{
717*56326Seric 				mlp->first = avp;
718*56326Seric 				mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ?
719*56326Seric 							avp + 1 : avp;
720*56326Seric 			}
721*56326Seric 
7223149Seric 			if (rp == NULL)
7233149Seric 				/* end-of-pattern before end-of-address */
7248058Seric 				goto backup;
725*56326Seric 
726*56326Seric 			/* Premature end of address */
727*56326Seric 			if (ap == NULL && avp != mlp->last)
728*56326Seric 				goto backup;
729*56326Seric 
730*56326Seric 			/*
731*56326Seric 			**  Simplest case - exact token comparison between
732*56326Seric 			**  pattern and address.  Such a match is not saved
733*56326Seric 			**  in mlp.
734*56326Seric 			*/
735*56326Seric 
736*56326Seric 			if (rvp < mlp->source)
737*56326Seric 			{
738*56326Seric 				if (ap == NULL || strcasecmp(ap, rp))
739*56326Seric 					goto backup;
740*56326Seric 				rvp++;
741*56326Seric 				avp++;
742*56326Seric 				continue;
743297Seric 			}
744*56326Seric 
745*56326Seric #ifdef MACVALUE
746*56326Seric 			/*
747*56326Seric 			**  This is special case handled.  The match is exact,
748*56326Seric 			**  but might span zero or more address tokens.  The
749*56326Seric 			**  result is saved in mlp.
750*56326Seric 			*/
751*56326Seric 
752*56326Seric 			if (*rp == MACVALUE)
7538058Seric 			{
754*56326Seric 				int len;
755*56326Seric 				rp = macvalue(rp[1], CurEnv);
756*56326Seric 
757*56326Seric 				if (rp)
758*56326Seric 					while (*rp)
759*56326Seric 					{
760*56326Seric 						if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp)))
761*56326Seric 							goto backup;
762*56326Seric 						rp += len;
763*56326Seric 						avp++;
764*56326Seric 					}
765*56326Seric 				mlp->last = avp;
766*56326Seric 				rvp++;
767*56326Seric 				mlp++;
768*56326Seric 				continue;
7698058Seric 			}
770*56326Seric #endif /* MACVALUE */
7713149Seric 
772*56326Seric 			/*
773*56326Seric 			**  All other matches are saved in mlp.  Initially
774*56326Seric 			**  assume they match at the shortest possible length
775*56326Seric 			**  for this pattern.  Variable patterns will be
776*56326Seric 			**  extended later as needed.
777*56326Seric 			*/
778*56326Seric 
779*56326Seric 			/* Fixed length first */
780*56326Seric 			if (!(mlp->flags & OP_VARLEN))
7813149Seric 			{
782*56326Seric 				switch (*rp)
783*56326Seric 				{
784*56326Seric 					register STAB *s;
7854060Seric 
786*56326Seric 				    case MATCHNCLASS:
787*56326Seric 					/* match any single token not in a class */
788*56326Seric 					s = stab(ap, ST_CLASS, ST_FIND);
789*56326Seric 					if (s != NULL && bitnset(rp[1], s->s_class))
7909585Seric 						goto backup;
791*56326Seric 					break;
7924468Seric 
793*56326Seric 				    case MATCHNMAP:
794*56326Seric 					/* match any token not in a DBM map */
795*56326Seric 					if ((rp = mapkey(rp[1], ap, 0, NULL)) != NULL)
796*56326Seric 					{
797*56326Seric 						free(rp);
798*56326Seric 						goto backup;
799*56326Seric 					}
800*56326Seric 					break;
8014476Seric 
802*56326Seric 				}
8034060Seric 
804*56326Seric 				avp = mlp->last;
805*56326Seric 				rvp++;
8068058Seric 				mlp++;
807*56326Seric 				continue;
808*56326Seric 			}
8098058Seric 
810*56326Seric 			/*
811*56326Seric 			**  We now have a variable length item.  It could
812*56326Seric 			**  be $+ or $* in which case no special checking
813*56326Seric 			**  is needed.  But a class match such as $=x must
814*56326Seric 			**  be verified.
815*56326Seric 			**
816*56326Seric 			**  As a speedup, if a variable length item is
817*56326Seric 			**  followed by a plain character token, we initially
818*56326Seric 			**  extend the match to the first such token we find.
819*56326Seric 			**  If the required character token cannot be found,
820*56326Seric 			**  we fail the match at this point.
821*56326Seric 			*/
822*56326Seric 
823*56326Seric 			avp = mlp->last;
824*56326Seric 
825*56326Seric 			/* If next token is char token */
826*56326Seric 			if (&rvp[1] < mlp[1].source)
827*56326Seric 			{
828*56326Seric 				while (*avp && strcasecmp(*avp, rvp[1]))
829*56326Seric 					avp++;
830*56326Seric 
831*56326Seric 				/*
832*56326Seric 				**  If we can't find the proper ending token,
833*56326Seric 				**  leave avp point to NULL.  This indicates
834*56326Seric 				**  we have run out of address tokens.  It is
835*56326Seric 				**  pointless to advance the beginning of this
836*56326Seric 				**  match and retry.
837*56326Seric 				*/
838*56326Seric 
839*56326Seric 				if (*avp == NULL)
8408058Seric 					goto backup;
841*56326Seric 				mlp->last = avp;
842*56326Seric 			}
843*56326Seric 			else if (rvp[1] == NULL)
844*56326Seric 			/* next token is end of address */
845*56326Seric 			{
846*56326Seric 				while (*avp)
847*56326Seric 					avp++;
848*56326Seric 				mlp->last = avp;
849*56326Seric 			}
850*56326Seric 
851*56326Seric 			if (mlp->flags & OP_CLASS)
852*56326Seric 			{
853*56326Seric 				register char *cp = tokbuf;
854*56326Seric 
855*56326Seric 				avp = mlp->first;
856*56326Seric 				strcpy(cp, *avp);
8574468Seric 				avp++;
858*56326Seric 				for (;;)
859*56326Seric 				{
860*56326Seric 					while (avp < mlp->last)
861*56326Seric 					{
862*56326Seric 						while (*cp)
863*56326Seric 							cp++;
864*56326Seric 						strcpy(cp, *avp);
865*56326Seric 						avp++;
866*56326Seric 					}
867*56326Seric 					switch (*rp)
868*56326Seric 					{
869*56326Seric 						register STAB *s;
870*56326Seric 
871*56326Seric 					    case MATCHCLASS:
872*56326Seric 						s = stab(tokbuf, ST_CLASS, ST_FIND);
873*56326Seric 						if (s != NULL && bitnset(rp[1], s->s_class))
874*56326Seric 							goto have_match;
875*56326Seric 						break;
876*56326Seric 
877*56326Seric 					    case MATCHMAP:
878*56326Seric 						if (mapkey(rp[1], tokbuf, sizeof(tokbuf)-1, NULL))
879*56326Seric 							goto have_match;
880*56326Seric 						break;
881*56326Seric 					}
882*56326Seric 
883*56326Seric 					/*
884*56326Seric 					**  Class match initially failed.
885*56326Seric 					**  Extend the tentative match.
886*56326Seric 					**  Again, if followed by a character
887*56326Seric 					**  token, extend all the way to that
888*56326Seric 					**  token before checking.
889*56326Seric 					*/
890*56326Seric 
891*56326Seric 					if (*avp)
892*56326Seric 					{
893*56326Seric 						(mlp->last)++;
894*56326Seric 						if (&rvp[1] < mlp[1].source)
895*56326Seric 						{
896*56326Seric 							while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1]))
897*56326Seric 								(mlp->last)++;
898*56326Seric 							if (*(mlp->last) == NULL)
899*56326Seric 								avp = mlp->last;
900*56326Seric 						}
901*56326Seric 					}
902*56326Seric 					if (*avp == NULL)
903*56326Seric 					{
904*56326Seric 						/*
905*56326Seric 						**  We could not find the
906*56326Seric 						**  ending token.  But we had
907*56326Seric 						**  found ending tokens before.
908*56326Seric 						**  A match is still plausible
909*56326Seric 						**  if the start of the
910*56326Seric 						**  tentative match is advanced.
911*56326Seric 						**  Hence we must not leave avp
912*56326Seric 						**  pointing to NULL.
913*56326Seric 						*/
914*56326Seric 						avp = mlp->first;
915*56326Seric 						goto backup;
916*56326Seric 					}
917*56326Seric 				}
9183149Seric 			}
9193149Seric 
920*56326Seric  have_match:
9213149Seric 			rvp++;
922*56326Seric 			mlp++;
9233149Seric 			continue;
9243149Seric 
925*56326Seric backup:
926*56326Seric 			/* We failed to match.  mlp marks point of failure */
927*56326Seric 
928*56326Seric 			/*
929*56326Seric 			**  There is a special case when we have exhausted
930*56326Seric 			**  the address, but have not exhausted the pattern.
931*56326Seric 			**  Under normal circumstances we could consider the
932*56326Seric 			**  failure permanent, since extending the number of
933*56326Seric 			**  address tokens matched by a '$+' or a '$*' will
934*56326Seric 			**  only worsen the situation.
935*56326Seric 			**
936*56326Seric 			**  There is an exception, however.  It is possible
937*56326Seric 			**  that we have matched a class token, say '$=x',
938*56326Seric 			**  with three or more tokens.  Extending a '$+' say,
939*56326Seric 			**  which precedes the '$=x' will move the beginning
940*56326Seric 			**  of the '$=x' match to the right, but it might match
941*56326Seric 			**  a smaller number of tokens then, possibly
942*56326Seric 			**  correcting the mismatch.
943*56326Seric 			**
944*56326Seric 			**  Thus in this case we initially back up to the
945*56326Seric 			**  $=x which matches three or more tokens.
946*56326Seric 			*/
947*56326Seric 
948*56326Seric 			if (*avp == NULL)
9493149Seric 			{
950*56326Seric 				while (--mlp > mlist)
9514468Seric 				{
952*56326Seric 					if ((mlp->flags & OP_CLASS) &&
953*56326Seric 					    mlp->last > 2 + mlp->first)
954*56326Seric 						break;
9554468Seric 				}
9563149Seric 			}
9573149Seric 
958*56326Seric 			/*
959*56326Seric 			**  Now backup till we find a match with a pattern
960*56326Seric 			**  whose length is extendable, and extend that.
961*56326Seric 			*/
962*56326Seric 
963*56326Seric 			mlp--;
964*56326Seric 			while (mlp >= mlist && !(mlp->flags & OP_VARLEN))
965*56326Seric 				mlp--;
966*56326Seric 
967*56326Seric 			/* Total failure to match */
968*56326Seric 			if (mlp < mlist)
969*56326Seric 				break;
970*56326Seric 
971*56326Seric 			avp = ++(mlp->last);
972*56326Seric 			rvp = mlp->source;
973*56326Seric 
974*56326Seric 			/*
975*56326Seric 			**  We have found a backup point.  Normally we would
976*56326Seric 			**  increase the matched amount by one token, and
977*56326Seric 			**  continue from the next item in the pattern.  But
978*56326Seric 			**  there are two special cases.  If this is a
979*56326Seric 			**  class-type match (OP_CLASS), we must test the
980*56326Seric 			**  validity of the extended match.  If this pattern
981*56326Seric 			**  item is directly followed by a character token, it
982*56326Seric 			**  is worth going back and locating the next such
983*56326Seric 			**  character token before we continue on.
984*56326Seric 			*/
985*56326Seric 			if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source))
9863149Seric 			{
987*56326Seric 				avp = mlp->first;
988*56326Seric 				extend_match = TRUE;
9893149Seric 			}
990*56326Seric 			else
991*56326Seric 			{
992*56326Seric 				mlp++;
993*56326Seric 				rvp++;
994*56326Seric 			}
995297Seric 		}
9963149Seric 
9973149Seric 		/*
998*56326Seric 		**  See if we successfully matched.
9993149Seric 		*/
10003149Seric 
1001*56326Seric 		if (mlp < mlist)
10023149Seric 		{
10039374Seric 			if (tTd(21, 10))
10049374Seric 				printf("----- rule fails\n");
10059374Seric 			rwr = rwr->r_next;
1006*56326Seric 			nmatches = 0;
10079374Seric 			continue;
10089374Seric 		}
10093149Seric 
1010*56326Seric 		if (nmatches++ > 200)
1011*56326Seric 		{
1012*56326Seric 			syserr("Loop in ruleset %d, rule %d (too many matches)",
1013*56326Seric 				ruleset, rwr - RewriteRules[ruleset]);
1014*56326Seric 			rwr = rwr->r_next;
1015*56326Seric 			nmatches = 0;
1016*56326Seric 			continue;
1017*56326Seric 		}
1018*56326Seric 
10199374Seric 		rvp = rwr->r_rhs;
10209374Seric 		if (tTd(21, 12))
10219374Seric 		{
10229374Seric 			printf("-----rule matches:");
1023*56326Seric 			printcav(rvp);
10249374Seric 		}
10259374Seric 
10269374Seric 		rp = *rvp;
10279374Seric 		if (*rp == CANONUSER)
10289374Seric 		{
10299374Seric 			rvp++;
10309374Seric 			rwr = rwr->r_next;
1031*56326Seric 			nmatches = 0;
10329374Seric 		}
10339374Seric 		else if (*rp == CANONHOST)
10349374Seric 		{
10359374Seric 			rvp++;
10369374Seric 			rwr = NULL;
10379374Seric 		}
10389374Seric 		else if (*rp == CANONNET)
10399374Seric 			rwr = NULL;
10409374Seric 
10419374Seric 		/* substitute */
10429374Seric 		for (avp = npvp; *rvp != NULL; rvp++)
10439374Seric 		{
10449374Seric 			register struct match *m;
10459374Seric 			register char **pp;
10469374Seric 
10478058Seric 			rp = *rvp;
1048*56326Seric 			if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9')
10498058Seric 			{
105016914Seric 				/* substitute from LHS */
105116914Seric 				m = &mlist[rp[1] - '1'];
105216914Seric 				if (m >= mlp)
10539374Seric 				{
1054*56326Seric 					syserr("rewrite: ruleset %d: replacement #%c out of bounds",
1055*56326Seric 						ruleset, rp[1]);
10569374Seric 					return;
10579374Seric 				}
105816914Seric 				if (tTd(21, 15))
105916914Seric 				{
106016914Seric 					printf("$%c:", rp[1]);
106116914Seric 					pp = m->first;
1062*56326Seric 					while (pp < m->last)
106316914Seric 					{
106416914Seric 						printf(" %x=\"", *pp);
106516914Seric 						(void) fflush(stdout);
106616914Seric 						printf("%s\"", *pp++);
106716914Seric 					}
106816914Seric 					printf("\n");
106916914Seric 				}
10709374Seric 				pp = m->first;
1071*56326Seric 				while (pp < m->last)
10723149Seric 				{
107316914Seric 					if (avp >= &npvp[MAXATOM])
1074*56326Seric 						goto toolong;
107516914Seric 					*avp++ = *pp++;
10763149Seric 				}
10773149Seric 			}
107816914Seric 			else
10798226Seric 			{
108016914Seric 				/* vanilla replacement */
10819374Seric 				if (avp >= &npvp[MAXATOM])
108216889Seric 				{
1083*56326Seric toolong:
108416889Seric 					syserr("rewrite: expansion too long");
108516889Seric 					return;
108616889Seric 				}
1087*56326Seric #ifdef MACVALUE
1088*56326Seric 				if (*rp == MACVALUE)
1089*56326Seric 				{
1090*56326Seric 					char *p = macvalue(rp[1], CurEnv);
1091*56326Seric 
1092*56326Seric 					if (tTd(21, 2))
1093*56326Seric 						printf("expanding runtime macro '%c' to \"%s\"\n",
1094*56326Seric 						    rp[1], p ? p : "(null)");
1095*56326Seric 					if (p)
1096*56326Seric 						*avp++ = p;
1097*56326Seric 				}
1098*56326Seric 				else
1099*56326Seric #endif /* MACVALUE */
1100*56326Seric 					*avp++ = rp;
11018226Seric 			}
11029374Seric 		}
11039374Seric 		*avp++ = NULL;
110416914Seric 
110516914Seric 		/*
1106*56326Seric 		**  Check for any hostname lookups.
110716914Seric 		*/
110816914Seric 
110916914Seric 		for (rvp = npvp; *rvp != NULL; rvp++)
111016914Seric 		{
1111*56326Seric 			char **hbrvp, **ubrvp;
111216914Seric 			char **xpvp;
111316914Seric 			int trsize;
111417473Seric 			char *olddelimchar;
1115*56326Seric 			char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1];
111616914Seric 			char *pvpb1[MAXATOM + 1];
111717174Seric 			char pvpbuf[PSBUFSIZE];
1118*56326Seric 			bool match, defaultpart;
1119*56326Seric 			char begintype;
1120*56326Seric 			char db = '\0';
112116914Seric 
1122*56326Seric 			if (**rvp != HOSTBEGIN && **rvp != KEYBEGIN)
112316914Seric 				continue;
112416914Seric 
112516914Seric 			/*
1126*56326Seric 			**  Got a hostname or database lookup.
112716914Seric 			**
112816914Seric 			**	This could be optimized fairly easily.
112916914Seric 			*/
113016914Seric 
1131*56326Seric 			begintype = **rvp;
113216914Seric 			hbrvp = rvp;
1133*56326Seric 			ubrvp = NULL;
1134*56326Seric 
1135*56326Seric 			/* read database name if that's what we're up for */
1136*56326Seric 			if (begintype == KEYBEGIN)
113753654Seric 			{
1138*56326Seric 				if (*++rvp != NULL)
1139*56326Seric 					db = **rvp;
114053654Seric 			}
114116914Seric 
114216914Seric 			/* extract the match part */
1143*56326Seric 			if (begintype == HOSTBEGIN)
1144*56326Seric 				while (*++rvp != NULL && **rvp != HOSTEND &&
1145*56326Seric 				   **rvp != CANONUSER)
1146*56326Seric 				continue;
1147*56326Seric 			else
1148*56326Seric 				while (*++rvp != NULL && **rvp != KEYEND &&
1149*56326Seric 				   **rvp != CANONHOST && **rvp != CANONUSER)
1150*56326Seric 					continue;
1151*56326Seric 			/* got a sprintf argument? */
1152*56326Seric 			if (**rvp == CANONHOST)
115353654Seric 			{
1154*56326Seric 				*rvp = NULL;
1155*56326Seric 				ubrvp = rvp+1;
1156*56326Seric 				while (*++rvp != NULL && **rvp != KEYEND &&
1157*56326Seric 				    **rvp != CANONUSER)
1158*56326Seric 					continue;
115953654Seric 			}
1160*56326Seric 			defaultpart = **rvp == CANONUSER;
116116914Seric 			if (*rvp != NULL)
116216914Seric 				*rvp++ = NULL;
116316914Seric 
116416914Seric 			/* save the remainder of the input string */
116516914Seric 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
116616914Seric 			bcopy((char *) rvp, (char *) pvpb1, trsize);
116716914Seric 
1168*56326Seric 			/* Look it up (lowercase version) */
1169*56326Seric 			cataddr(hbrvp + (begintype == HOSTBEGIN ? 1 : 2),
1170*56326Seric 			    hbuf, sizeof hbuf);
1171*56326Seric 			if (begintype == HOSTBEGIN)
1172*56326Seric #ifdef VMUNIX
1173*56326Seric 				match = maphostname(hbuf, sizeof hbuf);
1174*56326Seric #else /* !VMUNIX */
1175*56326Seric 				match = FALSE;
1176*56326Seric #endif /* VMUNIX */
117753654Seric 			else
117851317Seric 			{
1179*56326Seric 				if (ubrvp == NULL)
1180*56326Seric 				{
1181*56326Seric 					/* no sprintf argument part */
1182*56326Seric 					match = (mapkey(db, hbuf, sizeof hbuf, NULL) != NULL);
1183*56326Seric 				}
118453654Seric 				else
1185*56326Seric 				{
1186*56326Seric 					cataddr(ubrvp, ubuf, sizeof ubuf);
1187*56326Seric 					match = (mapkey(db, hbuf, sizeof hbuf, ubuf) != NULL);
1188*56326Seric 				}
118953654Seric 			}
1190*56326Seric 			if (match || !defaultpart)
119153654Seric 			{
1192*56326Seric 				/* scan the new route/host name */
1193*56326Seric 			olddelimchar = DelimChar;
1194*56326Seric 				xpvp = prescan(hbuf, '\0', pvpbuf);
1195*56326Seric 			DelimChar = olddelimchar;
119653654Seric 				if (xpvp == NULL)
119751317Seric 				{
1198*56326Seric 					syserr("rewrite: cannot prescan %s: %s",
1199*56326Seric 					    begintype == HOSTBEGIN ?
1200*56326Seric 					    "new hostname" :
1201*56326Seric 					    "dbm lookup result",
1202*56326Seric 					    hbuf);
1203*56326Seric 				return;
120451317Seric 			}
120551317Seric 
120616914Seric 			/* append it to the token list */
1207*56326Seric 				for (avp = hbrvp; *xpvp != NULL; xpvp++)
1208*56326Seric 				{
120917174Seric 				*avp++ = newstr(*xpvp);
121016920Seric 				if (avp >= &npvp[MAXATOM])
121116914Seric 					goto toolong;
1212*56326Seric 				}
121317174Seric 			}
1214*56326Seric 			else
1215*56326Seric 				avp = hbrvp;
121616914Seric 
121716914Seric 			/* restore the old trailing information */
1218*56326Seric 			rvp = avp - 1;
1219*56326Seric 			for (xpvp = pvpb1; *xpvp != NULL; xpvp++)
1220*56326Seric 			{
1221*56326Seric 				if (defaultpart && (begintype == HOSTBEGIN ?
1222*56326Seric 				    **xpvp == HOSTEND :
1223*56326Seric 				    **xpvp == KEYEND))
1224*56326Seric 				{
1225*56326Seric 					defaultpart = FALSE;
1226*56326Seric 					rvp = avp - 1;
1227*56326Seric 				}
1228*56326Seric 				else if (!defaultpart || !match)
1229*56326Seric 					*avp++ = *xpvp;
123016920Seric 				if (avp >= &npvp[MAXATOM])
123116914Seric 					goto toolong;
1232*56326Seric 			}
1233*56326Seric 			*avp++ = NULL;
123417174Seric 
1235*56326Seric 			/*break;*/
123616914Seric 		}
123716914Seric 
123816914Seric 		/*
123916914Seric 		**  Check for subroutine calls.
1240*56326Seric 		**  Then copy vector back into original space.
124116914Seric 		*/
124216914Seric 
1243*56326Seric 		callsubr(npvp);
1244*56326Seric 
1245*56326Seric 		for (avp = npvp; *avp++ != NULL;);
124617348Seric 			bcopy((char *) npvp, (char *) pvp,
124716900Seric 				(int) (avp - npvp) * sizeof *avp);
1248*56326Seric 
12499374Seric 		if (tTd(21, 4))
12509374Seric 		{
12519374Seric 			printf("rewritten as:");
1252*56326Seric 			printcav(pvp);
12539374Seric 		}
1254297Seric 	}
12558069Seric 
12569279Seric 	if (OpMode == MD_TEST || tTd(21, 2))
12578069Seric 	{
12588959Seric 		printf("rewrite: ruleset %2d returns:", ruleset);
1259*56326Seric 		printcav(pvp);
12608069Seric 	}
12613149Seric }
12623149Seric /*
1263*56326Seric **  CALLSUBR -- call subroutines in rewrite vector
1264*56326Seric **
1265*56326Seric **	Parameters:
1266*56326Seric **		pvp -- pointer to token vector.
1267*56326Seric **
1268*56326Seric **	Returns:
1269*56326Seric **		none.
1270*56326Seric **
1271*56326Seric **	Side Effects:
1272*56326Seric **		pvp is modified.
1273*56326Seric */
1274*56326Seric 
1275*56326Seric static void
1276*56326Seric callsubr(pvp)
1277*56326Seric 	char **pvp;
1278*56326Seric {
1279*56326Seric 	char **rvp;
1280*56326Seric 	int subr;
1281*56326Seric 
1282*56326Seric 	for (; *pvp != NULL; pvp++)
1283*56326Seric 		if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0]))
1284*56326Seric 		{
1285*56326Seric 			subr = atoi(pvp[1]);
1286*56326Seric 
1287*56326Seric 			if (tTd(21, 3))
1288*56326Seric 				printf("-----callsubr %d\n", subr);
1289*56326Seric 
1290*56326Seric 			/*
1291*56326Seric 			**  Take care of possible inner calls.
1292*56326Seric 			*/
1293*56326Seric 			callsubr(pvp+2);
1294*56326Seric 
1295*56326Seric 			/*
1296*56326Seric 			**  Move vector up over calling opcode.
1297*56326Seric 			*/
1298*56326Seric 			for (rvp = pvp+2; *rvp != NULL; rvp++)
1299*56326Seric 				rvp[-2] = rvp[0];
1300*56326Seric 			rvp[-2] = NULL;
1301*56326Seric 
1302*56326Seric 			/*
1303*56326Seric 			**  Call inferior ruleset.
1304*56326Seric 			*/
1305*56326Seric 			_rewrite(pvp, subr);
1306*56326Seric 
1307*56326Seric 			break;
1308*56326Seric 		}
1309*56326Seric }
1310*56326Seric /*
13113149Seric **  BUILDADDR -- build address from token vector.
13123149Seric **
13133149Seric **	Parameters:
13143149Seric **		tv -- token vector.
13153149Seric **		a -- pointer to address descriptor to fill.
13163149Seric **			If NULL, one will be allocated.
13173149Seric **
13183149Seric **	Returns:
13194279Seric **		NULL if there was an error.
13204279Seric **		'a' otherwise.
13213149Seric **
13223149Seric **	Side Effects:
13233149Seric **		fills in 'a'
13243149Seric */
13253149Seric 
1326*56326Seric static ADDRESS *
13273149Seric buildaddr(tv, a)
13283149Seric 	register char **tv;
13293149Seric 	register ADDRESS *a;
13303149Seric {
13313149Seric 	static char buf[MAXNAME];
13323149Seric 	struct mailer **mp;
13333149Seric 	register struct mailer *m;
13343149Seric 
13353149Seric 	if (a == NULL)
13363149Seric 		a = (ADDRESS *) xalloc(sizeof *a);
133716889Seric 	bzero((char *) a, sizeof *a);
13383149Seric 
13393149Seric 	/* figure out what net/mailer to use */
1340*56326Seric 	if (*tv == NULL || **tv != CANONNET)
13414279Seric 	{
13423149Seric 		syserr("buildaddr: no net");
13434279Seric 		return (NULL);
13444279Seric 	}
13453149Seric 	tv++;
134633725Sbostic 	if (!strcasecmp(*tv, "error"))
13474279Seric 	{
134810183Seric 		if (**++tv == CANONHOST)
134910183Seric 		{
135010183Seric 			setstat(atoi(*++tv));
135110183Seric 			tv++;
135210183Seric 		}
1353*56326Seric 		buf[0] = '\0';
1354*56326Seric 		for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
1355*56326Seric 		{
1356*56326Seric 			if (buf[0] != '\0')
1357*56326Seric 				(void) strcat(buf, " ");
1358*56326Seric 			(void) strcat(buf, *tv);
1359*56326Seric 		}
136010183Seric 		if (**tv != CANONUSER)
13614279Seric 			syserr("buildaddr: error: no user");
13624279Seric 		while (*++tv != NULL)
13634279Seric 		{
13644279Seric 			if (buf[0] != '\0')
13657005Seric 				(void) strcat(buf, " ");
13667005Seric 			(void) strcat(buf, *tv);
13674279Seric 		}
1368*56326Seric #ifdef LOG
1369*56326Seric 		if (LogLevel > 8)
1370*56326Seric 			syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
1371*56326Seric 				CurEnv->e_id, buf);
1372*56326Seric #endif /* LOG */
13734279Seric 		usrerr(buf);
13744279Seric 		return (NULL);
13754279Seric 	}
13764598Seric 	for (mp = Mailer; (m = *mp++) != NULL; )
13773149Seric 	{
137833725Sbostic 		if (!strcasecmp(m->m_name, *tv))
13793149Seric 			break;
13803149Seric 	}
13813149Seric 	if (m == NULL)
13824279Seric 	{
138324944Seric 		syserr("buildaddr: unknown mailer %s", *tv);
13844279Seric 		return (NULL);
13854279Seric 	}
13864598Seric 	a->q_mailer = m;
13873149Seric 
13883149Seric 	/* figure out what host (if any) */
1389*56326Seric 	if (**++tv != CANONHOST)
13903149Seric 	{
1391*56326Seric 		if (!bitnset(M_LOCAL, m->m_flags))
13924279Seric 		{
13933149Seric 			syserr("buildaddr: no host");
13944279Seric 			return (NULL);
13954279Seric 		}
1396*56326Seric 		else
1397*56326Seric 			a->q_host = NULL;
1398*56326Seric 	}
1399*56326Seric 	else
1400*56326Seric 	{
14015704Seric 		buf[0] = '\0';
1402*56326Seric 		while (*++tv != NULL && **tv != CANONUSER)
1403*56326Seric 			(void) strcat(buf, *tv);
14045704Seric 		a->q_host = newstr(buf);
14053149Seric 	}
14063149Seric 
14073149Seric 	/* figure out the user */
140836615Sbostic 	if (*tv == NULL || **tv != CANONUSER)
14094279Seric 	{
14103149Seric 		syserr("buildaddr: no user");
14114279Seric 		return (NULL);
14124279Seric 	}
141319040Seric 
1414*56326Seric 	/* define tohost before running mailer rulesets */
1415*56326Seric 	define('h', a->q_host, CurEnv);
141651317Seric 
141719040Seric 	/* rewrite according recipient mailer rewriting rules */
141819040Seric 	rewrite(++tv, 2);
1419*56326Seric 	if (m->m_re_rwset > 0)
1420*56326Seric 		rewrite(tv, m->m_re_rwset);
142119040Seric 	rewrite(tv, 4);
142219040Seric 
142319040Seric 	/* save the result for the command line/RCPT argument */
142411278Seric 	cataddr(tv, buf, sizeof buf);
14253149Seric 	a->q_user = buf;
14263149Seric 
14273149Seric 	return (a);
14283149Seric }
14293188Seric /*
14304228Seric **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
14314228Seric **
14324228Seric **	Parameters:
14334228Seric **		pvp -- parameter vector to rebuild.
14344228Seric **		buf -- buffer to build the string into.
14354228Seric **		sz -- size of buf.
14364228Seric **
14374228Seric **	Returns:
14384228Seric **		none.
14394228Seric **
14404228Seric **	Side Effects:
14414228Seric **		Destroys buf.
14424228Seric */
14434228Seric 
1444*56326Seric void
14454228Seric cataddr(pvp, buf, sz)
14464228Seric 	char **pvp;
14474228Seric 	char *buf;
14484228Seric 	register int sz;
14494228Seric {
14504228Seric 	bool oatomtok = FALSE;
1451*56326Seric 	bool natomtok;
14524228Seric 	register int i;
14534228Seric 	register char *p;
14544228Seric 
14558423Seric 	if (pvp == NULL)
14568423Seric 	{
145723109Seric 		(void) strcpy(buf, "");
14588423Seric 		return;
14598423Seric 	}
14604228Seric 	p = buf;
146111156Seric 	sz -= 2;
14624228Seric 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
14634228Seric 	{
14648078Seric 		natomtok = (toktype(**pvp) == ATM);
14654228Seric 		if (oatomtok && natomtok)
14669042Seric 			*p++ = SpaceSub;
14674228Seric 		(void) strcpy(p, *pvp);
14684228Seric 		oatomtok = natomtok;
14694228Seric 		p += i;
147011156Seric 		sz -= i + 1;
14714228Seric 		pvp++;
14724228Seric 	}
14734228Seric 	*p = '\0';
14744228Seric }
14754228Seric /*
14763188Seric **  SAMEADDR -- Determine if two addresses are the same
14773188Seric **
14783188Seric **	This is not just a straight comparison -- if the mailer doesn't
14793188Seric **	care about the host we just ignore it, etc.
14803188Seric **
14813188Seric **	Parameters:
14823188Seric **		a, b -- pointers to the internal forms to compare.
14833188Seric **
14843188Seric **	Returns:
14853188Seric **		TRUE -- they represent the same mailbox.
14863188Seric **		FALSE -- they don't.
14873188Seric **
14883188Seric **	Side Effects:
14893188Seric **		none.
14903188Seric */
14913188Seric 
14923188Seric bool
14939374Seric sameaddr(a, b)
14943188Seric 	register ADDRESS *a;
14953188Seric 	register ADDRESS *b;
14963188Seric {
14973188Seric 	/* if they don't have the same mailer, forget it */
14983188Seric 	if (a->q_mailer != b->q_mailer)
14993188Seric 		return (FALSE);
15003188Seric 
15013188Seric 	/* if the user isn't the same, we can drop out */
1502*56326Seric 	if (strcasecmp(a->q_user, b->q_user))
15033188Seric 		return (FALSE);
15043188Seric 
15053188Seric 	/* if the mailer ignores hosts, we have succeeded! */
150610690Seric 	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
15073188Seric 		return (TRUE);
15083188Seric 
15093188Seric 	/* otherwise compare hosts (but be careful for NULL ptrs) */
15103188Seric 	if (a->q_host == NULL || b->q_host == NULL)
15113188Seric 		return (FALSE);
1512*56326Seric 	if (strcasecmp(a->q_host, b->q_host))
15133188Seric 		return (FALSE);
15143188Seric 
15153188Seric 	return (TRUE);
15163188Seric }
15173234Seric /*
15183234Seric **  PRINTADDR -- print address (for debugging)
15193234Seric **
15203234Seric **	Parameters:
15213234Seric **		a -- the address to print
15223234Seric **		follow -- follow the q_next chain.
15233234Seric **
15243234Seric **	Returns:
15253234Seric **		none.
15263234Seric **
15273234Seric **	Side Effects:
15283234Seric **		none.
15293234Seric */
15303234Seric 
1531*56326Seric void
15323234Seric printaddr(a, follow)
15333234Seric 	register ADDRESS *a;
15343234Seric 	bool follow;
15353234Seric {
15365001Seric 	bool first = TRUE;
15375001Seric 
15383234Seric 	while (a != NULL)
15393234Seric 	{
15405001Seric 		first = FALSE;
15414443Seric 		printf("%x=", a);
15424085Seric 		(void) fflush(stdout);
154340973Sbostic 		printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n",
154440973Sbostic 		       a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name,
154540973Sbostic 		       a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>");
15468181Seric 		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
15478181Seric 		       a->q_alias);
15488181Seric 		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
15498181Seric 		       a->q_fullname);
15504996Seric 
15513234Seric 		if (!follow)
15523234Seric 			return;
15534996Seric 		a = a->q_next;
15543234Seric 	}
15555001Seric 	if (first)
15564443Seric 		printf("[NULL]\n");
15573234Seric }
15584317Seric 
15597682Seric /*
15607682Seric **  REMOTENAME -- return the name relative to the current mailer
15617682Seric **
15627682Seric **	Parameters:
15637682Seric **		name -- the name to translate.
15648069Seric **		m -- the mailer that we want to do rewriting relative
15658069Seric **			to.
15668069Seric **		senderaddress -- if set, uses the sender rewriting rules
15678069Seric **			rather than the recipient rewriting rules.
156810310Seric **		canonical -- if set, strip out any comment information,
156910310Seric **			etc.
1570*56326Seric **		headeraddress -- if set, use header specific rewriting
1571*56326Seric **			rulesets and uurelativize if M_RELATIVIZE is set.
15727682Seric **
15737682Seric **	Returns:
15747682Seric **		the text string representing this address relative to
15757682Seric **			the receiving mailer.
15767682Seric **
15777682Seric **	Side Effects:
15787682Seric **		none.
15797682Seric **
15807682Seric **	Warnings:
15817682Seric **		The text string returned is tucked away locally;
15827682Seric **			copy it if you intend to save it.
15837682Seric */
15847682Seric 
15857682Seric char *
1586*56326Seric remotename(name, m, senderaddress, canonical, headeraddress)
15877682Seric 	char *name;
1588*56326Seric 	MAILER *m;
15898069Seric 	bool senderaddress;
159010310Seric 	bool canonical;
1591*56326Seric 	bool headeraddress;
15927682Seric {
15938069Seric 	register char **pvp;
15948069Seric 	char *fancy;
1595*56326Seric 	char *oldg = macvalue('g', CurEnv);
15967682Seric 	static char buf[MAXNAME];
15977682Seric 	char lbuf[MAXNAME];
159816914Seric 	char pvpbuf[PSBUFSIZE];
15997682Seric 
16007755Seric 	if (tTd(12, 1))
16017755Seric 		printf("remotename(%s)\n", name);
16027755Seric 
160310177Seric 	/* don't do anything if we are tagging it as special */
1604*56326Seric 	if ((senderaddress ?
1605*56326Seric 	     (headeraddress ? m->m_sh_rwset : m->m_se_rwset) :
1606*56326Seric 	     (headeraddress ? m->m_rh_rwset : m->m_re_rwset)) < 0)
160710177Seric 		return (name);
160810177Seric 
16097682Seric 	/*
16108181Seric 	**  Do a heuristic crack of this name to extract any comment info.
16118181Seric 	**	This will leave the name as a comment and a $g macro.
16127889Seric 	*/
16137889Seric 
161410310Seric 	if (canonical)
161516155Seric 		fancy = "\001g";
161610310Seric 	else
161710310Seric 		fancy = crackaddr(name);
16187889Seric 
16198181Seric 	/*
16208181Seric 	**  Turn the name into canonical form.
16218181Seric 	**	Normally this will be RFC 822 style, i.e., "user@domain".
16228181Seric 	**	If this only resolves to "user", and the "C" flag is
16238181Seric 	**	specified in the sending mailer, then the sender's
16248181Seric 	**	domain will be appended.
16258181Seric 	*/
16268181Seric 
162716914Seric 	pvp = prescan(name, '\0', pvpbuf);
16287889Seric 	if (pvp == NULL)
16297889Seric 		return (name);
16308181Seric 	rewrite(pvp, 3);
1631*56326Seric 	if (CurEnv->e_fromdomain != NULL)
16328181Seric 	{
16338181Seric 		/* append from domain to this address */
16348181Seric 		register char **pxp = pvp;
16358181Seric 
16369594Seric 		/* see if there is an "@domain" in the current name */
16378181Seric 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
16388181Seric 			pxp++;
16398181Seric 		if (*pxp == NULL)
16408181Seric 		{
16419594Seric 			/* no.... append the "@domain" from the sender */
1642*56326Seric 			register char **qxq = CurEnv->e_fromdomain;
16438181Seric 
16449594Seric 			while ((*pxp++ = *qxq++) != NULL)
16459594Seric 				continue;
164611726Seric 			rewrite(pvp, 3);
16478181Seric 		}
16488181Seric 	}
16498181Seric 
16508181Seric 	/*
16518959Seric 	**  Do more specific rewriting.
1652*56326Seric 	**	Rewrite using ruleset 1 or 2 for envelope addresses and
1653*56326Seric 	**	5 or 6 for header addresses depending on whether this
1654*56326Seric 	**	is a sender address or not.
16558181Seric 	**	Then run it through any receiving-mailer-specific rulesets.
16568181Seric 	*/
16578181Seric 
16588069Seric 	if (senderaddress)
16597755Seric 	{
1660*56326Seric 		if (headeraddress)
1661*56326Seric 		{
1662*56326Seric 			rewrite(pvp, SplitRewriting ? 5 : 1);
1663*56326Seric 			if (m->m_sh_rwset > 0)
1664*56326Seric 				rewrite(pvp, m->m_sh_rwset);
1665*56326Seric 		}
1666*56326Seric 		else
1667*56326Seric 		{
1668*56326Seric 			rewrite(pvp, 1);
1669*56326Seric 			if (m->m_se_rwset > 0)
1670*56326Seric 				rewrite(pvp, m->m_se_rwset);
1671*56326Seric 		}
16728069Seric 	}
16738069Seric 	else
16748069Seric 	{
1675*56326Seric 		if (headeraddress)
1676*56326Seric 		{
1677*56326Seric 			rewrite(pvp, SplitRewriting ? 6 : 2);
1678*56326Seric 			if (m->m_rh_rwset > 0)
1679*56326Seric 				rewrite(pvp, m->m_rh_rwset);
1680*56326Seric 		}
1681*56326Seric 		else
1682*56326Seric 		{
1683*56326Seric 			rewrite(pvp, 2);
1684*56326Seric 			if (m->m_re_rwset > 0)
1685*56326Seric 				rewrite(pvp, m->m_re_rwset);
1686*56326Seric 		}
16877682Seric 	}
16887682Seric 
16898181Seric 	/*
16908959Seric 	**  Do any final sanitation the address may require.
16918959Seric 	**	This will normally be used to turn internal forms
16928959Seric 	**	(e.g., user@host.LOCAL) into external form.  This
16938959Seric 	**	may be used as a default to the above rules.
16948959Seric 	*/
16958959Seric 
16968959Seric 	rewrite(pvp, 4);
16978959Seric 
16988959Seric 	/*
1699*56326Seric 	**  Check if we're supposed to do make the address
1700*56326Seric 	**  UUCP !-relative to the rcpt host vs ourselves.
1701*56326Seric 	*/
1702*56326Seric 
1703*56326Seric 	if (headeraddress && bitnset(M_RELATIVIZE, m->m_flags))
1704*56326Seric 		uurelativize("\001k", "\001h", pvp);
1705*56326Seric 
1706*56326Seric 	/*
17078181Seric 	**  Now restore the comment information we had at the beginning.
17088181Seric 	*/
17098181Seric 
17107682Seric 	cataddr(pvp, lbuf, sizeof lbuf);
1711*56326Seric 	define('g', lbuf, CurEnv);
1712*56326Seric 	expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
1713*56326Seric 	define('g', oldg, CurEnv);
17147682Seric 
17157682Seric 	if (tTd(12, 1))
17167755Seric 		printf("remotename => `%s'\n", buf);
17177682Seric 	return (buf);
17187682Seric }
171951317Seric /*
1720*56326Seric **  UURELATIVIZE -- Make an address !-relative to recipient/sender nodes
172151317Seric **
172251317Seric **	Parameters:
1723*56326Seric **		from -- the sending node (usually "$k" or "$w")
1724*56326Seric **		to -- the receiving node (usually "$h")
1725*56326Seric **		pvp -- address vector
172651317Seric **
172751317Seric **	Returns:
172851317Seric **		none.
1729*56326Seric **
1730*56326Seric **	Side Effects:
1731*56326Seric **		The pvp is rewritten to be relative the "to" node
1732*56326Seric **		wrt the "from" node.  In other words, if the pvp
1733*56326Seric **		is headed by "to!" that part is stripped; otherwise
1734*56326Seric **		"from!" is prepended.  Exception: "to!user" addresses
1735*56326Seric **		with no '!'s in the user part are sent as is.
1736*56326Seric **
1737*56326Seric **	Bugs:
1738*56326Seric **		The pvp may overflow, but we don't catch it.
173951317Seric */
174051317Seric 
1741*56326Seric static void
1742*56326Seric uurelativize(from, to, pvp)
1743*56326Seric 	const char *from, *to;
1744*56326Seric 	char **pvp;
174551317Seric {
1746*56326Seric 	register char **pxp = pvp;
1747*56326Seric 	char expfrom[MAXNAME], expto[MAXNAME];
174851317Seric 
1749*56326Seric 	expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv);
1750*56326Seric 	expand(to, expto, &expto[sizeof expto - 1], CurEnv);
175151317Seric 
1752*56326Seric 	/*
1753*56326Seric 	 * supposing that we've got something, should
1754*56326Seric 	 * we add "from!" or remove "to!"?
1755*56326Seric 	 */
1756*56326Seric 	if (pvp[0] != NULL)
1757*56326Seric 		if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
1758*56326Seric 		    /*strcasecmp?*/ strcmp(pvp[0], expto) != 0)
1759*56326Seric 		{
1760*56326Seric 			/* either local name, no UUCP address, */
1761*56326Seric 			/* or not to "to!" ==> prepend address with "from!" */
176251317Seric 
1763*56326Seric 			/* already there? */
1764*56326Seric 			if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
1765*56326Seric 			    /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0)
1766*56326Seric 			{
176751317Seric 
1768*56326Seric 				/* no, put it there */
1769*56326Seric 				while (*pxp != NULL)
1770*56326Seric 					pxp++;
1771*56326Seric 				do
1772*56326Seric 					pxp[2] = *pxp;
1773*56326Seric 				while (pxp-- != pvp);
1774*56326Seric 				pvp[0] = newstr(expfrom);
1775*56326Seric 				pvp[1] = "!";
1776*56326Seric 			}
1777*56326Seric 		}
1778*56326Seric 		else
1779*56326Seric 		{
1780*56326Seric 			/* address is to "to!" -- remove if not "to!user" */
1781*56326Seric 			for (pxp = &pvp[2];
1782*56326Seric 			     *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++)
1783*56326Seric 				;
1784*56326Seric 			if (*pxp != NULL)
1785*56326Seric 				for (pxp = pvp; *pxp != NULL; pxp++)
1786*56326Seric 					*pxp = pxp[2];
1787*56326Seric 		}
178851317Seric }
1789