[net.sources] Path - find paths to UUCP sites

holloway@drivax.UUCP (03/25/87)

"path" is a quick utility to find paths from the local site to a remote
one. It uses pathalias to generate its database, which is incorporated into
the program in a highly compressed form.

Two utility programs massage the pathalias database before "path" gets hold
of it.

"pathfix" creates paths to certain domain gateways and leaf nodes in order
to facilitate the creation of the database, and

"walkdb" creates the ".c" files which make up the database. Ignore any
warnings from these two programs... they'll stop and tell you if there's
a serious problem.

"path" generates paths to sites, but it can also shorten those long
paths that "Rnmail" gets when you reply to netnews. The following
context diff illustrates this:

*** Rnmail.old	Thu Mar 12 12:06:07 1987
--- Rnmail	Thu Mar 12 14:58:56 1987
***************
*** 49,54
  cat=/bin/cat
  grep=/usr/ucb/grep
  rm=/bin/rm
  
  tmpart=/tmp/rnmail$$
  dotdir=${DOTDIR-${HOME-$LOGDIR}}

--- 49,56 -----
  cat=/bin/cat
  grep=/usr/ucb/grep
  rm=/bin/rm
+ mv=/bin/mv
+ lpath=/usr/local/bin/path
  
  tmpart=/tmp/rnmail$$
  dotdir=${DOTDIR-${HOME-$LOGDIR}}
***************
*** 59,64
  *)  case $1 in
      -h)
  	headerfile="$2"
  	case $# in
  	3) oldart=$3 ;;
  	esac

--- 61,71 -----
  *)  case $1 in
      -h)
  	headerfile="$2"
+ # cat $headerfile
+ 	$lpath -h <$headerfile >$tmpart
+ # cat $tmpart
+ 	$mv $tmpart $headerfile
+ # cat $headerfile
  	case $# in
  	3) oldart=$3 ;;
  	esac

#------------------------cut here-------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	Makefile
#	path.1
#	path.c
#	pathfix.c
#	walkdb.c
#	walkdb.h
# This archive created: Wed Mar 25 12:42:27 1987
# By:	Bruce Holloway (Digitalis Research, Inc.)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'README'" '(1443 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
"path" is a quick utility to find paths from the local site to a remote
one. It uses pathalias to generate its database, which is incorporated into
the program in a highly compressed form.

Two utility programs massage the pathalias database before "path" gets hold
of it.

"pathfix" creates paths to certain domain gateways and leaf nodes in order
to facilitate the creation of the database, and

"walkdb" creates the ".c" files which make up the database. Ignore any
warnings from these two programs... they'll stop and tell you if there's
a serious problem.

"path" generates paths to sites, but it can also shorten those long
paths that "Rnmail" gets when you reply to netnews. The following
context diff illustrates this:

*** Rnmail.old	Thu Mar 12 12:06:07 1987
--- Rnmail	Thu Mar 12 14:58:56 1987
***************
*** 49,54
  cat=/bin/cat
  grep=/usr/ucb/grep
  rm=/bin/rm
  
  tmpart=/tmp/rnmail$$
  dotdir=${DOTDIR-${HOME-$LOGDIR}}

--- 49,56 -----
  cat=/bin/cat
  grep=/usr/ucb/grep
  rm=/bin/rm
+ mv=/bin/mv
+ lpath=/usr/local/bin/path
  
  tmpart=/tmp/rnmail$$
  dotdir=${DOTDIR-${HOME-$LOGDIR}}
***************
*** 59,64
  *)  case $1 in
      -h)
  	headerfile="$2"
  	case $# in
  	3) oldart=$3 ;;
  	esac

--- 61,71 -----
  *)  case $1 in
      -h)
  	headerfile="$2"
+ # cat $headerfile
+ 	$lpath -h <$headerfile >$tmpart
+ # cat $tmpart
+ 	$mv $tmpart $headerfile
+ # cat $headerfile
  	case $# in
  	3) oldart=$3 ;;
  	esac
SHAR_EOF
fi
echo shar: "extracting 'Makefile'" '(763 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Makefile for PATH - requires PATHALIAS and the Usenet MAP.
# 
# Should work on any Unix system, but I've only tried it on BSD4.2.
# 
# v1.0	03/25/87	Bruce Holloway (drivax!holloway)

pathalias = /usr/local/bin/pathalias
getopt = /usr/local/bin/getopt.o

# MAP describes the location of the Usenet MAP files.

MAP = *.map /scratch/map/u.* /scratch/map/d.*

all:	pathfix walkdb path

path:	pathmap.a path.o
	cc -o path -O path.o pathmap.a $(getopt)

pathmap.a: $(MAP)
	$(pathalias) $(MAP) >path.out
	pathfix <path.out >path.fix
	walkdb <path.fix
	cc -c *.map.c
	rm pathmap.a
	ar r pathmap.a master.map.o br*.map.o
	ranlib pathmap.a
	rm *.map.*

pathfix: pathfix.o
	cc -o pathfix -O pathfix.o

walkdb:	walkdb.o
	cc -o walkdb -O walkdb.o

path.o walkdb.o: walkdb.h
SHAR_EOF
fi
echo shar: "extracting 'path.1'" '(1555 characters)'
if test -f 'path.1'
then
	echo shar: "will not over-write existing file 'path.1'"
else
cat << \SHAR_EOF > 'path.1'
.TH PATH 1 "25 March 1987"
.UC 4
.SH NAME
path \- find the best path to a UUCP site
.SH SYNOPSIS
.B path
site [site...]
.PP
.B path
-s pattern
.PP
.B path
-h <mail-header
.PP
.B path
-r path [path...]
.SH DESCRIPTION
This is a handy program to have when you're trying to find a path to a
machine, when all you have is its name. Also nice for finding cheap paths
to a 
.I very
remote machine, like
.I ihnp4.
.PP
.I path
understands domains, and will generate paths to the correct domain gateway
if no more direct path was found.
.I path
also understands the special case of the
.I .UUCP
domain, and will try to route the path directly to the destination site
before it tries for the gateway.
.TP 5
.B  \-s
This searches for paths to all sites matching the regular expression
.I pattern.
.TP 5
.B  \-r
Takes the full path to a user at another site, and finds a shorter or
cheaper path to it. If the destination site is unknown,
.I path
tries to find a path to the previous site on the list, and so on, until
it reaches the beginning of the list. If it still hasn't found a correct
path, it will assume the one given is correct and return that.
.TP 5
.B  \-h
Takes a standard mail or news header from standard input, and replaces
each path in a
.B To:
line with a shorter one, as in the
.I \-r
option.
This allows
.I Rnmail
(with a little modification) to automatically find shorter and
more reliable sites through
which to return mail than the ones given by default.
.TP 5
.I  site
Any valid UUCP site name.
.SH AUTHOR
Bruce Holloway (holloway@drivax.UUCP)
SHAR_EOF
fi
echo shar: "extracting 'path.c'" '(5175 characters)'
if test -f 'path.c'
then
	echo shar: "will not over-write existing file 'path.c'"
else
cat << \SHAR_EOF > 'path.c'
#include <stdio.h>
#include "walkdb.h"

#undef	DEBUG

extern WALK *dbase[];
extern int strcmp();
extern char *rindex();
extern char *index();

int strccmp();

WALK *lastsite = 0;

char prgname[128];
char site[128];
int numsites = 2;
char xpath[512];
int fullpath = 0;

int main(acnt,avar)
int acnt;
char *avar[];
{
    char *s;
    int argc, i;
    extern int optind;
    extern char *optarg;

    if(s = rindex(avar[0],'/')) ++s;
    else s = avar[0];
    strcpy(prgname,s);

    if(acnt < 2){
	fprintf(stderr,"%s: usage: %s <site> {<site>...}\n",prgname,prgname);
	exit(3);
	}

    while((i=getopt(acnt,avar,"s:rh")) != EOF){
	switch(i){
	    case 's':				/* Search for map */
		ssearch(optarg);
		break;
	    case 'r':
		fullpath = 1;
		break;
	    case 'h':
		header();
		return(0);
	    }
	}

   numsites = acnt - optind;
   for(; optind < acnt; ++optind){
	if(fullpath) upath(1,avar[optind]);
	else{
	    strcpy(site,avar[optind]);
	    findsite(1,xpath);
	    }
	}
}

header(){
    char iline[256];
    extern char *gets();

    fullpath = 1;
    numsites = 1;
    while(gets(iline)){
	if(!strncmp(iline,"To: ",4)){
	    upath(0,&iline[4]);
	    strcpy(&iline[4],xpath);
	    }
	printf("%s\n",iline);
	}
}

upath(prnt,fpath)
int prnt;
char *fpath;
{
    char uname[256];
    char itsite[256];
    int numbang, i;
    char *s;

    for(s=fpath, i=0; *s; ++s) i += (*s == '!');
    if(i < 2){
	strcpy(xpath,fpath);
	if(prnt) printf("%s\n",fpath);
	return;
	}

    for(numbang=1;; ++numbang){
	strcpy(itsite,fpath);
	for(s = &itsite[strlen(itsite)], i=0; s != itsite &&
							i < numbang ; --s)
	    if(*s == '!') ++i;
	if(i != numbang){
	    fprintf(stderr,"%s: couldn't find a known site.\n",prgname);
	    break;
	    }
	++s;
	strcpy(uname,s+1);
	*s = 0;
	s = rindex(itsite,'!');
	if(!s) s = itsite; else ++s;
	strcpy(site,s);
	if(findsite(0,xpath)){
	    strcat(xpath,"!");
	    strcat(xpath,uname);
	    if(prnt) printf("%s\n",xpath);
	    break;
	    }
	}
}

findsite(prnt,pname)
int prnt;
char *pname;
{
#ifdef DEBUG
fprintf(stderr,"findsite(0x%02x,pname) - site = %s\n",prnt,site);
#endif

    if(!(prnt & 0x80) && index(site,'.')) return(dodomain(prnt,pname));
    else{
	*pname = 0;
	if(!search(site[0],site,strcmp,pname) &&
           !search(chlower(site[0]),site,strccmp,pname) &&
           !search(chupper(site[0]),site,strccmp,pname)){
	    if(!(prnt & 0x40) && !fullpath)
		fprintf(stderr,"%s: Can't find a path to %s\n",prgname,site);
	    return(0);
	    }
	else{
	    if(prnt & 0x1){
		printf("%s\n",pname);
		}
	    return(1);
	    }
	}
}

dodomain(prnt,pname)
int prnt;
char *pname;
{
    char *s;
    char tsite[256];
    char tpath[256];

#ifdef DEBUG
fprintf(stderr,"dodomain(0x%02x,pname)\n",prnt);
#endif

    lastsite = 0;
    s = index(site,'.');
    strcpy(tsite,site);
    strcpy(site,s);
    if(!strccmp(site,".uucp")){
	strcpy(site,tsite);
	*s = 0;
	if(findsite(0xC0|prnt,pname)) return(1);
	strcpy(site,".uucp");
	}
    if(findsite(0xC0,tpath)){
	if(lastsite) strcpy(index(tsite,'.'),lastsite->wname);
	if((numsites > 1) && (s = index(tpath,':'))){
	    strcpy(pname,s);
	    strcpy(tpath,tsite);
	    strcat(tpath,pname);
	    }
	sprintf(pname,"%s!%s",tpath,tsite);
	if(prnt & 1) printf("%s\n",pname);
	return(1);
	}
    else{
	strcpy(site,tsite);
	return(findsite(prnt|0x80,pname));
	}
}

int chlower(ch)
int ch;
{
    return((ch >= 'A' && ch <= 'Z') ? ch-'A'+'a' : ch);
}

int chupper(ch)
int ch;
{
    return((ch >= 'a' && ch <= 'z') ? ch-'a'+'A' : ch);
}

int strccmp(s1,s2)
char *s1, *s2;
{
    while(*s1 && *s2 && chlower(*s1) == chlower(*s2)) ++s1, ++s2;
    return(chlower(*s1) - chlower(*s2));
}

ssearch(s)
char *s;
{
    register WALK *wr;
    register int j, i;
    int numfound = 0;
    char *e;
    extern char *re_comp();
    extern int re_exec();

    e = re_comp(s);
    if(e){
	fprintf(stderr,"%s: %s\n",prgname,e);
	return;
	}

    for(i=0; i<128; ++i){
	if(!(wr = dbase[i])) continue;
	for(j=0; wr; ++j, ++wr){
	    if(!(*(wr->wname))) break;
	    if(re_exec(wr->wname) == 1){
		strcpy(site,wr->wname);
		++numfound;
		findsite(1,xpath);
		}
	    }
	}
    if(!numfound)
	fprintf(stderr,"%s: Couldn't find any sites matching \"%s\".\n",
	    prgname,s);
}

int search(i,s,fn,pname)
int i;
char *s, *pname;
int (*fn)();
{
    register WALK *wr;
    register int j;

#ifdef DEBUG
fprintf(stderr,"search(0x%02x,\"%s\",fn,pname)\n",i,s);
#endif

    if(!(wr = dbase[i])) return(0);
    for(j=0; wr; ++j, ++wr){
	if(!(*(wr->wname))) break;
	if(!((*fn)(wr->wname,s))){
	    follow(i,j,pname);
	    return(1);
	    }
	}
    return(0);
}

follow(oi,oj,pname)
int oi, oj;
char *pname;
{
    int i, j;
    WALK *w;

    w = dbase[oi & 0x7F];
    w += oj;

    i = w->wflag;
    j = w->windex;

#ifdef DEBUG
fprintf(stderr,"%s: follow(0x%02x,0x%02x);\n",prgname,oi,oj);
fprintf(stderr,"\twname=\"%s\", wflag=0x%02x, windex=0x%02x.\n",
	w->wname,i,j);
#endif

    if((i & 0x7F) == oi && j == oj){
	if(numsites > 1) sprintf(pname+strlen(pname),"%s: ",site);
	strcat(pname,w->wname);
	return(1);
	}
    follow(i & 0x7F,j,pname);
    if(!(i & 0x80))
	sprintf(pname+strlen(pname),"!%s",w->wname);
    lastsite = w;
    return(0);
}
SHAR_EOF
fi
echo shar: "extracting 'pathfix.c'" '(1569 characters)'
if test -f 'pathfix.c'
then
	echo shar: "will not over-write existing file 'pathfix.c'"
else
cat << \SHAR_EOF > 'pathfix.c'
#include <stdio.h>

extern char *gets();
extern	char *malloc();
extern char *index(), *rindex();

char iline[256];

#define	PATH	struct _zap
PATH{
    PATH *next;
    char *site;
    char *path;
};

#define	newpath()	(PATH *)malloc(sizeof(PATH))

PATH *paths = 0;

main(){
    while(gets(iline)){
	addpath();
	}
    fixpaths();
    writepaths();
}

addpath(){
    char *s;
    char *s1;
    char *s2, *s3;
    PATH *p;

    s = index(iline,'\t');
    if(!s){
	fprintf(stderr,"No tab in path - \"%s\".\n",iline);
	return;
	}
    *s = 0;
    s1 = s+1;
    s = rindex(s1,'!');
    if(!s){
	fprintf(stderr,"No bang in path - \"%s\t%s\".\n",iline,s1);
	s = s1;
	}
    *s = 0;

    p = newpath();
    if(!p) outofmem();
    s2 = malloc(strlen(iline)+1);
    if(!s2) outofmem();
    s3 = malloc(strlen(s1)+1);
    if(!s3) outofmem();
    p->next = paths;
    paths = p;
    strcpy(s2,iline);
    strcpy(s3,s1);
    p->site = s2;
    p->path = s3;
}

outofmem(){
    fprintf(stderr,"Out of memory.\n");
    exit(1);
}

fixpaths(){
    register PATH *p1, *p2;
    register char *s;

    for(p1=paths; p1; p1=p1->next){
	s = rindex(p1->path,'!');
	if(!s) continue;
	++s;
	if(!strcmp(s,p1->site)) continue;
	for(p2=paths; p2; p2=p2->next){
	    if(!strcmp(s,p2->site)) goto cmain;
	    }
	sprintf(iline,"%s\t%s!%%s",s,p1->path);
	fprintf(stderr,"Adding line: \"%s\".\n",iline);
	addpath();
cmain: ;
	}
}

writepaths(){
    register PATH *p;

    for(p=paths; p; p=p->next){
	if(*(p->path))
	    printf("%s\t%s!%%s\n",p->site,p->path);
	else
	    printf("%s\t%%s\n",p->site);
	}
}
SHAR_EOF
fi
echo shar: "extracting 'walkdb.c'" '(3754 characters)'
if test -f 'walkdb.c'
then
	echo shar: "will not over-write existing file 'walkdb.c'"
else
cat << \SHAR_EOF > 'walkdb.c'
#include <stdio.h>

extern char *gets();
extern char *malloc();
extern char *index();
extern char *rindex();

char *zalloc();

#define	node	struct _node

node {
    node *nnext;
    char *myname;
    char *rname;
    char *lname;
    };

char msg_string[] = "string";
char msg_node[] = "node";

node *leaf[128];

char iline[1024];

#define	newstr(s)	zalloc(msg_string,strlen(s)+1)
#define	newnode()	(node *)zalloc(msg_node,sizeof(node))

main(){
    int i;

    for(i=0; i<128;) leaf[i++] = (node *)0;

    while(gets(iline)) addnode();
    writenode();
}

addnode(){
    int i;
    node *n;
    char *s, *s1, *s2;
    char *dstnode, *leafnode, *rootnode;
    static char zap[] = "!!";
    static char zip[] = "!!";

    dstnode = leafnode = rootnode = 0;

    s = index(iline,'\t');
    if(!s){
	fprintf(stderr,"warning: no tab in \"%s\".\n",iline);
	return;
	}
    dstnode = iline;
    *s = 0; s1 = s+1;
    s = rindex(s1,'!');
    if(!s){
	fprintf(stderr,"warning: better be host name \"%s\".\n",iline);
	return;
	}
    *s = 0;
    s2 = rindex(s1,'!');
    if(!s2){
	leafnode = s1;
	fprintf(stderr,"warning: better be direct link \"%s\".\n",iline);
	s2 = &zip[1];
	strcpy(zip,zap);
	}
    else leafnode = s2+1;
    *s2 = 0;
    s2 = rindex(s1,'!');
    if(!s2) s2 = s1;
    else ++s2;
    rootnode = s2;

    if(strcmp(dstnode,leafnode)) rootnode = 0;
    else leafnode = 0;    
#if 0
    if(rootnode)
	fprintf(stderr,"parent of %s is %s\n",dstnode,rootnode);
    else
	fprintf(stderr,"alias  of %s is %s\n",dstnode,leafnode);
#endif
    n = newnode();
    n->myname = newstr(dstnode);
    strcpy(n->myname,dstnode);
    if(rootnode){
	n->rname = newstr(rootnode);
	strcpy(n->rname,rootnode);
	}
    else n->rname = 0;
    if(leafnode){
	n->lname = newstr(leafnode);
	strcpy(n->lname,leafnode);
	}
    else n->lname = 0;
    i = *dstnode;
    n->nnext = leaf[i];
    leaf[i] = n;
}

char *zalloc(reason,size)
char *reason;
unsigned size;
{
    char *s;

    s = malloc(size);
    if(!s){
	fprintf(stderr,"Couldn't allocate %d bytes for a %s.\n",size,reason);
	exit(1);
	}
    return(s);
}

writenode(){
    int i, j;
    register node *n;
    FILE *ochan;
    char zap[50];

fprintf(stderr,"writing nodes now.\n");

    for(i=0; i<128; ++i)
	if(n=leaf[i]){
	    sprintf(zap,"br%d.map.c",i);
	    ochan = fopen(zap,"w");
	    if(!ochan){
		fprintf(stderr,"Couldn't open %s for writing.\n",zap);
		exit(2);
		}
	    fprintf(ochan,"#include \"walkdb.h\"\n\n");
	    for(j=0; n; ++j, n=n->nnext);
	    ++j;
	    fprintf(ochan,"WALK br%d[%d] = {\n",i,j);
	    for(n=leaf[i]; n; n=n->nnext){
		fprintf(ochan,"    { \"%s\", 0x", n->myname);
		if(n->rname){
		    fprintf(ochan,"%02x, ",*(n->rname));
		    j = lookfor(n->rname);
		    }
		else{
		    fprintf(ochan,"%02x, ",*(n->lname) + 0x80);
		    j = lookfor(n->lname);
		    }
		fprintf(ochan,"%d },\n",j);
		}
	    fprintf(ochan,"    { \"\", 0, 0 }\n};\n\n");
	    fclose(ochan);
	    }
    ochan = fopen("master.map.c","w");
    if(!ochan){
	fprintf(stderr,"Couldn't open master.map.c for writing.\n");
	exit(3);
	}
    fprintf(ochan,"#include \"walkdb.h\"\n\n");
    for(i=0; i<128; ++i)
	if(leaf[i])
	    fprintf(ochan,"extern WALK br%d[];\n",i);
    fprintf(ochan,"\n\nWALK *dbase[129] = {\n");
    for(i=0; i<128; ++i){
	if(leaf[i]) fprintf(ochan,"    br%d,\n",i);
	else fprintf(ochan,"    0,\n");
	}
    fprintf(ochan,"    0\n};\n");
    fclose(ochan);
}

lookfor(s)
char *s;
{
    register int i, j;
    register node *n;
    char *e;

    i = *s;
    n = leaf[i];
    if(!n){ e = "bad initial character"; goto error; }
    for(j=0; n; ++j, n=n->nnext){
	if(!strcmp(s,n->myname))
	    return(j);
	}
    e = "node not in leaf";
error:
fprintf(stderr,"lookfor(%s) - %s.\n",s,e);
    return(-1);
}
SHAR_EOF
fi
echo shar: "extracting 'walkdb.h'" '(85 characters)'
if test -f 'walkdb.h'
then
	echo shar: "will not over-write existing file 'walkdb.h'"
else
cat << \SHAR_EOF > 'walkdb.h'
#define	WALK	struct _walk

WALK {
    char *wname;
    int wflag;
    int windex;
};
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
Bruce Holloway - Relapsed Newsaholic
{seismo,hplabs,sun,ihnp4}!amdahl!drivax!holloway
Put the power of RANDOM NUMBERS to work FOR YOU!