parseaddr.c revision 71345
1103285Sikob/*
2113584Ssimokawa * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3103285Sikob *	All rights reserved.
4103285Sikob * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5103285Sikob * Copyright (c) 1988, 1993
6103285Sikob *	The Regents of the University of California.  All rights reserved.
7103285Sikob *
8103285Sikob * By using this file, you agree to the terms and conditions set
9103285Sikob * forth in the LICENSE file which can be found at the top level of
10103285Sikob * the sendmail distribution.
11103285Sikob *
12103285Sikob */
13103285Sikob
14103285Sikob#ifndef lint
15103285Sikobstatic char id[] = "@(#)$Id: parseaddr.c,v 8.234.4.9 2000/10/09 03:14:48 gshapiro Exp $";
16103285Sikob#endif /* ! lint */
17106802Ssimokawa
18103285Sikob#include <sendmail.h>
19103285Sikob
20103285Sikobstatic void	allocaddr __P((ADDRESS *, int, char *));
21103285Sikobstatic int	callsubr __P((char**, int, ENVELOPE *));
22103285Sikobstatic char	*map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
23103285Sikobstatic ADDRESS	*buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
24103285Sikob
25103285Sikob/*
26103285Sikob**  PARSEADDR -- Parse an address
27103285Sikob**
28103285Sikob**	Parses an address and breaks it up into three parts: a
29103285Sikob**	net to transmit the message on, the host to transmit it
30103285Sikob**	to, and a user on that host.  These are loaded into an
31103285Sikob**	ADDRESS header with the values squirreled away if necessary.
32103285Sikob**	The "user" part may not be a real user; the process may
33103285Sikob**	just reoccur on that machine.  For example, on a machine
34103285Sikob**	with an arpanet connection, the address
35103285Sikob**		csvax.bill@berkeley
36103285Sikob**	will break up to a "user" of 'csvax.bill' and a host
37106802Ssimokawa**	of 'berkeley' -- to be transmitted over the arpanet.
38103285Sikob**
39103285Sikob**	Parameters:
40103285Sikob**		addr -- the address to parse.
41103285Sikob**		a -- a pointer to the address descriptor buffer.
42103285Sikob**			If NULL, a header will be created.
43103285Sikob**		flags -- describe detail for parsing.  See RF_ definitions
44103285Sikob**			in sendmail.h.
45103285Sikob**		delim -- the character to terminate the address, passed
46103285Sikob**			to prescan.
47103285Sikob**		delimptr -- if non-NULL, set to the location of the
48103285Sikob**			delim character that was found.
49103285Sikob**		e -- the envelope that will contain this address.
50103285Sikob**
51103285Sikob**	Returns:
52103285Sikob**		A pointer to the address descriptor header (`a' if
53113584Ssimokawa**			`a' is non-NULL).
54103285Sikob**		NULL on error.
55103285Sikob**
56103285Sikob**	Side Effects:
57127468Ssimokawa**		none
58117067Ssimokawa*/
59117067Ssimokawa
60117067Ssimokawa/* following delimiters are inherent to the internal algorithms */
61127468Ssimokawa#define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */
62127468Ssimokawa
63127468SsimokawaADDRESS *
64127468Ssimokawaparseaddr(addr, a, flags, delim, delimptr, e)
65127468Ssimokawa	char *addr;
66127468Ssimokawa	register ADDRESS *a;
67127468Ssimokawa	int flags;
68127468Ssimokawa	int delim;
69103285Sikob	char **delimptr;
70103285Sikob	register ENVELOPE *e;
71113584Ssimokawa{
72103285Sikob	register char **pvp;
73103285Sikob	auto char *delimptrbuf;
74103285Sikob	bool qup;
75127468Ssimokawa	char pvpbuf[PSBUFSIZE];
76103285Sikob
77103285Sikob	/*
78106802Ssimokawa	**  Initialize and prescan address.
79103285Sikob	*/
80103285Sikob
81113584Ssimokawa	e->e_to = addr;
82103285Sikob	if (tTd(20, 1))
83103285Sikob		dprintf("\n--parseaddr(%s)\n", addr);
84113584Ssimokawa
85103285Sikob	if (delimptr == NULL)
86103285Sikob		delimptr = &delimptrbuf;
87103285Sikob
88103285Sikob	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
89103285Sikob	if (pvp == NULL)
90103285Sikob	{
91103285Sikob		if (tTd(20, 1))
92103285Sikob			dprintf("parseaddr-->NULL\n");
93103285Sikob		return NULL;
94113584Ssimokawa	}
95116376Ssimokawa
96124378Ssimokawa	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
97129585Sdfr	{
98103285Sikob		if (tTd(20, 1))
99103285Sikob			dprintf("parseaddr-->bad address\n");
100103285Sikob		return NULL;
101103285Sikob	}
102103285Sikob
103103285Sikob	/*
104103285Sikob	**  Save addr if we are going to have to.
105103285Sikob	**
106103285Sikob	**	We have to do this early because there is a chance that
107103285Sikob	**	the map lookups in the rewriting rules could clobber
108103285Sikob	**	static memory somewhere.
109103285Sikob	*/
110103285Sikob
111103285Sikob	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
112103285Sikob	{
113103285Sikob		char savec = **delimptr;
114103285Sikob
115103285Sikob		if (savec != '\0')
116103285Sikob			**delimptr = '\0';
117103285Sikob		e->e_to = addr = newstr(addr);
118103285Sikob		if (savec != '\0')
119103285Sikob			**delimptr = savec;
120103285Sikob	}
121103285Sikob
122103285Sikob	/*
123103285Sikob	**  Apply rewriting rules.
124103285Sikob	**	Ruleset 0 does basic parsing.  It must resolve.
125124169Ssimokawa	*/
126124169Ssimokawa
127124169Ssimokawa	qup = FALSE;
128124169Ssimokawa	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
129124169Ssimokawa		qup = TRUE;
130124169Ssimokawa	if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
131124169Ssimokawa		qup = TRUE;
132124169Ssimokawa
133129585Sdfr
134129585Sdfr	/*
135124169Ssimokawa	**  Build canonical address from pvp.
136124169Ssimokawa	*/
137124169Ssimokawa
138124169Ssimokawa	a = buildaddr(pvp, a, flags, e);
139113584Ssimokawa
140129585Sdfr	/*
141113584Ssimokawa	**  Make local copies of the host & user and then
142124169Ssimokawa	**  transport them out.
143124169Ssimokawa	*/
144124169Ssimokawa
145124169Ssimokawa	allocaddr(a, flags, addr);
146113584Ssimokawa	if (QS_IS_BADADDR(a->q_state))
147124169Ssimokawa		return a;
148124169Ssimokawa
149129585Sdfr	/*
150129585Sdfr	**  If there was a parsing failure, mark it for queueing.
151129585Sdfr	*/
152129585Sdfr
153124169Ssimokawa	if (qup && OpMode != MD_INITALIAS)
154124169Ssimokawa	{
155124169Ssimokawa		char *msg = "Transient parse error -- message queued for future delivery";
156113584Ssimokawa
157113584Ssimokawa		if (e->e_sendmode == SM_DEFER)
158113584Ssimokawa			msg = "Deferring message until queue run";
159103285Sikob		if (tTd(20, 1))
160103285Sikob			dprintf("parseaddr: queuing message\n");
161103285Sikob		message(msg);
162103285Sikob		if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
163103285Sikob			e->e_message = newstr(msg);
164103285Sikob		a->q_state = QS_QUEUEUP;
165103285Sikob		a->q_status = "4.4.3";
166103285Sikob	}
167103285Sikob
168112523Ssimokawa	/*
169103285Sikob	**  Compute return value.
170103285Sikob	*/
171103285Sikob
172103285Sikob	if (tTd(20, 1))
173103285Sikob	{
174103285Sikob		dprintf("parseaddr-->");
175103285Sikob		printaddr(a, FALSE);
176103285Sikob	}
177103285Sikob
178103285Sikob	return a;
179103285Sikob}
180103285Sikob/*
181103285Sikob**  INVALIDADDR -- check for address containing meta-characters
182103285Sikob**
183103285Sikob**	Parameters:
184103285Sikob**		addr -- the address to check.
185103285Sikob**
186103285Sikob**	Returns:
187103285Sikob**		TRUE -- if the address has any "wierd" characters
188103285Sikob**		FALSE -- otherwise.
189103285Sikob*/
190103285Sikob
191103285Sikobbool
192103285Sikobinvalidaddr(addr, delimptr)
193103285Sikob	register char *addr;
194103285Sikob	char *delimptr;
195103285Sikob{
196103285Sikob	char savedelim = '\0';
197113584Ssimokawa
198103285Sikob	if (delimptr != NULL)
199103285Sikob	{
200103285Sikob		savedelim = *delimptr;
201103285Sikob		if (savedelim != '\0')
202103285Sikob			*delimptr = '\0';
203103285Sikob	}
204103285Sikob	if (strlen(addr) > MAXNAME - 1)
205103285Sikob	{
206103285Sikob		usrerr("553 5.1.1 Address too long (%d bytes max)",
207103285Sikob		       MAXNAME - 1);
208103285Sikob		goto failure;
209103285Sikob	}
210103285Sikob	for (; *addr != '\0'; addr++)
211103285Sikob	{
212103285Sikob		if ((*addr & 0340) == 0200)
213103285Sikob			break;
214103285Sikob	}
215103285Sikob	if (*addr == '\0')
216103285Sikob	{
217103285Sikob		if (delimptr != NULL && savedelim != '\0')
218103285Sikob			*delimptr = savedelim;
219103285Sikob		return FALSE;
220103285Sikob	}
221103285Sikob	setstat(EX_USAGE);
222103285Sikob	usrerr("553 5.1.1 Address contained invalid control characters");
223103285Sikobfailure:
224103285Sikob	if (delimptr != NULL && savedelim != '\0')
225103285Sikob		*delimptr = savedelim;
226103285Sikob	return TRUE;
227103285Sikob}
228103285Sikob/*
229103285Sikob**  ALLOCADDR -- do local allocations of address on demand.
230103285Sikob**
231103285Sikob**	Also lowercases the host name if requested.
232103285Sikob**
233103285Sikob**	Parameters:
234103285Sikob**		a -- the address to reallocate.
235103285Sikob**		flags -- the copy flag (see RF_ definitions in sendmail.h
236103285Sikob**			for a description).
237103285Sikob**		paddr -- the printname of the address.
238103285Sikob**
239103285Sikob**	Returns:
240103285Sikob**		none.
241103285Sikob**
242103285Sikob**	Side Effects:
243103285Sikob**		Copies portions of a into local buffers as requested.
244103285Sikob*/
245103285Sikob
246103285Sikobstatic void
247103285Sikoballocaddr(a, flags, paddr)
248103285Sikob	register ADDRESS *a;
249103285Sikob	int flags;
250103285Sikob	char *paddr;
251103285Sikob{
252103285Sikob	if (tTd(24, 4))
253103285Sikob		dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
254103285Sikob
255103285Sikob	a->q_paddr = paddr;
256103285Sikob
257103285Sikob	if (a->q_user == NULL)
258103285Sikob		a->q_user = newstr("");
259103285Sikob	if (a->q_host == NULL)
260103285Sikob		a->q_host = newstr("");
261129585Sdfr
262129585Sdfr	if (bitset(RF_COPYPARSE, flags))
263103285Sikob	{
264129585Sdfr		a->q_host = newstr(a->q_host);
265103285Sikob		if (a->q_user != a->q_paddr)
266103285Sikob			a->q_user = newstr(a->q_user);
267103285Sikob	}
268103285Sikob
269103285Sikob	if (a->q_paddr == NULL)
270103285Sikob		a->q_paddr = newstr(a->q_user);
271103285Sikob}
272103285Sikob/*
273103285Sikob**  PRESCAN -- Prescan name and make it canonical
274103285Sikob**
275103285Sikob**	Scans a name and turns it into a set of tokens.  This process
276129585Sdfr**	deletes blanks and comments (in parentheses) (if the token type
277103285Sikob**	for left paren is SPC).
278103285Sikob**
279103285Sikob**	This routine knows about quoted strings and angle brackets.
280103285Sikob**
281129585Sdfr**	There are certain subtleties to this routine.  The one that
282103285Sikob**	comes to mind now is that backslashes on the ends of names
283103285Sikob**	are silently stripped off; this is intentional.  The problem
284103285Sikob**	is that some versions of sndmsg (like at LBL) set the kill
285103285Sikob**	character to something other than @ when reading addresses;
286103285Sikob**	so people type "csvax.eric\@berkeley" -- which screws up the
287103285Sikob**	berknet mailer.
288103285Sikob**
289103285Sikob**	Parameters:
290103285Sikob**		addr -- the name to chomp.
291103285Sikob**		delim -- the delimiter for the address, normally
292109280Ssimokawa**			'\0' or ','; \0 is accepted in any case.
293103285Sikob**			If '\t' then we are reading the .cf file.
294107653Ssimokawa**		pvpbuf -- place to put the saved text -- note that
295103285Sikob**			the pointers are static.
296107653Ssimokawa**		pvpbsize -- size of pvpbuf.
297107653Ssimokawa**		delimptr -- if non-NULL, set to the location of the
298107653Ssimokawa**			terminating delimiter.
299103285Sikob**		toktab -- if set, a token table to use for parsing.
300103285Sikob**			If NULL, use the default table.
301103285Sikob**
302103285Sikob**	Returns:
303129585Sdfr**		A pointer to a vector of tokens.
304106790Ssimokawa**		NULL on error.
305103285Sikob*/
306129585Sdfr
307108500Ssimokawa/* states and character types */
308103285Sikob#define OPR		0	/* operator */
309103285Sikob#define ATM		1	/* atom */
310108500Ssimokawa#define QST		2	/* in quoted string */
311108500Ssimokawa#define SPC		3	/* chewing up spaces */
312108500Ssimokawa#define ONE		4	/* pick up one character */
313103285Sikob#define ILL		5	/* illegal character */
314103285Sikob
315108500Ssimokawa#define NSTATES	6	/* number of states */
316103285Sikob#define TYPE		017	/* mask to select state type */
317103285Sikob
318103285Sikob/* meta bits for table */
319109280Ssimokawa#define M		020	/* meta character; don't pass through */
320103285Sikob#define B		040	/* cause a break */
321108500Ssimokawa#define MB		M|B	/* meta-break */
322109280Ssimokawa
323109280Ssimokawastatic short StateTab[NSTATES][NSTATES] =
324108527Ssimokawa{
325109280Ssimokawa   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
326108527Ssimokawa	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
327108527Ssimokawa	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
328108500Ssimokawa	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},
329108500Ssimokawa	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},
330108500Ssimokawa	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},
331108500Ssimokawa	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},
332108500Ssimokawa};
333109280Ssimokawa
334109280Ssimokawa/* token type table -- it gets modified with $o characters */
335108500Ssimokawastatic u_char	TokTypeTab[256] =
336109280Ssimokawa{
337108500Ssimokawa    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
338108500Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
339108500Ssimokawa    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
340108500Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
341108500Ssimokawa    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
342119118Ssimokawa	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
343108500Ssimokawa    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
344103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
345103285Sikob    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
346103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
347103285Sikob    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
348130585Sphk	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
349103285Sikob    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
350103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
351103285Sikob    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
352103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
353103285Sikob
354103285Sikob    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
355129585Sdfr	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
356103285Sikob    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
357103285Sikob	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
358103285Sikob    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
359103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
360103285Sikob    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
361103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
362103285Sikob    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
363103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
364103285Sikob    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
365103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
366103285Sikob    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
367103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
368103285Sikob    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
369103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
370103285Sikob};
371103285Sikob
372103285Sikob/* token type table for MIME parsing */
373103285Sikobu_char	MimeTokenTab[256] =
374103285Sikob{
375103285Sikob    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
376103285Sikob	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
377103285Sikob    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
378103285Sikob	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
379103285Sikob    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
380103285Sikob	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
381103285Sikob    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
382103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
383103285Sikob    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
384103285Sikob	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
385103285Sikob    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
386103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
387103285Sikob    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
388103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
389103285Sikob    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
390103285Sikob	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
391103285Sikob
392119118Ssimokawa    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
393119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
394119118Ssimokawa    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
395119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
396119118Ssimokawa    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
397119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
398119118Ssimokawa    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
399119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
400119118Ssimokawa    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
401119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
402119118Ssimokawa    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
403119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
404119118Ssimokawa    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
405119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
406103285Sikob    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
407119118Ssimokawa	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
408103285Sikob};
409103285Sikob
410103285Sikob/* token type table: don't strip comments */
411103285Sikobu_char	TokTypeNoC[256] =
412106790Ssimokawa{
413108530Ssimokawa    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
414108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
415103285Sikob    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
416129585Sdfr	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
417108530Ssimokawa    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
418108530Ssimokawa	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
419108530Ssimokawa    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
420108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
421108530Ssimokawa    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
422108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
423108530Ssimokawa    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
424108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
425108530Ssimokawa    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
426108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
427108530Ssimokawa    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
428108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
429108530Ssimokawa
430108530Ssimokawa    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
431108530Ssimokawa	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
432108530Ssimokawa    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
433108530Ssimokawa	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
434108530Ssimokawa    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
435108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
436108530Ssimokawa    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
437108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
438108701Ssimokawa    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
439108701Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
440108530Ssimokawa    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
441108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
442108530Ssimokawa    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
443108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
444108530Ssimokawa    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
445108530Ssimokawa	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
446108530Ssimokawa};
447108530Ssimokawa
448108530Ssimokawa
449108530Ssimokawa#define NOCHAR		-1	/* signal nothing in lookahead token */
450108530Ssimokawa
451108701Ssimokawachar **
452108701Ssimokawaprescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
453108530Ssimokawa	char *addr;
454108530Ssimokawa	int delim;
455108530Ssimokawa	char pvpbuf[];
456108530Ssimokawa	int pvpbsize;
457108530Ssimokawa	char **delimptr;
458108530Ssimokawa	u_char *toktab;
459108530Ssimokawa{
460108530Ssimokawa	register char *p;
461108530Ssimokawa	register char *q;
462108530Ssimokawa	register int c;
463108530Ssimokawa	char **avp;
464108530Ssimokawa	bool bslashmode;
465108530Ssimokawa	bool route_syntax;
466108530Ssimokawa	int cmntcnt;
467108530Ssimokawa	int anglecnt;
468108530Ssimokawa	char *tok;
469108530Ssimokawa	int state;
470108530Ssimokawa	int newstate;
471108530Ssimokawa	char *saveto = CurEnv->e_to;
472108530Ssimokawa	static char *av[MAXATOM + 1];
473108530Ssimokawa	static char firsttime = TRUE;
474108530Ssimokawa	extern int errno;
475108530Ssimokawa
476108530Ssimokawa	if (firsttime)
477108530Ssimokawa	{
478108530Ssimokawa		/* initialize the token type table */
479108530Ssimokawa		char obuf[50];
480108530Ssimokawa
481108530Ssimokawa		firsttime = FALSE;
482108530Ssimokawa		if (OperatorChars == NULL)
483108530Ssimokawa		{
484108530Ssimokawa			if (ConfigLevel < 7)
485108530Ssimokawa				OperatorChars = macvalue('o', CurEnv);
486108530Ssimokawa			if (OperatorChars == NULL)
487108530Ssimokawa				OperatorChars = ".:@[]";
488108530Ssimokawa		}
489108530Ssimokawa		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
490108701Ssimokawa		       CurEnv);
491129585Sdfr		(void) strlcat(obuf, DELIMCHARS, sizeof obuf);
492103285Sikob		for (p = obuf; *p != '\0'; p++)
493103285Sikob		{
494129541Sdfr			if (TokTypeTab[*p & 0xff] == ATM)
495108530Ssimokawa				TokTypeTab[*p & 0xff] = OPR;
496108530Ssimokawa			if (TokTypeNoC[*p & 0xff] == ATM)
497129541Sdfr				TokTypeNoC[*p & 0xff] = OPR;
498108530Ssimokawa		}
499108530Ssimokawa	}
500108530Ssimokawa	if (toktab == NULL)
501108530Ssimokawa		toktab = TokTypeTab;
502108530Ssimokawa
503108530Ssimokawa	/* make sure error messages don't have garbage on them */
504108530Ssimokawa	errno = 0;
505108530Ssimokawa
506108530Ssimokawa	q = pvpbuf;
507108530Ssimokawa	bslashmode = FALSE;
508108530Ssimokawa	route_syntax = FALSE;
509108701Ssimokawa	cmntcnt = 0;
510108530Ssimokawa	anglecnt = 0;
511108530Ssimokawa	avp = av;
512108530Ssimokawa	state = ATM;
513108530Ssimokawa	c = NOCHAR;
514108530Ssimokawa	p = addr;
515108530Ssimokawa	CurEnv->e_to = p;
516108530Ssimokawa	if (tTd(22, 11))
517108530Ssimokawa	{
518108530Ssimokawa		dprintf("prescan: ");
519108530Ssimokawa		xputs(p);
520108530Ssimokawa		dprintf("\n");
521108701Ssimokawa	}
522108701Ssimokawa
523108701Ssimokawa	do
524108701Ssimokawa	{
525108530Ssimokawa		/* read a token */
526108530Ssimokawa		tok = q;
527108701Ssimokawa		for (;;)
528108701Ssimokawa		{
529108701Ssimokawa			/* store away any old lookahead character */
530108701Ssimokawa			if (c != NOCHAR && !bslashmode)
531108701Ssimokawa			{
532108701Ssimokawa				/* see if there is room */
533108701Ssimokawa				if (q >= &pvpbuf[pvpbsize - 5])
534108701Ssimokawa				{
535108701Ssimokawa					usrerr("553 5.1.1 Address too long");
536108701Ssimokawa					if (strlen(addr) > (SIZE_T) MAXNAME)
537108701Ssimokawa						addr[MAXNAME] = '\0';
538108530Ssimokawa	returnnull:
539108530Ssimokawa					if (delimptr != NULL)
540108530Ssimokawa						*delimptr = p;
541108530Ssimokawa					CurEnv->e_to = saveto;
542108701Ssimokawa					return NULL;
543108530Ssimokawa				}
544113584Ssimokawa
545108530Ssimokawa				/* squirrel it away */
546108530Ssimokawa				*q++ = c;
547113584Ssimokawa			}
548108530Ssimokawa
549108530Ssimokawa			/* read a new input character */
550108701Ssimokawa			c = *p++;
551108530Ssimokawa			if (c == '\0')
552108642Ssimokawa			{
553108701Ssimokawa				/* diagnose and patch up bad syntax */
554108642Ssimokawa				if (state == QST)
555108642Ssimokawa				{
556108530Ssimokawa					usrerr("653 Unbalanced '\"'");
557108530Ssimokawa					c = '"';
558108530Ssimokawa				}
559108701Ssimokawa				else if (cmntcnt > 0)
560108701Ssimokawa				{
561108701Ssimokawa					usrerr("653 Unbalanced '('");
562116978Ssimokawa					c = ')';
563108701Ssimokawa				}
564108701Ssimokawa				else if (anglecnt > 0)
565108701Ssimokawa				{
566108701Ssimokawa					c = '>';
567116978Ssimokawa					usrerr("653 Unbalanced '<'");
568116978Ssimokawa				}
569116978Ssimokawa				else
570116978Ssimokawa					break;
571116978Ssimokawa
572116978Ssimokawa				p--;
573108530Ssimokawa			}
574108530Ssimokawa			else if (c == delim && cmntcnt <= 0 && state != QST)
575108530Ssimokawa			{
576108530Ssimokawa				if (anglecnt <= 0)
577108530Ssimokawa					break;
578108530Ssimokawa
579108530Ssimokawa				/* special case for better error management */
580108530Ssimokawa				if (delim == ',' && !route_syntax)
581108530Ssimokawa				{
582108701Ssimokawa					usrerr("653 Unbalanced '<'");
583129541Sdfr					c = '>';
584108530Ssimokawa					p--;
585108530Ssimokawa				}
586108530Ssimokawa			}
587108530Ssimokawa
588108530Ssimokawa			if (tTd(22, 101))
589108530Ssimokawa				dprintf("c=%c, s=%d; ", c, state);
590108530Ssimokawa
591108530Ssimokawa			/* chew up special characters */
592108530Ssimokawa			*q = '\0';
593108530Ssimokawa			if (bslashmode)
594108530Ssimokawa			{
595108530Ssimokawa				bslashmode = FALSE;
596121781Ssimokawa
597129585Sdfr				/* kludge \! for naive users */
598129585Sdfr				if (cmntcnt > 0)
599108530Ssimokawa				{
600113584Ssimokawa					c = NOCHAR;
601113584Ssimokawa					continue;
602113584Ssimokawa				}
603113584Ssimokawa				else if (c != '!' || state == QST)
604121781Ssimokawa				{
605103285Sikob					*q++ = '\\';
606121781Ssimokawa					continue;
607103285Sikob				}
608121781Ssimokawa			}
609121781Ssimokawa
610118416Ssimokawa			if (c == '\\')
611118416Ssimokawa			{
612118416Ssimokawa				bslashmode = TRUE;
613118416Ssimokawa			}
614129541Sdfr			else if (state == QST)
615110045Ssimokawa			{
616110045Ssimokawa				/* EMPTY */
617110045Ssimokawa				/* do nothing, just avoid next clauses */
618110045Ssimokawa			}
619110045Ssimokawa			else if (c == '(' && toktab['('] == SPC)
620110045Ssimokawa			{
621110045Ssimokawa				cmntcnt++;
622110045Ssimokawa				c = NOCHAR;
623103285Sikob			}
624129541Sdfr			else if (c == ')' && toktab['('] == SPC)
625118820Ssimokawa			{
626118820Ssimokawa				if (cmntcnt <= 0)
627103285Sikob				{
628103285Sikob					usrerr("653 Unbalanced ')'");
629103285Sikob					c = NOCHAR;
630103285Sikob				}
631103285Sikob				else
632103285Sikob					cmntcnt--;
633113584Ssimokawa			}
634113584Ssimokawa			else if (cmntcnt > 0)
635113584Ssimokawa			{
636113584Ssimokawa				c = NOCHAR;
637113584Ssimokawa			}
638103285Sikob			else if (c == '<')
639103285Sikob			{
640103285Sikob				char *ptr = p;
641103285Sikob
642103285Sikob				anglecnt++;
643113584Ssimokawa				while (isascii(*ptr) && isspace(*ptr))
644113584Ssimokawa					ptr++;
645113584Ssimokawa				if (*ptr == '@')
646113584Ssimokawa					route_syntax = TRUE;
647103285Sikob			}
648118293Ssimokawa			else if (c == '>')
649118293Ssimokawa			{
650118293Ssimokawa				if (anglecnt <= 0)
651118293Ssimokawa				{
652118293Ssimokawa					usrerr("653 Unbalanced '>'");
653103285Sikob					c = NOCHAR;
654103285Sikob				}
655110593Ssimokawa				else
656110593Ssimokawa					anglecnt--;
657103285Sikob				route_syntax = FALSE;
658103285Sikob			}
659103285Sikob			else if (delim == ' ' && isascii(c) && isspace(c))
660103285Sikob				c = ' ';
661103285Sikob
662103285Sikob			if (c == NOCHAR)
663103285Sikob				continue;
664103285Sikob
665103285Sikob			/* see if this is end of input */
666118293Ssimokawa			if (c == delim && anglecnt <= 0 && state != QST)
667118293Ssimokawa				break;
668103285Sikob
669103285Sikob			newstate = StateTab[state][toktab[c & 0xff]];
670103285Sikob			if (tTd(22, 101))
671103285Sikob				dprintf("ns=%02o\n", newstate);
672103285Sikob			state = newstate & TYPE;
673113584Ssimokawa			if (state == ILL)
674103285Sikob			{
675113584Ssimokawa				if (isascii(c) && isprint(c))
676113584Ssimokawa					usrerr("653 Illegal character %c", c);
677113584Ssimokawa				else
678113584Ssimokawa					usrerr("653 Illegal character 0x%02x", c);
679103285Sikob			}
680103285Sikob			if (bitset(M, newstate))
681103285Sikob				c = NOCHAR;
682116376Ssimokawa			if (bitset(B, newstate))
683116376Ssimokawa				break;
684103285Sikob		}
685103285Sikob
686103285Sikob		/* new token */
687103285Sikob		if (tok != q)
688103285Sikob		{
689103285Sikob			*q++ = '\0';
690103285Sikob			if (tTd(22, 36))
691103285Sikob			{
692113584Ssimokawa				dprintf("tok=");
693103285Sikob				xputs(tok);
694103285Sikob				dprintf("\n");
695129541Sdfr			}
696103285Sikob			if (avp >= &av[MAXATOM])
697113584Ssimokawa			{
698113584Ssimokawa				usrerr("553 5.1.0 prescan: too many tokens");
699113584Ssimokawa				goto returnnull;
700113584Ssimokawa			}
701108527Ssimokawa			if (q - tok > MAXNAME)
702108527Ssimokawa			{
703113584Ssimokawa				usrerr("553 5.1.0 prescan: token too long");
704129585Sdfr				goto returnnull;
705113584Ssimokawa			}
706113584Ssimokawa			*avp++ = tok;
707113584Ssimokawa		}
708113584Ssimokawa	} while (c != '\0' && (c != delim || anglecnt > 0));
709109736Ssimokawa	*avp = NULL;
710109736Ssimokawa	p--;
711113584Ssimokawa	if (delimptr != NULL)
712113584Ssimokawa		*delimptr = p;
713108527Ssimokawa	if (tTd(22, 12))
714108527Ssimokawa	{
715108527Ssimokawa		dprintf("prescan==>");
716113584Ssimokawa		printav(av);
717108527Ssimokawa	}
718108527Ssimokawa	CurEnv->e_to = saveto;
719103285Sikob	if (av[0] == NULL)
720113584Ssimokawa	{
721108527Ssimokawa		if (tTd(22, 1))
722108527Ssimokawa			dprintf("prescan: null leading token\n");
723108527Ssimokawa		return NULL;
724113584Ssimokawa	}
725108527Ssimokawa	return av;
726108527Ssimokawa}
727103285Sikob/*
728109814Ssimokawa**  REWRITE -- apply rewrite rules to token vector.
729109814Ssimokawa**
730109814Ssimokawa**	This routine is an ordered production system.  Each rewrite
731109814Ssimokawa**	rule has a LHS (called the pattern) and a RHS (called the
732103285Sikob**	rewrite); 'rwr' points the the current rewrite rule.
733109814Ssimokawa**
734109814Ssimokawa**	For each rewrite rule, 'avp' points the address vector we
735103285Sikob**	are trying to match against, and 'pvp' points to the pattern.
736103285Sikob**	If pvp points to a special match value (MATCHZANY, MATCHANY,
737103285Sikob**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
738103285Sikob**	matched is saved away in the match vector (pointed to by 'mvp').
739103285Sikob**
740103285Sikob**	When a match between avp & pvp does not match, we try to
741103285Sikob**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
742103285Sikob**	we must also back out the match in mvp.  If we reach a
743103285Sikob**	MATCHANY or MATCHZANY we just extend the match and start
744113584Ssimokawa**	over again.
745103285Sikob**
746113584Ssimokawa**	When we finally match, we rewrite the address vector
747113584Ssimokawa**	and try over again.
748113584Ssimokawa**
749103285Sikob**	Parameters:
750103285Sikob**		pvp -- pointer to token vector.
751103285Sikob**		ruleset -- the ruleset to use for rewriting.
752103285Sikob**		reclevel -- recursion level (to catch loops).
753106790Ssimokawa**		e -- the current envelope.
754113584Ssimokawa**
755113584Ssimokawa**	Returns:
756108530Ssimokawa**		A status code.  If EX_TEMPFAIL, higher level code should
757108530Ssimokawa**			attempt recovery.
758103285Sikob**
759108530Ssimokawa**	Side Effects:
760103285Sikob**		pvp is modified.
761106790Ssimokawa*/
762106790Ssimokawa
763106790Ssimokawastruct match
764103285Sikob{
765103285Sikob	char	**match_first;		/* first token matched */
766103285Sikob	char	**match_last;		/* last token matched */
767103285Sikob	char	**match_pattern;	/* pointer to pattern */
768103285Sikob};
769106790Ssimokawa
770129585Sdfr#define MAXMATCH	9	/* max params per rewrite */
771106790Ssimokawa
772103285Sikob
773103285Sikobint
774103285Sikobrewrite(pvp, ruleset, reclevel, e)
775103285Sikob	char **pvp;
776103285Sikob	int ruleset;
777108527Ssimokawa	int reclevel;
778108527Ssimokawa	register ENVELOPE *e;
779108527Ssimokawa{
780108527Ssimokawa	register char *ap;		/* address pointer */
781108527Ssimokawa	register char *rp;		/* rewrite pointer */
782113584Ssimokawa	register char *rulename;	/* ruleset name */
783113584Ssimokawa	register char *prefix;
784113584Ssimokawa	register char **avp;		/* address vector pointer */
785113584Ssimokawa	register char **rvp;		/* rewrite vector pointer */
786108527Ssimokawa	register struct match *mlp;	/* cur ptr into mlist */
787108527Ssimokawa	register struct rewrite *rwr;	/* pointer to current rewrite rule */
788108527Ssimokawa	int ruleno;			/* current rule number */
789108527Ssimokawa	int rstat = EX_OK;		/* return status */
790108527Ssimokawa	int loopcount;
791108527Ssimokawa	struct match mlist[MAXMATCH];	/* stores match on LHS */
792108527Ssimokawa	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */
793108527Ssimokawa	char buf[MAXLINE];
794108527Ssimokawa	char name[6];
795108527Ssimokawa
796108527Ssimokawa	if (ruleset < 0 || ruleset >= MAXRWSETS)
797108527Ssimokawa	{
798108527Ssimokawa		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
799108527Ssimokawa		return EX_CONFIG;
800108527Ssimokawa	}
801108655Ssimokawa	rulename = RuleSetNames[ruleset];
802108655Ssimokawa	if (rulename == NULL)
803108655Ssimokawa	{
804108655Ssimokawa		snprintf(name, sizeof name, "%d", ruleset);
805108655Ssimokawa		rulename = name;
806108655Ssimokawa	}
807106790Ssimokawa	if (OpMode == MD_TEST)
808113584Ssimokawa		prefix = "";
809113584Ssimokawa	else
810113584Ssimokawa		prefix = "rewrite: ruleset ";
811120660Ssimokawa	if (OpMode == MD_TEST)
812113584Ssimokawa	{
813113584Ssimokawa		printf("%s%-16.16s   input:", prefix, rulename);
814113584Ssimokawa		printav(pvp);
815113584Ssimokawa	}
816113584Ssimokawa	else if (tTd(21, 1))
817113584Ssimokawa	{
818113584Ssimokawa		dprintf("%s%-16.16s   input:", prefix, rulename);
819113584Ssimokawa		printav(pvp);
820113584Ssimokawa	}
821113584Ssimokawa	if (reclevel++ > MaxRuleRecursion)
822113584Ssimokawa	{
823113584Ssimokawa		syserr("rewrite: excessive recursion (max %d), ruleset %s",
824113584Ssimokawa			MaxRuleRecursion, rulename);
825113584Ssimokawa		return EX_CONFIG;
826113584Ssimokawa	}
827113584Ssimokawa	if (pvp == NULL)
828113584Ssimokawa		return EX_USAGE;
829113584Ssimokawa
830113584Ssimokawa	/*
831113584Ssimokawa	**  Run through the list of rewrite rules, applying
832113584Ssimokawa	**	any that match.
833113584Ssimokawa	*/
834113584Ssimokawa
835113584Ssimokawa	ruleno = 1;
836113584Ssimokawa	loopcount = 0;
837113584Ssimokawa	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
838113584Ssimokawa	{
839113584Ssimokawa		int status;
840106790Ssimokawa
841103285Sikob		/* if already canonical, quit now */
842103285Sikob		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
843120660Ssimokawa			break;
844103285Sikob
845129585Sdfr		if (tTd(21, 12))
846103285Sikob		{
847103285Sikob			if (tTd(21, 15))
848120660Ssimokawa				dprintf("-----trying rule (line %d):",
849103285Sikob				       rwr->r_line);
850120660Ssimokawa			else
851129585Sdfr				dprintf("-----trying rule:");
852103285Sikob			printav(rwr->r_lhs);
853108655Ssimokawa		}
854103285Sikob
855103285Sikob		/* try to match on this rule */
856103285Sikob		mlp = mlist;
857103285Sikob		rvp = rwr->r_lhs;
858103285Sikob		avp = pvp;
859103285Sikob		if (++loopcount > 100)
860103285Sikob		{
861103285Sikob			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
862103285Sikob				rulename, ruleno);
863103285Sikob			if (tTd(21, 1))
864103285Sikob			{
865103285Sikob				dprintf("workspace: ");
866103285Sikob				printav(pvp);
867103285Sikob			}
868103285Sikob			break;
869103285Sikob		}
870103285Sikob
871103285Sikob		while ((ap = *avp) != NULL || *rvp != NULL)
872103285Sikob		{
873103285Sikob			rp = *rvp;
874103285Sikob			if (tTd(21, 35))
875103285Sikob			{
876103285Sikob				dprintf("ADVANCE rp=");
877103285Sikob				xputs(rp);
878103285Sikob				dprintf(", ap=");
879103285Sikob				xputs(ap);
880120660Ssimokawa				dprintf("\n");
881103285Sikob			}
882103285Sikob			if (rp == NULL)
883120660Ssimokawa			{
884103285Sikob				/* end-of-pattern before end-of-address */
885113584Ssimokawa				goto backup;
886119155Ssimokawa			}
887119155Ssimokawa			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
888119155Ssimokawa			    (*rp & 0377) != MATCHZERO)
889119155Ssimokawa			{
890119155Ssimokawa				/* end-of-input with patterns left */
891119155Ssimokawa				goto backup;
892120660Ssimokawa			}
893103285Sikob
894103285Sikob			switch (*rp & 0377)
895113584Ssimokawa			{
896103285Sikob			  case MATCHCLASS:
897103285Sikob				/* match any phrase in a class */
898119155Ssimokawa				mlp->match_pattern = rvp;
899119155Ssimokawa				mlp->match_first = avp;
900103285Sikob	extendclass:
901103285Sikob				ap = *avp;
902103285Sikob				if (ap == NULL)
903113584Ssimokawa					goto backup;
904103285Sikob				mlp->match_last = avp++;
905103285Sikob				cataddr(mlp->match_first, mlp->match_last,
906103285Sikob					buf, sizeof buf, '\0');
907103285Sikob				if (!wordinclass(buf, rp[1]))
908113584Ssimokawa				{
909113584Ssimokawa					if (tTd(21, 36))
910119155Ssimokawa					{
911113584Ssimokawa						dprintf("EXTEND  rp=");
912103285Sikob						xputs(rp);
913103285Sikob						dprintf(", ap=");
914113584Ssimokawa						xputs(ap);
915113584Ssimokawa						dprintf("\n");
916103285Sikob					}
917113584Ssimokawa					goto extendclass;
918113584Ssimokawa				}
919113584Ssimokawa				if (tTd(21, 36))
920113584Ssimokawa					dprintf("CLMATCH\n");
921119155Ssimokawa				mlp++;
922113584Ssimokawa				break;
923103285Sikob
924111942Ssimokawa			  case MATCHNCLASS:
925103285Sikob				/* match any token not in a class */
926103285Sikob				if (wordinclass(ap, rp[1]))
927120660Ssimokawa					goto backup;
928113584Ssimokawa
929113584Ssimokawa				/* FALLTHROUGH */
930103285Sikob
931113584Ssimokawa			  case MATCHONE:
932120660Ssimokawa			  case MATCHANY:
933113584Ssimokawa				/* match exactly one token */
934113584Ssimokawa				mlp->match_pattern = rvp;
935103285Sikob				mlp->match_first = avp;
936111942Ssimokawa				mlp->match_last = avp++;
937113584Ssimokawa				mlp++;
938113584Ssimokawa				break;
939113584Ssimokawa
940113584Ssimokawa			  case MATCHZANY:
941113584Ssimokawa				/* match zero or more tokens */
942113584Ssimokawa				mlp->match_pattern = rvp;
943113584Ssimokawa				mlp->match_first = avp;
944113584Ssimokawa				mlp->match_last = avp - 1;
945113584Ssimokawa				mlp++;
946113584Ssimokawa				break;
947113584Ssimokawa
948111942Ssimokawa			  case MATCHZERO:
949111942Ssimokawa				/* match zero tokens */
950113584Ssimokawa				break;
951113584Ssimokawa
952111942Ssimokawa			  case MACRODEXPAND:
953111942Ssimokawa				/*
954113584Ssimokawa				**  Match against run-time macro.
955111942Ssimokawa				**  This algorithm is broken for the
956111942Ssimokawa				**  general case (no recursive macros,
957111942Ssimokawa				**  improper tokenization) but should
958111942Ssimokawa				**  work for the usual cases.
959103285Sikob				*/
960113584Ssimokawa
961113584Ssimokawa				ap = macvalue(rp[1], e);
962113584Ssimokawa				mlp->match_first = avp;
963113584Ssimokawa				if (tTd(21, 2))
964113584Ssimokawa					dprintf("rewrite: LHS $&%s => \"%s\"\n",
965113584Ssimokawa						macname(rp[1]),
966113584Ssimokawa						ap == NULL ? "(NULL)" : ap);
967113584Ssimokawa
968113584Ssimokawa				if (ap == NULL)
969103285Sikob					break;
970108655Ssimokawa				while (*ap != '\0')
971108655Ssimokawa				{
972108655Ssimokawa					if (*avp == NULL ||
973108655Ssimokawa					    strncasecmp(ap, *avp, strlen(*avp)) != 0)
974108655Ssimokawa					{
975103285Sikob						/* no match */
976103285Sikob						avp = mlp->match_first;
977113584Ssimokawa						goto backup;
978113584Ssimokawa					}
979113584Ssimokawa					ap += strlen(*avp++);
980113584Ssimokawa				}
981103285Sikob
982103285Sikob				/* match */
983103285Sikob				break;
984103285Sikob
985103285Sikob			  default:
986113584Ssimokawa				/* must have exact match */
987103285Sikob				if (sm_strcasecmp(rp, ap))
988103285Sikob					goto backup;
989103285Sikob				avp++;
990103285Sikob				break;
991103285Sikob			}
992103285Sikob
993107653Ssimokawa			/* successful match on this token */
994103285Sikob			rvp++;
995103285Sikob			continue;
996103285Sikob
997103285Sikob	  backup:
998113584Ssimokawa			/* match failed -- back up */
999113584Ssimokawa			while (--mlp >= mlist)
1000103285Sikob			{
1001103285Sikob				rvp = mlp->match_pattern;
1002103285Sikob				rp = *rvp;
1003103285Sikob				avp = mlp->match_last + 1;
1004107653Ssimokawa				ap = *avp;
1005107653Ssimokawa
1006103285Sikob				if (tTd(21, 36))
1007113584Ssimokawa				{
1008103285Sikob					dprintf("BACKUP  rp=");
1009103285Sikob					xputs(rp);
1010103285Sikob					dprintf(", ap=");
1011106790Ssimokawa					xputs(ap);
1012103285Sikob					dprintf("\n");
1013103285Sikob				}
1014103285Sikob
1015103285Sikob				if (ap == NULL)
1016106790Ssimokawa				{
1017106790Ssimokawa					/* run off the end -- back up again */
1018106790Ssimokawa					continue;
1019103285Sikob				}
1020103285Sikob				if ((*rp & 0377) == MATCHANY ||
1021103285Sikob				    (*rp & 0377) == MATCHZANY)
1022103285Sikob				{
1023103285Sikob					/* extend binding and continue */
1024106790Ssimokawa					mlp->match_last = avp++;
1025106790Ssimokawa					rvp++;
1026106790Ssimokawa					mlp++;
1027103285Sikob					break;
1028103285Sikob				}
1029103285Sikob				if ((*rp & 0377) == MATCHCLASS)
1030103285Sikob				{
1031103285Sikob					/* extend binding and try again */
1032106790Ssimokawa					mlp->match_last = avp;
1033106790Ssimokawa					goto extendclass;
1034106790Ssimokawa				}
1035103285Sikob			}
1036113584Ssimokawa
1037103285Sikob			if (mlp < mlist)
1038120660Ssimokawa			{
1039103285Sikob				/* total failure to match */
1040129585Sdfr				break;
1041113584Ssimokawa			}
1042103285Sikob		}
1043103285Sikob
1044113584Ssimokawa		/*
1045103285Sikob		**  See if we successfully matched
1046103285Sikob		*/
1047113584Ssimokawa
1048103285Sikob		if (mlp < mlist || *rvp != NULL)
1049103285Sikob		{
1050113584Ssimokawa			if (tTd(21, 10))
1051103285Sikob				dprintf("----- rule fails\n");
1052103285Sikob			rwr = rwr->r_next;
1053103285Sikob			ruleno++;
1054103285Sikob			loopcount = 0;
1055103285Sikob			continue;
1056103285Sikob		}
1057113584Ssimokawa
1058113584Ssimokawa		rvp = rwr->r_rhs;
1059103285Sikob		if (tTd(21, 12))
1060103285Sikob		{
1061113584Ssimokawa			dprintf("-----rule matches:");
1062113584Ssimokawa			printav(rvp);
1063103285Sikob		}
1064103285Sikob
1065103285Sikob		rp = *rvp;
1066103285Sikob		if (rp != NULL)
1067113584Ssimokawa		{
1068113584Ssimokawa			if ((*rp & 0377) == CANONUSER)
1069113584Ssimokawa			{
1070119155Ssimokawa				rvp++;
1071119155Ssimokawa				rwr = rwr->r_next;
1072119155Ssimokawa				ruleno++;
1073103285Sikob				loopcount = 0;
1074113584Ssimokawa			}
1075113584Ssimokawa			else if ((*rp & 0377) == CANONHOST)
1076103285Sikob			{
1077103285Sikob				rvp++;
1078103285Sikob				rwr = NULL;
1079103285Sikob			}
1080103285Sikob		}
1081103285Sikob
1082113584Ssimokawa		/* substitute */
1083103285Sikob		for (avp = npvp; *rvp != NULL; rvp++)
1084110577Ssimokawa		{
1085103285Sikob			register struct match *m;
1086103285Sikob			register char **pp;
1087103285Sikob
1088103285Sikob			rp = *rvp;
1089103285Sikob			if ((*rp & 0377) == MATCHREPL)
1090110577Ssimokawa			{
1091103285Sikob				/* substitute from LHS */
1092103285Sikob				m = &mlist[rp[1] - '1'];
1093103285Sikob				if (m < mlist || m >= mlp)
1094103285Sikob				{
1095103285Sikob					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
1096103285Sikob						rulename, rp[1]);
1097103285Sikob					return EX_CONFIG;
1098103285Sikob				}
1099103285Sikob				if (tTd(21, 15))
1100103285Sikob				{
1101103285Sikob					dprintf("$%c:", rp[1]);
1102103285Sikob					pp = m->match_first;
1103103285Sikob					while (pp <= m->match_last)
1104103285Sikob					{
1105103285Sikob						dprintf(" %lx=\"",
1106103285Sikob							(u_long) *pp);
1107103285Sikob						(void) dflush();
1108103285Sikob						dprintf("%s\"", *pp++);
1109103285Sikob					}
1110103285Sikob					dprintf("\n");
1111103285Sikob				}
1112103285Sikob				pp = m->match_first;
1113103285Sikob				while (pp <= m->match_last)
1114103285Sikob				{
1115110577Ssimokawa					if (avp >= &npvp[MAXATOM])
1116103285Sikob					{
1117113584Ssimokawa						syserr("554 5.3.0 rewrite: expansion too long");
1118119289Ssimokawa						return EX_DATAERR;
1119113584Ssimokawa					}
1120113584Ssimokawa					*avp++ = *pp++;
1121119289Ssimokawa				}
1122113584Ssimokawa			}
1123113584Ssimokawa			else
1124114218Ssimokawa			{
1125114218Ssimokawa				/* some sort of replacement */
1126114218Ssimokawa				if (avp >= &npvp[MAXATOM])
1127114218Ssimokawa				{
1128114218Ssimokawa	toolong:
1129114218Ssimokawa					syserr("554 5.3.0 rewrite: expansion too long");
1130114224Ssimokawa					return EX_DATAERR;
1131120660Ssimokawa				}
1132114218Ssimokawa				if ((*rp & 0377) != MACRODEXPAND)
1133114224Ssimokawa				{
1134114218Ssimokawa					/* vanilla replacement */
1135114218Ssimokawa					*avp++ = rp;
1136114218Ssimokawa				}
1137114218Ssimokawa				else
1138120660Ssimokawa				{
1139113584Ssimokawa					/* $&x replacement */
1140114218Ssimokawa					char *mval = macvalue(rp[1], e);
1141103285Sikob					char **xpvp;
1142110577Ssimokawa					int trsize = 0;
1143110577Ssimokawa					static size_t pvpb1_size = 0;
1144110577Ssimokawa					static char **pvpb1 = NULL;
1145110577Ssimokawa					char pvpbuf[PSBUFSIZE];
1146113584Ssimokawa
1147113584Ssimokawa					if (tTd(21, 2))
1148103285Sikob						dprintf("rewrite: RHS $&%s => \"%s\"\n",
1149110269Ssimokawa							macname(rp[1]),
1150103285Sikob							mval == NULL ? "(NULL)" : mval);
1151103285Sikob					if (mval == NULL || *mval == '\0')
1152103285Sikob						continue;
1153103285Sikob
1154103285Sikob					/* save the remainder of the input */
1155111956Ssimokawa					for (xpvp = pvp; *xpvp != NULL; xpvp++)
1156111956Ssimokawa						trsize += sizeof *xpvp;
1157111956Ssimokawa					if ((size_t) trsize > pvpb1_size)
1158111956Ssimokawa					{
1159111956Ssimokawa						if (pvpb1 != NULL)
1160111956Ssimokawa							free(pvpb1);
1161103285Sikob						pvpb1 = (char **)xalloc(trsize);
1162103285Sikob						pvpb1_size = trsize;
1163103285Sikob					}
1164103285Sikob
1165103285Sikob					memmove((char *) pvpb1,
1166103285Sikob						(char *) pvp,
1167103285Sikob						trsize);
1168103285Sikob
1169103285Sikob					/* scan the new replacement */
1170106790Ssimokawa					xpvp = prescan(mval, '\0', pvpbuf,
1171106790Ssimokawa						       sizeof pvpbuf, NULL,
1172106790Ssimokawa						       NULL);
1173103285Sikob					if (xpvp == NULL)
1174103285Sikob					{
1175113584Ssimokawa						/* prescan pre-printed error */
1176103285Sikob						return EX_DATAERR;
1177108527Ssimokawa					}
1178108527Ssimokawa
1179108527Ssimokawa					/* insert it into the output stream */
1180113584Ssimokawa					while (*xpvp != NULL)
1181103285Sikob					{
1182113584Ssimokawa						if (tTd(21, 19))
1183113584Ssimokawa							dprintf(" ... %s\n",
1184113584Ssimokawa								*xpvp);
1185113584Ssimokawa						*avp++ = newstr(*xpvp);
1186113584Ssimokawa						if (avp >= &npvp[MAXATOM])
1187113584Ssimokawa							goto toolong;
1188113584Ssimokawa						xpvp++;
1189103285Sikob					}
1190103285Sikob					if (tTd(21, 19))
1191103285Sikob						dprintf(" ... DONE\n");
1192113584Ssimokawa
1193110195Ssimokawa					/* restore the old trailing input */
1194103285Sikob					memmove((char *) pvp,
1195108527Ssimokawa						(char *) pvpb1,
1196103285Sikob						trsize);
1197106790Ssimokawa				}
1198106790Ssimokawa			}
1199113584Ssimokawa		}
1200103285Sikob		*avp++ = NULL;
1201103285Sikob
1202103285Sikob		/*
1203108642Ssimokawa		**  Check for any hostname/keyword lookups.
1204108642Ssimokawa		*/
1205108642Ssimokawa
1206108642Ssimokawa		for (rvp = npvp; *rvp != NULL; rvp++)
1207113584Ssimokawa		{
1208113584Ssimokawa			char **hbrvp;
1209113584Ssimokawa			char **xpvp;
1210113584Ssimokawa			int trsize;
1211113584Ssimokawa			char *replac;
1212113584Ssimokawa			int endtoken;
1213113584Ssimokawa			STAB *map;
1214113584Ssimokawa			char *mapname;
1215113584Ssimokawa			char **key_rvp;
1216113584Ssimokawa			char **arg_rvp;
1217117126Sscottl			char **default_rvp;
1218127468Ssimokawa			char cbuf[MAXNAME + 1];
1219117126Sscottl			char *pvpb1[MAXATOM + 1];
1220117228Ssimokawa			char *argvect[10];
1221117228Ssimokawa			char pvpbuf[PSBUFSIZE];
1222117228Ssimokawa			char *nullpvp[1];
1223113584Ssimokawa
1224113584Ssimokawa			if ((**rvp & 0377) != HOSTBEGIN &&
1225103285Sikob			    (**rvp & 0377) != LOOKUPBEGIN)
1226103285Sikob				continue;
1227103285Sikob
1228103285Sikob			/*
1229103285Sikob			**  Got a hostname/keyword lookup.
1230113584Ssimokawa			**
1231103285Sikob			**	This could be optimized fairly easily.
1232109379Ssimokawa			*/
1233103285Sikob
1234103285Sikob			hbrvp = rvp;
1235109379Ssimokawa			if ((**rvp & 0377) == HOSTBEGIN)
1236113584Ssimokawa			{
1237113584Ssimokawa				endtoken = HOSTEND;
1238113584Ssimokawa				mapname = "host";
1239113584Ssimokawa			}
1240113584Ssimokawa			else
1241124836Ssimokawa			{
1242103285Sikob				endtoken = LOOKUPEND;
1243103285Sikob				mapname = *++rvp;
1244103285Sikob			}
1245103285Sikob			map = stab(mapname, ST_MAP, ST_FIND);
1246103285Sikob			if (map == NULL)
1247113584Ssimokawa				syserr("554 5.3.0 rewrite: map %s not found", mapname);
1248113584Ssimokawa
1249113584Ssimokawa			/* extract the match part */
1250113584Ssimokawa			key_rvp = ++rvp;
1251113584Ssimokawa			default_rvp = NULL;
1252113584Ssimokawa			arg_rvp = argvect;
1253113584Ssimokawa			xpvp = NULL;
1254113584Ssimokawa			replac = pvpbuf;
1255113584Ssimokawa			while (*rvp != NULL && (**rvp & 0377) != endtoken)
1256113584Ssimokawa			{
1257113584Ssimokawa				int nodetype = **rvp & 0377;
1258103285Sikob
1259113584Ssimokawa				if (nodetype != CANONHOST && nodetype != CANONUSER)
1260108530Ssimokawa				{
1261108530Ssimokawa					rvp++;
1262108530Ssimokawa					continue;
1263108530Ssimokawa				}
1264108530Ssimokawa
1265108530Ssimokawa				*rvp++ = NULL;
1266103285Sikob
1267103285Sikob				if (xpvp != NULL)
1268103285Sikob				{
1269103285Sikob					cataddr(xpvp, NULL, replac,
1270103285Sikob						&pvpbuf[sizeof pvpbuf] - replac,
1271108642Ssimokawa						'\0');
1272108642Ssimokawa					*++arg_rvp = replac;
1273108642Ssimokawa					replac += strlen(replac) + 1;
1274103285Sikob					xpvp = NULL;
1275103285Sikob				}
1276108527Ssimokawa				switch (nodetype)
1277103285Sikob				{
1278106790Ssimokawa				  case CANONHOST:
1279106790Ssimokawa					xpvp = rvp;
1280106790Ssimokawa					break;
1281103285Sikob
1282103285Sikob				  case CANONUSER:
1283113584Ssimokawa					default_rvp = rvp;
1284109890Ssimokawa					break;
1285113584Ssimokawa				}
1286113584Ssimokawa			}
1287103285Sikob			if (*rvp != NULL)
1288103285Sikob				*rvp++ = NULL;
1289109890Ssimokawa			if (xpvp != NULL)
1290113584Ssimokawa			{
1291103285Sikob				cataddr(xpvp, NULL, replac,
1292103285Sikob					&pvpbuf[sizeof pvpbuf] - replac,
1293103285Sikob					'\0');
1294103285Sikob				*++arg_rvp = replac;
1295106790Ssimokawa			}
1296106790Ssimokawa			*++arg_rvp = NULL;
1297106790Ssimokawa
1298103285Sikob			/* save the remainder of the input string */
1299103285Sikob			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1300113584Ssimokawa			memmove((char *) pvpb1, (char *) rvp, trsize);
1301103285Sikob
1302103285Sikob			/* look it up */
1303103285Sikob			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
1304103285Sikob				map == NULL ? '\0' : map->s_map.map_spacesub);
1305109890Ssimokawa			argvect[0] = cbuf;
1306113584Ssimokawa			replac = map_lookup(map, cbuf, argvect, &rstat, e);
1307103285Sikob
1308103285Sikob			/* if no replacement, use default */
1309103285Sikob			if (replac == NULL && default_rvp != NULL)
1310103285Sikob			{
1311106790Ssimokawa				/* create the default */
1312113584Ssimokawa				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
1313106790Ssimokawa				replac = cbuf;
1314129585Sdfr			}
1315103285Sikob
1316113584Ssimokawa			if (replac == NULL)
1317103285Sikob			{
1318103285Sikob				xpvp = key_rvp;
1319103285Sikob			}
1320103285Sikob			else if (*replac == '\0')
1321106790Ssimokawa			{
1322106790Ssimokawa				/* null replacement */
1323103285Sikob				nullpvp[0] = NULL;
1324103285Sikob				xpvp = nullpvp;
1325113584Ssimokawa			}
1326129585Sdfr			else
1327103285Sikob			{
1328120660Ssimokawa				/* scan the new replacement */
1329103285Sikob				xpvp = prescan(replac, '\0', pvpbuf,
1330103285Sikob					       sizeof pvpbuf, NULL, NULL);
1331103285Sikob				if (xpvp == NULL)
1332103285Sikob				{
1333103285Sikob					/* prescan already printed error */
1334103285Sikob					return EX_DATAERR;
1335103285Sikob				}
1336103285Sikob			}
1337103285Sikob
1338103285Sikob			/* append it to the token list */
1339103285Sikob			for (avp = hbrvp; *xpvp != NULL; xpvp++)
1340103285Sikob			{
1341123740Speter				*avp++ = newstr(*xpvp);
1342103285Sikob				if (avp >= &npvp[MAXATOM])
1343103285Sikob					goto toolong;
1344103285Sikob			}
1345103285Sikob
1346103285Sikob			/* restore the old trailing information */
1347103285Sikob			rvp = avp - 1;
1348103285Sikob			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1349103285Sikob				if (avp >= &npvp[MAXATOM])
1350103285Sikob					goto toolong;
1351103285Sikob		}
1352113584Ssimokawa
1353113584Ssimokawa		/*
1354103285Sikob		**  Check for subroutine calls.
1355103285Sikob		*/
1356103285Sikob
1357109892Ssimokawa		status = callsubr(npvp, reclevel, e);
1358113584Ssimokawa		if (rstat == EX_OK || status == EX_TEMPFAIL)
1359113584Ssimokawa			rstat = status;
1360113584Ssimokawa
1361113584Ssimokawa		/* copy vector back into original space. */
1362103285Sikob		for (avp = npvp; *avp++ != NULL;)
1363103285Sikob			continue;
1364113584Ssimokawa		memmove((char *) pvp, (char *) npvp,
1365113584Ssimokawa		      (int) (avp - npvp) * sizeof *avp);
1366113584Ssimokawa
1367109280Ssimokawa		if (tTd(21, 4))
1368113584Ssimokawa		{
1369113584Ssimokawa			dprintf("rewritten as:");
1370113584Ssimokawa			printav(pvp);
1371103285Sikob		}
1372103285Sikob	}
1373103285Sikob
1374103285Sikob	if (OpMode == MD_TEST)
1375113584Ssimokawa	{
1376113584Ssimokawa		printf("%s%-16.16s returns:", prefix, rulename);
1377103285Sikob		printav(pvp);
1378103285Sikob	}
1379106790Ssimokawa	else if (tTd(21, 1))
1380106790Ssimokawa	{
1381106790Ssimokawa		dprintf("%s%-16.16s returns:", prefix, rulename);
1382103285Sikob		printav(pvp);
1383103285Sikob	}
1384109892Ssimokawa	return rstat;
1385129585Sdfr}
1386103285Sikob/*
1387120660Ssimokawa**  CALLSUBR -- call subroutines in rewrite vector
1388103285Sikob**
1389103285Sikob**	Parameters:
1390103285Sikob**		pvp -- pointer to token vector.
1391103285Sikob**		reclevel -- the current recursion level.
1392103285Sikob**		e -- the current envelope.
1393103285Sikob**
1394103285Sikob**	Returns:
1395103285Sikob**		The status from the subroutine call.
1396103285Sikob**
1397103285Sikob**	Side Effects:
1398103285Sikob**		pvp is modified.
1399103285Sikob*/
1400103285Sikob
1401103285Sikobstatic int
1402123740Spetercallsubr(pvp, reclevel, e)
1403103285Sikob	char **pvp;
1404103285Sikob	int reclevel;
1405103285Sikob	ENVELOPE *e;
1406103285Sikob{
1407103285Sikob	char **avp;
1408103285Sikob	char **rvp;
1409103285Sikob	register int i;
1410103285Sikob	int subr;
1411103285Sikob	int status;
1412103285Sikob	int rstat = EX_OK;
1413103285Sikob	char *tpvp[MAXATOM + 1];
1414103285Sikob
1415103285Sikob	for (avp = pvp; *avp != NULL; avp++)
1416108642Ssimokawa	{
1417103285Sikob		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
1418103285Sikob		{
1419103285Sikob			stripquotes(avp[1]);
1420103285Sikob			subr = strtorwset(avp[1], NULL, ST_FIND);
1421113584Ssimokawa			if (subr < 0)
1422113584Ssimokawa			{
1423113584Ssimokawa				syserr("Unknown ruleset %s", avp[1]);
1424103285Sikob				return EX_CONFIG;
1425109892Ssimokawa			}
1426109892Ssimokawa
1427113584Ssimokawa			if (tTd(21, 3))
1428113584Ssimokawa				dprintf("-----callsubr %s (%d)\n",
1429103285Sikob					avp[1], subr);
1430103285Sikob
1431113584Ssimokawa			/*
1432113584Ssimokawa			**  Take care of possible inner calls first.
1433113584Ssimokawa			**  use a full size temporary buffer to avoid
1434113584Ssimokawa			**  overflows in rewrite, but strip off the
1435113584Ssimokawa			**  subroutine call.
1436113584Ssimokawa			*/
1437103285Sikob
1438103285Sikob			for (i = 2; avp[i] != NULL; i++)
1439103285Sikob				tpvp[i - 2] = avp[i];
1440103285Sikob			tpvp[i - 2] = NULL;
1441113584Ssimokawa
1442113584Ssimokawa			status = callsubr(tpvp, reclevel, e);
1443103285Sikob			if (rstat == EX_OK || status == EX_TEMPFAIL)
1444113584Ssimokawa				rstat = status;
1445113584Ssimokawa
1446103285Sikob			/*
1447103285Sikob			**  Now we need to call the ruleset specified for
1448103285Sikob			**  the subroutine. we can do this with the
1449113584Ssimokawa			**  temporary buffer that we set up earlier,
1450103285Sikob			**  since it has all the data we want to rewrite.
1451103285Sikob			*/
1452103285Sikob
1453103285Sikob			status = rewrite(tpvp, subr, reclevel, e);
1454106790Ssimokawa			if (rstat == EX_OK || status == EX_TEMPFAIL)
1455106790Ssimokawa				rstat = status;
1456113584Ssimokawa
1457109890Ssimokawa			/*
1458109890Ssimokawa			**  Find length of tpvp and current offset into
1459109890Ssimokawa			**  pvp, if the total is greater than MAXATOM,
1460109890Ssimokawa			**  then it would overflow the buffer if we copied
1461109890Ssimokawa			**  it back in to pvp, in which case we throw a
1462109890Ssimokawa			**  fit.
1463113584Ssimokawa			*/
1464109890Ssimokawa
1465113584Ssimokawa			for (rvp = tpvp; *rvp != NULL; rvp++)
1466113584Ssimokawa				continue;
1467113584Ssimokawa			if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
1468109890Ssimokawa			{
1469109890Ssimokawa				syserr("554 5.3.0 callsubr: expansion too long");
1470109890Ssimokawa				return EX_DATAERR;
1471109890Ssimokawa			}
1472109890Ssimokawa
1473113584Ssimokawa			/*
1474109890Ssimokawa			**  Now we can copy the rewritten code over
1475109890Ssimokawa			**  the initial subroutine call in the buffer.
1476109890Ssimokawa			*/
1477109890Ssimokawa
1478109890Ssimokawa			for (i = 0; tpvp[i] != NULL; i++)
1479109890Ssimokawa				avp[i] = tpvp[i];
1480109890Ssimokawa			avp[i] = NULL;
1481109890Ssimokawa
1482109890Ssimokawa			/*
1483109890Ssimokawa			**  If we got this far, we've processed the left
1484109890Ssimokawa			**  most subroutine, and recursively called ourselves
1485109890Ssimokawa			**  to handle any other subroutines.  We're done.
1486109890Ssimokawa			*/
1487106790Ssimokawa
1488103285Sikob			break;
1489103285Sikob		}
1490103285Sikob	}
1491103285Sikob	return rstat;
1492103285Sikob}
1493109890Ssimokawa/*
1494129585Sdfr**  MAP_LOOKUP -- do lookup in map
1495109890Ssimokawa**
1496109890Ssimokawa**	Parameters:
1497103285Sikob**		map -- the map to use for the lookup.
1498103285Sikob**		key -- the key to look up.
1499109890Ssimokawa**		argvect -- arguments to pass to the map lookup.
1500109890Ssimokawa**		pstat -- a pointer to an integer in which to store the
1501109890Ssimokawa**			status from the lookup.
1502109890Ssimokawa**		e -- the current envelope.
1503109179Ssimokawa**
1504109890Ssimokawa**	Returns:
1505103285Sikob**		The result of the lookup.
1506113584Ssimokawa**		NULL -- if there was no data for the given key.
1507109179Ssimokawa*/
1508109179Ssimokawa
1509103285Sikobstatic char *
1510103285Sikobmap_lookup(smap, key, argvect, pstat, e)
1511103285Sikob	STAB *smap;
1512103285Sikob	char key[];
1513109890Ssimokawa	char **argvect;
1514109892Ssimokawa	int *pstat;
1515109890Ssimokawa	ENVELOPE *e;
1516109890Ssimokawa{
1517109890Ssimokawa	auto int status = EX_OK;
1518120660Ssimokawa	MAP *map;
1519109890Ssimokawa	char *replac;
1520113584Ssimokawa
1521113584Ssimokawa	if (smap == NULL)
1522109890Ssimokawa		return NULL;
1523109890Ssimokawa
1524109890Ssimokawa	map = &smap->s_map;
1525113584Ssimokawa	DYNOPENMAP(map);
1526113584Ssimokawa
1527113584Ssimokawa	if (e->e_sendmode == SM_DEFER &&
1528113584Ssimokawa	    bitset(MF_DEFER, map->map_mflags))
1529109892Ssimokawa	{
1530109890Ssimokawa		/* don't do any map lookups */
1531113584Ssimokawa		if (tTd(60, 1))
1532113584Ssimokawa			dprintf("map_lookup(%s, %s) => DEFERRED\n",
1533109892Ssimokawa				smap->s_name, key);
1534113584Ssimokawa		*pstat = EX_TEMPFAIL;
1535113584Ssimokawa		return NULL;
1536109892Ssimokawa	}
1537103285Sikob
1538109890Ssimokawa	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
1539109890Ssimokawa		stripquotes(key);
1540109890Ssimokawa
1541109403Ssimokawa	if (tTd(60, 1))
1542113584Ssimokawa	{
1543113584Ssimokawa		dprintf("map_lookup(%s, %s", smap->s_name, key);
1544109890Ssimokawa		if (tTd(60, 5))
1545109890Ssimokawa		{
1546113584Ssimokawa			int i;
1547113584Ssimokawa
1548113584Ssimokawa			for (i = 0; argvect[i] != NULL; i++)
1549109890Ssimokawa				dprintf(", %%%d=%s", i, argvect[i]);
1550109890Ssimokawa		}
1551109890Ssimokawa		dprintf(") => ");
1552113584Ssimokawa	}
1553109890Ssimokawa	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
1554113584Ssimokawa	if (tTd(60, 1))
1555109403Ssimokawa		dprintf("%s (%d)\n",
1556109403Ssimokawa			replac != NULL ? replac : "NOT FOUND",
1557109403Ssimokawa			status);
1558113584Ssimokawa
1559109890Ssimokawa	/* should recover if status == EX_TEMPFAIL */
1560109890Ssimokawa	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
1561113584Ssimokawa	{
1562113584Ssimokawa		*pstat = EX_TEMPFAIL;
1563113584Ssimokawa		if (tTd(60, 1))
1564109890Ssimokawa			dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
1565113584Ssimokawa				smap->s_name, key, errno);
1566113584Ssimokawa		if (e->e_message == NULL)
1567113584Ssimokawa		{
1568113584Ssimokawa			char mbuf[320];
1569109403Ssimokawa
1570109890Ssimokawa			snprintf(mbuf, sizeof mbuf,
1571109890Ssimokawa				"%.80s map: lookup (%s): deferred",
1572109890Ssimokawa				smap->s_name,
1573109890Ssimokawa				shortenstring(key, MAXSHORTSTR));
1574109890Ssimokawa			e->e_message = newstr(mbuf);
1575113584Ssimokawa		}
1576109890Ssimokawa	}
1577109890Ssimokawa	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
1578109890Ssimokawa	{
1579109356Ssimokawa		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
1580109356Ssimokawa		static char *rwbuf = NULL;
1581113584Ssimokawa		static size_t rwbuflen = 0;
1582109890Ssimokawa
1583109356Ssimokawa		if (i > rwbuflen)
1584109356Ssimokawa		{
1585109356Ssimokawa			if (rwbuf != NULL)
1586113584Ssimokawa				free(rwbuf);
1587113584Ssimokawa			rwbuflen = i;
1588113584Ssimokawa			rwbuf = (char *) xalloc(rwbuflen);
1589113584Ssimokawa		}
1590109403Ssimokawa		snprintf(rwbuf, rwbuflen, "%s%s", key, map->map_tapp);
1591109403Ssimokawa		if (tTd(60, 4))
1592113584Ssimokawa			dprintf("map_lookup tempfail: returning \"%s\"\n",
1593113584Ssimokawa				rwbuf);
1594113584Ssimokawa		return rwbuf;
1595109403Ssimokawa	}
1596109890Ssimokawa	return replac;
1597109890Ssimokawa}
1598113584Ssimokawa/*
1599103285Sikob**  INITERRMAILERS -- initialize error and discard mailers
1600109890Ssimokawa**
1601103285Sikob**	Parameters:
1602103285Sikob**		none.
1603106790Ssimokawa**
1604106790Ssimokawa**	Returns:
1605113584Ssimokawa**		none.
1606103285Sikob**
1607103285Sikob**	Side Effects:
1608109890Ssimokawa**		initializes error and discard mailers.
1609103285Sikob*/
1610129585Sdfr
1611109890Ssimokawastatic MAILER discardmailer;
1612113584Ssimokawastatic MAILER errormailer;
1613109890Ssimokawastatic char *discardargv[] = { "DISCARD", NULL };
1614109890Ssimokawastatic char *errorargv[] = { "ERROR", NULL };
1615103285Sikob
1616109890Ssimokawavoid
1617109890Ssimokawainiterrmailers()
1618109890Ssimokawa{
1619109890Ssimokawa	if (discardmailer.m_name == NULL)
1620109890Ssimokawa	{
1621109890Ssimokawa		/* initialize the discard mailer */
1622108995Ssimokawa		discardmailer.m_name = "*discard*";
1623108995Ssimokawa		discardmailer.m_mailer = "DISCARD";
1624109890Ssimokawa		discardmailer.m_argv = discardargv;
1625109890Ssimokawa	}
1626109890Ssimokawa	if (errormailer.m_name == NULL)
1627113584Ssimokawa	{
1628109890Ssimokawa		/* initialize the bogus mailer */
1629109179Ssimokawa		errormailer.m_name = "*error*";
1630109890Ssimokawa		errormailer.m_mailer = "ERROR";
1631103285Sikob		errormailer.m_argv = errorargv;
1632103285Sikob	}
1633103285Sikob}
1634103285Sikob/*
1635109890Ssimokawa**  BUILDADDR -- build address from token vector.
1636109890Ssimokawa**
1637109890Ssimokawa**	Parameters:
1638109890Ssimokawa**		tv -- token vector.
1639109890Ssimokawa**		a -- pointer to address descriptor to fill.
1640109890Ssimokawa**			If NULL, one will be allocated.
1641111892Ssimokawa**		flags -- info regarding whether this is a sender or
1642111892Ssimokawa**			a recipient.
1643109890Ssimokawa**		e -- the current envelope.
1644109890Ssimokawa**
1645120660Ssimokawa**	Returns:
1646109890Ssimokawa**		NULL if there was an error.
1647111942Ssimokawa**		'a' otherwise.
1648113584Ssimokawa**
1649113584Ssimokawa**	Side Effects:
1650113584Ssimokawa**		fills in 'a'
1651113584Ssimokawa*/
1652113584Ssimokawa
1653113584Ssimokawastatic struct errcodes
1654113584Ssimokawa{
1655113584Ssimokawa	char	*ec_name;		/* name of error code */
1656113584Ssimokawa	int	ec_code;		/* numeric code */
1657113584Ssimokawa} ErrorCodes[] =
1658111942Ssimokawa{
1659109890Ssimokawa	{ "usage",		EX_USAGE	},
1660113584Ssimokawa	{ "nouser",		EX_NOUSER	},
1661113584Ssimokawa	{ "nohost",		EX_NOHOST	},
1662109890Ssimokawa	{ "unavailable",	EX_UNAVAILABLE	},
1663109890Ssimokawa	{ "software",		EX_SOFTWARE	},
1664113584Ssimokawa	{ "tempfail",		EX_TEMPFAIL	},
1665103285Sikob	{ "protocol",		EX_PROTOCOL	},
1666109890Ssimokawa#ifdef EX_CONFIG
1667109890Ssimokawa	{ "config",		EX_CONFIG	},
1668109890Ssimokawa#endif /* EX_CONFIG */
1669103285Sikob	{ NULL,			EX_UNAVAILABLE	}
1670113584Ssimokawa};
1671113584Ssimokawa
1672109890Ssimokawa
1673109890Ssimokawastatic ADDRESS *
1674109890Ssimokawabuildaddr(tv, a, flags, e)
1675109890Ssimokawa	register char **tv;
1676109890Ssimokawa	register ADDRESS *a;
1677109890Ssimokawa	int flags;
1678109890Ssimokawa	register ENVELOPE *e;
1679109890Ssimokawa{
1680109890Ssimokawa	struct mailer **mp;
1681113584Ssimokawa	register struct mailer *m;
1682113584Ssimokawa	register char *p;
1683109890Ssimokawa	char *mname;
1684109890Ssimokawa	char **hostp;
1685109890Ssimokawa	char hbuf[MAXNAME + 1];
1686109890Ssimokawa	static char ubuf[MAXNAME + 2];
1687109890Ssimokawa
1688109890Ssimokawa	if (tTd(24, 5))
1689113584Ssimokawa	{
1690109890Ssimokawa		dprintf("buildaddr, flags=%x, tv=", flags);
1691109890Ssimokawa		printav(tv);
1692109890Ssimokawa	}
1693113584Ssimokawa
1694113584Ssimokawa	if (a == NULL)
1695113584Ssimokawa		a = (ADDRESS *) xalloc(sizeof *a);
1696103285Sikob	memset((char *) a, '\0', sizeof *a);
1697103285Sikob	hbuf[0] = '\0';
1698106790Ssimokawa
1699106790Ssimokawa	/* set up default error return flags */
1700110145Ssimokawa	a->q_flags |= DefaultNotify;
1701103285Sikob
1702103285Sikob	/* figure out what net/mailer to use */
1703103285Sikob	if (*tv == NULL || (**tv & 0377) != CANONNET)
1704103285Sikob	{
1705103285Sikob		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
1706103285Sikobbadaddr:
1707103285Sikob		if (ExitStat == EX_TEMPFAIL)
1708103285Sikob			a->q_state = QS_QUEUEUP;
1709103285Sikob		else
1710103285Sikob		{
1711103285Sikob			a->q_state = QS_BADADDR;
1712103285Sikob			a->q_mailer = &errormailer;
1713103285Sikob		}
1714103285Sikob		return a;
1715103285Sikob	}
1716103285Sikob	mname = *++tv;
1717103285Sikob
1718103285Sikob	/* extract host and user portions */
1719103285Sikob	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1720103285Sikob		hostp = ++tv;
1721103285Sikob	else
1722103285Sikob		hostp = NULL;
1723103285Sikob	while (*tv != NULL && (**tv & 0377) != CANONUSER)
1724103285Sikob		tv++;
1725103285Sikob	if (*tv == NULL)
1726116978Ssimokawa	{
1727118416Ssimokawa		syserr("554 5.3.5 buildaddr: no user");
1728118416Ssimokawa		goto badaddr;
1729116978Ssimokawa	}
1730108642Ssimokawa	if (tv == hostp)
1731103285Sikob		hostp = NULL;
1732103285Sikob	else if (hostp != NULL)
1733103285Sikob		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1734108642Ssimokawa	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1735108642Ssimokawa
1736108642Ssimokawa	/* save away the host name */
1737108642Ssimokawa	if (strcasecmp(mname, "error") == 0)
1738116978Ssimokawa	{
1739116978Ssimokawa		/* Set up triplet for use by -bv */
1740108642Ssimokawa		a->q_mailer = &errormailer;
1741108642Ssimokawa		a->q_user = newstr(ubuf);
1742129541Sdfr
1743108642Ssimokawa		if (hostp != NULL)
1744116978Ssimokawa		{
1745116978Ssimokawa			register struct errcodes *ep;
1746108642Ssimokawa
1747108642Ssimokawa			a->q_host = newstr(hbuf);
1748116978Ssimokawa			if (strchr(hbuf, '.') != NULL)
1749116978Ssimokawa			{
1750116978Ssimokawa				a->q_status = newstr(hbuf);
1751116978Ssimokawa				setstat(dsntoexitstat(hbuf));
1752116978Ssimokawa			}
1753116978Ssimokawa			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
1754108642Ssimokawa			{
1755108642Ssimokawa				setstat(atoi(hbuf));
1756108642Ssimokawa			}
1757108642Ssimokawa			else
1758108642Ssimokawa			{
1759108642Ssimokawa				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1760108642Ssimokawa					if (strcasecmp(ep->ec_name, hbuf) == 0)
1761108642Ssimokawa						break;
1762108642Ssimokawa				setstat(ep->ec_code);
1763103285Sikob			}
1764103285Sikob		}
1765129585Sdfr		else
1766103285Sikob		{
1767129585Sdfr			a->q_host = NULL;
1768103285Sikob			setstat(EX_UNAVAILABLE);
1769103285Sikob		}
1770103285Sikob		stripquotes(ubuf);
1771103285Sikob		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
1772103285Sikob		{
1773103285Sikob			char fmt[16];
1774103285Sikob			int off;
1775103285Sikob
1776103285Sikob			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
1777103285Sikob			{
1778103285Sikob				ubuf[off + 4] = '\0';
1779103285Sikob				off += 5;
1780103285Sikob			}
1781103285Sikob			else
1782103285Sikob			{
1783103285Sikob				off = 4;
1784103285Sikob				ubuf[3] = '\0';
1785103285Sikob			}
1786103285Sikob			(void) snprintf(fmt, sizeof fmt, "%s %%s", ubuf);
1787103285Sikob			if (off > 4)
1788103285Sikob				usrerr(fmt, ubuf + off);
1789103285Sikob			else if (isenhsc(hbuf, '\0') > 0)
1790103285Sikob				usrerrenh(hbuf, fmt, ubuf + off);
1791103285Sikob			else
1792103285Sikob				usrerr(fmt, ubuf + off);
1793103285Sikob			/* XXX ubuf[off - 1] = ' '; */
1794103285Sikob		}
1795103285Sikob		else
1796103285Sikob		{
1797103285Sikob			usrerr("553 5.3.0 %s", ubuf);
1798103285Sikob		}
1799103285Sikob		goto badaddr;
1800111074Ssimokawa	}
1801111074Ssimokawa
1802111074Ssimokawa	for (mp = Mailer; (m = *mp++) != NULL; )
1803111074Ssimokawa	{
1804111074Ssimokawa		if (strcasecmp(m->m_name, mname) == 0)
1805103285Sikob			break;
1806103285Sikob	}
1807103285Sikob	if (m == NULL)
1808103285Sikob	{
1809103285Sikob		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
1810103285Sikob		goto badaddr;
1811103285Sikob	}
1812103285Sikob	a->q_mailer = m;
1813103285Sikob
1814103285Sikob	/* figure out what host (if any) */
1815103285Sikob	if (hostp == NULL)
1816103285Sikob	{
1817110798Ssimokawa		if (!bitnset(M_LOCALMAILER, m->m_flags))
1818116376Ssimokawa		{
1819116376Ssimokawa			syserr("554 5.3.5 buildaddr: no host");
1820103285Sikob			goto badaddr;
1821111074Ssimokawa		}
1822103285Sikob		a->q_host = NULL;
1823103285Sikob	}
1824103285Sikob	else
1825103285Sikob		a->q_host = newstr(hbuf);
1826127468Ssimokawa
1827113584Ssimokawa	/* figure out the user */
1828113584Ssimokawa	p = ubuf;
1829127468Ssimokawa	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
1830127468Ssimokawa	{
1831113584Ssimokawa		p++;
1832103285Sikob		tv++;
1833109644Ssimokawa		a->q_flags |= QNOTREMOTE;
1834109644Ssimokawa	}
1835103285Sikob
1836109644Ssimokawa	/* do special mapping for local mailer */
1837109644Ssimokawa	if (*p == '"')
1838109644Ssimokawa		p++;
1839109644Ssimokawa	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1840109644Ssimokawa		a->q_mailer = m = ProgMailer;
1841109644Ssimokawa	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1842113584Ssimokawa		a->q_mailer = m = FileMailer;
1843103285Sikob	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
1844103285Sikob	{
1845103285Sikob		/* may be :include: */
1846103285Sikob		stripquotes(ubuf);
1847103285Sikob		if (strncasecmp(ubuf, ":include:", 9) == 0)
1848103285Sikob		{
1849103285Sikob			/* if :include:, don't need further rewriting */
1850127468Ssimokawa			a->q_mailer = m = InclMailer;
1851113584Ssimokawa			a->q_user = newstr(&ubuf[9]);
1852113584Ssimokawa			return a;
1853127468Ssimokawa		}
1854127468Ssimokawa	}
1855113584Ssimokawa
1856103285Sikob	/* rewrite according recipient mailer rewriting rules */
1857103285Sikob	define('h', a->q_host, e);
1858103285Sikob
1859103285Sikob#if _FFR_ADDR_TYPE
1860103285Sikob	/*
1861103285Sikob	**  Note, change the 9 to a 10 before removing #if FFR check
1862103285Sikob	**  in a future version.
1863103285Sikob	*/
1864103285Sikob
1865103285Sikob	if (ConfigLevel >= 9 ||
1866103285Sikob	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1867103285Sikob#else /* _FFR_ADDR_TYPE */
1868103285Sikob	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1869103285Sikob#endif /* _FFR_ADDR_TYPE */
1870106789Ssimokawa	{
1871103285Sikob		/* sender addresses done later */
1872103285Sikob		(void) rewrite(tv, 2, 0, e);
1873103285Sikob		if (m->m_re_rwset > 0)
1874103285Sikob		       (void) rewrite(tv, m->m_re_rwset, 0, e);
1875103285Sikob	}
1876103285Sikob	(void) rewrite(tv, 4, 0, e);
1877103285Sikob
1878103285Sikob	/* save the result for the command line/RCPT argument */
1879103285Sikob	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
1880106789Ssimokawa	a->q_user = newstr(ubuf);
1881103285Sikob
1882103285Sikob	/*
1883129585Sdfr	**  Do mapping to lower case as requested by mailer
1884103285Sikob	*/
1885103285Sikob
1886103285Sikob	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1887103285Sikob		makelower(a->q_host);
1888103285Sikob	if (!bitnset(M_USR_UPPER, m->m_flags))
1889111074Ssimokawa		makelower(a->q_user);
1890111074Ssimokawa
1891111787Ssimokawa	if (tTd(24, 6))
1892111787Ssimokawa	{
1893111787Ssimokawa		dprintf("buildaddr => ");
1894111787Ssimokawa		printaddr(a, FALSE);
1895111787Ssimokawa	}
1896111787Ssimokawa	return a;
1897112523Ssimokawa}
1898112523Ssimokawa/*
1899103285Sikob**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1900103285Sikob**
1901103285Sikob**	Parameters:
1902103285Sikob**		pvp -- parameter vector to rebuild.
1903113584Ssimokawa**		evp -- last parameter to include.  Can be NULL to
1904113584Ssimokawa**			use entire pvp.
1905113584Ssimokawa**		buf -- buffer to build the string into.
1906113584Ssimokawa**		sz -- size of buf.
1907113584Ssimokawa**		spacesub -- the space separator character; if null,
1908113584Ssimokawa**			use SpaceSub.
1909103285Sikob**
1910103285Sikob**	Returns:
1911103285Sikob**		none.
1912113584Ssimokawa**
1913103285Sikob**	Side Effects:
1914103285Sikob**		Destroys buf.
1915103285Sikob*/
1916113584Ssimokawa
1917103285Sikobvoid
1918103285Sikobcataddr(pvp, evp, buf, sz, spacesub)
1919103285Sikob	char **pvp;
1920103285Sikob	char **evp;
1921113584Ssimokawa	char *buf;
1922103285Sikob	register int sz;
1923113584Ssimokawa	int spacesub;
1924113584Ssimokawa{
1925113584Ssimokawa	bool oatomtok = FALSE;
1926113584Ssimokawa	bool natomtok = FALSE;
1927113584Ssimokawa	register int i;
1928109736Ssimokawa	register char *p;
1929109736Ssimokawa
1930109736Ssimokawa	if (sz <= 0)
1931109736Ssimokawa		return;
1932103285Sikob
1933129585Sdfr	if (spacesub == '\0')
1934113584Ssimokawa		spacesub = SpaceSub;
1935113584Ssimokawa
1936113584Ssimokawa	if (pvp == NULL)
1937113584Ssimokawa	{
1938113584Ssimokawa		*buf = '\0';
1939113584Ssimokawa		return;
1940127468Ssimokawa	}
1941110269Ssimokawa	p = buf;
1942110269Ssimokawa	sz -= 2;
1943110269Ssimokawa	while (*pvp != NULL && (i = strlen(*pvp)) < sz - 1)
1944110269Ssimokawa	{
1945110269Ssimokawa		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
1946110798Ssimokawa		if (oatomtok && natomtok)
1947110269Ssimokawa		{
1948113584Ssimokawa			*p++ = spacesub;
1949113584Ssimokawa			--sz;
1950103285Sikob		}
1951103285Sikob		(void) strlcpy(p, *pvp, sz);
1952103285Sikob		oatomtok = natomtok;
1953103285Sikob		p += i;
1954103285Sikob		sz -= i;
1955103285Sikob		if (pvp++ == evp)
1956103285Sikob			break;
1957103285Sikob	}
1958103285Sikob	*p = '\0';
1959103285Sikob}
1960103285Sikob/*
1961103285Sikob**  SAMEADDR -- Determine if two addresses are the same
1962103285Sikob**
1963103285Sikob**	This is not just a straight comparison -- if the mailer doesn't
1964103285Sikob**	care about the host we just ignore it, etc.
1965103285Sikob**
1966103285Sikob**	Parameters:
1967103285Sikob**		a, b -- pointers to the internal forms to compare.
1968103285Sikob**
1969103285Sikob**	Returns:
1970103285Sikob**		TRUE -- they represent the same mailbox.
1971103285Sikob**		FALSE -- they don't.
1972103285Sikob**
1973103285Sikob**	Side Effects:
1974103285Sikob**		none.
1975103285Sikob*/
1976103285Sikob
1977103285Sikobbool
1978103285Sikobsameaddr(a, b)
1979103285Sikob	register ADDRESS *a;
1980103285Sikob	register ADDRESS *b;
1981103285Sikob{
1982103285Sikob	register ADDRESS *ca, *cb;
1983103285Sikob
1984103285Sikob	/* if they don't have the same mailer, forget it */
1985103285Sikob	if (a->q_mailer != b->q_mailer)
1986113584Ssimokawa		return FALSE;
1987113584Ssimokawa
1988113584Ssimokawa	/* if the user isn't the same, we can drop out */
1989113584Ssimokawa	if (strcmp(a->q_user, b->q_user) != 0)
1990113584Ssimokawa		return FALSE;
1991129585Sdfr
1992113584Ssimokawa	/* if we have good uids for both but they differ, these are different */
1993113584Ssimokawa	if (a->q_mailer == ProgMailer)
1994113584Ssimokawa	{
1995113584Ssimokawa		ca = getctladdr(a);
1996113584Ssimokawa		cb = getctladdr(b);
1997113584Ssimokawa		if (ca != NULL && cb != NULL &&
1998113584Ssimokawa		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1999113584Ssimokawa		    ca->q_uid != cb->q_uid)
2000113584Ssimokawa			return FALSE;
2001113584Ssimokawa	}
2002113584Ssimokawa
2003129585Sdfr	/* otherwise compare hosts (but be careful for NULL ptrs) */
2004113584Ssimokawa	if (a->q_host == b->q_host)
2005113584Ssimokawa	{
2006129585Sdfr		/* probably both null pointers */
2007113584Ssimokawa		return TRUE;
2008113584Ssimokawa	}
2009113584Ssimokawa	if (a->q_host == NULL || b->q_host == NULL)
2010113584Ssimokawa	{
2011113584Ssimokawa		/* only one is a null pointer */
2012113584Ssimokawa		return FALSE;
2013113584Ssimokawa	}
2014113584Ssimokawa	if (strcmp(a->q_host, b->q_host) != 0)
2015113584Ssimokawa		return FALSE;
2016113584Ssimokawa
2017113584Ssimokawa	return TRUE;
2018113584Ssimokawa}
2019113584Ssimokawa/*
2020113584Ssimokawa**  PRINTADDR -- print address (for debugging)
2021113584Ssimokawa**
2022113584Ssimokawa**	Parameters:
2023113584Ssimokawa**		a -- the address to print
2024113584Ssimokawa**		follow -- follow the q_next chain.
2025113584Ssimokawa**
2026113584Ssimokawa**	Returns:
2027113584Ssimokawa**		none.
2028113584Ssimokawa**
2029113584Ssimokawa**	Side Effects:
2030113584Ssimokawa**		none.
2031103285Sikob*/
2032103285Sikob
2033103285Sikobstruct qflags
2034103285Sikob{
2035129585Sdfr	char	*qf_name;
2036113584Ssimokawa	u_long	qf_bit;
2037129585Sdfr};
2038113584Ssimokawa
2039103285Sikobstatic struct qflags	AddressFlags[] =
2040103285Sikob{
2041103285Sikob	{ "QGOODUID",		QGOODUID	},
2042103285Sikob	{ "QPRIMARY",		QPRIMARY	},
2043103285Sikob	{ "QNOTREMOTE",		QNOTREMOTE	},
2044103285Sikob	{ "QSELFREF",		QSELFREF	},
2045113584Ssimokawa	{ "QBOGUSSHELL",	QBOGUSSHELL	},
2046113584Ssimokawa	{ "QUNSAFEADDR",	QUNSAFEADDR	},
2047103285Sikob	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
2048113584Ssimokawa	{ "QPINGONFAILURE",	QPINGONFAILURE	},
2049113584Ssimokawa	{ "QPINGONDELAY",	QPINGONDELAY	},
2050113584Ssimokawa	{ "QHASNOTIFY",		QHASNOTIFY	},
2051113584Ssimokawa	{ "QRELAYED",		QRELAYED	},
2052113584Ssimokawa	{ "QEXPANDED",		QEXPANDED	},
2053113584Ssimokawa	{ "QDELIVERED",		QDELIVERED	},
2054113584Ssimokawa	{ "QDELAYED",		QDELAYED	},
2055113584Ssimokawa	{ "QTHISPASS",		QTHISPASS	},
2056113584Ssimokawa	{ "QRCPTOK",		QRCPTOK		},
2057113584Ssimokawa	{ NULL,			0		}
2058113584Ssimokawa};
2059113584Ssimokawa
2060113584Ssimokawavoid
2061113584Ssimokawaprintaddr(a, follow)
2062113584Ssimokawa	register ADDRESS *a;
2063113584Ssimokawa	bool follow;
2064103285Sikob{
2065103285Sikob	register MAILER *m;
2066116897Ssimokawa	MAILER pseudomailer;
2067103285Sikob	register struct qflags *qfp;
2068103285Sikob	bool firstone;
2069103285Sikob
2070129585Sdfr	if (a == NULL)
2071103285Sikob	{
2072103285Sikob		printf("[NULL]\n");
2073103285Sikob		return;
2074103285Sikob	}
2075103285Sikob
2076103285Sikob	while (a != NULL)
2077103285Sikob	{
2078103285Sikob		printf("%lx=", (u_long) a);
2079103285Sikob		(void) fflush(stdout);
2080103285Sikob
2081103285Sikob		/* find the mailer -- carefully */
2082103285Sikob		m = a->q_mailer;
2083113584Ssimokawa		if (m == NULL)
2084113584Ssimokawa		{
2085103285Sikob			m = &pseudomailer;
2086103285Sikob			m->m_mno = -1;
2087103285Sikob			m->m_name = "NULL";
2088106789Ssimokawa		}
2089103285Sikob
2090103285Sikob		printf("%s:\n\tmailer %d (%s), host `%s'\n",
2091103285Sikob		       a->q_paddr == NULL ? "<null>" : a->q_paddr,
2092103285Sikob		       m->m_mno, m->m_name,
2093103285Sikob		       a->q_host == NULL ? "<null>" : a->q_host);
2094103285Sikob		printf("\tuser `%s', ruser `%s'\n",
2095103285Sikob		       a->q_user,
2096103285Sikob		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
2097103285Sikob		printf("\tstate=");
2098107653Ssimokawa		switch (a->q_state)
2099108642Ssimokawa		{
2100103285Sikob		  case QS_OK:
2101103285Sikob			printf("OK");
2102103285Sikob			break;
2103103285Sikob
2104103285Sikob		  case QS_DONTSEND:
2105103285Sikob			printf("DONTSEND");
2106103285Sikob			break;
2107103285Sikob
2108103285Sikob		  case QS_BADADDR:
2109106790Ssimokawa			printf("BADADDR");
2110106790Ssimokawa			break;
2111103285Sikob
2112103285Sikob		  case QS_QUEUEUP:
2113120660Ssimokawa			printf("QUEUEUP");
2114109890Ssimokawa			break;
2115109890Ssimokawa
2116129585Sdfr		  case QS_SENT:
2117113584Ssimokawa			printf("SENT");
2118103285Sikob			break;
2119109890Ssimokawa
2120113584Ssimokawa		  case QS_VERIFIED:
2121109890Ssimokawa			printf("VERIFIED");
2122113584Ssimokawa			break;
2123119155Ssimokawa
2124119155Ssimokawa		  case QS_EXPANDED:
2125109890Ssimokawa			printf("EXPANDED");
2126109890Ssimokawa			break;
2127113584Ssimokawa
2128113584Ssimokawa		  case QS_SENDER:
2129109890Ssimokawa			printf("SENDER");
2130119155Ssimokawa			break;
2131113584Ssimokawa
2132113584Ssimokawa		  case QS_CLONED:
2133109890Ssimokawa			printf("CLONED");
2134109890Ssimokawa			break;
2135109890Ssimokawa
2136109890Ssimokawa		  case QS_DISCARDED:
2137109890Ssimokawa			printf("DISCARDED");
2138109890Ssimokawa			break;
2139109890Ssimokawa
2140109179Ssimokawa		  case QS_REPLACED:
2141109890Ssimokawa			printf("REPLACED");
2142109890Ssimokawa			break;
2143109423Ssimokawa
2144113584Ssimokawa		  case QS_REMOVED:
2145113584Ssimokawa			printf("REMOVED");
2146109890Ssimokawa			break;
2147109890Ssimokawa
2148109890Ssimokawa		  case QS_DUPLICATE:
2149109403Ssimokawa			printf("DUPLICATE");
2150109890Ssimokawa			break;
2151109890Ssimokawa
2152109890Ssimokawa		  case QS_INCLUDED:
2153103285Sikob			printf("INCLUDED");
2154106790Ssimokawa			break;
2155106790Ssimokawa
2156106790Ssimokawa		  default:
2157103285Sikob			printf("%d", a->q_state);
2158109179Ssimokawa			break;
2159120660Ssimokawa		}
2160109890Ssimokawa		printf(", next=%lx, alias %lx, uid %d, gid %d\n",
2161109890Ssimokawa		       (u_long) a->q_next, (u_long) a->q_alias,
2162129585Sdfr		       (int) a->q_uid, (int) a->q_gid);
2163113584Ssimokawa		printf("\tflags=%lx<", a->q_flags);
2164109179Ssimokawa		firstone = TRUE;
2165109890Ssimokawa		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
2166113584Ssimokawa		{
2167113584Ssimokawa			if (!bitset(qfp->qf_bit, a->q_flags))
2168113584Ssimokawa				continue;
2169113584Ssimokawa			if (!firstone)
2170109890Ssimokawa				printf(",");
2171113584Ssimokawa			firstone = FALSE;
2172109890Ssimokawa			printf("%s", qfp->qf_name);
2173113584Ssimokawa		}
2174113584Ssimokawa		printf(">\n");
2175113584Ssimokawa		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
2176109890Ssimokawa		       a->q_owner == NULL ? "(none)" : a->q_owner,
2177109890Ssimokawa		       a->q_home == NULL ? "(none)" : a->q_home,
2178113584Ssimokawa		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
2179113584Ssimokawa		printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
2180113584Ssimokawa		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
2181113584Ssimokawa		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
2182113584Ssimokawa		       a->q_status == NULL ? "(none)" : a->q_status);
2183113584Ssimokawa		printf("\trstatus=\"%s\"\n",
2184113584Ssimokawa		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2185113584Ssimokawa		printf("\tspecificity=%d, statdate=%s\n",
2186113584Ssimokawa		       a->q_specificity,
2187113584Ssimokawa		       a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
2188113584Ssimokawa
2189113584Ssimokawa		if (!follow)
2190113584Ssimokawa			return;
2191109890Ssimokawa		a = a->q_next;
2192109890Ssimokawa	}
2193109890Ssimokawa}
2194109890Ssimokawa/*
2195111942Ssimokawa**  EMPTYADDR -- return TRUE if this address is empty (``<>'')
2196109890Ssimokawa**
2197109890Ssimokawa**	Parameters:
2198111942Ssimokawa**		a -- pointer to the address
2199109890Ssimokawa**
2200113584Ssimokawa**	Returns:
2201113584Ssimokawa**		TRUE -- if this address is "empty" (i.e., no one should
2202109890Ssimokawa**			ever generate replies to it.
2203109890Ssimokawa**		FALSE -- if it is a "regular" (read: replyable) address.
2204103285Sikob*/
2205109890Ssimokawa
2206111942Ssimokawabool
2207111942Ssimokawaemptyaddr(a)
2208111942Ssimokawa	register ADDRESS *a;
2209111942Ssimokawa{
2210111942Ssimokawa	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2211111942Ssimokawa	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2212103285Sikob}
2213106790Ssimokawa/*
2214106790Ssimokawa**  REMOTENAME -- return the name relative to the current mailer
2215129585Sdfr**
2216106790Ssimokawa**	Parameters:
2217129585Sdfr**		name -- the name to translate.
2218103285Sikob**		m -- the mailer that we want to do rewriting relative
2219103285Sikob**			to.
2220103285Sikob**		flags -- fine tune operations.
2221103285Sikob**		pstat -- pointer to status word.
2222103285Sikob**		e -- the current envelope.
2223103285Sikob**
2224103285Sikob**	Returns:
2225103285Sikob**		the text string representing this address relative to
2226103285Sikob**			the receiving mailer.
2227103285Sikob**
2228103285Sikob**	Side Effects:
2229103285Sikob**		none.
2230103285Sikob**
2231103285Sikob**	Warnings:
2232103285Sikob**		The text string returned is tucked away locally;
2233103285Sikob**			copy it if you intend to save it.
2234103285Sikob*/
2235103285Sikob
2236113584Ssimokawachar *
2237103285Sikobremotename(name, m, flags, pstat, e)
2238103285Sikob	char *name;
2239103285Sikob	struct mailer *m;
2240103285Sikob	int flags;
2241103285Sikob	int *pstat;
2242113584Ssimokawa	register ENVELOPE *e;
2243103285Sikob{
2244103285Sikob	register char **pvp;
2245103285Sikob	char *fancy;
2246103285Sikob	char *oldg = macvalue('g', e);
2247103285Sikob	int rwset;
2248103285Sikob	static char buf[MAXNAME + 1];
2249103285Sikob	char lbuf[MAXNAME + 1];
2250103285Sikob	char pvpbuf[PSBUFSIZE];
2251103285Sikob#if _FFR_ADDR_TYPE
2252103285Sikob	char addrtype[4];
2253103285Sikob#endif /* _FFR_ADDR_TYPE */
2254103285Sikob
2255103285Sikob	if (tTd(12, 1))
2256103285Sikob		dprintf("remotename(%s)\n", name);
2257103285Sikob
2258106790Ssimokawa	/* don't do anything if we are tagging it as special */
2259106790Ssimokawa	if (bitset(RF_SENDERADDR, flags))
2260129585Sdfr	{
2261106790Ssimokawa		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2262103285Sikob						     : m->m_se_rwset;
2263113584Ssimokawa#if _FFR_ADDR_TYPE
2264120660Ssimokawa		addrtype[2] = 's';
2265103285Sikob#endif /* _FFR_ADDR_TYPE */
2266129585Sdfr	}
2267103285Sikob	else
2268103285Sikob	{
2269103285Sikob		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2270103285Sikob						     : m->m_re_rwset;
2271103285Sikob#if _FFR_ADDR_TYPE
2272103285Sikob		addrtype[2] = 'r';
2273103285Sikob#endif /* _FFR_ADDR_TYPE */
2274103285Sikob	}
2275103285Sikob	if (rwset < 0)
2276103285Sikob		return name;
2277103285Sikob#if _FFR_ADDR_TYPE
2278103285Sikob	addrtype[1] = ' ';
2279103285Sikob	addrtype[3] = '\0';
2280103285Sikob	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
2281103285Sikob	define(macid("{addr_type}", NULL), addrtype, e);
2282103285Sikob#endif /* _FFR_ADDR_TYPE */
2283103285Sikob
2284103285Sikob	/*
2285103285Sikob	**  Do a heuristic crack of this name to extract any comment info.
2286103285Sikob	**	This will leave the name as a comment and a $g macro.
2287103285Sikob	*/
2288103285Sikob
2289103285Sikob	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2290103285Sikob		fancy = "\201g";
2291103285Sikob	else
2292103285Sikob		fancy = crackaddr(name);
2293103285Sikob
2294103285Sikob	/*
2295103285Sikob	**  Turn the name into canonical form.
2296103285Sikob	**	Normally this will be RFC 822 style, i.e., "user@domain".
2297103285Sikob	**	If this only resolves to "user", and the "C" flag is
2298103285Sikob	**	specified in the sending mailer, then the sender's
2299103285Sikob	**	domain will be appended.
2300103285Sikob	*/
2301103285Sikob
2302103285Sikob	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2303103285Sikob	if (pvp == NULL)
2304103285Sikob		return name;
2305103285Sikob	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2306113584Ssimokawa		*pstat = EX_TEMPFAIL;
2307103285Sikob	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2308103285Sikob	{
2309103285Sikob		/* append from domain to this address */
2310103285Sikob		register char **pxp = pvp;
2311103285Sikob		int l = MAXATOM;	/* size of buffer for pvp */
2312103285Sikob
2313103285Sikob		/* see if there is an "@domain" in the current name */
2314103285Sikob		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2315103285Sikob		{
2316103285Sikob			pxp++;
2317103285Sikob			--l;
2318103285Sikob		}
2319103285Sikob		if (*pxp == NULL)
2320103285Sikob		{
2321113584Ssimokawa			/* no.... append the "@domain" from the sender */
2322103285Sikob			register char **qxq = e->e_fromdomain;
2323113584Ssimokawa
2324113584Ssimokawa			while ((*pxp++ = *qxq++) != NULL)
2325103285Sikob			{
2326113584Ssimokawa				if (--l <= 0)
2327113584Ssimokawa				{
2328103285Sikob					*--pxp = NULL;
2329113584Ssimokawa					usrerr("553 5.1.0 remotename: too many tokens");
2330113584Ssimokawa					*pstat = EX_UNAVAILABLE;
2331103285Sikob					break;
2332103285Sikob				}
2333103285Sikob			}
2334103285Sikob			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2335103285Sikob				*pstat = EX_TEMPFAIL;
2336106790Ssimokawa		}
2337106790Ssimokawa	}
2338120660Ssimokawa
2339129585Sdfr	/*
2340106790Ssimokawa	**  Do more specific rewriting.
2341103285Sikob	**	Rewrite using ruleset 1 or 2 depending on whether this is
2342103285Sikob	**		a sender address or not.
2343129585Sdfr	**	Then run it through any receiving-mailer-specific rulesets.
2344103285Sikob	*/
2345103285Sikob
2346103285Sikob	if (bitset(RF_SENDERADDR, flags))
2347103285Sikob	{
2348103285Sikob		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
2349103285Sikob			*pstat = EX_TEMPFAIL;
2350103285Sikob	}
2351103285Sikob	else
2352103285Sikob	{
2353103285Sikob		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
2354103285Sikob			*pstat = EX_TEMPFAIL;
2355103285Sikob	}
2356103285Sikob	if (rwset > 0)
2357103285Sikob	{
2358103285Sikob		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
2359103285Sikob			*pstat = EX_TEMPFAIL;
2360103285Sikob	}
2361103285Sikob
2362103285Sikob	/*
2363113584Ssimokawa	**  Do any final sanitation the address may require.
2364113584Ssimokawa	**	This will normally be used to turn internal forms
2365113584Ssimokawa	**	(e.g., user@host.LOCAL) into external form.  This
2366113584Ssimokawa	**	may be used as a default to the above rules.
2367127468Ssimokawa	*/
2368127468Ssimokawa
2369127468Ssimokawa	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
2370127468Ssimokawa		*pstat = EX_TEMPFAIL;
2371113972Ssimokawa
2372114142Ssimokawa	/*
2373108712Ssimokawa	**  Now restore the comment information we had at the beginning.
2374113584Ssimokawa	*/
2375113584Ssimokawa
2376113584Ssimokawa	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
2377113584Ssimokawa	define('g', lbuf, e);
2378113584Ssimokawa
2379113584Ssimokawa	/* need to make sure route-addrs have <angle brackets> */
2380113584Ssimokawa	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2381113584Ssimokawa		expand("<\201g>", buf, sizeof buf, e);
2382113584Ssimokawa	else
2383103285Sikob		expand(fancy, buf, sizeof buf, e);
2384103285Sikob
2385103285Sikob	define('g', oldg, e);
2386103285Sikob
2387103285Sikob	if (tTd(12, 1))
2388103285Sikob		dprintf("remotename => `%s'\n", buf);
2389103285Sikob	return buf;
2390103285Sikob}
2391103285Sikob/*
2392103285Sikob**  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2393103285Sikob**
2394103285Sikob**	Parameters:
2395103285Sikob**		a -- the address to map (but just the user name part).
2396103285Sikob**		sendq -- the sendq in which to install any replacement
2397103285Sikob**			addresses.
2398103285Sikob**		aliaslevel -- the alias nesting depth.
2399113584Ssimokawa**		e -- the envelope.
2400113584Ssimokawa**
2401113584Ssimokawa**	Returns:
2402113584Ssimokawa**		none.
2403103285Sikob*/
2404103285Sikob
2405103285Sikob#define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2406103285Sikob			 Q_PINGFLAGS|QHASNOTIFY|\
2407113584Ssimokawa			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
2408103285Sikob
2409103285Sikobvoid
2410103285Sikobmaplocaluser(a, sendq, aliaslevel, e)
2411113584Ssimokawa	register ADDRESS *a;
2412103285Sikob	ADDRESS **sendq;
2413103285Sikob	int aliaslevel;
2414103285Sikob	ENVELOPE *e;
2415113584Ssimokawa{
2416103285Sikob	register char **pvp;
2417103285Sikob	register ADDRESS *a1 = NULL;
2418103285Sikob	auto char *delimptr;
2419103285Sikob	char pvpbuf[PSBUFSIZE];
2420103285Sikob
2421103285Sikob	if (tTd(29, 1))
2422103285Sikob	{
2423103285Sikob		dprintf("maplocaluser: ");
2424103285Sikob		printaddr(a, FALSE);
2425106790Ssimokawa	}
2426106790Ssimokawa	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
2427106790Ssimokawa	if (pvp == NULL)
2428103285Sikob	{
2429103285Sikob		if (tTd(29, 9))
2430129585Sdfr			dprintf("maplocaluser: cannot prescan %s\n",
2431103285Sikob				a->q_user);
2432110577Ssimokawa		return;
2433103285Sikob	}
2434108276Ssimokawa
2435108276Ssimokawa	define('h', a->q_host, e);
2436129611Sdfr	define('u', a->q_user, e);
2437129611Sdfr	define('z', a->q_home, e);
2438129611Sdfr
2439129611Sdfr#if _FFR_ADDR_TYPE
2440129611Sdfr	define(macid("{addr_type}", NULL), "e r", e);
2441129611Sdfr#endif /* _FFR_ADDR_TYPE */
2442129611Sdfr	if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
2443108276Ssimokawa	{
2444108276Ssimokawa		if (tTd(29, 9))
2445108276Ssimokawa			dprintf("maplocaluser: rewrite tempfail\n");
2446103285Sikob		a->q_state = QS_QUEUEUP;
2447103285Sikob		a->q_status = "4.4.3";
2448109280Ssimokawa		return;
2449103285Sikob	}
2450109280Ssimokawa	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2451103285Sikob	{
2452109280Ssimokawa		if (tTd(29, 9))
2453103285Sikob			dprintf("maplocaluser: doesn't resolve\n");
2454103285Sikob		return;
2455103285Sikob	}
2456106790Ssimokawa
2457106790Ssimokawa	/* if non-null, mailer destination specified -- has it changed? */
2458106790Ssimokawa	a1 = buildaddr(pvp, NULL, 0, e);
2459103285Sikob	if (a1 == NULL || sameaddr(a, a1))
2460103285Sikob	{
2461103285Sikob		if (tTd(29, 9))
2462120660Ssimokawa			dprintf("maplocaluser: address unchanged\n");
2463103285Sikob		if (a1 != NULL)
2464120660Ssimokawa			free(a1);
2465103285Sikob		return;
2466103285Sikob	}
2467103285Sikob
2468103285Sikob	/* make new address take on flags and print attributes of old */
2469103285Sikob	a1->q_flags &= ~Q_COPYFLAGS;
2470103285Sikob	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2471103285Sikob	a1->q_paddr = newstr(a->q_paddr);
2472103285Sikob	a1->q_orcpt = a->q_orcpt;
2473103285Sikob
2474113584Ssimokawa	/* mark old address as dead; insert new address */
2475103285Sikob	a->q_state = QS_REPLACED;
2476113584Ssimokawa	if (tTd(29, 5))
2477109892Ssimokawa	{
2478103285Sikob		dprintf("maplocaluser: QS_REPLACED ");
2479120660Ssimokawa		printaddr(a, FALSE);
2480113584Ssimokawa	}
2481119155Ssimokawa	a1->q_alias = a;
2482113584Ssimokawa	allocaddr(a1, RF_COPYALL, newstr(a->q_paddr));
2483103285Sikob	(void) recipient(a1, sendq, aliaslevel, e);
2484103285Sikob}
2485113584Ssimokawa/*
2486113584Ssimokawa**  DEQUOTE_INIT -- initialize dequote map
2487113584Ssimokawa**
2488113584Ssimokawa**	This is a no-op.
2489103285Sikob**
2490113584Ssimokawa**	Parameters:
2491113584Ssimokawa**		map -- the internal map structure.
2492113584Ssimokawa**		args -- arguments.
2493109892Ssimokawa**
2494113584Ssimokawa**	Returns:
2495103285Sikob**		TRUE.
2496109892Ssimokawa*/
2497109892Ssimokawa
2498109892Ssimokawabool
2499113584Ssimokawadequote_init(map, args)
2500109892Ssimokawa	MAP *map;
2501113584Ssimokawa	char *args;
2502113584Ssimokawa{
2503109892Ssimokawa	register char *p = args;
2504103285Sikob
2505103285Sikob	/* there is no check whether there is really an argument */
2506103285Sikob	map->map_mflags |= MF_KEEPQUOTES;
2507109892Ssimokawa	for (;;)
2508113584Ssimokawa	{
2509113584Ssimokawa		while (isascii(*p) && isspace(*p))
2510109892Ssimokawa			p++;
2511109892Ssimokawa		if (*p != '-')
2512109280Ssimokawa			break;
2513109892Ssimokawa		switch (*++p)
2514109892Ssimokawa		{
2515109892Ssimokawa		  case 'a':
2516103285Sikob			map->map_app = ++p;
2517103285Sikob			break;
2518113584Ssimokawa
2519103285Sikob		  case 'D':
2520103285Sikob			map->map_mflags |= MF_DEFER;
2521103285Sikob			break;
2522106790Ssimokawa
2523106790Ssimokawa		  case 'S':
2524113584Ssimokawa		  case 's':
2525113584Ssimokawa			map->map_spacesub = *++p;
2526103285Sikob			break;
2527120660Ssimokawa		}
2528113584Ssimokawa		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2529103285Sikob			p++;
2530113584Ssimokawa		if (*p != '\0')
2531113584Ssimokawa			*p = '\0';
2532113584Ssimokawa	}
2533103285Sikob	if (map->map_app != NULL)
2534103285Sikob		map->map_app = newstr(map->map_app);
2535103285Sikob
2536113584Ssimokawa	return TRUE;
2537103285Sikob}
2538103285Sikob/*
2539113584Ssimokawa**  DEQUOTE_MAP -- unquote an address
2540113584Ssimokawa**
2541119155Ssimokawa**	Parameters:
2542120660Ssimokawa**		map -- the internal map structure (ignored).
2543113584Ssimokawa**		name -- the name to dequote.
2544129585Sdfr**		av -- arguments (ignored).
2545113584Ssimokawa**		statp -- pointer to status out-parameter.
2546113584Ssimokawa**
2547113584Ssimokawa**	Returns:
2548109892Ssimokawa**		NULL -- if there were no quotes, or if the resulting
2549113584Ssimokawa**			unquoted buffer would not be acceptable to prescan.
2550113584Ssimokawa**		else -- The dequoted buffer.
2551109892Ssimokawa*/
2552113584Ssimokawa
2553103285Sikob/* ARGSUSED2 */
2554106790Ssimokawachar *
2555106790Ssimokawadequote_map(map, name, av, statp)
2556113584Ssimokawa	MAP *map;
2557113584Ssimokawa	char *name;
2558103285Sikob	char **av;
2559120660Ssimokawa	int *statp;
2560113584Ssimokawa{
2561113584Ssimokawa	register char *p;
2562113584Ssimokawa	register char *q;
2563103285Sikob	register char c;
2564103285Sikob	int anglecnt = 0;
2565113584Ssimokawa	int cmntcnt = 0;
2566113584Ssimokawa	int quotecnt = 0;
2567113584Ssimokawa	int spacecnt = 0;
2568113584Ssimokawa	bool quotemode = FALSE;
2569113584Ssimokawa	bool bslashmode = FALSE;
2570113584Ssimokawa	char spacesub = map->map_spacesub;
2571103285Sikob
2572113584Ssimokawa	for (p = q = name; (c = *p++) != '\0'; )
2573113584Ssimokawa	{
2574113584Ssimokawa		if (bslashmode)
2575113584Ssimokawa		{
2576113584Ssimokawa			bslashmode = FALSE;
2577113584Ssimokawa			*q++ = c;
2578129585Sdfr			continue;
2579113584Ssimokawa		}
2580113584Ssimokawa
2581113584Ssimokawa		if (c == ' ' && spacesub != '\0')
2582113584Ssimokawa			c = spacesub;
2583113584Ssimokawa
2584113584Ssimokawa		switch (c)
2585113584Ssimokawa		{
2586113584Ssimokawa		  case '\\':
2587103285Sikob			bslashmode = TRUE;
2588103285Sikob			break;
2589113584Ssimokawa
2590113584Ssimokawa		  case '(':
2591113584Ssimokawa			cmntcnt++;
2592113584Ssimokawa			break;
2593103285Sikob
2594113584Ssimokawa		  case ')':
2595103285Sikob			if (cmntcnt-- <= 0)
2596113584Ssimokawa				return NULL;
2597113584Ssimokawa			break;
2598113584Ssimokawa
2599103285Sikob		  case ' ':
2600113584Ssimokawa		  case '\t':
2601113584Ssimokawa			spacecnt++;
2602103285Sikob			break;
2603106790Ssimokawa		}
2604113584Ssimokawa
2605113584Ssimokawa		if (cmntcnt > 0)
2606113584Ssimokawa		{
2607103285Sikob			*q++ = c;
2608113584Ssimokawa			continue;
2609129585Sdfr		}
2610120660Ssimokawa
2611113584Ssimokawa		switch (c)
2612113584Ssimokawa		{
2613113584Ssimokawa		  case '"':
2614103285Sikob			quotemode = !quotemode;
2615113584Ssimokawa			quotecnt++;
2616113584Ssimokawa			continue;
2617113584Ssimokawa
2618113584Ssimokawa		  case '<':
2619113584Ssimokawa			anglecnt++;
2620120660Ssimokawa			break;
2621113584Ssimokawa
2622113584Ssimokawa		  case '>':
2623113584Ssimokawa			if (anglecnt-- <= 0)
2624113584Ssimokawa				return NULL;
2625113584Ssimokawa			break;
2626113584Ssimokawa		}
2627113584Ssimokawa		*q++ = c;
2628113584Ssimokawa	}
2629113584Ssimokawa
2630113584Ssimokawa	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2631113584Ssimokawa	    quotemode || quotecnt <= 0 || spacecnt != 0)
2632113584Ssimokawa		return NULL;
2633113584Ssimokawa	*q++ = '\0';
2634113584Ssimokawa	return map_rewrite(map, name, strlen(name), NULL);
2635113584Ssimokawa}
2636113584Ssimokawa/*
2637113584Ssimokawa**  RSCHECK -- check string(s) for validity using rewriting sets
2638113584Ssimokawa**
2639103285Sikob**	Parameters:
2640120660Ssimokawa**		rwset -- the rewriting set to use.
2641120660Ssimokawa**		p1 -- the first string to check.
2642113584Ssimokawa**		p2 -- the second string to check -- may be null.
2643113584Ssimokawa**		e -- the current envelope.
2644120660Ssimokawa**		rmcomm -- remove comments?
2645103285Sikob**		cnt -- count rejections (statistics)?
2646113584Ssimokawa**		logl -- logging level
2647113584Ssimokawa**		host -- NULL or relay host.
2648113584Ssimokawa**
2649113584Ssimokawa**	Returns:
2650120660Ssimokawa**		EX_OK -- if the rwset doesn't resolve to $#error
2651103285Sikob**		else -- the failure status (message printed)
2652103285Sikob*/
2653103285Sikob
2654113584Ssimokawaint
2655103285Sikobrscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
2656120660Ssimokawa	char *rwset;
2657113584Ssimokawa	char *p1;
2658103285Sikob	char *p2;
2659120660Ssimokawa	ENVELOPE *e;
2660129585Sdfr	bool rmcomm, cnt;
2661120660Ssimokawa	int logl;
2662129585Sdfr	char *host;
2663120660Ssimokawa{
2664129585Sdfr	char *buf;
2665120660Ssimokawa	int bufsize;
2666110798Ssimokawa	int saveexitstat;
2667110798Ssimokawa	int rstat = EX_OK;
2668120660Ssimokawa	char **pvp;
2669110798Ssimokawa	int rsno;
2670110798Ssimokawa	bool discard = FALSE;
2671110798Ssimokawa	auto ADDRESS a1;
2672110798Ssimokawa	bool saveQuickAbort = QuickAbort;
2673120660Ssimokawa	bool saveSuprErrs = SuprErrs;
2674110798Ssimokawa	char buf0[MAXLINE];
2675103285Sikob	char pvpbuf[PSBUFSIZE];
2676103285Sikob	extern char MsgBuf[];
2677106790Ssimokawa
2678113584Ssimokawa	if (tTd(48, 2))
2679113584Ssimokawa		dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
2680120660Ssimokawa			p2 == NULL ? "(NULL)" : p2);
2681113584Ssimokawa
2682113584Ssimokawa	rsno = strtorwset(rwset, NULL, ST_FIND);
2683113584Ssimokawa	if (rsno < 0)
2684113584Ssimokawa		return EX_OK;
2685113584Ssimokawa
2686113584Ssimokawa	if (p2 != NULL)
2687113584Ssimokawa	{
2688113584Ssimokawa		bufsize = strlen(p1) + strlen(p2) + 2;
2689113584Ssimokawa		if (bufsize > sizeof buf0)
2690106790Ssimokawa			buf = xalloc(bufsize);
2691103285Sikob		else
2692103285Sikob		{
2693113584Ssimokawa			buf = buf0;
2694113584Ssimokawa			bufsize = sizeof buf0;
2695113584Ssimokawa		}
2696103285Sikob		(void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
2697129585Sdfr	}
2698129585Sdfr	else
2699103285Sikob	{
2700113584Ssimokawa		bufsize = strlen(p1) + 1;
2701103285Sikob		if (bufsize > sizeof buf0)
2702103285Sikob			buf = xalloc(bufsize);
2703103285Sikob		else
2704103285Sikob		{
2705103285Sikob			buf = buf0;
2706103285Sikob			bufsize = sizeof buf0;
2707103285Sikob		}
2708103285Sikob		(void) snprintf(buf, bufsize, "%s", p1);
2709103285Sikob	}
2710103285Sikob	SuprErrs = TRUE;
2711103285Sikob	QuickAbort = FALSE;
2712103285Sikob	pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
2713103285Sikob		      rmcomm ? NULL : TokTypeNoC);
2714103285Sikob	SuprErrs = saveSuprErrs;
2715103285Sikob	if (pvp == NULL)
2716103285Sikob	{
2717113584Ssimokawa		if (tTd(48, 2))
2718113584Ssimokawa			dprintf("rscheck: cannot prescan input\n");
2719113584Ssimokawa/*
2720113584Ssimokawa		syserr("rscheck: cannot prescan input: \"%s\"",
2721113584Ssimokawa			shortenstring(buf, MAXSHORTSTR));
2722113584Ssimokawa		rstat = EX_DATAERR;
2723113584Ssimokawa*/
2724113584Ssimokawa		goto finis;
2725113584Ssimokawa	}
2726129585Sdfr
2727113584Ssimokawa	MapOpenErr = FALSE;
2728113584Ssimokawa	(void) rewrite(pvp, rsno, 0, e);
2729113584Ssimokawa	if (MapOpenErr)
2730113584Ssimokawa		usrerrenh("4.3.0", "451 Temporary failure");
2731113584Ssimokawa
2732113584Ssimokawa	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
2733113584Ssimokawa	    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
2734103285Sikob			       strcmp(pvp[1], "discard") != 0))
2735106789Ssimokawa	{
2736106789Ssimokawa		goto finis;
2737113584Ssimokawa	}
2738113584Ssimokawa
2739113584Ssimokawa	if (strcmp(pvp[1], "discard") == 0)
2740103285Sikob	{
2741113584Ssimokawa		if (tTd(48, 2))
2742113584Ssimokawa			dprintf("rscheck: discard mailer selected\n");
2743113584Ssimokawa		e->e_flags |= EF_DISCARD;
2744113584Ssimokawa		discard = TRUE;
2745113584Ssimokawa	}
2746113584Ssimokawa	else
2747113584Ssimokawa	{
2748113584Ssimokawa		int savelogusrerrs = LogUsrErrs;
2749113584Ssimokawa		static bool logged = FALSE;
2750113584Ssimokawa
2751113584Ssimokawa		/* got an error -- process it */
2752113584Ssimokawa		saveexitstat = ExitStat;
2753113584Ssimokawa		LogUsrErrs = FALSE;
2754113584Ssimokawa		(void) buildaddr(pvp, &a1, 0, e);
2755113584Ssimokawa		LogUsrErrs = savelogusrerrs;
2756113584Ssimokawa		rstat = ExitStat;
2757113584Ssimokawa		ExitStat = saveexitstat;
2758113584Ssimokawa		if (!logged)
2759113584Ssimokawa		{
2760113584Ssimokawa			if (cnt)
2761103285Sikob				markstats(e, &a1, TRUE);
2762103285Sikob			logged = TRUE;
2763113584Ssimokawa		}
2764113584Ssimokawa	}
2765113584Ssimokawa
2766113584Ssimokawa	if (LogLevel >= logl)
2767113584Ssimokawa	{
2768113584Ssimokawa		char *relay;
2769113584Ssimokawa		char *p;
2770113584Ssimokawa		char lbuf[MAXLINE];
2771113584Ssimokawa
2772113584Ssimokawa		p = lbuf;
2773113584Ssimokawa		if (p2 != NULL)
2774113584Ssimokawa		{
2775103285Sikob			snprintf(p, SPACELEFT(lbuf, p),
2776113584Ssimokawa				", arg2=%s",
2777113584Ssimokawa				p2);
2778113584Ssimokawa			p += strlen(p);
2779113584Ssimokawa		}
2780103285Sikob
2781113584Ssimokawa		if (host != NULL)
2782113584Ssimokawa			relay = host;
2783113584Ssimokawa		else
2784103285Sikob			relay = macvalue('_', e);
2785113584Ssimokawa		if (relay != NULL)
2786113584Ssimokawa		{
2787113584Ssimokawa			snprintf(p, SPACELEFT(lbuf, p),
2788113584Ssimokawa				", relay=%s", relay);
2789113584Ssimokawa			p += strlen(p);
2790124145Ssimokawa		}
2791124145Ssimokawa		*p = '\0';
2792124145Ssimokawa		if (discard)
2793113584Ssimokawa			sm_syslog(LOG_NOTICE, e->e_id,
2794103285Sikob				  "ruleset=%s, arg1=%s%s, discard",
2795113584Ssimokawa				  rwset, p1, lbuf);
2796113584Ssimokawa		else
2797113584Ssimokawa			sm_syslog(LOG_NOTICE, e->e_id,
2798113584Ssimokawa				  "ruleset=%s, arg1=%s%s, reject=%s",
2799113584Ssimokawa				  rwset, p1, lbuf, MsgBuf);
2800113584Ssimokawa	}
2801113584Ssimokawa
2802120660Ssimokawa finis:
2803120660Ssimokawa	/* clean up */
2804124145Ssimokawa	QuickAbort = saveQuickAbort;
2805113584Ssimokawa	setstat(rstat);
2806113584Ssimokawa	if (buf != buf0)
2807113584Ssimokawa		free(buf);
2808113584Ssimokawa
2809113584Ssimokawa	if (rstat != EX_OK && QuickAbort)
2810113584Ssimokawa		longjmp(TopFrame, 2);
2811113584Ssimokawa	return rstat;
2812113584Ssimokawa}
2813113584Ssimokawa