[comp.sources.misc] v04i118: fcpio - a fast cpio partial replacement

joe@dayton.DHDSC.MN.ORG (Joseph P. Larson) (10/07/88)

Posting-number: Volume 4, Issue 118
Submitted-by: "Joseph P. Larson" <joe@dayton.DHDSC.MN.ORG>
Archive-name: fcpio

In any case, I wrote up the enclosed program one day when I got a little
too annoyed with cpio while dealing with some rather large cpio archives.
It doesn't need a makefile (I just say make fcpio).  The two include files
are from our <dayton/> directory, so people will need to patch the .c a
little to specify where these came from.  (They AREN'T replacements for
the regular includes of the same name!)

Hope someone finds this useful.  -Joe


----Cut Here----

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	fcpio.doc
#	fcpio.c
#	math.h
#	stdtyp.h
# This archive created: Wed Oct  5 09:54:01 1988
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'fcpio.doc'
then
	echo shar: "will not over-write existing file 'fcpio.doc'"
else
cat << \SHAR_EOF > 'fcpio.doc'
PROGRAM:

	fcpio -- a fast cpio for seekable devices


SYNOPSIS:

	fcpio [-options] archive-name [extract-pattern1 [ ... extract-patternn]]

	Options implemented to date:

		x - extract files
		t - table of contents


DESCRIPTION:

	fcpio is a partial cpio (tm Ma Bell) replacement.  It is very useful
	when performing extracts or retrieving a table of contents from a
	disk-resident cpio archive.  This is due to use of seeks to skip over
	unwanted data.  (Our copy of cpio reads through everything as it must
	assume it can't seek.)  Thus, a -t on a 3.3 Meg file runs at output
	(9600 baud) speeds with fcpio but has long pauses with cpio at all the
	long (> 20K) files within the archive.  An extract of a few items is
	also very fast.

	The extract patterns do not support wild carding or anything fancy --
	fcpio will extract any files that begin with one of the extract patterns
	(or all files if no extract pattern is specified).


AUTHOR:

	Joe Larson
	Dayton-Hudson Department Store Company
	700 On the Mall
	Minneapolis, Minnesota  55402

	612-375-3537


BUGS:

	1. Does not support non-seekable devices (like pipes?)
	2. Does not maintain the files original magic number, UID or GID.
	3. May not handle garbage at the end of the archive terribly well.
		(Our cpio puts an item called "TRAILER!!!" at the end, so fcpio
		equates that with EOF as well as ending on EOF.  Other cpios
		may behave differently....)
SHAR_EOF
fi
if test -f 'fcpio.c'
then
	echo shar: "will not over-write existing file 'fcpio.c'"
else
cat << \SHAR_EOF > 'fcpio.c'
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <dayton/math.h>
#include <dayton/stdtyp.h>

/*
 * fcpio - fast cpio for seek-able devices.
 *
 * Usage:
 *
 *		fcpio [-options] cpio-archive [extract-pattern-list]
 *
 * Implemented options are:
 *
 *		x - extract the named extract-pattern files.
 *		t - print a table of contents.
 *
 * In extract mode, the extracted files are the ones whose names begin
 *		with anything in the named list.  Skipped items are displayed
 *		by a "." to indicate progress being made.
 *
 * Author:
 *
 *		Joe Larson
 *		Dayton-Hudson Dept. Store Co.
 *		700 On The Mall
 *		Minneapolis, Minnesota 55402
 *
 *		UUCP:joe@dayton
 */

struct Hdr
{
	short	h_magic,
			h_dev;
	ushort	h_ino,
			h_mode,
			h_uid,
			h_gid,
			h_nlink,
			h_rdev,
			h_mtime[2],
			h_namesize,
			h_filesize[2];
	char	h_name[80];
};

#define READBUFSIZE 100000
char readbuf[READBUFSIZE];

main(argc,argv)
int argc;
char **argv;
{
	struct Hdr hstruct;		/* Indiv. file header in ifile.				*/
	int		ifile,			/* Archive file								*/
			cur_pos,		/* Position in ifile						*/
			fpos,			/* Another position in ifile				*/
			dummy,			/* Holds length of current file in ifile	*/
			i,				/* Counter for lots of things				*/
			ilens[20],		/* Lengths of x_names[].					*/
			count,			/* # of files we've skipped.				*/
			x_count,		/* Number of x_names[].						*/
			isize;			/* Used in seeking...						*/
	char	*a_name,		/* Name of archive file						*/
			*options,		/* Option string from cmnd line.			*/
			*x_names[20];	/* Name of extract file beginnings.			*/
	bool	do_extract,		/* Extract files?							*/
			do_table,		/* Do a table of contents?					*/
			x_here;			/* Extract this file?						*/

	a_name = NULL;
	options = "x";
	x_count = 0;
	do_extract = 0;
	do_table = 0;
	for (i = 1; i < argc; i++)
	{
		if (*argv[i] == '-')
			options = argv[i]+1;
		else if (a_name == NULL)
			a_name = argv[i];
		else
			x_names[x_count++] = argv[i];
	}
	if (a_name == NULL)
	{
		usage(argv[0]);
		exit(0);
	}
	do_extract = (strchr(options, 'x') != NULL);
	do_table = (strchr(options, 't') != NULL);
	ifile = open(a_name, O_RDONLY);
	if (ifile == -1)
	{
		perror(a_name);
		exit(errno);
	}
	for (i = 0; i < x_count; i++)
		ilens[i] = strlen(x_names[i]);
	cur_pos = 0;
	count = 0;
	while(1)
	{
		if (read(ifile, &hstruct, sizeof(struct Hdr)) < sizeof(struct Hdr))
			break;
		if (!strcmp(hstruct.h_name, "TRAILER!!!"))
			break;
		dummy = (hstruct.h_filesize[0] << 16) + hstruct.h_filesize[1];
		if (do_table)
		{
			fprintf(stderr, "%6ld %s\n", dummy, hstruct.h_name);
			fflush(stderr);
		}
		isize = (hstruct.h_namesize + 1) & ~(1L);
		if (do_extract)
		{
			x_here = !(x_count);	/* If no x_names, extract all */
			for (i = 0; i < x_count; i++)
				if (!strncmp(x_names[i], hstruct.h_name, ilens[i]))
				{
					x_here = 1;
					break;
				}
			if (x_here)
			{
				if (count)
					fprintf(stderr, "\n");
				count = 0;
				fpos = cur_pos + sizeof(struct Hdr) - 80 + isize;
				fprintf(stderr, "X: %s", hstruct.h_name);
				fflush(stderr);
				copy_file(ifile, fpos, &hstruct, dummy);
				fprintf(stderr, ".\n");
			}
			else
			{
				fprintf(stderr, ".");
				if (++count == 60)
				{
					fprintf(stderr, "\n");
					count = 0;
				}
				else
					fflush(stderr);
			}
		}
		cur_pos += (dummy + sizeof(struct Hdr) - 80 + isize + 1) & ~(1L);
		lseek(ifile, cur_pos, 0);
	}
	close(ifile);
}


copy_file(ifile, fpos, hstruct, ilen)
int ifile, fpos, ilen;
struct Hdr *hstruct;
/*
 * Copy out this file.  This routine still needs to do some things.
 *
 * For instance, no attempt is made to preserve the original files
 *		magic number, c/m/a times, owner or group.
 */
{
	int ofile, rlen, nlen;
	char name[132], *c1, *c2;

	if (hstruct->h_mode & 0040000L)
	{
		make_path(hstruct->h_name, 1);
		return;
	}
	for (c1 = NULL, c2=hstruct->h_name; *c2; c2++)
		if (*c2 == '/')
			c1 = c2;
	if (c1 != NULL)
	{
		nlen = c1 - hstruct->h_name;
		strncpy(name, hstruct->h_name, nlen);
		name[nlen] = 0;
		make_path(name, 1);
	}
	ofile = open(hstruct->h_name, O_WRONLY | O_CREAT, hstruct->h_mode&07777L);
	if (ofile == -1)
	{
		perror(hstruct->h_name);
		return;
	}
	lseek(ifile, fpos, 0L);
	while (ilen > 0)
	{
		rlen = MIN(READBUFSIZE, ilen);
		rlen = read(ifile, readbuf, rlen);
		if (rlen > 0)
			write(ofile, readbuf, rlen);
		else
			break;
		ilen -= rlen;
	}
	close(ofile);
}



usage(arg)
char *arg;
{
	fprintf(stderr, "Usage: %s infilename\n", arg);
}

make_path(p, flag)
char *p, flag;
/*
 * Make sure this entire pathname exists.
 */
{
	short ilen;
	char name[132], *c1, *c2;

	if (access(p, 00))
	{
		if (flag)
		{
			for (c1 = NULL, c2 = p; *c2; c2++)
			{
				if (*c2 == '/')
				{
					if (c1 != NULL)
					{
						ilen = c2 - p;
						strncpy(name, p, ilen);
						name[ilen] = 0;
						if (access(name, 00))
							make_path(p, 0);
					}
					c1 = c2;
				}
			}
		}
		if (!fork())
		{
			execlp("mkdir", "mkdir", p, 0);
			exit(errno);
		}
		else
			wait(0);
	}
}
SHAR_EOF
fi
if test -f 'math.h'
then
	echo shar: "will not over-write existing file 'math.h'"
else
cat << \SHAR_EOF > 'math.h'
/*
 * Dayton's math library definitions.
 */

#define		MIN(x,y)	((x < y) ? x : y)
#define		MAX(x,y)	((x > y) ? x : y)
#define		ABS(x)		((x < 0) ? -x : x)
SHAR_EOF
fi
if test -f 'stdtyp.h'
then
	echo shar: "will not over-write existing file 'stdtyp.h'"
else
cat << \SHAR_EOF > 'stdtyp.h'
/*
 * Standard types and defines for Dayton-Hudson.
 *
 */

#include	<stdtyp.h>

#ifndef		TRUE
#define		TRUE	(1)
#endif

#ifndef		FALSE
#define		FALSE	(0)
#endif

#define ULONG unsigned long
SHAR_EOF
fi
exit 0
#	End of shell archive