[net.sources] nmail/mkpath -- with my mods

cds@marvin.UUCP (Chris Seabrook) (12/14/84)

[]
	I picked up nmail and mkpath from the net a while ago. For those who
hace not seen them, mkpath builds a database from the net.nws.map articles
and then nmail uses this to transform addresses of the form site!user to
path-to-site!user before calling a mailer. The versions of the programs I
got hold of stored a list of site names with paths and did a sequantial
search through it, problem, to find a site on the full net map took up
tp 40 minutes !!! I have slightly re-worked the programs to keep an offset
file as well and to do a binary search. It should find an address in 2-5
seconds (vax 750, 4.2, 15 users).
	I am posting complete sources as I know there are too many versions
of this stuff around for diff's to be of much use. I am also including the
uuhosts shell script for extracting articles from the map newsgroups
automatically, see comments in the file for how to install it.
	The locations of the data files are defined in the makefile, and the
name of the mailer to invoke is somewhere in the depth of nmail.c. This 
stuff run on 4.2 and 5.2 on a vax 11/750, it should run on other things but
I cannot say for sure as I have not tried.

: This is a shar archieve.  Extract with sh, not csh.
: The rest of this file will extract:
: Makefile.bsd Makefile.usg mkpath.1 mkpath.c nmail.1 nmail.c uuhosts
echo extracting - Makefile.bsd
sed 's/^X//' > Makefile.bsd << '//FUNKY//STUFF//'
X# makefile for mkpath and nmail
XCC=cc
XCFLAGS=-O -s -DDATA=\"$(DATAROOT)/$(DATA)\" -DRDATA=\"$(DATAROOT)/r$(DATA)\"
XDATAROOT=/users/cds/etc
XDATA=paths
X
Xall: mkpath nmail
X
Xdbx: dmkpath dnmail
X
Xdmkpath: mkpath.c
X	$(CC) $(CFLAGS) -DDEBUG -g -o dmkpath mkpath.c 
X
Xdnmail: nmail.c
X	$(CC) $(CFLAGS) -DDEBUG -g -o dnmail nmail.c 
//FUNKY//STUFF//
echo extracting - Makefile.usg
sed 's/^X//' > Makefile.usg << '//FUNKY//STUFF//'
X# makefile for mkpath and nmail
XCC=cc
XCFLAGS=-O -s -DCDS -DDATA=\"$(DATAROOT)/$(DATA)\" -DRDATA=\"$(DATAROOT)/r$(DATA)\" -DUNAME -Dindex=strchr
XDATAROOT=/users/cds/etc
XDATA=paths
X
Xall: mkpath nmail
X
Xdbx: dmkpath dnmail
X
Xdmkpath: mkpath.c
X	$(CC) $(CFLAGS) -DDEBUG -g -o dmkpath mkpath.c 
X
Xdnmail: nmail.c
X	$(CC) $(CFLAGS) -DDEBUG -g -o dnmail nmail.c 
//FUNKY//STUFF//
echo extracting - mkpath.1
sed 's/^X//' > mkpath.1 << '//FUNKY//STUFF//'
X.TH MKPATH local
X.SH NAME
Xmkpath \- make a Usenet path data base
X.SH SYNOPSIS
X.B mkpath
X[
X.B \-2
X] [
X.B \-lsite
X] [
X.B \-dsite
X] [
X.B \-dsite1!site2
X] [
X.B \-asite1!site2
X]
Xmapfiles...
X.SH DESCRIPTION
X.I Mkpath
Xcreates a list of USENET sites and the UUCP routing path to each site.
XThe input is a list of USENET sites and what other sites each site is
Xconnected to.  The input data should look like:
X.nf
X
X	Name: (name of site)
X	(any other fields)
X	News: (site names separated by blanks, commas, or newlines)
X	(more site names)
X	Mail: (still more site names)
X	(even more site names)
X	(any other fields)
X
X.fi
XMark and Karen Horton just happen to send Usenet connection information
Xin this format once a month to the news group net.news.map.
XThe output is sent to stdout, and looks like:
X.nf
X
X	(name of site)	(printf string of how to get there)
X
X.fi
XThe routes to all possible sites are listed.
X
X.PP
XOptions:
X.TP 6
X.B \-2
XInterpret all connections as two-way links.
X.TP 6
X.B  \-lsite
XSet the originating system name to
X.I site.
XIf this option is not used,
X.I mkpath
Xuses the system name from <whoami.h>.
X.TP 6
X.B  \-dsite
XDelete all references to
X.I site
Xbefore building the UUCP paths.
X.TP 6
X.B \-dsite1!site2
XDelete the connection from
X.I site1
Xto
X.I site2
Xbefore building the UUCP paths.
X.TP 6
X.B \-asite1!site2
XAdd the connection from
X.I site1
Xto
X.I site2
Xbefore building the UUCP paths.
X.dt
X.SH "SEE ALSO"
Xnmail(1)
X.SH DIAGNOSTICS
XExit code 1 is returned for an invalid command line, 2 for
Xan inaccessible map input, 3 for starting system not in
Xthe input data, and 4 for not enough memory.
X.SH AUTHOR
XMike Mitchell  (duke!mcnc!ikonas!mcm)
X.SH ACKNOWLEDGEMENTS
XMark and Karen Horton	(cbosgd!mark)
X.br
XJohn Carl Zeigler	(duke!mcnc!ncsu!jcz)
X.SH BUGS
X.I Mkpath assumes every site is connected via UUCP and exchanges mail.
//FUNKY//STUFF//
echo extracting - mkpath.c
sed 's/^X//' > mkpath.c << '//FUNKY//STUFF//'
X/*
X * Mkpath creates UUCP routing information from the Usenet directory
X * Mark and Karen Horton send down the Usenet.  The map input should
X * look something like :
X *
X *	Name:	name-of-site
X *	(extra fields)
X *	News:	connected sites
X *	(any extra connected sites)
X *	Mail:	connected sites
X *	(any extra connected sites)
X *	Name:
X *
X * The News: and Mail: fields must not have any other fields between
X * them.  Blank lines can be used to separate fields.  The connected sites
X * can be separated by any number of blanks, tabs, commas, or new-lines.
X *
X * Usage: mkpath [-lsite][-2][-dsite][-dsite1!site2][-asite1!site2] map-file(s)
X *
X * -lsite creates paths starting at "site".  If not supplied,
X * the system name from the <whoami.h> file is used.
X *
X * -2 forces all routes to be two-way.
X *
X * -dsite removes "site" from the list; -dsite1!site2 removes the connection
X * from "site1" to "site2".
X *
X * -asite1!site2 adds the connection from "site1" to "site2".  If the -2
X * option is used, it adds the connection from "site2" to "site1" as well.
X *
X * The output goes to stdout.
X *
X * Version 3.0	2/12/83
X * Version 3.1  2/21/83		added -a option
X * Version 3.2	2/23/83		made storage for -a,-d options dynamic
X * 11/7/83			change the input format to look like
X *				Mark Horton's new format.
X *
X * Written by Mike Mitchell	decvax!duke!mcnc!ikonas!mcm
X * Modified by David B. Wollner	sdcsvax!dbw
X * Modified Nigel Horne for the uname system call.	root44!njh
X * Modified by Chris Seabrook qtlon!cds to add offset data file, this speeds up
X *					the nmail search for a site by an order
X *					of magnitude. Also made location of data
X *					and offset data compile time options.
X *
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#ifdef	UNAME
X#include <sys/utsname.h>
X#else
X#include <whoami.h>
X#endif
X
XFILE *map;			/* map input file */
X
Xstruct site {			/* structure for all needed info on a site */
X	char *name;		/* name of site */
X	struct site *route;	/* route to take to get there */
X	struct path *conn;	/* who the site is connected to */
X	struct site *nxtsite;	/* pointer to next site in list */
X	};
X
Xstruct path {			/* structure for connection & routing info */
X	struct site *siteptr;	/* pointer to site data */
X	struct path *nxtpath;	/* pointer to next hop in route */
X	};
X
Xstruct site *Begsite;		/* pointer to the start of site data */
Xstruct site *Strsite;		/* pointer to starting point's site data */
X#ifdef	UNAME
Xchar *SYSNAME;			/* system name from whoami.h */
Xstruct	utsname	utsname;
X#else
Xchar *SYSNAME = sysname;	/* system name from whoami.h */
X#endif
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int i, dno, ano, twoflg, upcase(), getcon();
X	register struct site *curptr;	/* current site */
X	register struct path *conptr;	/* pointer to connections */
X	struct site *search(), *getsite();
X	register struct site *sptr;
X	struct path *twoptr, *lnkptr;
X	struct path *ptalloc();
X	char buf[BUFSIZ], tmp[BUFSIZ], *fgets();
X	char **dsites, **asites, *stnam, *cp, *index();
X	char **calloc();
X	FILE *fopen();
X	int offset, nsite;
X	struct site **site_list;
X	int site_comp();
X
X	if (argc < 2) {
X		fprintf(stderr,
X	"Usage: %s [-lsite][-dsite][-dsite!site][-asite!site] mapfile...\n",
X			argv[0]);
X		exit(1);
X		}
X#ifdef	UNAME
X	uname(&utsname);
X	SYSNAME=utsname.nodename;
X#endif
X
X	Begsite = NULL;
X	i = 0;
X	argc--;
X	dno = 0;
X	ano = 0;
X	twoflg = 0;
X	stnam = SYSNAME;
X/*
X * scan for options we can take care of now.  Allocate space for -a and -d
X * options.
X */
X	while(i++ < argc) {
X		if (argv[i][0] == '-') {
X			switch(argv[i][1]) {
X				case 'l':  stnam = &argv[i][2];
X					   break;
X
X				case '2':  twoflg = 1;
X					   break;
X
X				case 'd':  dno++;
X					   break;
X
X				case 'a':  ano++;
X					   break;
X
X				default:   fprintf(stderr, "Bad option %s\n",
X						argv[i]);
X					   exit(1);
X				}
X			}
X		}
X	if (dno != 0) {
X		if ((dsites = calloc(dno, sizeof(char *))) == NULL) {
X			fprintf(stderr, "Can't get mem for -d options");
X			exit(4);
X			}
X		}
X	if (ano != 0) {
X		if ((asites = calloc(ano, sizeof(char *))) == NULL) {
X			fprintf(stderr, "Can't get mem for -a options");
X			exit(4);
X			}
X		}
X	ano = 0;
X	dno = 0;
X	i = 0;
X/*
X * Loop through the map data files, building the site & connection data
X */
X	while(i++ < argc) {
X
X		if (argv[i][0] == '-') {
X			switch(argv[i][1]) {
X
X				case 'd':  dsites[dno++] = &argv[i][2];
X					   break;
X
X				case 'a':  asites[ano++] = &argv[i][2];
X					   break;
X				}
X			continue;
X			}
X
X		map = fopen(argv[i], "r");
X		if (map == NULL) {
X			fprintf(stderr, "Cannot open %s for map input\n",
X				argv[i]);
X			exit(2);
X			}
X		while(fgets(buf, BUFSIZ, map) != NULL) {
X/*
X * look for a "Name:" field
X */
X			if ((upcase(buf))&&(strncmp(buf,"NAME:",5)==0)) {
X/*
X * find out if the current site is in our list.  If not, add it to the
X * list.
X */
X			    if (sscanf(buf, "NAME: %s", tmp)!=1)
X				continue;
X#ifdef DEBUG
Xprintf("site found -> %s\n", tmp);
X#endif
X			    curptr = getsite(tmp);
X			    buf[0] = '\0';
X/*
X * now grab the first connected site name, if any.
X */
X			    if (getcon(buf, tmp, 1)) {
X/*
X * allocate space for the path structure, then check to see if the
X * connection is a "known" site.  If it isn't, add it to our list
X * of "known" sites.  Make the recently allocated path structure
X * point to the site, and link it in with the rest of the connection
X * info.
X */
X				curptr->conn = ptalloc();
X				conptr = curptr->conn;
X				conptr->siteptr=getsite(tmp);
X/*
X * now get the rest of the connected site names
X */
X				while(getcon(buf, tmp, 0)) {
X				    conptr->nxtpath = ptalloc();
X				    conptr = conptr->nxtpath;
X				    conptr->siteptr = getsite(tmp);
X				    }
X				}
X			    }
X			}
X		fclose(map);
X		}
X/*
X * add any connections specified on the command line
X */
X	while(ano--) {
X		if ((cp=index(asites[ano],'!')) == NULL) {
X			fprintf(stderr,
X				"Warning: -a%s is an invalid -a command.\n",
X				asites[ano]);
X			continue;
X			}
X		*cp++ = '\0';
X		curptr = getsite(asites[ano]);
X		sptr = getsite(cp);
X/*
X * go through connection list for site1, and make sure site2 is not already
X * connected.
X */
X		conptr = curptr->conn;
X		while(conptr != NULL) {
X			if (conptr->siteptr->name == sptr->name)
X				break;
X			conptr = conptr->nxtpath;
X			}
X		if (conptr == NULL) {
X/*
X * add the site into the connection list.
X */
X			conptr = curptr->conn;
X			curptr->conn = ptalloc();
X			curptr->conn->nxtpath = conptr;
X			curptr->conn->siteptr = sptr;
X			}
X		}
X
X/*
X * go through the list and make sure the connections are two way.
X */
X    if (twoflg) {
X	curptr = Begsite;
X	while(curptr != NULL) {
X		twoptr = curptr->conn;
X/*
X * loop through each site's connection list
X */		while(twoptr != NULL) {
X
X			conptr = twoptr->siteptr->conn;
X/*
X * loop through the connection's connections, looking for the current
X * site.  Add the site to the connection's connections if it is not
X * found.
X */
X			while((conptr != NULL) && (conptr->siteptr != curptr))
X				conptr = conptr->nxtpath;
X			if (conptr == NULL) {
X				lnkptr = ptalloc();
X				lnkptr->siteptr = curptr;
X				lnkptr->nxtpath = twoptr->siteptr->conn;
X				twoptr->siteptr->conn = lnkptr;
X				}
X			twoptr = twoptr->nxtpath;
X			}
X		curptr = curptr->nxtsite;
X		}
X	    }
X/*
X * now handle the -d options.
X */
X	while( dno-- ) {
X/*
X * if a '!' is not in the argument, remove the site name.
X */
X		if ((cp=index(dsites[dno], '!')) == NULL) {
X			curptr = search(dsites[dno]);
X			if (curptr == NULL) {
X				fprintf(stderr,
X					"Warning: site %s already removed\n",
X					dsites[dno]);
X				continue;
X				}
X			remsite(curptr);	/* remove the site */
X			}
X/*
X * otherwise, remove the connection from the first site to the second site
X */
X		else {
X			*cp++ = '\0';
X			curptr = search(dsites[dno]);
X			sptr = search(cp);
X			if (curptr == NULL || sptr == NULL ) {
X				fprintf(stderr,
X				   "Warning: connection %s!%s non-existent\n",
X				    dsites[dno], cp);
X				continue;
X				}
X			rempath(curptr, sptr);	/* remove the connection */
X			}
X		}
X/*
X * look for the starting point
X */
X	Strsite = search(stnam);
X	if (Strsite == NULL) {
X		fprintf(stderr, "cannot find site %s\n", stnam);
X		exit(3);
X		}
X/*
X * The hard part -- create the UUCP route for every site.  Paths() is
X * a recursive routine that wanders through the connection info
X * looking for the best way to get somewhere.
X */
X	paths(Strsite);
X
X/*
X * now that paths has finished, output the routing information.
X * All we have to do is traverse the "route" pointers!
X */
X	curptr = Begsite;
X
X	freopen(DATA, "w", stdout);
X	
X	offset = creat(RDATA, 0644);
X	nsite = 0;
X
X	while(curptr != NULL) {
X		if(curptr->route != NULL)
X			nsite++;
X		curptr = curptr->nxtsite;
X	}
X
X	curptr = Begsite;
X
X	/*
X	 * qtlon!cds
X	 *
X	 * Build an array of pointers to site names so we can sort them.
X	 * This will enable nmail to use a sensible search algorithm when
X	 * it comes to read the data.
X	 */
X	site_list = (struct site **)calloc(nsite, sizeof(struct site *));
X	if(site_list == NULL) {
X		fprintf(stderr, "No mem for pointer list\n");
X		exit(5);
X	}
X	i = 0;
X	while(curptr != NULL) {
X		if(curptr->route != NULL)
X			site_list[i++] = curptr;
X		curptr = curptr->nxtsite;
X	}
X
X	qsort(site_list, nsite, sizeof(struct site *), site_comp);
X
X	/*
X	 * qtlon!cds
X	 *
X	 * Record the number of sites so nmail will know how much space to
X	 * allocate when it comes to read the file.
X	 */
X	write(offset, &nsite, sizeof(long));
X
X	/*
X	 * qtlon!cds
X	 *
X	 * Now dump the sorted list to the data file, dump the offset of the
X	 * start of each line to the raw data file so nmail will be able to
X	 * find it quickly.
X	 */
X	for(i=0;i<nsite;i++) {
X		long pos;
X
X		pos = ftell(stdout);
X#ifdef DEBUG
X		fprintf(stderr, "site %d offset %ld\n", i, pos);
X#endif
X		write(offset, &pos, sizeof(long));
X		printf("%s\t", site_list[i]->name);
X		proute(site_list[i]);
X		printf("%%s\n");
X	}
X	close(offset);
X#ifdef CDS
X	fprintf(stderr, "Database built, contains entries for %d sites\n", nsite);
X#endif
X	fclose(stdout);
X}
X
Xsite_comp(a, b) struct site **a, **b;
X{
X	return(strcmp((*a)->name, (*b)->name));
X}
X/*
X * search() searches the "known" sites, looking for the site name
X * passed to it.  If it finds it, it returns the site pointer to
X * it. Otherwise, it returns NULL.
X */
X
Xstruct site *search(nam)
Xregister char *nam;
X{
X	register struct site *sp;
X
X	sp = Begsite;
X	while(sp != NULL) {
X		if (strcmp(sp->name, nam) == 0)
X			return(sp);
X		sp = sp->nxtsite;
X		}
X	return(NULL);
X	}
X
X/*
X * getsite() searchs the site list for a given site.  If the site is not
X * found, it adds the site name passed to it to the list of "known"
X * sites.  It allocates the site structure & space for the name,
X * copies the name, and links everything in.  It returns a pointer to
X * the site structure.
X */
X
Xstruct site *getsite(nam)
Xregister char *nam;
X{
X	char *malloc() /*, *calloc() */ ;
X	register char *cp;
X	register struct site *sp;
X	struct site *search();
X
X	cp = nam;	/* remove any junk from the name */
X	while (*cp) {
X		if (isspace(*cp) || *cp == ',') {
X			*cp = '\0';
X			break;
X			}
X		cp++;
X		}
X
X	if ((sp = search(nam)) != NULL)
X		return(sp);
X
X	sp = (struct site *)calloc(1, sizeof(struct site));
X	if (sp == NULL) {
X		fprintf(stderr, "Cannot get mem for new site!\n");
X		exit(4);
X		}
X	cp = malloc(strlen(nam) + 1);
X	if (cp == NULL) {
X		fprintf(stderr, "Cannot get mem for name!\n");
X		exit(4);
X		}
X	strcpy(cp, nam);
X	sp->name = cp;
X	sp->nxtsite = Begsite;
X	Begsite = sp;
X	sp->route = NULL;
X	sp->conn = NULL;
X	return(sp);
X	}
X
X/*
X * getcon() grabs a site name from the "News:" field or the "Mail:"
X * field.  If the buffer passed is empty, it reads from the map file.
X * if it reads a "Name:" field, it returns false.  If it reads a line
X * with a colon in it and the passed flag is false, it returns false.
X * It reads until there is something on the line that is not a blank,
X * tab, comma, or a newline.  Once there is something useful in the
X * buffer, it removes the first "word" terminated by a blank, tab, comma,
X * or newline and returns true.
X */
X
Xgetcon(buf, name, flag)
Xchar *buf;
Xregister char *name;
Xint flag;
X{
X	long ftell(), pos;
X	register char *cp0, *cp1;
X	char *fgets(), *wrdpos();
X
X	cp0 = buf;
X	cp1 = wrdpos(cp0);
X	pos = ftell(map);
X/*
X * read until there is some useful data in buf
X */
X	while((cp1==NULL)&&((cp0=fgets(buf,BUFSIZ,map))!=NULL)) {
X/*
X * if there is a colon on this line, see if its a "Name:" field, "News:"
X * field, or a "Mail:" field.
X */
X		if (upcase(cp0)) {
X			if (strncmp(cp0, "NAME:", 5) == 0) {
X				fseek(map, pos, 0);
X				return(0);
X				}
X			if (strncmp(cp0, "NEWS:", 5) == 0)
X				cp0 += 5;
X			else if (strncmp(cp0, "MAIL:", 5) == 0)
X				cp0 += 5;
X			else if (!flag) {
X				fseek(map, pos, 0);
X				return(0);
X				}
X			else *cp0 = '\0';
X			}
X		else if (flag) *cp0 = '\0';
X		pos = ftell(map);
X		cp1 = wrdpos(cp0);
X		}
X	if (cp0 == NULL) {
X		fseek(map, pos, 0);
X		return(0);
X		}
X/*
X * now that there is some data, we copy the first "word" into name, and
X * remove the "word" from buf, returning true.
X */
X#ifdef DEBUG
Xprintf("connection found -> %s\n", cp1);
X#endif
X	while((*cp1) && (!isspace(*cp1)) && (*cp1 != ','))
X		*name++ = *cp1++;
X	*name = '\0';
X	cp1 = wrdpos(cp1);
X	if (cp1 == NULL)
X		*buf = '\0';
X	else strcpy(buf, cp1);
X	return(1);
X	}
X
X/*
X * ptalloc() allocates a path structure.
X */
Xstruct path *ptalloc()
X{
X/*	char *calloc(); */
X	register struct path *ptr;
X
X	ptr = (struct path *)calloc(1, sizeof(struct path));
X	if (ptr == NULL) {
X		fprintf(stderr, "Cannot allocate memory for path\n");
X		exit(4);
X		}
X	ptr->siteptr = NULL;
X	ptr->nxtpath = NULL;
X	return(ptr);
X	}
X
X/*
X * paths() does all the dirty work.  For each site in the connection list,
X * it checks the hop count of the stored route and the current route.
X * if the current route is shorter or there is no stored route, it
X * and assigns the current route as the stored route.
X * it then calls itself with the current site's connection list.
X */
X
Xpaths(sptr)
Xstruct site *sptr;
X{
X	register struct path *pptr;
X	register struct site *conptr;
X	int scnt, hopcnt();
X	register int cnt;
X
X/*
X * loop through all the connections
X */
X#ifdef DEBUG
X/*DBG*/ printf("\npaths:  at site %s\n", sptr->name);
X#endif
X	scnt = hopcnt(sptr);
X	pptr = sptr->conn;
X	while(pptr != NULL) {
X		conptr = pptr->siteptr;
X		if (conptr != Strsite) {
X		    cnt = hopcnt(conptr->route);
X/*
X * if there is no stored route or if the stored route is longer than
X * the current route, replace it.
X */
X		    if ((cnt == 0) || (cnt >= scnt)) {
X#ifdef DEBUG
X/*DBG*/ printf("changing path for %s.\nOld: ",conptr->name);
X/*DBG*/	proute(conptr->route);
X/*DBG*/	printf("\nNew:  ");
X/*DBG*/	proute(sptr);
X/*DBG*/	printf("\n");
X#endif
X			conptr->route = sptr;
X/*
X * final step, call paths() with this site's list of connections
X */
X			paths(conptr);
X#ifdef DEBUG
X/*DBG*/ printf("back from paths, now in %s\n", sptr->name);
X#endif
X			}
X		    }
X		pptr = pptr->nxtpath;
X		}
X	}
X
X/*
X * upcase() first scans to see if there is a colon in the buffer passed
X * to it.  If not, it returns false.  Otherwise, it converts everything
X * in the buffer up to the colon to uppercase and returns true.
X */
X
Xupcase(buf)
Xregister char *buf;
X{
X	register char *cp;
X	char *index();
X
X	cp = index(buf, ':');
X	if (cp == NULL)
X		return(0);
X
X	while(buf != cp) {
X		if (islower(*buf))
X			*buf = toupper(*buf);
X		buf++;
X		}
X	return(1);
X	}
X
X/*
X * wrdpos() returns a pointer to the first character that is not a
X * blank, tab, comma, or newline.  If it can't find one, it returns
X * NULL.
X */
X
Xchar *wrdpos(buf)
Xregister char *buf;
X{
X	while((*buf) && (isspace(*buf)||(*buf==',')))
X		buf++;
X	if (*buf)
X		return(buf);
X	return(NULL);
X	}
X
X/*
X * hopcnt() returns the number of site structures in the routing list its
X * argument points to.
X */
X
Xhopcnt(sptr)
Xregister struct site *sptr;
X{
X	register int cnt;
X
X	cnt = 0;
X	while(sptr != NULL) {
X		cnt++;
X		sptr = sptr->route;
X		}
X	return(cnt);
X	}
X
X/*
X * proute() prints out the way to get to the passed site.  It calls itself
X * with a pointer to the next site in the route, then prints the current
X * site name.
X */
X
Xproute(sptr)
Xregister struct site *sptr;
X{
X	if (sptr == NULL || sptr->route == NULL)
X		return;
X	proute(sptr->route);
X	printf("%s!", sptr->name);
X	}
X
X/*
X * rempath removes the connection from its first argument to its
X * second argument.
X */
X
Xrempath(sptr, cptr)
Xregister struct site *sptr, *cptr;
X{
X	register struct path *pptr;
X	register struct path *pptr0;
X
X	pptr = sptr->conn;
X/*
X * loop through connections
X */
X	while( pptr != NULL) {
X		if (pptr->siteptr == cptr) {
X/*
X * found the connection, now must remove it.
X */
X			if (pptr == sptr->conn) {
X				sptr->conn = pptr->nxtpath;
X				free(pptr);
X				pptr = sptr->conn;
X				}
X			else {
X				pptr0->nxtpath = pptr->nxtpath;
X				free(pptr);
X				pptr = pptr0->nxtpath;
X				}
X			}
X		else {
X			pptr0 = pptr;
X			pptr = pptr->nxtpath;
X			}
X		}
X	}
X
X/*
X * remsite removes a site from the data base.
X */
X
Xremsite(sptr)
Xregister struct site *sptr;
X{
X	register struct site *sitep, *sitep0;
X	struct path *pptr, *pptr0;
X
X	sitep = Begsite;
X/*
X * search the site list for the one we want.
X */
X	while( sitep != NULL) {
X		if (sitep == sptr) {
X/*
X * we found it.  Now free up the name and the connection list.
X */
X			free(sptr->name);
X			pptr = sptr->conn;
X			while(pptr != NULL) {
X				pptr0 = pptr->nxtpath;
X				free(pptr);
X				pptr = pptr0;
X				}
X/*
X * now link the data around the site.
X */
X			if (sitep == Begsite) {
X				Begsite = sitep->nxtsite;
X				free(sitep);
X				sitep = Begsite;
X				}
X			else {
X				sitep0->nxtsite = sitep->nxtsite;
X				free(sitep);
X				sitep = sitep0->nxtsite;
X				}
X			}
X		else {
X/*
X * remove any connections between the current site & the one we are removing
X */
X			rempath(sitep, sptr);
X			sitep0 = sitep;
X			sitep = sitep->nxtsite;
X			}
X		}
X	}
//FUNKY//STUFF//
echo extracting - nmail.1
sed 's/^X//' > nmail.1 << '//FUNKY//STUFF//'
X.TH NMAIL local
X.SH NAME
Xnmail  \-  a UUCP network mail interface
X.SH SYNOPSIS
X.B nmail
X[
X-p
X]
Xsite!person ...
X.SH DESCRIPTION
X.I Nmail
Xsearches each argument for an explanation point.
XIf it finds one, it replaces everything through
Xthe explanation point with the line it finds in
Xthe database.  Everything up to the explanation
Xpoint is used as a key into the database.  The
Xnew arguments are echoed, then the normal mailer
Xis executed with the new arguments.
XAn element in the database should look like:
X.nf
X
X(site name)	(printf string)
X
X.fi
XThis happens to be the format output by the
X.I mkpath(1)
Xprogram.  The database must be sorted.
X.SH OPTIONS
X.TP
X.B \-p	Returns the pathname only to standard output.  This
Xoption is usefull for emacs mail.
X.SH FILES
X.TP 20
X.I /usr/lib/PATHS
Xthe sorted database
X.TP 20
X.I /bin/mail
Xthe mailer
X.SH "SEE ALSO"
Xmkpath(1), mail(1)
X.SH AUTHOR
XMike Mitchell  (duke!mcnc!ikonas!mcm)
//FUNKY//STUFF//
echo extracting - nmail.c
sed 's/^X//' > nmail.c << '//FUNKY//STUFF//'
X/*
X * nmail.c -- a network mail interface.
X * This program accepts as arguments the standard network
X * addresses, and expands the section of an argument up to a
X * "!" into the actual path the letter must take to get to that
X * site.  For example, to get to swd at duke, the argument
X * "duke!swd" is expanded to "mcnc!duke!swd".  "decvax!ittvax!swatt"
X * would expand to "mcnc!duke!decvax!ittvax!swatt".
X *
X * This program uses the output of the mkpath program developed by
X * Mike Mitchell at ikonas (duke!mcnc!ikonas!mcm).
X *
X * The input data should look like:
X *	sitename (any number of blanks or tabs) printf-string to get there
X *
X * The input data must be sorted.
X *
X * Version 2.0	10/15/82
X *
X * Written by Mike Mitchell (decvax!duke!mcnc!ikonas!mcm)
X *
X *
X * M o d i f i c a t i o n    H i s t o r y
X *
X * BFE	3/8/83	wi001	Changed MAILER to /usr/ucb/mail
X *			Added -p option to return the path to stdio only,
X *			this is usefull for emacs mail.
X * BFE	3/9/83	wi002	We are going to substitute this program for standard
X *			mail since it goes on unnoticed to the user.  We only
X *			need to remove a line which prints 'mail ...' just
X *			before the actual mail call.  This way it is truely
X *			invisible to the user.
X * dbw	11/8/83	wi003	Change the MAILER to use MAILER environment variable
X *			with the default being a compile-time option, MAILER.
X *			Change the path	library to live in a compile-time
X *			option DATA.
X * qtlon!cds 21/8/84	Added data offset file to nmail and mkpath to speed
X *			up searches greatly. Location of raw data file as with
X *			data is a compile time option.
X */
X
X#include <stdio.h>
X
X#define MAILER	getenv("MAILER") == NULL ? "/bin/mail" : getenv("MAILER")
X
X#define OK	1	/* all expansions done correctly */
X#define BAD	0	/* an expansion done incorrectly */
X/*#define	DEBUG		/* debugging from root44!njh */
X
Xmain(argc,argv,envp)
Xint argc;
Xchar **argv;
Xchar **envp;
X{
X	FILE *fopen(),*fd;
X	char route[BUFSIZ],tmp[BUFSIZ];
X	char site[BUFSIZ],*cp,*index();
X	char **args,*pos, *malloc(), **calloc();
X	long *addr, naddr, first, last, try;
X	int data_addr, i;
X	/* wi001 - added pflg */
X	int num,rc,flag,pflg = 0;
X
X	/* wi001 - accept a '-p' option */
X	if(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'p') {
X		pflg++;
X		argc--;
X		argv++;
X	}
X
X	args = calloc(argc+1, sizeof(char *));
X	if (args == NULL) {
X		fprintf(stderr, "Cannot get memory for argument list.\n");
X		exit(1);
X		}
X	/* wi001 - if -p is present then delete 'mail' from argument */
X	if (!pflg) {
X		args[0] = "mail";
X		num = 1;
X	}
X	flag = OK;
X	data_addr = open(RDATA,0);
X	/*
X	 * qtlon!cds
X	 *
X	 * Read the data offsets from the raw data file, these tell us where
X	 * to find the start of lines in the main data file to helpo speed
X	 * the search
X	 */
X	if(data_addr < 0) {
X		fprintf(stderr, "Can't read data offsets from %s\n", RDATA);
X		exit(2);
X	} else {
X		if(read(data_addr, &naddr, sizeof(long)) != sizeof(long)) {
X			fprintf(stderr, "No offset data\n");
X			exit(3);
X		}
X#ifdef DEBUG
X		printf("naddr read as %d\n", naddr);
X#endif
X		/*
X		 * qtlon!cds
X		 *
X		 * Read all the offsets into a buffer so they are there when
X		 * needed. May cause problems with whole map on small
X		 * machines
X		 */
X		addr = (long *)calloc(naddr+1, sizeof(long));
X		if(read(data_addr,addr+1,naddr*sizeof(long)) != (naddr*sizeof(long))) {
X			fprintf(stderr, "Offset data read error\n");
X			exit(3);
X		}
X		close(data_addr);
X	}
X#ifdef DEBUG
X	for(i=0;i<=naddr;i++)
X		printf("addr[%d] = %d\n", i, addr[i]);
X#endif
X	fd = fopen(DATA,"r");
X	if (fd == NULL){	/* root44!njh */
X		fprintf(stderr, "Can't read %s\n", DATA);
X		exit(2);
X	}
X	while(--argc) {
X		argv++;
X		if ((cp=index(*argv,'!')) == NULL) {
X			pos = malloc(strlen(*argv) + 1);
X			if (pos == NULL) {
X				fprintf(stderr,
X					"Cannot get memory for name %s\n",
X					*argv);
X				exit(1);
X				}
X			strcpy(pos,*argv);
X			args[num++] = pos;
X			}
X		else {
X		/*
X		 * qtlon!cds
X		 *
X		 * With the help of the offset data do a binary chop search
X		 * through the site list looking for the required name.
X		 */
X			first = 0;
X			last = naddr + 1;
X			try = naddr/2;
X			fseek(fd, addr[try], 0);
X#ifdef DEBUG
X			printf("look for %s, first %d try %d last %d\n",
X					*argv,
X					first,
X					try,
X					last);
X			printf("fseek(fd, addr[try] (%d), 0)\n\n", addr[try]);
X#endif
X			*cp = '\0';
X			while( fscanf(fd,"%s%s",site,route) != EOF) {
X#ifdef	DEBUG
X				printf("got site %s route %s\n", site, route);
X				printf("first %d\ttry %d\tlast %d\n\n",
X						first,
X						try,
X						last);
X#endif
X				rc = strcmp(site, *argv);
X				if (rc == 0) {
X					sprintf(tmp,route,(cp+1));
X					pos = malloc(strlen(tmp)+1);
X					if (pos == NULL) {
X						fprintf(stderr,
X						"Can't get mem for path %s\n",
X						tmp);
X						exit(1);
X					}
X					strcpy(pos,tmp);
X					args[num++] = pos;
X					printf("%s\n", pos);
X					break;
X				}
X				if(rc < 0) {
X					first = try;
X					try += ((last - try) +1 ) / 2;
X				} else {
X					last = try;
X					try -= ((try - first) +1 ) / 2;
X				}
X				/*
X				 * qtlon!cds
X				 *
X				 * The end of the road. Thre are no more sites
X				 * to try, ours doesn't exist. At least the
X				 * map dooesn't show it.
X				 */
X				if(last - first <= 1 &&
X				( try == first || try == last))
X					break;
X				fseek(fd, addr[try], 0);
X			}
X			if (rc != 0) {
X				printf("Can't get to %s from here\n",*argv);
X				flag = BAD;
X				}
X			fseek(fd,0L,0);
X			}
X		}
X	if (flag) {
X		/* wi001 - do the mailing only if -p was not specified */
X		/* wi002 - print here only if pflg is on (for emacs use) */
X		if (pflg) {
X		    args[num] = NULL;
X		    for (rc = 0; rc < num; rc++)
X			    printf("%s ",args[rc]);
X			    printf("\n");
X		    } else {
X			    execve(MAILER,args,envp);
X			    fprintf(stderr, "execve of %s failed!\n", MAILER);
X			    exit(1);
X		    }
X		}
X}
X
//FUNKY//STUFF//
echo extracting - uuhosts
sed 's/^X//' > uuhosts << '//FUNKY//STUFF//'
X#!/bin/sh
X# '@(#) uuhosts.sh 1.17 84/08/04'
X
X# PATH will have to be adjusted for non-BSD systems.
XPATH=/users/cds/bin:/bin:/usr/bin:
XLIB=/users/cds/etc
X
X# Routing information produced by pathalias.
Xpaths=$LIB/paths
X
X# The directory $NEWSMAP should contain the USENET news map information
X# from newsgroup net.news.map that is posted about once a month from
X# cbosgd!map, extracted by a line like this in $LIB/news/sys:
X#
X#	newsmap:net.news.map:B:/usr/local/uuhosts -x
X#
X# Locally-known information should go in $LIB/news/net.news.map/Local.
X# The directory $MAILMAP is extracted by the same command from the
X# UUCP mail information posted to the same newsgroup.
XNEWSMAP=$LIB/net.news.map
XMAILMAP=$LIB/net.mail.map
Xcd $NEWSMAP
X
Xcase $1 in
X	-x)
X		# extract a new map piece into the map directory
X		temphead=/tmp/maphead.$$
X		temptext=/tmp/maptext.$$
X		awk '
XBEGIN	{
X	temphead = "'$temphead'";
X	isnewsmap = 0; ismailmap = 0;
X	shead = 0; stext = 1; state = shead;
X	print "Reply-To: cds" >> temphead;
X}
Xstate == shead && /^From: /	{
X	print "Original-" $0 >> temphead;
X}
Xstate == shead && /^Subject: /	{
X	if ($2 != "Re:")
X	for (x = 2; x <= NF; x++) {
X		if ($x == "UUCPmap") {
X			ismailmap = 1;
X			break;
X		}
X		if ($x == "map") {
X			if (x <= 2)
X				continue;
X			x--;
X			if ($x == "USENET") {
X				isnewsmap = 1;
X				break;
X			}
X			if ($x == "UUCP") {
X				ismailmap = 1;
X				break;
X			}
X			x++;
X		}
X	}
X	if (!isnewsmap && !ismailmap) {
X		print "Subject:  not a map update" >> temphead;
X		print "Original-" $0 >> temphead;
X	} else
X		print $0 >> temphead;
X}
Xstate == shead && /^$/		{ state = stext; }
Xstate == stext			{
X	if (isnewsmap != 0)
X		print | "/bin/sh";
X	else if (ismailmap != 0)
X		print | "cd '$MAILMAP'; /bin/sh; ar xv uucpmap.*.ar";
X	else
X		print;
X}
X' > $temptext 2>&1
X		cat $temphead $temptext | /bin/mail cds
X		rm -f $temphead $temptext
X		exit 0
X	;;
X
X	-g)
X		# by geographical region
X		shift
X		if test $# -eq 0
X		then
X			exec ls
X			exit 1
X		fi
X		exec cat $*
X		exit 1
X	;;
X
X	-k)
X		# by keyword
X		shift
X		exec awk '
XBEGIN		{ inside = 1; outside = 0; state = outside; }
X/^Name:/	{ state = inside; count = 0; useit = 0; }
Xstate == inside	{ block[count++] = $0; }
X/'"$*"'/	{ useit = 1; }
X/^$/ && state == inside	{
X	if (useit == 1) {
X		for (i = 0; i < count; i++) {
X			print block[i];
X		}
X	}
X	state = outside;
X}
X' *
X		exit 1
X	;;
X
X	-*)
X		# unknown option
X	;;
X
X	"")
X		# no arguments
X	;;
X
X	*)
X		# by site name
X		for arg in $*
X		do
X			echo 'UUCP mail path:'
X			grep '^'${arg} $paths
X			echo '
XUSENET news site information:'
X			sed -n -e "/^Name:[ 	]*${arg}/,/^$/p" *
X		done
X		exit 0
X	;;
Xesac
X
Xecho 'Usage:	'uuhosts' hostname ...
Xfor information about a particular UUCP or USENET host or hosts, or
X
X	'uuhosts' -g geographical-region
Xfor information about USENET news sites in a geographical region, or
X
X	'uuhosts' -g
Xfor a list of known USENET geographical-regions.
X'
Xexit 1
//FUNKY//STUFF//
-- 

		Chris Seabrook

UUCP:			{ENGLAND}!ukc!qtlon!cds
			qusavx!qtlon!cds
PHONE:			+44 1 637 7061