/* @(#)header.c	1.73 03/02/01 Copyright 1985, 1995 J. Schilling */
#ifndef lint
static	char sccsid[] =
	"@(#)header.c	1.73 03/02/01 Copyright 1985, 1995 J. Schilling";
#endif
/*
 *	Handling routines to read/write, parse/create
 *	archive headers
 *
 *	Copyright (c) 1985, 1995 J. Schilling
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <mconfig.h>
#include <stdio.h>
#include <stdxlib.h>
#include "star.h"
#include "props.h"
#include "table.h"
#include <dirdefs.h>
#include <standard.h>
#include <strdefs.h>
#define	__XDEV__	/* Needed to activate _dev_major()/_dev_minor() */
#include <device.h>
#include <schily.h>
#include "starsubs.h"

	/* ustar */
LOCAL	char	magic[TMAGLEN] = TMAGIC;
	/* star */
LOCAL	char	stmagic[STMAGLEN] = STMAGIC;
	/* gnu tar */
LOCAL	char	gmagic[GMAGLEN] = GMAGIC;

LOCAL	char	*hdrtxt[] = {
	/* 0 */	"UNDEFINED",
	/* 1 */	"unknown tar",
	/* 2 */	"old tar",
	/* 3 */	"star",
	/* 4 */	"gnu tar",
	/* 5 */	"ustar",
	/* 6 */	"xstar",
	/* 7 */	"xustar",
	/* 8 */	"exustar",
	/* 9 */	"pax",		/* USTAR POSIX.1-2001 */
	/*10 */	"suntar",
	/*11 */	"res11",	/* Reserved */
	/*12 */	"res12",	/* Reserved */
	/*13 */	"res13",	/* Reserved */
	/*14 */	"res14",	/* Reserved */
	/*15 */	"bar",
	/*16 */	"cpio binary",
	/*17 */	"cpio -c",
	/*18 */	"cpio",
	/*19 */	"cpio crc",
	/*20 */	"cpio ascii",
	/*21 */	"cpio ascii crc",
};

extern	FILE	*tty;
extern	FILE	*vpr;
extern	long	hdrtype;
extern	long	chdrtype;
extern	int	version;
extern	int	swapflg;
extern	BOOL	debug;
extern	BOOL	numeric;
extern	int	verbose;
extern	BOOL	xflag;
extern	BOOL	nflag;
extern	BOOL	ignoreerr;
extern	BOOL	signedcksum;
extern	BOOL	nowarn;
extern	BOOL	nullout;
extern	BOOL	modebits;

extern	Ullong	tsize;

extern	char	*bigbuf;
extern	int	bigsize;

LOCAL	Ulong	checksum	__PR((TCB * ptb));
LOCAL	Ulong	bar_checksum	__PR((TCB * ptb));
LOCAL	BOOL	signedtarsum	__PR((TCB *ptb, Ulong ocheck));
LOCAL	BOOL	isstmagic	__PR((char* s));
LOCAL	BOOL	isxmagic	__PR((TCB *ptb));
LOCAL	BOOL	ismagic		__PR((char* s));
LOCAL	BOOL	isgnumagic	__PR((char* s));
LOCAL	BOOL	strxneql	__PR((char* s1, char* s2, int l));
LOCAL	BOOL	ustmagcheck	__PR((TCB * ptb));
LOCAL	void	print_hdrtype	__PR((int type));
EXPORT	int	get_hdrtype	__PR((TCB * ptb, BOOL isrecurse));
EXPORT	int	get_compression	__PR((TCB * ptb));
EXPORT	int	get_tcb		__PR((TCB * ptb));
EXPORT	void	put_tcb		__PR((TCB * ptb, FINFO * info));
EXPORT	void	write_tcb	__PR((TCB * ptb, FINFO * info));
EXPORT	void	put_volhdr	__PR((char* name));
EXPORT	BOOL	get_volhdr	__PR((FINFO * info, char* vhname));
EXPORT	void	info_to_tcb	__PR((FINFO * info, TCB * ptb));
LOCAL	void	info_to_star	__PR((FINFO * info, TCB * ptb));
LOCAL	void	info_to_ustar	__PR((FINFO * info, TCB * ptb));
LOCAL	void	info_to_xstar	__PR((FINFO * info, TCB * ptb));
LOCAL	void	info_to_gnutar	__PR((FINFO * info, TCB * ptb));
EXPORT	int	tcb_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	tar_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	star_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	ustar_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	xstar_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	gnutar_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	void	cpiotcb_to_info	__PR((TCB * ptb, FINFO * info));
LOCAL	int	ustoxt		__PR((int ustype));
EXPORT	BOOL	ia_change	__PR((TCB * ptb, FINFO * info));
LOCAL	BOOL	checkeof	__PR((TCB * ptb));
LOCAL	BOOL	eofblock	__PR((TCB * ptb));
LOCAL	void	astoo_cpio	__PR((char* s, Ulong * l, int cnt));
LOCAL	void	stoli		__PR((char* s, Ulong * l));
EXPORT	void	stolli		__PR((char* s, Ullong * ull));
LOCAL	void	litos		__PR((char* s, Ulong l, int fieldw));
EXPORT	void	llitos		__PR((char* s, Ullong ull, int fieldw));
LOCAL	void	stob		__PR((char* s, Ulong * l, int fieldw));
LOCAL	void	stollb		__PR((char* s, Ullong * ull, int fieldw));
LOCAL	void	btos		__PR((char* s, Ulong l, int fieldw));
LOCAL	void	llbtos		__PR((char* s, Ullong ull, int fieldw));
EXPORT	void	dump_info	__PR((FINFO *info));

/*
 * XXX Hier sollte eine tar/bar universelle Checksummenfunktion sein!
 */
#define	CHECKS	sizeof(ptb->ustar_dbuf.t_chksum)
/*
 * We know, that sizeof(TCP) is 512 and therefore has no
 * reminder when dividing by 8
 *
 * CHECKS is known to be 8 too, use loop unrolling.
 */
#define	DO8(a)	a;a;a;a;a;a;a;a;

LOCAL Ulong
checksum(ptb)
	register	TCB	*ptb;
{
	register	int	i;
	register	Ulong	sum = 0;
	register	Uchar	*us;

	if (signedcksum) {
		register	char	*ss;

		ss = (char *)ptb;
		for (i=sizeof(*ptb)/8; --i >= 0;) {
			DO8(sum += *ss++);
		}
		if (sum == 0L)		/* Block containing 512 nul's */
			return(sum);

		ss=(char *)ptb->ustar_dbuf.t_chksum;
		DO8(sum -= *ss++);
		sum += CHECKS*' ';
	} else {
		us = (Uchar *)ptb;
		for (i=sizeof(*ptb)/8; --i >= 0;) {
			DO8(sum += *us++);
		}
		if (sum == 0L)		/* Block containing 512 nul's */
			return(sum);

		us=(Uchar *)ptb->ustar_dbuf.t_chksum;
		DO8(sum -= *us++);
		sum += CHECKS*' ';
	}
	return sum;
}
#undef	CHECKS

#define	CHECKS	sizeof(ptb->bar_dbuf.t_chksum)

LOCAL Ulong
bar_checksum(ptb)
	register	TCB	*ptb;
{
	register	int	i;
	register	Ulong	sum = 0;
	register	Uchar	*us;

	if (signedcksum) {
		register	char	*ss;

		ss = (char *)ptb;
		for (i=sizeof(*ptb); --i >= 0;)
			sum += *ss++;
		if (sum == 0L)		/* Block containing 512 nul's */
			return(sum);

		for (i=CHECKS, ss=(char *)ptb->bar_dbuf.t_chksum; --i >= 0;)
			sum -= *ss++;
		sum += CHECKS*' ';
	} else {
		us = (Uchar *)ptb;
		for (i=sizeof(*ptb); --i >= 0;)
			sum += *us++;
		if (sum == 0L)		/* Block containing 512 nul's */
			return(sum);

		for (i=CHECKS, us=(Uchar *)ptb->bar_dbuf.t_chksum; --i >= 0;)
			sum -= *us++;
		sum += CHECKS*' ';
	}
	return sum;
}
#undef	CHECKS

LOCAL BOOL
signedtarsum(ptb, ocheck)
	TCB	*ptb;
	Ulong	ocheck;
{
	BOOL	osigned = signedcksum;
	Ulong	check;

	signedcksum = !signedcksum;
	check = checksum(ptb);
	if (ocheck == check) {
		errmsgno(EX_BAD, "WARNING: archive uses %s checksums.\n",
				signedcksum?"signed":"unsigned");
		return (TRUE);
	}
	signedcksum = osigned;
	return (FALSE);
}

LOCAL BOOL
isstmagic(s)
	char	*s;
{
	return (strxneql(s, stmagic, STMAGLEN));
}

/*
 * Check for XUSTAR format.
 *
 * This is star's upcoming new standard format. This format understands star's
 * old extended POSIX format and in future will write POSIX.1-2001 extensions
 * using 'x' headers.
 */
LOCAL BOOL
isxmagic(ptb)
	TCB	*ptb;
{
	register int	i;

	/*
	 * prefix[130] is is granted to be '\0' with 'xstar'.
	 */
	if (ptb->xstar_dbuf.t_prefix[130] != '\0')
		return (FALSE);
	/*
	 * If atime[0]...atime[10] or ctime[0]...ctime[10]
	 * is not a POSIX octal number it cannot be 'xstar'.
	 * With the octal representation we may store any date
	 * for 1970 +- 136 years (1834 ... 2106). After 2106
	 * we will most likely always use POSIX.1-2001 'x'
	 * headers and thus don't need to check for base 256
	 * numbers.
	 */
	for (i = 0; i < 11; i++) {
		if (ptb->xstar_dbuf.t_atime[i] < '0' ||
		    ptb->xstar_dbuf.t_atime[i] > '7')
			return (FALSE);
		if (ptb->xstar_dbuf.t_ctime[i] < '0' ||
		    ptb->xstar_dbuf.t_ctime[i] > '7')
			return (FALSE);
	}

	/*
	 * Check for both POSIX compliant end of number characters.
	 */
	if ((ptb->xstar_dbuf.t_atime[11] != ' ' &&
	     ptb->xstar_dbuf.t_atime[11] != '\0') ||
	    (ptb->xstar_dbuf.t_ctime[11] != ' ' &&
	     ptb->xstar_dbuf.t_ctime[11] != '\0'))
		return (FALSE);

	return (TRUE);
}

LOCAL BOOL
ismagic(s)
	char	*s;
{
	return (strxneql(s, magic, TMAGLEN));
}

LOCAL BOOL
isgnumagic(s)
	char	*s;
{
	return (strxneql(s, gmagic, GMAGLEN));
}

LOCAL BOOL
strxneql(s1, s2, l)
	register char	*s1;
	register char	*s2;
	register int	l;
{
	while (--l >= 0)
		if (*s1++ != *s2++)
			return (FALSE);
	return (TRUE);
}

LOCAL BOOL
ustmagcheck(ptb)
	TCB	*ptb;
{
	if (ismagic(ptb->xstar_dbuf.t_magic) &&
				strxneql(ptb->xstar_dbuf.t_version, "00", 2))
		return (TRUE);
	return (FALSE);
}

LOCAL void
print_hdrtype(type)
	int	type;
{
	BOOL	isswapped = H_ISSWAPPED(type);

	if (H_TYPE(type) > H_MAX_ARCH)
		type = H_UNDEF;
	type = H_TYPE(type);

	error("%s%s archive.\n", isswapped?"swapped ":"", hdrtxt[type]);
}

EXPORT int
get_hdrtype(ptb, isrecurse)
	TCB	*ptb;
	BOOL	isrecurse;
{
	Ulong	check;
	Ulong	ocheck;
	int	ret = H_UNDEF;

	stoli(ptb->dbuf.t_chksum, &ocheck);
	check = checksum(ptb);
	if (ocheck != check && !signedtarsum(ptb, ocheck)) {
		if (debug && !isrecurse) {
			errmsgno(EX_BAD,
				"Bad tar checksum at: %lld: 0%lo should be 0%lo.\n",
							tblocks(),
							ocheck, check);
		}
		goto nottar;
	}

	if (isstmagic(ptb->dbuf.t_magic)) {	/* Check for 'tar\0' at end */
		if (ustmagcheck(ptb))
			ret = H_XSTAR;
		else
			ret = H_STAR;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (ustmagcheck(ptb)) {			/* 'ustar\000' POSIX magic */
		if (isxmagic(ptb)) {
			if (ptb->ustar_dbuf.t_typeflag == 'g' ||
			    ptb->ustar_dbuf.t_typeflag == 'x')
				ret = H_EXUSTAR;
			else
				ret = H_XUSTAR;
		} else {
			if (ptb->ustar_dbuf.t_typeflag == 'g' ||
			    ptb->ustar_dbuf.t_typeflag == 'x')
				ret = H_PAX;
			else if (ptb->ustar_dbuf.t_typeflag == 'X')
				ret = H_SUNTAR;
			else
				ret = H_USTAR;
		}
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (isgnumagic(&ptb->dbuf.t_vers)) {	/* 'ustar  ' GNU magic */
		ret = H_GNUTAR;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if ((ptb->dbuf.t_mode[6] == ' ' && ptb->dbuf.t_mode[7] == '\0')) {
		ret = H_OTAR;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (ptb->ustar_dbuf.t_typeflag == LF_VOLHDR ||
			    ptb->ustar_dbuf.t_typeflag == LF_MULTIVOL) {
		/*
		 * Gnu volume headers & multi volume headers
		 * are no real tar headers.
		 */
		if (debug) error("gnutar buggy archive.\n");
		ret = H_GNUTAR;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	/*
	 * The only thing we know here is:
	 * we found a header with a correct tar checksum.
	 */
	ret = H_TAR;
	if (debug) print_hdrtype(ret);
	return (ret);

nottar:
	if (ptb->bar_dbuf.bar_magic[0] == 'V') {
		stoli(ptb->bar_dbuf.t_chksum, &ocheck);
		check = bar_checksum(ptb);

		if (ocheck == check) {
			ret = H_BAR;
			if (debug) print_hdrtype(ret);
			return (ret);
		} else if (debug && !isrecurse) {
			errmsgno(EX_BAD,
				"Bad bar checksum at: %lld: 0%lo should be 0%lo.\n",
							tblocks(),
							ocheck, check);
		}

	}
	if (strxneql((char *)ptb, "070701", 6)) {
		ret = H_CPIO_ASC;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (strxneql((char *)ptb, "070702", 6)) {
		ret = H_CPIO_ACRC;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (strxneql((char *)ptb, "070707", 6)) {
		ret = H_CPIO_CHR;
		if (debug) print_hdrtype(ret);
		return (ret);

	}
	if (strxneql((char *)ptb, "\161\301", 2)) {
		ret = H_CPIO_NBIN;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (strxneql((char *)ptb, "\161\302", 2)) {
		ret = H_CPIO_CRC;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (strxneql((char *)ptb, "\161\307", 2)) {
		ret = H_CPIO_BIN;
		if (debug) print_hdrtype(ret);
		return (ret);
	}
	if (debug) error("no tar archive??\n");

	if (!isrecurse) {
		int	rret;
		swabbytes((char *)ptb, TBLOCK);
		rret = get_hdrtype(ptb, TRUE);
		swabbytes((char *)ptb, TBLOCK);
		rret = H_SWAPPED(rret);
		if (debug) print_hdrtype(rret);
		return (rret);
	}

	if (debug) print_hdrtype(ret);
	return (ret);
}

EXPORT int
get_compression(ptb)
	TCB	*ptb;
{
	char	*p = (char *)ptb;

	if (p[0] == '\037') {
		if ((p[1] == '\037') ||	/* Packed	     */
		    (p[1] == '\213') ||	/* Gzip compressed   */
		    (p[1] == '\235') ||	/* LZW compressed    */
		    (p[1] == '\236') ||	/* Freezed 	     */
		    (p[1] == '\240'))	/* SCO LZH compressed*/
		return (C_GZIP);
	}
	if (p[0] == 'B' && p[1] == 'Z' && p[2] == 'h')
		return (C_BZIP2);
	return (C_NONE);
}

EXPORT int
get_tcb(ptb)
	TCB	*ptb;
{
	Ulong	check;
	Ulong	ocheck;
	BOOL	eof;

	do {
		/*
		 * bei der Option -i wird ein genulltes File
		 * fehlerhaft als EOF Block erkannt !
		 * wenn nicht t_magic gesetzt ist.
		 */
		if (readblock((char *)ptb) == EOF) {
			errmsgno(EX_BAD, "Hard EOF on input, first EOF block is missing.\n");
			return (EOF);
		}
		/*
		 * First tar control block
		 */
		if (swapflg < 0) {
			BOOL	swapped;

			hdrtype = get_hdrtype(ptb, FALSE);
			swapped = H_ISSWAPPED(hdrtype);
			if (chdrtype != H_UNDEF &&
					swapped != H_ISSWAPPED(chdrtype)) {

				swapped = H_ISSWAPPED(chdrtype);
			}
			if (swapped) {
				swapflg = 1;
				swabbytes((char *)ptb, TBLOCK);	/* copy of TCB*/
				swabbytes(bigbuf, bigsize);	/* io buffer */
			} else {
				swapflg = 0;
			}
			/*
			 * wake up fifo (first block ist swapped)
			 */
			buf_resume();
			if (H_TYPE(hdrtype) == H_BAR) {
				comerrno(EX_BAD, "Can't handle bar archives (yet).\n");
			}
			if (H_TYPE(hdrtype) >= H_CPIO_BASE) {
/* XXX JS Test */if (H_TYPE(hdrtype) == H_CPIO_CHR) {
/* XXX JS Test */FINFO info;
/* XXX JS Test */tcb_to_info(ptb, &info);
/* XXX JS Test */}
				comerrno(EX_BAD, "Can't handle cpio archives (yet).\n");
			}
			if (H_TYPE(hdrtype) == H_UNDEF) {
				switch (get_compression(ptb)) {

				case C_GZIP:
					comerrno(EX_BAD, "Archive is compressed, try to use the -z option.\n");
					break;
				case C_BZIP2:
					comerrno(EX_BAD, "Archive is bzip2 compressed, try to use the -bz option.\n");
					break;
				}
				if (!ignoreerr) {
					comerrno(EX_BAD,
					"Unknown archive type (neither tar, nor bar/cpio).\n");
				}
			}
			if (chdrtype != H_UNDEF && chdrtype != hdrtype) {
				errmsgno(EX_BAD, "Found: ");
				print_hdrtype(hdrtype);
				errmsgno(EX_BAD, "WARNING: extracting as ");
				print_hdrtype(chdrtype);
				hdrtype = chdrtype;
			}
			setprops(hdrtype);
		}
		eof = (ptb->dbuf.t_name[0] == '\0') && checkeof(ptb);
		if (eof && !ignoreerr) {
			return (EOF);
		}
		/*
		 * XXX Hier muß eine Universalchecksummenüberprüfung hin
		 */
		stoli(ptb->dbuf.t_chksum, &ocheck);
		check = checksum(ptb);
		/*
		 * check == 0 : genullter Block.
		 */
		if (check != 0 && ocheck == check) {
			char	*tmagic = ptb->ustar_dbuf.t_magic;

			switch (H_TYPE(hdrtype)) {

			case H_XUSTAR:
			case H_EXUSTAR:
				if (ismagic(tmagic) && isxmagic(ptb))
					return (0);
				/*
				 * Both formats are equivalent.
				 * Acept XSTAR too.
				 */
				/* FALLTHROUGH */
			case H_XSTAR:
				if (ismagic(tmagic) &&
				    isstmagic(ptb->xstar_dbuf.t_xmagic))
					return (0);
				break;
			case H_PAX:
			case H_USTAR:
			case H_SUNTAR:
				if (ismagic(tmagic))
					return (0);
				break;
			case H_GNUTAR:
				if (isgnumagic(tmagic))
					return (0);
				break;
			case H_STAR: 
				tmagic = ptb->star_dbuf.t_magic;
				if (ptb->dbuf.t_vers < STVERSION ||
				    isstmagic(tmagic))
				return (0);
				break;
			default:
			case H_TAR:
			case H_OTAR:
				return (0);
			}
			errmsgno(EX_BAD, "Wrong magic at: %lld: '%.8s'.\n",
							tblocks(), tmagic);
			/*
			 * Allow buggy gnu Volheaders & Multivolheaders to work
			 */
			if (H_TYPE(hdrtype) == H_GNUTAR)
				return (0);

		} else if (eof) {
			errmsgno(EX_BAD, "EOF Block at: %lld ignored.\n",
							tblocks());
		} else {
			if (signedtarsum(ptb, ocheck))
				return (0);
			errmsgno(EX_BAD, "Checksum error at: %lld: 0%lo should be 0%lo.\n",
							tblocks(),
							ocheck, check);
		}
	} while (ignoreerr);
	exprstats(EX_BAD);
	/* NOTREACHED */
	return (EOF);		/* Keep lint happy */
}

EXPORT void
put_tcb(ptb, info)
	TCB	*ptb;
	register FINFO	*info;
{
	TCB	tb;
	int	x1 = 0;
	int	x2 = 0;

	if (info->f_flags & (F_LONGNAME|F_LONGLINK))
		x1++;
	if (info->f_xflags & (XF_PATH|XF_LINKPATH))
		x1++;

/*XXX start alter code und Test */
	if (( (info->f_flags & F_ADDSLASH) ? 1:0 +
	    info->f_namelen > props.pr_maxsname &&
	    (ptb->dbuf.t_prefix[0] == '\0' || H_TYPE(hdrtype) == H_GNUTAR)) ||
		    info->f_lnamelen > props.pr_maxslname)
		x2++;

	if ((x1 != x2) && info->f_xftype != XT_META) {
error("type: %ld name: '%s' x1 %d x2 %d namelen: %ld prefix: '%s' lnamelen: %ld\n",
info->f_filetype, info->f_name, x1, x2,
info->f_namelen, ptb->dbuf.t_prefix, info->f_lnamelen);
	}
/*XXX ende alter code und Test */

	if (x1 || x2 || (info->f_xflags != 0)) {
		if ((info->f_flags & F_TCB_BUF) != 0) {	/* TCB is on buffer */
			movetcb(ptb, &tb);
			ptb = &tb;
			info->f_flags &= ~F_TCB_BUF;
		}
		if (info->f_xflags != 0)
			info_to_xhdr(info, ptb);
		else
			write_longnames(info);
	}
	write_tcb(ptb, info);
}

EXPORT void
write_tcb(ptb, info)
	TCB	*ptb;
	register FINFO	*info;
{
	if (tsize > 0) {
		TCB	tb;
		Llong	left;
		off_t	size = info->f_rsize;

		left = tsize - tblocks();

		if (is_link(info))
			size = 0L;
						/* file + tcb + EOF */
		if (left < (tarblocks(size)+1+2)) {
			if ((info->f_flags & F_TCB_BUF) != 0) {
				movetcb(ptb, &tb);
				ptb = &tb;
				info->f_flags &= ~F_TCB_BUF;
			}
			nexttape();
		}
	}
	if (!nullout) {				/* 17 (> 16) Bit !!! */
		if (props.pr_fillc == '0')
			litos(ptb->dbuf.t_chksum, checksum(ptb) & 0x1FFFF, 7);
		else
			litos(ptb->dbuf.t_chksum, checksum(ptb) & 0x1FFFF, 6);
	}
	if ((info->f_flags & F_TCB_BUF) != 0)	/* TCB is on buffer */
		put_block();
	else
		writeblock((char *)ptb);
}

EXPORT void
put_volhdr(name)
	char	*name;
{
	FINFO	finfo;
	TCB	tb;
	struct timeval tv;

	if (name == 0)
		return;
	if ((props.pr_flags & PR_VOLHDR) == 0)
		return;

	gettimeofday(&tv, (struct timezone *)0);

	fillbytes((char *)&finfo, sizeof (FINFO), '\0');
	filltcb(&tb);
	finfo.f_name = name;
	finfo.f_namelen = strlen(name);
	finfo.f_xftype = XT_VOLHDR;
	finfo.f_mtime = tv.tv_sec;
	finfo.f_mnsec = tv.tv_usec*1000;
	finfo.f_tcb = &tb;

	if (!name_to_tcb(&finfo, &tb))	/* Name too long */
		return;

	info_to_tcb(&finfo, &tb);
	put_tcb(&tb, &finfo);
	vprint(&finfo);
}

EXPORT BOOL
get_volhdr(info, vhname)
	FINFO	*info;
	char	*vhname;
{
	error("Volhdr: %s\n", info->f_name);

	if (vhname) {
		return (streql(info->f_name, vhname));
	} else { 
		return (TRUE);
	}
}

EXPORT void
info_to_tcb(info, ptb)
	register FINFO	*info;
	register TCB	*ptb;
{
	if (nullout)
		return;

	if (props.pr_fillc == '0') {
		/*
		 * This is a POSIX compliant header, it is allowed to use
		 * 7 bytes from 8 byte headers as POSIX only requires a ' ' or
		 * '\0' as last char.
		 */
		if (modebits)
			litos(ptb->dbuf.t_mode, (info->f_mode|info->f_type) & 0xFFFF, 7);
		else
			litos(ptb->dbuf.t_mode, info->f_mode & 0xFFFF, 7);

		if (info->f_uid > MAXOCTAL7 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_UID;
		}
/* XXX */
		litos(ptb->dbuf.t_uid, info->f_uid & MAXOCTAL7, 7);

		if (info->f_gid > MAXOCTAL7 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_GID;
		}
/* XXX */
		litos(ptb->dbuf.t_gid, info->f_gid & MAXOCTAL7, 7);
	} else {
		/*
		 * This is a pre POSIX header, it is only allowed to use
		 * 6 bytes from 8 byte headers as historic TAR requires a ' '
		 * and a '\0' as last char.
		 */
		if (modebits)
			litos(ptb->dbuf.t_mode, (info->f_mode|info->f_type) & 0xFFFF, 6);
		else
			litos(ptb->dbuf.t_mode, info->f_mode & 0xFFFF, 6);

		if (info->f_uid > MAXOCTAL6 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_UID;
		}
/* XXX */
		litos(ptb->dbuf.t_uid, info->f_uid & MAXOCTAL6, 6);

		if (info->f_gid > MAXOCTAL7 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_GID;
		}
/* XXX */
		litos(ptb->dbuf.t_gid, info->f_gid & MAXOCTAL6, 6);
	}

	if (info->f_rsize > MAXOCTAL11 && (props.pr_flags & PR_XHDR)) {
		info->f_xflags |= XF_SIZE;
	}
/* XXX */
	if (info->f_rsize <= MAXINT32) {
		litos(ptb->dbuf.t_size, (Ulong)info->f_rsize, 11);
	} else {
		if (info->f_rsize > MAXOCTAL11 &&
		   (props.pr_flags & PR_BASE256) == 0) {
			litos(ptb->dbuf.t_size, (Ulong)0, 11);
		} else {
			llitos(ptb->dbuf.t_size, (Ullong)info->f_rsize, 11);
		}
	}
	litos(ptb->dbuf.t_mtime, (Ulong)info->f_mtime, 11);
	ptb->dbuf.t_linkflag = XTTOUS(info->f_xftype);

	if (H_TYPE(hdrtype) == H_USTAR) {
		info_to_ustar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_PAX) {
		info_to_ustar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_SUNTAR) {
		info_to_ustar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_XSTAR) {
		info_to_xstar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_XUSTAR) {
		info_to_xstar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_EXUSTAR) {
		info_to_xstar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_GNUTAR) {
		info_to_gnutar(info, ptb);
	} else if (H_TYPE(hdrtype) == H_STAR) {
		info_to_star(info, ptb);
	}
}

/*
 * Used to create old star format header.
 */
LOCAL void
info_to_star(info, ptb)
	register FINFO	*info;
	register TCB	*ptb;
{
	ptb->dbuf.t_vers = STVERSION;
	litos(ptb->dbuf.t_filetype, info->f_filetype & 0xFFFF, 6);	/* XXX -> 7 ??? */
	litos(ptb->dbuf.t_type, info->f_type & 0xFFFF, 11);
#ifdef	needed
	/* XXX we need to do something if st_rdev is > 32 bits */
	if ((info->f_rdevmaj > MAXOCTAL7 || info->f_rdevmin > MAXOCTAL7) &&
	    (props.pr_flags & PR_XHDR)) {
		info->f_xflags |= XF_DEVMAJOR|XF_DEVMINOR;
	}
#endif
	litos(ptb->dbuf.t_rdev, info->f_rdev, 11);
#ifdef	DEV_MINOR_NONCONTIG
	ptb->dbuf.t_devminorbits = '@';
	if (props.pr_flags & PR_XHDR) {
		info->f_xflags |= XF_DEVMAJOR|XF_DEVMINOR;
	}
#else
	ptb->dbuf.t_devminorbits = '@' + minorbits;
#endif

	litos(ptb->dbuf.t_atime, (Ulong)info->f_atime, 11);
	litos(ptb->dbuf.t_ctime, (Ulong)info->f_ctime, 11);
/*	strcpy(ptb->dbuf.t_magic, stmagic);*/
	ptb->dbuf.t_magic[0] = 't';
	ptb->dbuf.t_magic[1] = 'a';
	ptb->dbuf.t_magic[2] = 'r';
	if (!numeric) {
		nameuid(ptb->dbuf.t_uname, STUNMLEN, info->f_uid);
		/* XXX Korrektes overflowchecking */
		if (ptb->dbuf.t_uname[STUNMLEN-1] != '\0' &&
		    props.pr_flags & PR_XHDR) {
			info->f_xflags |= XF_UNAME;
		}
		namegid(ptb->dbuf.t_gname, STGNMLEN, info->f_gid);
		/* XXX Korrektes overflowchecking */
		if (ptb->dbuf.t_gname[STGNMLEN-1] != '\0' &&
		    props.pr_flags & PR_XHDR) {
			info->f_xflags |= XF_GNAME;
		}
		if (*ptb->dbuf.t_uname) {
			info->f_uname = ptb->dbuf.t_uname;
			info->f_umaxlen = STUNMLEN;
		}
		if (*ptb->dbuf.t_gname) {
			info->f_gname = ptb->dbuf.t_gname;
			info->f_gmaxlen = STGNMLEN;
		}
	}

	if (is_sparse(info)) {
		if (info->f_size > MAXOCTAL11 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_REALSIZE;
		}
		/* XXX Korrektes overflowchecking fuer xhdr */
		if (info->f_size <= MAXINT32) {
			litos(ptb->xstar_in_dbuf.t_realsize, (Ulong)info->f_size, 11);
		} else {
			llitos(ptb->xstar_in_dbuf.t_realsize, (Ullong)info->f_size, 11);
		}
	}
}

/*
 * Used to create USTAR, PAX, SunTAR format header.
 */
LOCAL void
info_to_ustar(info, ptb)
	register FINFO	*info;
	register TCB	*ptb;
{
/*	strcpy(ptb->ustar_dbuf.t_magic, magic);*/
	ptb->ustar_dbuf.t_magic[0] = 'u';
	ptb->ustar_dbuf.t_magic[1] = 's';
	ptb->ustar_dbuf.t_magic[2] = 't';
	ptb->ustar_dbuf.t_magic[3] = 'a';
	ptb->ustar_dbuf.t_magic[4] = 'r';
/*	strncpy(ptb->ustar_dbuf.t_version, TVERSION, TVERSLEN);*/
	/*
	 * strncpy is slow: use handcrafted replacement.
	 */
	ptb->ustar_dbuf.t_version[0] = '0';
	ptb->ustar_dbuf.t_version[1] = '0';

	if (!numeric) {
		/* XXX Korrektes overflowchecking fuer xhdr */
		nameuid(ptb->ustar_dbuf.t_uname, TUNMLEN, info->f_uid);
		/* XXX Korrektes overflowchecking fuer xhdr */
		namegid(ptb->ustar_dbuf.t_gname, TGNMLEN, info->f_gid);
		if (*ptb->ustar_dbuf.t_uname) {
			info->f_uname = ptb->ustar_dbuf.t_uname;
			info->f_umaxlen = TUNMLEN;
		}
		if (*ptb->ustar_dbuf.t_gname) {
			info->f_gname = ptb->ustar_dbuf.t_gname;
			info->f_gmaxlen = TGNMLEN;
		}
	}
	if (info->f_rdevmaj > MAXOCTAL7 && (props.pr_flags & PR_XHDR)) {
		info->f_xflags |= XF_DEVMAJOR;
	}
/* XXX */
	litos(ptb->ustar_dbuf.t_devmajor, info->f_rdevmaj, 7);
#if	DEV_MINOR_BITS > 21		/* XXX */
	/*
	 * XXX The DEV_MINOR_BITS autoconf macro is only tested with 32 bit
	 * XXX ints but this does not matter as it is sufficient to know that
	 * XXX it will not fit into a 7 digit octal number.
	 */
	if (info->f_rdevmin > MAXOCTAL7) {
		extern	BOOL	hpdev;

		if (props.pr_flags & PR_XHDR) {
			info->f_xflags |= XF_DEVMINOR;
		}
		if ((info->f_rdevmin <= MAXOCTAL8) && hpdev) {
			char	c;

			/*
			 * Implement the method from HP-UX that allows 24 bit
			 * for the device minor number. Note that this method
			 * violates the POSIX specs.
			 */
			c = ptb->ustar_dbuf.t_prefix[0];
			litos(ptb->ustar_dbuf.t_devminor, info->f_rdevmin, 8);
			ptb->ustar_dbuf.t_prefix[0] = c;
		} else {
			/*
			 * XXX If we ever need to write more than a long into
			 * XXX devmajor, we need to change llitos() to check
			 * XXX for 7 char limits too.
			 */
/* XXX */
			btos(ptb->ustar_dbuf.t_devminor, info->f_rdevmin, 7);
		}
	} else
#endif
		{
		litos(ptb->ustar_dbuf.t_devminor, info->f_rdevmin, 7);
	}
}

/*
 * Used to create XSTAR, XUSTAR, EXUSTAR format header.
 */
LOCAL void
info_to_xstar(info, ptb)
	register FINFO	*info;
	register TCB	*ptb;
{
	info_to_ustar(info, ptb);
	litos(ptb->xstar_dbuf.t_atime, (Ulong)info->f_atime, 11);
	litos(ptb->xstar_dbuf.t_ctime, (Ulong)info->f_ctime, 11);

	/*
	 * Help recognition in isxmagic(), make sure that prefix[130] is null.
	 */
	ptb->xstar_dbuf.t_prefix[130] = '\0';

	if (H_TYPE(hdrtype) == H_XSTAR) {
/*		strcpy(ptb->xstar_dbuf.t_xmagic, stmagic);*/
		ptb->xstar_dbuf.t_xmagic[0] = 't';
		ptb->xstar_dbuf.t_xmagic[1] = 'a';
		ptb->xstar_dbuf.t_xmagic[2] = 'r';
	}
	if (is_sparse(info)) {
		if (info->f_size > MAXOCTAL11 && (props.pr_flags & PR_XHDR)) {
			info->f_xflags |= XF_REALSIZE;
		}
		/* XXX Korrektes overflowchecking fuer xhdr */
		if (info->f_size <= MAXINT32) {
			litos(ptb->xstar_in_dbuf.t_realsize, (Ulong)info->f_size, 11);
		} else {
			llitos(ptb->xstar_in_dbuf.t_realsize, (Ullong)info->f_size, 11);
		}
	}
}

/*
 * Used to create GNU tar format header.
 */
LOCAL void
info_to_gnutar(info, ptb)
	register FINFO	*info;
	register TCB	*ptb;
{
	strcpy(ptb->gnu_dbuf.t_magic, gmagic);

	if (!numeric) {
		nameuid(ptb->ustar_dbuf.t_uname, TUNMLEN, info->f_uid);
		namegid(ptb->ustar_dbuf.t_gname, TGNMLEN, info->f_gid);
		if (*ptb->ustar_dbuf.t_uname) {
			info->f_uname = ptb->ustar_dbuf.t_uname;
			info->f_umaxlen = TUNMLEN;
		}
		if (*ptb->ustar_dbuf.t_gname) {
			info->f_gname = ptb->ustar_dbuf.t_gname;
			info->f_gmaxlen = TGNMLEN;
		}
	}
	if (info->f_xftype == XT_CHR || info->f_xftype == XT_BLK) {
		litos(ptb->ustar_dbuf.t_devmajor, info->f_rdevmaj, 6);	/* XXX -> 7 ??? */
		litos(ptb->ustar_dbuf.t_devminor, info->f_rdevmin, 6);	/* XXX -> 7 ??? */
	}

	/*
	 * XXX GNU tar only fill this if doing a gnudump.
	 */
	litos(ptb->gnu_dbuf.t_atime, (Ulong)info->f_atime, 11);
	litos(ptb->gnu_dbuf.t_ctime, (Ulong)info->f_ctime, 11);

	if (is_sparse(info)) {
		if (info->f_size <= MAXINT32) {
			litos(ptb->gnu_in_dbuf.t_realsize, (Ulong)info->f_size, 11);
		} else {
			llitos(ptb->gnu_in_dbuf.t_realsize, (Ullong)info->f_size, 11);
		}
	}
}

EXPORT int
tcb_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	int	ret = 0;
	char	xname;
	char	xlink;
	char	xpfx;
	Ulong	ul;
	Ullong	ull;
	int	xt = XT_BAD;
	int	rxt = XT_BAD;
static	BOOL	posixwarn = FALSE;
static	BOOL	namewarn = FALSE;
static	BOOL	modewarn = FALSE;

	/*
	 * F_HAS_NAME is only used from list.c when the -listnew option is
	 * present. Keep f_lname and f_name, don't read LF_LONGLINK/LF_LONGNAME
	 * in this case.
	 */
	if ((info->f_flags & F_HAS_NAME) == 0)
		info->f_lname = ptb->dbuf.t_linkname;
	info->f_uname = info->f_gname = NULL;
	info->f_umaxlen = info->f_gmaxlen = 0L;
	info->f_xftype = XT_BAD;
	info->f_rxftype = XT_BAD;
	info->f_xflags = 0;
	info->f_contoffset = (off_t)0;
	info->f_flags &= F_HAS_NAME;
	info->f_fflags = 0L;
	info->f_nlink = 0;
	info->f_dir = NULL;

/* XXX JS Test */if (H_TYPE(hdrtype) >= H_CPIO_BASE) {
/* XXX JS Test */cpiotcb_to_info(ptb, info);
/* XXX JS Test */list_file(info);
/* XXX JS Test */return (ret);
/* XXX JS Test */}

	while (pr_isxheader(ptb->dbuf.t_linkflag)) {
		/*
		 * Handle POSIX.1-2001 extensions.
		 */
		if ((ptb->dbuf.t_linkflag == LF_XHDR ||
				    ptb->dbuf.t_linkflag == LF_VU_XHDR)) {
			ret = tcb_to_xhdr(ptb, info);
			if (ret != 0)
				return (ret);

			xt  = info->f_xftype;
			rxt = info->f_rxftype;
		}
		/*
		 * Handle very long names the old (star & gnutar) way.
		 */
		if ((info->f_flags & F_HAS_NAME) == 0 &&
					props.pr_nflags & PR_LONG_NAMES) {
			while (ptb->dbuf.t_linkflag == LF_LONGLINK ||
				    ptb->dbuf.t_linkflag == LF_LONGNAME) {
				ret = tcb_to_longname(ptb, info);
			}
		}
	}
	if (!pr_validtype(ptb->dbuf.t_linkflag)) {
		errmsgno(EX_BAD,
		"WARNING: Archive contains unknown typeflag '%c' (0x%02X).\n",
			ptb->dbuf.t_linkflag, ptb->dbuf.t_linkflag);
	}

	if (ptb->dbuf.t_name[NAMSIZ] == '\0') {
		if (ptb->dbuf.t_name[NAMSIZ-1] == '\0') {
			if (!nowarn && !modewarn) {
				errmsgno(EX_BAD,
				"WARNING: Archive violates POSIX 1003.1 (mode field starts with null byte).\n");
				modewarn = TRUE;
			}
		} else if (!nowarn && !namewarn) {
			errmsgno(EX_BAD,
			"WARNING: Archive violates POSIX 1003.1 (100 char filename is null terminated).\n");
			namewarn = TRUE;
		}
		ptb->dbuf.t_name[NAMSIZ] = ' ';
	}
	stoli(ptb->dbuf.t_mode, &info->f_mode);
	if (info->f_mode & ~07777) {
		if (!nowarn && !modebits && H_TYPE(hdrtype) == H_USTAR && !posixwarn) {
			errmsgno(EX_BAD,
			"WARNING: Archive violates POSIX 1003.1 (too many bits in mode field).\n");
			posixwarn = TRUE;
		}
		info->f_mode &= 07777;
	}
	if ((info->f_xflags & XF_UID) == 0)
		stoli(ptb->dbuf.t_uid, &info->f_uid);
	if ((info->f_xflags & XF_UID) == 0)
		stoli(ptb->dbuf.t_gid, &info->f_gid);
	if ((info->f_xflags & XF_SIZE) == 0) {
		stolli(ptb->dbuf.t_size, &ull);
		info->f_size = ull;
	}

	switch (ptb->dbuf.t_linkflag) {

	case LNKTYPE:
	case DIRTYPE:
	case CHRTYPE:
	case BLKTYPE:
	case FIFOTYPE:
	case LF_META:
		info->f_rsize = 0L;
		break;

	default:
		if ((info->f_xflags & XF_SIZE) == 0)
			info->f_rsize = info->f_size;
		break;
	}

	if ((info->f_xflags & XF_MTIME) == 0) {
		stoli(ptb->dbuf.t_mtime, &ul);
		info->f_mtime = (time_t)ul;
		info->f_mnsec = 0L;
	}

	info->f_typeflag = ptb->ustar_dbuf.t_typeflag;

	switch (H_TYPE(hdrtype)) {

	default:
	case H_TAR:
	case H_OTAR:
		tar_to_info(ptb, info);
		break;
	case H_PAX:
	case H_USTAR:
	case H_SUNTAR:
		ustar_to_info(ptb, info);
		break;
	case H_XSTAR:
	case H_XUSTAR:
	case H_EXUSTAR:
		xstar_to_info(ptb, info);
		break;
	case H_GNUTAR:
		gnutar_to_info(ptb, info);
		break;
	case H_STAR:
		star_to_info(ptb, info);
		break;
	}
	info->f_rxftype = info->f_xftype;
	if (rxt != XT_BAD) {
		info->f_rxftype = rxt;
		info->f_filetype = XTTOST(info->f_rxftype);
		info->f_type = XTTOIF(info->f_rxftype);
		/*
		 * XT_LINK may be any 'real' file type,
		 * XT_META may be either a regular file or a contigouos file.
		 */
		if (info->f_xftype != XT_LINK && info->f_xftype != XT_META)
			info->f_xftype = info->f_rxftype;
	}
	if (xt != XT_BAD) {
		info->f_xftype = xt;
	}

	/*
	 * Hack for list module (option -newest) ...
	 * Save and restore t_name[NAMSIZ] & t_linkname[NAMSIZ]
	 */
	xname = ptb->dbuf.t_name[NAMSIZ];
	ptb->dbuf.t_name[NAMSIZ] = '\0';	/* allow 100 chars in name */
	xlink = ptb->dbuf.t_linkname[NAMSIZ];
	ptb->dbuf.t_linkname[NAMSIZ] = '\0';/* allow 100 chars in linkname */
	xpfx = ptb->dbuf.t_prefix[PFXSIZ];
	ptb->dbuf.t_prefix[PFXSIZ] = '\0';	/* allow 155 chars in prefix*/

	/*
	 * Handle long name in posix split form now.
	 * Also copy ptb->dbuf.t_linkname[] if namelen is == 100.
	 */
	tcb_to_name(ptb, info);

	ptb->dbuf.t_name[NAMSIZ] = xname;	/* restore remembered value */
	ptb->dbuf.t_linkname[NAMSIZ] = xlink;	/* restore remembered value */
	ptb->dbuf.t_prefix[PFXSIZ] = xpfx;	/* restore remembered value */

	return (ret);
}

/*
 * Used to convert from old tar format header.
 */
LOCAL void
tar_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	register int	typeflag = ptb->ustar_dbuf.t_typeflag;

	if (ptb->dbuf.t_name[strlen(ptb->dbuf.t_name) - 1] == '/') {
		typeflag = DIRTYPE;
		info->f_filetype = F_DIR;
		info->f_rsize = (off_t)0;	/* XXX hier?? siehe oben */
	} else if (typeflag == SYMTYPE) {
		info->f_filetype = F_SLINK;
	} else if (typeflag != DIRTYPE) {
		info->f_filetype = F_FILE;
	}
	info->f_xftype = USTOXT(typeflag);
	info->f_type = XTTOIF(info->f_xftype);
	info->f_rdevmaj = info->f_rdevmin = info->f_rdev = 0;
	info->f_ctime = info->f_atime = info->f_mtime;
	info->f_cnsec = info->f_ansec = 0L;
}

/*
 * Used to convert from old star format header.
 */
LOCAL void
star_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	Ulong	id;
	Ullong	ull;
	int	mbits;

	version = ptb->dbuf.t_vers;
	if (ptb->dbuf.t_vers < STVERSION) {
		tar_to_info(ptb, info);
		return;
	}
	stoli(ptb->dbuf.t_filetype, &info->f_filetype);
	stoli(ptb->dbuf.t_type, &info->f_type);
	/*
	 * star Erweiterungen sind wieder ANSI kompatibel, d.h. linkflag
	 * hält den echten Dateityp (LONKLINK, LONGNAME, SPARSE ...)
	 */
	if(ptb->dbuf.t_linkflag < '1')
		info->f_xftype = IFTOXT(info->f_type);
	else
		info->f_xftype = USTOXT(ptb->ustar_dbuf.t_typeflag);

	stoli(ptb->dbuf.t_rdev, &info->f_rdev);
	if ((info->f_xflags & (XF_DEVMAJOR|XF_DEVMINOR)) !=
						(XF_DEVMAJOR|XF_DEVMINOR)) {
		mbits = ptb->dbuf.t_devminorbits - '@';
		if (mbits == 0) {
			static	BOOL	dwarned = FALSE;
			if (!dwarned) {
				errmsgno(EX_BAD,
#ifdef	DEV_MINOR_NONCONTIG
				"WARNING: Minor device numbers are non contiguous, devices may not be extracted correctly.\n");
#else
				"WARNING: The archiving system used non contiguous minor numbers, cannot extract devices correctly.\n");
#endif
				dwarned = TRUE;
			}
			/*
			 * Let us hope that both, the archiving and the
			 * extracting system use the same major()/minor()
			 * mapping.
			 */
			info->f_rdevmaj	= major(info->f_rdev);
			info->f_rdevmin	= minor(info->f_rdev);
		} else {
			/*
			 * Convert from remote major()/minor() mapping to
			 * local major()/minor() mapping.
			 */
			if (mbits < 0)		/* Old star format */
				mbits = 8;
			info->f_rdevmaj	= _dev_major(mbits, info->f_rdev);
			info->f_rdevmin	= _dev_minor(mbits, info->f_rdev);
			info->f_rdev = makedev(info->f_rdevmaj, info->f_rdevmin);
		}
	}

	if ((info->f_xflags & XF_ATIME) == 0) {
		stoli(ptb->dbuf.t_atime, &id);
		info->f_atime = (time_t)id;
		info->f_ansec = 0L;
	}
	if ((info->f_xflags & XF_CTIME) == 0) {
		stoli(ptb->dbuf.t_ctime, &id);
		info->f_ctime = (time_t)id;
		info->f_cnsec = 0L;
	}

	if ((info->f_xflags & XF_UNAME) == 0) {
		if (*ptb->dbuf.t_uname) {
			info->f_uname = ptb->dbuf.t_uname;
			info->f_umaxlen = STUNMLEN;
		}
	}
	if (info->f_uname) {
		if (!numeric && uidname(info->f_uname, info->f_umaxlen, &id))
			info->f_uid = id;
	}
	if ((info->f_xflags & XF_GNAME) == 0) {
		if (*ptb->dbuf.t_gname) {
			info->f_gname = ptb->dbuf.t_gname;
			info->f_gmaxlen = STGNMLEN;
		}
	}
	if (info->f_gname) {
		if (!numeric && gidname(info->f_gname, info->f_gmaxlen, &id))
			info->f_gid = id;
	}

	if (is_sparse(info)) {
		if ((info->f_xflags & XF_REALSIZE) == 0) {
			stolli(ptb->xstar_in_dbuf.t_realsize, &ull);
			info->f_size = ull;
		}
	}
	if (is_multivol(info)) {
		stolli(ptb->xstar_in_dbuf.t_offset, &ull);
		info->f_contoffset = ull;
	}
}

/*
 * Used to convert from USTAR, PAX, SunTAR format header.
 */
LOCAL void
ustar_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	Ulong	id;
	char	c;

	info->f_xftype = USTOXT(ptb->ustar_dbuf.t_typeflag);
	info->f_filetype = XTTOST(info->f_xftype);
	info->f_type = XTTOIF(info->f_xftype);

	if ((info->f_xflags & XF_UNAME) == 0) {
		if (*ptb->ustar_dbuf.t_uname) {
			info->f_uname = ptb->ustar_dbuf.t_uname;
			info->f_umaxlen = TUNMLEN;
		}
	}
	if (info->f_uname) {
		if (!numeric && uidname(info->f_uname, info->f_umaxlen, &id))
			info->f_uid = id;
	}
	if ((info->f_xflags & XF_GNAME) == 0) {
		if (*ptb->ustar_dbuf.t_gname) {
			info->f_gname = ptb->ustar_dbuf.t_gname;
			info->f_gmaxlen = TGNMLEN;
		}
	}
	if (info->f_gname) {
		if (!numeric && gidname(info->f_gname, info->f_gmaxlen, &id))
			info->f_gid = id;
	}

	if ((info->f_xflags & XF_DEVMAJOR) == 0)
		stoli(ptb->ustar_dbuf.t_devmajor, &info->f_rdevmaj);

	if ((info->f_xflags & XF_DEVMINOR) == 0) {
		if (ptb->ustar_dbuf.t_devminor[0] & 0x80) {
			stob(ptb->ustar_dbuf.t_devminor, &info->f_rdevmin, 7);
		} else {
			/*
			 * The 'tar' that comes with HP-UX writes illegal tar
			 * archives. It includes 8 characters in the minor
			 * field and allows to archive 24 bits for the minor
			 * device which are used by HP-UX. As we like to be
			 * able to read these archives, we need to convert
			 * the number carefully by temporarily writing a NULL
			 * to the next character and restoring the right
			 * content afterwards.
			 */
			c = ptb->ustar_dbuf.t_prefix[0];
			ptb->ustar_dbuf.t_prefix[0] = '\0';
			stoli(ptb->ustar_dbuf.t_devminor, &info->f_rdevmin);
			ptb->ustar_dbuf.t_prefix[0] = c;
		}
	}

	info->f_rdev = makedev(info->f_rdevmaj, info->f_rdevmin);

	/*
	 * ANSI Tar hat keine atime & ctime im Header!
	 */
	if ((info->f_xflags & XF_ATIME) == 0) {
		info->f_atime = info->f_mtime;
		info->f_ansec = 0L;
	}
	if ((info->f_xflags & XF_CTIME) == 0) {
		info->f_ctime = info->f_mtime;
		info->f_cnsec = 0L;
	}
}

/*
 * Used to convert from XSTAR, XUSTAR, EXUSTAR format header.
 */
LOCAL void
xstar_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	Ulong	ul;
	Ullong	ull;

	ustar_to_info(ptb, info);

	if ((info->f_xflags & XF_ATIME) == 0) {
		stoli(ptb->xstar_dbuf.t_atime, &ul);
		info->f_atime = (time_t)ul;
		info->f_ansec = 0L;
	}
	if ((info->f_xflags & XF_CTIME) == 0) {
		stoli(ptb->xstar_dbuf.t_ctime, &ul);
		info->f_ctime = (time_t)ul;
		info->f_cnsec = 0L;
	}

	if (is_sparse(info)) {
		if ((info->f_xflags & XF_REALSIZE) == 0) {
			stolli(ptb->xstar_in_dbuf.t_realsize, &ull);
			info->f_size = ull;
		}
	}
	if (is_multivol(info)) {
		stolli(ptb->xstar_in_dbuf.t_offset, &ull);
		info->f_contoffset = ull;
	}
}

/*
 * Used to convert from GNU tar format header.
 */
LOCAL void
gnutar_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	Ulong	ul;
	Ullong	ull;

	ustar_to_info(ptb, info);

	if ((info->f_xflags & XF_ATIME) == 0) {
		stoli(ptb->gnu_dbuf.t_atime, &ul);
		info->f_atime = (time_t)ul;
		info->f_ansec = 0L;
		if (info->f_atime == 0 && ptb->gnu_dbuf.t_atime[0] == '\0')
			info->f_atime = info->f_mtime;
	}

	if ((info->f_xflags & XF_CTIME) == 0) {
		stoli(ptb->gnu_dbuf.t_ctime, &ul);
		info->f_ctime = (time_t)ul;
		info->f_cnsec = 0L;
		if (info->f_ctime == 0 && ptb->gnu_dbuf.t_ctime[0] == '\0')
			info->f_ctime = info->f_mtime;
	}

	if (is_sparse(info)) {
		stolli(ptb->gnu_in_dbuf.t_realsize, &ull);
		info->f_size = ull;
	}
	if (is_multivol(info)) {
		stolli(ptb->gnu_dbuf.t_offset, &ull);
		info->f_contoffset = ull;
	}
}

/*
 * XXX vorerst nur zum Test!
 */
LOCAL void
cpiotcb_to_info(ptb, info)
	register TCB	*ptb;
	register FINFO	*info;
{
	Ulong	ul;

	astoo_cpio(&((char *)ptb)[6], &ul, 6);
	info->f_dev = ul;
	astoo_cpio(&((char *)ptb)[12], &ul, 6);
	info->f_ino = ul;
error("ino: %lld\n", (Llong)info->f_ino);
	astoo_cpio(&((char *)ptb)[18], &info->f_mode, 6);
error("mode: %lo\n", info->f_mode);
	info->f_type = info->f_mode & S_IFMT;
	info->f_mode = info->f_mode & 07777;
	info->f_xftype = IFTOXT(info->f_type);
	info->f_filetype = XTTOST(info->f_xftype);
	astoo_cpio(&((char *)ptb)[24], &info->f_uid, 6);
	astoo_cpio(&((char *)ptb)[30], &info->f_gid, 6);
	astoo_cpio(&((char *)ptb)[36], &info->f_nlink, 6);
	astoo_cpio(&((char *)ptb)[42], &info->f_rdev, 6);

	astoo_cpio(&((char *)ptb)[48], &ul, 11);
	info->f_atime = (time_t)ul;

	astoo_cpio(&((char *)ptb)[59], &info->f_namelen, 6);

	astoo_cpio(&((char *)ptb)[65], &ul, 11);
	info->f_size = ul;
info->f_rsize = info->f_size;
	info->f_name = &((char *)ptb)[76];
}

LOCAL int
ustoxt(ustype)
	char	ustype;
{
	/*
	 * Map ANSI types
	 */
	if (ustype >= REGTYPE && ustype <= CONTTYPE)
		return _USTOXT(ustype);

	/*
	 * Map Vendor Unique (Gnu tar & Star) types ANSI: "local enhancements"
	 */
	if ((props.pr_flags & (PR_LOCAL_STAR|PR_LOCAL_GNU)) &&
					ustype >= 'A' && ustype <= 'Z')
		return _VTTOXT(ustype);

	/*
	 * treat unknown types as regular files conforming to standard
	 */
	return (XT_FILE);
}

/* ARGSUSED */
EXPORT BOOL
ia_change(ptb, info)
	TCB	*ptb;
	FINFO	*info;
{
	char	buf[NAMSIZ+1];	/* XXX nur 100 chars ?? */
	char	ans;
	int	len;

	if (verbose)
		list_file(info);
	else
		vprint(info);
	if (nflag)
		return (FALSE);
	fprintf(vpr, "get/put ? Y(es)/N(o)/C(hange name) :");fflush(vpr);
	fgetline(tty, buf, 2);
	if ((ans = toupper(buf[0])) == 'Y')
		return (TRUE);
	else if (ans == 'C') {
		for(;;) {
			fprintf(vpr, "Enter new name:");
			fflush(vpr);
			if ((len = fgetline(tty, buf, sizeof buf)) == 0)
				continue;
			if (len > sizeof(buf) - 1)
				errmsgno(EX_BAD, "Name too long.\n");
			else
				break;
		}
		strcpy(info->f_name, buf);	/* XXX nur 100 chars ?? */
		if (xflag && newer(info))
			return (FALSE);
		return (TRUE);
	}
	return (FALSE);
}

LOCAL BOOL
checkeof(ptb)
	TCB	*ptb;
{
	if (!eofblock(ptb))
		return (FALSE);
	if (debug)
		errmsgno(EX_BAD, "First  EOF Block OK\n");
	markeof();

	if (readblock((char *)ptb) == EOF) {
		errmsgno(EX_BAD, "Incorrect EOF, second EOF block is missing.\n");
		return (TRUE);
	}
	if (!eofblock(ptb)) {
		if (!nowarn)
			errmsgno(EX_BAD, "WARNING: Partial (single block) EOF detected.\n");
		return (FALSE);
	}
	if (debug)
		errmsgno(EX_BAD, "Second EOF Block OK\n");
	return (TRUE);
}

LOCAL BOOL
eofblock(ptb)
	TCB	*ptb;
{
	register short	i;
	register char	*s = (char *) ptb;

	if (props.pr_nflags & PR_DUMB_EOF)
		return (ptb->dbuf.t_name[0] == '\0');

	for (i=0; i < TBLOCK; i++)
		if (*s++ != '\0')
			return (FALSE);
	return (TRUE);
}

/*
 * Convert octal string -> long int
 */
LOCAL void /*char **/
astoo_cpio(s,l, cnt)
	register char	*s;
		 Ulong	*l;
	register int	cnt;
{
	register Ulong	ret = 0L;
	register char	c;
	register int	t;
	
	for(;cnt > 0; cnt--) {
		c = *s++;
		if(isoctal(c))
			t = c - '0';
		else
			break;
		ret *= 8;
		ret += t;
	}
	*l = ret;
	/*return(s);*/
}

/*
 * Convert string -> long int
 */
LOCAL void /*char **/
stoli(s,l)
	register char	*s;
		 Ulong	*l;
{
	register Ulong	ret = 0L;
	register char	c;
	register int	t;
	
	while(*s == ' ')
		s++;

	for(;;) {
		c = *s++;
		if(isoctal(c))
			t = c - '0';
		else
			break;
		ret *= 8;
		ret += t;
	}
	*l = ret;
	/*return(s);*/
}

/*
 * Convert string -> long long int
 */
EXPORT void /*char **/
stolli(s,ull)
	register char	*s;
		 Ullong	*ull;
{
	register Ullong	ret = (Ullong)0;
	register char	c;
	register int	t;
	
	if (*((Uchar*)s) & 0x80) {
		stollb(s, ull, 11);
		return;
	}

	while(*s == ' ')
		s++;

	for(;;) {
		c = *s++;
		if(isoctal(c))
			t = c - '0';
		else
			break;
		ret *= 8;
		ret += t;
	}
	*ull = ret;
	/*return(s);*/
}

/*
 * Convert long int -> string.
 */
LOCAL void
litos(s, l, fieldw)
		 char	*s;
	register Ulong	l;
	register int	fieldw;
{
	register char	*p	= &s[fieldw+1];
	register char	fill	= props.pr_fillc;

	/*
	 * Bei 12 Byte Feldern würde hier das Nächste Feld überschrieben, wenn
	 * entgegen der normalen Reihenfolge geschrieben wird!
	 * Da der TCB sowieso vorher genullt wird ist es aber kein Problem
	 * das bei 8 Bytes Feldern notwendige Nullbyte wegzulassen.
	 */
/*XXX	*p = '\0';*/
	/*
	 * Das Zeichen nach einer Zahl.
	 * XXX Soll hier besser ein NULL Byte bei POSIX Tar hin?
	 * XXX Wuerde das Probleme mit einer automatischen Erkennung geben?
	 */
	*--p = ' ';
/*???	*--p = '\0';*/

	do {
		*--p = (l%8) + '0';	/* Compiler optimiert */

	} while (--fieldw > 0 && (l /= 8) > 0);

	switch (fieldw) {

	default:
		break;
	case 11:	*--p = fill;	/* FALLTHROUGH */
	case 10:	*--p = fill;	/* FALLTHROUGH */
	case 9:		*--p = fill;	/* FALLTHROUGH */
	case 8:		*--p = fill;	/* FALLTHROUGH */
	case 7:		*--p = fill;	/* FALLTHROUGH */
	case 6:		*--p = fill;	/* FALLTHROUGH */
	case 5:		*--p = fill;	/* FALLTHROUGH */
	case 4:		*--p = fill;	/* FALLTHROUGH */
	case 3:		*--p = fill;	/* FALLTHROUGH */
	case 2:		*--p = fill;	/* FALLTHROUGH */
	case 1:		*--p = fill;	/* FALLTHROUGH */
	case 0:		;
	}
}

/*
 * Convert long long int -> string.
 */
EXPORT void
llitos(s, ull, fieldw)
		 char	*s;
	register Ullong	ull;
	register int	fieldw;
{
	register char	*p	= &s[fieldw+1];
	register char	fill	= props.pr_fillc;

	/*
	 * Currently only used with fieldwidth == 11.
	 * XXX Large 8 byte fields are handled separately.
	 */
	if (/*fieldw == 11 &&*/ ull > MAXOCTAL11) {
		llbtos(s, ull, fieldw);
		return;
	}

	/*
	 * Bei 12 Byte Feldern würde hier das Nächste Feld überschrieben, wenn
	 * entgegen der normalen Reihenfolge geschrieben wird!
	 * Da der TCB sowieso vorher genullt wird ist es aber kein Problem
	 * das bei 8 Bytes Feldern notwendige Nullbyte wegzulassen.
	 */
/*XXX	*p = '\0';*/
	/*
	 * Das Zeichen nach einer Zahl.
	 * XXX Soll hier besser ein NULL Byte bei POSIX Tar hin?
	 * XXX Wuerde das Probleme mit einer automatischen Erkennung geben?
	 */
	*--p = ' ';
/*???	*--p = '\0';*/

	do {
		*--p = (ull%8) + '0';	/* Compiler optimiert */

	} while (--fieldw > 0 && (ull /= 8) > 0);

	switch (fieldw) {

	default:
		break;
	case 11:	*--p = fill;	/* FALLTHROUGH */
	case 10:	*--p = fill;	/* FALLTHROUGH */
	case 9:		*--p = fill;	/* FALLTHROUGH */
	case 8:		*--p = fill;	/* FALLTHROUGH */
	case 7:		*--p = fill;	/* FALLTHROUGH */
	case 6:		*--p = fill;	/* FALLTHROUGH */
	case 5:		*--p = fill;	/* FALLTHROUGH */
	case 4:		*--p = fill;	/* FALLTHROUGH */
	case 3:		*--p = fill;	/* FALLTHROUGH */
	case 2:		*--p = fill;	/* FALLTHROUGH */
	case 1:		*--p = fill;	/* FALLTHROUGH */
	case 0:		;
	}
}

/*
 * Convert binary (base 256) string -> long int.
 */
LOCAL void /*char **/
stob(s, l, fieldw)
	register char	*s;
		 Ulong	*l;
	register int	fieldw;
{
	register Ulong	ret = 0L;
	register Uchar	c;
	
	c = *s++ & 0x7F;
	ret = c * 256;

	while (--fieldw >= 0) {
		c = *s++;
		ret *= 256;
		ret += c;
	}
	*l = ret;
	/*return(s);*/
}

/*
 * Convert binary (base 256) string -> long long int.
 */
LOCAL void /*char **/
stollb(s, ull, fieldw)
	register char	*s;
		 Ullong	*ull;
	register int	fieldw;
{
	register Ullong	ret = 0L;
	register Uchar	c;
	
	c = *s++ & 0x7F;
	ret = c * 256;

	while (--fieldw >= 0) {
		c = *s++;
		ret *= 256;
		ret += c;
	}
	*ull = ret;
	/*return(s);*/
}

/*
 * Convert long int -> binary (base 256) string.
 */
LOCAL void
btos(s, l, fieldw)
		 char	*s;
	register Ulong	l;
	register int	fieldw;
{
	register char	*p	= &s[fieldw+1];

	do {
		*--p = l%256;	/* Compiler optimiert */

	} while (--fieldw > 0 && (l /= 256) > 0);

	s[0] |= 0x80;
}

/*
 * Convert long long int -> binary (base 256) string.
 */
LOCAL void
llbtos(s, ull, fieldw)
		 char	*s;
	register Ullong	ull;
	register int	fieldw;
{
	register char	*p	= &s[fieldw+1];

	do {
		*--p = ull%256;	/* Compiler optimiert */

	} while (--fieldw > 0 && (ull /= 256) > 0);

	s[0] |= 0x80;
}

/*--------------------------------------------------------------------------*/
EXPORT void
dump_info(info)
	FINFO	*info;
{
	error("f_name:      '%s'\n", info->f_name);
	error("f_namelen:   %lu\n", info->f_namelen);
	error("f_lname:     '%s'\n", info->f_lname);
	error("f_lnamelen:  %lu\n", info->f_lnamelen);
	error("f_uname:     '%s'\n", info->f_uname);
	error("f_umaxlen:   %lu\n", info->f_umaxlen);
	error("f_gname:     '%s'\n", info->f_gname);
	error("f_gmaxlen:   %lu\n", info->f_gmaxlen);

	error("f_dir:       %p\n", info->f_dir);
	error("f_dirlen:    %d\n", info->f_dirlen);
	error("f_dev:       0x%llX\n", (Ullong)info->f_dev);
	error("f_ino:       %llu\n", (Ullong)info->f_ino);
	error("f_nlink:     %lu\n", info->f_nlink);
	error("f_mode:      0%lo\n", info->f_mode);
	error("f_uid:       %lu\n", info->f_uid);
	error("f_gid:       %lu\n", info->f_gid);

	error("f_size:      %lld\n", (Llong)info->f_size);
	error("f_rsize:     %lld\n", (Llong)info->f_rsize);
	error("f_contoffset:%lld\n", (Llong)info->f_contoffset);
	error("f_flags:     0x%lX\n", info->f_flags);
	error("f_xflags:    0x%lX\n", info->f_xflags);
	error("f_xftype:    %lu (%s)\n", info->f_xftype, XTTONAME(info->f_xftype));
	error("f_rxftype:   %lu (%s)\n", info->f_rxftype, XTTONAME(info->f_rxftype));
	error("f_filetype:  %lu\n", info->f_filetype);
	error("f_typeflag:  '%c'\n", info->f_typeflag);
	error("f_type:      0%lo\n", info->f_type);
	error("f_rdev:      %lu\n", info->f_rdev);
	error("f_rdevmaj:   %lu\n", info->f_rdevmaj);
	error("f_rdevmin:   %lu\n", info->f_rdevmin);
	error("f_atime:     %.24s +0.%9.9lu s\n", ctime(&info->f_atime), info->f_ansec);
	error("f_mtime:     %.24s +0.%9.9lu s\n", ctime(&info->f_mtime), info->f_mnsec);
	error("f_ctime:     %.24s +0.%9.9lu s\n", ctime(&info->f_ctime), info->f_cnsec);
	error("f_fflags:    0x%lX\n", info->f_fflags);
}


syntax highlighted by Code2HTML, v. 0.9.1