[comp.os.minix] Compress 4.3 sources

dal@syntel.UUCP (Dale Schumacher) (05/17/89)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	readme
#	makefile
#	compress.h
#	compress.fns
#	compusi.c
# This archive created: 17-May-1989 1:35:28 ENOENV
# By:	ENOENV (ENOENV)
cat << \SHAR_EOF > readme

The enclosed files are the COMPRESS v4.3 file compression utility
with modifications to compile on Minix-ST v1.1 with the ACK compiler.
This version supports compression/decompression with up to 16 bits.
It probably WON'T work (at least for >13 bit compression) under
Minix-PC.

				Dale Schumacher
	bungia!midgard.mn.org!syntel!dal -or- dal@syntel.UUCP


Notes on the port to MINIX-ST:

The get_one() function fails because MINIX won't support the dubious
practice of reading from fd=2 (aka stderr).  Instead, "/dev/tty" is
opened and read from directly.  This may not be the best way to handle
this problem, but it works.  I took the example from the Minix
distribution sources (compress.c,v 4.1 85/12/05 09:00:00 kent).

The defaults for the version of compress which was supplied with the
MINIX-ST 1.1 distribution are apparently quite different from those
of current Unix implementations.  I've added a compile-time switch
to allow 'verbose' to be default TRUE.  I've also taken advantange
of the switch for making 'keep source' also default TRUE.  The comments
in the code imply that both of these options are typically defaulted
to FALSE, but I've chosen to remain as close to the original compress
as possible.  Similarly, the default number of bits to use is 13,
although up to 16 will work properly.

In order to do 16-bit compression, you must "chmem =400000 compress".
If you still get a "not enough memory to compress" error, bump the
chmem value up a bit more.  If you only want to use 13 bits maximum
for compression, "chmem =65000" seems to be sufficient.  Decompression
takes far less space, so 16-bit decompression can be done even with
the smaller chmem value.  You may even be able to make the value
smaller and still do decompression, but the initial value from the
compiler of about 45000 is not large enough to compress with 13 bits.

Just for peace-of-mind, I have verified that this compress can
decompress files created with the original compress, and the
original compress can decompress files created with this new
compress, as long as you don't use more than 13 bits (of course).
SHAR_EOF
cat << \SHAR_EOF > makefile
#
# MAKE FILE FOR UNIX SYSTEMS (MINIX)
# add what optimizion options your system supports
# on some systems there may be no setvbuf() or the code may generate
# errors if the large buffer is used for the stream, if so :
# add -DNO_SETVBUF (this is done in compress.h under #ifdef UNIX  -Dal)
# and if your system doesn't support either setvbuf or setbuf
# add -DNO_SETBUF
# add -DALLOC if your system uses alloc() instead of malloc()
# add -DNOSIGNAL for faster processing on a unix pc

CFLAGS=-DMINIX -DUNIX -DNDEBUG

OFLAGS=
LIB=
PROG=compress
OBJ=compress.o compusi.o compapi.o
BIN=/usr/bin

$(PROG): $(OBJ)
	cc -o $(PROG) $(OFLAGS) $(OBJ) $(LIB)
	chmem =400000 $(PROG)

install:
	cp $(PROG) $(BIN)
	ln $(BIN)/$(PROG) $(BIN)/uncompress
	ln $(BIN)/$(PROG) $(BIN)/zcat

compapi.o: compress.h

compress.o: compress.h

compusi.o: compress.h
SHAR_EOF
cat << \SHAR_EOF > compress.h
/*@H************************ < COMPRESS HEADER > ****************************
*   $@(#) compress.c,v 4.3 88/12/26 08:00:00 don Release ^                  *
*                                                                           *
*   compress : compress.h <global defines, etc>                             *
*                                                                           *
*   port by  : Donald J. Gloistein                                          *
*                                                                           *
*   Source, Documentation, Object Code:                                     *
*   released to Public Domain.  This code is based on code as documented    *
*   below in release notes.                                                 *
*                                                                           *
*---------------------------  Module Description  --------------------------*
*   THIS HEADER CONTAINS MUCH IMPLEMENTATION INFORMATION AND ASSUMPTIONS    *
*   PLEASE PRINT IT OUT AND READ IT BEFORE COMPILING CODE FOR YOURSELF      *
*                                                                           *
*   This header supports a number of compiler defines and predefines.       *
*   Rather than explain all of them, please print the header and read the   *
*   notes. Also the unix and xenix makefiles are commented for the          *
*   various options. There continues to have a lot of Dos specific info in  *
*   the header. This is to help on 16 bit Msdos machines to get their       *
*   compiler to work properly. I make no appology for that, as this port    *
*   began as a way to implement 16 bit compress on a segmented MsDos machine*
*                                                                           *
*   However, for Unix and Xenix, all you should have to define is -DXENIX   *
*   or -DUNIX and compile. There may be a problem with whether your library *
*   supports alloc() or malloc(), but there is a define for that, also.     *
*                                                                           *
*   This header can be maintained to keep up with the different compilers   *
*   and systems. As distributed in don Release, the files will compile with *
*   no changes under Microsoft version 5.1 C compiler, and Xenix C compiler *
*   which is the Microsoft version 4 ported. If you are going to bind the   *
*   code for use in MsDos and OS/2 machines, then you must uncomment the    *
*   #define BIND in this header. Otherwise, this distribution of source     *
*   detect Msdos and Xenix predefines from the compiler and adjust.         *
*                                                                           *
*--------------------------- Implementation Notes --------------------------*
*                                                                           *
*   compiled with : compress.fns                                            *
*
*   NOTE!!!  Defaults of this code now are completely Unix, even for the    *
*            msdos ports.  That means that the program works as a filter,   *
*            and will just sit there waiting for input from stdin if you    *
*            issue just the command name. You must use -h or -? to get the  *
*            full help screen now. Also, it will unlink (kill) as a default *
*            on successful compression and decompression. That means the    *
*            source file will be erased.                                    *
*            These defaults are changed with the FILTER and KEEPFLAG        *
*            defines.                                                       *
*                                                                           *
*   NOTE!!!  Compiler predefines were taken out of the compress.h header.   *
*            You must either specify them on compile or uncomment the       *
*            compiler define in this header. Compiling without doing these  *
*            will result in a program that does unspecified actions.        *
*   problems:                                                               *
*            The inpath and outpath is a bit kludged. It should work okay.  *
*            Let me know if you have problems, especially under Unix.       *
*                                                                           *
*   CAUTION: The bound version will run on Dos 2.x, but you must use the    *
*            name compress.exe.  If you rename the file, it will not run    *
*            The unbound version will run on Dos 2.x with the name changed  *
*            but due to the dos version, will not detect its own name.      *
*                                                                           *
*   CAUTION: Non MsDos users. You must modify the _MAX_PATH defines for     *
*            your operating system if it is different from the assumed      *
*            standard.                                                      *
*                                                                           *
*   CAUTION: I have used a number of defines to make it possible to compile *
*            properly under a number of bit sizes and adjust for the memory *
*            allocation scheme needed. If you do not use a dos system,      *
*            PLEASE pay attention to the defines for MAXSEG_64 and the one  *
*            called SMALLMODEL. The SMALLMODEL define is set in the header  *
*            but if you don't have a compiler that triggers the MAXSEG_64   *
*            define, you may end up with bad pointers. Becareful.           *
*                                                                           *
*   Header for files using version 4 compress routines define MAIN          *
*   in the file with defining instance of the global variables.             *
*   There are a number of compilers for MsDos and Unix/Xenix.               *
*   So the user must define the actions required.                           *
*                                                                           *
*                                                                           *
*   Defines:  This header file contains most of the system wide defines.    *
*             the purpose for this was to consolodate compiler differences  *
*             into one area that is easily changed.                         *
*                                                                           *
*   define MAXBITS= if you want a different maximum bits. 16 bits will now  *
*   run in about 400K of memory.                                            *
*   define BIND if you are going to use Microsoft bind.exe program on the   *
*   executable.                                                             *
*                                                                           *
*   define MSDOS if you are compiling under MsDos or PcDos and your compiler*
*   does not predefine it.                                                  *
*                                                                           *
*     Initials ---- Name ---------------------------------                  *
*      DjG          Donald J. Gloistein, current port to MsDos 16 bit       *
*                   Plus many others, see rev.hst file for full list        *
*      LvR          Lyle V. Rains, many thanks for improved implementation  *
*                   of the compression and decompression routines.          *
*************************************************************************@H*/

#ifndef FALSE            /* let's get some sense to this */
#define FALSE 0
#define TRUE !FALSE
#endif



#ifdef UNIX
#define NPROTO
#define COMP40           /* take this out for a little more speed */
#ifdef ALLOC
char *alloc();
#define ALLOCATE(x,y)   alloc((unsigned int)x*y)
#else
char *malloc();
#define ALLOCATE(x,y)   malloc((unsigned int)x*y)
#endif
#define FREEIT(ptr)     free(ptr)
#define setbinary(fp)
#define NO_SETVBUF      /* most don't support setvbuf() function */
#endif

			/* NOTE: This program will not work on the */
			/* IBM/PC version of MINIX due to the small */
			/* memory model restrictions.  There is no */
			/* such thing as a FAR pointer in MINIX-PC */
#ifdef MINIX		/* Unix V7 clone for Atari ST */
#define	strchr	index
#define	strrchr	rindex
#define DFLTBITS 13	/* compatible with original MINIX compress, max=16 */
#ifndef KEEPFLAG
#define	KEEPFLAG 1	/* compatible with original MINIX compress */
			/* may be redefined from the Makefile if desired */
#endif
#ifndef VERBOSE
#define	VERBOSE TRUE	/* compatible with original MINIX compress */
			/* may be redefined from the Makefile if desired */
#endif
#define NPROTO
#define SIGTYPE int	/* MINIX defines this as pointer to int */
			/* for the return from a signal         */
#endif

                          /* Microsoft C compiler  v 4.0-5.1 */
                          /* MSC is defined in makefile.msc        */
#ifdef MSC
#define FAR far        
#define MAXSEG_64K
#define NO_REVSEARCH
#define CONST const
#define ALLOCTYPE void
#ifdef M_I86SM
#define SMALLMODEL        /* compiled in small model */
#endif
#define setbinary(fp)    setmode(fileno((fp)), O_BINARY)
#endif

                          /* Mark Williams C for Atari ST (V3.0.5) */
                          /* MWC is defined in makefile.mwc        */
#ifdef MWC
#define MSDOS
#define NOSIGNAL
#define DFLTBITS 14
#define NPROTO
/* #define MAXSEG_64K */  /* use this if your compiler has a problem with */
                          /* indexing arrays larger than 64k              */
#define NO_SETVBUF
#define NO_REVSEARCH
#define ALLOCATE(x,y)   lcalloc((unsigned long)(x),((unsigned long)(unsigned)(y)))
#define FREEIT(ptr)     free(ptr)
#define setbinary(fp)    ((fp)->_ff &= ~_FASCII)
#endif

                          /* Sozobon or Alcyon (4.14) C for Atari ST */
                          /* used with the dLibs standard library    */
#ifdef ALCYON
#define	SOZOBON	1
#endif
#ifdef SOZOBON
#define MSDOS
#define NOSIGNAL
#define DFLTBITS 14
#define NPROTO
/* #define MAXSEG_64K */  /* use this if your compiler has a problem with */
                          /* indexing arrays larger than 64k              */
#define ALLOCATE(x,y)   lalloc((unsigned long)(x) * ((unsigned long)(y)))
#define FREEIT(ptr)     free(ptr)
#define setbinary(fp)    ((fp)->_flag |= _IOBIN)
#define	FILTER	FALSE
#endif


#ifdef __ZTC__                 /* Zortech compiler           */
#define setbinary(fp)    ((fp)->_flag&=~_IOTRAN)
#define NO_REVSEARCH
#define CONST const
#define MAXSEG_64K
#endif

#ifdef XENIX
#define setbinary(fp)
#define FAR far
#define CONST
#define SIGTYPE int           /* xenix defines this as pointer to int */
                              /* for the return from a signal         */
#ifdef M_I286
#define MAXSEG_64K
#endif
#define NO_SETVBUF            /* evidently xenix chokes on the large buff*/
#endif                        /* really needs to be fine tuned           */

#ifdef MCH_AMIGA
#define CONST
#define MAXSEG_64K             /* Manx C compiler limitation */
#define NO_SETVBUF
#endif

#ifdef vms
#define NO_SETVBUF
#endif

#ifdef __TURBOC__
#define MSDOS
#define MAXSEG_64
#define NO_REVSEARCH
#ifdef __SMALL__
#define SMALLMODEL
#endif
#define CONST const
#define FAR             far
#define setbinary(fp)   setmode(fileno((fp)), O_BINARY)
#endif

/* FILTER  if you want the program to operate as a unix type filter */
/*  if not defined TRUE, then issuing command without parameters will */
/*  print a usage and help information                              */
/*  Use -DFILTER=0 to deactivate filter operation                   */
#ifndef FILTER
#define FILTER  TRUE
#endif

/* KEEPFLAG determines the default action on successful completion */
/* Unix convention is FALSE (erase input file)                     */
/* Use -DKEEPFLAG=1 to keep files as default or change here        */
/* if you don't set it before here and you are compiling the debug */
/* version, then files will be kept.                               */

#ifndef KEEPFLAG
#ifdef NDEBUG
#define KEEPFLAG FALSE
#else
#define KEEPFLAG TRUE
#endif
#endif


/* Does your compiler support extended prototyping?  */
/* Uncomment the following if your compiler does not.*/
/* support full prototyping, such as:                */
/*      char *emalloc(unsigned,int);                 */
/* if defined it will use the following:             */
/*      char *emalloc();                             */
/* #define NPROTO */

/* putting the include files in one location */

#include <ctype.h>

#ifdef MINIX
#define	assert(x)
extern char *index(), *rindex(), *strcat(), *strcpy(), *strncat(), *strncpy();
#else
#include <assert.h>
#include <string.h>
#endif

#ifndef NOSIGNAL
#include <signal.h>
#endif

#ifdef MWC
#include <stdlib.h>
#include <types.h>
#include <stat.h>
#else
#ifdef SOZOBON
#include <stdio.h>
#include <stat.h>
#include <limits.h>
#include <malloc.h>
#include <errno.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif
#endif

#ifdef M_XENIX
#include <fcntl.h>
#endif


#ifdef MSC
#include <stdlib.h>
#include <io.h>
#include <sys/utime.h>
#include <fcntl.h>
#include <limits.h>
#endif

#ifdef __TURBOC__
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <limits.h>
#endif

#ifndef NOSIGNAL
#ifndef SIGTYPE
#define SIGTYPE void
#endif
#ifndef SIG_ERR
#define SIG_ERR (SIGTYPE(*)())-1
#endif
#endif

/* This is for Microsoft C v5.1 to compile and be bound  */
/* for use with os/2.  Also if compiled just for os/2    */
/* The signal function is different between protected    */
/* and real mode.                                        */

/* #define BIND */

#ifdef INCL_DOSPROCESS
#define ISOS2 TRUE
#endif

#ifdef BIND
#define ISOS2 TRUE
#endif

/*  The following is defined in MSC 5.1 stdlib.h
    Replace with appropriate values for your system.
    These values are used for MSDos system.
*/
#ifndef _MAX_PATH
#define _MAX_PATH  144      /* max. length of full pathname       */
#define _MAX_DRIVE   3      /* max. length of drive component     */
#define _MAX_DIR   130      /* max. length of path component      */
#define _MAX_FNAME   9      /* max. length of file name component */
#define _MAX_EXT     5      /* max. length of extension component */
#endif

/* the following tells the system that the maximum segment is 64k */
/* if your compiler is not one of these and has this limitation   */
/* Because of this, this code should compile with minimum porting */
/* in the COMPUSI.XEN module to most unix systems.                */
/* This is also used to keep array indexing to 16 bit integer     */
/* if not predefined in compiler implementation, you must define  */
/* it separately if applicable to your compiler/system            */
/* #define MAXSEG_64K                                             */

/* put this in if you are compiling in small code */
/* model and your compiler does not predefine it  */
/* this is for CPU' with 64k segment limitation.  */
/* Use this define for small code, it is used by  */
/* the header to decide on value for NEARHEAP     */
/* #define SMALLMODEL */

/* does your system use far pointers ? if you want it enabled keep this */
/* if you have segment limit and compile in larger than 13 bits         */
/* then you will have to use compact or large model if your compiler    */
/* does not support far pointer keyword.                                */

#ifndef FAR
#define FAR
#endif

/* What type does the alloc() function return, char or void? */

#ifndef ALLOCTYPE
#define ALLOCTYPE char
#endif

/* If you use compusi.dos and your computer supports the unix */
/* file links and the link code is set properly in the stat() */
/* then define the following:                                 */
/* #define USE_LINKS */

/* Does your run time library support the ANSI functions for:*/

/* reverse string set search?  strrpbrk() if so:             */
/*#define NO_REVSEARCH*//* dos module uses this function. */
/* #define NO_REVSEARCH */

/* Does your library include strrchr()? If not define this:  */
/*#define NO_STRRCHR*//* unix/xenix module uses this function*/

/* Does your library include strchr()? If not define this:   */
/*#define NO_STRCHR*//* dos module uses this function.       */

/* definition for const key word if supported */
#ifndef CONST
#define CONST
#endif


/*  And now for some typedefs */
typedef unsigned short CODE;
typedef unsigned char UCHAR;
typedef unsigned int HASH;
typedef int FLAG;

  /* 
   * You can define the value of MAXBITS to be anything betweeen MINBITS
   * and MAXMAXBITS.  This is will determine the maximum memory you will
   * use and how the tables will be handled.  I recommend you just leave
   * it at MAXMAXBITS, because you can define DFLTBITS in compiling the
   * module COMPRESS.C to set the default, and you can vary the number
   * of bits at runtime by using the -b switch.
   */

   /*
   * The only reason to change MAXBITS is if you absolutely must have
   * faster performance. If you specify 14 bits, the tables will not
   * be split; at 13 bits, you can fit in the MSDOS small memory model
   * and allocate tables in near heap.
   * This value is available to other modules through the variable maxbits.
   */

#define INITBITS    9
#define MINBITS     12
#define MAXMAXBITS  16

#ifndef MAXBITS
#define MAXBITS    MAXMAXBITS
#endif

#if (MAXBITS > MAXMAXBITS)
#undef MAXBITS
#define MAXBITS    MAXMAXBITS
#endif

#if (MAXBITS < MINBITS)
#undef MAXBITS
#define MAXBITS  MINBITS
#endif

  /* You should define DFLTBITS to be the default compression code
   * bit length you desire on your system.
   * (I define mine in the compiler command line in my Makefile.LvR)
   * (I leave mine alone and keep to the maximum. DjG)
   */

#ifndef DFLTBITS
#define DFLTBITS MAXBITS
#endif
#if (DFLTBITS < MINBITS)
#undef DFLTBITS
#define DFLTBITS MINBITS
#endif
#if (DFLTBITS > MAXBITS)
#undef DFLTBITS
#define DFLTBITS MAXBITS
#endif

/* THIS IS TO COMPILE A 13 BIT MODEL IN SMALL MEMORY AND NEAR HEAP */
/* AS SET UP IT WILL WORK WITH MICROSOFT C COMPILER                */

#if (MAXBITS < 14)
#ifdef SMALLMODEL
#define NEARHEAP TRUE
#undef FAR
#define FAR
#endif
#endif

/* correcting for different types of pointer arithmatic */
/* probably won't have to change it                     */
#define NULLPTR(type)   ((type FAR *) NULL)


/* in making this program portable the following allocation and          */
/* free functions are called, with the following parameters:             */
/*        ALLOCTYPE FAR *emalloc(unsigned int x, int y)                  */
/*                 void  efree(ALLOCTYPE FAR *ptr)                       */
/* you must define the allocation function and the free function         */
/* keep in mind that the casts must be correct for your compiler         */
/* NOTE these are the two functions to change for allocating pointers to */
/* far data space if you are not using Microsoft C v.5.1                 */
/* Consult your compiler manual and find the low level function that     */
/* returns a far pointer when compiled in the small model.               */
/* if your compiler does not support that, you will have to compile with */
/* a model that defaults to far pointers to data (compact or large model)*/
/* HERE ARE SOME SAMPLE PREDEFINED ONES                                  */

#ifdef MSC
#include <malloc.h>
#ifdef NEARHEAP
#define ALLOCATE(x,y)   malloc((size_t)((x)*(y)))
#define FREEIT(ptr)     free((ptr))
#else
#define ALLOCATE(x,y)   _fmalloc((size_t)((x)*(y)))
#define FREEIT(ptr)     _ffree((ptr))
#endif
#endif

#ifdef HUGE
#include <malloc.h>
#define ALLOCATE(x,y)   halloc((long)(x),(size_t)(y))
#define FREEIT(ptr)     hfree(ptr)
#endif

#ifdef __TURBOC__
#include <alloc.h>
#ifdef NEARHEAP
#define ALLOCATE(x,y)  malloc((unsigned int)((x)*(y)))
#define FREEIT(x)      free(x)
#else
#define ALLOCATE(x,y)  farmalloc((unsigned long)((x)*(y)))
#define FREEIT(x)    farfree(x)
#endif
#endif

/* default allocation function, in segmented addressing, must return */
/* a far pointer or compile with far pointer data as default         */
#ifndef ALLOCATE
#include <malloc.h>
#define ALLOCATE(x,y)   malloc((unsigned int)x*y)
#define FREEIT(ptr)     free((ptr))
#endif


# ifdef MAXSEG_64K
#   if  MAXBITS > 14
#       define SPLIT_HT   TRUE
#   else
#       define SPLIT_HT   0
#   endif
# else
#   define SPLIT_HT   0
# endif

# ifdef MAXSEG_64K
#   if  MAXBITS > 15
#       define SPLIT_PFX   TRUE
#   else
#       define SPLIT_PFX   0
#   endif
# else
#   define SPLIT_PFX   0
# endif

#ifndef BUFSIZ
#define BUFSIZ 512
#endif

#ifdef NO_SETBUF
#define NO_SETVBUF
#endif

#ifdef NO_SETVBUF
#   ifndef NO_SETBUF
#       define setvbuf(fp,buf,mode,size) setbuf((fp),(buf))
#       define ZBUFSIZE BUFSIZ
#       define XBUFSIZE BUFSIZ
#   else
#       define setvbuf(fp,buf,mode,size)
#       define ZBUFSIZE (1)
#       define XBUFSIZE (1)
#   endif
#else
#   ifdef NEARHEAP
#       define XBUFSIZE    (0xC00)
#       define ZBUFSIZE    (0x1800)
#   else
#       define XBUFSIZE    (0x3000)      /* 12k bytes */
#       define ZBUFSIZE    (0x6000)      /* 24k bytes */
#   endif
#endif

#define UNUSED      ((CODE)0)   /* Indicates hash table value unused    */
#define CLEAR       ((CODE)256) /* Code requesting table to be cleared  */
#define FIRSTFREE ((CODE)(CLEAR+1))/* First free code for token encoding */
#define MAXTOKLEN   512         /* Max chars in token; size of buffer   */
#define OK          0           /* Result codes from functions:         */
#ifdef vms
#define ERROR       0x10000004  /* General unspecified error            */
#define NORMAL      1           /* No error                             */
#define unlink(x)   remove(x)
#else
#    define NORMAL      0
#    ifdef ERROR
#        if (ERROR == NORMAL)
#            define ERROR   1	/* force redefine if (ERROR == NORMAL)  */
#        endif
#    else
#        define ERROR       1
#    endif
#endif
#define SIGNAL_ERROR -1         /*   signal function error              */
#define NOMEM       2           /*   Ran out of memory                  */
#define TOKTOOBIG   3           /*   Token longer than MAXTOKLEN chars  */
#define READERR     4           /*   I/O error on input                 */
#define WRITEERR    5           /*   I/O error on output                */
#define INFILEBAD   6           /*   Infile not in compressed format    */
#define CODEBAD     7           /*   Infile contained a bad token code  */
#define TABLEBAD    8           /*   The tables got corrupted (!)       */
#define NOSAVING    9           /*   no saving in file size             */
#define NOTOPENED  10           /*   output file couldn't be opened     */
#define YES         1
#define NO          0


#include "compress.fns"

/* defines opening mode for files */
/* and suffixes for compressed file */

#ifdef MSDOS
#define WRITE_FILE_TYPE "wb"
#define READ_FILE_TYPE "rb"
#define SUFFIX "Z"
#else
#ifdef vms
#define SUFFIX "_Z"
#define WRITE_FILE_TYPE "wb"
#define READ_FILE_TYPE "rb"
#else
#define WRITE_FILE_TYPE "w"
#define READ_FILE_TYPE "r"
#define SUFFIX ".Z"
#endif
#endif


/* The VERBOSE flag defines the default value of the verbose variable */
/* If it's not already defined, we set it to FALSE here since most */
/* systems set the default that way.  -Dal */
#ifndef VERBOSE
#define	VERBOSE	FALSE
#endif


/* 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 CHECK_GAP 10000L     /* ratio check interval */

#ifdef MAIN
UCHAR magic_header[] = { 0x1F,0x9D };  /* 1F 9D */

char rcs_ident[] = "@(#) compress,v 4.3 88/12/26 08:00:00 don Release $";

#ifdef MWC
long _stksize = 20000L;     /* set MWC's stack to 20,000 */
#ifdef MWC_NAME           /* if defined in makefile set _cmdname for */
char _cmdname[]=MWC_NAME;   /* compress,zcat,uncomp check for desktop and */
#endif                      /* dumb shells */
#endif
#ifdef SOZOBON
long _STKSIZ = 20000L;		/* set runtime stack to 20,000 bytes */
#ifndef CMDNAME
#define	CMDNAME "compress"
#endif
char _cmdname[]=CMDNAME;	/* force command name */
#endif

int overwrite = 0;          /* Do not overwrite unless given -f flag */
int maxbits = DFLTBITS;     /* user settable max # bits/code */

int exit_stat = 0;
int keep = KEEPFLAG;            /* True = don't kill file */
int keep_error = FALSE;     /* True = keep output file even if error exist */
char *prog_name;
char ifname[_MAX_DIR];
char inpath[_MAX_DIR];
char ofname [_MAX_DIR];
char outpath[_MAX_DIR];
int is_list = FALSE;            /* flag for file parameters */
char endchar[1];
char xbuf[XBUFSIZE];
char zbuf[ZBUFSIZE];
#ifdef MSDOS
char separator[] = "\\";
#else
char separator[] = "/";
#endif
int nomagic = FALSE;  /* Use a 3-byte magic number header, unless old file */
int zcat_flg = FALSE; /* Write output on stdout, suppress messages */
int quiet = !VERBOSE; /* 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;
#ifdef COMP40
long int ratio = 0L;
long checkpoint = CHECK_GAP;
#endif

/* force the overwrite */
int force = 0;

#ifndef NDEBUG
int verbose = VERBOSE;
int debug = FALSE;
#endif /* !NDEBUG */

#ifndef NOSIGNAL
SIGTYPE (*bgnd_flag)();
#endif

int do_decomp = FALSE;

#else               /* not defining instance */

extern UCHAR magic_header[];
extern char rcs_ident[];
#ifdef MWC
extern long _stksize;
#endif
extern int overwrite;
extern int maxbits;


extern int exit_stat;
extern int keep;
extern int keep_error;
extern char *prog_name;
extern char inpath[];
extern char outpath[];
extern int is_list;
extern char endchar[];
extern char xbuf[];
extern char zbuf[];
extern char ifname[];
extern char ofname[];
extern char separator[];
extern int nomagic;
extern int zcat_flg;
extern int quiet;
extern int block_compress;
#ifdef COMP40
extern long int ratio;
extern long checkpoint;
#endif
extern int force;

#ifndef NDEBUG
extern int verbose;
extern int debug;
#endif /* !NDEBUG */

#ifndef NOSIGNAL
extern SIGTYPE (*bgnd_flag)();
#endif
extern int do_decomp;
#endif
SHAR_EOF
cat << \SHAR_EOF > compress.fns
/*  COMPRESS.FNS  global function declarations */
/*  this should be compatible with any type of declaration for external
    functions. See compress.h for explaination */
#ifdef  NPROTO
extern  void Usage();
extern  int check_error();
extern  char *name_index();
extern  char *get_program_name();
#ifdef NO_STRCHR
extern char *strchr();
#endif
#ifdef NO_STRRCHR
extern char *strrchr();
#endif
#ifdef NO_REVSEARCH
extern char *strrpbrk();
#endif
extern  char get_one();
extern  int is_z_name();
extern  int cl_block();
extern  int make_z_name();
extern  void unmake_z_name();
extern  void compress();
extern  void putcode();
extern  void decompress();
extern  CODE getcode();
extern  void writeerr();
extern  void copystat();
#ifndef NOSIGNAL
extern  int foreground();
extern  SIGTYPE onintr();
extern  SIGTYPE oops();
#endif
extern  void prratio();
extern  void version();
#ifdef NEARHEAP
extern ALLOCTYPE *emalloc();
extern void efree();
#else
extern  ALLOCTYPE FAR *emalloc();
extern  void efree();
#endif
extern  int alloc_tables();
extern  void init_tables();
extern  int nextcode();
#else
extern  void Usage(int);
extern  int  check_error(void);
extern  char *name_index(char *);
extern  int cl_block(void);
extern  char *get_program_name(char *);
extern  char get_one(void);
extern  int is_z_name(char *);
extern  int make_z_name(char *);
extern  void unmake_z_name(char *);
#ifdef NO_STRCHR
extern char *strchr(char *,int);
#endif
#ifdef NO_STRRCHR
extern char *strrchr(char *,int);
#endif
#ifdef NO_REVSEARCH
extern char *strrpbrk(char *,char *);
#endif
extern  void compress(void);
extern  void putcode(CODE,int);
extern  void decompress(void);
extern  CODE getcode(void);
extern  void writeerr(void);
extern  void copystat(char *,char *);
#ifndef NOSIGNAL
extern  int foreground(void);
extern  SIGTYPE onintr(void);
extern  SIGTYPE oops(void);
#endif
extern  void prratio(FILE *,long,long);
extern  void version(void);
#ifdef NEARHEAP
extern ALLOCTYPE *emalloc(unsigned int,int);
extern void efree(ALLOCTYPE *);
#else
extern  ALLOCTYPE FAR *emalloc(unsigned int,int);
extern  void efree(ALLOCTYPE FAR *);
#endif
extern  int alloc_tables(CODE,HASH);
extern  void init_tables(void );
extern  int nextcode(CODE *);
#endif
SHAR_EOF
cat << \SHAR_EOF > compusi.c
/*@H************************ < COMPRESS utility> ****************************
*                                                                           *
*   compress : compusi.uni <Unix/Xenix support functions>                   *
*                                                                           *
*   port by  : Donald J. Gloistein                                          *
*                                                                           *
*   Source, Documentation, Object Code:                                     *
*   released to Public Domain. This code is ported from compress v4.0       *
*   release joe.                                                            *
*                                                                           *
*---------------------------  Module Description  --------------------------*
*   Unix system dependent routines. These are specific to either the        *
*   unix file structure or some unix only functions.                        *
*                                                                           *
*   Separated out for ease of writing the main module.                      *
*                                                                           *
*--------------------------- Implementation Notes --------------------------*
*                                                                           *
*   compiled with : compress.h compress.fns                                 *
*   linked with   : compress.o compapi.o                                    *
*                                                                           *
*   To use, copy or rename this file to compusi.c and recompile.            *
*   Set the defines in compress.h to reflect the status of your compiler's  *
*   runtime library, allocation type for malloc()'s, and memory models if   *
*   applicable, and your library function call for malloc() and free().     *
*                                                                           *
*   problems: header has some hardcoded defines you may need to change      *
*             for your compiler. Please read the header thoroughly.         *
*                                                                           *
*---------------------------      Author(s)        -------------------------*
*     Initials ---- Name ---------------------------------                  *
*      DjG          Donald J. Gloistein                                     *
*                   Plus many others, see rev.hst file for full list        *
*      LvR          Lyle V. Rains, thanks for the improved implementation   *
*                   of the compression and decompression routines.          *
*************************************************************************@H*/

#include <stdio.h>

#include "compress.h" /* contains the rest of the include file declarations */

/* For those who don't have it in libc.a */

#ifdef NO_STRRCHR
char  *strrchr(s,c)
char *s;
int c;
{
    register int count;

    while (*s){
        s++;
        count++;
    }
    s--;
    while (count--)
        if (*s == (char)c)
            return(s);
        else
            s--;
    return(NULL);
}
#endif

char *get_program_name(ptr)
char *ptr;
{
    char *cp;
    if ((cp = strrchr(ptr, '/')) != NULL)
        cp++;
    else
        cp = ptr;

    if(strcmp(cp,"uncompress") == 0) {
        do_decomp = 1;
    }
    else
    if(strcmp(cp, "zcat") == 0) {
        keep = TRUE;
        zcat_flg = do_decomp = 1;
    }
    return (cp);
}


char *name_index(ptr)
char *ptr;
{
    char *p;

    p = strrchr(ptr,'/');
    return ((p)? ++p: ptr);
}

int is_z_name(ptr)       /* checks if it is already a z name */
char *ptr;
{
    return (!(strcmp(ptr + strlen(ptr) -2,".Z")));
}

int make_z_name(ptr)
char *ptr;
{
#ifndef BSD4_2
    if (strlen(name_index(ptr)) > 12 ) {
        fprintf(stderr,"%s: filename too long to add .Z\n",name_index(ptr));
        return FALSE;
    }
#endif
    strcat(ptr,".Z");
    return TRUE;
}
void unmake_z_name(ptr)
char *ptr;
{
    register int len = strlen(ptr)-2;

    ptr[len] = '\0';
}

#ifndef NOSIGNAL
SIGTYPE onintr ( )
{
    if (!zcat_flg && !keep_error){
        fclose(stdout);
        unlink ( ofname );
    }
    exit ( ERROR );
}

SIGTYPE oops ( )    /* wild pointer -- assume bad input */
{
    if ( do_decomp == 1 )
        fprintf ( stderr, "%s: corrupt input: %s\n",prog_name,ifname);
    if (!zcat_flg && !keep_error){
        fclose(stdout);
        unlink ( ofname );
    }
    exit ( ERROR );
}
#endif

void copystat(ifname, ofname)
char *ifname, *ofname;
{
    struct stat statbuf;
    int mode;
    time_t timep[2];

    fclose(stdout);
    if (stat(ifname, &statbuf)) {       /* Get stat on input file */
        perror(ifname);
        return;
    }
    if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) {
        if(quiet)
            fprintf(stderr, "%s: ", ifname);
        fprintf(stderr, " -- not a regular file: unchanged");
        exit_stat = 1;
    }
    else if (statbuf.st_nlink > 1) {
        if(quiet)
            fprintf(stderr, "%s: ", ifname);
        fprintf(stderr, " -- has %d other links: unchanged",
            statbuf.st_nlink - 1);
        exit_stat = ERROR;
    }
    else if (exit_stat == NOSAVING && (!force)) { /* No compression: remove file.Z */
    if(!quiet)
        fprintf(stderr, " -- no savings -- file unchanged");
    }
    else if (exit_stat == NOMEM){
        if (!quiet)
            fprintf(stderr, " -- file unchanged");
        if (!do_decomp)
            exit(ERROR);
        else
            return;     /* otherwise will unlink outfile */
    }
    else if (exit_stat == OK) {  /* ***** Successful Compression ***** */
        mode = statbuf.st_mode & 07777;
        if (chmod(ofname, mode))        /* Copy modes */
                perror(ofname);
        chown(ofname,statbuf.st_uid,statbuf.st_gid); /* Copy Ownership */
        timep[0] = statbuf.st_atime;
        timep[1] = statbuf.st_mtime;
        utime(ofname,timep);   /* Update last accessed and modified times */
        if (!keep){
            fclose(stdin);
            if (unlink(ifname)) /* Remove input file */
                perror(ifname);
            if(!quiet)
                fprintf(stderr, " -- replaced with %s", ofname);
        }
        else{
            if(!quiet)
                fprintf(stderr, " -- compressed to %s", ofname);
        }
        return;     /* Successful return */
    }

    /* Unsuccessful return -- one of the tests failed */
    fclose(stdout);
    if (unlink(ofname))
        perror(ofname);
}
void version()
{
#ifdef XENIX
#ifndef NDEBUG
    fprintf(stderr, "%s\nOptions: Xenix %s MAXBITS = %d\n", rcs_ident,
        "DEBUG",MAXBITS);
#else
    fprintf(stderr, "%s\nOptions: Xenix MAXBITS = %d\n", rcs_ident,MAXBITS);
#endif
#else
#ifndef NDEBUG
    fprintf(stderr, "%s\nOptions: Unix %s MAXBITS = %d\n", rcs_ident,
        "DEBUG",MAXBITS);
#else
    fprintf(stderr, "%s\nOptions: Unix MAXBITS = %d\n", rcs_ident,MAXBITS);
#endif
#endif
}

ALLOCTYPE FAR *emalloc(x,y)
unsigned int x;
int y;
{
    ALLOCTYPE FAR *p;
    p = (ALLOCTYPE FAR *)ALLOCATE(x,y);
    return(p);
}

void efree(ptr)
ALLOCTYPE FAR *ptr;
{
    FREEIT(ptr);
}

SHAR_EOF
#	End of shell archive
exit 0

--
      Dale Schumacher                         399 Beacon Ave.
      (alias: Dalnefre')                      St. Paul, MN  55104-3527
      ...bungia!midgard.mn.org!syntel!dal     United States of America
             "I may be competitive, but I'm never ruthless"
 

dal@syntel.UUCP (Dale Schumacher) (05/17/89)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	compress.c
#	compapi.c
# This archive created: 17-May-1989 1:35:52 ENOENV
# By:	ENOENV (ENOENV)
cat << \SHAR_EOF > compress.c
/*@H************************ < COMPRESS utility> ****************************
*                                                                           *
*   compress : compress.c                                                   *
*              Main and Operating System Independent support functions      *
*                                                                           *
*   port by  : Donald J. Gloistein                                          *
*                                                                           *
*   Source, Documentation, Object Code:                                     *
*   released to Public Domain. This code is ported from compress v4.0       *
*   release joe.                                                            *
*---------------------------  Module Description  --------------------------*
*   The compress program is compatible with the compression/decompression   *
*   used on the Unix systems compress programs.  This is version 4 and      *
*   supports up to 16 bits compression. The porting retained the Unix       *
*   meanings of all options, added a couple for MsDos and modified the      *
*   file name conventions to make more sense.                               *
*                                                                           *
*--------------------------- Implementation Notes --------------------------*
*                                                                           *
*   compiled with : compress.h compress.fns                                 *
*   linked with   : compapi.obj  compusi.obj                                *
*   problems:                                                               *
*              See notes in compress.h for defines needed.                  *
*              It should work now with Xenix                                *
*                                                                           *
*              Check the signal() handler functions in your compiler        *
*              documentation. This code assumes ANSI SYS V compatible       *
*              header and return values. Change as appropriate for your     *
*              compiler and operating system.                               *
*                                                                           *
*              This source compiles properly with Microsoft C compiler      *
*              version 5.1.                                                 *
*                                                                           *
*   CAUTION:   because the program is in modules, make sure you recompile   *
*              all modules if you change the header or a define in the      *
*              compress.c file                                              *
*                                                                           *
* Algorithm from "A Technique for High Performance Data Compression",       *
* Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.          *
*                                                                           *
* 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.   *
*                                                                           *
*                                                                           *
*---------------------------      Author(s)        -------------------------*
*     Initials ---- Name ---------------------------------                  *
*      DjG          Donald J. Gloistein                                     *
*                   Plus many others, see rev.hst file for full list        *
*      LvR          Lyle V. Rains, many thanks for improved implementation  *
*************************************************************************@H*/

/*@R************************< Revision History >*****************************
*                                                                           *
*   version -- date -- init ---Notes----------------------                  *
*    4.01    08-29-88  DjG    first cut  for 16 bit MsDos version           *
*            09-04-88  DjG    fixed unlink on zcat if interupted.           *
*                             added msdos filename logic and functions      *
*    4.10    10-27-88  DjG  revised API with coding changes by LvR.         *
*    4.10a   10-30-88  DjG  cleaned up code and fixed bug in freeing ptr.   *
*    4.10b   11-01-88  DjG  cleaned up the logic for inpath/outpath         *
*                           Changed the logic to finding the file name      *
*                           Fixed the allocation bug in the api             *
*                           Added some more portability macros              *
*    4.10c   11-04-88  DjG  Changed maxcode from global to static in api.   *
*                           Supplied some library functions for those who   *
*                           don't have them, changed dos usi to use the     *
*                           strrpbrk(). Checked casts in api again. Compiles*
*                           without warnings at pick level 3.               *
*    4.10d   11-25-88  DjG  revised some memory allocation, put more in the *
*                           header file. Corrected some typos.              *
*                           Changed prog_name() to force lower case         *
*                           Corrected bug, no longer unlinks existing file  *
*                           if not enough memory to compress or decompress  *
*            12-06-88  DjG  VERY minor changes for casts and header defines *
*            12-08-88  DjG  Adjusted path separator check in main function  *
*                           Amiga uses split seg because of compiler        *
*            12-09-88  DjG  Debugging done, all defaults now Unix compress  *
*                           defaults, including unlinking input file and    *
*                           acting as a filter. Must use -h option to get   *
*                           help screen.                                    *
*    4.10e   12-11-88  DjG  Fixed more casts, prototypes and header file.   *
*    4.10f   12-12-88  DjG  Fixed unlinking open files on error. This fails *
*                           on shared or os/2 platforms.                    *
*            12-15-88  DjG  Fixed SIGTYPE for function passed to signal     *
*                           Fixed problems with Xenix 2.2.1                 *
*    4.2     12-19-88  DjG  Replaced adaptive reset as an option.           *
*    4.3     12-26-88  DjG  Fixed long file name bug, fixed bug with        *
*                           compressdir. -B option added, same as -b option *
*            05-06-89  Dal  Ported to Sozobon/Alcyon C for Atari ST.  Also, *
*                           created get_one() for console prompting.        *
*            05-08-89  Dal  Ported to Minix-ST                              *
*************************************************************************@R*/

#include <stdio.h>

#define MAIN        /* header has defining instances of globals */
#include "compress.h" /* contains the rest of the include file declarations */

#define ARGVAL() (*++(*argv) || (--argc && *++argv))
char suffix[] = SUFFIX ;          /* only used in this file */

void main( argc, argv )
register int argc; char **argv;
{
    char **filelist, **fileptr,*temp;
    struct stat statbuf;

#ifndef NOSIGNAL
    if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) {
        /* ANSI/SYS V compatible */
        /* the following test checks for error on setting signals */
        /* check your documentation on the value to test          */
        /* if your signal.h doesn't support the return, it is     */
        /* essentially a no-op test                               */
        if (bgnd_flag == SIG_ERR){   
            exit_stat = SIGNAL_ERROR;
            check_error();
        }
        if( (signal(SIGINT,onintr) == SIG_ERR)
        || (signal(SIGSEGV,oops) == SIG_ERR)) {/* check your compiler docs. */
            exit_stat = SIGNAL_ERROR;
            check_error();
        }
    }
#endif

    /* set up array for files to be converted */
#ifdef ALLOC
    filelist = fileptr = (char **)(alloc(argc * sizeof(char *)));
#else
    filelist = fileptr = (char **)(malloc(argc * sizeof(char *)));
#endif
    *filelist = NULL;

    /* gets name, compares and sets defaults */
    prog_name = get_program_name(argv[0]);

    /* now parse command line and get file list */
    for (argc--, argv++; argc > 0; argc--, argv++) {
        if (**argv == '-') {        /* A flag argument */
            while (*++(*argv)) {    /* Process all flags in this arg */
                switch (**argv) {
#if !defined(NDEBUG)
                    case 'D':
                        debug = TRUE;
                        keep_error = TRUE;
                        break;
                    case 'V':
                        verbose = TRUE;
                        version();
                        break;
#else
                    case 'V':
                        version();
                        break;
#endif /*!NDEBUG */
                    case 'v':
                        quiet = !quiet;
                        break;
                    case 'd':
                        do_decomp = TRUE;
                        break;
                    case 'f':
                        force = overwrite = TRUE;
                        break;
                    case 'n':
                        nomagic = TRUE;
                        break;
                    case 'C':
                        block_compress = FALSE;
                        break;
                    case 'b': case 'B':
                        if (!ARGVAL()) {
                            fprintf(stderr, "Missing maxbits\n");
                            Usage(1);
                            exit(ERROR);
                        }
                        maxbits = atoi(*argv);
                        goto nextarg;
                    case 'I':
                        if (!ARGVAL()) {
                            fprintf(stderr, "Missing in_path name\n");
                            Usage(1);
                            exit(ERROR);
                        }
                        strcpy(inpath,*argv);
                        temp = &inpath[strlen(inpath)-1];
#ifdef MSDOS
                        if (*temp != '\\' && *temp != '/')
#else
                        if (*temp != separator[0])
#endif
                            strcat(inpath,separator);
                        goto nextarg;
                    case 'O':
                        if (!ARGVAL()){
                            fprintf(stderr, "Missing out_path name\n");
                            Usage(1);
                            exit(ERROR);
                        }
                        strcpy(outpath,*argv);
                        temp = &outpath[strlen(outpath)-1];
#ifdef MSDOS
                        if (*temp != '\\' && *temp != '/')
#else
                        if (*temp != separator[0])
#endif
                            strcat(outpath,separator);
                        goto nextarg;
                    case 'c':
                        keep = zcat_flg = TRUE;
                        break;
                    case 'K':
                        keep_error = TRUE;
                        break;
                    case 'k':
                        keep = !keep;
                        break;
                    case '?':case 'h':case 'H':
                        Usage(0);
                        exit(NORMAL);
                        break;
                    case 'q':
                        quiet = TRUE;
                        break;
                    default:
                        fprintf(stderr, "%s : Unknown flag: '%c'\n",prog_name, **argv);
                        Usage(1);
                        exit(ERROR);
                } /* end switch */
            } /* end while processing this argument */
        }  /* end if option parameter */
        else {                                  /* must be input file name */
            *fileptr++ = *argv;                 /* Build input file list */
            *fileptr = NULL;
        } /* end else */
nextarg: 	continue;                          /* process nextarg */
    } /* end command line processing */

    /* adjust for possible errors or conflicts */
    if(maxbits < MINBITS || maxbits > MAXBITS){
        fprintf(stderr,"\n%s: illegal bit value, range = %d to %d\n",prog_name,MINBITS,MAXBITS);
        exit(NORMAL);
    }
    if (zcat_flg && *outpath)         /* can't have an out path and zcat */
        *outpath = '\0';

    /* to make the error messages make sense */
    strcpy(ifname,"stdin");
    strcpy(ofname,"stdout");

    if (*filelist) {         /* Check if there are files specified */
                             /* *fileptr must continue to specify  */
                             /* command line in/out file name      */
        is_list = TRUE;
        for (fileptr = filelist; *fileptr; fileptr++) {
            exit_stat = 0;
            endchar[0] = '\0';
            if (do_decomp) {                /* DECOMPRESSION          */
                if (*inpath){               /* adjust for inpath name */
                    strcpy(ifname,inpath);  /* and copy into ifname   */
                    strcat(ifname,name_index(*fileptr));
                }
                else
                    strcpy(ifname,*fileptr);
                if(!is_z_name(ifname))         /* Check for .Z suffix    */
                    if(!(make_z_name(ifname))) /* No .Z: tack one on     */
                        continue;
                                               /* Open input file        */
                if ((freopen(ifname, READ_FILE_TYPE, stdin)) == NULL) {
                    perror(ifname);
                    continue;
                }
                else
                    setvbuf(stdin,zbuf,_IOFBF,ZBUFSIZE);
                if (!nomagic) {             /* Check the magic number */
                    if ((getchar() != (magic_header[0] & 0xFF))
                      || (getchar() != (magic_header[1] & 0xFF))) {
                        fprintf(stderr, "%s: not in compressed format\n",
                            *ifname);
                        continue;
                    }
                    maxbits = getchar();    /* set -b from file */
                    block_compress = maxbits & BLOCK_MASK;
                    maxbits &= BIT_MASK;
                    if(maxbits > MAXBITS) {
                        fprintf(stderr,
                        "%s: compressed with %d bits, can only handle %d bits\n",
                        ifname, maxbits, MAXBITS);
                        continue;
                    }
                }  /* end if nomagic */
                                             /* Generate output filename */
                if (*outpath){               /* adjust for outpath name */
                    strcpy(ofname,outpath);  /* and copy into ofname   */
                    strcat(ofname,name_index(ifname));
                }
                else
                    strcpy(ofname,ifname); /* DjG may screw up the placement */
                                           /* of the outfile */
                unmake_z_name(ofname);     /* strip off Z or .Z */
            }
            else {            /* COMPRESSION */
                if (*inpath){               /* adjust for inpath name */
                    strcpy(ifname,inpath);  /* and copy into ifname   */
                    strcat(ifname,name_index(*fileptr));
                }
                else
                    strcpy(ifname,*fileptr);
                if (is_z_name(ifname)) {
                    fprintf(stderr, "%s: already has %s suffix -- no change\n",
                        ifname,suffix);
                    continue;
                }
                /* Open input file */
                if ((freopen(ifname,READ_FILE_TYPE, stdin)) == NULL) {
                    perror(ifname);
                    continue;
                }
                else
                    setvbuf(stdin,xbuf,_IOFBF,XBUFSIZE);
                /* Generate output filename */
                if (*outpath){               /* adjust for outpath name */
                    strcpy(ofname,outpath);  /* and copy into ofname   */
                    strcat(ofname,name_index(ifname));
                }
                else  /* place it in directory of input file */
                    strcpy(ofname,ifname); /* DjG may screw up the placement */
                                           /* of the outfile */
                if (!(make_z_name(ofname)))
                    continue;
            } /* end else compression  we now have the files set up */

            /* Check for overwrite of existing file */
            if (!overwrite && !zcat_flg) {
                if (!stat(ofname, &statbuf)) {
                    char response, get_one();

                    response = 'n';
                    fprintf(stderr, "%s already exists;", ofname);
#ifndef NOSIGNAL
                    if (foreground()) {
#else
                    if (TRUE) {
#endif
                        fprintf(stderr, "\ndo you wish to overwrite %s (y or n)? ",
                        ofname);
                        fflush(stderr);
			response = get_one();
                    }
                    if ((response != 'y') && (response != 'Y')) {
                        fprintf(stderr, "\tnot overwritten\n");
                        continue;
                    }
                } /* end if stat */
            } /* end if overwrite */
            /* Output file  is opened in compress/decompress routines */

            /* Actually do the compression/decompression  on files */
            if (!do_decomp){
                compress();
                check_error();
            }
            else{
                decompress();
                check_error();
            }
            if(!zcat_flg) {
                copystat(ifname, ofname); /* Copy stats */
                if((exit_stat ) || (!quiet))
                    putc('\n', stderr);
            }       /* end if zcat */
        }           /*end for  loop */
    }               /* end if filelist */
    else {          /* it is standard input to standard output*/
#if (FILTER == FALSE)     /* filter is defined as true or false */
    /* DjG added to make more sense.  The following tests for standard
       input being a character device. If so, there is no use in MsDos
       for the program, as that will compress from the keyboard to the
       console. Sure not what is needed. Instead, the usage function
       is called. In Xenix/Unix systems, there is a need for this type
       of pipe as the input may be from a char dev, remote station.
     */

        /* check if input is unredirected */
        if ( isatty(fileno(stdin)) ){
            Usage(1);
            exit(NORMAL);
        }
#endif
        /* filter */
        if (do_decomp){
            setvbuf(stdin,zbuf,_IOFBF,ZBUFSIZE);  /* make the buffers larger */
            setvbuf(stdout,xbuf,_IOFBF,XBUFSIZE);
        }
        else{
            setvbuf(stdin,xbuf,_IOFBF,XBUFSIZE);  /* make the buffers larger */
            setvbuf(stdout,zbuf,_IOFBF,ZBUFSIZE);
        }
        if (!do_decomp) {   /* compress stdin to stdout */
            compress();
            check_error();
            if(!quiet)
                putc('\n', stderr);
        } /* end compress stdio */
        else {   /* decompress stdin to stdout */
            /* Check the magic number */
            if (!nomagic) {
                if ((getchar()!=(magic_header[0] & 0xFF))
                 || (getchar()!=(magic_header[1] & 0xFF))) {
                    fprintf(stderr, "stdin: not in compressed format\n");
                    exit(ERROR);
                }
                maxbits = getchar();    /* set -b from file */
                block_compress = maxbits & BLOCK_MASK;
                maxbits &= BIT_MASK;
                if(maxbits > MAXBITS) {
                    fprintf(stderr,
                    "stdin: compressed with %d bits, can only handle %d bits\n",
                    maxbits, MAXBITS);
                    exit(ERROR);
                }
            }
            decompress();
            check_error();
        } /* end else decomp stdio */
    } /* end else standard input */
    exit(exit_stat);
}

void Usage(flag)
int flag;
{
static char *keep2 =  "keep";
static char *keep3 =  "kill (erase)";
static char *on = "on";
static char *off = "off";

#ifndef NDEBUG
    fprintf(stderr,"Usage: %s [-cCdDf?hkKvV][-b maxbits][-Iinpath][-Ooutpath][filenames...]\n",
        prog_name);
#else
    fprintf(stderr,"Usage: %s [-cCdf?hkKvV][-b maxbits][-Iinpath][-Ooutpath][filenames...]\n",
        prog_name);
#endif
    if (flag)
        return;
    fprintf(stderr,"Argument Processing..case is significant:\n");
    fprintf(stderr,"     MUST use '-' for switch character\nAll flags are optional.\n");
#ifndef NDEBUG
    fprintf(stderr,"     -D => debug; Keep file on error.\n");
    fprintf(stderr,"     -V => print Version; debug verbose\n");
#else
    fprintf(stderr,"     -V => print Version\n");
#endif
    fprintf(stderr,"     -d => do_decomp default = %s\n",(do_decomp)?on:off);
    fprintf(stderr,"     -v => verbose default = %s\n", (quiet)?off:on);
    fprintf(stderr,"     -f => force overwrite of output file default = %s\n",
        (force)?on:off);
    fprintf(stderr,"     -n => no header: useful to uncompress old files\n");
    fprintf(stderr,"     -c => cat all output to stdout default = %s\n",
        (zcat_flg)?on:off);
    fprintf(stderr,"     -C => generate output compatible with compress 2.0.\n");
    fprintf(stderr,"     -k => %s input file, default = %s\n",(keep)?keep3:keep2,
            (keep)?keep2:keep3);
    fprintf(stderr,"     -K => %s output file on error, default = %s\n",
            (keep_error)?keep3:keep2,(keep_error)?keep2:keep3);
    fprintf(stderr,"     -b maxbits  => default = %d bits, max = %d bits\n",maxbits,MAXBITS);
    fprintf(stderr,"     -I pathname => infile path  = %s\n",inpath);
    fprintf(stderr,"     -O pathname => outfile path = %s\n",outpath);
    fprintf(stderr,"     -? -h => help usage.\n");
}

char get_one()
/*
 * get a single character, with echo.
 */
{
        char tmp[2];
	int fd;

#ifdef SOZOBON
	return(0x7F & getche());
#endif
#ifdef MSC
	return(getche());
#endif
/*
 * All previous #ifdef'ed code should return() a value.
 * If no other option is available, the following is the original code.
 * It not only reads from stderr (not a defined operation)
 * but it does so via an explicit read() call on file descriptor 2!
 * So much for portability.                    -Dal
 */
#if MINIX
	fd = open("/dev/tty", 0);	/* open the tty directly */
#else
	fd = 2;				/* read from stderr */
#endif
        read(fd, tmp, 2);
        while (tmp[1] != '\n') {
                if (read(fd, tmp+1, 1) < 0) {   /* Ack! */
                        perror("stderr");
                        break;
                }
        }
	return(tmp[0]);
}

void writeerr()
{
    perror ( ofname );
    if (!zcat_flg && !keep_error){
        fclose(stdout);
        unlink ( ofname );
    }
    exit ( 1 );
}

#ifndef NOSIGNAL
/*
 * This routine returns 1 if we are running in the foreground and stderr
 * is a tty.
 */
int foreground()
{
    if(bgnd_flag) { /* background? */
        return(0);
    }
    else {            /* foreground */
        if(isatty(2)) {     /* and stderr is a tty */
            return(1);
        } else {
            return(0);
        }
    }
}
#endif

void 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 = (int) (num / (den / 10000L));
    }
    else {
        q = (int) (10000L * num / den);     /* Long calculations, though */
    }
    if (q < 0) {
        putc('-', stream);
        q = -q;
    }
    fprintf(stream, "%d.%02d%%", q / 100, q % 100);
}


int check_error()     /* returning OK continues with processing next file */
{
    switch(exit_stat) {
  case OK:
    return (OK);
  case NOMEM:
    if (do_decomp)
        fprintf(stderr,"%s: not enough memory to decompress '%s'.\n", prog_name, ifname);
    else
        fprintf(stderr,"%s: not enough memory to compress '%s'.\n", prog_name, ifname);
    return(OK);
  case SIGNAL_ERROR:
    fprintf(stderr,"%s: error setting signal interupt.\n",prog_name);
    exit(ERROR);
    break;
  case READERR:
    fprintf(stderr,"%s: read error on input '%s'.\n", prog_name, ifname);
    break;
  case WRITEERR:
    fprintf(stderr,"%s: write error on output '%s'.\n", prog_name, ofname);
    break;
   case TOKTOOBIG:
    fprintf(stderr,"%s: token too long in '%s'.\n", prog_name, ifname);
    break;
  case INFILEBAD:
    fprintf(stderr, "%s: '%s' in unknown compressed format.\n", prog_name, ifname);
    break;
 case CODEBAD:
    fprintf(stderr,"%s: file token bad in '%s'.\n", prog_name,ifname);
    break;
 case TABLEBAD:
    fprintf(stderr,"%s: internal error -- tables corrupted.\n", prog_name);
    break;
  case NOTOPENED:
    fprintf(stderr,"%s: could not open output file %s\n",prog_name,ofname);
    exit(ERROR);
    break;
  case NOSAVING:
    if (force)
        exit_stat = OK;
    return (OK);
  default:
    fprintf(stderr,"%s: internal error -- illegal return value = %d.\n", prog_name,exit_stat);
  }
  if (!zcat_flg && !keep_error){
        fclose(stdout);         /* won't get here without an error */
        unlink ( ofname );
    }
  exit(exit_stat);
  return(ERROR);
}
SHAR_EOF
cat << \SHAR_EOF > compapi.c
/*@H************************ < COMPRESS API    > ****************************
*                                                                           *
*   compress : compapi.c  <current version of compress algorithm>           *
*                                                                           *
*   port by  : Donald J. Gloistein                                          *
*                                                                           *
*   Source, Documentation, Object Code:                                     *
*   released to Public Domain.  This code is based on code as documented    *
*   below in release notes.                                                 *
*                                                                           *
*---------------------------  Module Description  --------------------------*
*   Contains source code for modified Lempel-Ziv method (LZW) compression   *
*   and decompression.                                                      *
*                                                                           *
*   This code module can be maintained to keep current on releases on the   *
*   Unix system. The command shell and dos modules can remain the same.     *
*                                                                           *
*--------------------------- Implementation Notes --------------------------*
*                                                                           *
*   compiled with : compress.h compress.fns compress.c                      *
*   linked with   : compress.obj compusi.obj                                *
*                                                                           *
*   problems:                                                               *
*                                                                           *
*                                                                           *
*   CAUTION: Uses a number of defines for access and speed. If you change   *
*            anything, make sure about side effects.                        *
*                                                                           *
* Compression:                                                              *
* 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 block compression with an adaptive reset was used in original code,  *
* whereby the code table is cleared when the compression ration decreases   *
* but after the table fills.  This was removed from this edition. The table *
* is re-sized at this point when it is filled , and a special CLEAR code is *
* generated for the decompressor. This results in some size difference from *
* straight version 4.0 joe Release. But it is fully compatible in both v4.0 *
* and v4.01                                                                 *
*                                                                           *
* Decompression:                                                            *
* 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.       *
*                                                                           *
*     Initials ---- Name ---------------------------------                  *
*      DjG          Donald J. Gloistein, current port to MsDos 16 bit       *
*                   Plus many others, see rev.hst file for full list        *
*      LvR          Lyle V. Rains, many thanks for improved implementation  *
*                   of the compression and decompression routines.          *
*************************************************************************@H*/

#include <stdio.h>

#include "compress.h" /* contains the rest of the include file declarations */

static int offset;
static long int in_count ;         /* length of input */
static long int bytes_out;         /* length of compressed output */

static CODE prefxcode, nextfree;
static CODE highcode;
static CODE maxcode;
static HASH hashsize;
static int  bits;


/*
 * The following two parameter tables are the hash table sizes and
 * maximum code values for various code bit-lengths.  The requirements
 * are that Hashsize[n] must be a prime number and Maxcode[n] must be less
 * than Maxhash[n].  Table occupancy factor is (Maxcode - 256)/Maxhash.
 * Note:  I am using a lower Maxcode for 16-bit codes in order to
 * keep the hash table size less than 64k entries.
 */
CONST HASH hs[] = {
  0x13FF,       /* 12-bit codes, 75% occupancy */
  0x26C3,       /* 13-bit codes, 80% occupancy */
  0x4A1D,       /* 14-bit codes, 85% occupancy */
  0x8D0D,       /* 15-bit codes, 90% occupancy */
  0xFFD9        /* 16-bit codes, 94% occupancy, 6% of code values unused */
};
#define Hashsize(maxb) (hs[(maxb) -MINBITS])

CONST CODE mc[] = {
  0x0FFF,       /* 12-bit codes */
  0x1FFF,       /* 13-bit codes */
  0x3FFF,       /* 14-bit codes */
  0x7FFF,       /* 15-bit codes */
  0xEFFF        /* 16-bit codes, 6% of code values unused */
};
#define Maxcode(maxb) (mc[(maxb) -MINBITS])

#ifdef __STDC__
#ifndef NDEBUG
#define allocx(type, ptr, size) \
    (((ptr) = (type FAR *) emalloc((unsigned int)(size),sizeof(type))) == NULLPTR(type) \
    ?   (fprintf(stderr,"%s: "#ptr" -- ", prog_name), NOMEM) : OK \
    )
#else
#define allocx(type,ptr,size) \
    (((ptr) = (type FAR *) emalloc((unsigned int)(size),sizeof(type))) == NULLPTR(type) \
    ? NOMEM : OK \
    )
#endif
#else
#define allocx(type,ptr,size) \
    (((ptr) = (type FAR *) emalloc((unsigned int)(size),sizeof(type))) == NULLPTR(type) \
    ? NOMEM : OK \
    )
#endif

#define free_array(type,ptr,offset) \
    if (ptr != NULLPTR(type)) { \
        efree((ALLOCTYPE FAR *)((ptr) + (offset))); \
        (ptr) = NULLPTR(type); \
    }

  /*
   * Macro to allocate new memory to a pointer with an offset value.
   */
#define alloc_array(type, ptr, size, offset) \
    ( allocx(type, ptr, (size) - (offset)) != OK \
      ? NOMEM \
      : (((ptr) -= (offset)), OK) \
    )

static char FAR *sfx = NULLPTR(char) ;
#define suffix(code)     sfx[code]


#if (SPLIT_PFX)
  static CODE FAR *pfx[2] = {NULLPTR(CODE), NULLPTR(CODE)};
#else
  static CODE FAR *pfx = NULLPTR(CODE);
#endif


#if (SPLIT_HT)
  static CODE FAR *ht[2] = {NULLPTR(CODE),NULLPTR(CODE)};
#else
  static CODE FAR *ht = NULLPTR(CODE);
#endif


int alloc_tables(maxcode, hashsize)
  CODE maxcode;
  HASH hashsize;
{
  static CODE oldmaxcode = 0;
  static HASH oldhashsize = 0;

  if (hashsize > oldhashsize) {
#if (SPLIT_HT)
      free_array(CODE,ht[1], 0);
      free_array(CODE,ht[0], 0);
#else
      free_array(CODE,ht, 0);
#endif
    oldhashsize = 0;
  }

    if (maxcode > oldmaxcode) {
#if (SPLIT_PFX)
        free_array(CODE,pfx[1], 128);
        free_array(CODE,pfx[0], 128);
#else
        free_array(CODE,pfx, 256);
#endif
        free_array(char,sfx, 256);

        if (   alloc_array(char, sfx, maxcode + 1, 256)
#if (SPLIT_PFX)
            || alloc_array(CODE, pfx[0], (maxcode + 1) / 2, 128)
            || alloc_array(CODE, pfx[1], (maxcode + 1) / 2, 128)
#else
            || alloc_array(CODE, pfx, (maxcode + 1), 256)
#endif
        ) {
            oldmaxcode = 0;
            exit_stat = NOMEM;
            return(NOMEM);
        }
        oldmaxcode = maxcode;
    }
    if (hashsize > oldhashsize) {
        if (
#if (SPLIT_HT)
            alloc_array(CODE, ht[0], (hashsize / 2) + 1, 0)
            || alloc_array(CODE, ht[1], hashsize / 2, 0)
#else
            alloc_array(CODE, ht, hashsize, 0)
#endif
        ) {
            oldhashsize = 0;
            exit_stat = NOMEM;
            return(NOMEM);
        }
        oldhashsize = hashsize;
    }
    return (OK);
}

# if (SPLIT_PFX)
    /*
     * We have to split pfx[] table in half,
     * because it's potentially larger than 64k bytes.
     */
#   define prefix(code)   (pfx[(code) & 1][(code) >> 1])
# else
    /*
     * Then pfx[] can't be larger than 64k bytes,
     * or we don't care if it is, so we don't split.
     */
#   define prefix(code) (pfx[code])
# endif


/* The initializing of the tables can be done quicker with memset() */
/* but this way is portable through out the memory models.          */
/* If you use Microsoft halloc() to allocate the arrays, then       */
/* include the pragma #pragma function(memset)  and make sure that  */
/* the length of the memory block is not greater than 64K.          */
/* This also means that you MUST compile in a model that makes the  */
/* default pointers to be far pointers (compact or large models).   */
/* See the file COMPUSI.DOS to modify function emalloc().           */

# if (SPLIT_HT)
    /*
     * We have to split ht[] hash table in half,
     * because it's potentially larger than 64k bytes.
     */
#   define probe(hash)    (ht[(hash) & 1][(hash) >> 1])
#   define init_tables() \
    { \
      hash = hashsize >> 1; \
      ht[0][hash] = 0; \
      while (hash--) ht[0][hash] = ht[1][hash] = 0; \
      highcode = ~(~(CODE)0 << (bits = INITBITS)); \
      nextfree = (block_compress ? FIRSTFREE : 256); \
    }

# else

    /*
     * Then ht[] can't be larger than 64k bytes,
     * or we don't care if it is, so we don't split.
     */
#   define probe(hash) (ht[hash])
#   define init_tables() \
    { \
      hash = hashsize; \
      while (hash--) ht[hash] = 0; \
      highcode = ~(~(CODE)0 << (bits = INITBITS)); \
      nextfree = (block_compress ? FIRSTFREE : 256); \
    }

# endif

#ifdef COMP40
/* table clear for block compress */
/* this is for adaptive reset present in version 4.0 joe release */
/* DjG, sets it up and returns TRUE to compress and FALSE to not compress */
int cl_block ()     
{
    register long int rat;

    checkpoint = in_count + CHECK_GAP;
#ifndef NDEBUG
	if ( debug ) {
        fprintf ( stderr, "count: %ld, ratio: ", in_count );
        prratio ( stderr, in_count, bytes_out );
		fprintf ( stderr, "\n");
	}
#endif

    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;
        return FALSE;
    }
    else {
        ratio = 0;
#ifndef NDEBUG
        if(debug)
    		fprintf ( stderr, "clear\n" );
#endif
        return TRUE;    /* clear the table */
    }
    return FALSE; /* don't clear the table */
}
#endif

/*
 * compress stdin to stdout
 *
 */
void compress()
{
    int c,adjbits;
    register HASH hash;
    register CODE code;
    HASH hashf[256];

    maxcode = Maxcode(maxbits);
    hashsize = Hashsize(maxbits);

#ifdef COMP40
/* Only needed for adaptive reset */
    checkpoint = CHECK_GAP;
    ratio = 0;
#endif

    adjbits = maxbits -10;
    for (c = 256; --c >= 0; ){
        hashf[c] = ((( c &0x7) << 7) ^ c) << adjbits;
    }
    exit_stat = OK;
    if (alloc_tables(maxcode, hashsize))  /* exit_stat already set */
        return;
    init_tables();
    /* if not zcat or filter */
    if(is_list && !zcat_flg) {  /* Open output file */
        if (freopen(ofname, WRITE_FILE_TYPE, stdout) == NULL) {
            exit_stat = NOTOPENED;
            return;
        }
        if (!quiet)
            fprintf(stderr, "%s: ",ifname);
        setvbuf(stdout,zbuf,_IOFBF,ZBUFSIZE);
    }
 /*
   * Check the input stream for previously seen strings.  We keep
   * adding characters to the previously seen prefix string until we
   * get a character which forms a new (unseen) string.  We then send
   * the code for the previously seen prefix string, and add the new
   * string to our tables.  The check for previous strings is done by
   * hashing.  If the code for the hash value is unused, then we have
   * a new string.  If the code is used, we check to see if the prefix
   * and suffix values match the current input; if so, we have found
   * a previously seen string.  Otherwise, we have a hash collision,
   * and we try secondary hash probes until we either find the current
   * string, or we find an unused entry (which indicates a new string).
   */
    if (!nomagic) {
        putchar(magic_header[0]); putchar(magic_header[1]);
        putchar((char)(maxbits | block_compress));
        if(ferror(stdout)){  /* check it on entry */
            exit_stat = WRITEERR;
            return;
        }
        bytes_out = 3L;     /* includes 3-byte header mojo */
    }
    else
        bytes_out = 0L;      /* no 3-byte header mojo */
    in_count = 1L;
    offset = 0;

    if ((c = getchar()) == EOF) {
        exit_stat = ferror(stdin) ? READERR : OK;
        return;
    }
    prefxcode = (CODE)c;

    while ((c = getchar()) != EOF) {
        in_count++;
        hash = prefxcode ^ hashf[c];
        /* I need to check that my hash value is within range
        * because my 16-bit hash table is smaller than 64k.
        */
        if (hash >= hashsize)
            hash -= hashsize;
        if ((code = probe(hash)) != UNUSED) {
            if (suffix(code) != (char)c || prefix(code) != prefxcode) {
            /* hashdelta is subtracted from hash on each iteration of
            * the following hash table search loop.  I compute it once
            * here to remove it from the loop.
            */
                HASH hashdelta = (0x120 - c) << (adjbits);
                do  {
                    /* rehash and keep looking */
                    assert(code >= FIRSTFREE && code <= maxcode);
                    if (hash >= hashdelta) hash -= hashdelta;
                        else hash += (hashsize - hashdelta);
                    assert(hash < hashsize);
                    if ((code = probe(hash)) == UNUSED)
                        goto newcode;
                } while (suffix(code) != (char)c || prefix(code) != prefxcode);
            }
            prefxcode = code;
        }
        else {
            newcode: {
                putcode(prefxcode, bits);
                code = nextfree;
                assert(hash < hashsize);
                assert(code >= FIRSTFREE);
                assert(code <= maxcode + 1);
                if (code <= maxcode) {
                    probe(hash) = code;
                    prefix(code) = prefxcode;
                    suffix(code) = (char)c;
                    if (code > highcode) {
                        highcode += code;
                        ++bits;
                    }
                    nextfree = code + 1;
                }
#ifdef COMP40
                else if (in_count >= checkpoint && block_compress ) {
                    if (cl_block()){
#else
                else if (block_compress){
#endif
                        putcode((CODE)c, bits);
                        putcode((CODE)CLEAR,bits);
                        init_tables();
                        if ((c = getchar()) == EOF)
                            break;
                        in_count++;
#ifdef COMP40
                    }
#endif
                }
                prefxcode = (CODE)c;
            }
        }
    }
    putcode(prefxcode, bits);
    putcode((CODE)CLEAR, 0);
    if (ferror(stdout)){ /* check it on exit */
        exit_stat = WRITEERR;
        return;
    }
    /*
     * Print out stats on stderr
     */
    if(zcat_flg == 0 && !quiet) {
#ifndef NDEBUG
        fprintf( stderr,
            "%ld chars in, (%ld bytes) out, compression factor: ",
            in_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",
            prefxcode - 1, bits );
#else
        fprintf( stderr, "Compression: " );
        prratio( stderr, in_count-bytes_out, in_count );
#endif /* NDEBUG */
    }
    if(bytes_out > in_count)      /*  if no savings */
        exit_stat = NOSAVING;
    return ;
}

CONST UCHAR rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};

void putcode(code,bits)
CODE code;
register int bits;
{
  static int oldbits = 0;
  static UCHAR outbuf[MAXBITS];
  register UCHAR *buf;
  register int shift;

  if (bits != oldbits) {
    if (bits == 0) {
      /* bits == 0 means EOF, write the rest of the buffer. */
      if (offset > 0) {
          fwrite(outbuf,1,(offset +7) >> 3, stdout);
          bytes_out += ((offset +7) >> 3);
      }
      offset = 0;
      oldbits = 0;
      fflush(stdout);
      return;
    }
    else {
      /* Change the code size.  We must write the whole buffer,
       * because the expand side won't discover the size change
       * until after it has read a buffer full.
       */
      if (offset > 0) {
        fwrite(outbuf, 1, oldbits, stdout);
        bytes_out += oldbits;
        offset = 0;
      }
      oldbits = bits;
#ifndef NDEBUG
      if ( debug )
        fprintf( stderr, "\nChange to %d bits\n", bits );
#endif /* !NDEBUG */
    }
  }
  /*  Get to the first byte. */
  buf = outbuf + ((shift = offset) >> 3);
  if ((shift &= 7) != 0) {
    *(buf) |= (*buf & rmask[shift]) | (UCHAR)(code << shift);
    *(++buf) = (UCHAR)(code >> (8 - shift));
    if (bits + shift > 16)
        *(++buf) = (UCHAR)(code >> (16 - shift));
  }
  else {
    /* Special case for fast execution */
    *(buf) = (UCHAR)code;
    *(++buf) = (UCHAR)(code >> 8);
  }
  if ((offset += bits) == (bits << 3)) {
    bytes_out += bits;
    fwrite(outbuf,1,bits,stdout);
    offset = 0;
  }
  return;
}

int nextcode(codeptr)
CODE *codeptr;
/* Get the next code from input and put it in *codeptr.
 * Return (TRUE) on success, or return (FALSE) on end-of-file.
 * Adapted from COMPRESS V4.0.
 */
{
  static int prevbits = 0;
  register CODE code;
  static int size;
  static UCHAR inbuf[MAXBITS];
  register int shift;
  UCHAR *bp;

  /* If the next entry is a different bit-size than the preceeding one
   * then we must adjust the size and scrap the old buffer.
   */
  if (prevbits != bits) {
    prevbits = bits;
    size = 0;
  }
  /* If we can't read another code from the buffer, then refill it.
   */
  if (size - (shift = offset) < bits) {
    /* Read more input and convert size from # of bytes to # of bits */
    if ((size = fread(inbuf, 1, bits, stdin) << 3) <= 0 || ferror(stdin))
      return(NO);
    offset = shift = 0;
  }
  /* Get to the first byte. */
  bp = inbuf + (shift >> 3);
  /* Get first part (low order bits) */
  code = (*bp++ >> (shift &= 7));
  /* high order bits. */
  code |= *bp++ << (shift = 8 - shift);
  if ((shift += 8) < bits) code |= *bp << shift;
  *codeptr = code & highcode;
  offset += bits;
  return (TRUE);
}

void decompress()
{
  register int i;
  register CODE code;
  char sufxchar;
  CODE savecode;
  FLAG fulltable, cleartable;
  static char token[MAXTOKLEN];         /* String buffer to build token */

  exit_stat = OK;

  if (alloc_tables(maxcode = ~(~(CODE)0 << maxbits),0)) /* exit_stat already set */
     return;

    /* if not zcat or filter */
    if(is_list && !zcat_flg) {  /* Open output file */
        if (freopen(ofname, WRITE_FILE_TYPE, stdout) == NULL) {
            exit_stat = NOTOPENED;
            return;
        }
        if (!quiet)
            fprintf(stderr, "%s: ",ifname);
        setvbuf(stdout,xbuf,_IOFBF,XBUFSIZE);
    }
  cleartable = TRUE;
  savecode = CLEAR;
  offset = 0;
  do {
    if ((code = savecode) == CLEAR && cleartable) {
      highcode = ~(~(CODE)0 << (bits = INITBITS));
      fulltable = FALSE;
      nextfree = (cleartable = block_compress) == FALSE ? 256 : FIRSTFREE;
      if (!nextcode(&prefxcode))
        break;
      putc((sufxchar = (char)prefxcode), stdout);
      continue;
    }
    i = 0;
    if (code >= nextfree && !fulltable) {
      if (code != nextfree){
        exit_stat = CODEBAD;
        return ;     /* Non-existant code */
    }
      /* Special case for sequence KwKwK (see text of article)         */
      code = prefxcode;
      token[i++] = sufxchar;
    }
    /* Build the token string in reverse order by chasing down through
     * successive prefix tokens of the current token.  Then output it.
     */
    while (code >= 256) {
#     if !defined(NDEBUG)
        /* These are checks to ease paranoia. Prefix codes must decrease
         * monotonically, otherwise we must have corrupt tables.  We can
         * also check that we haven't overrun the token buffer.
         */
        if (code <= prefix(code)){
            exit_stat= TABLEBAD;
            return;
        }
        if (i >= MAXTOKLEN){
            exit_stat = TOKTOOBIG;
            return;
        }
#     endif
      token[i++] = suffix(code);
      code = prefix(code);
    }
    putc(sufxchar = (char)code, stdout);
    while (--i >= 0)
        putc(token[i], stdout);
    if (ferror(stdout)) {
        exit_stat = WRITEERR;
        return;
    }
    /* If table isn't full, add new token code to the table with
     * codeprefix and codesuffix, and remember current code.
     */
    if (!fulltable) {
      code = nextfree;
      assert(256 <= code && code <= maxcode);
      prefix(code) = prefxcode;
      suffix(code) = sufxchar;
      prefxcode = savecode;
      if (code++ == highcode) {
        if (highcode >= maxcode) {
          fulltable = TRUE;
          --code;
        }
        else {
          ++bits;
          highcode += code;           /* nextfree == highcode + 1 */
        }
      }
      nextfree = code;
    }
  } while (nextcode(&savecode));
  exit_stat = (ferror(stdin))? READERR : OK;
  return ;
}

SHAR_EOF
#	End of shell archive
exit 0

--
      Dale Schumacher                         399 Beacon Ave.
      (alias: Dalnefre')                      St. Paul, MN  55104-3527
      ...bungia!midgard.mn.org!syntel!dal     United States of America
             "I may be competitive, but I'm never ruthless"