V10/cmd/odist/pax/ship/pax/910208/base

0707070000000000011006440044230044230000010000000430040163500002300000002174HISTORYUgsfGgsfportable archive interchange change history

08/11/88 fix delta checksum bug involving header <dev,ino>
07/17/88 ----- first pax release -----
07/15/88 convert to pax
07/11/88 change putops(), addnumop(), addstrop() to *xop*()
	 change end of medium handler
06/22/88 call doupdate() rather than update()
	 emty files are valid archives
06/16/88 move *_header definition from cpio.h to cpio.c
06/01/88 add delta checksum and drop delta verify entries
05/24/88 use Phong's new delta/update interface
05/11/88 add -z for -io
05/10/88 complete libdelta additions for -z and -A options
05/03/88 add `-z file' delta/translate option
03/30/88 add dynamic buffer size
03/29/88 add tar interpretation for cpio file args
	 fix binary format file name output alignment bug
12/22/87 add CPIO_EXTENDED extended field code
12/08/87 recode for updated hash library interface
12/01/87 fix link hash bug that linked unrelated files
10/06/87 add `-e filter' file output filter option
08/18/87 delete verbose obsolete option warning
08/11/87 use standard cpio file types rather than local S_IFMT from sys/stat.h
06/11/87 ----- first release -----
05/01/87 first code
0707070000000000021006440044230044230000010000000472225247300002400000000445MakefileUgsfGgsf/*
 * pax makefile
 */

ancestor = 3

DEBUG == 1

SYSTEM == "$(_release_:O=1) $(_version_:O=1)"

pax :: RELEASE HISTORY pax.1 pax.1.posix \
	pax.h bio.c convert.c copy.c pax.c \
	delta.c file.c format.c misc.c \
	-lodelta -lx

:INSTALLDIR: cpio tar

cpio :: cpio.sh

tar :: tar.sh

:: stubs.c
0707070000000000031006440044230044230000010000000470261773200002300000005455RELEASEUgsfGgsfportable archive interchange changes since the last release

10/01/90 add mips ar format out of date check
08/11/90 header/trailer alignment now table driven
07/20/90 fix binary output bug that set header size and time to 1
06/01/90 fix misc delta compression bugs
05/01/90 fix DELTA_create bug that did not check COMPRESS
03/28/90 bump regular output block size to 8k
02/11/90 delta changes held off by #if new_delta_format until new libdelta
	 base archive checksums incompatible with previous versions
02/06/90 fix newio() bug that looped when write() returned 0
01/25/90 add proposed posix 1003.1b archive/interchange format
	 tune buffering
	 change -R option for general record support
	 generalize delta id file name -- incompatible with old format
	 prepare for new libdelta and subsequent incompatibilities
12/01/89 add self-delta support, delete -A option (one algorithm with versions)
11/18/89 fix dir mode restoration bug
11/11/89 fix delta update with older file
10/31/89 add portarch and randarch readonly object formats
10/11/89 allow -f with -rwz
10/01/89 add -Bmaxblocks and -C to match latest tar/cpio extensions
08/22/89 align read() buffers to IOALIGN
07/27/89 add EFBIG & EDQUOT checks to newio()
07/04/89 fix cpio binary header mtime and size swab bug
	 fix -rw bug that makes it work now!
	 redo file post processing restoration
05/11/89 handle GNU-tar USTAR format botch
	 missing intermediate directories inherit mode of nearest ancestor
	 fix names on stdin bug that only did -P
	 incorporate lar changes from David Muir
	 expand -R arg syntax
	 handle ansi/ibm D,F,S,U,V formats on output
03/28/89 fix bget() buffer boundary error for reads < buffersize
03/01/89 ignore DELTA_PASS file set*() calls
02/22/89 fix bread bug for 0 count and bget memcpy overlap
01/18/89 fix copyout() rfd<0 delta bug that generated multiple entries
01/11/89 fix dirsize!=0 bug; change ftwalk XDEV implementation
12/11/88 fix symlink->nowhere bug
11/22/88 add vmsbackup readonly format; fix lseek validity checks
11/17/88 add -T040 to simulate tape device blocking on input
11/11/88 add s5r4 asc and aschk formats
11/07/88 hard links and deltas don't cross volumes
11/01/88 add -n to read exact file list
10/20/88 add bread(0,...) to skip, bget() and bput() io optimizations
10/11/88 fix delta ops to use pattern args
10/04/88 fix argc bug that botched -rw with file args
10/01/88 for delta update, verify that untouched files match base archive
09/30/88 fix allocate() bug: malloc() -> calloc()
08/23/88 use libx/memsum() for checksum calculations
08/20/88 add `![command]' to end of medium replies
08/18/88 table of contents output to stdout
08/08/88 update for libx/ftwalk() and libx/strls()
07/28/88 streamline ftwalk() interface
07/21/88 finish up -a and -u tar compatibility
07/17/88 add cpio.sh and tar.sh interface scripts
07/15/88 convert to pax
0707070000000000041006440044230044230000010000000472007024000002100000045630bio.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax block io support
 */

#include "pax.h"

#include "FEATURE/mtio"
#ifdef _sys_mtio
#include <ttyinfo.h>
#include <sys/mtio.h>
#endif

#include "FEATURE/bcopy"
#ifdef _lib_bcopy
#define memcpy(a,b,c)	bcopy(b,a,c)
#endif

#if DEBUG

/*
 * -RBi		input is BLOK file
 * -RBo		output file is BLOK file
 */

static int
blokread(fd, buf, n)
int	fd;
char*	buf;
int	n;
{
	register int		i;
	register int		j;
	char			c;

	static int		eof;

	if (!state.in.blokflag)
	{
		state.in.blokflag = 1;
		eof = 0;
		if ((i = read(fd, buf, state.in.blok ? 4 : n)) < 4 || !strneq(buf, "\002\014\017\013", 4))
		{
			if (state.in.blok) error(3, "input archive is not a BLOK file");
			return(i);
		}
		if (i > 4 && lseek(fd, 4L, 0) != 4L)
			error(3, "cannot seek on input archive BLOK file -- use -RBi");
		state.in.blok = 1;
	}
	if (state.in.blok)
	{
		j = 0;
		do
		{
			if ((i = read(fd, &c, 1)) < 1)
				return(i < 0 && ++eof == 1 ? 0 : -1);
			j <<= 7;
			j |= c & 0177;
		} while (c & 0200);
		if (j > 0)
		{
			if (j > n) error(2, "blokread: buffer overflow (%d>%d)", j, n);
			if ((i = read(fd, buf, j)) != j) error(2, "blokread: blocking error");
		}
		else i = 0;
	}
	else i = read(fd, buf, n);
	return(i);
}

static int
blokwrite(fd, buf, n)
int	fd;
char*	buf;
int	n;
{
	register char*	s;
	register int	i;
	register int	j;
	char		blk[9];

	if (state.out.blok)
	{
		s = blk;
		if (!state.out.blokflag)
		{
			state.out.blokflag = 1;
			*s++ = 002;
			*s++ = 014;
			*s++ = 017;
			*s++ = 013;
		}
		i = 0;
		if (j = (n >> 21) & 0177)
		{
			*s++ = j | 0200;
			i = 1;
		}
		if ((j = (n >> 14) & 0177) || i)
		{
			*s++ = j | 0200;
			i = 1;
		}
		if ((j = (n >> 7) & 0177) || i)
		{
			*s++ = j | 0200;
			i = 1;
		}
		*s++ = n & 0177;
		j = s - blk;
		if ((i = write(fd, blk, j)) != j)
			error(ERROR_SYSTEM|3, "blokwrite: count write error (%d!=%d)", i, j);
		if (n <= 0) i = n;
		else if ((i = write(fd, buf, n)) != n)
			error(ERROR_SYSTEM|3, "blokwrite: data write error (%d!=%d", i, n);
	}
	else i = write(fd, buf, n);
	return(i);
}

#define read(f,b,n)	blokread(f,b,n)
#define write(f,b,n)	blokwrite(f,b,n)

#endif

static int		bfill();

/*
 * initialize buffered io
 */

void
binit()
{
	struct stat		st;

	state.buffersize = DEFBUFFER * DEFBLOCKS;
	if (state.operation == OUT)
	{
		if (!state.blocksize)
		{
			if (fstat(1, &st)) error(3, "%s: cannot stat", state.file);
#if DEBUG
			if (state.test & 040) st.st_mode = S_IFCHR;
#endif
			if ((st.st_mode & S_IFMT) == S_IFREG)
			{
				state.blocksize = format[state.formatout].regular;
				state.out.unblocked = 1;
			}
			else state.blocksize = format[state.formatout].special;
			state.buffersize = state.blocksize *= BLOCKSIZE;
		}
		switch (state.formatout)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			if (!state.record.format) state.record.format = state.formatout == PAX ? PAX_RECFORMAT : ALAR_RECFORMAT;
			if (state.record.size <= 0) switch (state.record.format)
			{
			case 'D':
			case 'U':
				state.record.size = state.blocksize;
				break;
			case 'F':
				state.record.size = state.formatout == PAX ? PAX_RECSIZE : ALAR_RECSIZE;
				break;
			case 'S':
			case 'V':
				state.record.size = 0;
				break;
			default:
				error(3, "%s %c record format not supported on output", format[state.formatout].name, state.record.format);
			}
			if (state.blocksize < state.record.size)
				error(3, "block size (%d) must be >= record size (%d)", state.blocksize, state.record.size);
			if (state.record.size && (state.blocksize / state.record.size) * state.record.size != state.blocksize)
				error(1, "block size should be a multiple of record size");
			break;
		default:
			if (state.record.format || state.record.size)
				error(1, "record format and size ignored for %s format", format[state.formatout].name);
			break;
		}
	}
	else
	{
		if (state.blocksize) state.buffersize = state.blocksize;
		else state.blocksize = DEFBLOCKS * BLOCKSIZE;
		if (state.record.size) error(1, "record size automatically determined on archive read");
	}
	if (state.buffersize < state.blocksize) state.buffersize = state.blocksize;
	state.tmp.buffersize = state.buffersize;
	if (!(state.tmp.buffer = malloc(state.tmp.buffersize)))
		error(3, "cannot allocate buffer");
	if (state.delta.op)
	{
		state.delta.buffersize = state.buffersize;
		if (!(state.delta.buffer = malloc(state.delta.buffersize)) || !(state.delta.hdr = state.delta.hdrbuf = malloc(state.delta.buffersize)))
			error(3, "cannot allocate buffer");
	}
	switch (state.operation)
	{
	case OUT:
		if (!(format[state.formatout].flags & IN)) error(3, "%s: archive format not supported on output" , format[state.formatout].name);
		balloc(&state.out, 2 * state.buffersize);
		if (!state.append && !state.delta.op) break;
		/*FALLTHROUGH*/
	case IN:
		if (!(format[state.formatout].flags & OUT)) error(3, "%s: archive format not supported on input" , format[state.formatout].name);
		balloc(&state.in, 2 * state.buffersize + MAXUNREAD);
		break;
	case IN|OUT:
		break;
	}
#if DEBUG
	message(-1, "blocksize=%d buffersize=%d recordsize=%d", state.blocksize, state.buffersize, state.record.size);
#endif
}

/*
 * allocate buffers for buffered io stream
 */

void
balloc(io, n)
register struct bio*	io;
register int		n;
{
	if (!(io->buffer = malloc(n)))
		error(3, "cannot allocate buffer");
	io->next = io->last = io->buffer;
}

/*
 * buffered char input
 * at least n chars required, m chars max
 * if b is 0 then n chars are skipped
 * if must!=0 then EOF causes query for next input volume file
 */

int
bread(b, n, m, must)
register char*	b;
register long	n;
long		m;
int		must;
{
	register int	c;
	register char*	ob;

	if (state.in.eof) return(-1);
	if (m <= 0) return(0);
	ob = b;
	if (state.in.blocked)
	{
		if (!b) b = state.tmp.buffer;
		while ((c = read(state.append, b, m)) <= 0)
		{
			if (must) newio(state.append, c, 0);
			else if (state.in.empty)
			{
				state.in.eof = 1;
				return(-1);
			}
			else
			{
				if (c < 0) state.in.eof = 1;
				else
				{
					state.in.empty = 1;
#if DEBUG
					if (ob) message(-7, "bread(%d@%ld):", c, state.in.count);
#endif
				}
				return(c);
			}
		}
		state.in.empty = 0;
		if (must && c < n) return(-1);
		state.in.count += c;
		if (state.delta.sum > 0) state.delta.membersum = memsum(ob ? ob : state.tmp.buffer, c, state.delta.membersum);
#if DEBUG
		if (ob) message(-7, "bread(%d@%ld): %-.*s%s", c, state.in.count, c > 32 ? 32 : c, ob, c > 32 ? "..." : "");
#endif
		return(c);
	}
	else
	{
		if (n <= 0) n = m;
		else m = n;
		for (;;)
		{
			if (n > (c = state.in.last - state.in.next))
			{
				if (c > 0)
				{
					if (state.delta.sum > 0) state.delta.membersum = memsum(state.in.next, c, state.delta.membersum);
					if (ob) memcpy(b, state.in.next, c);
					b += c;
					n -= c;
					state.in.count += c;
				}
				state.in.next = state.in.last = state.in.buffer + MAXUNREAD;
				if (!ob && state.delta.sum <= 0)
				{
					static int	seekok = -1;

					if (seekok < 0)
					{
						struct stat	st;

						seekok = lseek(state.append, 0L, 1) == (state.in.count + state.in.last - state.in.next) && fstat(state.append, &st) != -1 && st.st_size > 0;
					}
					if (seekok && (c = n / BUFFERSIZE) && lseek(state.append, (long)(c *= BUFFERSIZE), 1) != -1)
					{
						b += c;
						n -= c;
						state.in.count += c;
#if DEBUG
						message(-7, "bread: skip(%d@%ld) [%ld]", c, state.in.count);
#endif
					}
				}
				if (bfill(must) < 0)
				{
					if (ob && (c = (b - ob)))
					{
						bunread(ob, c);
						return(0);
					}
					return(-1);
				}
			}
			else
			{
				if (state.delta.sum > 0) state.delta.membersum = memsum(state.in.next, n, state.delta.membersum);
				if (ob) memcpy(b, state.in.next, n);
				state.in.next += n;
				state.in.count += n;
#if DEBUG
				if (ob)
				{
					n += b - ob;
					message(-7, "bread(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, ob, n > 32 ? "..." : "");
				}
#endif
				return(m);
			}
		}
	}
}

/*
 * fill input buffer at state.in.last
 * if must!=0 then EOF causes query for next input volume file
 */

static int
bfill(must)
int	must;
{
	register int	c;

	if (state.in.eof) return(-1);
	while ((c = read(state.append, state.in.last, state.buffersize)) <= 0)
	{
		if (must) newio(state.append, c, 0);
		else
		{
			state.in.eof = 1;
			return(-1);
		}
	}
#if DEBUG
	message(-8, "read(%d): %-.*s%s", c, c > 32 ? 32 : c, state.in.last, c > 32 ? "..." : "");
#endif
	state.in.eof = 0;
	state.in.last += c;
	return(0);
}

/*
 * pushback for next bread()
 */

void
bunread(b, n)
register char*	b;
register int	n;
{
	state.in.count -= n;
	if ((state.in.next -= n) < state.in.buffer + MAXUNREAD)
	{
		if (state.in.next < state.in.buffer) error(PANIC, "bunread(%d): too much pushback", n);
		memcpy(state.in.next, b, n);
	}
#if DEBUG
	message(-7, "bunread(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, b, n > 32 ? "..." : "");
#endif
}

/*
 * bread() n chars and return a pointer to the char buffer
 */

char*
bget(n)
register int	n;
{
	register char*	s;

	state.in.count += n;
	s = state.in.next;
	state.in.next += n;
	while (state.in.next > state.in.last)
	{
		if (state.in.last > state.in.buffer + MAXUNREAD + state.buffersize)
		{
                        register char*  b;
                        register int    k;
                        register int    m;

                        k = state.in.last - s;
			m = round(k, IOALIGN) - k;
#if DEBUG
			if (m) message(-8, "bget: buffer alignment offset=%d", m);
#endif
                        b = state.in.next = state.in.buffer + MAXUNREAD + m;
                        state.in.last = b + k;
                        m = s - b;
                        while (k > m)
                        {
#if DEBUG
                                message(-8, "bget: overlapping memcpy n=%d k=%d m=%d", n, k, m);
#endif
                                (void)memcpy(b, s, m);
                                b += m;
                                s += m;
                                k -= m;
                        }
                        (void)memcpy(b, s, k);
			s = state.in.next;
			state.in.next += n;
		}
		if (bfill(1) < 0) return(0);
	}
#if DEBUG
	message(-7, "bget(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, s, n > 32 ? "..." : "");
#endif
	return(s);
}

/*
 * back up input to bsave()'d position and prime output buffer
 */

void
backup()
{
	register long	n;
	register long	m;
#ifdef MTIOCTOP
	struct mtop	mt;
#endif

	switch (state.formatin)
	{
	case ALAR:
	case IBMAR:
	case PAX:
#ifdef MTIOCTOP
		mt.mt_op = MTBSF;
		mt.mt_count = 1;
		if (!(ioctl(1, MTIOCTOP, &mt))) return;
#endif
		break;
	default:
		m = state.in.next - (state.in.buffer + MAXUNREAD);
		if ((n = state.in.count - m) > state.backup.count)
		{
#if DEBUG
			message(-1, "backup(): reread %ld", n + m);
#endif
			m = state.backup.last - (state.backup.buffer + MAXUNREAD);
			if (lseek(1, -(n + m), 1) == -1L)
			{
#ifdef MTIOCTOP
				mt.mt_op = MTBSR;
				mt.mt_count = 2;
				if (ioctl(1, MTIOCTOP, &mt)) break;
#else
				break;
#endif
			}
			if (read(1, state.in.buffer + MAXUNREAD, m) != m) break;
		}
		else m = state.in.last - (state.in.buffer + MAXUNREAD);
#if DEBUG
		message(-1, "backup(): %ld", m);
#endif
		if ((m = lseek(1, -m, 1)) == -1L)
		{
#ifdef MTIOCTOP
			mt.mt_op = MTBSR;
			mt.mt_count = 1;
			if (ioctl(1, MTIOCTOP, &mt)) break;
#else
			break;
#endif
		}
		if (state.backup.next < state.backup.last)
			bwrite(state.in.buffer + MAXUNREAD, state.backup.next - (state.backup.buffer + MAXUNREAD));
		return;
	}
	error(3, "%s: cannot position %s archive for append", state.file, format[state.formatin].name);
}

/*
 * flush buffered input
 */

void
bflushin()
{
	state.in.count += state.in.last - state.in.next;
	state.in.next = state.in.last = state.in.buffer + MAXUNREAD;
	if (!state.in.eof)
	{
		while (read(state.append, state.tmp.buffer, state.buffersize) > 0);
		state.in.eof = 1;
	}
}

/*
 * flush buffered output
 */

void
bflushout()
{
	register int	n;
	register int	c;

	if (n = state.out.next - state.out.buffer)
	{
		state.out.next = state.out.buffer;
		while ((c = write(1, state.out.next, n)) != n)
		{
			if (c <= 0) newio(1, c, n);
			else
			{
				state.out.next += c;
				n -= c;
			}
		}
		state.out.next = state.out.buffer;
	}
}

/*
 * buffered output
 */

void
bwrite(b, n)
register char*	b;
register int	n;
{
	register int	c;

	if (state.maxout && state.out.count >= state.maxout)
	{
		bflushout();
		newio(1, 0, 0);
	}
	state.out.count += n;
	if (state.out.blocked)
	{
#if DEBUG
		if (n > 0) {message(-7, "bwrite(%d@%ld): %-.*s...", n, state.out.count + n, n > 32 ? 32 : n, b);}
		else message(-7, "bwrite(%d@%ld):", n, state.out.count + n);
#endif
		while ((c = write(1, b, n)) != n)
		{
			if (n <= 0)
			{
#ifdef MTIOCTOP
				{
					struct mtop	mt;

					mt.mt_op = MTWEOF;
					mt.mt_count = 1;
					if (ioctl(1, MTIOCTOP, &mt) >= 0) break;
				}
#endif
				error(3, "cannot write tape EOF marks");
			}
			if (c <= 0) newio(1, c, n);
			else if ((n -= c) > 0) b += c;
			else break;
		}
	}
	else
	{
#if DEBUG
		if (n > 0) {message(-7, "bwrite(%d@%ld): %-.*s...", n, state.out.count + n, n > 32 ? 32 : n, b);}
		else message(-7, "bwrite(%d@%ld):", n, state.out.count + n);
#endif
		for (;;)
		{
			if ((c = state.out.buffer + state.blocksize - state.out.next) <= n)
			{
				if (c)
				{
					memcpy(state.out.next, b, c);
					n -= c;
					b += c;
				}
				state.out.next = state.out.buffer;
				while ((c = write(1, state.out.next, state.blocksize)) != state.blocksize)
				{
					if (c <= 0) newio(1, c, n);
					else
					{
						memcpy(state.tmp.buffer, state.out.buffer + c, state.blocksize - c);
						memcpy(state.out.buffer, state.tmp.buffer, state.blocksize - c);
						state.out.next = state.out.buffer + state.blocksize - c;
						break;
					}
				}
#if DEBUG
				message(-8, "write(%d): %-.32s...", c, state.out.buffer);
#endif
			}
			else
			{
				memcpy(state.out.next, b, n);
				state.out.next += n;
				break;
			}
		}
	}
}

/*
 * bwrite() n chars that have been placed in state.out.next
 */

void
bput(n)
register int	n;
{
	state.out.count += n;
#if DEBUG
	message(-7, "bput(%d@%ld): %-.*s%s", n, state.out.count, n > 32 ? 32 : n, state.out.next, n > 32 ? "..." : "");
#endif
	if ((state.out.next += n) > state.out.buffer + state.blocksize)
	{
		n = (state.out.next - state.out.buffer) - state.blocksize;
		state.out.count -= n;

		/*
		 * flush out the buffer and slide over the remains
		 */

		bwrite(state.out.next = state.out.buffer + state.blocksize, n);
	}
}

static struct stat*	devst;

/*
 * find path name in /dev for <devst->st_dev,devst->st_ino>
 * called by ftwalk()
 */

/*ARGSUSED*/
static int
devname(ftw)
register struct FTW*	ftw;
{
	if (ftw->info == FTW_F && ftw->statb.st_dev == devst->st_dev && ftw->statb.st_ino == devst->st_ino)
	{
#if DEBUG
		message(-1, "device name is %s", ftw->path);
#endif
		state.file = strdup(ftw->path);
		return(1);
	}
	return(0);
}

/*
 * initilize tty file pointers for interactive prompting
 */

void
interactive()
{
	if (!state.rtty)
	{
		if (!(state.rtty = fopen("/dev/tty", "r")) || !(state.wtty = fopen("/dev/tty", "w")))
			error(ERROR_SYSTEM|3, "cannot prompt for interactive input");
		setbuf(state.rtty, (char*)0);
		setbuf(state.wtty, (char*)0);
	}
}

/*
 * check for new input or output stream
 * c is the io count causing the newio()
 * n is the pending buffered io count
 */

void
newio(fd, c, n)
register int	fd;
int		c;
int		n;
{
	register char*	s;
	register char*	rw;
	char*		file;
	char*		io;
	char*		t;
	int		vol;
	long		z;
	struct stat	st;

	static int	locked;
	static long	total;

	vol = 0;
	if (fd)
	{
		rw = "write";
		io = "output";
		state.out.offset += state.out.count - n;
		state.out.count = n;
		z = state.out.offset + state.out.count;
		if (state.out.blocked && state.record.file) switch (state.formatout)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			if (locked) return;
			locked = 1;
			putlabels(state.record.file, "EOV");
			locked = 0;
			vol = 1;
			break;
		}
	}
	else
	{
		rw = "read";
		io = "input";
		z = state.in.offset + state.in.count;
	}
	if (fstat(fd, &st) < 0) error(ERROR_SYSTEM|3, "%s: cannot stat", io);
	switch (st.st_mode & S_IFMT)
	{
	case S_IFBLK:
	case S_IFCHR:
		file = 0;
		break;
	default:
		if (fd) switch (c < 0 ? errno : 0)
		{
		case 0:
#ifdef EFBIG
		case EFBIG:
#endif
#ifdef EDQUOT
		case EDQUOT:
#endif
			file = "file";
			break;
		default:
			error(ERROR_SYSTEM|3, "%s %s error -- cannot recover", io, rw);
			break;
		}
		else file = "file";
		break;
	}
	switch (c < 0 ? errno : 0)
	{
	case 0:
	case ENOSPC:
	case ENXIO:
		error(1, "end of %s medium", io);
		break;
	default:
		error(ERROR_SYSTEM|1, "%s %s error", io, rw);
		break;
	}
	if (total == z) error(1, "no %s on part %d", io, state.part--);
	else total = z;
	if (!file && state.file != definput && state.file != defoutput)
	{
		devst = &st;
		(void)ftwalk("/dev", devname, 0, (int(*)())0);
	}
	close(fd);
	if (file && state.file != definput && state.file != defoutput && strmatch(state.file, "*.+([0-9])") && (s = strrchr(state.file, '.')) && ((c = strtol(++s, (char*)0, 10)) == state.part || c == (state.part - 1)))
	{
		if (state.part == 1 && (!(state.file = strdup(state.file)) || !(s = strrchr(state.file, '.'))))
			error(3, "out of space");
		s += strlen(s);
		while (*--s != '.')
		{
			if (*s < '9')
			{
				(*s)++;
				break;
			}
			*s = '0';
		}
		if (*s != '.' && creat(state.file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == fd)
		{
			state.part++;
			error(1, "continuing %s %d %s on %s", state.part == state.volume + 1 ? "volume" : "part", state.part, io, state.file);
			return;
		}
		error(ERROR_SYSTEM|1, "%s: cannot create for writing", state.file);
	}
	if (file || state.file == definput || state.file == defoutput)
	{
		for (;;)
		{
			interactive();
			putc('\007', state.wtty);
			fprintf(state.wtty, "Enter part %d %s %s name: ", state.part + 1, io, file ? file : "device");
			if (!(s = fgetline(state.rtty, 0)))
			{
				putc('\n', state.wtty);
				finish(2);
			}
			if (*s)
			{
				if (!file) break;
				if (fd)
				{
					if ((n = creat(s, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) == fd) break;
					error(ERROR_SYSTEM|1, "%s: cannot creat", s);
				}
				else
				{
					if ((n = open(s, 0)) == fd) break;
					error(ERROR_SYSTEM|1, "%s: cannot read", s);
				}
				if (n >= 0) close(n);
			}
		}
		state.file = strdup(s);
	}
	if (!file)
	{
		for (;;)
		{
			interactive();
			putc('\007', state.wtty);
			fprintf(state.wtty, eomprompt, state.part + 1);
			if (!(s = fgetline(state.rtty, 0)))
			{
				putc('\n', state.wtty);
				finish(2);
			}
			if (*s == '!')
			{
				static char*	last;

				if (*++s)
				{
					if (last) free(last);
					last = strdup(s);
				}
				else s = last;
				if (!s) error(1, "no previous command");
				else if (n = system(s)) error(1, "exit status %d", n);
			}
			else
			{
				file = *s ? s : state.file;
				if (open(file, fd + state.append) == fd) break;
				file = strtape(file, &t);
				if (!*t && open(file, fd + state.append) == fd) break;
				error(ERROR_SYSTEM|1, "cannot %s %s", rw, *s ? s : state.file);
			}
		}
		if (state.file != file) state.file = strdup(file);
	}
	state.part++;
	if (vol && !locked)
	{
		locked = 1;
		putprologue();
		putlabels(state.record.file, "HDR");
		locked = 0;
	}
}
0707070000000000051006440044230044230000010000000471771460200002500000006257convert.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax conversion support
 */

#include "pax.h"

/*
 * convert string to lower case in place
 */

char*
strlower(s)
register char*	s;
{
	register int	c;
	register char*	t;

	for (t = s; c = *t; t++)
		if (isupper(c))
			*t = tolower(c);
	return(s);
}

/*
 * convert string to upper case in place
 */

char*
strupper(s)
register char*	s;
{
	register int	c;
	register char*	t;

	for (t = s; c = *t; t++)
		if (islower(c))
			*t = toupper(c);
	return(s);
}

/*
 * convert binary header shorts to long
 */

long
cpio_long(s)
register short*	s;
{
	union integral	u;

	u.l = 1;
	if (u.c[0])
	{
		u.s[0] = s[1];
		u.s[1] = s[0];
	}
	else
	{
		u.s[0] = s[0];
		u.s[1] = s[1];
	}
	return(u.l);
}

/*
 * convert long to binary header shorts
 */

void
cpio_short(s, n)
register short*	s;
long		n;
{
	union integral	u;

	u.l = 1;
	if (u.c[0])
	{
		u.l = n;
		s[0] = u.s[1];
		s[1] = u.s[0];
	}
	else
	{
		u.l = n;
		s[0] = u.s[0];
		s[1] = u.s[1];
	}
}

/*
 * convert local mode to cpio mode
 */

int
cpio_mode(f)
register struct fileinfo*	f;
{
	register int	type;

	switch (f->st->st_mode & S_IFMT)
	{
	case 0:
		type = 0;
		break;
	case S_IFIFO:
		type = CPIO_FIFO;
		break;
	case S_IFDIR:
		type = CPIO_DIR;
		break;
	case S_IFCHR:
		type = CPIO_CHR;
		break;
	case S_IFBLK:
		type = CPIO_BLK;
		break;
	case S_IFLNK:
		type = CPIO_LNK;
		break;
	case S_IFSOCK:
		type = CPIO_SOCK;
		break;
	default:
		error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT);
		/*FALLTHROUGH*/
	case S_IFREG:
		type = CPIO_REG;
		break;
	}
	return((f->st->st_mode & ~S_IFMT) | type);
}

/*
 * compute tar_header checksum
 */

int
tar_checksum()
{
	register char*	p;
	register int	n;

	p = tar_header.chksum;
	while (p < &tar_header.chksum[sizeof(tar_header.chksum)]) *p++ = ' ';
	n = 0;
	p = tar_block;
	while (p < &tar_block[TAR_HEADER]) n += *p++;
	return(n);
}

/*
 * compute running s5r4 file content checksum
 */

long
asc_checksum(b, n, sum)
register unsigned char*	b;
int			n;
register unsigned long	sum;
{
	register unsigned char*	e;

	e = b + n;
	while (b < e) sum += *b++;
	return(sum);
}

/*
 * get label header number
 */

long
getlabnum(p, byte, width, base)
register char*	p;
int		byte;
int		width;
int		base;
{
	register char*	e;
	register int	c;
	long		n;

	p += byte - 1;
	c = *(e = p + width);
	*e = 0;
	n = strtol(p, (char*)0, base);
	*e = c;
	return(n);
}

/*
 * get label header string
 */

char*
getlabstr(p, byte, width, s)
register char*	p;
int		byte;
int		width;
register char*	s;
{

	register char*	e;
	char*		v;

	v = s;
	p += byte - 1;
	e = p + width;
	while (p < e && (*s = *p++) != ' ') s++;
	*s = 0;
	return(v);
}

#if !new_delta_format

/*
 * this is an obsolete version of the libx implementation
 */

#undef	HASHPART
#define HASHPART(b,h,c,l,r)	(h = ((h = (h << (l)) ^ (h >> (r)) ^ (c)) & (1 << (b)) ? ~h : h) & ((((1 << ((b) - 1)) - 1) << 2) | 3))

#undef	HASHLPART
#define HASHLPART(h,c)		HASHPART(31, h, c, 3, 2)

unsigned long
memsum(b, n, c)
char*			b;
int			n;
register unsigned long	c;
{
	register unsigned char*	p;
	register unsigned char*	e;

	p = (unsigned char*)b;
	e = p + n;
	while (p < e) HASHLPART(c, *p++ + 1);
	return(c);
}

#endif
0707070000000000061006440044230044230000010000000472007013200002200000046057copy.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax file copy support
 */

#include "pax.h"

static void	recordin();
static void	savesetin();
static void	recordout();

/*
 * copy files in from archive
 */

void
copyin()
{
	register struct fileinfo*	f;

	f = &file;
	while (getprologue())
	{
		while (getheader(f))
		{
			if (selectfile(f)) filein(f);
			else fileskip(f);
			gettrailer(f);
		}
		getepilogue();
	}
	deltaverify();
}

/*
 * copy a single file out to the archive
 * called by ftwalk()
 */

int
copyout(ftw)
register struct FTW*	ftw;
{
	register struct fileinfo*	f;
	register struct deltainfo*	d;
	int				fd;
	int				dfd;

	if (getfile(f = &file, ftw))
	{
		if (selectfile(f) && (!state.verify || verify(f)))
		{
			fd = openin(f);
			d = state.delta.tab ? (struct deltainfo*)hashget(state.delta.tab, f->name) : (struct deltainfo*)0;
			if (state.delta.op & (COMPRESS|OUT))
			{
				if (d) d->mark = 1;
				if (fd >= 0 && (!d || f->st->st_mtime != d->mtime))
				{
					if (d)
					{
						f->delta.op = DELTA_update;
						f->st->st_dev = d->dev;
						f->st->st_ino = d->ino;
#if DEBUG
						message(-2, "delta: delta: file=%s offset=%ld size=%ld", f->name, d->offset, d->size);
#endif
						if (d->size)
						{
							if (state.delta.buffersize < d->size)
							{
								state.delta.buffersize = round(d->size, state.blocksize);
								if (state.delta.buffer) free(state.delta.buffer);
								if (!(state.delta.buffer = malloc(state.delta.buffersize)))
									error(3, "input files too large to delta");
							}
							dfd = deltafd(d);
							if (read(dfd, state.delta.buffer, d->size) != d->size)
								error(3, "%s: base archive read error", f->name);
							(void)close(dfd);
						}
					}
					else
					{
						f->delta.op = DELTA_create;
#if DEBUG
						message(-2, "delta: create: file=%s", f->name);
#endif
					}
					if (d || (state.delta.op & COMPRESS))
					{
						if (state.tmp.buffersize < f->st->st_size)
						{
							state.tmp.buffersize = round(f->st->st_size, state.blocksize);
							if (state.tmp.buffer) free(state.tmp.buffer);
							if (!(state.tmp.buffer = malloc(state.tmp.buffersize)))
								error(3, "input files too large to delta");
						}
						if (f->st->st_size && read(fd, state.tmp.buffer, f->st->st_size) != f->st->st_size)
							error(ERROR_SYSTEM|3, "%s: read error", f->path);
						(void)close(fd);
						fd = dodelta(f, state.delta.buffer, d ? d->size : 0L, state.tmp.buffer);
					}
				}
				else
				{
#if DEBUG
					message(-2, "delta: verify: file=%s", f->name);
#endif
					f->delta.op = DELTA_verify;
					f->st->st_size = 0;
				}
			}
			if (fd >= 0)
			{
				if (!d || d->mtime != f->st->st_mtime) fileout(f, fd);
				else if (fd >= 0) close(fd);
			}
		}
		else ftw->status = FTW_SKIP;
	}
	return(0);
}

/*
 * low level for copyout()
 * if rfd<0 && st_size>0 then input from bread()
 */

void
fileout(f, rfd)
register struct fileinfo*	f;
int				rfd;
{
	register int	n;
	register long	c;
	int		err;
	FILE*		rfp;

	if (f->delta.op == DELTA_verify)
	{
		state.selected--;
		if (rfd >= 0) close(rfd);
	}
	else
	{
		putheader(f);
		switch (state.formatout)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			if (state.out.blocked)
			{
				if (f->st->st_size > 0)
				{
					if (state.record.format == 'F' && !state.record.line)
					{
						/*
						 * this is faster than recordout()
						 */

						state.record.file = f;
						err = 0;
						c = f->st->st_size;
						while (c > 0)
						{
							n = c > state.record.size ? state.record.size : c;

							/*
							 * NOTE: we expect that all but the last
							 *	 read returns state.record.size
							 *	 if not the the intermediate short
							 *	 reads are filled with 0's
							 */

							if (!err)
							{
								if (rfd >= 0) n = read(rfd, state.tmp.buffer, n);
								else if (bread(state.tmp.buffer, 0L, (long)n, 1) <= 0) n = -1;
							}
							if (n <= 0)
							{
								if (n) error(ERROR_SYSTEM|2, "%s: read error", f->path);
								else error(2, "%s: file size changed", f->path);
								(void)memset(state.tmp.buffer, 0, state.record.size);
								err = 1;
							}
							else
							{
								c -= n;
								if (n < state.record.size && (c > 0 || state.record.pad))
								{
									(void)memset(state.tmp.buffer + n, 0, state.record.size - n);
									n = state.record.size;
								}
								bwrite(state.tmp.buffer, n);
							}
						}
						state.record.file = 0;
						if (rfd >= 0) close(rfd);
					}
					else if (rfd < 0) recordout(f, (FILE*)0);
					else if (!(rfp = fdopen(rfd, "r")))
					{
						error(1, "%s: cannot read", f->path);
						close(rfd);
					}
					else
					{
						recordout(f, rfp);
						fclose(rfp);
					}
				}
				break;
			}
			/*FALLTHROUGH*/
		default:
			err = 0;
			c = f->st->st_size;
			while (c > 0)
			{
				n = c > state.buffersize ? state.buffersize : c;
				if (!err)
				{
					if (rfd >= 0) n = read(rfd, state.out.next, n);
					else if (bread(state.out.next, 0L, (long)n, 1) <= 0) n = -1;
				}
				if (n <= 0)
				{
					if (n) error(ERROR_SYSTEM|2, "%s: read error", f->path);
					else error(2, "%s: file size changed", f->path);
					(void)memset(state.out.next, 0, state.buffersize);
					err = 1;
				}
				else
				{
					c -= n;
					bput(n);
				}
			}
			if (rfd >= 0) close(rfd);
			break;
		}
		puttrailer(f);
	}
	if (state.acctime && f->type != S_IFLNK && !(state.delta.op & IN))
		settime(f->name, f->st->st_atime, f->st->st_mtime);
}

/*
 * fileout() record support
 */

static void
recordout(f, fp)
struct fileinfo*	f;
FILE*			fp;
{
	register int	c;
	register char*	p;
	register char*	recdat;
	register char*	blkdat;
	char*		rec;
	char*		blk;
	int		span;

	int		count = 0;
	int		partial = 0;
	int		truncated = 0;

	static char	span_out[] = "0132";
	static char*	pardat;

	if (!fp) error(3, "cannot handle record output from buffer");
	state.record.file = f;
	f->record.blocks = 0;
	span = 0;
	blk = state.tmp.buffer;

	/*
	 * file loop
	 */

	for (;;)
	{
		p = blk;
		switch (state.record.format)
		{
		case 'V':
			p += 4;
			break;
		}
		blkdat = p;

		/*
		 * block loop
		 */

		for (;;)
		{
			rec = p;
			switch (state.record.format)
			{
			case 'D':
			case 'V':
				p += 4;
				break;
			case 'S':
				p += 5;
				break;
			}
			recdat = p;

			/*
			 * check for partial record from previous block
			 */

			if (partial)
			{
				(void)memcpy(recdat, pardat, partial);
				p += partial;
				partial = 0;
			}

			/*
			 * record loop
			 */

			span &= 01;
			span <<= 1;
			for (;;)
			{
				if (p >= &rec[state.record.size] && state.record.size)
				{
					if (state.record.line)
					{
						truncated++;
						while ((c = getc(fp)) != EOF && c != '\n');
					}
					break;
				}
				else if (p >= &blk[state.blocksize])
				{
					if (state.record.format == 'S' || state.record.format == 'V')
					{
						if (p > recdat)
						{
							span |= 01;
							break;
						}
					}
					else if (partial = p - recdat)
					{
						/*
						 * save partial record for next block
						 */

						if (!pardat && !(pardat = malloc(state.blocksize))) error(3, "out of space [record pushback buffer]");
						(void)memcpy(pardat, recdat, partial);
					}
					p = rec;
					goto eob;
				}
				else if ((c = getc(fp)) == EOF)
				{
					if (p == recdat)
					{
						if (rec == blkdat) goto eof;
						p = rec;
						goto eob;
					}
					break;
				}
				else if (c == '\n' && state.record.line) break;
				else *p++ = c;
			}
			switch (state.record.format)
			{
			case 'D':
				c = recdat[0];
				(void)sprintf(rec, "%04d", p - rec);
				recdat[0] = c;
				break;
			case 'F':
				if (c != EOF || state.record.pad)
				{
					(void)memset(p, ' ', state.record.size - (p - rec));
					p = rec + state.record.size;
				}
				break;
			case 'S':
				c = recdat[0];
				(void)sprintf(rec, "%c%04d", span_out[span], p - rec);
				recdat[0] = c;
				break;
			case 'U':
				if (p == recdat) *p++ = ' ';
				break;
			case 'V':
				rec[0] = ((p - rec) >> 8) & 0xff;
				rec[1] = (p - rec) & 0xff;
				rec[2] = span;
				rec[3] = 0;
				break;
			}
			if (state.record.charset && state.formatout == IBMAR) cvtatoe(recdat, recdat, p - recdat);
			count++;
			if (p >= &blk[state.blocksize] || state.record.format == 'U') break;
		}
	eob:
		switch (state.record.format)
		{
		case 'D':
		case 'S':
			if (state.record.pad)
			{
				(void)memset(p, '^', state.blocksize - (p - blk));
				p = blk + state.blocksize;
			}
			break;
		case 'V':
			blk[0] = ((p - blk) >> 8) & 0xff;
			blk[1] = (p - blk) & 0xff;
			blk[2] = 0;
			blk[3] = 0;
			break;
		}
		bwrite(blk, p - blk);
		f->record.blocks++;
	}
 eof:
	state.record.file = 0;
	if (truncated) error(1, "%s: %d out of %d record%s truncated", f->name, truncated, count, count == 1 ? "" : "s");
}

/*
 * low level for copyin()
 */

void
filein(f)
register struct fileinfo*	f;
{
	register long	c;
	register int	n;
	register char*	s;
	int		wfd;
	long		checksum;
	struct stat	st;

	if (f->skip || state.list) fileskip(f);
	else switch (f->delta.op)
	{
	case DELTA_create:
		if (f->delta.base) error(3, "%s: base archive mismatch", f->name);
		if (state.delta.op & COMPRESS) goto update;
		goto regular;
	case DELTA_update:
		if (!f->delta.base || f->delta.base->mtime >= f->st->st_mtime) error(3, "%s: base archive mismatch", f->name);
	update:
		if ((wfd = openout(f)) < 0) fileskip(f);
		else if (doupdate(f, state.delta.fd, f->name, wfd) < 0)
			error(ERROR_SYSTEM|2, "%s: delta update error", f->name);
		break;
	case DELTA_verify:
		if (!f->delta.base || f->delta.base->mtime != f->st->st_mtime) error(3, "%s: base archive mismatch", f->name);
		if ((*state.statf)(f->name, &st)) error(2, "%s: not copied from base archive", f->name);
		else if (st.st_size != f->delta.base->size || state.modtime && st.st_mtime != f->st->st_mtime) error(1, "%s: changed from base archive", f->name);
		break;
	case DELTA_delete:
		if (!f->delta.base) error(3, "%s: base archive mismatch", f->name);
		/*FALLTHROUGH*/
	default:
	regular:
		wfd = openout(f);
		switch (state.formatin)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			recordin(f, wfd);
			break;
#if SAVESET
		case SAVESET:
			savesetin(f, wfd);
			break;
#endif
		default:
			if (wfd >= 0)
			{
				checksum = 0;
				for (c = f->st->st_size; c > 0; c -= n)
				{
					n = (c > state.buffersize) ? state.buffersize : c;
					if (!(s = bget(n)))
					{
						error(ERROR_SYSTEM|2, "%s: read error", f->name);
						break;
					}
					if (write(wfd, s, n) != n)
					{
						error(ERROR_SYSTEM|2, "%s: write error", f->name);
						break;
					}
					if (state.formatin == ASCHK) checksum = asc_checksum(s, n, checksum);
				}
				close(wfd);
				setfile(f);
				if (state.formatin == ASCHK && checksum != f->checksum) error(1, "%s: %s checksum error (0x%08x != 0x%08x)", f->name, format[state.formatin].name, checksum, f->checksum);
			}
			else fileskip(f);
			break;
		}
	}
	listentry(f);
}

/*
 * filein() record support
 */

static void
recordin(f, wfd)
register struct fileinfo*	f;
int				wfd;
{
	register long			n;
	register long			size;
	int				c;
	int				i;
	int				j;
	int				k;
	int				nl;
	long				m;
	FILE*				wfp;

	if (wfd < 0) wfp = 0;
	else if (!(wfp = fdopen(wfd, "w"))) error(1, "%s: cannot write", f->name);
	state.in.empty = 0;
	nl = state.record.line;
	size = 0;
	for (;;)
	{
		if (state.in.blocked) n = bread(state.tmp.buffer, 0L, (long)state.buffersize, 0);
		else if ((m = f->st->st_size - size) <= 0) n = 0;
		else if (wfp) 
		{
			if (m > state.buffersize) m = state.buffersize;
			n = bread(state.tmp.buffer, 0L, m, 1);
		}
		else n = bread((char*)0, 0L, m, 1);
		if (n < 0) break;
		if (n == 0)
		{
			k = 1;
			state.delta.sum--;
			while (getlabel(f))
			{
				if (strneq(alar_header, "EOV1", 4)) k = 0;
				else if (!strneq(alar_header, "EOF", 3) && !strneq(alar_header, "EOV", 3) && !strneq(alar_header, "UTL", 3) && ++n >= 16 && !state.keepgoing)
					error(3, "%s: %d invalid %s end of file/volume labels detected", f->name, n, format[state.formatin].name);
			}
			if (n) error(1, "%s: %d invalid %s end of file/volume labels detected", f->name, n, format[state.formatin].name);
			if (k)
			{
				state.delta.sum++;
				break;
			}
			f->record.section++;
			f->id = strcpy(state.tmp.buffer, f->id);
			f->name = strcpy(state.tmp.buffer + ALAR_NAMESIZE + 1, f->name);
			for (;;)
			{
				newio(0, 0, 0);
				if (getprologue())
				{
					struct fileinfo	v;
					struct stat	st;

					v.st = &st;
					if (getheader(&v))
					{
						if (streq(f->id, v.id) && streq(f->name, v.name) && f->record.section == v.record.section)
						{
							f->id = v.id;
							f->name = v.name;
							break;
						}
						error(1, "volume containing %s id %s section %d required", f->name, f->id, f->record.section);
					}
					state.volume--;
				}
				state.part--;
			}
			state.delta.sum++;
			continue;
		}
		if (f->record.format == 'V')
		{
			if ((k = ((unsigned char*)state.tmp.buffer)[0] << 8 | ((unsigned char*)state.tmp.buffer)[1]) != n)
				error(3, "%s: invalid %s V format block descriptor [%d!=%d]", f->name, format[state.formatin].name, k, n);
			i = 4;
		}
		else i = 0;
		while (i < n)
		{
			i += state.record.offset;
			if (state.tmp.buffer[i] == '^') switch (f->record.format)
			{
			case 'F':
				if (state.formatin == IBMAR || state.formatin == PAX) break;
				for (j = i; j < n && state.tmp.buffer[j] == '^'; j++);
				if (j < n) break;
				/*FALLTHROUGH*/
			case 'D':
			case 'S':
				i = n;
				continue;
			}

			/*
			 * get record size
			 */

			switch (f->record.format)
			{
			case 'D':
				if (sscanf(&state.tmp.buffer[i], "%4d", &k) != 1) k = -1;
				j = i + 4;
				break;
			case 'F':
				if (i + state.record.size > n) k = n - i;
				else if (state.record.line || state.record.offset) k = state.record.size;
				else k = n;
				j = i;
				break;
			case 'S':
				switch (state.tmp.buffer[i])
				{
				case '0':
				case '3':
					nl = 1;
					break;
				default:
					nl = 0;
					break;
				}
				if (sscanf(&state.tmp.buffer[i + 1], "%4d", &k) != 1) k = -1;
				j = i + 5;
				break;
			case 'U':
				k = n;
				j = i;
				break;
			case 'V':
				nl = !(state.tmp.buffer[i + 2] & 01);
				k = ((unsigned char*)state.tmp.buffer)[i] << 8 | ((unsigned char*)state.tmp.buffer)[i + 1];
				j = i + 4;
				break;
			}
			if (k < 0)
			{
				error(2, "invalid %s %c record size", format[state.formatin].name, f->record.format);
				break;
			}
			m = i += k;
			if (state.record.charset && state.formatin == IBMAR) cvtetoa(&state.tmp.buffer[j], &state.tmp.buffer[j], m - j);
			if (state.record.line) switch (f->record.format)
			{
			case 'F':
			case 'U':
				while (--m >= j && state.tmp.buffer[m] == ' ');
				m++;
				break;
			}
			k = m - j + nl;
			size += k;
			if (wfp)
			{
				if (nl)
				{
					c = state.tmp.buffer[m];
					state.tmp.buffer[m] = '\n';
				}
				if (fwrite(&state.tmp.buffer[j], 1, k, wfp) != k)
				{
					error(ERROR_SYSTEM|1, "%s: write error", f->name);
					break;
				}
				if (nl) state.tmp.buffer[m] = c;
			}
		}
	}
	if (f->st->st_size && f->st->st_size != size) error(1, "%s: header size %ld does not match data size %ld", f->name, f->st->st_size, size);
	f->st->st_size = size;
	if (wfp)
	{
		fclose(wfp);
		setfile(f);
	}
	if (n < 0) error(ERROR_SYSTEM|3, "%s: archive read error", f->name);
}

#if SAVESET

/*
 * filein() saveset support
 */

static void
savesetin(f, wfd)
register struct fileinfo*	f;
int				wfd;
{
	register long		c;
	int			i;
	int			j;
	int			k;
	FILE*			wfp;

	if (wfd < 0) wfp = 0;
	else if (!(wfp = fdopen(wfd, "w"))) error(1, "%s: cannot write", f->name);
	j = 0;
	k = 0;
	c = 0;
	while (getsaveset(f, 0))
	{
		/*
		 * this part transcribed from vmsbackup
		 */

		i = 0;
		if (wfp) while ((c + i) < f->st->st_size && i < state.saveset.lastsize) switch (state.saveset.recfmt)
		{
		case 1:	/* fixed length		*/
			if (j <= 0) j = state.saveset.reclen;
			fputc(state.saveset.bp[i], wfp);
			i++;
			j--;
			break;
		case 2:	/* variable length	*/
		case 3:	/* with fixed control	*/
			if (j <= 0)
			{
				j = k = gethalf(BYTE|HALF, &state.saveset.bp[i]);
				i += 2;
				if (state.saveset.recfmt == 3)
				{
					i += state.saveset.recvfc;
					j -= state.saveset.recvfc;
				}
			}
			else
			{
				if (j == k && state.saveset.recatt == 1)
				{
					if (state.saveset.bp[i] == '0') state.saveset.bp[i] = '\n';
					else if (state.saveset.bp[i] == '1') state.saveset.bp[i] = '\f';
				}
				fputc(state.saveset.bp[i], wfp);
				i++;
				j--;
			}
			if (j <= 0)
			{
				fputc('\n', wfp);
				if (i & 1) i++;
			}
			break;
		case 4:	/* seq stream		*/
		case 5:	/* seq LF stream	*/
			if (j <= 0) j = 512;
			if (state.saveset.bp[i] == '\n') j = 0;
			else j--;
			fputc(state.saveset.bp[i], wfp);
			i++;
			break;
		case 6:	/* seq CR stream	*/
			if (state.saveset.bp[i] == '\r') state.saveset.bp[i] = '\n';
			fputc(state.saveset.bp[i], wfp);
			i++;
			break;
		default:
			error(state.keepgoing ? 1 : 3, "%s: invalid %s format data record format=%d", f->name, format[state.formatin].name, state.saveset.recfmt);
			goto next;
		}
	next:
		c += i;
	}
	if (wfp)
	{
		fclose(wfp);
		setfile(f);
	}
}

#endif

/*
 * pass data from rfd to wfd
 */

void
filepass(f, rfd, wfd)
register struct fileinfo*	f;
int				rfd;
int				wfd;
{
	register long	c;
	register long	n;

	for (c = f->st->st_size; c > 0; c -= n)
	{
		if ((n = read(rfd, state.tmp.buffer, (c > state.buffersize) ? state.buffersize : c)) <= 0)
		{
			error(ERROR_SYSTEM|2, "%s: read error", f->name);
			break;
		}
		if (write(wfd, state.tmp.buffer, n) != n)
		{
			error(ERROR_SYSTEM|2, "%s: write error", f->name);
			break;
		}
		state.out.count += n;
	}
	(void)close(rfd);
	(void)close(wfd);
	setfile(f);
	listentry(f);
}

/*
 * skip over archive member f file data
 */

void
fileskip(f)
register struct fileinfo*	f;
{
	switch (state.formatin)
	{
	case ALAR:
	case IBMAR:
	case PAX:
		recordin(f, -1);
		break;
#if SAVESET
	case SAVESET:
		savesetin(f, -1);
		break;
#endif
	default:
		if (bread((char*)0, 0L, f->st->st_size, 1) < 0)
			error(ERROR_SYSTEM|2, "%s: skip error", f->name);
		break;
	}
}

/*
 * single file copyin() and copyout() smashed together
 * called by ftwalk()
 */

int
copyinout(ftw)
struct FTW*	ftw;
{
	register struct fileinfo*	f;
	register int			rfd;
	register int			wfd;

	static char			path[PATH_MAX];

	if (getfile(f = &file, ftw) && selectfile(f))
	{
		(void)strcpy(path, state.pwd);
		(void)strcpy(path + state.pwdlen, f->name + (*f->name == '/'));
		f->name = path;
		if ((wfd = openout(f)) >= 0)
		{
			if ((rfd = openin(f)) >= 0) filepass(f, rfd, wfd);
			else (void)close(wfd);
		}
		else if (wfd != -1) listentry(f);
	}
	return(0);
}

/*
 * compare ft1 and ft2 for ftwalk() sort
 */

int
cmpftw(ft1, ft2)
struct FTW*	ft1;
struct FTW*	ft2;
{
	return(strcmp(ft1->name, ft2->name));
}


/*
 * copy files out using copyfile
 */

void
copy(copyfile)
register int	(*copyfile)();
{
	register char*	s;
	register int	n;

	if (state.files) n = ftwalk((char*)state.files, copyfile, state.ftwflags|FTW_MULTIPLE, state.exact ? (int(*)())0 : cmpftw);
	else
	{
		n = 0;
		while (s = fgetline(stdin, 0))
			if (n = ftwalk(s, copyfile, state.ftwflags, (int(*)())0)) break;
		if (n) error(2, "%s: not completely copied", s);
	}
}

/*
 * position archive for appending
 */

void
append()
{
	if (state.update) initdelta();
	state.operation = IN;
	state.formatin = IN_DEFAULT;
	copyin();
	state.append = 0;
	state.formatout = state.formatin;
	state.operation = OUT;
}
0707070000000000071006440044230044230000010000000467117701400002300000005120cpio.shUgsfGgsf:
# Glenn Fowler
# AT&T Bell Laboratories
# @(#)cpio.sh (ulysses!gsf) 08/11/90
#
# cpio -> pax interface script
#

command=cpio
usage="
Usage: $command -o[acvBV] [-C size] [-M mesg] [-O file | >file ] <list
       $command -i[bcdfkmrtsuvBSV6] [-I file | <file] [pattern ...]
       $command -p[adlmuvV] directory"

case $1 in
*i*)	mode="-r" ;;
*o*)	mode="-w" ;;
*p*)	mode="-rw" ;;
*)	echo "$command: one of -i, -o, -p must be specified$usage" >&2; exit 1 ;;
esac
options=""
blocksize=1b
debug=
format=binary
list=""
logphys=-P
d_default="-d"
m_default="-m"
u_default="-u"
r_ok="1"
w_ok="1"
p_ok="1"
while	:
do	case $# in
	0)	break ;;
	esac
	case $1 in
	--)	shift; break ;;
	-*)	for opt in `echo '' $1 | sed -e 's/-//' -e 's/./& /g'`
		do	case $opt in
			'#')	case $debug in
				"")	debug=echo ;;
				*)	debug=args ;;
				esac
				;;
			[bsS6]) ;;
			[klvV])	options="$options -$opt" ;;
			a)	r_ok="" options="$options -p" ;;
			c)	format=cpio ;;
			d)	w_ok="" d_default="" ;;
			f)	w_ok="" p_ok="" options="$options -c" ;;
			i)	w_ok="" p_ok="" ;;
			m)	w_ok="" m_default="" ;;
			o)	r_ok="" p_ok="" u_default="" ;;
			p)	r_ok="" w_ok="" ;;
			r)	w_ok="" p_ok="" options="$options -i" ;;
			t)	w_ok="" p_ok="" list="1" ;;
			u)	w_ok="" u_default="" ;;
			B)	blocksize=5k ;;
			L)	logphys=-L ;;
			[CIMO])	a=`echo '' $1 | sed -e "s/[^$opt]*$opt//"`
				case $a in
				"")	case $# in
					1)	echo "$command: option -$opt requires an argument$usage" >&2; exit 1 ;;
					esac
					shift
					a=$1
					;;
				esac
				case $opt in
				C)	case $a in
					*[0-9])	a=${a}c ;;
					esac
					blocksize=$a
					;;
				I)	w_ok="" p_ok="" options="$options -f '$a'" ;;
				O)	r_ok="" p_ok="" options="$options -f '$a'" ;;
				M)	options="$options -$opt '$a'" ;;
				esac
				break
				;;
			*)	echo "$command: invalid option -$opt$usage" >&2; exit 1 ;;
			esac
		done
		;;
	*)	break ;;
	esac
	shift
done
case $mode in
-r)	case $r_ok in
	"")	echo "$command: options inconsistent with archive read" >&2; exit 1 ;;
	esac
	options="$options -b $blocksize"
	;;
-w)	case $w_ok in
	"")	echo "$command: options inconsistent with archive write" >&2; exit 1 ;;
	esac
	case $# in
	0)	;;
	*)	echo "$command: arguments not expected" >&2; exit 1 ;;
	esac
	options="$options -x $format -b $blocksize"
	;;
-rw)	case $p_ok in
	"")	echo "$command: options inconsistent with file pass" >&2; exit 1 ;;
	esac
	case $# in
	1)	;;
	*)	echo "$command: a single directory argument is expected$usage" >&2; exit 1 ;;
	esac
	;;
esac
case $list in
"1")	mode="" d_default="" m_default="" u_default="" ;;
esac
$debug pax $mode $logphys $options $d_default $m_default $u_default "$@"
0707070000000000101006440044230044230000010000000472226354600002300000026365delta.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax delta archive support
 */

#include "pax.h"

static void		deltacopy();

extern int		delta();
extern int		update();

#if 0

static char*
getdelstr(f)
register struct fileinfo*	f;
{
	register char*	s;
	register char*	b = state.delta.hdrbuf;
	unsigned char	c;

	s = b;
	do
	{
		if (f->st->st_size <= 0) return(0);
		if (bread(&c, 1L, 1L, 1) <= 0)
			error(ERROR_SYSTEM|3, "%s: read error", f->path);
		f->st->st_size--;
	} while (*s++ = c);
	return(*b ? b : 0);
}

#endif


/*
 * get supplemental delta header info
 */

void
getdeltaheader(f)
register struct fileinfo*	f;
{
	register char*	s;

	f->delta.version = VERSION;
	f->delta.size = -1;
	if ((state.delta.op & (COLLECT|IN)) == IN)
	{
		if (state.delta.tab && (f->delta.base = (struct deltainfo*)hashget(state.delta.tab, f->path)))
			f->delta.base->mark = 1;
		f->delta.op = DELTA_create;
#if new_delta_format
		if (f->st->st_size && (s = getdelstr(f)))
		{
			if (f->delta.op = *s++) f->delta.version = *s;
			while (s = getdelstr(f)) switch (*s++)
			{
			case 's':
				f->delta.size = strtol(s, (char*)0, 16);
				break;
			/*
			 * NOTE: ignore unknown ops for future extensions
			 */
			}
		}
#else
		if (f->st->st_size)
		{
			char	c;

			f->delta.op = 0;
			do
			{
				if (bread(&c, 1L, 1L, 1) <= 0)
					error(ERROR_SYSTEM|3, "%s: read error", f->path);
				f->st->st_size--;
				if (!f->delta.op) f->delta.op = c;
			} while (c != '\n');
		}
#endif
	}
	else if (state.delta.op & CONVERT) f->delta.op = DELTA_pass;
	else f->delta.op = 0;
}

/*
 * add delta header string
 */

void
adddelstr(op, s)
int		op;
register char*	s;
{
	register char*	p = state.delta.hdr;
	register char*	e = state.delta.hdrbuf + state.buffersize - 3;

	if (p < e)
	{
		*p++ = op;
		while (*s && p < e) *p++ = *s++;
		*p++ = 0;
		state.delta.hdr = p;
	}
#if DEBUG
	if (*s) error(PANIC, "adddelstr('%c',\"%s\") overflow", op, s);
#endif
}

/*
 * add delta header number
 */

void
adddelnum(op, n)
int	op;
long	n;
{
	char	buf[17];

	(void)sprintf(buf, "%x", n);
	adddelstr(op, buf);
}

/*
 * output supplementary delta header info
 */

void
putdeltaheader(f)
register struct fileinfo*	f;
{
	register char*	s;
	int		n;

	if (f->delta.op && (n = state.delta.hdr - state.delta.hdrbuf))
	{
		switch (state.formatout)
		{
		case IBMAR:
			if (state.formatout == IBMAR) cvtatoe(state.delta.hdrbuf, state.delta.hdrbuf, n);
			/*FALLTHROUGH*/
		default:
			bwrite(state.delta.hdrbuf, n);
			break;
		}
		f->st->st_size -= n;
		f->linknamesize -= n;
		state.delta.hdr = state.delta.hdrbuf;
	}
}

/*
 * initialize delta tables
 */

void
initdelta()
{
	if (!(state.delta.tab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_name, "delta", 0)))
			error(3, "cannot allocate delta table");
}

/*
 * get delta base archive info
 */

void
deltabase()
{
	register char*	s;
	register int	fd;
	int		format;
	int		operation;
	struct stat	st;

	if (!state.delta.base) state.delta.base = "/dev/null";
	if ((state.delta.fd = open(state.delta.base, 0)) < 0 || fstat(state.delta.fd, &st))
		error(ERROR_SYSTEM|3, "%s: cannot open base archive", state.delta.base);
	initdelta();
	if (st.st_size)
	{
		operation = state.operation;
		state.operation = IN;
		fd = dup(0);
		(void)close(0);
		if (dup(state.delta.fd) != 0)
			error(ERROR_SYSTEM|3, "%s: cannot dup base archive", state.delta.base);
		if (lseek(0, 0L, 0) != 0)
			error(ERROR_SYSTEM|3, "%s: base archive must be seekable", state.delta.base);
		s = state.file;
		state.file = state.delta.base;
		format = state.formatin;
		state.formatin = IN_DEFAULT;
		if (state.delta.op)
		{
			state.delta.op |= COLLECT;
			state.delta.sum++;
		}
		copyin();
		if (state.delta.op)
		{
			state.delta.op &= ~COLLECT;
			state.delta.sum--;
		}
		state.formatin = format;
		state.file = s;
		(void)close(0);
		if (dup(fd) != 0)
			error(3, "cannot dup standard input");
		(void)close(fd);
		state.delta.size = state.in.offset + state.in.count;
		state.in.offset = 0;
		state.in.count = 0;
#if DEBUG
		state.in.blokflag = 0;
#endif
		state.in.eof = 0;
		state.volume = 0;
		state.operation = operation;
	}
	else state.delta.op |= COMPRESS;
}

/*
 * return read fd to delta base member d
 * some formats may require a tmp file copy
 */

int
deltafd(d)
register struct deltainfo*      d;
{
	register int	fd;

	if ((fd = dup(state.delta.fd)) < 0)
		error(ERROR_SYSTEM|3, "%s: cannot reopen", state.delta.base);
	if (lseek(fd, d->offset, 0) < 0)
		error(ERROR_SYSTEM|3, "%s: base archive seek error", state.delta.base);
	return(fd);
}

/*
 * verify untouched base files
 */

void
deltaverify()
{
	register int			wfd;
	register struct deltainfo*	d;
	HASHPOSITION			pos;

	if (!state.list && (state.delta.op & (COLLECT|IN)) == IN)
	{
#if DEBUG
		message(-2, "verify untouched base files");
#endif
		hashscan(state.delta.tab, 0, &pos);
		while (hashnext(&pos))
		{
			d = (struct deltainfo*)pos.bucket->value;
message(-1, "%s: mark=%d", d->info->name, d->mark);
			if (!d->mark && selectfile(d->info) && (wfd = openout(d->info)) >= 0)
			{
				state.entries++;
				filepass(d->info, deltafd(d), wfd);
			}
		}
		hashdone(&pos);
	}
}

/*
 * update file deltas from archive and output to archive
 */

void
deltapass()
{
	register struct fileinfo*	f;
	register long			c;
	register long			n;
	struct deltainfo*		d;
	char*				p;
	int				fd;
	HASHPOSITION			pos;

#if DEBUG
	message(-1, "delta PASS %s", operations());
#endif
	putprologue();
	f = &file;
	while (getprologue())
	{
		while (getheader(f))
		{
			switch (f->delta.op)
			{
			case DELTA_create:
				if (f->delta.base) error(3, "%s: base archive mismatch", f->name);
				/*FALLTHROUGH*/
			case DELTA_pass:
				if (validout(f) && selectfile(f))
				{
					if (state.delta.op & COMPRESS)
					{
						if (state.tmp.buffersize < f->st->st_size)
						{
							state.tmp.buffersize = round(f->st->st_size, state.blocksize);
							if (state.tmp.buffer) free(state.tmp.buffer);
							if (!(state.tmp.buffer = malloc(state.tmp.buffersize)))
								error(3, "input files too large to delta");
						}
						p = state.tmp.buffer;
						for (c = f->st->st_size; c > 0; c -= state.buffersize)
						{
							n = c > state.buffersize ? state.buffersize : c;
							if (bread(p, n, n, 1) <= 0)
							{
								error(ERROR_SYSTEM|2, "%s: read error", f->name);
								break;
							}
							p += n;
						}
						goto delta;
					}
					f->delta.op = 0;
					fileout(f, -1);
				}
				else fileskip(f);
				break;
			case DELTA_delete:
				if (!f->delta.base) error(3, "%s: base archive mismatch", f->name);
				break;
			case DELTA_update:
				if (!f->delta.base || f->delta.base->mtime >= f->st->st_mtime) error(3, "%s: base archive mismatch", f->name);
				if (validout(f) && selectfile(f))
				{
					if ((fd = creat(state.tmp.file, S_IRUSR|S_IRGRP|S_IROTH)) < 0)
						error(ERROR_SYSTEM|3, "%s: cannot create delta temporary file", state.tmp.file);
					if (doupdate(f, state.delta.fd, state.tmp.file, fd) < 0)
						error(ERROR_SYSTEM|2, "%s: delta update error", f->name);
					if ((fd = open(state.tmp.file, 0)) < 0)
						error(3, "%s: cannot read delta temporary file", state.tmp.file);
					if (remove(state.tmp.file))
						error(1, "%s: cannot remove delta temporary file", state.tmp.file);
					if (state.delta.op & COMPRESS)
					{
						if (state.tmp.buffersize < f->st->st_size)
						{
							state.tmp.buffersize = round(f->st->st_size, state.blocksize);
							if (state.tmp.buffer) free(state.tmp.buffer);
							if (!(state.tmp.buffer = malloc(state.tmp.buffersize)))
								error(3, "input files too large to delta");
						}
						if (f->st->st_size && read(fd, state.tmp.buffer, f->st->st_size) != f->st->st_size)
							error(ERROR_SYSTEM|3, "%s: read error", f->path);
						(void)close(fd);
					delta:
						f->delta.op = DELTA_create;
						fd = dodelta(f, state.delta.buffer, 0L, state.tmp.buffer);
					}
					else f->delta.op = 0;
					fileout(f, fd);
				}
				else fileskip(f);
				break;
			case DELTA_verify:
				if (!f->delta.base || f->delta.base->mtime != f->st->st_mtime) error(3, "%s: base archive mismatch", f->name);
				if (validout(f) && selectfile(f))
				{
					f->delta.op = 0;
					deltacopy(f);
				}
				else fileskip(f);
				break;
			default:
				error(3, "%s: not a delta archive (2)", state.file);
				break;
			}
			gettrailer(f);
		}
		getepilogue();
	}
	if (state.delta.tab)
	{
		/*
		 * copy the non-empty untouched base hard links first
		 */

#if DEBUG
		message(-2, "copy non-empty untouched base hard links");
#endif
		hashscan(state.delta.tab, 0, &pos);
		while (hashnext(&pos))
		{
			d = (struct deltainfo*)pos.bucket->value;
			if (!d->mark && d->info->st->st_nlink > 1 && d->info->st->st_size > 0 && selectfile(d->info))
			{
				d->mark = 1;
				deltacopy(d->info);
			}
		}
		hashdone(&pos);

		/*
		 * copy the remaining untouched base files
		 */

#if DEBUG
		message(-2, "copy remaining untouched base files");
#endif
		hashscan(state.delta.tab, 0, &pos);
		while (hashnext(&pos))
		{
			d = (struct deltainfo*)pos.bucket->value;
			if (!d->mark && selectfile(d->info))
			{
				state.entries++;
				deltacopy(d->info);
			}
		}
		hashdone(&pos);
	}
	putepilogue();
	state.volume = 0;
}

/*
 * copy file from input to output archive
 */

static void
deltacopy(f)
register struct fileinfo*	f;
{
	f->st->st_size = f->delta.base->size;
	fileout(f, deltafd(f->delta.base));
}

/*
 * copy delta base archive delete entries
 */

void
deltadelete()
{
	register struct fileinfo*	f;
	HASHPOSITION			pos;

	f = &file;
	hashscan(state.delta.tab, 0, &pos);
	while (hashnext(&pos))
	{
		if (!((struct deltainfo*)pos.bucket->value)->mark)
		{
			state.entries++;
			state.selected++;
			initfile(f, pos.bucket->name, S_IFREG);
			f->delta.op = DELTA_delete;
			putheader(f);
			puttrailer(f);
		}
	}
	hashdone(&pos);
}

/*
 * delta algorithm wrapper
 */

/*ARGSUSED*/
int
dodelta(f, old, oldsize, new)
struct fileinfo*	f;
char*			old;
long			oldsize;
char*			new;
{
	int	fd;

	if ((fd = creat(state.tmp.file, S_IRUSR)) < 0)
		error(3, "%s: cannot create delta temporary file", state.tmp.file);
	if (delta(old, oldsize, new, f->st->st_size, fd) < 0)
		error(3, "%s: delta write error", f->name);
	f->delta.size = f->st->st_size;
	if ((f->st->st_size = lseek(fd, 0L, 2)) < 0)
		error(3, "%s: delta seek error", f->name);
	(void)close(fd);
	if ((fd = open(state.tmp.file, 0)) < 0)
		error(3, "%s: cannot read delta temporary file", state.tmp.file);
	if (remove(state.tmp.file))
		error(1, "%s: cannot remove delta temporary file", state.tmp.file);
	return(fd);
}

/*
 * delta update algorithm wrapper
 */

/*ARGSUSED*/
int
doupdate(f, oldfd, file, wfd)
struct fileinfo*	f;
int			oldfd;
char*			file;
int			wfd;
{
	register long	c;
	register int	n;
	register int	v;
	int		rfd;

	switch (f->delta.version)
	{
	case VERSION:
		c = lseek(0, 0L, 1);
		if (lseek(0, state.in.count, 0) != state.in.count) return(-1);
		if ((rfd = open(file, 0)) < 0)
			error(ERROR_SYSTEM|3, "%s: cannot read delta temporary file", file);
		v = update(oldfd, f->delta.base->offset, 0, wfd, rfd, 0L);
		if ((n = lseek(wfd, 0L, 2)) < 0)
			error(ERROR_SYSTEM|3, "%s: update seek error", f->name);
		(void)close(rfd);
		(void)close(wfd);
		if (state.operation == IN) setfile(f);
		if (lseek(0, c, 0) != c || v < 0) return(-1);
		fileskip(f);
		f->st->st_size = n;
		return(0);
	default:
		(void)close(wfd);
		fileskip(f);
		error(2, "%s: version %c delta not supported", f->name, f->delta.version);
		return(-1);
	}
}
0707070000000000111006440044230044230000010000000474562247500002200000043006file.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax file support
 */

#include "pax.h"

extern char*	fmtuid();
extern char*	fmtgid();

/*
 * return read file descriptor for current input file
 */

int
openin(f)
register struct fileinfo*	f;
{
	register int	n;
	int		rfd;
	int		wfd;
	int		pfd;
	char*		cmdargv[3];

	extern int	cmdopen();
	extern int	cmdclose();

	if (f->type != S_IFREG) return(-1);
	if (!state.filter)
	{
		if ((rfd = open(f->path, 0)) < 0) error(ERROR_SYSTEM|2, "%s: cannot read", f->path);
		else if (state.formatout == ASCHK)
		{
			f->checksum = 0;
			if (lseek(rfd, 0L, 0) != 0) error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, format[state.formatout].name);
			else
			{
				while ((n = read(rfd, state.tmp.buffer, state.buffersize)) > 0)
					f->checksum = asc_checksum(state.tmp.buffer, n, f->checksum);
				if (n < 0) error(ERROR_SYSTEM|2, "%s: %s checksum read error", f->path, format[state.formatout].name);
				if (lseek(rfd, 0L, 0) != 0) error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, format[state.formatout].name);
			}
		}
		return(rfd);
	}
	if ((wfd = creat(state.tmp.file, S_IRUSR)) < 0)
	{
		error(2, "%s: cannot create filter temporary %s", f->path, state.tmp.file);
		return(-1);
	}
	if ((rfd = open(state.tmp.file, 0)) < 0)
	{
		error(2, "%s: cannot open filter temporary %s", f->path, state.tmp.file);
		(void)close(wfd);
		if (remove(state.tmp.file)) error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file);
		return(-1);
	}
	if (remove(state.tmp.file)) error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file);
	cmdargv[0] = state.filter;
	cmdargv[1] = f->path;
	cmdargv[2] = 0;
	if ((pfd = cmdopen(state.filter, cmdargv, (char**)0, (int*)0, "r")) < 0)
	{
		error(2, "%s: cannot execute filter %s", f->path, state.filter);
		(void)close(rfd);
		(void)close(wfd);
		return(-1);
	}
	if (state.formatout == ASCHK) f->checksum = 0;
	f->st->st_size = 0;
	while ((n = read(pfd, state.tmp.buffer, state.buffersize)) > 0)
	{
		if (write(wfd, state.tmp.buffer, n) != n)
		{
			error(2, "%s: filter write error", f->path);
			break;
		}
		if (state.formatout == ASCHK) f->checksum = asc_checksum(state.tmp.buffer, n, f->checksum);
		f->st->st_size += n;
	}
	if (n < 0) error(ERROR_SYSTEM|2, "%s: %s filter read error", f->path, state.filter);
	if (n = cmdclose(pfd)) error(2, "%s: %s filter exit code %d", f->path, state.filter, n);
	(void)close(wfd);
#if DEBUG
	message(-1, "%s: filter file size = %ld", f->path, f->st->st_size);
#endif
	return(rfd);
}

/*
 * open file for writing, set all necessary info
 */

int
openout(f)
register struct fileinfo*	f;
{
	register int	fd;
	struct stat	st;

	(void)pathcanon(f->name);
	if ((*state.statf)(f->name, &st)) st.st_mode = 0;
	if (f->delta.op == DELTA_delete)
	{
		switch (st.st_mode & S_IFMT)
		{
		case 0:
			break;
		case S_IFDIR:
			if (!streq(f->name, ".") && !streq(f->name, "..") && rmdir(f->name))
				error(ERROR_SYSTEM|2, "%s: cannot remove directory", f->name);
			break;
		default:
			if (remove(f->name))
				error(ERROR_SYSTEM|2, "%s: cannot remove file", f->name);
			break;
		}
		return(-1);
	}
	if (state.operation == (IN|OUT))
	{
		if (st.st_mode && f->st->st_ino == st.st_ino && f->st->st_dev == st.st_dev)
		{
			error(2, "attempt to pass %s to self", f->name);
			return(-1);
		}
		if (state.linkf && f->type != S_IFDIR && (state.linkf == putsymlink || f->st->st_dev == state.dev))
		{
			if (st.st_mode) remove(f->name);
			if ((*state.linkf)(f->path, f->name))
			{
				if (!st.st_mode && missdir(f))
				{
					error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
					return(-1);
				}
				if (st.st_mode || (*state.linkf)(f->path, f->name))
				{
					error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->path, f->name);
					return(-1);
				}
			}
			setfile(f);
			return(-2);
		}
	}
	if ((state.delta.op & IN) && st.st_mode == f->st->st_mode && f->st->st_mtime == st.st_mtime && f->st->st_size == st.st_size) return(-1);
	switch (f->type)
	{
	case S_IFDIR:
		f->st->st_size = 0;
		if (f->name[0] == '.' && (f->name[1] == 0 || f->name[1] == '.' && f->name[2] == 0)) return(-1);
		if (st.st_mode)
		{
			if ((st.st_mode & S_IFMT) != S_IFDIR)
			{
				error(1, "current %s is not a directory", f->name);
				return(-1);
			}
		}
		else if (mkdir(f->name, f->st->st_mode) && (missdir(f) || mkdir(f->name, f->st->st_mode)))
		{
			error(ERROR_SYSTEM|2, "%s: cannot create directory", f->name);
			return(-1);
		}
		setfile(f);
		return(state.update && st.st_mode ? -1 : -2);
	case S_IFLNK:
		if (streq(f->name, f->linkname))
		{
			error(1, "%s: symbolic link loops to self", f->name);
			return(-1);
		}
		if (st.st_mode)
		{
			if (state.update && f->st->st_mtime <= st.st_mtime) return(-1);
			if (remove(f->name))
			{
				error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
				return(-1);
			}
		}
		if (putsymlink(f->linkname, f->name))
		{
			if (!st.st_mode && missdir(f))
			{
				error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
				return(-1);
			}
			if (st.st_mode || putsymlink(f->linkname, f->name))
			{
				error(ERROR_SYSTEM|2, "%s: cannot symlink to %s", f->name, f->linkname);
				return(-1);
			}
		}
		return(-2);
	}
	if (!addlink(f)) return(-1);
	if (st.st_mode)
	{
		if (state.update && f->st->st_mtime <= st.st_mtime)
		{
			if (f->st->st_mtime < st.st_mtime) error(1, "current %s is newer", f->name);
			return(-1);
		}
		if (remove(f->name))
		{
			error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
			return(-1);
		}
	}
	switch (f->type)
	{
	case S_IFIFO:
	case S_IFSOCK:
		f->st->st_rdev = 0;
	case S_IFBLK:
	case S_IFCHR:
		f->st->st_size = 0;
		if (mknod(f->name, f->st->st_mode, f->st->st_rdev))
		{
			if (errno == EPERM)
			{
				error(ERROR_SYSTEM|2, "%s: cannot create %s special file", f->name, (f->type == S_IFBLK) ? "block" : "character");
				return(-1);
			}
			if (!st.st_mode && missdir(f))
			{
				error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
				return(-1);
			}
			if (st.st_mode || mknod(f->name, f->st->st_mode, f->st->st_rdev))
			{
				error(ERROR_SYSTEM|2, "%s: cannot mknod", f->name);
				return(-1);
			}
		}
		setfile(f);
		return(-2);
	default:
		error(1, "%s: unknown file type 0%03o -- creating regular file", f->name, f->type >> 12);
		/*FALLTHROUGH*/
	case S_IFREG:
		if ((fd = creat(f->name, f->st->st_mode)) < 0)
		{
			if (!st.st_mode && missdir(f))
			{
				error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
				return(-1);
			}
			if (st.st_mode || (fd = creat(f->name, f->st->st_mode)) < 0)
			{
				error(ERROR_SYSTEM|2, "%s: cannot create", f->name);
				return(-1);
			}
		}
		return(fd);
	}
}

/*
 * get file info for output
 */

int
getfile(f, ftw)
register struct fileinfo*	f;
register struct FTW*		ftw;
{
	register char*		name;

	static struct stat	st;
	static char		pathbuffer[PATH_MAX];
	static char		namebuffer[PATH_MAX];

	name = ftw->path;
#if DEBUG
	message(-4, "getfile(%s)", name);
#endif
	if (ftw->pathlen >= sizeof(namebuffer))
	{
		error(2, "%s: file name too long", name);
		return(0);
	}
	switch (ftw->info)
	{
	case FTW_NS:
		error(2, "%s: not found", name);
		return(0);
	case FTW_DNR:
		if (state.files) error(2, "%s: cannot read directory", name);
		break;
	case FTW_D:
	case FTW_DNX:
		if (!state.files) ftw->status = FTW_SKIP;
		else if (ftw->info == FTW_DNX)
		{
			error(2, "%s: cannot search directory", name);
			ftw->status = FTW_SKIP;
		}
		break;
	}
	if (state.xdev && ftw->statb.st_dev != ftw->parent->statb.st_dev)
	{
		ftw->status = FTW_SKIP;
		return(0);
	}
	state.delta.hdr = state.delta.hdrbuf;
	f->path = strcpy(pathbuffer, name);
	(void)pathcanon(strcpy(namebuffer, name));
	f->name = map(namebuffer);
	if (state.files && state.operation == (IN|OUT) && dirprefix(state.destination, name)) return(0);
	f->namesize = strlen(f->name) + 1;
/* this may not be necessary */
	st = ftw->statb;
	f->st = &st;
	f->type = f->st->st_mode & S_IFMT;
	f->linktype = NOLINK;
	f->linkname = 0;
	f->linknamesize = 0;
	f->uidname = 0;
	f->gidname = 0;
	if (!validout(f)) return(0);
	if (state.operation == OUT && f->type != S_IFDIR)
	{
		if (!addlink(f)) f->st->st_size = 0;
#if DEBUG
		message(-3, "getfile(%s): dev'=%d ino'=%d", f->name, f->st->st_dev, f->st->st_ino);
#endif
	}
	state.entries++;
	f->delta.op = 0;
	f->skip = 0;
#if DEBUG
	message(-2, "getfile(): path=%s name=%s mode=%s size=%ld", name, f->name, fmtmode(f->st->st_mode), f->st->st_size);
#endif
	return(1);
}

/*
 * check that f is valid for archive output
 */

int
validout(f)
register struct fileinfo*	f;
{
	register char*	s;

	static char	linkbuffer[PATH_MAX];
	static char	idbuffer[ALAR_NAMESIZE + 1];

	switch (f->type)
	{
	case S_IFCHR:
	case S_IFBLK:
		f->st->st_size = 0;
		break;
	case S_IFREG:
		f->st->st_rdev = 0;
		break;
	case S_IFLNK:
		if (f->st->st_size >= sizeof(linkbuffer) - 1)
		{
			error(2, "%s: symbolic link too long", f->path);
			return(0);
		}
		if (readlink(f->path, linkbuffer, sizeof(linkbuffer) - 1) != f->st->st_size)
		{
			error(2, "%s: cannot read symbolic link", f->path);
			return(0);
		}
		linkbuffer[f->st->st_size] = 0;
		f->linktype = SOFTLINK;
		(void)pathcanon(linkbuffer);
		f->linkname = (state.ftwflags & FTW_PHYSICAL) ? linkbuffer : map(linkbuffer);
		f->linknamesize = strlen(f->linkname) + 1;
		if (streq(f->path, f->linkname))
		{
			error(2, "%s: symbolic link loops to self", f->path);
			return(0);
		}
		f->st->st_size = 0;
		f->st->st_rdev = 0;
		break;
	case S_IFDIR:
		if (streq(f->path, ".") || streq(f->path, "..")) return(0);
		f->st->st_size = 0;
		f->st->st_rdev = 0;
		break;
	}
	switch (state.formatout)
	{
	case ALAR:
	case IBMAR:
	case SAVESET:
		if (f->type != S_IFREG)
		{
			error(2, "%s: only regular files copied in %s format", f->path, format[state.formatout].name);
			return(0);
		}
		if (s = strrchr(f->name, '/'))
		{
			s++;
			error(1, "%s: file name stripped to %s", f->name, s);
		}
		else s = f->name;
		if (strlen(s) > sizeof(idbuffer) - 1)
		{
			error(2, "%s: file name too long", f->name);
			return(0);
		}
		f->id = strupper(strcpy(idbuffer, s));
		break;
	case BINARY:
		if (f->namesize > BINARY_NAMESIZE)
		{
			error(2, "%s: file name too long", f->name);
			return(0);
		}
		break;
	case PAX:
		if (s = strrchr(f->name, '/')) s++;
		else s = f->name;
		f->id = strupper(strncpy(idbuffer, s, sizeof(idbuffer) - 1));
		break;
	case TAR:
	case USTAR:
		if (f->namesize > sizeof(tar_header.name) + ((state.formatout == TAR) ? -(f->type == S_IFDIR) : sizeof(tar_header.prefix)))
		{
			error(2, "%s: file name too long", f->name);
			return(0);
		}
		if (f->linknamesize > sizeof(tar_header.linkname))
		{
			error(2, "%s: link name too long", f->name);
			return(0);
		}
		break;
	}
	return(1);
}

/*
 * add file which may be a link
 * 0 returned if <dev,ino> already added
 */

int
addlink(f)
register struct fileinfo*	f;
{
	register struct linkinfo*	p;
	register char*			s;
	int				fmt;
	struct fileid			id;
	unsigned short			us;

	static int			warned;

	id.dev = f->st->st_dev;
	id.ino = f->st->st_ino;
	if (!(state.delta.op & (IN|OUT))) switch (state.operation)
	{
	case IN:
		us = id.dev;
		if (us > state.devcnt)
		{
			state.devcnt = us;
			state.inocnt = id.ino;
		}
		else if (us == state.devcnt)
		{
			us = id.ino;
			if (us > state.inocnt) state.inocnt = us;
		}
		break;
	case OUT:
		if (!++state.inocnt)
		{
			if (!++state.devcnt) goto toomany;
			state.inocnt = 1;
		}
		f->st->st_dev = state.devcnt;
		f->st->st_ino = state.inocnt;
		break;
	}
	if (f->type == S_IFDIR) return(0);
	fmt = state.operation == IN ? state.formatin : state.formatout;
	switch (fmt)
	{
	case ALAR:
	case IBMAR:
	case SAVESET:
		if (state.operation == IN || f->st->st_nlink <= 1) return(1);
		break;
	case TAR:
	case USTAR:
		if (state.operation == IN)
		{
			if (f->linktype == NOLINK) return(1);
			goto linked;
		}
		/*FALLTHROUGH*/
	default:
		if (f->st->st_nlink <= 1) return(1);
		break;
	}
	if (p = (struct linkinfo*)hashget(state.linktab, (char*)&id))
	{
		switch (fmt)
		{
		case ALAR:
		case IBMAR:
		case SAVESET:
			error(1, "%s: hard link information lost in %s format", f->name, format[fmt].name);
			return(1);
		}
		f->st->st_dev = p->id.dev;
		f->st->st_ino = p->id.ino;
		f->linktype = HARDLINK;
		f->linkname = p->name;
		f->linknamesize = p->namesize;
		if (state.operation == OUT) return(0);
	linked:
#if DEBUG
		message(-1, "addlink(%s,%s)", f->name, f->linkname);
#endif
		if (streq(f->name, f->linkname))
		{
			error(2, "%s: hard link loops to self", f->name);
			return(0);
		}
		if (!state.list)
		{
			s = f->linkname;
			if (access(s, 0))
			{
				f->skip = 1;
				error(2, "%s must exist for hard link %s", s, f->name);
				return(0);
			}
			remove(f->name);
			if (state.operation == IN && *s != '/')
			{
				(void)strcpy(state.pwd + state.pwdlen, s);
				s = state.pwd;
			}
			if (link(s, f->name))
			{
				if (missdir(f))
				{
					error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
					return(0);
				}
				if (link(s, f->name))
				{
					error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->linkname, f->name);
					return(-1);
				}
			}
		}
		return(0);
	}
	if (!(p = allocate(struct linkinfo)) || !(p->name = strdup(f->name))) goto toomany;
	p->namesize = strlen(p->name) + 1;
	p->id.dev = f->st->st_dev;
	p->id.ino = f->st->st_ino;
	(void)hashput(state.linktab, (char*)0, p);
	return(-1);
 toomany:
	if (!warned)
	{
		warned = 1;
		error(1, "too many hard links -- some links may become copies");
	}
	return(-1);
}

/*
 * get file uid and gid names given numbers
 */

void
getidnames(f)
register struct fileinfo*	f;
{
	if (!f->uidname) f->uidname = fmtuid(f->st->st_uid);
	if (!f->gidname) f->gidname = fmtgid(f->st->st_gid);
}

/*
 * set file uid and gid numbers given names
 */

void
setidnames(f)
register struct fileinfo*	f;
{
	register int	id;

	if (f->uidname)
	{
		if ((id = struid(f->uidname)) < 0)
		{
			if (id == -1 && state.owner) error(1, "%s: invalid user name", f->uidname);
			f->uidname = 0;
			id = state.uid;
		}
		f->st->st_uid = id;
	}
	if (f->gidname)
	{
		if ((id = strgid(f->gidname)) < 0)
		{
			if (id == -1 && state.owner) error(1, "%s: invalid group name", f->gidname);
			f->gidname = 0;
			id = state.gid;
		}
		f->st->st_gid = id;
	}
}

/*
 * initialize file info with name and mode
 */

void
initfile(f, name, mode)
register struct fileinfo*	f;
register char*			name;
int				mode;
{
	static struct stat	st;

	(void)memset(f, 0, sizeof(*f));
	(void)memset(&st, 0, sizeof(st));
	f->st = &st;
	if (name)
	{
		f->id = f->name = f->path = name;
		f->namesize = strlen(name) + 1;
	}
	f->st->st_mode = mode;
	f->st->st_nlink = 1;		/* system V needs this!!! */
}

/*
 * set copied file info
 */

void
setfile(f)
register struct fileinfo*	f;
{
	register struct postinfo*	p;
	struct postinfo			post;

	if (!(state.delta.op & OUT)) switch (f->type)
	{
	case S_IFLNK:
		break;
	case S_IFDIR:
		if (state.modtime || state.owner || (f->st->st_mode & (S_IWUSR|S_IXUSR)) != (S_IWUSR|S_IXUSR))
		{
			if (!(p = allocate(struct postinfo))) error(3, "not enough space for file restoration info");
			p->mtime = f->st->st_mtime;
			p->uid = f->st->st_uid;
			p->gid = f->st->st_gid;
			p->mode = f->st->st_mode;
			if ((f->st->st_mode & S_IRWXU) != S_IRWXU && chmod(f->name, f->st->st_mode|S_IRWXU))
				error(1, "%s: cannot chmod to %s", f->name, fmtmode(f->st->st_mode|S_IRWXU) + 1);
			(void)hashput(state.restore, f->name, p);
			break;
		}
		/*FALLTHROUGH*/
	default:
		p = &post;
		p->mtime = f->st->st_mtime;
		p->uid = f->st->st_uid;
		p->gid = f->st->st_gid;
		p->mode = f->st->st_mode;
		(void)restore(f->name, p);
		break;
	}
}

/*
 * set access and modification times of file
 */

void
settime(name, atime, mtime)
char*	name;
time_t	atime;
time_t	mtime;
{
	if (touch(name, atime, mtime, 0)) error(1, "%s: cannot set times", name);
}

/*
 * create directory and all path name components leading to directory
 */

int
missdir(f)
register struct fileinfo*	f;
{
	register char*	s;
	register char*	t;
	struct stat*	st;
	struct stat*	sp;
	struct stat	st0;
	struct stat	st1;

	s = f->name;
	(void)pathcanon(s);
	if (t = strchr(*s == '/' ? s + 1 : s, '/'))
	{
		if (!state.intermediate)
		{
			static int	warned;

			if (!warned)
			{
				error(1, "omit the -d option to create intermediate directories");
				warned = 1;
			}
			return(-1);
		}
		st = 0;
		sp = &st0;
		do
		{
			*t = 0;
			if (stat(s, sp))
			{
				*t = '/';
				break;
			}
			*t = '/';
			st = sp;
			sp = (sp == &st0) ? &st1 : &st0;
		} while (t = strchr(t + 1, '/'));
		if (t)
		{
			if (!st && stat(".", st = &st0))
				error(ERROR_SYSTEM|3, "%s: cannot stat .", s);
			sp = f->st;
			f->st = st;
			do
			{
				*t = 0;
				if (mkdir(s, st->st_mode & state.modemask))
				{
					error(ERROR_SYSTEM|2, "%s: cannot create directory", s);
					*t = '/';
					f->st = sp;
					return(-1);
				}
				setfile(f);
				*t = '/';
			} while (t = strchr(t + 1, '/'));
			f->st = sp;
		}
	}
	return(0);
}

/*
 * restore file status after processing
 */

int
restore(name, p)
register char*			name;
register struct postinfo*	p;
{
	int		m;
	struct stat	st;

	if (state.owner)
	{
		if (state.flags & SETIDS)
		{
			p->uid = state.setuid;
			p->gid = state.setgid;
		}
		if (chown(name, p->uid, p->gid) < 0)
			error(1, "%s: cannot chown to (%d,%d)", name, p->uid, p->gid);
	}
	if (p->mode != S_IFMT)
	{
		if (chmod(name, p->mode & state.modemask))
			error(1, "%s: cannot chmod to %s", name, fmtmode(p->mode & state.modemask) + 1);
		else if (m = p->mode & (S_ISUID|S_ISGID|S_ISVTX))
		{
			if (stat(name, &st))
				error(1, "%s: not found", name);
			else if (m ^= (st.st_mode & (S_ISUID|S_ISGID|S_ISVTX)))
				error(1, "%s: mode %s not set", name, fmtmode(m) + 1);
		}
	}
	if (state.modtime) settime(name, p->mtime, p->mtime);
	return(0);
}
0707070000000000121006440044230044230000010000000474561654100002400000137345format.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax archive format support
 */

#include "pax.h"

static int		isalar();

#if CPIO_EXTENDED
static void		getxops();
static void		setxops();
static void		putxops();
static void		addxopstr();
static void		addxopnum();
#endif

/*
 * read archive prologue before files are copied
 */

int
getprologue()
{
	long	size;

	if (state.volume && state.append) return(0);
	state.formatin = IN_DEFAULT;
	state.id.volume[0] = 0;
	state.in.offset += state.in.count;
	state.in.count = 0;
	if (bread(alar_header, (long)ALAR_HEADER, (long)ALAR_HEADER, 0) <= 0)
	{
		if (!bcount(state.in)) return(0);
	}
	else if (!isalar(alar_header))
	{
		if (strneq(alar_header, PORTAR_MAG, PORTAR_MAGSIZ))
		{
			bunread(alar_header, ALAR_HEADER - 8);
			if (bread(&portar_header, 0, (long)PORTAR_HEADER, 0) > 0)
			{
				if (!strneq(portar_header.ar_fmag, PORTAR_END, PORTAR_ENDSIZ) || sscanf(portar_header.ar_size, "%ld", &size) != 1)
				{
					bunread(&portar_header, PORTAR_HEADER);
					bunread(PORTAR_MAG, PORTAR_MAGSIZ);
				}
				else if (strmatch(portar_header.ar_name, PORTAR_SYM) && (state.formatin = PORTAR) || strmatch(portar_header.ar_name, RANDAR_SYM) && (state.formatin = RANDAR))
				{
					size += (size & 01);
					if (bread((char*)0, 0L, size, 0) <= 0)
						error(3, "invalid %s format symbol table", format[state.formatin].name);
				}
				else
				{
					size = portar_header.ar_date[0];
					portar_header.ar_date[0] = 0;
					state.formatin = strchr(portar_header.ar_name, PORTAR_TERM) ? PORTAR : RANDAR;
					portar_header.ar_date[0] = size;
					bunread(&portar_header, PORTAR_HEADER);
				}
			}
		}
		else if (strneq(alar_header, "<ar>", 4) ||	/* s5r1	 */
			gethalf(0, alar_header) == 0177545 ||	/* pdp11 */
			gethalf(BYTE, alar_header) == 0177545)	/* pdp11 */
		    		error(3, "%s: use ar(1) for library archives", state.file);
		else bunread(alar_header, ALAR_HEADER);
	}
	if (state.volume++)
	{
		if (state.delta.op)
		{
			if (state.operation == (IN|OUT) || !(state.delta.op & COMPRESS))
				error(3, "%s: %s archive cannot be multi-volume", state.file, (state.delta.op & COLLECT) ? "base" : "delta");
			state.delta.op = 0;
		}

		/*
		 * no hard links between volumes
		 */

		hashfree(state.linktab);
		if (!(state.linktab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(struct fileid), HASH_name, "links", 0)))
			error(3, "cannot re-allocate hard link table");
	}
	state.entry = 0;
	return(1);
}

/*
 * check for ASCII or EBCDIC ALAR prologue in alar_header
 */

static int
isalar(hdr)
register char*	hdr;
{
	char		buf[4];

	if (!strneq(hdr, "VOL1", 4))
	{
		cvtetoa(hdr, buf, 4);
		if (!strneq(buf, "VOL1", 4)) return(0);
		cvtetoa(hdr, hdr, ALAR_HEADER);
		state.formatin = IBMAR;
	}
	else state.formatin = ALAR;
	(void)getlabstr(hdr, 5, sizeof(state.id.volume) - 1, state.id.volume);
	(void)getlabstr(hdr, 25, 6, state.id.format);
	(void)getlabstr(hdr, 31, 7, state.id.implementation);
	(void)getlabstr(hdr, 38, 14, state.id.owner);
	state.in.blocked = !bcount(state.in);
	if (streq(state.id.format, PAX_ID))
	{
		state.formatin = PAX;
		state.record.line = 0;
	}
	return(1);
}

/*
 * write archive prologue before files are copied
 */

void
putprologue()
{
	switch (state.formatout)
	{
	case PAX:
		state.record.line = 0;
		/*FALLTHROUGH*/
	case ALAR:
	case IBMAR:
#if DEBUG
		if (state.out.blok) state.out.blocked = 1;
		else
#endif
		state.out.blocked = !state.out.unblocked;
		if (!state.id.owner[0])
		{
			(void)strncpy(state.id.owner, fmtuid(getuid()), sizeof(state.id.owner) - 1);
			state.id.owner[sizeof(state.id.owner) - 1] = 0;
			(void)strupper(state.id.owner);
		}
		if (!state.id.volume[0])
		{
			(void)strncpy(state.id.volume, state.id.owner, sizeof(state.id.volume) - 1);
			state.id.volume[sizeof(state.id.volume) - 1] = 0;
		}
		(void)strncpy(state.id.format, state.formatout == PAX ? PAX_ID : ALAR_ID, sizeof(state.id.format) - 1);
		(void)strncpy(state.id.implementation, IMPLEMENTATION, sizeof(state.id.implementation) - 1);
		(void)sprintf(alar_header, "VOL1%-6.6s              %-6.6s%-7.7s%-14.14s                            4", state.id.volume, state.id.format, state.id.implementation, state.id.owner);
		if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER);
		bwrite(alar_header, ALAR_HEADER);
		(void)sprintf(alar_header, "VOL2     00000000000000                                                         ");
		if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER);
		bwrite(alar_header, ALAR_HEADER);
		if (state.delta.op & (COMPRESS|OUT))
		{
			(void)sprintf(alar_header, "UVL1 %-6.6s%c%c%010ld%010ld                                               ", ID, (state.delta.op & COMPRESS) ? TYPE_COMPRESS : TYPE_DELTA, VERSION, state.operation == OUT ? state.delta.size : 0L, state.operation == OUT ? state.delta.checksum : 0L);
			if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER);
			bwrite(alar_header, ALAR_HEADER);
		}
		break;
	default:
		if (state.delta.op & (COMPRESS|OUT))
		{
			register struct fileinfo*	f;
			struct fileinfo			file;

			f = &file;
#if new_delta_format
			(void)sprintf(state.tmp.file, "%c%s%c%c%c%c", INFO_SEP, ID, INFO_SEP, state.operation == OUT && state.delta.size ? TYPE_DELTA : TYPE_COMPRESS, VERSION, INFO_SEP);
			initfile(f, state.tmp.file, S_IFREG);
#else
			initfile(f, "DELTA!!!", S_IFREG);
#endif
			f->skip = 1;
			if (state.operation == OUT)
			{
				f->st->st_mtime = state.delta.size;
				f->st->st_uid = DELTA_LO(state.delta.checksum);
				f->st->st_gid = DELTA_HI(state.delta.checksum);
			}
			putheader(f);
			puttrailer(f);
		}
		break;
	}
}

/*
 * read archive epilogue after all files have been copied
 */

void
getepilogue()
{
	register char*	s;
	register int	n;
	register int	i;

	if (state.append) backup();
	else
	{
		switch (state.formatin)
		{
		case ALAR:
		case IBMAR:
		case PAX:
		case PORTAR:
		case RANDAR:
#if SAVESET
		case SAVESET:
#endif
			break;
		default:
			/*
			 * check for more volumes
			 * volumes begin on BLOCKSIZE boundaries
			 * separated by up to MAXBLOCKS null byte filler
			 */

			i = MAXBLOCKS;
			if (!(n = round(state.in.count, BLOCKSIZE) - state.in.count) || bread(state.tmp.buffer, 0L, (long)n, 0) > 0) do
			{
				if (*(s = state.tmp.buffer) && n == BLOCKSIZE)
				{
					bunread(state.tmp.buffer, BLOCKSIZE);
					state.formatin = IN_DEFAULT;
#if DEBUG
					message(-2, "go for next volume %-.32s...", state.tmp.buffer);
#endif
					return;
				}
				while (s < state.tmp.buffer + n && !*s++);
				if (s < state.tmp.buffer + n)
				{
					if (state.volume > 1) error(1, "junk data after volume %d", state.volume);
					break;
				}
				n = BLOCKSIZE;
			} while (i-- > 0 && bread(state.tmp.buffer, 0L, n, 0) > 0);
			bflushin();
			break;
		}
		state.formatin = IN_DEFAULT;
	}
}

/*
 * write archive epilogue after files have been copied
 */

void
putepilogue()
{
	register int			n;
	register int			boundary;
	register struct fileinfo*	f;

	struct fileinfo			tmp;

	static int			selected;

	if (state.selected > selected)
	{
		selected = state.selected;
		boundary = state.out.count;
		switch (state.formatout)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			bwrite(alar_header, 0);
			bwrite(alar_header, 0);
			break;
		case BINARY:
		case CPIO:
		case ASC:
		case ASCHK:
			f = &tmp;
			initfile(f, CPIO_TRAILER, 0);
			f->skip = 1;
			putheader(f);
			puttrailer(f);
			boundary = state.out.unblocked ? BLOCKSIZE : state.blocksize;
			break;
		case TAR:
		case USTAR:
			memset(tar_block, 0, TAR_HEADER);
			bwrite(tar_block, TAR_HEADER);
			bwrite(tar_block, TAR_HEADER);
			boundary = state.out.unblocked ? BLOCKSIZE : state.blocksize;
			break;
		}
		if ((n = boundary - state.out.count) < 0) n = round(state.out.count, boundary) - state.out.count;
		if (n)
		{
			memset(state.tmp.buffer, 0, n);
			bwrite(state.tmp.buffer, n);
		}
		bflushout();
		state.volume++;
	}
	else
	{
		state.out.count = state.out.offset = 0;
		state.out.next = state.out.buffer;
	}
}

#if DEBUG

/*
 * try to repair pax binary cpio header botch that always set
 * mtime and size to 1
 */

static void
repair(f)
register struct fileinfo*	f;
{
	register char*		b;
	register char*		e;
	register int		null;
	int			n;
	long			pos;
	unsigned long		size;
	struct stat		st;
	struct binary_header	hdr;
	char			buf[4096];

	static int		magic0;
	static int		magic1;
	static time_t		mtime;
	static int		warned;

	if (!warned)
	{
		warned = 1;
		error(1, "repairing botched headers -- time stamps will be incorrect");
		mtime = fstat(state.append, &st) ? (time_t)1 : st.st_mtime;
		b = (char*)&binary_header.magic;
		magic0 = b[state.swap != 0];
		magic1 = b[state.swap == 0];
	}

	/*
	 * determine the file size by locating the next header
	 *
	 * NOTE: magic number is on even byte boundary
	 *	 file data is on even byte boundary
	 *       input buffer size is even
	 */

	null = 0;
	pos = 0;
	size = 0;
	b = state.in.next;
	e = state.in.last;
	for (;;)
	{
		while (b < e)
		{
			if (*b++ == magic0)
			{
				if (*b++ == magic1)
				{
					/*
					 * check a few header fields to weed
					 * out bad magic
					 */

					if ((n = e - (b - 2)) < BINARY_HEADER)
					{
						memcpy(&hdr, b - 2, n);
						if (read(state.append, (char*)&hdr + n, BINARY_HEADER - n) != BINARY_HEADER - n)
							error(ERROR_SYSTEM|3, "repair read error");
						if (lseek(state.append, (long)(n - BINARY_HEADER), 1) == -1L)
							error(3, "can only repair seekable input archives");
					}
					else memcpy(&hdr, b - 2, BINARY_HEADER);
					if (state.swap) memswap(state.swap, (char*)&hdr, BINARY_HEADER);
					if (cpio_long(hdr.mtime) == 1 && cpio_long(hdr.size) == 1)
					{
						/*
						 * found the next header
						 */

						f->st->st_size = size - (null == 1);
						f->st->st_mtime = mtime;
						if (pos && lseek(state.append, pos, 0) != pos)
							error(ERROR_SYSTEM|3, "repair seek error");
						return;
					}
				}
				else if (*(b - 1) == 0) null++;
			}
			else
			{
				if (*(b - 1) == 0) null++;
				if (*b++ == 0) null++;
			}
			size += 2;
		}
		if (!pos && (pos = lseek(state.append, 0L, 1)) == -1L)
			error(3, "can only repair seekable input archives");
		b = buf;
		if ((n = read(state.append, b, sizeof(buf))) <= 0)
			error(3, "cannot repair -- header missing");
		e = buf + n - 1;
	}
}

#endif

/*
 * read next archive entry header
 */

int
getheader(f)
register struct fileinfo*	f;
{
	register char*	s;
	register int	i;
	register long	n;
	char*		typename;
	long		num;
	int		warned;
	int		checkdelta;
	int		lab;
	int		type;
	short		magic;

	struct
	{
		long	dev;
		long	ino;
		long	mode;
		long	uid;
		long	gid;
		long	nlink;
		long	rdev;
		long	mtime;
		long	size;
		long	dev_major;
		long	dev_minor;
		long	rdev_major;
		long	rdev_minor;
		long	checksum;
	}		lst;

	static char	namebuffer[PATH_MAX * 2];
	static char	pathbuffer[PATH_MAX];
	static char	linkbuffer[PATH_MAX];
	static char	idbuffer[ALAR_NAMESIZE + 1];
	static char	uidname[9];
	static char	gidname[9];

 volume:
	warned = 0;
	checkdelta = !state.entry++ && !(state.test & 020);
	type = 0;
	typename = "";
	if (state.append) bsave();
 again:
	for (;;)
	{
		f->record.format = 0;
		f->skip = 0;
#if DEBUG
		message(-2, "%s:", format[state.formatin].name);
#endif
		switch (state.formatin)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			if (!(lab = getlabel(f))) return(0);
			f->name = namebuffer;
			f->st->st_dev = 0;
			f->st->st_ino = 0;
			f->st->st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
			f->st->st_uid = state.uid;
			f->st->st_gid = state.gid;
			f->st->st_nlink = 1;
			f->st->st_rdev = 0;
			f->st->st_size = 0;
			f->linktype = NOLINK;
			f->linkname = 0;
			f->uidname = 0;
			f->gidname = 0;
			type = 0;
			do
			{
				if (state.formatin == IBMAR) cvtetoa(alar_header, alar_header, ALAR_HEADER);
				if (checkdelta && strneq(alar_header, "UVL1", 4) && strneq(alar_header + 5, ID, IDLEN))
				{
					checkdelta = 0;
					s = alar_header + 10;
					f->st->st_mtime = getlabnum(alar_header, 14, 10, 10);
					n = getlabnum(alar_header, 24, 10, 10);
					f->st->st_uid = DELTA_LO(n);
					f->st->st_gid = DELTA_HI(n);
					goto deltaverify;
				}
				else if (strneq(alar_header, "HDR", 3))
				{
					if (getlabnum(alar_header, 4, 1, 10) != ++type) error(3, "%s format HDR label out of sequence", format[state.formatin].name);
					if (type == 1)
					{
						s = namebuffer;
						for (i = 4; i <= ALAR_NAMESIZE + 3; i++)
						{
							if (alar_header[i] == ' ')
							{
								if (i >= ALAR_NAMESIZE + 3 || alar_header[i + 1] == ' ') break;
								*s++ = '.';
							}
							else *s++ = isupper(alar_header[i]) ? tolower(alar_header[i]) : alar_header[i];
						}
						if ((n = getlabnum(alar_header, 40, 2, 10)) > 0 && n < 99) (void)sprintf(s, ".%02d", n);
						else *s = 0;
						f->record.section = getlabnum(alar_header, 28, 4, 10);
						getlabstr(alar_header, 5, ALAR_NAMESIZE, f->id = idbuffer);
						getlabstr(alar_header, 61, 6, state.id.format);
						getlabstr(alar_header, 67, 7, state.id.implementation);
						if (streq(state.id.format, PAX_ID))
							state.formatin = PAX;
#if SAVESET
						else if (streq(state.id.format, SAVESET_ID) && streq(state.id.implementation, SAVESET_IMPL))
							state.formatin = SAVESET;
#endif
						f->st->st_mtime = state.present;
						if (n = getlabnum(alar_header, 43, 2, 10))
						{
							if (alar_header[41] == '0') n += 100;
							if ((i = getlabnum(alar_header, 45, 3, 10)) >= 0 && i <= 365)
							{
								f->st->st_mtime = i;
								while (n-- > 70) f->st->st_mtime += ((n % 4) || n == 100) ? 365 : 366;
								f->st->st_mtime *= 24L * 60L * 60L;
								f->st->st_mtime += 12L * 60L * 60L;
							}
						}
					}
					else if (type == 2)
					{
						switch (f->record.format = alar_header[4])
						{
						case 'F': /* fixed length	*/
						case 'D': /* decimal variable	*/
						case 'S': /* spanned		*/
						case 'U': /* input block size	*/
						case 'V': /* binary variable	*/
							break;
						default:
							error(2, "%s record format %c not supported", format[state.formatin].name, f->record.format);
							f->skip = 1;
						}
						state.blocksize = getlabnum(alar_header, 6, 5, 10);
						state.record.size = getlabnum(alar_header, 11, 5, 10);
						if (!state.in.blocked) f->st->st_size = getlabnum(alar_header, 16, 10, 10);
						state.record.offset = getlabnum(alar_header, 51, 2, 10);
					}
					else if (state.formatin == PAX)
					{
						if (type == 3)
						{
							f->st->st_mode = strmode(alar_header + 20);
							if (alar_header[20] == 'z')
							{
								f->st->st_mode |= S_IFREG;
								f->linktype = HARDLINK;
							}
							getlabstr(alar_header, 5, 8, f->uidname = uidname);
							getlabstr(alar_header, 13, 8, f->gidname = gidname);
							f->st->st_mtime = getlabnum(alar_header, 31, 10, 10);
							f->st->st_ctime = getlabnum(alar_header, 41, 10, 10);
							f->st->st_atime = getlabnum(alar_header, 51, 10, 10);
							f->st->st_size = getlabnum(alar_header, 61, 10, 10);
						}
						else if (type == 4)
						{
							if (f->linktype != NOLINK)
							{
								if ((n = lab - ALAR_VARHDR) >= sizeof(linkbuffer))
								{
									n = sizeof(linkbuffer) - 1;
									error(1, "%s: link text too long -- truncating to %d", f->name, n);
								}
								getlabstr(alar_header, 10, n, f->linkname = linkbuffer);
								f->linknamesize = n + 1;
							}
							else switch (f->st->st_mode & S_IFMT)
							{
							case S_IFBLK:
							case S_IFCHR:
								i = getlabnum(alar_header, 5, 8, 8);
								num = getlabnum(alar_header, 13, 8, 8);
								f->st->st_rdev = makedev(i, num);
								break;
							case S_IFDIR:
							case S_IFREG:
							case S_IFIFO:
							case S_IFSOCK:
								break;
							default:
								error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT);
								f->st->st_mode = S_IFREG|(f->st->st_mode & S_IFMT);
								break;
							}
						}
						else if (type == 5)
						{
							if ((n = lab - ALAR_VARHDR) >= sizeof(namebuffer))
							{
								n = sizeof(namebuffer) - 1;
								error(1, "%s: name too long -- truncating to %d", f->name, n);
							}
							getlabstr(alar_header, 10, n, f->name = namebuffer);
							f->namesize = n + 1;
						}
					}
				}
				else if (!state.in.blocked && strneq(alar_header, "VOL1", 4))
				{
					bunread(alar_header, lab);
					if (!(getprologue())) return(0);
					goto volume;
				}
			} while ((lab = getlabel(f)));
#if SAVESET
			if (state.formatin != SAVESET) goto found;
			state.saveset.time = f->st->st_mtime;
			if (state.blocksize > state.saveset.blocksize)
			{
				state.saveset.blocksize = state.blocksize;
				if (state.saveset.block) free(state.saveset.block);
				if (!(state.saveset.block = malloc(state.saveset.blocksize)))
					error(3, "cannot allocate %s format buffer", format[state.formatin].name);
			}
			state.saveset.bp = state.saveset.block + state.blocksize;
			/*FALLTHROUGH*/
		case SAVESET:
			f->name = namebuffer;
			if (!getsaveset(f, 1)) goto again;
#endif
			goto found;
		case BINARY:
			if (bread(&binary_header, (long)BINARY_HEADER, (long)BINARY_HEADER, 0) <= 0) break;
			if (state.swap)
			{
				memcpy(state.tmp.buffer, &binary_header, BINARY_HEADER);
				memswap(state.swap, (char*)&binary_header, BINARY_HEADER);
			}
			f->magic = binary_header.magic;
			if (f->magic == CPIO_MAGIC)
			{
				f->namesize = binary_header.namesize;
				f->st->st_dev = binary_header.dev;
				f->st->st_ino = binary_header.ino;
				f->st->st_mode = binary_header.mode;
				f->st->st_uid = binary_header.uid;
				f->st->st_gid = binary_header.gid;
				f->st->st_nlink = binary_header.links;
				f->st->st_rdev = binary_header.rdev;
				f->st->st_mtime = cpio_long(binary_header.mtime);
				f->st->st_size = cpio_long(binary_header.size);
			cpio_common:
				f->linktype = NOLINK;
				f->linkname = 0;
				f->uidname = 0;
				f->gidname = 0;
				f->name = namebuffer;
				switch (state.formatin)
				{
				case BINARY:
					i = BINARY_ALIGN;
					n = BINARY_HEADER;
					break;
				case ASC:
				case ASCHK:
					i = ASC_ALIGN;
					n = ASC_HEADER;
					break;
				default:
					i = 0;
					break;
				}
				if (i)
				{
					if (n = (n + f->namesize) % i) i -= n;
					else i = 0;
				}
				if (f->namesize >= sizeof(namebuffer))
				{
					error(2, "entry %d.%d file name too long", state.volume, state.entry);
					for (n = f->namesize + i; n > 0; n -= sizeof(namebuffer))
						(void)bread(namebuffer, 0L, n > sizeof(namebuffer) ? (long)sizeof(namebuffer) : n, 1);
					f->skip = 1;
				}
				else
				{
					(void)bread(namebuffer, 0, (long)(f->namesize + i), 1);
					if (namebuffer[f->namesize - 1])
					{
						bunread(&namebuffer[f->namesize - 1], 1);
						namebuffer[f->namesize - 1] = 0;
						error(state.keepgoing ? 1 : 3, "entry %d.%d file name terminating null missing", state.volume, state.entry);
					}
#if CPIO_EXTENDED
					getxops(f);
#endif
				}
				if (streq(f->name, CPIO_TRAILER))
				{
					getdeltaheader(f);
					return(0);
				}
				switch (f->st->st_mode & CPIO_FMT)
				{
				case CPIO_FIFO:
					f->type = S_IFIFO;
					break;
				case CPIO_DIR:
					f->type = S_IFDIR;
					break;
				case CPIO_CHR:
					f->type = S_IFCHR;
					break;
				case CPIO_BLK:
					f->type = S_IFBLK;
					break;
				case CPIO_LNK:
					f->type = S_IFLNK;
					break;
				case CPIO_SOCK:
					f->type = S_IFSOCK;
					break;
				default:
					error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT);
					/*FALLTHROUGH*/
				case CPIO_REG:
					f->type = S_IFREG;
					break;
				}
				f->st->st_mode &= 07777;
				f->st->st_mode |= f->type;
#if DEBUG
				if (f->st->st_mtime == 1 && f->st->st_size == 1) repair(f);
#endif
				switch (f->type)
				{
				case S_IFLNK:
					getdeltaheader(f);
					if (f->st->st_size > sizeof(linkbuffer) - 1)
					{
						error(2, "entry %d.%d symbolic link text too long", state.volume, state.entry);
						f->skip = 1;
					}
					else
					{
						f->linktype = SOFTLINK;
						f->linkname = linkbuffer;
						(void)bread(linkbuffer, 0L, f->st->st_size, 1);
						linkbuffer[f->st->st_size] = 0;
						f->st->st_size = 0;
					}
					break;
				default:
					f->linktype = NOLINK;
					break;
				}
				goto found;
			}
			bunread(state.swap ? state.tmp.buffer : (char*)&binary_header, BINARY_HEADER);
			break;
		case CPIO:
			if (bread(state.tmp.buffer, 0L, (long)CPIO_HEADER, 0) <= 0) break;
			state.tmp.buffer[CPIO_HEADER] = 0;
			if (state.tmp.buffer[0] == '0' && sscanf(state.tmp.buffer, "%6o%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11lo",
				&f->magic,
				&lst.dev,
				&lst.ino,
				&lst.mode,
				&lst.uid,
				&lst.gid,
				&lst.nlink,
				&lst.rdev,
				&lst.mtime,
				&f->namesize,
				&lst.size) == 11 && f->magic == CPIO_MAGIC)
			{
				f->st->st_dev = lst.dev;
				f->st->st_ino = lst.ino;
				f->st->st_mode = lst.mode;
				f->st->st_uid = lst.uid;
				f->st->st_gid = lst.gid;
				f->st->st_nlink = lst.nlink;
				f->st->st_rdev = lst.rdev;
				f->st->st_mtime = lst.mtime;
				f->st->st_size = lst.size;
				goto cpio_common;
			}
			bunread(state.tmp.buffer, CPIO_HEADER);
			break;
		case TAR:
		case USTAR:
			if (bread(tar_block, 0L, (long)TAR_HEADER, 0) <= 0) break;
			if (!*tar_header.name)
			{
				if (state.entry == 1) goto notar;
				return(0);
			}
			if (sscanf(tar_header.mode, "%11lo", &num) != 1) goto notar;
			f->st->st_mode = num;
			if (sscanf(tar_header.uid, "%11lo", &num) != 1) goto notar;
			f->st->st_uid = num;
			if (sscanf(tar_header.gid, "%11lo", &num) != 1) goto notar;
			f->st->st_gid = num;
			if (sscanf(tar_header.size, "%11lo", &num) != 1) goto notar;
			f->st->st_size = num;
			if (sscanf(tar_header.mtime, "%11lo", &num) != 1) goto notar;
			f->st->st_mtime = num;
			if (sscanf(tar_header.chksum, "%11lo", &num) != 1) goto notar;
			if ((n = num) != (i = tar_checksum()))
			{
				if (state.entry == 1) goto notar;
				error(state.keepgoing ? 1 : 3, "%s format checksum error (%d != %d)", format[state.formatin].name, n, i);
			}
			if (state.formatin == USTAR)
			{
				if (!streq(tar_header.magic, TMAGIC))
				{
					if (strneq(tar_header.magic, TMAGIC, TMAGLEN - 1)) error(1, "%s format botched -- %s format assumed", format[state.formatin].name, format[TAR].name);
					else if (state.entry > 1) goto notar;
					state.formatin = TAR;
				}
				else if (!strneq(tar_header.version, TVERSION, sizeof(tar_header.version)))
				{
					error(1, "%s format version %-.*s incompatible with implementation version %-.*s -- assuming %s", format[state.formatin].name, sizeof(tar_header.version), tar_header.version, sizeof(tar_header.version), TVERSION, format[TAR].name);
					state.formatin = TAR;
				}
			}
			tar_header.name[sizeof(tar_header.name)] = 0;
			if (state.formatin == USTAR && *tar_header.prefix) (void)sprintf(f->name = namebuffer, "%-.%*s%s", sizeof(tar_header.prefix), tar_header.prefix, tar_header.name);
			else f->name = tar_header.name;
			tar_header.linkname[sizeof(tar_header.name)] = 0;
			f->linkname = tar_header.linkname;
			f->linktype = NOLINK;
			f->st->st_nlink = 1;
			switch (tar_header.typeflag)
			{
			case LNKTYPE:
				f->linktype = HARDLINK;
				f->st->st_mode |= S_IFREG;
				f->st->st_nlink = 2;
				f->st->st_size = 0;	/* lest they forget */
				break;
			case SYMTYPE:
				f->linktype = SOFTLINK;
				f->st->st_mode |= S_IFLNK;
				break;
			case CHRTYPE:
				f->st->st_mode |= S_IFCHR;
			device:
				if (sscanf(tar_header.devmajor, "%11o", &num) != 1) goto notar;
				i = num;
				if (sscanf(tar_header.devminor, "%11o", &num) != 1) goto notar;
				f->st->st_rdev = makedev(i, num);
				break;
			case BLKTYPE:
				f->st->st_mode |= S_IFBLK;
				goto device;
			case DIRTYPE:
				f->st->st_mode |= S_IFDIR;
				break;
			case FIFOTYPE:
				f->st->st_mode |= S_IFIFO;
				break;
#ifdef SOKTYPE
			case SOKTYPE:
				f->st->st_mode |= S_IFSOCK;
				break;
#endif
			default:
				error(1, "unknown file type `%c' -- regular file assumed", tar_header.typeflag);
				/*FALLTHROUGH*/
			case REGTYPE:
			case AREGTYPE:
				f->namesize = strlen(f->name) + 1;
				if (f->name[f->namesize - 2] == '/')
				{
					f->st->st_mode |= S_IFDIR;
					if (f->namesize > 2) f->name[--f->namesize - 1] = 0;
				}
				else f->st->st_mode |= S_IFREG;
				break;
			}
			f->uidname = 0;
			f->gidname = 0;
			if (state.formatin == USTAR)
			{
				if (*tar_header.uname) f->uidname = tar_header.uname;
				if (*tar_header.gname) f->gidname = tar_header.gname;
			}
			goto found;
		notar:
			bunread(tar_block, TAR_HEADER);
			break;
		case ASC:
		case ASCHK:
			if (bread(state.tmp.buffer, 0L, (long)ASC_HEADER, 0) <= 0) break;
			state.tmp.buffer[ASC_HEADER] = 0;
			if (state.tmp.buffer[0] == '0' && sscanf(state.tmp.buffer, "%6o%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8x%8lx",
				&f->magic,
				&lst.ino,
				&lst.mode,
				&lst.uid,
				&lst.gid,
				&lst.nlink,
				&lst.mtime,
				&lst.size,
				&lst.dev_major,
				&lst.dev_minor,
				&lst.rdev_major,
				&lst.rdev_minor,
				&f->namesize,
				&lst.checksum) == 14 && (f->magic == ASC_MAGIC || f->magic == ASCHK_MAGIC))
			{
				if (f->magic == ASCHK_MAGIC) state.formatin = ASCHK;
				f->checksum = lst.checksum;
				f->st->st_dev = makedev(lst.dev_major, lst.dev_minor);
				f->st->st_ino = lst.ino;
				f->st->st_mode = lst.mode;
				f->st->st_uid = lst.uid;
				f->st->st_gid = lst.gid;
				f->st->st_nlink = lst.nlink;
				f->st->st_rdev = makedev(lst.rdev_major, lst.rdev_minor);
				f->st->st_mtime = lst.mtime;
				f->st->st_size = lst.size;
				goto cpio_common;
			}
			bunread(state.tmp.buffer, ASC_HEADER);
			break;
		case PORTAR:
		case RANDAR:
			if (bread(&portar_header, 0L, (long)PORTAR_HEADER, 0) <= 0) return(0);
			if (strneq(portar_header.ar_fmag, PORTAR_END, PORTAR_ENDSIZ) && sscanf(portar_header.ar_date, "%12ld%6ld%6ld%8lo%10ld",
				&lst.mtime,
				&lst.uid,
				&lst.gid,
				&lst.mode,
				&lst.size) == 5)
			{
				f->name = portar_header.ar_name;
				portar_header.ar_date[0] = 0;
				if ((s = strchr(f->name, PORTAR_TERM)) || (s = strchr(f->name, RANDAR_TERM))) *s = 0;
				f->st->st_dev = 0;
				f->st->st_ino = 0;
				f->st->st_mode = S_IFREG|(lst.mode&07777);
				f->st->st_uid = lst.uid;
				f->st->st_gid = lst.gid;
				f->st->st_nlink = 1;
				f->st->st_rdev = 0;
				f->st->st_mtime = lst.mtime;
				f->st->st_size = lst.size;
				f->linktype = NOLINK;
				f->linkname = 0;
				f->uidname = 0;
				f->gidname = 0;
				goto found;
			}
			bunread(&portar_header, PORTAR_HEADER);
			return(0);
		default:
			error(PANIC, "%s: incomplete input format implementation", format[state.formatin].name);
			break;
		}
		if (state.entry == 1) for (;;) switch (state.formatin)
		{
		case BINARY:
			state.formatin = USTAR;
			goto again;
		case CPIO:
			state.formatin = BINARY;
			state.swap = 0;
			if (bread(&binary_header.magic, 0L, (long)sizeof(binary_header.magic), 0) <= 0) break;
			bunread(&binary_header.magic, sizeof(binary_header.magic));
			if (binary_header.magic == CPIO_MAGIC) goto again;
			state.swap = BYTE;
			magic = binary_header.magic;
			memswap(state.swap, (char*)&magic, sizeof(magic));
#if DEBUG
			message(-1, "binary_header.magic=0%05o swap(BYTE)=0%05o", (unsigned short)binary_header.magic, (unsigned short)magic);
#endif
			if (magic == CPIO_MAGIC)
			{
#if DEBUG
				message(-1, "swapping %s header bytes", format[state.formatin].name);
#endif
				goto again;
			}
			break;
		case TAR:
		case USTAR:
			state.formatin = ASC;
			goto again;
		case ASC:
		case ASCHK:
			if (state.entries > 0 && state.volume > 1)
			{
				if (--state.volume > 1) error(1, "junk data after volume %d", state.volume);
				finish(0);
			}
			if (!state.keepgoing) error(3, "%s: unknown input format", state.file);
			state.formatin = IN_DEFAULT;
			goto skip;
		}
	skip:
		i = 3;
		if (state.keepgoing && bread(namebuffer, 0L, 1L, 0) > 0)
		{
			if (warned) continue;
			warned = 1;
			i = 1;
		}
		error(i, "entry %d.%d is out of phase", state.volume, state.entry);
		if (state.entry > 1) state.entry++;
	}
 found:
	if (checkdelta)
	{
		checkdelta = 0;
		i = 0;
#if new_delta_format
		if (!f->st->st_size && !f->st->st_dev && !f->st->st_ino && !(f->st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) && (s = f->name) && *s++ == INFO_SEP && *(s + strlen(s) - 1) == INFO_SEP)
		{
			if (strneq(s, ID, IDLEN) && (s = strchr(s, INFO_SEP)))
			{
			deltaverify:
				switch (*++s)
				{
				case TYPE_COMPRESS:
					typename = "compressed ";
					break;
				case TYPE_DELTA:
					typename = "delta ";
					break;
				default:
					error(3, "type %c encoding not supported", *s);
				}
				if (*++s != VERSION)
					error(3, "version %c encoding not supported", *s);

				/*
				 * NOTE: [<INFO_SEP><OP><VAL>]* may appear here
				 */

				i = 1;
			}
			else
			{
				if (s = strchr(f->name + 3, INFO_SEP)) *s = 0;
				error(1, "unknown %s header ignored", f->name + 3);
				goto again;
			}
		}
#else
		if (!f->st->st_size && !f->st->st_dev && !f->st->st_ino && !(f->st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) && streq(f->name, "DELTA!!!"))
		{
		deltaverify:
			typename = "delta ";
			i = 1;
		}
#endif
		n = (state.delta.op & (COLLECT|IN)) == IN;
#if DEBUG
		message(-5, "checkdelta: %sop=%d i=%d n=%d size=%ld:%ld LO=%d:%d HI=%d:%d", typename, state.delta.op, i, n, f->st->st_mtime, state.delta.size, DELTA_LO(f->st->st_uid), DELTA_LO(state.delta.checksum), DELTA_LO(f->st->st_gid), DELTA_HI(state.delta.checksum));
#endif
		if (i != n)
		{
			if (n) error(3, "%s: not a delta archive", state.file);
			else if (!(state.delta.op & COLLECT))
			{
				if (*typename == 'd') error(state.list ? 1 : 3, "%s: base archive must be specified", state.file);
				/*HUH*/if (!(state.delta.op & IN)) deltabase();
			}
		}
		if (i)
		{
			if (n)
			{
				if ((i = f->st->st_mtime != state.delta.size) || DELTA_LO(f->st->st_uid) != DELTA_LO(state.delta.checksum) || DELTA_LO(f->st->st_gid) != DELTA_HI(state.delta.checksum))
					error(i ? 3 : 1, "base archive %s mismatch", i ? "size" : "checksum");
			}
			if (!f->st->st_size) goto again;
		}
	}
	state.entries++;
	f->path = strcpy(pathbuffer, f->name);
	(void)pathcanon(f->name);
	f->name = map(f->name);
	f->namesize = strlen(f->name) + 1;
	if (f->linkname)
	{
		(void)pathcanon(f->linkname);
		if (!(state.ftwflags & FTW_PHYSICAL)) f->linkname = map(f->linkname);
		f->linknamesize = strlen(f->linkname) + 1;
	}
	else f->linknamesize = 0;
	if (f->uidname || f->gidname) setidnames(f);
	f->type = f->st->st_mode & S_IFMT;
	getdeltaheader(f);
#if DEBUG
	if (state.trace)
	{
		s = &state.tmp.buffer[0];
		if (f->record.format) (void)sprintf(s, " [%c,%d,%d]", f->record.format, state.blocksize, state.record.size);
		else *s = 0;
		message(-1, "path=%s name=%s entry=%d.%d size=%ld%s", f->path, f->name, state.volume, state.entry, f->st->st_size, s);
	}
#endif
	if (state.entry == 1)
	{
		if (state.delta.op & COLLECT)
		{
			if (state.in.blocked) error(3, "%s: blocked base archives are not supported", state.delta.base);
			switch (state.formatin)
			{
			case ALAR:
			case IBMAR:
#if SAVESET
			case SAVESET:
#endif
				error(3, "%s: %s format base archives not supported", state.delta.base, format[state.formatin].name);
			}
		}
		if (state.summary && state.verbose)
		{
			if (state.in.blok) fprintf(stderr, "BLOK ");
			if (state.delta.op & COLLECT) fprintf(stderr, "base");
			else fprintf(stderr, "volume %d", state.volume);
			if (state.id.volume[0]) fprintf(stderr, " label %s", state.id.volume);
			fprintf(stderr, " in %s%s format", typename, format[state.formatin].name);
			if (state.trace)
			{
				if (*state.id.format) fprintf(stderr, " %s", state.id.format);
				if (*state.id.implementation) fprintf(stderr, " implementation %s", state.id.implementation);
			}
			fprintf(stderr, "\n");
		}
	}
	state.delta.sum++;
	return(1);
}

/*
 * write next archive entry header
 */

void
putheader(f)
register struct fileinfo*	f;
{
	register char*	s;
	register int	n;

	if (f->delta.op)
	{
		s = state.delta.hdrbuf;
#if new_delta_format
		*s++ = f->delta.op;
		*s++ = VERSION;
		*s++ = 0;
		state.delta.hdr = s;
		adddelnum('s', f->delta.size);
		*state.delta.hdr++ = 0;
#else
		*s++ = f->delta.op;
		*s++ = '2';
		*s++ = '\n';
		state.delta.hdr = s;
#endif
		n = state.delta.hdr - state.delta.hdrbuf;
		f->st->st_size += n;
		f->linknamesize += n;
	}
	if (state.complete)
	{
		unsigned long	c = f->st->st_size;

		switch (state.formatout)
		{
		case ALAR:
		case IBMAR:
		case PAX:
			c += 4 * ALAR_HEADER;
			break;
		case ASC:
		case ASCHK:
			c += ASC_HEADER + f->namesize;
			break;
		case BINARY:
			c += BINARY_HEADER + f->namesize;
			c = round(c, 2);
			break;
		case CPIO:
			c += CPIO_HEADER + f->namesize;
			break;
		case TAR:
		case USTAR:
			c += TAR_HEADER;
			c = round(c, BLOCKSIZE);
			break;
		}
		if (state.out.count + c > state.maxout)
		{
			if (c > state.maxout) error(1, "%s: too large to fit in one volume", f->name);
			else
			{
				state.complete = 0;
				putepilogue();
				newio(1, 0, 0);
				putprologue();
				state.complete = 1;
			}
		}
	}
	switch (state.formatout)
	{
	case ALAR:
	case IBMAR:
	case PAX:
		putlabels(f, "HDR");
		break;
	case BINARY:
		binary_header.magic = CPIO_MAGIC;
		binary_header.namesize = f->namesize;
		cpio_short(binary_header.size, f->type == S_IFLNK ? (long)f->linknamesize : f->st->st_size);
		binary_header.dev = f->st->st_dev;
		binary_header.ino = f->st->st_ino;
		binary_header.mode = cpio_mode(f);
		binary_header.uid = f->st->st_uid;
		binary_header.gid = f->st->st_gid;
		binary_header.links = f->st->st_nlink;
		binary_header.rdev = f->st->st_rdev;
		if (binary_header.rdev != f->st->st_rdev) error(1, "%s: special device numbers truncated", f->name);
		cpio_short(binary_header.mtime, (long)f->st->st_mtime);
		bwrite(&binary_header, BINARY_HEADER);
		bwrite(f->name, f->namesize);
		if (n = (BINARY_HEADER + f->namesize) % BINARY_ALIGN)
			while (n++ < BINARY_ALIGN) bwrite("", 1);
		if (f->type == S_IFLNK)
		{
		cpio_link:
			putdeltaheader(f);
			if (streq(f->name, f->linkname)) error(1, "%s: symbolic link loops to self", f->name);
			bwrite(f->linkname, f->linknamesize);
		}
		break;
	case CPIO:
#if CPIO_EXTENDED
		if (!f->skip)
		{
			getidnames(f);
			addxopstr('U', f->uidname);
			addxopstr('G', f->gidname);
			if (CPIO_TRUNCATE(f->st->st_rdev) != f->st->st_rdev) addxopnum('d', f->st->st_rdev);
#if NUMBER_EVEN_THOUGH_NAME
			if (CPIO_TRUNCATE(f->st->st_gid) != f->st->st_gid) addxopnum('g', f->st->st_gid);
			if (CPIO_TRUNCATE(f->st->st_uid) != f->st->st_uid) addxopnum('u', f->st->st_uid);
#endif
			setxops(f);
		}
#else
		if (CPIO_TRUNCATE(f->st->st_rdev) != f->st->st_rdev) error(1, "%s: special device number truncated", f->name);
		if (CPIO_TRUNCATE(f->st->st_gid) != f->st->st_gid) error(1, "%s: gid number truncated", f->name);
		if (CPIO_TRUNCATE(f->st->st_uid) != f->st->st_uid) error(1, "%s: uid number truncated", f->name);
#endif
		(void)sprintf(state.tmp.buffer, "%.6o%.6o%.6o%.6o%.6o%.6o%.6o%.6o%.11lo%.6o%.11lo",
			CPIO_TRUNCATE(CPIO_MAGIC),
			CPIO_TRUNCATE(f->st->st_dev),
			CPIO_TRUNCATE(f->st->st_ino),
			CPIO_TRUNCATE(cpio_mode(f)),
			CPIO_TRUNCATE(f->st->st_uid),
			CPIO_TRUNCATE(f->st->st_gid),
			CPIO_TRUNCATE(f->st->st_nlink),
			CPIO_TRUNCATE(f->st->st_rdev),
			f->st->st_mtime,
			f->namesize,
			f->type == S_IFLNK ? f->linknamesize : f->st->st_size);
		bwrite(state.tmp.buffer, CPIO_HEADER);
#if CPIO_EXTENDED
		putxops(f);
#else
		bwrite(f->name, f->namesize);
#endif
		if (f->type == S_IFLNK) goto cpio_link;
		break;
	case ASC:
		f->checksum = 0;
		/*FALLTHROUGH*/
	case ASCHK:
		(void)sprintf(state.tmp.buffer, "%.6o%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx",
			state.formatout == ASC ? ASC_MAGIC : ASCHK_MAGIC,
			(long)f->st->st_ino,
			(long)cpio_mode(f),
			(long)f->st->st_uid,
			(long)f->st->st_gid,
			(long)f->st->st_nlink,
			(long)f->st->st_mtime,
			f->type == S_IFLNK ? (long)f->linknamesize : (long)f->st->st_size,
			(long)major(f->st->st_dev),
			(long)minor(f->st->st_dev),
			(long)major(f->st->st_rdev),
			(long)minor(f->st->st_rdev),
			(long)f->namesize,
			f->checksum);
		bwrite(state.tmp.buffer, ASC_HEADER);
		bwrite(f->name, f->namesize);
		if (n = (ASC_HEADER + f->namesize) % ASC_ALIGN)
			while (n++ < ASC_ALIGN) bwrite("", 1);
		if (f->type == S_IFLNK) goto cpio_link;
		break;
	case TAR:
	case USTAR:
message(-1, "tar_header#1: name=%s namesize=%d max=%d", f->name, f->namesize, sizeof(tar_header.name));
		memset(tar_block, 0, TAR_HEADER);
		if (f->namesize > sizeof(tar_header.name) + 1)
		{
			(void)sprintf(tar_header.prefix, "%-.*s", sizeof(tar_header.prefix), f->name);
			s = f->name + sizeof(tar_header.prefix);
		}
		else s = f->name;
		(void)sprintf(tar_header.name, "%s%s", s, f->type == S_IFDIR ? "/" : "");
		switch (f->linktype)
		{
		case HARDLINK:
			tar_header.typeflag = LNKTYPE;
		linked:
			(void)sprintf(tar_header.linkname, "%s", f->linkname);
			break;
		case SOFTLINK:
			tar_header.typeflag = SYMTYPE;
			goto linked;
		default:
			switch (state.formatout == USTAR ? f->type : S_IFREG)
			{
			case S_IFCHR:
				tar_header.typeflag = CHRTYPE;
			special:
				(void)sprintf(tar_header.devmajor, "%*o ", sizeof(tar_header.devmajor) - 2, major(f->st->st_rdev));
				(void)sprintf(tar_header.devminor, "%*o ", sizeof(tar_header.devminor) - 2, minor(f->st->st_rdev));
				break;
			case S_IFBLK:
				tar_header.typeflag = BLKTYPE;
				goto special;
			case S_IFDIR:
				tar_header.typeflag = DIRTYPE;
				break;
			case S_IFIFO:
				tar_header.typeflag = FIFOTYPE;
				break;
			case S_IFSOCK:
				tar_header.typeflag = SOKTYPE;
				goto special;
			default:
				if (!f->skip) error(1, "%s: unknown file type 0%03o -- assuming regular file", f->name, f->type >> 12);
				/*FALLTHROUGH*/
			case S_IFREG:
				tar_header.typeflag = REGTYPE;
				break;
			}
			break;
		}
		(void)sprintf(tar_header.mode, "%*o ", sizeof(tar_header.mode) - 2, f->st->st_mode & 07777);
		(void)sprintf(tar_header.uid, "%*o ", sizeof(tar_header.uid) - 2, f->st->st_uid);
		(void)sprintf(tar_header.gid, "%*o ", sizeof(tar_header.gid) - 2, f->st->st_gid);
		(void)sprintf(tar_header.size, "%*lo ", sizeof(tar_header.size) - 1, f->st->st_size);
		(void)sprintf(tar_header.mtime, "%*lo ", sizeof(tar_header.mtime) - 2, f->st->st_mtime);
		if (state.formatout == USTAR)
		{
			(void)strncpy(tar_header.magic, TMAGIC, sizeof(tar_header.magic));
			(void)strncpy(tar_header.version, TVERSION, sizeof(tar_header.version));
			getidnames(f);
			(void)strcpy(tar_header.uname, f->uidname);
			(void)strcpy(tar_header.gname, f->gidname);
		}
		(void)sprintf(tar_header.chksum, "%*o ", sizeof(tar_header.chksum) - 2, tar_checksum());
		bwrite(tar_block, TAR_HEADER);
		break;
	}
	putdeltaheader(f);
}

/*
 * read entry trailer
 */

/*ARGSUSED*/
void
gettrailer(f)
struct fileinfo*	f;
{
	register int	n;

	if (state.delta.sum-- > 0)
	{
		state.delta.checksum ^= state.delta.membersum;
		state.delta.membersum = 0;
	}
	if ((n = format[state.formatin].align) && (n = round(state.in.count, n) - state.in.count))
		(void)bread(state.tmp.buffer, 0L, (long)n, 1);
}

/*
 * write entry trailer
 */

void
puttrailer(f)
register struct fileinfo*	f;
{
	register int	n;

	switch (state.formatout)
	{
	case ALAR:
	case IBMAR:
	case PAX:
		putlabels(f, "EOF");
		break;
	}
	if ((n = format[state.formatout].align) && (n = round(state.out.count, n) - state.out.count))
	{
		memset(state.tmp.buffer, 0, n);
		bwrite(state.tmp.buffer, n);
	}
	listentry(f);
}

/*
 * return length of next label
 * variable length labels have label number > 3 and Vnnnn at position 5
 * where nnnn is the decimal length of the entire label
 * nnnn may be < ALAR_HEADER but label block must be >= ALAR_HEADER
 * 0 returned at end of label group
 */

int
getlabel(f)
register struct fileinfo*	f;
{
	register int	c;
	register int	n;

	static char	last[5];
	static int	done;

	if (done || (c = bread(alar_header, (long)ALAR_HEADER, (long)ALAR_LABEL, 0)) < ALAR_HEADER) return(*last = done = c = 0);
	if (state.formatin == IBMAR) cvtetoa(alar_header, alar_header, ALAR_HEADER);
	if (alar_header[4] == 'V' && ((n = getlabnum(alar_header, 4, 1, 10)) < 1 || n > 3) && (n = getlabnum(alar_header, 6, 4, 10)) != c)
	{
		if ((c = n - c) > 0)
		{
			if (state.in.blocked || bread(alar_header + ALAR_HEADER, 0L, (long)c, 1) != c)
			{
				c = ALAR_HEADER;
				error(2, "%s: %-*.*s: variable length label record too short", f->name, c, c, alar_header);
			}
			else
			{
				if (state.formatin == IBMAR) cvtetoa(alar_header + ALAR_HEADER, alar_header + ALAR_HEADER, c);
				c = n;
			}
		}
		else if (n <= ALAR_VARHDR) c = ALAR_VARHDR;
		else c = n;
	}
	if (!state.in.blocked && !*last && alar_header[3] == '2' && (strneq(alar_header, "HDR", 3) || strneq(alar_header, "EOF", 3) || strneq(alar_header, "EOV", 3)))
		getlabstr(alar_header, 26, 4, last);
	if (*last && strneq(alar_header, last, 4)) done = 1;
#if DEBUG
	message(-4, "label: %-*.*s", c, c, alar_header);
#endif
	return(c);
}

/*
 * output file HDR/EOF/EOV labels
 */

void
putlabels(f, type)
register struct fileinfo*	f;
char*				type;
{
	register int	n;
	char*		s;
	struct tm*	tm;

	static int	section = 1;
	static int	sequence;

	switch (*type)
	{
	case 'E':
		bwrite(alar_header, 0);
		break;
	case 'H':
		sequence++;
		break;
	}
	tm = gmtime(&f->st->st_mtime);
	(void)sprintf(alar_header, "%s1%-17.17s000001%04d%04d000100 %02d%03d 00000 %06d%-6.6s%-7.7s       ", type, f->id, section, sequence, tm->tm_year, tm->tm_yday, f->record.blocks, state.id.format, state.id.implementation);
	if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER);
	bwrite(alar_header, ALAR_HEADER);
	(void)sprintf(alar_header, "%s2%c%05d%05d%010d%s%c                     00                            ", type, state.record.format, state.blocksize, state.record.size, f->st->st_size, type, state.formatout == PAX && *type == 'H' ? '5' : '2');
	if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER);
	bwrite(alar_header, ALAR_HEADER);
	if (state.formatout == PAX && *type == 'H')
	{
		getidnames(f);
		s = fmtmode(f->st->st_mode);
		if (f->linktype == HARDLINK) *s = 'z';
		(void)sprintf(alar_header, "%s3%-8.8s%-8.8s%-10.10s%010d%010d%010d%010d          ", type, f->uidname, f->gidname, s, f->st->st_mtime, f->st->st_ctime, f->st->st_atime, f->st->st_size);
		bwrite(alar_header, ALAR_HEADER);
		n = ALAR_HEADER;
		if (f->linkname)
		{
			if ((n = f->linknamesize + 8) < ALAR_HEADER) n = ALAR_HEADER;
			(void)sprintf(alar_header, "%s4V%04d%-80s", type, f->linknamesize + 8, f->linkname);
		}
		else switch (f->type)
		{
		case S_IFBLK:
		case S_IFCHR:
			(void)sprintf(alar_header, "%s4%08o%08o%-15.15s                                             ", type, major(f->st->st_rdev), minor(f->st->st_rdev), SYSTEM);
			break;
		case S_IFDIR:
		case S_IFREG:
		case S_IFIFO:
			(void)sprintf(alar_header, "%s4                                                                            ", type);
			break;
		default:
			(void)sprintf(alar_header, "%s4V%04d                                                                       ", type, ALAR_VARHDR);
			break;
		}
		bwrite(alar_header, n);
		if ((n = f->namesize + 8) < ALAR_HEADER) n = ALAR_HEADER;
		(void)sprintf(alar_header, "%s5V%04d%-80s", type, f->namesize + 8, f->name);
		bwrite(alar_header, n);
	}
	bwrite(alar_header, 0);
	if (streq(type, "EOV"))
	{
		section++;
		sequence = 0;
	}
	else section = 1;
}

#ifdef SAVESET

/*
 * get next saveset record
 * if header!=0 then all records skipped until REC_file found
 * otherwise REC_vbn cause non-zero return
 * 0 returned for no record match
 */

int
getsaveset(f, header)
register struct fileinfo*	f;
int				header;
{
	register char*	p;
	register char*	s;
	char*		t;
	int		i;
	long		n;

	for (;;)
	{
		state.saveset.bp += state.saveset.lastsize;
		while (state.saveset.bp >= state.saveset.block + state.blocksize)
		{
			state.saveset.bp = state.saveset.block;
			state.saveset.lastsize = 0;
			if (bread(state.saveset.bp, 0L, (long)state.blocksize, 0) <= 0)
			{
				state.formatin = ALAR;
				if (header) gettrailer(f);
				return(0);
			}
			if (gethalf(BYTE|HALF, state.saveset.bp + BLKHDR_hdrsiz) != BLKHDR_SIZE)
				error(3, "invalid %s format block header", format[state.formatin].name);
			if (!(n = getlong(BYTE|HALF, state.saveset.bp + BLKHDR_blksiz)))
				state.saveset.bp += state.blocksize;
			else if (n != state.blocksize)
				error(3, "invalid %s format blocksize=%ld", format[state.formatin].name, n);
			state.saveset.bp += BLKHDR_SIZE;
		}
		state.saveset.lastsize = gethalf(BYTE|HALF, state.saveset.bp + RECHDR_size);
		i = gethalf(BYTE|HALF, state.saveset.bp + RECHDR_type);
		state.saveset.bp += RECHDR_SIZE;
#if DEBUG
		message(-2, "record: type=%d size=%d", i, state.saveset.lastsize);
#endif
		if (i == REC_file)
		{
			if (header)
			{
				p = state.saveset.bp;
				if (gethalf(BYTE|HALF, p) != FILHDR_MAGIC)
					error(3, "invalid %s format file header", format[state.formatin].name);
				p += 2;
				i = gethalf(BYTE|HALF, p + FILHDR_size);
				if (p + FILHDR_data + i > state.saveset.block + state.blocksize)
					error(3, "invalid %s format file attribute", format[state.formatin].name);
				t = f->name;
				n = 0;
				for (s = p + FILHDR_data + 1; s < p + FILHDR_data + i; s++)
				{
					if (isupper(*s)) *t++ = tolower(*s);
					else if (n)
					{
						if (*s == ';') break;
						*t++ = *s;
					}
					else if (*s == ']')
					{
						n = 1;
						*t++ = '/';
					}
					else if (*s == '.') *t++ = '/';
					else *t++ = *s;
				}
				*t = 0;
				for (i = 0; i < 5; i++)
				{
					s = p + FILHDR_size;
					if ((p += FILHDR_SIZE + gethalf(BYTE|HALF, s)) > state.saveset.block + state.blocksize)
						error(3, "invalid %s format file attribute", format[state.formatin].name);
				}
				state.saveset.recatt = getbyte(BYTE|HALF, p + FILHDR_data + FILATT_recfmt);
				state.saveset.recfmt = getbyte(BYTE|HALF, p + FILHDR_data + FILATT_recfmt);
				state.saveset.reclen = gethalf(BYTE|HALF, p + FILHDR_data + FILATT_reclen);
				state.saveset.recvfc = gethalf(BYTE|HALF, p + FILHDR_data + FILATT_recvfc);
				f->st->st_size = (long)(gethalf(BYTE|HALF, p + FILHDR_data + FILATT_blocks) - 1) * BLOCKSIZE + (long)gethalf(BYTE|HALF, p + FILHDR_data + FILATT_frag);
				for (; i < 15; i++)
				{
					s = p + FILHDR_size;
					if ((p += FILHDR_SIZE + gethalf(BYTE|HALF, s)) > state.saveset.block + state.blocksize)
						error(3, "invalid %s format file attribute", format[state.formatin].name);
				}

				/*
				 * pure guesswork based on 100 nsec epoch
				 * 17-NOV-1858 00:00:00 GMT
				 */

				if ((n = getlong(BYTE|HALF, p + FILHDR_data + 4) - 7355000) < 0) n = 1;
				else n = (n << 8) + getbyte(BYTE|HALF, p + FILHDR_data + 3);
				f->st->st_mtime = (time_t)n;
				return(1);
			}
			state.saveset.bp -= RECHDR_SIZE;
			state.saveset.lastsize = 0;
			return(0);
		}
		else if (i == REC_vbn && !header) return(1);
	}
}

#endif

#if CPIO_EXTENDED

static char	opsbuf[PATH_MAX];	/* extended ops buffer		*/

static char*	ops = opsbuf;		/* opsbuf output pointer	*/

/*
 * get and execute extended ops from input
 */

static void
getxops(f)
register struct fileinfo*	f;
{
	register char*	p;
	register char*	s;
	register int	c;
	long		n;

	if (f->namesize > (n = strlen(f->name) + 1)) for (p = f->name + n; c = *p++;)
	{
		for (s = p; *p; p++);
		p++;
		n = strtol(s, (char*)0, 16);
#if DEBUG
		message(-2, "%s: entry %d.%d op = %c%s", f->name, state.volume, state.entry, c, s);
#endif
		switch (c)
		{
		case 'd':
			f->st->st_rdev = n;
			break;
		case 'g':
			f->st->st_gid = n;
			break;
		case 'u':
			f->st->st_uid = n;
			break;
		case 'G':
			f->gidname = s;
			break;
		case 'U':
			f->uidname = s;
			break;

			/*
			 * NOTE: ignore unknown ops for future extensions
			 */
		}
	}
}

/*
 * set end of extended ops
 */

static void
setxops(f)
register struct fileinfo*	f;
{
	register int	n;

	if (n = ops - opsbuf)
	{
		n++;
		*ops++ = 0;
		if ((f->namesize += n) > CPIO_NAMESIZE) error(1, "%s: extended ops may crash older cpio programs", f->name);
	}
}

/*
 * output filename and extended ops
 */

static void
putxops(f)
register struct fileinfo*	f;
{
	register int	n;

	n = ops - opsbuf;
	bwrite(f->name, f->namesize -= n);
	if (n) bwrite(ops = opsbuf, n);
}


/*
 * add extended op string
 */

static void
addxopstr(op, s)
int		op;
register char*	s;
{
	register char*	p = ops;
	register char*	e = opsbuf + sizeof(opsbuf) - 3;

	if (p < e)
	{
		*p++ = op;
		while (*s && p < e) *p++ = *s++;
		*p++ = 0;
		ops = p;
	}
#if DEBUG
	if (*s) error(PANIC, "addxopstr('%c',\"%s\") overflow", op, s);
#endif
}

/*
 * add numeric op for current entry
 */

static void
addxopnum(op, n)
int	op;
long	n;
{
	char	buf[17];

	(void)sprintf(buf, "%x", n);
	addxopstr(op, buf);
}

#endif
0707070000000000131006440044230044230000010000000471771643500002200000013622misc.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax miscellaneous support
 */

#include "pax.h"

/*
 * return format index given format name
 */

int
getformat(name)
register char*	name;
{
	register int	i;

	(void)strlower(name);
	for (i = 0; format[i].name; i++)
		if (streq(name, format[i].name))
			break;
	return(format[i].name ? i : streq(name, "-") || streq(name, "pax") ? OUT_DEFAULT : -1);
}

/*
 * check f with patterns given on cmd line
 */

int
selectfile(f)
register struct fileinfo*	f;
{
	register struct deltainfo*	d;

	if (f->skip || f->namesize <= 1) return(0);
	if (f->record.format && state.record.pattern)
	{
		static char	fmt[2];

		fmt[0] = f->record.format;
		if (!strmatch(fmt, state.record.pattern)) return(0);
	}
	if (state.append || (state.delta.op & COLLECT))
	{
		(void)addlink(f);
		if (state.delta.tab)
		{
			if (!(d = allocate(struct deltainfo)))
				error(3, "%s: out of space", f->name);
			d->dev = f->st->st_dev;
			d->ino = f->st->st_ino;
			d->mtime = f->st->st_mtime;
			d->offset = state.in.offset + state.in.count;
			d->size = f->st->st_size;
			if (state.delta.op & COLLECT)
			{
				if (!(d->info = (struct fileinfo*)memdup(f, sizeof(struct fileinfo))) || !(d->info->st = (struct stat*)memdup(f->st, sizeof(struct stat))))
					error(3, "%s: out of space", f->name);
				d->info->name = hashput(state.delta.tab, f->name, (char*)d);
				if (d->info->uidname) d->info->uidname = strdup(d->info->uidname);
				if (d->info->gidname) d->info->gidname = strdup(d->info->gidname);
				d->info->delta.base = d;
			}
			else (void)hashput(state.delta.tab, f->name, (char*)d);
		}
		return(0);
	}
	if (!match(f->name) || state.verify && f->type != S_IFDIR && !verify(f)) return(0);
	state.selected++;
	if (state.list) (void)addlink(f);
	return(1);
}

/*
 * verify action on file
 *
 *	EOF	exit
 *	NULL	skip file
 *	.	keep file
 *	<else>	rename file
 */

int
verify(f)
register struct fileinfo*	f;
{
	register char*	prompt;
	register int	n;

	static char	namebuffer[PATH_MAX + 2];

	if (state.yesno) switch (state.operation)
	{
	case IN:
		prompt = "Read";
		break;
	case OUT:
		prompt = "Write";
		break;
	default:
		prompt = "Pass";
		break;
	}
	else prompt = "Rename";
	fprintf(state.wtty, "%s %s: " , prompt, f->name);
	if (!fgets(namebuffer, sizeof(namebuffer), state.rtty))
	{
		putc('\n', state.wtty);
		finish(2);
	}
	n = strlen(namebuffer);
	namebuffer[n - 1] = 0;
	if (state.yesno) return(namebuffer[0] == 'y');
	switch (namebuffer[0])
	{
	case 0:
		return(0);
	case '.':
		if (!namebuffer[1]) break;
		/*FALLTHROUGH*/
	default:
		f->namesize = pathcanon(f->name = namebuffer) - namebuffer + 1;
		break;
	}
	return(1);
}

/*
 * check for file name mapping
 * static data possibly returned
 * two simultaneous calls supported
 */

char*
map(name)
register char*	name;
{
	register struct maplist*	mp;

	static char			filebuffer[2][PATH_MAX];
	static char*			filename = filebuffer[0];

	filename = (filename == filebuffer[0]) ? filebuffer[1] : filebuffer[0];
	for (mp = state.maps; mp; mp = mp->next)
		if (reexec(mp->re, name))
		{
			(void)resub(mp->re, name, mp->into, filename, mp->flags);
			if (mp->flags & RE_VERBOSE) fprintf(stderr, "%s >> %s\n", name, filename);
			return(filename);
		}
	return(name);
}

/*
 * list entry information based on state.drop, state.list and state.verbose
 */

void
listentry(f)
register struct fileinfo*	f;
{
	register char*	s;
	long		size;
	char		buf[PATH_MAX];

	if (!f->skip && (state.drop || state.list || state.verbose))
	{
		if (state.drop)
		{
			if (++state.dropcount >= 50)
			{
				state.dropcount = 0;
				fprintf(stderr, ".\n");
			}
			else
			{
				fprintf(stderr, ".");
				fflush(stderr);
			}
		}
		else
		{
			switch (f->delta.op)
			{
			case 0:
				s = 0;
				break;
			case DELTA_create:
				s = "create";
				break;
			case DELTA_delete:
				s = "delete";
				break;
			case DELTA_update:
				s = "update";
				break;
			case DELTA_verify:
				s = "verify";
				break;
			default:
				s = "??????";
				break;
			}
			size = f->st->st_size;
			if (f->linktype == SOFTLINK) f->st->st_size = f->linknamesize - 1;
			else if (f->delta.size != -1) f->st->st_size = f->delta.size;
			(void)fmtls(buf, f->name, f->st, s, f->linktype == NOLINK ? (char*)0 : f->linkname, (state.list && state.verbose) ? LS_LONG : 0);
			f->st->st_size = size;
			fprintf(state.list ? stdout : stderr, "%s\n", buf);
		}
	}
}

/*
 * prepare patterns for match()
 */

char**
initmatch(p)
char**	p;
{
	register char**	a;

	a = p;
	while (*a)
		(void)pathcanon(*a++);
	return(p);
}

/*
 * determine if file s matches input patterns
 */

int
match(s)
register char*	s;
{
	register char**	p;
	register char*	t;
	int		n;

	if (!(p = state.patterns)) return(state.matchsense);
	if (state.exact)
	{
		n = 0;
		while (t = *p++)
			if (*t)
			{
				if (streq(s, t))
				{
					*--p = "";
					return(1);
				}
				n = 1;
			}
		if (!n) finish(0);
	}
	else while (t = *p++)
	{
		if (dirprefix(t, s) || strmatch(s, t))
			return(state.matchsense);
	}
	return(!state.matchsense);
}

/*
 * return 1 if p is a directory prefix of s
 */

int
dirprefix(p, s)
register char*	p;
register char*	s;
{
	while (*p)
		if (*p++ != *s++)
			return(0);
	return(!*s || *s == '/');
}

#if DEBUG

/*
 * place op names from op into buf
 */

static char*
opnames(p, name, op)
register char*	p;
char*		name;
register int	op;
{
	register char*	s;
	register int	n;

	static struct
	{
		char*	name;
		int	op;
	}		ops[] =
	{
		"IN",		IN,
		"OUT",		OUT,
		"COLLECT",	COLLECT,
		"COMPRESS",	COMPRESS,
		"CONVERT",	CONVERT,
	};

	extern char*	strcopy();

	if (name)
	{
		p = strcopy(p, name);
		*p++ = '=';
	}
	s = p;
	for (n = 0; n < elements(ops); n++)
		if (op & ops[n].op)
		{
			if (s != p) *p++ = '|';
			p = strcopy(p, ops[n].name);
		}
	return(p);
}

/*
 * return the current operation names
 */

char*
operations()
{
	static char	buf[256];

	(void)sprintf(opnames(opnames(buf, "operation", state.operation), " delta", state.delta.op), " sum=%d", state.delta.sum);
	return(buf);
}

#endif
0707070000000000141006440044230044230000010000000470731313600002100000032544pax.1UgsfGgsf.\"
.\" G. S. Fowler
.\" AT&T Bell Laboratories
.\"
.\" @(#)pax.1 (ulysses!gsf) 01/11/90
.\"
.TH PAX 1
.SH NAME
pax \- portable archive interchange
.SH SYNOPSIS
.B pax
[
.B \-rmnov
] [
.B \-f
.I archive
] [
.B \-s
.RI / old / new /[gp]
] [
.I "pattern ..."
]
.LP
.B pax
.B \-w
[
.B mv
] [
.B \-b
.I blocking
] [
.B \-f
.I archive
] [
.B \-s
.RI / old / new /[gp]
] [
.B \-x
.I format
] [
.I "pathname ..."
]
.LP
.B pax
.B \-rw
[
.B mov
] [
.B \-s
.RI / old / new /[gp]
] [
.I "pathname ..."
]
.I directory
.SH DESCRIPTION
.I pax
reads and writes archive files in various formats.
There are four operation modes controlled by combinations of the
.B \-r
and
.B \-w
options.
.PP
.B "pax \-w"
writes the files and directories named by the
.I pathname
arguments to the standard output together with
pathname and status information.
A directory
.I pathname
argument refers to the files and (recursively) subdirectories
of that directory.
If no
.I pathname
arguments are given then the standard input is read to get
a list of pathnames to copy, one pathname per line.
In this case only those pathnames appearing on the standard input are copied.
.PP
.B "pax \-r"
reads files from the standard input that is assumed
to be the result of a previous
.B "pax \-w"
command.
Only files with names that match any of the
.I pattern
arguments are selected.
A
.I pattern
is given in the name-generating notation of
.IR sh (1),
except that the
.B /
character is also matched.
The default if no
.I pattern
is given is
.BR * ,
which selects all files.
The selected files are conditionally created and copied relative
to the current directory tree, subject to the options described below.
By default the owner and group of selected files will be that of the
current user, and the permissions and modify times will be the same
as those in the archive.
If the
.B \-r
option is omitted then a table of contents of the selected files is
listed on the standard output.
.PP
.B "pax \-rw"
reads the files and directories named in the
.I pathname
arguments and copies them to the destination
.IR directory .
A directory
.I pathname
argument refers to the files and (recursively) subdirectories
of that directory.
If no
.I pathname
arguments are given then the standard input is read to get
a list of pathnames to copy, one pathname per line.
In this case only those pathnames appearing on the standard input are copied.
.I directory
must exist before the copy.
.PP
The standard archive formats are automatically detected on input.
The default output archive format is implementation defined,
but may be overridden by the
.B \-x
option described below.
.I pax
archives may be concatenated to combine multiple volumes on a single
tape or file.
This is accomplished by forcing any format prescribed pad data to be null bytes.
Hard links are not maintained between volumes, and
delta and base archives cannot be multi-volume.
.PP
A single archive may span many files/devices.
The second and subsequent file names are prompted for on the terminal input.
The response may be:
.TP
.BI ! command
Execute
.I command
via
.IR system (3)
and prompt again for file name.
.TP
.B EOF
Exit without further processing.
.TP
.B CR
An empty input line retains the previous file name.
.TP
.I pathname
The file name for the next archive part.
.SS "Basic Options"
These options support basic archive operations.
.TP
.BI b " blocking"
Set the output blocking size.
If no suffix (or a
.B c
suffix) is specified then
.I blocking
is in 1 character units.
A
.B b
suffix multiplies
.I blocking
by 512 (1 block), a
.B k
suffix multiplies
.I blocking
by 1024 (1 kilobyte) and an
.B m
suffix multiplies
.I blocking
by 1048576 (1 megabyte).
.I blocking
is automatically determined on input and is ignored for
.BR \-rw .
The default
.I blocking
is
.B 10k
for block and character special archive files and
implementation defined otherwise.
The minimum
.I blocking
is
.BR 1c .
.TP
.BI f " archive"
.I archive
is the pathname of the input or output archive, overriding the default
standard input for
.B \-r
and
.B \-rw
or standard output for
.BR \-w .
.TP
.B m
File modification times are not retained.
.TP
.B n
For
.B \-r
the pattern arguments are treated as ordinary file names.
Only the first occurrence of each of these files in the
input archive is read.
.I pax
exits with zero exit status after all files in the list have been read.
If one or more files in the list is not found,
.I pax
writes a message to standard error for each of these files
and exits with a non-zero exit status.
The file names are compared before any of the
.B \-i,
.B \-s,
or
.B \-y
options are applied.
.TP
.B o
Restore file ownership as specified in the archive.
The current user must have appropriate privileges.
.TP
\fBs\fP /\fIold\fP/\fInew\fP/[\fIglpu\fP]
File names and symbolic link text are mapped according
to the
.IR ed (1)
style substitution expression.
Any non-null character may be used as a delimiter
.RB ( /
shown here).
Multiple
.B \-s
expressions may be specified; the expressions are applied from left to right,
terminating with the first successful substitution.
A trailing
.B l
converts the matched string to lower case.
A trailing
.B p
causes successful mappings to be listed on the standard error.
A trailing
.B u
converts the matched string to upper case.
File names that substitute to the null string are ignored on
both input and output.
The
.B \-P
option inhibits symbolic link text substitution.
.TP
.B v
Produces a verbose table of contents listing on the standard output when both
.B \-r
and
.B \-w
are omitted.
Otherwise the file names are listed on the standard error
as they are encountered.
.TP
.BI x " format"
Specifies the output archive
.IR format .
If specified with
.B \-rw
then the standard input is treated as an archive that is converted to a
.I format
archive on the standard output.
The input format, which must be one of the following,
is automatically determined.
The default output format, named by
.BR \- ,
is
.BR cpio .
The formats are:
.RS
.PD 0
.TP
.B asc
The
.B s5r4
extended
.IR cpio (5)
character format.
.TP
.B ansi
ANSI standard label tape format.
Only regular files with simple pathnames are archived.
Valid only for blocked devices.
.TP
.B asc
The
.B s5r4
extended
.IR cpio (5)
character format.
.TP
.B aschk
The
.B s5r4
extended
.IR cpio (5)
character format with header checksum.
This format is misnamed
.B crc
in the
.B s5r4
documentation.
.TP
.B binary
The
.IR cpio (5)
binary format with symbolic links.
This format is obsolete and should not be used on output.
.TP
.B cpio
The
.IR cpio (5)
character format with symbolic links.
This is the default output format.
.TP
.B ibmar
EBCDIC standard label tape format.
Only regular files with simple pathnames are archived.
Valid only for tape devices.
.TP
.B posix
The IEEE 1003.1b-1990 interchange format, partially compatible with
the X3.27 standard labeled tape format.
.TP
.B portarch
The svr2 portable object library format.
Valid only on input.
.TP
.B randarch
The BSD ranlib object library format.
Valid only on input.
.TP
.B tar
The
.IR tar (5)
format with symbolic links.
.TP
.B ustar
The POSIX IEEE Std 1003.1-1988 tar format.
.TP
.B vmsbackup
ANSI standard label VMS backup savset tape format.
Valid only for input tape devices.
.PD
.RE
.SS "Compatibility Options"
These options provide functional compatibility with the old
.IR cpio (1)
and
.IR tar (1)
commands.
.TP
.B a
For
.B \-w
append files to the end of the archive.
.TP
.B c
Complement the match sense of the
.I pattern
arguments.
.TP
.B d
Intermediate directories not explicitly listed in the archive
are not created.
.TP
.B i
Interactively
.I rename
files.
A file is skipped if a null line is entered and
.I pax
exits if
.B EOF
is encountered.
.TP
.B l
For
.BR \-rw ,
files are linked rather than copied when possible.
.TP
.B p
Preserve the access times of input files after they have been copied.
.TP
.BI t " device"
.I device
is an identifier that names the input or output archive device,
overriding the default standard input for
.B \-r
or standard output for
.BR \-w .
Tape devices may be specified as
.IR drive [ density\|rewind ]
where
.I drive
is a drive number in the range [0\-7],
.I density
is one of
.BR l ,
.B m
and
.B h
for
.B low
(800 bpi),
.B medium
(1600 bpi \- default)
and
.B high
(6250 bpi)
tape densities and
.I rewind
is
.B n
to inhibit rewinding of the tape device when it is closed.
Other forms for
.I device
are implementation defined.
.TP
.B u
Copy each file only if it is newer than a pre-existing file with the same name.
This option implies
.BR \-a .
.TP
.B y
Interactively prompt for the disposition of each file.
.B EOF
or an input line starting with
.B q
causes
.I pax
to exit.
Otherwise an input line starting with anything other than
.B y
causes the file to be ignored.
.SS "Extended Options"
These options provide fine archive control, including delta archive operations.
.TP
.BI e " filter"
Run the
.I filter
command on each file to be output.
The current name of the file to be output is appended to the filter command
string before the command is executed by the shell.
.TP
.B h
Inhibit archive heading and summmary information messages to stderr.
.TP
.B k
For
.B \-r
continue processing the archive after encountering an error by attempting
to locate the next valid entry.
This is useful for archives stored on unreliable media.
.TP
.BI z " base"
Specifies the delta base archive
.I base
that is assumed to be the result of a previous
.B "pax \-w"
command.
For
.B \-w
the input files are compared with the files in
.I base
and file delta information is placed in the output archive
using the delta algorithm.
For
.B \-r
the delta information in the input archive is used to update the
output files with respect to the files in
.IR base .
For
.B \-rw
the delta information in the archive on the standard input is used
to generate an archive on the standard output whose entries are updated
with respect to the files in
.IR base .
If
.I base
is
.B \-
or an empty file then the input files are simply compressed.
.B "\-z -"
must also be specified to produce a compressed archive for
.BR \-rw .
.TP
.BI B " count"
Sets the maximum archive part output character count.
.I pax
prompts for the next archive part file name.
Valid only with
.BR \-w .
.TP
.B C
Archive entries smaller than
.BI \-B " maxblocks"
must be contained within a single part.
Valid only with
.BR \-B .
.TP
.B L
Copy a logical view of the input files.
Symbolic links are followed, causing the pointed to files to be copied
rather than the symbolic link information.
This is the default.
.TP
.BI M " message"
Set the
.I "end of medium"
prompt to
.IR message .
This message is used to prompt interactively for the next tape
reel or cartridge in cases where the tape runs out before
all files have been copied.
.I message
may contain one
.IR printf (3)
style integer format specification that is replaced with the
next part number.
.TP
.B P
Copy a physical view of the input files.
Causes symbolic link information to be copied as opposed to the
default (logical view) action of following symbolic links
and copying the pointed to files.
.TP
\fBR\fP \fIoption\fP[\fIvalue\fP][,\fIoption\fP[\fIvalue\fP]...]
Set record oriented format options.
Multiple options may be concatenated using
.BR , .
Some options may be fixed for some formats.
The options are:
.RS
.PD 0
.TP
.B c
Record data is subject to character set conversions.
.TP
.BI f format
Set the output record format to
.IR format .
The supported record formats are:
.RS
.TP
.B D
Variable length with 4 byte record header.
The record size default is 512.
.TP
.B F
Fixed length with no record header.
The record size default is 128.
.TP
.B S
Spanned variable length with 4 byte record header.
The record size default is 0 (no limit).
.TP
.B U
Variable length with no record header.
The output block size matches the size of each output record.
The record size default is 512.
.TP
.B V
Spanned variable length with binary 4 byte record header.
The record size default is 0 (no limit).
The
.B D
format is preferred.
.PD
.RE
.TP
.BI m pattern
Only those files with input record format matching
.I pattern
are processed.
.TP
.B p
Partial output blocks are padded to the full blocksize.
.TP
.BI s size
Set the output record size to
.IR size .
.I size
should divide the output blocking.
.TP
.BI v label
Set the output volume label to
.IR label .
Some formats may truncate and/or case-convert
.IR label .
.PD
.RE
.TP
.B S
Similar to
.B \-l
except that symbolic links are created.
.TP
.BI U " id"
Set file ownership to the default of the user named
.IR id .
Valid only for the super-user.
.TP
.B V
Output a `.' as each file is encountered.
This overrides the
.B \-v
option.
.TP
.B X
Do not cross mount points when searching for files to output.
.SH DIAGNOSTICS
The number of files, blocks, and optionally the number of volumes and
media parts are listed on the standard error.
For
.B \-v
the input archive formats are also listed on the standard error.
.SH EXAMPLES
.TP
.B "pax \-w \-t 1m ."
Copies the contents of the current directory to tape drive 1, medium density.
.TP
.PD 0
.BI mkdir "  newdir"
.TP
.BI cd "  olddir"
.TP
.BI "pax  \-rw  ." "  newdir"
.PD
Copies the
.I olddir
directory hierarchy to
.IR newdir .
.SH "SEE ALSO"
ar(1), cpio(1), find(1), ksh(1), tar(1), tw(1), libdelta(3), cpio(5), tar(5)
.SH BUGS
Special privileges may be required to copy special files.
.br
Each archive format has a hard upper limit on member pathname sizes.
.br
Device, user-id and group-id numbers larger than 65535 cause additional
header records to be output.
These records are ignored by old versions of
.IR cpio (1)
and
.IR tar (1).
0707070000000000151006440044230044230000010000000430261237100002700000014714pax.1.posixUgsfGgsf\"
\" G. S. Fowler
\" AT&T Bell Laboratories
\"
\" @(#)pax.1 (ulysses!gsf) 08/08/88
\"
.TH PAX 1
.SH NAME
pax \- portable archive interchange
.SH SYNOPSIS
.B pax
[
.B \-rcimopuvy
] [
.B \-f
.I archive
] [
.B \-s
.RI / old / new /[gp]
] [
.B \-t 
.I device
] [
.I "pattern ..."
]
.LP
.B pax
.B \-w
[
.B adimuvy
] [
.B \-b
.I blocking
] [
.B \-f
.I archive
] [
.B \-s
.RI / old / new /[gp]
] [
.B \-t 
.I device
] [
.B \-x
.I format
] [
.I "pathname ..."
]
.LP
.B pax
.B \-rw
[
.B ilmopuvy
] [
.B \-s
.RI / old / new /[gp]
] [
.I "pathname ..."
]
.I directory
.SH DESCRIPTION
.I pax
reads and writes archive files in the
.IR cpio (5)
and
.IR tar (5)
formats.
There are four operation modes controlled by combinations of the
.B \-r
and
.B \-w
options.
.PP
.B "pax \-w"
writes the files and directories named by the
.I pathname
arguments to the standard output together with
pathname and status information.
A directory
.I pathname
argument refers to the files and (recursively) subdirectories
of that directory.
If no
.I pathname
arguments are given then the standard input is read to get
a list of pathnames to copy, one pathname per line.
In this case only those pathnames appearing on the standard input are copied.
.PP
.B "pax \-r"
reads files from the standard input that is assumed
to be the result of a previous
.B "pax \-w"
command.
Only files with names that match any of the
.I pattern
arguments are selected.
A
.I pattern
is given in the name-generating notation of
.IR sh (1),
except that the
.B /
character is also matched.
The default if no
.I pattern
is given is
.BR * ,
which selects all files.
The selected files are conditionally created and copied relative
to the current directory tree, subject to the options described below.
By default the owner and group of selected files will be that of the
current user, and the permissions and modify times will be the same
as those in the archive.
If the
.B \-r
option is omitted then a table of contents of the selected files is
listed on the standard error.
.PP
.B "pax \-rw"
reads the files and directories named in the
.I pathname
arguments and copies them to the destination
.IR directory .
A directory
.I pathname
argument refers to the files and (recursively) subdirectories
of that directory.
If no
.I pathname
arguments are given then the standard input is read to get
a list of pathnames to copy, one pathname per line.
In this case only those pathnames appearing on the standard input are copied.
.I directory
must exist before the copy.
.PP
The standard archive formats are automatically detected on input.
The default output archive format is implementation defined,
but may be overridden by the
.B \-x
option described below.
.I pax
archives may be concatenated to combine multiple volumes on a single
tape or file.
This is accomplished by forcing any pad data to be null bytes.
.SS "Basic Options"
These options support basic archive operations.
.TP
.BI b " blocking"
Block the output at
.I blocking
bytes per record.
A
.B k
suffix multiplies
.I blocking
by 1024
and a
.B b
suffix multiplies
.I blocking
by 512.
.I blocking
is automatically determined on input and is ignored for
.BR \-rw .
The default
.I blocking
is
.B 10k
for block and character special archive files and
.B 1b
otherwise.
The minimum
.I blocking
is
.BR 1b .
.TP
.BI f " archive"
.I archive
is the pathname of the input or output archive, overriding the default
standard input for
.B \-r
or standard output for
.BR \-w .
.TP
.B m
File modification times are not retained.
.TP
.B o
Restore file ownership as specified in the archive.
The current user must have appropriate privileges.
.TP
\fBs\fP /\fIold\fP/\fInew\fP/[\fIgp\fP]
File names are mapped according
to the
.IR ed (1)
style substitution expression.
Any non-null character may be used as a delimiter
.RB ( /
shown here).
Multiple
.B \-s
expressions may be specified; the expressions are applied from left to right,
terminating with the first successful substitution.
The optional trailing
.B p
causes successful mappings to be listed on the standard error.
File names that substitute to the null string are ignored on
both input and output.
.TP
.B v
List file names as they are encountered.
Produces a verbose table of contents listing when both
.B \-r
and
.B \-w
are omitted.
.TP
.B x
Specifies the output archive
.IR format .
The input format, which must be one of the following,
is automatically determined.
The formats are:
.RS
.PD 0
.TP
.B cpio
The
.IR cpio (5)
character format.
.TP
.B tar
The
.IR tar (5)
format.
.TP
.B ustar
The proposed POSIX tar format.
.PD
.RE
.SS "Compatibility Options"
These options provide functional compatibility with the old
.IR cpio (1)
and
.IR tar (1)
commands.
.TP
.B a
For
.B \-w
append files to the end of the archive.
.TP
.B c
Complement the match sense of the
.I pattern
arguments.
.TP
.B d
Intermediate directories not explicitly listed in the archive
are not created.
.TP
.B i
Interactively
.I rename
files.
A file is skipped if a null line is entered and
.I pax
exits if
.B EOF
is encountered.
.TP
.B l
For
.BR \-rw ,
files are linked rather than copied when possible.
.TP
.B p
Preserve the access times of input files after they have been copied.
.TP
.BI t " device"
.I device
is an implementation defined identifier that names the
the input or output archive device, overriding the default
standard input for
.B \-r
or standard output for
.BR \-w .
.TP
.B u
Copy each file only if it is newer than a pre-existing file with the same name.
This option implies
.BR \-a .
.TP
.B y
Interactively prompt for the disposition of each file.
.B EOF
or an input line starting with
.B q
causes
.I pax
to exit.
Otherwise an input line starting with anything other than
.B y
causes the file to be ignored.
.SH DIAGNOSTICS
The number of files, blocks, and optionally the number of volumes and
media parts are listed on the standard error.
For
.B \-v
the input archive formats are also listed on the standard error.
.SH EXAMPLES
.TP
.B "pax \-w \-t 1m ."
Copies the contents of the current directory to tape drive 1, medium density.
.TP
.PD 0
.BI mkdir "  newdir"
.TP
.BI cd "  olddir"
.TP
.BI "pax  \-rw  ." "  newdir"
.PD
Copies the
.I olddir
directory hierarchy to
.IR newdir .
.SH "SEE ALSO"
ar(1), cpio(1), find(1), sh(1), tar(1), cpio(5), tar(5)
.SH BUGS
Special privileges may be required to copy special files.
.br
Each archive format has a hard upper limit on member pathname sizes.
.br
Device, user-id and group-id numbers larger than 65535 cause additional
header records to be output.
These records are ignored by old versions of
.IR cpio (1)
and
.IR tar (1).
0707070000000000161006440044230044230000010000000474560466500002100000033640pax.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax -- portable archive interchange
 *
 * current tests:
 *
 *	010	list bget() buffer alignment
 *	020	ignore delta/compress header info
 *	040	pretend output device is char special
 */

#if !lint
static char id[] = "\n@(#)pax (AT&T Bell Laboratories) 01/01/91\0\n";
#endif

#include "pax.h"

char*			definput = "standard input";
char*			defoutput = "standard output";
char*			eomprompt = "Change to part %d and hit RETURN: ";
char			alar_header[ALAR_LABEL];
struct binary_header	binary_header;
union tar_header_block	tar_header_block;
struct portar_header	portar_header;
struct fileinfo		file;
struct stateinfo	state;

struct formatinfo	format[] =
{
ALAR_NAME,	ALAR_REGULAR,	ALAR_SPECIAL,	ALAR_ALIGN,	ALAR_FLAGS,
BINARY_NAME,	BINARY_REGULAR,	BINARY_SPECIAL,	BINARY_ALIGN,	BINARY_FLAGS,
CPIO_NAME,	CPIO_REGULAR,	CPIO_SPECIAL,	CPIO_ALIGN,	CPIO_FLAGS,
IBMAR_NAME,	IBMAR_REGULAR,	IBMAR_SPECIAL,	IBMAR_ALIGN,	IBMAR_FLAGS,
TAR_NAME,	TAR_REGULAR,	TAR_SPECIAL,	TAR_ALIGN,	TAR_FLAGS,
USTAR_NAME,	USTAR_REGULAR,	USTAR_SPECIAL,	USTAR_ALIGN,	USTAR_FLAGS,
ASC_NAME,	ASC_REGULAR,	ASC_SPECIAL,	ASC_ALIGN,	ASC_FLAGS,
ASCHK_NAME,	ASCHK_REGULAR,	ASCHK_SPECIAL,	ASCHK_ALIGN,	ASCHK_FLAGS,
SAVESET_NAME,	SAVESET_REGULAR,SAVESET_SPECIAL,SAVESET_ALIGN,	SAVESET_FLAGS,
PAX_NAME,	PAX_REGULAR,	PAX_SPECIAL,	PAX_ALIGN,	PAX_FLAGS,
PORTAR_NAME,	PORTAR_REGULAR,	PORTAR_SPECIAL,	PORTAR_ALIGN,	PORTAR_FLAGS,
RANDAR_NAME,	RANDAR_REGULAR,	RANDAR_SPECIAL,	RANDAR_ALIGN,	RANDAR_FLAGS,
0,		0,		0,		0
};

static int		signals[] =	/* signals caught by interrupt() */
{
	SIGHUP,
	SIGINT,
#if !DEBUG
	SIGQUIT,
#endif
	SIGALRM,
	SIGTERM,
};

static void	interrupt();

/*ARGSUSED*/
main(argc, argv)
int	argc;
char**	argv;
{
	register int	i;
	register char*	s;
	char*		idname;
	char*		p;
	char*		t;
	int		n;
	struct maplist*	mp;
	struct maplist*	lastmap;

	seterror('I', "pax", '2', &state.errors, 't', &state.trace, 0);
	state.gid = getegid();
	state.uid = geteuid();
	(void)umask(state.modemask = umask(0));
	(void)time(&state.present);
	state.formatout = -1;
#if physical_is_the_default
	state.ftwflags = FTW_DOT|FTW_PHYSICAL;
#else
	state.ftwflags = FTW_DOT;
#endif
	state.intermediate = 1;
	state.matchsense = 1;
	state.modtime = 1;
	state.delta.offset = 1;
	state.delta.sum = -1;
	state.part = 1;
	state.record.charset = 1;
	state.record.line = 1;
	state.summary = 1;
	initfile(&file, (char*)0, 0);
	state.tmp.file = pathtemp((char*)0, (char*)0, "pax");
	idname = 0;
	lastmap = 0;
	while (i = optget(argv, "b:[blocking]f:[archive]kmors:[/old/new/[pg]]vwx:[format]z:[base]LP?R:[record-control]e:[filter]hnD#[debug]T#[test]cdilpat:[tape]uyB:[vol-max]CM:[vol-message]SU:[user]VX [<file-list | <archive] [file ... [directory]] | [pattern ...]")) switch (i)
	{

	/* basic options */

	case 'b':
		state.blocksize = strton(opt_arg, &p, BLOCKSIZE);
		if (*p) error(3, "%s: invalid block size", opt_arg);
		if (state.blocksize < MINBLOCK) error(3, "block size must be at least %d", MINBLOCK);
		if (state.blocksize & (BLOCKSIZE - 1)) error(1, "block size should probably be a multiple of %d", BLOCKSIZE);
		break;
	case 'f':
		if (state.file) error(1, "%s overrides %s", opt_arg, state.file);
		state.file = opt_arg;
		break;
	case 'm':
		state.modtime = 0;
		break;
	case 'o':
		state.owner = 1;
		break;
	case 'r':
		state.operation |= IN;
		break;
	case 's':
		s = opt_arg;
		if (!(n = *s++)) error(3, "empty substitution");
		while (*s && *s != n)
			if (*s++ == '\\' && !*s++)
				error(3, "`\\' cannot terminate lhs of substitution");
		if (*s != n) error(3, "missing `%c' delimiter for lhs of substitution", n);
		*s++ = 0;
		if (!(mp = allocate(struct maplist))) error(3, "not enough space for substitution");
		mp->re = recomp(opt_arg + 1, RE_EDSTYLE|RE_MATCH);
		mp->into = s;
		while (*s && *s != n)
			if (*s++ == '\\' && !*s++)
				error(3, "`\\' cannot terminate rhs of substitution");
		if (*s != n) error(3, "missing `%c' delimiter for rhs of substitution", n);
		*s++ = 0;
		while (*s) switch (*s++)
		{
		case 'g':
			mp->flags |= RE_ALL;
			break;
		case 'l':
			mp->flags |= RE_LOWER;
			break;
		case 'p':
			mp->flags |= RE_VERBOSE;
			break;
		case 'u':
			mp->flags |= RE_UPPER;
			break;
		default:
			error(3, "extra character%s `%s' after substitution", *s ? "s" : "", s - 1);
			break;
		}
		if (lastmap) lastmap = lastmap->next = mp;
		else state.maps = lastmap = mp;
		break;
	case 'v':
		state.verbose = 1;
		break;
	case 'w':
		state.operation |= OUT;
		break;
	case 'x':
		if ((state.formatout = getformat(opt_arg)) < 0) error(3, "%s: unknown archive format", opt_arg);
		break;
	case 'R':
		s = opt_arg;
		do
		{
			if (t = strchr(s, ',')) *t++ = ',';
			switch (*s++)
			{
			case 'c':
				state.record.charset = 0;
				break;
			case 'f':
				state.record.format = *s;
				break;
			case 'l':
				state.record.line = 0;
				break;
			case 'm':
				state.record.pattern = s;
				break;
			case 'p':
				state.record.pad = 1;
				break;
			case 's':
				state.record.size = strton(s, &p, 0);
				if (*p) error(3, "%s: invalid record size", s);
				break;
			case 'v':
				(void)strncpy(state.id.volume, s, sizeof(state.id.volume) - 1);
				state.id.volume[sizeof(state.id.volume) - 1] = 0;
				(void)strupper(state.id.volume);
				break;
#if DEBUG
			case 'B':
				if (!*s) state.in.blok = state.out.blok = 1;
				else while (*s) switch (*s++)
				{
				case 'i':
					state.in.blok = 1;
					break;
				case 'o':
					state.out.blok = 1;
					break;
				default:
					error(3, "-RB[io] expected", *(s - 1));
				}
				break;
#endif
			case 'U':
				if (!*s) state.out.unblocked = state.out.unblocked = 1;
				else while (*s) switch (*s++)
				{
				case 'i':
					state.in.unblocked = 1;
					break;
				case 'o':
					state.out.unblocked = 1;
					break;
				default:
					error(3, "-RU[io] expected", *(s - 1));
				}
				break;
			default:
				error(3, "invalid tape option %c", *(s - 1));
			}
		} while (s = t);
		break;

	/* extended options */

	case 'e':
		state.filter = opt_arg;
		break;
	case 'h':
		state.summary = 0;
		break;
	case 'k':
		state.keepgoing = 1;
		break;
	case 'n':
		state.exact = 1;
		break;
	case 'z':
		if (streq(opt_arg, FILE_DEFAULT)) state.delta.op |= COMPRESS;
		else state.delta.base = opt_arg;
		break;
	case 'D':
		state.trace = -opt_num;
		break;
	case 'L':
		state.ftwflags &= ~FTW_PHYSICAL;
		break;
	case 'P':
		state.ftwflags |= FTW_PHYSICAL;
		break;
	case 'T':
		state.test |= opt_num;
		break;

	/* tar compatibility options */

	case 'a':
		state.append = 1;
		break;
	case 't':
		if (state.file) error(1, "%s overrides %s", opt_arg, state.file);
		state.file = strtape(opt_arg, &p);
		if (*p) error(3, "%s: invalid tape unit specification", opt_arg);
		break;
	case 'u':
		state.update = 1;
		break;
	case 'y':
		state.verify = 1;
		state.yesno = 1;
		break;

	/* cpio compatibility options */

	case 'c':
		state.matchsense = 0;
		break;
	case 'd':
		state.intermediate = 0;
		break;
	case 'i':
		state.verify = 1;
		break;
	case 'l':
		state.linkf = link;
		break;
	case 'p':
		state.acctime = 1;
		break;
	case 'B':
		state.maxout = strton(opt_arg, &p, 0);
		if (*p) error(3, "%s: invalid block count", opt_arg);
		break;
	case 'C':
		state.complete = 1;
		break;
	case 'M':
		eomprompt = opt_arg;
		break;
	case 'S':
		state.linkf = putsymlink;
		break;
	case 'U':
		idname = opt_arg;
		break;
	case 'V':
		state.drop = 1;
		break;
	case 'X':
		state.xdev = 1;
		break;

	/* option errors */

	case '?':
		error(ERROR_USAGE|4, opt_arg);
		break;
	case ':':
		error(2, opt_arg);
		break;

	}
	argv += opt_index;
	argc -= opt_index;
	if (state.errors) error(ERROR_USAGE|4, optusage((char*)0));
	if (!state.operation)
	{
		state.operation = IN;
		state.list = 1;
	}
	state.statf = (state.ftwflags & FTW_PHYSICAL) ? lstat : lpstat;
	if (state.delta.base) state.delta.op |= (state.operation & IN) ? IN : OUT;
	if (state.formatout < 0) state.formatout = OUT_DEFAULT;
	else switch (state.operation)
	{
	case OUT:
		if (state.append) error(1, "format ignored for archive append");
		break;
	case (IN|OUT):
		if (!(state.delta.op & (IN|OUT))) state.delta.op |= (state.delta.op & COMPRESS) ? IN : CONVERT;
		break;
	default:
		error(1, "format ignored for archive read");
		state.formatout = OUT_DEFAULT;
		break;
	}
	switch (state.operation)
	{
	case IN|OUT:
		if (state.append) error(1, "append ignored for pass mode");
		if (!(state.delta.op & COMPRESS) && state.file)
		{
			error(1, "%s: file name ignored for pass mode", state.file);
			break;
		}
		/*FALLTHROUGH*/
	case IN:
		if (state.operation == IN && (state.delta.op & (COMPRESS|IN)) == COMPRESS) error(1, "compress ignored for archive read");
		if (state.append) error(1, "append ignored for archive read");
		if (!state.file || streq(state.file, FILE_DEFAULT)) state.file = definput;
		else
		{
			close(0);
			if (open(state.file, 0) != 0) error(ERROR_SYSTEM|3, "%s: cannot read", state.file);
		}
		break;
	case OUT:
		if (state.update) state.append = 1;
		if (!state.file || streq(state.file, FILE_DEFAULT))
		{
			if (state.append) error(3, "archive pathname must be specified for append");
			state.file = defoutput;
		}
		else
		{
			close(1);
			if (state.append)
			{
				if (state.delta.op) error(3, "cannot append delta archives");
				if (open(state.file, 2) != 1) error(ERROR_SYSTEM|3, "%s: cannot append", state.file);
			}
			else if (creat(state.file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 1) error(ERROR_SYSTEM|3, "%s: cannot write", state.file);
		}
		break;
	}
	if (!(state.linktab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(struct fileid), HASH_name, "links", 0)))
		error(3, "cannot allocate hard link table");
	if ((state.operation & IN) && !state.list && !(state.restore = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_name, "restore", 0)))
		error(3, "cannot allocate directory table");
	if (state.modtime) state.modemask = 0;
	if (state.owner || idname)
	{
		if (state.operation & IN)
		{
			state.owner = 1;
			state.modemask = 0;
			if (idname)
			{
				if ((state.setuid = struid(idname)) < 0 || (state.setgid = strgid(idname)) < 0)
					error(3, "%s: invalid user name", idname);
				state.flags |= SETIDS;
			}
		}
		else error(1, "ownership assignment ignored on archive write");
	}
	if (state.verify) interactive();
	state.modemask = ~state.modemask & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
	for (i = 0; i < elements(signals); i++)
		if (signal(signals[i], interrupt) == SIG_IGN)
			(void)signal(signals[i], SIG_IGN);
#if DEBUG
	message(-1, "%s", operations());
#endif
	switch (state.operation)
	{
	case IN:
		if (*argv)
		{
			state.patterns = initmatch(argv);
			if (state.exact) state.matchsense = 1;
		}
		else if (state.exact) error(3, "file arguments expected");
		binit();
		(void)getcwd(state.pwd, PATH_MAX);
		state.pwdlen = strlen(state.pwd);
		state.pwd[state.pwdlen++] = '/';
		if (state.delta.op & IN) deltabase();
		copyin();
		if (state.exact)
		{
			argv = state.patterns;
			while (s = *argv++)
				if (*s) error(2, "%s: file not found in archive", s);
		}
		break;

	case OUT:
		if (*argv) state.files = argv;
		binit();
		if (!state.maxout && state.complete)
			error(3, "maximum block count required");
		if (state.append) append();
		if (state.delta.op & OUT) deltabase();
		putprologue();
		copy(copyout);
		if (state.delta.op & OUT) deltadelete();
		putepilogue();
		break;

	case (IN|OUT):
		if (state.delta.op)
		{
			if (*argv) state.patterns = initmatch(argv);
			state.operation = OUT;
			binit();
			state.operation = IN|OUT;
			if (state.delta.op & IN) deltabase();
			deltapass();
		}
		else
		{
			if (--argc < 0)
			{
				error(2, "destination directory required for pass mode");
				error(ERROR_USAGE|4, optusage((char*)0));
			}
			state.destination = argv[argc];
			argv[argc] = 0;
			if (*argv) state.files = argv;
			if (state.blocksize) error(1, "blocking ignored in pass mode");
			binit();
			if (state.record.size) error(1, "record size ignored in pass mode");

			/*
			 * initialize destination dir
			 */

			(void)pathcanon(state.destination);
			if (stat(state.destination, file.st) || (file.st->st_mode & S_IFMT) != S_IFDIR)
				error(3, "%s: destination must be a directory", state.destination);
			state.dev = file.st->st_dev;
			(void)strcpy(state.pwd, state.destination);
			state.pwdlen = strlen(state.pwd);
			state.pwd[state.pwdlen++] = '/';
			copy(copyinout);
		}
		break;
	}
	finish(0);
}

/*
 * print number of blocks actually copied and exit
 */

void
finish(code)
int	code;
{
	register char*	x1 = &state.tmp.buffer[0];
	register char*	x2 = &state.tmp.buffer[state.buffersize / 2];
	register long	n;

	(void)remove(state.tmp.file);
	if (state.restore) (void)hashwalk(state.restore, 0, restore);
	fflush(stdout);
	if (state.dropcount)
	{
		fprintf(stderr, "\n");
		fflush(stderr);
	}
	if (state.summary)
	{
		switch (state.operation)
		{
		case OUT:
			n = state.out.count + state.out.offset;
			break;
		case IN|OUT:
			n = state.out.count + state.out.offset;
			if (n || !(state.delta.op & IN)) break;
			/*FALLTHROUGH*/
		case IN:
			n = state.in.count + state.in.offset;
			break;
		}
		if (state.entries)
		{
			if (state.volume > 1) (void)sprintf(x1, ", %d volumes", state.volume);
			else *x1 = 0;
			if (state.volume > 0 && state.part > state.volume) (void)sprintf(x2, ", %d parts", state.part - state.volume + 1);
			else *x2 = 0;
			n = (n + BLOCKSIZE - 1) / BLOCKSIZE;
			if (state.verbose) fprintf(stderr, "%d file%s, %ld block%s%s%s\n", state.selected, state.selected == 1 ? "" : "s", n, n == 1 ? "" : "s", x1, x2);
			else fprintf(stderr, "%ld block%s%s%s\n", n, n == 1 ? "" : "s", x1, x2);
		}
	}
	fflush(stderr);
	if (state.interrupt)
	{
		(void)signal(state.interrupt, SIG_DFL);
		kill(getpid(), state.interrupt);
		pause();
	}
	exit(code ? code : state.errors != 0);
}

/*
 * clean up dir info before exit
 */

static void
interrupt(sig)
int	sig;
{
	(void)signal(sig, SIG_IGN);
	sigunblock(sig);
	switch (sig)
	{
	case SIGINT:
	case SIGQUIT:
		fprintf(stderr, "\n");
		break;
	}
	state.interrupt = sig;
	finish(1);
}

/*
 * error hook for re lib
 */

void
reerror(msg)
char*	msg;
{
	error(3, "substitution: %s", msg);
}
0707070000000000171006440044230044230000010000000474560470700002100000044273pax.hUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax common definitions
 */

#include <ls.h>
#include <stdio.h>
#include <sig.h>
#include <ftwalk.h>
#include <ctype.h>
#include <limits.h>
#include <hash.h>
#include <re.h>
#include <error.h>
#include <time.h>
#include <swap.h>
#include <align.h>

#ifndef PATH_MAX
#define PATH_MAX	1024
#endif

#ifndef S_IFIFO
#define S_IFIFO		0010000
#endif
#ifndef S_IFLNK
#define S_IFLNK		0120000
#endif
#ifndef S_IFSOCK
#define S_IFSOCK	0140000
#endif

#define PANIC		ERROR_PANIC|ERROR_SOURCE,__FILE__,__LINE__

#define allocate(x)	(x*)calloc(1,sizeof(x))
#define bcount(io)	(io.last-io.next)
#define bsave()		(state.backup=state.in)
#define elements(x)	(sizeof(x)/sizeof(x[0]))
#define message		if(state.trace<0)error
#define streq(a,b)	((*(a))==(*(a))&&!strcmp(a,b))
#define strneq(a,b,n)	((*(a))==(*(a))&&!strncmp(a,b,n))
#define round(x,y)	(((long)(x)+((y)-1))&~((y)-1))

#define RE_VERBOSE	(1<<RE_EXTERNAL)/* list re substitution		*/

/*
 * info per archive format
 *
 * NOTES:
 *
 *	format indices must agree with format[] table below
 *
 *	the *_NAMESIZE maximum file name lengths include the trailing \0
 *
 *	PATH_MAX must be >= max(*_NAMESIZE)
 */

#define ALAR		0
#define ALAR_NAME	"ansi"
#define ALAR_REGULAR	4
#define ALAR_SPECIAL	ALAR_REGULAR
#define ALAR_LABEL	2048
#define ALAR_HEADER	80
#define ALAR_VARHDR	9
#define ALAR_NAMESIZE	17
#define ALAR_RECSIZE	(BLOCKSIZE>>2)
#define ALAR_RECFORMAT	'D'
#define ALAR_ALIGN	0
#define ALAR_FLAGS	IN|OUT
#define ALAR_ID		"SLT"

#define BINARY		1
#define BINARY_NAME	"binary"
#define BINARY_REGULAR	DEFBUFFER
#define BINARY_SPECIAL	DEFBLOCKS
#define BINARY_HEADER	26
#define BINARY_NAMESIZE	256
#define BINARY_ALIGN	2
#define BINARY_FLAGS	IN|OUT

struct binary_header
{
	short		magic;
	unsigned short	dev;
	unsigned short	ino;
	unsigned short	mode;
	unsigned short	uid;
	unsigned short	gid;
	short		links;
	unsigned short	rdev;
	unsigned short	mtime[2];
	short		namesize;
	unsigned short	size[2];
};

#define CPIO		2
#define CPIO_NAME	"cpio"
#define CPIO_REGULAR	DEFBUFFER
#define CPIO_SPECIAL	DEFBLOCKS
#define CPIO_HEADER	76
#define CPIO_MAGIC	070707
#define CPIO_EXTENDED	1
#define CPIO_TRAILER	"TRAILER!!!"
#define CPIO_TRUNCATE(x) ((x)&0177777)
#define CPIO_NAMESIZE	256
#define CPIO_ALIGN	0
#define CPIO_FLAGS	IN|OUT

#define CPIO_FMT	0170000
#define CPIO_FIFO	0010000
#define CPIO_DIR	0040000
#define CPIO_CHR	0020000
#define CPIO_BLK	0060000
#define CPIO_REG	0100000
#define CPIO_LNK	0120000
#define CPIO_SOCK	0140000

#define IBMAR		3
#define IBMAR_NAME	"ibm"
#define IBMAR_REGULAR	ALAR_REGULAR
#define IBMAR_SPECIAL	ALAR_SPECIAL
#define IBMAR_ALIGN	ALAR_ALIGN
#define IBMAR_FLAGS	IN|OUT

/*
 * NOTE: since USTAR is an extension of TAR they share the same header
 */

#define TAR		4
#define TAR_NAME	"tar"
#define TAR_REGULAR	DEFBUFFER
#define TAR_SPECIAL	DEFBLOCKS
#define TAR_HEADER	TBLOCK
#define TAR_NAMESIZE	100
#define TAR_ALIGN	BLOCKSIZE
#define TAR_FLAGS	IN|OUT

#define header		tar_header_info
#define hblock		tar_header_block
#include <tar.h>
#undef header
#undef hblock

#define USTAR		5
#define USTAR_NAME	TMAGIC
#define USTAR_REGULAR	TAR_REGULAR
#define USTAR_SPECIAL	TAR_SPECIAL
#define USTAR_HEADER	TAR_HEADER
#define USTAR_ALIGN	TAR_ALIGN
#define USTAR_FLAGS	IN|OUT

#define tar_header	tar_header_block.dbuf
#define tar_block	tar_header_block.dummy

/*
 * s5r4 expanded cpio(5) char format
 * pax won't even bother with the binary counterpart
 */

#define ASC		6
#define ASC_NAME	"asc"
#define ASC_REGULAR	CPIO_REGULAR
#define ASC_SPECIAL	CPIO_SPECIAL
#define ASC_HEADER	110
#define ASC_MAGIC	070701
#define ASC_NAMESIZE	1024
#define ASC_ALIGN	4
#define ASC_FLAGS	IN|OUT

/*
 * s5r4 expanded cpio(5) char format with checksum
 * (misnamed ``crc'' -- the check is a simple sum of the header)
 * pax won't even bother with the binary counterpart
 */

#define ASCHK		7
#define ASCHK_NAME	"aschk"
#define ASCHK_REGULAR	ASC_REGULAR
#define ASCHK_SPECIAL	ASC_SPECIAL
#define ASCHK_MAGIC	070702
#define ASCHK_ALIGN	ASC_ALIGN
#define ASCHK_FLAGS	IN|OUT

/*
 * backup saveset format
 */

#define SAVESET		8
#define SAVESET_NAME	"vmsbackup"
#define SAVESET_REGULAR	0
#define SAVESET_SPECIAL	0
#define SAVESET_ALIGN	0
#define SAVESET_FLAGS	IN
#define SAVESET_ID	"DECVMS"
#define SAVESET_IMPL	"BACKUP"

#define BLKHDR_SIZE	256
#define BLKHDR_hdrsiz	0
#define BLKHDR_blksiz	40

#define FILHDR_SIZE	4
#define FILHDR_MAGIC	257
#define FILHDR_namelen	128
#define FILHDR_size	0
#define FILHDR_type	2
#define FILHDR_data	4

#define FILATT_blocks	10
#define FILATT_frag	12
#define FILATT_recatt	1
#define FILATT_recfmt	0
#define FILATT_reclen	2
#define FILATT_recvfc	15

#define RECHDR_SIZE	16
#define RECHDR_size	0
#define RECHDR_type	2

#define REC_file	3
#define REC_vbn		4

/*
 * the proposed POSIX IEEE Std 1003.1B-1990 interchange format
 */

#define PAX		9
#define PAX_NAME	"posix"
#define PAX_REGULAR	10
#define PAX_SPECIAL	10
#define PAX_ALIGN	0
#define PAX_FLAGS	IN|OUT
#define PAX_RECSIZE	(state.blocksize)
#define PAX_RECFORMAT	'F'
#define PAX_ID		"POSIX1"

/*
 * portable (object) archive
 */

#define PORTAR		10
#define PORTAR_NAME	"portarch"
#define PORTAR_REGULAR	0
#define PORTAR_SPECIAL	0
#define PORTAR_HEADER	sizeof(portar_header)
#define PORTAR_MAG	"!<arch>\n"
#define PORTAR_MAGSIZ	8
#define PORTAR_SYM	"(/|__________E???_)*"
#define PORTAR_END	"`\n"
#define PORTAR_ENDSIZ	2
#define PORTAR_TERM	'/'
#define PORTAR_ALIGN	2
#define PORTAR_FLAGS	IN

struct portar_header
{
	char	ar_name[16];
	char	ar_date[12];	/* left-adj; decimal char*; blank fill	*/
	char	ar_uid[6];	/*	"				*/
	char	ar_gid[6];	/*	"				*/
	char	ar_mode[8];	/* left-adj; octal char*; blank fill	*/
	char	ar_size[10];	/* left-adj; decimal char*; blank fill	*/
	char	ar_fmag[2];	/* PORTAR_END				*/
};

/*
 * ranlib (object) archive -- almost PORTAR
 */

#define RANDAR		11
#define RANDAR_NAME	"randarch"
#define RANDAR_REGULAR	0
#define RANDAR_SPECIAL	0
#define RANDAR_SYM	"(__.SYMDEF|__________E???X)*"
#define RANDAR_TERM	' '
#define RANDAR_ALIGN	PORTAR_ALIGN
#define RANDAR_FLAGS	IN

#define FILE_DEFAULT	"-"		/* default in/out/delta file	*/

#define IN_DEFAULT	CPIO		/* first getheader() state	*/
#define OUT_DEFAULT	CPIO		/* default output format	*/

#define IN		(1<<0)		/* copy in			*/
#define OUT		(1<<1)		/* copy out			*/

#define COLLECT		(1<<2)		/* collect input member info	*/
#define COMPRESS	(1<<3)		/* output is self delta		*/
#define CONVERT		(1<<4)		/* convert archive format	*/

#define SETIDS		(1<<0)		/* set explicit uid and gid	*/

#define INFO_SEP	'!'		/* info header field separator	*/

#define ID		"PAX"		/* info header id		*/
#define IDLEN		(sizeof(ID)-1)	/* strlen(ID)			*/

#define IMPLEMENTATION	"ATTPAX1"	/* implementation id		*/

#define TYPE_COMPRESS	'C'		/* compress encoding type	*/
#define TYPE_DELTA	'D'		/* delta encoding type		*/
#define VERSION		'A'		/* encoding type version	*/

#define DELTA_create	'c'		/* delta create data op		*/
#define DELTA_delete	'd'		/* delta delete data op		*/
#define DELTA_pass	'p'		/* delta pass pseudo op		*/
#define DELTA_update	'u'		/* delta update data op		*/
#define DELTA_verify	'v'		/* delta verify data op		*/

#define DELTA_LO(c)	((c)&0xffff)	/* lo order checksum bits	*/
#define DELTA_HI(c)	DELTA_LO(c>>16)	/* hi order checksum bits	*/

#define NOLINK		0		/* not a link			*/
#define HARDLINK	'1'		/* hard link to previous entry	*/
#define SOFTLINK	'2'		/* soft link to previous entry	*/

#define BLOCKSIZE	512		/* block size			*/
#define IOALIGN		BOUND1		/* io buffer alignment		*/
#define MINBLOCK	1		/* smallest block size		*/
#define DEFBLOCKS	20		/* default blocking		*/
#define DEFBUFFER	16		/* default io buffer blocking	*/
#define MAXBLOCKS	40		/* largest blocking		*/
#define MAXUNREAD	BLOCKSIZE	/* max bunread() count		*/

struct bio				/* buffered io info		*/
{
	char*		next;		/* next char pointer		*/
	char*		last;		/* last char+1 pointer		*/
	char*		buffer;		/* io buffer			*/
	unsigned long	count;		/* char transfer count		*/
	unsigned long	offset;		/* volume offset		*/
	unsigned int	blocked:1;	/* blocked device io		*/
	unsigned int	blok:1;		/* BLOK io file			*/
	unsigned int	blokflag:1;	/* io file BLOK flag		*/
	unsigned int	empty:1;	/* last read was empty		*/
	unsigned int	eof:1;		/* hit EOF			*/
	unsigned int	unblocked:1;	/* set unblocked device io	*/
};

struct fileinfo				/* common internal file info	*/
{
	int		magic;		/* header magic number		*/
	char*		id;		/* archive file id		*/
	char*		name;		/* archive file name		*/
	int		namesize;	/* name size with null byte	*/
	char*		path;		/* local file name for reading	*/
	struct
	{
	int		op;		/* op				*/
	struct deltainfo* base;		/* base file pointer		*/
	long		size;		/* target file size		*/
	int		version;	/* encoding type version	*/
	}		delta;		/* delta info			*/
	struct stat*	st;		/* stat() info from ftwalk()	*/
	int		type;		/* st_mode type (S_IFMT bits)	*/
	int		linktype;	/* NOLINK, HARDLINK, SOFTLINK	*/
	char*		linkname;	/* link to this path		*/
	int		linknamesize;	/* linkname size with null byte	*/
	char*		uidname;	/* user id name			*/
	char*		gidname;	/* group id name		*/
	struct
	{
	int		blocks;		/* io block count		*/
	int		format;		/* format			*/
	int		section;	/* file section number		*/
	}		record;		/* record format info		*/
	long		checksum;	/* checksum			*/
	unsigned int	skip:1;		/* skip this entry		*/
};

struct deltainfo			/* delta entry info		*/
{
	struct fileinfo* info;		/* deltapass() file info	*/
	short		dev;		/* dev				*/
	short		ino;		/* ino				*/
	long		mtime;		/* modify time			*/
	long		offset;		/* data offset			*/
	long		size;		/* data size			*/
	unsigned int	mark:1;		/* visit mark			*/
};

struct fileid				/* unique file identifier	*/
{
	int		dev;		/* device			*/
	int		ino;		/* inode			*/
};

struct formatinfo			/* format info			*/
{
	char*		name;		/* name				*/
	int		regular;	/* default regular blocking	*/
	int		special;	/* default special blocking	*/
	int		align;		/* trailer alignment		*/
	int		flags;		/* io info			*/
};

struct linkinfo				/* link info			*/
{
	char*		name;		/* name				*/
	int		namesize;	/* name size with null byte	*/
	struct fileid	id;		/* generated link file id	*/
};

struct maplist				/* file name map list		*/
{
	struct maplist*	next;		/* next in list			*/
	reprogram*	re;		/* compiled match re		*/
	char*		into;		/* map into this		*/
	int		flags;		/* resub() flags		*/
};

struct postinfo				/* post processing restoration	*/
{
	time_t		mtime;		/* modify time			*/
	int		mode;		/* permissions			*/
	int		uid;		/* user id			*/
	int		gid;		/* group id			*/
};

union integral				/* byte|half swap probe		*/
{
	unsigned long	l;
	unsigned short	s[2];
	unsigned char	c[4];
};

struct stateinfo			/* program state		*/
{
	int		acctime;	/* reset file access times	*/
	int		append;		/* append -- must be 0 or 1 !!!	*/
	struct bio	backup;		/* backup() position		*/
	int		blocksize;	/* explicit buffer size		*/
	int		buffersize;	/* io buffer size		*/
	int		complete;	/* files completely in volume	*/
	int		current;	/* current file[] index		*/
	struct
	{
	char*		base;		/* base archive for delta	*/
	char*		buffer;		/* temporary buffer		*/
	int		buffersize;	/* temporary buffer size	*/
	long		checksum;	/* archive running checksum	*/
	long		count;		/* delta file count		*/
	int		fd;		/* file read descriptor		*/
	char*		hdr;		/* header pointer		*/
	char*		hdrbuf;		/* header buffer		*/
	long		membersum;	/* archive member checksum	*/
	long		offset;		/* base file offset		*/
	int		op;		/* delta op state		*/
	long		size;		/* base file size		*/
	int		sum;		/* compute input checksum if >0	*/
	HASHTABLE*	tab;		/* entry table			*/
	}		delta;		/* delta sub state		*/
	char*		destination;	/* pass mode destination dir	*/
	dev_t		dev;		/* . device number		*/
	unsigned short	devcnt;		/* dev assignment count		*/
	HASHTABLE*	restore;	/* post proc restoration table	*/
	int		drop;		/* drop a `.' for each file	*/
	int		dropcount;	/* current line drop count	*/
	int		entries;	/* total archive entry count	*/
	int		entry;		/* archive entry number		*/
	int		errors;		/* error count			*/
	int		exact;		/* exact archive read		*/
	char**		files;		/* alternate file name list	*/
	char*		file;		/* io file name			*/
	char*		filter;		/* file output filter command	*/
	int		flags;		/* flags			*/
	int		formatin;	/* input archive format index	*/
	int		formatout;	/* output archive format index	*/
	int		ftwflags;	/* ftwalk() flags		*/
	int		gid;		/* current group id		*/
	struct
	{
	char		volume[7];	/* volume id			*/
	char		format[7];	/* format id			*/
	char		implementation[8];/* implementation id		*/
	char		owner[15];	/* owner id			*/
	}		id;		/* id strings (including '\0')	*/
	struct bio	in;		/* buffered input info		*/
	unsigned short	inocnt;		/* ino assignment count		*/
	int		intermediate;	/* make intermediate dirs	*/
	int		interrupt;	/* this signal caused exit	*/
	int		keepgoing;	/* keep going on error		*/
	int		(*linkf)();	/* use when possible for -rw	*/
	HASHTABLE*	linktab;	/* hard link table		*/
	int		list;		/* full file trace		*/
	struct maplist*	maps;		/* file name maps		*/
	int		matchsense;	/* pattern match sense		*/
	unsigned long	maxout;		/* max volume/part output count	*/
	int		modemask;	/* and with mode for chmod()	*/
	int		modtime;	/* retain mtime			*/
	int		newer;		/* append only if newer		*/
	long		offset;		/* relative archive byte offset	*/
	int		operation;	/* IN|OUT operation mode	*/
	struct bio	out;		/* buffered output info		*/
	int		owner;		/* set owner info		*/
	int		part;		/* device change count		*/
	char**		patterns;	/* name match patterns		*/
	char		pwd[PATH_MAX];	/* full path of .		*/
	int		pwdlen;		/* pwd length sans null		*/
	time_t		present;	/* for long file listing	*/
	struct
	{
	int		charset;	/* convert record charset	*/
	struct fileinfo* file;		/* current output file		*/
	int		format;		/* output format		*/
	int		line;		/* convert records<->lines	*/
	int		pad;		/* pad output record blocks	*/
	char*		pattern;	/* format match pattern		*/
	int		offset;		/* data buffer offset		*/
	int		size;		/* io size			*/
	}		record;		/* record info			*/
	FILE*		rtty;		/* tty file read pointer	*/
#if SAVESET
	struct
	{
	char*		block;		/* current block		*/
	int		blocksize;	/* max block size		*/
	char*		bp;		/* block pointer		*/
	int		recatt;		/* record attributes		*/
	int		recfmt;		/* record format		*/
	int		reclen;		/* record length		*/
	int		recvfc;		/* record fixed control length	*/
	int		lastsize;	/* size of last record		*/
	time_t		time;		/* backup time			*/
	}		saveset;	/* backup saveset state		*/
#endif
	int		selected;	/* number of selected files	*/
	int		setgid;		/* set file gid to this value	*/
	int		setuid;		/* set file uid to this value	*/
	int		(*statf)();	/* -L=lpstat() -P=lstat()	*/
	int		summary;	/* output summary info		*/
	int		swap;		/* {BYTE,HALF} swap operation	*/
	int		test;		/* debug test bits		*/
	struct
	{
	char*		buffer;		/* temporary buffer		*/
	int		buffersize;	/* temporary buffer size	*/
	char*		file;		/* tmp file name		*/
	}		tmp;		/* temporary stuff		*/
	int		trace;		/* error() debug trace level	*/
	int		uid;		/* current user id		*/
	int		update;		/* copy file only if newer	*/
	int		verbose;	/* trace files when acted upon	*/
	int		verify;		/* verify action on file	*/
	int		volume;		/* archive volume number	*/
	FILE*		wtty;		/* tty file write pointer	*/
	int		xdev;		/* don't cross device boundaries*/
	int		yesno;		/* interactive answer is yes/no	*/
};

extern char*			definput;
extern char*			defoutput;
extern char*			eomprompt;
extern char			uidname[];
extern char			gidname[];

extern struct formatinfo	format[];

extern struct fileinfo		file;
extern struct stateinfo		state;

extern char			alar_header[];
extern struct binary_header	binary_header;
extern union tar_header_block	tar_header_block;
extern struct portar_header	portar_header;

extern void		finish();
extern void		usage();
extern void		copyin();
extern void		copy();
extern int		copyout();
extern void		fileout();
extern void		filein();
extern void		filepass();
extern void		fileskip();
extern int		copyinout();
extern void		append();
extern void		initdelta();
extern void		deltabase();
extern int		deltafd();
extern void		deltaverify();
extern void		deltapass();
extern void		deltadelete();
extern int		getprologue();
extern void		putprologue();
extern void		getepilogue();
extern void		putepilogue();
extern int		getheader();
extern void		getdeltaheader();
extern void		adddelstr();
extern void		adddelnum();
extern void		putheader();
extern void		putdeltaheader();
extern void		gettrailer();
extern void		puttrailer();
extern int		getlabel();
extern void		putlabels();
#if SAVESET
extern int		getsaveset();
extern int		mkbyte();
extern int		mkhalf();
extern long		mklong();
#endif
extern int		getfile();
extern int		selectfile();
extern int		getnumber();
extern int		getformat();
extern int		verify();
extern int		openin();
extern int		openout();
extern int		validout();
extern int		addlink();
extern char*		map();
extern void		listentry();
extern void		getidnames();
extern void		setidnames();
extern void		initfile();
extern char**		initmatch();
extern int		match();
extern int		dirprefix();
extern char*		strlower();
extern char*		strupper();
extern void		swap();
extern void		setfile();
extern void		settime();
extern int		missdir();
extern int		restore();
extern long		cpio_long();
extern void		cpio_short();
extern int		cpio_mode();
extern int		tar_checksum();
extern long		asc_checksum();
extern long		getlabnum();
extern char*		getlabstr();
extern void		binit();
extern void		balloc();
extern int		bread();
extern void		bunread();
extern char*		bget();
extern void		backup();
extern void		bflushin();
extern void		bflushout();
extern void		bwrite();
extern void		bput();
extern void		interactive();
extern void		newio();
extern int		dodelta();
extern int		doupdate();
#if DEBUG
extern char*		operations();
#endif

extern char*		ctime();
extern char*		fgetline();
extern char*		fmtmode();
extern int		fstat();
extern int		link();
extern long		lseek();
extern int		lstat();
extern int		lpstat();
extern char*		malloc();
extern char*		memcpy();
extern char*		memdup();
extern unsigned long	memsum();
extern char*		pathcanon();
extern char*		pathtemp();
extern int		remove();
extern int		stat();
extern char*		strcpy();
extern char*		strncpy();
extern char*		strchr();
extern char*		strrchr();
extern char*		strtape();
extern long		strtol();
extern long		strton();
extern char*		strdup();
extern int		strmatch();
extern int		struid();
extern int		strgid();
extern int		getsymlink();
extern int		putsymlink();

#if __STDC__

extern FILE*		fdopen(int, char*);

#endif
0707070000000000201006440044230044230000010000000472663607400002300000000625stubs.cUgsfGgsf/*
 * G. S. Fowler
 * AT&T Bell Laboratories
 *
 * pax libdelta stubs for external distribution
 */

#include "pax.h"

/*ARGSUSED*/
int
delta(old, oldsize, new, newsize, fd)
char*	old;
long	oldsize;
char*	new;
long	newsize;
int	fd;
{
	return(-1);
}

/*ARGSUSED*/
int
update(oldfd, oldoffset, dfd, wfd, rfd, newoffset)
int	oldfd;
long	oldoffset;
int	dfd;
int	wfd;
int	rfd;
long	newoffset;
{
	return(-1);
}
0707070000000000211006440044230044230000010000000441674764000002200000003461tar.shUgsfGgsf:
# Glenn Fowler
# AT&T Bell Laboratories
# @(#)tar.sh (ulysses!gsf) 04/06/89
#
# tar -> pax interface script
#

command=tar
usage="
Usage: $command c[vwfbB[[0-9][hlm]]] [pathname ...]
       $command r[vwfbB[[0-9][hlm]]] [files ...]
       $command t[vwfbB[[0-9][hlm]]
       $command u[vwfbB[[0-9][hlm]]] [pathname ...]
       $command x[vwfblmB[[0-9][hlm]]] [pathname ...]"

case $1 in
*[tx]*)		mode="-r" ;;
*[cru]*)	mode="-w" ;;
*)		echo "$command: one of crtux must be specified$usage" >&2; exit 1 ;;
esac
options="-P"
file="-t 0"
list=""
r_ok="1"
w_ok="1"
arg=$1
lastopt=""
shift
for opt in `echo '' $arg | sed -e 's/^ -//' -e 's/./& /g'`
do	case $opt in
	[0-9])	file="-t $opt" ;;
	[hlm])	case $lastopt in
		[0-9])	file="${file}$opt" ;;
		*)	case $opt in
			h)	options="$options -L" ;;
			l)	;;
			m)	r_ok="" options="$options -$opt" ;;
			esac
			;;
		esac
		;;
	[v])	options="$options -$opt" ;;
	b)	case $# in
		0)	echo "$command: blocking factor argument expected$usage" >&2; exit 1 ;;
		esac
		options="$options -b ${1}b"
		shift
		;;
	c)	r_ok="" ;;
	f)	case $# in
		0)	echo "$command: file name argument expected$usage" >&2; exit 1 ;;
		esac
		case $1 in
		-)	file="" ;;
		*)	file="-f $1" ;;
		esac
		shift
		;;
	r)	r_ok="" options="$options -a" ;;
	t)	w_ok="" list="1" ;;
	u)	r_ok="" options="$options -u" ;;
	w)	options="$options -y" ;;
	x)	w_ok="" ;;
	B)	options="$options -b 10k" ;;
	*)	echo "$command: invalid option -$opt$usage" >&2; exit 1 ;;
	esac
	lastopt=$opt
done
case $mode in
-r)	case $r_ok in
	"")	echo "$command: options inconsistent with archive read" >&2; exit 1 ;;
	esac
	;;
-w)	case $w_ok in
	"")	echo "$command: options inconsistent with archive write" >&2; exit 1 ;;
	esac
	case $# in
	0)	set - "." ;;
	esac
	options="$options -x tar"
	;;
esac
case $list in
"1")	mode="" ;;
esac
pax $mode $options $file "$@"
0707070000000000221006440044230044230000010000000475460106100002300000020027MamfileUgsfGgsfnote # # make abstract machine file generated from Makefile # #
setv AS as
setv ASFLAGS
setv AR ar
setv ARFLAGS cr
setv CC cc
setv CCFLAGS "-O"
setv CPP "$CC -E"
setv CPIO cpio
setv CPIOFLAGS
setv F77 f77
setv INSTALLROOT $HOME
setv LD ld
setv LDFLAGS 
setv LEX lex
setv LEXFLAGS
setv LPR lpr
setv LPRFLAGS
setv M4FLAGS 
setv MAKE nmake
setv MAKEFLAGS
setv PR pr
setv PRFLAGS
setv TAR tar
setv YACC yacc
setv YACCFLAGS -d
make install
make all
make pax
make bio.o
make bio.c
attr perm
attr scan
make FEATURE/bcopy
exec : generate local FEATURE information for bcopy
.... set -
.... if	test ! -d FEATURE
.... then	rm -rf FEATURE
.... 	mkdir FEATURE
.... fi
.... {
.... echo '/* local info for bcopy */'
.... case "bcopy" in
.... *.*)	f=bcopy
.... 	i=bcopy
.... 	;;
.... *)	f=bcopy
.... 	i=bcopy
.... 	echo "#undef $i
.... 		int $i;" > x.${!-$$}.c
.... 	cmd=:
.... 	opt=
.... 	if	$CC $CCFLAGS   -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1
.... 	then	if	$CC $CCFLAGS   -Bstatic -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1
.... 		then	opt=-Bstatic
.... 		else	cmd=x.${!-$$}.x
.... 		fi
.... 	fi
.... 	echo "#undef $i
.... 		extern int $i();static int ((*i)())=$i;main(){exit(i==(int((*)()))0);}" > x.${!-$$}.c
.... 	if	$CC $CCFLAGS   $opt -c x.${!-$$}.c > /dev/null 2>&1
.... 	then	if	{ $CC $CCFLAGS   $opt -o x.${!-$$}.x x.${!-$$}.o && $cmd ;} > /dev/null 2>&1
.... 		then	echo "#define	_lib_$i 1	/* $i() in default lib(s) */"
.... 		elif	{ $CC $CCFLAGS   $opt -o x.${!-$$}.x x.${!-$$}.o -lm && $cmd ;} > /dev/null 2>&1
.... 		then	echo "#define	_mth_$i 1	/* $i() in math lib */"
.... 		fi
.... 	fi
.... 	;;
.... esac
.... echo "#include \"$f.h\"" > x.${!-$$}.c
.... if	$CC $CCFLAGS   -E x.${!-$$}.c > /dev/null 2>&1
.... then	echo "#define	_hdr_$i 1	/* #include \"$f.h\" ok */"
.... fi
.... echo "#include \"sys/$f.h\"" > x.${!-$$}.c
.... if	$CC $CCFLAGS   -E x.${!-$$}.c > /dev/null 2>&1
.... then	echo "#define	_sys_$i 1	/* #include \"sys/$f.h\" ok */"
.... fi
.... for j in / /usr/
.... do	for i in bin etc ucb
.... 	do	if	test -f $j$i/$f
.... 		then	echo "#define	_bin_$f 1	/* $f in ?(/usr)/(bin|etc|ucb) */"
.... 			break 2
.... 		fi
.... 	done
.... done
.... } > FEATURE/bcopy
.... rm -rf x.${!-$$}.c x.${!-$$}.o x.${!-$$}.x
attr scan
attr impl
done FEATURE/bcopy
make FEATURE/mtio
exec : generate local FEATURE information for mtio
.... set -
.... if	test ! -d FEATURE
.... then	rm -rf FEATURE
.... 	mkdir FEATURE
.... fi
.... {
.... echo '/* local info for mtio */'
.... case "mtio" in
.... *.*)	f=mtio
.... 	i=mtio
.... 	;;
.... *)	f=mtio
.... 	i=mtio
.... 	echo "#undef $i
.... 		int $i;" > x.${!-$$}.c
.... 	cmd=:
.... 	opt=
.... 	if	$CC $CCFLAGS   -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1
.... 	then	if	$CC $CCFLAGS   -Bstatic -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1
.... 		then	opt=-Bstatic
.... 		else	cmd=x.${!-$$}.x
.... 		fi
.... 	fi
.... 	echo "#undef $i
.... 		extern int $i();static int ((*i)())=$i;main(){exit(i==(int((*)()))0);}" > x.${!-$$}.c
.... 	if	$CC $CCFLAGS   $opt -c x.${!-$$}.c > /dev/null 2>&1
.... 	then	if	{ $CC $CCFLAGS   $opt -o x.${!-$$}.x x.${!-$$}.o && $cmd ;} > /dev/null 2>&1
.... 		then	echo "#define	_lib_$i 1	/* $i() in default lib(s) */"
.... 		elif	{ $CC $CCFLAGS   $opt -o x.${!-$$}.x x.${!-$$}.o -lm && $cmd ;} > /dev/null 2>&1
.... 		then	echo "#define	_mth_$i 1	/* $i() in math lib */"
.... 		fi
.... 	fi
.... 	;;
.... esac
.... echo "#include \"$f.h\"" > x.${!-$$}.c
.... if	$CC $CCFLAGS   -E x.${!-$$}.c > /dev/null 2>&1
.... then	echo "#define	_hdr_$i 1	/* #include \"$f.h\" ok */"
.... fi
.... echo "#include \"sys/$f.h\"" > x.${!-$$}.c
.... if	$CC $CCFLAGS   -E x.${!-$$}.c > /dev/null 2>&1
.... then	echo "#define	_sys_$i 1	/* #include \"sys/$f.h\" ok */"
.... fi
.... for j in / /usr/
.... do	for i in bin etc ucb
.... 	do	if	test -f $j$i/$f
.... 		then	echo "#define	_bin_$f 1	/* $f in ?(/usr)/(bin|etc|ucb) */"
.... 			break 2
.... 		fi
.... 	done
.... done
.... } > FEATURE/mtio
.... rm -rf x.${!-$$}.c x.${!-$$}.o x.${!-$$}.x
attr scan
attr impl
done FEATURE/mtio
make pax.h
attr perm
attr scan
attr impl
make ../../../include/tar.h
attr perm
attr scan
attr impl
done ../../../include/tar.h
make ../../../include/align.h
attr perm
attr scan
attr impl
done ../../../include/align.h
make ../../../include/swap.h
attr perm
attr scan
attr impl
done ../../../include/swap.h
make /usr/include/time.h
attr perm
attr scan
attr impl
done /usr/include/time.h
make ../../../include/error.h
attr perm
attr scan
attr impl
done ../../../include/error.h
make ../../../include/re.h
attr perm
attr scan
attr impl
done ../../../include/re.h
make ../../../include/hash.h
attr perm
attr scan
attr impl
done ../../../include/hash.h
make ../../../include/limits.h
attr perm
attr scan
attr impl
done ../../../include/limits.h
make /usr/include/ctype.h
attr perm
attr scan
attr impl
done /usr/include/ctype.h
make ../../../include/ftwalk.h
attr perm
attr scan
attr impl
done ../../../include/ftwalk.h
make ../../../include/sig.h
attr perm
attr scan
attr impl
done ../../../include/sig.h
make ../../../include/ls.h
attr perm
attr scan
attr impl
done ../../../include/ls.h
done pax.h
done bio.c
prev bio.c
setv DEBUG -DDEBUG
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c bio.c
done bio.o
make convert.o
make convert.c
attr perm
attr scan
prev pax.h
done convert.c
prev convert.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c convert.c
done convert.o
make copy.o
make copy.c
attr perm
attr scan
prev pax.h
done copy.c
prev copy.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c copy.c
done copy.o
make pax.o
make pax.c
attr perm
attr scan
prev pax.h
done pax.c
prev pax.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c pax.c
done pax.o
make delta.o
make delta.c
attr perm
attr scan
prev pax.h
done delta.c
prev delta.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c delta.c
done delta.o
make file.o
make file.c
attr perm
attr scan
prev pax.h
done file.c
prev file.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c file.c
done file.o
make format.o
make format.c
attr perm
attr scan
prev pax.h
done format.c
prev format.c
setv SYSTEM -DSYSTEM="\"bsd \""
exec $CC $CCFLAGS -I. -I../../../include "$SYSTEM" "$DEBUG" -c format.c
done format.o
make misc.o
make misc.c
attr perm
attr scan
prev pax.h
done misc.c
prev misc.c
exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c misc.c
done misc.o
make ../../../lib/libodelta.a
attr arch
attr perm
done ../../../lib/libodelta.a
make ../../../lib/libx.a
attr arch
attr perm
done ../../../lib/libx.a
exec $CC $CCFLAGS "$DEBUG" "$SYSTEM"  $LDFLAGS  "$DEBUG" "$SYSTEM" -o pax bio.o convert.o copy.o pax.o delta.o file.o format.o misc.o ../../../lib/libodelta.a ../../../lib/libx.a 
done pax
make cpio
make cpio.sh
attr perm
attr scan
done cpio.sh
prev cpio.sh
exec case '' in
.... "")	cp cpio.sh cpio
.... 	;;
.... *)	{
.... 	i=`(read x; echo $x) < cpio.sh`
.... 	case $i in
.... 	\#!*)	echo $i ;;
.... 	esac
.... 	echo 
.... 	cat cpio.sh
.... 	} > cpio
.... 	;;
.... esac
.... chmod u+w,+x cpio
done cpio
make tar
make tar.sh
attr perm
attr scan
done tar.sh
prev tar.sh
exec case '' in
.... "")	cp tar.sh tar
.... 	;;
.... *)	{
.... 	i=`(read x; echo $x) < tar.sh`
.... 	case $i in
.... 	\#!*)	echo $i ;;
.... 	esac
.... 	echo 
.... 	cat tar.sh
.... 	} > tar
.... 	;;
.... esac
.... chmod u+w,+x tar
done tar
done all
make $INSTALLROOT/bin
exec if	 test ! -d $INSTALLROOT/bin
.... then	rm -rf $INSTALLROOT/bin && mkdir  $INSTALLROOT/bin  		    		    || { rm -rf $INSTALLROOT && mkdir  $INSTALLROOT && mkdir  $INSTALLROOT/bin  		    		    ;} || true
.... fi
done $INSTALLROOT/bin
make $INSTALLROOT/bin/pax
prev pax
exec { cp pax $INSTALLROOT/bin/pax 2>/dev/null  		    		    ;} || true
done $INSTALLROOT/bin/pax
make $INSTALLROOT/man/man1
exec if	 test ! -d $INSTALLROOT/man/man1
.... then	rm -rf $INSTALLROOT/man/man1 && mkdir  $INSTALLROOT/man/man1  		    		    || { rm -rf $INSTALLROOT/man && mkdir  $INSTALLROOT/man && mkdir  $INSTALLROOT/man/man1  		    		    ;} || true
.... fi
done $INSTALLROOT/man/man1
make $INSTALLROOT/man/man1/pax.1
make pax.1
attr perm
done pax.1
exec { cp pax.1 $INSTALLROOT/man/man1/pax.1 2>/dev/null  		    		    ;} || true
done $INSTALLROOT/man/man1/pax.1
done install
0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!