[net.sources] dump/file-restor code

petec@umcp-cs.UUCP (02/28/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting filtapmgr.c'
sed 's/^X//' <<'//go.sysin dd *' >filtapmgr.c
static	char	*sccsid = "@(#) filtapmgr  (University of Maryland)";

X/* 			File Tape Manager
*
* Copyright 1984 - University of Maryland
*
*	This program is a file/tape manager.  It's purpose is to maintain
* a record of every file dumped onto a particular tape.  The record
* contains the file name, its inode number, a directory flag, and three tape
* names (one primary & two  secondary).  The information is kept in a compact
* form in a file called "/etc/filesontapes".  Several file systems can
* be maintained in this one file.
*	This program is called using a "file system name", a dumpdir
* file, and a tape name.  The file system name should be a logical name
* (ie. the name of a mounted file system.)  The dumpdir file should be
* the name of a file containing the output from the dumpdir program.
* The tape name can be any 2-letter name (defined by MAXTAPNAM).
*	The number of Back-ups for each file can be varied (MAXBACKUPS).
* For each back-up, the inode number is kept.  This is not necessary, but
* might prove helpful, if the restor program is changed to accept inode #'s
* as a "key" to finding & restoring the file.
*/
X/* Original design by:  Patrick Steranka (patrick@umcp-cs)  --  Dec. 1982 
   Appended to and maintained by:  Pete Cottrell (petec@umcp-cs) 

   Please send all comments, bug reports, etc, to Pete Cottrell.
   Patrick has graduated and is no longer working with this.	
*/
#include <stdio.h>
#include <sys/param.h>
#include <dir.h>
#include <ctype.h>
#include "filtap.h"

PTRNODE marker; /* This marker marks the end of a directory */
PTRNODE	base_sys; /* Pointer to the base of the in-core file system */

char	*filsys_nam,	/* Logical Name of file system currently working on */
	*dd_file,	/* Name of current dumpdir for current file system */
	*tape_name;	/* Tape Name to be used for updating all entries in
			   dumpdir file */
char	*filename;
ino_t	inode;
FILE	*fp_dd; /* This is used for the dumpdir files */
FILE	*fp; /* This is used for /etc/filesontapes */
char	*tapeused;
X/* Option flags */
int	topt = 0;	/* tape option */
int	fopt = 0;	/* file option */
int	popt = 0;	/* purge-only option */
int	vopt = 0;	/* verbose option */
int	dopt = 0;	/* disable tape-check option */
int	sopt = 0;	/* summary option */
X/* Summary Information */
int	s_nofils = 0;	/* Record of # files read per files system */
int	s_nomkrs = 0;	/* # of markers in filsys_file */

int	s_totent = 0;	/* total number of entries in filsys_file */
int	s_filent = 0;	/* number of actual file entries in filsys_file */
int	s_dirent = 0;	/* number of directory entries in filsys_file */
int	s_dotent = 0;	/* number of dot(.) or dotdot(..) entries */
                     /* above totals tallied during writing of filsys_file */

int     r_totent = 0;   /* number of entries read from filsys_file */
int  	d_totent = 0;	/* number of entries deleted from filsys_file during
                           reading due to outdated info */


X/* Global Procedures and Functions used */
char	**argv; /* this will be used globally */
int	argc;	/* this will be used globally */

char	*index(),*adjust();
PTRNODE	talloc(),makdir(),findname(),getnode(),getentry(),getvalentry();
int	ckfilsys(),getnextfile();
FILE	*fopen();


main(argn,arg)
int	argn;
char	**arg;
{

		/* Initialize the marker variable, do it here vs during 
		initialization so if MAXBACKUPS changes then this will 
			still work */
	marker = getnode("/",0); 
	argc = argn; /* make argc global */
	argv = arg;  /* make argv global */
	if (argv[1][0] == '-') setopts(); 
	if(!popt) /* make sure we have enough arguments */
	  if ((argc < 4 && topt == 0) || (argc < 3 && topt == 1)){
usage:		fprintf(stderr,"Usage: filtapmgr [-f filsys_file] [-sdv] [-t tape_name] filsys_nam dumpdir_fil tape_name\n");
		fprintf(stderr,"or     filtapmgr [-f filsys_file] [-dvs] -p tape_name\n");
		exit(1);
	}

	if (vopt) /* if user want's to know what's going on */
		printf("Building in core file system using %s.\n",filsys_file);
	/* let's check the tape name (don't want any small letters) */
	if (!dopt && (topt || popt)){
		tapeused = adjust(tapeused);
	}
	/* Get a node to be the base of the fil_sys */
	base_sys = getnode("_root_",0);

	if (Build_Sys(base_sys,filsys_file) != 1){
		fprintf(stderr,"Fatal Error in Build_Sys after %d entries.\n",
			r_totent);
		fprintf(stderr,"%d entries were deleted before the error\n",
			d_totent);
		exit(1);
	}
	if (popt) /* we are simply purging, no need to add or update */
		printf("Purge is complete, Comrade Hacker!!!\n");
	else
	/* For each file system -- add or update files dumped onto tape */
		while (nextfilsys(&filsys_nam,&dd_file,&tape_name)){
			s_nofils = 0; /* count # of files in dumpdir file */
			if (vopt)
		    	    printf("Processing file system %s\n",filsys_nam);
			include_these_files(filsys_nam,dd_file,tape_name);
			if (sopt)
				printf("%d files processed (on %s)\n",s_nofils, 
					filsys_nam);
		}

	if(vopt)
		printf("Creating (or Updating) compacted file system file %s.\n",filsys_file);

	Write_Sys(base_sys,filsys_file);
	if (sopt){
		/* print out summary information */
		printf("%d\tTotal entries\n",s_totent);
		printf("%d\tdirectory entries\n",s_dirent);
		printf("%d\tfile entries\n",s_filent);
		printf("%d\tmarker entries\n",s_nomkrs);
		printf("%d\tdirectory entries (including '.' & '..')\n",
				s_dirent+s_dotent);
	}
	exit(0);
}

setopts()
{
	char *opts;

	while(argv[1][0] == '-'){
		opts = *++argv;
		argc--;
		while(*++opts){
			switch(*opts){
			case 'v': /* verbose option */
				vopt++;
				break;
			case 's': /* summary option */
				sopt++;
				break;
			case 'd': /* disable tape check option */
				dopt++;
				break;
			case 'p': /* purge option */
				popt++;
				break;
			case 't': /* tape option */
				tapeused = *++argv;
				argc--;
				topt++;
				break;
			case 'f': /* file option */
				filsys_file = *++argv;
				argc--;
				fopt++;
				break;
			default:
				fprintf(stderr,"Invalid option `%c' ignored.\n",*opts);
				break;
			} /* end case */
		} /* end while (opts) */
	} /* end while */
}


Build_Sys(base,filsys_fil)
PTRNODE	base;
char	*filsys_fil;
{
	if ((fp = fopen(filsys_fil,"r")) == NULL){
		fprintf(stderr,"\n\nNo previous `files on tape' information.\n");
		fprintf(stderr,"File %s will be created.\n",filsys_fil);
		creat(filsys_fil,0644);
		return(1); /* File system built OK */
	}
	if (Recurse_Read(base) != 1){
		fclose(fp);
		return(0);
	} /* Some Error in creating incore file system */
	fclose(fp);
	if (topt){
		printf("File system data file built. Due to old info,\n");
		printf("%d\t entries out of %d\t were deleted.\n",d_totent,
			r_totent);
	}
	return(1); /* File system Built OK */
}

Recurse_Read(st_ptr)
PTRNODE st_ptr;
{
	PTRNODE e,dot,dotdot,trav;

	/* St_ptr should be pointing to a directory name. */
	/* So "go down" to its "." entry and write all its sub-entries */
	if ((dot=getvalentry(1)) == NULL) return(1); /* This is the */
	if ((dotdot=getvalentry(1)) == NULL){	       /*  normal return */
		/* What missing ".." entry ??? */
		fprintf(stderr,"Unexpected EOF in file /etc/filesontapes\n");
		return(ERROR);
	}
	if ((dot == (PTRNODE) ERROR) || 
		(dotdot == (PTRNODE) ERROR)) return(ERROR);
	/* Link "." to directory name & "." to ".." */
	st_ptr->down = dot;
	linknode(dotdot,dot);
	if(strcmp(".",dot->name) != 0 || strcmp("..",dotdot->name) != 0){
		fprintf(stderr,". or .. entries missing from directory %s\n",st_ptr->name);
		fprintf(stderr,". and .. entries are:\n");
		printnode(dot);
		printnode(dotdot);
		fprintf(stderr,"`%s' not found\n",st_ptr->name);
		return(ERROR);
	}
	trav = dotdot;
	while((e=getvalentry(1)) != NULL && strcmp("/",e->name) != 0){
		/* Add this entry to the directory list */
		if (e == (PTRNODE) ERROR) return(ERROR);
		linknode(e,trav);
		if (e->dir)
			if(Recurse_Read(e) == ERROR) return(ERROR);
		trav = trav->next;
	}
	return(1);
}


PTRNODE
getvalentry(ckflag)
int ckflag;
{
    register int havemarker;
    register int valid=0;
    PTRNODE e;
    while (!valid){
	e=getentry();
	if (e==(PTRNODE) NULL || e==(PTRNODE) ERROR) return(e);
	if (!topt || strcmp("/",e->name)==0 || 
            strcmp(".",e->name)==0 || strcmp("..",e->name)==0){
		valid++;
	}
	else{			/* topt is set, so let's clean the system */
        	if (e->dir && ((ckflag)?delete_file_entry(e):1)){
		  /* we are possibly at a directory with outdated info, or
		    we are recursively deleting sub-directories */
		   havemarker=0; /* When we are deleting a sub-directory, we
				   want to keep going until we hit the file 
				   entry containing a '/', known as a marker.
				   Havemarker is a flag that tells us when we
				   have reached the marker */
		   while (!havemarker){
		       e=getvalentry(0);
                       d_totent++;
		       if (e==(PTRNODE) NULL || e==(PTRNODE) ERROR) return(e);
		       if (strcmp("/",e->name)==0) havemarker++;
		   }
                }
		else if (e->dir) valid++;
		else /* at file node, or at a '.' or '..' node, so lets
                        get rid of old info */
		   ckflag?(delete_file_entry(e)?d_totent++:valid++):valid++;
        }
    }
    return(e);
}

delete_file_entry(e)
PTRNODE e;
{
    int i,ii,j;
    int delete=1;
    for (ii=0,i=0; ii<MAXBACKUPS; ii++)
	if (strcmp(e->tapes[i],tapeused)==0){
	    for (j=i; j<MAXBACKUPS-1; j++){
		strcpy(e->tapes[j],e->tapes[j+1]);
		e->inodes[j] = e->inodes[j+1];
	    }
	    e->inodes[MAXBACKUPS-1] = 0;
	    for (j=0;j<MAXTAPNAM; j++)
		e->tapes[MAXBACKUPS-1][j] = ' ';
	    e->tapes[MAXBACKUPS-1][MAXTAPNAM] = '\0';
	    /* keep the value of i so we can look at this position again */
	}
	else i++; /* this position of tapes[] looks OK, go to next */
    for (i=0; i<MAXBACKUPS; i++)
	if (strcmp(e->tapes[i],"  ") != 0) delete--;
    if (delete==1) return(1);
    return(0);
}	    
 
	
nextfilsys(filsys_nam,dumpdir_fil,tape_name)
char	**filsys_nam,**dumpdir_fil,**tape_name;
{
	if (*++argv == 0) return(0); /* Stop processing */
	*filsys_nam = *argv;
	*dumpdir_fil = *++argv;
	if (ckfilsys(*filsys_nam) == 0){
		fprintf(stderr,"File system %s is not recognized\n",*filsys_nam);
		return(0); /* Stop processing */
	}
	if (!topt)
		if (dopt) 
			*tape_name = *++argv;
		else 
			*tape_name = adjust(*++argv);
	else
			*tape_name = tapeused; 
	if (strlen(*tape_name) > MAXTAPNAM){
		(*tape_name)[MAXTAPNAM] = '\0';
		fprintf(stderr,"Tape name truncated to `%s'\n",*tape_name);
	}
	return(1);
}


X/* ckfilsys:  Map logical file system to a device & return(1).  If no
   mapping can be done, return(0).  (ie. check to make sure a valid
   file system is given). Amended to handle /u files, PBC 7/83 */

ckfilsys(filsystem)
char *filsystem;
{
	if (strcmp(filsystem,"/usr") == 0 || strcmp(filsystem,"/g") == 0
       	|| strcmp(filsystem,"/") == 0 || strcmp(filsystem,"/u") == 0)
		return(1);
	else
		return(0);
}

include_these_files(filsys_nam,dd_file,i_tapnam)
char *filsys_nam,*dd_file,*i_tapnam;
{
	char i_filnam[100];
	ino_t i_inode,i;
	char tline[100];
	
	if ((fp_dd = fopen(dd_file,"r")) == NULL){
		fprintf(stderr,"Can't open dumpdir file %s\n",dd_file);
		return(0);
	}
	/* Skip over dates given in dumpdir output */
	for(i=0;i<2;i++)
		fgets(tline,100,fp_dd);
	while (getnextfile(i_filnam,&i_inode,fp_dd) != EOF){
		addorupdate(i_filnam,i_inode,i_tapnam,base_sys);
	}
	fclose(fp_dd);
	return;
}

getnextfile(fname,finode,fp)
char	*fname;
ino_t	*finode;
FILE	*fp;
{
	static int j;
	char c,tname[100];

	j = fscanf(fp,"%d",finode);
	while((c=getc(fp)) == '\t' || c == ' ')
		;
	ungetc(c,fp);
	fgets(tname,100,fp);
	tname[strlen(tname)-1] = '\0';
	strcpy(fname,filsys_nam); /* Prefix file name with logical File system name */
	strcat(fname,tname);
	if (j != EOF) s_nofils++; /* inc # files processed */
	return(j); /* Return -1 on EOF */
}

addorupdate(fnam,finode,tapnam,st_ptr)
char	*fnam,*tapnam;
ino_t	finode;
PTRNODE	st_ptr;
{
	char *idxslash,name[100],*restofname;
	int eoname,found;
	PTRNODE trav,e;

	if (*fnam == '/'){ /* If leading slashes then */
		while (*fnam == '/')
		fnam++; /* Skip over leading slashes */
		if (st_ptr->down == NULL)
			/* No way down -- so create a directory */
			trav = makdir(st_ptr);
		else	/* Directory exists -- just traverse */
			trav = st_ptr->down;
	}
	else{ 	/* Must be starting in a "current" directory */
		trav = st_ptr;
	}
	/* Get Name & set eoname flag */
	/* eoname is true if no more slashes in name */
	eoname = ((idxslash = index(fnam,'/')) == NULL);
	if (eoname == 0) /* if not end of name */
		restofname = idxslash; /* whatevers after the slash */
	strncpy(name,fnam,(eoname == 0)? (idxslash-fnam) : strlen(fnam));
	name[(eoname == 0)? (idxslash-fnam) : strlen(fnam)] = '\0';
	/* Look for name -- along list of files */
	trav = findname(trav,name,eoname==0,&found);
	if (found){
		/* Found a directory or a file */
		if (eoname){ /* A file was found -- just update its info */
			update(trav,finode,tapnam);
		}
		else{	/* This is just a directory on the way to a file */
			/* So, update its info if we haven't been here before */
			if(trav->Marked == 'n')
				update(trav,finode,tapnam);
			addorupdate(restofname,finode,tapnam,trav);
		}
	}
	else{	/* File or directory not found -- so add it */
		e = getnode(name,0); /* Could be a directory or a file */
		linknode(e,trav);
		if (eoname)
			/* file just added */
			update(e,finode,tapnam);
		else{	/* directory just added - now recurse to add it's
			   "." and ".." entries */
			update(e,finode,tapnam);
			addorupdate(restofname,finode,tapnam,e);
		}
	}
	return;
}

PTRNODE
makdir(st_ptr)
PTRNODE	st_ptr;
{
	PTRNODE dot,dotdot;

	st_ptr->dir = 1; /* Make node comming from a directory */
	/* Create "." and ".." entries for a directory */
	dot = getnode(".",0);
	dot->dir = 1;
	st_ptr->down = dot; /* Link st_ptr to the dot entry */
	dotdot = getnode("..",0);
	dotdot->dir = 1;
	/* Link dot to the dotdot entry */
	dot->next = dotdot; /* Link dot to the dotdot entry */
	return(dot); /* Return a pointer to the "." entry */
}

PTRNODE
findname(st_ptr,name,dir,found)
PTRNODE	st_ptr;
char	*name;
int	dir,*found;
{
	PTRNODE	trav,last;

	if (strcmp(".",st_ptr->name) == 0){
		/* Handle "." & ".." case, first */
		if (strcmp(name,".") == 0){
			/* Looking for ".", so return "."'s entry */
			(*found) = 1;
			return(st_ptr);
		}
		if (strcmp(name,"..") == 0){
			/* Looking for "..", so return ".."'s entry */
			(*found) = 1;
			return(st_ptr->next);
		}
		/* Begin searching a directory */
		trav = st_ptr->next; /* point to ".." entry */
		if (trav->next == NULL){
			/* No files in this directory, except "." & ".." */
			(*found) = 0;
			return(trav); /* Insertion would go after .. entry */
		}
	}
	else{	/* Start searching, somewhere in the middle of a directory */
		trav = st_ptr;
	}
	last = trav;
	trav = trav->next; /* Don't include . or .. for alphabetical order */
	/* Look for entry "name" in this directory */
	while (trav != NULL && strcmp(trav->name,name) < 0){
		/* Look for "name", & know list is in alphabetical order */
		last = trav;
		trav = trav->next;
	}
	if (trav == NULL){
		/* easy case, Searched entire directory and didn't find "name",
		   so it would be added at the end of this directory list */
		(*found) = 0;
		return(last); /* last should point to last entry in dir list */
	}
	if (strcmp(trav->name,name) > 0){
		/* "name" was not found in dir list, and to keep the dir
		   list in alphabetical order, the entry "name" should be
		   added after the entry last */
		(*found) = 0; /* "name" was not found in directory */
		return(last);
	}
	/* If this code is reached, then "name" was found in the directory
	   list.  However, the directory status must also match in order for
	   the file to be considered "found" */
	if (trav->dir == dir){
		/* "name" has been found in the directory list */
		/* either as a file or as a directory */
		(*found) = 1;
		return(trav);
	}
	/* If this code is reached, then a peculiar situation has arisen.
	   Both a file & a directory have the same name.  In this case,
	   the directory should appear before the file, in the dir list.
	   There is no "special" purpose for having the directory first.
	   It was merely a choice of the designer. */
	printf("Duplicate file & directory of same name (%s)\n",trav->name);
	if (dir==0){
		/* "name" is a normal file, so lets check next entry
		   and maybe it will be there */
		if (trav->next != NULL){ /* avoid going past end of list */
			if (strcmp(trav->next->name,name) == 0){
				/* entry WAS FOUND after directory entry
				   of same name */
				(*found) = 1;
				return(trav->next);
			}
		}
		/* entry "name" was not found, so return a pointer to
		   where it should be added, if it were going to be added */
		(*found) = 0;
		return(trav);
	}
	else{	/* Looking for a directory, but entry found was a normal file */
		if (last==trav){
			/* This could only happen if searching was not begun
			   at the begining of a directory (ie. at the entry ".")
			*/
			fprintf(stderr,"Directory %s, possibly out of order\n",
				name);
		}
		(*found) = 0;
		return(last); /* Directory should be inserted before the
				 file of the same name */
	}
}

X/* GetNode:  Returns a newly allocated node & initialize it to contain 
   the name & inode passed.  The other entries default as follows:
   Next & Down are NULL. Dir is false(0), Marked is 'n' */

PTRNODE
getnode(name,inode)
char	*name;
ino_t	inode;
{
	PTRNODE e;
	int	i,j;

	e = talloc();
	strcpy(e->name,name);
	e->Marked = 'n';
	e->next = NULL;
	e->down = NULL;
	e->dir = 0;
	/* Initialize all tape back-up names to nulls */
	for(i=0; i < MAXBACKUPS; i++){
		for(j=0; j < MAXTAPNAM; j++)
			e->tapes[i][j] = ' ';
		e->tapes[i][MAXTAPNAM] = '\0'; /* Stick null at end of tape name */
	}
	for(i=1; i < MAXBACKUPS; i++)
		e->inodes[i] = 0;
	e->inodes[0] = inode;

	return(e);
}

X/* LinkNode:  Links the node passed after the pointer passed. */

linknode(e,ptr)
PTRNODE	e,ptr;
{
	e->next = ptr->next;
	ptr->next = e;
}

X/* Adjust:  Standardizes the daily dump tape names. At this point (12/83),
            the tapes are the one-character capitalized alphabetics A thru J.
            If the tape name is a one-character alphabetic, adjust will
            convert it to upper-case.                                    */

char *adjust(tapename)
char 	*tapename;
{
	if ((strlen(tapename) == 1) && (isalpha(*tapename))){
		if (islower(*tapename)) tapename[0] = (toupper(tapename[0]));
	}
	return(tapename);
}

Write_Sys(st_ptr,filsys_fil)
PTRNODE	st_ptr;
char	*filsys_fil;
{
	if ((fp=fopen(filsys_fil,"w")) == NULL){
		fprintf(stderr,"Can't open %s to UPDATE\n",filsys_fil);
		exit(1);
	}
	Recurse_Write(st_ptr);
	fclose(fp);
	return;
}

Recurse_Write(st_ptr)
PTRNODE	st_ptr;
{
	PTRNODE	trav;
	/* This marker marks the end of a directory, in the filsys_fil */

	trav = st_ptr->down;
	while (trav != NULL){
		/* Make an entry for this node */
		makentry(trav);
		if (trav->dir && strcmp(".",trav->name) != 0
				&& strcmp("..",trav->name) != 0){
			/* If this node is a directory, then Recurse it's
			   sub-dirs and write out its nodes */
			Recurse_Write(trav);
		}
		trav = trav->next;
	}
	/* A directory has just been written, so add a marker
	   to mark the end of the directory */
	makentry(marker);
}

X/*============================================================================*/
X/* GetEntry & MakEntry are the only interface between the file 
   /etc/filesontapes and the user program.  If format of file changes
   these two routines must jointly agree on the changed format */

makentry(e)
PTRNODE	e;
{
	int i;
	/* format is: 	inodes, (not converted to ascii)
			tapenames,
			dirflag (byte)
			file name (terminated with null byte)
	*/
	/* write out inode numbers (not converted to ascii) */
	for (i=0; i < MAXBACKUPS; i++)
		fwrite((char*)&(e->inodes[i]),sizeof(e->inodes)/MAXBACKUPS,
			1,fp);
	/* write out tape names (not terminated with null byte) */
	for (i=0; i < MAXBACKUPS; i++)
		fwrite(e->tapes[i],MAXTAPNAM,1,fp);
	/* write out directory flag */
	if (e->dir)
		/* then this entry is a directory */
		fwrite("1",1,1,fp);
	else
		/* this entry is a normal file */
		fwrite("0",1,1,fp);
	/* write file name - including null byte */
	fwrite(e->name,strlen(e->name)+1,1,fp);
	if (sopt){ /* If Keeping Summary information */
		s_totent++; /* total # of entries */
		if (e->dir && strcmp(e->name,".") != 0 && 
			strcmp(e->name,"..") != 0){ /* This is a directory */
			s_dirent++; /* inc # of directory entries */
		}
		/* If "." or ".." entry */
		if (e->dir && (strcmp(e->name,".") == 0 || 
			strcmp(e->name,"..") == 0)){
			s_dotent++; /* inc # of dot or dotdot entries */
		}
		/* If normal file */
		if (e->dir == 0 && strcmp(e->name,"/") != 0) 
			s_filent++; /* inc # of file entries */
		if (e->dir == 0 && strcmp(e->name,"/") == 0)
			s_nomkrs++; /* inc # of markers */
	}

	return;
}

PTRNODE
getentry()
{
	PTRNODE e;
	int	nmread=1,i;
	char 	c,*name;
 
        r_totent++;
	e = getnode("",0); /* get and initialize a node to use */
	/* read inodes */
	for (i=0; i < MAXBACKUPS && nmread > 0; i++)
		nmread = fread((char*)&(e->inodes[i]),
					sizeof(e->inodes[i]),1,fp);
	/* Check for end of file */
	/* Actually, if an incomplete record occured here it would go
	   undetected. Oh well. */
	if (nmread = 0) /* Reached EOF - normal case */
		return(NULL);
	/* read tape names */
	for (i=0; i < MAXBACKUPS; i++){
		fread(e->tapes[i],MAXTAPNAM,1,fp);
		e->tapes[i][MAXTAPNAM] = '\0'; /* Add null byte to end of tape name */
	}
	/* read directory flag */
	fread(&c,1,1,fp);
	switch (c){
	case '0': /* This entry is a normal file */
		e->dir = 0;
		break;
	case '1': /* This entry is a directory */
		e->dir = 1;
		break;
	default: /* Error in file */
		fprintf(stderr,"Error in /etc/filesontapes - bad directory flag %c\n",c);
		break;
	}
	/* Read in file name */
	for (name=e->name; (nmread = fread(&c,1,1,fp)) > 0 && c != '\0'; )
		*name++ = c; /* Read until null byte */
	*name = '\0'; /* Add null byte at end of name */
	/* Just double check to see if unexpected end of file occured */
	if (nmread = 0){
		fprintf(stderr,"INCOMPLETE Record - at EOF\n");
		return((PTRNODE) ERROR);
	}

	/* Return a pointer to the node just read */
	return(e);
}

X/*============================================================================*/

printlist(ptr)
PTRNODE	ptr;
{
	PTRNODE t;
	printf("\nList of nodes starting from %d\n",ptr);
	t = ptr;
	while (t != NULL){
		/* print a short list of just names */
		printf("%s%s",t->name,(t->next==NULL)?"\n\n":",");
		t = t->next;
	}
	printf("\n\n");
}

X/* This routine updates the information kept for each node.
   Information is stacked. (ie. Whenever an update is made the newest
   information is pushed on the front of the stack, pushing all the
   other information back one position.)
   Only MAXBACKUPS backup entries are kept. So any attempts made to
   updates beyond that cause the oldest entry to be lost.  This is
   consistent to stack behavior. */

update(ptr,inode,tapnam)
PTRNODE	ptr;
ino_t	inode;
char	*tapnam;
{
	int i;

	ptr->Marked = 'y';
	for (i=MAXBACKUPS-2; i >= 0; i--)
		strcpy(ptr->tapes[i+1],ptr->tapes[i]);
	strcpy(ptr->tapes[0],tapnam);
	for (i=MAXBACKUPS-2; i >= 0; i--)
		ptr->inodes[i+1] = ptr->inodes[i];
	ptr->inodes[0] = inode;
}


PTRNODE
talloc()
{	char *malloc();
	static PTRNODE heap,retptr;
	static int  count = 0;

	if (count == 0){
		heap = (PTRNODE) malloc(NUMALLOCS * sizeof(NODE));
		count = NUMALLOCS;
	}
	retptr = heap++;
	count--;
	return((PTRNODE) retptr);
}

X/* printree:  This routine recursively prints all or part of the file
   system, begining at a particular node. */

printree(st_ptr,pathname)
PTRNODE	st_ptr;
char	*pathname;
{
register	PTRNODE	trav;
	char	fullname[100];

	trav = st_ptr->down;
	while (trav != NULL){
		/* Print all entries in "current" directory */
		strcpy(fullname,pathname);
		strcat(fullname,trav->name);
		fullprint(fullname,trav);
		if (trav->dir && strcmp(fullname,".") != 0 && 
			strcmp(fullname,"..") != 0){
			/* This entry is a directory, so recursively
			   print all of its entries */
			strcat(fullname,"/"); /* prepare path name */
			printree(trav,fullname);
		}
		trav = trav->next;
	}
}

fullprint(fullname,ptr)
char	*fullname;
PTRNODE	ptr;
{
	int i,j,idx;

	printf("file: %s\t(%d)\n",fullname,ptr);
	/* print out inodes for this entry */
	for (i=0; i < (MAXBACKUPS-1)/3 + 1; i++){ /* for each row */
		for (j=0; j<3; j++){ /* for each column */
			if ((idx=j+i*3) < MAXBACKUPS){ 
				/* only print as many columns as are needed */
				if (j == 0)
					printf("%s",(i==0)?"inodes:\t":"\t");
				printf("(%d) = %5d\t",idx+1,ptr->inodes[idx]);
			}
		}
		printf("\n");
	} /* print out inodes */
	/* print out tape names for this entry */
	for (i=0; i < (MAXBACKUPS-1)/3 + 1; i++){ /* for each row */
		for (j=0; j<3; j++){ /* for each column */
			if ((idx=j+i*3) < MAXBACKUPS){ 
				/* only print as many columns as are needed */
				if (j == 0)
					printf("%s",(i==0)?"tapes:\t":"\t");
				printf("(%d) = %5s\t",idx+1,ptr->tapes[idx]);
			}
		}
		printf("\n");
	} /* print out tape names */
}

printnode(ptr)
PTRNODE	ptr;
{
	int i;

	if (ptr != NULL){
		fullprint(ptr->name,ptr);
		printf("\tdown = %d  next = %d\n",ptr->down,ptr->next);
	}
	else
		printf("Null Node -- can't be printed\n");
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 filtapmgr.c
	/bin/echo -n '	'; /bin/ls -ld filtapmgr.c
fi
-- 
Call-Me:   Pete Cottrell, Univ. of Md. Comp. Sci. Dept.
UUCP:	   {seismo,allegra,brl-bmd}!umcp-cs!petec
CSNet:	   petec@umcp-cs
ARPA:	   petec.umcp-cs@CSNet-Relay