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

/*
 * Compress - data compression program Modified by Mark Rinfret for
 * conditional compilation as "zcat".
 */
#define min(a,b)        ((a>b) ? b : a)

#ifndef BITS
# define BITS 16                /* default is 16 bits */
#endif

#if BITS == 16
# define HSIZE	69001               /* 95% occupancy */
#endif
#if BITS == 15
# define HSIZE	35023               /* 94% occupancy */
#endif
#if BITS == 14
# define HSIZE	18013               /* 91% occupancy */
#endif
#if BITS == 13
# define HSIZE	9001                /* 91% occupancy */
#endif
#if BITS <= 12
# define HSIZE	5003                /* 80% occupancy */
#endif

/*
 * 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 [-fvcd] [-b maxbits] [file ...]\n");
}
int             nomagic = 0;    /* Use a 3-byte magic number header, unless
                                 * old file */
#ifdef ZCAT
int             zcat_flg = 1;   /* Write output on stdout, supress messages */
#else
int             zcat_flg = 0;   /* Write output on stdout, suppress messages */
#endif
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) ();

#ifdef ZCAT
int             do_decomp = 1;
#else
int             do_decomp = 0;
#endif

/*****************************************************************
 * 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) {
        perror("compress");
        exit(1);
    }
    filelist = fileptr = (char **) (malloc(argc * sizeof(*argv)));
    *filelist = NULL;

    if ((cp = rindex(argv[0], '/')) != 0) {
        cp++;
    } else {
        cp = argv[0];
    }

#ifndef ZCAT
    if (strcmp(cp, "uncompress") == 0) {
        do_decomp = 1;
    } else if (strcmp(cp, "zcat") == 0) {
        do_decomp = 1;
        zcat_flg = 1;
    }
#endif

    /*
     * 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) {
#ifndef ZCAT
                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;
#endif
                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 */
#ifndef ZCAT
                /* 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;
                }
#endif
                /* 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;
                    }
                }
#ifndef ZCAT
                /* Generate output filename */
                strcpy(ofname, *fileptr);
                ofname[strlen(*fileptr) - 2] = '\0';    /* Strip off .Z */
#endif
            }
#ifndef ZCAT
            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");
            }
#endif

#ifndef ZCAT
            /* 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);
            }
#endif
            /* Actually do the compression/decompression */
#ifndef ZCAT
            if (do_decomp == 0)
                compress();
            else
#endif
                decompress();
#ifndef ZCAT
            if (zcat_flg == 0) {
                copystat(*fileptr, ofname);     /* Copy stats */
                precious = 1;
                if ((exit_stat == 1) || (!quiet))
                    putc('\n', stderr);
            }
#endif
        }
    }
#ifndef ZCAT 
    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();
        }
    }
#endif
    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) */

#ifndef ZCAT
/*
 * 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;
}
#endif

/*****************************************************************
 * 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};

#ifndef ZCAT
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();
    }
}
#endif

/*
 * 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);
}

#ifndef ZCAT
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);
}
#endif

onintr()
{
#ifndef ZCAT
    if (!precious)
        unlink(ofname);
#endif
    exit(1);
}

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

#ifndef ZCAT
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);
    }
}
#endif

#ifndef ZCAT
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;
}
#endif
#ifndef ZCAT
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);
}
#endif

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);
}

#ifndef ZCAT
/*
 * 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;
}
#endif

#ifndef ZCAT
/*---------------------------------------------------------------------*/
/* 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;
}
#endif


#ifndef ZCAT
/*
 * Copy the last modified date from one file to another. Called with: from:
 * me 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;
}
#endif

#ifndef ZCAT
/****************************************************************************/
/*
 * 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;
}
#endif

#ifndef ZCAT
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);
}
#endif
