[comp.sources.misc] v06i093: pathrpt -- a reporting tool companion for pathalias

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (04/24/89)

Posting-number: Volume 6, Issue 93
Submitted-by: david@dhw68k.cts.com (David H. Wolfskill)
Archive-name: pathrpt

I asked the folks in comp.mail.uucp if they would find a program such as
this useful or helpful; the overwhelming response was "Yes!" (along with
requests to mail copies).  Anyway, I think comp.sources.misc would be a
better place for it than comp.sources.unix, mostly because it's not
terribly sophisticated -- it's really just a little filter to accompany
pathalias.

I've been using it for several months (in one form or another), and
I've sent copies off to some folks.  The only reported problems I've
heard with its use came from a VMS site, and I didn't understand what
the problems were....

What this thing is:  pathrpt makes a report based on the output of
pathalias.  The report indicates how many of the paths (from the
pathalias output) start at each of your uucp neighbors.  It also gives
some clue as to the lengths of those paths -- and it checks to see if
the uucp maps indicate you have a neighbor you don't think you have.

The above report(s) come out on stderr, so make it easier to use the
program for its other purpose in life -- taking the place of the "sed"
step in the pathproc distributed with smail.  (The smail-format
pathalias output comes out on stdout.)

Folks who don't run pathalias probably have no use for this program.

Thanks,
david  (Wed Apr 19 07:02:55 PDT 1989)

#! /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 the files:
#	pathrpt.c
# This archive created: Wed Apr 19 06:47:00 1989
export PATH; PATH=/bin:$PATH
echo shar: extracting "'pathrpt.c'" '(20748 characters)'
if test -f 'pathrpt.c'
then
	echo shar: will not over-write existing file "'pathrpt.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'pathrpt.c'
	X/*
	X *
	X * This program is Copyright 1989, by David H. Wolfskill.
	X *
	X * Anyone may freely copy it and use it for any purpose, as long as:
	X * 1) it is not sold, and
	X * 2) no one else attempts to claim authorship of the program.
	X * (In all humility and honesty, I can't imagine why anyone would want to do
	X * either of the above, but human nature is a truly marvelous thing....)
	X *
	X * [First part of above is freely adapted from "rn," by Larry Wall.... dhw]
	X *
	X * Disclaimer: Although the author finds the program useful, there is no
	X *             guarantee that anyone else will find it useful for any given
	X *             purpose.  Caveat Emptor, and all that.
	X *             
	X * I cannot, of course, promise to fix things that some folks may think are
	X * broken; nor can I promise to implement enhancements.  However, I have a
	X * strong desire to do what I can to help improve email, especially over dialup
	X * 'phone lines; accordingly, it is my present intent to support this program
	X * as a tool that can be helpful in some circumstances... as long as what it
	X * reports isn't trusted too much....  :-)
	X *
	X * In all seriousness, comments & suggestions may be sent to me via
	X *
	X * InterNet: david@dhw68k.cts.com
	X * uucp: ...{spsd,zardoz,felix}!dhw68k!david	
	X *
	X * and -- unless you have been explicitly informed otherwise by someone
	X * in a position to know (such as me) -- I welcome suggestions for improvement.
	X *
	X * David H. Wolfskill	16 April, 1989
	X *
	X */
	X
	X/*
	X *	Program to list statistics, some of which may be of interest,
	X *	regarding the output of "pathalias," as well as do a basic
	X *	"reality check" on that output.
	X
	X *	By its nature, this program is intended to be used in conjunction
	X *	with pathalias.  I really don't think you should spend any time
	X *	or other resources on this program if you neither run nor have
	X *	any intent to run pathalias.
	X
	X *	This version can read the pathalias output either in the
	X *	original order, or either of the 2 rotations:
	X
	X *	"pathalias":	<cost>	<destination>	<path>
	X *	"smail":	<destination>	<path>	<cost>
	X *	unknown:	<path>	<cost>	<destination>
	X
	X *	It does *not* (presently) handle "<destination>	<cost>	<path>"
	X *	(or any of its rotations); if you really think it's something
	X *	worth doing, I'm willing to discuss it further, but really...!
	X
	X *	In order to make the determination of the stdin format, the
	X *	first line is read, and its 3 fields are scanned to find one
	X *	that ends in "%s".  If no such field is found, the program
	X *	claims that the input is not pathalias output, and exits.  If
	X *	one *is* found, however (the normal case, we hope!), that field
	X *	is assumed to be the <path> field; the next field (to the right,
	X *	wrapping around if necessary) is the <cost>, and the next is
	X *	<destination>.
	X
	X *	smail likes the second order, so the stdout of this program
	X *	is the same information in "smail" order (regardless of the
	X *	order in which stdin is).  This program may thus be used in
	X *	place of the "sed" segment of the "pathproc" pipeline that is
	X *	distributed with smail.
	X
	X *	To facilitate its use in that application, the report the
	X *	program generates comes out on stderr.
	X
	X *	The overall method is straightforward, brute-force:  uuname (or
	X *	whatever -- see macro NBRLIST) is used to generate a list of
	X *	known immediate uucp neighbors; that is run through sort.  (I
	X *	do it this way because uuname is set[gu]id; I think it
	X *	appropriate to minimize the need for programs to run as
	X *	set[gu]id, so I use the facilities provided by a common tool,
	X *	rather than require special privileges for this program.)  Then
	X *	a "neighbor" (spelled "nbr" for typing constraint relief)
	X *	struct is allocated for each neighbor thus found -- after one
	X *	of these structs is allocated for the totals.  The nbr structs
	X *	are allocated as a linked list.  (A temporary file is created
	X *	to hold the output of sort; after the nbr structs are all
	X *	allocated, it is unlinked and closed.)
	X
	X *	Once this housekeeping is finished, a single pass through stdin
	X *	(which is expected to be in the above-documented -- possibly
	X *	rotated -- pathalias output format) is made, updating the
	X *	appropriate counters in the various nbr structs:  the "tots"
	X *	nbr struct is always updated; the "first hop" in the path is
	X *	determined, and the nbr struct that corresponds to it is also
	X *	updated.
	X
	X *	A <path> field that consists entirely of "%s" is assumed to
	X *	have a "first hop" of the local host.  The program will
	X *	determine this by using the -l flag (ala' pathalias); if -l was
	X *	not specified, routine hostname() is invoked, which either uses
	X *	uname() (if UNAME is defined during compilation) or
	X *	gethostname() (if it wasn't).  If hostname() is invoked and
	X *	fails, the program complains bitterly and exits -- in such a
	X *	case, you may wish to change the logic in hostname() or specify
	X *	the -l flag, depending on how ambitious you are.
	X
	X *	For a <path> field that is more than "%s", everything from the
	X *	beginning of the field up to (but not including) the first
	X *	occurrence of '!' is assumed to be the name of the "first hop"
	X *	-- which ought to be the name of one of the specified host's
	X *	neighbors.
	X
	X *	(Note the pathological case of the pathalias output claiming
	X *	the existence of a neighbor that NBRLIST doesn't know about.
	X *	That actually happened in the early testing of the program, and
	X *	as a result of that experience, I put in code to create a "nbr"
	X *	struct on demand (as it were) in such a case, then in the
	X *	output phase, spit out a warning message.)
	X
	X *	Once EOF is reached on stdin, a pair of traversals through the
	X *	linked list is made; each prints a report based on the
	X *	accumulated totals, and the second one also frees the elements
	X *	of the list.  The second report shows a distribution of how
	X *	long the paths are; its width -- how far out to count distinct
	X *	pathlengths -- is controlled by the MAXDIST macro and the -d
	X *	flag.
	X
	X *	Then we're done -- what could be simpler...?  :-)
	X
	X
	X
	X *	Notes about the flags:
	X
	X *	Since the program was first written, the number of flags
	X *	("command line arguments" for picky folk) has become large;
	X *	herewith is a list of them:
	X
	X *	-B <limit>      <limit> specifies an OUTPUT limit: if the
	X *			cost to reach a destination is below the
	X *			specified limit, the stdout will not contain
	X *			that line of input data.  To make this flag
	X *			ineffective, use a value of 0.  Default is
	X *			BOTOUT (0).
	X
	X *	-T <limit>      <limit> specifies an OUTPUT limit: if the
	X *			cost to reach a destination is above the
	X *			specified limit, the stdout will not contain
	X *			that line of input data.  To make this flag
	X *			ineffective, use a value of 0.  Default is
	X *			TOPOUT (0).
	X
	X *	-b <limit>	<limit> specifies a REPORTING limit: if the
	X *			cost to reach a destination is below the
	X *			specified limit, the reports (on stderr) will
	X *			not reflect that line of input data.  To make
	X *			this flag ineffective, use a value of 0.
	X *			Default is BOTCNT (0).
	X
	X *	-t <limit>	<limit> specifies a REPORTING limit: if the
	X *			cost to reach a destination is above the
	X *			specified limit, the reports (on stderr) will
	X *			not reflect that line of input data.  To make
	X *			this flag ineffective, use a value of 0.
	X *			Default is TOPCNT (0).
	X
	X *	-d <dist>	<dist> specifies the number of "distribution
	X *			buckets" for the 2nd report -- the one that
	X *			(sort of) shows a "histogram" of the hopcounts
	X *			of the paths to each of the neighboring sites.
	X *			The default value is DDIST (20).
	X
	X *	-l <host>	<host> specifies a local hostname other than
	X *			the default; it is intended to be similar in
	X *			operation to the pathalias flag of similar name
	X *			and intent.
	X
	X *	-z 		specifies that the reports are not to include
	X *			lines for neighboring sites that are not "first
	X *			hops" in the stdin.
	X
	X *	Note that each of the flags is completely independent of any
	X *	other flag -- and that some combinations are (arguably) useful,
	X *	while some are misleading or dangerous.
	X
	X *	In the normal course of the use of the program, it is expected
	X *	that it will be executed in a shell script, so once the flags
	X *	have been figured out, this should be substantially less of a
	X *	burden than typing all that stuff....  Of course, the defaults
	X *	for each of the -b, -t, -B, -T, and -d flags can be compiled
	X *	in, too....
	X
	X */
	X
	X
	X
	X#include <stdio.h>
	X#include <string.h>
	X
	X#ifndef	MAXLINE
	X#define	MAXLINE	256	/* Assumed maximum field length from input */
	X#endif	MAXLINE
	X
	X#ifndef	ERRLEN
	X#define	ERRLEN	256	/* Assumed maximum line length for error message */
	X#endif	ERRLEN
	X
	X#ifndef	HOSTLEN
	X#define	HOSTLEN	128	/* Assumed maximum length for host name */
	X#endif	HOSTLEN
	X
	X#ifndef	SYSLEN
	X#define	SYSLEN	80	/* Assumed maximum length for "system (2)" call */
	X#endif	SYSLEN
	X
	X#ifndef	NBRLIST
	X#ifdef	UNAME
	X#define	NBRLIST	"(uuname -l;uuname) | sort"	/* Command for list of hosts */
	X#else	/*  !UNAME  */
	X#define	NBRLIST	"(hostname;uuname) | sort"	/* Command for list of hosts */
	X#endif	/* UNAME */
	X#endif	NBRLIST
	X
	X#ifndef	MAXDIST
	X#define	MAXDIST	20	/* max # "buckets" for pathlength distribution chart */
	X#endif	MAXDIST
	X
	X#ifndef	DDIST
	X#define	DDIST	MAXDIST	/* default # "buckets" for pathlength distribution chart */
	X#endif	DDIST
	X
	X#ifndef	STRCHR
	X#define	STRCHR	strchr	/* strchr() or index() function */
	X#endif	STRCHR
	X
	X#ifndef	DEAD
	X#define	DEAD	30000000	/* Intended to match the pathalias value */
	X#endif	DEAD
	X
	X#ifndef	TOPOUT
	X#define	TOPOUT	0	/* "0" == don't supress any output; !0 == threshhold */
	X#endif	TOPOUT
	X
	X#ifndef	BOTOUT
	X#define	BOTOUT	0	/* "0" == don't supress any output; !0 == threshhold */
	X#endif	BOTOUT
	X
	X#ifndef	TOPCNT
	X#define	TOPCNT	0	/* "0" == don't supress any output; !0 == threshhold */
	X#endif	TOPCNT
	X
	X#ifndef	BOTCNT
	X#define	BOTCNT	0	/* "0" == don't supress any output; !0 == threshhold */
	X#endif	BOTCNT
	X
	Xstruct nbr {		/* struct for "neighbor" host                 */
	X	struct nbr *next;	/* pointer to next neighbor on the list       */
	X	char	*host;		/* name of neighboring host                   */
	X	int	firsthop;	/* count of times this appears as "1st hop"   */
	X	int	hopcount;	/* sum of path lengths that start here        */
	X	int	pthlng[MAXDIST];	/* buckets for pathlength dist.       */
	X	double	cost;		/* sum of COSTS for paths that start here     */
	X};
	X
	Xint	main(argc, argv)  /* find interesting statistics about a "paths" file */
	Xint	argc;
	Xchar	*argv[];
	X
	X{
	X	struct nbr *tots;	/* "pseudo-neighbor" for totals               */
	X	struct nbr *tnbr;	/* temporary pointer to a "nbr" struct        */
	X	struct nbr *lnbr;	/* pointer to last-allocated "nbr" struct     */
	X	struct nbr *bnbr;	/* pointer to first "bad" "nbr" struct        */
	X	struct nbr *getnbr();	/* routine to create a new nbr struct */
	X
	X	extern int	errno;		/* used by perror() and friends       */
	X	extern int	optind;		/* for getopt()			      */
	X	extern char	*optarg;	/* for getopt()			      */
	X
	X	char	*tempnam();	/* function to generate name of work file     */
	X	char	*tmpf;		/* points to pathname of temporary file       */
	X	FILE	* tf;		/* FILE pointer for temporary file            */
	X
	X	char	sys[SYSLEN];	/* command line used to get list of neighbors */
	X
	X	char	*hostname();	/* function to go find out where I am         */
	X	char	site[HOSTLEN];	/* char array to hold name of site            */
	X
	X	char	str[3][MAXLINE];/* input strings for pathalias output         */
	X	int	field;		/* index for str[][] array                    */
	X	int	strl;		/* length of str[][] array                    */
	X	char	*dest;		/* used for both "destination" and "1st hop"  */
	X	char	*path;		/* string for uucp "path" to get to dest      */
	X	char	*scost;		/* pointer to cost data in string form        */
	X	double	cost;		/* pathalias "COST" to get to a given dest    */
	X
	X	int	top_out;	/* controls high end of output suppression    */
	X	int	bot_out;	/* controls low end of output suppression     */
	X	int	top_cnt;	/* controls high end of counting              */
	X	int	bot_cnt;	/* controls low end of counting               */
	X	int	zflag;		/* controls reporting neighbors w/ 0 paths    */
	X
	X	int	error;		/* "error" flag returned by program           */
	X	int	hops;		/* number of "hops" to get to given dest      */
	X	int	dist;		/* number of "buckets" for pathlength distr.  */
	X	int	ch;		/* garden-variety character/EOF-holder        */
	X	char	*name;		/* name of program -- as invoked, for msgs    */
	X	char	errmsg[ERRLEN];	/* string for constructing error messages     */
	X	char	*chptr;		/* garden-variety pointer-to-char work var    */
	X
	X	name = argv[0];
	X	error = 0;
	X	dist = DDIST;
	X	bnbr = NULL;
	X	site[0] = 0;
	X	top_out = TOPOUT;
	X	bot_out = BOTOUT;
	X	top_cnt = TOPCNT;
	X	bot_cnt = BOTCNT;
	X	zflag = 1;
	X
	X	while ((ch = getopt(argc, argv, "b:B:d:l:t:T:z")) != EOF)
	X		switch (ch) {
	X		case 'B':
	X			(void)sscanf(optarg, "%d", &bot_out);
	X			break;
	X		case 'b':
	X			(void)sscanf(optarg, "%d", &bot_cnt);
	X			break;
	X		case 'd':
	X			(void)sscanf(optarg, "%d", &dist);
	X			break;
	X		case 'l':
	X			if (strlen(optarg) < HOSTLEN) 
	X				(void)strcpy(site, optarg);
	X			else	 {
	X				(void)strncpy(site, optarg, HOSTLEN - 1);
	X				site[HOSTLEN-1] = 0;
	X			}
	X			break;
	X		case 'T':
	X			(void)sscanf(optarg, "%d", &top_out);
	X			break;
	X		case 't':
	X			(void)sscanf(optarg, "%d", &top_cnt);
	X			break;
	X		case 'z':
	X			zflag = 0;
	X			break;
	X		default:
	X			(void)fprintf(stderr, "usage: %s -d dist -l host -b limit -B limit -t limit -T limit -z\n", name);
	X			exit(error = 1);
	X		}
	X
	X	if ((site[0] == 0) && (hostname(site) == NULL)) {
	X		(void)sprintf(errmsg, "%s: hostname() failed", name);
	X		(void)perror(errmsg);
	X		exit(error = errno);
	X	}
	X
	X	dist = dist > MAXDIST ? MAXDIST : dist;
	X
	X	if ((tots = tnbr = getnbr("TOTALS")) == NULL) {
	X		(void)sprintf(errmsg, "%s: getnbr(\"TOTALS\") failed", name);
	X		(void)perror(errmsg);
	X		exit(error = errno);
	X	}
	X
	X	tmpf = tempnam((char *)NULL, (char *)NULL);
	X	(void)sprintf(sys, "%s >%s", NBRLIST, tmpf);
	X	(void)system(sys);
	X
	X	if ((tf = fopen(tmpf, "r")) == NULL) {
	X		(void)sprintf(errmsg, "%s: fopen(%s, \"r\") failed", name, tmpf);
	X		(void)perror(errmsg);
	X		exit(error = errno);
	X	}
	X
	X	while (fscanf(tf, "%s", str[0]) != EOF) {
	X		if ((tnbr->next = getnbr(str[0])) == NULL) {
	X			(void)sprintf(errmsg, "%s: getnbr(%s) failed", name, str[0]);
	X			(void)perror(errmsg);
	X			exit(error = errno);
	X		}
	X		tnbr = tnbr->next;
	X	}
	X
	X	if (unlink(tmpf) != 0) {
	X		(void)sprintf(errmsg, "%s: unlink(%s) failed", name, tmpf);
	X		(void)perror(errmsg);
	X		exit(error = errno);
	X	}
	X
	X	if (fclose(tf) == EOF) {
	X		(void)sprintf(errmsg, "%s: fclose(tf) failed", name);
	X		(void)perror(errmsg);
	X		exit(error = errno);
	X	}
	X
	X	if (scanf("%s%s%s", &str[0][0], &str[1][0], &str[2][0]) != EOF) {
	X		for (field = 0; field < 3; field++) {
	X			if ((strl = strlen(str[field])) > 1) {
	X				if (strcmp(&str[field][strl-2], "%s") == 0)
	X					break;
	X			}
	X		}
	X		if (field > 2) {
	X			(void)fprintf(stderr, "%s: input is not pathalias format\n", name);
	X			exit(error = field);
	X		}
	X		path = &str[field++][0];
	X		scost = &str[(field++)%3][0];
	X		dest = &str[(field++)%3][0];
	X
	X		do {
	X			(void)sscanf(scost, "%lg", &cost);
	X			if ((bot_out <= cost) && ((top_out == 0) || (top_out >= cost)))
	X				(void)printf("%s\t%s\t%s\n", dest, path, scost);
	X			if ((bot_cnt <= cost) && ((top_cnt == 0) || (top_cnt >= cost))) {
	X				tots->firsthop++;
	X				tots->cost += cost;
	X				chptr = path;
	X				hops = strcspn(path, "!");
	X				(void)strncpy(dest, path, hops);	/* save name of "first hop" */
	X				*(dest + hops) = 0;		/* ensure proper null term. */
	X
	X				hops = 0;
	X				while ((chptr = STRCHR(chptr, '!')) != NULL) {
	X					chptr++;
	X					hops++;
	X				}
	X
	X				tots->hopcount += hops;
	X				tots->pthlng[hops<dist?hops:dist-1]++;
	X				if (hops == 0 && strcmp(dest, "%s") == 0) 
	X					(void)strcpy(dest, site);
	X
	X				if (tnbr->next == NULL) 
	X					tnbr = tots;
	X				if ((strcmp(dest, tnbr->next->host)) != 0) {
	X					tnbr = tots;
	X					while ((tnbr->next != NULL) && (strcmp(dest, tnbr->next->host)) != 0)
	X						tnbr = tnbr->next;
	X				}
	X
	X				if (tnbr->next == NULL) {
	X					if ((lnbr = getnbr(dest)) == NULL) {
	X						(void)sprintf(errmsg, "%s: getnbr(%s) failed", name, dest);
	X						(void)perror(errmsg);
	X						exit(error = errno);
	X					}
	X					tnbr->next = lnbr;
	X					if (bnbr == NULL) 
	X						bnbr = lnbr;
	X				}
	X
	X				tnbr->next->firsthop++;
	X				tnbr->next->cost += cost;
	X				tnbr->next->hopcount += hops;
	X				tnbr->next->pthlng[hops<dist?hops:dist-1]++;
	X			}
	X
	X		} while (scanf("%s%s%s", &str[0][0], &str[1][0], &str[2][0]) != EOF);
	X	}
	X
	X	(void)fprintf(stderr, "\n Adj              Total         Total   #Hops        Cost");
	X	(void)fprintf(stderr, "\nSite       #Dest  #Hops          Cost   /Dest       /Dest\n");
	X
	X	lnbr = tots;
	X	while (tots != NULL) {
	X		if (bnbr == tots)
	X			(void)fprintf(stderr, "\n *** WARNING! Bogus \"neighbor\" sites follow! ***\n");
	X		if (zflag | tots->firsthop)
	X			(void)fprintf(stderr, "%-9s% 7d% 7d% 14.0f% 8.2f% 12.2f\n", tots->host,
	X			    tots->firsthop, tots->hopcount, tots->cost,
	X			    tots->firsthop ? (double)tots->hopcount / tots->firsthop : (double)0,
	X			    tots->firsthop ? (double)tots->cost / tots->firsthop : (double)0);
	X		tots = tots->next;
	X	}
	X
	X	tots = lnbr;
	X	(void)fprintf(stderr, "\n Adj               #Hops       --- Pathlength Distribution ---");
	X	(void)fprintf(stderr, "\nSite       #Dest   /Dest");
	X	for (hops = 0; hops < dist; hops++) 
	X		(void)fprintf(stderr, "% 5d", hops);
	X	(void)fprintf(stderr, "+\n");
	X
	X	while (tots != NULL) {
	X		if (bnbr == tots)
	X			(void)fprintf(stderr, "\n *** WARNING! Bogus \"neighbor\" sites follow! ***\n");
	X		if (zflag | tots->firsthop) {
	X			(void)fprintf(stderr, "%-9s% 7d% 8.2f", tots->host,
	X			    tots->firsthop,
	X			    tots->firsthop ? (double)tots->hopcount / tots->firsthop : (double)0);
	X			for (hops = 0; hops < dist; hops++) 
	X				(void)fprintf(stderr, "% 5d", tots->pthlng[hops]);
	X			(void)fprintf(stderr, "\n");
	X		}
	X		if (tots->host != NULL) 
	X			(void)free((char *)tots->host);
	X		tnbr = tots->next;
	X		(void)free((char *)tots);
	X		tots = tnbr;
	X	}
	X	exit(error);
	X}
	X
	X
	X
	X
	X/*
	X *	This routine is given the name of a neighbor host ("nbrname"),
	X *	then uses malloc() to acquire storage for a new "nbr" struct for
	X *	that host.  It then uses malloc() again, this time to acquire
	X *	enough storage for a copy of the nbrname, initializes that copy
	X *	from the nbrname given, & anchors the nbrname in the newly-created
	X *	nbr struct.
	X
	X *	Lastly, a pointer to the (newly-created) nbr struct is returned.
	X
	X *	If anything goes wrong during this, NULL is returned in lieu of a
	X *	valid pointer to a nbr struct -- and any storage acquired in the
	X *	process is freed.  (That is, there is a concious attempt to "back
	X *	out" cleanly.)
	X */
	X
	Xstruct nbr *(getnbr(nbrname)
	X)
	Xchar	*nbrname;
	X
	X{
	X
	X	char	*malloc();	/* to make lint less unhappy		      */
	X
	X	extern int	errno;		/* used by perror() and friends       */
	X
	X	struct nbr *tnbr;	/* temporary anchor for new nbr struct        */
	X	int	hops;		/* subscript for pathlength buckets           */
	X
	X	if ((tnbr = (struct nbr *)malloc((unsigned)sizeof(struct nbr))) == NULL) {
	X		return((struct nbr *)NULL);
	X	}
	X	if ((tnbr->host = (char *)malloc((unsigned)(1 + strlen(nbrname)))) == NULL) {
	X		(void)free((char *)tnbr);
	X		return((struct nbr *)NULL);
	X	}
	X
	X	tnbr->next = NULL;
	X	(void)strcpy(tnbr->host, nbrname);
	X	tnbr->firsthop = 0;
	X	tnbr->hopcount = 0;
	X	tnbr->cost = 0;
	X	for (hops = 0; hops < MAXDIST; hops++) 
	X		tnbr->pthlng[hops] = 0;
	X	return(tnbr);
	X}
	X
	X
	X
	X
	X/*
	X *	This routine is called to find the name of the host from whose
	X *	perspective the pathalias data was created.
	X
	X *	Unfortunately, I do not know of a way to ensure consistency in
	X *	this respect; I have tried to make consistency easy to accomplish,
	X *	however, by providing the same mechanism pathalias uses to determine
	X *	the name of the current host -- including the use of the l flag.
	X
	X *	(Thus, this routine isn't invoked if the l flag is specified when
	X *	the program is invoked.)
	X
	X *	[The following source is basically plagiarized from the sources for
	X *	pathalias.  I hope that neither Peter Honeyman nor Steve Bellovin
	X *	mind too much.... dhw]
	X
	X *	Anyway, if a problem occurs, NULL is returned.
	X */
	X
	Xchar	*hostname(nmptr)
	Xchar	*nmptr;
	X
	X{
	X
	X	extern int	errno;		/* used by perror() and friends               */
	X
	X#ifdef	UNAME
	X#include <sys/utsname.h>
	X
	X	int	uname();	/* system call to find out where I am         */
	X	struct utsname ustrct;	/* utsname struct for uname()                 */
	X
	X	if (uname(&ustrct) < 0) 
	X		return(NULL);
	X	else {
	X		(void)strcpy(nmptr, ustrct.nodename);
	X
	X#else	/*  !UNAME  */
	X#include <sys/param.h>
	X
	X	int	gethostname();	/* system call to find out where I am         */
	X	char	name[MAXHOSTNAMELEN];	/* array to hold the host name        */
	X
	X	if (gethostname(name, (int)MAXHOSTNAMELEN) < 0) 
	X		return(NULL);
	X	else {
	X		(void)strcpy(nmptr, name);
	X#endif	/* UNAME */
	X		return(nmptr);
	X	}
	X
	X}
SHAR_EOF
if test 20748 -ne "`wc -c < 'pathrpt.c'`"
then
	echo shar: error transmitting "'pathrpt.c'" '(should have been 20748 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0