🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sources.reviewed
Article #22

v02i026: ncompress - (Ver. 4.2.3) an improved file compressor, Part02/02

#22
From: Peter Jannesen
Date: Fri, 28 Aug 1992 22:37
1963 lines
54521 bytes
Submitted-by: Peter Jannesen <peter@ncs.nl>
Posting-number: Volume 2, Issue 26
Archive-name: ncompress/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  compress42.c
# Wrapped by csr@calvin.dgbt.doc.ca on Wed Aug  5 18:27:12 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'compress42.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'compress42.c'\"
else
echo shar: Extracting \"'compress42.c'\" \(48463 characters\)
sed "s/^X//" >'compress42.c' <<'END_OF_FILE'
X/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
X *
X * Authors:
X *   Spencer W. Thomas   (decvax!harpo!utah-cs!utah-gr!thomas)
X *   Jim McKie           (decvax!mcvax!jim)
X *   Steve Davies        (decvax!vax135!petsd!peora!srd)
X *   Ken Turkowski       (decvax!decwrl!turtlevax!ken)
X *   James A. Woods      (decvax!ihnp4!ames!jaw)
X *   Joe Orost           (decvax!vax135!petsd!joe)
X *   Dave Mack           (csu@alembic.acs.com)
X *   Peter Jannesen, Network Communication Systems
X *                       (peter@ncs.nl)
X *
X * Revision 4.2.3  92/03/14 peter@ncs.nl
X *   Optimise compress and decompress function and a lot of cleanups.
X *   New fast hash algoritme added (if more than 800Kb available).
X *
X * Revision 4.1  91/05/26 csu@alembic.acs.com
X *   Modified to recursively compress directories ('r' flag). As a side
X *   effect, compress will no longer attempt to compress things that
X *   aren't "regular" files. See Changes.
X *
X * Revision 4.0  85/07/30  12:50:00  joe
X *   Removed ferror() calls in output routine on every output except first.
X *   Prepared for release to the world.
X * 
X * Revision 3.6  85/07/04  01:22:21  joe
X *   Remove much wasted storage by overlaying hash table with the tables
X *   used by decompress: tab_suffix[1<<BITS], stack[8000].  Updated USERMEM
X *   computations.  Fixed dump_tab() DEBUG routine.
X *
X * Revision 3.5  85/06/30  20:47:21  jaw
X *   Change hash function to use exclusive-or.  Rip out hash cache.  These
X *   speedups render the megamemory version defunct, for now.  Make decoder
X *   stack global.  Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
X *
X * Revision 3.4  85/06/27  12:00:00  ken
X *   Get rid of all floating-point calculations by doing all compression ratio
X *   calculations in fixed point.
X *
X * Revision 3.3  85/06/24  21:53:24  joe
X *   Incorporate portability suggestion for M_XENIX.  Got rid of text on #else
X *   and #endif lines.  Cleaned up #ifdefs for vax and interdata.
X *
X * Revision 3.2  85/06/06  21:53:24  jaw
X *   Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
X *   Default to "quiet" output (no compression statistics).
X *
X * Revision 3.1  85/05/12  18:56:13  jaw
X *   Integrate decompress() stack speedups (from early pointer mods by McKie).
X *   Repair multi-file USERMEM gaffe.  Unify 'force' flags to mimic semantics
X *   of SVR2 'pack'.  Streamline block-compress table clear logic.  Increase 
X *   output byte count by magic number size.
X * 
X * Revision 3.0   84/11/27  11:50:00  petsd!joe
X *   Set HSIZE depending on BITS.  Set BITS depending on USERMEM.  Unrolled
X *   loops in clear routines.  Added "-C" flag for 2.0 compatibility.  Used
X *   unsigned compares on Perkin-Elmer.  Fixed foreground check.
X *
X * Revision 2.7   84/11/16  19:35:39  ames!jaw
X *   Cache common hash codes based on input statistics; this improves
X *   performance for low-density raster images.  Pass on #ifdef bundle
X *   from Turkowski.
X *
X * Revision 2.6   84/11/05  19:18:21  ames!jaw
X *   Vary size of hash tables to reduce time for small files.
X *   Tune PDP-11 hash function.
X *
X * Revision 2.5   84/10/30  20:15:14  ames!jaw
X *   Junk chaining; replace with the simpler (and, on the VAX, faster)
X *   double hashing, discussed within.  Make block compression standard.
X *
X * Revision 2.4   84/10/16  11:11:11  ames!jaw
X *   Introduce adaptive reset for block compression, to boost the rate
X *   another several percent.  (See mailing list notes.)
X *
X * Revision 2.3   84/09/22  22:00:00  petsd!joe
X *   Implemented "-B" block compress.  Implemented REVERSE sorting of tab_next.
X *   Bug fix for last bits.  Changed fwrite to putchar loop everywhere.
X *
X * Revision 2.2   84/09/18  14:12:21  ames!jaw
X *   Fold in news changes, small machine typedef from thomas,
X *   #ifdef interdata from joe.
X *
X * Revision 2.1   84/09/10  12:34:56  ames!jaw
X *   Configured fast table lookup for 32-bit machines.
X *   This cuts user time in half for b <= FBITS, and is useful for news batching
X *   from VAX to PDP sites.  Also sped up decompress() [fwrite->putc] and
X *   added signal catcher [plus beef in write_error()] to delete effluvia.
X *
X * Revision 2.0   84/08/28  22:00:00  petsd!joe
X *   Add check for foreground before prompting user.  Insert maxbits into
X *   compressed file.  Force file being uncompressed to end with ".Z".
X *   Added "-c" flag and "zcat".  Prepared for release.
X *
X * Revision 1.10  84/08/24  18:28:00  turtlevax!ken
X *   Will only compress regular files (no directories), added a magic number
X *   header (plus an undocumented -n flag to handle old files without headers),
X *   added -f flag to force overwriting of possibly existing destination file,
X *   otherwise the user is prompted for a response.  Will tack on a .Z to a
X *   filename if it doesn't have one when decompressing.  Will only replace
X *   file if it was compressed.
X *
X * Revision 1.9  84/08/16  17:28:00  turtlevax!ken
X *   Removed scanargs(), getopt(), added .Z extension and unlimited number of
X *   filenames to compress.  Flags may be clustered (-Ddvb12) or separated
X *   (-D -d -v -b 12), or combination thereof.  Modes and other status is
X *   copied with copystat().  -O bug for 4.2 seems to have disappeared with
X *   1.8.
X *
X * Revision 1.8  84/08/09  23:15:00  joe
X *   Made it compatible with vax version, installed jim's fixes/enhancements
X *
X * Revision 1.6  84/08/01  22:08:00  joe
X *   Sped up algorithm significantly by sorting the compress chain.
X *
X * Revision 1.5  84/07/13  13:11:00  srd
X *   Added C version of vax asm routines.  Changed structure to arrays to
X *   save much memory.  Do unsigned compares where possible (faster on
X *   Perkin-Elmer)
X *
X * Revision 1.4  84/07/05  03:11:11  thomas
X *   Clean up the code a little and lint it.  (Lint complains about all
X *   the regs used in the asm, but I'm not going to "fix" this.)
X *
X * Revision 1.3  84/07/05  02:06:54  thomas
X *   Minor fixes.
X *
X * Revision 1.2  84/07/05  00:27:27  thomas
X *   Add variable bit length output.
X *
X */
X#include	<stdio.h>
X#include	<fcntl.h>
X#include	<ctype.h>
X#include	<signal.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#include	<errno.h>
X
X#ifdef DIRENT
X#	include	<dirent.h>
X#	define	RECURSIVE		1
X#	undef	SYSDIR
X#endif
X#ifdef SYSDIR
X#	include	<sys/dir.h>
X#	define	RECURSIVE		1
X#endif
X#ifdef UTIME_H
X#	include	<utime.h>
X#else
X	struct utimbuf {
X		time_t actime;
X		time_t modtime;
X	};
X#endif
X
X#ifdef	__STDC__
X#	define	ARGS(a)				a
X#else
X#	define	ARGS(a)				()
X#endif
X
X#define	LARGS(a)	()	/* Relay on include files for libary func defs. */
X
X#ifndef SIG_TYPE
X#	define	SIG_TYPE	void (*)()
X#endif
X
X#ifndef NOFUNCDEF
X	extern	void	*malloc	LARGS((int));
X	extern	void	free	LARGS((void *));
X#ifndef _IBMR2
X	extern	int		open	LARGS((char const *,int,...));
X#endif
X	extern	int		close	LARGS((int));
X	extern	int		read	LARGS((int,void *,int));
X	extern	int		write	LARGS((int,void const *,int));
X	extern	int		chmod	LARGS((char const *,int));
X	extern	int		unlink	LARGS((char const *));
X	extern	int		chown	LARGS((char const *,int,int));
X	extern	int		utime	LARGS((char const *,struct utimbuf const *));
X	extern	char	*strcpy	LARGS((char *,char const *));
X	extern	char	*strcat	LARGS((char *,char const *));
X	extern	int		strcmp	LARGS((char const *,char const *));
X	extern	unsigned strlen	LARGS((char const *));
X	extern	void	*memset	LARGS((void *,char,unsigned int));
X	extern	void	*memcpy	LARGS((void *,void const *,unsigned int));
X	extern	int		atoi	LARGS((char const *));
X	extern	void	exit	LARGS((int));
X	extern	int		isatty	LARGS((int));
X#endif
X	
X#define	MARK(a)	{ asm(" .globl M.a"); asm("M.a:"); }
X
X#ifdef	DEF_ERRNO
X	extern int	errno;
X#endif
X
X#include "patchlevel.h"
X
X#undef	min
X#define	min(a,b)	((a>b) ? b : a)
X
X#ifndef	IBUFSIZ
X#	define	IBUFSIZ	BUFSIZ	/* Defailt input buffer size							*/
X#endif
X#ifndef	OBUFSIZ
X#	define	OBUFSIZ	BUFSIZ	/* Default output buffer size							*/
X#endif
X
X#define MAXPATHLEN 1024		/* MAXPATHLEN - maximum length of a pathname we allow 	*/
X#define	SIZE_INNER_LOOP		256	/* Size of the inter (fast) compress loop			*/
X
X							/* Defines for third byte of header 					*/
X#define	MAGIC_1		(char_type)'\037'/* First byte of compressed file				*/
X#define	MAGIC_2		(char_type)'\235'/* Second byte of compressed file				*/
X#define BIT_MASK	0x1f			/* Mask for 'number of compresssion bits'		*/
X									/* Masks 0x20 and 0x40 are free.  				*/
X									/* I think 0x20 should mean that there is		*/
X									/* a fourth header byte (for expansion).    	*/
X#define BLOCK_MODE	0x80			/* Block compresssion if table is full and		*/
X									/* compression rate is dropping flush tables	*/
X
X			/* the next two codes should not be changed lightly, as they must not	*/
X			/* lie within the contiguous general code space.						*/
X#define FIRST	257					/* first free entry 							*/
X#define	CLEAR	256					/* table clear output code 						*/
X
X#define INIT_BITS 9			/* initial number of bits/code */
X
X#ifndef SACREDMEM
X	/*
X 	 * SACREDMEM is the amount of physical memory saved for others; compress
X 	 * will hog the rest.
X 	 */
X#	define SACREDMEM	0
X#endif
X
X#ifndef USERMEM
X	/*
X 	 * Set USERMEM to the maximum amount of physical user memory available
X 	 * in bytes.  USERMEM is used to determine the maximum BITS that can be used
X 	 * for compression.
X	 */
X#	define USERMEM 	450000	/* default user memory */
X#endif
X
X#ifndef	BYTEORDER
X#	define	BYTEORDER	0000
X#endif
X
X#ifndef	NOALLIGN
X#	define	NOALLIGN	0
X#endif
X
X/*
X * machine variants which require cc -Dmachine:  pdp11, z8000, DOS
X */
X
X#ifdef interdata	/* Perkin-Elmer													*/
X#	define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned 			*/
X#endif
X
X#ifdef pdp11	 	/* PDP11: don't forget to compile with -i 						*/
X#	define	BITS 		12	/* max bits/code for 16-bit machine 					*/
X#	define	NO_UCHAR		/* also if "unsigned char" functions as signed char 	*/
X#endif /* pdp11 */
X
X#ifdef z8000		/* Z8000: 														*/
X#	define	BITS 	12	/* 16-bits processor max 12 bits							*/
X#	undef	vax			/* weird preprocessor 										*/
X#endif /* z8000 */
X
X#ifdef	DOS			/* PC/XT/AT (8088) processor									*/
X#	define	BITS   16	/* 16-bits processor max 12 bits							*/
X#	if BITS == 16
X#		define	MAXSEG_64K
X#	endif
X#	undef	BYTEORDER
X#	define	BYTEORDER 	4321
X#	undef	NOALLIGN
X#	define	NOALLIGN	1
X#	define	COMPILE_DATE	__DATE__
X#endif /* DOS */
X
X#ifndef	O_BINARY
X#	define	O_BINARY	0	/* System has no binary mode							*/
X#endif
X
X#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
X#	if BITS == 16 		/* more than 65535 bytes - so we fake it */
X# 		define MAXSEG_64K
X#	else
X#	if BITS > 13			/* Code only handles BITS = 12, 13, or 16 */
X#		define BITS	13
X#	endif
X#	endif
X#endif
X
X#ifndef BITS		/* General processor calculate BITS								*/
X#	if USERMEM >= (800000+SACREDMEM)
X#		define FAST
X#	else
X#	if USERMEM >= (433484+SACREDMEM)
X#		define BITS	16
X#	else
X#	if USERMEM >= (229600+SACREDMEM)
X#		define BITS	15
X#	else
X#	if USERMEM >= (127536+SACREDMEM)
X#		define BITS	14
X#   else
X#	if USERMEM >= (73464+SACREDMEM)
X#		define BITS	13
X#	else
X#		define BITS	12
X#	endif
X#	endif
X#   endif
X#	endif
X#	endif
X#endif /* BITS */
X
X#ifdef FAST
X#	define	HBITS		17			/* 50% occupancy */
X#	define	HSIZE	   (1<<HBITS)
X#	define	HMASK	   (HSIZE-1)
X#	define	HPRIME		 9941
X#	define	BITS		   16
X#	undef	MAXSEG_64K
X#else
X#	if BITS == 16
X#		define HSIZE	69001		/* 95% occupancy */
X#	endif
X#	if BITS == 15
X#		define HSIZE	35023		/* 94% occupancy */
X#	endif
X#	if BITS == 14
X#		define HSIZE	18013		/* 91% occupancy */
X#	endif
X#	if BITS == 13
X#		define HSIZE	9001		/* 91% occupancy */
X#	endif
X#	if BITS <= 12
X#		define HSIZE	5003		/* 80% occupancy */
X#	endif
X#endif
X
X#define CHECK_GAP 10000
X
Xtypedef long int			code_int;
X
X#ifdef SIGNED_COMPARE_SLOW
X	typedef unsigned long int	count_int;
X	typedef unsigned short int	count_short;
X	typedef unsigned long int	cmp_code_int;	/* Cast to make compare faster	*/
X#else
X	typedef long int	 		count_int;
X	typedef long int			cmp_code_int;
X#endif
X
Xtypedef	unsigned char	char_type;
X
X#define ARGVAL() (*++(*argv) || (--argc && *++argv))
X
X#define MAXCODE(n)	(1L << (n))
X
X#ifndef	REGISTERS
X#	define	REGISTERS	2
X#endif
X#define	REG1	
X#define	REG2	
X#define	REG3	
X#define	REG4	
X#define	REG5	
X#define	REG6	
X#define	REG7	
X#define	REG8	
X#define	REG9	
X#define	REG10
X#define	REG11	
X#define	REG12	
X#define	REG13
X#define	REG14
X#define	REG15
X#define	REG16
X#if REGISTERS >= 1
X#	undef	REG1
X#	define	REG1	register
X#endif
X#if REGISTERS >= 2
X#	undef	REG2
X#	define	REG2	register
X#endif
X#if REGISTERS >= 3
X#	undef	REG3
X#	define	REG3	register
X#endif
X#if REGISTERS >= 4
X#	undef	REG4
X#	define	REG4	register
X#endif
X#if REGISTERS >= 5
X#	undef	REG5
X#	define	REG5	register
X#endif
X#if REGISTERS >= 6
X#	undef	REG6
X#	define	REG6	register
X#endif
X#if REGISTERS >= 7
X#	undef	REG7
X#	define	REG7	register
X#endif
X#if REGISTERS >= 8
X#	undef	REG8
X#	define	REG8	register
X#endif
X#if REGISTERS >= 9
X#	undef	REG9
X#	define	REG9	register
X#endif
X#if REGISTERS >= 10
X#	undef	REG10
X#	define	REG10	register
X#endif
X#if REGISTERS >= 11
X#	undef	REG11
X#	define	REG11	register
X#endif
X#if REGISTERS >= 12
X#	undef	REG12
X#	define	REG12	register
X#endif
X#if REGISTERS >= 13
X#	undef	REG13
X#	define	REG13	register
X#endif
X#if REGISTERS >= 14
X#	undef	REG14
X#	define	REG14	register
X#endif
X#if REGISTERS >= 15
X#	undef	REG15
X#	define	REG15	register
X#endif
X#if REGISTERS >= 16
X#	undef	REG16
X#	define	REG16	register
X#endif
X
X
Xunion	bytes
X{
X	long	word;
X	struct
X	{
X#if BYTEORDER == 4321
X		char_type	b1;
X		char_type	b2;
X		char_type	b3;
X		char_type	b4;
X#else
X#if BYTEORDER == 1234
X		char_type	b4;
X		char_type	b3;
X		char_type	b2;
X		char_type	b1;
X#else
X#	undef	BYTEORDER
X		int				dummy;
X#endif
X#endif
X	} bytes;
X} ;
X#if BYTEORDER == 4321 && NOALLIGN == 1
X#define	output(b,o,c,n)	{													\
X							*(long *)&((b)[(o)>>3]) |= ((long)(c))<<((o)&0x7);\
X							(o) += (n);										\
X						}
X#else
X#ifdef BYTEORDER
X#define	output(b,o,c,n)	{	REG1 char_type	*p = &(b)[(o)>>3];				\
X							union bytes i;									\
X							i.word = ((long)(c))<<((o)&0x7);				\
X							p[0] |= i.bytes.b1;								\
X							p[1] |= i.bytes.b2;								\
X							p[2] |= i.bytes.b3;								\
X							(o) += (n);										\
X						}
X#else
X#define	output(b,o,c,n)	{	REG1 char_type	*p = &(b)[(o)>>3];				\
X							REG2 long		 i = ((long)(c))<<((o)&0x7);	\
X							p[0] |= (char_type)(i);							\
X							p[1] |= (char_type)(i>>8);						\
X							p[2] |= (char_type)(i>>16);						\
X							(o) += (n);										\
X						}
X#endif
X#endif
X#if BYTEORDER == 4321 && NOALLIGN == 1
X#define	input(b,o,c,n,m){													\
X							(c) = (*(long *)(&(b)[(o)>>3])>>((o)&0x7))&(m);	\
X							(o) += (n);										\
X						}
X#else
X#define	input(b,o,c,n,m){	REG1 char_type 		*p = &(b)[(o)>>3];			\
X							(c) = ((((long)(p[0]))|((long)(p[1])<<8)|		\
X									 ((long)(p[2])<<16))>>((o)&0x7))&(m);	\
X							(o) += (n);										\
X						}
X#endif
X
Xchar			*progname;			/* Program name									*/
Xint 			silent = 0;			/* don't tell me about errors					*/
Xint 			quiet = 1;			/* don't tell me about compression 				*/
Xint				do_decomp = 0;		/* Decompress mode								*/
Xint				force = 0;			/* Force overwrite of files and links			*/
Xint				nomagic = 0;		/* Use a 3-byte magic number header,			*/
X									/* unless old file 								*/
Xint				block_mode = BLOCK_MODE;/* Block compress mode -C compatible with 2.0*/
Xint				maxbits = BITS;		/* user settable max # bits/code 				*/
Xint 			zcat_flg = 0;		/* Write output on stdout, suppress messages 	*/
Xint				recursive = 0;  	/* compress directories 						*/
Xint				exit_code = -1;		/* Exitcode of compress (-1 no file compressed)	*/
X
Xchar_type		inbuf[IBUFSIZ+64];	/* Input buffer									*/
Xchar_type		outbuf[OBUFSIZ+2048];/* Output buffer								*/
X
Xstruct stat		infstat;			/* Input file status							*/
Xchar			*ifname;			/* Input filename								*/
Xint				remove_ofname = 0;	/* Remove output file on a error				*/
Xchar 			ofname[MAXPATHLEN];	/* Output filename								*/
Xint				fgnd_flag = 0;		/* Running in background (SIGINT=SIGIGN)		*/
X
Xlong 			bytes_in;			/* Total number of byte from input				*/
Xlong 			bytes_out;			/* Total number of byte to output				*/
X
X/*
X * 8086 & 80286 Has a problem with array bigger than 64K so fake the array
X * For processors with a limited address space and segments.
X */
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X#ifdef MAXSEG_64K
X	count_int htab0[8192];
X	count_int htab1[8192];
X	count_int htab2[8192];
X	count_int htab3[8192];
X	count_int htab4[8192];
X	count_int htab5[8192];
X	count_int htab6[8192];
X	count_int htab7[8192];
X	count_int htab8[HSIZE-65536];
X	count_int * htab[9] = {htab0,htab1,htab2,htab3,htab4,htab5,htab6,htab7,htab8};
X
X	unsigned short code0tab[16384];
X	unsigned short code1tab[16384];
X	unsigned short code2tab[16384];
X	unsigned short code3tab[16384];
X	unsigned short code4tab[16384];
X	unsigned short * codetab[5] = {code0tab,code1tab,code2tab,code3tab,code4tab};
X
X#	define	htabof(i)			(htab[(i) >> 13][(i) & 0x1fff])
X#	define	codetabof(i)		(codetab[(i) >> 14][(i) & 0x3fff])
X#	define	tab_prefixof(i)		codetabof(i)
X#	define	tab_suffixof(i)		((char_type *)htab[(i)>>15])[(i) & 0x7fff]
X#	define	de_stack			((char_type *)(&htab2[8191]))
X	void	clear_htab()
X	{
X		memset(htab0, -1, sizeof(htab0));
X		memset(htab1, -1, sizeof(htab1));
X		memset(htab2, -1, sizeof(htab2));
X		memset(htab3, -1, sizeof(htab3));
X		memset(htab4, -1, sizeof(htab4));
X		memset(htab5, -1, sizeof(htab5));
X		memset(htab6, -1, sizeof(htab6));
X		memset(htab7, -1, sizeof(htab7));
X		memset(htab8, -1, sizeof(htab8));
X	 }
X#	define	clear_tab_prefixof()	memset(code0tab, 0, 256);
X#else	/* Normal machine */
X	count_int		htab[HSIZE];
X	unsigned short	codetab[HSIZE];
X
X#	define	htabof(i)				htab[i]
X#	define	codetabof(i)			codetab[i]
X#	define	tab_prefixof(i)			codetabof(i)
X#	define	tab_suffixof(i)			((char_type *)(htab))[i]
X#	define	de_stack				((char_type *)&(htab[HSIZE-1]))
X#	define	clear_htab()			memset(htab, -1, sizeof(htab))
X#	define	clear_tab_prefixof()	memset(codetab, 0, 256);
X#endif	/* MAXSEG_64K */
X
X#ifdef FAST
X	int primetab[256] =		/* Special secudary hash table.		*/
X	{
X    	 1013, -1061, 1109, -1181, 1231, -1291, 1361, -1429,
X    	 1481, -1531, 1583, -1627, 1699, -1759, 1831, -1889,
X    	 1973, -2017, 2083, -2137, 2213, -2273, 2339, -2383,
X    	 2441, -2531, 2593, -2663, 2707, -2753, 2819, -2887,
X    	 2957, -3023, 3089, -3181, 3251, -3313, 3361, -3449,
X    	 3511, -3557, 3617, -3677, 3739, -3821, 3881, -3931,
X    	 4013, -4079, 4139, -4219, 4271, -4349, 4423, -4493,
X    	 4561, -4639, 4691, -4783, 4831, -4931, 4973, -5023,
X    	 5101, -5179, 5261, -5333, 5413, -5471, 5521, -5591,
X    	 5659, -5737, 5807, -5857, 5923, -6029, 6089, -6151,
X    	 6221, -6287, 6343, -6397, 6491, -6571, 6659, -6709,
X    	 6791, -6857, 6917, -6983, 7043, -7129, 7213, -7297,
X    	 7369, -7477, 7529, -7577, 7643, -7703, 7789, -7873,
X    	 7933, -8017, 8093, -8171, 8237, -8297, 8387, -8461,
X    	 8543, -8627, 8689, -8741, 8819, -8867, 8963, -9029,
X    	 9109, -9181, 9241, -9323, 9397, -9439, 9511, -9613,
X    	 9677, -9743, 9811, -9871, 9941,-10061,10111,-10177,
X   		10259,-10321,10399,-10477,10567,-10639,10711,-10789,
X   		10867,-10949,11047,-11113,11173,-11261,11329,-11423,
X   		11491,-11587,11681,-11777,11827,-11903,11959,-12041,
X   		12109,-12197,12263,-12343,12413,-12487,12541,-12611,
X   		12671,-12757,12829,-12917,12979,-13043,13127,-13187,
X   		13291,-13367,13451,-13523,13619,-13691,13751,-13829,
X   		13901,-13967,14057,-14153,14249,-14341,14419,-14489,
X   		14557,-14633,14717,-14767,14831,-14897,14983,-15083,
X   		15149,-15233,15289,-15359,15427,-15497,15583,-15649,
X   		15733,-15791,15881,-15937,16057,-16097,16189,-16267,
X   		16363,-16447,16529,-16619,16691,-16763,16879,-16937,
X   		17021,-17093,17183,-17257,17341,-17401,17477,-17551,
X   		17623,-17713,17791,-17891,17957,-18041,18097,-18169,
X   		18233,-18307,18379,-18451,18523,-18637,18731,-18803,
X   		18919,-19031,19121,-19211,19273,-19381,19429,-19477
X	} ;
X#endif
X
Xvoid  	main			ARGS((int,char **));
Xvoid  	Usage			ARGS((void));
Xvoid  	comprexx		ARGS((char **));
Xvoid  	compdir			ARGS((char *));
Xvoid  	compress		ARGS((int,int));
Xvoid  	decompress		ARGS((int,int));
Xchar  	*rindex			ARGS((char *,int));
Xvoid  	read_error		ARGS((void));
Xvoid  	write_error		ARGS((void));
Xvoid 	abort_compress	ARGS((void));
Xvoid  	prratio			ARGS((FILE *,long,long));
Xvoid  	about			ARGS((void));
X
X/*****************************************************************
X * TAG( main )
X *
X * Algorithm from "A Technique for High Performance Data Compression",
X * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
X *
X * Usage: compress [-dfvc] [-b bits] [file ...]
X * Inputs:
X *   -d:     If given, decompression is done instead.
X *
X *   -c:     Write output on stdout, don't remove original.
X *
X *   -b:     Parameter limits the max number of bits/code.
X *
X *   -f:     Forces output file to be generated, even if one already
X *           exists, and even if no space is saved by compressing.
X *           If -f is not used, the user will be prompted if stdin is
X *           a tty, otherwise, the output file will not be overwritten.
X *
X *   -v:     Write compression statistics
X *
X *   -r:     Recursive. If a filename is a directory, descend
X *           into it and compress everything in it.
X *
X * file ...:
X *           Files to be compressed.  If none specified, stdin is used.
X * Outputs:
X *   file.Z:     Compressed form of file with same mode, owner, and utimes
X *   or stdout   (if stdin used as input)
X *
X * Assumptions:
X *   When filenames are given, replaces with the compressed version
X *   (.Z suffix) only if the file decreases in size.
X *
X * Algorithm:
X *   Modified Lempel-Ziv method (LZW).  Basically finds common
X *   substrings and replaces them with a variable size code.  This is
X *   deterministic, and can be done on the fly.  Thus, the decompression
X *   procedure needs no input table, but tracks the way the table was built.
X */ 
Xvoid
Xmain(argc, argv)
X	REG1	int 	 argc;
X	REG2	char	*argv[];
X	{
X    	REG3	char		**filelist;
X		REG4	char		**fileptr;
X
X    	if (fgnd_flag = (signal(SIGINT, SIG_IGN) != SIG_IGN))
X			signal(SIGINT, (SIG_TYPE)abort_compress);
X
X		signal(SIGTERM, (SIG_TYPE)abort_compress);
X#ifndef DOS
X		signal(SIGHUP, (SIG_TYPE)abort_compress);
X#endif
X
X#ifdef COMPATIBLE
X    	nomagic = 1;	/* Original didn't have a magic number */
X#endif
X
X    	filelist = fileptr = (char **)malloc(argc*sizeof(char *));
X    	*filelist = NULL;
X
X    	if((progname = rindex(argv[0], '/')) != 0)
X			progname++;
X		else
X			progname = argv[0];
X
X    	if (strcmp(progname, "uncompress") == 0)
X			do_decomp = 1;
X		else
X		if (strcmp(progname, "zcat") == 0)
X			do_decomp = zcat_flg = 1;
X
X    	/* Argument Processing
X     	 * All flags are optional.
X     	 * -V => print Version; debug verbose
X     	 * -d => do_decomp
X     	 * -v => unquiet
X     	 * -f => force overwrite of output file
X     	 * -n => no header: useful to uncompress old files
X     	 * -b maxbits => maxbits.  If -b is specified, then maxbits MUST be given also.
X     	 * -c => cat all output to stdout
X     	 * -C => generate output compatible with compress 2.0.
X     	 * -r => recursively compress directories
X     	 * if a string is left, must be an input filename.
X     	 */
X
X    	for (argc--, argv++; argc > 0; argc--, argv++)
X		{
X			if (**argv == '-')
X			{/* A flag argument */
X		    	while (*++(*argv))
X				{/* Process all flags in this arg */
X					switch (**argv)
X					{
X			    	case 'V':
X						about();
X						break;
X
X					case 's':
X						silent = 1;
X						quiet = 1;
X						break;
X
X			    	case 'v':
X						silent = 0;
X						quiet = 0;
X						break;
X
X			    	case 'd':
X						do_decomp = 1;
X						break;
X
X			    	case 'f':
X			    	case 'F':
X						force = 1;
X						break;
X
X			    	case 'n':
X						nomagic = 1;
X						break;
X
X			    	case 'C':
X						block_mode = 0;
X						break;
X
X			    	case 'b':
X						if (!ARGVAL())
X						{
X					    	fprintf(stderr, "Missing maxbits\n");
X					    	Usage();
X						}
X
X						maxbits = atoi(*argv);
X						goto nextarg;
X
X		    		case 'c':
X						do_decomp = zcat_flg = 1;
X						break;
X
X			    	case 'q':
X						quiet = 1;
X						break;
X			    	case 'r':
X			    	case 'R':
X#ifdef	RECURSIVE
X						recursive = 1;
X#else
X						fprintf(stderr, "%s -r not availble (du to missing directory functions)\n", **argv);
X#endif
X						break;
X
X			    	default:
X						fprintf(stderr, "Unknown flag: '%c'; ", **argv);
X						Usage();
X					}
X		    	}
X			}
X			else
X			{
X		    	*fileptr++ = *argv;	/* Build input file list */
X		    	*fileptr = NULL;
X			}
X
Xnextarg:	continue;
X    	}
X
X    	if (maxbits < INIT_BITS)	maxbits = INIT_BITS;
X    	if (maxbits > BITS) 		maxbits = BITS;
X
X    	if (*filelist != NULL)
X		{
X      		for (fileptr = filelist; *fileptr; fileptr++)
X				comprexx(fileptr);
X    	}
X		else
X		{/* Standard input */
X			ifname = "";
X			exit_code = 0;
X			remove_ofname = 0;
X
X			if (do_decomp == 0)
X			{
X				compress(0, 1);
X
X				if (zcat_flg == 0 && !quiet)
X				{
X					fprintf(stderr, "Compression: ");
X					prratio(stderr, bytes_in-bytes_out, bytes_in);
X					fprintf(stderr, "\n");
X				}
X
X				if (bytes_out >= bytes_in && !(force))
X					exit_code = 2;
X			}
X			else
X				decompress(0, 1);
X		}
X
X		exit((exit_code== -1) ? 1:exit_code);
X	}
X
Xvoid
XUsage()
X	{
X		fprintf(stderr, "\
XUsage: %s [-dfvcVr] [-b maxbits] [file ...]\n\
X       -d   If given, decompression is done instead.\n\
X       -c   Write output on stdout, don't remove original.\n\
X       -b   Parameter limits the max number of bits/code.\n", progname);
X		fprintf(stderr, "\
X       -f   Forces output file to be generated, even if one already.\n\
X            exists, and even if no space is saved by compressing.\n\
X            If -f is not used, the user will be prompted if stdin is.\n\
X            a tty, otherwise, the output file will not be overwritten.\n\
X       -v   Write compression statistics.\n\
X       -V   Output vesion and compile options.\n\
X       -r   Recursive. If a filename is a directory, descend\n\
X            into it and compress everything in it.\n");
X
X    		exit(1);
X	}
X
Xvoid
Xcomprexx(fileptr)
X	char	**fileptr;
X	{
X		int		fdin;
X		int		fdout;
X		char	tempname[MAXPATHLEN];
X
X		strcpy(tempname,*fileptr);
X		errno = 0;
X
X#ifdef	LSTAT
X		if (lstat(tempname,&infstat) == -1)
X#else
X		if (stat(tempname,&infstat) == -1)
X#endif
X		{
X		  	if (do_decomp)
X			{
X	    		switch (errno)
X				{
X		    	case ENOENT:	/* file doesn't exist */
X	      			/*
X	      			** if the given name doesn't end with .Z, try appending one
X	      			** This is obviously the wrong thing to do if it's a 
X	      			** directory, but it shouldn't do any harm.
X	      			*/
X		      		if (strcmp(tempname + strlen(tempname) - 2, ".Z") != 0)
X					{
X						strcat(tempname,".Z");
X						errno = 0;
X#ifdef	LSTAT
X						if (lstat(tempname,&infstat) == -1)
X#else
X						if (stat(tempname,&infstat) == -1)
X#endif
X						{
X						  	perror(tempname);
X							exit_code = 1;
X						  	return;
X						}
X
X						if ((infstat.st_mode & S_IFMT) != S_IFREG)
X						{
X							fprintf(stderr, "%s: Not a regular file.\n", tempname);
X							exit_code = 1;
X							return ;
X						}
X		      		}
X		      		else
X					{
X						perror(tempname);
X						exit_code = 1;
X						return;
X		      		}
X
X		      		break;
X
X	    		default:
X		      		perror(tempname);
X					exit_code = 1;
X		      		return;
X	    		}
X	  		}
X	  		else
X			{
X	      		perror(tempname);
X				exit_code = 1;
X	      		return;
X	  		}
X		}
X
X		switch (infstat.st_mode & S_IFMT)
X		{
X		case S_IFDIR:	/* directory */
X#ifdef	RECURSIVE
X		  	if (recursive)
X		    	compdir(tempname);
X		  	else
X#endif
X			if (!quiet)
X		    	fprintf(stderr,"%s is a directory -- ignored\n", tempname);
X		  	break;
X
X		case S_IFREG:	/* regular file */
X		  	if (do_decomp != 0)
X			{/* DECOMPRESSION */
X		    	if (!zcat_flg)
X				{
X			      	if (strcmp(tempname + strlen(tempname) - 2, ".Z") != 0)
X					{
X						if (!quiet)
X		  					fprintf(stderr,"%s - no .Z suffix\n",tempname);
X
X						return;
X	      			}
X		    	}
X
X				strcpy(ofname, tempname);
X
X				/* Strip of .Z suffix */
X
X				if (strcmp(tempname + strlen(tempname) - 2, ".Z") == 0)
X			  		ofname[strlen(tempname) - 2] = '\0';
X	   		}
X	   		else
X			{/* COMPRESSION */
X				if (strcmp(tempname + strlen(tempname) - 2, ".Z") == 0)
X				{
X		 	 		fprintf(stderr, "%s: already has .Z suffix -- no change\n", tempname);
X			  		return;
X				}
X
X				if (infstat.st_nlink > 1 && (!force))
X				{
X			  		fprintf(stderr, "%s has %d other links: unchanged\n",
X									tempname, infstat.st_nlink - 1);
X					exit_code = 1;
X			  		return;
X				}
X
X				strcpy(ofname, tempname);
X				strcat(ofname, ".Z");
X    		}
X
X	    	if ((fdin = open(ifname = tempname, O_RDONLY|O_BINARY)) == -1)
X			{
X		      	perror(tempname);
X				exit_code = 1;
X				return;
X	    	}
X
X    		if (zcat_flg == 0)
X			{
X				int				c;
X				int				s;
X				struct stat		statbuf;
X				struct stat		statbuf2;
X
X				if (stat(ofname, &statbuf) == 0)
X				{
X					if ((s = strlen(ofname)) > 8)
X					{
X						c = ofname[s-1];
X						ofname[s-1] = '\0';
X
X						statbuf2 = statbuf;
X
X						if (!stat(ofname, &statbuf2) &&
X							statbuf.st_mode  == statbuf2.st_mode &&
X							statbuf.st_ino   == statbuf2.st_ino &&
X							statbuf.st_dev   == statbuf2.st_dev &&
X							statbuf.st_uid   == statbuf2.st_uid &&
X							statbuf.st_gid   == statbuf2.st_gid &&
X							statbuf.st_size  == statbuf2.st_size &&
X							statbuf.st_atime == statbuf2.st_atime &&
X							statbuf.st_mtime == statbuf2.st_mtime &&
X							statbuf.st_ctime == statbuf2.st_ctime)
X						{
X							fprintf(stderr, "%s: filename too long to tack on .Z\n", tempname);
X							exit_code = 1;
X							return;
X						}
X
X						ofname[s-1] = (char)c;
X					}
X
X					if (!force)
X					{
X		    			inbuf[0] = 'n';
X
X		    			fprintf(stderr, "%s already exists.\n", ofname);
X
X		    			if (fgnd_flag && isatty(0))
X						{
X							fprintf(stderr, "Do you wish to overwrite %s (y or n)? ", ofname);
X							fflush(stderr);
X	
X			    			if (read(0, inbuf, 1) > 0)
X							{
X								if (inbuf[0] != '\n')
X								{
X									do
X									{
X										if (read(0, inbuf+1, 1) <= 0)
X										{
X											perror("stdin");
X											break;
X										}
X									}
X									while (inbuf[1] != '\n');
X								}
X							}
X							else
X								perror("stdin");
X		    			}
X
X		    			if (inbuf[0] != 'y')
X						{
X							fprintf(stderr, "%s not overwritten\n", ofname);
X							exit_code = 1;
X							return;
X		    			}
X					}
X
X					if (unlink(ofname))
X					{
X						fprintf(stderr, "Can't remove old output file\n");
X						perror(ofname);
X						exit_code = 1;
X						return ;
X					}
X				}
X
X		    	if ((fdout = open(ofname, O_WRONLY|O_CREAT|O_EXCL|O_BINARY,0600)) == -1)
X				{
X			      	perror(tempname);
X					return;
X		    	}
X
X				if ((s = strlen(ofname)) > 8)
X				{
X					if (fstat(fdout, &statbuf))
X					{
X						fprintf(stderr, "Can't get status op output file\n");
X						perror(ofname);
X						exit_code = 1;
X						return ;
X					}
X
X					c = ofname[s-1];
X					ofname[s-1] = '\0';
X					statbuf2 = statbuf;
X
X					if (!stat(ofname, &statbuf2) &&
X						statbuf.st_mode  == statbuf2.st_mode &&
X						statbuf.st_ino   == statbuf2.st_ino &&
X						statbuf.st_dev   == statbuf2.st_dev &&
X						statbuf.st_uid   == statbuf2.st_uid &&
X						statbuf.st_gid   == statbuf2.st_gid &&
X						statbuf.st_size  == statbuf2.st_size &&
X						statbuf.st_atime == statbuf2.st_atime &&
X						statbuf.st_mtime == statbuf2.st_mtime &&
X						statbuf.st_ctime == statbuf2.st_ctime)
X					{
X						fprintf(stderr, "%s: filename too long to tack on .Z\n", tempname);
X
X						if (unlink(ofname))
X						{
X							fprintf(stderr, "can't remove bad output file\n");
X							perror(ofname);
X						}
X						exit_code = 1;
X						return;
X					}
X
X					ofname[s-1] = (char)c;
X				}
X
X				if(!quiet)
X					fprintf(stderr, "%s: ", tempname);
X
X				remove_ofname = 1;
X    		}
X			else
X			{
X				fdout = 1;
X				ofname[0] = '\0';
X				remove_ofname = 0;
X			}
X
X    		if (do_decomp == 0)
X				compress(fdin, fdout);
X    		else
X				decompress(fdin, fdout);
X
X			close(fdin);
X
X			if (fdout != 1 && close(fdout))
X				write_error();
X
X			if (bytes_in == 0)
X			{
X				if (remove_ofname)
X				{
X					if (unlink(ofname))	/* Remove input file */
X					{
X						fprintf(stderr, "\nunlink error (ignored) ");
X	    				perror(ofname);
X						exit_code = 1;
X					}
X		
X					remove_ofname = 0;
X				}
X			}
X			else
X    		if (zcat_flg == 0)
X			{
X		    	struct utimbuf	timep;
X
X		    	if (!do_decomp && bytes_out >= bytes_in && (!force))
X				{/* No compression: remove file.Z */
X					if(!quiet)
X						fprintf(stderr, "No compression -- %s unchanged\n", ifname);
X
X			    	if (unlink(ofname))
X					{
X						fprintf(stderr, "unlink error (ignored) ");
X						perror(ofname);
X					}
X
X					remove_ofname = 0;
X					exit_code = 2;
X		    	}
X				else
X				{/* ***** Successful Compression ***** */
X					if(!quiet)
X					{
X						fprintf(stderr, " -- replaced with %s",ofname);
X
X						if (!do_decomp)
X						{
X							fprintf(stderr, " Compression: ");
X							prratio(stderr, bytes_in-bytes_out, bytes_in);
X						}
X
X						fprintf(stderr, "\n");
X					}
X
X					if (chmod(ofname, infstat.st_mode & 07777))		/* Copy modes */
X					{
X						fprintf(stderr, "\nchmod error (ignored) ");
X				    	perror(ofname);
X						exit_code = 1;
X					}
X#ifndef	DOS
X					chown(ofname, infstat.st_uid, infstat.st_gid);	/* Copy ownership */
X#endif
X
X					timep.actime = infstat.st_atime;
X					timep.modtime = infstat.st_mtime;
X
X					if (utime(ofname, &timep))
X					{
X						fprintf(stderr, "\nutime error (ignored) ");
X				    	perror(ofname);
X						exit_code = 1;
X					}
X
X					remove_ofname = 0;
X
X					if (unlink(ifname))	/* Remove input file */
X					{
X						fprintf(stderr, "\nunlink error (ignored) ");
X	    				perror(ifname);
X						exit_code = 1;
X					}
X    			}
X    		}
X
X			if (exit_code == -1)
X				exit_code = 0;
X
X	  		break;
X
X		default:
X	  		fprintf(stderr,"%s is not a directory or a regular file - ignored\n",
X			  		tempname);
X	  		break;
X		}
X	}
X
X#ifdef	RECURSIVE
Xvoid
Xcompdir(dir)
X	REG3	char	*dir;
X	{
X#ifndef	DIRENT
X		REG1 	struct direct	*dp;
X#else
X		REG1 	struct dirent	*dp;
X#endif
X		REG2	DIR				*dirp;
X		char					 nbuf[MAXPATHLEN];
X		char					*nptr = nbuf;
X
X		dirp = opendir(dir);
X
X		if (dirp == NULL)
X		{
X			printf("%s unreadable\n", dir);		/* not stderr! */
X			return ;
X		}
X		/*
X		** WARNING: the following algorithm will occasionally cause
X		** compress to produce error warnings of the form "<filename>.Z
X		** already has .Z suffix - ignored". This occurs when the
X		** .Z output file is inserted into the directory below
X		** readdir's current pointer.
X		** These warnings are harmless but annoying. The alternative
X		** to allowing this would be to store the entire directory
X		** list in memory, then compress the entries in the stored
X		** list. Given the depth-first recursive algorithm used here,
X		** this could use up a tremendous amount of memory. I don't
X		** think it's worth it. -- Dave Mack
X		*/
X
X		while (dp = readdir(dirp))
X		{
X			if (dp->d_ino == 0)
X				continue;
X
X			if (strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0)
X				continue;
X
X			if ((strlen(dir)+strlen(dp->d_name)+1) < (MAXPATHLEN - 1))
X			{
X			  	strcpy(nbuf,dir);
X			  	strcat(nbuf,"/");
X			  	strcat(nbuf,dp->d_name);
X		  		comprexx(&nptr);
X			}
X			else
X		  		fprintf(stderr,"Pathname too long: %s/%s\n", dir, dp->d_name);
X  		}
X
X		closedir(dirp);
X
X		return;
X	}
X#endif
X/*
X * compress fdin to fdout
X *
X * Algorithm:  use open addressing double hashing (no chaining) on the 
X * prefix code / next character combination.  We do a variant of Knuth's
X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
X * secondary probe.  Here, the modular division first probe is gives way
X * to a faster exclusive-or manipulation.  Also do block compression with
X * an adaptive reset, whereby the code table is cleared when the compression
X * ratio decreases, but after the table fills.  The variable-length output
X * codes are re-sized at this point, and a special CLEAR code is generated
X * for the decompressor.  Late addition:  construct the table according to
X * file size for noticeable speed improvement on small files.  Please direct
X * questions about this implementation to ames!jaw.
X */
Xvoid
Xcompress(fdin, fdout)
X	int		fdin;
X	int		fdout;
X	{
X		REG2	long		hp;
X		REG3	int			rpos;
X#if REGISTERS >= 5
X		REG5	long		fc;
X#endif
X		REG6	int			outbits;
X		REG7    int			rlop;
X		REG8	int			rsize;
X		REG9	int			stcode;
X		REG10	code_int	free_ent;
X		REG11	int			boff;
X		REG12	int			n_bits;
X		REG13	int			ratio;
X		REG14	long		checkpoint;
X		REG15	code_int	extcode;
X		union
X		{
X			long			code;
X			struct
X			{
X				char_type		c;
X				unsigned short	ent;
X			} e;
X		} fcode;
X
X		ratio = 0;
X		checkpoint = CHECK_GAP;
X		extcode = MAXCODE(n_bits = INIT_BITS)+1;
X		stcode = 1;
X		free_ent = FIRST;
X
X		memset(outbuf, 0, sizeof(outbuf));
X		bytes_out = 0; bytes_in = 0;
X		outbuf[0] = MAGIC_1;
X		outbuf[1] = MAGIC_2;
X		outbuf[2] = (char)(maxbits | block_mode);
X		boff = outbits = (3<<3);
X		fcode.code = 0;
X
X		clear_htab();
X
X		while ((rsize = read(fdin, inbuf, IBUFSIZ)) > 0)
X		{
X			if (bytes_in == 0)
X			{
X				fcode.e.ent = inbuf[0];
X				rpos = 1;
X			}
X			else
X				rpos = 0;
X
X			rlop = 0;
X
X			do
X			{
X				if (free_ent >= extcode && fcode.e.ent < FIRST)
X				{
X					if (n_bits < maxbits)
X					{
X						boff = outbits = (outbits-1)+((n_bits<<3)-
X								  	((outbits-boff-1+(n_bits<<3))%(n_bits<<3)));
X						if (++n_bits < maxbits)
X							extcode = MAXCODE(n_bits)+1;
X						else
X							extcode = MAXCODE(n_bits);
X					}
X					else
X					{
X						extcode = MAXCODE(16)+OBUFSIZ;
X						stcode = 0;
X					}
X				}
X
X				if (!stcode && bytes_in >= checkpoint && fcode.e.ent < FIRST)
X				{
X					REG1 long int rat;
X
X					checkpoint = bytes_in + CHECK_GAP;
X
X					if (bytes_in > 0x007fffff)
X					{							/* shift will overflow */
X						rat = (bytes_out+(outbits>>3)) >> 8;
X
X						if (rat == 0)				/* Don't divide by zero */
X							rat = 0x7fffffff;
X						else
X							rat = bytes_in / rat;
X					}
X					else
X						rat = (bytes_in << 8) / (bytes_out+(outbits>>3));	/* 8 fractional bits */
X					if (rat >= ratio)
X						ratio = (int)rat;
X					else
X					{
X						ratio = 0;
X						clear_htab();
X						output(outbuf,outbits,CLEAR,n_bits);
X						boff = outbits = (outbits-1)+((n_bits<<3)-
X								  	((outbits-boff-1+(n_bits<<3))%(n_bits<<3)));
X						extcode = MAXCODE(n_bits = INIT_BITS)+1;
X						free_ent = FIRST;
X						stcode = 1;
X					}
X				}
X
X				if (outbits >= (OBUFSIZ<<3))
X				{
X					if (write(fdout, outbuf, OBUFSIZ) != OBUFSIZ)
X						write_error();
X
X					outbits -= (OBUFSIZ<<3);
X					boff = -(((OBUFSIZ<<3)-boff)%(n_bits<<3));
X					bytes_out += OBUFSIZ;
X
X					memcpy(outbuf, outbuf+OBUFSIZ, (outbits>>3)+1);
X					memset(outbuf+(outbits>>3)+1, '\0', OBUFSIZ);
X				}
X
X				{
X					REG1	int		i;
X
X					i = rsize-rlop;
X
X					if ((code_int)i > extcode-free_ent)	i = (int)(extcode-free_ent);
X					if (i > ((sizeof(outbuf) - 32)*8 - outbits)/n_bits)
X						i = ((sizeof(outbuf) - 32)*8 - outbits)/n_bits;
X					
X					if (!stcode && (long)i > checkpoint-bytes_in)
X						i = (int)(checkpoint-bytes_in);
X
X					rlop += i;
X					bytes_in += i;
X				}
X
X				goto next;
Xhfound:			fcode.e.ent = codetabof(hp);
Xnext:  			if (rpos >= rlop)
X	   				goto endlop;
Xnext2: 			fcode.e.c = inbuf[rpos++];
X#ifndef FAST
X				{
X					REG1 	code_int	i;
X#if REGISTERS >= 5
X					fc = fcode.code;
X#else
X#	define			fc fcode.code
X#endif
X					hp = (((long)(fcode.e.c)) << (BITS-8)) ^ (long)(fcode.e.ent);
X
X					if ((i = htabof(hp)) == fc)
X						goto hfound;
X
X					if (i != -1)
X					{
X						REG4 long		disp;
X
X						disp = (HSIZE - hp)-1;	/* secondary hash (after G. Knott) */
X
X						do
X						{
X							if ((hp -= disp) < 0)	hp += HSIZE;
X
X							if ((i = htabof(hp)) == fc)
X								goto hfound;
X						}
X						while (i != -1);
X					}
X				}
X#else
X				{
X					REG1 long	i;
X					REG4 long	p;
X#if REGISTERS >= 5
X					fc = fcode.code;
X#else
X#	define			fc fcode.code
X#endif
X					hp = ((((long)(fcode.e.c)) << (HBITS-8)) ^ (long)(fcode.e.ent));
X
X					if ((i = htabof(hp)) == fc)	goto hfound;
X					if (i == -1)				goto out;
X
X					p = primetab[fcode.e.c];
Xlookup:				hp = (hp+p)&HMASK;
X					if ((i = htabof(hp)) == fc)	goto hfound;
X					if (i == -1)				goto out;
X					hp = (hp+p)&HMASK;
X					if ((i = htabof(hp)) == fc)	goto hfound;
X					if (i == -1)				goto out;
X					hp = (hp+p)&HMASK;
X					if ((i = htabof(hp)) == fc)	goto hfound;
X					if (i == -1)				goto out;
X					goto lookup;
X				}
Xout:			;
X#endif
X				output(outbuf,outbits,fcode.e.ent,n_bits);
X
X				{
X#if REGISTERS < 5
X#	undef	fc
X					REG1 long	fc;
X					fc = fcode.code;
X#endif
X					fcode.e.ent = fcode.e.c;
X
X
X					if (stcode)
X					{
X						codetabof(hp) = (unsigned short)free_ent++;
X						htabof(hp) = fc;
X					}
X				} 
X
X				goto next;
X
Xendlop:			if (fcode.e.ent >= FIRST && rpos < rsize)
X					goto next2;
X
X				if (rpos > rlop)
X				{
X					bytes_in += rpos-rlop;
X					rlop = rpos;
X				}
X			}
X			while (rlop < rsize);
X		}
X
X		if (rsize < 0)
X			read_error();
X
X		if (bytes_in > 0)
X			output(outbuf,outbits,fcode.e.ent,n_bits);
X
X		if (write(fdout, outbuf, (outbits+7)>>3) != (outbits+7)>>3)
X			write_error();
X
X		bytes_out += (outbits+7)>>3;
X
X		return;
X	}
X
X/*
X * Decompress stdin to stdout.  This routine adapts to the codes in the
X * file building the "string" table on-the-fly; requiring no table to
X * be stored in the compressed file.  The tables used herein are shared
X * with those of the compress() routine.  See the definitions above.
X */
X
Xvoid
Xdecompress(fdin, fdout)
X	int		fdin;
X	int		fdout;
X	{
X	    REG2 	char_type 		*stackp;
X	    REG3	code_int		 code;
X    	REG4	int				 finchar;
X		REG5	code_int		 oldcode;
X		REG6	code_int		 incode;
X		REG7	int				 inbits;
X		REG8	int				 posbits;
X		REG9	int				 outpos;
X		REG10	int				 insize;
X		REG11	int				 bitmask;
X		REG12	code_int		 free_ent;
X		REG13	code_int		 maxcode;
X		REG14	code_int		 maxmaxcode;
X		REG15	int				 n_bits;
X		REG16	int				 rsize;
X
X		bytes_in = 0;
X		bytes_out = 0;
X		insize = 0;
X
X		while (insize < 3 && (rsize = read(fdin, inbuf+insize, IBUFSIZ)) > 0)
X			insize += rsize;
X
X		if (insize < 3 || inbuf[0] != MAGIC_1 || inbuf[1] != MAGIC_2)
X		{
X			if (rsize < 0)
X				read_error();
X
X			if (insize > 0)
X			{
X				fprintf(stderr, "%s: not in compressed format\n",
X									(ifname[0] != '\0'? ifname : "stdin"));
X				exit_code = 1;
X			}
X
X			return ;
X		}
X
X		maxbits = inbuf[2] & BIT_MASK;
X		block_mode = inbuf[2] & BLOCK_MODE;
X		maxmaxcode = MAXCODE(maxbits);
X
X		if (maxbits > BITS)
X		{
X			fprintf(stderr,
X					"%s: compressed with %d bits, can only handle %d bits\n",
X					(*ifname != '\0' ? ifname : "stdin"), maxbits, BITS);
X			exit_code = 4;
X			return;
X		}
X
X		bytes_in = insize;
X	    maxcode = MAXCODE(n_bits = INIT_BITS)-1;
X		bitmask = (1<<n_bits)-1;
X		oldcode = -1;
X		finchar = 0;
X		outpos = 0;
X		posbits = 3<<3;
X
X	    free_ent = ((block_mode) ? FIRST : 256);
X
X		clear_tab_prefixof();	/* As above, initialize the first
X								   256 entries in the table. */
X
X	    for (code = 255 ; code >= 0 ; --code)
X			tab_suffixof(code) = (char_type)code;
X
X		do
X		{
Xresetbuf:	;
X			{
X				REG1	 int	i;
X				int				e;
X				int				o;
X
X				e = insize-(o = (posbits>>3));
X
X				for (i = 0 ; i < e ; ++i)
X					inbuf[i] = inbuf[i+o];
X
X				insize = e;
X				posbits = 0;
X			}
X
X			if (insize < sizeof(inbuf)-IBUFSIZ)
X			{
X				if ((rsize = read(fdin, inbuf+insize, IBUFSIZ)) < 0)
X					read_error();
X
X				insize += rsize;
X			}
X
X			inbits = ((rsize > 0) ? (insize - insize%n_bits)<<3 : 
X									(insize<<3)-(n_bits-1));
X
X			while (inbits > posbits)
X			{
X				if (free_ent > maxcode)
X				{
X					posbits = ((posbits-1) + ((n_bits<<3) -
X									 (posbits-1+(n_bits<<3))%(n_bits<<3)));
X
X					++n_bits;
X					if (n_bits == maxbits)
X						maxcode = maxmaxcode;
X					else
X					    maxcode = MAXCODE(n_bits)-1;
X
X					bitmask = (1<<n_bits)-1;
X					goto resetbuf;
X				}
X
X				input(inbuf,posbits,code,n_bits,bitmask);
X
X				if (oldcode == -1)
X				{
X					outbuf[outpos++] = (char_type)(finchar = (int)(oldcode = code));
X					continue;
X				}
X
X				if (code == CLEAR && block_mode)
X				{
X					clear_tab_prefixof();
X	    			free_ent = FIRST - 1;
X					posbits = ((posbits-1) + ((n_bits<<3) -
X								(posbits-1+(n_bits<<3))%(n_bits<<3)));
X				    maxcode = MAXCODE(n_bits = INIT_BITS)-1;
X					bitmask = (1<<n_bits)-1;
X					goto resetbuf;
X				}
X
X				incode = code;
X			    stackp = de_stack;
X
X				if (code >= free_ent)	/* Special case for KwKwK string.	*/
X				{
X					if (code > free_ent)
X					{
X						REG1 char_type 		*p;
X
X						posbits -= n_bits;
X						p = &inbuf[posbits>>3];
X
X						fprintf(stderr, "insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)\n", insize, posbits,
X								p[-1],p[0],p[1],p[2],p[3], (posbits&07));
X			    		fprintf(stderr, "uncompress: corrupt input\n");
X						abort_compress();
X					}
X
X        	    	*--stackp = (char_type)finchar;
X		    		code = oldcode;
X				}
X
X				while ((cmp_code_int)code >= (cmp_code_int)256)
X				{ /* Generate output characters in reverse order */
X			    	*--stackp = tab_suffixof(code);
X			    	code = tab_prefixof(code);
X				}
X
X				*--stackp =	(char_type)(finchar = tab_suffixof(code));
X
X			/* And put them out in forward order */
X
X				{
X					REG1 int	i;
X
X					if (outpos+(i = (de_stack-stackp)) >= OBUFSIZ)
X					{
X						do
X						{
X							if (i > OBUFSIZ-outpos) i = OBUFSIZ-outpos;
X
X							if (i > 0)
X							{
X								memcpy(outbuf+outpos, stackp, i);
X								outpos += i;
X							}
X
X							if (outpos >= OBUFSIZ)
X							{
X								if (write(fdout, outbuf, outpos) != outpos)
X									write_error();
X
X								outpos = 0;
X							}
X							stackp+= i;
X						}
X						while ((i = (de_stack-stackp)) > 0);
X					}
X					else
X					{
X						memcpy(outbuf+outpos, stackp, i);
X						outpos += i;
X					}
X				}
X
X				if ((code = free_ent) < maxmaxcode) /* Generate the new entry. */
X				{
X			    	tab_prefixof(code) = (unsigned short)oldcode;
X			    	tab_suffixof(code) = (char_type)finchar;
X	    			free_ent = code+1;
X				} 
X
X				oldcode = incode;	/* Remember previous code.	*/
X			}
X
X			bytes_in += rsize;
X	    }
X		while (rsize > 0);
X
X		if (outpos > 0 && write(fdout, outbuf, outpos) != outpos)
X			write_error();
X	}
X
Xchar *
Xrindex(s, c)		/* For those who don't have it in libc.a */
X	REG1 char	*s;
X	REG2 int	 c;
X	{
X		char *p;
X
X		for (p = NULL; *s; s++)
X		    if (*s == (char)c)
X				p = s;
X
X		return(p);
X	}
X
Xvoid
Xread_error()
X	{
X		fprintf(stderr, "\nread error on");
X	    perror((ifname[0] != '\0') ? ifname : "stdin");
X		abort_compress();
X	}
X
Xvoid
Xwrite_error()
X	{
X		fprintf(stderr, "\nwrite error on");
X	    perror((ofname[0] != '\0') ? ofname : "stdout");
X		abort_compress();
X	}
X
Xvoid
Xabort_compress()
X	{
X		if (remove_ofname)
X	    	unlink(ofname);
X
X		exit(1);
X	}
X
Xvoid
Xprratio(stream, num, den)
X	FILE		*stream;
X	long int	 num;
X	long int	 den;
X	{
X		REG1 int q;			/* Doesn't need to be long */
X
X		if (den > 0)
X		{
X			if (num > 214748L) 
X				q = (int)(num/(den/10000L));	/* 2147483647/10000 */
X			else
X				q = (int)(10000L*num/den);		/* Long calculations, though */
X		}
X		else
X			q = 10000;
X
X		if (q < 0)
X		{
X			putc('-', stream);
X			q = -q;
X		}
X
X		fprintf(stream, "%d.%02d%%", q / 100, q % 100);
X	}
X
Xvoid
Xabout()
X	{
X		fprintf(stderr, "Compress version: %s, compiled: %s\n", version_id, COMPILE_DATE);
X		fprintf(stderr, "Compile options:\n        ");
X#if BYTEORDER == 4321 && NOALLIGN == 1
X		fprintf(stderr, "USE_BYTEORDER, ");
X#endif
X#ifdef FAST
X		fprintf(stderr, "FAST, ");
X#endif
X#ifdef vax
X		fprintf(stderr, "vax, ");
X#endif
X#ifdef DIRENT
X		fprintf(stderr,"DIRENT, ");
X#endif
X#ifdef SYSDIR
X		fprintf(stderr,"SYSDIR, ");
X#endif
X#ifdef NO_UCHAR
X		fprintf(stderr, "NO_UCHAR, ");
X#endif
X#ifdef SIGNED_COMPARE_SLOW
X		fprintf(stderr, "SIGNED_COMPARE_SLOW, ");
X#endif
X#ifdef MAXSEG_64K
X		fprintf(stderr, "MAXSEG_64K, ");
X#endif
X#ifdef DOS
X		fprintf(stderr, "DOS, ");
X#endif
X#ifdef DEBUG
X		fprintf(stderr, "DEBUG, ");
X#endif
X#ifdef LSTAT
X		fprintf(stderr, "LSTAT, ");
X#endif
X		fprintf(stderr, "\n        REGISTERS=%d IBUFSIZ=%d, OBUFSIZ=%d, BITS=%d\n", 
X			REGISTERS, IBUFSIZ, OBUFSIZ, BITS);
X
X		fprintf(stderr, "\n\
XAuthor version 4.2 (Speed improvement & source cleanup):\n\
X     Peter Jannesen  (peter@ncs.nl)\n\
X\n\
XAuthor version 4.1 (Added recursive directory compress):\n\
X     Dave Mack  (csu@alembic.acs.com)\n\
X\n\
XAuthors version 4.0 (World release in 1985):\n\
X     Spencer W. Thomas, Jim McKie, Steve Davies,\n\
X     Ken Turkowski, James A. Woods, Joe Orost\n");
X
X		exit(0);
X	}
END_OF_FILE
if test 48463 -ne `wc -c <'compress42.c'`; then
    echo shar: \"'compress42.c'\" unpacked with wrong size!
fi
# end of 'compress42.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
        Andrew Patrick acting as Comp.Sources.Reviewed Moderator
              Department of Communications, Ottawa, CANADA
                         csr@calvin.dgbt.doc.CA
Technical Details
Message-ID: <1992Aug28.223709.1638@rick.dgbt.doc.ca>
Path: arc1-ca.pugleaf.net!sparky!uunet!paladin.american.edu!darwin.sura.net!jvnc.net!rutgers!ub!netfs!dgbt!rick.dgbt.doc.ca!calvin.dgbt.doc.ca!csr

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!paladin.american.edu!darwin.sura.net!jvnc.net!rutgers!ub!netfs!dgbt!rick.dgbt.doc.ca!calvin.dgbt.doc.ca!csr
From: peter@ncs.nl (Peter Jannesen)
Newsgroups: comp.sources.reviewed
Subject: v02i026: ncompress - (Ver. 4.2.3) an improved file compressor, Part02/02
Message-ID: <1992Aug28.223709.1638@rick.dgbt.doc.ca>
Date: 28 Aug 92 22:37:09 GMT
Sender: news@rick.dgbt.doc.ca
Organization: Communications Research Centre, Ottawa
Lines: 1963
Approved: csr@calvin.dgbt.doc.ca
Originator: csr@calvin.dgbt.doc.ca
Nntp-Posting-Host: calvin.dgbt.doc.ca