[net.sources] Sort of uusnap

dennis@rlgvax.UUCP (Dennis Bednar) (07/08/86)

A few weeks ago I posted a request for a uusnap for System 5
uucp's with a "flat" uucp spool directory.  Prior to that request,
I already had a private tool that I call "uustatd" (UUcp STATistics
on spool Directory), but I was hoping to receive a better tool.

By the way, seismo!rick responded that it would be simple to
to change 4.2's uusnap.c to handle "flat" directories.  I
spent a short period of time attempting this, but found that
a major rework is still needed because of the way the remote
machine name is embedded in X.machine* files.  Our version of
uucp stores all execute files in the form "D.localX*", where local
is our local site (rlgvax).  However, the version of uusnap.c
for 4.2 assumes that X files are stored in the form "X.remote*".
To find the machine associated with the D.localX file requires
that the C.remote* file be read.
As a result of this, the hacked up uusnap reports 0 X.* files
for all machines, and reports lots of D.* files destined to
our local machine.

Below is the uustatd program.

-dennis bednar

#--------------- 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 the files:
#	Makefile
#	README.uustatd
#	config.h
#	lookdir.c
#	machine.h
#	sample.out
#	uustatd.c
# This archive created: Tue Jul  8 02:51:19 EDT 1986
#
if test -f Makefile
then
echo shar: will not over-write existing file 'Makefile'
else
echo x - Makefile
# ............    F  I   L   E      B  E  G  .......... Makefile
cat << '\SHAR_EOF' > Makefile
OBJS = uustatd.o lookdir.o

# CHANGE THIS FOR YOUR LOCAL SITE
# YOU MUST ALSO CHANGE the include file config.h for your local site.
# All that matters for lookdir.c is whether you have
#	DIRFIXLEN - USG 14 char fixed length file names
#	DIRVARLEN - 4.X BSD variable (up to 255 chars) per directory entry
#
INSTALLDIR = .

uustatd: $(OBJS)
	cc $(LDFLAGS) $(OBJS)
	mv a.out $@

lookdir.o: machine.h config.h

uustatd.o:

# install uustat with set uid uucp so it has permission read
# the uucp spool files.  You must do the install as root.
install: uustatd
	chown uucp uustatd
	chmod 4751 uustatd
	-mv uustatd $(INSTALLDIR)

clean:
	rm -f $(OBJS) uustatd a.out core
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... Makefile
fi # end of overwriting check
if test -f README.uustatd
then
echo shar: will not over-write existing file 'README.uustatd'
else
echo x - README.uustatd
# ............    F  I   L   E      B  E  G  .......... README.uustatd
cat << '\SHAR_EOF' > README.uustatd
This is a uucp statistics program for uucp's that use the "flat"
spool directory.

To install it:
	Edit machine.h and config.h so that config.h properly defines
	either DIRFIXLEN or DIRVARLEN.  See the comments in the
	Makefile for what these mean.

	Change INSTALLDIR in Makefile.

	make			# make a local copy of uustatd

	su root

	make install		# install it whereever it goes

	uustatd			# try running the program

LAST,
	Report any bugs or improvements to {seismo}!rlgvax!dennis.
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... README.uustatd
fi # end of overwriting check
if test -f config.h
then
echo shar: will not over-write existing file 'config.h'
else
echo x - config.h
# ............    F  I   L   E      B  E  G  .......... config.h
cat << '\SHAR_EOF' > config.h
/*
 * config.h
 * configure the local options
 *
 * Based on OS:
 *	termio.h vs. sgtty.h
 *	index/rindex vs strchr/strrchr
 *	if lstat() symbolic link stat is supported
 *
 * You pick:
 *	if fixed len or variable len directory entries are supported
 */


/*
 * bsd 4.1 and 4.2 uses sgtty for ioctl's
 * s3, s5 uses termio.h for ioctls
 * On output, defines WTERMIO==1 or undefined
 * On output, defines TERMIO to be either sgttyb or termio, for defining
 * variables.
 */

#if BSD4_1 == 1 || BSD4_2 == 1
#define TERMIO sgttyb
/* don't want termio so leave WTERMIO undefined */
#else !BSD4_1
#if S3 == 1 || S5 == 1 || S5R2 == 1 || CPGCOS == 1
#define TERMIO termio
#define WTERMIO 1
#else !S3
Unknown version of UNIX.  Only BSD 4.[12], S3, S5, S5R2, and CPGCOS known.
#endif S3
#endif BSD4_1


/*
 * on 4.1 and 4.2 BSD systems, the S3, S5, and S5R2 string(3)
 * names are not around
 */
#if BSD4_1 == 1 || BSD4_2 == 1
#define strchr index
#define strrchr rindex
#endif

#if BSD4_2 == 1 || CPGCOS == 1
#define WSYMLINK 1
#endif

#if BSD4_2 == 1 || CPGCOS == 1
# define DIRVARLEN 1
#else !BSD4_2
# if S3 == 1 || S5 == 1 || S5R2 == 1
#  define DIRFIXLEN 1
# else
   I don't know the directory conventions on this machine
   bsd4.1
# endif
#endif
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... config.h
fi # end of overwriting check
if test -f lookdir.c
then
echo shar: will not over-write existing file 'lookdir.c'
else
echo x - lookdir.c
# ............    F  I   L   E      B  E  G  .......... lookdir.c
cat << '\SHAR_EOF' > lookdir.c
/*
 * lookdir.c
 * author - dennis bednar 5 3 84
 * reads directory entries one at a time for you
 * By recompiling with proper ifdef flag, it works with either fixed length
 * (S3) or variable length names up to 255 chars as in Berkeley.
 *
 *
 * to use:
 *
 * setdir (char *name);		// to open directory "name" for reading
 *				// returns -1 iff error
 * char *getdir ();		// gets next directory name from this directory
 *				// returns (char *) 0 if done
 *
 * you do NOT need to call enddir () to close the directory, this
 * is automatically done when the eof on the directory is encountered.
 *
 * TODO
 * handle multiple open directories, reading from any.
 * allow user to call enddir() when he feels like it.
 */


/* pass DDIRFIXLEN ==1 or DDIRVARLEN == 1 from config.h which is
 * included by machine.h
 */

#include <stdio.h>
#include "machine.h"
#ifdef DIRFIXLEN
#include <sys/types.h>	/* added for osg5 only */
#include <sys/dir.h>	/* old directory format */
#else
#include <sys/types.h>	/* define u_long */
#include <sys/dir.h>	/* new directory format */
#endif


#ifdef DIRFIXLEN
#ifdef DIRVARLEN
	both defined error
#endif
#endif

#ifndef DIRFIXLEN
#ifndef DIRVARLEN
	neither defined error
#endif
#endif


#ifdef DIRVARLEN
DIR *opendir ();	/* open a directory, Berkley variable len format */
struct direct *readdir ();	/* read a filename from a directory */
extern void closedir ();	/* finally, close the directory opened by opendir() */
#endif

#define	MAXPATH		512

/* globals */
static	char	errbuf [MAXPATH];	/* build error message here */
#ifdef DIRVARLEN
	static DIR *headptr = (DIR *)0;	/* pointer to directory structure returned by opendir */
	static struct direct *dirptr;	/* pointer to directory entry structure returned by readdir */
#else
	static int fd = -1;	/* file descriptor associated with directory */
	static struct direct dirbuf;	/* holds directory entry read in from directory */
#endif

/* global routines */
char *getdir ();


/*
 * open the directory "name"
 * returns
 *	0 = good
 *	-1 = can't open directory
 */

setdir (name)
char *name;

{
	/* open the full path name of this dir */

#ifdef DIRFIXLEN
	/* if already open, close it first */
	if (fd != -1)
		enddir ();
	if ((fd = open (name, 0)) == -1)

#else
	/* if already open, close it first */
	if (headptr != (DIR *) 0)
		enddir ();
	if ((headptr = opendir (name)) == (DIR *) NULL)
#endif

		{
		return (-1);
		}
	else
		return 0;

	/* headptr is a pointer to a memory block containing info about this dir */

}


/*
 * return the name of the next directory entry
 * (char *) 0 means end of names
 */

char *getdir ()

{
	static char filename [MAXPATH];	/* read in file name from directory here */


#ifdef DIRFIXLEN

	/* check that directory is indeed open */
	if (fd == -1)
		{
		fprintf (stderr, "dir: getdir: forget to call setdir () first\n");
		exit (1);
		}

	while (read(fd, (char *)&dirbuf, sizeof(dirbuf))>0) {	/* not err or eof */
		if (dirbuf.d_ino == 0)	/* i-node is zero in this slot */
			continue;	/* skip this slot */
		if (strcmp (dirbuf.d_name, ".") == 0	/* then myself */
		||  strcmp (dirbuf.d_name, "..") == 0)	/* or my parent */
			continue;	/* skip this dir entry */
		strncpy (filename, dirbuf.d_name, 14);	/* tail end of name */
#ifdef DEBUG
		printf ("debug: %s\n", filename);
		fflush (stdout);
#endif
		return (filename);	/* next call read next entry */
		}

#else
	/* check that directory is indeed open */
	if (headptr == (DIR *) 0)
		{
		fprintf (stderr, "dir: getdir: forget to call setdir () first\n");
		exit (1);
		}


	while ((dirptr = readdir (headptr)) != (struct direct *)NULL) {	/* not err or eof */
		/* readdir will automatically skip over inodes which are zero */
		if (strcmp (dirptr->d_name, ".") == 0	/* then myself */
		||  strcmp (dirptr->d_name, "..") == 0)	/* or my parent */
			continue;	/* skip this dir entry */
		strcpy (filename, dirptr->d_name);	/* tail end of name */
		return (filename);	/* next call read next entry */
		}
#endif




enddir ();		/* have come to eof */
return (char *) 0;


}


/*
 * called internally when eof is detected
 * sets file descriptor to indicate that file is closed
 */

static enddir ()

{
#ifdef DIRFIXLEN
	close (fd);
	fd = -1;
#else
	closedir (headptr);
	headptr = (DIR *) 0;
#endif
}


#ifdef STAND
/*
 * standalone test code
 *	calling format:

	readdir		// lists all files in the current directory
	readdir dir	// lists files in "dir"
 */

main (argc, argv)
int argc;
char **argv;

{
	int i;		/* loop index into nargv [] */
	int nargc;	/* new arg count returned by getfnames */
	char **nargv;	/* new argv [] array returned by getfnames */


	if (argc == 1)
		getfnames (".", &nargc, &nargv);
	else if (argc == 2)
		getfnames (argv[1], &nargc, &nargv);
	else
		{
		fprintf (stderr, "Wrong number of arguments: usage: %s [directory]\n", argv [0]);
		exit (1);
		}

	if (nargc == -1)
		{
		fprintf (stderr, "%s: getfnames returned error\n", argv[0]);
		exit (1);
		}


	/* sort file names */
	sortargs (nargc-1, &nargv [1]);

	/* pretty print in columns */
	pretty (nargc, nargv, 80);

}
#endif


/*
 * print working directory
 * returns the name in curdir
 */
#if 0
pwd (curdir, dirlen)
char *curdir;
int dirlen;

{
	FILE *Fd;		/* file descriptor for popen */
	int fd;			/* file descriptor for open */
	char *p;		/* index within the dir */
	static char tempfile [] = "/tmp/pwdXXXXXX";	/* temporary file */
	static char pwdcmd [80] = "/bin/pwd >";		/* indirect to a file */
	static char rmcmd [80] = "rm ";		/* indirect to a file */



	/* new way is 10 times cleaner than before !! */
	Fd = popen ("/bin/pwd", "r");	/* open a pipe to pwd cmd */
	fgets (curdir, dirlen, Fd);	/* read in the answer */
	pclose (Fd);

	curdir[strlen(curdir) - 1] = '\0';	/* change the newline to null */

}

#endif


/*
 * returns the file names of all files in "dir" in an argv array
 * just like the shell does, with count in argc.
 *	*pargc = argc returned
 *		(-1 = error and *pargv is garbage,
 *		(1 = no files, and argv[0] = ""),
 *		(2 = 1 file, pointed to by argv[1])
 *	*pargv = pointer to array of pointers to file names
 * ie.
 * argv is returned in *pargv.
 * argv[0] contains the "" string
 * argv[1], ..., argv [argc-1] are pointers to the file names returned
 */

getfnames (dirname, pargc, pargv)
char *dirname;
int *pargc;
char ***pargv;

{
	char *rp;	/* return pointer to file name returned from getdir */
	char **argv;	/* argv [] pointer array dynamically growing */
	char *fp;	/* filename pointer malloc'ed, stuffed into argv[i] */
	int argc;	/* number of elements in the array so far */
	int increase;	/* number of bytes to expand argv[] array by */
	int numentries;	/* number of slots allocated in argv [] array */
	int cursize;	/* total number of bytes in argv[] currently */
	char *malloc (), *realloc ();	/* malloc (3)		*/


	if (setdir (dirname) == -1)
		{
		sprintf (errbuf, "setdir: can't open %s", dirname);
		perror (errbuf);
		exit (1);
		}

/* number of slots in argv array each time expanded by */
#define NUMWANTED 25

	/* compute size in bytes for expanding argv[] by NUMWANTED slots */
	increase = cursize = NUMWANTED * sizeof (*argv);

	/* allocate pointer array the first time */
	if ((argv = (char **) malloc (cursize)) == (char **) 0)
		{
		*pargc = -1;
		return;
		}
	else
		{
		numentries = NUMWANTED;		/* number of slots allocated */
		argv [0] = "";
		argc = 1;	/* number of slots filled so far */
		}


	while ((rp = getdir ()) != (char *) 0)
		{

		/* first expand the array if we need to */

		if (++argc > numentries)
			{
			/* have to reallocate a bigger array */
			argv = (char **) realloc ((char *)argv, cursize = cursize + increase);
			if (argv == (char **) 0)
				{
				*pargc = -1;	/* lost the old array!!! */
				return;
				}
			numentries += NUMWANTED;
			}


		/* get room to copy file name from static area returned
		 * from getdir (), if can't, loose filename.
		 */
		fp = malloc ((strlen (rp) + 1));	/* NULL at end */
		if (fp == (char *) 0)
			{
			--argc;
			break;
			}

		/* now this name will fit in the array, so put it there */
		strcpy (fp, rp);
		argv [argc-1] = fp;

		}

/* return with answers */
*pargc = argc;
*pargv = argv;


}
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... lookdir.c
fi # end of overwriting check
if test -f machine.h
then
echo shar: will not over-write existing file 'machine.h'
else
echo x - machine.h
# ............    F  I   L   E      B  E  G  .......... machine.h
cat << '\SHAR_EOF' > machine.h
/*
 * must define only one of S3, S5, S5R2, BSD4_1, or BSD4_2, CPGCOS to be 1
 */
#define CPGCOS 1

#include "config.h"
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... machine.h
fi # end of overwriting check
if test -f sample.out
then
echo shar: will not over-write existing file 'sample.out'
else
echo x - sample.out
# ............    F  I   L   E      B  E  G  .......... sample.out
cat << '\SHAR_EOF' > sample.out
	Outgoing Traffic
Machine	C Bytes	D Bytes	X Bytes	Files
eli	129	252	60	1
ccicpg	386	25617	195	3
cci632	252	2194	134	2
men1	544	122334	224	4
vrdxhq	710	288291	315	5
dsi1	680	179694	280	5
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... sample.out
fi # end of overwriting check
if test -f uustatd.c
then
echo shar: will not over-write existing file 'uustatd.c'
else
echo x - uustatd.c
# ............    F  I   L   E      B  E  G  .......... uustatd.c
cat << '\SHAR_EOF' > uustatd.c
/*
 * uustatd.c
 * dennis bednar 05 23 86
 * Computer Consoles Inc, 11490 Commerce Park Drive, Reston VA 22091
 * seismo!rlgvax!dennis (UUCP address)
 *
 * uucp statistics program.  "d" stands for dennis to avoid confusion
 * with the standard "uustat" program.
 *
 * Read C. "work" files in the current directory, and
 * print statistics of what we have for outgoing traffic.
 *
 * Read X. "local execute" files in the current directory, and
 * print statistics of what we have for incoming traffic.
 *
 *	-x99 = debug flag
 *	-s<sys> = machine name or system name
 *	-dir<dir> = process directory other that /usr/spool/uucp
 *
 *
 *
 * NOTE: This command ONLY works on a version of uucp in which
 * there are *no* subdirectories in the UUCPSPOOLDIR.  It will
 * *NEED* modification to work on versions of uucp which
 * have sub-directories.  If you decide to modify this code,
 * then I suggest you #ifdef it with SUBDIR so that it can easily
 * be compiled to work with either kind of machine.  Send modifications
 * back to the original author (dennis@rlgvax.uucp).
 *
 * The external directory reading routines can be #ifdef'ed
 * for V7/S3/S5 14 character fixed length names or can be
 * #ifdefed for 4.2BSD variable (up to 255 chars) file names.
 *
 */

#include <stdio.h>
#include <ctype.h>	/* isdigit() */
#include <sys/types.h>
#include <sys/stat.h>
#include <dir.h>

#define TRUE		1
#define	FALSE		0
#define STR_SAME	!strcmp
#define STR_DIFF	strcmp
#define STRN_SAME	!strncmp
#define STRN_DIFF	strncmp
#define	MAXFILELEN	100
#define MAXLINE	256

/* routines that read directory entries */
extern	int	setdir();
extern	char	*getdir();

extern	char	*malloc();

#define MAXMCHNAME	20
struct statblock {
	/* s_next *must* be first */
	struct	statblock	*s_next;	/* next one in queue */

	char	s_mchname[MAXMCHNAME+1];	/* store machine name here */
	long	s_csize;	/* number of chars in all C. command files */
	long	s_dsize;	/* number of chars in all D. data files */
	long	s_xsize;	/* number of chars in all X. xqt files */
	int	s_numq;		/* number queued up */
	};

struct statblock	*getstatp();	/* get statistics block pointer */
long	filesize();
char	*u_errmesg();
char	*getmchname();
char	*getl_file();

char	*cmd;
int	debug = 0;	/* >=1 prints Warning msgs. >=10 prints all */
char	*flag_mch;	/* only interested in this machine */
char	*flag_dir;	/* special case to work in this directory
			 * instead of default UUCPSPOOLDIR.
			 */

/* shared by getstatb(), dump_stat(), and clean_stat() */
static	struct statblock	*mchlist = NULL;

#define UUCPSPOOLDIR "/usr/spool/uucp"


main(argc, argv)
	int	argc;
	char	*argv[];
{
	char	*wdir;		/* name of directory to work in */

	cmd = argv[0];

	getargs(argc, argv);

	if (flag_dir)
		wdir = flag_dir;	/* user overridden directory */
	else
		wdir = UUCPSPOOLDIR;	/* default directory */

	if (chdir( wdir ) == -1)
		{
		fprintf(stderr, "%s: cannot chdir(%s)\n", cmd, wdir);
		exit(1);
		}

	pass1();

	pass2();

	exit(0);

}


/*
 * Outgoing traffic is processed
 * Also each D. and X. file "attached" to a C. file is recorded now.
 * Later in pass 2, when the entire directory is rescanned, we
 * will report those "detached" D. and X. files.
 */
pass1()
{
	char	*fname;		/* file name */

	/* open the current directory */
	if ( setdir(".") == -1)
		{
		fprintf(stderr, "%s: cannot open directory\n", cmd);
		exit(1);
		}


	/* read until eof on directory */
	while ( (fname = getdir()) != (char *)NULL)
		{
		if (debug > 10)
			printf("%s\n", fname);
		process("C.",  fname);
		}


	if (mchlist)		/* have statistics for at least one machine */
		{
		printf("	Outgoing Traffic\n");
		dump_stat();
		clean_stat();
		}

}

/*
 * Incoming traffic is processed
 */
pass2()
{
	char	*fname;		/* file name */

	/* open the current directory */
	if ( setdir(".") == -1)
		{
		fprintf(stderr, "%s: cannot open directory\n", cmd);
		exit(1);
		}


	/* read file names */
	while ( (fname = getdir()) != (char *)NULL)
		{
		if (debug > 10)
			printf("%s\n", fname);
		if (STRN_SAME(fname, "X.", 2) || STRN_SAME(fname, "D.", 2))
			chk_detach(fname);	/* see if detached */
		process("X.", fname);
		}

	if (mchlist)
		{
		printf("\n	Incoming Traffic (Unprocessed XQT files)\n");
		dump_stat();
		/* don't worry about clearing statistics 2nd and last time */
		}
}


/*
 * only process the C. files, because they contain text info
 * that points to the attached D. datafile, and attached X.
 * execute file.
 *
 * For example, for outgoing traffic:
 * the file C.dsi1AD0060 contains:
S D.dsi1BC0060 D.dsi1BC0060 network - D.dsi1BC0060 0666 network 
S D.rlgvaxXA0060 X.rlgvaxXA0060 network - D.rlgvaxXA0060 0666 network 
 * the file D.dsi1BC0060 contains a news batch created by 'batch',
 * and the fiile D.rlgvaxXA0060 contains the commands:
U network rlgvax
F D.dsi1BC0060
I D.dsi1BC0060
C rnews 
 *
 * For incoming traffic, there is an X.<remotesite>... file that
 * contains an "F" or "I" line which contains the name of the
 * local file which will be processed by /usr/lib/uuxqt.
 */
process(prefix, cfile)
	char	*cfile;			/* name of C. command file */
{
	FILE	*fp;
	char	*rtn1,
		*rtn2;
	char	line1[MAXLINE+2];
	char	line2[MAXLINE+2];	/* room for newline, null */
	char	*mch;			/* name of machine */
	char	*dfile;			/* name of datafile */
	char	*xfile;			/* name of execute file */
static	char	ddfile[MAXFILELEN+1];	/* room for null */

	if (debug > 10) printf("process(%s) called\n", cfile);

	if (!isregfile(cfile))
		{
		if (debug >= 1)
		fprintf(stderr, "%s: Warning, ignoring irregular file %s\n", cmd, cfile);
		return;
		}

	/* skip files that don't begin with file prefix */
	/* in pass1, (Outgoing files), skip files that don't begin with C. */
	/* in pass2, (Incoming files), skip files that don't begin with X. */
	if (STRN_DIFF(cfile, prefix, strlen(prefix)))
		return;

	/* skip files that don't have the desired machine embedded in them */

	mch = getmchname(cfile);
	if (debug > 10) printf("process: machine name was %s\n", mch);
	if (flag_mch && STR_DIFF(mch, flag_mch))
		return;


	fp = fopen(cfile, "r");
	if (fp == NULL)
		{
		fprintf(stderr, "%s: cannot open %s: %s\n", cmd, cfile, u_errmesg());
		exit(1);
		}

	/* if premature EOF on either line, ignore this file */

	if (STR_SAME(prefix, "C."))
		{
		/* process outgoing C. "work" file */
		rtn1 = fgets(line1, sizeof(line1), fp);

		rtn2 = fgets(line2, sizeof(line2), fp);

		if ( (rtn1 == NULL) || (rtn2 == NULL) )
			{
			if (debug >= 1) fprintf(stderr, "%s: Warning ignorning file w/o 2 lines: %s\n", cmd, cfile);
			return;
			}

		}
	else
		{
		/* process incoming X. file */
		/* in the X. file, search for an F file directive */
		/* the file name after it refers to a file on the local mch */
		while (fgets(line1, sizeof(line1), fp) != NULL)
			{
			if (line1[0] == 'F')
				break;
			}
		if (debug > 2) printf("read F line <%s>\n", line1);
		}

	fclose(fp);


	/* Warning *must* copy static dfile to ddfile because getl_file
	 * reuses the static buffer within.
	 */

	if (STR_SAME(prefix, "C."))
		{
		/* process outgoing C. file */
		dfile = getl_file("S ", line1);	/* get name of D. file from line */
		if (dfile && debug > 10) printf("process: Data File = %s\n", dfile);
		if (dfile) strcpy(ddfile, dfile);

		xfile = getl_file("S ", line2, cfile);
		if (xfile && debug > 10) printf("process: XQT  File = %s\n", xfile);

		if (dfile && xfile)
			add_stat(mch, cfile, ddfile, xfile);
		else
			fprintf(stderr, "%s: Warning, possible bad format in file '%s'\n", cmd, cfile);
		}
	else
		{
		/* process incoming X. file */
		xfile = cfile;		/* file name passed */
		dfile = getl_file("F ", line1, cfile);
		if (dfile)
			add_stat(mch, NULL, dfile, xfile);
		else
			fprintf(stderr, "%s: Warning, skipping file '%s'\n", cmd, cfile);
		}

}

/*
 * return UNIX error message associated with errno
 * more flexible than perror(3)
 */

char *
u_errmesg()
{
	extern	int	errno;
	extern	int	sys_nerr;
	extern	char	*sys_errlist[];
static	char	buffer[50];

	if (errno < 0 || errno >= sys_nerr)
		{
		sprintf( buffer, "errno %d undefined (%d=max)", errno, sys_nerr);
		return(buffer);
		}

	return( sys_errlist[errno] );
}


/*
 * return true if the file is a regular file (ie not directory, pipe, etc.)
 */
isregfile(fname)
	char	*fname;		/* file name terminated by null */
{
	int	fd;		/* file descriptor */
	struct	stat statbuf;	/* read info about a file into here */
	int	rtn;		/* return code from system call */

	fd = open(fname, 0);
	if (fd == -1)
		{
		fprintf(stderr, "%s: isregfile: cannot open %s: %s\n", cmd, fname, u_errmesg());
		return FALSE;
		}

	rtn = fstat(fd, &statbuf);
	if (rtn == -1)
		{
		fprintf(stderr, "%s: cannot stat %s: %s\n", cmd, fname, u_errmesg());
		exit(1);
		}

	close(fd);		/* ignore error */

	if ( (statbuf.st_mode & S_IFMT) == S_IFREG)	/* is a reg file */
		return TRUE;
	else
		return FALSE;

}



/*
 * return number of characters in a file
 */
long
filesize(fname)
	char	*fname;		/* file name terminated by null */
{
	int	fd;		/* file descriptor */
	struct	stat statbuf;	/* read info about a file into here */
	int	rtn;		/* return code from system call */

	/* no file name was passed */
	if (fname == NULL)
		return 0L;

	fd = open(fname, 0);
	if (fd == -1)
		{
		if (debug >= 1) fprintf(stderr, "%s: Warning, cannot open %s to get size: %s\n", cmd, fname, u_errmesg());
		return 0L;
		}

	rtn = fstat(fd, &statbuf);
	if (rtn == -1)
		{
		fprintf(stderr, "%s: cannot stat %s: %s\n", cmd, fname, u_errmesg());
		exit(1);
		}

	close(fd);		/* ignore error */

	return (long) statbuf.st_size;
}



/*
 * get machine name from a UUCP file name
 * format is D.machineXY0000
 * 	     C.machineAY0000
 * "machine" could end with a digit such as "dsi1"
 *
 */
char	*
getmchname(fname)
	char	*fname;		/* CANNOT have slash in file name */
{
	register	char	*cp;
#define MCHNAMESIZE	20
	static	char	mchname[MCHNAMESIZE+1];	/* room for null */
	int	len;

	cp = fname;

	cp += strlen(fname);	/* points to null terminator */

	--cp;			/* last char of fname */

	/* skip backwards over digits until a letter is seen */
	/* this works even if the number of digits is not 4 */
	while ( (cp > fname) && isdigit(*cp))
		--cp;

	/* have come to 2nd letter of pair */
	cp--;		/* cp now points to first letter of pair, or one
			 * past the machine name.
			 */

	len = (cp - (fname+2) );	/* compute length of machine name */

	if ( (len <= 0) || (len > MCHNAMESIZE) )
		{
		fprintf(stderr, "%s: getmchname(%s) failed\n", cmd, fname);
		exit(1);
		}

	strncpy(mchname, fname+2, len);
	mchname[len] = '\0';		/* strncpy doesn't copy a NULL */

	return mchname;
}



/*
 * get name of file from the line that looks like:
S file ... more stuff
 */
char	*
getl_file(prefix, line, rdfile)
	char	*prefix;	/* either "S " or "F " */
	char	*line;		/* line of data from 'rdfile' */
	char	*rdfile;	/* name of file in case of error */
{
	static	char	filename[MAXFILELEN+1];	/* room for null */
	char	*cp;
	char	*dp;
	int	len;

	/* skip over the "S " or "F " part of the line */
	cp = line;
	if (STRN_DIFF(line, prefix, strlen(prefix)))
		{
badformat:
		fprintf(stderr, "%s: prefix '%s' expected in file '%s', line '%s'\n", cmd, prefix, rdfile, line);
		return NULL;
		}
	cp += 2;

#define iswhite(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\0'))

	if ( iswhite(*cp) )
		{
		fprintf(stderr, "%s: iswhite char (%c) found at line+2\n", cmd, *cp);
		goto badformat;
		}


	/* copy the file name from 'cp' to 'filename' */

	dp = filename;
	len = 0;
	while ( !iswhite(*cp))
		{
		*dp++ = *cp++;
		if (++len > MAXFILELEN)
			{
			fprintf(stderr, "%s: filename in line %s was too long\n", cmd, line);
			exit(1);
			}
		}

	*dp = '\0';

	return filename;
}


/*
 * add statistics for this machine name.
 */
add_stat(mch, cfile, dfile, xfile)
	char	*mch,
		*cfile,
		*dfile,
		*xfile;

{
	struct	statblock	*sp;

	if (debug > 10)
	printf("addstat(mch=%s, cfile=%s, dfile=%s, xfile=%s) called\n",
	 mch, cfile, dfile, xfile);


	/* get an existing or brand-new statistics block for this machine */
	sp = getstatp(mch);

	if (cfile)
		sp->s_csize += filesize(cfile);

	if (dfile)
		{
		sp->s_dsize += filesize(dfile);
		addtolist(dfile);		/* D. file is attached */
		}

	if (xfile)
		{
		sp->s_xsize += filesize(xfile);
		addtolist(xfile);		/* X. file is attached */
		}

	sp->s_numq++;

	if (debug > 10) printf("add_stat:	%s	%ld	%ld	%ld	%d\n",
	 sp->s_mchname, sp->s_csize, sp->s_dsize, sp->s_xsize, sp->s_numq);
}


/*
 * get a pointer to a statistics block.
 * Search the existing list, if found, return it.
 * Otherwise allocate a new block, hook onto end of list, zero out
 * statistics counters, and return it.
 */


struct	statblock *
getstatp(mch)
	char	*mch;
{
	struct	statblock	**p;	/* previous item.ptr in list */
	struct	statblock	*n;	/* next item in list */
	struct	statblock	*new;	/* new malloced statistics block */

	/* search existing list */
	for (p = &mchlist; n = *p; p = (struct statblock **)n)
		{
		if (STR_SAME(mch, n->s_mchname))
			return n;
		}

	/* not found, p points to ptr where new stat block ptr will be stored */
	/* note how this code is cleverly written to work even if the
	 * list is empty the first time, or when the list is non-empty
	 * the 2nd, 3rd, etc. times.
	 */

	new = (struct statblock *)malloc(sizeof(struct statblock));
	if (new == NULL)
		{
		fprintf(stderr, "%s: malloc failed\n", cmd);
		exit(1);
		}

	/* hook onto end of existing list if any */
	*p = new;

	new->s_next = NULL;		/* ground end of linked list */
	strcpy(new->s_mchname, mch);	/* store new machine name */
	new->s_csize = new->s_dsize = new->s_xsize = 0L;
	new->s_numq = 0;

	return new;
}


dump_stat()
{
	struct	statblock	*p;

	p = mchlist;

	printf("Machine	C Bytes	D Bytes	X Bytes	Files\n");

	while (p)
		{
		printf("%s	%ld	%ld	%ld	%d\n",
		 p->s_mchname, p->s_csize, p->s_dsize, p->s_xsize, p->s_numq);
		p = p->s_next;
		}
}

/*
 * discard old statistics by freeing statistics blocks in linked list
 */
clean_stat()
{
	struct	statblock	*p,	/* current statblock item */
				*n;	/* next one after this one */

	for (p = mchlist; p; p = n)
		{
		n = p->s_next;		/* save next item ptr ... */
		free(p);		/* before discarding current item */
		}

	mchlist = NULL;			/* list is empty again */
}


/*
 * get arguments from command line, assign global flag variables
 */
getargs(argc, argv)
	int	argc;
	char	**argv;
{
	register	int	i;

	if (argc != 1)
		for (i = 1; i < argc; ++i)
			{
			if ( STRN_SAME(argv[i], "-x", 2) )
				debug = atoi(&argv[1][2]);
			else if (STRN_SAME( argv[i], "-s", 2) )
				flag_mch = argv[i]+2;
			else if (STRN_SAME( argv[i], "-dir", 4) )
				flag_dir = argv[i]+4;
			}
}

struct node {
	struct list	*l_next;	/* MUST be first, next item in list */
	char		l_name[1];	/* domain name + 1 for null */
	};

struct node	*head = NULL;

/*
 * standard FIFO implemented by linked list.
 * NO sorting.
 */
addtolist(name)
	char	*name;
{
	register	struct	node	**p,	/* previous item */
					*n;	/* next item */
	struct	node	*np;			/* node pointer */

	/* find node pointed to by p which is the end of the list to tack onto */
	for (p = &head; n = *p; p = (struct node **)n)
		if (STR_SAME(n->l_name, name))
			return;		/* already in list */

	/* use same trick that cpio uses */
	np = (struct node *) malloc( sizeof(struct node) + strlen(name) );

	if (np == NULL)
		{
		perror("out of memory");
		exit(1);
		}

	/* stuff node with domain name */
	np->l_next = NULL;	/* ground end of list */
	strcpy(np->l_name, name);

	/* hook onto end of list */
	*p = np;


/*	printf("dbg: %s\n", name);	/* print domains as we hit them */

	/* done */
}


/*
 * search the linked list, returning TRUE iff found.
 * The arg passed will be a D. or X. file name saved during
 * pass 1, hopefully.  If not, its detached.
 */
inlist(fname)
	char	*fname;
{
	register	struct	node	**p,	/* previous item */
					*n;	/* next item */

	for (p = &head; n = *p; p = (struct node **)n)
		if (STR_SAME(n->l_name, fname))
			return 1;
	return 0;
}

chk_detach(fname)
	char	*fname;
{
	if (!inlist(fname))
		fprintf(stderr, "%s: Warning: %s is detached\n", cmd, fname);
}

\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... uustatd.c
fi # end of overwriting check
# end of shell archive
exit 0
-- 
-Dennis Bednar
{decvax,ihnp4,harpo,allegra}!seismo!rlgvax!dennis	UUCP