[net.sources] re-release of mkpath, nmail

mcm (05/12/83)

Relay-Version:version B 2.10 gamma 4/3/83; site mhuxt.UUCP
Message-ID:<2189@ncsu.UUCP>
Date:Wed, 11-May-83 21:15:38 EDT


Here is the distribution for nmail and mkpath.  If you've never heard of
them before, mkpath creates a list of site names and paths to them based
on the data Mark and Karen Horton send down net.news.map once a month.
Nmail uses the lists generated by mkpath to expand its arguments, which
it hands to the system's mailer.

To unpackage the stuff, delete this header up to and including the '*****'
change the mode of the file so you can execute it, then execute the file.
It should create 5 files, 'notes', 'mkpath.c', 'mkpath.1', 'nmail.c',
and 'nmail.1'.  Good luck!

	Mike Mitchell
	decvax!duke!mcnc!ncsu!mcm

**********************************************************
sed 's/^	//' >notes <<\*-*-END-*-*
	Notes on 3.5 distribution of Mkpath....

	This release contains fixes to the most commonly reported portability
	problems, not having index() and not having <whoami.h>.  Index() is
	included in the source, and there is a set of 3 defines in the source,
	'CCWHOAMI', 'UNAME', and 'UUNAME'.  If 'CCWHOAMI' is defined, it uses
	information in the include file <whoami.h> to decide what the site name
	is.  If 'UNAME' is defined, it uses the uname() system call at run time
	to determine the site name.  If 'UUNAME' is defined, it reads the file
	'/etc/uucpname' to determine the site name.  Only one of the three should
	be defined.

	The format of the input data has changed.  Mkpath now looks for the
	fields 'News:' and 'Mail:' rather than 'Usenet:' and 'UUCP:'.
	A continuation line MUST start with a blank or a tab.  This means that
	those people feeding in uuname output by adding the lines 'Name: site'
	and 'Mail:' to the output of uuname, must also put a blank in front of
	each site.

	The nmail distribution has changed slightly also.  It now looks for the
	data in /usr/lib/nmail.paths rather than /usr/lib/PATHS.  /usr/lib/PATHS
	was chosen for historical reasons.

	Please remember to change the #defines in both nmail and mkpath to suit
	your site.

		Mike Mitchell
		decvax!duke!mcnc!ncsu!mcm
*-*-END-*-*
sed 's/^	//' >mkpath.c <<\*-*-END-*-*
	/*
	 * Mkpath creates UUCP routing information from the Usenet directory
	 * Mark and Karen Horton send down the Usenet.  The map input should
	 * look something like :
	 *
	 *	Name:	name-of-site
	 *	(extra fields)
	 *	News:	connected sites
	 *	(any extra connected sites)
	 *	Mail:	connected sites
	 *	(any extra connected sites)
	 *	Name:
	 *
	 * The News: and Mail: fields must not have any other fields between
	 * them.  Blank lines can be used to separate fields.  The connected sites
	 * can be separated by any number of blanks, tabs, or commas.  Continuation
	 * lines must start with a blank or a tab.
	 *
	 * Usage: mkpath [-lsite][-2][-dsite][-dsite1!site2][-asite1!site2] map-file(s)
	 *
	 * -lsite creates paths starting at "site".  If not supplied,
	 * the current system name is used.
	 *
	 * -2 forces all routes to be two-way.
	 *
	 * -dsite removes "site" from the list; -dsite1!site2 removes the connection
	 * from "site1" to "site2".
	 *
	 * -asite1!site2 adds the connection from "site1" to "site2".  If the -2
	 * option is used, it adds the connection from "site2" to "site1" as well.
	 *
	 * The output goes to stdout.
	 *
	 * Version 3.0	2/12/83
	 * Version 3.1  2/21/83		added -a option
	 * Version 3.2	2/23/83		made storage for -a,-d options dynamic
	 * Version 3.3	4/3/83		no longer ignores blank lines when looking
	 *				for connections
	 * Version 3.4	4/10/83		added routine 'uucpname' to get site name
	 * Version 3.5	5/3/83		Changed to use Mark's new format.  Also
	 *				forced continuation lines to start with
	 *				a blank or a tab.
	 *
	 * Written by Mike Mitchell	decvax!duke!mcnc!ncsu!mcm
	 *
	 */

	#include <stdio.h>
	#include <ctype.h>

	/*
	 * define CCWHOAMI if you have a <whoami.h> file with the site name in it,
	 * define UUNAME if you have the site name in the file '/etc/uucpname',
	 * or define UNAME if you have the 'uname()' system call.
	 * only one of the three should be defined.
	 */
	#define CCWHOAMI	/* compile in <whoami.h> */
	/* #define UUNAME */	/* read the '/etc/uucpname' file */
	/* #define UNAME */	/* use the 'uname()' system call */

	FILE *map;			/* map input file */

	struct site {			/* structure for all needed info on a site */
		char *name;		/* name of site */
		struct site *route;	/* route to take to get there */
		struct path *conn;	/* who the site is connected to */
		struct site *nxtsite;	/* pointer to next site in list */
		};

	struct path {			/* structure for connection & routing info */
		struct site *siteptr;	/* pointer to site data */
		struct path *nxtpath;	/* pointer to next hop in route */
		};

	struct site *Begsite;		/* pointer to the start of site data */
	struct site *Strsite;		/* pointer to starting point's site data */

	main(argc, argv)
	int argc;
	char **argv;
	{
		int i, dno, ano, twoflg, upcase(), getcon();
		register struct site *curptr;	/* current site */
		register struct path *conptr;	/* pointer to connections */
		struct site *search(), *getsite();
		struct site *sptr;
		struct path *twoptr, *lnkptr;
		struct path *ptalloc();
		char buf[BUFSIZ], tmp[BUFSIZ], *fgets();
		char **dsites, **asites, stnam[8], *cp, *index();
		char *calloc();
		FILE *fopen();

		if (argc < 2)
			usage(argv[0]);		/* usage() never returns */

		Begsite = NULL;
		i = 0;
		argc--;
		dno = 0;
		ano = 0;
		twoflg = 0;
		uucpname(stnam);
	/*
	 * scan for options we can take care of now.  Allocate space for -a and -d
	 * options.
	 */
		while(i++ < argc) {
			if (argv[i][0] == '-') {
				switch(argv[i][1]) {
					case 'l':  strncpy(stnam, &argv[i][2],7);
						   break;

					case '2':  twoflg = 1;
						   break;

					case 'd':  dno++;
						   break;

					case 'a':  ano++;
						   break;

					default:  fprintf(stderr, "Bad option '%s'.\n",
							argv[i]);
						   usage(argv[0]);
					}
				}
			}
		if (dno != 0) {
			if ((dsites = (char **)calloc(dno, sizeof(char *))) == NULL) {
				fprintf(stderr, "Can't get mem for -d options.");
				exit(4);
				}
			}
		if (ano != 0) {
			if ((asites = (char **)calloc(ano, sizeof(char *))) == NULL) {
				fprintf(stderr, "Can't get mem for -a options.");
				exit(4);
				}
			}
		ano = 0;
		dno = 0;
		i = 0;
	/*
	 * Loop through the map data files, building the site & connection data
	 */
		while(i++ < argc) {

			if (argv[i][0] == '-') {
				switch(argv[i][1]) {

					case 'd':  dsites[dno++] = &argv[i][2];
						   break;

					case 'a':  asites[ano++] = &argv[i][2];
						   break;
					}
				continue;
				}

			map = fopen(argv[i], "r");
			if (map == NULL) {
				fprintf(stderr, "Cannot open '%s' for map input.\n",
					argv[i]);
				exit(2);
				}
			while(fgets(buf, BUFSIZ, map) != NULL) {
	/*
	 * look for a "Name:" field
	 */
				if ((upcase(buf))&&(strncmp(buf,"NAME:",5)==0)) {
	/*
	 * find out if the current site is in our list.  If not, add it to the
	 * list.
	 */
				    if (sscanf(buf, "NAME: %s", tmp)!=1)
					continue;
				    curptr = getsite(tmp);
				    buf[0] = '\0';
	/*
	 * now grab the first connected site name, if any.
	 */
				    if (getcon(buf, tmp, 1)) {
	/*
	 * allocate space for the path structure, then check to see if the
	 * connection is a "known" site.  If it isn't, add it to our list
	 * of "known" sites.  Make the recently allocated path structure
	 * point to the site, and link it in with the rest of the connection
	 * info.
	 */
					lnkptr = curptr->conn;
					curptr->conn = ptalloc();
					conptr = curptr->conn;
					conptr->siteptr=getsite(tmp);
	/*
	 * now get the rest of the connected site names
	 */
					while(getcon(buf, tmp, 0)) {
					    conptr->nxtpath = ptalloc();
					    conptr = conptr->nxtpath;
					    conptr->siteptr = getsite(tmp);
					    }
					conptr->nxtpath = lnkptr;
					}
				    }
				}
			fclose(map);
			}
	/*
	 * add any connections specified on the command line
	 */
		while(ano--) {
			if ((cp=index(asites[ano],'!')) == NULL) {
				fprintf(stderr,
					"Warning: '-a%s' is an invalid -a command.\n",
					asites[ano]);
				continue;
				}
			*cp++ = '\0';
			curptr = getsite(asites[ano]);
			sptr = getsite(cp);
	/*
	 * go through connection list for site1, and make sure site2 is not already
	 * connected.
	 */
			conptr = curptr->conn;
			while(conptr != NULL) {
				if (conptr->siteptr->name == sptr->name)
					break;
				conptr = conptr->nxtpath;
				}
			if (conptr == NULL) {
	/*
	 * add the site into the connection list.
	 */
				conptr = curptr->conn;
				curptr->conn = ptalloc();
				curptr->conn->nxtpath = conptr;
				curptr->conn->siteptr = sptr;
				}
			}

	/*
	 * go through the list and make sure the connections are two way.
	 */
	    if (twoflg) {
		curptr = Begsite;
		while(curptr != NULL) {
			twoptr = curptr->conn;
	/*
	 * loop through each site's connection list
	 */		while(twoptr != NULL) {

				conptr = twoptr->siteptr->conn;
	/*
	 * loop through the connection's connections, looking for the current
	 * site.  Add the site to the connection's connections if it is not
	 * found.
	 */
				while((conptr != NULL) && (conptr->siteptr != curptr))
					conptr = conptr->nxtpath;
				if (conptr == NULL) {
					lnkptr = ptalloc();
					lnkptr->siteptr = curptr;
					lnkptr->nxtpath = twoptr->siteptr->conn;
					twoptr->siteptr->conn = lnkptr;
					}
				twoptr = twoptr->nxtpath;
				}
			curptr = curptr->nxtsite;
			}
		    }
	/*
	 * now handle the -d options.
	 */
		while( dno-- ) {
	/*
	 * if a '!' is not in the argument, remove the site name.
	 */
			if ((cp=index(dsites[dno], '!')) == NULL) {
				curptr = search(dsites[dno]);
				if (curptr == NULL) {
					fprintf(stderr,
					    "Warning: site '%s' already removed.\n",
					    dsites[dno]);
					continue;
					}
				remsite(curptr);	/* remove the site */
				}
	/*
	 * otherwise, remove the connection from the first site to the second site
	 */
			else {
				*cp++ = '\0';
				curptr = search(dsites[dno]);
				sptr = search(cp);
				if (curptr == NULL || sptr == NULL ) {
				    fprintf(stderr,
					"Warning: connection '%s!%s' non-existent.\n",
					dsites[dno], cp);
				    continue;
				    }
				rempath(curptr, sptr);	/* remove the connection */
				}
			}
	/*
	 * look for the starting point
	 */
		Strsite = search(stnam);
		if (Strsite == NULL) {
			fprintf(stderr, "cannot find site '%s'.\n", stnam);
			exit(3);
			}
	/*
	 * The hard part -- create the UUCP route for every site.  Paths() is
	 * a recursive routine that wanders through the connection info
	 * looking for the best way to get somewhere.
	 */
		paths(Strsite);

	/*
	 * now that paths has finished, output the routing information.
	 * All we have to do is traverse the "route" pointers!
	 */
		curptr = Begsite;
		while(curptr != NULL) {
			if (curptr->route != NULL) {
				printf("%s\t", curptr->name);
				proute(curptr);
				printf("%%s\n");
				}
			curptr = curptr->nxtsite;
			}
		}

	/*
	 * search() searches the "known" sites, looking for the site name
	 * passed to it.  If it finds it, it returns the site pointer to
	 * it. Otherwise, it returns NULL.
	 */

	struct site *search(nam)
	register char *nam;
	{
		register struct site *sp;

		sp = Begsite;
		while(sp != NULL) {
			if (strncmp(sp->name, nam, 7) == 0)
				return(sp);
			sp = sp->nxtsite;
			}
		return(NULL);
		}

	/*
	 * getsite() searchs the site list for a given site.  If the site is not
	 * found, it adds the site name passed to it to the list of "known"
	 * sites.  It allocates the site structure & space for the name,
	 * copies the name, and links everything in.  It returns a pointer to
	 * the site structure.
	 */

	struct site *getsite(nam)
	register char *nam;
	{
		char *malloc(), *calloc();
		register char *cp;
		register struct site *sp;
		struct site *search();

		cp = nam;	/* remove any junk from the name */
		while (*cp) {
			if (isspace(*cp) || *cp == ',') {
				*cp = '\0';
				break;
				}
			cp++;
			}

		if ((sp = search(nam)) != NULL)
			return(sp);

		sp = (struct site *)calloc(1, sizeof(struct site));
		if (sp == NULL) {
			fprintf(stderr, "Cannot get mem for new site!\n");
			exit(4);
			}
		cp = malloc(strlen(nam) + 1);
		if (cp == NULL) {
			fprintf(stderr, "Cannot get mem for name!\n");
			exit(4);
			}
		strcpy(cp, nam);
		sp->name = cp;
		sp->nxtsite = Begsite;
		Begsite = sp;
		sp->route = NULL;
		sp->conn = NULL;
		return(sp);
		}

	/*
	 * getcon() grabs a site name from the "News:" field or the "Mail:"
	 * field.  If the buffer passed is empty, it reads from the map file.
	 * if it reads a "Name:" field, it returns false.  If it reads a line
	 * with a colon in it and the passed flag is false, it returns false.
	 * It reads until there is something on the line that is not a blank,
	 * tab, comma, or a newline.  Once there is something useful in the
	 * buffer, it removes the first "word" terminated by a blank, tab, comma,
	 * or newline and returns true.
	 */

	getcon(buf, name, flag)
	char *buf;
	register char *name;
	int flag;
	{
		long ftell(), pos;
		register char *cp0, *cp1;
		char *fgets(), *wrdpos();

		cp0 = buf;
		cp1 = wrdpos(cp0);
		pos = ftell(map);
	/*
	 * read until there is some useful data in buf
	 */
		while((cp1==NULL)&&((cp0=fgets(buf,BUFSIZ,map))!=NULL)) {
	/*
	 * if there is a colon on this line, see if its a "Name:" field, "News:"
	 * field, or a "Mail:" field.
	 */
			if (upcase(cp0)) {
				if (strncmp(cp0, "NAME:", 5) == 0) {
					fseek(map, pos, 0);
					return(0);
					}
				if (strncmp(cp0, "NEWS:", 5) == 0) {
					cp0 += 5;
					flag = 0;
					}
				else if (strncmp(cp0, "MAIL:", 5) == 0) {
					cp0 += 5;
					flag = 0;
					}
				else if (!flag) {
					fseek(map, pos, 0);
					return(0);
					}
				else *cp0 = '\0';
				}
			else if (flag) *cp0 = '\0';
	/*
	 * leave if the line is not a continuation line (first char not blank or tab)
	 */
			if ((!flag) && (*cp0 != '\t') && (*cp0 != ' '))
				return(0);

			pos = ftell(map);
			cp1 = wrdpos(cp0);
			}
		if (cp0 == NULL) {
			fseek(map, pos, 0);
			return(0);
			}
	/*
	 * now that there is some data, we copy the first "word" into name, and
	 * remove the "word" from buf, returning true.
	 */
		while((*cp1) && (!isspace(*cp1)) && (*cp1 != ','))
			*name++ = *cp1++;
		*name = '\0';
		cp1 = wrdpos(cp1);
		if (cp1 == NULL)
			*buf = '\0';
		else strcpy(buf, cp1);
		return(1);
		}

	/*
	 * ptalloc() allocates a path structure.
	 */
	struct path *ptalloc()
	{
		char *calloc();
		register struct path *ptr;

		ptr = (struct path *)calloc(1, sizeof(struct path));
		if (ptr == NULL) {
			fprintf(stderr, "Cannot allocate memory for path.\n");
			exit(4);
			}
		ptr->siteptr = NULL;
		ptr->nxtpath = NULL;
		return(ptr);
		}

	/*
	 * paths() does all the dirty work.  For each site in the connection list,
	 * it checks the hop count of the stored route and the current route.
	 * if the current route is shorter or there is no stored route, it
	 * and assigns the current route as the stored route.
	 * it then calls itself with the current site's connection list.
	 */

	paths(sptr)
	struct site *sptr;
	{
		register struct path *pptr;
		register struct site *conptr;
		int scnt, hopcnt();
		register int cnt;

	/*
	 * loop through all the connections
	 */
	#ifdef DEBUG
	/*DBG*/ printf("\npaths:  at site %s\n", sptr->name);
	#endif
		scnt = hopcnt(sptr);
		pptr = sptr->conn;
		while(pptr != NULL) {
			conptr = pptr->siteptr;
			if (conptr != Strsite) {
			    cnt = hopcnt(conptr->route);
	/*
	 * if there is no stored route or if the stored route is longer than
	 * the current route, replace it.
	 */
			    if ((cnt == 0) || (cnt >= scnt)) {
	#ifdef DEBUG
	/*DBG*/ printf("changing path for %s.\nOld: ",conptr->name);
	/*DBG*/	proute(conptr->route);
	/*DBG*/	printf("\nNew:  ");
	/*DBG*/	proute(sptr);
	/*DBG*/	printf("\n");
	#endif
				conptr->route = sptr;
	/*
	 * final step, call paths() with this site's list of connections
	 */
				paths(conptr);
	#ifdef DEBUG
	/*DBG*/ printf("back from paths, now in %s\n", sptr->name);
	#endif
				}
			    }
			pptr = pptr->nxtpath;
			}
		}

	/*
	 * upcase() first scans to see if there is a colon in the buffer passed
	 * to it.  If not, it returns false.  Otherwise, it converts everything
	 * in the buffer up to the colon to uppercase and returns true.
	 */

	upcase(buf)
	register char *buf;
	{
		register char *cp;
		char *index();

		cp = index(buf, ':');
		if (cp == NULL)
			return(0);

		while(buf != cp) {
			if (islower(*buf))
				*buf = toupper(*buf);
			buf++;
			}
		return(1);
		}

	/*
	 * wrdpos() returns a pointer to the first character that is not a
	 * blank, tab, comma, or newline.  If it can't find one, it returns
	 * NULL.
	 */

	char *wrdpos(buf)
	register char *buf;
	{
		while((*buf) && (isspace(*buf)||(*buf==',')))
			buf++;
		if (*buf)
			return(buf);
		return(NULL);
		}

	/*
	 * hopcnt() returns the number of site structures in the routing list its
	 * argument points to.
	 */

	hopcnt(sptr)
	register struct site *sptr;
	{
		register int cnt;

		cnt = 0;
		while(sptr != NULL) {
			cnt++;
			sptr = sptr->route;
			}
		return(cnt);
		}

	/*
	 * proute() prints out the way to get to the passed site.  It calls itself
	 * with a pointer to the next site in the route, then prints the current
	 * site name.
	 */

	proute(sptr)
	register struct site *sptr;
	{
		if (sptr == NULL || sptr->route == NULL)
			return;
		proute(sptr->route);
		printf("%s!", sptr->name);
		}

	/*
	 * rempath removes the connection from its first argument to its
	 * second argument.
	 */

	rempath(sptr, cptr)
	register struct site *sptr, *cptr;
	{
		register struct path *pptr;
		struct path *pptr0;

		pptr = sptr->conn;
	/*
	 * loop through connections
	 */
		while( pptr != NULL) {
			if (pptr->siteptr == cptr) {
	/*
	 * found the connection, now must remove it.
	 */
				if (pptr == sptr->conn) {
					sptr->conn = pptr->nxtpath;
					free(pptr);
					pptr = sptr->conn;
					}
				else {
					pptr0->nxtpath = pptr->nxtpath;
					free(pptr);
					pptr = pptr0->nxtpath;
					}
				}
			else {
				pptr0 = pptr;
				pptr = pptr->nxtpath;
				}
			}
		}

	/*
	 * remsite removes a site from the data base.
	 */

	remsite(sptr)
	register struct site *sptr;
	{
		register struct site *sitep, *sitep0;
		struct path *pptr, *pptr0;

		sitep = Begsite;
	/*
	 * search the site list for the one we want.
	 */
		while( sitep != NULL) {
			if (sitep == sptr) {
	/*
	 * we found it.  Now free up the name and the connection list.
	 */
				free(sptr->name);
				pptr = sptr->conn;
				while(pptr != NULL) {
					pptr0 = pptr->nxtpath;
					free(pptr);
					pptr = pptr0;
					}
	/*
	 * now link the data around the site.
	 */
				if (sitep == Begsite) {
					Begsite = sitep->nxtsite;
					free(sitep);
					sitep = Begsite;
					}
				else {
					sitep0->nxtsite = sitep->nxtsite;
					free(sitep);
					sitep = sitep0->nxtsite;
					}
				}
			else {
	/*
	 * remove any connections between the current site & the one we are removing
	 */
				rempath(sitep, sptr);
				sitep0 = sitep;
				sitep = sitep->nxtsite;
				}
			}
		}

	/*
	 * index(s,c) returns a pointer to the first occurrance of 'c' in string
	 * 's', or NULL if 'c' is not in string 's'.
	 */

	char *index(s, c)
	register char *s, c;
	{
		if (s == NULL) return (NULL);
		while(*s && *s != c)
			s++;
		if (*s == c) return(s);
		return(NULL);
		}

	/*
	 * Compare strings (at most n bytes):  s1>s2: >0  s1==s2: 0  s1<s2: <0
	 */

	strncmp(s1, s2, n)
	register char *s1, *s2;
	register n;
	{

		if (s1 == NULL) {
			if (s2 == NULL)
				return(0);
			return(-1);
			}
		if (s2 == NULL)
			return(1);

		while (--n >= 0 && *s1 == *s2++)
			if (*s1++ == '\0')
				return(0);
		return(n<0 ? 0 : *s1 - *--s2);
		}
	/*
	 * usage() prints the usage message, then exits.
	 */
	usage(s)
	char *s;
	{
	  fprintf(stderr,
	    "Usage: %s [-2][-lsite][-dsite][-dsite!site][-asite!site] mapfile...\n",
		s);
	  exit(1);
	  }

	/*
	 * The following routine was taken from our UUCP source.
	 * I made some mods so it would fit in better with Mkpath.
	 *	Mike Mitchell, 4/10/83
	 */

	#include <stdio.h>
	#include <sys/types.h>

	#ifdef	UNAME
	#include <sys/utsname.h>
	#endif

	#ifdef	CCWHOAMI
	/*  compile in local uucp name of this machine */
	#include <whoami.h>
	#endif

	/*
	 * uucpname(name) puts the site name into the buffer pointed
	 * to by 'name'
	 */

	uucpname(name)
	register char *name;
	{
		register char *s, *d;

	#ifdef	UUNAME		/* This gets home site name from file  */
	    {
		FILE *uucpf;
		char stmp[10];

		s = stmp;
		if (((uucpf = fopen("/etc/uucpname", "r")) == NULL &&
		    (uucpf = fopen("/local/uucpname", "r")) == NULL) ||
		    fgets(s, 8, uucpf) == NULL) {
			s = "unknown";
			}
		else {
			for (d = stmp; *d && *d != '\n' && d < stmp + 8; d++)
				;
			*d = '\0';
			}
		if (uucpf != NULL)
			fclose(uucpf);
		}
	#endif

	#ifdef	UNAME
	    {
		struct utsname utsn;

		uname(&utsn);
		s = utsn.nodename;
	    }
	#endif

	#ifdef	CCWHOAMI
	    {
		s = sysname;
	    }
	#endif


		d = name;
		while ((*d = *s++) && d < name + 7)
			d++;
		*(name + 7) = '\0';
		return;
	}
*-*-END-*-*
sed 's/^	//' >mkpath.1 <<\*-*-END-*-*
	.TH MKPATH 1
	.SH NAME
	mkpath \- make a Usenet path data base
	.SH SYNOPSIS
	.B mkpath
	[
	.B \-2
	] [
	.B \-lsite
	] [
	.B \-dsite
	] [
	.B \-dsite1!site2
	] [
	.B \-asite1!site2
	]
	mapfiles...
	.SH DESCRIPTION
	.I Mkpath
	creates a list of USENET sites and the UUCP routing path to each site.
	The input is a list of USENET sites and what other sites each site is
	connected to.  The input data should look like:
	.nf

		Name: (name of site)
		(any other fields)
		News: (site names separated by blanks, commas, or newlines)
		(more site names)
		Mail: (still more site names)
		(even more site names)
		(any other fields)

	.fi
	Mark and Karen Horton just happen to send Usenet connection information
	in this format once a month to the news group net.news.map.
	Continuation lines must start with either a blank or tab.
	The output is sent to stdout, and looks like:
	.nf

		(name of site)	(printf string of how to get there)

	.fi
	The routes to all possible sites are listed.

	.PP
	Options:
	.TP 6
	.B \-2
	Interpret all connections as two-way links.
	.TP 6
	.B  \-lsite
	Set the originating system name to
	.I site.
	If this option is not used,
	.I mkpath
	gets the system name from one of several places, depending upon
	compile-time options.
	.TP 6
	.B  \-dsite
	Delete all references to
	.I site
	before building the UUCP paths.
	.TP 6
	.B \-dsite1!site2
	Delete the connection from
	.I site1
	to
	.I site2
	before building the UUCP paths.
	.TP 6
	.B \-asite1!site2
	Add the connection from
	.I site1
	to
	.I site2
	before building the UUCP paths.
	.dt
	.SH "SEE ALSO"
	nmail(1)
	.SH DIAGNOSTICS
	Exit code 1 is returned for an invalid command line, 2 for
	an inaccessible map input, 3 for starting system not in
	the input data, and 4 for not enough memory.
	.SH AUTHOR
	Mike Mitchell  (duke!mcnc!ncsu!mcm)
	.SH ACKNOWLEDGEMENTS
	Mark and Karen Horton	(cbosgd!mark)
	.br
	John Carl Zeigler	(duke!mcnc!ncsu!jcz)
	.SH BUGS
	.I Mkpath assumes every site is connected via UUCP and exchanges mail.
*-*-END-*-*
sed 's/^	//' >nmail.c <<\*-*-END-*-*
	/*
	 * nmail.c -- a network mail interface.
	 * This program accepts as arguments the standard network
	 * addresses, and expands the section of an argument up to a
	 * "!" into the actual path the letter must take to get to that
	 * site.  For example, to get to swd at duke, the argument
	 * "duke!swd" is expanded to "mcnc!duke!swd".  "decvax!ittvax!swatt"
	 * would expand to "mcnc!duke!decvax!ittvax!swatt".
	 *
	 * This program uses the output of the mkpath program developed by
	 * Mike Mitchell at NCSU (duke!mcnc!ncsu!mcm).
	 *
	 * The input data should look like:
	 *	sitename (any number of blanks or tabs) printf-string to get there
	 *
	 * The input data must be sorted.
	 *
	 * Version 2.0	10/15/82
	 *
	 * Written by Mike Mitchell (decvax!duke!mcnc!ncsu!mcm)
	 *
	 */

	#include <stdio.h>

	#define MAILER	"/bin/mail"		/* the mail program to use */
	#define DATA	"/usr/lib/nmail.paths"	/* where the data is kept */
	#define OK	1	/* all expansions done correctly */
	#define BAD	0	/* an expansion done incorrectly */

	main(argc,argv,envp)
	int argc;
	char **argv;
	char **envp;
	{
		FILE *fopen(),*fd;
		char route[BUFSIZ],tmp[BUFSIZ];
		char site[BUFSIZ],*cp,*index();
		char **args,*pos, *malloc(), *calloc();
		int num,rc,flag;

		args = (char **)calloc(argc+1, sizeof(char *));
		if (args == NULL) {
			fprintf(stderr, "Cannot get memory for argument list.\n");
			exit(1);
			}
		args[0] = "mail";
		num = 1;
		flag = OK;
		fd = fopen(DATA,"r");
		while(--argc) {
			argv++;
			if ((cp=index(*argv,'!')) == NULL) {
				pos = malloc(strlen(*argv) + 1);
				if (pos == NULL) {
					fprintf(stderr,
						"Cannot get memory for name %s\n",
						*argv);
					exit(1);
					}
				strcpy(pos,*argv);
				args[num++] = pos;
				}
			else {
				*cp = '\0';
				while( fscanf(fd,"%s%s",site,route) != EOF) {
					rc = strcmp(site, *argv);
					if (rc > 0) break;
					if (rc == 0) {
						sprintf(tmp,route,(cp+1));
						pos = malloc(strlen(tmp)+1);
						if (pos == NULL) {
							fprintf(stderr,
							"Can't get mem for path %s\n",
							tmp);
							exit(1);
							}
						strcpy(pos,tmp);
						args[num++] = pos;
						break;
						}
					}
				if (rc != 0) {
					printf("Can't get to %s from here\n",*argv);
					flag = BAD;
					}
				fseek(fd,0L,0);
				}
			}
		if (flag) {
			args[num] = NULL;
			for (rc = 0; rc < num; rc++)
				printf("%s ",args[rc]);
			printf("\n");
			execve(MAILER,args,envp);
			fprintf(stderr, "execve of %s failed!\n", MAILER);
			exit(1);
			}
		}
	/*
	 * index(s,c) returns a pointer to the first occurrance of 'c' in string
	 * 's', or NULL if 'c' is not in string 's'.
	 */

	char *index(s, c)
	register char *s, c;
	{
		if (s == NULL) return (NULL);
		while(*s && *s != c)
			s++;
		if (*s == c) return(s);
		return(NULL);
		}
*-*-END-*-*
sed 's/^	//' >nmail.1 <<\*-*-END-*-*
	.TH NMAIL 1
	.SH NAME
	nmail  \-  a UUCP network mail interface
	.SH SYNOPSIS
	.B nmail
	site!person ...
	.SH DESCRIPTION
	.I Nmail
	searches each argument for an explanation point.
	If it finds one, it replaces everything through
	the explanation point with the line it finds in
	the database.  Everything up to the explanation
	point is used as a key into the database.  The
	new arguments are echoed, then the normal mailer
	is executed with the new arguments.
	An element in the database should look like:
	.nf

	(site name)	(printf string)

	.fi
	This happens to be the format output by the
	.I mkpath(1)
	program.  The database must be sorted.
	.SH FILES
	.TP 20
	.I /usr/lib/nmail.paths
	the database
	.TP 20
	.I /bin/mail
	the mailer
	.SH "SEE ALSO"
	mkpath(1), mail(1)
	.SH AUTHOR
	Mike Mitchell  (duke!mcnc!ncsu!mcm)
*-*-END-*-*