Tools for determining BITNET paths (source code included)

Wes Morgan morgan at ms.uky.edu
Fri Jun 28 01:50:06 AEST 1991


Occasionally, I find that I need to know the path between
two BITNET nodes.  I developed the tools included below to per-
form this task.

Both the REXX and Unix versions of this program require the
BITNET LINKS file, which is available from LISTSERV at BITNIC.
I have tested these programs using the most recent copy of
BITNET LINKS; if you've made any local changes to BITNET
LINKS (or its format), you're on your own.

I can't take full credit for the REXX version; it was sent
to me (in a very rough form) several years ago.  I merely
cleaned up the code, added the descriptive output, and
added comments.  The Unix script, however, is all mine.

The REXX routine was tested under CMS Rel 5.  The Unix script
was tested under AT&T System V Release 3.2.3.  If you're run-
ning another operating system, you may need to alter the code.
I only request that you clearly mark your changes before passing
the code to other users; I don't want to receive questions about
code I didn't write!  8)

Comments/questions/suggestions/flames can be directed to me at
any of the email addresses at the end of this posting.

Having said all that, here's the code:

------------------begin REXX code------------------------
/************************************************/
/* NODE EXEC - find and list path between nodes */
/*                                              */
/* Syntax NODE node1 node2                      */
/*     or NODE node2  <home node is default>    */
/*                                              */
/* Original Author : Unknown                    */
/* Version 2 by: Wes Morgan                     */
/*                morgan at engr.uky.edu           */
/*                                              */
/* Summary of revisions                         */
/*    Date       by      Comments               */
/*    ????     Unknown   Original code. Several */
/*                       versions on BITNET.    */
/*    8804       WM      Cleaned up code. Added */
/*                       comments.              */
/*    8805       WM      Added code to display  */
/*                       description of sites   */
/*                       for those people who   */
/*                       don't know node names  */
/*                       by heart.              */
/*                                              */
/* Requires: BITNET LINKS file, available from  */
/*           LISTSERV at BITNIC                    */
/************************************************/
 
 
/* General setup.  The user's home node is extracted from IDENTIFY. */
/* Two arguments are expected: a start node and a destination node. */
/* If no start node is given, the default is the user's home node.  */
/* Failure to give any arguments results in a help message.         */
 
'identify (stack'
parse pull . . localnode .
parse arg from to .
upper from to
if from = ''
   then do
      say 'Syntax : NODE fromnode tonode'
      say '    or : NODE tonode'
      exit 1
   end
if to = ''
   then do
      to = from
      from = localnode
   end
 
/* Finding the routing path between the two nodes.  The file  */
/* BITNET LINKS is read completely onto the stack. After get- */
/* ting past the file header, the information for each node   */
/* is placed in an array indexed by node name.  For instance, */
/* the array element bitnet.UKCC contains the value "UKCCB",  */
/* which is UKCC's connection to the net at large.  This loop */
/* continues as long as there are lines on the stack.         */
/*                                                            */
/* The array description. contains the site description.      */
 
say 'Finding path from 'from' to 'to
say
'execio * diskr bitnet links * (finis'
beforestart = 1
bitnet. = ''
description. = ''
do queued()
   parse pull 6 node ' ' 15 root ' ' 25 sitename 62 .
   if root = 'ROOT' then beforestart = 0
   if beforestart then iterate
   bitnet.node = root
   description.node = sitename
end
 
/* Finding the path from the start node to the destination node. */
/* The first element of the uplist structure is the start node.  */
/* The home node's connection node, as stored in the array cre-  */
/* ated above, is added to the uplist structure.  The array is   */
/* then scanned for the next connection.  This process continues */
/* until we get to the "ROOT" entry, which means we've gone as */
/* far as we can. Notice that this approach will rarely find the */
/* complete path.  That's fine; we don't want the complete path  */
/* yet.  We want the path back to ROOT, which is CUNYVM. After */
/* we find the path to CUNYVM for the destination node, we will  */
/* simply find the two paths' common ground and go from there.   */
 
uppoint = 0        /* Initialize counter */
uplist.0 = from    /* First element of uplist is start node */
updesc.0 = description.from
do while uplist.uppoint ^= 'ROOT'  /* As long as connections exist */
   lookup = uplist.uppoint           /* Index by node name */
   updesc.uppoint = description.lookup /* Get the site description */
   uppoint = uppoint + 1             /* Increment counter */
   uplist.uppoint = bitnet.lookup /* Next node = connection of last */
   if uplist.uppoint = ''         /* If null, exit */
      then do
         say 'No entry for 'lookup' found'
         exit 999
      end
end
 
/* Find path back from destination node to CUNYVM (root).   */
/* This is accomplished in the same manner as the uplist    */
/* created above.                                           */
 
dnpoint = 0
dnlist.0 = to
dndesc.dnpoint = description.to
do while dnlist.dnpoint ^= 'ROOT'
   lookup = dnlist.dnpoint
   dndesc.dnpoint = description.lookup
   dnpoint = dnpoint + 1
   dnlist.dnpoint = bitnet.lookup
   if dnlist.dnpoint = ''
      then do
        say 'No entry for 'lookup' found'
        exit 999
      end
end
 
/* Find the common ground between the two lists.  Basically,  */
/* we are finding the halfway point, i.e. that point at which */
/* the uplist and downlist merge.  As the uplist is printed,  */
/* the downlist is scanned for a match. Once a match is found */
/* in the downlist, the uplist is dropped, and the downlist   */
/* "takes up where we left off", working back to our goal.    */
 
count = 0                /* Initialize node count */
do loop = 0 to uppoint   /* Do as long as elements remain in uplist */
   prnt = justify(uplist.loop,12)  /* Justify for nice printing */
   say prnt updesc.loop  /* Output the information */
   count = count + 1     /* Increment node count */
   do search = 0 to dnpoint /* Search all elements of dnlist */
      match = 0          /* Set match flag low */
      if uplist.loop = dnlist.search /* If we find a match,        */
         then do
            match = 1                /* set the flag high,         */
            dnpoint = search -1      /* set startpoint for dnlist, */
            leave search             /* and get out of here        */
         end
   end
   if match then leave loop          /* If match flag high, quit   */
end
do loop = dnpoint to 0 by -1     /* Working back from match point, */
   prnt = justify(dnlist.loop,12) /* Justify for nice output */
   say prnt dndesc.loop  /* Output site and description */
   count = count + 1             /* and increment node count       */
end
say
say count - 1' nodes away'       /* Print node count               */
exit count -1                    /* Returned value is node count   */
return                           /* That's all, folks!             */
--------------End REXX code----------------

And now, the Unix shell script:

--------------Begin Unix shell script-----------------
#!/bin/sh
#
# bitpath - Determine path between two BITNET nodes
#
# Written by Wes Morgan, morgan at engr.uky.edu, 27 Jun 91
#
# This shell script requires the BITNET LINKS file, which is available
# from LISTSERV at BITNIC (aka listserv at bitnic.bitnet).
#
# Syntax: bitpath [-f] [site1] site2
#	If -f is specified, a "full" listing is output, containing each
#		site's description and ISO country code.
#	If "site1" is specified, it will be used as the origin for the
#		search.  If omitted, the site named in DEFAULT, below,
#		will be used as the origin.
#	"site2" is the destination node for the search.
#
# This shell script has been tested under AT&T System V Release 3.2.3  
#
# Known problems:
#	Some entries contain descriptions which "run into" the ISO
#	country code.  If this happens, you will see an extra field
#	(the "software" field) displayed for that site.
# 

# LINKFILE should be set to the name of your copy of BITNET LINKS.
LINKFILE=bitlinks

# DEFAULT should be set to the default origin node for searches.
# This nodename MUST be in CAPTIAL LETTERS!! 
# I use UKCC, which is the University of Kentucky Computing Center.
DEFAULT=UKCC

#
# Parse the command line.
#
errflag=0
full=0
if [ -z "$1" ]
then
	errflag=1
fi
if [ "$1" = "-f" -o "$1" = "-F" ]
then
	full=1
	shift
fi
if [ -z "$1" ]
then
	errflag=1
else
	if [ -z "$2" ]
	then
		upnode=$DEFAULT
		uplist=$DEFAULT
		dnnode=`echo $1 | tr "[a-z]" "[A-Z]"`
		dnlist=$dnnode
	else
		upnode=`echo $1 | tr "[a-z]" "[A-Z]"`
		uplist=$upnode
		dnnode=`echo $2 | tr "[a-z]" "[A-Z]"`
		dnlist=$dnnode
	fi
fi

if [ $errflag -eq 1 ]
then
	echo Syntax: bitpath [-f] [site1] site2
	exit 1
fi
echo

#
# BITNET is a tree-based network, originating from the City University
# of New York (CUNYVM).  Therefore, our initial task is to determine the
# path from the origin node back to the "root" at CUNYVM.  
#
# The format of BITNET LINKS is as follows:
# 0693 UKCC     UGA       U Kentucky Comp Ctr              ,US RSCS  91/03/31
# 
# From right to left, the fields are:
#	- BITNET node number (unused in this script)
#	- Site name
#	- The next site up the network tree
#	- Description of the site
#	- ISO country code
#	- Network software used for BITNET  (unused in this script)
#	- Last update of this site's entry (unused in this script)
#
# At this stage, we are only interested in the second and third fields.
# We will construct a list of sites from the origin to CUNYVM.  Since
# CUNYVM is the root of the BITNET tree, it's "next site" entry is "ROOT";
# we use that entry to end the search loop.

updone=0
while [ $updone -eq 0 ]
do
	line=`grep $upnode $LINKFILE | awk '$2 ~ /^'"$upnode"'$/'`
	if [ -z "$line" ]
	then
		echo "No entry for $upnode found."
		exit 1
	fi
	next=`echo $line | awk '{ print $3 }'`
	if [ "$next" = "ROOT" ] 
	then
		updone=1
	else
		upnode=$next
		uplist=`echo $uplist $next`
	fi
done

#
# Construct a path from the destination node to CUNYVM (the root).
# The algorithm is identical; the only change is that we construct
# the list in reverse order.
#

dndone=0
while [ $dndone -eq 0 ]
do
	line=`grep $dnnode $LINKFILE | awk '$2 ~ /^'"$dnnode"'$/'`
	if [ -z "$line" ]
	then
		echo "No entry for $dnnode found."
		exit 1
	fi
	next=`echo $line | awk '{ print $3 }'`
	if [ "$next" = "ROOT" ] 
	then
		dndone=1
	else
		dnnode=$next
		dnlist=`echo $next $dnlist`
	fi
done

#
# Now that we have our two paths, we need to find the "common ground"
# between them.  We move through the list from left to right.  At each
# step, we scan ahead, looking for a duplicate entry.  If we find one,
# that site is the "common ground"; we skip over to the duplicate and
# finish the path from that point.  
#
# For instance, the path from UKCC (Kentucky) through CUNYVM (the root) to
# SUVM (Syracuse) is generated as:
#    UKCC UGA CUNYVMV2 CUNYVM CUNYVM CUNYVMV2 CORNELLC SUVM
# We don't need the redundant "CUNVMV2 CUNYVM CUNYVM CUNYVMV2" portion of
# this path, because actual network traffic will not reach CUNYVM; it will
# be routed from CUMYVMV2 to CORNELLC.  Therefore, in this example, CUNYVMV2
# is the "common ground" for which this section of code searches.
#

cleanlist=`echo "$uplist $dnlist" | awk '	{ for(x=1;x<=NF;x++) {
					print $x;
					for(y=x+1;y<=NF;y++) {
						if($y == $x) {
							x = y;
							break;
						}
					}
			      		}
			    	}'`

#
# Output results.  If the -f option is used, the listing includes
# site description and country code; if not, only the node names
# are printed, separated with "->".
#
if [ $full -eq 1 ]
then
	echo $cleanlist | 
	awk '{ for(x=1;x<=NF;x++) printf("%s\n",$x); }' |
	while read target
	do
		line=`grep $target $LINKFILE | awk '$2 ~ /^'"$target"'$/'`
		descrip=`echo $line | awk '{ for(x=4;x<NF;x++) {
						printf("%s ",$x); 
						if (index($x,",") == 1) x=NF;
						}
					   }
				       END { printf("\n"); }'`
		justify=`echo $target | awk '{ printf("%-8s",$1); }'`
		echo "$justify\t$descrip"
	done
	echo
else 
	echo $cleanlist | sed "s/ /->/g"
fi
exit 0
------------------end Unix shell script----------------------

-- 
 morgan at ms.uky.edu    |Wes Morgan, not speaking for|     ....!ukma!ukecc!morgan
 morgan at engr.uky.edu  |the University of Kentucky's|   morgan%engr.uky.edu at UKCC
 morgan at ie.pa.uky.edu |Engineering Computing Center| morgan at wuarchive.wustl.edu



More information about the Alt.sources mailing list