[comp.sources.atari.st] v02i048: gnutar -- GNU's version of UNIX "tar" part02/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 48
Archive-name: gnutar/part02

#!/bin/sh
# this is part 2 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file COPYING continued
#
CurArch=2
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 COPYING"
sed 's/^X//' << 'SHAR_EOF' >> COPYING
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE GNU tar AS PERMITTED ABOVE, BE LIABLE TO
XYOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER
XSPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
XINABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
XBEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
XFAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GNU tar, EVEN
XIF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR
XANY CLAIM BY ANY OTHER PARTY.
SHAR_EOF
echo "File COPYING is complete"
chmod 0644 COPYING || echo "restore of COPYING fails"
echo "x - extracting CREATE.C (Text)"
sed 's/^X//' << 'SHAR_EOF' > CREATE.C &&
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 * Create a tar archive.
X *
X * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#)create.c 1.36 11/6/87 - gnu
X */
X#ifdef atarist
X#include <types.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X#include <stdio.h>
X
X/* JF: this one is my fault */
X/* #include "utils.h" */
X
X#ifndef V7
X#include <fcntl.h>
X#endif
X
X#ifndef	MSDOS
X#include <pwd.h>
X#include <grp.h>
X#endif
X
X#ifdef BSD42
X#include <sys/dir.h>
X#else
X#if (defined(MSDOS) && !defined(atarist))
X#include <sys/dir.h>
X#else
X#ifdef USG
X#include "dirent.h"
X#define direct dirent
X#define DP_NAMELEN(x) strlen((x)->d_name)
X#else
X/*
X * FIXME: On other systems there is no standard place for the header file
X * for the portable directory access routines.  Change the #include line
X * below to bring it in from wherever it is.
X */
X#include "dirent.h"
X#define direct dirent		/* If you have the POSIX version */
X#define DP_NAMELEN(x) strlen((x)->d_name) /* ditto */
X#endif
X#endif
X#endif
X
X#ifndef DP_NAMELEN
X#define DP_NAMELEN(x)	(x)->d_namlen
X#endif
X
X#ifdef USG
X#include <sys/sysmacros.h>	/* major() and minor() defined here */
X#endif
X
X/*
X * V7 doesn't have a #define for this.
X */
X#ifndef O_RDONLY
X#define	O_RDONLY	0
X#endif
X
X/*
X * Most people don't have a #define for this.
X */
X#ifndef	O_BINARY
X#define	O_BINARY	0
X#endif
X
X#include "tar.h"
X#include "port.h"
X
X#ifdef atarist
X#undef S_IFCHR
X#endif
X
Xextern union record *head;		/* Points to current tape header */
Xextern struct stat hstat;		/* Stat struct corresponding */
Xextern int head_standard;		/* Tape header is in ANSI format */
X
X/* JF */
Xextern struct name *gnu_list_name;
X
X/*
X * If there are no symbolic links, there is no lstat().  Use stat().
X */
X#ifndef S_IFLNK
X#define lstat stat
X#endif
X
Xextern char	*malloc();
Xextern char	*strcpy();
Xextern char	*strncpy();
Xextern void	bzero();
Xextern void	bcopy();
Xextern int	errno;
X
Xextern void print_header();
X
Xunion record *start_header();
Xvoid finish_header();
Xvoid finduname();
Xvoid findgname();
Xchar *name_next();
Xvoid to_oct();
Xvoid dump_file();
X
Xstatic nolinks;			/* Gets set if we run out of RAM */
X
Xvoid
Xcreate_archive()
X{
X	register char	*p;
X	char *name_from_list();
X
X	open_archive(0);		/* Open for writing */
X
X	if(f_gnudump) {
X		char buf[MAXNAMLEN],*q,*bufp;
X
X		collect_and_sort_names();
X
X		while(p=name_from_list())
X			dump_file(p,-1);
X		/* if(!f_dironly) { */
X			blank_name_list();
X			while(p=name_from_list()) {
X				strcpy(buf,p);
X				if(p[strlen(p)-1]!='/')
X					strcat(buf,"/");
X				bufp=buf+strlen(buf);
X				for(q=gnu_list_name->dir_contents;*q;q+=strlen(q)+1) {
X					if(*q=='Y') {
X						strcpy(bufp,q+1);
X						dump_file(buf,-1);
X					}
X				}
X			}
X		/* } */
X
X	} else {
X		while (p = name_next(1)) {
X			dump_file(p, -1);
X		}
X	}
X
X	write_eot();
X	close_archive();
X	name_close();
X}
X
X/*
X * Dump a single file.  If it's a directory, recurse.
X * Result is 1 for success, 0 for failure.
X * Sets global "hstat" to stat() output for this file.
X */
Xvoid
Xdump_file (p, curdev)
X	char	*p;			/* File name to dump */
X	int	curdev;			/* Device our parent dir was on */
X{
X	union record	*header;
X	char type;
X	extern char *save_name;		/* JF for multi-volume support */
X	extern long save_totsize;
X	extern long save_sizeleft;
X
X	extern time_t new_time;
X
X	if(f_confirm && !confirm("add",p))
X		return;
X
X	/*
X	 * Use stat if following (rather than dumping) 4.2BSD's
X	 * symbolic links.  Otherwise, use lstat (which, on non-4.2
X	 * systems, is #define'd to stat anyway.
X	 */
X	if (0 != f_follow_links? stat(p, &hstat): lstat(p, &hstat))
X	{
Xbadperror:
X		msg_perror("can't add file %s",p);
Xbadfile:
X		errors++;
X		return;
X	}
X
X	/* See if we only want new files, and check if this one is too old to
X	   put in the archive. */
X	if(f_new_files && new_time>hstat.st_mtime &&
X 	   new_time>hstat.st_ctime && (hstat.st_mode&S_IFMT)!=S_IFDIR) {
X		if(curdev<0) {
X			msg("%s: is unchanged; not dumped",p);
X		}
X		return;
X	}
X
X	/*
X	 * See if we are crossing from one file system to another,
X	 * and avoid doing so if the user only wants to dump one file system.
X	 */
X	if (f_local_filesys && curdev >= 0 && curdev != hstat.st_dev) {
X		msg("%s: is on a different filesystem; not dumped",p);
X		return;
X	}
X
X	/*
X	 * Check for multiple links.
X	 *
X	 * We maintain a list of all such files that we've written so
X	 * far.  Any time we see another, we check the list and
X	 * avoid dumping the data again if we've done it once already.
X	 */
X	if (hstat.st_nlink > 1) switch (hstat.st_mode & S_IFMT) {
X		register struct link	*lp;
X
X	case S_IFREG:			/* Regular file */
X#ifdef S_IFCTG
X	case S_IFCTG:			/* Contigous file */
X#endif
X#ifdef S_IFCHR
X	case S_IFCHR:			/* Character special file */
X#endif
X
X#ifdef S_IFBLK
X	case S_IFBLK:			/* Block     special file */
X#endif
X
X#ifdef S_IFIFO
X	case S_IFIFO:			/* Fifo      special file */
X#endif
X
X		/* First quick and dirty.  Hashing, etc later FIXME */
X		for (lp = linklist; lp; lp = lp->next) {
X			if (lp->ino == hstat.st_ino &&
X			    lp->dev == hstat.st_dev) {
X				/* We found a link. */
X				hstat.st_size = 0;
X				header = start_header(p, &hstat);
X				if (header == NULL) goto badfile;
X				strcpy(header->header.linkname,
X					lp->name);
X				header->header.linkflag = LF_LINK;
X				finish_header(header);
X		/* FIXME: Maybe remove from list after all links found? */
X				return;		/* We dumped it */
X			}
X		}
X
X		/* Not found.  Add it to the list of possible links. */
X		lp = (struct link *) malloc( (unsigned)
X			(strlen(p) + sizeof(struct link) - NAMSIZ));
X		if (!lp) {
X			if (!nolinks) {
X				fprintf(stderr,
X	"tar: no memory for links, they will be dumped as separate files\n");
X				nolinks++;
X			}
X		}
X		lp->ino = hstat.st_ino;
X		lp->dev = hstat.st_dev;
X		strcpy(lp->name, p);
X		lp->next = linklist;
X		linklist = lp;
X	}
X
X	/*
X	 * This is not a link to a previously dumped file, so dump it.
X	 */
X	switch (hstat.st_mode & S_IFMT) {
X
X	case S_IFREG:			/* Regular file */
X#ifdef S_IFCTG
X	case S_IFCTG:			/* Contigous file */
X#endif
X	{
X		int	f;		/* File descriptor */
X		long	bufsize, count;
X		register long	sizeleft;
X		register union record 	*start;
X
X		sizeleft = hstat.st_size;
X		/* Don't bother opening empty, world readable files. */
X		if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) {
X			f = open(p, O_RDONLY|O_BINARY);
X			if (f < 0) goto badperror;
X		} else {
X			f = -1;
X		}
X
X		header = start_header(p, &hstat);
X		if (header == NULL) goto badfile;
X#ifdef S_IFCTG
X		/* Mark contiguous files, if we support them */
X		if (f_standard && (hstat.st_mode & S_IFMT) == S_IFCTG) {
X			header->header.linkflag = LF_CONTIG;
X		}
X#endif
X		finish_header(header);
X
X		while (sizeleft > 0) {
X			if(f_multivol) {
X				save_name = p;
X				save_sizeleft = sizeleft;
X				save_totsize = hstat.st_size;
X			}
X			start = findrec();
X			bufsize = endofrecs()->charptr - start->charptr;
X			if (sizeleft < bufsize) {
X				/* Last read -- zero out area beyond */
X				bufsize = (int)sizeleft;
X				count = bufsize % RECORDSIZE;
X				if (count) 
X					bzero(start->charptr + sizeleft,
X						(int)(RECORDSIZE - count));
X			}
X			count = read(f, start->charptr, bufsize);
X			if (count < 0) {
X				msg_perror("read error at byte %ld, reading\
X %d bytes, in file %s",  hstat.st_size - sizeleft, bufsize,p);
X				goto padit;
X			}
X			sizeleft -= count;
X
X			/* This is nonportable (the type of userec's arg). */
X			userec(start+(count-1)/RECORDSIZE);
X
X			if (count == bufsize) continue;
X			msg( "file %s shrunk by %d bytes, padding with zeros.\n", p, sizeleft);
X			goto padit;		/* Short read */
X		}
X
X		if(f_multivol)
X			save_name = 0;
X
X		if (f >= 0)
X			(void)close(f);
X
X		break;
X
X		/*
X		 * File shrunk or gave error, pad out tape to match
X		 * the size we specified in the header.
X		 */
X	padit:
X		while(sizeleft>0) {
X			save_sizeleft=sizeleft;
X			start=findrec();
X			bzero(start->charptr,RECORDSIZE);
X			userec(start);
X			sizeleft-=RECORDSIZE;
X		}
X		if(f_multivol)
X			save_name=0;
X		if(f>=0)
X			(void)close(f);
X		break;
X/*		abort(); */
X	}
X
X#ifdef S_IFLNK
X	case S_IFLNK:			/* Symbolic link */
X	{
X		int size;
X
X		hstat.st_size = 0;		/* Force 0 size on symlink */
X		header = start_header(p, &hstat);
X		if (header == NULL) goto badfile;
X		size = readlink(p, header->header.linkname, NAMSIZ);
X		if (size < 0) goto badperror;
X		if (size == NAMSIZ) {
X			msg("symbolic link %s too long\n",p);
X			break;
X		}
X		header->header.linkname[size] = '\0';
X		header->header.linkflag = LF_SYMLINK;
X		finish_header(header);		/* Nothing more to do to it */
X	}
X		break;
X#endif
X
X	case S_IFDIR:			/* Directory */
X	{
X		register DIR *dirp;
X		register struct direct *d;
X		char namebuf[NAMSIZ+2];
X		register int len;
X		int our_device = hstat.st_dev;
X
X		/* Build new prototype name */
X		strncpy(namebuf, p, sizeof (namebuf));
X		len = strlen(namebuf);
X		while (len >= 1 && '/' == namebuf[len-1]) 
X			len--;			/* Delete trailing slashes */
X		namebuf[len++] = '/';		/* Now add exactly one back */
X		namebuf[len] = '\0';		/* Make sure null-terminated */
X
X		/*
X		 * Output directory header record with permissions
X		 * FIXME, do this AFTER files, to avoid R/O dir problems?
X		 * If old archive format, don't write record at all.
X		 */
X		if (!f_oldarch) {
X			hstat.st_size = 0;	/* Force 0 size on dir */
X			/*
X			 * If people could really read standard archives,
X			 * this should be:		(FIXME)
X			header = start_header(f_standard? p: namebuf, &hstat);
X			 * but since they'd interpret LF_DIR records as
X			 * regular files, we'd better put the / on the name.
X			 */
X			header = start_header(namebuf, &hstat);
X			if (header == NULL)
X				goto badfile;	/* eg name too long */
X
X			if (f_gnudump)
X				header->header.linkflag = LF_DUMPDIR;
X			else if (f_standard)
X				header->header.linkflag = LF_DIR;
X
X			/* If we're gnudumping, we aren't done yet so don't close it. */
X			if(!f_gnudump)
X				finish_header(header);	/* Done with directory header */
X		}
X
X		/* Hack to remove "./" from the front of all the file names */
X		if (len == 2 && namebuf[0] == '.') {
X			len = 0;
X		}
X
X		if(f_gnudump) {
X			int sizeleft;
X			int totsize;
X			int bufsize;
X			union record *start;
X			int count;
X			char *buf,*p_buf;
X
X			buf=gnu_list_name->dir_contents; /* FOO */
X			totsize=0;
X			for(p_buf=buf;*p_buf;) {
X				int tmp;
X
X				tmp=strlen(p_buf)+1;
X				totsize+=tmp;
X				p_buf+=tmp;
X			}
X			totsize++;
X			to_oct((long)totsize,1+12,header->header.size);
X			finish_header(header);
X			p_buf=buf;
X			sizeleft=totsize;
X			while(sizeleft>0) {
X				if(f_multivol) {
X					save_name=p;
X					save_sizeleft=sizeleft;
X					save_totsize=totsize;
X				}
X				start=findrec();
X				bufsize=endofrecs()->charptr - start->charptr;
X				if(sizeleft<bufsize) {
X					bufsize=sizeleft;
X					count=bufsize%RECORDSIZE;
X					if(count)
X						bzero(start->charptr+sizeleft,RECORDSIZE-count);
X				}
X				bcopy(p_buf,start->charptr,bufsize);
X				sizeleft-=bufsize;
X				p_buf+=bufsize;
X				userec(start+(bufsize-1)/RECORDSIZE);
X			}
X			if(f_multivol)
X				save_name = 0;
X			break;
X		}
X
X		/* Now output all the files in the directory */
X		/* if (f_dironly)
X			break;		/* Unless the cmdline said not to */
X
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",
X					p);
X			}
X			break;
X		}
X
X		/* Should speed this up by cd-ing into the dir, FIXME */
X		while (NULL != (d=readdir(dirp))) {
X			/* Skip . and .. */
X			if(is_dot_or_dotdot(d->d_name))
X				continue;
X
X			if (DP_NAMELEN(d) + len >= NAMSIZ) {
X				msg("file name %s%s too long\n", 
X					namebuf, d->d_name);
X				continue;
X			}
X			strcpy(namebuf+len, d->d_name);
X			if(f_exclude && check_exclude(namebuf))
X				continue;
X			dump_file(namebuf, our_device);
X		}
X
X		closedir(dirp);
X	}
X		break;
X
X#ifdef S_IFCHR
X	case S_IFCHR:			/* Character special file */
X		type = LF_CHR;
X		goto easy;
X#endif
X
X#ifdef S_IFBLK
X	case S_IFBLK:			/* Block     special file */
X		type = LF_BLK;
X		goto easy;
X#endif
X
X#ifdef S_IFIFO
X	case S_IFIFO:			/* Fifo      special file */
X		type = LF_FIFO;
X#endif
X
X	easy:
X		if (!f_standard) goto unknown;
X
X		hstat.st_size = 0;		/* Force 0 size */
X		header = start_header(p, &hstat);
X		if (header == NULL) goto badfile;	/* eg name too long */
X
X		header->header.linkflag = type;
X		if (type != LF_FIFO) {
X			to_oct((long) major(hstat.st_rdev), 8,
X				header->header.devmajor);
X			to_oct((long) minor(hstat.st_rdev), 8,
X				header->header.devminor);
X		}
X
X		finish_header(header);
X		break;
X
X	default:
X	unknown:
X		msg("%s: Unknown file type; file ignored.\n", p);
X		break;
X	}
X}
X
X
X/*
X * Make a header block for the file  name  whose stat info is  st .
X * Return header pointer for success, NULL if the name is too long.
X */
Xunion record *
Xstart_header(name, st)
X	char	*name;
X	register struct stat *st;
X{
X	register union record *header;
X
X	header = (union record *) findrec();
X	bzero(header->charptr, sizeof(*header)); /* XXX speed up */
X
X	/*
X	 * Check the file name and put it in the record.
X	 */
X#ifdef MSDOS
X	if(name[1]==':') {
X		static int warned_once = 0;
X		name+=2;
X		if(!warned_once++) {
X			msg("Removing drive spec from names in the archive");
X		}
X	}
X#endif
X	while ('/' == *name) {
X		static int warned_once = 0;
X
X		name++;				/* Force relative path */
X		if (!warned_once++) {
X			msg("Removing leading / from absolute path names in the archive.");
X		}
X	}
X	strcpy(header->header.name, name);
X	if (header->header.name[NAMSIZ-1]) {
X		msg("%s: name too long\n", name);
X		return NULL;
X	}
X
X	to_oct((long) (st->st_mode & ~S_IFMT),
X					8,  header->header.mode);
X	to_oct((long) st->st_uid,	8,  header->header.uid);
X	to_oct((long) st->st_gid,	8,  header->header.gid);
X	to_oct((long) st->st_size,	1+12, header->header.size);
X	to_oct((long) st->st_mtime,	1+12, header->header.mtime);
X	/* header->header.linkflag is left as null */
X	if(f_gnudump) {
X		to_oct((long) st->st_atime, 1+12, header->header.atime);
X		to_oct((long) st->st_ctime, 1+12, header->header.ctime);
X	}
X
X#ifndef NONAMES
X	/* Fill in new Unix Standard fields if desired. */
X	if (f_standard) {
X		header->header.linkflag = LF_NORMAL;	/* New default */
X		strcpy(header->header.magic, TMAGIC);	/* Mark as Unix Std */
X		finduname(header->header.uname, st->st_uid);
X		findgname(header->header.gname, st->st_gid);
X	}
X#endif
X	return header;
X}
X
X/* 
X * Finish off a filled-in header block and write it out.
X * We also print the file name and/or full info if verbose is on.
X */
Xvoid
Xfinish_header(header)
X	register union record *header;
X{
X	register int	i, sum;
X	register char	*p;
X	void bcopy();
X
X	bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
X
X	sum = 0;
X	p = header->charptr;
X	for (i = sizeof(*header); --i >= 0; ) {
X		/*
X		 * We can't use unsigned char here because of old compilers,
X		 * e.g. V7.
X		 */
X		sum += 0xFF & *p++;
X	}
X
X	/*
X	 * Fill in the checksum field.  It's formatted differently
X	 * from the other fields:  it has [6] digits, a null, then a
X	 * space -- rather than digits, a space, then a null.
X	 * We use to_oct then write the null in over to_oct's space.
X	 * The final space is already there, from checksumming, and
X	 * to_oct doesn't modify it.
X	 *
X	 * This is a fast way to do:
X	 * (void) sprintf(header->header.chksum, "%6o", sum);
X	 */
X	to_oct((long) sum,	8,  header->header.chksum);
X	header->header.chksum[6] = '\0';	/* Zap the space */
X
X	userec(header);
X
X	if (f_verbose) {
X		/* These globals are parameters to print_header, sigh */
X		head = header;
X		/* hstat is already set up */
X		head_standard = f_standard;
X		print_header();
X	}
X
X	return;
X}
X
X
X/*
X * Quick and dirty octal conversion.
X * Converts long "value" into a "digs"-digit field at "where",
X * including a trailing space and room for a null.  "digs"==3 means
X * 1 digit, a space, and room for a null.
X *
X * We assume the trailing null is already there and don't fill it in.
X * This fact is used by start_header and finish_header, so don't change it!
X *
X * This should be equivalent to:
X *	(void) sprintf(where, "%*lo ", digs-2, value);
X * except that sprintf fills in the trailing null and we don't.
X */
Xvoid
Xto_oct(value, digs, where)
X	register long	value;
X	register int	digs;
X	register char	*where;
X{
X	
X	--digs;				/* Trailing null slot is left alone */
X	where[--digs] = ' ';		/* Put in the space, though */
X
X	/* Produce the digits -- at least one */
X	do {
X		where[--digs] = '0' + (char)(value & 7); /* one octal digit */
X		value >>= 3;
X	} while (digs > 0 && value != 0);
X
X	/* Leading spaces, if necessary */
X	while (digs > 0)
X		where[--digs] = ' ';
X
X}
X
X
X/*
X * Write the EOT record(s).
X * We actually zero at least one record, through the end of the block.
X * Old tar writes garbage after two zeroed records -- and PDtar used to.
X */
Xwrite_eot()
X{
X	union record *p;
X	int bufsize;
X	void bzero();
X
X	p = findrec();
X	bufsize = endofrecs()->charptr - p->charptr;
X	bzero(p->charptr, bufsize);
X	userec(p);
X}
SHAR_EOF
chmod 0644 CREATE.C || echo "restore of CREATE.C fails"
echo "x - extracting DIFFARCH.C (Text)"
sed 's/^X//' << 'SHAR_EOF' > DIFFARCH.C &&
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 * Diff files from a tar archive.
X *
X * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#) diffarch.c 1.10 87/11/11 - gnu
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifdef atarist
X#include <types.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#endif
X
X#ifdef atarist
X#include <file.h>
X#endif
X
X#ifdef USG
X#include <fcntl.h>
X#endif
X
X/* Some systems don't have these #define's -- we fake it here. */
X#ifndef O_RDONLY
X#define	O_RDONLY	0
X#endif
X#ifndef	O_NDELAY
X#define	O_NDELAY	0
X#endif
X
X#ifndef S_IFLNK
X#define lstat stat
X#endif
X
Xextern int errno;			/* From libc.a */
Xextern char *valloc();			/* From libc.a */
X
X#include "tar.h"
X#include "port.h"
X#include "rmt.h"
X
Xextern union record *head;		/* Points to current tape header */
Xextern struct stat hstat;		/* Stat struct corresponding */
Xextern int head_standard;		/* Tape header is in ANSI format */
X
Xextern void print_header();
Xextern void skip_file();
X
Xextern FILE *msg_file;
X
Xint now_verifying = 0;		/* Are we verifying at the moment? */
X
Xchar	*diff_name;		/* head->header.name */
X
Xint	diff_fd;		/* Descriptor of file we're diffing */
X
Xchar	*diff_buf = 0;		/* Pointer to area for reading
X					   file contents into */
X
Xchar	*diff_dir;		/* Directory contents for LF_DUMPDIR */
X
Xint different = 0;
X
X/*
X * Initialize for a diff operation
X */
Xdiff_init()
X{
X
X	/*NOSTRICT*/
X	diff_buf = (char *) valloc((unsigned)blocksize);
X	if (!diff_buf) {
X		msg("could not allocate memory for diff buffer of %d bytes\n",
X			blocking);
X		exit(EX_ARGSBAD);
X	}
X}
X
X/*
X * Diff a file against the archive.
X */
Xvoid
Xdiff_archive()
X{
X	register char *data;
X	int check, namelen;
X	int err;
X	long offset;
X	struct stat filestat;
X	char linkbuf[NAMSIZ+3];
X	int compare_chunk();
X	int compare_dir();
X	dev_t	dev;
X	ino_t	ino;
X	char *get_dir_contents();
X	long from_oct();
X	long lseek();
X
X#ifndef atarist
X	errno = EPIPE;			/* FIXME, remove perrors */
X#endif
X	saverec(&head);			/* Make sure it sticks around */
X	userec(head);			/* And go past it in the archive */
X	decode_header(head, &hstat, &head_standard, 1);	/* Snarf fields */
X
X	if(now_verifying)
X		fprintf(msg_file,"Verify ");
X	/* Print the record from 'head' and 'hstat' */
X	if (f_verbose)
X		print_header();
X
X	diff_name = head->header.name;
X	switch (head->header.linkflag) {
X
X	default:
X		msg("Unknown file type '%c' for %s, diffed as normal file\n",
X			head->header.linkflag, diff_name);
X		/* FALL THRU */
X
X	case LF_OLDNORMAL:
X	case LF_NORMAL:
X	case LF_CONTIG:
X		/*
X		 * Appears to be a file.
X		 * See if it's really a directory.
X		 */
X		namelen = strlen(diff_name)-1;
X		if (diff_name[namelen] == '/')
X			goto really_dir;
X
X		
X		if(do_stat(&filestat))
X			goto quit;
X
X		if ((filestat.st_mode & S_IFMT) != S_IFREG) {
X			fprintf(msg_file, "%s: not a regular file\n",
X				diff_name);
X			skip_file((long)hstat.st_size);
X			different++;
X			goto quit;
X		}
X
X		filestat.st_mode &= ~S_IFMT;
X		if (filestat.st_mode != hstat.st_mode)
X			sigh("mode");
X		if (filestat.st_uid  != hstat.st_uid)
X			sigh("uid");
X		if (filestat.st_gid  != hstat.st_gid)
X			sigh("gid");
X		if (filestat.st_mtime != hstat.st_mtime)
X			sigh("mod time");
X		if (filestat.st_size != hstat.st_size) {
X			sigh("size");
X			skip_file((long)hstat.st_size);
X			goto quit;
X		}
X
X		diff_fd = open(diff_name, O_NDELAY|O_RDONLY);
X
X		if (diff_fd < 0) {
X			msg_perror("cannot open %s",diff_name);
X			skip_file((long)hstat.st_size);
X			different++;
X			goto quit;
X		}
X
X		wantbytes((long)(hstat.st_size),compare_chunk);
X
X		check = close(diff_fd);
X		if (check < 0) {
X			msg_perror("Error while closing %s",diff_name);
X		}
X
X	quit:
X		break;
X
X	case LF_LINK:
X		if(do_stat(&filestat))
X			break;
X		dev = filestat.st_dev;
X		ino = filestat.st_ino;
X		err = stat(head->header.linkname, &filestat);
X		if (err < 0) {
X			if (errno==ENOENT) {
X				fprintf(msg_file, "%s: does not exist\n",diff_name);
X			} else {
X				msg_perror("cannot stat file %s",diff_name);
X			}
X			different++;
X			break;
X		}
X		if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
X			fprintf(msg_file, "%s not linked to %s\n",diff_name,head->header.linkname);
X			break;
X		}
X		break;
X
X#ifdef S_IFLNK
X	case LF_SYMLINK:
X		check = readlink(diff_name, linkbuf,
X				 (sizeof linkbuf)-1);
X		
X		if (check < 0) {
X			if (errno == ENOENT) {
X				fprintf(msg_file,
X					"%s: no such file or directory\n",
X					diff_name);
X			} else {
X				msg_perror("cannot read link %s",diff_name);
X			}
X			different++;
X			break;
X		}
X
X		linkbuf[check] = '\0';	/* Null-terminate it */
X		if (strncmp(head->header.linkname, linkbuf, check) != 0) {
X			fprintf(msg_file, "%s: symlink differs\n",
X				head->header.linkname);
X			different++;
X		}
X		break;
X#endif
X
X	case LF_CHR:
X		hstat.st_mode |= S_IFCHR;
X		goto check_node;
X
X#ifdef S_IFBLK
X	/* If local system doesn't support block devices, use default case */
X	case LF_BLK:
X		hstat.st_mode |= S_IFBLK;
X		goto check_node;
X#endif
X
X#ifdef S_IFIFO
X	/* If local system doesn't support FIFOs, use default case */
X	case LF_FIFO:
X		hstat.st_mode |= S_IFIFO;
X		hstat.st_rdev = 0;		/* FIXME, do we need this? */
X		goto check_node;
X#endif
X
X	check_node:
X		/* FIXME, deal with umask */
X		if(do_stat(&filestat))
X			break;
X		if(hstat.st_rdev != filestat.st_rdev) {
X			fprintf(msg_file, "%s: device numbers changed\n", diff_name);
X			different++;
X			break;
X		}
X		if(hstat.st_mode != filestat.st_mode) {
X			fprintf(msg_file, "%s: mode or device-type changed\n", diff_name);
X			different++;
X			break;
X		}
X		break;
X
X	case LF_DUMPDIR:
X		data=diff_dir=get_dir_contents(diff_name,0);
X		wantbytes((long)(hstat.st_size),compare_dir);
X		free(data);
X		/* FALL THROUGH */
X
X	case LF_DIR:
X		/* Check for trailing / */
X		namelen = strlen(diff_name)-1;
X	really_dir:
X		while (namelen && diff_name[namelen] == '/')
X			diff_name[namelen--] = '\0';	/* Zap / */
X
X		if(do_stat(&filestat))
X			break;
X		if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
X			fprintf(msg_file, "%s is no longer a directory\n",diff_name);
X			different++;
X			break;
X		}
X		if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
X			sigh("mode");
X		break;
X
X	case LF_VOLHDR:
X		break;
X
X	case LF_MULTIVOL:
X		namelen = strlen(diff_name)-1;
X		if (diff_name[namelen] == '/')
X			goto really_dir;
X
X		if(do_stat(&filestat))
X			break;
X
X		if ((filestat.st_mode & S_IFMT) != S_IFREG) {
X			fprintf(msg_file, "%s: not a regular file\n",
X				diff_name);
X			skip_file((long)hstat.st_size);
X			different++;
X			break;
X		}
X
X		filestat.st_mode &= ~S_IFMT;
X		offset = from_oct(1+12, head->header.offset);
X		if (filestat.st_size != hstat.st_size + offset) {
X			sigh("size");
X			skip_file((long)hstat.st_size);
X			different++;
X			break;
X		}
X
X		diff_fd = open(diff_name, O_NDELAY|O_RDONLY);
X
X		if (diff_fd < 0) {
X			msg_perror("cannot open file %s",diff_name);
X			skip_file((long)hstat.st_size);
X			different++;
X			break;
X		}
X		err = lseek(diff_fd, offset, 0);
X		if(err!=offset) {
X			msg_perror("cannot seek to %ld in file %s",offset,diff_name);
X			different++;
X			break;
X		}
X
X		wantbytes((long)(hstat.st_size),compare_chunk);
X
X		check = close(diff_fd);
X		if (check < 0) {
X			msg_perror("Error while closing %s",diff_name);
X		}
X		break;
X
X	}
X
X	/* We don't need to save it any longer. */
X	saverec((union record **) 0);	/* Unsave it */
X}
X
Xint
Xcompare_chunk(bytes,buffer)
Xint bytes;
Xchar *buffer;
X{
X	int err;
X
X	err=read(diff_fd,diff_buf,bytes);
X	if(err!=bytes) {
X		if(err<0) {
X			msg_perror("can't read %s",diff_name);
X		} else {
X			fprintf(msg_file,"%s: could only read %d of %d bytes\n",err,bytes);
X		}
X		different++;
X		return -1;
X	}
X	if(bcmp(buffer,diff_buf,bytes)) {
X		fprintf(msg_file, "%s: data differs\n",diff_name);
X		different++;
X		return -1;
X	}
X	return 0;
X}
X
Xint
Xcompare_dir(bytes,buffer)
Xint bytes;
Xchar *buffer;
X{
X	if(bcmp(buffer,diff_dir,bytes)) {
X		fprintf(msg_file, "%s: data differs\n",diff_name);
X		different++;
X		return -1;
X	}
X	diff_dir+=bytes;
X	return 0;
X}
X
X/*
X * Sigh about something that differs.
X */
Xsigh(what)
X	char *what;
X{
X
X	fprintf(msg_file, "%s: %s differs\n",
X		diff_name, what);
X}
X
Xverify_volume()
X{
X	int status;
X#ifdef MTIOCTOP
X	struct mtop t;
X	int er;
X#endif
X
X	if(!diff_buf)
X		diff_init();
X#ifdef MTIOCTOP
X	t.mt_op = MTBSF;
X	t.mt_count = 1;
X	if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
X		if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
X#endif
X			if(rmtlseek(archive,0L,0)!=0) {
X				/* Lseek failed.  Try a different method */
X				msg_perror("Couldn't rewind archive file for verify");
X				return;
X			}
X#ifdef MTIOCTOP
X		}
X	}
X#endif
X	ar_reading=1;
X	now_verifying = 1;
X	fl_read();
X	for(;;) {
X		status = read_header();
X		if(status==0) {
X			unsigned n;
X
X			n=0;
X			do {
X				n++;
X				status=read_header();
X			} while(status==0);
X			msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
X		}
X		if(status==2 || status==EOF)
X			break;
X		diff_archive();
X	}
X	ar_reading=0;
X	now_verifying = 0;
X}
X
Xint do_stat(statp)
Xstruct stat *statp;
X{
X	int err;
X
X	err = f_follow_links ? stat(diff_name, statp) : lstat(diff_name, statp);
X	if (err < 0) {
X		if (errno==ENOENT) {
X			fprintf(msg_file, "%s: does not exist\n",diff_name);
X		} else
X			msg_perror("can't stat file %s",diff_name);
X		skip_file((long)hstat.st_size);
X		different++;
X		return 1;
X	} else
X		return 0;
X}
SHAR_EOF
chmod 0644 DIFFARCH.C || echo "restore of DIFFARCH.C fails"
echo "x - extracting EXTRACT.C (Text)"
sed 's/^X//' << 'SHAR_EOF' > EXTRACT.C &&
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 * Extract files from a tar archive.
X *
X * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#) extract.c 1.32 87/11/11 - gnu
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifdef atarist
X#include <types.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#endif
X
X#ifdef USG
X#include <fcntl.h>
X#endif
X
X#ifdef	MSDOS
X#include <fcntl.h>
X#endif	/* MSDOS */
X
X/*
X * Some people don't have a #define for these.
X */
X#ifndef	O_BINARY
X#define	O_BINARY	0
X#endif
X#ifndef O_NDELAY
X#define	O_NDELAY	0
X#endif
X
X#ifdef NO_OPEN3
X/* We need the #define's even though we don't use them. */
X#include "open3.h"
X#endif
X
X#ifdef EMUL_OPEN3
X/* Simulated 3-argument open for systems that don't have it */
X#include "open3.h"
X#endif
X
Xextern int errno;			/* From libc.a */
Xextern time_t time();			/* From libc.a */
Xextern char *index();			/* From libc.a or port.c */
X
X#include "tar.h"
X#include "port.h"
X
Xextern FILE *msg_file;
X
Xextern union record *head;		/* Points to current tape header */
Xextern struct stat hstat;		/* Stat struct corresponding */
Xextern int head_standard;		/* Tape header is in ANSI format */
X
Xextern char *save_name;
Xextern long save_totsize;
Xextern long save_sizeleft;
X
Xextern void print_header();
Xextern void skip_file();
Xextern void pr_mkdir();
X
Xint make_dirs();			/* Makes required directories */
X
Xstatic time_t now = 0;			/* Current time */
Xstatic we_are_root = 0;			/* True if our effective uid == 0 */
Xstatic int notumask = ~0;		/* Masks out bits user doesn't want */
X
X/*
X * Set up to extract files.
X */
Xextr_init()
X{
X	int ourmask;
X
X	now = time((time_t *)0);
X	if (geteuid() == 0)
X		we_are_root = 1;
X
X	/*
X	 * We need to know our umask.  But if f_use_protection is set,
X	 * leave our kernel umask at 0, and our "notumask" at ~0.
X	 */
X	ourmask = umask(0);		/* Read it */
X	if (!f_use_protection) {
X		(void) umask (ourmask);	/* Set it back how it was */
X		notumask = ~ourmask;	/* Make umask override permissions */
X	}
X}
X
X
X/*
X * Extract a file from the archive.
X */
Xvoid
Xextract_archive()
X{
X	register char *data;
X	int fd, check, namelen, written, openflag;
X	long size;
X	time_t acc_upd_times[2];
X	register int skipcrud;
X
X	saverec(&head);			/* Make sure it sticks around */
X	userec(head);			/* And go past it in the archive */
X	decode_header(head, &hstat, &head_standard, 1);	/* Snarf fields */
X
X	if(f_confirm && !confirm("extract",head->header.name)) {
X		skip_file((long)hstat.st_size);
X		saverec((union record **)0);
X		return;
X	}
X
X	/* Print the record from 'head' and 'hstat' */
X	if (f_verbose)
X		print_header();
X
X	/*
X	 * Check for fully specified pathnames and other atrocities.
X	 *
X	 * Note, we can't just make a pointer to the new file name,
X	 * since saverec() might move the header and adjust "head".
X	 * We have to start from "head" every time we want to touch
X	 * the header record.
X	 */
X	skipcrud = 0;
X	while ('/' == head->header.name[skipcrud]) {
X		static int warned_once = 0;
X
X		skipcrud++;	/* Force relative path */
X		if (!warned_once++) {
X			msg("Removing leading / from absolute path names in the archive.");
X		}
X	}
X
X	switch (head->header.linkflag) {
X
X	default:
X		msg("Unknown file type '%c' for %s, extracted as normal file",
X			head->header.linkflag, skipcrud+head->header.name);
X		/* FALL THRU */
X
X	case LF_OLDNORMAL:
X	case LF_NORMAL:
X	case LF_CONTIG:
X		/*
X		 * Appears to be a file.
X		 * See if it's really a directory.
X		 */
X		namelen = strlen(skipcrud+head->header.name)-1;
X		if (head->header.name[skipcrud+namelen] == '/')
X			goto really_dir;
X
X		/* FIXME, deal with protection issues */
X	again_file:
X		openflag = f_keep?
X			O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
X			O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC;
X		if(f_exstdout) {
X			fd = 1;
X			goto extract_file;
X		}
X#ifdef O_CTG
X		/*
X		 * Contiguous files (on the Masscomp) have to specify
X		 * the size in the open call that creates them.
X		 */
X		if (head->header.lnkflag == LF_CONTIG)
X			fd = open(skipcrud+head->header.name, openflag | O_CTG,
X				hstat.st_mode, hstat.st_size);
X		else
X#endif
X		{
X#ifdef NO_OPEN3
X			/*
X			 * On raw V7 we won't let them specify -k (f_keep), but
X			 * we just bull ahead and create the files.
X			 */
X			fd = creat(skipcrud+head->header.name, 
X				hstat.st_mode);
X#else
X			/*
X			 * With 3-arg open(), we can do this up right.
X			 */
X			fd = open(skipcrud+head->header.name, openflag,
X				hstat.st_mode);
X#endif
X		}
X
X		if (fd < 0) {
X			if (make_dirs(skipcrud+head->header.name))
X				goto again_file;
X			msg_perror("Could not create file %s",skipcrud+head->header.name);
X			skip_file((long)hstat.st_size);
X			goto quit;
X		}
X
X	extract_file:
X		for (size = hstat.st_size;
X		     size > 0;
X		     size -= written) {
X
X			if(f_multivol) {
X				save_name=head->header.name;
X				save_totsize=hstat.st_size;
X				save_sizeleft=size;
X			}
X
X			/*
X			 * Locate data, determine max length
X			 * writeable, write it, record that
X			 * we have used the data, then check
X			 * if the write worked.
X			 */
X			data = findrec()->charptr;
X			if (data == NULL) {	/* Check it... */
X				msg("Unexpected EOF on archive file");
X				break;
X			}
X			written = endofrecs()->charptr - data;
X			if (written > size)
X				written = size;
X			errno = 0;
X			check = write(fd, data, written);
X			/*
X			 * The following is in violation of strict
X			 * typing, since the arg to userec
X			 * should be a struct rec *.  FIXME.
X			 */
X			userec((union record *)(data + written - 1));
X			if (check == written) continue;
X			/*
X			 * Error in writing to file.
X			 * Print it, skip to next file in archive.
X			 */
X			if(check<0)
X				msg_perror("couldn't write to file %s",skipcrud+head->header.name);
X			else
X				msg("could only write %d of %d bytes to file %s",written,check,skipcrud+head->header.name);
X			skip_file((long)(size - written));
X			break;	/* Still do the close, mod time, chmod, etc */
X		}
X
X		if(f_multivol)
X			save_name = 0;
X
X			/* If writing to stdout, don't try to do anything
X			   to the filename; it doesn't exist, or we don't
X			   want to touch it anyway */
X		if(f_exstdout)
X			break;
X
X		check = close(fd);
X		if (check < 0) {
X			msg_perror("Error while closing %s",skipcrud+head->header.name);
X		}
X		
X	set_filestat:
X#ifdef atarist
X	/* GEMDOS doesn't allow changing attributes of directories */
X		if (hstat.st_mode & S_IFDIR)
X			break;
X#endif
X		/*
X		 * Set the modified time of the file.
X		 * 
X		 * Note that we set the accessed time to "now", which
X		 * is really "the time we started extracting files".
X		 * unless f_gnudump is used, in which case .st_atime is used
X		 */
X		if (!f_modified) {
X			/* fixme if f_gnudump should set ctime too, but how? */
X			if(f_gnudump) acc_upd_times[0]=hstat.st_atime;
X			else acc_upd_times[0] = now;	         /* Accessed now */
X			acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
X			if (utime(skipcrud+head->header.name,
X			    acc_upd_times) < 0) {
X				msg_perror("couldn't change access and modification times of %s",skipcrud+head->header.name);
X			}
X		}
X
X		/*
X		 * If we are root, set the owner and group of the extracted
X		 * file.  This does what is wanted both on real Unix and on
X		 * System V.  If we are running as a user, we extract as that
X		 * user; if running as root, we extract as the original owner.
X		 */
X		if (we_are_root) {
X			if (chown(skipcrud+head->header.name, hstat.st_uid,
X				  hstat.st_gid) < 0) {
X				msg_perror("cannot chown file %s to uid %d gid %d",skipcrud+head->header.name,hstat.st_uid,hstat.st_gid);
X			}
X		}
X
X		/*
X		 * If '-k' is not set, open() or creat() could have saved
X		 * the permission bits from a previously created file,
X		 * ignoring the ones we specified.
X		 * Even if -k is set, if the file has abnormal
X		 * mode bits, we must chmod since writing or chown() has
X		 * probably reset them.
X		 *
X		 * If -k is set, we know *we* created this file, so the mode
X		 * bits were set by our open().   If the file is "normal", we
X		 * skip the chmod.  This works because we did umask(0) if -p
X		 * is set, so umask will have left the specified mode alone.
X		 */
X		if ((!f_keep)
X		    || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
X			if (chmod(skipcrud+head->header.name,
X				  notumask & (int)hstat.st_mode) < 0) {
X				msg_perror("cannot change mode of file %s to %ld",skipcrud+head->header.name,notumask & (int)hstat.st_mode);
X			}
X		}
X
X	quit:
X		break;
X
X	case LF_LINK:
X	again_link:
X		check = link (head->header.linkname,
X			      skipcrud+head->header.name);
X		if (check == 0)
X			break;
X		if (make_dirs(skipcrud+head->header.name))
X			goto again_link;
X		if(f_gnudump && errno==EEXIST)
X			break;
X		msg_perror("Could not link %s to %s",
X			skipcrud+head->header.name,head->header.linkname);
X		break;
X
X#ifdef S_IFLNK
X	case LF_SYMLINK:
X	again_symlink:
X		check = symlink(head->header.linkname,
X			        skipcrud+head->header.name);
X		/* FIXME, don't worry uid, gid, etc... */
X		if (check == 0)
X			break;
X		if (make_dirs(skipcrud+head->header.name))
X			goto again_symlink;
X		msg_perror("Could not create symlink to %s",head->header.linkname);
X		break;
X#endif
X
X#ifdef S_IFCHR
X	case LF_CHR:
X		hstat.st_mode |= S_IFCHR;
X		goto make_node;
X#endif
X
X#ifdef S_IFBLK
X	case LF_BLK:
X		hstat.st_mode |= S_IFBLK;
X		goto make_node;
X#endif
X
X#ifdef S_IFIFO
X	/* If local system doesn't support FIFOs, use default case */
X	case LF_FIFO:
X		hstat.st_mode |= S_IFIFO;
X		hstat.st_rdev = 0;		/* FIXME, do we need this? */
X		goto make_node;
X#endif
X
X	make_node:
X		check = mknod(skipcrud+head->header.name,
X			      (int) hstat.st_mode, (int) hstat.st_rdev);
X		if (check != 0) {
X			if (make_dirs(skipcrud+head->header.name))
X				goto make_node;
X			msg_perror("Could not make %s",skipcrud+head->header.name);
X			break;
X		};
X		goto set_filestat;
X
X	case LF_DIR:
X	case LF_DUMPDIR:
X		namelen = strlen(skipcrud+head->header.name)-1;
X	really_dir:
X		/* Check for trailing /, and zap as many as we find. */
X		while (namelen && head->header.name[skipcrud+namelen] == '/')
X			head->header.name[skipcrud+namelen--] = '\0';
X		if(f_gnudump) {		/* Read the entry and delete files
X					   that aren't listed in the archive */
X			gnu_restore(skipcrud+head->header.name);
X		
X		} else if(head->header.linkflag==LF_DUMPDIR)
X			skip_file((long)(hstat.st_size));
X
X	
X	again_dir:
X		check = mkdir(skipcrud+head->header.name,
X			      0300 | (int)hstat.st_mode);
X		if (check != 0) {
X			if (make_dirs(skipcrud+head->header.name))
X				goto again_dir;
X			/* If we're trying to create '.', let it be. */
X			if (head->header.name[skipcrud+namelen] == '.' && 
X			    (namelen==0 ||
X			     head->header.name[skipcrud+namelen-1]=='/'))
X				goto check_perms;
X			if(f_gnudump && errno==EEXIST)
X				break;
X			msg_perror("Could not make directory %s",skipcrud+head->header.name);
X			break;
X		}
X		
X	check_perms:
X		if (0300 != (0300 & (int) hstat.st_mode)) {
X			hstat.st_mode |= 0300;
X			msg("Added write and execute permission to directory %s",
X			  skipcrud+head->header.name);
X		}
X#ifdef atarist
X		hstat.st_mode |= S_IFDIR;
X#endif
X		goto set_filestat;
X		/* FIXME, Remember timestamps for after files created? */
X		/* FIXME, change mode after files created (if was R/O dir) */
X	case LF_VOLHDR:
X		if(f_verbose) {
X			printf("Reading %s\n",head->header.name);
X		}
X	case LF_MULTIVOL:
X		msg("Can't extract '%s'--file is continued from another volume\n",head->header.name);
X		skip_file((long)hstat.st_size);
X		break;
X
X	}
X
X	/* We don't need to save it any longer. */
X	saverec((union record **) 0);	/* Unsave it */
X}
X
X/*
X * After a file/link/symlink/dir creation has failed, see if
X * it's because some required directory was not present, and if
X * so, create all required dirs.
X */
Xint
Xmake_dirs(pathname)
X	char *pathname;
X{
X	char *p;			/* Points into path */
X	int madeone = 0;		/* Did we do anything yet? */
X	int save_errno = errno;		/* Remember caller's errno */
X	int check;
X
X#ifdef atarist
X	if (errno != EPATH)
X#else
X	if (errno != ENOENT)
X#endif
X		return 0;		/* Not our problem */
X
X	for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
X		/* Avoid mkdir of empty string, if leading or double '/' */
X		if (p == pathname || p[-1] == '/')
X			continue;
X		/* Avoid mkdir where last part of path is '.' */
X		if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
X			continue;
X		*p = 0;				/* Truncate the path there */
X		check = mkdir (pathname, 0777);	/* Try to create it as a dir */
X		if (check == 0) {
X			/* Fix ownership */
X			if (we_are_root) {
X				if (chown(pathname, hstat.st_uid,
X					  hstat.st_gid) < 0) {
X					msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid);
X				}
X			}
X			pr_mkdir(pathname, p-pathname, notumask&0777);
X			madeone++;		/* Remember if we made one */
X			*p = '/';
X			continue;
X		}
X		*p = '/';
X		if (errno == EEXIST)		/* Directory already exists */
X			continue;
X		/*
SHAR_EOF
echo "End of part 2"
echo "File EXTRACT.C is continued in part 3"
echo "3" > s2_seq_.tmp
exit 0