translation tables in sendmail

sources-request at panda.UUCP sources-request at panda.UUCP
Sat Nov 2 00:10:13 AEST 1985


Mod.sources:  Volume 3, Issue 31
Submitted by: Bruce Israel <talcott!mimsy.umd.edu:gymble!israel>


Here are my modifications to sendmail that I announced recently.  I
am posting these due to the number of requests.  These modifications
allow translations to be used in the sendmail.cf file, such as
pathalias information and other stuff.  Run the shar in a clean
directory and then read the README.  Enjoy.

Bruce Israel

University of Maryland, Computer Science Dept.
{rlgvax,seismo}!umcp-cs!israel (Usenet)    israel at Maryland (Arpanet)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ x$1 = x-a ]; then
	all=TRUE
fi
/bin/echo 'Extracting 4.2diffs'
sed 's/^X//' <<'//go.sysin dd *' >4.2diffs
diff -c main.c.old main.c
*** /usr/4.2/src/usr.lib/sendmail/src/main.c	Sun Sep 25 21:06:10 1983
--- /usr/israel/sendmail/src/main.c	Wed Mar  6 15:45:26 1985
***************
*** 695,700 ****
--- 695,707 ----
  	'*', MATCHZANY,	'+', MATCHANY,	'-', MATCHONE,	'=', MATCHCLASS,
  	'~', MATCHNCLASS,
  
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 	/* for RHS translation table */
+ 	'&', EQUIVREPL,
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  	/* these are RHS metasymbols */
  	'#', CANONNET,	'@', CANONHOST,	':', CANONUSER,	'>', CALLSUBR,
  
***************
*** 808,813 ****
--- 815,822 ----
  	union frz fhdr;
  	extern char edata;
  	extern char Version[];
+  	extern char **environ;
+  	char	**envsave;
  
  	if (freezefile == NULL)
  		return (FALSE);
***************
*** 835,840 ****
--- 844,855 ----
  		(void) close(f);
  		return (FALSE);
  	}
+  /**
+   **  Arrrrg!  Since the pointer to the environment is in BSS, and our
+   **  bss get's blasted over when the freeze file is read in, we need to
+   **  save and restore the environ pointer for getenv()
+   **/
+  	envsave = environ;		/* save pointer to environment */
  
  	/* now read in the freeze file */
  	if (read(f, (char *) &edata, fhdr.frzinfo.frzbrk - &edata) !=
***************
*** 844,849 ****
--- 859,866 ----
  		write(2, "Cannot read freeze file\n", 24);
  		_exit(EX_SOFTWARE);
  	}
+ 
+  	environ = envsave;
  
  	(void) close(f);
  	return (TRUE);
diff -c parseaddr.c.old parseaddr.c
*** /usr/4.2/src/usr.lib/sendmail/src/parseaddr.c	Mon Jul 25 23:20:46 1983
--- /usr/israel/sendmail/src/parseaddr.c	Thu Oct 31 17:45:31 1985
***************
*** 2,7 ****
--- 2,9 ----
  
  SCCSID(@(#)parseaddr.c	4.1		7/25/83);
  
+ char **copyplist();
+ 
  /*
  **  PARSEADDR -- Parse an address
  **
***************
*** 204,214 ****
--- 206,241 ----
  
  char	*DelimChar;		/* set to point to the delimiter */
  
+ #ifdef ROUTING	/* ******************************** */
+ 
+ char **prescan1();
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  char **
  prescan(addr, delim)
  	char *addr;
  	char delim;
  {
+ 	static char buf[MAXNAME+MAXATOM];
+ 	static char *av[MAXATOM+1];
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 	return(prescan1(addr,delim,buf,av,sizeof buf));
+ }
+ 
+ char **
+ prescan1(addr,delim,buf,av,bufsize)
+ 	char *addr;
+ 	char delim;
+ 	char buf[];
+ 	char *av[];
+ 	int  bufsize;
+ {
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  	register char *p;
  	register char *q;
  	register int c;
***************
*** 219,226 ****
  	char *tok;
  	int state;
  	int newstate;
- 	static char buf[MAXNAME+MAXATOM];
- 	static char *av[MAXATOM+1];
  
  	q = buf;
  	bslashmode = FALSE;
--- 246,251 ----
***************
*** 249,255 ****
  			if (c != NOCHAR)
  			{
  				/* squirrel it away */
! 				if (q >= &buf[sizeof buf - 5])
  				{
  					usrerr("Address too long");
  					DelimChar = p;
--- 274,280 ----
  			if (c != NOCHAR)
  			{
  				/* squirrel it away */
! 				if (q >= &buf[bufsize - 5])
  				{
  					usrerr("Address too long");
  					DelimChar = p;
***************
*** 396,401 ****
--- 421,429 ----
  	}
  	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
  		return (ONE);
+ #ifdef ROUTING	/* ******************************** */
+ 	if (c == EQUIVREPL) return (ONE);
+ #endif ROUTING	/* ******************************** */
  	if (c == '"')
  		return (QST);
  	if (!isascii(c))
***************
*** 442,447 ****
--- 470,478 ----
  {
  	char	**first;	/* first token matched */
  	char	**last;		/* last token matched */
+ #ifdef ROUTING	/* ******************************** */
+ 	char	*equiv;		/* equivalence name for match */
+ #endif ROUTING	/* ******************************** */
  };
  
  # define MAXMATCH	9	/* max params per rewrite */
***************
*** 528,533 ****
--- 559,571 ----
  				else if (*rp == MATCHNCLASS)
  					goto backup;
  
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 				mlp -> equiv = getequiv(rp[1],s);
+ 
+ #endif ROUTING	/* ******************************** */
+ 
+ 
  				/* explicit fall-through */
  
  			  case MATCHONE:
***************
*** 536,541 ****
--- 574,582 ----
  				mlp->first = avp;
  				mlp->last = avp++;
  				mlp++;
+ #ifdef ROUTING	/* ******************************** */
+ 				mlp -> equiv = NULL;
+ #endif ROUTING	/* ******************************** */
  				break;
  
  			  case MATCHZANY:
***************
*** 543,548 ****
--- 584,592 ----
  				mlp->first = avp;
  				mlp->last = avp - 1;
  				mlp++;
+ #ifdef ROUTING	/* ******************************** */
+ 				mlp -> equiv = NULL;
+ #endif ROUTING	/* ******************************** */
  				break;
  
  			  default:
***************
*** 576,581 ****
--- 620,628 ----
  				{
  					/* back out binding */
  					mlp--;
+ #ifdef ROUTING	/* ******************************** */
+ 					mlp -> equiv = NULL;
+ #endif ROUTING	/* ******************************** */
  				}
  			}
  
***************
*** 630,636 ****
  			register char **pp;
  
  			rp = *rvp;
! 			if (*rp != MATCHREPL)
  			{
  				if (avp >= &npvp[MAXATOM])
  				{
--- 677,687 ----
  			register char **pp;
  
  			rp = *rvp;
! 			if (*rp != MATCHREPL
! #ifdef ROUTING	/* ******************************** */
! 			 && *rp != EQUIVREPL
! #endif ROUTING	/* ******************************** */
! 					    )
  			{
  				if (avp >= &npvp[MAXATOM])
  				{
***************
*** 641,651 ****
--- 692,722 ----
  				continue;
  			}
  
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 			/* if the $& is not followed by a digit, put the
+ 			   '&' on the output string. */
+ 
+ 			if (*rp == EQUIVREPL && (rp[1] < '1' || rp[1] > '9'))
+ 			{
+ 				*avp++ = "&";
+ 				continue;
+ 			}
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  			/* substitute from LHS */
  			m = &mlist[rp[1] - '1'];
  # ifdef DEBUG
  			if (tTd(21, 15))
  			{
+ #ifdef ROUTING	/* ******************************** */
+ 			    if (*rp == EQUIVREPL) {
+ 			        printf("$&%c:%x=\"%s\"\n",
+ 					rp[1], *m->equiv, *m->equiv);
+ 			    }
+ 			    else {
+ #endif ROUTING	/* ******************************** */
  				printf("$%c:", rp[1]);
  				pp = m->first;
  				while (pp <= m->last)
***************
*** 655,662 ****
  					printf("%s\"", *pp++);
  				}
  				printf("\n");
! 			}
  # endif DEBUG
  			pp = m->first;
  			while (pp <= m->last)
  			{
--- 726,763 ----
  					printf("%s\"", *pp++);
  				}
  				printf("\n");
! #ifdef ROUTING	/* ******************************** */
! 			    }
! #endif ROUTING	/* ******************************** */
! 			};
  # endif DEBUG
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 		      if (*rp == EQUIVREPL) {
+ 		        char buf[1024];
+ 			char *av[MAXATOM+1];
+ 			char *Dchar = DelimChar; /* save! */
+ 	
+ 			pp = copyplist(prescan1(m -> equiv,
+ 				'\0',buf,av,sizeof buf),TRUE);
+ 
+ 			/* restore saved version, recursive */
+ 			DelimChar = Dchar;
+ 
+ 			while (*pp)
+ 			{
+ 			    if (avp >= &npvp[MAXATOM])
+ 			    {
+ 				syserr("rewrite: expansion too long");
+ 				return;
+ 			    }
+ 			    *avp++ = *pp++;
+ 			}
+ 		      }
+ 		      else
+ 		      {
+ #endif ROUTING	/* ******************************** */
  			pp = m->first;
  			while (pp <= m->last)
  			{
***************
*** 667,672 ****
--- 768,776 ----
  				}
  				*avp++ = *pp++;
  			}
+ #ifdef ROUTING	/* ******************************** */
+ 		      }
+ #endif ROUTING	/* ******************************** */
  		}
  		*avp++ = NULL;
  		if (**npvp == CALLSUBR)
diff -c readcf.c.old readcf.c
*** /usr/4.2/src/usr.lib/sendmail/src/readcf.c	Sun Sep 25 21:06:11 1983
--- /usr/israel/sendmail/src/readcf.c	Fri Feb  1 17:59:14 1985
***************
*** 110,119 ****
  				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
  
  			/* expand and save the RHS */
! 			while (*++p == '\t')
  				continue;
  			q = p;
! 			while (*p != '\0' && *p != '\t')
  				p++;
  			*p = '\0';
  			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
--- 110,119 ----
  				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
  
  			/* expand and save the RHS */
! 			while (*++p == '\t' || *p == ' ')
  				continue;
  			q = p;
! 			while (*p != '\0' && *p != '\t' && *p != ' ')
  				p++;
  			*p = '\0';
  			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
***************
*** 140,165 ****
  			(void) chompheader(&buf[1], TRUE);
  			break;
  
- 		  case 'C':		/* word class */
  		  case 'F':		/* word class from file */
! 			/* read list of words from argument or file */
! 			if (buf[0] == 'F')
  			{
! 				/* read from file */
! 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
! 					continue;
! 				if (*p == '\0')
! 					p = "%s";
! 				else
! 				{
! 					*p = '\0';
! 					while (isspace(*++p))
! 						continue;
! 				}
! 				fileclass(buf[1], &buf[2], p);
! 				break;
  			}
  
  			/* scan the list of words and set class for all */
  			for (p = &buf[2]; *p != '\0'; )
  			{
--- 140,161 ----
  			(void) chompheader(&buf[1], TRUE);
  			break;
  
  		  case 'F':		/* word class from file */
! 
! 		        /* read from file */
! 			for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
! 				continue;
! 			if (*p == '\0') p = "%s";
! 			else
  			{
! 				*p = '\0';
! 				while (isspace(*++p)) continue;
  			}
+ 			fileclass(buf[1], &buf[2], p);
+ 			break;
  
+ 		  case 'C':		/* word class */
+ 
  			/* scan the list of words and set class for all */
  			for (p = &buf[2]; *p != '\0'; )
  			{
***************
*** 179,184 ****
--- 175,221 ----
  			}
  			break;
  
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 		  case 'E':		/* word equivalence class */
+ 
+ 			/* scan the list of words as a <word, translation>
+ 			   pair, and set class for all */
+ 
+ 			for (p = &buf[2]; *p != '\0'; )
+ 			{
+ 				register char *wd;
+ 				register char *wd2;
+ 				char delim;
+ 				STAB *s;
+ 
+ 				while (*p != '\0' && isspace(*p))
+ 					p++;
+ 				wd = p;
+ 				while (*p != '\0' && !isspace(*p))
+ 					p++;
+ 				delim = *p;
+ 				*p = '\0';
+ 				if (wd[0] != '\0')
+ 					s = setclass(buf[1], wd);
+ 				*p = delim;
+ 
+ 				while (*p != '\0' && isspace(*p))
+ 					p++;
+ 				wd2 = p;
+ 				while (*p != '\0' && !isspace(*p))
+ 					p++;
+ 				delim = *p;
+ 				*p = '\0';
+ 				if (*wd != NULL)
+ 				    makeequiv(buf[1],wd2,s);
+ 				*p = delim;
+ 			}
+ 			break;
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  		  case 'M':		/* define mailer */
  			makemailer(&buf[1], safe);
  			break;
***************
*** 269,274 ****
--- 306,322 ----
  **
  **		puts all lines in filename that match a scanf into
  **			the named class.
+ **
+ **	Comment: equivalence class modification
+ **		fileclass has been modified so that if you use a format
+ **		string returning two strings, i.e.:
+ **
+ **		FN/usr/lib/net-gateways "%s%s"
+ **
+ **		it will store the second name as a name translation of
+ **		the first, accessable through the $&<digit> macro.
+ **						- BNI 9/5/84
+ **
  */
  
  fileclass(class, filename, fmt)
***************
*** 279,284 ****
--- 327,338 ----
  	register FILE *f;
  	char buf[MAXLINE];
  
+ #ifdef DEBUG
+ 	if (tTd(86,5))
+ 	    printf("fileclass: class = %c, file = %s, fmt = \"%s\".\n"
+ 		    , class, filename, fmt);
+ #endif DEBUG
+ 
  	f = fopen(filename, "r");
  	if (f == NULL)
  	{
***************
*** 291,300 ****
--- 345,391 ----
  		register STAB *s;
  		char wordbuf[MAXNAME+1];
  
+ #ifdef ROUTING	/* ******************************** */
+ 		char word2[MAXNAME+1];
+ 		int ssc;
+ #ifdef DEBUG
+ 		if (tTd(86,11))
+ 			printf("fileclass: got line '%s'.\n",buf);
+ #endif DEBUG
+ 		word2[0] = '\0';
+ 		
+ 		if ((ssc=sscanf(buf, fmt, wordbuf, word2, 0)) < 1) {
+ #ifdef DEBUG
+ 			if(tTd(86,8))
+ 			      printf("fileclass: found nothing on line.\n");
+ #endif DEBUG
+ 			continue;
+ 		}
+ #ifdef DEBUG
+ 		if (tTd(86,8))
+ 			if (*word2)
+ 			    printf("fileclass: found word translation '%s' --> '%s'.\n",
+ 				wordbuf,word2);
+ 			else printf("fileclass: found word '%s'.\n",wordbuf);
+ #endif DEBUG
+ 
+ #else ROUTING	/* ******************************** */
+ 
  		if (sscanf(buf, fmt, wordbuf) != 1)
  			continue;
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  		s = stab(wordbuf, ST_CLASS, ST_ENTER);
  		setbitn(class, s->s_class);
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ 		if (*word2 != NULL)
+ 		    makeequiv(class,word2,s);
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  	}
  
  	(void) fclose(f);
***************
*** 805,816 ****
  **		word -- the word to enter
  **
  **	Returns:
! **		none.
  **
  **	Side Effects:
  **		puts the word into the symbol table.
  */
  
  setclass(class, word)
  	int class;
  	char *word;
--- 896,915 ----
  **		word -- the word to enter
  **
  **	Returns:
! **		a pointer to the symbol table entry created.
! **		- changed from 'no return value' 9/5/84 - BNI
  **
  **	Side Effects:
  **		puts the word into the symbol table.
  */
  
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ STAB *
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  setclass(class, word)
  	int class;
  	char *word;
***************
*** 819,822 ****
--- 918,926 ----
  
  	s = stab(word, ST_CLASS, ST_ENTER);
  	setbitn(class, s->s_class);
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 	return (s);
+ #endif ROUTING	/* ******************************** */
+ 
  }
diff -c stab.c.old stab.c
*** /usr/4.2/src/usr.lib/sendmail/src/stab.c	Mon Jul 25 23:18:41 1983
--- /usr/israel/sendmail/src/stab.c	Fri Feb  1 17:00:59 1985
***************
*** 106,108 ****
--- 106,185 ----
  
  	return (s);
  }
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ /*
+ **  MAKEEQUIV -- make a name equivalence entry
+ **
+ **	Parameters:
+ **		class -- the class the equivalence is in.
+ **		name  -- The name to be translated into.
+ **		s     -- a symbol table entry to the orginal name.
+ **
+ **	Returns:
+ **		none.
+ **
+ **	Side Effects:
+ **		none.
+ **
+ **	Author:
+ **		Bruce Israel  --  9/4/84.
+ **
+ */
+ 
+ makeequiv(class,name,s)
+ char class;
+ char *name;
+ STAB *s;
+ {
+ 	EQUIV *eq;
+ 
+ #ifdef DEBUG
+ 	if (tTd(87,5))
+ 		printf("makeequiv: defining '%s' --> '%s' in class '%c'.\n",
+ 			s -> s_name, name, class);
+ #endif DEBUG
+ 
+ 	eq = (EQUIV *) xalloc(sizeof *eq);
+ 	eq -> class = class;
+ 	eq -> equivname = newstr(name);
+ 	eq -> next = s ->s_equiv;
+ 	s -> s_equiv = eq;
+ }
+ 
+ /*
+ **  GETQUIV -- get a name equivalence for a class
+ **
+ **	Parameters:
+ **		class -- the class the equivalence is in.
+ **		s     -- The symbol table entry of the word.
+ **
+ **	Returns:
+ **		a pointer to a string containing the translation
+ **		name. If the symbol table entry has no translation
+ **		name, the original name will be returned.
+ **
+ **	Side Effects:
+ **		none.
+ **
+ **	Author:
+ **		Bruce Israel  --  9/4/84.
+ **
+ */
+ 
+ char *getequiv(class,s)
+ char class;
+ STAB *s;
+ {
+ 	EQUIV *eq;
+ 
+ 	eq = s -> s_equiv;
+ 	while (eq != NULL) {
+ 	    if (eq -> class == class) return (eq -> equivname);
+ 	    eq = eq -> next;
+ 	}
+ 	return(s -> s_name);
+ }
+ 
+ #endif ROUTING	/* ******************************** */
diff -c sendmail.h.old sendmail.h
*** /usr/4.2/src/usr.lib/sendmail/src/sendmail.h	Sun Sep 25 21:06:12 1983
--- /usr/israel/sendmail/src/sendmail.h	Fri Feb  1 17:00:42 1985
***************
*** 2,9 ****
  **  SENDMAIL.H -- Global definitions for sendmail.
  */
  
  
- 
  # ifdef _DEFINE
  # define EXTERN
  # ifndef lint
--- 2,9 ----
  **  SENDMAIL.H -- Global definitions for sendmail.
  */
  
+ # define ROUTING	/* Mods for equivalence name classes - BNI 9/5/84 */
  
  # ifdef _DEFINE
  # define EXTERN
  # ifndef lint
***************
*** 290,299 ****
--- 290,319 ----
  # define CONDIF		'\031'	/* conditional if-then */
  # define CONDELSE	'\032'	/* conditional else */
  # define CONDFI		'\033'	/* conditional fi */
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ # define EQUIVREPL	'\034'	/* equivalence class replacement */
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  /*
  **  Symbol table definitions
  */
  
+ #ifdef ROUTING	/* ******************************** */
+ 
+ struct equivstr
+ {
+ 	char		class;		/* class the name belongs in */
+ 	char		*equivname;	/* translated version of name */
+ 	struct equivstr	*next;		/* next item on equivalence list */
+ };
+ 
+ typedef struct equivstr EQUIV;
+ 
+ #endif ROUTING	/* ******************************** */
+ 
  struct symtab
  {
  	char		*s_name;	/* name to be entered */
***************
*** 306,311 ****
--- 326,334 ----
  		MAILER	*sv_mailer;	/* pointer to mailer */
  		char	*sv_alias;	/* alias */
  	}	s_value;
+ #ifdef ROUTING	/* ******************************** */
+ 	EQUIV		*s_equiv;	/* pointer to equivalence names list */
+ #endif ROUTING	/* ******************************** */
  };
  
  typedef struct symtab	STAB;
***************
*** 323,328 ****
--- 346,358 ----
  # define s_alias	s_value.sv_alias
  
  extern STAB	*stab();
+ 
+ #ifdef ROUTING	/* ******************************** */
+ 
+ STAB	*setclass();
+ char	*getequiv();
+ 
+ #endif ROUTING	/* ******************************** */
  
  /* opcodes to stab */
  # define ST_FIND	0	/* find entry */
//go.sysin dd *
if [ `wc -c < 4.2diffs` != 17321 ]; then
	made=FALSE
	/bin/echo 'error transmitting "4.2diffs" --'
	/bin/echo 'length should be 17321, not' `wc -c < 4.2diffs`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 4.2diffs
	/bin/echo -n '	'; /bin/ls -ld 4.2diffs
fi
/bin/echo 'Extracting 4.3diffs'
sed 's/^X//' <<'//go.sysin dd *' >4.3diffs
RCS file: RCS/main.c,v
retrieving revision 1.1
diff -c -r1.1 main.c
*** /tmp/,RCSt1001795	Thu Oct 31 18:03:04 1985
--- main.c	Fri Oct 18 19:39:15 1985
***************
*** 778,783 ****
--- 778,784 ----
  
  	/* these are RHS metasymbols */
  	'#', CANONNET,	'@', CANONHOST,	':', CANONUSER,	'>', CALLSUBR,
+ 	'&', EQUIVREPL,
  
  	/* the conditional operations */
  	'?', CONDIF,	'|', CONDELSE,	'.', CONDFI,
===================================================================
RCS file: RCS/parseaddr.c,v
retrieving revision 1.1
diff -c -r1.1 parseaddr.c
*** /tmp/,RCSt1001795	Thu Oct 31 18:03:12 1985
--- parseaddr.c	Fri Oct 18 19:39:19 1985
***************
*** 213,218 ****
--- 213,222 ----
  **
  **	Side Effects:
  **		none.
+ **
+ **	N.B.: prescan's work is all done by the internal routine
+ **	_prescan, which merely has one additional parameter:
+ **		av -- place to put the pointers.
  */
  
  /* states and character types */
***************
*** 250,255 ****
--- 254,272 ----
  	char delim;
  	char pvpbuf[];
  {
+ 	static char *av[MAXATOM+1];
+ 	char **_prescan();
+ 
+ 	return (_prescan(addr, delim, pvpbuf, av));
+ }
+ 
+ static char **
+ _prescan(addr, delim, pvpbuf, av)
+ 	char *addr;
+ 	char delim;
+ 	char pvpbuf[];
+ 	char *av[];
+ {
  	register char *p;
  	register char *q;
  	register int c;
***************
*** 260,266 ****
  	char *tok;
  	int state;
  	int newstate;
- 	static char *av[MAXATOM+1];
  	extern int errno;
  
  	/* make sure error messages don't have garbage on them */
--- 277,282 ----
***************
*** 443,449 ****
  		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
  		(void) strcat(buf, DELIMCHARS);
  	}
! 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
  		return (ONE);
  	if (c == '"')
  		return (QST);
--- 459,466 ----
  		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
  		(void) strcat(buf, DELIMCHARS);
  	}
! 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS ||
! 	    c == EQUIVREPL)
  		return (ONE);
  	if (c == '"')
  		return (QST);
***************
*** 491,496 ****
--- 508,514 ----
  {
  	char	**first;	/* first token matched */
  	char	**last;		/* last token matched */
+ 	char	*equiv;		/* equivalence name for match */ /* XXX */
  };
  
  # define MAXMATCH	9	/* max params per rewrite */
***************
*** 576,582 ****
  				}
  				else if (*rp == MATCHNCLASS)
  					goto backup;
! 
  				/* explicit fall-through */
  
  			  case MATCHONE:
--- 594,604 ----
  				}
  				else if (*rp == MATCHNCLASS)
  					goto backup;
! 				/*
! 				 * XXX	Should only note equiv parameters
! 				 *	here, and do translation later.
! 				 */
! 				mlp->equiv = getequiv(rp[1], s);
  				/* explicit fall-through */
  
  			  case MATCHONE:
***************
*** 585,590 ****
--- 607,613 ----
  				mlp->first = avp;
  				mlp->last = avp++;
  				mlp++;
+ 				mlp->equiv = NULL;
  				break;
  
  			  case MATCHZANY:
***************
*** 592,597 ****
--- 615,621 ----
  				mlp->first = avp;
  				mlp->last = avp - 1;
  				mlp++;
+ 				mlp->equiv = NULL;
  				break;
  
  			  default:
***************
*** 625,630 ****
--- 649,655 ----
  				{
  					/* back out binding */
  					mlp--;
+ 					mlp->equiv = NULL;
  				}
  			}
  
***************
*** 679,689 ****
  			register char **pp;
  
  			rp = *rvp;
! 			if (*rp == MATCHREPL)
  			{
  				/* substitute from LHS */
  				m = &mlist[rp[1] - '1'];
! 				if (m >= mlp)
  				{
  					syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
  					return;
--- 704,714 ----
  			register char **pp;
  
  			rp = *rvp;
! 			if (*rp == MATCHREPL || *rp == EQUIVREPL)
  			{
  				/* substitute from LHS */
  				m = &mlist[rp[1] - '1'];
! 				if (rp[1] < '1' || rp[1] > '9' || m >= mlp)
  				{
  					syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
  					return;
***************
*** 691,697 ****
  # ifdef DEBUG
  				if (tTd(21, 15))
  				{
! 					printf("$%c:", rp[1]);
  					pp = m->first;
  					while (pp <= m->last)
  					{
--- 716,725 ----
  # ifdef DEBUG
  				if (tTd(21, 15))
  				{
! 					if (*rp == EQUIVREPL)
! 						printf("$&%c:", rp[1]);
! 					else
! 						printf("$%c:", rp[1]);
  					pp = m->first;
  					while (pp <= m->last)
  					{
***************
*** 699,716 ****
  						(void) fflush(stdout);
  						printf("%s\"", *pp++);
  					}
  					printf("\n");
  				}
  # endif DEBUG
! 				pp = m->first;
! 				while (pp <= m->last)
  				{
! 					if (avp >= &npvp[MAXATOM])
  					{
! 						syserr("rewrite: expansion too long");
! 						return;
  					}
! 					*avp++ = *pp++;
  				}
  			}
  			else
--- 727,770 ----
  						(void) fflush(stdout);
  						printf("%s\"", *pp++);
  					}
+ 					if (*rp == EQUIVREPL)
+ 						printf(" => \"%s\"",
+ 							m->equiv);
  					printf("\n");
  				}
  # endif DEBUG
! 				if (*rp == EQUIVREPL)
  				{
! 					char equivbuf[PSBUFSIZE];
! 					char *equivav[MAXATOM+1];
! 					char *saveDelim = DelimChar;
! 					extern char **copyplist();
! 
! 					/*
! 					 * XXX	waste of memory!
! 					 */
! 					pp = copyplist(_prescan(m->equiv,
! 								'\0',
! 								equivbuf,
! 								equivav),
! 							TRUE);
! 					DelimChar = saveDelim;
! 					while (*pp)
  					{
! 						if (avp >= &npvp[MAXATOM])
! 							goto toolong;
! 						*avp++ = *pp++;
  					}
! 				}
! 				else
! 				{
! 					pp = m->first;
! 					while (pp <= m->last)
! 					{
! 						if (avp >= &npvp[MAXATOM])
! 							goto toolong;
! 						*avp++ = *pp++;
! 					}
  				}
  			}
  			else
===================================================================
RCS file: RCS/readcf.c,v
retrieving revision 1.1
diff -c -r1.1 readcf.c
*** /tmp/,RCSt1001795	Thu Oct 31 18:03:23 1985
--- readcf.c	Fri Oct 18 19:39:22 1985
***************
*** 164,189 ****
  			(void) chompheader(&buf[1], TRUE);
  			break;
  
- 		  case 'C':		/* word class */
  		  case 'F':		/* word class from file */
  			/* read list of words from argument or file */
! 			if (buf[0] == 'F')
  			{
! 				/* read from file */
! 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
  					continue;
- 				if (*p == '\0')
- 					p = "%s";
- 				else
- 				{
- 					*p = '\0';
- 					while (isspace(*++p))
- 						continue;
- 				}
- 				fileclass(buf[1], &buf[2], p);
- 				break;
  			}
  
  			/* scan the list of words and set class for all */
  			for (p = &buf[2]; *p != '\0'; )
  			{
--- 164,186 ----
  			(void) chompheader(&buf[1], TRUE);
  			break;
  
  		  case 'F':		/* word class from file */
  			/* read list of words from argument or file */
! 			/* read from file */
! 			for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
! 				continue;
! 			if (*p == '\0')
! 				p = "%s";
! 			else
  			{
! 				*p = '\0';
! 				while (isspace(*++p))
  					continue;
  			}
+ 			fileclass(buf[1], &buf[2], p);
+ 			break;
  
+ 		  case 'C':		/* word class */
  			/* scan the list of words and set class for all */
  			for (p = &buf[2]; *p != '\0'; )
  			{
***************
*** 203,208 ****
--- 200,247 ----
  			}
  			break;
  
+ /* BEGIN STRANGE */
+ /* Do we actually use this??? */
+ 		  case 'E':		/* word equivalence class */
+ 			for (p = &buf[2]; *p != '\0'; )
+ 			{
+ 				register char *wd;
+ 				register char *eqwd;
+ 				char delim;
+ 				register STAB *s;
+ 
+ 				while (*p != '\0' && isspace(*p))
+ 					p++;
+ 				wd = p;
+ 				while (*p != '\0' && !isspace(*p))
+ 					p++;
+ 				delim = *p;
+ 				*p = '\0';
+ 				if (wd[0] == 0)
+ 				{
+ 					*p = delim;
+ 					break;
+ 				}
+ 				/*
+ 				 * Cannot use setclass here as we
+ 				 * need the sym entry
+ 				 */
+ 				s = stab(wd, ST_CLASS, ST_ENTER);
+ 				setbitn(buf[1], s->s_class);
+ 				*p = delim;
+ 				while (*p != '\0' && isspace(*p))
+ 					p++;
+ 				eqwd = p;
+ 				while (*p != '\0' && !isspace(*p))
+ 					p++;
+ 				delim = *p;
+ 				*p = 0;
+ 				makeequiv(buf[1], eqwd, s);
+ 				*p = delim;
+ 			}
+ 			break;
+ /* END STRANGE */
+ 
  		  case 'M':		/* define mailer */
  			makemailer(&buf[1]);
  			break;
***************
*** 314,324 ****
  	{
  		register STAB *s;
  		char wordbuf[MAXNAME+1];
! 
! 		if (sscanf(buf, fmt, wordbuf) != 1)
  			continue;
  		s = stab(wordbuf, ST_CLASS, ST_ENTER);
  		setbitn(class, s->s_class);
  	}
  
  	(void) fclose(f);
--- 353,382 ----
  	{
  		register STAB *s;
  		char wordbuf[MAXNAME+1];
! 		char equivbuf[MAXNAME+1];
! 		int nscanned;
! 		
! 		if ((nscanned = sscanf(buf, fmt, wordbuf, equivbuf)) < 1)
! 		{
! #ifdef DEBUG
! 			if (tTd(86, 8))
! 				printf("fileclass: found nothing on line.\n");
! #endif DEBUG
  			continue;
+ 		}
+ #ifdef DEBUG
+ 		if (tTd(86, 8))
+ 		{
+ 			printf("fileclass: found word \"%s\"", wordbuf);
+ 			if (nscanned > 1)
+ 				printf(" => \"%s\"", equivbuf);
+ 			printf(".\n");
+ 		}
+ #endif DEBUG
  		s = stab(wordbuf, ST_CLASS, ST_ENTER);
  		setbitn(class, s->s_class);
+ 		if (nscanned > 1)
+ 			makeequiv(class, equivbuf, s);
  	}
  
  	(void) fclose(f);
===================================================================
RCS file: RCS/sendmail.h,v
retrieving revision 1.1
diff -c -r1.1 sendmail.h
*** /tmp/,RCSt1001795	Thu Oct 31 18:03:32 1985
--- sendmail.h	Sat Oct 19 03:03:24 1985
***************
*** 57,64 ****
--- 57,69 ----
  #define BYTEBITS	8	/* number of bits in a byte */
  
  /* internal macros */
+ #if BYTEBITS == 8 && (defined(vax) || defined(mc68000) || defined(ns32000))
+ #define _BITWORD(bit)	((bit) >> 5)
+ #define	_BITBIT(bit)	(1 << ((bit) & (BYTEBITS * sizeof (int) - 1)))
+ #else BYTEBITS == 8
  #define _BITWORD(bit)	(bit / (BYTEBITS * sizeof (int)))
  #define _BITBIT(bit)	(1 << (bit % (BYTEBITS * sizeof (int))))
+ #endif BYTEBITS == 8
  
  typedef int	BITMAP[BITMAPBYTES / sizeof (int)];
  
***************
*** 321,326 ****
--- 326,334 ----
  # define HOSTBEGIN	'\035'	/* hostname lookup begin */
  # define HOSTEND	'\036'	/* hostname lookup end */
  
+ /* canonicalization */
+ # define EQUIVREPL	'\037'	/* equivalence class replacement */
+ 
  /* \001 is also reserved as the macro expansion character */
  /*
  **  Information about hosts that we have looked up recently.
***************
*** 352,357 ****
--- 360,379 ----
  **  Symbol table definitions
  */
  
+ /*
+  * The canonicalizing operator $&n looks up names and replacements.
+  * The replacment name(s) for a name are stored in the following
+  * linked list, which is then attached to the symbol table entry.
+  */
+ struct equivstr
+ {
+ 	char		class;		/* class the replacement is in */
+ 	char		*equivname;	/* the replacement name */
+ 	struct equivstr *next;		/* linked list */
+ };
+ 
+ typedef struct equivstr EQUIV;
+ 
  struct symtab
  {
  	char		*s_name;	/* name to be entered */
***************
*** 367,372 ****
--- 389,395 ----
  		HOSTINFO	sv_host;	/* host information */
  # endif HOSTINFO
  	}	s_value;
+ 	EQUIV		*s_equiv;	/* list of replacements */
  };
  
  typedef struct symtab	STAB;
***************
*** 386,391 ****
--- 409,415 ----
  # define s_host		s_value.sv_host
  
  extern STAB	*stab();
+ extern char	*getequiv();
  
  /* opcodes to stab */
  # define ST_FIND	0	/* find entry */
===================================================================
RCS file: RCS/stab.c,v
retrieving revision 1.1
diff -c -r1.1 stab.c
*** /tmp/,RCSt1001795	Thu Oct 31 18:03:39 1985
--- stab.c	Fri Oct 18 19:39:27 1985
***************
*** 118,120 ****
--- 118,221 ----
  
  	return (s);
  }
+ 
+ /*
+ **  MAKEEQUIV -- make a name equivalence entry
+ **
+ **	Parameters:
+ **		class -- the class the equivalence is in.
+ **		name -- the replacement (new) name.
+ **		s -- the symbol table entry of the source (old) name.
+ **
+ **	Returns:
+ **		none.
+ **
+ **	Side Effects:
+ **		Updates the equivalences attached to `s'.
+ **
+ **	Author:
+ **		Bruce Israel -- 9/4/84.
+ **		Fixes 17 Oct 1985, Chris Torek
+ */
+ 
+ makeequiv(class, name, s)
+ 	register int class;
+ 	char *name;
+ 	register STAB *s;
+ {
+ 	register EQUIV *eq;
+ 
+ 	for (eq = s->s_equiv; eq != NULL; eq = eq->next)
+ 	{
+ 		if (eq->class != class)
+ 			continue;
+ 
+ 		/*
+ 		 * To replace, or to reject: that is the question.
+ 		 * Whether 'tis nobler to suffer the slings and
+ 		 * arrows of outrageous remappings, or, by opposing,
+ 		 * to end them.  ...Chris
+ 		 */
+ #ifdef DEBUG
+ 		if (tTd(87, 5))
+ 			printf("makeequiv: attempt to replace \"%s\" => \"%s\" (class %c)\n\twith  => \"%s\" (ignored).\n",
+ 				s->s_name, eq->equivname, class, name);
+ #endif DEBUG
+ #ifdef notdef
+ 		free(eq->equivname);
+ 		eq->equivname = newstr(name);
+ #endif notdef
+ 		return;
+ 	}
+ 	eq = (EQUIV *)xalloc(sizeof *eq);
+ 	eq->class = class;
+ 	eq->equivname = newstr(name);
+ 	eq->next = s->s_equiv;
+ 	s->s_equiv = eq;
+ }
+ 
+ /*
+ **  GETEQUIV -- get the replacement name for a stab entry given a
+ **  class.
+ **
+ **	Parameters:
+ **		class -- the replacement class.
+ **		s -- the old name.
+ **
+ **	Returns:
+ **		a pointer to a string containing the new name.
+ **		If the symbol table entry has no translation
+ **		name, the original name will be returned.
+ **
+ **	Side Effects:
+ **		none.
+ **
+ **	Author:
+ **		Bruce Israel -- 9/4/84.
+ */
+ 
+ char *
+ getequiv(class, s)
+ 	register int  class;
+ 	register STAB *s;
+ {
+ 	register EQUIV *eq;
+ 
+ 	for (eq = s->s_equiv; eq != NULL; eq = eq->next)
+ 	{
+ 		if (eq->class != class)
+ 			continue;
+ #ifdef DEBUG
+ 		if (tTd(87, 5))
+ 			printf("getequiv: \"%s\" => \"%s\" (class %c).\n",
+ 				s->s_name, eq->equivname, class);
+ #endif DEBUG
+ 		return (eq->equivname);
+ 	}
+ #ifdef DEBUG
+ 	if (tTd(87, 5))
+ 		printf("getequiv: \"%s\" not mapped in class %c; unchanged.\n",
+ 			s->s_name);
+ #endif DEBUG
+ 	return (s->s_name);
+ }
//go.sysin dd *
if [ `wc -c < 4.3diffs` != 13636 ]; then
	made=FALSE
	/bin/echo 'error transmitting "4.3diffs" --'
	/bin/echo 'length should be 13636, not' `wc -c < 4.3diffs`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 4.3diffs
	/bin/echo -n '	'; /bin/ls -ld 4.3diffs
fi
/bin/echo 'Extracting README'
sed 's/^X//' <<'//go.sysin dd *' >README
This documents the University of Maryland modifications to sendmail to
allow translation tables to be used in sendmail.cf.

There are 5 files here.  They are:

4.2diffs
4.3diffs - these are diff -c listings for the changes to the sendmail
    source.  The changes exist in 5 files, parseaddr.c (the meat
    of the changes), readcf.c, stab.c main.c and sendmail.h.

mimsy.cf - The standard University of Maryland .cf file.  This file is
    almost completely host independent, with only the real name, UUCP
    name, and valid local domain definitions hardcoded into Define's.
    Any other system information has been moved to files in the
    directory /usr/lib/hostnames/.

host-doc - This documents the various files in /usr/lib/hostnames/
    that are used by the sendmail.cf file.  It is intended to be a
    README for that directory.

names - this is a shell script that makes a translation table for all
    local network users.  It has hard-coded into it all machines on
    the local network, and when run on a machine, builds a file of
    accounts and aliases on the local network but NOT on the current
    host.  This file is used by the .cf file to route mail to users on
    the local net.  In this fashion, if JOE has an account on one
    local machine, mail to JOE@<any-local-machine> will get to him.
    If JOE has an account on multiple machines, it will route his
    mail to one of them and assume that he has a .forward.  Note
    that this requires that names on local network machines be unique
    (i.e. two different people would not be given the same account).

- How to use these mods -

After installing the source mods, you will have a sendmail executable
that has this extra feature of translation tables.  In normal
sendmails, you use the 'C' and 'F' commands in the .cf file to define
classes (C for classes define in the .cf file, and F for classes
defined in an external file).  Translation tables are defined with
the 'E' and 'F' commands.  A class is a list of items.  A translation
class is a list of pairs, items and the translated version of the
item.  i.e. a translation file for some uucp sites to be routed (i.e.
pathalias stuff) could look like:

seismo	umcp-cs!seismo
chimera	gymble!harvard!chimera
godot	gymble!harvard!think
elsie	elsie
ihnp4	umcp-cs!seismo!ihnp4

To read in a translation table from a file, use 'F' like always but
give a format string with two %s's in it, i.e.

FU/usr/lib/hostnames/uucp-routing %s%s

and both the name and its translation would be read in.  The 'E'
command is like 'C'.  It reads pairs of items on the current line (and
continuation lines) and groups them together.  i.e. a list of people
and the machine that they use might be:

EPjoe site1 bill site2 john site1 ed site3

E was added for orthogonality with C, and is unused in the sample
sendmail.cf file enclosed.

Accessing Translations

Normally in sendmail you use $<number> in a RHS to access items
matched on the LHS.  If you want the translation instead, follow the $
with an &, i.e. $&3.  For example, rules using the above classes might
look like:

R$=U!$+		$:$&1!$2	Expand UUCP paths from file
R$=P		$1@$&1		Route mail for users to their machine

Notice that both $num and $&num can be used together.  If a
class was not specified as a translation table, then the translation
of any item in that class is equivalent to the item itself.

For more information, see the examples in mimsy.cf or send mail to
israel at mimsy.umd.edu (Arpanet) or seismo!umcp-cs!israel (Usenet).

Enjoy.
//go.sysin dd *
if [ `wc -c < README` != 3527 ]; then
	made=FALSE
	/bin/echo 'error transmitting "README" --'
	/bin/echo 'length should be 3527, not' `wc -c < README`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 README
	/bin/echo -n '	'; /bin/ls -ld README
fi
/bin/echo 'Extracting host-doc'
sed 's/^X//' <<'//go.sysin dd *' >host-doc
This file documents the directory /usr/lib/hostnames that is
frequently used in sendmail.cf.  It is intended to be a README for
that directory.

This directory contains all routing tables for sendmail.  The different files
are used to route mail to different hosts on various nets.  The files are:

combined-hosts	This file contains sites that are known to be on both the
		arpanet and USEnet.  For such sites, mail is routed via
		the ARPAnet if a specific delivery method (i.e. .UUCP) is
		not specified.

csnet		This file contains the list of CSNet Phonenet sites.  It is
		in the form '<alias> <real-name>'.

cur-site	Aliases for the local site.

locals		Aliases for all local area network sites.  Lines are of
		the form '<site-alias> <real-site-name>'.

mailhosts	list of users and mailing lists on the other machines of
		the local network.  Lines are of the form:
		<name> <name>@<site>
		This file is created by the 'names' executable nightly.

relays		list of domains and an accessible host that knows how
		to relay all mail intended for that domain.  For
		example, 'csnet csnet-relay.arpa'.

specials	list of hosts that need to be specially processed, and
		the real way to get to that host.  This can be used for
		defining special, non-arpanet hosts or hosts who need
		a special path (like if a connection is broken).  Each
		line is '<site> <real-site-with-routing>'.  For example:
		if ARPASITE's arpanet connection is down indefinitely, you
		might put in: 'ARPASITE ARPASITE.uucp at ARPANEIGHBOR.uucp' and
		then mail will be routed to ARPANEIGHBOR via UUCP and then
		to ARPASITE.  Because of the purpose of this file, any
		path in here overrides all other paths (including user
		specified paths).

uucp-direct	List of sites that this site talks to directly via UUCP.

uucp-routing	List of UUCPnet sites that we know how to route to.
		Lines are of the form '<name> <route>!<name>'.
//go.sysin dd *
if [ `wc -c < host-doc` != 1903 ]; then
	made=FALSE
	/bin/echo 'error transmitting "host-doc" --'
	/bin/echo 'length should be 1903, not' `wc -c < host-doc`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 host-doc
	/bin/echo -n '	'; /bin/ls -ld host-doc
fi
/bin/echo 'Extracting mimsy.cf'
sed 's/^X//' <<'//go.sysin dd *' >mimsy.cf
############################################################
############################################################
#####
#####		SENDMAIL CONFIGURATION FILE
#####
#####		@(#) U of Maryland, October 1985
#####               - Bruce Israel israel at mimsy.umd.edu,
#####                              umcp-cs!israel
#####
############################################################
############################################################



############################################################
###	local info
############################################################

# load to start queuing.  (busted--never queue.  MDW 8/3/84)
Ox50

# UUCP name
DUumcp-cs
CU$U

Dwmimsy

# internet hostname for current host - put into a file for portability
Fw/usr/lib/hostnames/cur-site
DA$w

# domain
DDumd.edu
CDumd.edu arpa csnet uucp

# official hostname
Dj$w.$D

# csnet names
FC/usr/lib/hostnames/csnet %s%s

# domain relays, (domain relay-site)
FR/usr/lib/hostnames/relays %s%s

# UUCP hosts that we talk directly to 
FW/usr/lib/hostnames/uucp-direct

# users mailhosts, local network people (user user at real-host)
FH/usr/lib/hostnames/mailhosts %s%s

# local network sites (site-alias real-site-name)
FL/usr/lib/hostnames/locals %s%s

# uucp routing table (site uucp-path!site)
FP/usr/lib/hostnames/uucp-routing %s%s
FP/usr/lib/hostnames/uucp-direct

# combination uucp/arpanet hosts, all local hosts are networked
FX/usr/lib/hostnames/combined-hosts
FX/usr/lib/hostnames/locals %s

# Hosts to handle specially (site override-path)
FS/usr/lib/hostnames/specials %s%s

############################################################
#
#	General configuration information
#
#	This information is basically just "boiler-plate"; it must be
#	there, but is essentially constant.
#
#	Information in this file should be independent of location --
#	i.e., although there are some policy decisions made, they are
#	not specific to Berkeley per se.
#
#		@(#)base.m4	4.3		8/30/83
#
############################################################

#	@(#)version.m4	4.7		8/31/83

DV4.7

##########################
###   Special macros   ###
##########################

# my name
DnMAILER-DAEMON
# UNIX header format
DlFrom $g  $d  $y
# delimiter (operator) characters
Do.:%@!^=/[]
# format of a total name
# yucky style... Dq$g$?x ($x)$.
Dq$?x$x $.<$g>
# SMTP login message
De$j Sendmail $v/$V (with U of MD hacks) ready at $b

# forwarding host -- redefine this if you can't talk to the relay directly
DF$R

###################
###   Options   ###
###################

# location of alias file
OA/usr/lib/aliases
# default delivery mode (deliver in background)
Odbackground
# (don't) connect to "expensive" mailers
#Oc
# temporary file mode
OF0644
# default GID
Og1
# location of help file
OH/usr/lib/sendmail.hf
# log level
OL9
# default messages to old style
Oo
# queue directory
OQ/usr/spool/mqueue
# read timeout -- violates protocols
Or2h
# status file
OS/usr/lib/sendmail.st
# queue up everything before starting transmission
Os
# default timeout interval
OT9d
# time zone names (V6 only)
OtEST,EDT
# default UID
Ou1

###############################
###   Message precedences   ###
###############################

Pfirst-class=0
Pspecial-delivery=100
Pjunk=-100

#########################
###   Trusted users   ###
#########################

Troot
Tdaemon
Tuucp
Tmark
Tisrael
Tnetwork
Ttewok

#############################
###   Format of headers   ###
#############################

H?P?Return-Path: <$g>
HReceived: $?sfrom $s $.by $j ($v/$V)
	id $i; $b
H?D?Resent-Date: $a
H?D?Date: $a
H?F?Resent-From: $q
H?F?From: $q
H?x?Full-Name: $x
HSubject:
# HPosted-Date: $a
# H?l?Received-Date: $b
H?M?Resent-Message-Id: <$t.$i@$j>
H?M?Message-Id: <$t.$i@$j>

###########################
###   Rewriting rules   ###
###########################


################################
#  Sender Field Pre-rewriting  #
################################
S1
#R$*<$*>$*		$1$2$3				defocus

###################################
#  Recipient Field Pre-rewriting  #
###################################
S2
#R$*<$*>$*		$1$2$3				defocus

#################################
#  Final Output Post-rewriting  #
#################################
S4

R@			$@				handle <> error addr

# externalize local domain info
R$*<$*LOCAL>$*		$1<$2$D>$3			change local info
R$*<$+>$*		$1$2$3				defocus
R@$+:$+:$+		$@@$1,$2:$3			<route-addr> canonical

# UUCP must always be presented in old form
R$+@$-.UUCP		$2!$1				u at h.UUCP => h!u

# delete duplicate local names -- mostly for arpaproto.mc
R$+%$=w@$=w		$1@$3				u%UCB at UCB => u at UCB
R$+%$=w@$=w.$=D		$1@$3.ARPA			u%UCB at UCB => u at UCB

###########################
#  Name Canonicalization  #
###########################
S3

# handle "from:<>" special case
R<>			$@@				turn into magic token

# basic textual canonicalization
R$*<$+>$*		$2				basic RFC821/822 parsing
R$+ at $+		$1@$2				"at" -> "@" for RFC 822
R$*<$*>$*		$1$2$3				in case recursive

# make sure <@a, at b, at c:user at d> syntax is easy to parse -- undone later
R@$+,$+			@$1:$2				change all "," to ":"

# localize and dispose of domain-based addresses
R@$+:$+			$@$>6<@$1>:$2			handle <route-addr>

# more miscellaneous cleanup
R$+:$*;@$+		$@$>6$1:$2;@$3			list syntax
R$+@$+			$:$1<@$2>			focus on domain
R$+<$+@$+>		$1$2<@$3>			move gaze right
R$+<@$+>		$@$>6$1<@$2>			already canonical

# convert old-style addresses to a domain-based address
R$+%$+			$@$>6$1<@$2>			user%host
R$-:$+			$@$>6$2<@$1>			host:user
R$-.$+			$@$>6$2<@$1>			host.user
R$+^$+			$1!$2				convert ^ to !
R$-!$+			$@$>6$2<@$1.UUCP>		resolve uucp names
R$-=$+			$@$>6$2<@$1.BITNET>		resolve bitnet names
R$*			$@$>6$1			process everything else

############################################################
############################################################
#####
#####		RULESET ZERO PREAMBLE
#####
#####	The beginning of ruleset zero is constant through all
#####	configurations.
#####
#####		@(#)zerobase.m4	4.1		7/25/83
#####
############################################################
############################################################

S0

# first make canonical
R$*<$*>$*		$1$2$3				defocus
R$+			$:$>3$1				make canonical

# handle special cases.....
R@			$#local$:MAILER-DAEMON		handle <> form
R$*<@[$+]>$*		$#tcp$@[$2]$:$1@[$2]$3		numeric internet spec

# arrange for local names to be fully qualified
R$*<$+.ARPA.LOCAL>$*	$1<$2.ARPA>$3			because ARPA is a host

# now delete the local info
R$*<$*$=w.LOCAL>$*	$1<$2>$4			thishost.LOCAL
R$*<$*$=w.ARPA>$*	$1<$2>$4			thishost.ARPA
R$*<$*$=w.UUCP>$*	$1<$2>$4			thishost.UUCP
R$*<$*$=w>$*		$1<$2>$4			thishost
R$*<$*.>$*		$1<$2>$3			drop trailing dot
R<@>:$*			$@$>0$1				retry after route strip
R$*<@>			$@$>0$1				strip null trash & retry

##################################
#  End of ruleset zero preamble  #
##################################


################################################
###  Machine dependent part of ruleset zero  ###
################################################

# Do host routing

R$+<@$=w.$=D>	$#local$:$1	If local host, deliver
R$+<@$=w.$D>	$#local$:$1	If local host, deliver
R$+<@$=w>	$#local$:$1	If local host, deliver

# Do UUCP delivery for known UUCP sites
R<@$=W.UUCP>$+		$#uucp$@$1$:$2			@host.UUCP: ...
R$+<@$=W.UUCP>		$#uucp$@$2$:$1			user at host.UUCP
R$+<@$-.UUCP>		$:$1<@$2.ARPA>			if not uucp, try net

# resolve ARPA names we can handle locally
R$*<@$-.ARPA>$*		$#tcp$@$2$:$1<@$2.ARPA>$3	remove ARPA domain
R$*<@$+>$*		$#tcp$@$2$:$1<@$2>$3		deliver w/ domain

# catch whatever may have fallen thru the cracks.
R$+%$+			$:$>28$1%$2			if it still has @ or %,
R$+@$+			$:$>28$1@$2			 then retry after doing
R$+<@$+>		$@$>0$1<@$2>			 simple re-canon.

# remaining names must be local
R$+			$#local$:$1			everything else

############################################################
############################################################
#####
#####		Local and Program Mailer specification
#####
#####		@(#)localm.m4	4.1		7/25/83
#####
############################################################
############################################################

Mlocal,	P=/bin/mail, F=lsDFMmn, S=10, R=20, A=mail -d $u

# 4.3 should be:
#Mlocal,	P=/bin/mail, F=rlsDFMmn, S=10, R=20, A=mail -d $u

Mprog,	P=/bin/sh,   F=lsDFMe,   S=10, R=20, A=sh -c $u

S10
R@			MAILER-DAEMON			errors to mailer-daemon

############################################################
############################################################
#####
#####		Arpanet TCP Mailer specification
#####
#####		@(#)tcpm.m4	4.1		7/25/83
#####
############################################################
############################################################

Mtcp,	P=[IPC], F=msDFMueXL, S=14, R=14, A=IPC $h, E=\r\n

# 4.3 version is:
#Mtcp,	P=[IPC], F=mDFMueXL, S=14, R=14, A=IPC $h, E=\r\n

S14

# pass <route-addr>'s through
R<@$+>$*		$@<@$1>$2			resolve <route-addr>

# map colons to dots everywhere.....
R$*:$*			$1.$2				map colons to dots

# handle the simple case....
R$+<@$-.ARPA>		$@$1<@$2.ARPA>			user at host.ARPA

# output local hosts in user%host at mimsy syntax
R$+<@LOCAL>		$@$1<@$A>			local names
R$+<@$+.LOCAL>		$@$1%$2<@$A>			local hosts

# handle other external cases
R$+<@$->		$@$1<@$2>			no .ARPA on simple names
R$+<@$+.$-.ARPA>	$@$1%$2<@$3.ARPA>		approximate something
R$+<@[$+]>		$@$1<@[$2]>			already ok

# convert remaining addresses to old format and externalize appropriately
R$+			$:$>5$1				=> old format
R$-:$+			$@$1.$2<@$A>			convert berk hosts
R$+<@$+>		$@$1<@$2>			don't pessmize
R$+			$:$1<@$w.$D>			tack on our hostname


#####################################################
#  General code to convert back to old style names  #
#####################################################
S5

R$+<@$-.LOCAL>		$2:$1				u at h.LOCAL => h:u
R$+<@$-.UUCP>		$2!$1				u at host.UUCP => host!u
R$+@$+.ARPA		$1@$2				u at host.ARPA => u at host


Muucp,	P=/usr/bin/uux, F=sDFMhuU, S=13, R=23, M=100000,
	A=uux - -r $h!rmail ($u)

# 4.3 version is:
#Muucp,	P=/usr/bin/uux, F=sDFMmhuU, S=13, R=23, M=100000,
#	A=uux - -r -z -a$g $h!rmail ($u)

S13
R$+			$:$>5$1				convert to old style
R$=w!$+			$2				strip local name
R$*<@$->$*		$1<@$2.ARPA>$3			resolve abbreviations
R$+			$:$U!$1				stick on our host name
# R$=w!$=R:$+		$:$1!$3				ucbvax!ucbvax:xxx

S23
R$+			$:$>5$1				convert to old style
R$*<@$->$*		$1<@$2.ARPA>$3			resolve abbreviations

#
# S6 - Fully qualify all addresses.
#
# This ruleset is the last thing that Ruleset 3 calls before returning.
# it fully qualifies all addresses by making sure that the proper host
# and domain names are present.  It handles domainifying, mail host
# routing, and UUCP, csnet, and bitnet routing in the following way:
#
# 1) if the address is for an account on another machine on the local
#    network, it appends the appropriate host of the account.
# 2) local accounts have the local hostname added, and all local
#    network machines have the network domain appended.
# 3) destinations that are handled by relays, i.e. csnet and bitnet
#    domains, are forwarded to the correct relay machine.
# 4) addresses destined for a UUCP host have the explicit route
#    to that host added.
# 5) any remaining addresses without domains are assumed to be
#    ARPAnet hosts.

S6

# source routing - don't process address at all

R<@$-.$+>:$*		$@<@$1.$2>:$3
R<@$*>:$*		$@<@$1.arpa>:$2

# Handle special hosts.  (i.e. Hosts that need special handling,
#  such as broken connections, or overriding paths.

R$*<@$=S$*>		$>28$1%$&2	match on hostname only, ignoring domain

# local

R$+<@$=w.$=D>		$:$>29$1	remove all local hosts,
R$+<@$=w.$D>		$:$>29$1	  with any real local
R$+<@$=w>		$:$>29$1	  domain.

R$*			$:$>28$1	put back in canonical

# expand mailhosts
R$=H			$:$>28$&1	modify to mailhost address

R$-			$@$1<@$w.$D>	if no host, then local (fully qual)

# add local domain if needed
R$+<@$=L>		$@$1<@$&2.$D>	if local net host, use local domain
R$+<@$=L.$=D>		$@$1<@$&2.$D>	if local net host, use local domain

# do relayed domain
R$*<@$=C>		$:$1<@$&2.CSNET>	add domain to csnet sites
R$*<@$+.$=R>		$@$1%$2.$3<@$&3>	if relayed domain, use relay

# ARPAize appropriate hosts
R$+<@$=X>		$@$1<@$2.ARPA>		if uucp and arpa, use arpa

# route known UUCP hosts
R$+<@$=P>		$:$>29$&2!$1		if unknown but UUCP, use route
R$+<@$=P.UUCP>		$:$>29$&2!$1		if known but UUCP, use route

# add ARPA domain to domainless hosts

R$*<@$->		$@$1<@$2.ARPA>		If still domainless, then ARPA
R$*<$*>			$@$1<$2>		Return if canonical

R$*!!$*			$1!$2			Eliminate all double-bangs
R$-!$+			$@$2<@$1.UUCP>		convert non-can. UUCP to canon.

# S29 - recursively remove all local host names with either the proper
#	domain, an ARPA domain, or no domain.

S29

R$+@$=w.$=D		$@$>29$1
R$+@$=w.$D		$@$>29$1
R$+@$=w			$@$>29$1

R$+%$=w.$=D		$@$>29$1
R$+%$=w.$D		$@$>29$1
R$+%$=w			$@$>29$1

R$=w!$+			$@$>29$2

# convert any non-canonical to canonical

S28
R$*<@$*>		$@$1<@$2>		Already canonical, accept

R$*%$*			$1@$2			Convert all %'s to @'s
R$*@$*@$*		$1%$2@$3		Convert all but last back

R$*@$*			$@$1<@$2>		make into canonical
//go.sysin dd *
if [ `wc -c < mimsy.cf` != 13094 ]; then
	made=FALSE
	/bin/echo 'error transmitting "mimsy.cf" --'
	/bin/echo 'length should be 13094, not' `wc -c < mimsy.cf`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 644 mimsy.cf
	/bin/echo -n '	'; /bin/ls -ld mimsy.cf
fi
/bin/echo 'Extracting names'
sed 's/^X//' <<'//go.sysin dd *' >names
#! /bin/csh -f
#
# names - generate a list of aliases on the current host for addresses
#	that are on the local network but NOT on the current host.
#	these will be used by the mail system so that any addresses on
#	the local network appear to be addresses local to the current
#	host.
#
#	method: all addresses on the local net are processed thru
#	  getting their aliases and passwd files.
#	  aliases on any machine take priority over accounts anywhere.
#	  current machine takes priority over other machines.
#
# temp files:	uniq - unique names in list (for eliminating repeats)
#		list - generated list
#		tmp1,2 - intermediate processing files
#
# set the machines on the local network here (in order of priority).
set locals = (mimsy tove gymble gyre brilig)
#
set uniq = /tmp/names.u.$$ list = /tmp/names.l.$$
set tmp1 = /tmp/names.1.$$ tmp2 = /tmp/names.2.$$
cp /dev/null $list
cp /dev/null $uniq
foreach file ( /usr/lib/aliases /etc/passwd )
# process current host addresses from $file first
	awk -F: '{print $1}' < $file | grep -v "#" | grep -v '^$' | \
	    sort | uniq> $tmp1
# put current host addresses into uniq file ONLY.  That way they won't be
# generated from another machine, and the mail system will get them itself
# from local files.
	cat $tmp1 $uniq | sort -o $uniq
	foreach host ( $locals )
	    set work = tmp/$host.$file:t
	    rcp ${host}:${file} $work
# get name from file, ignoring comments and blank lines
	    awk -F: '{print $1}' <$work | grep -v "#" | \
		grep -v '^$' | sort | uniq > $tmp1
# get only names that haven't already been used (i.e. $tmp1 - $uniq)
	    comm -13 $uniq $tmp1 > $tmp2
# add new names to list with host, and to unique names list
	    awk '{print $1 " " $1 "@'$host'" }' < $tmp2 >> $list
	    cat $tmp2 $uniq | sort -o $uniq
	end
end
sort $list
rm -f $uniq $list $tmp1 $tmp2
//go.sysin dd *
if [ `wc -c < names` != 1834 ]; then
	made=FALSE
	/bin/echo 'error transmitting "names" --'
	/bin/echo 'length should be 1834, not' `wc -c < names`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	/bin/chmod 755 names
	/bin/echo -n '	'; /bin/ls -ld names
fi



More information about the Mod.sources mailing list