Ultrix-3.1/src/cmd/ltf/putfile.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 = "@(#)putfile.c	3.0	(ULTRIX)	4/21/86";
#endif	lint

/**/
/*
 *
 *	File name:
 *
 *		putfile.c
 *
 *	Source file description:
 *
 *		This file contains the logic to put 
 *		individual files on an output volume for
 *		the Labeled Tape Facility (LTF).	
 *		
 *
 *	Functions:
 *
 *		bflush()	Flushes Fortran Unformatted File
 *				record buffer data to output
 *				volume.
 *
 *		addrtyp()	?_? Used during Fortran Formatted
 *				File output record processing.
 *
 *		append()	Appends (writes) the current
 *				file to the output volume.
 *
 *		process()	The function that actually
 *				writes the ANSI file header
 *				and ANSI file trailer
 *				records on the output volume.
 *
 *		putfile()	Determines the "type" of file
 *				being put on the output volume
 *				and calls function "process" to
 *				cause the file to be output.
 *
 *	Usage:
 *
 *		n/a
 *
 *	Compile:
 *
 *	    cc -O -c putfile.c		<- For Ultrix-32/32m
 *
 *	    cc CFLAGS=-DU11-O putfile.c	<- For Ultrix-11
 *
 *
 *	Modification history:
 *	~~~~~~~~~~~~~~~~~~~~
 *
 *	revision			comments
 *	--------	-----------------------------------------------
 *	  01.0		2-May-85	Ray Glaser
 *			Create original version.	
 *
 *	  01.1		10-Sep-85	Suzanne Logcher
 *			Add logic to detect current century
 *
 *	  01.2		24-Sep-85	Suzanne Logcher
 *			Add logic to treat directory and subdirectory
 *			entries using ../ or ./ as relative pathnames
 *
 *	  01.3		31-Oct-85	Suzanne Logcher
 *			Add logic to create segmented files
 */

/*
 * +--> Local Includes
 */

#include	"ltfdefs.h"
char	*rindex();

/**/
/*
 *
 * Function:
 *
 *	bflush
 *
 * Function Description:
 *
 *	Flushes (writes to output volume) the Fortran
 *	Unformatted File  record buffer.
 *
 * Arguments:
 *
 *	char	*name	filename used when error in write
 *	char	*Inbuf	file writing buffer
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	Data is written to the output volume from the
 *	and the blocks written count is updated.
 */

bflush(name, Inbuf)
	char	*name;	/* filename */
	char	*Inbuf; /* File writing buffer */
{
if (Rb - Bb > 0) {
	while (Rb < Bb + Blocksize)
		*Rb++ = PAD;

	if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    perror(Magtdev);
	    ceot();
	}
	Blocks++;
}
}/*E bflush() */
/**/
/*
 *
 * Function:
 *
 *	addrtyp
 *
 * Function Description:
 *
 *	Used during output to a volume for Fortran
 *	Unformatted File proccessing   ?_?
 *
 * Arguments:
 *
 *	char	*inch	?_?
 *	char	*typ	?_?
 *
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	not known at this time	
 */

addrtyp(inch, typ)
	char	*inch;
	char	*typ;
{
inch[4] = typ[0];
inch[5] = typ[1];

}/*E addrtyp() */
/**/
/*
 *
 * Function:
 *
 *	append
 *
 * Function Description:
 *
 *	Appends (writes) the given file to the output volume.
 *
 * Arguments:
 *
 *	char	*name	filename
 *	FILE	*fp	Pointer to file to output
 *	int	max	Maximum line length	
 *	int	type	Type of ANSI tape file being output
 *	int	tftype	True file type of file being output
 *	char	*Inbuf	File writing buffer
 *
 * Return values:
 *
 *	Returns a LONG count of number of blocks output.
 *
 *	Returns -1  and  -2  as error indicators.
 *
 * Side Effects:
 *
 *	
 */

long append(name, fp, type, tftype, max, Inbuf)
	char	*name;	/* filename */
	FILE	*fp;
	int	type, tftype, max;
	char	*Inbuf;	/* File writing buffer */
{
/*
 * +--> Local Variables
 */

int	done;		/* Boolean used for segmented records */
int	fillbuf;	/* Integer of amount of data to read to fill 
			 * line buffer to Blocksize */
char	*index();	/* subroutine to find first char in string */
int	length;		/* Length of line */
char	line[MAXBLKWRT+1];	/* Line read in from file to output */
int	midway;		/* Boolean used for segmented records */
int	notfini;	/* Boolean used for TEXT loop */
char	*p;		/* Pointer to the line input buffer */
char	*q;		/* Pointer to the line input buffer */
int	used = 0;	/* Number of characters used in Buffer for 
			 * segmented records */

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

Blocks = 0L;
p = Inbuf;

/*------*\
	TEXT FILE
\*------*/

if (type == TEXT) {
	    notfini = TRUE;
	    midway = FALSE;
	    q = line;
	    fillbuf = Blocksize;
	    while (notfini != EOF) {
		if (tftype != SYMLNK || Nosym) {
		    if ((i = read(fileno(fp), q, fillbuf)) <= 0)
			if (i < 0) {
			    PERROR "\n%s: %s %s\n", Progname, CANTRD, name);
			    perror(Progname);
			    exit(FAIL);
			}
			else
			    break;
		    q[i] = '\0';
		}
		else
		    strcpy(line, Inbuf);

		q = line;
		done = FALSE;
		while (done == FALSE) {
		    if (cp = index(q, '\n'))
			*cp = '\0';
		    else
			if (Format == VARIABLE)
			    break;

		    length = strlen(q);
		    if (length > max)
			return((long)(-1));

		    if (Format == VARIABLE) {
			if (&p[length+4] > &Inbuf[Blocksize]) {
		    	    while (p < &Inbuf[Blocksize])
				*p++ = PAD;
			    if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    			PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    			perror(Magtdev);
	    			ceot();
			    }
		    	    p = Inbuf;
		    	    Blocks++;
	        	}
			sprintf(p, "%04d%s", length+4, q);
			p = &p[length+4];
			q = ++cp;
		    }/*T if (Format == VARIABLE) */
		    else {
			if (&p[length+5] <= &Inbuf[Blocksize]) {
			    if (cp) {
				if (midway == FALSE)
				    sprintf(p, "%01d%04d%s", 0, length+5, q);
				else {
				    sprintf(p, "%01d%04d%s", 3, length+5, q);
				    midway = FALSE;
				}/*F if (midway == FALSE) */
				used += length + 5;
				p = &p[length+5];
				q = ++cp;
			    }/*T if (cp) */
			    else {
				strcpy(line, q);
				q = &line[length];
				fillbuf = Blocksize - length;
				done = TRUE;
			    }/*F if (cp) */
			}/* T if (&p[length+5] <= ... */
			else {
			    if (used + 5 < Blocksize) {
				if (midway == FALSE) {
				    sprintf(p, "%01d%04d%s", 1, Blocksize-used, q);
				    midway = TRUE;
				}/*T if (midway == FALSE) */
				else
				    sprintf(p, "%01d%04d%s", 2, Blocksize-used, q);
				q = &q[Blocksize-used-5];
				length -= Blocksize-used-5;
			    }/* T if (used + 5 < Blocksize) */ 
			    else
				while (p < &Inbuf[Blocksize])
				    *p++ = PAD;
			    used = 0;
			    if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    			PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    			perror(Magtdev);
	    			ceot();
			    }
			    p = Inbuf;
			    Blocks++;
			    if (cp)
				q[length] = '\n';
			    if (strlen(q) == 0) {
				q = line;
				fillbuf = Blocksize;
				done = TRUE;
			    }
		        }/*F if (&p[length+5] <= ... */
		    }/*F if (Format == VARIABLE) */
	    	}/*E while (done == FALSE) */
		if (tftype == SYMLNK && !Nosym)
		    notfini = EOF;
		if (Format == VARIABLE) {
		    length = strlen(q);
		    strcpy(line, q);
		    q = &line[length];
		    fillbuf = Blocksize - length;
		}
	    }/*E while (notfini != EOF */
	    if (p != Inbuf) {
		while (p < &Inbuf[Blocksize])
	    	    *p++ = PAD;
		if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    	    PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    	    perror(Magtdev);
	    	    ceot();
		}
		Blocks++;
	    }
}/*T if type == TEXT */


/*------*\
	BINARY FILE
\*------*/

if (type == BINARY) {
	while ((length = read(fileno(fp), p=Inbuf, Blocksize)) > 0) {
		if (length < Blocksize) {
			p = &p[length];
			while (p < &Inbuf[Blocksize])
				*p++ = PAD;
		}
		if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    	    PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    	    perror(Magtdev);
	    	    ceot();
		}

		p = Inbuf;
		Blocks++;

    	}/*E while length = read ..*/
	if (length < 0) {
	    PERROR "\n%s: %s %s\n", Progname, CANTRD, name);
	    perror(Progname);
	    exit(FAIL);
	}
}/*T if (type == BINARY) */


/*------*\
	COUNTED RECORD FILE 
\*------*/

if (type == COUNTED) {
    while ( fread( &length, sizeof (short), 1, fp)) {
	if ( length == -1 || p+length+4 > &Inbuf[Blocksize]) {
		while ( p < &Inbuf[Blocksize])
			*p++ = PAD;

		if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
	    	    PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
	    	    perror(Magtdev);
	    	    ceot();
		}

		p = Inbuf;
		Blocks++;
	}/*E if length == -1 ..*/

	sprintf( p, "%04d", length+4);
	fread( p+4, 2, (length+1)/2, fp);

	/*
	 * Fudge for COUNTED records always beginning
	 * on a word boundary in file
	 */
	p += length+4;
    }/*E while fread ..*/
		
    if (p != Inbuf) {
	while (p < &Inbuf[Blocksize])
		*p++ = PAD;

	if (write(fileno(Magtfp), Inbuf, Blocksize) != Blocksize) {
    	    PERROR "\n%s: %s %s\n", Progname, ERRWRF, name);
    	    perror(Magtdev);
    	    ceot();
	}
	Blocks++;

    }/*E if p != Inbuf */
}/*T if type == counted */


/*------*\
	FORTRAN UNFORMATTED FILE
\*------*/

if (type == FUF) {
	/*
	 * +--> Local Variables
	 */
	long	bytcnt;
	int	irec;
	int	lastbit;
	int	nrec;
	long	nbytes, nbytesl;
	char	rectype;
	int	res, resl;
	char	*rp;

	/*
	 * Initialize buffer pointers
	 */
	Bb = Inbuf;
	Rb = Bb;
	rp = Rb + RECOFF;

	/*
	 * Loop over all records in the file.  The unformatted
	 * format for f77 is as follows: each record is
	 * followed and preceded by a long int which contains
	 * the byte count of the record excluding the 2 long
	 * integers.
	 */
	while ((res = read(fileno(fp), &nbytes, sizeof(long))) > 0) {
		if (nbytes > 0L) {
			bytcnt = nbytes;
			nrec = ((int)nbytes-1) / MAXREC6;
			irec = nrec;

			/*
			 * if record is greater than MAXREC6,
			 * then output it in chunks of MAXREC6
			 * first.
			 */
			 while (irec--) {
				if (Rb - Bb + MAXRECFUF > Blocksize) {
					bflush(name, Inbuf);
					Rb = Bb;
					rp = Rb + RECOFF;
				}
				/*
			 	* Read record in chunks of MAXREC6
			 	*/
				res = read(fileno(fp), rp, MAXREC6);
				if (res < 0) {
			    	PERROR "\n%s: %s%c\n\n",
					Progname, EOFINM, BELL);
			    	return((long)(-1));
				}
				if (res != MAXREC6) {
				    PERROR "%\n%s: %s%c\n\n",
					Progname, WRLINM, BELL);
				return((long)(-1));
				}

				/*
				 * Set up the record type.  Note:
				 * to get here the record must
				 * be > MAXREC6 and it must be
				 * split.  Thus there must be a
				 * FIRST record and all other
				 * records must be MIDDLE records.
				 * There could be a LAST record
				 * if the record was an exact
				 * multiple of MAXREC6.
				 */
				 if (irec == nrec - 1)
					rectype = FIRST;
				else if (bytcnt > MAXREC6)
					rectype = MIDDLE;
				else
					rectype = LAST;

				/*
				 * Now output chunk of record
				 * on volume as VARIABLE format
				 */
				resl = res + RECOFF;

				sprintf(Rb, "%04d", resl);
				addrtyp(Rb, &rectype);

				bytcnt -= res;
				Rb += resl;
				rp = Rb + RECOFF;

			}/*E While irec-- */

			/*
			 * Output final part (unless exact
			 * multiple of MAXREC6) of record or
			 * all of it if record is < MAXREC6.
			 */
			 if (bytcnt > 0) {
				lastbit = bytcnt;

				if (Rb - Bb + lastbit + RECOFF > Blocksize) {
					bflush(name, Inbuf);
					Rb = Bb;
					rp = Rb + RECOFF;
				}

				res = read(fileno(fp), rp, lastbit);

				if (res < 0) {
			    	    PERROR "\n%s: %s%c\n\n",
					Progname, EOFINM, BELL);
				    return((long)(-1));
				}
				if (res != lastbit) {
				    PERROR "%\n%s: %s%c\n\n",
					Progname, WRLINM, BELL);
				    return((long)(-1));
				}

				if (lastbit == nbytes)
					rectype = ALL;
				else
					rectype = LAST;

				resl = res + RECOFF;

				sprintf(Rb, "%04d", resl);
				addrtyp(Rb, &rectype);

				Rb += resl;
				rp = Rb + RECOFF;

			}/*E if bycnt > 0 */

		}/*T if nbytes > 0L */
		else {
			if (Rb - Bb + RECOFF > Blocksize) {
				bflush(name, Inbuf);
				Rb = Bb;
				rp = Rb + RECOFF;
			}

			sprintf(Rb, "%04d", RECOFF);
			rectype = ALL;
			addrtyp(Rb, &rectype);

			Rb += RECOFF;
			rp = Rb + RECOFF;

		}/*F if nbytes > 0L */

		res = read(fileno(fp), &nbytesl, sizeof(long));

		if (res < 0) {
			PERROR "\n%s: %s%c\n\n",
				Progname, EOFINM, BELL);
			return((long)(-1));
		}
		if (nbytesl != nbytes) {
			PERROR "\n%s: %s%c\n\n",
			    Progname, BFRCNE, BELL);
			return((long)(-1));
		}
	
	}/*E while res= read ..*/
	if (res < 0) {
	    PERROR "\n%s: %s %s\n", Progname, CANTRD, name);
	    perror(Progname);
	    exit(FAIL);
	}

	bflush(name, Inbuf);

}/*E if type == FUF */

/*
 *	Return to the caller the number of blocks
 *	that were output to the volume.
 */
return(Blocks);


}/*E append()*/
/**/
/*
 *
 * Function:
 *
 *	process
 *
 * Function Description:
 *
 *	This functions writes the ANSI file header labels,
 *	file data, and ANSI file trailer labels to the
 *	output volume.
 *
 * Arguments:
 *
 *	char	*reallong;	Contains the full pathname & file name
 *	char	*longname;	Contains the pathname & file name
 *	char	*shortname;	File name only
 *	int	type;		Ultrix disk file type
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	If the function encounters an error condition during
 *	the output of the file, a message is output to 
 *	"stderr" and the function exits to system control.	
 */

process(reallong, longname, shortname, type)
	char	*reallong;	/* Full path name_+_filename */
	char	*longname;	/* Relative path name_+_filename */
	char	*shortname;	/* Contains the file name only */
	int	type;		/* Type of Ultrix disk file */
{
/*
 * +--> Local Variables
 */
char	ansift;		/* Save ANSI file type character from HDR2
			 * for later verbose messge output.
			 */
int	ch;		/* Character returned from getc to check line 
			 * size for TEXT files */
char	crecent;	/* Creation date century */
char	*ctime();	/* Declare ctime routine */
char	dummy[MAXPATHLEN+1]; /* dummy variable */
int	found = 0;	/* a head link is not on tape yet */
FILE	*fp;
int	hlink = 0;	/* Flag set for field in HDR2 if hard link */
char	Inbuf[MAXBLKWRT+1]; /* Buffer for writing files */
char	interchange[18];	/* Interchange name */
int	length;		/* size of complete filename */
int	linkflag = NO;	/* Used to trigger output linked files 
			 * message. Set to YES if the current file 
			 * is linked to a file that has already 
			 * been appended to the output volume.
			 */
int	lnkseq = 0;	/* Field in HDR2 for file sequence number that
			 * a hard link points to */
struct	tm *localtime();
struct	ALINKBUF *lp;	/* Node for hard link files */
struct	tm *ltime;
int	max;		/* maximum line length */
int	nlinks;		/* number of links to file */
int	otype = 0;	/* If outputting sym link file, save SYMLNK 
			 * file type to check in append routine */
char	pathname[MAXPATHLEN+1]; /* path name */
int	version = 1;	/* version number (1 is default) */


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

if (strlen(longname) >= MAXPATHLEN) {
	PERROR "\n%s: %s %s%c\n\n",
		Progname, FNTL, longname, BELL);
	/*
	 * A string that long is probably fatal...
	 */
	exit(FAIL);
}
strcpy(dummy, longname);

pathname[0] = '\0';
if (cp = rindex(dummy, '/')) {
	*++cp = '\0';

/*
 * ?_? NOTE:	When we  get to putting loong file names on
 *		the output volume.. This logic will need to
 *		be updated...
 */
	if (strlen(dummy) >= MAXPATHLEN) {
		PERROR "\n%s: %s %s%c\n\n",
			Progname, FNTL, longname, BELL);
		/*
	 	* A string that long is probably fatal...
	 	*/
		exit(FAIL);
	}
	strcpy(pathname, dummy);
}
/*
 * Make the ANSI interchange file name
 */ 
strcpy(dummy,shortname);
j = 0;
if (!(filter_to_a(dummy,REPORT_ERRORS)))
    if (Warning) {
	PERROR "\n%s: %s %s", Progname, NONAFN, shortname);
	PERROR "\n%s: %s %s", Progname, INVVID2, dummy);
	j++;
    }
if (strlen(dummy) > 17) {
    /*
     * If user has requested warnings, tell user file name
     * is too long for HDR1 and that it will be truncated.
     */
    dummy[17] = '\0';
    if (Warning) {
	PROMPT "\n%s: %s %s%c", Progname, FNTL, shortname, BELL);
	j++;
    }
}
if ((j > 0) && Warning)
    PERROR "\n%s: %s\n\n", Progname, FILENNG);

strcpy(interchange, dummy);
interchange[17] = '\0';
if ((fp = fopen(reallong, "r")) == NULL) {
	PERROR "\n%s: %s %s%c\n\n", Progname, CANTOPEN, reallong, BELL);
	perror(Progname);
	exit(FAIL);
}
nlinks = Inode.st_nlink;

/*
 * Get file owner's name string for HDR3.
 */
if (getpw(Inode.st_uid,Name)) {
	PERROR "\n%s: %s %d%c\n", Progname, CANTFPW, Inode.st_uid, BELL);
	strcpy(Name, "          ");
}
/*
 * Owner id field in HDR3 label is maximum of 10 characters
 */
for (j=0; j < 11 && Name[j] && Name[j] != ':'; j++)
	Owner[j] = Name[j];

Owner[j] = NULL;

cp = ctime(&Inode.st_mtime);
if (cp[20] == '2' && cp [21] == '0')
    crecent = '0';
else
    crecent = ' ';
ltime = localtime(&Inode.st_mtime);
Blocks = 0L;
max = 0;

if (type == TEXT || type == SYMLNK && Nosym) {
	/* 
	 * If we find a really long line in the text,
	 * we should output it as a spanned/segmented 
	 * record rather variable length record.
	 *
	 * Find length of longest line.
	 */
	Format = 0;
	ch = TRUE;
	while (ch != EOF) {
	    length = 0;
	    while ((ch = getc(fp)) != EOF && ch != '\n')
		length++;
	    if (length > max)
		max = length;
	}/*E while (ch) */
	fclose(fp);
	if ((fp = fopen(reallong, "r")) == NULL) {
	    PERROR "\n%s: %s %s%c\n\n", Progname, CANTOPEN, reallong, BELL);
	    exit(FAIL);
	}
	if (max + 5 > Reclength && Reclength != MAXRECSIZE) {
	    PERROR "\n%s: %s %s%c\n\n", Progname, RECLTS, longname, BELL);
	    return;
	}/*T if (max + 5 > Reclength ... */
	else
	    if (max + 5 > Reclength || max + 5 > Blocksize) {
		Format = SEGMENT;
		Maxrec = max;
	    }/*T if (max + 5 ... */
	    else {
		Format = VARIABLE;
		Maxrec = max + 4;
	    }/*F if (max + 5 ... */
}/*E if (type == TEXT || type == SYMLNK && Nosym) */

fseek(fp, 0L, 0);

/****\
 *	Write  HDR1  on volume..
 ****/
sprintf(Dummy,
	"HDR1%-17.17s%-6.6s%04d%04d%04d%02d%c%02d%03d %02d%03d %06d%-13.13s%7.7s",
	interchange, Volid, Fsecno, Fseqno,
	(version-1) / 100 + 1, (version-1) % 100,
	crecent, ltime->tm_year, ltime->tm_yday+1, 99, 366, 0,
	type == FUF ? "DECFILE11A" : IMPID, Spaces);
if (Ansiv != '4') 
	filter_to_a(Dummy,IGNORE_ERRORS);

if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    perror(Magtdev);
    ceot();
}

if (type == FUF)
	max = MAXREC4;


/****\
 *	Write  HDR2  on volume..
 ****/

/*
 * Directories always have a link count > 1 ...
 * So, we don't bother to enter them in the linked files list.
 * Also, can't hard link directories ...
 */
if (type != DIRECT && nlinks > 1 && !Nosym) {
    hlink = 1;
    /*	If this file is linked to another,
     *	find the ANSI file sequence number that it
     *	is linked to.
     */
    for (lp = A_head; lp; lp = lp->a_next)
	if (lp->a_inum == Inode.st_ino && lp->a_dev == Inode.st_dev) {
	    found++;
	    break;
	}

    /* If we found the file that this file is linked to in our list,
     * get it's file sequence number and set linkflag.
     */
    if (found) {
	lnkseq = lp->a_fseqno;
	linkflag = YES;
    }
    else {
	/* Else, enter info into a node for a possible reference
	 */
	lp = (struct ALINKBUF *) malloc(sizeof(*lp));
	if (lp == NULL) {
	    PERROR "\n%s: %s%c%c\n\n", Progname, NOMEM, BELL, BELL);
	    exit(FAIL);
	}/*T if lp == NULL */
	else {
	    lp->a_next = A_head;
	    A_head = lp;
	    lp->a_inum = Inode.st_ino;
	    lp->a_dev = Inode.st_dev;
	    lp->a_fsecno = Fsecno;
	    lp->a_fseqno = Fseqno;
	    lp->a_pathname = (char *) malloc(strlen(longname) + 1);
	    if (lp == NULL) {
	        PERROR "\n%s: %s%c%c\n\n", Progname, NOMEM, BELL, BELL);
	        exit(FAIL);
	    }/*T if lp == NULL */
	    else {
		strcpy(lp->a_pathname, longname);
	    }
	    lnkseq = 0;
	}/*F if lp == NULL */
    }/*F if (found) */
}/*E if (type != DIRECT && nlinks > 1) */

/*
 * Perform any special functions required for symbolic links
 */
if (type == SYMLNK) {
	otype = type;
	type = TEXT;
	if (!Nosym) {
	    /*
	     * Read the link to find out what
	     * it points to.
	     */
	    dummy[0] = 0;
	    if ((ch = readlink(reallong, dummy, MAXNAMLEN)) < 0) {
		PERROR "\n%s: %s %s\n",Progname, CANTRSL, reallong);
		perror(Progname);
	    }
	    else
		dummy[ch] = '\0';
	    linkflag = YES;
	    /*
 	     * For Symbolic links, the file data is the full 
 	     * path name of the file pointed to by the 
	     * symbolic link.
 	     */
	    strcpy(Inbuf,dummy);
	    max = strlen(Inbuf);
	    Inbuf[max] = '\n';
	    Inbuf[max+1] = '\0';
	    if (max + 5 > Reclength || max + 5 > Blocksize || max + 5 > MAXRECSIZE) {
		Format = SEGMENT;
		Maxrec = max;
	    }
	    else {
		Format = VARIABLE;
		Maxrec = max + 4;
	    }
	}/*T if (!Nosym) */ 
}/*T if (type == SYMLNK) */

/******
 *
 * Calculate the number of HDR / EOF  labels that will be needed
 * to contain the full path/file name.
 *
 */

length = (strlen(longname)) -1;

Lhdrl = 3;
Leofl = 0;

length -= 36;
/*
 * Determine the number of HDR labels needed.
 */
while ((length > 0) && (Lhdrl !=9)) {
	length -= 76;
	Lhdrl++;
}
/*
 * Determine the number of EOF labels needed.
 */
if (length > 0) {
	Leofl = 2;
	while ((length >0) && (Leofl != 9)) {
		length -= 76;
		Leofl++;
	}
}
/* Set the format of the record if it is not a TEXT file.  If it is,
 * it has already been set. 
 */
if (type == FUF || type == COUNTED)
    Format = VARIABLE;
else
    if (type != TEXT)
	Format = FIXED;
if (type == FUF)
    Maxrec = max + 4;
else
    if (type != TEXT)
	Maxrec = Reclength;

sprintf(Dummy,
    "HDR2%c%05d%05d%06o%04d%04d%04d%3.3s%c%010ld%1.1d%1.1d%1.1d00%-28.28s",
	Format, Blocksize, Maxrec,
	Inode.st_mode & 0177777,
	Inode.st_uid, Inode.st_gid,
	lnkseq, Tftypes,
	(type == BINARY) ? 'M' : Carriage,
	(type == SYMLNK && !Nosym) ? max : Inode.st_size, 
	Lhdrl, Leofl, hlink, Spaces);
/*
 * Save ANSI file type for verbose messgae use.
 */
ansift = Dummy[4];

if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    perror(Magtdev);
    ceot();
}

if (!Noheader3) {
    /****\
     *	Write  HDR3  on volume..
     ****/
    sprintf(Dummy, "HDR3%010ld%-10.10s%-20.20s%-36.36s",
	Inode.st_mtime,
	Owner,
	Hostname,
	longname);

    if (Ansiv != '4') 
	filter_to_a(Dummy,IGNORE_ERRORS);

    if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
        PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
        perror(Magtdev);
        ceot();
    }

    /****\
     *	Write additional file header labels (HDR4 - HDRn)
     *	to contain the remaining portions of very long
     *	path/file names.
     ****/

    if (Lhdrl > 3) {
	int i;

	for (i=4,j=36; i<=Lhdrl; i++,j += 76) {

		sprintf(Dummy, "HDR%1.1d%-76.76s", i,&longname[j]);	

		if (Ansiv != '4') 
			filter_to_a(Dummy,IGNORE_ERRORS);

		if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
		    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
		    perror(Magtdev);
		    ceot();
		}
	}	
    }
}/* T !Noheader3 */

/****\
 *	Write an end-of-file mark on tape that separates 
 *	the file header labels from the data ....
 ****/

weof();

/****\
 *	Write the actual file data on the output volume.
 ****/

if ((Blocks = append(longname, fp, type, otype, max, Inbuf)) < 0L)
    if (Blocks == (long)(-1)) {
	PERROR "\n%s: %s %s%c\n\n", Progname, ERRWRF, longname, BELL);
	ceot();
    }

if (fclose(fp) < 0) {
    PERROR "\n%s: %s %s\n", Progname, CANTCLS, longname);
    perror(longname);
    exit(FAIL);
}
/* If outputting symlnk, change type from TEST back to SYMLNK
 */
if (otype)
    type = otype;
/****\
 *	Write an end-of-file mark on tape
 *	Separates the data from the file trailer labels...
 ****/

weof();

/****\
 *	Write  EOF1  to output volume..
 ****/

sprintf(Dummy,
	"EOF1%-17.17s%-6.6s%04d%04d%04d%02d%c%02d%03d %02d%03d %06ld%-13.13s%7.7s",
	interchange, Volid, Fsecno, Fseqno,
	(version-1) / 100 + 1, (version-1) % 100,
	crecent, ltime->tm_year, ltime->tm_yday+1, 99, 366, Blocks,
	type == FUF ? "DECFILE11A" : IMPID, Spaces);
if (Ansiv != '4') 
	filter_to_a(Dummy,IGNORE_ERRORS);

if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    perror(Magtdev);
    ceot();
}

/****\
 *	Write  EOF2  to output volume..
 ****/

sprintf(Dummy,
    "EOF2%c%05d%05d%06o%04d%04d%04d%3.3s%c%010ld%1.1d%1.1d%1.1d00%-28.28s",
	Format, Blocksize, Maxrec,
	Inode.st_mode & 0177777,
	Inode.st_uid, Inode.st_gid,
	lnkseq, Tftypes,
	(type == BINARY) ? 'M' : Carriage,
	(type == SYMLNK && !Nosym) ? max : Inode.st_size, 
	Lhdrl, Leofl, hlink, Spaces);

if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    perror(Magtdev);
    ceot();
}
if (!Noheader3) {
    /*
     *	Write  EOF3 - EOFn  to output volume.
     *
     *	EOF3 thru EOF'n'  contain the remaining characters of a
     *	very long path/file name. If no characters need be stored
     *	in the EOF labels for a path/file name, at a minimum we
     *	output the same number of trailer lables as header labels
     *	in order to keep things tidy.
     */
    if (Leofl) {
	int i;

	for (i=3,j=492; i<= Leofl; i++,j += 76) {
		sprintf(Dummy, "EOF%1.1d%-76.76s", i,&longname[j]);	
		if (Ansiv != '4') 
			filter_to_a(Dummy,IGNORE_ERRORS);
		if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    		    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    		    perror(Magtdev);
    		    ceot();
		}
	}
    }/* if (Leofl) */
    /* 
     * If Leofl = 0, no EOF lables are required to hold path/file
     * name characters. Therefore, we output as many space filled EOFx
     * lables as we need to in order to have the same number of EOF
     * trailer labesl as HDR header labels to keep ANSI happy.  If some 
     * number of EOF labels were required for path/file characters,
     * they will have been output by the code above. If this does not
     * result in the same number of EOF labels as HDR labels, write out
     * the required number of EOF labels.
     */

    if (Leofl < Lhdrl) {
	/*       \----> By accident, all HDR and all EOF labels may
 	 *		have been needed, and have been output, to
	 *		contain path/file name characters. Therefor,
	 *		the following code has no function. The case
	 *		referred to as an accident is one where the
	 *		path/file name is in the order of 949 to
	 *		1024 characters in length.
	 */ 
	if (!Leofl) /* If no EOF labels were required for path/file
		     * characters, begin with EOF3, and continue
		     * with blank EOF lables until we output as many
		     * EOF labesl as HDR labels.
		     */
		j = 3;	
	else
		j = Leofl + 1; /* If padding out EOF labels to the
				* same number of HDR labels  and
				* some EOF labels have been used for
				* path/file name characters, start with
				* the next sequential label number.
				*/
	while (j <= Lhdrl) {
		sprintf(Dummy, "EOF%1.1d%-76.76s", j++,Spaces);	
		if (write(fileno(Magtfp), Dummy, BUFSIZE) != BUFSIZE) {
    		    PERROR "\n%s: %s %s\n", Progname, ERRWRF, longname);
    		    perror(Magtdev);
    		    ceot();
		}
	}
    }/* if (Leofl < Lhdrl)*/
}/* T !Noheader3 */

/*****\
 *	Write tape mark to separate us from next label group
 *	if we are going to output more files.
 *****/

weof();

term_string(shortname,DELNL,TRUNCATE);

if (Verbose) {
	printf("c<%s>%c  %s%s",Tftypes, ansift, pathname, shortname);
#if 0
	/* If we decide we care about tape blocks, put
	 * this logic back in. -else- Just announce the
	 * byte count.
	 */
	if (Blocks == 1L)
		printf(",\t001%s   ", TAPEB);
	else
		printf(",\t%03ld%s  ", Blocks, TAPEBS);
#endif
	if (Inode.st_size == 1L || (type == SYMLNK && !Nosym && max == 1))
		printf(",\t0001%s   ", BYTE);
	else
	    if (type == SYMLNK && !Nosym)
		printf(",\t%04d%s  ", max, BYTES);
	    else
		printf(",\t%04ld%s  ", Inode.st_size, BYTES);
	if (linkflag || type == SYMLNK) {
		if (strlen(longname) > 19 || strlen(dummy) > 19) 
			printf("\n       ");
		if (type == SYMLNK)
			printf("%s %s", SLINKTO, dummy);
		else 
			printf("%s %s", HLINKTO, lp->a_pathname);
	}
	printf("\n");
}/*T if Verbose */
else
	printf("c  %s%s\n", pathname, shortname);

/*	Increment the ANSI file sequence number for the next
 *	file to go out (if any).
 */
Fseqno++;

}/*E process() */
/**/
/*
 *
 * Function:
 *
 *	putfile
 *
 * Function Description:
 *
 *	This function determines the Ultrix disk file type
 *	(if required) and calls function "process" to output
 *	a file to the volume.
 *
 * Arguments:
 *
 *	char	*longname	Full pathname_+_filename
 *	char	*shortname	File name only
 *--
 * Accesses Global variable:
 *--
 *	int	Dfiletype	Default/designated Ultrix
 *				disk file type.
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	If the user has specified a filename that is in fact
 *	a directory -AND- for that argument the user has
 *	specified a default/designated Ultrix disk file
 *	type, then ALL files under that heirarchy will take
 *	on the attributes appropriate for the given default
 *	file type. The Dfiletype variable should only be
 *	applied to singular file names, applying it to
 *	a directory may or may not be what the user intends.
 *
 *	If errors occur during the file type determination
 *	process, the function outputs a message to "stderr"
 *	and exits to system control.	
 */

DIR	*dirfile;
struct	stat inodes;
char	dirent[14+1];
int	tfiletype;		/* Result from Filetype */
struct	direct	*dirp;
char	sbuf[MAXPATHLEN+1];
char	fullpath[MAXPATHLEN+1];	/* Full pathname */
char	dummy4[MAXPATHLEN+1];	/* The current working file/directory */

/* #define DEBUG 	/* turn on debug output */

putfile(longname, shortname, iflag, workdir)
	char	*longname;	/* Pathname_+_filename */
	char	*shortname;	/* File name only */
	int	iflag;		/* If iflag = 1, then stdin */
	char	*workdir;	/* Working directory */
{
/*
 * +--> Local Variables
 */

long	p;		/* WAS AN int, MUST BE long FOR PDP */
int	savindex;	/* index into sbuf; the place to terminate on return */

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

/*
 * We build dummy4[] for the fstat call, instead of adding the file
 * name to the end of fullpath.  This leaves the last component of
 * fullpath always being a directory, which later turns into sbuf.
 * Then, new directories are added to the end of fullpath, just after
 * we have descended the new directory.  NOTE: in this manner, fullpath
 * always points to the current directory!  fullpath components are
 * popped off just before we chdir up to the new level.  Search for
 * keyword "HERE".
 */

#ifdef DEBUG
  printf("\n\nIn putfile:\n");
  printf("	longname: %s\n", longname);
  printf("	shortname: %s\n", shortname);
  printf("	workdir: %s\n\n", workdir);
#endif

if (longname[0] == '/' || !(strcmp(shortname, "/"))) {
    if (strlen(longname) == 0)
	strcpy(dummy4, shortname);
    else
	strcpy(dummy4, longname);
}
else {
    strcpy(dummy4, workdir);
    strcat(dummy4, "/");
    strcat(dummy4, longname);
}

#ifdef DEBUG
	printf("fullpath is Currently: %s\n",
		strlen(fullpath) > 0 ? fullpath : "Unknown");
	printf("about to stat(%s)\n",dummy4);
#endif

/* Do the correct "stat" call
 */
i = stat(dummy4, &inodes);
j = lstat(dummy4, &Inode);
if (i < 0 || j < 0)
    if (skip && iflag == 1)
	return(EOF);
    else {
	if ((Inode.st_mode & S_IFMT) == S_IFLNK && i < 0)
	    PERROR "\n%s: %s %s%c\n", Progname, CANTSTS, dummy4, BELL);
	else
	    PERROR "\n%s: %s %s%c\n", Progname, CANTSTW, dummy4, BELL);
	perror(Progname);
	return(EOF);
    }
if (Nosym)
    Inode = inodes;
/*
 * Go put any required directory entries on the output volume.
 */

if (Ansiv != '3' && !Noheader3)
    if ((i = putdir(longname, shortname, workdir)) == EOF)
	return;

/* Check if current file (dummy4) is a directory - start recursive volume creation
 */

tfiletype = Filetype(dummy4, Progname);

if ((Inode.st_mode & S_IFMT) == S_IFDIR) {
	if (chdir(dummy4) < 0) {
	    /* Check if error is permission denied
	     */
	    if (errno != EACCES) {
	        PERROR "\n%s: %s %s%c\n", Progname, CANTCHD, dummy4, BELL);
	        perror(Progname);
		exit(FAIL);
	    }
	    else {
	        PERROR "\n%s: %s %s%c\n", Progname, CANTCHW, dummy4, BELL);
	        perror(Progname);
		return;
	    }
	}
#ifdef DEBUG
	printf("just chdir'd down to %s\n",dummy4);
#endif
	/*
	 * HERE, we update fullpath to reflect where we are currently,
	 * so we can later get back by popping the last component,
	 * search for keyword "HERE".
	 */
	strcpy(fullpath, dummy4);

#ifdef DEBUG
	printf("new fullpath is: %s\n", fullpath);
#endif

	/* Read through current directory
	 */
	if (!( dirfile = opendir( "."))) {
		PERROR "\n%s: %s %s%c\n\n", Progname, CANTOD, longname, BELL);
		perror(Progname);
		exit(FAIL);
	}
	while (dirp = readdir(dirfile)) {

		/* Make sure we don't read empty slots in the
		 * directory ...
		 */	
		if (!dirp->d_ino)
			continue;

		/* Likewise, don't try to process the current
		 * and previous directory entries which are
		 * present in all directories.
		 */
		if (!strcmp(".", dirp->d_name) ||
		    !strcmp("..", dirp->d_name))
			continue;

		/* Get new contents of sbuf 
		 */
    		strcpy(sbuf, longname);
		savindex = strlen(sbuf);
		strcat(sbuf, "/");
		strcat(sbuf, dirp->d_name);
		
		/*
		 * RECURSIVE UPON THINESELF !
		 *
		 * But, first save a pointer to where we are in the
		 * current directory and close it, lest we have too
		 * many open files dangling about the premises. 
		 */

		p = telldir(dirfile);

		/* Save the directory name because as soon as we
		 * issue the closedir we will lose the "dirp"
		 * pointer !
		 */
		strcpy(dirent, dirp->d_name);
		closedir(dirfile);

		/* RECURSE !*@#$!
		 */
/**************
printf("push sbuf = %s, dirent = %s, p = %ld\n", sbuf, dirent, p);
printf("push fullpath = %s\n", fullpath);
***************/

		putfile(sbuf, dirent, iflag, workdir);
		sbuf[savindex] = '\0';

		/* When we return, re-open and position the
		 * current directory.
		 */
		dirfile = opendir(".");
		seekdir(dirfile, p);
#ifdef DEBUG
		printf("Continuing with opendir...");
#endif
	}/*E while dirp ..*/

#ifdef DEBUG
	printf("DONE with opendir...\n\n");
#endif
	closedir(dirfile);

	/*
	 * HERE: pop 1 off end of fullpath to get back up 1 level,
	 *       check if going up to / 
	 */
	if ((strlen(fullpath) > 1) && (cp = rindex(fullpath, '/'))) {
	    if (cp == fullpath)
		cp++;
	    *cp = '\0';
	}
#ifdef DEBUG
	printf("\nNew working directory will be: %s\n\n", fullpath);
#endif

	if (chdir(fullpath) < 0) {
	    PERROR "\n%s: %s %s%c\n", Progname, CANTCHD, fullpath, BELL);
	    exit(FAIL);
	}

#ifdef DEBUG
	printf("just chdir'd back up to %s\n", fullpath);
#endif
    	return; 

}/*E if ((Inode.st_mode & S_IFMT) == S_IFDIR) */

if ((Inode.st_mode & S_IFMT) == S_IFCHR) {
special:
	PERROR "\n%s: %s %s%c", Progname, SPCLDF, longname, BELL);
	return;
}
	
if ((Inode.st_mode & S_IFMT) == S_IFBLK)
	goto special;

if ((Inode.st_mode & S_IFMT) != S_IFDIR) {
    /*	Has the user given us a default Ultrix disk file
     *	type for this file ?  If yes, use it and bypass our
     *	file determination logic.
     */
    if (Dfiletype) {
	/*
	 * Go put a file on the output volume,
	 * making sure user is aware s/he is overriding our
	 * disk file type determination logic.
	 */
	PROMPT "\n%s: %s %s%c\n", Progname, USEDF, shortname, BELL);
	process(dummy4, longname, shortname, Dfiletype);
    }/*T if Dfiletype */
    else {
	/* Go put a file on the output volume.
	*/
	if (tfiletype != EOF) {
	    process(dummy4, longname, shortname, tfiletype);
	}
    }/*F if Dfiletype */
    return;

}/* if ((Inode.st_mode & S_IFMT) != S_IFDIR) */
else
    return;
}/*E putfile() */

/**\\**\\**\\**\\**\\**  EOM  putfile.c  **\\**\\**\\**\\**\\*/
/**\\**\\**\\**\\**\\**  EOM  putfile.c  **\\**\\**\\**\\**\\*/