[bit.listserv.infonets] Tools for determining BITNET paths

morgan@ms.uky.edu (Wes Morgan) (06/27/91)

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@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@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@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@engr.uky.edu, 27 Jun 91
#
# This shell script requires the BITNET LINKS file, which is available
# from LISTSERV@BITNIC (aka listserv@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@ms.uky.edu    |Wes Morgan, not speaking for|     ....!ukma!ukecc!morgan
 morgan@engr.uky.edu  |the University of Kentucky's|   morgan%engr.uky.edu@UKCC
 morgan@ie.pa.uky.edu |Engineering Computing Center| morgan@wuarchive.wustl.edu

PMW1@psuvm.psu.edu (Peter M. Weiss) (06/28/91)

In article <1991Jun27.155006.29927@ms.uky.edu>, morgan@ms.uky.edu (Wes Morgan)
says:
>
>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.

Date:         Sat, 18 May 91 07:38:35 EDT
From:         BITNODES@AUVM
Subject:      BITNODES Refcard

Processing your query "HELP" at 07:38:36:

*             B I T N O D E S
*
* BITNODES is  a service  machine at  The American
* University in Washington DC  which will help you
* find information about BitNet  nodes. It uses as
* its  source the  official  BitNet BITEARN  NODES
* file.  This  file  is updated  once  per  month.
* BITNODES will  accept interactive  messages from
* the   BitNet   network,   or   electronic   mail
* ("E-Mail") from BitNet or any connected network.
* It  will respond  using the  same medium  as was
* used for the request.
*
* At  AU,  the  command to  interactively  message
* BITNODES would look like:
*      TELL BITNODES command
*
* From a  VM/CMS system  on BitNet the  command to
* interactively message BITNODES would look like:
*      TELL BITNODES AT AUVM command
*
* From  a  VAX system  on  BitNet  the command  to
* interactively message BITNODES would look like:
*      SEND BITNODES@AUVM command
*
* Using  E-mail, the  request  should  be sent  to
* BITNODES@AUVM.BITNET  or  BITNODES@AUVM.AUVM.EDU
* depending on the addressing  scheme used at your
* site to  mail to BitNet addresses.  The BITNODES
* command can be either the first line of the mail
* message, or (if there is no mail message) in the
* subject.
*
* The BITNODES commands are:
*
* HELP         - this message is returned
* FIND string  - a list of nodes is returned. This
*                is a list of all nodes which have
*                "string" (or something similar to
*                it) in  their site  name. Example
*                is  "FIND AMERICAN".  Care should
*                be  taken  not  to search  for  a
*                commonly    used   string    like
*                "UNIVERSITY" or "COLLEGE".
* WHOIS nodeid - information is returned about the
*                node you specify. The name of the
*                site,  their  location,  and  the
*                names of their  contacts are just
*                some of the information returned.
*                Example is "WHOIS AUVM".
* PATH nd1 nd2 - information is  returned  showing
*                the BitNet path between the nodes
*                requested (nd1 and nd2).  Example
*                is "PATH AUVM UDCVM".
*
* For  more  information,  write to  Jim  McIntosh
* at JIM@AUVM.AUVM.EDU.

/Pete
--
Peter M. Weiss                     | pmw1 @ PSUADMIN  |  psuvm.psu.edu|psuvm
31 Shields Bldg - PennState Univ.  | not affiliated with psuvm.psu.edu|psuvm
University Park, PA USA 16802-1202 | A hexadecimal kindof guy in a decimal world