Ultrix-3.1/src/cmd/tar/command.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 = "@(#)command.c	3.1	(ULTRIX)	9/5/87";
#endif	lint

/**/
/*
 *
 *	File name:
 *
 *		command.c
 *
 *	Source file description:
 *
 *		Tar command line processing
 *		& other user interface routines.
 *		Also contains not often used routines
 *		for read/write in order to minimize the
 *		size of overlays when built for non-split
 *		i/d PDP-11s.
 *
 *	Functions:
 *
 *	Usage:
 *
 *	Compile:
 *
 *	Modification history:
 *	~~~~~~~~~~~~~~~~~~~~
 *
 *	revision			comments
 *	--------	-----------------------------------------------
 *	III		28-Jan-86 rjg/
 *			Do not allow non-super user to set pflag
 *
 *	II		Ray Glaser, 09-Jan-86
 *			Do not set block size to 10 for disks (d flag)
 *
 *	I		19-Dec-85 rjg/
 *			Create original version.
 *	
 */
#include "tar.h"

/*.sbttl checkf() */

/* Function:
 *
 *	checkf
 *
 * Function Description:
 *
 *   Routine to determine whether the specified file should
 *   be skipped or not. Implements the F, FF and FFF modifiers.
 *
 *   The set of files skipped depends upon the value of howmuch.
 *
 *   When howmuch > 0 the set of skip files consists of:
 *
 *		SCCS directories
 *		core files
 *		errs files
 *
 *    Howmuch > 1 increases the skip set to include:
 *
 *		a.out
 *		*.o
 *
 *    Howmuch > 2 causes executable files to be skipped
 *    as well. (A file is determined to be executable by
 *    looking at its "magic numbers")
 *
 *
 * Arguments:
 *
 *	char	*longname	Pointer to name string of file to
 *				to be checked
 *	int	mode		File mode bits of the file
 *	int	howmuch		Skip set modifier
 *
 * Return values:
 *
 *	1	The file is to be skipped
 *	0	The file is not to be skipped
 *
 * Side Effects:
 *
 *	The routine assumes that the file is in the current directory.
 *	The routine is not fully implemented as described above. See
 *	the #ifdef below.. 
 *	
 */
checkf(longname, mode, howmuch)
	STRING_POINTER	longname;
	int	mode;
	int	howmuch;
{
/*------*\
  Locals
\*------*/

	STRING_POINTER	shortname;

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

/* Strip off any directory and get the base filename.
 */ 
if ((shortname = rindex(longname, '/')))
	shortname++;
else
	shortname = longname;

/* Basic skip set ?
 */
if (howmuch > 0) {
	/*
	 * Skip SCCS directories on input
	 */
	if ((mode & S_IFMT) == S_IFDIR)
		return (strcmp(shortname, "SCCS") != 0);

	/*
	 * Skip SCCS directory files on extraction
	 * Check SCCS as a toplevel directory and
	 * as a subdirectory.
	 */
	if (strfind(longname,"SCCS/") == longname || strfind(longname,"/SCCS/"))
		return (0);
	/*
	 * Skip core and errs files. 
	 */
	if (strcmp(shortname,"core")==0 || strcmp(shortname,"errs")==0)
		return (0);

}/*E if howmuch > 0 */

/* First level additions ?
 */
if (howmuch > 1) {
	int l;

	l = strlen(shortname);	/* get string len */

	/* Skip .o files
	 */
	if (shortname[--l] == 'o' && shortname[--l] == '.')
		return (0);

	/* Skip a.out
	 */
	if (strcmp(shortname, "a.out") == 0)
		return (0);

}/*E if howmuch > 1 */

#ifdef notdefFFF
/*
 * This routine works for -c and -r options, but is
 * not sufficent for -x option.  Since it cannot be implemented
 * fully, it is not implemented at all at this time.
 */
/*
 * Second level additions ?
 */
if (howmuch > 2) {
	/*
	 * Open the file to examine the magic numbers.
	 * If the file cannot be opened, then assume
	 * that it is not to be skipped and that another
	 * routine will perform the error handling for it.
	 * Otherwise, read from the file and check the
	 * magic numbers, indicate skipping if they are good.
	 */
	int	ifile, in;
	struct exec	hdr;

	ifile = open(shortname,O_RDONLY,0);

	if (ifile >= 0)	{
		in = read(ifile, &hdr, sizeof (struct exec));
		close(ifile);
		if (in > 0 && ! N_BADMAG(hdr))	/* executable: skip */
			return (0);
	}
}/* end if howmuch > 2 */
#endif notdefFFF

return (1);	/* Default action is not to skip */

}/*E checkf() */

/*.sbttl done() */

/* Function:
 *
 *	done
 *
 * Function Description:
 *
 *
 * Arguments:
 *
 *	int	n	Desired exit status
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 */
done(n)
{
/*------*\
  Locals
\*------*/

	int	i,status;

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

status = unlink(tname);
close(mt);

if (NFLAG)
	exit(n);

if (n == A_WRITE_ERR) {

	struct linkbuf	*lihead;
	struct linkbuf	*linkp;
	

	/* Return malloc'd linked file list to freemem
	 */
	for (lihead = ihead; lihead;) {
		linkp = lihead->nextp;	
		free ((char *)lihead);
		lihead = linkp;
	}
	ihead = 0;

	/* Return malloc'd directory list to freemem
	 */
	fdlist();

	start_archive = CARCH;
	size_of_media[CARCH] = size_of_media[0];
	blocks_used = 0L;
	PUTE = AFLAG = EOTFLAG = EODFLAG = recno = NMEM4L = NMEM4D = 0;
	dcount1 = dcount2 = dcount3 = lcount1 = lcount2 = 0;

	if (chdir(hdir) < 0) {
		fprintf(stderr, "%s: Can't change directory back ?", progname);
		perror(hdir);
		exit(FAIL);
	}

REOPEN:
/**/
	fprintf(stderr,"\007%s: Please press  RETURN  to re-write %s %d ",progname, Archive, CARCH);

	CARCH = 1;
	sprintf(CARCHS, "%d", CARCH);
	response();
	fprintf(stderr,"\n%s: Starting error recovery\n",progname);

	if ((mt = open(usefile, O_RDWR))< 0) {
		fprintf(stderr,"\n%s: Can't open: %s\n",progname,usefile);
		perror(usefile);
		goto REOPEN;
	}
	return(A_WRITE_ERR);
}
if (n == SUCCEED)
	n = 0;

exit(n);

}/*E done() */

/*.sbttl fdlist() */

/* Function:
 *
 *	fdlist
 *
 * Function Description:
 *
 *	Return the directory list structures to free mem.
 *
 * Arguments:
 *
 *	none
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	none	
 */

fdlist()
{
/*------*\
  Locals
\*------*/

	struct DIRE	*lDhead;
	struct DIRE	*dlinkp;


if (Dhead)
	dcount3++;

/* Return malloc'd directory list to freemem
 */
for (lDhead = Dhead; lDhead;) {

	dlinkp = lDhead->dir_next;	
	free ((char *)lDhead);
	lDhead = dlinkp;
}
Dhead = 0;

}/*E fdlist() */

/*.sbttl flushtape() */

/* Function:
 *
 *	flushtape
 *
 * Function Description:
 *
 *
 * Arguments:
 *
 *
 * Return values:
 *
 *
 * Side Effects:
 *
 *	
 */

flushtape()
{
/*------*\
   Code
\*------*/

if (CARCH >= start_archive) {
	if (recno) {
		if (write(mt, tbuf, TBLOCK*nblock) < 0) {

			if ((ioctl(mt, MTIOCGET, &mtsts)<0) ||
				size_of_media[CARCH] ||
				(errno != ENOSPC) || NFLAG) {
FERR:
/**/
				fprintf(stderr,"\n\n\007%s: Archive %d write error on last block\n",
					progname, CARCH);

				fprintf(stderr,"%s: Blocks used = %ld\n",progname, blocks_used);
				perror(usefile);
				done(A_WRITE_ERR);
				return(A_WRITE_ERR);
			}
			else {
				if (!(mtsts.mt_softstat & MT_EOT))
					goto FERR;
				else {
					mtops.mt_op = MTCSE;
					if (ioctl(mt,MTIOCTOP,&mtops)< 0)
						goto FERR;
					else {

						OARCH = CARCH;
						EOTFLAG++;
						MULTI++;
						FEOT++;

						if (writetape(tbuf,recno,recno,
						(char *)cblock.dbuf.name,
						(char *)cblock.dbuf.name) == A_WRITE_ERR)
							goto FERR;
					}
				}
			}
		}/*E if write ..*/
	}/*E if recno */
}/*E if CARCH >= start_archive */

if (vflag) {
	if (CARCH >= start_archive) {
		if (MULTI)
	        	fprintf(stderr,"\n%s: End of %s media",
				progname,Archive);
		if (VFLAG)
			fprintf(stderr,"\n%s: %ld Blocks used on %s for %s %d\n",
				progname, blocks_used, usefile, Archive, CARCH);
	}
	else
		if (AFLAG || ((start_archive > CARCH) && VFLAG))
			fprintf(stderr,"%s: %s %d  skipped.\n",
				progname, Archive, CARCH);
}/*E if vflag */

recno = 0;
return(SUCCEED);

}/*E flushtape() */

/*.sbttl parse() - Parse the command line */

/* Parse the command line and set up control variables.
 */
parse(argc,argv)
	int	argc;
	char	*argv[];
{

/*------*\
  Locals
\*------*/
	STRING_POINTER	cp;
	INDEX	i;

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

chksum = 2;

progname = argv[0];	/* Get our name*/

#ifdef PRO
set_size(800L);
#else
set_size(0L);	/* Default media size to tapes/files = unlimited */
#endif

if (!(cp = rindex(progname,'/')))
	cp = progname;
else 
	cp++;


if ((strcmp(cp,mdtar))) {
	MDTAR = FALSE;
}
else {
	/* Task name MDTAR implies multiple archive function
	 * with 800 block rx50 as the default output device.
	 */
	set_size(800L);
	MDTAR = TRUE;
}

if (argc < 2)
	usage();

tfile = NULL;

/* Setup the default archive
 */
usefile =  magtape;
argv[argc] = 0;
argv++;

for (cp = *argv++; *cp; cp++) {
	int uid;

	switch(*cp) {
		/*
	 	 *  Switches that either TAR or MDTAR will accept..
	 	 */

		/* A: Archive. Number of the archive, counting from 1,
		 * to begin physically writing.
		 */
		case 'A':
			if (!*argv) {
				fprintf(stderr, "%s: Archive number must be specified with 'A' option\n", progname);
				usage();
			}
			start_archive = atoi(*argv++);
			chksum++;
			if ((start_archive < 1) ||
				(start_archive > MAXARCHIVE) ) {

				fprintf(stderr, "%s: Invalid archive number:  %d\n", progname,start_archive);
				done(FAIL);
			}
			AFLAG++;
			break;

		/* b: use next argument as blocksize
		 */
		case 'b':
			bflag++;
			if (!*argv) {
				fprintf(stderr, "%s: Blocksize must be specified with 'b' option\n", progname);
				usage();
			}
			nblock = atoi(*argv++);
			chksum++;

			if (nblock <= 0 || nblock > NBLOCK) {
				fprintf(stderr, "%s: Invalid blocksize \"%s\"\n", progname, *argv);
				done(FAIL);
			}
			break;

		/* B: Force I/O blocking to 20 blocks per record
		 */
		case 'B':
			Bflag++;
			break;

		/* -c: create new archive. Note that -r is implied
		 * by this switch also.
		 */
		case 'c':
			cflag++;
			rflag++;
			break;

		/* -D: Directory output in old style to conserve
		 * memory and archive utilization.
		 */
		case 'D':
			DFLAG++;
			break;

		/* -d: select default RX50 diskette device.
		 * -e: select default RX33 diskette device.
		 * "device" means diskette type, not drive type.
		 */
		case 'd':
		case 'e':
			/* Set up default media size table.
 			*/
			if(*cp == 'd')
				set_size(800L);
			else
				set_size(2400L);
			dflag++;
#ifdef U11
#ifdef PRO
			magtape[6] = 'r';
			magtape[7] = 'x';
			magtape[8] = '0';
#else
			magtape[6] = 'r';
			magtape[7] = 'x';
			magtape[8] = '1';
#endif
#endif
#ifndef U11
			magtape[6] = 'r';
			magtape[7] = 'a';
			magtape[8] = '1';
			magtape[9] = 'a';
#endif
			if (unitflag)
				magtape[8] = unitc;
			break;


		/* -f: use next argument as the archive of choice
		 * instead of the default.
		 */
		case 'f':
			if (!*argv) {
				fprintf(stderr, "%s: Archive file  must be specified with 'f' option\n", progname);
				usage();
			}
			usefile = *argv++;
			chksum++;
			if (cflag) {
				i = stat(usefile, &stbuf);
				if ((i<0) || ((stbuf.st_mode & S_IFMT) == S_IFREG)) {
					if (!sflag && !MDTAR)
						set_size(0L);

					if (open(usefile,O_TRUNC,0)<0) {
						if (errno != ENOENT) {
							fprintf(stderr, "%s: Can't open:  %s\n", progname, usefile);
							perror(usefile);
							done(FAIL);
						}
					}
				}
			}
			fflag++;
			break;

		/* F: Fast. F causes SCCS dirs, core & errs files
		 * to be skipped.
		 * FF:	Skip  .o & a.out's also
		 * FFF: Skip executable files.
		 */
		case 'F':
			Fflag++;
			break;
#ifdef U11
		/* g: select 6250 GCR device
		 */
		case 'g':
			set_size(0L);
			magtape[6] = 'g';
			magtape[7] = 't';
			magtape[8] = '0';
			if (unitflag)
				magtape[8] = unitc;
			break;
#endif

		/* H: User has requested the help function. Provide
		 * expanded information about the switches/options.
		 */
		case 'H':
			HELP++;
			usage();

		/* h: Treat symbolic links as if they were normal
		 * files. ie. Put a physical copy of the linked to
		 * file on the archive.
		 */
		case 'h':
			hflag++;
			break;
#ifdef U11
		/* k: select TK50 device
		 */
		case 'k':
			set_size(0L);
			magtape[6] = 't';
			magtape[7] = 'k';
			magtape[8] = '0';

			if (unitflag)
				magtape[8] = unitc;
			break;
#endif
		/* l: Print errors if all links to a file cannot
		 * be resolved.
		 */
		case 'l':
			lflag++;
			break;

		/* -r: Write named files at the END of the archive.
		 *     Implied in -u & -c
		 */
		case 'r':
			rflag++;
			break;

		/* -u: Add to archive only those files
		 *     not already in/on it.
		 */
		case 'u':
			mktemp(tname);
			if ((tfile = fopen(tname, "w")) == NULL) {
	    		    fprintf(stderr, "%s: Can't create temporary file (%s)\n", progname, tname);
			    perror(tname);
			    done(FAIL);
			}
			fprintf(tfile, "!!!!!/!/!/!/!/!/!/! 000\n");

			/* IMPLIED 'r' FUNCTION 
			 */
			rflag++;
			break;

		/* V: VERBOSE mode. Big verbose displays directory
		 * information not displayed by little verbose.
		 * Note that 'V' implies 'v'.
		 */
		case 'V':
			VFLAG++;
			/*_FALL THRU_*/

		/* v: Verbose mode.  Display the filenames as they
		 * are processed. Also supply further information
		 * about the archive and operation. See man page.
		 */
		case 'v':
			vflag++;
			if (VFLAG)
				fprintf(stderr,"\n%s: rev. %d.%-d\n", progname,revwhole,revdec);
			break;

		/* w: Wait mode. Request user confirmation prior to
		 * processing each file argument.
		 */
		case 'w':
			wflag++;
			break;

		/* n: Select an alternate unit number.
		 */
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			unitflag++;
			unitc = *cp;

			if (!fflag)
				magtape[8] = *cp;

				/* w/o dflag Ultrix-11 dev = /dev/rht1
				 *	     Ultrix-Pro dev= /dev/rrx1
				 *           Ultrix-32 dev = /dev/rmt8
				 */ 
			usefile = magtape;

			break;

		/* -: ignored. To allow some degree of consistency
		 * with other unix commands that do not insist on the
		 * leading -.
		 */
		case '-':
			break;

		/* i: Do not terminate if checksum errors are detected
		 * during an archive read.
		 */
		case 'i':
			iflag++;
			break;

		/* m: Do not restore modification times from the input
		 * archive file header block.
		 */
		case 'm':
			mflag++;
			break;

		/* M: Specify maximum writtable archive number.
		 * (for debugging)
		 */
		case 'M':
			if (!*argv) {
				fprintf(stderr, "%s: Archive number must be specified with 'M' option\n", progname);
				usage();
			}
			MAXAR = atoi(*argv++);
			chksum++;
			if ((MAXAR < 1) ||
				(MAXAR > MAXARCHIVE) ) {
				fprintf(stderr, "%s: Invalid archive number:  %d\n", progname,MAXAR);
				done(FAIL);
			}
			MFLAG++;
			break;

		/* N: No multi-archive, file splitting, or
		 * new header format.
		 */
		case 'N':
			NFLAG++;
			set_size(0L);
			if (MDTAR) {
				fprintf(stderr,"\n\007\007%s: Warning: intended  mdtar  functionality is disabled by the 'N' switch.\n\n",progname);
				MDTAR = FALSE;
			}
			break;

		/* n: Select 800 bpi tape. By default this is assumed 
		 * to be  /dev/rmt0.
		 */
		case 'n':
			set_size(0L);
			magtape[6] = 'm';
			magtape[7] = 't';
			magtape[8] = '0';

			if (unitflag)
				magtape[8] = unitc;
			break;

		/* O: Include file owner and group names in 
		 * verbose output if present in this archive format.
		 */
		case 'O':
			OFLAG++;
			break;

		/* o: create the archive w/o directory information
		 * to produce archives that early version of tar
		 * can process.
		 */
		case 'o':
			oflag++;
			break;

		/* p: Restore files to original their orginal modes
		 * and owners as recorded in the file header block.
		 * NOTE: This is only allowed if you are the super-user.
		 */
		case 'p':
			uid = getuid();
			if (uid == 0)
				pflag++;
			else {
				fprintf(stderr,"\n%s: \007Warning: Only the super-user may invoke the 'p' option\n\n", progname);
			}
			break;

		/* S: Output User Group Standard archive format.
		 */
		case 'S':
			SFLAG++;
			break;

		/* s:  Size of the media in blocks.
		 */
		case 's':
			if (!*argv) {
				fprintf(stderr, "%s: Media size must be specified with 's' option\n", progname);
				usage();
			}
			size_of_media[0] = atoi(*argv++);
			chksum++;

			if (size_of_media[0] <= 4) {
		    		fprintf(stderr, "%s: Invalid media size:  %s\n", progname, *argv);
		    		done(FAIL);
			}
			set_size(size_of_media[0]);
			sflag++;
			break;


		/* t: List the names of the files that are in/on the
		 * given archive.
		 */
		case 't':
			tflag++;
			break;

		/* x: eXtract the named files from the given
		 * input archive.
		 */
		case 'x':
			xflag++;
			break;

		default:
			fprintf(stderr, "%s: Unknown option:  %c\n", progname, *cp);
			usage();

		}/*E switch(*cp)*/
}/*E for (cp = *argv++; *cp; cp++) */


NOARGS:
/*----:
 */
if (MAXAR < start_archive) {
	fprintf(stderr,"\n%s: Max archive < start_archive\n",progname);
	done(FAIL);
}

if (AFLAG && !size_of_media[0]) {
	fprintf(stderr,"\n%s: Media size conflict\n",progname);
	done(FAIL);
}

/*
 * Check to see that a function letter was specified, 
 * either directly or by implication.
 */
if (!rflag && !xflag && !tflag)
	usage();

if (cflag || rflag)
	if (argc < 3)
		usage();

tbuf = (union hblock *)malloc(nblock*TBLOCK);

if (tbuf == NULL) {
	fprintf(stderr, "%s: Blocksize %d too big, can't get memory\n", progname, nblock);
		done(FAIL);
}

/* Does the user want to add files to the end of the current
 * archive device ?
 */
if (rflag) {

	if (cflag && tfile != NULL) {
		fprintf(stderr,"%s: c and u functions may not be given in the same command\n", progname);
		done(FAIL);
	}
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, onhup);

#ifdef notdef
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, onintr);

	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, onquit);

	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, onterm);
#endif

	/* Has user specified  stdout  as the archive meadia ?
 	 */
	if (strcmp(usefile, "-") == 0) {
		if (!cflag) {
			fprintf(stderr, "%s: Can only CREATE standard output archives\n", progname);
			done(FAIL);
		}
		/* When pipe output has been specified,
		 * ignore invocation name of MDTAR
		 * and any possible size of device given by
		 * cancelling the flags.
 		 */
		MDTAR = FALSE;
		sflag = FALSE;
		set_size(0L);
		mt = dup(1);
		nblock = 1;
	} else
		{
		int status;
TRYOPA:
/**/
	 	if ((mt = open(usefile, O_RDWR)) < 0) {
			if (!cflag || (mt = creat(usefile, 0666)) < 0) {
				fprintf(stderr, "\n\007%s: Can't open:  %s\n", progname, usefile);
				perror(usefile);
				fprintf(stderr,"%s: Please press  RETURN  to retry ", progname);
				response();
				goto TRYOPA;
			}
		}
	}
	return(chksum);

}/*E if rflag */

/* Has user specified  stdin  as the archive device ?
 */
if (!strcmp(usefile, "-")) {
	pipein++;
	mt = dup(0);
	nblock = 1;
} else
	{
OPMTA:
/**/
	if ((mt = open(usefile, O_RDONLY)) < 0) {
		fprintf(stderr, "\n\007%s: Can't open:  %s\n", progname,usefile);
		perror(usefile);
		fprintf(stderr,"%s: Please press RETURN  to retry ", progname);	
		response();
		goto OPMTA;
	}
}

return(chksum);

}/*E parse() */

/*.sbttl putempty()  Put empty (EOF) blocks on archive */

/* Function:
 *
 *	putempty
 *
 * Function Description:
 *
 *	Writes an empty block on the output archive.
 *	Used when writting the end of archive blocks.
 *
 * Arguments:
 *
 *	none
 *
 * Return values:
 *
 *
 * Side Effects:
 *
 */
putempty()
{
/*------*\
  Locals
\*------*/

	INDEX	i;

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

for (i=0; i < sizeof(iobuf); i++)
	iobuf[i] = 0;

/* Use to debug code.. flags our empty block as opposed
 * to just bumping into one.
 *
 *strcpy(iobuf+(TBLOCK-6),"eoa");
 */

if (writetape(iobuf,1,1,eoa,eoa) == A_WRITE_ERR)
	return(A_WRITE_ERR);

return(SUCCEED);

}/*E putempty() */

/*.sbttl puteoa()  Put an end of archive block in the buffer */

/* Function:
 *
 *	puteoa
 *
 * Function Description:
 *
 *	This function formats and puts the END OF ARCHIVE
 *	block in the buffer when splitting a file across
 *	an archive.
 *
 * Arguments:
 *	none
 *
 * Return values:
 *
 * Side Effects:
 *
 */

puteoa()
{
/*------*\
  Locals
\*------*/

	STRING_POINTER	cp;
	STRING_POINTER	from;
	COUNTER	nc;
	STRING_POINTER	to;

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

for (to = (char *)&tbuf[recno++], nc = TBLOCK; nc; nc--)
	*to++ = 0;
for (to = (char *)&tbuf[recno++], nc = TBLOCK; nc; nc--)
	*to++ = 0;

/* Copy file name into output buffer.
 */
for (from = (char *)dblock.dbuf.name, to = (char *)&tbuf[recno], nc=NAMSIZ; nc; nc--)
	*to++ = *from++;

/* Indicate End of Archive by ascii zero fill of remaining header
 * block fields.
 */
for (nc=TBLOCK-NAMSIZ-1,cp = (char *)&tbuf[recno]+NAMSIZ; nc ; nc--)
	*cp++ = '0';

return(flushtape());

}/*E puteoa() */

/*.sbttl response()  Get a yes/no answer from user */

/* Function:
 *
 *	response
 *
 * Function Description:
 *
 *	Gets a yes/no response from the user at the terminal.
 *	Used in conjunction with the "wait" function to selectively
 *	include/exclude files from this operation.
 *	It also is used when we are waiting for the user to
 *	physically change an archive when doing multi-archive i/o.
 *
 * Arguments:
 *
 *	none
 *
 * Return values:
 *
 *	The character user typed. If none typed, then 'n'.
 *
 * Side Effects:
 *
 *	
 */
response()
{
/*------*\
  Locals
\*------*/

	char	c;

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

c = getchar();
if (c != '\n')
	while (getchar() != '\n')
		;/*_NOP_*/
else
	c = 'n';

if (term)
	done(FAIL);

fprintf(stderr,"\n\n");
return (c);

}/*E response() */

/*.sbttl set_size() */

/* Function:
 *
 *	set_size
 *
 * Function Description:
 *
 *	Set up the media size table.
 *	
 * Arguments:
 *
 *	SIZE_L	size	Number of blocks on the media.
 *
 * Return values:
 *
 *
 * Side Effects:
 *
 *	
 */
set_size(size)
	SIZE_L	size; 
{
/*------*\
  Locals
\*------*/

	INDEX	i;

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

for (i = MAXARCHIVE; i >= 0; i--)
	size_of_media[i] = size;

return;

}/*E set_size() */

/*.sbttl strfind() */

/* Function:
 *
 *	strfind
 *
 * Function Description:
 *
 *	Searches for  substr  in text.
 *	
 * Arguments:
 *
 *	char	*text		Text to search
 *	char	*substr		String to locate
 *
 * Return values:
 *
 *	Pointer to  substr  starting position in text if found.
 *	NULL if not found.
 *
 * Side Effects:
 *
 *	
 */
STRING_POINTER	strfind(text,substr)
		STRING_POINTER text, substr;
{
/*------*\
  Locals
\*------*/

	COUNTER	i;	/* counter for possible fits */
	SIZE_I	substrlen;/* len of substr--to avoid recalculating */

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

substrlen = strlen(substr);

/* Loop through text until not found or match.
 */
for (i = strlen(text) - substrlen; i >= 0 &&
     strncmp(text, substr, substrlen); i--)

	text++;

/* Return NULL if not found, ptr else NULL.
 */ 
return ((i < 0 ? NULL : text));

}/*E strfind() */

/*.sbttl usage()  Show the user correct command format(s) */

/* Function:
 *
 *	usage	aka useage
 *
 * Function Description:
 *
 *
 * Arguments:
 *
 *	none
 *
 * Return values:
 *
 *	none
 *
 * Side Effects:
 *
 *	Always exits to the system with error status
 */

usage()
{
/*------*\
   Code
\*------*/

#ifndef PRO
if (!HELP)
	fprintf(stderr,"\n%s: specify H (Help) for expanded definition of switches\n", progname);
#endif
#ifdef U11
	fprintf(stderr, "\nusage:\n%s [-]{crtux} [ABb-CDdFfghiklMmNnOopsVvw] [archivefile] [blocksize]\n   [archivenumber] [maxarchive #] [mediasize] [archivefile]\n    directory/file1 directory/file2 ..\n\n", progname);
#else
	fprintf(stderr, "\nusage:\n%s [-]{crtux} [ABb-CDdFfHhilMmNnOopsVvw] [archivefile] [blocksize]\n   [archivenumber] [maxarchive #] [mediasize] [archivefile]\n   directory/file1 directory/file2 ..\n\n", progname);
#endif

#ifndef PRO
	/* If user has selected HELP mode, give an expanded version
	 * of the letters and switches. Useful on small systems
	 * that don't have man pages and user doesn't have a
	 * manual set handy.
	 */
if (HELP) {
	printf("%s: One of the function keys enclosed in  {}  is required.\n\n", progname);
	printf("%s: c = create new archive, previous content is overwritten\n", progname);
	printf("%s: r = revise archive by adding files to end of current content\n", progname);
	printf("%s: t = give table of contents with verbosity defined by v or V\n", progname);
	printf("%s: u = update archive. Add files to end either if they are not already there\n", progname);
	printf("%s:     or if they have been modified since last put to archive. \n", progname);
	printf("%s: x = extract files from the named archive\n", progname);
	
	printf("\n%s: Items enclosed in  []  are optional\n\n", progname);
	printf("\n\n\n\n\n%s: Press RETURN to continue ..", progname); 
	response();
	fprintf(stderr,"\n\n");

	printf("%s: A = use next argument as archive number with which to begin output\n", progname);

	printf("%s: B = Invoke proper blocking logic for tar functions across machines\n", progname);
	printf("%s: b = use next argument as blocking factor for archive records\n", progname);
	printf("%s:-C = change directory to following file name (-C dirname)\n", progname);
	printf("%s: D = Directory output in original tar style\n", progname);
	printf("%s: d = select rx50 as output\n", progname);
#ifndef U11
	printf("%s:     (/dev/rra1a)\n", progname);
#endif

#ifdef U11
	printf("%s:     (/dev/rrx1)\n", progname);
#endif

	printf("%s: F[F] = operate in fast mode. Skip all SCCS directories, core files,\n", progname);
	printf("%s:        & errs file. FF also skips a.out and *.o files\n", progname);
	printf("%s: f = use following argument as the name of the archive\n", progname);
#ifdef U11
	printf("%s: g = use  /dev/rgt0  (6250 GCR)\n", progname);
#endif
	printf("%s: H = Help mode. Print this summary\n", progname);
	printf("%s: h = have a copy of a symbolically linked\n", progname);
	printf("%s:     file placed on archive instead of symbolic link\n", progname);
	printf("%s: i = ignore checksum errors in header blocks\n", progname);
#ifdef U11
	printf("%s: k = use  /dev/rtk0  (TK50)\n", progname);
#endif
	printf("%s: l = output link resolution error message summary\n", progname);
	printf("%s: M = Next arg specifies maximum archive number to be written and\n",progname);
	printf("%s:     prints current archive number on output line\n",progname);
	printf("%s: m = do not restore modification times. Use time of extraction\n", progname);

	printf("\n\n%s: Press RETURN to continue ..", progname); 
	response();
	fprintf(stderr,"\n\n");

	printf("%s: N = No multi-archive, file splitting, or new header format on output\n", progname);
	printf("%s:     Output directories in previous tar format. On input, set file\n",progname);
	printf("%s:     UID & GID from file header vs. values in /etc/passwd & group files\n",progname);


	printf("%s: n = select 800 bpi tape device (/dev/rmt0)\n", progname);
	printf("%s: O = include file owner & group names in verbose output (t & x functions)\n",progname);
	printf("%s:     if present in archive header.\n",progname);
	printf("%s:     Output warning message if owner or group name not found in\n",progname);
	printf("%s:     /etc/passwd or /etc/group file (cru functions)\n",progname);
	printf("%s: o = omit directory blocks from output archive\n", progname);

	printf("%s: p = change permissions and owner of extracted files to original values\n", progname);
	printf("%s:     (only works if you are the super user)\n", progname);
	printf("%s: S = Output User Group Standard archive format\n",progname);
	printf("%s: s = next argument specifies size of archive in 512 byte blocks\n", progname);
	printf("%s: V = big verbose. Most informative information about current operation\n", progname);
	printf("%s: v = verbose mode. Provide additional information about files/operation\n", progname);
	printf("%s: w = wait mode. Ask for user confirmation before including specified file\n", progname);
	printf("%s: 0..9 select the named unit number for archive device\n\n\n\n", progname);
}/*E if HELP */

#endif /*ndef PRO*/

done(FAIL);

}/*E usage() aka useage*/