[comp.sources.atari.st] v02i052: gnutar -- GNU's version of UNIX "tar" part06/08

koreth%panarthea.ebay@sun.com (Steven Grimm) (07/20/89)

Submitted-by: 7103_300@uwovax.uwo.ca (Eric R. Smith)
Posting-number: Volume 2, Issue 52
Archive-name: gnutar/part06

#!/bin/sh
# this is part 6 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file TAR.C continued
#
CurArch=6
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file TAR.C"
sed 's/^X//' << 'SHAR_EOF' >> TAR.C
X			add_exclude(optarg);
X			break;
X
X		case 'z':		/* Easy to type */
X		case 'Z':		/* Like the filename extension .Z */
X			f_compress++;
X			break;
X
X		case '?':
X		badopt:
X			describe();
X			exit(EX_ARGSBAD);
X
X		}
X	}
X
X	blocksize = blocking * RECORDSIZE;
X}
X
X
X/* 
X * Print as much help as the user's gonna get.
X *
X * We have to sprinkle in the KLUDGE lines because too many compilers
X * cannot handle character strings longer than about 512 bytes.  Yuk!
X * In particular, MSDOS MSC 4.0 (and 5.0) and PDP-11 V7 Unix have this
X * problem.
X */
Xvoid
Xdescribe()
X{
X	fprintf(stderr,"%s: choose one of the following:\n",tar);
X	fputs("\
X-A	Append tar files to an archive.\n\
X-c	Create a new archive.\n\
X-d	Find differences between archive and file system.\n\
X-D	Delete from the archive (NOT for use on mag tapes!)\n\
X-r	Append files to the end of an archive.\n\
X-t	List the contents of an archive.\n\
X-u	Only append files that are newer than the copy in an archive.\n\
X-x	Extract files from an archive.\n",stderr);
X
X	fputs("\
XOther options:\n\
X-b N	blocking factor N (block size = Nx512 bytes)\n\
X-B	reblock as we read (for reading 4.2BSD pipes)\n\
X-C dir	change to directory DIR\n\
X", stderr); /* KLUDGE */ fputs("\
X-f F	read/write archive from file or device F (or hostname:/ForD)\n\
X-G	create/list/extract GNU-format backup\n\
X-h	don't dump symbolic links; dump the files they point to\n\
X-i	ignore blocks of zeros in the archive, which normally mean EOF\n\
X-k	keep existing files, don't overwrite them from the archive\n\
X-K file	begin at \"file\" in the archive\n\
X-l	stay in the local file system (like dump(8)) when creating an archive\n\
X", stderr); /* KLUDGE */ fputs("\
X-m	don't extract file modified time\n\
X-M	create/list/extract multi-volume archive\n\
X-N date	only store files newer than DATE\n\
X-o	write an old V7 format archive, rather than ANSI [draft 6] format\n\
X-O	extract files to standard output\n\
X-p	do extract all protection information\n\
X-R	dump record number within archive with each message\n\
X-s	list of names to extract is sorted to match the archive\n\
X", stderr); /* KLUDGE */ fputs("\
X-T F	get names to extract or create from file F\n\
X-v	verbosely list what files we process\n\
X-V vnam	create archive with volume name 'vnam'\n\
X-w	ask for confirmation for every action\n\
X-W	attempt to verify the archive after writing it\n\
X-X file	eXclude files listed in \"file\"\n\
X-z or Z	run the archive through compress(1)\n\
X-[0-7][lmh] specify drive and density\n\
X", stderr);
X}
X
X
X/*
X * Set up to gather file names for tar.
X *
X * They can either come from stdin or from argv.
X */
Xname_init(argc, argv)
X	int	argc;
X	char	**argv;
X{
X
X	if (f_namefile) {
X		if (optind < argc) {
X			fprintf(stderr, "tar: too many args with -T option\n");
X			exit(EX_ARGSBAD);
X		}
X		if (!strcmp(name_file, "-")) {
X			namef = stdin;
X		} else {
X			namef = fopen(name_file, "r");
X			if (namef == NULL) {
X				msg_perror("can't open file %s",name_file);
X				exit(EX_BADFILE);
X			}
X		}
X	} else {
X		/* Get file names from argv, after options. */
X		n_argc = argc;
X		n_argv = argv;
X	}
X}
X
X/*
X * Get the next name from argv or the name file.
X *
X * Result is in static storage and can't be relied upon across two calls.
X */
X
X/* C is non-zero if we should deal with -C */
Xchar *
Xname_next(c)
X{
X	static char	buffer[NAMSIZ+2];	/* Holding pattern */
X	register char	*p;
X	register char	*q;
X	extern char *un_quote_string();
X
X tryagain:
X	if (namef == NULL) {
X		/* Names come from argv, after options */
X		if (optind < n_argc) {
X			/* JF trivial support for -C option.  I don't know if
X			   chdir'ing at this point is dangerous or not.
X			   It seems to work, which is all I ask. */
X			if(c && n_argv[optind][0]=='-' && n_argv[optind][1]=='C' && n_argv[optind][2]=='\0') {
X				optind++;
X				if(chdir(n_argv[optind])<0) {
X					msg_perror("Can't chdir to %s",n_argv[optind]);
X				}
X				optind++;
X			}
X			/* End of JF quick -C hack */
X
X			if(f_exclude && check_exclude(n_argv[optind])) {
X				optind++;
X				goto tryagain;
X			}
X			return un_quote_string(n_argv[optind++]);
X		}
X		return (char *)NULL;
X	}
X	while(p = fgets(buffer, NAMSIZ+1 /*nl*/, namef)) {
X		q = p+strlen(p)-1;		/* Find the newline */
X		if (q <= p)			/* Ignore empty lines */
X			continue;
X		*q-- = '\0';			/* Zap the newline */
X		while (q > p && *q == '/')	/* Zap trailing /s */
X			*q-- = '\0';
X		if(f_exclude && check_exclude(p))
X			goto tryagain;
X		return un_quote_string(p);
X	}
X	return NULL;
X}
X
X
X/*
X * Close the name file, if any.
X */
Xname_close()
X{
X
X	if (namef != NULL && namef != stdin) fclose(namef);
X}
X
X
X/*
X * Gather names in a list for scanning.
X * Could hash them later if we really care.
X *
X * If the names are already sorted to match the archive, we just
X * read them one by one.  name_gather reads the first one, and it
X * is called by name_match as appropriate to read the next ones.
X * At EOF, the last name read is just left in the buffer.
X * This option lets users of small machines extract an arbitrary
X * number of files by doing "tar t" and editing down the list of files.
X */
Xname_gather()
X{
X	register char *p;
X	static struct name namebuf[1];	/* One-name buffer */
X	static char *chdir_name;
X
X	if (f_sorted_names) {
X		p = name_next(0);
X		if (p) {
X			if(*p=='-' && p[1]=='C' && p[2]=='\0') {
X				chdir_name=name_next(0);
X				p=name_next(0);
X				if(!p) {
X					fprintf(stderr,"Missing file name after -C\n");
X					exit(EX_ARGSBAD);
X				}
X				namebuf[0].change_dir=chdir_name;
X			}
X			namebuf[0].length = strlen(p);
X			if (namebuf[0].length >= sizeof namebuf[0].name) {
X				fprintf(stderr, "Argument name too long: %s\n",
X					p);
X				namebuf[0].length = (sizeof namebuf[0].name) - 1;
X			}
X			strncpy(namebuf[0].name, p, namebuf[0].length);
X			namebuf[0].name[ namebuf[0].length ] = 0;
X			namebuf[0].next = (struct name *)NULL;
X			namebuf[0].found = 0;
X			namelist = namebuf;
X			namelast = namelist;
X		}
X		return;
X	}
X
X	/* Non sorted names -- read them all in */
X	while (p = name_next(0))
X		addname(p);
X}
X
X/*
X * Add a name to the namelist.
X */
Xaddname(name)
X	char	*name;			/* pointer to name */
X{
X	register int	i;		/* Length of string */
X	register struct name	*p;	/* Current struct pointer */
X	static char *chdir_name;
X	char *new_name();
X#define MAXPATHLEN 1024
X
X	if(name[0]=='-' && name[1]=='C' && name[2]=='\0') {
X		chdir_name=name_next(0);
X		name=name_next(0);
X		if(!name) {
X			fprintf(stderr,"Missing file name after -C\n");
X			exit(EX_ARGSBAD);
X		}
X		if(chdir_name[0]!='/') {
X			char path[MAXPATHLEN];
X#ifdef MSDOS
X			int getcwd();
X
X			if(!getcwd(path,MAXPATHLEN))
X				fprintf(stderr,"Couldn't get current dir\n");
X				exit(EX_SYSTEM);
X#else
X			char *getwd();
X
X			if(!getwd(path)) {
X				fprintf(stderr,"Couldn't get current dir: %s\n",path);
X				exit(EX_SYSTEM);
X			}
X#endif
X			chdir_name=new_name(path,chdir_name);
X		}
X	}
X
X	i = strlen(name);
X	/*NOSTRICT*/
X	p = (struct name *)
X		malloc((unsigned)(i + sizeof(struct name) - NAMSIZ));
X	if (!p) {
X		fprintf(stderr,"tar: cannot allocate mem for name %s\n",name);
X		exit(EX_SYSTEM);
X	}
X	p->next = (struct name *)NULL;
X	p->length = i;
X	strncpy(p->name, name, i);
X	p->name[i] = '\0';	/* Null term */
X	p->found = 0;
X	p->regexp = 0;		/* Assume not a regular expression */
X	p->firstch = 1;		/* Assume first char is literal */
X	p->change_dir=chdir_name;
X	p->dir_contents = 0;	/* JF */
X	if (index(name, '*') || index(name, '[') || index(name, '?')) {
X		p->regexp = 1;	/* No, it's a regexp */
X		if (name[0] == '*' || name[0] == '[' || name[0] == '?')
X			p->firstch = 0;		/* Not even 1st char literal */
X	}
X
X	if (namelast) namelast->next = p;
X	namelast = p;
X	if (!namelist) namelist = p;
X}
X
Xadd_dir_to_name(name,dirc)
Xchar *name;
Xchar *dirc;
X{
X	struct name *n;
X
X	for(n=namelist;n;n=n->next) {
X		if(!strcmp(n->name,name)) {
X			n->dir_contents = dirc;
X			return;
X		}
X	}
X}
X
X/*
X * Match a name from an archive, p, with a name from the namelist.
X */
Xname_match(p)
X	register char *p;
X{
X	register struct name	*nlp;
X	register int		len;
X
Xagain:
X	if (0 == (nlp = namelist))	/* Empty namelist is easy */
X		return 1;
X	len = strlen(p);
X	for (; nlp != 0; nlp = nlp->next) {
X		/* If first chars don't match, quick skip */
X		if (nlp->firstch && nlp->name[0] != p[0])
X			continue;
X
X		/* Regular expressions */
X		if (nlp->regexp) {
X			if (wildmat(p, nlp->name)) {
X				nlp->found = 1;	/* Remember it matched */
X				if(f_startfile) {
X					free((void *)namelist);
X					namelist=0;
X				}
X				return 1;	/* We got a match */
X			}
X			continue;
X		}
X
X		/* Plain Old Strings */
X		if (nlp->length <= len		/* Archive len >= specified */
X		 && (p[nlp->length] == '\0' || p[nlp->length] == '/')
X						/* Full match on file/dirname */
X		 && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
X		{
X			nlp->found = 1;		/* Remember it matched */
X			if(f_startfile) {
X				free((void *)namelist);
X				namelist = 0;
X			}
X			return 1;		/* We got a match */
X		}
X	}
X
X	/*
X	 * Filename from archive not found in namelist.
X	 * If we have the whole namelist here, just return 0.
X	 * Otherwise, read the next name in and compare it.
X	 * If this was the last name, namelist->found will remain on.
X	 * If not, we loop to compare the newly read name.
X	 */
X	if (f_sorted_names && namelist->found) {
X		name_gather();		/* Read one more */
X		if (!namelist->found) goto again;
X	}
X	return 0;
X}
X
X
X/*
X * Print the names of things in the namelist that were not matched.
X */
Xnames_notfound()
X{
X	register struct name	*nlp;
X	register char		*p;
X
X	for (nlp = namelist; nlp != 0; nlp = nlp->next) {
X		if (!nlp->found) {
X			fprintf(stderr, "tar: %s not found in archive\n",
X				nlp->name);
X		}
X		/*
X		 * We could free() the list, but the process is about
X		 * to die anyway, so save some CPU time.  Amigas and
X		 * other similarly broken software will need to waste
X		 * the time, though.
X		 */
X#ifndef unix
X		if (!f_sorted_names)
X			free(nlp);
X#endif
X	}
X	namelist = (struct name *)NULL;
X	namelast = (struct name *)NULL;
X
X	if (f_sorted_names) {
X		while (0 != (p = name_next(1)))
X			fprintf(stderr, "tar: %s not found in archive\n", p);
X	}
X}
X
X/* These next routines were created by JF */
X
Xname_expand()
X{
X;
X}
X/* p is a directory.  Add all the files in P to the namelist.  If any of the
X   files is a directory, recurse on the subdirectory. . . */
Xstatic
Xadd_dir_name(p,device)
Xchar *p;
Xint device;
X{
X	char *new_buf;
X	char *p_buf;
X	char *get_dir_contents();
X	new_buf=get_dir_contents(p,device);
X
X	add_dir_to_name(p,new_buf);
X	{
X		char namebuf[NAMSIZ+2];
X		register int len;
X
X		(void)strcpy(namebuf,p);
X		len=strlen(namebuf);
X		if(namebuf[len-1]!='/') {
X			namebuf[len++]='/';
X			namebuf[len]='\0';
X		}
X		for(p_buf=new_buf;*p_buf;p_buf+=strlen(p_buf)+1) {
X			if(*p_buf=='D') {
X				(void)strcpy(namebuf+len,p_buf+1);
X				addname(namebuf);
X				add_dir_name(namebuf,device);
X			}
X		}
X	}
X}
X
Xchar *
Xget_dir_contents(p,device)
Xchar *p;
Xint device;
X{
X	register DIR *dirp;
X	register struct direct *d;
X	char namebuf[NAMSIZ+2];
X	register int len;
X	extern int errno;
X
X#ifdef	__GNU__
X	void *the_buffer;
X#else
X	char *the_buffer;
X#endif
X		char *buf,*p_buf;
X		char **vec,**p_vec;
X		int n_strs,n_size;
X		char *new_buf;
X		int dirent_cmp();
X
X	if(f_local_filesys && device<0) {
X		struct stat hs;
X
X		if (0 != f_follow_links ? stat(p, &hs) : lstat(p, &hs))
X			msg_perror("can't stat %s",namebuf);
X		else
X			device=hs.st_dev;
X	}
X	errno=0;
X	dirp=opendir(p);
X	if(!dirp) {
X		if(errno)
X			msg_perror("can't open directory %s",p);
X		else
X			msg("error opening directory %s",p);
X		return "\0\0\0\0";
X	}
X	(void) strcpy(namebuf,p);
X	if(p[strlen(p)-1]!='/')
X		(void) strcat(namebuf,"/");
X	len=strlen(namebuf);
X
X	the_buffer=init_buffer();
X	while(d=readdir(dirp)) {
X		struct stat hs;
X
X		/* Skip . and .. */
X		if(is_dot_or_dotdot(d->d_name))
X			continue;
X		if(DP_NAMELEN(d) + len >=NAMSIZ) {
X			msg("%s%s name too long: skipped",namebuf,d->d_name);
X			continue;
X		}
X		(void) strcpy(namebuf+len,d->d_name);
X		if (0 != f_follow_links? stat(namebuf, &hs): lstat(namebuf, &hs)) {
X			msg_perror("can't stat %s",namebuf);
X			continue;
X		}
X		if((f_new_files && new_time>hs.st_mtime &&
X 		    new_time>hs.st_ctime && (hs.st_mode&S_IFMT)!=S_IFDIR)
X		   || (f_local_filesys && device>=0 && device!=hs.st_dev))
X			add_buffer(the_buffer,"N",1);
X		else if((hs.st_mode&S_IFMT)==S_IFDIR)
X			add_buffer(the_buffer,"D",1);
X		else
X			add_buffer(the_buffer,"Y",1);
X		add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
X	}
X	add_buffer(the_buffer,"\000\000",2);
X	closedir(dirp);
X
X	/* Well, we've read in the contents of the dir, now sort them */
X	buf=get_buffer(the_buffer);
X	n_strs=0;
X	n_size=0;
X	for(p_buf=buf;*p_buf;) {
X		int tmp;
X
X		tmp=strlen(p_buf)+1;
X		n_strs++;
X		n_size+=tmp;
X		p_buf+=tmp;
X	}
X	if(n_strs==0) {
X		flush_buffer(the_buffer);
X		return "\0\0\0\0";
X	}
X	vec=(char **)malloc(sizeof(char *)*(n_strs+1));
X	for(p_vec=vec,p_buf=buf;*p_buf;p_buf+=strlen(p_buf)+1)
X		*p_vec++= p_buf;
X	*p_vec= 0;
X	qsort((void *)vec,n_strs,sizeof(char *),dirent_cmp);
X	new_buf=(char *)malloc(n_size+2);
X	for(p_vec=vec,p_buf=new_buf;*p_vec;p_vec++) {
X		char *p_tmp;
X
X		for(p_tmp= *p_vec;*p_buf++= *p_tmp++;)
X			;
X	}
X	*p_buf++='\0';
X	free(vec);
X	flush_buffer(the_buffer);
X	return new_buf;
X}
X
Xint dirent_cmp(p1,p2)
Xchar **p1,**p2;
X{
X	char *frst,*scnd;
X
X	frst= (*p1)+1;
X	scnd= (*p2)+1;
X
X	return strcmp(frst,scnd);
X}
X
X/* This is like name_match(), except that it returns a pointer to the name
X   it matched, and doesn't set ->found  The caller will have to do that
X   if it wants to.  Oh, and if the namelist is empty, it returns 0, unlike
X   name_match(), which returns TRUE */
X
Xstruct name *
Xname_scan(p)
Xregister char *p;
X{
X	register struct name	*nlp;
X	register int		len;
X
Xagain:
X	if (0 == (nlp = namelist))	/* Empty namelist is easy */
X		return 0;
X	len = strlen(p);
X	for (; nlp != 0; nlp = nlp->next) {
X		/* If first chars don't match, quick skip */
X		if (nlp->firstch && nlp->name[0] != p[0])
X			continue;
X
X		/* Regular expressions */
X		if (nlp->regexp) {
X			if (wildmat(p, nlp->name))
X				return nlp;	/* We got a match */
X			continue;
X		}
X
X		/* Plain Old Strings */
X		if (nlp->length <= len		/* Archive len >= specified */
X		 && (p[nlp->length] == '\0' || p[nlp->length] == '/')
X						/* Full match on file/dirname */
X		 && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
X			return nlp;		/* We got a match */
X	}
X
X	/*
X	 * Filename from archive not found in namelist.
X	 * If we have the whole namelist here, just return 0.
X	 * Otherwise, read the next name in and compare it.
X	 * If this was the last name, namelist->found will remain on.
X	 * If not, we loop to compare the newly read name.
X	 */
X	if (f_sorted_names && namelist->found) {
X		name_gather();		/* Read one more */
X		if (!namelist->found) goto again;
X	}
X	return (struct name *) 0;
X}
X
X/* This returns a name from the namelist which doesn't have ->found set.
X   It sets ->found before returning, so successive calls will find and return
X   all the non-found names in the namelist */
X
Xstruct name *gnu_list_name;
X
Xchar *
Xname_from_list()
X{
X	if(!gnu_list_name)
X		gnu_list_name = namelist;
X	while(gnu_list_name && gnu_list_name->found)
X		gnu_list_name=gnu_list_name->next;
X	if(gnu_list_name) {
X		gnu_list_name->found++;
X		if(gnu_list_name->change_dir)
X			if(chdir(gnu_list_name->change_dir)<0)
X				msg_perror("can't chdir to %s",gnu_list_name->change_dir);
X		return gnu_list_name->name;
X	}
X	return (char *)0;
X}
X
Xblank_name_list()
X{
X	struct name *n;
X
X	gnu_list_name = 0;
X	for(n=namelist;n;n=n->next)
X		n->found = 0;
X}
X
X/* Collect all the names from argv[] (or whatever), then expand them into
X   a directory tree, and put all the directories at the beginning. */
Xcollect_and_sort_names()
X{
X	struct name *n,*n_next;
X	int num_names;
X	int name_cmp();
X	char *merge_sort();
X
X	name_gather();
X
X	if(!namelist) addname(".");
X	for(n=namelist;n;n=n_next) {
X		n_next=n->next;
X		if(n->found || n->dir_contents)
X			continue;
X		if(n->regexp)		/* FIXME just skip regexps for now */
X			continue;
X		if(n->change_dir)
X			if(chdir(n->change_dir)<0) {
X				msg_perror("can't chdir to %s",n->change_dir);
X				continue;
X			}
X
X		if(is_a_directory(n->name)) {
X			n->found++;
X			add_dir_name(n->name,-1);
X		}
X	}
X
X	num_names=0;
X	for(n=namelist;n;n=n->next)
X		num_names++;
X	namelist=(struct name *)merge_sort((void *)namelist,num_names,(char *)(&(namelist->next))-(char *)namelist,name_cmp);
X
X	for(n=namelist;n;n=n->next) {
X		n->found=0;
X	}
X}
X
Xint name_cmp(n1,n2)
Xstruct name *n1,*n2;
X{
X	if(n1->found) {
X		if(n2->found)
X			return strcmp(n1->name,n2->name);
X		else
X			return -1;
X	} else if(n2->found)
X		return 1;
X	else
X		return strcmp(n1->name,n2->name);
X}
X
Xchar *new_name();
X
Xgnu_restore(name)
Xchar *name;
X{
X	char *current_dir;
X/*	int current_dir_length; */
X
X	char *archive_dir;
X/*	int archive_dir_length; */
X#ifdef __GNU__
X	void *the_buffer;
X#else
X	char *the_buffer;
X#endif
X	char	*p;
X	DIR	*dirp;
X	struct direct *d;
X	char *cur,*arc;
X	extern struct stat hstat;		/* Stat struct corresponding */
X	long size,copied;
X	char *from,*to;
X
X	dirp=opendir(name);
X
X	if(!dirp) {
X			/* The directory doesn't exist now.  It'll be created.
X			   In any case, we don't have to delete any files out
X			   of it */
X		return;
X	}
X
X	the_buffer=init_buffer();
X	while(d=readdir(dirp)) {
X		if(is_dot_or_dotdot(d->d_name))
X			continue;
X
X		add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
X	}
X	closedir(dirp);
X	add_buffer(the_buffer,"",1);
X
X	current_dir=get_buffer(the_buffer);
X	archive_dir=(char *)malloc(hstat.st_size);
X	if(archive_dir==0) {
X		fprintf(stderr,"Can't allocate %d bytes for restore\n",hstat.st_size);
X		return;
X	}
X	to=archive_dir;
X	for(size=hstat.st_size;size>0;size-=copied) {
X		from=findrec()->charptr;
X		if(!from) {
X			msg("Unexpected EOF in archive\n");
X			break;
X		}
X		copied=endofrecs()->charptr - from;
X		if(copied>size)
X			copied=size;
X		bcopy((void *)from,(void *)to,(int)copied);
X		to+=copied;
X		userec((union record *)(from+copied-1));
X	}
X
X	for(cur=current_dir;*cur;cur+=strlen(cur)+1) {
X		for(arc=archive_dir;*arc;arc+=strlen(arc)+1) {
X			arc++;
X			if(!strcmp(arc,cur))
X				break;
X		}
X		if(*arc=='\0') {
X			p=new_name(name,cur);
X			if(f_confirm && !confirm("delete",p)) {
X				free(p);
X				continue;
X			}
X			if(f_verbose)
X				printf("%s: deleting %s\n",p);
X			if(recursively_delete(p)) {
X				msg("%s: Error while deleting\n",p);
X			}
X			free(p);
X		}
X		
X	}
X	flush_buffer(the_buffer);
X	free(archive_dir);
X}
X
Xrecursively_delete(path)
Xchar *path;
X{
X	struct stat sbuf;
X	DIR *dirp;
X	struct direct *dp;
X	char *path_buf;
X	/* int path_len; */
X
X	
X	if(lstat(path,&sbuf)<0)
X		return 1;
X	if((sbuf.st_mode &S_IFMT)==S_IFDIR) {
X
X		/* path_len=strlen(path); */
X		dirp=opendir(path);
X		if(dirp==0)
X			return 1;
X		while(dp=readdir(dirp)) {
X			if(is_dot_or_dotdot(dp->d_name))
X				continue;
X			path_buf=new_name(path,dp->d_name);
X			if(recursively_delete(path_buf)) {
X				free(path_buf);
X				closedir(dirp);
X				return 1;
X			}
X			free(path_buf);
X		}
X		closedir(dirp);
X
X		if(rmdir(path)<0)
X			return 1;
X		return 0;
X	}
X	if(unlink(path)<0)
X		return 1;
X	return 0;
X}
X
Xchar *
Xnew_name(path,name)
Xchar *path,*name;
X{
X	char *path_buf;
X
X	path_buf=(char *)malloc(strlen(path)+strlen(name)+2);
X	if(path_buf==0) {
X		fprintf(stderr,"Can't allocate memory for name '%s/%s\n",path,name);
X		exit(EX_SYSTEM);
X	}
X	(void) sprintf(path_buf,"%s/%s",path,name);
X	return path_buf;
X}
X
X/* return non-zero if p is the name of a directory */
Xis_a_directory(p)
Xchar *p;
X{
X	struct stat stbuf;
X
X	if(lstat(p,&stbuf)<0) {
X		msg_perror("can't stat %s",p);
X		return 0;
X	}
X	if((stbuf.st_mode&S_IFMT)==S_IFDIR)
X		return 1;
X	return 0;
X}
X
X/* Returns non-zero if p is . or ..   This could be a macro for speed. */
Xis_dot_or_dotdot(p)
Xchar *p;
X{
X	return (p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0')));
X}
X
X/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
X
Xint
Xconfirm(action,file)
Xchar *action, *file;
X{
X	int	c,nl;
X	static FILE *confirm_file = 0;
X	extern FILE *msg_file;
X
X	fprintf(msg_file,"%s %s?", action, file);
X	if(!confirm_file) {
X#ifdef atarist
X		confirm_file = (archive == 0) ? fopen("CON:", 0) : stdin;
X#else
X		confirm_file = (archive == 0) ? fopen("/dev/tty") : stdin;
X#endif
X		if(!confirm_file) {
X			fprintf(stderr,"Can't read confirmation from user\n");
X			exit(EX_SYSTEM);
X		}
X	}
X	c=getc(confirm_file);
X	for(nl = c; nl != '\n' && nl != EOF; nl = getc(confirm_file))
X		;
X	return (c=='y' || c=='Y');
X}
X
Xchar *x_buffer = 0;
Xint size_x_buffer;
Xint free_x_buffer;
X
Xchar **exclude = 0;
Xint size_exclude = 0;
Xint free_exclude = 0;
X
Xchar **re_exclude = 0;
Xint size_re_exclude = 0;
Xint free_re_exclude = 0;
X
Xadd_exclude(file)
Xchar *file;
X{
X	FILE *fp;
X	char buf[1024];
X	extern char *rindex();
X
X	fp=fopen(file,"r");
X	if(!fp) {
X		msg_perror("can't open %s",file);
X		exit(2);
X	}
X	while(fgets(buf,1024,fp)) {
X		int size_buf;
X		char *end_str;
X
X		end_str=rindex(buf,'\n');
X		*end_str='\0';
X
X		un_quote_string(buf);
X		size_buf = strlen(buf);
X
X		if(x_buffer==0) {
X			x_buffer = (char *)ck_malloc(size_buf+1024);
X			free_x_buffer=1024;
X		} else if(free_x_buffer<=size_buf) {
X			char *old_x_buffer;
X			char **tmp_ptr;
X
X			old_x_buffer = x_buffer;
X			x_buffer = (char *)ck_realloc(x_buffer,size_x_buffer+1024);
X			free_x_buffer = 1024;
X			for(tmp_ptr=exclude;tmp_ptr<exclude+size_exclude;tmp_ptr++)
X				*tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
X			for(tmp_ptr=re_exclude;tmp_ptr<re_exclude+size_re_exclude;tmp_ptr++)
X				*tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer);
X		}
X
X		if(is_regex(buf)) {
X			if(free_re_exclude==0) {
X				re_exclude= (char **)(re_exclude ? ck_realloc(re_exclude,(size_re_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
X				free_re_exclude+=32;
X			}
X			re_exclude[size_re_exclude]=x_buffer+size_x_buffer;
X			size_re_exclude++;
X			free_re_exclude--;
X		} else {
X			if(free_exclude==0) {
X				exclude=(char **)(exclude ? ck_realloc(exclude,(size_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
X				free_exclude+=32;
X			}
X			exclude[size_exclude]=x_buffer+size_x_buffer;
X			size_exclude++;
X			free_exclude--;
X		}
X		strcpy(x_buffer+size_x_buffer,buf);
X		size_x_buffer+=size_buf+1;
X		free_x_buffer-=size_buf+1;
X	}
X	fclose(fp);
X}
X
Xint
Xis_regex(str)
Xchar *str;
X{
X	return index(str,'*') || index(str,'[') || index(str,'?');
X}
X
Xint
Xcheck_exclude(name)
Xchar *name;
X{
X	int n;
X
X	for(n=0;n<size_re_exclude;n++) {
X		if(wildmat(name,re_exclude[n]))
X			return 1;
X	}
X	for(n=0;n<size_exclude;n++) {
X		if(strstr(name,exclude[n]))
X			return 1;
X	}
X	return 0;
X}
SHAR_EOF
echo "File TAR.C is complete"
chmod 0644 TAR.C || echo "restore of TAR.C fails"
echo "x - extracting TAR.H (Text)"
sed 's/^X//' << 'SHAR_EOF' > TAR.H &&
X/*
X
X	Copyright (C) 1988 Free Software Foundation
X
XGNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
XWARRANTY.  No author or distributor accepts responsibility to anyone
Xfor the consequences of using it or for whether it serves any
Xparticular purpose or works at all, unless he says so in writing.
XRefer to the GNU tar General Public License for full details.
X
XEveryone is granted permission to copy, modify and redistribute GNU tar,
Xbut only under the conditions described in the GNU tar General Public
XLicense.  A copy of this license is supposed to have been given to you
Xalong with GNU tar so you can know your rights and responsibilities.  It
Xshould be in a file named COPYING.  Among other things, the copyright
Xnotice and this notice must be preserved on all copies.
X
XIn other words, go ahead and share GNU tar, but don't try to stop
Xanyone else from sharing it farther.  Help stamp out software hoarding!
X*/
X
X/*
X * Header file for tar (tape archive) program.
X *
X * @(#)tar.h 1.24 87/11/06
X *
X * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X */
X
X/*
X * Kludge for handling systems that can't cope with multiple
X * external definitions of a variable.  In ONE routine (tar.c),
X * we #define TAR_EXTERN to null; here, we set it to "extern" if
X * it is not already set.
X */
X#ifndef TAR_EXTERN
X#define TAR_EXTERN extern
X#endif
X
X/*
X * Header block on tape.
X *
X * I'm going to use traditional DP naming conventions here.
X * A "block" is a big chunk of stuff that we do I/O on.
X * A "record" is a piece of info that we care about.
X * Typically many "record"s fit into a "block".
X */
X#define	RECORDSIZE	512
X#define	NAMSIZ	100
X#define	TUNMLEN	32
X#define	TGNMLEN	32
X
Xunion record {
X	char		charptr[RECORDSIZE];
X	struct header {
X		char	name[NAMSIZ];
X		char	mode[8];
X		char	uid[8];
X		char	gid[8];
X		char	size[12];
X		char	mtime[12];
X		char	chksum[8];
X		char	linkflag;
X		char	linkname[NAMSIZ];
X		char	magic[8];
X		char	uname[TUNMLEN];
X		char	gname[TGNMLEN];
X		char	devmajor[8];
X		char	devminor[8];
X		/* these following fields were added by JF for gnu */
X		/* and are NOT standard */
X		char	atime[12];
X		char	ctime[12];
X		char	offset[12];
X		char	longnames[4];
X	} header;
X};
X
X/* The checksum field is filled with this while the checksum is computed. */
X#define	CHKBLANKS	"        "	/* 8 blanks, no null */
X
X/* The magic field is filled with this if uname and gname are valid. */
X#define	TMAGIC		"ustar  "	/* 7 chars and a null */
X
X/* The linkflag defines the type of file */
X#define	LF_OLDNORMAL	'\0'		/* Normal disk file, Unix compat */
X#define	LF_NORMAL	'0'		/* Normal disk file */
X#define	LF_LINK		'1'		/* Link to previously dumped file */
X#define	LF_SYMLINK	'2'		/* Symbolic link */
X#define	LF_CHR		'3'		/* Character special file */
X#define	LF_BLK		'4'		/* Block special file */
X#define	LF_DIR		'5'		/* Directory */
X#define	LF_FIFO		'6'		/* FIFO special file */
X#define	LF_CONTIG	'7'		/* Contiguous file */
X/* Further link types may be defined later. */
X
X#define LF_DUMPDIR	'D'		/* This is a dir entry that contains
X					   the names of files that were in
X					   the dir at the time the dump
X					   was made */
X#define LF_MULTIVOL	'M'		/* This is the continuation
X					   of a file that began on another
X					   volume */
X#define LF_VOLHDR	'V'		/* This file is a tape/volume header */
X					/* Ignore it on extraction */
X
X/*
X * Exit codes from the "tar" program
X */
X#define	EX_SUCCESS	0		/* success! */
X#define	EX_ARGSBAD	1		/* invalid args */
X#define	EX_BADFILE	2		/* invalid filename */
X#define	EX_BADARCH	3		/* bad archive */
X#define	EX_SYSTEM	4		/* system gave unexpected error */
X
X
X/*
X * Global variables
X */
XTAR_EXTERN union record	*ar_block;	/* Start of block of archive */
XTAR_EXTERN union record	*ar_record;	/* Current record of archive */
XTAR_EXTERN union record	*ar_last;	/* Last+1 record of archive block */
XTAR_EXTERN char		ar_reading;	/* 0 writing, !0 reading archive */
XTAR_EXTERN int		blocking;	/* Size of each block, in records */
XTAR_EXTERN int		blocksize;	/* Size of each block, in bytes */
XTAR_EXTERN char		*ar_file;	/* File containing archive */
XTAR_EXTERN char		*name_file;	/* File containing names to work on */
XTAR_EXTERN char		*tar;		/* Name of this program */
X
X/*
X * Flags from the command line
X */
XTAR_EXTERN int cmd_mode;
X#define CMD_NONE	0
X#define CMD_CAT		1		/* -A */
X#define CMD_CREATE	2		/* -c */
X#define CMD_DIFF	3		/* -d */
X#define CMD_APPEND	4		/* -r */
X#define CMD_LIST	5		/* -t */
X#define CMD_UPDATE	6		/* -u */
X#define CMD_EXTRACT	7		/* -x */
X#define CMD_DELETE	8		/* -D */
X
X					/* -[0-9][lmh] */
X			/* CMD_CAT	   -A */
X					/* -b */
XTAR_EXTERN char	f_reblock;		/* -B */
X			/* CMD_CREATE	   -c */
X					/* -C */
X			/* CMD_DIFF	   -d */
X/* TAR_EXTERN char	f_dironly;	/* -D */
X					/* -f */
XTAR_EXTERN char f_gnudump;		/* -G */
XTAR_EXTERN char	f_follow_links;		/* -h */
XTAR_EXTERN char	f_ignorez;		/* -i */
X			/* CMD_DELETE	   -J */
XTAR_EXTERN char	f_keep;			/* -k */
XTAR_EXTERN char f_startfile;		/* -K */
XTAR_EXTERN char f_local_filesys;	/* -l */
XTAR_EXTERN char	f_modified;		/* -m */
XTAR_EXTERN char f_multivol;		/* -M */
XTAR_EXTERN char f_new_files;		/* -N */
XTAR_EXTERN char	f_oldarch;		/* -o */
XTAR_EXTERN char f_exstdout;		/* -O */
XTAR_EXTERN char	f_use_protection;	/* -p */
XTAR_EXTERN char	f_sayblock;		/* -R */
XTAR_EXTERN char	f_sorted_names;		/* -s */
XTAR_EXTERN char	f_namefile;		/* -T */
X			/* CMD_UPDATE	   -u */
XTAR_EXTERN char	f_verbose;		/* -v */
XTAR_EXTERN char *f_volhdr;		/* -V */
XTAR_EXTERN char f_confirm;		/* -w */
XTAR_EXTERN char f_verify;		/* -W */
X			/* CMD_EXTRACT     -x */
XTAR_EXTERN char f_exclude;		/* -X */
XTAR_EXTERN char	f_compress;		/* -z */
X					/* -Z */
X
X/*
X * We now default to Unix Standard format rather than 4.2BSD tar format.
X * The code can actually produce all three:
X *	f_standard	ANSI standard
X *	f_oldarch	V7
X *	neither		4.2BSD
X * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
X * The only advantage to the "neither" option is that we can cmp(1) our
X * output to the output of 4.2BSD tar, for debugging.
X */
X#define		f_standard		(!f_oldarch)
X
X/*
X * Structure for keeping track of filenames and lists thereof.
X */
Xstruct name {
X	struct name	*next;
X	short		length;		/* cached strlen(name) */
X	char		found;		/* A matching file has been found */
X	char		firstch;	/* First char is literally matched */
X	char		regexp;		/* This name is a regexp, not literal */
X	char		*change_dir;	/* JF set with the -C option */
X	char		*dir_contents;	/* JF for f_gnudump */
X	char		name[NAMSIZ+1];
X};
X
XTAR_EXTERN struct name	*namelist;	/* Points to first name in list */
XTAR_EXTERN struct name	*namelast;	/* Points to last name in list */
X
XTAR_EXTERN int		archive;	/* File descriptor for archive file */
XTAR_EXTERN int		errors;		/* # of files in error */
X
X/*
X *
X * Due to the next struct declaration, each routine that includes
X * "tar.h" must also include <sys/types.h>.  I tried to make it automatic,
X * but System V has no defines in <sys/types.h>, so there is no way of
X * knowing when it has been included.  In addition, it cannot be included
X * twice, but must be included exactly once.  Argghh!
X *
X * Thanks, typedef.  Thanks, USG.
X */
Xstruct link {
X	struct link	*next;
X	dev_t		dev;
X	ino_t		ino;
X	short		linkcount;
X	char		name[NAMSIZ+1];
X};
X
XTAR_EXTERN struct link	*linklist;	/* Points to first link in list */
X
X
X/*
X * Error recovery stuff
X */
XTAR_EXTERN char		read_error_flag;
X
X
X/*
X * Declarations of functions available to the world.
X */
Xunion record *findrec();
Xvoid userec();
Xunion record *endofrecs();
Xvoid anno();
Xvoid msg();
Xvoid msg_perror();
X/* #define	 annorec(stream, msg)	anno(stream, msg, 0)	/* Cur rec */
X/* #define	annofile(stream, msg)	anno(stream, msg, 1)	/* Saved rec */
SHAR_EOF
chmod 0644 TAR.H || echo "restore of TAR.H fails"
echo "x - extracting TAR.TEX (Text)"
sed 's/^X//' << 'SHAR_EOF' > TAR.TEX &&
X\input texinfo @c -*-texinfo-*-
X
X@setfilename tar
X@settitle tar
X@ifinfo
XThis file documents the tape archive of the GNU system.
X
XCopyright (C) 1988 Free Software Foundation, Inc.
X
XPermission is granted to make and distribute verbatim copies of this
Xmanual provided the copyright notice and this permission notice are
Xpreserved on all copies.
X
X@ignore
XPermission is granted to process this file through TeX and print the
Xresults, provided the printed document carries copying permission
Xnotice identical to this one except for the removal of this paragraph
X(this paragraph not being relevant to the printed manual).
X
X@end ignore
XPermission is granted to copy and distribute modified versions of
Xthis manual under the conditions for verbatim copying, provided that
Xthe entire resulting derived work is distributed under the terms of
Xa permission notice identical to this one.
X
XPermission is granted to copy and distribute translations of this
Xmanual into another language, under the above conditions for
Xmodified versions.
X@end ifinfo
X
X@iftex
X@finalout
X@end iftex
X
X@titlepage
X@center @titlefont{tar}
X@sp 1
X@center The GNU tape archive
X@sp 2
X@center Jay Fenlason
X@sp 3
XThis manual describes the GNU tape archiver, @code{tar}, and how you
Xcan use it to store copies of a file or a group of files in an
X@dfn{archive}.  This archive may be written directly to a magnetic
Xtape or other storage medium, stored as a file, or sent through a
Xpipe to another program.  @code{tar} can also be used to add files
Xto an already existing archive, list the files in an archive, or
Xextract the files in the archive.
X
XGNU @code{tar} was written by John Gilmore, and modified by many
Xpeople.  The GNU enhancements were written by Jay Fenlason.
X@page
X@vskip 0pt plus 1filll
XCopyright @copyright{} 1988 Free Software Foundation, Inc.
X
XPermission is granted to make and distribute verbatim copies of
Xthis manual provided the copyright notice and this permission notice
Xare preserved on all copies.
X
X@ignore
XPermission is granted to process this file through Tex and print the
Xresults, provided the printed document carries copying permission
Xnotice identical to this one except for the removal of this paragraph
X(this paragraph not being relevant to the printed manual).
X
X@end ignore
XPermission is granted to copy and distribute modified versions of this
Xmanual under the conditions for verbatim copying, provided that the entire
Xresulting derived work is distributed under the terms of a permission
Xnotice identical to this one.
X
XPermission is granted to copy and distribute translations of this manual
Xinto another language, under the same conditions as for modified versions.
X
X@end titlepage
X
X@ifinfo
X@node Top, Why, , (dir)
X@ichapter Using the Tape Archiver
X@menu
X* Why::			What @code{tar} archives are good for.
X* Commands::		How to tell @code{tar} what to do.
X* Options::		Options that change the way @code{tar} behaves.
X* Format::		The format of a @code{tar} archive.
X@end menu
X@end ifinfo
X
X@node Why, Commands, Top, Top
X@chapter The Uses of Tape Archives
X
XThe tape archiver @code{tar} allows you to store many files in an
X@dfn{archive file} or @dfn{tar file} which describes the names and contents
Xof the constituent files.  Later you can extract some or all of these files
Xfrom the archive.
X
XTar files are not restricted to magnetic tapes.  The @code{tar} program
Xcan equally well use an ordinary file, or a pipe, or any device, as the
Xarchive.  But they were originally designed for use with magnetic tapes,
Xand that is how the name ``tar'' came about.
X
XArchive files can be used for transporting a group of files from one system
Xto another:  put all relevant files into an archive on one computer system,
Xtransfer the archive to another, and extract the contents there.  The basic
Xtransfer medium might be magnetic tape, Internet FTP, or even electronic
Xmail (though you must encode the archive with @code{uuencode} in order to
Xtransport it properly by mail).  Both machines do not have to use the same
Xoperating system, as long as they both support the @code{tar} program.
X
XA magnetic tape can store several files in sequence, but has no names for
Xthem, just relative position on the tape.  A tar file or something like it
Xis the only way to store several files on one tape and retain their names.
XEven when the basic transfer mechanism can keep track of names, as FTP can,
Xthe nuisance of handling multiple files, directories, and multiple links,
Xmay make a tar file a much easier method.
X
XArchive files are also used for long-term storage, which you can think
Xof as transportation from one time to another.
X
XPiping one @code{tar} to another is an easy way to copy a directory's
Xcontents from one disk to another, while preserving the dates, modes, owners
Xand link-structure of all the files therein.
X
XThe GNU version of @code{tar} has special features that allow it to be
Xused to make incremental and full dumps of all the files in a
Xfilesystem.
X
X@node Commands, Options, Why, Top
X@chapter The Different Operations @code{tar} Can Perform
X
XOne program, @code{tar}, is used to create an archive, to extract files
Xfrom an archive, to modify an archive, or to list the contents.  Each
Xtime you run @code{tar}, you must give a @dfn{command} to specify which
Xone of these things you want to do.
X
XThe command must always be in the first argument to @code{tar}.  This
Xargument can also contain options (@pxref{Options}).  For compatibility
Xwith Unix @code{tar}, the first argument is always treated as containing
Xcommand and option letters even if it doesn't start with @samp{-}.  Thus,
X@samp{tar c} is equivalent to @samp{tar -c}: both of them specify the
X@samp{-c} command to create an archive.
X
XThe remaining arguments to @code{tar} are either options, if they start
Xwith @samp{-}, or files to operate on.
X
XThe file names that you give as argument are the files that @code{tar} will
Xact on--for example, they are the files to put in the archive, or the files
Xto extract from it.  If you don't give any file name arguments, the default
Xdepends on which command you used.  Some commands use all relevant files;
Xsome commands have no default and will report an error if you don't specify
Xfiles.
X
XIf a file name argument actually names a directory, then that directory
Xand all files and subdirectories in it are used.
X
XHere is a list of all the @code{tar} commands:
X
X@table @samp
X@item -c
XThe @samp{-c} command tells @code{tar} to create a new archive that
Xcontains the file(s) specified on the command line.  If you don't
Xspecify files, all the files in the current directory are used.
X
XIf the archive file already exists, it is overwritten; the old contents
Xare lost.
X
X@item -d
XThe @samp{-d} command causes @code{tar} to compare the archive with
Xthe files in the file system.  It will report differences in file
Xsize, mode, owner, and contents.  If a file exists in the archive, but
Xnot in the file system, @code{tar} will report this.
X
XIf you specify file names, those files are compared with the tape and
Xthey must all exist in the archive.  If you don't specify files, all
Xthe files in the archive are compared.
X
X@item -r
XThe @samp{-r} command causes @code{tar} to add the specified file(s)
Xto the end of the archive.  This assumes that the archive file already
Xexists and is in the proper format (which probably means it was
Xcreated previously with the @code{tar} program).  If the archive is
Xnot in a format that @code{tar} understands, the results will be
Xunpredictable.
X
XYou must specify the files to be used; there is no default.
X
X@item -t
XThe @samp{-t} command causes @code{tar} to display a list of the files
Xin the archive.  If you specify file names, only the files
Xthat you specify will be mentioned (but each of them is mentioned only
Xif it appears in the archive).
X
X@item -u
XThe @samp{-u} command causes @code{tar} to add the specified files to
Xthe end of the archive, like @w{@samp{-r}}, but only when a file doesn't
Xalready exist in the archive or is newer than the version in the
Xarchive (last-modification time is compared).  This command can be
Xvery slow.
X
XYou must specify the files to be used; there is no default.
X
X@item -x
XThe @samp{-x} command causes @code{tar} to extract the specified files
Xfrom the archive.  If no file names are given, all the files in the
Xarchive will be extracted.
X
X@item -A
XThe @samp{-A} command is used for concatenating several archive files
Xinto one big archive file.  The files to operate on should all be
Xarchive files.  They are all appended to the end of @emph{the} archive
Xfile which @code{tar} works on.  (The other files are not changed).
X
XYou might be tempted to use @code{cat} for this, but it won't
Xordinarily work.  A @code{tar} archive contains data which indicates
Xthe end of the archive, so more material added to the end with
X@code{cat} would be ignored.  The @samp{tar -A} command works because
Xit removes the end-of-archive markers from the middle of the result.
X
X@item -D
XThe @samp{-D} command causes @code{tar} to delete the specified files
Xfrom the archive.  This command is extremely slow.  Warning:  Use of
Xthis command on archives stored on magnetic tape may result in a
Xscrambled archive.  There is no safe way (except for completely
Xre-writing the archive) to delete files from a magnetic tape.
X@end table
X
X@node Options, General Options, Commands, Top
X@chapter Options That Change How @code{tar} Works
X
XOptions may be specified as individual arguments starting with @samp{-}.
XIn this case, if the option wants an argument (as does, for example,
X@samp{-f}) then the argument should come after the option, separated
Xfrom it by a space.
XAll options are optional.  Some options make sense with any command, while
Xothers are meaningful only with particular commands.@refill
X
X@menu
X* General Options::
X* Creation Options::
X* Extraction Options::
X* Option Syntax::
X@end menu
X
X@node General Options, Creation Options, Options, Options
X@section Options That Are Always Meaningful
X
X@table @code
X@item -b @var{number}
XThis option is used to specify a @dfn{blocking factor} for the
Xarchive.  When reading or writing the archive, @code{tar}, will do
Xreads and writes of the archive in blocks of @var{number}*512 bytes.
X
XThe default blocking factor is set when @code{tar} is compiled, and is
Xtypically 20.
X
XBlocking factors larger than 20 cannot be read by very old versions of
X@code{tar}, or by some newer versions of @code{tar} running on old machines
Xwith small address spaces.
X
XWith a magnetic tape, larger blocks give faster throughput and fit
Xmore data on a tape (because there are fewer inter-record gaps).  If
Xthe archive is in a disk file or a pipe, you may want to specify a
Xsmaller blocking factor, since a large one will result in a large
Xnumber of null bytes at the end of the archive.
X
XWhen writing cartridge or other streaming tapes, a much larger
Xblocking factor (say 126 or more) will greatly increase performance.
XHowever, you must specify the same blocking factor when reading or
Xupdating the archive.
X
XWith GNU @code{tar} the blocking factor is limited only by the maximum
Xblock size of the device containing the archive, or by the amount of
Xavailable virtual memory.
X
X@item -f @var{filename}
XThis option is used to specify the file name of the archive @code{tar}
Xworks on.
X
XIf no @samp{-f} option is given, but the environment variable
X@code{TAPE} exists, its value is used; otherwise, a default archive
Xname (which was picked when @code{tar} was compiled) is used.  The
Xdefault is normally set up to be the ``first'' tape drive or other
Xtransportable I/O medium on the system.
X
XIf the filename is @samp{-}, @code{tar} reads the archive from
Xstandard input (when listing or extracting), or writes it to standard
Xoutput (when creating).  If the @samp{-} filename is given when
Xupdating an archive, @code{tar} will read the original archive from
Xits standard input, and will write the entire new archive to its
Xstandard output.
X
XIf the filename contains @samp{:/dev/}, it is interpreted as
X@samp{hostname:filename}.  If the @var{hostname} contains an ampersand
X(@samp{@@}), it is treated as @samp{user@@hostname:filename}.
XIn either case, @code{tar} will invoke the command @code{rsh}
X(or @code{remsh}) to start
Xup an @code{/etc/rmt} on the remote machine.  If you give an alternate login
Xname, it will be given to the @code{rsh}.  Naturally, the remote machine must
Xhave a copy of @file{/etc/rmt}.  @code{/etc/rmt} is free software
Xfrom the University of California, and a copy of the source code can be found
Xwith the sources for @code{tar}.  @code{/etc/rmt} will have to be modified to
Xrun on non-BSD4.3 systems.@refill
SHAR_EOF
echo "End of part 6"
echo "File TAR.TEX is continued in part 7"
echo "7" > s2_seq_.tmp
exit 0