1 # include "sendmail.h"
2 
3 SCCSID(@(#)parseaddr.c	4.6		03/17/84);
4 
5 /*
6 **  PARSEADDR -- Parse an address
7 **
8 **	Parses an address and breaks it up into three parts: a
9 **	net to transmit the message on, the host to transmit it
10 **	to, and a user on that host.  These are loaded into an
11 **	ADDRESS header with the values squirreled away if necessary.
12 **	The "user" part may not be a real user; the process may
13 **	just reoccur on that machine.  For example, on a machine
14 **	with an arpanet connection, the address
15 **		csvax.bill@berkeley
16 **	will break up to a "user" of 'csvax.bill' and a host
17 **	of 'berkeley' -- to be transmitted over the arpanet.
18 **
19 **	Parameters:
20 **		addr -- the address to parse.
21 **		a -- a pointer to the address descriptor buffer.
22 **			If NULL, a header will be created.
23 **		copyf -- determines what shall be copied:
24 **			-1 -- don't copy anything.  The printname
25 **				(q_paddr) is just addr, and the
26 **				user & host are allocated internally
27 **				to parse.
28 **			0 -- copy out the parsed user & host, but
29 **				don't copy the printname.
30 **			+1 -- copy everything.
31 **		delim -- the character to terminate the address, passed
32 **			to prescan.
33 **
34 **	Returns:
35 **		A pointer to the address descriptor header (`a' if
36 **			`a' is non-NULL).
37 **		NULL on error.
38 **
39 **	Side Effects:
40 **		none
41 */
42 
43 /* following delimiters are inherent to the internal algorithms */
44 # define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
45 
46 ADDRESS *
47 parseaddr(addr, a, copyf, delim)
48 	char *addr;
49 	register ADDRESS *a;
50 	int copyf;
51 	char delim;
52 {
53 	register char **pvp;
54 	register struct mailer *m;
55 	extern char **prescan();
56 	extern ADDRESS *buildaddr();
57 
58 	/*
59 	**  Initialize and prescan address.
60 	*/
61 
62 	CurEnv->e_to = addr;
63 # ifdef DEBUG
64 	if (tTd(20, 1))
65 		printf("\n--parseaddr(%s)\n", addr);
66 # endif DEBUG
67 
68 	pvp = prescan(addr, delim);
69 	if (pvp == NULL)
70 		return (NULL);
71 
72 	/*
73 	**  Apply rewriting rules.
74 	**	Ruleset 0 does basic parsing.  It must resolve.
75 	*/
76 
77 	rewrite(pvp, 3);
78 	rewrite(pvp, 0);
79 
80 	/*
81 	**  See if we resolved to a real mailer.
82 	*/
83 
84 	if (pvp[0][0] != CANONNET)
85 	{
86 		setstat(EX_USAGE);
87 		usrerr("cannot resolve name");
88 		return (NULL);
89 	}
90 
91 	/*
92 	**  Build canonical address from pvp.
93 	*/
94 
95 	a = buildaddr(pvp, a);
96 	if (a == NULL)
97 		return (NULL);
98 	m = a->q_mailer;
99 
100 	/*
101 	**  Make local copies of the host & user and then
102 	**  transport them out.
103 	*/
104 
105 	if (copyf > 0)
106 	{
107 		extern char *DelimChar;
108 		char savec = *DelimChar;
109 
110 		*DelimChar = '\0';
111 		a->q_paddr = newstr(addr);
112 		*DelimChar = savec;
113 	}
114 	else
115 		a->q_paddr = addr;
116 	if (copyf >= 0)
117 	{
118 		if (a->q_host != NULL)
119 			a->q_host = newstr(a->q_host);
120 		else
121 			a->q_host = "";
122 		if (a->q_user != a->q_paddr)
123 			a->q_user = newstr(a->q_user);
124 	}
125 
126 	/*
127 	**  Convert host name to lower case if requested.
128 	**	User name will be done later.
129 	*/
130 
131 	if (!bitnset(M_HST_UPPER, m->m_flags))
132 		makelower(a->q_host);
133 
134 	/*
135 	**  Compute return value.
136 	*/
137 
138 # ifdef DEBUG
139 	if (tTd(20, 1))
140 	{
141 		printf("parseaddr-->");
142 		printaddr(a, FALSE);
143 	}
144 # endif DEBUG
145 
146 	return (a);
147 }
148 /*
149 **  LOWERADDR -- map UPPER->lower case on addresses as requested.
150 **
151 **	Parameters:
152 **		a -- address to be mapped.
153 **
154 **	Returns:
155 **		none.
156 **
157 **	Side Effects:
158 **		none.
159 */
160 
161 loweraddr(a)
162 	register ADDRESS *a;
163 {
164 	register MAILER *m = a->q_mailer;
165 
166 	if (!bitnset(M_USR_UPPER, m->m_flags))
167 		makelower(a->q_user);
168 }
169 /*
170 **  PRESCAN -- Prescan name and make it canonical
171 **
172 **	Scans a name and turns it into a set of tokens.  This process
173 **	deletes blanks and comments (in parentheses).
174 **
175 **	This routine knows about quoted strings and angle brackets.
176 **
177 **	There are certain subtleties to this routine.  The one that
178 **	comes to mind now is that backslashes on the ends of names
179 **	are silently stripped off; this is intentional.  The problem
180 **	is that some versions of sndmsg (like at LBL) set the kill
181 **	character to something other than @ when reading addresses;
182 **	so people type "csvax.eric\@berkeley" -- which screws up the
183 **	berknet mailer.
184 **
185 **	Parameters:
186 **		addr -- the name to chomp.
187 **		delim -- the delimiter for the address, normally
188 **			'\0' or ','; \0 is accepted in any case.
189 **			If '\t' then we are reading the .cf file.
190 **
191 **	Returns:
192 **		A pointer to a vector of tokens.
193 **		NULL on error.
194 **
195 **	Side Effects:
196 **		none.
197 */
198 
199 /* states and character types */
200 # define OPR		0	/* operator */
201 # define ATM		1	/* atom */
202 # define QST		2	/* in quoted string */
203 # define SPC		3	/* chewing up spaces */
204 # define ONE		4	/* pick up one character */
205 
206 # define NSTATES	5	/* number of states */
207 # define TYPE		017	/* mask to select state type */
208 
209 /* meta bits for table */
210 # define M		020	/* meta character; don't pass through */
211 # define B		040	/* cause a break */
212 # define MB		M|B	/* meta-break */
213 
214 static short StateTab[NSTATES][NSTATES] =
215 {
216    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
217 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
218 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
219 	/*QST*/		QST,	QST,	OPR,	QST,	QST,
220 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
221 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
222 };
223 
224 # define NOCHAR		-1	/* signal nothing in lookahead token */
225 
226 char	*DelimChar;		/* set to point to the delimiter */
227 
228 char **
229 prescan(addr, delim)
230 	char *addr;
231 	char delim;
232 {
233 	register char *p;
234 	register char *q;
235 	register int c;
236 	char **avp;
237 	bool bslashmode;
238 	int cmntcnt;
239 	int anglecnt;
240 	char *tok;
241 	int state;
242 	int newstate;
243 	static char buf[MAXNAME+MAXATOM];
244 	static char *av[MAXATOM+1];
245 	extern int errno;
246 
247 	/* make sure error messages don't have garbage on them */
248 	errno = 0;
249 
250 	q = buf;
251 	bslashmode = FALSE;
252 	cmntcnt = 0;
253 	anglecnt = 0;
254 	avp = av;
255 	state = OPR;
256 	c = NOCHAR;
257 	p = addr;
258 # ifdef DEBUG
259 	if (tTd(22, 45))
260 	{
261 		printf("prescan: ");
262 		xputs(p);
263 		putchar('\n');
264 	}
265 # endif DEBUG
266 
267 	do
268 	{
269 		/* read a token */
270 		tok = q;
271 		for (;;)
272 		{
273 			/* store away any old lookahead character */
274 			if (c != NOCHAR)
275 			{
276 				/* see if there is room */
277 				if (q >= &buf[sizeof buf - 5])
278 				{
279 					usrerr("Address too long");
280 					DelimChar = p;
281 					return (NULL);
282 				}
283 
284 				/* squirrel it away */
285 				*q++ = c;
286 			}
287 
288 			/* read a new input character */
289 			c = *p++;
290 			if (c == '\0')
291 				break;
292 			c &= ~0200;
293 
294 # ifdef DEBUG
295 			if (tTd(22, 101))
296 				printf("c=%c, s=%d; ", c, state);
297 # endif DEBUG
298 
299 			/* chew up special characters */
300 			*q = '\0';
301 			if (bslashmode)
302 			{
303 				c |= 0200;
304 				bslashmode = FALSE;
305 			}
306 			else if (c == '\\')
307 			{
308 				bslashmode = TRUE;
309 				c = NOCHAR;
310 			}
311 			else if (state == QST)
312 			{
313 				/* do nothing, just avoid next clauses */
314 			}
315 			else if (c == '(')
316 			{
317 				cmntcnt++;
318 				c = NOCHAR;
319 			}
320 			else if (c == ')')
321 			{
322 				if (cmntcnt <= 0)
323 				{
324 					usrerr("Unbalanced ')'");
325 					DelimChar = p;
326 					return (NULL);
327 				}
328 				else
329 					cmntcnt--;
330 			}
331 			else if (cmntcnt > 0)
332 				c = NOCHAR;
333 			else if (c == '<')
334 				anglecnt++;
335 			else if (c == '>')
336 			{
337 				if (anglecnt <= 0)
338 				{
339 					usrerr("Unbalanced '>'");
340 					DelimChar = p;
341 					return (NULL);
342 				}
343 				anglecnt--;
344 			}
345 			else if (delim == ' ' && isspace(c))
346 				c = ' ';
347 
348 			if (c == NOCHAR)
349 				continue;
350 
351 			/* see if this is end of input */
352 			if (c == delim && anglecnt <= 0 && state != QST)
353 				break;
354 
355 			newstate = StateTab[state][toktype(c)];
356 # ifdef DEBUG
357 			if (tTd(22, 101))
358 				printf("ns=%02o\n", newstate);
359 # endif DEBUG
360 			state = newstate & TYPE;
361 			if (bitset(M, newstate))
362 				c = NOCHAR;
363 			if (bitset(B, newstate))
364 				break;
365 		}
366 
367 		/* new token */
368 		if (tok != q)
369 		{
370 			*q++ = '\0';
371 # ifdef DEBUG
372 			if (tTd(22, 36))
373 			{
374 				printf("tok=");
375 				xputs(tok);
376 				putchar('\n');
377 			}
378 # endif DEBUG
379 			if (avp >= &av[MAXATOM])
380 			{
381 				syserr("prescan: too many tokens");
382 				DelimChar = p;
383 				return (NULL);
384 			}
385 			*avp++ = tok;
386 		}
387 	} while (c != '\0' && (c != delim || anglecnt > 0));
388 	*avp = NULL;
389 	DelimChar = --p;
390 	if (cmntcnt > 0)
391 		usrerr("Unbalanced '('");
392 	else if (anglecnt > 0)
393 		usrerr("Unbalanced '<'");
394 	else if (state == QST)
395 		usrerr("Unbalanced '\"'");
396 	else if (av[0] != NULL)
397 		return (av);
398 	return (NULL);
399 }
400 /*
401 **  TOKTYPE -- return token type
402 **
403 **	Parameters:
404 **		c -- the character in question.
405 **
406 **	Returns:
407 **		Its type.
408 **
409 **	Side Effects:
410 **		none.
411 */
412 
413 toktype(c)
414 	register char c;
415 {
416 	static char buf[50];
417 	static bool firstime = TRUE;
418 
419 	if (firstime)
420 	{
421 		firstime = FALSE;
422 		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
423 		(void) strcat(buf, DELIMCHARS);
424 	}
425 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
426 		return (ONE);
427 	if (c == '"')
428 		return (QST);
429 	if (!isascii(c))
430 		return (ATM);
431 	if (isspace(c) || c == ')')
432 		return (SPC);
433 	if (iscntrl(c) || index(buf, c) != NULL)
434 		return (OPR);
435 	return (ATM);
436 }
437 /*
438 **  REWRITE -- apply rewrite rules to token vector.
439 **
440 **	This routine is an ordered production system.  Each rewrite
441 **	rule has a LHS (called the pattern) and a RHS (called the
442 **	rewrite); 'rwr' points the the current rewrite rule.
443 **
444 **	For each rewrite rule, 'avp' points the address vector we
445 **	are trying to match against, and 'pvp' points to the pattern.
446 **	If pvp points to a special match value (MATCHZANY, MATCHANY,
447 **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
448 **	matched is saved away in the match vector (pointed to by 'mvp').
449 **
450 **	When a match between avp & pvp does not match, we try to
451 **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
452 **	we must also back out the match in mvp.  If we reach a
453 **	MATCHANY or MATCHZANY we just extend the match and start
454 **	over again.
455 **
456 **	When we finally match, we rewrite the address vector
457 **	and try over again.
458 **
459 **	Parameters:
460 **		pvp -- pointer to token vector.
461 **
462 **	Returns:
463 **		none.
464 **
465 **	Side Effects:
466 **		pvp is modified.
467 */
468 
469 struct match
470 {
471 	char	**first;	/* first token matched */
472 	char	**last;		/* last token matched */
473 };
474 
475 # define MAXMATCH	9	/* max params per rewrite */
476 
477 
478 rewrite(pvp, ruleset)
479 	char **pvp;
480 	int ruleset;
481 {
482 	register char *ap;		/* address pointer */
483 	register char *rp;		/* rewrite pointer */
484 	register char **avp;		/* address vector pointer */
485 	register char **rvp;		/* rewrite vector pointer */
486 	register struct match *mlp;	/* cur ptr into mlist */
487 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
488 	struct match mlist[MAXMATCH];	/* stores match on LHS */
489 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
490 	extern bool sameword();
491 
492 	if (OpMode == MD_TEST || tTd(21, 2))
493 	{
494 		printf("rewrite: ruleset %2d   input:", ruleset);
495 		printav(pvp);
496 	}
497 	if (pvp == NULL)
498 		return;
499 
500 	/*
501 	**  Run through the list of rewrite rules, applying
502 	**	any that match.
503 	*/
504 
505 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
506 	{
507 # ifdef DEBUG
508 		if (tTd(21, 12))
509 		{
510 			printf("-----trying rule:");
511 			printav(rwr->r_lhs);
512 		}
513 # endif DEBUG
514 
515 		/* try to match on this rule */
516 		mlp = mlist;
517 		rvp = rwr->r_lhs;
518 		avp = pvp;
519 		while ((ap = *avp) != NULL || *rvp != NULL)
520 		{
521 			rp = *rvp;
522 # ifdef DEBUG
523 			if (tTd(21, 35))
524 			{
525 				printf("ap=");
526 				xputs(ap);
527 				printf(", rp=");
528 				xputs(rp);
529 				printf("\n");
530 			}
531 # endif DEBUG
532 			if (rp == NULL)
533 			{
534 				/* end-of-pattern before end-of-address */
535 				goto backup;
536 			}
537 			if (ap == NULL && *rp != MATCHZANY)
538 			{
539 				/* end-of-input */
540 				break;
541 			}
542 
543 			switch (*rp)
544 			{
545 				register STAB *s;
546 
547 			  case MATCHCLASS:
548 			  case MATCHNCLASS:
549 				/* match any token in (not in) a class */
550 				s = stab(ap, ST_CLASS, ST_FIND);
551 				if (s == NULL || !bitnset(rp[1], s->s_class))
552 				{
553 					if (*rp == MATCHCLASS)
554 						goto backup;
555 				}
556 				else if (*rp == MATCHNCLASS)
557 					goto backup;
558 
559 				/* explicit fall-through */
560 
561 			  case MATCHONE:
562 			  case MATCHANY:
563 				/* match exactly one token */
564 				mlp->first = avp;
565 				mlp->last = avp++;
566 				mlp++;
567 				break;
568 
569 			  case MATCHZANY:
570 				/* match zero or more tokens */
571 				mlp->first = avp;
572 				mlp->last = avp - 1;
573 				mlp++;
574 				break;
575 
576 			  default:
577 				/* must have exact match */
578 				if (!sameword(rp, ap))
579 					goto backup;
580 				avp++;
581 				break;
582 			}
583 
584 			/* successful match on this token */
585 			rvp++;
586 			continue;
587 
588 		  backup:
589 			/* match failed -- back up */
590 			while (--rvp >= rwr->r_lhs)
591 			{
592 				rp = *rvp;
593 				if (*rp == MATCHANY || *rp == MATCHZANY)
594 				{
595 					/* extend binding and continue */
596 					avp = ++mlp[-1].last;
597 					avp++;
598 					rvp++;
599 					break;
600 				}
601 				avp--;
602 				if (*rp == MATCHONE || *rp == MATCHCLASS ||
603 				    *rp == MATCHNCLASS)
604 				{
605 					/* back out binding */
606 					mlp--;
607 				}
608 			}
609 
610 			if (rvp < rwr->r_lhs)
611 			{
612 				/* total failure to match */
613 				break;
614 			}
615 		}
616 
617 		/*
618 		**  See if we successfully matched
619 		*/
620 
621 		if (rvp < rwr->r_lhs || *rvp != NULL)
622 		{
623 # ifdef DEBUG
624 			if (tTd(21, 10))
625 				printf("----- rule fails\n");
626 # endif DEBUG
627 			rwr = rwr->r_next;
628 			continue;
629 		}
630 
631 		rvp = rwr->r_rhs;
632 # ifdef DEBUG
633 		if (tTd(21, 12))
634 		{
635 			printf("-----rule matches:");
636 			printav(rvp);
637 		}
638 # endif DEBUG
639 
640 		rp = *rvp;
641 		if (*rp == CANONUSER)
642 		{
643 			rvp++;
644 			rwr = rwr->r_next;
645 		}
646 		else if (*rp == CANONHOST)
647 		{
648 			rvp++;
649 			rwr = NULL;
650 		}
651 		else if (*rp == CANONNET)
652 			rwr = NULL;
653 
654 		/* substitute */
655 		for (avp = npvp; *rvp != NULL; rvp++)
656 		{
657 			register struct match *m;
658 			register char **pp;
659 
660 			rp = *rvp;
661 			if (*rp != MATCHREPL)
662 			{
663 				if (avp >= &npvp[MAXATOM])
664 				{
665 					syserr("rewrite: expansion too long");
666 					return;
667 				}
668 				*avp++ = rp;
669 				continue;
670 			}
671 
672 			/* substitute from LHS */
673 			m = &mlist[rp[1] - '1'];
674 # ifdef DEBUG
675 			if (tTd(21, 15))
676 			{
677 				printf("$%c:", rp[1]);
678 				pp = m->first;
679 				while (pp <= m->last)
680 				{
681 					printf(" %x=\"", *pp);
682 					(void) fflush(stdout);
683 					printf("%s\"", *pp++);
684 				}
685 				printf("\n");
686 			}
687 # endif DEBUG
688 			pp = m->first;
689 			while (pp <= m->last)
690 			{
691 				if (avp >= &npvp[MAXATOM])
692 				{
693 					syserr("rewrite: expansion too long");
694 					return;
695 				}
696 				*avp++ = *pp++;
697 			}
698 		}
699 		*avp++ = NULL;
700 		if (**npvp == CALLSUBR)
701 		{
702 			bmove((char *) &npvp[2], (char *) pvp,
703 				(avp - npvp - 2) * sizeof *avp);
704 # ifdef DEBUG
705 			if (tTd(21, 3))
706 				printf("-----callsubr %s\n", npvp[1]);
707 # endif DEBUG
708 			rewrite(pvp, atoi(npvp[1]));
709 		}
710 		else
711 		{
712 			bmove((char *) npvp, (char *) pvp,
713 				(avp - npvp) * sizeof *avp);
714 		}
715 # ifdef DEBUG
716 		if (tTd(21, 4))
717 		{
718 			printf("rewritten as:");
719 			printav(pvp);
720 		}
721 # endif DEBUG
722 	}
723 
724 	if (OpMode == MD_TEST || tTd(21, 2))
725 	{
726 		printf("rewrite: ruleset %2d returns:", ruleset);
727 		printav(pvp);
728 	}
729 }
730 /*
731 **  BUILDADDR -- build address from token vector.
732 **
733 **	Parameters:
734 **		tv -- token vector.
735 **		a -- pointer to address descriptor to fill.
736 **			If NULL, one will be allocated.
737 **
738 **	Returns:
739 **		NULL if there was an error.
740 **		'a' otherwise.
741 **
742 **	Side Effects:
743 **		fills in 'a'
744 */
745 
746 ADDRESS *
747 buildaddr(tv, a)
748 	register char **tv;
749 	register ADDRESS *a;
750 {
751 	static char buf[MAXNAME];
752 	struct mailer **mp;
753 	register struct mailer *m;
754 	extern bool sameword();
755 
756 	if (a == NULL)
757 		a = (ADDRESS *) xalloc(sizeof *a);
758 	clear((char *) a, sizeof *a);
759 
760 	/* figure out what net/mailer to use */
761 	if (**tv != CANONNET)
762 	{
763 		syserr("buildaddr: no net");
764 		return (NULL);
765 	}
766 	tv++;
767 	if (sameword(*tv, "error"))
768 	{
769 		if (**++tv == CANONHOST)
770 		{
771 			setstat(atoi(*++tv));
772 			tv++;
773 		}
774 		if (**tv != CANONUSER)
775 			syserr("buildaddr: error: no user");
776 		buf[0] = '\0';
777 		while (*++tv != NULL)
778 		{
779 			if (buf[0] != '\0')
780 				(void) strcat(buf, " ");
781 			(void) strcat(buf, *tv);
782 		}
783 		usrerr(buf);
784 		return (NULL);
785 	}
786 	for (mp = Mailer; (m = *mp++) != NULL; )
787 	{
788 		if (sameword(m->m_name, *tv))
789 			break;
790 	}
791 	if (m == NULL)
792 	{
793 		syserr("buildaddr: unknown net %s", *tv);
794 		return (NULL);
795 	}
796 	a->q_mailer = m;
797 
798 	/* figure out what host (if any) */
799 	tv++;
800 	if (!bitnset(M_LOCAL, m->m_flags))
801 	{
802 		if (**tv++ != CANONHOST)
803 		{
804 			syserr("buildaddr: no host");
805 			return (NULL);
806 		}
807 		buf[0] = '\0';
808 		while (*tv != NULL && **tv != CANONUSER)
809 			(void) strcat(buf, *tv++);
810 		a->q_host = newstr(buf);
811 	}
812 	else
813 		a->q_host = NULL;
814 
815 	/* figure out the user */
816 	if (**tv != CANONUSER)
817 	{
818 		syserr("buildaddr: no user");
819 		return (NULL);
820 	}
821 	rewrite(++tv, 4);
822 	cataddr(tv, buf, sizeof buf);
823 	a->q_user = buf;
824 
825 	return (a);
826 }
827 /*
828 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
829 **
830 **	Parameters:
831 **		pvp -- parameter vector to rebuild.
832 **		buf -- buffer to build the string into.
833 **		sz -- size of buf.
834 **
835 **	Returns:
836 **		none.
837 **
838 **	Side Effects:
839 **		Destroys buf.
840 */
841 
842 cataddr(pvp, buf, sz)
843 	char **pvp;
844 	char *buf;
845 	register int sz;
846 {
847 	bool oatomtok = FALSE;
848 	bool natomtok = FALSE;
849 	register int i;
850 	register char *p;
851 
852 	if (pvp == NULL)
853 	{
854 		strcpy(buf, "");
855 		return;
856 	}
857 	p = buf;
858 	sz -= 2;
859 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
860 	{
861 		natomtok = (toktype(**pvp) == ATM);
862 		if (oatomtok && natomtok)
863 			*p++ = SpaceSub;
864 		(void) strcpy(p, *pvp);
865 		oatomtok = natomtok;
866 		p += i;
867 		sz -= i + 1;
868 		pvp++;
869 	}
870 	*p = '\0';
871 }
872 /*
873 **  SAMEADDR -- Determine if two addresses are the same
874 **
875 **	This is not just a straight comparison -- if the mailer doesn't
876 **	care about the host we just ignore it, etc.
877 **
878 **	Parameters:
879 **		a, b -- pointers to the internal forms to compare.
880 **
881 **	Returns:
882 **		TRUE -- they represent the same mailbox.
883 **		FALSE -- they don't.
884 **
885 **	Side Effects:
886 **		none.
887 */
888 
889 bool
890 sameaddr(a, b)
891 	register ADDRESS *a;
892 	register ADDRESS *b;
893 {
894 	/* if they don't have the same mailer, forget it */
895 	if (a->q_mailer != b->q_mailer)
896 		return (FALSE);
897 
898 	/* if the user isn't the same, we can drop out */
899 	if (strcmp(a->q_user, b->q_user) != 0)
900 		return (FALSE);
901 
902 	/* if the mailer ignores hosts, we have succeeded! */
903 	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
904 		return (TRUE);
905 
906 	/* otherwise compare hosts (but be careful for NULL ptrs) */
907 	if (a->q_host == NULL || b->q_host == NULL)
908 		return (FALSE);
909 	if (strcmp(a->q_host, b->q_host) != 0)
910 		return (FALSE);
911 
912 	return (TRUE);
913 }
914 /*
915 **  PRINTADDR -- print address (for debugging)
916 **
917 **	Parameters:
918 **		a -- the address to print
919 **		follow -- follow the q_next chain.
920 **
921 **	Returns:
922 **		none.
923 **
924 **	Side Effects:
925 **		none.
926 */
927 
928 # ifdef DEBUG
929 
930 printaddr(a, follow)
931 	register ADDRESS *a;
932 	bool follow;
933 {
934 	bool first = TRUE;
935 
936 	while (a != NULL)
937 	{
938 		first = FALSE;
939 		printf("%x=", a);
940 		(void) fflush(stdout);
941 		printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
942 		       a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host,
943 		       a->q_user);
944 		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
945 		       a->q_alias);
946 		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
947 		       a->q_fullname);
948 
949 		if (!follow)
950 			return;
951 		a = a->q_next;
952 	}
953 	if (first)
954 		printf("[NULL]\n");
955 }
956 
957 # endif DEBUG
958 /*
959 **  REMOTENAME -- return the name relative to the current mailer
960 **
961 **	Parameters:
962 **		name -- the name to translate.
963 **		m -- the mailer that we want to do rewriting relative
964 **			to.
965 **		senderaddress -- if set, uses the sender rewriting rules
966 **			rather than the recipient rewriting rules.
967 **		canonical -- if set, strip out any comment information,
968 **			etc.
969 **
970 **	Returns:
971 **		the text string representing this address relative to
972 **			the receiving mailer.
973 **
974 **	Side Effects:
975 **		none.
976 **
977 **	Warnings:
978 **		The text string returned is tucked away locally;
979 **			copy it if you intend to save it.
980 */
981 
982 char *
983 remotename(name, m, senderaddress, canonical)
984 	char *name;
985 	struct mailer *m;
986 	bool senderaddress;
987 	bool canonical;
988 {
989 	register char **pvp;
990 	char *fancy;
991 	register char *p;
992 	extern char *macvalue();
993 	char *oldg = macvalue('g', CurEnv);
994 	static char buf[MAXNAME];
995 	char lbuf[MAXNAME];
996 	extern char **prescan();
997 	extern char *crackaddr();
998 
999 # ifdef DEBUG
1000 	if (tTd(12, 1))
1001 		printf("remotename(%s)\n", name);
1002 # endif DEBUG
1003 
1004 	/* don't do anything if we are tagging it as special */
1005 	if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
1006 		return (name);
1007 
1008 	/*
1009 	**  Do a heuristic crack of this name to extract any comment info.
1010 	**	This will leave the name as a comment and a $g macro.
1011 	*/
1012 
1013 	if (canonical)
1014 		fancy = "\001g";
1015 	else
1016 		fancy = crackaddr(name);
1017 
1018 	/*
1019 	**  Turn the name into canonical form.
1020 	**	Normally this will be RFC 822 style, i.e., "user@domain".
1021 	**	If this only resolves to "user", and the "C" flag is
1022 	**	specified in the sending mailer, then the sender's
1023 	**	domain will be appended.
1024 	*/
1025 
1026 	pvp = prescan(name, '\0');
1027 	if (pvp == NULL)
1028 		return (name);
1029 	rewrite(pvp, 3);
1030 	if (CurEnv->e_fromdomain != NULL)
1031 	{
1032 		/* append from domain to this address */
1033 		register char **pxp = pvp;
1034 
1035 		/* see if there is an "@domain" in the current name */
1036 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1037 			pxp++;
1038 		if (*pxp == NULL)
1039 		{
1040 			/* no.... append the "@domain" from the sender */
1041 			register char **qxq = CurEnv->e_fromdomain;
1042 
1043 			while ((*pxp++ = *qxq++) != NULL)
1044 				continue;
1045 			rewrite(pvp, 3);
1046 		}
1047 	}
1048 
1049 	/*
1050 	**  Do more specific rewriting.
1051 	**	Rewrite using ruleset 1 or 2 depending on whether this is
1052 	**		a sender address or not.
1053 	**	Then run it through any receiving-mailer-specific rulesets.
1054 	*/
1055 
1056 	if (senderaddress)
1057 	{
1058 		rewrite(pvp, 1);
1059 		if (m->m_s_rwset > 0)
1060 			rewrite(pvp, m->m_s_rwset);
1061 	}
1062 	else
1063 	{
1064 		rewrite(pvp, 2);
1065 		if (m->m_r_rwset > 0)
1066 			rewrite(pvp, m->m_r_rwset);
1067 	}
1068 
1069 	/*
1070 	**  Do any final sanitation the address may require.
1071 	**	This will normally be used to turn internal forms
1072 	**	(e.g., user@host.LOCAL) into external form.  This
1073 	**	may be used as a default to the above rules.
1074 	*/
1075 
1076 	rewrite(pvp, 4);
1077 
1078 	/*
1079 	**  Now restore the comment information we had at the beginning.
1080 	*/
1081 
1082 	cataddr(pvp, lbuf, sizeof lbuf);
1083 	define('g', lbuf, CurEnv);
1084 	expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
1085 	define('g', oldg, CurEnv);
1086 
1087 # ifdef DEBUG
1088 	if (tTd(12, 1))
1089 		printf("remotename => `%s'\n", buf);
1090 # endif DEBUG
1091 	return (buf);
1092 }
1093