apb/mje900117 patches to smail 2.5

Alan P. Barrett barrett at Daisy.EE.UND.AC.ZA
Wed Feb 20 08:32:32 AEST 1991


Archive-name: smail2.5/apbmje900117

apb/mje900117 patches to smail 2.5
==================================

Here are some patches for smail version 2.5.  The diffs below are
relative to the original distribution of smail2.5 (patchlevel 00).  This
patch will not change the patchlevel in 'patchlevel.h', because it is
not an 'official' patch, but it will change the version string in
defs.h.

Changes marked [apb] are mine (Alan Barrett <barrett at ee.und.ac.za>), and
changes marked [mje] are originally by Mark Elkins <mje at olsa99.UUCP>.  I
have made minor modifications to his code, including adding #defines in
defs.h and putting the code inside #ifdef ...  #endif where appropriate.


How to apply the patch
----------------------

(1)  If you have a version of the 'patch' program that understands
     unidiff format, just pipe this file into the standard input of a
     command like "patch -l -d /usr/local/src/smail".  The '-l' option
     tells patch not to be too fussy about white space (because I have
     been working from sources in which the white space has been munged
     -- sorry).  The '-d' option tells it to apply the patch to files in
     the directory whose name appears next.  Make sure you use the right
     directory name.

(2)  If your patch doesn't like unidiffs, first get Wayne Davison's
     unidiff package from comp.sources.unix volume 14.

(3)  If you don't have 'patch', use an editor and lots of patience to
     make all the changes, or get 'patch' first.


List of changes
---------------

There are a few bug fixes:

(1)  The binary search routines were wrong (in two separate places,
     dealing with the paths file and the fullnames file).  [apb]

(2)  Addresses that contained characters that are special to the shell
     were not treated properly.  Now the address is quoted in such a way
     that the shell will never clobber them.  (Changed definition of
     LARG and RARG in defs.h, added calls to sh_quote() in several
     places.)  [apb]

(3)  There are a few more places where the address should be checked for
     "postmaster".  [apb]

(4)  The stderr file should be opened in append mode, so that stdout and
     stderr don't get garbled in the "standard error follows" section of
     a return-to-sender error message.  [apb]

(5)  When a line in the paths file starts with a dot then the '%s' has
     to be replaced by 'domain!user', not just by the 'user' name.
     [apb]

There are a few enhancements:

(1)  You can define FAILPOSTMASTER (in defs.h) to make the postmaster
     get a copy of all failed mail messages.  [apb]

(2)  You can define DOT_EOF and DOT_EOF_ALL (in defs.h) to modify
     smail's behaviour when it sees a message that contains a line with
     only a dot on it.  The original version treated it like EOF, but
     now you can tell the program to (a) always treat a dot line as EOF,
     (b) treat a dot line as EOF only if input is from a tty, or (c)
     tell the program to never treat a dot line as EOF.  [apb]

(3)  The arpadate function now returns a string that is legal according
     to a strict interpretation of RFC822.  See the comments in the code
     for details.  [apb]

(4)  "postmast" is now treated exactly like "postmaster".  This is for
     compatibility with sites that have 8-character user names.  [apb]

(5)  Look for a smart-host even if routing is not set to REROUTE.  This
     may not have been such a good idea, but I like it and it works
     better now that change number 9 below has also been made.  [apb]

(6)  If we get a local address with a '%' in it, change the '%' to an
     '@' and try again.  [apb]

(7)  If a path cannot be found in the usual way, check whether the
     desired domain (or host) name is a truncated version of a known
     domain name.  This will find 'abc.def.ghi' in the paths file if it
     is looking for 'abc.def'.  Controlled by TRUNC_DOM in defs.h.
     [mje]

(8)  If that also didn't work, see whether the leftmost part of the
     desired domain (or host) name matches the leftmost part of any
     domain (or host) name known to the paths file.  This will find
     'abc.something.else' or plain 'abc' in tha paths file if it is
     looking for 'abc.def'.  Controlled by TRUNC_DOM_LEFT in defs.h.
     [mje]

(9)  If that also didn't work, see if the leftmost part of the desired
     domain (or host) name matches any of the uucp neighbours reported
     by the uuname command.  This will find 'abc' in your L.sys or
     Systems file if it is looking for 'abc.def.ghi'.  Controlled by
     UUNAME in defs.h.  [mje]

(10) Remove all explicit routing through the local system during the
     alias processing stage, and be much more robust about deciding when
     a domain name refers to the local system.  Now, paths like
     hostname!hostname.hostdomain!hostname!user will correctly resolve
     to 'user' instead of being treated as erroneous.  There are still
     some problems that may show up if routing = REROUTE and the local
     system name appears at the end of a path via other systems.  [apb]

(11) Try harder to be sensible about quoted local parts in addresses
     like <"user"@myname>, or even <"user at some.where"@myname.mydom>.
     [apb]

(12) When looking in the paths file for a route to something like
     'abc.def.ghi,' first try the full name without an initial dot
     ('abc.def.ghi'), before trying with an initial dot
     ('.abc.def.ghi').  Both might appear in the paths file, and we want
     to find the right one first.  If that is not found, then continue
     as before, looking for '.def.ghi', 'def.ghi', '.ghi' and 'ghi' in
     that order.  [apb]

(13) New RLFROM macro in defs.h allows the "From " line in mail from a
     remote user to a local user to be different from that in mail from
     a remote user to a remote user.  Useful because mail sent via uucp
     should have 'remote from thishost' in the From line, but you may
     not want 'remote from otherhost' in the From line of mail delivered
     locally.  [apb]

(14) Get real name in the From line even if the person sending the mail
     is su'ed to another user id.  [mje]

(15) Make the stderr file unbuffered, to help with debugging.  Line
     buffering would also be fine, on systems where it works.  [apb]

There are a few gratuitous changes:

(1) The version name (in defs.h).

(2)  The LMAIL macro in defs.h calls /usr/local/bin/deliver, which I
     prefer because it doesn't chop messages off when it sees a dot
     line.  "deliver" also allows one to do some fancy stuff, but I
     haven't really tried that.  [apb]

(3)  MAXCLEN in defs.h is much bigger, to allow more addresses to be
     passed with a single copy of a message.  Make sure my value isn't
     too big for your system, and remember that smail will actually
     exceed the limit you specify here, so be conservative.  [apb]

(5) The text of the return-to-sender message is altered.  [apb]

(6)  Indentation has been changed in functions that were creeping off
     the right hand side of the page.  [apb]


----- start of diffs -----

Index: Makefile
*** old/Makefile	Thu Jan 18 17:03:41 1990
--- new/Makefile	Thu Jan 18 17:04:09 1990
@@ -8,7 +8,8 @@
 #
 #LIBS	=	-lmalloc
 
-OBJECTS =	main.o map.o resolve.o deliver.o misc.o alias.o pw.o headers.o getpath.o str.o getopt.o
+OBJECTS =	main.o map.o resolve.o deliver.o misc.o alias.o pw.o \
+		headers.o getpath.o str.o getopt.o uuname.o
 
 all: smail svbinmail lcasep pathproc mkfnames nptx
 

Index: alias.c
*** old/alias.c	Thu Jan 18 17:03:42 1990
--- new/alias.c	Thu Jan 18 17:04:09 1990
@@ -61,6 +61,7 @@
 static int nargc = 0;
 static char *nargv[MAXARGS];
 
+char 	*postmaster();
 void	add_horz();
 void	load_alias(), strip_comments();
 int	recipients();
@@ -83,10 +84,10 @@
 /*
 **  alias the addresses
 */
-	int	i;
-	char	domain[SMLBUF], ubuf[SMLBUF], *user;
-	node	*addr, addrstk;
-	node	*flist,  fliststk, *u;
+      int	i;
+      char	domain[SMLBUF], ubuf[SMLBUF], *user;
+      node	*addr, addrstk;
+      node	*flist,  fliststk, *u;
 
 #ifndef SENDMAIL
 	FILE	*fp;
@@ -111,7 +112,8 @@
 	** push all of the addresses onto a stack
 	*/
 	for(i=0; i < *pargc; i++) {
-		push(addr, argv[i]);
+		removelocal (argv[i]);
+		push (addr, argv[i]);
 	}
 
 	/*
@@ -166,6 +168,9 @@
 #endif
 		user = escape(ubuf);
 
+		/* check for postmaster */
+		user = postmaster(user);
+
 		(void) strcpy(u->string, user);	/* local => elide domain */
 #ifndef SENDMAIL
 		/*
@@ -259,20 +264,23 @@
 		if (index(user, '.') != NULL)
 #endif
 		{
-			static char t_dom[SMLBUF], t_unam[SMLBUF];
-			char *t_user = res_fname(user);
-			if (t_user != NULL) {
-				if(islocal(t_user, t_dom, t_unam) == 0) {
-					/* aliased to non-local address */
-					push(addr, t_user);
-					continue;
-				}
-				if(strcmp(t_unam, user) != 0) {
-					/* aliased to different local address */
-					push(addr, t_unam);
-					continue;
-				}
+		    static char t_dom[SMLBUF], t_unam[SMLBUF];
+		    char t_userbuf[SMLBUF];
+		    char *t_user = res_fname(user);
+		    if (t_user != NULL) {
+			(void) strcpy (t_userbuf, t_user);
+			NODEBUGremovelocal (t_userbuf);
+			if(islocal(t_userbuf, t_dom, t_unam) == 0) {
+			    /* aliased to non-local address */
+			    push(addr, t_userbuf);
+			    continue;
 			}
+			if(strcmp(t_unam, user) != 0) {
+			    /* aliased to different local address */
+			    push(addr, t_unam);
+			    continue;
+			}
+		    }
 		}
 #endif
 
@@ -479,7 +487,10 @@
 		*/
 		if((islocal(b, d, u) == 0)
 		|| (strcmpic(h->string, u) != 0)) {
-			add_horz(h, b);
+			char temp[SMLBUF];
+			(void) strcpy (temp, b);
+			NODEBUGremovelocal (temp);
+			add_horz (h, temp);
 			ret = 1;
 		}
 		SKIPSPACE(p);
@@ -534,6 +545,9 @@
 	if((p = index(str, ':')) != NULL) {
 		*p = CNULL;
 	}
+	/* check for postmaster */
+	str = postmaster(str);
+
 	if((new = (node *) malloc(sizeof(node))) != NNULL) {
 		if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
 			free(new);
@@ -560,6 +574,9 @@
 {
 	char *malloc();
 	node *new;
+
+	/* check for postmaster */
+	str = postmaster(str);
 
 	if((new = (node *) malloc(sizeof(node))) != NNULL) {
 		if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {

Index: defs.h
*** old/defs.h	Thu Jan 18 17:03:42 1990
--- new/defs.h	Thu Jan 18 17:06:31 1990
@@ -17,7 +17,7 @@
 */
 
 #ifndef VERSION
-#define	VERSION	"smail2.5"
+#define	VERSION	"smail2.5+apb/mje900117"
 #endif
 
 /*#define BSD				/* if system is a Berkeley system */
@@ -97,6 +98,59 @@
 #define SMARTHOST  "smart-host"	/* pathalias alias for relay host */
 
 /*
+**  Define FAILPOSTMASTER as the string "postmaster" if you want a copy of
+**  return-to-sender messages to be sent to the local postmaster.  Define
+**  it as an empty string "" if you don't want copies.
+*/
+
+#define FAILPOSTMASTER "postmaster"	/* postmaster gets failure messages */
+/*#define FAILPOSTMASTER ""		/* postmaster does not get messages */
+
+/*
+**  DOT_EOF should be defined  and DOT_EOF_ALL should be undefined if you
+**  want a line containing just a dot to be treated as the end of a
+**  message that is read from a terminal.
+**  Both DOT_EOF and DOT_EOF_ALL should be defined if you always want
+**  a dot line to end the message, even when stdin is not a terminal.
+**  They should both be undefined if you never want a dot line to end
+**  a message (but beware, many local delivery programs will still chop
+**  the message, even if smail doesn't).
+*/
+
+#define DOT_EOF				/* do dot lines end messages */
+/*#define DOT_EOF_ALL			/* even if stdin not a tty? */
+
+/*
+** Three symbols that control some extra 'guessing' about routes.
+**
+**  TRUNC_DOM should be defined if you want to try searching through the
+**  paths file for a route that appears to have been truncated.
+**  For example, if you are looking for a route to 'abc.def'
+**  and no route can be found in the normal way, but there is a
+**  route to 'abc.def.xyz.com' then that route will be used.
+**  (But if there is a route to 'def.xyz.com' it still won't be found.)
+**  Leave the symbol undefined if you don't want this behaviour.
+**
+**  TRUNC_DOM_LEFT works like TRUNC_DOM, but it checks only the leftmost
+**  part of the domain name.  For example, if you are looking for
+**  'abc.def', TRUNC_DOM_LEFT will allow a route to 'abc' or to
+**  'abc.anything.else' to be used instead.
+**
+**  UUNAME should be defined as the name of a program that prints
+**  the names of all uucp neighbours, one per line.
+**  When an address vannot be resolved with the paths file, this is used
+**  to guess whether it is a uucp neighbour.  For example, if you are
+**  looking for 'abc.def' but can't resolve it using the paths file, but
+**  'abc' is known to your system as a uucp neighbour, then that route
+**  will be used.
+**  Leave UUNAME undefined if you don't want to make this test.
+*/
+
+#define TRUNC_DOM			/* do truncated search ? */
+/* #define TRUNC_DOM_LEFT			/* also for leftmost part? */
+#define UUNAME "/usr/bin/uuname"	/* uuname program */
+
+/*
 **  ALIAS and CASEALIAS are used only if SENDMAIL is NOT defined.
 **  Sites using sendmail have to let sendmail do the aliasing.
 **  LOWERLOGNAME maps all local login names into lower case.  This
@@ -152,6 +206,10 @@
 **	LMAIL is the command to invoke the local mail transfer agent.
 **	LARG is how to insulate metacharacters from LMAIL. 
 **	RLARG is LARG with host! on the front - to pass a uux addr to sendmail.
+**	LFROM is the From_ line in mail from a local user, and also in all
+**	mail passed on to sendmail.
+**	RFROM is the From_ line in mail from a remote user to a remote user.
+**	RLFROM is the From_ line in mail from a remote user to a local user.
 **	SENDMAIL selects one of two sets of defines below for either
 **	using sendmail or /bin/lmail.
 */	
@@ -180,9 +239,19 @@
 #define RMAIL(flags,from,sys) "%s -a%s %s - %s!rmail",UUX,from,flags,sys /* */
 /*#define RMAIL(flags,from,sys) "%s %s - %s!rmail",UUX,flags,sys /* */
 
-#define RARG(user)		" '(%s)'",user
-#define RFROM(frm,now,host) 	"From %s  %.24s remote from %s\n",frm,now,host
+#define RARG(user)		" \\(%s\\)",user
+#define RFROM(frm,now,host) 	"From %s %.24s remote from %s\n",frm,now,host
 
+/*
+** Choose one of the following definitions of RLFROM, depending on whether
+** you want the 'remote from' stuff in mail that is delivered locally.
+** Mail delivered via uux to another system uses RFROM (not RLFROM), and that
+** probably should contain the 'remote from' stuff.
+*/
+
+#define RLFROM(frm,now,host) 	"From %s!%s %.24s\n",host,frm,now /* */
+/*#define RLFROM(frm,now,host) 	RFROM(frm,now,host) /* */
+
 #ifdef SENDMAIL
 
 #define HANDLE	JUSTUUCP	/* see HANDLE definition below */
@@ -189,8 +258,8 @@
 #define ROUTING JUSTDOMAIN	/* see ROUTING definition below */
 
 #define LMAIL(frm,sys) 	"%s -em -f%s",SENDMAIL,frm
-#define LARG(user)		" '%s'",postmaster(user)
-#define RLARG(sys,frm)		" '%s!%s'",sys,frm
+#define LARG(user)		" %s",postmaster(user)
+#define RLARG(sys,frm)		" %s!%s",sys,frm
 #define LFROM(frm,now,host)	"From %s %.24s\n",frm,now
 
 #else
@@ -201,11 +270,12 @@
 #ifdef BSD
 #define LMAIL(frm,sys)		"/bin/mail"	/* BSD local delivery agent */
 #else
-#define LMAIL(frm,sys)		"/bin/lmail"	/* SV  local delivery agent */
+/*#define LMAIL(frm,sys)	"/bin/lmail"	/* SV  local delivery agent */
+#define LMAIL(frm,sys)		"/usr/local/bin/deliver" /* deliver */
 #endif
 
-#define LARG(user)		" '%s'",postmaster(user)
-#define RLARG(sys,frm)		" '%s!%s'",sys,frm
+#define LARG(user)		" %s",postmaster(user)
+#define RLARG(sys,frm)		" %s!%s",sys,frm
 #define LFROM(frm,now,host)	"From %s %.24s\n",frm,now
 
 #endif
@@ -254,7 +324,7 @@
 ** a small value (like 0).
 */
 
-#define MAXCLEN			128	/* longest command allowed (approx.)
+#define MAXCLEN			2048	/* longest command allowed (approx.)
 					/* this is to keep other's buffers
 					** from overflowing
 					*/

Index: deliver.c
*** old/deliver.c	Thu Jan 18 17:03:42 1990
--- new/deliver.c	Thu Jan 18 17:04:10 1990
@@ -32,6 +32,8 @@
 extern char arpanows[];		/* local time in arpadate format*/
 char stderrfile[20];		/* error file for stderr traping*/
 
+char *sh_quote();		/* quote string to protect it from the shell */
+
 /*
 **
 **  deliver():  hand the letter to the proper mail programs.
@@ -69,6 +71,7 @@
 	char scommand[SMLBUF];		/* retry  command issued	*/
 	char *command;			/* actual command		*/
 	char buf[SMLBUF];		/* copying rest of the letter   */
+	char temp[SMLBUF];		/* temp storage			*/
 	enum eform form;		/* holds form[i] for speed 	*/
 	long size;			/* number of bytes of message 	*/
 	char *flags;			/* flags for uux		*/
@@ -106,10 +109,11 @@
 		(void) unlink(stderrfile);
 		(void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
 		(void) mktemp(stderrfile);
-		(void) freopen(stderrfile, "w", stderr);
+		(void) freopen(stderrfile, "a", stderr);
 		if(debug != YES) {
-			(void) freopen(stderrfile, "w", stdout);
+			(void) freopen(stderrfile, "a", stdout);
 		}
+		(void) setbuf (stderr, _IONBF);
 
 		*lend = *rend = *send = '\0';
 
@@ -145,8 +149,9 @@
 			sflag = "";
 		}
 
-		(void) sprintf(lcommand, LMAIL(from, hostv[i]));
-		(void) sprintf(rcommand, RMAIL(flags, from, hostv[i]));
+		(void) sprintf(lcommand, LMAIL(sh_quote(temp,from), hostv[i]));
+		(void) sprintf(rcommand, RMAIL(flags,
+				sh_quote(temp,from), hostv[i]));
 
 /*
 **  For each address with the same host name and form, append the user
@@ -184,14 +189,19 @@
 			rend += strlen(rend);
 
 			if (form == LOCAL) {
-				(void) sprintf(lend, LARG(userv[j]));
-				(void) sprintf(send, LARG(userv[j]));
+				(void) sprintf(lend,
+				    LARG(sh_quote(temp,userv[j])));
+				(void) sprintf(send,
+				    LARG(sh_quote(temp,userv[j])));
 			} else {
-				(void) sprintf(lend, RLARG(hostv[i], userv[j]));
-				(void) sprintf(send, RLARG(hostv[i], userv[j]));
+				char host[SMLBUF], user[SMLBUF];
+				(void) sh_quote(host, hostv[i]);
+				(void) sh_quote(user, userv[i]);
+				(void) sprintf(lend, RLARG(host,user));
+				(void) sprintf(send, RLARG(host,user));
 			}
 
-			(void) sprintf(rend, RARG(userv[j]));
+			(void) sprintf(rend, RARG(sh_quote(temp,userv[j])));
 			formv[j] = SENT;
 		}
 retry:
@@ -269,7 +279,14 @@
 **  Output our From_ line.
 */
 		if (form == LOCAL) {
+			/*
+			** Mail to a local user
+			*/
 #ifdef SENDMAIL
+			/*
+			** LFROM will probably be:
+			** From xxxxx!yyyyy!uuuuu ttttt
+			*/
 			(void) sprintf(buf, LFROM(from, nows, hostname));
 			size += strlen(buf);
 			(void) fputs(buf, out);
@@ -276,13 +293,27 @@
 #else
 			char *p;
 			if((p=index(from, '!')) == NULL) {
+				/*
+				** Mail from a local user to a remote user.
+				**
+				** LFROM will probably be:
+				** From xxxxx!yyyyy!uuuuu ttttt
+				*/
 				(void) sprintf(buf,
 					LFROM(from, nows, hostname));
 				size += strlen(buf);
 				(void) fputs(buf, out);
 			} else {
-				*p = NULL;
-				(void) sprintf(buf, RFROM(p+1, nows, from));
+				/*
+				** Mail from a remote user to a remote user.
+				**
+				** RLFROM will probably be:
+				** From yyyyy!uuuuu ttttt remote from xxxxx
+				** or:
+				** From xxxxx!yyyyy!uuuuu ttttt
+				*/
+				*p = '\0';
+				(void) sprintf(buf, RLFROM(p+1, nows, from));
 				size += strlen(buf);
 				(void) fputs(buf, out);
 				*p = '!';
@@ -289,6 +320,12 @@
 			}
 #endif
 		} else {
+			/*
+			** Mail to a remote user.
+			**
+			** RFROM will probably be:
+			** From xxxxx!yyyyy!uuuuu ttttt remote from hhhhh
+			*/
 			(void) sprintf(buf, RFROM(from, nows, hostname));
 			size += strlen(buf);
 			(void) fputs(buf, out);
@@ -418,6 +455,7 @@
 {
 	char buf[SMLBUF];
 	char domain[SMLBUF], user[SMLBUF];
+	char dom[SMLBUF], usr[SMLBUF];
 	char *r;
 	FILE *fp, *out, *popen();
 	int i = 0;
@@ -428,11 +466,16 @@
 	r += strlen(r);
 
 	if(islocal(from, domain, user)) {
-		(void) sprintf(r, LARG(user));
+		(void) sprintf(r, LARG(sh_quote(usr,user)));
 	} else {
-		(void) sprintf(r, RLARG(domain, user));
+		(void) sh_quote(dom,domain);
+		(void) sh_quote(usr,user);
+		(void) sprintf(r, RLARG(dom, usr));
 	}
 
+	r += strlen(r);
+	(void) sprintf (r, " %s", FAILPOSTMASTER);
+
 	i = 0;
 	do {
 		out = popen(buf, "w");
@@ -451,11 +494,16 @@
 		return;
 	}
 
+	(void) fprintf(out, LFROM("postmaster", nows, hostname));
 	(void) fprintf(out, "Date: %s\n", arpanows);
-	(void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
-	(void) fprintf(out, "Subject: failed mail\n");
+	(void) fprintf(out, "From: Smail Mail Server <Postmaster@%s>\n", hostdomain);
+	(void) fprintf(out, "Subject: Failed mail\n");
 	(void) fprintf(out, "To: %s\n", from);
 	(void) fprintf(out, "\n");
+	(void) fprintf(out, "\
+Your message could not be delivered, because the following command returned\n\
+an error status.  Perhaps one or more of the destination addresses is\n\
+incorrect.\n\n");
 	(void) fprintf(out, "=======     command failed      =======\n\n");
 	(void) fprintf(out, " COMMAND: %s\n\n", fcommand);
 

Index: getpath.c
*** old/getpath.c	Thu Jan 18 17:03:43 1990
--- new/getpath.c	Thu Jan 18 17:04:10 1990
@@ -16,87 +16,104 @@
 **
 */
 
-getpath( key, path , cost)
+getpath( key, path , cost, trunk)
 char *key;		/* what we are looking for */
 char *path;		/* where the path results go */
 int *cost;		/* where the cost results go */
+int trunk;		/* Do we accept a truncated name ? */
 {
-	long pos, middle, hi, lo;
-	static long pathlength = 0;
-	register char *s;
-	int c;
-	static FILE *file;
-	int flag;
+    long pos, middle, hi, lo;
+    static long pathlength = 0;
+    register char *s;
+    int c;
+    static FILE *file;
+    int flag;
 
-DEBUG("getpath: looking for '%s'\n", key);
+DEBUG("getpath: looking for '%s'%s\n", key, (trunk?" (truncated)":""));
 
-	if(pathlength == 0) {	/* open file on first use */
-		if((file = fopen(pathfile, "r")) == NULL) {
-			(void) printf("can't access %s.\n", pathfile);
-			pathlength = -1;
-		} else {
-			(void) fseek(file, 0L, 2);	/* find length */
-			pathlength = ftell(file);
-		}
+    if(pathlength == 0) {	/* open file on first use */
+	if((file = fopen(pathfile, "r")) == NULL) {
+	    (void) printf("can't access %s.\n", pathfile);
+	    pathlength = -1;
+	} else {
+	    (void) fseek(file, 0L, 2);	/* find length */
+	    pathlength = ftell(file);
 	}
-	if( pathlength == -1 )
-		return( EX_OSFILE );
+    }
+    if( pathlength == -1 )
+	return( EX_OSFILE );
 
-	lo = 0;
-	hi = pathlength;
-	(void) strcpy( path, key );
-	(void) strcat( path, "\t" );
-/*
-** "Binary search routines are never written right the first time around."
-** - Robert G. Sheldon.
-*/
-	for( ;; ) {
-		pos = middle = ( hi+lo+1 )/2;
-		(void) fseek(file, pos, 0);	/* find midpoint */
-		if(pos != 0)
-			while(((c = getc(file)) != EOF) && (c != '\n'))
-				;	/* go to beginning of next line */
-		if(c == EOF) {
-			return(EX_NOHOST);
+    lo = 0;
+    hi = pathlength;
+    (void) strcpy( path, key );
+    (void) strcat( path, "\t" );
+    /*
+    ** "Binary search routines are never written right the first time around."
+    ** - Robert G. Sheldon.
+    */
+    for( ; lo <= hi ; ) {
+	pos = middle = ( hi+lo+1 )/2;
+	(void) fseek(file, pos, 0);	/* find midpoint */
+	if(pos != 0)
+	    while(((c = getc(file)) != EOF) && (c != '\n'))
+		    ;	/* go to beginning of next line */
+	if(c == EOF) {
+	    flag = 1;
+	}
+	else {
+	    for( flag = 0, s = path; flag == 0; s++ ) { /* match??? */
+		if( *s == '\0' ) {
+		    goto solved;
 		}
-		for( flag = 0, s = path; flag == 0; s++ ) { /* match??? */
-			if( *s == '\0' ) {
-				goto solved;
-			}
-			if((c = getc(file)) == EOF) {
-				return(EX_NOHOST);
-			}
-			flag = lower(c) - lower(*s);
-		} 
-		if(lo >= middle) {		/* failure? */
-			return(EX_NOHOST);
+		if((c = getc(file)) == EOF) {
+		    flag = 1;
 		}
-		if((c != EOF) && (flag < 0)) {	/* close window */
-			lo = middle;
-		} else {
-			hi = middle - 1;
-		}
+		else {
+		    flag = lower(c) - lower(*s);
+		    /*
+		    ** if trunk is set, and the end of the desired key
+		    ** coincides with a dot in the current key,
+		    ** then treat this as a match.
+		    ** For example, desired="abc.xyz.com" and
+		    ** actual="abc.xyz".
+		    */
+		    if (trunk && *s =='\t' && c == '.') {
+			while(((c = getc(file)) != EOF) && c!='\t');
+			goto solved;
+		    }
+		} 
+	    } /* end for */
 	}
-/* 
-** Now just copy the result.
-*/
-solved:
-	while(((c  = getc(file)) != EOF) && (c != '\t') && (c != '\n')) {
-		*path++ = c;
+	if(flag < 0) {	/* close window */
+	    lo = middle + 1;
+	} else {
+	    hi = middle - 1;
 	}
-	*path = '\0';
-/*
-** See if the next field on the line is numeric.
-** If so, use it as the cost for the route.
-*/
-	if(c == '\t') {
-		int tcost = -1;
-		while(((c = getc(file)) != EOF) && isdigit(c)) {
-			if(tcost < 0) tcost = 0;
-			tcost *= 10;
-			tcost += c - '0';
-		}
-		if(tcost >= 0) *cost = tcost;
+    }
+    /*
+    ** If we get here, the key was not found.
+    */
+    return(EX_NOHOST);
+    /* 
+    ** It was found.  Now just copy the result.
+    */
+    solved:
+    while(((c  = getc(file)) != EOF) && (c != '\t') && (c != '\n')) {
+	*path++ = c;
+    }
+    *path = '\0';
+    /*
+    ** See if the next field on the line is numeric.
+    ** If so, use it as the cost for the route.
+    */
+    if(c == '\t') {
+	int tcost = -1;
+	while(((c = getc(file)) != EOF) && isdigit(c)) {
+	    if(tcost < 0) tcost = 0;
+	    tcost *= 10;
+	    tcost += c - '0';
 	}
-	return (EX_OK);
+	if(tcost >= 0) *cost = tcost;
+    }
+    return (EX_OK);
 }

Index: headers.c
*** old/headers.c	Thu Jan 18 17:03:43 1990
--- new/headers.c	Thu Jan 18 17:04:11 1990
@@ -45,7 +45,86 @@
 	NULL 		,	NULL		,	'N'
 };
 
+/*
+**
+** removelocal(): delete local domain name from an address.
+**
+** The following rules are applied repeatedly until they all fail.
+**
+** @myname:address		==>	address
+** @myname, at elsewhere:address	==>	@elsewhere:address
+** address at myname		==>	address
+** myname!address		==>	address
+** "address"			==>	address
+** address%domain		==>	address at domain
+**
+**  This function modifies the input address in-place.
+**
+*/
 
+void
+removelocal (address)
+char *address;
+{
+/**
+    repeat forever  {{ actually, until a 'terminate' statement }}
+	parse the address to {is-local, localpart, domain}.
+	    {{ use the 'islocal' function to do this }}
+	if is-local then
+	    {{ there were no '@' or '!' chars in the address, or
+		there were such chars but the system name was
+		recognised as the local system }}
+	    select
+	      localpart is quoted :
+		    address := removequotes( localpart )
+	      localpart is not quoted and contains "@" or "!" :
+		    address := localpart
+	      localpart is not quoted, does not contain "@" or "!",
+					    but contains "%" :
+		    address := convert-%-to-@ ( localpart )
+	      localpart is not quoted and does not contain "@", "!" or "%" :
+		    address := localpart
+		    return
+	    endselect
+	else
+	    return
+	endif
+    endrepeat
+**/
+    char localpart[SMLBUF], domain[SMLBUF], temp[SMLBUF], *p;
+    extern int rfc822_do_unquote();
+
+    for (;;) {
+	
+	DEBUG("removelocal: checking '%s'\n", address);
+
+	if (islocal (address, domain, localpart)) {
+	    /* check if localpart is quoted */
+	    if ( rfc822_do_unquote (temp, localpart) ) {
+		(void) strcpy (address, temp);
+	    }
+	    /* check if localpart contains '@' or '!' */
+	    else if (strpbrk(localpart,"@!")) {
+		(void) strcpy (address, localpart);
+	    }
+	    /* check if localpart contains "%" */
+	    else if (p = rindex(localpart,'%')) {
+		*p = '@';
+		(void) strcpy (address, localpart);
+	    }
+	    /* else localpart is a truly local address */
+	    else {
+		(void) strcpy (address, localpart);
+		break; /* out of the loop */
+	    }
+	} else {
+	    break; /* out of the loop */
+	}
+    }
+  DEBUG("removelocal: result is '%s'\n", address);
+  return;
+}
+
 /*
 **
 ** parse(): parse <address> into <domain, user, form>.
@@ -221,7 +300,14 @@
 {
 		enum eform form, parse();
 		extern char hostuucp[];
+		char path[SMLBUF];
+		int cost;
+#ifdef TRUNC_DOM_LEFT
+		char leftpart[SMLBUF], *p;
+#endif
 
+		/* DEBUG("islocal: checking '%s'\n", addr); /* */
+
 		/*
 		** parse the address
 		*/
@@ -235,9 +321,31 @@
 		||(strcmpic(domain, &MYDOM[0]) == 0)	/* user at MYDOM w/ dot */
 		||(strcmpic(domain, &MYDOM[1]) == 0)	/* user at MYDOM no dot */
 #endif
-		||(strcmpic(domain, hostuucp)   == 0)) {/* user at hostuucp */
+		||(strcmpic(domain, hostuucp) == 0)){	/* user at hostuucp */
 			return(1);
 		}
+
+		/*
+		** Check the paths file.
+		*/
+		if (NODEBUGgetpath (domain, path, &cost, 0) == EX_OK)
+		    return (strcmp (path, "%s") == 0);
+
+#ifdef TRUNC_DOM
+		if (NODEBUGgetpath (domain, path, &cost, 1) == EX_OK)
+		    return (strcmp (path, "%s") == 0);
+#endif /* TRUNC_DOM */
+
+#ifdef TRUNC_DOM_LEFT
+		(void) strcpy (leftpart, domain);
+		if (p=index(leftpart,'.')) *p = '\0';
+		if (NODEBUGgetpath (leftpart, path, &cost, 1) == EX_OK)
+		    return (strcmp (path, "%s") == 0);
+#endif /* TRUNC_DOM_LEFT */
+
+		/*
+		** Still not found.  It can't be local.
+		*/
 		return(0);
 }
 
@@ -268,6 +376,11 @@
 	static char splbuf[SMLBUF];
 	char from[SMLBUF], domain[SMLBUF], user[SMLBUF];
 	void rline(), scanheaders(), compheaders();
+#ifdef DOT_EOF
+#ifndef DOT_EOF_ALL
+	int is_tty = isatty(0); /* is stdin a tty ? */
+#endif
+#endif
 
 	/*
 	** if the mail has already been spooled by
@@ -337,13 +450,26 @@
 
 		/*
 		** now, copy the rest of the letter into the spool file
-		** terminate on either EOF or '^.$'
+		** terminate on EOF.
+		** We might also terminate on a line that just
+		** contains a dot, depending on compile time options
+		** and whether the stdin is a terminal.
 		*/
 
 		while(ieof != NULL) {
 			(void) fputs(buf, spoolfp);
-			if((fgets(buf, SMLBUF, stdin) == NULL)
-			|| (buf[0] == '.' && buf[1] == '\n')) {
+#ifndef DOT_EOF
+			if( fgets(buf, SMLBUF, stdin) == NULL )
+#else
+#if DOT_EOF_ALL
+			if( (fgets(buf, SMLBUF, stdin) == NULL)
+			|| (buf[0] == '.' && buf[1] == '\n') )
+#else
+			if( (fgets(buf, SMLBUF, stdin) == NULL)
+			|| (is_tty && buf[0] == '.' && buf[1] == '\n') )
+#endif
+#endif
+			{
 				ieof = NULL;
 			}
 		}
@@ -461,7 +587,16 @@
 
 	if (from[0] == '\0') {
 		char *login;
-		if ((login = pwuid(getuid())) == NULL) {
+		char *tmpname;
+		/*
+		** Get login name with cuserid(), then convert to uid with
+		** pwuserid().  If cuserid() fails, use getuid().
+		** When we know the uid, let pwuid() convert it to a name.
+		** (This seems very complicated.)
+		*/
+		if ((login = pwuid(
+		      (tmpname=cuserid(NULL)) ? pwuserid(tmpname) : getuid()
+		    )) == NULL) {
 			(void) strcpy(from, "nobody");	/* bad news */
 		} else {
 			(void) strcpy(from, login);
@@ -706,7 +841,7 @@
 
 	char *nameptr;
 	char name[SMLBUF];
-	char *getenv(), *login;
+	char *getenv(), *login, *tmpname;
 	char *pwfnam(), *pwuid();
 
 	if (from_addr != NULL) {
@@ -717,7 +852,9 @@
 	name[0] = '\0';
 	if((nameptr = getenv("NAME")) != NULL) {
 		(void) strcpy(name, nameptr);
-	} else if((login = pwuid(getuid())) != NULL) {
+	} else if((login = pwuid(
+		    (tmpname=cuserid(NULL)) ? pwuserid(tmpname) : getuid()
+		)) != NULL) {
 		if((nameptr = pwfnam(login)) != NULL) {
 			(void) strcpy(name, nameptr);
 		}
@@ -755,14 +892,7 @@
 			(void) strcat(bol, hostdomain);
 		}
 		if(i+1 < argc) {
-#if 0
-   /*** The following line was there, but ii is definitely wrong.
-	I think it should be changed to what appears after the #else.
-	[A P Barrett, 890503] ***/
-			\main);(bol);
-#else
 			n = strlen(bol);
-#endif
 			if(n > 50) {
 				(void) strcat(bol, ",\n\t");
 				bol = bol + strlen(bol);
@@ -774,3 +904,35 @@
 		}
 	}
 }
+
+/*
+** call getpath with the DEBUG flag turned off
+*/
+
+int NODEBUGgetpath (key, path , cost, trunk)
+char *key;		/* what we are looking for */
+char *path;		/* where the path results go */
+int *cost;		/* where the cost results go */
+int trunk;		/* Do we accept a truncated name ? */
+{
+	enum edebug tdebug = debug;
+	int retcode;
+	debug = NO;
+	retcode = getpath (key, path, cost, trunk);
+	debug = tdebug;
+	return retcode;
+}
+
+/*
+** call removelocal with the DEBUG flag turned off
+*/
+
+void NODEBUGremovelocal (address)
+char *address;
+{
+	enum edebug tdebug = debug;
+	debug = NO;
+	removelocal (address);
+	debug = tdebug;
+}
+

Index: misc.c
*** old/misc.c	Thu Jan 18 17:03:44 1990
--- new/misc.c	Thu Jan 18 17:04:12 1990
@@ -136,6 +136,17 @@
 **		Some sites are now inserting the timezone into the
 **		local date.  This routine should figure out what
 **		the format is and work appropriately.
+**
+**	Modified by A P Barrett, July 1989:
+**	    1	RFC822 format should have weekday at the beginning, with
+**		a comma between it and the rest of the time, instead of
+**		in parentheses at the end.
+**	    2	If the timezone is not one of the few valid RFC822 timezones,
+**		then use the difference between local and GMT to create a
+**		timezone in US Military single-character format (if
+**		#define TZ_MIL is used), or in ANSI X3.51-1975 format (+hhmm)
+**		(if TZ_MIL is not #define'd).  In both cases, put the Unix
+**		timezone in as a comment.
 */
 
 char *
@@ -150,6 +161,7 @@
 #ifndef BSD
 	extern char *tzname[];
 	time_t t, time();
+	extern long timezone;
 #else
 	/* V7 and 4BSD */
 	struct timeb t;
@@ -176,10 +188,22 @@
 
 	/*
 	**  Crack the UNIX date line in a singularly unoriginal way.
+	**
+	**	Unix:	Mon Sep 16 01:03:52 1979
+	**	RFC822:	Mon, 16 Sep 79 01:03:52 PST
+	**	    or:	Mon, 16 Sep 79 01:03:52 H (PST)
+	**	    or:	Mon, 16 Sep 79 01:03:52 -0800 (PST)
 	*/
 
 	q = b;
 
+	p = &ud[0];		/* Mon */
+	*q++ = *p++;
+	*q++ = *p++;
+	*q++ = *p++;
+	*q++ = ',';
+	*q++ = ' ';
+
 	p = &ud[8];		/* 16 */
 	if (*p == ' ')
 		p++;
@@ -209,36 +233,57 @@
 #else
 	p = timezone(t.timezone, localtime(&t.time)->tm_isdst);
 #endif
-	if (p[3] != '\0')
+	/* Only a few timezone names are legal in RFC822 */
+	if (strcmp(p,"UT")==0 ||
+	    strcmp(p,"GMT")==0 ||
+	    strcmp(p,"EST")==0 ||
+	    strcmp(p,"EDT")==0 ||
+	    strcmp(p,"CST")==0 ||
+	    strcmp(p,"CDT")==0 ||
+	    strcmp(p,"MST")==0 ||
+	    strcmp(p,"MDT")==0 ||
+	    strcmp(p,"PST")==0 ||
+	    strcmp(p,"PDT")==0 ||
+	    (isalpha(*p) && *(p+1)=='\0'))
 	{
-		/* hours from GMT */
-		p += 3;
-		*q++ = *p++;
-		if (p[1] == ':')
-			*q++ = '0';
-		else
-			*q++ = *p++;
-		*q++ = *p++;
-		p++;		/* skip ``:'' */
-		*q++ = *p++;
-		*q++ = *p++;
+		*q++ = ' ';
+		do { } while ( *q++ = *p++ ); q--;
 	}
 	else
 	{
+#ifdef TZ_MIL
+		/* Make a US Military style timezone.
+		 * Z=GMT, A=GMT-1, N=GMT+1, etc. (J is unused) */
+		static char mil_tz[] = "YXWVUTSRQPONZABCDEFGHIKLM";
+#ifndef BSD
+		int hr = (timezone+1800)/3600; /* round to nearest hour */
+#else
+		int hr = (t.timezone+1800)/3600; /* round to nearest hour */
+#endif
 		*q++ = ' ';
-		*q++ = *p++;
-		*q++ = *p++;
-		*q++ = *p++;
+		*q++ = mil_tz[hr+12];
+#else (not TZ_MIL)
+		/* Make an ANSI X3.51-1975 timezone. +hhmm */
+#ifndef BSD
+		int hr = abs(timezone)/3600;
+		int mi = (abs(timezone)/60)%60;
+#else
+		int hr = abs(t.timezone)/3600;
+		int mi = (abs(t.timezone)/60)%60;
+#endif
+		*q++ = ' ';
+		*q++ = (timezone<0 ? '+' : '-'); /* yes, really */
+		sprintf (q,"%02.2d%02.2d",hr,mi);
+		q += 4;
+#endif
+
+		/* Add the Unix timezone as a comment */
+		*q++ = ' ';
+		*q++ = '(';
+		do { } while ( *q++ = *p++ ); q--;
+		*q++ = ')';
 	}
 
-	p = &ud[0];		/* Mon */
-	*q++ = ' ';
-	*q++ = '(';
-	*q++ = *p++;
-	*q++ = *p++;
-	*q++ = *p++;
-	*q++ = ')';
-
 	*q = '\0';
 	return (b);
 }
@@ -249,6 +294,9 @@
  *	used to convert all case variants of "postmaster" to all lower
  *	case.  If the user name passed in is not "postmaster", it is
  *	returned unchanged.
+ *
+ *	Modified by A P Barrett, July 1989:
+ *		Treat "postmast" just like "postmaster"
  */
 char *
 postmaster(user)
@@ -255,8 +303,9 @@
 char *user;
 {
 	static char *pm = "postmaster";
+	static char *pm2 = "postmast";
 
-	if(strcmpic(user, pm) == 0) {
+	if (strcmpic(user, pm) == 0 || strcmpic(user, pm2) == 0) {
 		return(pm);
 	} else {
 		return(user);
@@ -340,3 +389,231 @@
 
 	(void) strcat(strcpy(hostuucp, hostname), ".UUCP");
 }
+
+/* rfc822_quote --
+ * quotes a character string according to the RFC-822 standard.
+ * Returns a pointer to the result string.
+ *
+ * 	fred			==>	"fred"
+ *	funny'name"thing	==>	"funny'name\"thing"
+ *	"already quoted"	==>	"\"already quoted\""
+ */
+
+char *rfc822_quote (result, string)
+char *result, *string;
+  {
+    char *p = string;
+    char *q = result;
+
+    /* start with the opening quote mark {"} */
+
+    *q++ = '"';
+
+    /* process the string */
+
+    for ( ; *p != '\0' ; p++ ) {
+	if (*p=='"' || *p=='\\') {
+	    /* convert embedded {"} or {\} to {\"} or {\\} */
+	    *q++ = '\\';
+	    *q++ = *p;
+	  }
+	else {
+	  *q++ = *p;
+	}
+    }
+
+    /* add last {"} */
+
+    *q++ = '"';
+
+    /* terminate with a nul */
+
+    *q = '\0';
+
+    return (result);
+  }
+
+/* rfc822_isquoted --
+ * determines whether a string is quoted according to the RFC-822 standard.
+ * Returns 1 for TRUE (yes, the string was quoted) or 0 for FALSE.
+ *
+ *	fred			==>	FALSE
+ * 	"fred"			==>	TRUE
+ *	"funny'name\"thing"	==>	TRUE
+ *	"partially"quoted	==>	FALSE
+ *	unmatched"quote		==>	FALSE
+ *	"\"doubly quoted\""	==>	TRUE
+ */
+
+int rfc822_isquoted (string)
+char *string;
+  {
+    return rfc822_do_unquote (NULL, string);
+  }
+
+/* rfc822_unquote --
+ * unquotes a character string according to the RFC-822 standard.
+ * Returns a pointer to the result string.
+ * The result is identical to the input if the input string was not
+ * properly quoted.
+ *
+ *	fred			==>	fred
+ * 	"fred"			==>	fred
+ *	"funny'name\"thing"	==>	funny'name"thing
+ *	"partially"quoted	==>	"partially"quoted
+ *	unmatched"quote		==>	unmatched"quote
+ *	"\"doubly quoted\""	==>	"doubly quoted"
+ */
+
+char *rfc822_unquote (result, string)
+char *result, *string;
+  {
+    (void) rfc822_do_unquote (result, string);
+    return result;
+  }
+
+/* rfc822_do_unquote --
+ * does the work for both rfc822_isquoted and rfc822_unquote.
+ * The work is the same in both cases; those functions are simply
+ * wrappers that give a different calling interface to this routine.
+ *
+ * Unquotes the string and stores the result in 'result', unless
+ * result is NULL.  Returns a flag saying whether or not tha
+ * input string was quoted.
+ */
+
+int rfc822_do_unquote (result, string)
+char *result, *string;
+  {
+    char *p = string;
+    char *q = result;
+    char ch;
+    int wasquoted;
+    typedef enum { start_s, /* at the start of the string */
+		  inquotes_s, /* inside a quoted string */
+		  backslash_s, /* seen a backslash inside the quoted string */
+		  closequote_s, /* seen a quote mark that might be the close of
+		  	         * the quoted string */
+		  endquoted_s, /* seen the end of the quoted string */
+		  notquoted_s, /* decided that the string was not quoted */
+		  end_s /* finished everything */ }
+	    state_t;
+    state_t state;
+    int needch;
+
+    /* This finite state automaton goes through the input string and creates
+     * the output string if the input was properly quoted.  If the input
+     * was not properly quoted, it may give up processing the input before
+     * the end of string is reached, and the output will be undefined.
+     * The variable "wasquoted" is set to 1 if the input string was correctly
+     * quoted (in which case the output string will contain the correctly
+     * un-quoted version of the input); this variable is set to 0 if the
+     * input was not correctly quoted. */
+
+    for ( state = start_s, needch = 1 ; state != end_s ; ) {
+	if (needch) {
+	    ch = *p++;
+	    needch = 0;
+	  }
+	switch (state) {
+	  case start_s:
+		/* if the first character is a quote mark then it might be a
+		 * properly quoted string, otherwise it cannot be a properly
+		 * quoted string */
+		state = ( ch == '"' ? inquotes_s : notquoted_s );
+		needch = 1;
+		break;
+	  case inquotes_s:
+		/* backslash and quote marks are special, but just copy other
+		 * chars to the output */
+		switch (ch) {
+		  case '\\': state = backslash_s; break;
+		  case '"': state = closequote_s; break;
+		  default: if(q){ *q++ = ch;}
+		  }
+		needch = 1;
+		break;
+	  case backslash_s:
+		/* if end of string occurs just after a backslash then string
+		 * was not properly quoted.  Otherwise output the char that
+		 * follows the backslash. */
+		if (ch == '\0') { state = notquoted_s; }
+		else { if(q){ *q++ = ch;} state = inquotes_s; needch = 1; }
+		break;
+	  case closequote_s:
+		/* if end of string occurs just after a quote mark then
+		 * string was properly quoted, otherwise it was not */
+		if (ch == '\0') { if(q){*q++ = '\0';} state = endquoted_s; }
+		else { state = notquoted_s; }
+		break;
+	  case endquoted_s:
+		/* String was quoted and we have finished processing it */
+		wasquoted = 1;
+		state = end_s;
+		break;
+	  case notquoted_s:
+		/* String was not quoted and we have finished processing it */
+		wasquoted = 0;
+		state = end_s;
+		break;
+	  }
+      }
+    
+    /* if the input string was quoted, all the work has been done already.
+     * otherwise, make the output a copy of the input */
+
+    if (result && ! wasquoted) {
+	(void) strcpy (result, string);
+      }
+    
+    return (wasquoted);
+  }
+
+
+/* sh_quote --
+ * quotes a character string so that, when /bin/sh interprets the result,
+ * the interpretation will be identical to the original string.
+ * Returns a pointer to the result string.
+ *
+ * 	fred at somewhere		==>	'fred at somewhere'
+ *	"funny'name\"thing"@somewhere
+ *				==>	'"funny'"'"'name\"thing"@somewhere
+ */
+
+char *sh_quote (result, string)
+char *result, *string;
+  {
+    char *p = string;
+    char *q = result;
+
+    /* start with the opening quote mark {'} */
+
+    *q++ = '\'';
+
+    /* process the string */
+
+    for ( ; *p != '\0' ; p++ ) {
+	if (*p == '\'') {
+	    /* convert embedded {'} to {'"'"'} */
+	    *q++ = '\'';
+	    *q++ = '\"';
+	    *q++ = '\'';
+	    *q++ = '\"';
+	    *q++ = '\'';
+	  }
+	else {
+	  *q++ = *p;
+	}
+    }
+
+    /* add last {'} */
+
+    *q++ = '\'';
+
+    /* terminate with a nul */
+
+    *q = '\0';
+
+    return (result);
+  }
+

Index: pw.c
*** old/pw.c	Thu Jan 18 17:03:45 1990
--- new/pw.c	Thu Jan 18 17:04:13 1990
@@ -54,6 +54,32 @@
 	return(NULL);
 }
 
+pwuserid(user)
+char *user;
+{
+	pwlist *f;
+
+	/*
+	** check for previously cached user
+	*/
+
+	for(f=pwhead; f != NULL; f=f->vlink) {
+		if(strcmp(user, f->lname) == 0) {
+			return(f->uid);
+		}
+	}
+	/*
+	** not found parse the password file
+	*/
+
+	while((f=pwparse()) != PNULL) {
+		if(strcmp(user, f->lname) == 0) {
+			return(f->uid);
+		}
+	}
+	return(0);
+}
+
 char *
 pwuid(uid)
 int uid;
@@ -232,14 +258,16 @@
 			if ( *s == '\0' ) {
 				goto solved;
 			}
-			c = getc( file );
-			flag = lower( c ) - lower( *s );
+			if ((c = getc( file )) == EOF)
+				flag = 1;
+			else
+				flag = lower( c ) - lower( *s );
 		} 
-		if (lo >= middle)		/* failure? */
+		if (lo > hi)		/* failure? */
 			return(NULL);
 
-		if(c != EOF && flag < 0)	/* close window */
-			lo = middle;
+		if (flag < 0)	/* close window */
+			lo = middle + 1;
 		else 
 			hi = middle - 1;
 	}

Index: resolve.c
*** old/resolve.c	Thu Jan 18 17:03:45 1990
--- new/resolve.c	Thu Jan 18 17:04:13 1990
@@ -50,6 +50,14 @@
 **  This is a gnarly piece of code, but it does it all.  Each section 
 **  is documented.
 **
+** **** Note:
+** Alias and fullname translation should be done whenever an address
+** resolves to a local name, but it is not.  This is only a problem
+** if routing = REROUTE and we find the local system name at the end of
+** a path through other systems; in all other cases the local system
+** name will have been removed already by the aliasing code before
+** the main program calls resolve().
+**
 */
 
 enum eform
@@ -59,13 +67,15 @@
 char *user;				/* the returned user 	*/
 int *cost;				/* the returned cost 	*/
 {
-	enum eform form;		/* the returned form	*/ 
-	enum eform parse();		/* to crack addresses	*/
-	int parts;			/* to ssplit addresses	*/
-	char *partv[MAXPATH];		/* "  "      "		*/
-	char temp[SMLBUF];		/* "  "      "		*/
-	int i;
-		
+    enum eform form;			/* the returned form	*/ 
+    enum eform parse();			/* to crack addresses	*/
+    int parts;				/* to ssplit addresses	*/
+    char *partv[MAXPATH];		/* "  "      "		*/
+    char temp[SMLBUF];			/* "  "      "		*/
+    int i;
+    char *p;
+	    
+beginning:
 
 /*
 **  If we set REROUTE and are prepared to deliver UUCP mail, we split the 
@@ -73,32 +83,32 @@
 **  substrings until we succeed.  Otherwise, we just resolve the whole thing 
 **  once.
 */
-	if ((routing == REROUTE) && (rsvp( UUCP ) == UUCP)) {
-		parts = ssplit( address, '!', partv );
-	} else {
-		parts = 1;
-		partv[0] = address;
-	}
+    if ((routing == REROUTE) && (rsvp( UUCP ) == UUCP)) {
+	parts = ssplit( address, '!', partv );
+    } else {
+	parts = 1;
+	partv[0] = address;
+    }
 /*
 **  This for(i) loop selects successively larger
 **  righthand substrings of the address.
 */
-	for( i = parts - 1; i >= 0; i-- ) {
+    for( i = parts - 1; i >= 0; i-- ) {
 /*
 **  Parse the address.
 */
-		(void) strcpy( temp, partv[i] );
-		form = parse( temp, domain, user );
+	(void) strcpy( temp, partv[i] );
+	form = parse( temp, domain, user );
 
 DEBUG("resolve: parse address '%s' = '%s' @ '%s' (%s)\n",
-	temp,user,domain,sform(form));
+    temp,user,domain,sform(form));
 
 /*
 **  If we are looking at a substring (that's not the entire string)
 **  which parses to a LOCAL address, we skip to the next larger substring.
 */
-		if((i != 0) && (form == LOCAL))
-			continue;
+	if((i != 0) && (form == LOCAL))
+	    continue;
 /*
 **  Routing, when required, is the next step.
 **  We route the address if we have a ROUTE form
@@ -105,69 +115,79 @@
 **  or if we have a UUCP form and we are told to
 **  route ALWAYS or REROUTE (i.e., routing != JUSTDOMAIN)
 */
-		if((rsvp( form ) == ROUTE)
-		 ||((rsvp( form ) == UUCP) && (routing != JUSTDOMAIN ))) {
+	if((rsvp( form ) == ROUTE)
+	 ||((rsvp( form ) == UUCP) && (routing != JUSTDOMAIN ))) {
 
-			int look_smart = 0;
+	    int look_smart = 0;
 
-			if((routing == REROUTE) && (i == 0)) {
-				look_smart = 1; /* last chance */
-			}
+	    if( /* (routing == REROUTE) && */ (i == 0)) {
+		look_smart = 1; /* last chance */
+	    }
 
-			/* route() puts the new route in 'temp' */
-			if(route(domain,user,look_smart,temp,cost) != EX_OK) {
-				continue;	/* If routing fails, try
-						/* next larger substring.
-						/* */
-			}
+	    /* route() puts the new route in 'temp' */
+	    if(route(domain,user,look_smart,temp,cost) != EX_OK) {
+		continue;   /* If routing fails, try
+			    /* next larger substring.
+			    /* */
+	    }
 /*
 **  After routing, reparse the new route into domain and user. 
 */
-			form = parse( temp, domain, user );
+	    form = parse( temp, domain, user );
 
 DEBUG("resolve: parse route '%s' = '%s' @ '%s' (%s)\n",
-	temp,user,domain,sform(form));
+    temp,user,domain,sform(form));
 
-		} else if((getcost) && (rsvp(form) == UUCP)) {
-			/* get the cost of the route
-			** even if we're not going route the mail.
-			** this allows smart decisions about using
-			** the -r flag to uux when we're not routing.
-			*/
-			char junk[SMLBUF];
-			if(route(domain,user,0,junk,cost) != EX_OK) {
-				continue;	/* If routing fails, try
-						/* next larger substring.
-						/* */
-			}
-		}
-		break;	/* route is resolved */
+
+	} else if((getcost) && (rsvp(form) == UUCP)) {
+	    /* get the cost of the route
+	    ** even if we're not going route the mail.
+	    ** this allows smart decisions about using
+	    ** the -r flag to uux when we're not routing.
+	    */
+	    char junk[SMLBUF];
+	    if(route(domain,user,0,junk,cost) != EX_OK) {
+		    continue;	/* If routing fails, try
+				/* next larger substring.
+				/* */
+	    }
 	}
-/*
-**  For LOCAL mail in non-local format, we rewrite the full address into 
-**  <user> and leave <domain> blank.
-*/
-	if ((rsvp( form ) == LOCAL) && (form != LOCAL )) {
-		build( domain, user, form, temp );
-		(void) strcpy( user, temp );
-		(void) strcpy( domain, "" );
-		form = LOCAL;
-	}
-/*
-**  If we were supposed to route an address but failed (form == ERROR), 
-**  or after routing we are left with an address that still needs to
-**  be routed (rsvp( form ) == ROUTE), complain.
-*/
-	if ((form == ERROR) || (rsvp( form ) == ROUTE )) {
-		exitstat = EX_NOHOST;
-		ADVISE("resolve failed '%s' = '%s' @ '%s' (%s)\n",
-			address, user, domain, sform(form));
-		form = ERROR;
-	} else {
-		ADVISE("resolve '%s' = '%s' @ '%s' (%s)\n",
-			address, user, domain, sform(form));
-	}
-	return ( form );
+	break;	/* route is resolved */
+    }
+    /*
+    **  For LOCAL mail in non-local format, we rewrite the full address into 
+    **  <user> and leave <domain> blank.
+    */
+    if ((rsvp( form ) == LOCAL) && (form != LOCAL )) {
+	build( domain, user, form, temp );
+	(void) strcpy( user, temp );
+	(void) strcpy( domain, "" );
+	form = LOCAL;
+    }
+    /*
+    **  If we were supposed to route an address but failed (form == ERROR), 
+    **  or after routing we are left with an address that still needs to
+    **  be routed (rsvp( form ) == ROUTE), complain.
+    */
+    if ((form == ERROR) || (rsvp( form ) == ROUTE )) {
+	exitstat = EX_NOHOST;
+	ADVISE("resolve failed '%s' = '%s' @ '%s' (%s)\n",
+		address, user, domain, sform(form));
+	form = ERROR;
+    }
+    /*
+    ** If we have a LOCAL address that contains a % then change the %
+    ** to an @ and start again from the beginning.
+    */
+    else if ((form == LOCAL) && ((p = rindex(user,'%')) != NULL)) {
+	*p = '@';
+	strcpy (address, user);
+	goto beginning;
+    } else {
+	ADVISE("resolve '%s' = '%s' @ '%s' (%s)\n",
+		address, user, domain, sform(form));
+    }
+    return ( form );
 }
 
 /*
@@ -185,93 +205,173 @@
 char *result;			/* output route 	*/
 int *cost;			/* cost of output route */
 {
-	int	uucpdom = 0;
-	int	domains, step;			/* to split domain	*/
-	char	*domainv[MAXDOMS];		/* "  "     "		*/
-	char	temp[SMLBUF], path[SMLBUF];
+    int     uucpdom = 0;
+    int     dotkey;                 /* did getpath() find a path for a
+				       key that began with a "."? */
+    int     domains, step;                  /* to split domain      */
+    char    *domainv[MAXDOMS];              /* "  "     "           */
+    char    temp[SMLBUF], path[SMLBUF];
+    char    leftpart[SMLBUF], *up;
 
-/*
-**  Fully qualify the domain, and then strip the last (top level domain) 
-**  component off, so that we look it up separately.
-*/
-	temp[0] = '.';
-	(void) strcpy(temp+1, domain );
+    /*
+    **  Fully qualify the domain, and then strip the last (top level domain) 
+    **  component off, so that we look it up separately.
+    */
+    temp[0] = '.';
+    (void) strcpy(temp+1, domain );
 
-	domains = ssplit( temp+1, '.', domainv );
+    domains = ssplit( temp+1, '.', domainv );
 
-/*
-** check target domain for the local host name and host domain.
-** if it matches, then skip the lookup in the database.
-** this prevents mail loops for cases where SMARTHOST is defined
-** in the routing table, but the local host is not.  It also is
-** a little faster when the local host is the target domain.
-*/
-	if((strcmpic(domain, hostname) == 0)
-	|| (strcmpic(domain, hostdomain) == 0)) {
-		step = 0;
-		*cost = 0;
-		(void) strcpy(path, "%s");
-DEBUG("route: '%s' is local\n", domain);
-		goto route_complete;
-	}
+    /*
+    ** check target domain for the local host name and host domain.
+    ** if it matches, then skip the lookup in the database.
+    ** this prevents mail loops for cases where SMARTHOST is defined
+    ** in the routing table, but the local host is not.  It also is
+    ** a little faster when the local host is the target domain.
+    */
+    if((strcmpic(domain, hostname) == 0)
+    || (strcmpic(domain, hostdomain) == 0)) {
+	step = 0;
+	*cost = 0;
+	(void) strcpy(path, "%s");
+	DEBUG("route: '%s' is local\n", domain);
+	goto route_complete;
+    }
 
-	/* If the domain ends in .UUCP, trim that off. */
-	if((domains > 0) && isuucp(domainv[domains-1])) {
-		domains--;
-		domainv[domains][-1] = '\0';
-		uucpdom = 1;
-	}
-/*
-**  Try to get the path for successive components of the domain.  
-**  Example for osgd.cb.att.uucp:
-**	osgd.cb.att
-**	cb.att
-**	att
-**	uucp ( remember stripping top level? )
-**	SMARTHOST
-**  Returns with error if we find no path.
-*/
-	for(step = 0; (step < domains); step++) {
-		if((getpath(domainv[step]-1, path, cost) == EX_OK) /* w/ dot */
-		|| (getpath(domainv[step]  , path, cost) == EX_OK))/* no dot */
-			break;
-	}
+    /* If the domain ends in .UUCP, trim that off. */
+    if((domains > 0) && isuucp(domainv[domains-1])) {
+	domains--;
+	domainv[domains][-1] = '\0';
+	uucpdom = 1;
+    }
+    /*
+    **  Try to get the path for successive components of the domain.  
+    **  Example for osgd.cb.att.uucp:
+    **	osgd.cb.att
+    **	cb.att
+    **	att
+    **	uucp ( remember stripping top level? )
+    **  truncated name (in this case, osgd or osgd.cb in the paths file )
+    **  osgd ( using uuname to check the uucp L.sys or Systems file )
+    **	SMARTHOST
+    **  Returns with error if we find no path.
+    */
+    for(step = 0; (step < domains); step++) {
+	/*
+	** Look in the paths file for a route to whatever portion
+	** of the domain name interests us right now.
+	** If we are interested in the entire name (step=0) then
+	** try a path that doesn't start with a dot before trying
+	** one that does start with a dot.
+	** If we are interested in something other than the entire
+	** domain name, try the dot path first.
+	*/
+	if (step == 0) {
+	    if ((dotkey=0, /* no dot */
+		getpath(domainv[step]  , path, cost, 0) == EX_OK)
+	    ||  (dotkey=1, /* with dot */
+		getpath(domainv[step]-1, path, cost, 0) == EX_OK))
+		    break; /* found it */
+        } else {
+	    if ((dotkey=1, /* with dot */
+		getpath(domainv[step]-1, path, cost, 0) == EX_OK)
+	    ||  (dotkey=0, /* no dot */
+		getpath(domainv[step]  , path, cost, 0) == EX_OK))
+		    break; /* found it */
+        }
+    } /* end for */
 
-	if(step == domains) {
+    if(step == domains) {
 	/*
-	** we've looked at each component of the domain without success
+	** We've looked at each component of the domain without success
+	**
+	** If domain is a UUCP address, look for a UUCP gateway.
 	*/
+	if((uucpdom == 0)
+	|| (dotkey=1, getpath(".UUCP", path, cost, 0) != EX_OK)) {
+	    /*
+	    ** The domain not is a UUCP address, or we can't
+	    ** find a UUCP gateway.
+	    */
+	    /* extract the leftmost part of the domain name, for use
+	    ** with TRUNC_DOM, UUNAME etc.
+	    */
+	    strcpy(leftpart,domain);
+	    if(up=index(leftpart,'.')) *up='\0';
+#ifdef TRUNC_DOM
+	    /*
+	    ** See if the domain we want appears
+	    ** as a truncated part of a domain in the paths file.
+	    */
+	    if(getpath(domain, path, cost, 1) == EX_OK) {
+		dotkey = 0;	/* no dot */
+		step = 0;	/* pretend we matched on full name */
+		goto route_complete;
+	    }
+#endif /* TRUNC_DOM */
+#ifdef TRUNC_DOM_LEFT
+	    /*
+	    ** See if the leftmost part of the domain we want appears
+	    ** as the leftmost part of a domain in the paths file.
+	    */
+	    if(getpath(leftpart, path, cost, 1) == EX_OK) {
+		dotkey = 0;	/* no dot */
+		step = 0;	/* pretend we matched on full name */
+		goto route_complete;
+	    }
+#endif /* TRUNC_DOM_LEFT */
+#ifdef UUNAME
+	    /*
+	    ** See if there is a directly connected UUCP
+	    ** system whose name matches the first part of
+	    ** the full domain name.
+	    */
+	    if(look_smart == 1) {
+		DEBUG("route: '%s': look for '%s' uucp neighbour\n",
+		    domain,leftpart);
+		if (uuname(leftpart)) {
+		    DEBUG("route: '%s' is a uucp neighbour\n",leftpart);
+		    strcpy(path,leftpart);
+		    strcat(path,"!%s");    /* Create a bang path */
+		    *cost = 300;	/* Average Cost ? */
+		    dotkey = 0;		/* no dot */
+		    step = 0;		/* pretend we matched whole name */
+		    goto route_complete;
+		}
+	    }
+#endif /* UUNAME */
+
+	    /*
+	    ** If this is our last chance,
+	    ** look for a smarter host to deliver the mail.
+	    */
+	    if((look_smart == 0)
+	    || (dotkey=0, getpath(SMARTHOST,path,cost,0) != EX_OK)) {
 		/*
-		** If domain is a UUCP address, look for a UUCP gateway.
+		** All our efforts have been in vain.
+		** Tell them the bad news.
 		*/
-		if((uucpdom == 0) || (getpath(".UUCP", path, cost) != EX_OK)) {
-			/*
-			** The domain not is a UUCP address, or we can't
-			** find a UUCP gateway.  If this is our last chance,
-			** look for a smarter host to deliver the mail.
-			*/
-			if((look_smart == 0)
-			|| (getpath(SMARTHOST, path, cost) != EX_OK)) {
-				/*
-				** All our efforts have been in vain.
-				** Tell them the bad news.
-				*/
-				DEBUG("route '%s' failed\n", domain);
-				return( EX_NOHOST );
-			}
-		}
+		DEBUG("route '%s' failed\n", domain);
+		return( EX_NOHOST );
+	    }
 	}
+    } /* end if (step==domains) */
 
-route_complete:
+    /*
+    ** If we get here, we have a route to use.
+    */
+    route_complete:
 
-DEBUG("route:  '%s' (%s) = '%s' (%d)\n", domain, domainv[step]?domainv[step]:"NULL", path, *cost);
+    DEBUG("route:  '%s' (%s) = '%s' (%d)\n", domain,
+	domainv[step]?domainv[step]:"NULL", path, *cost);
 
-/*
-**  If we matched on the entire domain name, this address is fully resolved, 
-**  and we plug <user> into it.  If we matched on only part of the domain 
-**  name, we plug <domain>!<user> in.  
-*/
-	build(domain, user, (step == 0) ? LOCAL : UUCP, temp);
-	(void) sprintf(result, path, temp);
-	return( EX_OK );
+    /*
+    **  If we matched on the entire domain name, this address is fully
+    **  resolved, and we plug <user> into it.  If we matched on only part
+    **  of the domain name, or if the match started fith a ".", we plug
+    **  <domain>!<user> in.  
+    */
+    build (domain, user, (step == 0 && dotkey == 0) ? LOCAL : UUCP, temp);
+    (void) sprintf(result, path, temp);
+    return( EX_OK );
 }

Index: uuname.c
*** old/uuname.c	Thu Jan 18 17:04:01 1990
--- new/uuname.c	Thu Jan 18 17:04:14 1990
***************
*** 1 ****
--- 1,44 ----
+ #include <stdio.h>
+ #include "defs.h"
+ 
+ /*
+ **  UUNAME - function takes one argument - the name of a possible valid
+ **  uucp site - thats one hop away (ie from the Systems file). If it finds
+ **  name - it returns '1' (ie TRUE) - otherwise it returns a '0' (False).
+ **
+ ** It runs the command 'uuname' and checks the output for that sitename 
+ ** (Your arguement). Why a pipe?  - This means that you don't have to have
+ ** special permissions to read your Systems file - and it works happily
+ ** regardless of the type of UUCP you are running. Also - its easier to
+ ** fudge if things change - all you need is a program in /usr/bin/uuname!
+ **
+ ** Author - Mark J Elkins - mje at olsa99.UUCP
+ **
+ ** Modified by A P Barrett to use popen instead of pipe/fork/exec,
+ ** and to use the UUNAME preprocessor symbol to define the program to run.
+ */
+ 
+ #ifdef UUNAME
+ 
+ uuname(match)
+ char *match;
+ {
+ 	FILE *fp;
+ 	char buff[40], *p;
+ 
+ 	if ((fp = popen(UUNAME,"r")) == NULL) {
+ 	    perror("smail: cannot popen uuname");
+ 	    return(0);
+ 	}
+ 	while(fgets(buff,36,fp)) {
+ 	    if(p=index(buff,'\n'))  *p = '\0';
+ 	    if(strcmp(match,buff) == 0) { /* found */
+ 		(void) pclose(fp);
+ 		return(1);
+ 	    }
+ 	}
+ 	(void) pclose(fp);
+ 	return(0);
+ }
+ 
+ #endif

----- end of diffs -----



More information about the Alt.sources mailing list