Arc v5.21 (4 of 6)

egray at fthood.UUCP egray at fthood.UUCP
Tue Jan 3 09:53:00 AEST 1989


This is part 4 (of 6) to the Arc v5.21 distribution package.

Emmet P. Gray				US Army, HQ III Corps & Fort Hood
...!uunet!uiucuxc!fthood!egray		Attn: AFZF-DE-ENV
					Directorate of Engineering & Housing
					Environmental Management Office
					Fort Hood, TX 76544-5057

------------------------------------------------------------------------------
#! /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:
#	arclzw.c
#	arcmatch.c
#	arcmisc.c
#	arcpack.c
#	arcrun.c
#	arcs.h
# This archive created: Sun Jan  1 12:48:23 1989
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'arclzw.c'" '(22114 characters)'
if test -f 'arclzw.c'
then
	echo shar: "will not over-write existing file 'arclzw.c'"
else
sed 's/^X//' << \SHAR_EOF > 'arclzw.c'
X/*
X * $Header: arclzw.c,v 1.6 88/07/31 18:49:49 hyc Exp $
X */
X
X/*
X * ARC - Archive utility - ARCLZW
X * 
X * Version 2.03, created on 10/24/86 at 11:46:22
X * 
X * (C) COPYRIGHT 1985,86 by System Enhancement Associates; ALL RIGHTS RESERVED
X * 
X * By:  Thom Henderson
X * 
X * Description: This file contains the routines used to implement Lempel-Zev
X * data compression, which calls for building a coding table on the fly.
X * This form of compression is especially good for encoding files which
X * contain repeated strings, and can often give dramatic improvements over
X * traditional Huffman SQueezing.
X * 
X * Language: Computer Innovations Optimizing C86
X * 
X * Programming notes: In this section I am drawing heavily on the COMPRESS
X * program from UNIX.  The basic method is taken from "A Technique for High
X * Performance Data Compression", Terry A. Welch, IEEE Computer Vol 17, No 6
X * (June 1984), pp 8-19.  Also see "Knuth's Fundamental Algorithms", Donald
X * Knuth, Vol 3, Section 6.4.
X * 
X * As best as I can tell, this method works by tracing down a hash table of code
X * strings where each entry has the property:
X * 
X * if <string> <char> is in the table then <string> is in the table.
X */
X#include <stdio.h>
X#include "arc.h"
X
Xvoid	putc_pak(), abort(), putc_ncr();
Xint	getc_unp();
X#if	MSDOS
Xchar	*setmem();
X#else
Xchar	*memset();
X#endif
X
Xstatic void	putcode();
X/* definitions for older style crunching */
X
X#define FALSE    0
X#define TRUE     !FALSE
X#define TABSIZE  4096
X#define NO_PRED  0xFFFF
X#define EMPTY    0xFFFF
X#define NOT_FND  0xFFFF
X
Xstatic unsigned short inbuf;	/* partial input code storage */
Xstatic int      sp;		/* current stack pointer */
X
Xstruct entry {		/* string table entry format */
X	char            used;	/* true when this entry is in use */
X	unsigned char   follower;	/* char following string */
X	unsigned short  next;	/* ptr to next in collision list */
X	unsigned short  predecessor;	/* code for preceeding string */
X};            /* string_tab[TABSIZE];	   the code string table */
X
X
X/* definitions for the new dynamic Lempel-Zev crunching */
X
X#define BITS   12		/* maximum bits per code */
X#define HSIZE  5003		/* 80% occupancy */
X#define INIT_BITS 9		/* initial number of bits/code */
X
Xstatic int      n_bits;		/* number of bits/code */
Xstatic int      maxcode;	/* maximum code, given n_bits */
X#define MAXCODE(n)      ((1<<(n)) - 1)	/* maximum code calculation */
Xstatic int      maxcodemax = 1 << BITS;	/* largest possible code (+1) */
X
Xstatic char     buf[BITS];	/* input/output buffer */
X
Xstatic unsigned char lmask[9] =	/* left side masks */
X{
X 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00
X};
Xstatic unsigned char rmask[9] =	/* right side masks */
X{
X 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
X};
X
Xstatic int      offset;		/* byte offset for code output */
Xstatic long     in_count;	/* length of input */
Xstatic long     bytes_out;	/* length of compressed output */
Xstatic long     bytes_ref;	/* output quality reference */
Xstatic long     bytes_last;	/* output size at last checkpoint */
Xstatic unsigned short ent;
X
X/*
X * To save much memory (which we badly need at this point), we overlay the
X * table used by the previous version of Lempel-Zev with those used by the
X * new version.  Since no two of these routines will be used together, we can
X * safely do this.
X */
X
Xextern long     htab[HSIZE];		/* hash code table   (crunch) */
Xextern unsigned short codetab[HSIZE];	/* string code table (crunch) */
Xstatic struct	entry *string_tab=(struct entry *)htab;	/* old crunch string table */
X
Xstatic unsigned short *prefix=codetab;	/* prefix code table (uncrunch) */
Xstatic unsigned char *suffix=(unsigned char *)htab;	/* suffix table (uncrunch) */
X
Xstatic int      free_ent;	/* first unused entry */
Xstatic int      firstcmp;	/* true at start of compression */
Xextern unsigned char stack[HSIZE];	/* local push/pop stack */
X
X/*
X * block compression parameters -- after all codes are used up, and
X * compression rate changes, start over.
X */
X
Xstatic int      clear_flg;
X#define CHECK_GAP 2048		/* ratio check interval */
Xstatic long     checkpoint;
Xvoid            upd_tab();
X
X/*
X * the next two codes should not be changed lightly, as they must not lie
X * within the contiguous general code space.
X */
X#define FIRST   257		/* first free entry */
X#define CLEAR   256		/* table clear output code */
X
X/*
X * The cl_block() routine is called at each checkpoint to determine if
X * compression would likely improve by resetting the code table.  The method
X * chosen to determine this is based on empirical observation that, in
X * general, every 2k of input data should compress at least as well as the
X * first 2k of input.
X */
X
Xstatic          void
Xcl_block(t)			/* table clear for block compress */
X	FILE           *t;	/* our output file */
X{
X	checkpoint = in_count + CHECK_GAP;
X
X	if (bytes_ref) {
X		if (bytes_out - bytes_last > bytes_ref) {
X			setmem(htab, HSIZE * sizeof(long), 0xff);
X			free_ent = FIRST;
X			clear_flg = 1;
X			putcode(CLEAR, t);
X			bytes_ref = 0;
X		}
X	} else
X		bytes_ref = bytes_out - bytes_last;
X
X	bytes_last = bytes_out;	/* remember where we were */
X}
X
X/*****************************************************************
X *
X * Output a given code.
X * Inputs:
X *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
X *              that n_bits =< (LONG)wordsize - 1.
X * Outputs:
X *      Outputs code to the file.
X * Assumptions:
X *      Chars are 8 bits long.
X * Algorithm:
X *      Maintain a BITS character long buffer (so that 8 codes will
X * fit in it exactly).  When the buffer fills up empty it and start over.
X */
X
Xstatic          void
Xputcode(code, t)		/* output a code */
X	int             code;	/* code to output */
X	FILE           *t;	/* where to put it */
X{
X	int             r_off = offset;	/* right offset */
X	int             bits = n_bits;	/* bits to go */
X	char           *bp = buf;	/* buffer pointer */
X	int             n;	/* index */
X
X	register int	ztmp;
X
X	if (code >= 0) {	/* if a real code *//* Get to the first byte. */
X		bp += (r_off >> 3);
X		r_off &= 7;
X
X		/*
X		 * Since code is always >= 8 bits, only need to mask the
X		 * first hunk on the left. 
X		 */
X		ztmp = (code << r_off) & lmask[r_off];
X		*bp = (*bp & rmask[r_off]) | ztmp;
X		bp++;
X		bits -= (8 - r_off);
X		code >>= (8 - r_off);
X
X		/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X		if (bits >= 8) {
X			*bp++ = code;
X			code >>= 8;
X			bits -= 8;
X		}
X		/* Last bits. */
X		if (bits)
X			*bp = code;
X		offset += n_bits;
X
X		if (offset == (n_bits << 3)) {
X			bp = buf;
X			bits = n_bits;
X			bytes_out += bits;
X			do
X				putc_pak(*bp++, t);
X			while (--bits);
X			offset = 0;
X		}
X		/*
X		 * If the next entry is going to be too big for the code
X		 * size, then increase it, if possible. 
X		 */
X		if (free_ent > maxcode || clear_flg > 0) {
X			/*
X			 * Write the whole buffer, because the input side
X			 * won't discover the size increase until after
X			 * it has read it. 
X			 */
X			if (offset > 0) {
X				bp = buf;	/* reset pointer for writing */
X				bytes_out += n = n_bits;
X				while (n--)
X					putc_pak(*bp++, t);
X			}
X			offset = 0;
X
X			if (clear_flg) {	/* reset if clearing */
X				maxcode = MAXCODE(n_bits = INIT_BITS);
X				clear_flg = 0;
X			} else {/* else use more bits */
X				n_bits++;
X				if (n_bits == BITS)
X					maxcode = maxcodemax;
X				else
X					maxcode = MAXCODE(n_bits);
X			}
X		}
X	} else {		/* dump the buffer on EOF */
X		bytes_out += n = (offset + 7) / 8;
X
X		if (offset > 0)
X			while (n--)
X				putc_pak(*bp++, t);
X		offset = 0;
X	}
X}
X
X/*****************************************************************
X *
X * Read one code from the standard input.  If EOF, return -1.
X * Inputs:
X *      cmpin
X * Outputs:
X *      code or -1 is returned.
X */
X
Xstatic          int
Xgetcode(f)			/* get a code */
X	FILE           *f;	/* file to get from */
X{
X	int             code;
X	static int      loffset = 0, size = 0;
X	int             r_off, bits;
X	unsigned char  *bp = (unsigned char *) buf;
X
X	if (clear_flg > 0 || loffset >= size || free_ent > maxcode) {
X		/*
X		 * If the next entry will be too big for the current code
X		 * size, then we must increase the size. This implies
X		 * reading a new buffer full, too. 
X		 */
X		if (free_ent > maxcode) {
X			n_bits++;
X			if (n_bits == BITS)
X				maxcode = maxcodemax;	/* won't get any bigger
X							 * now */
X			else
X				maxcode = MAXCODE(n_bits);
X		}
X		if (clear_flg > 0) {
X			maxcode = MAXCODE(n_bits = INIT_BITS);
X			clear_flg = 0;
X		}
X		for (size = 0; size < n_bits; size++) {
X			if ((code = getc_unp(f)) == EOF)
X				break;
X			else
X				buf[size] = (char) code;
X		}
X		if (size <= 0)
X			return -1;	/* end of file */
X
X		loffset = 0;
X		/* Round size down to integral number of codes */
X		size = (size << 3) - (n_bits - 1);
X	}
X	r_off = loffset;
X	bits = n_bits;
X
X	/*
X	 * Get to the first byte. 
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X
X	/* Get first part (low order bits) */
X	code = (*bp++ >> r_off);
X	bits -= 8 - r_off;
X	r_off = 8 - r_off;	/* now, offset into code word */
X
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if (bits >= 8) {
X		code |= *bp++ << r_off;
X		r_off += 8;
X		bits -= 8;
X	}
X	/* high order bits. */
X	code |= (*bp & rmask[bits]) << r_off;
X	loffset += n_bits;
X
X	return code & MAXCODE(BITS);
X}
X
X/*
X * compress a file
X * 
X * Algorithm:  use open addressing double hashing (no chaining) on the prefix
X * code / next character combination.  We do a variant of Knuth's algorithm D
X * (vol. 3, sec. 6.4) along with G. Knott's relatively-prime secondary probe.
X * Here, the modular division first probe is gives way to a faster
X * exclusive-or manipulation.  Also do block compression with an adaptive
X * reset, where the code table is cleared when the compression ratio
X * decreases, but after the table fills.  The variable-length output codes
X * are re-sized at this point, and a special CLEAR code is generated for the
X * decompressor.
X */
X
Xvoid
Xinit_cm(t)			/* initialize for compression */
X	FILE           *t;	/* where compressed file goes */
X{
X	offset = 0;
X	bytes_out = bytes_last = 1;
X	bytes_ref = 0;
X	clear_flg = 0;
X	in_count = 1;
X	checkpoint = CHECK_GAP;
X	maxcode = MAXCODE(n_bits = INIT_BITS);
X	free_ent = FIRST;
X	setmem(htab, HSIZE * sizeof(long), 0xff);
X	n_bits = INIT_BITS;	/* set starting code size */
X
X	putc_pak(BITS, t);	/* note our max code length */
X
X	firstcmp = 1;		/* next byte will be first */
X}
X
Xvoid
Xputc_cm(c, t)			/* compress a character */
X	unsigned char   c;	/* character to compress */
X	FILE           *t;	/* where to put it */
X{
X	static long     fcode;
X	static int      hshift;
X	int             i;
X	int             disp;
X
X	if (firstcmp) {		/* special case for first byte */
X		ent = c;	/* remember first byte */
X
X		hshift = 0;
X		for (fcode = (long) HSIZE; fcode < 65536L; fcode *= 2L)
X			hshift++;
X		hshift = 8 - hshift;	/* set hash code range bound */
X
X		firstcmp = 0;	/* no longer first */
X		return;
X	}
X	in_count++;
X
X	fcode = (long) (((long) c << BITS) + ent);
X	i = (c << hshift) ^ ent;/* xor hashing */
X
X	if (htab[i] == fcode) {
X		ent = codetab[i];
X		return;
X	} else if (htab[i] < 0)	/* empty slot */
X		goto nomatch;
X	disp = HSIZE - i;	/* secondary hash (after G.Knott) */
X	if (i == 0)
X		disp = 1;
X
Xprobe:
X	if ((i -= disp) < 0)
X		i += HSIZE;
X
X	if (htab[i] == fcode) {
X		ent = codetab[i];
X		return;
X	}
X	if (htab[i] > 0)
X		goto probe;
X
Xnomatch:
X	putcode(ent, t);
X	ent = c;
X	if (free_ent < maxcodemax) {
X		codetab[i] = free_ent++;	/* code -> hashtable */
X		htab[i] = fcode;
X	}
X	if (in_count >= checkpoint)
X		cl_block(t);	/* check for adaptive reset */
X}
X
Xlong
Xpred_cm(t)			/* finish compressing a file */
X	FILE           *t;	/* where to put it */
X{
X	putcode(ent, t);	/* put out the final code */
X	putcode(-1, t);		/* tell output we are done */
X
X	return bytes_out;	/* say how big it got */
X}
X
X/*
X * Decompress a file.  This routine adapts to the codes in the file building
X * the string table on-the-fly; requiring no table to be stored in the
X * compressed file.  The tables used herein are shared with those of the
X * compress() routine.  See the definitions above.
X */
X
Xvoid
Xdecomp(f, t)			/* decompress a file */
X	FILE           *f;	/* file to read codes from */
X	FILE           *t;	/* file to write text to */
X{
X	unsigned char  *stackp;
X	int             finchar;
X	int             code, oldcode, incode;
X
X	if ((code = getc_unp(f)) != BITS)
X		abort("File packed with %d bits, I can only handle %d", code, BITS);
X
X	n_bits = INIT_BITS;	/* set starting code size */
X	clear_flg = 0;
X
X	/*
X	 * As above, initialize the first 256 entries in the table. 
X	 */
X	maxcode = MAXCODE(n_bits = INIT_BITS);
X	setmem(prefix, 256 * sizeof(short), 0);	/* reset decode string table */
X	for (code = 255; code >= 0; code--)
X		suffix[code] = (unsigned char) code;
X
X	free_ent = FIRST;
X
X	finchar = oldcode = getcode(f);
X	if (oldcode == -1)	/* EOF already? */
X		return;		/* Get out of here */
X	putc_ncr((unsigned char) finchar, t);	/* first code must be 8 bits=char */
X	stackp = stack;
X
X	while ((code = getcode(f)) > -1) {
X		if (code == CLEAR) {	/* reset string table */
X			setmem(prefix, 256 * sizeof(short), 0);
X			clear_flg = 1;
X			free_ent = FIRST - 1;
X			if ((code = getcode(f)) == -1)	/* O, untimely death! */
X				break;
X		}
X		incode = code;
X		/*
X		 * Special case for KwKwK string. 
X		 */
X		if (code >= free_ent) {
X			if (code > free_ent) {
X				if (warn) {
X					printf("Corrupted compressed file.\n");
X					printf("Invalid code %d when max is %d.\n",
X						code, free_ent);
X				}
X				nerrs++;
X				return;
X			}
X			*stackp++ = finchar;
X			code = oldcode;
X		}
X		/*
X		 * Generate output characters in reverse order 
X		 */
X		while (code >= 256) {
X			*stackp++ = suffix[code];
X			code = prefix[code];
X		}
X		*stackp++ = finchar = suffix[code];
X
X		/*
X		 * And put them out in forward order 
X		 */
X		do
X			putc_ncr(*--stackp, t);
X		while (stackp > stack);
X
X		/*
X		 * Generate the new entry. 
X		 */
X		if ((code = free_ent) < maxcodemax) {
X			prefix[code] = (unsigned short) oldcode;
X			suffix[code] = finchar;
X			free_ent = code + 1;
X		}
X		/*
X		 * Remember previous code. 
X		 */
X		oldcode = incode;
X	}
X}
X
X
X/*************************************************************************
X * Please note how much trouble it can be to maintain upwards            *
X * compatibility.  All that follows is for the sole purpose of unpacking *
X * files which were packed using an older method.                        *
X *************************************************************************/
X
X
X/*
X * The h() pointer points to the routine to use for calculating a hash value.
X * It is set in the init routines to point to either of oldh() or newh().
X * 
X * oldh() calculates a hash value by taking the middle twelve bits of the square
X * of the key.
X * 
X * newh() works somewhat differently, and was tried because it makes ARC about
X * 23% faster.  This approach was abandoned because dynamic Lempel-Zev
X * (above) works as well, and packs smaller also.  However, inadvertent
X * release of a developmental copy forces us to leave this in.
X */
X
Xstatic unsigned short(*h) ();	/* pointer to hash function */
X
Xstatic unsigned short
Xoldh(pred, foll)		/* old hash function */
X	unsigned short  pred;	/* code for preceeding string */
X	unsigned char   foll;	/* value of following char */
X{
X	long            local;	/* local hash value */
X
X	local = ((pred + foll) | 0x0800) & 0xFFFF; /* create the hash key */
X	local *= local;		/* square it */
X	return (local >> 6) & 0x0FFF;	/* return the middle 12 bits */
X}
X
Xstatic unsigned short
Xnewh(pred, foll)		/* new hash function */
X	unsigned short  pred;	/* code for preceeding string */
X	unsigned char   foll;	/* value of following char */
X{
X	return (((pred + foll) & 0xFFFF) * 15073) & 0xFFF; /* faster hash */
X}
X
X/*
X * The eolist() function is used to trace down a list of entries with
X * duplicate keys until the last duplicate is found.
X */
X
Xstatic unsigned short
Xeolist(index)			/* find last duplicate */
X	unsigned short  index;
X{
X	int             temp;
X
X	while (temp = string_tab[index].next)	/* while more duplicates */
X		index = temp;
X
X	return index;
X}
X
X/*
X * The hash() routine is used to find a spot in the hash table for a new
X * entry.  It performs a "hash and linear probe" lookup, using h() to
X * calculate the starting hash value and eolist() to perform the linear
X * probe.  This routine DOES NOT detect a table full condition.  That MUST be
X * checked for elsewhere.
X */
X
Xstatic unsigned short
Xhash(pred, foll)		/* find spot in the string table */
X	unsigned short  pred;	/* code for preceeding string */
X	unsigned char   foll;	/* char following string */
X{
X	unsigned short  local, tempnext;	/* scratch storage */
X	struct entry   *ep;	/* allows faster table handling */
X
X	local = (*h) (pred, foll);	/* get initial hash value */
X
X	if (!string_tab[local].used)	/* if that spot is free */
X		return local;	/* then that's all we need */
X
X	else {			/* else a collision has occured */
X		local = eolist(local);	/* move to last duplicate */
X
X		/*
X		 * We must find an empty spot. We start looking 101 places
X		 * down the table from the last duplicate. 
X		 */
X
X		tempnext = (local + 101) & 0x0FFF;
X		ep = &string_tab[tempnext];	/* initialize pointer */
X
X		while (ep->used) {	/* while empty spot not found */
X			if (++tempnext == TABSIZE) {	/* if we are at the end */
X				tempnext = 0;	/* wrap to beginning of table */
X				ep = string_tab;
X			} else
X				++ep;	/* point to next element in table */
X		}
X
X		/*
X		 * local still has the pointer to the last duplicate, while
X		 * tempnext has the pointer to the spot we found.  We use
X		 * this to maintain the chain of pointers to duplicates. 
X		 */
X
X		string_tab[local].next = tempnext;
X
X		return tempnext;
X	}
X}
X
X/*
X * The init_tab() routine is used to initialize our hash table. You realize,
X * of course, that "initialize" is a complete misnomer.
X */
X
Xstatic          void
Xinit_tab()
X{				/* set ground state in hash table */
X	unsigned int    i;	/* table index */
X
X	setmem((char *) string_tab, sizeof(string_tab), 0);
X
X	for (i = 0; i < 256; i++)	/* list all single byte strings */
X		upd_tab(NO_PRED, i);
X
X	inbuf = EMPTY;		/* nothing is in our buffer */
X}
X
X/*
X * The upd_tab routine is used to add a new entry to the string table. As
X * previously stated, no checks are made to ensure that the table has any
X * room.  This must be done elsewhere.
X */
X
Xvoid
Xupd_tab(pred, foll)		/* add an entry to the table */
X	unsigned short  pred;	/* code for preceeding string */
X	unsigned short  foll;	/* character which follows string */
X{
X	struct entry   *ep;	/* pointer to current entry */
X
X	/* calculate offset just once */
X
X	ep = &string_tab[hash(pred, foll)];
X
X	ep->used = TRUE;	/* this spot is now in use */
X	ep->next = 0;		/* no duplicates after this yet */
X	ep->predecessor = pred;	/* note code of preceeding string */
X	ep->follower = foll;	/* note char after string */
X}
X
X/*
X * This algorithm encoded a file into twelve bit strings (three nybbles). The
X * gocode() routine is used to read these strings a byte (or two) at a time.
X */
X
Xstatic          int
Xgocode(fd)			/* read in a twelve bit code */
X	FILE           *fd;	/* file to get code from */
X{
X	unsigned short  localbuf, returnval;
X	int             temp;
X
X	if (inbuf == EMPTY) {	/* if on a code boundary */
X		if ((temp = getc_unp(fd)) == EOF)	/* get start of next
X							 * code */
X			return EOF;	/* pass back end of file status */
X		localbuf = temp & 0xFF;	/* mask down to true byte value */
X		if ((temp = getc_unp(fd)) == EOF)
X			/* get end of code, * start of next */
X			return EOF;	/* this should never happen */
X		inbuf = temp & 0xFF;	/* mask down to true byte value */
X
X		returnval = ((localbuf << 4) & 0xFF0) + ((inbuf >> 4) & 0x00F);
X		inbuf &= 0x000F;/* leave partial code pending */
X	} else {		/* buffer contains first nybble */
X		if ((temp = getc_unp(fd)) == EOF)
X			return EOF;
X		localbuf = temp & 0xFF;
X
X		returnval = localbuf + ((inbuf << 8) & 0xF00);
X		inbuf = EMPTY;	/* note no hanging nybbles */
X	}
X	return returnval;	/* pass back assembled code */
X}
X
Xstatic          void
Xpush(c)				/* push char onto stack */
X	int             c;	/* character to push */
X{
X	stack[sp] = ((char) c);	/* coerce integer into a char */
X
X	if (++sp >= TABSIZE)
X		abort("Stack overflow\n");
X}
X
Xstatic          int
Xpop()
X{				/* pop character from stack */
X	if (sp > 0)
X		return ((int) stack[--sp]);	/* leave ptr at next empty
X						 * slot */
X
X	else
X		return EMPTY;
X}
X
X/***** LEMPEL-ZEV DECOMPRESSION *****/
X
Xstatic int      code_count;	/* needed to detect table full */
Xstatic int      firstc;		/* true only on first character */
X
Xvoid
Xinit_ucr(inew)			/* get set for uncrunching */
X	int             inew;	/* true to use new hash function */
X{
X	if (inew)		/* set proper hash function */
X		h = newh;
X	else
X		h = oldh;
X
X	sp = 0;			/* clear out the stack */
X	init_tab();		/* set up atomic code definitions */
X	code_count = TABSIZE - 256;	/* note space left in table */
X	firstc = 1;		/* true only on first code */
X}
X
Xint
Xgetc_ucr(f)			/* get next uncrunched byte */
X	FILE           *f;	/* file containing crunched data */
X{
X	int             code, newcode;
X	static int      oldcode, finchar;
X	struct entry   *ep;	/* allows faster table handling */
X
X	if (firstc) {		/* first code is always known */
X		firstc = FALSE;	/* but next will not be first */
X		oldcode = gocode(f);
X		return finchar = string_tab[oldcode].follower;
X	}
X	if (!sp) {		/* if stack is empty */
X		if ((code = newcode = gocode(f)) == EOF)
X			return EOF;
X
X		ep = &string_tab[code];	/* initialize pointer */
X
X		if (!ep->used) {/* if code isn't known */
X			code = oldcode;
X			ep = &string_tab[code];	/* re-initialize pointer */
X			push(finchar);
X		}
X		while (ep->predecessor != NO_PRED) {
X			push(ep->follower);	/* decode string backwards */
X			code = ep->predecessor;
X			ep = &string_tab[code];
X		}
X
X		push(finchar = ep->follower);	/* save first character also */
X
X		/*
X		 * The above loop will terminate, one way or another, with
X		 * string_tab[code].follower equal to the first character in
X		 * the string. 
X		 */
X
X		if (code_count) {	/* if room left in string table */
X			upd_tab(oldcode, finchar);
X			--code_count;
X		}
X		oldcode = newcode;
X	}
X	return pop();		/* return saved character */
X}
SHAR_EOF
if test 22114 -ne "`wc -c < 'arclzw.c'`"
then
	echo shar: "error transmitting 'arclzw.c'" '(should have been 22114 characters)'
fi
fi
echo shar: "extracting 'arcmatch.c'" '(3257 characters)'
if test -f 'arcmatch.c'
then
	echo shar: "will not over-write existing file 'arcmatch.c'"
else
sed 's/^X//' << \SHAR_EOF > 'arcmatch.c'
X/*
X * $Header: arcmatch.c,v 1.6 88/07/31 18:50:18 hyc Exp $
X */
X
X/*
X * ARC - Archive utility - ARCMATCH
X * 
X * Version 2.17, created on 12/17/85 at 20:32:18
X * 
X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X * 
X * By:  Thom Henderson
X * 
X * Description: This file contains service routines needed to maintain an
X * archive.
X * 
X * Language: Computer Innovations Optimizing C86
X */
X#include <stdio.h>
X#include "arc.h"
X
Xint	strcmp(), strlen();
Xvoid	abort();
Xchar	*strcpy();
X
Xint
Xmatch(n, t)			/* test name against template */
X	char           *n;	/* name to test */
X	char           *t;	/* template to test against */
X{
X#ifdef LOCAL
X	extern int unix_names;
X#endif /* LOCAL */
X#if	MTS
X	fortran         patbuild(), patmatch(), patfree();
X	static int      patlen = (-1);
X	static int      patwork = 0;
X	static int      patswch = 0;
X	char            patccid[4];
X	static char     patchar[2] = "?";
X	static char     oldtemp[16] = 0;
X	int             namlen, RETCODE;
X
X	if (strcmp(t, oldtemp)) {
X		if (patwork)
X			patfree(&patwork);
X		strcpy(oldtemp, t);
X		patlen = strlen(oldtemp);
X		patbuild(oldtemp, &patlen, &patwork, &patswch, patccid, patchar, _retcode RETCODE);
X		if (RETCODE > 8) {
X			printf("MTS: patbuild returned %d\n", RETCODE);
X			abort("bad wildcard in filename");
X		}
X	}
X	namlen = strlen(n);
X	patmatch(n, &namlen, &patwork, _retcode RETCODE);
X	switch (RETCODE) {
X	case 0:
X		return (1);
X	case 4:
X		return (0);
X	default:
X		abort("wildcard pattern match failed");
X	}
X}
X
X#else
X#if	!UNIX
X	upper(n);
X	upper(t);		/* avoid case problems */
X#endif	/* UNIX */
X#if	GEMDOS
X	char *strstr(), *i;	/* allow "*.*" to mean '*' */
X	if (i=strstr(t,"*.*")) {
X		i++;
X		*i='\0';
X	}
X	return(pnmatch(n, t, 0));
X#else
X	/* first match name part */
X
X#ifdef LOCAL
X	if (!unix_names) {
X		upper(n);
X		upper(t);
X	}
X#endif /* LOCAL */
X
X	while ((*n && *n != '.') || (*t && *t != '.')) {
X		if (*n != *t && *t != '?') {	/* match fail? */
X			if (*t != '*')	/* wildcard fail? */
X				return 0;	/* then no match */
X			else {	/* else jump over wildcard */
X				while (*n && *n != '.')
X					n++;
X				while (*t && *t != '.')
X					t++;
X				break;	/* name part matches wildcard */
X			}
X		} else {	/* match good for this char */
X			n++;	/* advance to next char */
X			t++;
X		}
X	}
X
X	if (*n && *n == '.')
X		n++;		/* skip extension delimiters */
X	if (*t && *t == '.')
X		t++;
X
X	/* now match name part */
X
X	while (*n || *t) {
X		if (*n != *t && *t != '?') {	/* match fail? */
X			if (*t != '*')	/* wildcard fail? */
X				return 0;	/* then no match */
X			else
X				return 1;	/* else good enough */
X		} else {	/* match good for this char */
X			n++;	/* advance to next char */
X			t++;
X		}
X	}
X
X	return 1;		/* match worked */
X#endif	/* GEMDOS */
X}
X#endif
X
Xvoid
Xrempath(nargs, arg)		/* remove paths from filenames */
X	int             nargs;	/* number of names */
X	char           *arg[];	/* pointers to names */
X{
X	char           *i, *rindex();	/* string index, reverse indexer */
X	int             n;	/* index */
X
X	for (n = 0; n < nargs; n++) {	/* for each supplied name */
X		if (!(i = rindex(arg[n], '\\')))	/* search for end of
X							 * path */
X			if (!(i = rindex(arg[n], '/')))
X				i = rindex(arg[n], ':');
X		if (i)		/* if path was found */
X			arg[n] = i + 1;	/* then skip it */
X	}
X}
SHAR_EOF
if test 3257 -ne "`wc -c < 'arcmatch.c'`"
then
	echo shar: "error transmitting 'arcmatch.c'" '(should have been 3257 characters)'
fi
fi
echo shar: "extracting 'arcmisc.c'" '(8946 characters)'
if test -f 'arcmisc.c'
then
	echo shar: "will not over-write existing file 'arcmisc.c'"
else
sed 's/^X//' << \SHAR_EOF > 'arcmisc.c'
X/*
X * Miscellaneous routines to get ARC running on non-MSDOS systems...
X * $Header: arcmisc.c,v 1.8 88/07/31 18:50:56 hyc Exp $ 
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "arc.h"
X
X#if	MSDOS
X#include <dir.h>
X#include <stat.h>
X#endif
X
X#if	GEMDOS
X#include <osbind.h>
X#include <stat.h>
Xchar           *index(), *rindex();
X
Xvoid 
Xexitpause()
X{
X	while (Cconis())
X		Cnecin();
X	fprintf(stderr, "Press any key to continue: ");
X	fflush(stderr);
X	Cnecin();
X	fprintf(stderr, "\n");
X}
X
Xint
Xchdir(dirname)
X	char           *dirname;
X{
X	char           *i;
X	int             drv;
X
X	i = dirname;
X	if ((i = index(dirname, ':')) != NULL) {
X		drv = i[-1];
X		i++;		/* Move past device spec */
X		if (drv > '\'')
X			drv -= 'a';
X		else
X			drv -= 'A';
X		if (drv >= 0 && drv < 16)
X			Dsetdrv(drv);
X	}
X	if (*i != '\0')
X		return (Dsetpath(i));
X}
X#endif
X
X#if	UNIX
X#include <sys/types.h>
X#include <sys/dir.h>
X#include <sys/stat.h>
X	int	rename(), unlink();
X#endif
X
X#if	0
X#include <dirent.h>
X#define DIRECT dirent
X#else
X#define DIRECT direct
X#endif
X
X#if	BSD
Xchar	*
Xmemset(s, c, n)		/* oops. Thought it was standard BSD, but my Sun */
X	char	*s;	/* fooled me again. -- hyc */
X	int	c, n;
X{
X	register int i;
X	for(i=0;i<n;i++)
X		s[i]=c;
X	return(s);
X}
X#endif
X
Xchar           *strcpy(), *strcat(), *malloc();
Xint             strlen(), strcmp(), match();
X
Xint
Xmove(oldnam, newnam)
X	char           *oldnam, *newnam;
X{
X	FILE           *fopen(), *old, *mnew;
X#if	!MTS
X	struct stat     oldstat;
X#endif
X	char           *strcpy();
X	void		filecopy();
X#if	GEMDOS
X	if (Frename(0, oldnam, newnam))
X#else
X	if (rename(oldnam, newnam))
X#endif
X#if	!MTS
X	{
X		if (stat(oldnam, &oldstat))	/* different partition? */
X			return (-1);
X		old = fopen(oldnam, OPEN_R);
X		if (old == NULL)
X			return (-1);
X		mnew = fopen(newnam, OPEN_W);
X		if (mnew == NULL)
X			return (-1);
X		filecopy(old, mnew, oldstat.st_size);
X		return(unlink(oldnam));
X	}
X	return 0;
X#else
X	return(-1);
X#endif
X}
X
Xstatic void
X_makefn(source, dest)
X	char           *source;
X	char           *dest;
X{
X	int             j;
X#if	MSDOS
X	char	       *setmem();
X#else
X	char	       *memset();
X#endif
X
X	setmem(dest, 17, 0);	/* clear result field */
X	for (j = 0; *source && *source != '.'; ++source)
X		if (j < 8)
X			dest[j++] = *source;
X	for (j = 9; *source; ++source)
X		if (j < 13)
X			dest[j++] = *source;
X}
X/*
X * make a file name using a template 
X */
X
Xchar           *
Xmakefnam(rawfn, template, result)
X	char           *rawfn;	/* the original file name */
X	char           *template;	/* the template data */
X	char           *result;	/* where to place the result */
X{
X	char            et[17], er[17], rawbuf[STRLEN], *i, *rindex();
X
X	*rawbuf = 0;
X	strcpy(rawbuf, rawfn);
X#if	MTS
X	i = rawbuf;
X	if (rawbuf[0] == tmpchr[0]) {
X		i++;
X		strcpy(rawfn, i);
X	} else
X#endif
X	if ((i = rindex(rawbuf, CUTOFF))) {
X		i++;
X		strcpy(rawfn, i);
X	}
X#if	DOS
X	else if ((i = rindex(rawbuf, ':'))) {
X		i++;
X		strcpy(rawfn, i);
X	}
X#endif
X	if (i)
X		*i = 0;
X	else
X		*rawbuf = 0;
X
X	_makefn(template, et);
X	_makefn(rawfn, er);
X	*result = 0;		/* assure no data */
X	strcat(result, rawbuf);
X	strcat(result, er[0] ? er : et);
X	strcat(result, er[9] ? er + 9 : et + 9);
X	return ((char *) &result[0]);
X}
X
X#if	MSDOS || SYSV
X
Xint
Xalphasort(dirptr1, dirptr2)
X	struct DIRECT **dirptr1, **dirptr2;
X{
X	return (strcmp((*dirptr1)->d_name, (*dirptr2)->d_name));
X}
X
X#endif
X
X#ifdef LOCAL
Xvoid
Xlower(string)
X	char           *string;
X{
X	char           *p;
X
X	for (p = string; *p; p++)
X		if (isupper(*p))
X			*p = tolower(*p);
X}
X#endif /* LOCAL */
X
Xvoid
Xupper(string)
X	char           *string;
X{
X	char           *p;
X
X	for (p = string; *p; p++)
X		if (islower(*p))
X			*p = toupper(*p);
X}
X/* VARARGS1 */
Xvoid
Xabort(s, arg1, arg2, arg3)
X	char           *s;
X{
X	fprintf(stderr, "ARC: ");
X	fprintf(stderr, s, arg1, arg2, arg3);
X	fprintf(stderr, "\n");
X#if	UNIX
X	perror("UNIX");
X#endif
X#if	GEMDOS
X	exitpause();
X#endif
X	exit(1);
X}
X
X#if	!MTS
X
Xchar           *
Xgcdir(dirname)
X	char           *dirname;
X
X{
X	char           *getwd();
X#if	GEMDOS
X	int             drv;
X	char           *buf;
X#endif
X	if (dirname == NULL || strlen(dirname) == 0)
X		dirname = (char *) malloc(1024);
X
X#if	!GEMDOS
X	getwd(dirname);
X#else
X	buf = dirname;
X	*buf++ = (drv = Dgetdrv()) + 'A';
X	*buf++ = ':';
X	Dgetpath(buf, 0);
X#endif
X	return (dirname);
X}
X
X#if	UNIX
Xchar           *pattern;	/* global so that fmatch can use it */
X#endif
X
Xchar           *
Xdir(filename)		/* get files, one by one */
X	char           *filename;	/* template, or NULL */
X{
X#if	GEMDOS
X	static int      Nnum = 0;
X	static DMABUFFER dbuf, *saved;
X	char           *name;
X
X	if (Nnum == 0) {	/* first call */
X		saved = (DMABUFFER *) Fgetdta();
X		Fsetdta(&dbuf);
X		if (Fsfirst(filename, 0) == 0) {
X			name = malloc(FNLEN);
X			strcpy(name, dbuf.d_fname);
X			Nnum++;
X			return (name);
X		} else {
X			Fsetdta(saved);
X			return (NULL);
X		}
X	} else {
X		if (Fsnext() == 0) {
X			name = malloc(FNLEN);
X			strcpy(name, dbuf.d_fname);
X			return (name);
X		} else {
X			Nnum = 0;
X			Fsetdta(saved);
X			return (NULL);
X		}
X	}
X}
X#else
X	static struct DIRECT **namelist;
X	static char   **NameList;
X	static char	namecopy[STRLEN], *dirname;
X#if	UNIX
X	int             alphasort();
X	int             scandir();
X#endif				/* UNIX */
X	int             fmatch(), free();
X	static int      Nnum = 0, ii;
X	char		*rindex();
X
X
X	if (Nnum == 0) {	/* first call */
X		strcpy(namecopy,filename);
X		if(pattern=rindex(namecopy,CUTOFF)) {
X			*pattern = 0;
X			pattern++;
X			dirname = namecopy;
X		} else {
X			pattern = filename;
X			dirname = ".";
X		}
X		Nnum = scandir(dirname, &namelist, fmatch, alphasort);
X		NameList = (char **) malloc(Nnum * sizeof(char *));
X		for (ii = 0; ii < Nnum; ii++) {
X			(NameList)[ii] = malloc(strlen(namelist[ii]->d_name) + 1);
X			strcpy((NameList)[ii], namelist[ii]->d_name);
X		}
X		ii = 0;
X	}
X	if (ii >= Nnum) {	/* all out of files */
X		if (Nnum) {	/* there were some files found */
X			for (ii = 0; ii < Nnum; ii++)
X				free(namelist[ii]);
X			free(namelist);
X		}
X		Nnum = 0;
X		return (NULL);
X	} else {
X		return ((NameList)[ii++]);
X	}
X}
X
X/*
X * Filename match - here, * matches everything 
X */
X
Xint
Xfmatch(direntry)
X	struct DIRECT  *direntry;
X{
X	char           *string;
X
X	string = direntry->d_name;
X
X	if (!strcmp(pattern, "") || !strcmp(pattern, "*.*") || !strcmp(pattern, "*"))
X		return (1);
X#if	UNIX
X	return(!strcmp(pattern, string));
X#else
X	return (match(string, pattern));
X#endif
X}
X#endif				/* GEMDOS */
X#else
X/* dir code for MTS under Bell Labs C... */
X
Xchar           *
Xdir(filepattern)
X	char           *filepattern;	/* template or NULL */
X{
X	char           *malloc(), *index();
X#if	USECATSCAN
X	fortran void    catscan(), fileinfo();
X
X	struct catname {
X		short           len;
X		char            name[257];
X	}               pattern;
X
X	struct catval {
X		int             maxlen;
X		int             actlen;
X		char            name[257];
X	}               catreturn;
X
X	char           *i;
X	int             j, RETCODE;
X
X	static int      catptr = 0;
X	static int      catflag = 0x200;
X	static int      cattype = 1;
X	static int      patflag = 0;
X
X	catreturn.maxlen = 256;
X
X	if (patflag) {
X		patflag = 0;
X		catptr = 0;
X		return (NULL);
X	}
X	if (filepattern) {
X		strcpy(pattern.name, filepattern);
X		pattern.len = strlen(filepattern);
X		if (!index(filepattern, '?'))
X			patflag = 1;
X	}
X	if (patflag) {
X		fileinfo(&pattern, &cattype, "CINAME  ", &catreturn, _retcode RETCODE);
X		catptr = RETCODE ? 0 : 1;
X	} else
X		catscan(&pattern, &catflag, &cattype, &catreturn, &catptr);
X
X	if (!catptr)
X		return (NULL);
X	else {
X		char           *k;
X
X		k = index(catreturn.name, ' ');
X		if (k)
X			*k = 0;
X		else {
X			j = catreturn.actlen;
X			catreturn.name[j] = 0;
X		}
X		k = catreturn.name;
X		if (catreturn.name[0] == tmpchr[0])
X			k++;
X		else if ((k = index(catreturn.name, sepchr[0])))
X			k++;
X		else
X			k = catreturn.name;
X		j = strlen(k);
X		i = malloc(++j);
X		strcpy(i, k);
X		return (i);
X	}
X#else
X	fortran void    gfinfo();
X	static char     gfname[24];
X	static char     pattern[20];
X	static int      gfdummy[2] = {
X				      0, 0
X	},              gfflags;
X	int             i, RETCODE;
X	char           *j, *k;
X
X	if (filepattern) {
X		strcpy(pattern, filepattern);
X		strcat(pattern, " ");
X		for (i = 20; i < 24; i++)
X			gfname[i] = '\0';
X		if (index(pattern, '?'))
X			gfflags = 0x0C;
X		else
X			gfflags = 0x09;
X	} else if (gfflags == 0x09)
X		return (NULL);
X
X	gfinfo(pattern, gfname, &gfflags, gfdummy, gfdummy, gfdummy, _retcode RETCODE);
X	if (RETCODE)
X		return (NULL);
X	else {
X		k = index(gfname, ' ');
X		*k = '\0';
X		k = gfname;
X		if (gfname[0] == tmpchr[0])
X			k++;
X		else if ((k = index(gfname, sepchr[0])))
X			k++;
X		else
X			k = gfname;
X		i = strlen(k);
X		j = malloc(++i);
X		strcpy(j, k);
X		return (j);
X	}
X#endif
X}
X
Xint
Xunlink(path)
X	char           *path;	/* name of file to delete */
X{
X	fortran void    destroy();
X	int             RETCODE;
X
X	char            name[258];
X
X	strcpy(name, path);
X	strcat(name, " ");
X	destroy(name, _retcode RETCODE);
X	if (RETCODE)
X		return (-1);
X	else
X		return (0);
X}
X#endif
SHAR_EOF
if test 8946 -ne "`wc -c < 'arcmisc.c'`"
then
	echo shar: "error transmitting 'arcmisc.c'" '(should have been 8946 characters)'
fi
fi
echo shar: "extracting 'arcpack.c'" '(7438 characters)'
if test -f 'arcpack.c'
then
	echo shar: "will not over-write existing file 'arcpack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'arcpack.c'
X/*
X * $Header: arcpack.c,v 1.12 88/11/16 17:18:06 hyc Exp $
X */
X
X/*  ARC - Archive utility - ARCPACK
X
X    Version 3.49, created on 04/21/87 at 11:26:51
X
X(C) COPYRIGHT 1985-87 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:	 Thom Henderson
X
X    Description:
X	 This file contains the routines used to compress a file
X	 when placing it in an archive.
X
X    Language:
X	 Computer Innovations Optimizing C86
X*/
X#include <stdio.h>
X#include "arc.h"
X#if	MTS
X#include <ctype.h>
X#endif
X
Xvoid	setcode(), sqinit_cm(), sqputc_cm(), init_cm(), putc_cm();
Xvoid	filecopy(), abort(), putc_tst(), init_sq(), scan_sq();
Xint	getch(), addcrc();
X
X/* stuff for non-repeat packing */
X
X#define DLE 0x90		/* repeat sequence marker */
X
Xstatic unsigned char state;	/* current packing state */
X
X/* non-repeat packing states */
X
X#define NOHIST  0		/* don't consider previous input */
X#define SENTCHAR 1		/* lastchar set, no lookahead yet */
X#define SENDNEWC 2		/* run over, send new char next */
X#define SENDCNT 3		/* newchar set, send count next */
X
X/* packing results */
X
Xstatic long     stdlen;		/* length for standard packing */
Xstatic short    crcval;		/* CRC check value */
X
Xvoid
Xpack(f, t, hdr)			/* pack file into an archive */
X	FILE           *f, *t;	/* source, destination */
X	struct heads   *hdr;	/* pointer to header data */
X{
X	int             c;	/* one character of stream */
X	long            ncrlen;	/* length after packing */
X	long		huflen;	/* length after squeezing */
X	long            lzwlen;	/* length after crunching */
X	long		pred_sq(), file_sq();	/* stuff for squeezing */
X	long            pred_cm(), sqpred_cm();	/* dynamic crunching cleanup */
X	long            tloc, ftell();	/* start of output */
X	int		getch();
X	int             getc_ncr();
X	void            putc_pak();
X
X	/* first pass - see which method is best */
X
X	tloc = ftell(t);	/* note start of output */
X
X	if (!nocomp) {		/* if storage kludge not active */
X		if (note) {
X			printf(" analyzing, ");
X			fflush(stdout);
X		}
X		state = NOHIST;	/* initialize ncr packing */
X		stdlen = ncrlen = 0;	/* reset size counters */
X		crcval = 0;	/* initialize CRC check value */
X		setcode();	/* initialize encryption */
X		init_sq();	/* initialize for squeeze scan */
X
X		if (dosquash) {
X			sqinit_cm();
X			while ((c = getch(f)) != EOF) {	/* for each byte of file */
X				ncrlen++;	/* one more packed byte */
X				scan_sq(c);	/* see what squeezing can do */
X				sqputc_cm(c, t);	/* see what squashing
X							 * can do */
X			}
X			lzwlen = sqpred_cm(t);
X		} else {
X			init_cm(t);	/* initialize for crunching */
X	
X			while ((c = getc_ncr(f)) != EOF) {	/* for each byte of file */
X				ncrlen++;	/* one more packed byte */
X				scan_sq(c);	/* see what squeezing can do */
X				putc_cm(c, t);	/* see what crunching can do */
X			}
X			lzwlen = pred_cm(t);	/* finish up after crunching */
X		}
X		huflen = pred_sq();	/* finish up after squeezing */
X	} else {		/* else kludge the method */
X		stdlen = 0;	/* make standard look best */
X		ncrlen = huflen = lzwlen = 1;
X	}
X
X	/* standard set-ups common to all methods */
X
X	fseek(f, 0L, 0);	/* rewind input */
X	hdr->crc = crcval;	/* note CRC check value */
X	hdr->length = stdlen;	/* set actual file length */
X	state = NOHIST;		/* reinitialize ncr packing */
X	setcode();		/* reinitialize encryption */
X
X	/* choose and use the shortest method */
X
X	if (kludge && note)
X		printf("\n\tS:%ld  P:%ld  S:%ld  C:%ld,\t ",
X			stdlen, ncrlen, huflen, lzwlen);
X
X	if (stdlen <= ncrlen && stdlen <= huflen && stdlen <= lzwlen) {
X		if (note) {
X			printf("storing, ");	/* store without compression */
X			fflush(stdout);
X		}
X		hdrver = 2;	/* note packing method */
X		fseek(t, tloc, 0);	/* reset output for new method */
X		stdlen = crcval = 0;	/* recalc these for kludge */
X		while ((c = getch(f)) != EOF)	/* store it straight */
X			putc_pak(c, t);
X		hdr->crc = crcval;
X		hdr->length = hdr->size = stdlen;
X	} else if (ncrlen < lzwlen && ncrlen < huflen) {
X		if (note) {
X			printf("packing, ");	/* pack with repeat */
X			fflush(stdout);		/* suppression */
X		}
X		hdrver = 3;	/* note packing method */
X		hdr->size = ncrlen;	/* set data length */
X		fseek(t, tloc, 0);	/* reset output for new method */
X		while ((c = getc_ncr(f)) != EOF)
X			putc_pak(c, t);
X	} else if (huflen < lzwlen) {
X		if (note) {
X			printf("squeezing, ");
X			fflush(stdout);
X		}
X		hdrver = 4;	/* note packing method */
X		fseek(t, tloc, 0);	/* reset output for new method */
X		hdr->size = file_sq(f, t);	/* note final size */
X	} else {
X		if (note)
X			printf(dosquash ? "squashed, " : "crunched, ");
X		hdrver = dosquash ? 9 : 8;
X		hdr->size = lzwlen;	/* size should not change */
X	}
X
X	/* standard cleanups common to all methods */
X
X	if (note)
X		printf("done. (%ld%%)\n",hdr->length == 0 ?
X				0L : 100L - (100L*hdr->size)/hdr->length);
X}
X
X/*
X * Non-repeat compression - text is passed through normally, except that a
X * run of more than two is encoded as:
X * 
X * <char> <DLE> <count>
X * 
X * Special case: a count of zero indicates that the DLE is really a DLE, not a
X * repeat marker.
X */
X
Xint
Xgetc_ncr(f)			/* get bytes with collapsed runs */
X	FILE           *f;	/* file to get from */
X{
X	static int      lastc;	/* value returned on last call */
X	static int      repcnt;	/* repetition counter */
X	static int      c;	/* latest value seen */
X
X	switch (state) {	/* depends on our state */
X	case NOHIST:		/* no relevant history */
X		state = SENTCHAR;
X		return lastc = getch(f);	/* remember the value next
X						 * time */
X
X	case SENTCHAR:		/* char was sent. look ahead */
X		switch (lastc) {/* action depends on char */
X		case DLE:	/* if we sent a real DLE */
X			state = NOHIST;	/* then start over again */
X			return 0;	/* but note that the DLE was real */
X
X		case EOF:	/* EOF is always a special case */
X			return EOF;
X
X		default:	/* else test for a repeat */
X			for (repcnt = 1; (c = getch(f)) == lastc && repcnt < 255; repcnt++);
X			/* find end of run */
X
X			switch (repcnt) {	/* action depends on run size */
X			case 1:/* not a repeat */
X				return lastc = c;	/* but remember value
X							 * next time */
X
X			case 2:/* a repeat, but too short */
X				state = SENDNEWC;	/* send the second one
X							 * next time */
X				return lastc;
X
X			default:	/* a run - compress it */
X				state = SENDCNT;	/* send repeat count
X							 * next time */
X				return DLE;	/* send repeat marker this
X						 * time */
X			}
X		}
X
X	case SENDNEWC:		/* send second char of short run */
X		state = SENTCHAR;
X		return lastc = c;
X
X	case SENDCNT:		/* sent DLE, now send count */
X		state = SENDNEWC;
X		return repcnt;
X
X	default:
X		abort("Bug - bad ncr state\n");
X	}
X	return 0;
X}
X
Xint
Xgetch(f)			/* special get char for packing */
X	FILE           *f;	/* file to get from */
X{
X	int		c;	/* a char from the file */
X#if	!DOS
X	static int      cr = 0;	/* add \r before \n ? */
X
X	if (cr) {
X		c = '\n';
X#if	MTS
X		c = toascii(c);
X#endif
X		crcval = addcrc(crcval, c);
X		stdlen++;
X		cr = 0;
X		return (c);
X	}
X#endif
X
X	if ((c = fgetc(f)) != EOF) {	/* if not the end of file */
X#if	!DOS
X		if (!image && (c == '\n')) {
X			c = '\r';
X			cr = 1;
X		}
X#endif
X#if	MTS
X		if (!image)
X			c = toascii(c);
X#endif
X		crcval = addcrc(crcval, c);	/* then update CRC check
X						 * value */
X		stdlen++;	/* and bump length counter */
X	}
X	return c;
X}
X
Xvoid
Xputc_pak(c, f)			/* put a packed byte into archive */
X	char            c;	/* byte to put */
X	FILE           *f;	/* archive to put it in */
X{
X	unsigned char		code();
X	putc_tst(code(c), f);	/* put encoded byte, with checks */
X}
SHAR_EOF
if test 7438 -ne "`wc -c < 'arcpack.c'`"
then
	echo shar: "error transmitting 'arcpack.c'" '(should have been 7438 characters)'
fi
fi
echo shar: "extracting 'arcrun.c'" '(3859 characters)'
if test -f 'arcrun.c'
then
	echo shar: "will not over-write existing file 'arcrun.c'"
else
sed 's/^X//' << \SHAR_EOF > 'arcrun.c'
X/*
X * $Header: arcrun.c,v 1.4 88/07/31 18:52:50 hyc Exp $
X */
X
X/*
X * ARC - Archive utility - ARCRUN
X * 
X * Version 1.20, created on 03/24/86 at 19:34:31
X * 
X * (C) COPYRIGHT 1985,85 by System Enhancement Associates; ALL RIGHTS RESERVED
X * 
X * By:  Thom Henderson
X * 
X * Description: This file contains the routines used to "run" a file which is
X * stored in an archive.  At present, all we really do is (a) extract a
X * temporary file, (b) give its name as a system command, and then (c) delete
X * the file.
X * 
X * Language: Computer Innovations Optimizing C86
X */
X#include <stdio.h>
X#include "arc.h"
X
Xvoid	rempath(), openarc(), closearc(), abort();
Xint	readhdr(), match(), unpack();
Xstatic	void	runfile();
Xchar	*strcat();
X
Xvoid
Xrunarc(num, arg)		/* run file from archive */
X	int             num;	/* number of arguments */
X	char           *arg[];	/* pointers to arguments */
X{
X	struct heads    hdr;	/* file header */
X	char           *makefnam();	/* filename fixer */
X	char            buf[STRLEN];	/* filename buffer */
X	FILE           *fopen();/* file opener */
X	char	       *dummy[2];
X
X	dummy[0]="dummy";
X	dummy[1]=NULL;
X	rempath(num, arg);	/* strip off paths */
X
X	openarc(0);		/* open archive for reading */
X
X	if (num) {		/* if files were named */
X		while (readhdr(&hdr, arc)) {	/* while more files to check */
X			if (match(hdr.name, makefnam(arg[0], ".*", buf)))
X				runfile(&hdr, num, arg);
X			else
X				fseek(arc, hdr.size, 1);
X		}
X	} else
X		while (readhdr(&hdr, arc))	/* else run all files */
X			runfile(&hdr, 1, dummy);
X
X	closearc(0);		/* close archive after changes */
X}
X
Xstatic  void
Xrunfile(hdr, num, arg)		/* run a file */
X	struct heads   *hdr;	/* pointer to header data */
X	int             num;	/* number of arguments */
X	char           *arg[];	/* pointers to arguments */
X{
X	FILE           *tmp, *fopen();	/* temporary file */
X	char           *dir, *gcdir();	/* directory stuff */
X	char            buf[STRLEN], *makefnam();	/* temp file name, fixer */
X#if	DOS
X	char		nbuf[64], *i, *rindex();
X#endif
X#if	!GEMDOS
X	int             n;	/* index */
X	char            sys[STRLEN];	/* invocation command buffer */
X#endif
X
X	/* makefnam("$ARCTEMP",hdr->name,buf); */
X#if	UNIX
X	sprintf(buf, "%s.RUN", arctemp);
X	strcpy(sys, buf);
X#else
X	strcpy(nbuf, arctemp);
X	makefnam(nbuf,hdr->name,buf);
X	i = rindex(buf,'.');
X#endif
X#if	MSDOS
X	if (!strcmp(i, ".BAS")) {
X		strcpy(sys, "BASICA ");
X		strcat(sys, buf);
X	}
X	else if (!strcmp(i, ".BAT")
X		 || !strcmp(i, ".COM")
X		 || !strcmp(i, ".EXE"))
X		strcpy(sys, buf);
X
X	else {
X		if (warn) {
X			printf("File %s is not a .BAS, .BAT, .COM, or .EXE\n",
X			       hdr->name);
X			nerrs++;
X		}
X		fseek(arc, hdr->size, 1);	/* skip this file */
X		return;
X	}
X#endif
X#if	GEMDOS
X      if (strcmp(i, ".PRG")
X              && strcmp(i, ".TTP")
X              && strcmp(i, ".TOS"))
X      {
X              if (warn) {
X                      printf("File %s is not a .PRG, .TOS, or .TTP\n",
X                              hdr->name);
X                      nerrs++;
X              }
X              fseek(arc, hdr->size, 1);       /* skip this file */
X              return;
X      }
X#endif
X
X	if (warn)
X		if (tmp = fopen(buf, "r"))
X			abort("Temporary file %s already exists", buf);
X	if (!(tmp = fopen(buf, OPEN_W)))
X		abort("Unable to create temporary file %s", buf);
X
X	if (note)
X		printf("Invoking file: %s\n", hdr->name);
X
X	dir = gcdir("");	/* see where we are */
X	unpack(arc, tmp, hdr);	/* unpack the entry */
X	fclose(tmp);		/* release the file */
X	chmod(buf, "700");	/* make it executable */
X#if	GEMDOS
X	execve(buf, arg, NULL);
X#else
X	for (n = 1; n < num; n++) {	/* add command line arguments */
X		strcat(sys, " ");
X		strcat(sys, arg[n]);
X	}
X	system(buf);		/* try to invoke it */
X#endif
X	chdir(dir);
X	free(dir);		/* return to whence we started */
X	if (unlink(buf) && warn) {
X		printf("Cannot unsave temporary file %s\n", buf);
X		nerrs++;
X	}
X}
SHAR_EOF
if test 3859 -ne "`wc -c < 'arcrun.c'`"
then
	echo shar: "error transmitting 'arcrun.c'" '(should have been 3859 characters)'
fi
fi
echo shar: "extracting 'arcs.h'" '(1645 characters)'
if test -f 'arcs.h'
then
	echo shar: "will not over-write existing file 'arcs.h'"
else
sed 's/^X//' << \SHAR_EOF > 'arcs.h'
X/*
X * $Header: arcs.h,v 1.2 88/04/17 18:53:19 hyc Exp $
X */
X
X/*
X * ARC - Archive utility - Archive file header format
X * 
X * Version 2.12, created on 12/17/85 at 14:40:26
X * 
X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X * 
X * By:  Thom Henderson
X * 
X * Description: This file defines the format of an archive file header,
X * excluding the archive marker and the header version number.
X * 
X * Each entry in an archive begins with a one byte archive marker, which is set
X * to 26.  The marker is followed by a one byte header type code, from zero
X * to 7.
X * 
X * If the header type code is zero, then it is an end marker, and no more data
X * should be read from the archive.
X * 
X * If the header type code is in the range 2 to 7, then it is followed by a
X * standard archive header, which is defined below.
X * 
X * If the header type code is one, then it is followed by an older format
X * archive header.  The older format header does not contain the true length.
X * A header should be read for a length of sizeof(struct heads)-sizeof(long).
X * Then set length equal to size and change the header version to 2.
X * 
X * Programming note: The crc value given in the header is based on the unpacked
X * data.
X * 
X * Language: Computer Innovations Optimizing C86
X */
X
Xstruct heads {			/* archive entry header format */
X    char    name[FNLEN];		/* file name */
X            long size;		/* size of file, in bytes */
X    unsigned    short date;	/* creation date */
X    unsigned    short time;	/* creation time */
X                short crc;	/* cyclic redundancy check */
X                long length;	/* true file length */
X};
SHAR_EOF
if test 1645 -ne "`wc -c < 'arcs.h'`"
then
	echo shar: "error transmitting 'arcs.h'" '(should have been 1645 characters)'
fi
fi
exit 0
#	End of shell archive



More information about the Unix-pc.sources mailing list