static char sccsid[] = "@(#)compress.c  @(#)compress.c  5.9 (Berkeley) 5/11/86";

/*
 * Compress - data compression program
 */
#define min(a,b)        ((a>b) ? b : a)

#define HSIZE  69001	       /* 95% occupancy */

/*
 * a code_int must be able to hold 2**BITS values of type int, and also -1
 */
typedef long int	code_int;
typedef long int	count_int;

typedef unsigned char	char_type;
char_type magic_header[] = { "\037\235" };      /* 1F 9D */

/* Defines for third byte of header */
#define BIT_MASK	0x1f
#define BLOCK_MASK	0x80
/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
   a fourth header byte (for expansion).
*/
#define INIT_BITS 9			/* initial number of bits/code */

static char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";

#include <stdio.h>
#include <ctype.h>
#include <stat.h>
#include <time.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>


#define ARGVAL() (*++(*argv) || (--argc && *++argv))

int n_bits;				/* number of bits/code */
int maxbits = BITS;			/* user settable max # bits/code */
code_int maxcode;			/* maximum code, given n_bits */
code_int maxmaxcode = 1 << BITS;	/* should NEVER generate this code */
#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)

/* extern int errno; */

count_int *htab;
unsigned short *codetab;

#define htabof(i)       htab[i]
#define codetabof(i)    codetab[i]

code_int hsize = HSIZE; 		/* for dynamic table sizing */
count_int fsize;

/*
 * To save much memory, we overlay the table used by compress() with those
 * used by decompress().  The tab_prefix table is the same size and type
 * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
 * get this from the beginning of htab.  The output stack uses the rest
 * of htab, and contains characters.  There is plenty of room for any
 * possible stack (stack used to be 8000 characters).
 */

#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i)        ((char_type *)(htab))[i]
#define de_stack	       ((char_type *)&tab_suffixof(1<<BITS))

code_int free_ent = 0;			/* first unused entry */
int exit_stat = 0;			/* per-file status */
int perm_stat = 0;			/* permanent status */

code_int getcode();

Usage() {
fprintf(stderr,"Usage: compress [-fvc] [-b maxbits] [file ...]\n");
}
int nomagic = 0;	/* Use a 3-byte magic number header, unless old file */
int zcat_flg = 0;	/* Write output on stdout, suppress messages */
int precious = 1;	/* Don't unlink output file on interrupt */
int quiet = 1;		/* don't tell me about compression */

/*
 * block compression parameters -- after all codes are used up,
 * and compression rate changes, start over.
 */
int block_compress = BLOCK_MASK;
int clear_flg = 0;
long int ratio = 0;
#define CHECK_GAP 10000 /* ratio check interval */
count_int checkpoint = CHECK_GAP;
/*
 * the next two codes should not be changed lightly, as they must not
 * lie within the contiguous general code space.
 */
#define FIRST	257	/* first free entry */
#define CLEAR	256	/* table clear output code */

int force = 0;
char ofname [100];
int (*oldint)();

int do_decomp = 0;

/*****************************************************************
 * TAG( main )
 *
 * Algorithm from "A Technique for High Performance Data Compression",
 * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
 *
 * Usage: compress [-dfvc] [-b bits] [file ...]
 * Inputs:
 *	-d:	    If given, decompression is done instead.
 *
 *	-c:	    Write output on stdout, don't remove original.
 *
 *	-b:	    Parameter limits the max number of bits/code.
 *
 *	-f:	    Forces output file to be generated, even if one already
 *		    exists, and even if no space is saved by compressing.
 *		    If -f is not used, the user will be prompted if stdin is
 *		    a tty, otherwise, the output file will not be overwritten.
 *
 *	-v:	    Write compression statistics
 *
 *	file ...:   Files to be compressed.  If none specified, stdin
 *		    is used.
 * Outputs:
 *	file.Z:     Compressed form of file with same mode, owner, and utimes
 *	or stdout   (if stdin used as input)
 *
 * Assumptions:
 *	When filenames are given, replaces with the compressed version
 *	(.Z suffix) only if the file decreases in size.
 * Algorithm:
 *	Modified Lempel-Ziv method (LZW).  Basically finds common
 * substrings and replaces them with a variable size code.  This is
 * deterministic, and can be done on the fly.  Thus, the decompression
 * procedure needs no input table, but tracks the way the table was built.
 */

main( argc, argv )
register int argc; char **argv;
{
    int overwrite = 0;	/* Do not overwrite unless given -f flag */
    char tempname[100];
    char **filelist, **fileptr;
    char *cp, *rindex(), *strcpy(), *malloc();
    struct stat statbuf;
    extern onintr(), oops();

    freopen("*","r+", stderr);

    htab = (count_int *)malloc(HSIZE * sizeof(count_int));
    codetab = (unsigned short *)malloc(HSIZE * sizeof(unsigned short));
    if (htab == NULL || codetab == NULL) {
	fprintf(stderr,"compress: out of memory\n");
	exit(1);
    }
    filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
    *filelist = NULL;

    if((cp = rindex(argv[0], '/')) != 0) {
	cp++;
    } else {
	cp = argv[0];
    }
    if(strcmp(cp, "uncompress") == 0) {
	do_decomp = 1;
    } else if(strcmp(cp, "zcat") == 0) {
	do_decomp = 1;
	zcat_flg = 1;
    }

    /* Argument Processing
     * All flags are optional.
     * -D => debug
     * -V => print Version; debug verbose
     * -d => do_decomp
     * -v => unquiet
     * -f => force overwrite of output file
     * -n => no header: useful to uncompress old files
     * -b maxbits => maxbits.  If -b is specified, then maxbits MUST be
     *	    given also.
     * -c => cat all output to stdout
     * -C => generate output compatible with compress 2.0.
     * if a string is left, must be an input filename.
     */
    for (argc--, argv++; argc > 0; argc--, argv++) {
	if (**argv == '-') {    /* A flag argument */
	    while (*++(*argv)) {        /* Process all flags in this arg */
		switch (**argv) {
		    case 'V':
			version();
			break;
		    case 'v':
			quiet = 0;
			break;
		    case 'd':
			do_decomp = 1;
			break;
		    case 'f':
		    case 'F':
			overwrite = 1;
			force = 1;
			break;
		    case 'n':
			nomagic = 1;
			break;
		    case 'C':
			block_compress = 0;
			break;
		    case 'b':
			if (!ARGVAL()) {
			    fprintf(stderr, "Missing maxbits\n");
			    Usage();
			    exit(1);
			}
			maxbits = atoi(*argv);
			goto nextarg;
		    case 'c':
			zcat_flg = 1;
			break;
		    case 'q':
			quiet = 1;
			break;
		    default:
			fprintf(stderr, "Unknown flag: '%c'; ", **argv);
			Usage();
			exit(1);
		}
	    }
	} else {	  /* Input file name */
	    *fileptr++ = *argv; /* Build input file list */
	    *fileptr = NULL;
	    /* process nextarg; */
	}
	nextarg: continue;
    }

    if (maxbits < INIT_BITS) maxbits = INIT_BITS;
    if (maxbits > BITS) maxbits = BITS;
    maxmaxcode = 1 << maxbits;

    if (*filelist != NULL) {
	for (fileptr = filelist; *fileptr; fileptr++) {
	    exit_stat = 0;
	    if (do_decomp) {                    /* DECOMPRESSION */
		/* Check for .Z suffix */
		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {
		    /* No .Z: tack one on */
		    strcpy(tempname, *fileptr);
		    strcat(tempname, ".Z");
		    *fileptr = tempname;
		}
		/* Open input file */
		if ((freopen(*fileptr, "r", stdin)) == NULL) {
		    perror(*fileptr);
		    perm_stat = 1;
		    continue;
		}
		/* Check the magic number */
		if (nomagic == 0) {
		    if ((getc(stdin) != (magic_header[0] & 0xFF))
		     || (getc(stdin) != (magic_header[1] & 0xFF))) {
			fprintf(stderr, "%s: not in compressed format\n",
			    *fileptr);
			continue;
		    }
		    maxbits = getc(stdin);        /* set -b from file */
		    block_compress = maxbits & BLOCK_MASK;
		    maxbits &= BIT_MASK;
		    maxmaxcode = 1 << maxbits;
		    if(maxbits > BITS) {
			fprintf(stderr,
			"%s: compressed with %d bits, can only handle %d bits\n",
			*fileptr, maxbits, BITS);
			continue;
		    }
		}
		/* Generate output filename */
		strcpy(ofname, *fileptr);
		ofname[strlen(*fileptr) - 2] = '\0';  /* Strip off .Z */
	    } else {					/* COMPRESSION */
		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
		    fprintf(stderr, "%s: already has .Z suffix -- no change\n",
			    *fileptr);
		    continue;
		}
		/* Open input file */
		if ((freopen(*fileptr, "r", stdin)) == NULL) {
		    perror(*fileptr);
		    perm_stat = 1;
		    continue;
		}
		stat ( *fileptr, &statbuf );
		fsize = (long) statbuf.st_size;
		/*
		 * tune hash table size for small files -- ad hoc,
		 * but the sizes match earlier #defines, which
		 * serve as upper bounds on the number of output codes.
		 */
		hsize = HSIZE;
		if ( fsize < (1 << 12) )
		    hsize = min ( 5003, HSIZE );
		else if ( fsize < (1 << 13) )
		    hsize = min ( 9001, HSIZE );
		else if ( fsize < (1 << 14) )
		    hsize = min ( 18013, HSIZE );
		else if ( fsize < (1 << 15) )
		    hsize = min ( 35023, HSIZE );
		else if ( fsize < 47000 )
		    hsize = min ( 50021, HSIZE );

		/* Generate output filename */
		strcpy(ofname, *fileptr);
		strcat(ofname, ".Z");
	    }
	    /* Check for overwrite of existing file */
	    if (overwrite == 0 && zcat_flg == 0) {
		if (stat(ofname, &statbuf) == 0) {
		    char response[2];
		    response[0] = 'n';
		    fprintf(stderr, "%s already exists;", ofname);
		    if (isatty(2)) {
			fprintf(stderr, " do you wish to overwrite %s (y or n)? ",
					ofname);
			fflush(stderr);
			read(2, response, 2);
			while (response[1] != '\n') {
			    if (read(2, response+1, 1) < 0) {   /* Ack! */
				perror("stderr"); break;
			}
		    }
		}
		if (response[0] != 'y') {
		    fprintf(stderr, "\tnot overwritten\n");
			continue;
		}
	    }
	}
	if(zcat_flg == 0) {         /* Open output file */
	    if (freopen(ofname, "w", stdout) == NULL) {
		perror(ofname);
		perm_stat = 1;
		continue;
	    }
	    precious = 0;
	    if(!quiet)
		fprintf(stderr, "%s: ", *fileptr);
	}

	    /* Actually do the compression/decompression */
	if (do_decomp == 0)
	    compress();
	else
	    decompress();
	if(zcat_flg == 0) {
	    copystat(*fileptr, ofname);     /* Copy stats */
	    precious = 1;
	    if((exit_stat == 1) || (!quiet))
		putc('\n', stderr);
	    }
	}
    } else {		/* Standard input */
	if (do_decomp == 0) {
	    compress();
	    if(!quiet)
		putc('\n', stderr);
	} else {
	    /* Check the magic number */
	    if (nomagic == 0) {
		if ((getc(stdin)!=(magic_header[0] & 0xFF))
		 || (getc(stdin)!=(magic_header[1] & 0xFF))) {
		    fprintf(stderr, "stdin: not in compressed format\n");
		    exit(1);
		}
		maxbits = getc(stdin);    /* set -b from file */
		block_compress = maxbits & BLOCK_MASK;
		maxbits &= BIT_MASK;
		maxmaxcode = 1 << maxbits;
		fsize = 100000; 	/* assume stdin large for USERMEM */
		if(maxbits > BITS) {
			fprintf(stderr,
			 "stdin: compressed with %d bits, can only handle %d bits\n",
			 maxbits, BITS);
			exit(1);
		}
	    }
	    decompress();
	}
    }
    exit(perm_stat ? perm_stat : exit_stat);
}

static int offset;
long int in_count = 1;			/* length of input */
long int bytes_out;			/* length of compressed output */
long int out_count = 0; 		/* # of codes output (for debugging) */

/*
 * compress stdin to stdout
 *
 * Algorithm:  use open addressing double hashing (no chaining) on the
 * prefix code / next character combination.  We do a variant of Knuth's
 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
 * secondary probe.  Here, the modular division first probe is gives way
 * to a faster exclusive-or manipulation.  Also do block compression with
 * an adaptive reset, whereby the code table is cleared when the compression
 * ratio decreases, but after the table fills.	The variable-length output
 * codes are re-sized at this point, and a special CLEAR code is generated
 * for the decompressor.  Late addition:  construct the table according to
 * file size for noticeable speed improvement on small files.  Please direct
 * questions about this implementation to ames!jaw.
 */

compress() {
    register long fcode;
    register code_int i = 0;
    register int c;
    register code_int ent;
    register int disp;
    register code_int hsize_reg;
    register int hshift;

    if (nomagic == 0) {
	putc(magic_header[0], stdout); putc(magic_header[1], stdout);
	putc((char)(maxbits | block_compress), stdout);
	if(ferror(stdout))
		writeerr();
    }

    offset = 0;
    bytes_out = 3;		/* includes 3-byte header mojo */
    out_count = 0;
    clear_flg = 0;
    ratio = 0;
    in_count = 1;
    checkpoint = CHECK_GAP;
    maxcode = MAXCODE(n_bits = INIT_BITS);
    free_ent = ((block_compress) ? FIRST : 256 );

    ent = getc(stdin);

    hshift = 0;
    for ( fcode = (long) hsize;  fcode < 65536L; fcode *= 2L )
	hshift++;
    hshift = 8 - hshift;		/* set hash code range bound */

    hsize_reg = hsize;
    cl_hash( (count_int) hsize_reg);            /* clear hash table */

    while ((c = getc(stdin)) != EOF) {
	in_count++;
	fcode = (long) (((long) c << maxbits) + ent);
	i = ((c << hshift) ^ ent);      /* xor hashing */

	if ( htabof (i) == fcode ) {
	    ent = codetabof (i);
	    continue;
	} else if ((long)htabof (i) < 0)      /* empty slot */
	    goto nomatch;
	disp = hsize_reg - i;		/* secondary hash (after G. Knott) */
	if ( i == 0 )
	    disp = 1;
probe:
	if ((i -= disp) < 0)
	    i += hsize_reg;

	if (htabof (i) == fcode) {
	    ent = codetabof (i);
	    continue;
	}
	if ((long)htabof (i) > 0)
	    goto probe;
nomatch:
	output ((code_int) ent);
	out_count++;
	ent = c;
	if ( free_ent < maxmaxcode ) {
	    codetabof (i) = free_ent++; /* code -> hashtable */
	    htabof (i) = fcode;
	}
	else if ( (count_int)in_count >= checkpoint && block_compress )
	    cl_block ();
    }
    /*
     * Put out the final code.
     */
    output((code_int)ent);
    out_count++;
    output((code_int)-1);

    /*
     * Print out stats on stderr
     */
    if(zcat_flg == 0 && !quiet) {
	fprintf( stderr, "Compression: " );
	prratio( stderr, in_count-bytes_out, in_count );
    }
    if(bytes_out > in_count)    /* exit(2) if no savings */
	exit_stat = 2;
    return;
}

/*****************************************************************
 * TAG(output)
 *
 * Output the given code.
 * Inputs:
 *	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
 *		that n_bits =< (long)wordsize - 1.
 * Outputs:
 *	Outputs code to the file.
 * Assumptions:
 *	Chars are 8 bits long.
 * Algorithm:
 *	Maintain a BITS character long buffer (so that 8 codes will
 * fit in it exactly).	Use the VAX insv instruction to insert each
 * code in turn.  When the buffer fills up empty it and start over.
 */

static char buf[BITS];

char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};

output( code )
code_int  code;
{

    /*
     * On the VAX, it is important to have the register declarations
     * in exactly the order given, or the asm will break.
     */
    register int r_off = offset, bits= n_bits;
    register char * bp = buf;

    if (code >= 0) {
/*
 * byte/bit numbering on the VAX is simulated by the following code
 */
	/*
	 * Get to the first byte.
	 */
	bp += (r_off >> 3);
	r_off &= 7;
	/*
	 * Since code is always >= 8 bits, only need to mask the first
	 * hunk on the left.
	 */
	*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
	bp++;
	bits -= (8 - r_off);
	code >>= 8 - r_off;
	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
	if ( bits >= 8 ) {
	    *bp++ = code;
	    code >>= 8;
	    bits -= 8;
	}
	/* Last bits. */
	if(bits)
	    *bp = code;
	offset += n_bits;
	if (offset == (n_bits << 3)) {
	    bp = buf;
	    bits = n_bits;
	    bytes_out += bits;
	    do
		putc(*bp++, stdout);
	    while(--bits);
	    offset = 0;
	}

	/*
	 * If the next entry is going to be too big for the code size,
	 * then increase it, if possible.
	 */
	if (free_ent > maxcode || (clear_flg > 0))
	{
	    /*
	     * Write the whole buffer, because the input side won't
	     * discover the size increase until after it has read it.
	     */
	    if (offset > 0) {
		if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
			writeerr();
		bytes_out += n_bits;
	    }
	    offset = 0;

	    if ( clear_flg ) {
		maxcode = MAXCODE (n_bits = INIT_BITS);
		clear_flg = 0;
	    }
	    else {
		n_bits++;
		if ( n_bits == maxbits )
		    maxcode = maxmaxcode;
		else
		    maxcode = MAXCODE(n_bits);
	    }
	}
    } else {
	/*
	 * At EOF, write the rest of the buffer.
	 */
	if ( offset > 0 )
	    fwrite( buf, 1, (offset + 7) / 8, stdout );
	bytes_out += (offset + 7) / 8;
	offset = 0;
	fflush( stdout );
	if(ferror(stdout))
		writeerr();
    }
}

/*
 * Decompress stdin to stdout.	This routine adapts to the codes in the
 * file building the "string" table on-the-fly; requiring no table to
 * be stored in the compressed file.  The tables used herein are shared
 * with those of the compress() routine.  See the definitions above.
 */

decompress() {
    register char_type *stackp;
    register int finchar;
    register code_int code, oldcode, incode;

    /*
     * As above, initialize the first 256 entries in the table.
     */
    maxcode = MAXCODE(n_bits = INIT_BITS);
    for ( code = 255; code >= 0; code-- ) {
	tab_prefixof(code) = 0;
	tab_suffixof(code) = (char_type)code;
    }
    free_ent = ((block_compress) ? FIRST : 256 );

    finchar = oldcode = getcode();
    if(oldcode == -1)   /* EOF already? */
	return; 		/* Get out of here */
    putc((char)finchar, stdout);  /* first code must be 8 bits = char */
    if(ferror(stdout))          /* Crash if can't write */
	writeerr();
    stackp = de_stack;

    while ((code = getcode()) > -1) {

	if ((code == CLEAR) && block_compress) {
	    for ( code = 255; code >= 0; code-- )
		tab_prefixof(code) = 0;
	    clear_flg = 1;
	    free_ent = FIRST - 1;
	    if ( (code = getcode ()) == -1 )    /* O, untimely death! */
		break;
	}
	incode = code;
	/*
	 * Special case for KwKwK string.
	 */
	if ( code >= free_ent ) {
	    *stackp++ = finchar;
	    code = oldcode;
	}

	/*
	 * Generate output characters in reverse order
	 */
	while ( code >= 256 ) {
	    *stackp++ = tab_suffixof(code);
	    code = tab_prefixof(code);
	}
	*stackp++ = finchar = tab_suffixof(code);

	/*
	 * And put them out in forward order
	 */
	do
	    putc(*--stackp, stdout);
	while ( stackp > de_stack );

	/*
	 * Generate the new entry.
	 */
	if ( (code=free_ent) < maxmaxcode ) {
	    tab_prefixof(code) = (unsigned short)oldcode;
	    tab_suffixof(code) = finchar;
	    free_ent = code+1;
	}
	/*
	 * Remember previous code.
	 */
	oldcode = incode;
    }
    fflush( stdout );
    if(ferror(stdout))
	writeerr();
}

/*****************************************************************
 * TAG( getcode )
 *
 * Read one code from the standard input.  If EOF, return -1.
 * Inputs:
 *	stdin
 * Outputs:
 *	code or -1 is returned.
 */

code_int
getcode() {
    /*
     * On the VAX, it is important to have the register declarations
     * in exactly the order given, or the asm will break.
     */
    register code_int code;
    static int offset = 0, size = 0;
    static char_type buf[BITS];
    register int r_off, bits;
    register char_type *bp = buf;

    if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) {
	/*
	 * If the next entry will be too big for the current code
	 * size, then we must increase the size.  This implies reading
	 * a new buffer full, too.
	 */
	if ( free_ent > maxcode ) {
	    n_bits++;
	    if ( n_bits == maxbits )
		maxcode = maxmaxcode;	/* won't get any bigger now */
	    else
		maxcode = MAXCODE(n_bits);
	}
	if ( clear_flg > 0) {
	    maxcode = MAXCODE (n_bits = INIT_BITS);
	    clear_flg = 0;
	}
	size = fread( buf, 1, n_bits, stdin );
	if ( size <= 0 )
	    return -1;			/* end of file */
	offset = 0;
	/* Round size down to integral number of codes */
	size = (size << 3) - (n_bits - 1);
    }
    r_off = offset;
    bits = n_bits;
    /*
     * Get to the first byte.
     */
    bp += (r_off >> 3);
    r_off &= 7;
    /* Get first part (low order bits) */
    code = (*bp++ >> r_off);
    bits -= (8 - r_off);
    r_off = 8 - r_off;		    /* now, offset into code word */
    /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
    if ( bits >= 8 ) {
	code |= *bp++ << r_off;
	r_off += 8;
	bits -= 8;
    }
    /* high order bits. */
    code |= (*bp & rmask[bits]) << r_off;
    offset += n_bits;

    return code;
}


writeerr()
{
    perror ( ofname );
    unlink ( ofname );
    exit ( 1 );
}

copystat(ifname, ofname)
char *ifname, *ofname;
{
    BOOL CopyFileDate();

    fclose(stdout);
    fclose(stdin);
    if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */
	if(!quiet)
	    fprintf(stderr, " -- file unchanged"), fflush(stderr);
    } else {			/* ***** Successful Compression ***** */
	exit_stat = 0;
	if (CopyFileAttr(ifname, ofname) || CopyFileDate(ifname, ofname))
	    fprintf(stderr, " -- couldn't copy file attributes"), fflush(stderr);
	if (unlink(ifname))     /* Remove input file */
	    perror(ifname), fflush(stderr);
	else if(!quiet)
	    fprintf(stderr, " -- replaced with %s", ofname), fflush(stderr);
	return; 	/* Successful return */
    }

    /* Unsuccessful return -- one of the tests failed */
    if (unlink(ofname))
	perror(ofname), fflush(stderr);
}

onintr ( )
{
    if (!precious)
	unlink ( ofname );
    exit ( 1 );
}

oops ( )        /* wild pointer -- assume bad input */
{
    if ( do_decomp )
	fprintf ( stderr, "uncompress: corrupt input\n" );
    unlink ( ofname );
    exit ( 1 );
}

cl_block ()             /* table clear for block compress */
{
    register long int rat;

    checkpoint = in_count + CHECK_GAP;

    if(in_count > 0x007fffff) { /* shift will overflow */
	rat = bytes_out >> 8;
	if(rat == 0) {          /* Don't divide by zero */
	    rat = 0x7fffffff;
	} else {
	    rat = in_count / rat;
	}
    } else {
	rat = (in_count << 8) / bytes_out;      /* 8 fractional bits */
    }
    if ( rat > ratio ) {
	ratio = rat;
    } else {
	ratio = 0;
	cl_hash ( (count_int) hsize );
	free_ent = FIRST;
	clear_flg = 1;
	output ( (code_int) CLEAR );
    }
}

cl_hash(hsize)          /* reset code table */
    register count_int hsize;
{
    register count_int *htab_p = &htab[hsize];
    register long i;
    register long m1 = -1;

    i = hsize - 16;
    do {			    /* might use Sys V memset(3) here */
	*(htab_p-16) = m1;
	*(htab_p-15) = m1;
	*(htab_p-14) = m1;
	*(htab_p-13) = m1;
	*(htab_p-12) = m1;
	*(htab_p-11) = m1;
	*(htab_p-10) = m1;
	*(htab_p-9) = m1;
	*(htab_p-8) = m1;
	*(htab_p-7) = m1;
	*(htab_p-6) = m1;
	*(htab_p-5) = m1;
	*(htab_p-4) = m1;
	*(htab_p-3) = m1;
	*(htab_p-2) = m1;
	*(htab_p-1) = m1;
	htab_p -= 16;
    } while ((i -= 16) >= 0);
    for ( i += 16; i > 0; i-- )
	*--htab_p = m1;
}

prratio(stream, num, den)
FILE *stream;
long int num, den;
{
    register int q;		    /* Doesn't need to be long */

    if(num > 214748L) {             /* 2147483647/10000 */
	q = num / (den / 10000L);
    } else {
	q = 10000L * num / den; 	/* Long calculations, though */
    }
    if (q < 0) {
	putc('-', stream);
	q = -q;
    }
    fprintf(stream, "%d.%02d%%", q / 100, q % 100);
}

version()
{
    fprintf(stderr, "%s, Berkeley 5.9 5/11/86\n", rcs_ident);
    fprintf(stderr, "Options: ");
    fprintf(stderr, "AMIGA, ");
    fprintf(stderr, "BITS = %d\n", BITS);
}




/* Function:
 *		GetFileDate
 *
 * Called with:
 *		name:	file name
 *		date:	pointer to DateStamp structure
 *
 * Returns:
 *		result: 1 => got a date, 0 => didn't
 *
 * Description:
 *		GetFileDate attempts to get the creation/modification date
 *		of a file (unfortunately, they're one and the same) and stores
 *		it into the location pointed to by <date>.  If the file doesn't
 *		exist or for some reason the date can't be obtained, <date>
 *	is set to zeros and a zero is returned.  Otherwise, <date> is set
 *		to the file date and a 1 is returned.
 */

BOOL
GetFileDate(name, date)
	char   *name; struct DateStamp *date;
{
	struct FileInfoBlock *Fib;
	ULONG	FLock;
	int	result = FALSE;
	register struct DateStamp  *d;

	if ((FLock = (ULONG) Lock(name,(long)(ACCESS_READ)))== NULL)
		goto exit1;

	Fib = (struct FileInfoBlock *)
		AllocMem((long)sizeof(struct FileInfoBlock),
					(long)(MEMF_CHIP|MEMF_PUBLIC));

	if (Fib == NULL )
		result = FALSE;
	else{
		if (!Examine(FLock, Fib )) {
			result = FALSE;
		}
		else
			if (Fib->fib_DirEntryType > 0 )
				result = FALSE; 	/* It's a directory */
			else{
				d = &Fib->fib_Date;
				date->ds_Days = d->ds_Days;
				date->ds_Minute = d->ds_Minute;
				date->ds_Tick = d->ds_Tick;
				result = TRUE;
			}
		FreeMem((void *)Fib,(long)sizeof(struct FileInfoBlock));
	}

	UnLock(FLock);
exit1:
	if (! result ) {
		date->ds_Days = 0;
		date->ds_Minute = 0;
		date->ds_Tick = 0;
	}
	return result;
}


/*---------------------------------------------------------------------*/
/*  SetFileDate: datestamp the given file with the given date.	       */
/*---------------------------------------------------------------------*/

#define ACTION_SETDATE_MODE 34L 	/* Set creation date on file */

BOOL
SetFileDate( name, date )
	char *name; struct DateStamp *date;
{
	struct MsgPort	   *task;		/* for process id handler */
	ULONG	arg[4]; 				/* array of arguments	  */
	int nameleng;
	char   *bstr, *strcpy();                /* of file to be set      */
	long	rc;
	char   *strchr();
	int	strlen();

	rc = 0;

	nameleng = strlen(name);
	if (!(bstr = (char *)AllocMem((long) (nameleng + 2),MEMF_PUBLIC)))
		goto exit2;

	if (!(task = (struct MsgPort *)DeviceProc(name )))
		goto exit1;

 /* Dos Packet needs the filename in Bstring format */

	(void) strcpy(bstr+1, name );
	*bstr = nameleng;

	arg[0]= (ULONG)NULL;
	arg[1]= (ULONG)IoErr();                 /* lock on parent director set by
									   DeviceProc() */
	arg[2]= (ULONG)bstr >> 2;
	arg[3]= (ULONG)date;
	rc = sendpkt(task,ACTION_SETDATE_MODE,arg,4L );

exit1: if (bstr )
		FreeMem((void *)bstr, (long) (nameleng + 2));
exit2: if (rc == DOSTRUE )
		return TRUE;
	else
		return FALSE;
}




/* Copy the last modified date from one file to another.
 * Called with:
 *		from:		name of source file
 *		to:			name of destination file
 * Returns:
 *		0 => success, 1 => failure
 * Note:
 *		Dynamic memory allocation of the DateStamp struction is
 *		necessary to insure longword alignment.
 */

BOOL
CopyFileDate(from,to)
	char *from, *to;
{
	struct DateStamp *date;
	int status = 1; 			/* default is fail code */

	if (date = (struct DateStamp *)
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC)) {
		if (GetFileDate(from,date))
			if (SetFileDate(to,date))
				status = 0;
		FreeMem(date, (long) sizeof(struct DateStamp));
	}
	return status;
}


/****************************************************************************/
/* Function:
 *		CopyFileAttr - Copy File Attributes
 *
 * Called with:
 *		srcName:		source file name
 *		dstName:		destination file name
 *
 * Returns:
 *		status where 0 => success
 *
 * Description:
 *		CopyFileAttr is used by file copying functions to assign the
 *		attributes of the source file to the destination file.
 */
int
CopyFileAttr(srcName, dstName)
	char *srcName, *dstName;
{
	struct Lock *srcLock = NULL;
	struct FileInfoBlock *srcFIB = NULL;
	int status = 0;

	if (! (srcFIB = AllocMem( (long) sizeof(*srcFIB), MEMF_FAST) ) ) {
nomem:
		status = ERROR_NO_FREE_STORE;
		goto done;
	}

	if (! (srcLock = (struct Lock *) Lock(srcName, ACCESS_READ) ) ) {
err:
		status = IoErr();
		goto done;
	}

	if (!Examine(srcLock, srcFIB)) goto err;
	SetFileDate(dstName, &srcFIB->fib_Date);
	if (srcFIB->fib_Comment[0])
		SetComment(dstName, srcFIB->fib_Comment);
	SetProtection(dstName, srcFIB->fib_Protection);

done:
	if (srcLock) UnLock(srcLock);
	if (srcFIB) FreeMem(srcFIB, (long) sizeof(*srcFIB));
	return status;
}


LONG
sendpkt(id,type,args,nargs)
	struct MsgPort *id;				/* process indentifier ... (handler's
									   message port ) */
	LONG type,						/* packet type ... (what you want
									   handler to do )   */
	args[], 						/* a pointer to argument list */
	nargs;							/* number of arguments in list	*/
{

	struct MsgPort	   *replyport;
	struct StandardPacket  *packet;

	LONG count,*pargs,res1=NULL;

	if (!(replyport = (struct MsgPort   *) CreatePort(NULL,NULL)))
		return(NULL);

	packet = (struct StandardPacket *)
		   AllocMem((LONG)sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR);

	if (packet) {
		packet->sp_Msg.mn_Node.ln_Name = &(packet->sp_Pkt);/* link packet */
		packet->sp_Pkt.dp_Link = &(packet->sp_Msg);/* to message    */
		packet->sp_Pkt.dp_Port = replyport;/* set-up reply port   */
		packet->sp_Pkt.dp_Type = type;/* what to do... */

	/* move all the arguments to the packet */
		pargs = &(packet->sp_Pkt.dp_Arg1);/* address of first argument */
		for (count=0; (count < nargs) && (count < 7); count++)
			pargs[count] = args[count];

		PutMsg(id,packet);                      /* send packet */
		WaitPort(replyport);            /* wait for packet to come back */
		GetMsg(replyport);                      /* pull message */

		res1 = packet->sp_Pkt.dp_Res1;/* get result */
		FreeMem(packet,(LONG)sizeof(*packet));

	}
	DeletePort(replyport);
	return(res1);
}

