#!mkcmd
# $Id: tart.m,v 1.4 2001/07/23 22:23:59 ksb Exp $
#
from '<stdio.h>'
from '<stdlib.h>'
from '<sys/stat.h>'

basename "tart" ""

require "std_help.m" "std_control.m" "std_version.m"
require "samepath.m"
%i
static char rcsid[] =
	"$Id: tart.m,v 1.4 2001/07/23 22:23:59 ksb Exp $";
#define TART_BLOCKS	20		/* this is for old school tars */
#define TART_BLOCK_SIZE	512

/* status for tar output
 */
typedef struct TBnode {
	int icount;
	FILE *fpout;
	char aacblock[TART_BLOCKS][TART_BLOCK_SIZE];
} TAR_BUF;
%%

boolean 'b' {
	named "fNoPad"
	init '0'
	help "do not pad output files to an even multiple of 20 blocks"
}
boolean 'r' {
	named "fReverse"
	init "0"
	help "reverse the output streams -- matches to stdout"
}
boolean '1' {
	named "fOnce"
	init "0"
	help "remove a pattern from the list after it matches the first file"
}
file ["wb"] 'O' {
	named "fpCatch" "pcCatch"
	param "caught"
	init '"/dev/null"'
	help "a tar file of the nodes that matched the patterns"
}

list {
	param "patterns"
	help "filenames to divert away from the output archive"
}

%c
/* Copy an input tar chunk to the named file, or if pcHeader		(ksb)
 * is (char *)0 close the tar file off at even 20b
 */
static void
CopyOut(pTB, lSkip, pcHeader)
TAR_BUF *pTB;
long lSkip;
char *pcHeader;
{
	if ((char *)0 == pcHeader) {
		if (0 == pTB->icount) {
			return;
		}
		if (fNoPad) {
			goto no_pad;
		}
		for (/* nada */; pTB->icount < TART_BLOCKS; ++pTB->icount) {
			memset((void *)pTB->aacblock[pTB->icount], '\000', TART_BLOCK_SIZE);
		}
	} else {
		memmove((void *)pTB->aacblock[pTB->icount], pcHeader, TART_BLOCK_SIZE);
		for (++pTB->icount; lSkip > 0; --lSkip) {
			if (TART_BLOCKS == pTB->icount) {
				if (TART_BLOCKS != fwrite((void *)pTB->aacblock[0], TART_BLOCK_SIZE, pTB->icount, pTB->fpout)) {
					fprintf(stderr, "%s: fwrite: %s\n", progname, strerror(errno));
					exit(8);
				}
				pTB->icount = 0;
			}
			if (1 == fread((void *)pTB->aacblock[pTB->icount++], TART_BLOCK_SIZE, 1, stdin)) {
				continue;
			}
			fprintf(stderr, "%s: fread: stdin: %s\n", progname, strerror(errno));
			exit(4);
		}
	}
	if (TART_BLOCKS != pTB->icount) {
		return;
	}

no_pad:
	if (pTB->icount != fwrite((void *)pTB->aacblock[0], TART_BLOCK_SIZE, pTB->icount, pTB->fpout)) {
		fprintf(stderr, "%s: fwrite: %s\n", progname, strerror(errno));
		exit(9);
	}
	pTB->icount = 0;
}

/* return 1 if the filename matches any of the patterns			(ksb)
 */
int
GlobCmp(iCount, ppcPats, pcName)
int iCount;
char **ppcPats, *pcName;
{
	register int i;

	for (i = 0; i < iCount; ++i) {
		if ((char *)0 == ppcPats[i] || !SamePath(ppcPats[i], pcName)) {
			continue;
		}
		if (fOnce) {
			ppcPats[i] = (char *)0;
		}
		return 1;
	}
	return 0;
}

/* Function(s) to parse header of tar file for names and file sizes	(sea)
 * Position(range) for the following fields:
 * header[0 - 100] is the name
 * header[125 - 136] is the size (in octal)
 * Actual file starts at next block of 512 bytes
 */
void
list_func(int argc, char **argv)
{
	register long lSkip;
	register int iCmp;
	auto ldiv_t ldBlocks;
	auto char acHeader[TART_BLOCK_SIZE];
	auto long nLen;
	auto int cKeep;
	auto TAR_BUF TBOut, TBCaught;

	TBOut.icount = TBCaught.icount = 0;
	TBOut.fpout = stdout;
	TBCaught.fpout = fpCatch;
	while (1 == fread(acHeader, sizeof(acHeader), 1, stdin)) {
		if ('\000' == acHeader[0]) {
			break;
		}
		sscanf(acHeader+125, "%lo", &nLen);
		ldBlocks = ldiv(nLen, (long)TART_BLOCK_SIZE);
		lSkip = ldBlocks.quot + (ldBlocks.rem != 0);

		cKeep = acHeader[101];
		acHeader[101] = '\000';
		iCmp = fReverse ^ GlobCmp(argc, argv, acHeader);
		if (fVerbose) {
			fprintf(stderr, "%s is %ld block%s, %d\n", acHeader, lSkip, (1 != lSkip) + "s", iCmp);
		}
		acHeader[101] = cKeep;

		if (!fExec) {
			if (0 != fseek(stdin, lSkip*(long)TART_BLOCK_SIZE, SEEK_CUR)) {
				fprintf(stderr, "%s: fseek error\n", progname);
				break;
			}
			continue;
		}
		CopyOut(iCmp ? &TBCaught : &TBOut, lSkip, acHeader);
	}
	CopyOut(&TBOut, 0L, (char *)0);
	CopyOut(&TBCaught, 0L, (char *)0);
}
