tcompress: import Plan9 compress - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit cb58f3291cd93a50489d646a198f5437fdffc3ef
parent 6510a2d3530132753a6a1dfb2589e9ad82bc271c
Author: sean 
Date:   Wed, 15 Jan 2020 11:54:20 +0000

compress: import Plan9 compress

Add #define USED(x)... boilerplate

compress: import Plan9 manpage.

Diffstat:
  A man/man1/compress.1                 |     237 +++++++++++++++++++++++++++++++
  A src/cmd/compress/compress.c         |    1274 +++++++++++++++++++++++++++++++
  A src/cmd/compress/mkfile             |      15 +++++++++++++++

3 files changed, 1526 insertions(+), 0 deletions(-)
---
diff --git a/man/man1/compress.1 b/man/man1/compress.1
t@@ -0,0 +1,237 @@
+.TH COMPRESS 1
+.SH NAME
+compress, uncompress, zcat \- compress and expand data
+.SH SYNOPSIS
+.B compress
+[
+.B \-f
+] [
+.B \-v
+] [
+.B \-c
+] [
+.B \-V
+] [
+.B \-b
+.I bits
+] [
+.I "name \&..."
+]
+.PP
+.B uncompress
+[
+.B \-f
+] [
+.B \-v
+] [
+.B \-c
+] [
+.B \-V
+] [
+.I "name \&..."
+]
+.PP
+.B zcat
+[
+.B \-V
+] [
+.I "name \&..."
+]
+.SH DESCRIPTION
+.I Compress
+reduces the size of the named files using adaptive Lempel-Ziv coding.
+Whenever possible,
+each file is replaced by one with the extension
+.B "\&.Z,"
+while keeping the same ownership modes, access and modification times.
+If no files are specified, the standard input is compressed to the
+standard output.
+Compressed files can be restored to their original form using
+.I uncompress
+or
+.I zcat.
+.PP
+The
+.B \-f
+option will force compression of
+.I name.
+This is useful for compressing an entire directory,
+even if some of the files do not actually shrink.
+If
+.B \-f
+is not given and
+.I compress
+is run in the foreground,
+the user is prompted as to whether an existing file should be overwritten.
+.PP
+The
+.B \-c
+option makes
+.I compress/uncompress
+write to the standard output; no files are changed.
+The nondestructive behavior of
+.I zcat
+is identical to that of
+.I uncompress
+.B \-c.
+.PP
+.I Compress
+uses the modified Lempel-Ziv algorithm popularized in
+"A Technique for High Performance Data Compression",
+Terry A. Welch,
+.I "IEEE Computer,"
+vol. 17, no. 6 (June 1984), pp. 8-19.
+Common substrings in the file are first replaced by 9-bit codes 257 and up.
+When code 512 is reached, the algorithm switches to 10-bit codes and
+continues to use more bits until the
+limit specified by the
+.B \-b
+flag is reached (default 16).
+.I Bits
+must be between 9 and 16.  The default can be changed in the source to allow
+.I compress
+to be run on a smaller machine.
+.PP
+After the
+.I bits
+limit is attained,
+.I compress
+periodically checks the compression ratio.  If it is increasing,
+.I compress
+continues to use the existing code dictionary.  However,
+if the compression ratio decreases,
+.I compress
+discards the table of substrings and rebuilds it from scratch.  This allows
+the algorithm to adapt to the next "block" of the file.
+.PP
+Note that the
+.B \-b
+flag is omitted for
+.I uncompress,
+since the 
+.I bits
+parameter specified during compression
+is encoded within the output, along with
+a magic number to ensure that neither decompression of random data nor
+recompression of compressed data is attempted. 
+.PP
+.ne 8
+The amount of compression obtained depends on the size of the
+input, the number of
+.I bits
+per code, and the distribution of common substrings.
+Typically, text such as source code or English
+is reduced by 50\-60%.
+Compression is generally much better than that achieved by
+Huffman coding (as used in
+.IR pack ),
+or adaptive Huffman coding
+.RI ( compact ),
+and takes less time to compute.
+.PP
+Under the
+.B \-v
+option,
+a message is printed yielding the percentage of
+reduction for each file compressed.
+.PP
+If the
+.B \-V
+option is specified, the current version and compile options are printed on
+stderr.
+.PP
+Exit status is normally 0;
+if the last file is larger after (attempted) compression, the status is 2;
+if an error occurs, exit status is 1.
+.SH "SEE ALSO"
+pack(1), compact(1)
+.SH "DIAGNOSTICS"
+Usage: compress [\-dfvcV] [\-b maxbits] [file ...]
+.in +8
+Invalid options were specified on the command line.
+.in -8
+Missing maxbits
+.in +8
+Maxbits must follow
+.BR \-b \.
+.in -8
+.IR file :
+not in compressed format
+.in +8
+The file specified to
+.I uncompress
+has not been compressed.
+.in -8
+.IR file :
+compressed with 
+.I xx
+bits, can only handle 
+.I yy
+bits
+.in +8
+.I File
+was compressed by a program that could deal with
+more 
+.I bits
+than the compress code on this machine.
+Recompress the file with smaller
+.IR bits \.
+.in -8
+.IR file :
+already has .Z suffix -- no change
+.in +8
+The file is assumed to be already compressed.
+Rename the file and try again.
+.in -8
+.IR file :
+filename too long to tack on .Z
+.in +8
+The file cannot be compressed because its name is longer than
+12 characters.
+Rename and try again.
+This message does not occur on BSD systems.
+.in -8
+.I file
+already exists; do you wish to overwrite (y or n)?
+.in +8
+Respond "y" if you want the output file to be replaced; "n" if not.
+.in -8
+uncompress: corrupt input
+.in +8
+A SIGSEGV violation was detected which usually means that the input file has
+been corrupted.
+.in -8
+Compression: 
+.I "xx.xx%"
+.in +8
+Percentage of the input saved by compression.
+(Relevant only for
+.BR \-v \.)
+.in -8
+-- not a regular file: unchanged
+.in +8
+When the input file is not a regular file,
+(e.g. a directory), it is
+left unaltered.
+.in -8
+-- has 
+.I xx 
+other links: unchanged
+.in +8
+The input file has links; it is left unchanged.  See
+.IR ln "(1)"
+for more information.
+.in -8
+-- file unchanged
+.in +8
+No savings is achieved by
+compression.  The input remains virgin.
+.in -8
+.SH SOURCE
+.B \*9/src/cmd/compress/compress.c
+.SH "BUGS"
+Although compressed files are compatible between machines with large memory,
+.BR \-b \12
+should be used for file transfer to architectures with 
+a small process data space (64KB or less, as exhibited by the DEC PDP
+series, the Intel 80286, etc.)
diff --git a/src/cmd/compress/compress.c b/src/cmd/compress/compress.c
t@@ -0,0 +1,1274 @@
+/*
+ * compress - File compression ala IEEE Computer, June 1984.
+ *
+ * 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:
+ *        -b:        limit the max number of bits/code.
+ *        -c:        write output on stdout, don't remove original.
+ *        -d:        decompress instead.
+ *        -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.
+
+ * Authors:        Spencer W. Thomas        (decvax!harpo!utah-cs!utah-gr!thomas)
+ *                Jim McKie                (decvax!mcvax!jim)
+ *                Steve Davies                (decvax!vax135!petsd!peora!srd)
+ *                Ken Turkowski                (decvax!decwrl!turtlevax!ken)
+ *                James A. Woods                (decvax!ihnp4!ames!jaw)
+ *                Joe Orost                (decvax!vax135!petsd!joe)
+ */
+
+#ifndef USED
+#  define USED(x) if(x);else
+#endif
+
+#define _PLAN9_SOURCE
+#define _BSD_EXTENSION
+#define _POSIX_SOURCE
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define        min(a,b)        ((a>b) ? b : a)
+
+#define BITS        16
+#define HSIZE        69001                /* 95% occupancy */
+
+/*
+ * a code_int must be able to hold 2**BITS values of type int, and also -1
+ */
+typedef long        code_int;
+typedef long        count_int;
+
+static char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
+
+uchar magic_header[] = { 0x1F, 0x9D };        /* 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 */
+
+#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)
+
+count_int htab[HSIZE];
+ushort codetab[HSIZE];
+
+#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)        ((uchar *)(htab))[i]
+#define de_stack                ((uchar *)&tab_suffixof(1< 0; argc--, argv++) {
+        if (**argv == '-') {        /* A flag argument */
+                while (*++(*argv)) {        /* Process all flags in this arg */
+                switch (**argv) {
+                case 'C':
+                        block_compress = 0;
+                        break;
+#ifdef DEBUG
+                case 'D':
+                        debug = 1;
+                        break;
+                case 'V':
+                        verbose = 1;
+                        version();
+                        break;
+#else
+                case 'V':
+                        version();
+                        break;
+#endif
+                case 'b':
+                        if (!ARGVAL()) {
+                                fprintf(stderr, "Missing maxbits\n");
+                                Usage();
+                                exit(1);
+                        }
+                        maxbits = atoi(*argv);
+                        goto nextarg;
+                case 'c':
+                        zcat_flg = 1;
+                        break;
+                case 'd':
+                        do_decomp = 1;
+                        break;
+                case 'f':
+                case 'F':
+                        overwrite = 1;
+                        force = 1;
+                        break;
+                case 'n':
+                        nomagic = 1;
+                        break;
+                case 'q':
+                        quiet = 1;
+                        break;
+                case 'v':
+                        quiet = 0;
+                        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 != 0) {                        /* 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);
+                        continue;
+                }
+                /* Check the magic number */
+                if (nomagic == 0) {
+                        if ((getchar() != (magic_header[0] & 0xFF))
+                        || (getchar() != (magic_header[1] & 0xFF))) {
+                                fprintf(stderr, "%s: not in compressed format\n",
+                                        *fileptr);
+                                continue;
+                        }
+                        maxbits = getchar();        /* 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);
+                        continue;
+                }
+                (void) 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);
+#ifndef BSD4_2
+                if ((cp=strrchr(ofname,'/')) != NULL)
+                        cp++;
+                else
+                        cp = ofname;
+                /*
+                 *** changed 12 to 25;  should be NAMELEN-3, but I don't want
+                 * to fight the headers.        ehg 5 Nov 92 **
+                 */
+                if (strlen(cp) > 25) {
+                        fprintf(stderr, "%s: filename too long to tack on .Z\n",
+                                cp);
+                        continue;
+                }
+#endif
+                strcat(ofname, ".Z");
+                }
+                /* Check for overwrite of existing file */
+                if (overwrite == 0 && zcat_flg == 0 &&
+                   stat(ofname, &statbuf) == 0) {
+                        char response[2];
+
+                        response[0] = 'n';
+                        fprintf(stderr, "%s already exists;", ofname);
+                        if (foreground()) {
+                                fprintf(stderr,
+                                    " do you wish to overwrite %s (y or n)? ",
+                                        ofname);
+                                fflush(stderr);
+                                (void) 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);
+                                continue;
+                        }
+                        if(!quiet)
+                                fprintf(stderr, "%s: ", *fileptr);
+                }
+
+                /* Actually do the compression/decompression */
+                if (do_decomp == 0)
+                        compress();
+#ifndef DEBUG
+                else
+                        decompress();
+#else
+                else if (debug == 0)
+                        decompress();
+                else
+                        printcodes();
+                if (verbose)
+                        dump_tab();
+#endif                                                /* DEBUG */
+                if(zcat_flg == 0) {
+                        copystat(*fileptr, ofname);        /* Copy stats */
+                        if (exit_stat == 1 || !quiet)
+                                putc('\n', stderr);
+                }
+        }
+        } else {                /* Standard input */
+        if (do_decomp == 0) {
+                compress();
+#ifdef DEBUG
+                if(verbose)
+                        dump_tab();
+#endif
+                if(!quiet)
+                        putc('\n', stderr);
+        } else {
+                /* Check the magic number */
+                if (nomagic == 0) {
+                if ((getchar()!=(magic_header[0] & 0xFF))
+                 || (getchar()!=(magic_header[1] & 0xFF))) {
+                        fprintf(stderr, "stdin: not in compressed format\n");
+                        exit(1);
+                }
+                maxbits = getchar();        /* 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);
+                }
+                }
+#ifndef DEBUG
+                decompress();
+#else
+                if (debug == 0)
+                        decompress();
+                else
+                        printcodes();
+                if (verbose)
+                        dump_tab();
+#endif                                                /* DEBUG */
+        }
+        }
+        exit(exit_stat);
+        return 0;
+}
+
+static int offset;
+long in_count = 1;                /* length of input */
+long bytes_out;                        /* length of compressed output */
+long 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.
+ */
+void
+compress(void)
+{
+        code_int ent, hsize_reg;
+        code_int i;
+        int c, disp, hshift;
+        long fcode;
+
+        if (nomagic == 0) {
+                putchar(magic_header[0]);
+                putchar(magic_header[1]);
+                putchar((char)(maxbits | block_compress));
+                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 = getchar ();
+
+        hshift = 0;
+        for (fcode = (long)hsize;  fcode < 65536L; fcode *= 2)
+                hshift++;
+        hshift = 8 - hshift;                /* set hash code range bound */
+
+        hsize_reg = hsize;
+        cl_hash( (count_int) hsize_reg);                /* clear hash table */
+
+        while ((c = getchar()) != 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) {
+#ifdef DEBUG
+        fprintf( stderr,
+                "%ld chars in, %ld codes (%ld bytes) out, compression factor: ",
+                in_count, out_count, bytes_out );
+        prratio( stderr, in_count, bytes_out );
+        fprintf( stderr, "\n");
+        fprintf( stderr, "\tCompression as in compact: " );
+        prratio( stderr, in_count-bytes_out, in_count );
+        fprintf( stderr, "\n");
+        fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n",
+                free_ent - 1, n_bits );
+#else /* !DEBUG */
+        fprintf( stderr, "Compression: " );
+        prratio( stderr, in_count-bytes_out, in_count );
+#endif /* DEBUG */
+        }
+        if(bytes_out > in_count)        /* exit(2) if no savings */
+                exit_stat = 2;
+}
+
+/*
+ * 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).  When the buffer fills up empty it and start over.
+ */
+
+static char buf[BITS];
+
+uchar lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
+uchar rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+void
+output( code )
+code_int  code;
+{
+#ifdef DEBUG
+        static int col = 0;
+#endif
+        int r_off = offset, bits= n_bits;
+        char *bp = buf;
+
+#ifdef DEBUG
+        if (verbose)
+                fprintf(stderr, "%5d%c", code,
+                        (col+=6) >= 74? (col = 0, '\n'): ' ');
+#endif
+        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 {
+                                putchar(*bp++);
+                        } 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);
+                        }
+#ifdef DEBUG
+                        if ( debug ) {
+                                fprintf(stderr,
+                                        "\nChange to %d bits\n", n_bits);
+                                col = 0;
+                        }
+#endif
+                }
+        } 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 );
+#ifdef DEBUG
+                if ( verbose )
+                        fprintf( stderr, "\n" );
+#endif
+                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.
+ */
+void
+decompress(void)
+{
+        int finchar;
+        code_int code, oldcode, incode;
+        uchar *stackp;
+
+        /*
+        * 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) = (uchar)code;
+        }
+        free_ent = (block_compress? FIRST: 256);
+
+        finchar = oldcode = getcode();
+        if(oldcode == -1)                /* EOF already? */
+                return;                        /* Get out of here */
+        putchar((char)finchar);                /* 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 {
+                        putchar(*--stackp);
+                } while (stackp > de_stack);
+
+                /*
+                 * Generate the new entry.
+                 */
+                if ( (code=free_ent) < maxmaxcode ) {
+                        tab_prefixof(code) = (ushort)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()
+{
+        int r_off, bits;
+        code_int code;
+        static int offset = 0, size = 0;
+        static uchar buf[BITS];
+        uchar *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;
+}
+
+#ifdef DEBUG
+printcodes()
+{
+        /*
+        * Just print out codes from input file.  For debugging.
+        */
+        code_int code;
+        int col = 0, bits;
+
+        bits = n_bits = INIT_BITS;
+        maxcode = MAXCODE(n_bits);
+        free_ent = ((block_compress) ? FIRST : 256 );
+        while ( ( code = getcode() ) >= 0 ) {
+        if ( (code == CLEAR) && block_compress ) {
+                        free_ent = FIRST - 1;
+                        clear_flg = 1;
+        }
+        else if ( free_ent < maxmaxcode )
+                free_ent++;
+        if ( bits != n_bits ) {
+                fprintf(stderr, "\nChange to %d bits\n", n_bits );
+                bits = n_bits;
+                col = 0;
+        }
+        fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
+        }
+        putc( '\n', stderr );
+        exit( 0 );
+}
+
+code_int sorttab[1<= 0) {
+                        sorttab[codetabof(i)] = i;
+                }
+        }
+        first = block_compress ? FIRST : 256;
+        for(i = first; i < free_ent; i++) {
+                fprintf(stderr, "%5d: \"", i);
+                de_stack[--stack_top] = '\n';
+                de_stack[--stack_top] = '"';
+                stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff,
+        stack_top);
+                for(ent=htabof(sorttab[i]) & ((1< 256;
+                        ent=htabof(sorttab[ent]) & ((1<> maxbits,
+                                                stack_top);
+                }
+                stack_top = in_stack(ent, stack_top);
+                fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr);
+                        stack_top = STACK_SIZE;
+        }
+        } else if(!debug) {        /* decompressing */
+
+        for ( i = 0; i < free_ent; i++ ) {
+                ent = i;
+                c = tab_suffixof(ent);
+                if ( isascii(c) && isprint(c) )
+                fprintf( stderr, "%5d: %5d/'%c'  \"",
+                                ent, tab_prefixof(ent), c );
+                else
+                fprintf( stderr, "%5d: %5d/\\%03o \"",
+                                ent, tab_prefixof(ent), c );
+                de_stack[--stack_top] = '\n';
+                de_stack[--stack_top] = '"';
+                for ( ; ent != NULL;
+                        ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) {
+                stack_top = in_stack(tab_suffixof(ent), stack_top);
+                }
+                fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr );
+                stack_top = STACK_SIZE;
+        }
+        }
+}
+
+int
+in_stack(int c, int stack_top)
+{
+        if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
+                de_stack[--stack_top] = c;
+        } else {
+                switch( c ) {
+                case '\n': de_stack[--stack_top] = 'n'; break;
+                case '\t': de_stack[--stack_top] = 't'; break;
+                case '\b': de_stack[--stack_top] = 'b'; break;
+                case '\f': de_stack[--stack_top] = 'f'; break;
+                case '\r': de_stack[--stack_top] = 'r'; break;
+                case '\\': de_stack[--stack_top] = '\\'; break;
+                default:
+                 de_stack[--stack_top] = '0' + c % 8;
+                 de_stack[--stack_top] = '0' + (c / 8) % 8;
+                 de_stack[--stack_top] = '0' + c / 64;
+                 break;
+                }
+                de_stack[--stack_top] = '\\';
+        }
+        return stack_top;
+}
+#endif /* DEBUG */
+
+void
+writeerr(void)
+{
+        perror(ofname);
+        unlink(ofname);
+        exit(1);
+}
+
+void
+copystat(ifname, ofname)
+char *ifname, *ofname;
+{
+        int mode;
+        time_t timep[2];                        /* should be struct utimbuf */
+        struct stat statbuf;
+
+        fclose(stdout);
+        if (stat(ifname, &statbuf)) {                /* Get stat on input file */
+                perror(ifname);
+                return;
+        }
+        if (!S_ISREG(statbuf.st_mode)) {
+                if (quiet)
+                        fprintf(stderr, "%s: ", ifname);
+                fprintf(stderr, " -- not a regular file: unchanged");
+                exit_stat = 1;
+        } else if (exit_stat == 2 && !force) {
+                /* No compression: remove file.Z */
+                if (!quiet)
+                        fprintf(stderr, " -- file unchanged");
+        } else {                        /* Successful Compression */
+                exit_stat = 0;
+                mode = statbuf.st_mode & 0777;
+                if (chmod(ofname, mode))                /* Copy modes */
+                        perror(ofname);
+                /* Copy ownership */
+                chown(ofname, statbuf.st_uid, statbuf.st_gid);
+                timep[0] = statbuf.st_atime;
+                timep[1] = statbuf.st_mtime;
+                /* Update last accessed and modified times */
+                utime(ofname, (struct utimbuf *)timep);
+//                if (unlink(ifname))        /* Remove input file */
+//                        perror(ifname);
+                return;                        /* success */
+        }
+
+        /* Unsuccessful return -- one of the tests failed */
+        if (unlink(ofname))
+                perror(ofname);
+}
+
+/*
+ * This routine returns 1 if we are running in the foreground and stderr
+ * is a tty.
+ */
+int
+foreground(void)
+{
+        if(bgnd_flag)                        /* background? */
+                return 0;
+        else                                /* foreground */
+                return isatty(2);        /* and stderr is a tty */
+}
+
+void
+onintr(int x)
+{
+        USED(x);
+        unlink(ofname);
+        exit(1);
+}
+
+void
+oops(int x)                /* wild pointer -- assume bad input */
+{
+        USED(x);
+        if (do_decomp == 1)
+                fprintf(stderr, "uncompress: corrupt input\n");
+        unlink(ofname);
+        exit(1);
+}
+
+void
+cl_block(void)                /* table clear for block compress */
+{
+        long rat;
+
+        checkpoint = in_count + CHECK_GAP;
+#ifdef DEBUG
+        if ( debug ) {
+                fprintf ( stderr, "count: %ld, ratio: ", in_count );
+                prratio ( stderr, in_count, bytes_out );
+                fprintf ( stderr, "\n");
+        }
+#endif /* DEBUG */
+
+        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;
+#ifdef DEBUG
+                if (verbose)
+                        dump_tab();        /* dump string table */
+#endif
+                cl_hash((count_int)hsize);
+                free_ent = FIRST;
+                clear_flg = 1;
+                output((code_int)CLEAR);
+#ifdef DEBUG
+                if (debug)
+                        fprintf(stderr, "clear\n");
+#endif /* DEBUG */
+        }
+}
+
+void
+cl_hash(count_int hsize)                /* reset code table */
+{
+        count_int *htab_p = htab+hsize;
+        long i;
+        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;
+}
+
+void
+prratio(stream, num, den)
+FILE *stream;
+long num, den;
+{
+        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);
+}
+
+void
+version(void)
+{
+        fprintf(stderr, "%s\n", rcs_ident);
+        fprintf(stderr, "Options: ");
+#ifdef DEBUG
+        fprintf(stderr, "DEBUG, ");
+#endif
+#ifdef BSD4_2
+        fprintf(stderr, "BSD4_2, ");
+#endif
+        fprintf(stderr, "BITS = %d\n", BITS);
+}
+
+/*
+ * The revision-history novel:
+ *
+ * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $
+ * $Log:        compress.c,v $
+ * Revision 4.0  85/07/30  12:50:00  joe
+ * Removed ferror() calls in output routine on every output except first.
+ * Prepared for release to the world.
+ *
+ * Revision 3.6  85/07/04  01:22:21  joe
+ * Remove much wasted storage by overlaying hash table with the tables
+ * used by decompress: tab_suffix[1<putc] and
+ * added signal catcher [plus beef in writeerr()] to delete effluvia.
+ *
+ * Revision 2.0        84/08/28  22:00:00  petsd!joe
+ * Add check for foreground before prompting user.  Insert maxbits into
+ * compressed file.  Force file being uncompressed to end with ".Z".
+ * Added "-c" flag and "zcat".  Prepared for release.
+ *
+ * Revision 1.10  84/08/24  18:28:00  turtlevax!ken
+ * Will only compress regular files (no directories), added a magic number
+ * header (plus an undocumented -n flag to handle old files without headers),
+ * added -f flag to force overwriting of possibly existing destination file,
+ * otherwise the user is prompted for a response.  Will tack on a .Z to a
+ * filename if it doesn't have one when decompressing.  Will only replace
+ * file if it was compressed.
+ *
+ * Revision 1.9  84/08/16  17:28:00  turtlevax!ken
+ * Removed scanargs(), getopt(), added .Z extension and unlimited number of
+ * filenames to compress.  Flags may be clustered (-Ddvb12) or separated
+ * (-D -d -v -b 12), or combination thereof.  Modes and other status is
+ * copied with copystat().  -O bug for 4.2 seems to have disappeared with
+ * 1.8.
+ *
+ * Revision 1.8  84/08/09  23:15:00  joe
+ * Made it compatible with vax version, installed jim's fixes/enhancements
+ *
+ * Revision 1.6  84/08/01  22:08:00  joe
+ * Sped up algorithm significantly by sorting the compress chain.
+ *
+ * Revision 1.5  84/07/13  13:11:00  srd
+ * Added C version of vax asm routines.  Changed structure to arrays to
+ * save much memory.  Do unsigned compares where possible (faster on
+ * Perkin-Elmer)
+ *
+ * Revision 1.4  84/07/05  03:11:11  thomas
+ * Clean up the code a little and lint it.  (Lint complains about all
+ * the regs used in the asm, but I'm not going to "fix" this.)
+ *
+ * Revision 1.3  84/07/05  02:06:54  thomas
+ * Minor fixes.
+ *
+ * Revision 1.2  84/07/05  00:27:27  thomas
+ * Add variable bit length output.
+ */
diff --git a/src/cmd/compress/mkfile b/src/cmd/compress/mkfile
t@@ -0,0 +1,15 @@
+<$PLAN9/src/mkhdr
+
+TARG=\
+        compress \
+        zcat \
+        uncompress
+
+$O.uncompress:Q: $O.compress
+        cp $prereq $target
+
+$O.zcat:Q: $O.compress
+        cp $prereq $target
+
+<$PLAN9/src/mkmany
+