[comp.os.minix] afio part 1

dono@killer.DALLAS.TX.US (Don OConnell) (12/27/88)

This is a replacement for the 'cpio -c' command. It is running under my minix
system, although I haven't given it much testing yet. It was originally 
posted to comp.sources.unix earlier this year or last.

cat afio.1.c afio.2.c >afio.c         and    compile
posting 2 has the documentation with it.

Have fun with it.
-----------------------CUT HERE-----------------------------------------
#! /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 shell archive."
# Contents:  afio.1.c
# Wrapped by dono@killer on Mon Dec 26 14:49:44 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f afio.1.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"afio.1.c\"
else
echo shar: Extracting \"afio.1.c\" \(34443 characters\)
sed "s/^X//" >afio.1.c <<'END_OF_afio.1.c'
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#define MEMCPY
X#define VOIDFIX
X
X#include <stdio.h>
X#include <errno.h>
X/* #include <sys/signal.h> */
X#include <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   512             /* Default archive block size -- orig. 5120 */
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  "%6o%6o%6o%6o%6o%6o%6o%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();
X/* VOIDFN  (*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
X/*
X * linkleft()
X *
X * Complain about files with unseen links.
X */
XSTATIC void
Xlinkleft()
X{
X	reg Link	*lp;
X	reg Link	**base;
X
X	for (base = linkbase; base < linkbase + nel(linkbase); ++base)
X		for (lp = *base; lp; lp = lp->l_forw)
X			if (lp->l_nlink)
X				VOID warn(lp->l_path->p_name, "Unseen link(s)");
X}
X
X/*
X * linkto()
X *
X * Remember a file with outstanding links. Returns a
X * pointer to the associated link structure, or NULL
X * when linking is not possible.
X */
XSTATIC Link *
Xlinkto(name, asb)
Xchar		*name;
Xreg Stat	*asb;
X{
X	reg Link	*linkp;
X	reg Path	*path;
X	reg Link	**abase;
X
X	if ((asb->sb_mode & S_IFMT) == S_IFDIR
X	    || (linkp = (Link *) memget(sizeof(Link))) == NULL
X	    || (path = (Path *) memget(sizeof(Path))) == NULL
X	    || (path->p_name = memstr(name)) == NULL)
X		return (NULL);
X	linkp->l_dev = asb->sb_dev;
X	linkp->l_ino = asb->sb_ino;
X	linkp->l_nlink = asb->sb_nlink - 1;
X	linkp->l_size = asb->sb_size;
X	linkp->l_path = path;
X	path->p_forw = NULL;
X	path->p_back = path;
X	if (linkp->l_forw = *(abase = linkhash(asb->sb_ino)))
X		linkp->l_forw->l_back = linkp;
X	linkp->l_back = NULL;
X	return (*abase = linkp);
X}
X
X#ifndef	MEMCPY
X
X/*
X * memcpy()
X *
X * A simple block move.
X */
XSTATIC void
Xmemcpy(to, from, len)
Xreg char	*to;
Xreg char	*from;
Xuint		len;
X{
X	reg char	*toend;
X
X	for (toend = to + len; to < toend; *to++ = *from++)
X		;
X}
X
X#endif	/* MEMCPY */
X
X/*
X * memget()
X *
X * Allocate memory.
X */
XSTATIC char *
Xmemget(len)
Xuint		len;
X{
X	reg char	*mem;
X	static short	outofmem;
X
X	if ((mem = malloc(len)) == NULL && !outofmem)
X		outofmem = warn("memget()", "Out of memory");
X	return (mem);
X}
X
X/*
X * memstr()
X *
X * Duplicate a string into dynamic memory.
X */
XSTATIC char *
Xmemstr(str)
Xreg char	*str;
X{
X	reg char	*mem;
X
X	if (mem = memget((uint) strlen(str) + 1))
X		VOID strcpy(mem, str);
X	return (mem);
X}
X
X#ifndef	MKDIR
X
X/*
X * mkdir()
X *
X * Make a directory via "/bin/mkdir". Sets errno to a
X * questionably sane value upon failure.
X */
XSTATIC int
Xmkdir(name, mode)
Xreg char	*name;
Xreg ushort	mode;
X{
X	reg int		pid;
X
X	if ((pid = xfork("mkdir()")) == 0) {
X		VOID close(fileno(stdin));
X		VOID close(fileno(stdout));
X		VOID close(fileno(stderr));
X		VOID open("/dev/null", O_RDWR);
X		VOID dup(fileno(stdin));
X		VOID dup(fileno(stdin));
X		VOID umask(~mode);
X		VOID execl("/bin/mkdir", "mkdir", name, (char *) NULL);
X		exit(1);
X	}
X	if (xwait(pid, "mkdir()") == 0)
X		return (0);
X	errno = EACCES;
X	return (-1);
X}
X
X#endif	/* MKDIR */
X
X/*
X * nameadd()
X *
X * Add a name to the pattern list.
X */
XSTATIC void
Xnameadd(name, not)
Xreg char	*name;
Xint		not;
X{
X	reg Pattern	*px;
X
X	px = (Pattern *) memget(sizeof(Pattern));
X	px->p_forw = pattern;
X	px->p_str = name;
X	px->p_len = strlen(name);
X	px->p_not = not;
X	pattern = px;
X}
X
X/*
X * namecmp()
X *
X * Compare a pathname with the pattern list. Returns 0 for
X * a match, -1 otherwise.
X */
XSTATIC int
Xnamecmp(name)
Xreg char	*name;
X{
X	reg Pattern	*px;
X	reg int		positive;
X	reg int		match;
X
X	positive = match = 0;
X	for (px = pattern; px; px = px->p_forw) {
X		if (!px->p_not)
X			++positive;
X		if (strncmp(name, px->p_str, px->p_len) == 0
X		  && (name[px->p_len] == '/' || name[px->p_len] == '\0')) {
X			if (px->p_not)
X				return (-1);
X			++match;
X		}
X	}
X	return (match || !positive ? 0 : -1);
X}
X
X/*
X * nameopt()
X *
X * Optimize a pathname. Confused by "<symlink>/.." twistiness.
X * Returns the number of final pathname elements (zero for "/"
X * or ".") or -1 if unsuccessful.
X */
XSTATIC int
Xnameopt(begin)
Xchar	*begin;
X{
X	reg char	*name;
X	reg char	*item;
X	reg int		idx;
X	int		absolute;
X	auto char	*element[PATHELEM];
X
X	absolute = (*(name = begin) == '/');
X	idx = 0;
X	for (;;) {
X		if (idx == PATHELEM)
X			return (warn(begin, "Too many elements"));
X		while (*name == '/')
X			++name;
X		if (*name == '\0')
X			break;
X		element[idx] = item = name;
X		while (*name && *name != '/')
X			++name;
X		if (*name)
X			*name++ = '\0';
X		if (strcmp(item, "..") == 0)
X			if (idx == 0)
X				if (absolute)
X					;
X				else
X					++idx;
X			else if (strcmp(element[idx - 1], "..") == 0)
X				++idx;
X			else
X				--idx;
X		else if (strcmp(item, ".") != 0)
X			++idx;
X	}
X	if (idx == 0)
X		element[idx++] = absolute ? "" : ".";
X	element[idx] = NULL;
X	name = begin;
X	if (absolute)
X		*name++ = '/';
X	for (idx = 0; item = element[idx]; ++idx, *name++ = '/')
X		while (*item)
X			*name++ = *item++;
X	*--name = '\0';
X	return (idx);
X}
X
X
END_OF_afio.1.c
if test 34443 -ne `wc -c <afio.1.c`; then
    echo shar: \"afio.1.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0