/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1987-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped

/*
 * pax { asc aschk binary cpio } formats
 */

#include "format.h"

#define ASC			1
#define ASCHK			2
#define BINARY			3
#define CPIO			4

#define CPIO_MAGIC		070707
#define CPIO_EXTENDED		1
#define CPIO_HEADER		76
#define CPIO_TRAILER		"TRAILER!!!"
#define CPIO_TRUNCATE(x)	((x)&0177777)
#define CPIO_NAMESIZE		256
#define CPIO_ALIGN		0

#define ASC_HEADER		110
#define ASC_MAGIC		070701
#define ASC_NAMESIZE		1024
#define ASC_ALIGN		4

#define ASCHK_MAGIC		070702

#define BINARY_HEADER		sizeof(Binary_header_t)
#define BINARY_NAMESIZE		256
#define BINARY_ALIGN		2

typedef struct Binary_header_s
{
	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];
} Binary_header_t;

typedef struct Localstat_s
{
	long	dev;
	long	ino;
	long	mode;
	long	uid;
	long	gid;
	long	nlink;
	long	rdev;
	long	mtime;
	off_t	size;
	long	dev_major;
	long	dev_minor;
	long	rdev_major;
	long	rdev_minor;
	long	checksum;
} Localstat_t;

typedef struct Cpio_s
{
	int	trailer;
#if CPIO_EXTENDED
	char	opsbuf[PATH_MAX];	/* extended ops buffer		*/
	char*	ops;			/* opsbuf output pointer	*/
#endif
} Cpio_t;

#if CPIO_EXTENDED

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

static void
getxops(register Archive_t* ap, register File_t* f)
{
	register char*	p;
	register char*	s;
	register int	c;

	if (f->namesize > (c = strlen(f->name) + 1))
		for (p = f->name + c; c = *p++;)
		{
			for (s = p; *p; p++);
			p++;
			message((-2, "%s: %s: entry %d.%d op = %c%s", ap->name, f->name, ap->volume, ap->entry, c, s));
			switch (c)
			{
			case 'd':
				IDEVICE(f->st, strtol(s, NiL, 16));
				break;
			case 'g':
				f->st->st_gid = strtol(s, NiL, 16);
				break;
			case 's':
				f->st->st_size = strtoll(s, NiL, 16);
				break;
			case 'u':
				f->st->st_uid = strtol(s, NiL, 16);
				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(Archive_t* ap, register File_t* f)
{
	register Cpio_t*	cpio = (Cpio_t*)ap->data;
	register int		n;

	if (n = cpio->ops - cpio->opsbuf)
	{
		n++;
		*cpio->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(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	register Cpio_t*	cpio = (Cpio_t*)ap->data;
	register int		n;

	n = cpio->ops - cpio->opsbuf;
	paxwrite(pax, ap, f->name, f->namesize -= n);
	if (n)
		paxwrite(pax, ap, cpio->ops = cpio->opsbuf, n);
}


/*
 * add extended op string
 */

static void
addxopstr(Archive_t* ap, int op, register char* s)
{
	register Cpio_t*	cpio = (Cpio_t*)ap->data;
	register char*		p = cpio->ops;
	register char*		e = cpio->opsbuf + sizeof(cpio->opsbuf) - 3;

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

/*
 * add extended op number
 */

static void
addxopnum(Archive_t* ap, int op, Sflong_t n)
{
	char	buf[33];

	sfsprintf(buf, sizeof(buf), "%I*x", sizeof(n), n);
	addxopstr(ap, op, buf);
}

#endif

static int
init(Archive_t* ap)
{
	register Cpio_t*	cpio;

	if (ap->data)
	{
		cpio = (Cpio_t*)ap->data;
		memset(cpio, 0, sizeof(*cpio));
	}
	else
	{
		if (!(cpio = newof(0, Cpio_t, 1, 0)))
			nospace();
		ap->data = cpio;
	}
#if CPIO_EXTENDED
	cpio->ops = cpio->opsbuf;
#endif
	return 1;
}

static int
cpio_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	int		magic;

	if (size >= CPIO_HEADER && buf[0] == '0' && sfsscanf((char*)buf, "%6o", &magic) == 1 && magic == CPIO_MAGIC)
		return init(ap);
	return 0;
}

static int
cpio_common(Pax_t* pax, register Archive_t* ap, register File_t* f, int align, int header)
{
	char*		s;

	f->linktype = NOLINK;
	f->linkpath = 0;
	f->uidname = 0;
	f->gidname = 0;
	switch (ap->format->variant)
	{
	case BINARY:
		align = BINARY_ALIGN;
		header = BINARY_HEADER;
		break;
	case ASC:
	case ASCHK:
		align = ASC_ALIGN;
		header = ASC_HEADER;
		break;
	default:
		align = 0;
		break;
	}
	if (align)
	{
		if (header = (header + f->namesize) % align)
			align -= header;
		else
			align = 0;
	}
	f->name = paxstash(pax, &ap->stash.head, NiL, f->namesize + align);
	paxread(pax, ap, f->name, (off_t)0, (off_t)(f->namesize + align), 1);
	if (f->name[f->namesize - 1])
	{
		paxunread(pax, ap, &f->name[f->namesize - 1], 1);
		f->name[f->namesize - 1] = 0;
		error(state.keepgoing ? 1 : 3, "%s: entry %d.%d file name terminating null missing", ap->name, ap->volume, ap->entry);
	}
#if CPIO_EXTENDED
	getxops(ap, f);
#endif
	if (streq(f->name, CPIO_TRAILER))
	{
		((Cpio_t*)ap->data)->trailer++;
		getdeltaheader(ap, f);
		if (ap->delta)
			setinfo(ap, f);
		return 0;
	}
	switch (f->type = X_ITYPE(f->st->st_mode))
	{
	case X_IFBLK:
	case X_IFCHR:
	case X_IFDIR:
	case X_IFIFO:
	case X_IFLNK:
	case X_IFREG:
	case X_IFSOCK:
		break;
	default:
		error(1, "%s: %s: unknown file type %07o -- regular file assumed", ap->name, f->name, f->type);
		f->type = X_IFREG;
		break;
	}
	f->st->st_mode &= X_IPERM;
	f->st->st_mode |= f->type;
	switch (f->type)
	{
	case X_IFLNK:
		f->linkpath = paxstash(pax, &ap->stash.link, NiL, f->st->st_size);
		f->linktype = SOFTLINK;
		s = f->linkpath;
		while (paxread(pax, ap, s, (off_t)1, (off_t)1, 1) > 0)
		{
			f->st->st_size--;
			if (!*s++)
				break;
			if (!f->st->st_size)
			{
				*s = 0;
				break;
			}
		}
		break;
	default:
		f->linktype = NOLINK;
		break;
	}
	return 1;
}

static int
cpio_done(Pax_t* pax, register Archive_t* ap)
{
	if (ap->data)
	{
		free(ap->data);
		ap->data = 0;
	}
	return 0;
}

static int
cpio_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Localstat_t	lst;

	if (paxread(pax, ap, state.tmp.buffer, (off_t)0, (off_t)CPIO_HEADER, 0) <= 0)
		return 0;
	state.tmp.buffer[CPIO_HEADER] = 0;
	if (state.tmp.buffer[0] == '0' && sfsscanf(state.tmp.buffer, "%6o%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11I*o",
		&f->magic,
		&lst.dev,
		&lst.ino,
		&lst.mode,
		&lst.uid,
		&lst.gid,
		&lst.nlink,
		&lst.rdev,
		&lst.mtime,
		&f->namesize,
		sizeof(lst.size), &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;
		IDEVICE(f->st, lst.rdev);
		f->st->st_mtime = lst.mtime;
		f->st->st_size = lst.size;
		return cpio_common(pax, ap, f, CPIO_ALIGN, CPIO_HEADER);
	}
	paxunread(pax, ap, state.tmp.buffer, CPIO_HEADER);
	return 0;
}

static int
cpio_getepilogue(Pax_t* pax, register Archive_t* ap)
{
	register Cpio_t*	cpio = (Cpio_t*)ap->data;

	if (!cpio->trailer)
	{
		error(2, "%s: %s format corrupt -- epilogue (%s) not found", ap->name, ap->format->name, CPIO_TRAILER);
		return -1;
	}
	return 0;
}

static int
cpio_putprologue(Pax_t* pax, Archive_t* ap)
{
	return init(ap);
}

static int
cpio_putheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	if (state.complete)
		complete(ap, f, CPIO_HEADER + f->namesize);
#if CPIO_EXTENDED
	if (!f->skip && !state.strict)
	{
		getidnames(f);
		addxopstr(ap, 'U', f->uidname);
		addxopstr(ap, 'G', f->gidname);
		if (CPIO_TRUNCATE(idevice(f->st)) != idevice(f->st))
			addxopnum(ap, 'd', (Sflong_t)idevice(f->st));
#if NUMBER_EVEN_THOUGH_NAME
		if (CPIO_TRUNCATE(f->st->st_gid) != f->st->st_gid)
			addxopnum(ap, 'g', (Sflong_t)f->st->st_gid);
		if (CPIO_TRUNCATE(f->st->st_uid) != f->st->st_uid)
			addxopnum(ap, 'u', (Sflong_t)f->st->st_uid);
#endif
		if (f->st->st_size > 0x7fffffff)
			addxopnum(ap, 's', (Sflong_t)f->st->st_size);
		setxops(ap, f);
	}
	else
#endif
	{
		if (CPIO_TRUNCATE(idevice(f->st)) != idevice(f->st))
			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);
	}
	sfsprintf(state.tmp.buffer, state.tmp.buffersize, "%0.6lo%0.6lo%0.6lo%0.6lo%0.6lo%0.6lo%0.6lo%0.6lo%0.11lo%0.6o%0.11I*o",
		(long)CPIO_TRUNCATE(CPIO_MAGIC),
		(long)CPIO_TRUNCATE(f->st->st_dev),
		(long)CPIO_TRUNCATE(f->st->st_ino),
		(long)CPIO_TRUNCATE(f->st->st_mode),
		(long)CPIO_TRUNCATE(f->st->st_uid),
		(long)CPIO_TRUNCATE(f->st->st_gid),
		(long)CPIO_TRUNCATE(f->st->st_nlink),
		(long)CPIO_TRUNCATE(idevice(f->st)),
		(long)f->st->st_mtime,
		(long)f->namesize,
		sizeof(_ast_intmax_t), (_ast_intmax_t)(f->st->st_size + (f->type == X_IFLNK ? f->linkpathsize : 0)));
	paxwrite(pax, ap, state.tmp.buffer, CPIO_HEADER);
#if CPIO_EXTENDED
	putxops(pax, ap, f);
#else
	paxwrite(pax, ap, f->name, f->namesize);
#endif
	if (f->type == X_IFLNK)
	{
		if (streq(f->name, f->linkpath))
			error(1, "%s: symbolic link loops to self", f->name);
		paxwrite(pax, ap, f->linkpath, f->linkpathsize);
		putdeltaheader(ap, f);
	}
	return 1;
}

static off_t
cpio_putepilogue(Pax_t* pax, Archive_t* ap)
{
	char	buf[sizeof(CPIO_TRAILER)];

	putinfo(ap, strcpy(buf, CPIO_TRAILER), ap->delta && !(ap->delta->format->flags & PSEUDO) ? ap->delta->index + 1 : 0, 0);
	return ap->io->unblocked ? BLOCKSIZE : state.blocksize;
}

static int
cpio_event(Pax_t* pax, Archive_t* ap, File_t* f, void* data, unsigned long event)
{
	off_t		n;

	switch (event)
	{
	case PAX_EVENT_BUG_19951031:
		/*
		 * compensate for a pre 19951031 pax bug
		 * that added linknamesize to st_size
		 */

		if (f->st->st_size == f->linkpathsize && paxread(pax, ap, state.tmp.buffer, (off_t)0, n = f->st->st_size + 6, 0) > 0)
		{
			paxunread(pax, ap, state.tmp.buffer, n);
			state.tmp.buffer[6] = 0;
			state.tmp.buffer[n] = 0;
			if (strtol(state.tmp.buffer, NiL, 8) == CPIO_MAGIC && strtol(state.tmp.buffer + f->st->st_size, NiL, 8) != CPIO_MAGIC)
			{
				f->st->st_size = 0;
				if (!ap->warnlinkhead)
				{
					ap->warnlinkhead = 1;
					error(1, "%s: compensating for invalid %s header hard link sizes", ap->name, ap->format->name);
				}
			}
		}
		return 1;
	case PAX_EVENT_DELTA_EXTEND:
		return 1;
	}
	return 0;
}

static int
asc_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	unsigned int		magic;

	if (size >= ASC_HEADER && buf[0] == '0' && sfsscanf((char*)buf, "%6o", &magic) == 1 && magic == ASC_MAGIC)
		return init(ap);
	return 0;
}

static int
asc_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Localstat_t	lst;

	if (paxread(pax, ap, state.tmp.buffer, (off_t)0, (off_t)ASC_HEADER, 0) <= 0)
		return 0;
	state.tmp.buffer[ASC_HEADER] = 0;
	if (state.tmp.buffer[0] == '0' && sfsscanf(state.tmp.buffer, "%6o%8lx%8lx%8lx%8lx%8lx%8lx%8I*x%8lx%8lx%8lx%8lx%8x%8lx",
		&f->magic,
		&lst.ino,
		&lst.mode,
		&lst.uid,
		&lst.gid,
		&lst.nlink,
		&lst.mtime,
		sizeof(lst.size), &lst.size,
		&lst.dev_major,
		&lst.dev_minor,
		&lst.rdev_major,
		&lst.rdev_minor,
		&f->namesize,
		&lst.checksum) == 14 && f->magic == ASC_MAGIC)
	{
		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;
		IDEVICE(f->st, makedev(lst.rdev_major, lst.rdev_minor));
		f->st->st_mtime = lst.mtime;
		f->st->st_size = lst.size;
		return cpio_common(pax, ap, f, ASC_ALIGN, ASC_HEADER);
	}
	paxunread(pax, ap, state.tmp.buffer, ASC_HEADER);
	return 0;
}

static int
asc_putheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	int	n;

	if (state.complete)
		complete(ap, f, ASC_HEADER + f->namesize);
	if (ap->format->variant == ASC)
		f->checksum = 0;
	sfsprintf(state.tmp.buffer, state.tmp.buffersize, "%0.6lo%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx%0.8lx",
		(long)(ap->format->variant == ASC ? ASC_MAGIC : ASCHK_MAGIC),
		(long)f->st->st_ino,
		(long)f->st->st_mode,
		(long)f->st->st_uid,
		(long)f->st->st_gid,
		(long)f->st->st_nlink,
		(long)f->st->st_mtime,
		(long)f->st->st_size + (long)f->linkpathsize,
		(long)major(f->st->st_dev),
		(long)minor(f->st->st_dev),
		(long)major(idevice(f->st)),
		(long)minor(idevice(f->st)),
		(long)f->namesize,
		f->checksum);
	paxwrite(pax, ap, state.tmp.buffer, ASC_HEADER);
	paxwrite(pax, ap, f->name, f->namesize);
	if (n = (ASC_HEADER + f->namesize) % ASC_ALIGN)
		while (n++ < ASC_ALIGN)
			paxwrite(pax, ap, "", 1);
	return 1;
}

static int
aschk_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	unsigned int		magic;

	if (size >= ASC_HEADER && buf[0] == '0' && sfsscanf((char*)buf, "%6o", &magic) == 1 && magic == ASCHK_MAGIC)
		return init(ap);
	return 0;
}

static int
aschk_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Localstat_t	lst;

	if (paxread(pax, ap, state.tmp.buffer, (off_t)0, (off_t)ASC_HEADER, 0) <= 0)
		return 0;
	state.tmp.buffer[ASC_HEADER] = 0;
	if (state.tmp.buffer[0] == '0' && sfsscanf(state.tmp.buffer, "%6o%8lx%8lx%8lx%8lx%8lx%8lx%8I*x%8lx%8lx%8lx%8lx%8x%8lx",
		&f->magic,
		&lst.ino,
		&lst.mode,
		&lst.uid,
		&lst.gid,
		&lst.nlink,
		&lst.mtime,
		sizeof(lst.size), &lst.size,
		&lst.dev_major,
		&lst.dev_minor,
		&lst.rdev_major,
		&lst.rdev_minor,
		&f->namesize,
		&lst.checksum) == 14 && f->magic == ASCHK_MAGIC)
	{
		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;
		IDEVICE(f->st, makedev(lst.rdev_major, lst.rdev_minor));
		f->st->st_mtime = lst.mtime;
		f->st->st_size = lst.size;
		return cpio_common(pax, ap, f, ASC_ALIGN, ASC_HEADER);
	}
	paxunread(pax, ap, state.tmp.buffer, ASC_HEADER);
	return 0;
}

static unsigned long
aschk_checksum(Pax_t* pax, Archive_t* ap, File_t* f, void* ab, size_t n, register unsigned long sum)
{
	register unsigned char* b = (unsigned char*)ab;
	register unsigned char*	e;

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

/*
 * convert binary header shorts to long
 */

static long
binary_long(register unsigned short* s)
{
	Integral_t	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
 */

static void
binary_short(register unsigned short* s, long n)
{
	Integral_t	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];
	}
}

static int
binary_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	short		magic = CPIO_MAGIC;
	int		swap;

	if (size >= BINARY_HEADER && (swap = swapop(&magic, buf, 2)) >= 0)
	{
		ap->swap = swap;
		return init(ap);
	}
	return 0;
}

static int
binary_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Binary_header_t	hdr;

	if (paxread(pax, ap, &hdr, (off_t)BINARY_HEADER, (off_t)BINARY_HEADER, 0) <= 0)
		return 0;
	if (ap->swap)
	{
		memcpy(state.tmp.buffer, &hdr, BINARY_HEADER);
		swapmem(ap->swap, &hdr, &hdr, BINARY_HEADER);
	}
	f->magic = hdr.magic;
	if (f->magic == CPIO_MAGIC)
	{
		f->namesize = hdr.namesize;
		f->st->st_dev = hdr.dev;
		f->st->st_ino = hdr.ino;
		f->st->st_mode = hdr.mode;
		f->st->st_uid = hdr.uid;
		f->st->st_gid = hdr.gid;
		f->st->st_nlink = hdr.links;
		IDEVICE(f->st, hdr.rdev);
		f->st->st_mtime = binary_long(hdr.mtime);
		f->st->st_size = binary_long(hdr.size);
		return cpio_common(pax, ap, f, CPIO_ALIGN, CPIO_HEADER);
	}
	paxunread(pax, ap, state.tmp.buffer, CPIO_HEADER);
	return 0;
}

static int
binary_putheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Binary_header_t	hdr;
	int		n;

	if (state.complete)
		complete(ap, f, BINARY_HEADER + f->namesize);
	hdr.magic = CPIO_MAGIC;
	hdr.namesize = f->namesize;
	binary_short(hdr.size, f->st->st_size + (f->type == X_IFLNK ? f->linkpathsize : 0));
	hdr.dev = f->st->st_dev;
	hdr.ino = f->st->st_ino;
	hdr.mode = f->st->st_mode;
	hdr.uid = f->st->st_uid;
	hdr.gid = f->st->st_gid;
	hdr.links = f->st->st_nlink;
	hdr.rdev = idevice(f->st);
	if (hdr.rdev != idevice(f->st))
		error(1, "%s: special device numbers truncated", f->name);
	binary_short(hdr.mtime, (long)f->st->st_mtime);
	paxwrite(pax, ap, &hdr, BINARY_HEADER);
	paxwrite(pax, ap, f->name, f->namesize);
	if (n = (BINARY_HEADER + f->namesize) % BINARY_ALIGN)
		while (n++ < BINARY_ALIGN)
			paxwrite(pax, ap, "", 1);
	if (f->type == X_IFLNK)
	{
		if (streq(f->name, f->linkpath))
			error(1, "%s: symbolic link loops to self", f->name);
		paxwrite(pax, ap, f->linkpath, f->linkpathsize);
		putdeltaheader(ap, f);
	}
	return 1;
}

static int
binary_validate(Pax_t* pax, Archive_t* ap, File_t* f)
{
	if (f->namesize > (CPIO_NAMESIZE + 1))
	{
		error(2, "%s: file name too long", f->name);
		return 0;
	}
	return 1;
}

Format_t	pax_asc_format =
{
	"asc",
	0,
	"s5r4 extended cpio character",
	ASC,
	ARCHIVE|IN|OUT,
	DEFBUFFER,
	DEFBLOCKS,
	4,
	pax_asc_next,
	0,
	cpio_done,
	asc_getprologue,
	asc_getheader,
	0,
	0,
	cpio_getepilogue,
	cpio_putprologue,
	asc_putheader,
	0,
	0,
	cpio_putepilogue,
	0,
	0,
	0
};

Format_t	pax_aschk_format =
{
	"aschk",
	0,
	"s5r4 extended cpio character with checksum",
	ASCHK,
	ARCHIVE|IN|OUT,
	DEFBUFFER,
	DEFBLOCKS,
	4,
	pax_aschk_next,
	0,
	cpio_done,
	aschk_getprologue,
	aschk_getheader,
	0,
	0,
	cpio_getepilogue,
	cpio_putprologue,
	asc_putheader,
	0,
	0,
	cpio_putepilogue,
	0,
	0,
	aschk_checksum
};

Format_t	pax_binary_format =
{
	"binary",
	"binary-cpio",
	"cpio binary with symlinks",
	BINARY,
	ARCHIVE|IN|OUT,
	DEFBUFFER,
	DEFBLOCKS,
	2,
	pax_binary_next,
	0,
	cpio_done,
	binary_getprologue,
	binary_getheader,
	0,
	0,
	cpio_getepilogue,
	cpio_putprologue,
	binary_putheader,
	0,
	0,
	cpio_putepilogue,
	0,
	0,
	0,
	binary_validate
};

Format_t	pax_cpio_format =
{
	"cpio",
	0,
	"cpio character with symlinks",
	CPIO,
	ARCHIVE|IN|OUT,
	DEFBUFFER,
	DEFBLOCKS,
	0,
	PAXNEXT(pax_cpio_next),
	0,
	cpio_done,
	cpio_getprologue,
	cpio_getheader,
	0,
	0,
	cpio_getepilogue,
	cpio_putprologue,
	cpio_putheader,
	0,
	0,
	cpio_putepilogue,
	0,
	0,
	0,
	0,
	cpio_event,
	PAX_EVENT_BUG_19951031|PAX_EVENT_DELTA_EXTEND
};

PAXLIB(&pax_cpio_format)
