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


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

#ifndef lint
static	char	*sccsid = "@(#)putdir.c	3.0	(ULTRIX)	4/21/86";
#endif	lint

/**/
/*
 *
 *	File name:
 *
 *		putdir.c
 *
 *	Source file description:
 *
 *		This file contains the logic to put 
 *		individual directory files on an output
 *		volume for the Labeled Tape Facility (LTF).
 *		
 *
 *	Functions:
 *
 *		fdlist()	Returns directory list to memory
 *
 *		dprocess()	Outputs a directory file to the volume.
 *
 *		putdir()	Examines the current long path
 *				name for directory entries and
 *				outputs those which have not yet
 *				been placed on the output volume.
 *
 *	Usage:
 *
 *		n/a
 *
 *	Compile:
 *
 *	    cc -O -c putdir.c		<- For Ultrix-32/32m
 *
 *	    cc CFLAGS=-DU11-O putdir.c	<- For Ultrix-11
 *
 *
 *	Modification history:
 *	~~~~~~~~~~~~~~~~~~~~
 *
 *	revision			comments
 *	--------	-----------------------------------------------
 *	  01.0		3-Jun-85	Ray Glaser
 *			Create original version.	
 *
 *	  01.1		10-SEP-85	Suzanne Logcher
 *			Add logic to check current century 
 *
 *	  01.2		24-SEP-85	Suzanne Logcher
 *			Correct the representation of directories and
 *			subdirectories when creating a volume
 */

/*
 * +--> Local Includes
 */

#include	"ltfdefs.h"
/**/
/*
 *
 * Function:
 *
 *	fdlist
 *
 * Function Description:
 *
 *	Returns directory list to memory
 *
 * Arguments:
 *
 *	none
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	none
 */

fdlist()
{

/*------*\
  Locals
\*------*/

struct DIRE	*lDhead;
struct DIRE	*dlinkp;

/* Return malloc'd directory list to freemem
 */
for (lDhead = Dhead; lDhead;) {
	dlinkp = lDhead->dir_next;	
	free ((char *)lDhead);
	lDhead = dlinkp;
}
Dhead = 0;	/* The list is now empty */
}/*E fdlist() */
/**/
/*
 *
 * Function:
 *
 *	dprocess
 *
 * Function Description:
 *
 *	This functions writes the ANSI file header labels
 *	and ANSI file trailer labels to the output volume
 *	for a directory file.
 *
 * Arguments:
 *
 *	char	*longname;	Contains the complete directory name
 *	char	*shortname;	Current/last sub-directory name
 *
 * 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.	
 */

dprocess(longname, shortname)
	char	*longname;
	char	*shortname;
{
/*
 * +--> Local Variables
 */
char	ansift;		/* Saves ANSI file type character from HDR2
			 * for later verbose messge output.
			 */
char	crecent;	/* Creation date century */
char	*ctime();	/* Declare ctime routine */
char	interchange[18];	/* Interchange name */
int	length;
struct	tm *localtime();
struct	tm *ltime;
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 fatal...
	 */
	exit(FAIL);
}
/*
 * Make the ANSI interchange file name
 */ 
i = 0;
strcpy(Dummy,shortname);
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);
		i++;
	}
}

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);
	    i++;
	}
}
if ((i > 0) && Warning)
    PERROR "\n%s: %s\n\n", Progname, FILENNG);

strcpy(interchange, Dummy);
strcpy(Tftypes,"dir");
Inode.st_size = 0L;

/*
 * 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;

/****\
 *	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, 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();
} 


/******
 *
 * 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++;
	}
}

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

sprintf(Dummy,
    "HDR2%c%05d%05d%06o%04d%04d%04d%3.3s%c%010ld%1.1d%1.1d 00%28.28s",
	FIXED, Blocksize, 0,
	Inode.st_mode & 0177777,
	Inode.st_uid, Inode.st_gid,
	0, Tftypes,
	Carriage, Inode.st_size, Lhdrl, Leofl, 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();
} 


/****\
 *	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();
		} 
	}	
}

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

weof();

/****\
 *	An empty file is written for a directory.
 *	(write an end-of-file mark on tape)
 ****/

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,
	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 00%28.28s",
	FIXED, Blocksize, 0,
	Inode.st_mode & 0177777,
	Inode.st_uid, Inode.st_gid,
	0, Tftypes,
	Carriage, Inode.st_size, Lhdrl, Leofl, Spaces);

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


/****\
 *	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 = 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();
		} 

	}
}

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

weof();

if (Dverbose) {
	if (Verbose) {

		printf("c<%s>%c  %s\n",Tftypes, ansift, longname);

	}/*T if Verbose */
	else
		printf("c  %s\n", longname);

}/*E if Dverbose */

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

}/*E dprocess() */
/**/
/*
 *
 * Function:
 *
 *	putdir
 *
 * Function Description:
 *
 *	This function determines if there are directory components
 *	in the given path name that have not yet been placed on
 *	the output volume and places them in the list of directories
 *	output and then calls "dprocess" to write a directory file
 *	entry on the output volume.
 *
 * Arguments:
 *
 *	char	*longname	Full pathname_+_filename
 *	char	*shortname	File name only
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	none
 */

putdir(longname, shortname, workdir)
	char	*longname;	/* Pathname_+_filename */
	char	*shortname;	/* File name only */
	char	*workdir;	/* Working directory */
{
/*
 * +--> Local Variables
 */

int	d;
char	dir[MAXNAMLEN+1];
int	i, j, k;
struct	stat inode;
char	path[MAXPATHLEN+1];
int	relpath;
char	relpaths[MAXPATHLEN+1];
int	ret;
int	ret1;
int	s;
struct	stat tnode;

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

if (!strcmp(longname, ".") || !strcmp(longname, "..") || 
    !strcmp(shortname, "/"))
    return(TRUE);

relpaths[0] = '\0';
if (longname[0] == '/')
	relpath = FALSE;
else {
	relpath = TRUE;
	strcpy(relpaths, workdir);
	strcat(relpaths,"/");
}/*F longname[0] = '/' */
i = strlen(longname);
path[0] = '\0';
dir[0] = '\0';

for (s = 0, j = 0, d = 0; j < i; ) {

	/* Add to the current full directory tree name.
	 */
	path[j] = longname[j];

	/* Add to the current sub-directory name used for
	 * the interchange file name by dprocess().
	 */
	if (longname[s] == '/')
		/*
		 * Relative subdirectories do not begin their
		 * names with a slash.
		 * This "if" is needed in the event a relative
		 * name without a leading slash is given.
		 * An absolute path name does begin with a / ,
		 * but the following name string is really a
		 * relative name to the root directory (/) and
		 * so we always want to strip the leading slash
		 * so that the HDR1 name output by dprocess
		 * is always  "name/" .. The correct/complete
		 * path (absolute or relative) is contained
		 * in our path string and goes into HDR3, etc.
		 */
		dir[d++] = longname[j+1];
	else 
		dir[d++] = longname[j];
	j++;

	if (longname[j] == '/' || longname[j] == '\n' ||
	    !longname[j] || d >= MAXNAMLEN) {

		dir[d] = path[j] = 0;
		s = j;

		ret1 = 0;
		if (relpath) {
			strcat(relpaths, dir);
			k = strlen(relpaths);
			relpaths[k] = '\0';
			ret = stat(relpaths, &Inode);
			ret1 = lstat(relpaths, &inode);
			if (ret < 0 || ret1 < 0) {
			    if (inode.st_mode & S_IFMT == S_IFLNK && ret < 0)
			        PERROR "\n%s: %s %s%c\n", Progname, CANTSTS, relpaths, BELL);
			    else
			        PERROR "\n%s: %s %s%c\n", Progname, BADST, relpaths, BELL);
			    system("pwd");
			    perror(Progname);
			    return(EOF);
			}
			strcat(relpaths,"/");
		}
		else {
			ret = stat(path, &Inode);
			ret1 = lstat(path, &inode);
			if (ret < 0 || ret1 < 0) {
			    if ((inode.st_mode & S_IFMT) == S_IFLNK && ret < 0)
			        PERROR "\n%s: %s %s%c\n", Progname, CANTSTS, path, BELL);
			    else
			        PERROR "\n%s: %s %s%c\n", Progname, BADST, path, BELL);
			    system("pwd");
			    perror(Progname);
			    return(EOF);
			}
		}
		if ((Inode.st_mode & S_IFMT) == S_IFDIR) {

			int	found = 0;
			struct	DIRE *dp;

			if ((inode.st_mode & S_IFMT) == S_IFLNK) {
			    tnode = Inode;
			    Inode = inode;
			    if (!Nosym)
				return(TRUE);
			}
			if (dir[d-1] != '/')
				/*
				 * Sub-directory names are always
				 * terminated by a / character.
				 */
				strcat(dir,"/");

			/* If this is a partial tree name, ensure
			 * that we include the trailing / .
			 */
			if (longname[j] == '/') {
				path[j] = longname[j++];
				path[j] = 0;
				s = j;
			}
			else
				/* It is the end of the tree name &
				 * we have to add a slash for
				 * ourselves.
				 */
				strcat(path,"/");
			for (dp = Dhead; dp; dp = dp->dir_next)
				if (dp->rdev == Inode.st_rdev &&
				    dp->inode == Inode.st_ino) {
					found = TRUE;
					break;
				}
			if (!found && !NMEM4D) {
    			    /* Get some memory for our next
     			     * directory list entry.
     			     */
			    dp=(struct DIRE *) malloc(sizeof(*dp));
			    if (!dp) {
				/* If no mem, return what we
 	 		 	 * have & try again ?
 	 			 */
				fdlist();
			        dp=(struct DIRE *) malloc(sizeof(*dp));
			        if (!dp) {
				    PERROR "\n%s: %s%c\n", Progname, NOMDIR, BELL);
				    NMEM4D++;
	    		        }/*T if !dp */
				else {
				    dp->dir_next = Dhead;
				    Dhead = dp; /*Lists run backwards*/
				    dp->rdev = Inode.st_rdev;
				    dp->inode = Inode.st_ino;
				}/*F if !dp */
			    }/*T if !dp */
			    else {
				dp->dir_next = Dhead;
				Dhead = dp; /*Lists run backwards*/
				dp->rdev = Inode.st_rdev;
				dp->inode = Inode.st_ino;
			    }/*F if !dp */
			    /*
			     * Go put a new directory entry on
			     * the output volume if Nodir switch
			     * is FALSE.
			     */
			    if (!Nodir)
				dprocess(path, dir);
			}/*T if (!found && !NMEM4D) */
			d = 0;
			if ((inode.st_mode & S_IFMT) == S_IFLNK)
			    Inode = tnode;
		}/*T if ((Inode.st_mode &S_IFMT) == S_IFDIR) */
		else {
			/* If something other than a directory was
			 * detected in the given path, exit back
			 * to caller for ordinary file processing.
			 */
			if (!Nosym)
			    Inode = inode;
			return(TRUE);
		}
	}/*T if (longname[j] == '/' || ... */
}/*E for (s=0, j=0, d=0; j<i; ) */
for (cp = longname, i = 0; *cp; cp++)
    if (*cp == '/')
	i++; 
if (!relpath) {
    for (cp = workdir, j = 0; *cp; cp++)
	if (*cp == '/')
	    j++; 
    numdir = i - j;
}
else
    numdir = i + 1; 
if (!Nosym)
    Inode = inode;
    
}/*E putdir() */

/**\\**\\**\\**\\**\\**  EOM  putdir.c  **\\**\\**\\**\\**\\*/
/**\\**\\**\\**\\**\\**  EOM  putdir.c  **\\**\\**\\**\\**\\*/