Ultrix-3.1/src/cmd/ltf/scantape.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

#ifndef lint
static	char	*sccsid = "@(#)scantape.c	3.1	(ULTRIX)	12/3/87";
#endif	lint

/**/
/*
 *
 *	File name:
 *
 *		scantape.c
 *
 *	Source file description:
 *
 *		This module "scans" ( ie.  READS )  the input volume
 *		when an  eXtract or Table of contents function has
 *		been requested of the LTF.
 *
 *	Functions:
 *
 *		date_time()	Fills a given 'sdate' string with
 *				the appropriate month, day, and
 *				modification time.
 *
 *		date_year()	Fills a given 'sdate' string with
 *				the appropriate month, day, and
 *				year.
 *
 *		expand_mode()	Expands the uid and gid mode fields
 *		    expandm()	for print out on TABLE or EXTRACT
 *				functions.
 *
 *		scantape()	Top level logic to read the Volume
 *
 *		wbadlab()	Writes out "bad" label data encountered
 *
 *	Usage:
 *
 *		n/a
 *
 *	Compile:
 *
 *	    cc -O -c scantape.c		 <- For Ultrix-32/32m
 *
 *	    cc CFLAGS=-DU11-O scantape.c <- For Ultrix-11
 *
 *
 *	Modification history:
 *	~~~~~~~~~~~~~~~~~~~~
 *
 *	revision			comments
 *	--------	-----------------------------------------------
 *	 01.0		20-Apr-85	Ray Glaser
 *			Create orginal version
 *
 *	 01.1		4-Sep-85	Suzanne Logcher
 *			Add interchange name to TABLE w/o v or V
 *			Change use of position number to table or 
 *			   extract files
 *			Correct use of Noheader3	
 */

/*
 * ->	Local Includes
 */

#include "ltfdefs.h"

/**/
/*
 *
 * Function:
 *
 *	date_time
 *
 * Function Description:
 *
 *	Fills the string 'sdate' with the appropriate
 *	month, day, and modification time.
 *
 * Arguments:
 *
 *	char	*sdate		The string to be filled
 *	long	*sec		Source of the time..
 *
 * Return values:
 *
 *	The string given is date/time filled.
 *
 * Side Effects:
 *
 *	none	
 */
date_time(sdate, sec)
	char	*sdate;
	long	*sec;
{
/*
 * +--> Local Variables
 */

int	century;
long	clock;
char	*ctime();
long	rettime;
int	thisyear;
struct	tm *localtime();
struct	tm *ltime;
long	time();

/*------*\
   Code
\*------*/

rettime = time(&clock);
ltime =	localtime(&rettime);
thisyear = ltime->tm_year;

ltime = localtime(sec);

if (ltime->tm_year == thisyear)
	sprintf(sdate, "%s %2d %02d:%02d", Months[ltime->tm_mon + 1],
		ltime->tm_mday, ltime->tm_hour, ltime->tm_min);
else {
	cp = ctime(sec);
	if (cp[20] == '2' && cp [21] == '0')
	    century = 2000;
	else {
	    if (cp[20] != '1' || cp[21] != '9')
		PERROR "\n%s: %s\n", Progname, BADCENT);
	    century = 1900;
	}
	sprintf(sdate, "%s %2d  %4d", Months[ltime->tm_mon + 1],
		ltime->tm_mday, ltime->tm_year + century);
}
}/*E date_time() */
/**/
/*
 *
 * Function:
 *
 *	date_year
 *
 * Function Description:
 *
 *	Fills the given string with the appropriate month,
 *	day, and year.
 *
 * Arguments:
 *
 *	char	*sdate
 *	char	*century
 *	char	*date
 *
 * Return values:
 *
 *	String is filled with .. etc.. 
 *
 *
 * Side Effects:
 *
 *	none
 *	
 */

date_year(sdate, century, date)
	char	*sdate, *century, *date;
{
/*
 * +--> Local Variables
 */

int	leap;
int	year;
int	yearday;
int	realyear;

/*------*\
   Code
\*------*/

sscanf(date, "%2d%3d", &year, &yearday);
if (*century == '0')
    i = 2000 + year;
else
    if (*century != ' ')
	PERROR "\n%s: %s\n", Progname, BADCENT);
    i = 1900 + year;
	
realyear = i;
leap = i%4 == 0 && i%100 != 0 || i%400 == 0;

for (i= 0; yearday > Days[leap][i]; i++)
	yearday -= Days[leap][i];

sprintf(sdate, "%s %2d  %4d", Months[i], yearday, realyear);

}/*E date_year();
/**/
/*
 *
 * Function:
 *
 *	expand_mode, expandm
 *
 * Function Description:
 *
 *	These siamese functions expand and print the 9 mode
 *	fields of an Ultrix  uid / gid  file status.
 *
 * Arguments:
 *
 *	int	mode	Word containing the  st_mode value
 *
 * Return values:
 *
 *	The ASCII representation of the uid/gid is printed
 *	on stdout.
 *
 * Side Effects:
 *
 *	none	
 */

expand_mode(mode)
int	mode;
{
/*
 * +--> Local Variables
 */

register int **mp;

/*------*\
   Code
\*------*/

for (mp = &M[0]; mp < &M[9]; )
	expandm(*mp++, mode);

}/*E expand_mode() */
/*
 * Sub-function:
 *
 *  expandm		Determines the status of the next mode field.
 *			This function is a sub-function used only for
 *			the above expand_mode function. It was done
 *			this way for ease of implementation.
 */

expandm(choices, mode)
	int	*choices; /* Number of choices for this field */
	int	mode;
{
/*
 * +--> Local Variables
 */
register int	*ch;

/*------*\
   Code
\*------*/

ch = choices;
i = *ch++;
while (--i >= 0 && (mode & *ch++) == 0)
	ch++;

printf("%c", *ch);

}/*E expandm()
/**/
/*
 * Function:
 *
 *	scantape
 *
 * Function Description:
 *
 *	This function is the top level logic to read a volume
 *	for the  TABLE  and  EXTRACT  functions.
 *
 * Arguments:
 *
 *	No direct arguments are passed. However the logic depends
 *	on flag variables set by the main-line logic to take the 
 *	correct actions.
 *
 * Return values:
 *
 *	This function does not return to the caller.  Wether or not
 *	the operation was successful, the function exits to system
 *	control. If an error occurs, a message is output to stderr.
 *
 * Side Effects:
 *
 *	none
 */

scantape()
{
/*
 * ->	Local variables
 */

int	Bufoff;		/* Buffer offset read from HDR2 (51-52) */
char	cat_misc[20];	/* misc message buf */
long	charcnt;	/* Character count of binary files.
			 * A precaution  in case the last character
			 * of a binary file is the same as the
			 * the padding character. */
struct	FILESTAT *fstat;
int	getlen();	/* Declare getlen proc. to get length of F 
			 * or S record */
int	gid;		/* Group id */
int	hlink;		/* Field in HDR2, if set, file has hard link */
int	k;		/* Temporary variable */
int	linkflag;	/* Set if link() is successful */
int	lnk_fseqno;	/* File sequence no. of head link file */
char	lnk_msg[30];	/* Used to output Hard/Symbolic link msgs */
char	lnk_name[MAXPATHLEN+1];/* File name of head link file */
struct	XLINKBUF *lp;
char	pathname[MAXPATHLEN+1]; /* temporary variable */
int	mode;		/* File mode */
long	modtime;	/* Modification time of file. */
int	nbytes;
long	ret;
long	save;
char	sdate[13];	/* Creation-date string */
int	skip;		/* Position number skip counter */
char	sysvol[14];	/* Holds copy of VOL1 L_systemid */
int	tfiletype;	/* True Ultrix file type */
int	uid;		/* User id */
int	wildc = NO;
long	xtractf();

/*------*\
   Code
\*------*/

/***
 *	READ / PROCESS  +-->  VOL1
 ***/

/* Initialize lp just in case */
lp = (struct XLINKBUF *) NULL;

if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
	PERROR "\n%s: %s VOL1 (%s)%c\n", Progname, CANTRL, Magtdev, BELL);
	perror(Magtdev);
	if (i < 0)
	    ceot();
	exit(FAIL);
}
	
L_labid[0] = 0;
sscanf(Labelbuf, "%3c%1d%6c%14c%13c%14c",
	L_labid, &L_labno, L_volid, Dummy, L_systemid, L_ownrid);
strcpy(sysvol, L_systemid);

if (strncmp(L_labid, "VOL",3)) {
	PERROR "\n%s: %s VOL1%c\n", Progname, INVLF, BELL); 
	/*
	 * Go display the bad data and exit.
	 */
	wbadlab();
}
/*
 * First VOLume label number should always be 1.
 */
if (L_labno != 1) {
	PERROR "\n%s: %s VOL1 %s%c\n", Progname, INVLF, INVLNO, BELL);
	wbadlab();
}
/*
 * Pick up, save, & verify the ANSI version number of this volume.
 */
Ansiv = Labelbuf[79];

if (Ansiv != '3' && Ansiv != '4') {
	PERROR "\n%s: %s VOL1 %s-> %c%c\n", Progname, INVLF,
	  UNSAV, Ansiv, BELL);
	wbadlab();
}
/* Save volume id.
 */
strcpy(Volid, L_volid);

/*
 * Display the volume label information if requested.
 *
 */
if (Verbose) {
	PROMPT "\n%s: %s %s %s %c\n",
		Progname, VOLIS, Volid, ANSIV, Ansiv); 
	PROMPT "%s: %s %s\n", Progname, OWNRID, L_ownrid);
}
	/*	*-*  NOTE  *-*
	 *	     ----
	 * If we see an ANSI version 4 tape, we may have to:
	 ***
	 *	READ +-->  VOL2 - VOL9		(if present)
	 *
	 * For both 3/4 we may have:
	 *
	 *	READ +-->  UVL1 - UVL9		(if present)
	 ***/

/**/
/*
 *	SCAN the TAPE -- and do what is required for an
 *
 *		EXTRACT or TABLE  function..
 *
 */
skip = 1;
FOREVER {
    charcnt		= 0L;
    linkflag	= FALSE;
    modtime		= 0L;
    pathname[0]	= 0;

    /*******
     *	READ / PROCESS	+-->  HDR1
     ******/
	if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
	    /*
	     * This is the normal way that the LTF currently exits
	     * from the  forever loop we are inside of. When it 
	     * exhausts the usable data on the input volume, the
	     * above read returns a  <= 0 condition. Normally this
	     * should be due to the fact that it has reached the
	     * set of "double" end of tape marks that signify the
 	     * end of data. Empty files may pose a premature exit,
 	     * but at this time, enough is not yet known about empty
 	     * file handling to determine if this is in fact a
 	     * real possiblity.
 	     */
	    if (i < 0)
		ceot();
    	    if (Seqno && skip <= Seqno) {
	    	PERROR "\n%s: %s %s\n", Progname, CANTFSF, Magtdev);
	    	perror(Magtdev);
	        printf("\n");
	    	exit(FAIL);
	    }
	    printf("\n");
	    exit(SUCCEED);
	}
	sscanf(Labelbuf, "%3s%1d%17s%6s%4d%4d%4d%2d%c%5s %5s%*7c%13s", 
		L_labid, &L_labno, L_filename, L_volid, &L_fsecno, 
		&L_fseqno, &L_gen, &L_genver, &L_crecent, L_credate, 
		L_expirdate, L_systemid);
	if (Ansiv == '3')
	    strcpy(sysvol, L_systemid);

	/* Abort if multi-volume indicator is seen !
	 */
	if (!strcmp(L_labid, "EOV")) {
	/*
	 * As the LTF is not designed to process multi-volume
	 * sets, we must abandon the operation at this point.
	 * ie. There is no more data on the tape. A partial
	 * file may have been written to the output device.
	 * If we were to try to deal with "multi-volume" sets,
	 * logic would be added at this point to switch to
	 * the next volume and continue ...
	 */
		PERROR "\n%s: %s\n\n", Progname, MULTIV1);
		exit(FAIL);
	}
	/*
	 * Set the flag that tells us whether or not this volume
	 * was created by an Ultrix system. Volmo makes sure we
	 * do the announcement and flag set only wonce.
	 */
	if (!Volmo) {
	    if (Verbose)
		PROMPT "%s: %s  %s\n", Progname, IMPIDM, L_systemid);
	}
	if (!strncmp(L_systemid,IMPID,13) && !strncmp(L_systemid, sysvol, 13))
	    Ultrixvol = TRUE;
	else {
	    Ultrixvol = FALSE;
	    Noheader3 = TRUE;
	}
	if (strncmp(L_systemid, sysvol, 13))
	    PERROR "%s: %s %s\n", Progname, IMPIDC, L_systemid);

	if (strcmp(L_labid, "HDR") || L_labno != 1) {
		if (Tape) {
hdr1err:
		    PERROR "\n%s: %s HDR1%c\n", Progname, INVLF, BELL); 
		    wbadlab();
		}/*T if Tape */
		else {
			/*
			 * If i/o device is not a tape AND
			 * we see a dummy tape_mark when we
			 * are looking for HDR1, assume it is
			 * the double tape mark - end of data
			 * on volume condition.
			 */
			if (!tape_mark(Labelbuf))
				goto hdr1err;
			else {
				cat_misc[0] = 0;	
				printf("\n");

				/* Complain about files that were not
				 * found on the volume.
				 */
				for (fstat = F_head; fstat; fstat = fstat->f_next) {
					if (!fstat->f_found) {
					    if (!Seqno)
					    	PERROR "%s: %s %s\n", Progname,NOTONV, fstat->f_src);
					    else
					    	PERROR "%s: %s %s\n", Progname,NOTONP, fstat->f_src);
					    cat_misc[0] = '\n';
					}
				}/*E for fstat ..*/

				fprintf(stderr,"%c",cat_misc[0]);
    	    			if (Seqno && skip <= Seqno) {
	    			    PERROR "\n%s: %s %s\n", Progname, CANTFSF, Magtdev);
	    			    perror(Magtdev);
				    printf("\n");
	    			    exit(FAIL);
	    			}
				exit(SUCCEED);	
			}
		}/*F if tape */
	}/*E if HDR1 */
	/*
 	 *	Lower case the HDR1 File ID string.
 	 *	IF: We  MUST  use the HDR1 string for this files' name.
 	 */
	if (!Ultrixvol || Noheader3 || Ansiv != '4') {
		cp = L_filename;
		while (*cp) {
			*cp = isupper(*cp) ? *cp-'A'+'a' : *cp;
			cp++;
		}
	}
	/**/
	/*******
 	*	READ / PROCESS	+-->  HDR2
 	******/
	if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
		PERROR "\n%s: %s HDR2%c\n", Progname, CANTRL, BELL);
		perror(Progname);
		if (i < 0)
		    ceot();
		exit(FAIL);
	}

	if (Ultrixvol) {
	    sscanf(Labelbuf, "%3s%1d%c%5d%5d%6o%4d%4d%4d%3c%1c%10ld%1d%1d%1d%2d",
		L_labid, &L_labno, &L_recformat, &L_blklen,
		    &L_reclen, &mode, &uid, &gid, &lnk_fseqno,
			Tftypes,Dummy,&charcnt,&Lhdrl, &Leofl, 
			   &hlink, &Bufoff);
	}
	else {
		sscanf(Labelbuf, "%3s%1d%c%5d%5d%35c%2d", L_labid,
		    &L_labno, &L_recformat, &L_blklen, &L_reclen,Dummy, &Bufoff);
		strcpy(Tftypes, "???");
	}
	/*
	 * Make sure we are seeing the correct label at this point.
	 */
	if (strcmp(L_labid, "HDR") || L_labno != 2) {
		PERROR "\n%s: %s HDR2%c\n", Progname, INVLF, BELL); 
		wbadlab();
	}

	if (Func == EXTRACT) {
	    if (L_blklen < MINBLKSIZE || L_blklen > MAXBLKSIZE) {
		PERROR "\n%s: %s %d%s\n", Progname, INVBS, MAXBLKSIZE, BYTES);
		exit(FAIL);
	    }
	    if ((strncmp(Tftypes, "dir", 3)) && (L_recformat == FIXED) && (L_reclen < 1 || L_reclen > MAXRECSIZE)) {
		PERROR "\n%s: %s %d%s\n", Progname, INVRS, MAXRECSIZE, BYTES);
		exit(FAIL);
	    }
	}
	/**/
	/*******
 	*	READ / PROCESS	+-->  HDR3
 	******/
	
	/*	If user doesn't want HDR3, etc. data to be used,
 	*	skip this stuff that gets name from HDR3 - EOF9.
 	*/
	if (!Noheader3 && skip >= Seqno) {
	    if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
		PERROR "\n%s: %s HDR3%c\n", Progname, CANTRL, BELL);
		PERROR "\n%s: %s\n", Progname, TRYNH3);
		perror(Progname);
		if (i < 0)
	    	    ceot();
		exit(FAIL);
	    }

	    Labelbuf[BUFSIZE] = 0;

	    /* For one of our volumes, get the
	     * data we have on this file from HDR3.
	     */

	    sscanf(Labelbuf, "%3s%1d%10ld%10c%20c%36s", L_labid, &L_labno, &modtime, Owner, Hostname, pathname);
	    if (strcmp(L_labid, "HDR") || L_labno != 3) {
		PERROR "\n%s: %s HDR3%c", Progname, INVLF, BELL);
		PERROR "\n%s: %s\n\n", Progname, TRYNH3);
	        wbadlab();		
	    }
	    /* 
	     * If not an ANSI version 4 volume, lower case the
	     * pathname string component by default.
	     */
	    if (Ansiv != '4') {
		cp = pathname;
		while (*cp) {
			*cp = isupper(*cp) ? *cp-'A'+'a' : *cp;
			cp++;
		}
	    }
	    /**/
	    /*******
	    *	READ / PROCESS	+-->  HDR4 thru HDRn
	    ******/

	    if (Lhdrl > 3) {
		char epathname[77];
		int labelno=4;

		for (;labelno <= Lhdrl; labelno++) {
			if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
				PERROR "\n%s: %s HDR%d%c\n", Progname, CANTRL, labelno, BELL);
				perror(Progname);
				if (i < 0)
				    ceot();
				exit(FAIL);
			}
			/*
			 * Extract the label number and extended
			 * pathname characters from this HDRn.
			 */
			sscanf(Labelbuf, "%3s%1d%76s", L_labid, &L_labno, epathname);
			/*
			 * Make sure we are seeing something
			 * that looks like the correct
			 * HDRn label.
			 */
			if (strcmp(L_labid, "HDR") || L_labno != labelno) {
					PERROR "\n%s: %s HDR%d%c", Progname, INVLF, labelno, BELL);
					PERROR "\n%s: %s\n\n", Progname, TRYNH3);
    					wbadlab();		
			}
			strncat(pathname,epathname,76);
		}/*E for ;labelno ..*/
		/*
		 * If there are extended path/file name
		 * characters tucked away in the EOF labels,
		 * extract them and tack them on to the
		 * real path name of the file before
		 * we read the file data. A tedious, but
		 * necessary step.
		 */
		if (Leofl) {
			if (Tape) {
			    rew(1);
			    /*
			     * This should place us at the EOF1
			     * label of the file.
			     */
			    fsf(((L_fseqno - 1) *3) + 2);
			}/*T if (Tape) */
			else {
			    save = ftell(Magtfp);
			    fsf(1);
			}/*F if (Tape) */
			for (labelno =1; labelno <= Leofl; labelno++) {
				if ((i = read(fileno(Magtfp), Labelbuf, BUFSIZE)) <= 0) {
					PERROR "\n%s: %s EOF%d%c\n", Progname, CANTRL, labelno, BELL);
					perror(Progname);
					if (i < 0)
				    	    ceot();
					exit(FAIL);
				}
				/*
			 	 * Extract the label number &
				 * extended pathname characters
				 * from this EOFn.
			 	 */
				sscanf(Labelbuf, "%3s%1d%76s", L_labid, &L_labno, epathname);
				/*
			 	 * Make sure we are seeing
				 * something that looks like
			 	 * an EOFn label.
			 	 */
				if (strcmp(L_labid, "EOF") || L_labno != labelno) {
						PERROR "\n%s: %s EOF%d%c", Progname, INVLF, labelno, BELL);
						PERROR "\n%s: %s\n\n", Progname, TRYNH3);
	    					wbadlab();		
				}
				/* First extended path/file
				 * name characters appear in
				 * EOF3 and can continue thru
				 * the EOF9 label.
				 */
				if(L_labno > 2){
					strncat(pathname,epathname,76);
				}
			}/*E for labelno=1;labelno ..*/
			/*
			 * Now re-position the volume for
			 * reading of the file data after
			 * extracting the extended path/file
			 * name characters from the EOF
			 * label set. We will end up at HDR1
			 * of the file and the xtractf() 
			 * function will take us forward to
			 * the start of the data with a fsf().
			 */
			if (Tape) {
			    rew(1);
			    /*
			     * This should place us at the HDR1
			     * label of the file.
			     */
			    fsf((L_fseqno - 1) *3);
			}/*T if (Tape) */
			else {
			    fseek(Magtfp, save, 0);
			}/*F if (Tape) */
		}/*E if (Leofl) */
	    }/*E if Lhdrl > 3 */
	}/*F if Noheader3 */

	/**/
	if (!Volmo) {
	    if (Verbose && Ultrixvol && !Noheader3) 
		PERROR "%s: %s  %s\n", Progname, VOLCRE, Hostname);
	    Volmo++;
	    printf("\n");
	}
	if (!Noheader3)
    	    strcpy(Name,pathname);
	else {
    	    modtime = 0l;
    	    Owner[0] = NULL;
    	    Hostname[0] = NULL;
    	    /*
    	    * L_filename is 17 characters of the "interchange"
    	    * file name from HDR1.
    	    */
    	    strcpy(Name,L_filename);
    	    strcpy(pathname,L_filename);
	}
	/* Go position to file data, then check if buffer offset
	 * is zero.  If not, read past specified bytes 
	 */
	fsf(1);
	if (Bufoff != 0)
	    if ((i = read(fileno(Magtfp), Labelbuf, Bufoff)) <= 0) {
		PERROR "\n%s: %s %2d %c\n", Progname, CANTBUF, Bufoff, BELL);
		perror(Progname);
		if (i < 0)
		    ceot();
		exit(FAIL);
	    }
	/* If positioning by sequence number and not there yet,
	 * increment skipper and go to start of forever loop
	 */ 
	if (Seqno && skip < Seqno) {
	    fsf(2);
	    skip++;
	    continue;
	}/*T if (Seqno ... */
	skip++;
	/*
	* Numrecs equals 0 if no file arguments are specified.
	* Thus the entire tape must be processed.
	*/
	if (! Numrecs || (fstat = Lookup(Name))) {
		/* Is this a symbolic link ?
		 */
		if (!strcmp(Tftypes,"sym") && Ultrixvol) {

			char	Inbuf[MAXBLKSIZE+1];

			strcpy(lnk_msg,SLINKTO);
			linkflag = YES;

			/* Read the link from input volume
			 * and save it in  lnk_name for later.
			 */
			if ((nbytes = read(fileno(Magtfp), Inbuf, L_blklen)) <= 0) {
				PERROR "\n%s: %s %s%c\n", Progname, CANTRD, Name, BELL);
				if (nbytes < 0)
		    		    ceot();
				exit(FAIL);
			}
			i = getlen(Inbuf);
			if (L_recformat == VARIABLE)
			    strncpy(lnk_name,&Inbuf[4],i);
			else {
			    strncpy(lnk_name,&Inbuf[5],i);
			    while (Inbuf[0] != '3') {
				if ((nbytes = read(fileno(Magtfp), Inbuf, L_blklen)) <= 0) {
				    PERROR "\n%s: %s %s%c\n", Progname, CANTRD, Name, BELL);
				    if (nbytes < 0)
		    			ceot();
				    exit(FAIL);
				}
				j = getlen(Inbuf);
				i += j;
				strncat(lnk_name,&Inbuf[5],j);
			    }/*E while (&Inbuf[0] != '3') */
			}
			lnk_name[i] = 0;
			if (Func != TABLE) {
				unlink(Name);
				if (symlink(lnk_name,Name) < 0) {
			    		PERROR "\n%s: %s -> %s\n    to -> %s%c\n",
					Progname, CANTLF, Name,
					lnk_name, BELL);
			    		perror(Progname);
					printf("\n");
				}
			}/*E if Func != TABLE */
			/* Skip to EOF1
			*/
			fsf(1);
		}/*E if !strcmp(Tfypes, "sym") && Ultrixvol) */
		if ((Func == TABLE) && !linkflag) {
			/* Skip to start of EOF labels
	 	 	 */
			fsf(1);
		}
		/*
		 * Is this file hard linked to another file ?
		 */
		if (lnk_fseqno && Ultrixvol) {
			int found = 0;
			strcpy(lnk_msg,HLINKTO);
			for (lp = X_head; lp; lp = lp->x_next) {
				if (lp->x_fseqno == lnk_fseqno) {
					found++;
					linkflag = YES;
					strcpy(lnk_name,lp->x_pathname);
					break;
				}
			}/*E for lp = X_head ..*/
			if (!found)
				PERROR "\n%s:  %s %s\n%s%c\n", Progname,CANTL1,Name,MHL,BELL);
			if (found && (Func == EXTRACT)) {
				unlink(Name);
				if (link(lnk_name, Name) < 0) {
			    		PERROR "\n%s: %s -> %s\n    to -> %s%c\n", Progname, CANTLF, Name, lnk_name, BELL);
					perror(Progname);
					printf("\n");
			    		linkflag = NO;
				}
				else {
					/* If link found, skip to next
					 * EOF1?
					 */
					fsf(1);
				}
			}/*E if found */
		}/*E if lnk_fseqno ..*/
		if (!lnk_fseqno && hlink && Ultrixvol) {
		    lp = (struct XLINKBUF *) malloc(sizeof(*lp));
		    if (!lp) {
			PERROR "\n%s: %s%c\n", Progname,NOMEM,BELL);
		    	exit(FAIL);
		    }/*E if !lp */

		    /* Save enough information about this file
	 	     * in order to identify it in case there are
	 	     * other files linked to it on the input volume.
	 	     */
		    lp->x_next = X_head; /* Pointers run backward ! */
		    X_head = lp;
		    lp->x_fseqno = L_fseqno;
		    lp->x_fsecno = L_fsecno;
		    lp->x_pathname = (char *) malloc(strlen(Name) + 1);
		    if (!lp) {
			PERROR "\n%s: %s%c\n", Progname,NOMEM,BELL);
		    	exit(FAIL);
		    }/*E if !lp */
		    else
			strcpy(lp->x_pathname, Name);
		}/*T if (!lnk_fseqno && hlink %% Ultrixvol) */
/**/

		if (Func == EXTRACT && Numrecs && fstat->f_flags) {

			if (fstat->f_flags & FUF && fstat->f_flags & DD) {
				PERROR "\n%s: %s\n", Progname, MS1);
				exit(FAIL);
			}
					
			if (fstat->f_flags & FUF) {
				if (L_recformat == VARIABLE)
	    				L_recformat = FUF;
				else {
	    				PERROR "\n%s: %s\n", Progname, MS2);
					exit(FAIL);
				}
			}/*E if fstat->f_flags & FUF */

			if (fstat->f_flags & DD) {
				if (L_recformat == VARIABLE)
	    				L_recformat = DD;
				else {
	    				PERROR "\n%s: %s\n", Progname, MS3);
					exit(FAIL);
				}
			}/*E if fstat->f_flags & DD */
	
		}/*E if (Func == EXTRACT && Numrecs && fstat->f_flags) */
/**/
		if (Func == EXTRACT && !linkflag) {
			Xname[0] = 0;
		 	ret = xtractf(pathname, ! Numrecs ? "" : fstat->f_src, charcnt, Xname);
			if (Xname[0] != 0)
			    strcpy(Name, Xname);
			if (ret >= 0L && !strcmp(L_systemid, IMPID)) {
				if (permission) {
				    chmod(Name, mode);
				    chown(Name, uid, gid);
				}
				if (modtime > 0L) {
					time_t	timep[2];
					timep[0] = time(NULL);
					timep[1] = modtime;
					utime(Name, timep);
				}/*E if modtime > 0L */
			}/*E if (ret >= 0L && ! strcmp(L_systemid, IMPID)) */
			if ( !linkflag  && lp ) {
				if (ret < 0L) {
#if 0
					X_head = lp->x_next;
					free((char*)lp);
#endif
					continue;
				}
				else if (Xname[0] != 0 && !Wildc)
					strcpy(lp->x_pathname, Xname);

			}/*E if !linkflag && lp */

			/* Stop processing any files with
	 	 	 * ret < 0L that haven't been caught
	 	 	 * before this.  e.g., non-head link
	 	 	 * files that were not extracted.
	 	 	 */
			if (ret < 0L)
				continue;
	
		}/*E if (Func == EXTRACT && ! linkflag) */
/**/
		if ((nbytes = read(fileno(Magtfp), Labelbuf, BUFSIZE)) < 0){
			PERROR "\n%s: %s EOF1\n", Progname, CANTRL);
		        ceot();
		}
		sscanf(Labelbuf, "%3s%1d%*50c%6ld", L_labid,
			&L_labno, &L_nblocks);

		if (strcmp(L_labid, "EOF") || L_labno != 1) {
			PERROR "\n%s: %s EOF1\n", Progname, INVLF);
			wbadlab();
		}
		/*
	 	 *	Skip over the rest of the "EOF" label
	 	 *	set ...  for the TIME BEING !!
	 	 */
		fsf(1);	

		if (Func == TABLE) {
			if (Verbose) {
			    /*
			     * If this file is not a directory file,
			     * list it. If it is a directory file and
			     * the user really wants to see it (them),
			     * list the name. Else, directory files
			     * are not listed.
			     */
			    if (strcmp(Tftypes,"dir") || Dverbose) {

				sprintf(cat_misc, "t(%d,%d)",
					L_fseqno, L_fsecno);
				printf("%-7s", cat_misc);
	
				/* If this is not an Ultrix volume.
			 	 */
				if (strcmp(L_systemid, IMPID))
					printf("---------   -/-   ");
				else {
					expand_mode(mode);
					printf("%4d/%-4d%s", uid, gid, Owner);
				}
				if (modtime > 0L)
					date_time(sdate, &modtime);
				else
					date_year(sdate, &L_crecent, L_credate);
		
				printf("%12s", sdate);

				if (!Ultrixvol) {
					sprintf(cat_misc, "%ld(%d)%c", L_nblocks,
			    		L_blklen, L_recformat);
					printf("%11s", cat_misc);

				}/*T if !Ultrixvol */
				else {
					if (strcmp(Tftypes,"dir"))
					    sprintf(cat_misc,"  %04ld bytes  <%s>%c",
				    	    charcnt,Tftypes,L_recformat);

					else 
					     /* Directory files are
					      * always 0000 bytes long,
					      * so don't bother to list
					      * the size.
					      */
					    sprintf(cat_misc,"              <%s>%c",
					    Tftypes,L_recformat);

					printf("%s", cat_misc);

				}/*F if !Ultrixvol */

				/* For loong path names..
			 	 */
				if (strlen(Name) > 12)
					printf("\n       %s\n", Name);
				else
					printf(" %s", Name);
		
			   }/*E if strcmp(Tftypes .. */
			}/*E if (Verbose) */
			else {
			    /*
			     * If this file is not a directory file,
			     * list it. If it is a directory file and
			     * the user really wants to see it (them),
			     * list the name. Else, directory files
			     * are not listed.
			     */
			    if (strcmp(Tftypes,"dir") || Dverbose) {
				printf("t  %s", Name);
				if (!Noheader3)
				    printf("  (%s %s)", INTERCH, L_filename);
			    }
			}
			if (linkflag)
				if (Verbose) 
					printf("\n      %s %s\n", lnk_msg, lnk_name);
				else
					printf ("  %s %s\n", lnk_msg, lnk_name);
			else
			    if (strcmp(Tftypes,"dir") || Dverbose) 
				printf ("\n");
	
		}/*T if (Func == TABLE) */
		else {
			/*
		 	 * Func == EXTRACT
		 	 */
			if (Verbose) {
			    /*
			     * If this file is not a directory file,
			     * list it. If it is a directory file and
			     * the user really wants to see it (them),
			     * list the name. Else, directory files
			     * are not listed.
			     */
			    if (strcmp(Tftypes,"dir") || Dverbose || Dircre) {
				if (linkflag)
					printf("x<%s>%c %04ld byte%c,  %s  %s %s", 
					Tftypes, L_recformat, charcnt, 
					charcnt == 1 ? ' ' : 's', Name, 
					lnk_msg, lnk_name);
				else {
#if 0
	/* If we want to see how many tape blocks, put this back in.
	 */
				printf("x<%s>%c %04ld byte%c, %03ld %d-byte tape block%c  %s\n",
				Tftypes,L_recformat,
				ret, ret == 1 ? ' ' : 's',
				L_nblocks, L_blklen,
				L_nblocks == 1L ? ' ' : 's',
				Name);
#endif
					/* If not a directory file, list
					 * its size in bytes, else not.
					 * Directory files are always
					 * 0000 bytes long and when
					 * using the Dverbose mode, they
					 * tend to cloud the output.
					 */
					if (strcmp(Tftypes,"dir"))
					    printf("x<%s>%c %04ld byte%c,  %s",
					    Tftypes,L_recformat,
					    ret, ret == 1 ? ' ' : 's', Name);
					else
					    printf("x<%s>%c              %s",Tftypes,L_recformat,Name);

				}

				if (Dircre) {
					printf(" %s",DIRCRE);
					Dircre = FALSE;
				}	
				printf("\n");

			    }/*E if strcmp(Tftypes .. */
			}/*T if Verbose (Func = EXTRACT) */

			else {
				/* Func == EXTRACT
			 	 * not verbose..
			 	 */
			    if (strcmp(Tftypes,"dir") || Dverbose || Dircre) {
				printf("x  %s", Name);

				if (linkflag)
					printf("  %s %s\n", lnk_msg, lnk_name);
				else
					printf("\n");
			    }
			}/*F if Verbose (Func = Extract) */

			if ((charcnt != ret) && Ultrixvol && !linkflag) 
				PERROR "\n%s: %s %s\n%s %ld %s %ld%c\n\n",
			  	Progname, BADCNT1, Name, BADCNT2, charcnt, BADCNT3, ret, BELL);

		}/*F if Func == TABLE */
/**/
/* The following logic was added as a result of a QPR on "rdt". 
 * It complained that when only 1 distinct file was given for
 * an extract, the entire tape was searched for all copies.
 * This was true. VMS tape routines & tar do the same thing however.
 * But, this loses if you have a large number of files on tape. 
 * ie. It takes a long time to read 1300 files looking for all copies. 
 * As the user may specify, via wildcards (* and ?), that all
 * copies are desired, the following logic was added to stop
 * the extract on the first instance of the requested file.
 */
	    if (Numrecs) {
		cp = fstat->f_src;
		while (*cp && *cp != '\n') {
	    		if ((*cp == '*') || (*cp == '?')) {
				wildc = YES;
				break;
	    		}
	    		cp++;

		}/*E while *cp ..*/
		if (!wildc) {
			free((char*)fstat->f_src);
			fstat->f_src = (char *) malloc (12);
			if (!fstat->f_src) {
			    PERROR "\n%s: %s%c\n", Progname,NOMEM,BELL);
		    	    exit(FAIL);
			}
	    		strcpy(fstat->f_src,"1extracted1");
	    		fstat->f_numleft =0;
			Numrecs--;
	    		if (!Numrecs) {
    	    			if (Seqno && skip <= Seqno) {
	    			    PERROR "\n%s: %s %s\n", Progname, CANTFSF, Magtdev);
	    			    perror(Magtdev);
				    printf("\n");
	    			    exit(FAIL);
	    			}
				printf("\n");
				exit(SUCCEED);
			}
		}/*E if !wildc */
	    }/*T if (Numrecs) */
	}/*T if (! Numrecs || (!(fstat = Lookup(Name))))*/ 
	else {
	    /* Getting here implies that the user has specified
	     * a list of names to be extracted, or tabled &
	     * the current file on the input volume is not one
	     * of the ones about which the user is concerned.
	     * The fsf (Forward Space File) will skip over the
	     * file and its' ANSI label sets (header & trailer)
	     * and place a pointer at the next HDR1 or the end.
	     */
	    fsf(2);
	}
}/*E FOREVER loop */

}/*E scantape() */
/**/
/*
 *
 * Function:
 *
 *	wbadlab
 *
 * Function Description:
 *
 *	This function saves a lot of repetative code by
 *	outputting the common information for all bad
 *	labels encountered.
 *
 * Arguments:
 *
 *	None
 *
 * Return values:
 *
 *	None, the function always exits to system control.
 *
 * Side Effects:
 *
 *	None
 *	
 */

wbadlab()
{
/*
 * +--> Local Variables
 */

int i;

/*------*\
   Code
\*------*/
	
PERROR "\n%s: %s\n", Progname, INVLD);

for (i=0; i < BUFSIZE+1; i++) {
	if (Labelbuf[i] < ' ' || Labelbuf[i] > '~') Labelbuf[i] = ' ';
}
filter_to_a(Labelbuf, i);
PERROR "%s", Labelbuf);
PERROR "\n%s: %s\n", Progname, EINVLD);
exit(FAIL);

}/*E wbadlabd() */

/**\\**\\**\\**\\**\\**  EOM  scantape.c  **\\**\\**\\**\\**\\*/
/**\\**\\**\\**\\**\\**  EOM  scantape.c  **\\**\\**\\**\\**\\*/