[comp.sources.unix] v12i047: Manipulate CPIO-format archive and files, Part02/02

rsalz@uunet.UU.NET (Rich Salz) (10/27/87)

Submitted-by: Mark Brukhartz <ihnp4!laidbak!mdb>
Posting-number: Volume 12, Issue 47
Archive-name: afio/part02

[  This is a replacement for cpio, a (tape-) archive program.  I wrote the
   Makefile.  I had to split the source file into two halves, which will
   need to be joined together.  --r$ ]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  afio.c.P1
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'afio.c.P1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'afio.c.P1'\"
else
echo shar: Extracting \"'afio.c.P1'\" \(29675 characters\)
sed "s/^X//" >'afio.c.P1' <<'END_OF_FILE'
X/*
X * afio.c
X *
X * Manipulate archives and files.
X *
X * Copyright (c) 1985 Lachman Associates, Inc..
X *
X * This software was written by Mark Brukhartz at Lachman Associates,
X * Inc.. It may be distributed within the following restrictions:
X *	(1) It may not be sold at a profit.
X *	(2) This credit and notice must remain intact.
X * This software may be distributed with other software by a commercial
X * vendor, provided that it is included at no additional charge.
X *
X * Please report bugs to "..!ihnp4!laidbak!mdb".
X *
X * Options:
X *  o Define INDEX to use index() in place of strchr() (v7, BSD).
X *  o Define MEMCPY when an efficient memcpy() exists (SysV).
X *  o Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3).
X *  o Define NOVOID if your compiler doesn't like void casts.
X *  o Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD).
X *  o Define VOIDFIX to allow pointers to functions returning void (non-PCC).
X *  o Define CTC3B2 to support AT&T 3B2 streaming cartridge tape.
X */
X
Xstatic char *ident = "$Header: afio.c,v 1.68 86/12/15 13:07:11 mdb Exp $";
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/signal.h>
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <grp.h>
X
X#ifndef	major
X#	include <sys/sysmacros.h>
X#endif	/* major */
X
X#ifdef	SYSTIME
X#	include <sys/time.h>
X#else	/* SYSTIME */
X#	include <time.h>
X#endif	/* SYSTIME */
X
X#ifdef	CTC3B2
X#	include <sys/vtoc.h>
X#	include <sys/ct.h>
X#endif	/* CTC3B2 */
X
X/*
X * Address link information base.
X */
X#define	linkhash(ino)	\
X	(linkbase + (ino) % nel(linkbase))
X
X/*
X * Mininum value.
X */
X#define	min(one, two)	\
X	(one < two ? one : two)
X
X/*
X * Number of array elements.
X */
X#define	nel(a)		\
X	(sizeof(a) / sizeof(*(a)))
X
X/*
X * Remove a file or directory.
X */
X#define	remove(name, asb) \
X	(((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name))
X
X/*
X * Swap bytes.
X */
X#define	swab(n)		\
X	((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00))
X
X/*
X * Cast and reduce to unsigned short.
X */
X#define	ush(n)		\
X	(((ushort) (n)) & 0177777)
X
X/*
X * Definitions.
X */
X#define	reg	register	/* Convenience */
X#define	uint	unsigned int	/* Not always in types.h */
X#define	ushort	unsigned short	/* Not always in types.h */
X#define	BLOCK	5120		/* Default archive block size */
X#define	FSBUF	(8*1024)	/* Filesystem buffer size */
X#define	H_COUNT	10		/* Number of items in ASCII header */
X#define	H_PRINT	"%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo"
X#define	H_SCAN	"%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo"
X#define	H_STRLEN 70		/* ASCII header string length */
X#define	M_ASCII "070707"	/* ASCII magic number */
X#define	M_BINARY 070707		/* Binary magic number */
X#define	M_STRLEN 6		/* ASCII magic number length */
X#define	NULLDEV	-1		/* Null device code */
X#define	NULLINO	0		/* Null inode number */
X#define	PATHELEM 256		/* Pathname element count limit */
X#define	PATHSIZE 1024		/* Pathname length limit */
X#define	S_IFSHF	12		/* File type shift (shb in stat.h) */
X#define	S_IPERM	07777		/* File permission bits (shb in stat.h) */
X#define	S_IPEXE	07000		/* Special execution bits (shb in stat.h) */
X#define	S_IPOPN	0777		/* Open access bits (shb in stat.h) */
X#define	STDIN	0		/* Standard input file descriptor */
X#define	STDOUT	1		/* Standard output file descriptor */
X#define	TTY	"/dev/tty"	/* For volume-change queries */
X
X/*
X * Some versions of the portable "C" compiler (PCC) can't handle
X * pointers to functions returning void.
X */
X#ifdef	VOIDFIX
X#	define	VOIDFN	void	/* Expect "void (*fnptr)()" to work */
X#else	/* VOIDFIX */
X#	define	VOIDFN	int	/* Avoid PCC "void (*fnptr)()" bug */
X#endif	/* VOIDFIX */
X
X/*
X * Trailer pathnames. All must be of the same length.
X */
X#define	TRAILER	"TRAILER!!!"	/* Archive trailer (cpio compatible) */
X#define	TRAILZ	11		/* Trailer pathname length (including null) */
X
X/*
X * Open modes; there is no <fcntl.h> with v7 UNIX.
X */
X#define	O_RDONLY 0		/* Read-only */
X#define	O_WRONLY 1		/* Write-only */
X#define	O_RDWR	2		/* Read/write */
X
X/*
X * V7 and BSD UNIX use old-fashioned names for a couple of
X * string functions.
X */
X#ifdef	INDEX
X#	define	strchr	index	/* Forward character search */
X#	define	strrchr	rindex	/* Reverse character search */
X#endif	/* INDEX */
X
X/*
X * Some compilers can't handle void casts.
X */
X#ifdef	NOVOID
X#	define	VOID		/* Omit void casts */
X#else	/* NOVOID */
X#	define	VOID	(void)	/* Quiet lint about ignored return values */
X#endif	/* NOVOID */
X
X/*
X * Adb is more palatable when static functions and variables are
X * declared as globals. Lint gives more useful information when
X * statics are truly static.
X */
X#ifdef	lint
X#	define	STATIC	static	/* Declare static variables for lint */
X#else	/* lint */
X#	define	STATIC		/* Make static variables global for adb */
X#endif	/* lint */
X
X/*
X * Simple types.
X */
Xtypedef struct group	Group;	/* Structure for getgrgid(3) */
Xtypedef struct passwd	Passwd;	/* Structure for getpwuid(3) */
Xtypedef struct tm	Time;	/* Structure for localtime(3) */
X
X#ifdef	S_IFLNK
X	/*
X	 * File status with symbolic links. Kludged to hold symbolic
X	 * link pathname within structure.
X	 */
X	typedef struct {
X		struct stat	sb_stat;
X		char		sb_link[PATHSIZE];
X	} Stat;
X#	define	STAT(name, asb)		stat(name, &(asb)->sb_stat)
X#	define	FSTAT(fd, asb)		fstat(fd, &(asb)->sb_stat)
X#	define	LSTAT(name, asb)	lstat(name, &(asb)->sb_stat)
X#	define	sb_dev		sb_stat.st_dev
X#	define	sb_ino		sb_stat.st_ino
X#	define	sb_mode		sb_stat.st_mode
X#	define	sb_nlink	sb_stat.st_nlink
X#	define	sb_uid		sb_stat.st_uid
X#	define	sb_gid		sb_stat.st_gid
X#	define	sb_rdev		sb_stat.st_rdev
X#	define	sb_size		sb_stat.st_size
X#	define	sb_atime	sb_stat.st_atime
X#	define	sb_mtime	sb_stat.st_mtime
X#	define	sb_ctime	sb_stat.st_ctime
X#	define	sb_blksize	sb_stat.st_blksize
X#	define	sb_blocks	sb_stat.st_blocks
X#else	/* S_IFLNK */
X	/*
X	 * File status without symbolic links.
X	 */
X	typedef	struct stat	Stat;
X#	define	STAT(name, asb)		stat(name, asb)
X#	define	FSTAT(fd, asb)		fstat(fd, asb)
X#	define	LSTAT(name, asb)	stat(name, asb)
X#	define	sb_dev		st_dev
X#	define	sb_ino		st_ino
X#	define	sb_mode		st_mode
X#	define	sb_nlink	st_nlink
X#	define	sb_uid		st_uid
X#	define	sb_gid		st_gid
X#	define	sb_rdev		st_rdev
X#	define	sb_size		st_size
X#	define	sb_atime	st_atime
X#	define	sb_mtime	st_mtime
X#	define	sb_ctime	st_ctime
X#endif	/* S_IFLNK */
X
X/*
X * Binary archive header (obsolete).
X */
Xtypedef struct {
X	short	b_dev;			/* Device code */
X	ushort	b_ino;			/* Inode number */
X	ushort	b_mode;			/* Type and permissions */
X	ushort	b_uid;			/* Owner */
X	ushort	b_gid;			/* Group */
X	short	b_nlink;		/* Number of links */
X	short	b_rdev;			/* Real device */
X	ushort	b_mtime[2];		/* Modification time (hi/lo) */
X	ushort	b_name;			/* Length of pathname (with null) */
X	ushort	b_size[2];		/* Length of data */
X} Binary;
X
X/*
X * Child process structure.
X */
Xtypedef struct child {
X	struct child	*c_forw;	/* Forward link */
X	int		c_pid;		/* Process ID */
X	int		c_flags;	/* Flags (CF_) */
X	int		c_status;	/* Exit status */
X} Child;
X
X/*
X * Child process flags (c_flags).
X */
X#define	CF_EXIT	(1<<0)			/* Exited */
X
X/*
X * Hard link sources. One or more are chained from each link
X * structure.
X */
Xtypedef struct name {
X	struct name	*p_forw;	/* Forward chain (terminated) */
X	struct name	*p_back;	/* Backward chain (circular) */
X	char		*p_name;	/* Pathname to link from */
X} Path;
X
X/*
X * File linking information. One entry exists for each unique
X * file with with outstanding hard links.
X */
Xtypedef struct link {
X	struct link	*l_forw;	/* Forward chain (terminated) */
X	struct link	*l_back;	/* Backward chain (terminated) */
X	dev_t		l_dev;		/* Device */
X	ino_t		l_ino;		/* Inode */
X	ushort		l_nlink;	/* Unresolved link count */
X	off_t		l_size;		/* Length */
X	Path		*l_path;	/* Pathname(s) to link from */
X} Link;
X
X/*
X * Pathnames to (or to not) be processed.
X */
Xtypedef struct pattern {
X	struct pattern	*p_forw;	/* Forward chain */
X	char		*p_str;		/* String */
X	int		p_len;		/* Length of string */
X	int		p_not;		/* Reverse logic */
X} Pattern;
X
X/*
X * External functions.
X */
Xvoid	_exit();
Xvoid	exit();
Xvoid	free();
Xchar	*getenv();
Xushort	getgid();
XGroup	*getgrgid();
XPasswd	*getpwuid();
Xushort	getuid();
XTime	*localtime();
Xoff_t	lseek();
Xchar	*malloc();
XVOIDFN	(*signal())();
Xuint	sleep();
Xchar	*strcat();
Xchar	*strchr();
Xchar	*strcpy();
Xchar	*strncpy();
Xchar	*strrchr();
Xtime_t	time();
Xushort	umask();
X
X/*
X * Internal functions.
X */
XVOIDFN	copyin();
XVOIDFN	copyout();
Xint	dirchg();
Xint	dirmake();
Xint	dirneed();
Xvoid	fatal();
XVOIDFN	in();
Xvoid	inalloc();
Xint	inascii();
Xint	inavail();
Xint	inbinary();
Xint	indata();
Xint	inentry();
Xint	infill();
Xint	inhead();
Xint	inread();
Xint	inskip();
Xint	inswab();
Xint	lineget();
Xvoid	linkalso();
XLink	*linkfrom();
Xvoid	linkleft();
XLink	*linkto();
Xvoid	memcpy();
Xchar	*memget();
Xchar	*memstr();
Xint	mkdir();
Xvoid	nameadd();
Xint	namecmp();
Xint	nameopt();
Xvoid	next();
Xvoid	nextask();
Xvoid	nextclos();
Xint	nextopen();
Xint	openi();
Xint	openo();
Xint	openq();
Xint	options();
Xoff_t	optsize();
XVOIDFN	out();
Xvoid	outalloc();
Xuint	outavail();
Xint	outdata();
Xvoid	outeof();
Xvoid	outflush();
Xvoid	outhead();
Xvoid	outpad();
Xvoid	outwait();
Xvoid	outwrite();
XVOIDFN	pass();
Xvoid	passdata();
Xint	passitem();
Xint	pipechld();
Xint	pipeopen();
Xvoid	pipewait();
Xvoid	prsize();
Xint	rmdir();
Xint	swrite();
Xchar	*syserr();
XVOIDFN	toc();
Xvoid	tocentry();
Xvoid	tocmode();
Xvoid	usage();
Xint	warn();
Xint	warnarch();
Xint	xfork();
Xvoid	xpause();
Xint	xwait();
X
X/*
X * External variables.
X */
Xextern int	errno;		/* System error code */
Xextern char	*sys_errlist[];	/* System error messages */
Xextern int	sys_nerr;	/* Number of sys_errlist entries */
X
X/*
X * Static variables.
X */
X#ifdef	CTC3B2
XSTATIC short	Cflag;		/* Enable 3B2 CTC streaming (kludge) */
X#endif	/* CTC3B2 */
XSTATIC short	dflag;		/* Don't create missing directories */
XSTATIC short	fflag;		/* Fork before writing to archive */
XSTATIC short	gflag;		/* Change to input file directories */
XSTATIC short	hflag;		/* Follow symbolic links */
XSTATIC short	jflag;		/* Don't generate sparse filesystem blocks */
XSTATIC short	kflag;		/* Skip initial junk to find a header */
XSTATIC short	lflag;		/* Link rather than copying (when possible) */
XSTATIC short	mflag;		/* Ignore archived timestamps */
XSTATIC short	nflag;		/* Keep newer existing files */
XSTATIC short	uflag;		/* Report files with unseen links */
XSTATIC short	vflag;		/* Verbose */
XSTATIC short	xflag;		/* Retain file ownership */
XSTATIC short	zflag;		/* Print final statistics */
XSTATIC uint	arbsize = BLOCK;/* Archive block size */
XSTATIC short	areof;		/* End of input volume reached */
XSTATIC int	arfd;		/* Archive file descriptor */
XSTATIC off_t	arleft;		/* Space remaining within current volume */
XSTATIC char	*arname;	/* Expanded archive name */
XSTATIC uint	arpad;		/* Final archive block padding boundary */
XSTATIC char	arspec[PATHSIZE];/* Specified archive name */
XSTATIC off_t	aruntil;	/* Volume size limit */
XSTATIC uint	arvolume;	/* Volume number */
XSTATIC uint	buflen;		/* Archive buffer length */
XSTATIC char	*buffer;	/* Archive buffer */
XSTATIC char	*bufidx;	/* Archive buffer index */
XSTATIC char	*bufend;	/* End of data within archive buffer */
XSTATIC Child	*children;	/* Child processes */
XSTATIC ushort	gid;		/* Group ID */
XSTATIC Link	*linkbase[256];	/* Unresolved link information */
XSTATIC ushort	mask;		/* File creation mask */
XSTATIC char	*myname;	/* Arg0 */
XSTATIC char	*optarg;	/* Option argument */
XSTATIC int	optind;		/* Command line index */
XSTATIC int	outpid;		/* Process ID of outstanding outflush() */
XSTATIC Pattern	*pattern;	/* Pathname matching patterns */
XSTATIC char	pwd[PATHSIZE];	/* Working directory (with "-g") */
XSTATIC int	pipepid;	/* Pipeline process ID */
XSTATIC time_t	timenow;	/* Current time */
XSTATIC time_t	timewait;	/* Time spent awaiting new media */
XSTATIC off_t	total;		/* Total number of bytes transferred */
XSTATIC int	ttyf;		/* For interactive queries (yuk) */
XSTATIC ushort	uid;		/* User ID */
X
Xmain(ac, av)
Xint		ac;
Xreg char	**av;
X{
X	reg int		c;
X	reg uint	group = 1;
X	reg VOIDFN	(*fn)() = NULL;
X	reg time_t	timedone;
X	auto char	remote[PATHSIZE];
X
X	if (myname = strrchr(*av, '/'))
X		++myname;
X	else
X		myname = *av;
X	mask = umask(0);
X	ttyf = openq();
X	uid = getuid();
X	gid = getgid();
X	if (uid == 0)
X		++xflag;
X	VOID signal(SIGPIPE, SIG_IGN);
X	while (c = options(ac, av, "ioptIOVCb:c:de:fghjklmns:uvxXy:Y:z")) {
X		switch (c) {
X		case 'i':
X			if (fn)
X				usage();
X			fn = in;
X			break;
X		case 'o':
X			if (fn)
X				usage();
X			fn = out;
X			break;
X		case 'p':
X			if (fn)
X				usage();
X			fn = pass;
X			break;
X		case 't':
X			if (fn)
X				usage();
X			fn = toc;
X			break;
X		case 'I':
X			if (fn)
X				usage();
X			fn = copyin;
X			break;
X		case 'O':
X			if (fn)
X				usage();
X			fn = copyout;
X			break;
X		case 'V':
X			VOID printf("%s\n", ident);
X			exit(0);
X#ifdef	CTC3B2
X		case 'C':
X			++Cflag;
X			arbsize = 31 * 512;
X			group = 10;
X			aruntil = 1469 * 31 * 512;
X			break;
X#endif	/* CTC3B2 */
X		case 'b':
X			if ((arbsize = (uint) optsize(optarg)) == 0)
X				fatal(optarg, "Bad block size");
X			break;
X		case 'c':
X			if ((group = (uint) optsize(optarg)) == 0)
X				fatal(optarg, "Bad buffer count");
X			break;
X		case 'd':
X			++dflag;
X			break;
X		case 'e':
X			arpad = (uint) optsize(optarg);
X			break;
X		case 'f':
X			++fflag;
X			break;
X		case 'g':
X			++gflag;
X			break;
X		case 'h':
X			++hflag;
X			break;
X		case 'j':
X			++jflag;
X			break;
X		case 'k':
X			++kflag;
X			break;
X		case 'l':
X			++lflag;
X			break;
X		case 'm':
X			++mflag;
X			break;
X		case 'n':
X			++nflag;
X			break;
X		case 's':
X			aruntil = optsize(optarg);
X			break;
X		case 'u':
X			++uflag;
X			break;
X		case 'v':
X			++vflag;
X			break;
X		case 'x':
X			++xflag;
X			break;
X		case 'X':
X			xflag = 0;
X			break;
X		case 'y':
X			nameadd(optarg, 0);
X			break;
X		case 'Y':
X			nameadd(optarg, 1);
X			break;
X		case 'z':
X			++zflag;
X			break;
X		default:
X			usage();
X		}
X	}
X	if (fn == NULL || av[optind] == NULL)
X		usage();
X	buflen = arbsize * group;
X	if (arpad == 0)
X		arpad = arbsize;
X	if (fn != pass) {
X		reg char	*colon;
X		reg char	*equal;
X		reg int		isoutput = (fn == out || fn == copyout);
X
X		arname = strcpy(arspec, av[optind++]);
X		if (colon = strchr(arspec, ':')) {
X			*colon++ = '\0';
X			if (equal = strchr(arspec, '='))
X				*equal++ = '\0';
X			VOID sprintf(arname = remote,
X			    "!rsh %s %s -%c -b %u -c %u %s",
X			    arspec, equal ? equal : myname,
X			    isoutput ? 'O' : 'I', arbsize,
X			    group, colon);
X			if (equal)
X				*--equal = '=';
X			*--colon = ':';
X		}
X		if (gflag && *arname != '/' && *arname != '!')
X			fatal(arspec, "Relative pathname");
X		if ((buffer = bufidx = bufend = malloc(buflen)) == NULL)
X			fatal(arspec, "Cannot allocate I/O buffer");
X		if (nextopen(isoutput ? O_WRONLY : O_RDONLY) < 0)
X			exit(1);
X	}
X	timenow = time((time_t *) NULL);
X	(*fn)(av + optind);
X	timedone = time((time_t *) NULL);
X	if (uflag)
X		linkleft();
X	if (zflag) {
X		reg FILE	*stream;
X
X		stream = fn == toc || arfd == STDOUT ? stderr : stdout;
X		VOID fprintf(stream, "%s: ", myname);
X		prsize(stream, total);
X		VOID fprintf(stream, " bytes %s in %lu seconds\n",
X		  fn == pass
X		    ? "transferred"
X		    : fn == out || fn == copyout
X		      ? "written"
X		      : "read",
X		  timedone - timenow - timewait);
X	}
X	nextclos();
X	exit(0);
X	/* NOTREACHED */
X}
X
X/*
X * copyin()
X *
X * Copy directly from the archive to the standard output.
X */
XSTATIC VOIDFN
Xcopyin(av)
Xreg char	**av;
X{
X	reg int		got;
X	reg uint	have;
X
X	if (*av)
X		fatal(*av, "Extraneous argument");
X	while (!areof) {
X		VOID infill();
X		while (have = bufend - bufidx)
X			if ((got = write(STDOUT, bufidx, have)) < 0)
X				fatal("<stdout>", syserr());
X			else if (got > 0)
X				bufidx += got;
X			else
X				return;
X	}
X}
X
X/*
X * copyout()
X *
X * Copy directly from the standard input to the archive.
X */
XSTATIC VOIDFN
Xcopyout(av)
Xreg char	**av;
X{
X	reg int		got;
X	reg uint	want;
X
X	if (*av)
X		fatal(*av, "Extraneous argument");
X	for (;;) {
X		while ((want = bufend - bufidx) == 0)
X			outflush();
X		if ((got = read(STDIN, bufidx, want)) < 0)
X			fatal("<stdin>", syserr());
X		else if (got == 0)
X			break;
X		else
X			bufidx += got;
X	}
X	outflush();
X	if (fflag)
X		outwait();
X}
X
X/*
X * dirchg()
X *
X * Change to the directory containing a given file.
X */
XSTATIC int
Xdirchg(name, local)
Xreg char	*name;
Xreg char	*local;
X{
X	reg char	*last;
X	reg int		len;
X	auto char	dir[PATHSIZE];
X
X	if (*name != '/')
X		return (warn(name, "Relative pathname"));
X	for (last = name + strlen(name); last[-1] != '/'; --last)
X		;
X	len = last - name;
X	strncpy(dir, name, len)[len] = '\0';
X	VOID strcpy(local, *last ? last : ".");
X	if (strcmp(dir, pwd) == 0)
X		return (0);
X	if (chdir(dir) < 0)
X		return (warn(name, syserr()));
X	VOID strcpy(pwd, dir);
X	return (0);
X}
X
X/*
X * dirmake()
X *
X * Make a directory. Returns zero if successful, -1 otherwise.
X */
XSTATIC int
Xdirmake(name, asb)
Xreg char	*name;
Xreg Stat	*asb;
X{
X	if (mkdir(name, asb->sb_mode & S_IPOPN) < 0)
X		return (-1);
X	if (asb->sb_mode & S_IPEXE)
X		VOID chmod(name, asb->sb_mode & S_IPERM);
X	if (xflag)
X		VOID chown(name,
X		    uid == 0 ? ush(asb->sb_uid) : uid,
X		    ush(asb->sb_gid));
X	return (0);
X}
X
X/*
X * dirneed()
X *
X * Recursively create missing directories (with the same permissions
X * as their first existing parent). Temporarily modifies the 'name'
X * argument string. Returns zero if successful, -1 otherwise.
X */
XSTATIC int
Xdirneed(name)
Xchar		*name;
X{
X	reg char	*cp;
X	reg char	*last;
X	reg int		ok;
X	static Stat	sb;
X
X	last = NULL;
X	for (cp = name; *cp; )
X		if (*cp++ == '/')
X			last = cp;
X	if (last == NULL)
X		return (STAT(".", &sb));
X	*--last = '\0';
X	ok = STAT(*name ? name : "/", &sb) == 0
X	    ? ((sb.sb_mode & S_IFMT) == S_IFDIR)
X	    : (!dflag && dirneed(name) == 0 && dirmake(name, &sb) == 0);
X	*last = '/';
X	return (ok ? 0 : -1);
X}
X
X/*
X * fatal()
X *
X * Print fatal message and exit.
X */
XSTATIC void
Xfatal(what, why)
Xchar		*what;
Xchar		*why;
X{
X	VOID fprintf(stderr,
X	    "%s: \"%s\": %s\n",
X	    myname, what, why);
X	exit(1);
X}
X
X/*
X * in()
X *
X * Read an archive.
X */
XSTATIC VOIDFN
Xin(av)
Xreg char	**av;
X{
X	auto Stat	sb;
X	auto char	name[PATHSIZE];
X
X	if (*av)
X		fatal(*av, "Extraneous argument");
X	name[0] = '\0';
X	while (inhead(name, &sb) == 0) {
X		if (namecmp(name) < 0 || inentry(name, &sb) < 0)
X			if (inskip(sb.sb_size) < 0)
X				VOID warn(name, "Skipped file data is corrupt");
X		if (vflag)
X			VOID fprintf(stderr, "%s\n", name);
X	}
X}
X
X/*
X * inalloc()
X *
X * Allocate input buffer space (which was previously indexed
X * by inavail()).
X */
XSTATIC void
Xinalloc(len)
Xreg uint	len;
X{
X	bufidx += len;
X	total += len;
X}
X
X/*
X * inascii()
X *
X * Read an ASCII header. Returns zero if successful;
X * -1 otherwise. Assumes that the entire magic number
X * has been read.
X */
XSTATIC int
Xinascii(magic, name, asb)
Xreg char	*magic;
Xreg char	*name;
Xreg Stat	*asb;
X{
X	auto uint	namelen;
X	auto char	header[H_STRLEN + 1];
X
X	if (strncmp(magic, M_ASCII, M_STRLEN) != 0)
X		return (-1);
X	if (inread(header, H_STRLEN) < 0)
X		return (warnarch("Corrupt ASCII header", (off_t) H_STRLEN));
X	header[H_STRLEN] = '\0';
X	if (sscanf(header, H_SCAN, &asb->sb_dev,
X	    &asb->sb_ino, &asb->sb_mode, &asb->sb_uid,
X	    &asb->sb_gid, &asb->sb_nlink, &asb->sb_rdev,
X	    &asb->sb_mtime, &namelen, &asb->sb_size) != H_COUNT)
X		return (warnarch("Bad ASCII header", (off_t) H_STRLEN));
X	if (namelen == 0 || namelen >= PATHSIZE)
X		return (warnarch("Bad ASCII pathname length", (off_t) H_STRLEN));
X	if (inread(name, namelen) < 0)
X		return (warnarch("Corrupt ASCII pathname", (off_t) namelen));
X	if (name[namelen - 1] != '\0')
X		return (warnarch("Bad ASCII pathname", (off_t) namelen));
X	return (0);
X}
X
X/*
X * inavail()
X *
X * Index availible input data within the buffer. Stores a pointer
X * to the data and its length in given locations. Returns zero with
X * valid data, -1 if unreadable portions were replaced with nulls.
X */
XSTATIC int
Xinavail(bufp, lenp)
Xreg char	**bufp;
Xuint		*lenp;
X{
X	reg uint	have;
X	reg int		corrupt = 0;
X
X	while ((have = bufend - bufidx) == 0)
X		corrupt |= infill();
X	*bufp = bufidx;
X	*lenp = have;
X	return (corrupt);
X}
X
X/*
X * inbinary()
X *
X * Read a binary header. Returns the number of trailing alignment
X * bytes to skip; -1 if unsuccessful.
X */
XSTATIC int
Xinbinary(magic, name, asb)
Xreg char	*magic;
Xreg char	*name;
Xreg Stat	*asb;
X{
X	reg uint	namefull;
X	auto Binary	binary;
X
X	if (*((ushort *) magic) != M_BINARY)
X		return (-1);
X	memcpy((char *) &binary,
X	    magic + sizeof(ushort),
X	    M_STRLEN - sizeof(ushort));
X	if (inread((char *) &binary + M_STRLEN - sizeof(ushort),
X	    sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0)
X		return (warnarch("Corrupt binary header",
X		    (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
X	asb->sb_dev = binary.b_dev;
X	asb->sb_ino = binary.b_ino;
X	asb->sb_mode = binary.b_mode;
X	asb->sb_uid = binary.b_uid;
X	asb->sb_gid = binary.b_gid;
X	asb->sb_nlink = binary.b_nlink;
X	asb->sb_rdev = binary.b_rdev;
X	asb->sb_mtime = binary.b_mtime[0] << 16 | binary.b_mtime[1];
X	asb->sb_size = binary.b_size[0] << 16 | binary.b_size[1];
X	if (binary.b_name == 0 || binary.b_name >= PATHSIZE)
X		return (warnarch("Bad binary pathname length",
X		    (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
X	if (inread(name, namefull = binary.b_name + binary.b_name % 2) < 0)
X		return (warnarch("Corrupt binary pathname", (off_t) namefull));
X	if (name[binary.b_name - 1] != '\0')
X		return (warnarch("Bad binary pathname", (off_t) namefull));
X	return (asb->sb_size % 2);
X}
X
X/*
X * indata()
X *
X * Install data from an archive. Returns given file descriptor.
X */
XSTATIC int
Xindata(fd, size, name)
Xint		fd;
Xreg off_t	size;
Xchar		*name;
X{
X	reg uint	chunk;
X	reg char	*oops;
X	reg int		sparse;
X	reg int		corrupt;
X	auto char	*buf;
X	auto uint	avail;
X
X	corrupt = sparse = 0;
X	oops = NULL;
X	while (size) {
X		corrupt |= inavail(&buf, &avail);
X		size -= (chunk = size < avail ? (uint) size : avail);
X		if (oops == NULL && (sparse = swrite(fd, buf, chunk)) < 0)
X			oops = syserr();
X		inalloc(chunk);
X	}
X	if (corrupt)
X		VOID warn(name, "Corrupt archive data");
X	if (oops)
X		VOID warn(name, oops);
X	else if (sparse > 0
X	  && (lseek(fd, (off_t) -1, 1) < 0
X	    || write(fd, "", 1) != 1))
X		VOID warn(name, syserr());
X	return (fd);
X}
X
X/*
X * inentry()
X *
X * Install a single archive entry. Returns zero if successful, -1 otherwise.
X */
XSTATIC int
Xinentry(name, asb)
Xchar		*name;
Xreg Stat	*asb;
X{
X	reg Link	*linkp;
X	reg int		ifd;
X	reg int		ofd;
X	auto time_t	tstamp[2];
X
X	if ((ofd = openo(name, asb, linkp = linkfrom(asb), 0)) > 0)
X		if (asb->sb_size || linkp == NULL || linkp->l_size == 0)
X			VOID close(indata(ofd, asb->sb_size, name));
X		else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0)
X			VOID warn(linkp->l_path->p_name, syserr());
X		else {
X			passdata(linkp->l_path->p_name, ifd, name, ofd);
X			VOID close(ifd);
X			VOID close(ofd);
X		}
X	else if (ofd < 0)
X		return (-1);
X	else if (inskip(asb->sb_size) < 0)
X		VOID warn(name, "Redundant file data is corrupt");
X	tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime;
X	VOID utime(name, tstamp);
X	return (0);
X}
X
X/*
X * infill()
X *
X * Fill the archive buffer. Remembers mid-buffer read failures and
X * reports them the next time through. Replaces unreadable data with
X * null characters. Returns zero with valid data, -1 otherwise.
X */
XSTATIC int
Xinfill()
X{
X	reg int		got;
X	static int	failed;
X
X	bufend = bufidx = buffer;
X	if (!failed) {
X		if (areof)
X			if (total == 0)
X				fatal(arspec, "No input");
X			else
X				next(O_RDONLY, "Input EOF");
X		if (aruntil && arleft < arbsize)
X			next(O_RDONLY, "Input limit reached");
X		while (!failed
X		    && !areof
X		    && (aruntil == 0 || arleft >= arbsize)
X		    && buffer + buflen - bufend >= arbsize) {
X			if ((got = read(arfd, bufend, arbsize)) > 0) {
X				bufend += got;
X				arleft -= got;
X			} else if (got < 0)
X				failed = warnarch(syserr(),
X				    (off_t) 0 - (bufend - bufidx));
X			else
X				++areof;
X		}
X	}
X	if (failed && bufend == buffer) {
X		failed = 0;
X		for (got = 0; got < arbsize; ++got)
X			*bufend++ = '\0';
X		return (-1);
X	}
X	return (0);
X}
X
X/*
X * inhead()
X *
X * Read a header. Quietly translates old-fashioned binary cpio headers
X * (and arranges to skip the possible alignment byte). Returns zero if
X * successful, -1 upon archive trailer.
X */
XSTATIC int
Xinhead(name, asb)
Xreg char	*name;
Xreg Stat	*asb;
X{
X	reg off_t	skipped;
X	auto char	magic[M_STRLEN];
X	static int	align;
X
X	if (align > 0)
X		VOID inskip((off_t) align);
X	align = 0;
X	for (;;) {
X		VOID inread(magic, M_STRLEN);
X		skipped = 0;
X		while ((align = inascii(magic, name, asb)) < 0
X		    && (align = inbinary(magic, name, asb)) < 0
X		    && (align = inswab(magic, name, asb)) < 0) {
X			if (++skipped == 1) {
X				if (!kflag && total - sizeof(magic) == 0)
X					fatal(arspec, "Unrecognizable archive");
X				VOID warnarch("Bad magic number",
X				    (off_t) sizeof(magic));
X				if (name[0])
X					VOID warn(name, "May be corrupt");
X			}
X			memcpy(magic, magic + 1, sizeof(magic) - 1);
X			VOID inread(magic + sizeof(magic) - 1, 1);
X		}
X		if (skipped) {
X			VOID warnarch("Apparently resynchronized",
X			    (off_t) sizeof(magic));
X			VOID warn(name, "Continuing");
X		}
X		if (strcmp(name, TRAILER) == 0)
X			return (-1);
X		if (nameopt(name) >= 0)
X			break;
X		VOID inskip(asb->sb_size + align);
X	}
X#ifdef	S_IFLNK
X	if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
X		if (inread(asb->sb_link, (uint) asb->sb_size) < 0) {
X			VOID warn(name, "Corrupt symbolic link");
X			return (inhead(name, asb));
X		}
X		asb->sb_link[asb->sb_size] = '\0';
X		asb->sb_size = 0;
X	}
X#endif	/* S_IFLNK */
X	if (name[0] == '/')
X		if (name[1])
X			while (name[0] = name[1])
X				++name;
X		else
X			name[0] = '.';
X	asb->sb_atime = asb->sb_ctime = asb->sb_mtime;
X	return (0);
X}
X
X/*
X * inread()
X *
X * Read a given number of characters from the input archive. Returns
X * zero with valid data, -1 if unreadable portions were replaced by
X * null characters.
X */
XSTATIC int
Xinread(dst, len)
Xreg char	*dst;
Xuint		len;
X{
X	reg uint	have;
X	reg uint	want;
X	reg int		corrupt = 0;
X	char		*endx = dst + len;
X
X	while (want = endx - dst) {
X		while ((have = bufend - bufidx) == 0)
X			corrupt |= infill();
X		if (have > want)
X			have = want;
X		memcpy(dst, bufidx, have);
X		bufidx += have;
X		dst += have;
X		total += have;
X	}
X	return (corrupt);
X}
X
X/*
X * inskip()
X *
X * Skip input archive data. Returns zero under normal circumstances,
X * -1 if unreadable data is encountered.
X */
XSTATIC int
Xinskip(len)
Xreg off_t	len;
X{
X	reg uint	chunk;
X	reg int		corrupt = 0;
X
X	while (len) {
X		while ((chunk = bufend - bufidx) == 0)
X			corrupt |= infill();
X		if (chunk > len)
X			chunk = len;
X		bufidx += chunk;
X		len -= chunk;
X		total += chunk;
X	}
X	return (corrupt);
X}
X
X/*
X * inswab()
X *
X * Read a reversed byte order binary header. Returns the number
X * of trailing alignment bytes to skip; -1 if unsuccessful.
X */
XSTATIC int
Xinswab(magic, name, asb)
Xreg char	*magic;
Xreg char	*name;
Xreg Stat	*asb;
X{
X	reg ushort	namesize;
X	reg uint	namefull;
X	auto Binary	binary;
X
X	if (*((ushort *) magic) != swab(M_BINARY))
X		return (-1);
X	memcpy((char *) &binary,
X	    magic + sizeof(ushort),
X	    M_STRLEN - sizeof(ushort));
X	if (inread((char *) &binary + M_STRLEN - sizeof(ushort),
X	    sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0)
X		return (warnarch("Corrupt swapped header",
X		    (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
X	asb->sb_dev = (dev_t) swab(binary.b_dev);
X	asb->sb_ino = (ino_t) swab(binary.b_ino);
X	asb->sb_mode = swab(binary.b_mode);
X	asb->sb_uid = swab(binary.b_uid);
X	asb->sb_gid = swab(binary.b_gid);
X	asb->sb_nlink = swab(binary.b_nlink);
X	asb->sb_rdev = (dev_t) swab(binary.b_rdev);
X	asb->sb_mtime = swab(binary.b_mtime[0]) << 16 | swab(binary.b_mtime[1]);
X	asb->sb_size = swab(binary.b_size[0]) << 16 | swab(binary.b_size[1]);
X	if ((namesize = swab(binary.b_name)) == 0 || namesize >= PATHSIZE)
X		return (warnarch("Bad swapped pathname length",
X		    (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
X	if (inread(name, namefull = namesize + namesize % 2) < 0)
X		return (warnarch("Corrupt swapped pathname", (off_t) namefull));
X	if (name[namesize - 1] != '\0')
X		return (warnarch("Bad swapped pathname", (off_t) namefull));
X	return (asb->sb_size % 2);
X}
X
X/*
X * lineget()
X *
X * Get a line from a given stream. Returns 0 if successful, -1 at EOF.
X */
XSTATIC int
Xlineget(stream, buf)
Xreg FILE	*stream;
Xreg char	*buf;
X{
X	reg int		c;
X
X	for (;;) {
X		if ((c = getc(stream)) == EOF)
X			return (-1);
X		if (c == '\n')
X			break;
X		*buf++ = c;
X	}
X	*buf = '\0';
X	return (0);
X}
X
X/*
X * linkalso()
X *
X * Add a destination pathname to an existing chain. Assumes that
X * at least one element is present.
X */
XSTATIC void
Xlinkalso(linkp, name)
Xreg Link	*linkp;
Xchar		*name;
X{
X	reg Path	*path;
X
X	if ((path = (Path *) memget(sizeof(Path))) == NULL
X	    || (path->p_name = memstr(name)) == NULL)
X		return;
X	path->p_forw = NULL;
X	path->p_back = linkp->l_path->p_back;
X	path->p_back->p_forw = path;
X	linkp->l_path->p_back = path;
X}
X
X/*
X * linkfrom()
X *
X * Find a file to link from. Returns a pointer to a link
X * structure, or NULL if unsuccessful.
X */
XSTATIC Link *
Xlinkfrom(asb)
Xreg Stat	*asb;
X{
X	reg Link	*linkp;
X	reg Link	*linknext;
X	reg Path	*path;
X	reg Path	*pathnext;
X	reg Link	**abase;
X
X	for (linkp = *(abase = linkhash(asb->sb_ino)); linkp; linkp = linknext)
X		if (linkp->l_nlink == 0) {
X			for (path = linkp->l_path; path; path = pathnext) {
X				pathnext = path->p_forw;
X				free(path->p_name);
X			}
X			free((char *) linkp->l_path);
X			if (linknext = linkp->l_forw)
X				linknext->l_back = linkp->l_back;
X			if (linkp->l_back)
X				linkp->l_back->l_forw = linkp->l_forw;
X			else
X				*abase = linkp->l_forw;
X			free((char *) linkp);
X		} else if (linkp->l_ino == asb->sb_ino
X		    && linkp->l_dev == asb->sb_dev) {
X			--linkp->l_nlink;
X			return (linkp);
X		} else
X			linknext = linkp->l_forw;
X	return (NULL);
X}
X
END_OF_FILE
if test 29675 -ne `wc -c <'afio.c.P1'`; then
    echo shar: \"'afio.c.P1'\" unpacked with wrong size!
fi
# end of 'afio.c.P1'
fi
echo shar: End of archive 2 \(of 2\).
##  End of shell archive.
exit 0