[alt.sources] Freeze/Melt program

apg@hq.demos.su (Paul G. Antonov) (11/25/90)

	 ___________________________________________________
	/ Please send the comments, bug fixes,              \
	| compression/speed improvement patches DIRECTLY to |
	\              <leo@s514.ipmce.su>.                 /
	 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This program is a "compilation" from compress - GNU FSF and
lharc (lzhuf) - H. Yoshizaki & M. Tagawa.
Two minor improvements were made - pipe facility and 20% speedup
by moving the code of GetBit routine into DecodeChar routine.

----------------------- cut here -------------------------
/*
 * Freeze - data freezing program
 * Version 1.0:
 * This program is made from GNU compress.c and Yoshizaki/Tagawa's
 * lzhuf.c. (Thanks to all of them.)
 * The algorithm is modified for using in pipe
 * (added ENDOF symbol in Huffman table).
 * Version 1.1:
 * Check for lack of bytes in frozen file when melting.
 * Put the GetBit routine into DecodeChar for reduce function-
 * call overhead when melting.
 *
 * I think the code is portable, but it is presently working only
 * under SCO XENIX and ix/386 (compiled with GNU C).
 */
char magic_header[] = { "\037\236" };      /* 1F 9E = compress + 1 */

static char ident[] = "@(#) freeze.c 1.1 11/24/90 leo@s514.ipmce.su";

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef MSDOS
#include <stdlib.h>
#endif

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


int exit_stat = 0;

Usage() {
#ifdef DEBUG

# ifdef MSDOS
    fprintf(stderr,"Usage: freeze [-cdDfivV] [file ...]\n");
# else
    fprintf(stderr,"Usage: freeze [-cdDfvV] [file ...]\n");
# endif /* MSDOS */

}
int debug = 0;
#else

# ifdef MSDOS
    fprintf(stderr,"Usage: freeze [-cdfivV] [file ...]\n");
# else
    fprintf(stderr,"Usage: freeze [-cdfvV] [file ...]\n");
# endif /* MSDOS */

}
#endif /* DEBUG */
int fcat_flg = 0;       /* Write output on stdout, suppress messages */
int precious = 1;	/* Don't unlink output file on interrupt */
int quiet = 1;	  /* don't tell me about freezing */

/* Note indicator_threshold is triangle number of Kbytes (see below) */

long int indicator_threshold, indicator_count;

int force = 0;
char ofname [100];

#ifdef MSDOS
# define PATH_SEP '\\'
int image = 2;		/* 1 <=> image (binary) mode; 2 <=> text mode */
#else
# define PATH_SEP '/'
#endif

#ifdef DEBUG
int verbose = 0;
char * pr_char();
long symbols_out = 0, refers_out = 0;
#endif /* DEBUG */
int (*bgnd_flag)();

int do_melt = 0;

/*****************************************************************
 * TAG( main )
 *
 *
 * Usage: freeze [-cdfivV] [file ...]
 * Inputs:
 *
 *	-c:	    Write output on stdout, don't remove original.
 *
 *      -d:	 If given, melting is done instead.
 *
 *	-f:	    Forces output file to be generated, even if one already
 *		  exists, and even if no space is saved by freezeing.
 *		    If -f is not used, the user will be prompted if stdin is
 *		    a tty, otherwise, the output file will not be overwritten.
 *
 *	-i:	    Image mode (defined only under MS-DOS).  Prevents
 *		    conversion between UNIX text representation (LF line
 *		  termination) in frozen form and MS-DOS text
 *		  representation (CR-LF line termination) in melted
 *		    form.  Useful with non-text files.
 *
 *      -v:	 Write freezing statistics
 *
 *	-V:	    Write version and compilation options.
 *
 *      file ...:   Files to be frozen.  If none specified, stdin
 *		    is used.
 * Outputs:
 *      file.F:     Frozen form of file with same mode, owner, and utimes
 *	or stdout   (if stdin used as input)
 *
 * Assumptions:
 *      When filenames are given, replaces with the frozen version
 *      (.F suffix) only if the file decreases in size.
 * Algorithm:
 *      Modified Lempel-Ziv-SS method (LZSS), adaptive Huffman coding
 *      for literal symbols and length info.
 *      Static Huffman coding for position info. (It is optimal ?)
 *      Lower bits of position info are put in output
 *      file without any coding because of their random distribution.
 */

/* From compress.c. Replace .Z --> .F etc */

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

#ifdef MSDOS
    char *sufp;
#else
    extern oops();
#endif

#ifndef MSDOS
#ifdef __GNUC__
    if ( (bgnd_flag = (int (*) ()) signal ( SIGINT, SIG_IGN )) != (int (*) ()) SIG_IGN )
#else
    if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN )
#endif
#endif
    {
	signal ( SIGINT, onintr );
#ifndef MSDOS
	signal ( SIGSEGV, oops );
#endif
    }

    filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
    *filelist = NULL;

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

#ifdef MSDOS
    if(strcmp(cp, "MELT.EXE") == 0) {
#else
    if(strcmp(cp, "melt") == 0) {
#endif

	do_melt = 1;
	
#ifdef MSDOS
    } else if(strcmp(cp, "FCAT.EXE") == 0) {
#else
    } else if(strcmp(cp, "fcat") == 0) {
#endif

	do_melt = 1;
	fcat_flg = 1;
    }

#ifdef BSD4_2
    /* 4.2BSD dependent - take it out if not */
    setlinebuf( stderr );
#endif /* BSD4_2 */

    /* Argument Processing
     * All flags are optional.
     * -D => debug
     * -V => print Version; debug verbose
     * -d => do_melt
     * -v => unquiet
     * -f => force overwrite of output file
     * -c => cat all output to stdout
     * if a string is left, must be an input filename.
     */
    for (argc--, argv++; argc > 0; argc--, argv++) {
	if (**argv == '-') {	/* A flag argument */
	    while (*++(*argv)) {	/* Process all flags in this arg */
		switch (**argv) {
#ifdef DEBUG
		    case 'D':
			debug = 1;
			break;
		    case 'V':
			verbose = 1;
			version();
			break;
#else
		    case 'V':
			version();
			break;
#endif /* DEBUG */

#ifdef MSDOS
		    case 'i':
			image = 1;
			break;
#endif

		    case 'v':
			quiet = 0;
			break;
		    case 'd':
			do_melt = 1;
			break;
		    case 'f':
		    case 'F':
			overwrite = 1;
			force = 1;
			break;
		    case 'c':
			fcat_flg = 1;
			break;
		    case 'q':
			quiet = 1;
			break;
		    default:
			fprintf(stderr, "Unknown flag: '%c'; ", **argv);
			Usage();
			exit(1);
		}
	    }
	}
	else {		/* Input file name */
	    *fileptr++ = *argv; /* Build input file list */
	    *fileptr = NULL;
	}
    }

    if (*filelist != NULL) {
	for (fileptr = filelist; *fileptr; fileptr++) {
	    exit_stat = 0;
	    if (do_melt != 0) {		       /* MELTING */

#ifdef MSDOS
		/* Check for .F or XF suffix; add one if necessary */
		cp = *fileptr + strlen(*fileptr) - 2;
		if ((*cp != '.' && *cp != 'X' && *cp != 'x') ||
		    (*(++cp) != 'F' && *cp != 'f')) {
		    strcpy(tempname, *fileptr);
		    if ((cp=rindex(tempname,'.')) == NULL)
			strcat(tempname, ".F");
		    else if(*(++cp) == '\0') strcat(tempname, "F");
		    else {
			*(++cp) = '\0';
			strcat(tempname, "XF");
		    }
		    *fileptr = tempname;
		}
#else
		/* Check for .F suffix */
		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") != 0) {
		    /* No .F: tack one on */
		    strcpy(tempname, *fileptr);
		    strcat(tempname, ".F");
		    *fileptr = tempname;
		}
#endif /*MSDOS */

		/* Open input file for melting */

#ifdef MSDOS
		if ((freopen(*fileptr, "rb", stdin)) == NULL)
#else
		if ((freopen(*fileptr, "r", stdin)) == NULL)
#endif
		{
			perror(*fileptr); continue;
		}
		/* Check the magic number */
		if ((getchar() != (magic_header[0] & 0xFF))
		 || (getchar() != (magic_header[1] & 0xFF))) {
		    fprintf(stderr, "%s: not in frozen format\n",
			*fileptr);
		continue;
		}
		/* Generate output filename */
		strcpy(ofname, *fileptr);
		ofname[strlen(*fileptr) - 2] = '\0';  /* Strip off .F */
	    } else {				    /* FREEZING */

#ifdef MSDOS
		cp = *fileptr + strlen(*fileptr) - 2;
		if ((*cp == '.' || *cp == 'X' || *cp == 'x') &&
		    (*(++cp) == 'F' || *cp == 'f')) {
		    fprintf(stderr,"%s: already has %s suffix -- no change\n",
			*fileptr,--cp); /* } */
#else
		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") == 0) {
		    fprintf(stderr, "%s: already has .F suffix -- no change\n",
			*fileptr);
#endif /* MSDOS */

		    continue;
		}
		/* Open input file for freezing */

#ifdef MSDOS
		if ((freopen(*fileptr, image == 2 ? "rt" : "rb", stdin))
		    == NULL)
#else
		if ((freopen(*fileptr, "r", stdin)) == NULL)
#endif
		{
		    perror(*fileptr); continue;
		}
		stat ( *fileptr, &statbuf );

		/* Generate output filename */
		strcpy(ofname, *fileptr);
#ifndef BSD4_2		/* Short filenames */
		if ((cp = rindex(ofname, PATH_SEP)) != NULL) cp++;
		else					cp = ofname;
# ifdef MSDOS
		if (fcat_flg == 0 && (sufp = rindex(cp, '.')) != NULL &&
		    strlen(sufp) > 2) fprintf(stderr,
		    "%s: part of filename extension will be replaced by XF\n",
		    cp);
# else
		if (strlen(cp) > 12) {
		    fprintf(stderr,"%s: filename too long to tack on .F\n",cp);
		    continue;
		}
# endif
#endif	/* BSD4_2		Long filenames allowed */
							 
#ifdef MSDOS
		if ((cp = rindex(ofname, '.')) == NULL) strcat(ofname, ".F");
		else {
		   if(*(++cp) != '\0') *(++cp) = '\0';
		   strcat(ofname, "XF");
		}
#else
		strcat(ofname, ".F");
#endif /* MSDOS */

	    }
	    precious = 0;
	    /* Check for overwrite of existing file */
	    if (overwrite == 0 && fcat_flg == 0) {
		if (stat(ofname, &statbuf) == 0) {
		    char response[2];
		    response[0] = 'n';
		    fprintf(stderr, "%s already exists;", ofname);
#ifndef MSDOS
		    if (foreground()) {
#endif
			fprintf(stderr,
			    " do you wish to overwrite %s (y or n)? ", ofname);
			fflush(stderr);
			read(2, response, 2);
			while (response[1] != '\n') {
			    if (read(2, response+1, 1) < 0) {	/* Ack! */
				perror("stderr"); break;
			    }
			}
#ifndef MSDOS
		    }
#endif
		    if (response[0] != 'y') {
			fprintf(stderr, "\tnot overwritten\n");
			continue;
		    }
		}
	    }
	    if(fcat_flg == 0) {	 /* Open output file */

#ifdef DEBUG
		if (do_melt == 0 || debug == 0) {
#endif
#ifdef MSDOS
		if (freopen(ofname, do_melt && image == 2 ? "wt" : "wb",
		    stdout) == NULL)
#else		 
		if (freopen(ofname, "w", stdout) == NULL)
#endif
		{
		    perror(ofname); continue;
		}
#ifdef DEBUG
		}
#endif
		if(!quiet) {
			fprintf(stderr, "%s:", *fileptr);
			indicator_threshold = 2048;
			indicator_count = 1024;
		}
	    }
	    /* Actually do the freezing/melting */
	    if (do_melt == 0) freeze();
#ifndef DEBUG
	    else			melt();
#else
	    else if (debug == 0)	melt();
	    else			printcodes();
#endif /* DEBUG */
	    if(fcat_flg == 0) {
		copystat(*fileptr, ofname);	/* Copy stats */
		if((exit_stat == 1) || (!quiet))
			putc('\n', stderr);
	    }
	 }
    } else {		/* Standard input */
	if (do_melt == 0) {
		freeze();
		if(!quiet)
			putc('\n', stderr);
	} else {
	    /* Check the magic number */
	    if ((getchar()!=(magic_header[0] & 0xFF))
	     || (getchar()!=(magic_header[1] & 0xFF))) {
		fprintf(stderr, "stdin: not in frozen format\n");
		exit(1);
	    }
#ifndef DEBUG
	    melt();
#else
	    if (debug == 0)     melt();
	    else		printcodes();
#endif /* DEBUG */
	}
    }
    exit(exit_stat);
}

long int in_count = 1;			/* length of input */
long int bytes_out;		     /* length of frozen output */

/*----------------------------------------------------------------------*/
/*									*/
/*              LZSS ENCODING   (lzhuf.c)                               */
/*									*/
/*----------------------------------------------------------------------*/

#ifdef  lint
#define N               256
#else
#define N               4096    /* buffer size */
#endif

#define F		60	/* pre-sence buffer size */
#define THRESHOLD	2
#define NIL		N	/* term of tree */
/*
 * Increasing of N and F ( N = 8192, F = 80) gives 1.2 - 1.5% of additional
 * reducing, but 2 times slower.
 */
unsigned char    text_buf[N + F - 1];
unsigned int     match_position, match_length;
short            lson[N + 1], rson[N + 1 + N], dad[N + 1];
unsigned char    same[N + 1];


/* Initialize Tree */
InitTree ()
{
	register short *p, *e;

	for (p = rson + N + 1, e = rson + N + N; p <= e; )
		*p++ = NIL;
	for (p = dad, e = dad + N; p < e; )
		*p++ = NIL;
}


/* Insert to node */
InsertNode (r)
	register int r;
{
	register int		p;
	int			cmp;
	register unsigned char	*key;
	register unsigned int	c;
	register unsigned int	i, j;

	cmp = 1;
	key = &text_buf[r];
	i = key[1] ^ key[2];
	i ^= i >> 4;
	p = N + 1 + key[0] + ((i & 0x0f) << 8);
	rson[r] = lson[r] = NIL;
	match_length = 0;
	i = j = 1;
	for ( ; ; ) {
		if (cmp >= 0) {
			if (rson[p] != NIL) {
				p = rson[p];
				j = same[p];
			} else {
				rson[p] = r;
				dad[r] = p;
				same[r] = i;
				return;
			}
		} else {
			if (lson[p] != NIL) {
				p = lson[p];
				j = same[p];
			} else {
				lson[p] = r;
				dad[r] = p;
				same[r] = i;
				return;
			}
		}

		if (i > j) {
			i = j;
			cmp = key[i] - text_buf[p + i];
		} else
		if (i == j) {
			for (; i < F; i++)
				if ((cmp = key[i] - text_buf[p + i]) != 0)
					break;
		}

		if (i > THRESHOLD) {
			if (i > match_length) {
				match_position = ((r - p) & (N - 1)) - 1;
				if ((match_length = i) >= F)
					break;
			} else
			if (i == match_length) {
				if ((c = ((r - p) & (N - 1)) - 1) < match_position) {
					match_position = c;
				}
			}
		}
	}
	same[r] = same[p];
	dad[r] = dad[p];
	lson[r] = lson[p];
	rson[r] = rson[p];
	dad[lson[p]] = r;
	dad[rson[p]] = r;
	if (rson[dad[p]] == p)
		rson[dad[p]] = r;
	else
		lson[dad[p]] = r;
	dad[p] = NIL;  /* remove p */
}

static
void
link (n, p, q)
	int n, p, q;
{
	register unsigned char *s1, *s2, *s3;
	if (p >= NIL) {
		same[q] = 1;
		return;
	}
	s1 = text_buf + p + n;
	s2 = text_buf + q + n;
	s3 = text_buf + p + F;
	while (s1 < s3) {
		if (*s1++ != *s2++) {
#ifdef __GNUC__         /* Not an ideal compiler !! */
			n = s1 - text_buf;
			same[q] = n - 1 - p;
#else
			same[q] = s1 - 1 - text_buf - p;
#endif
			return;
		}
	}
	same[q] = F;
}


linknode (p, q, r)
	int p, q, r;
{
	int cmp;

	if ((cmp = same[q] - same[r]) == 0) {
		link((int)same[q], p, r);
	} else if (cmp < 0) {
		same[r] = same[q];
	}
}

DeleteNode (p)
	register int p;
{
	register int  q;

	if (dad[p] == NIL)
		return;			/* has no linked */
	if (rson[p] == NIL) {
		if ((q = lson[p]) != NIL)
			linknode(dad[p], p, q);
	} else
	if (lson[p] == NIL) {
		q = rson[p];
		linknode(dad[p], p, q);
	} else {
		q = lson[p];
		if (rson[q] != NIL) {
			do {
				q = rson[q];
			} while (rson[q] != NIL);
			if (lson[q] != NIL)
				linknode(dad[q], q, lson[q]);
			link(1, q, lson[p]);
			rson[dad[q]] = lson[q];
			dad[lson[q]] = dad[q];
			lson[q] = lson[p];
			dad[lson[p]] = q;
		}
		link(1, dad[p], q);
		link(1, q, rson[p]);
		rson[q] = rson[p];
		dad[rson[p]] = q;
	}
	dad[q] = dad[p];
	if (rson[dad[p]] == p)
		rson[dad[p]] = q;
	else
		lson[dad[p]] = q;
	dad[p] = NIL;
}

/*----------------------------------------------------------------------*/
/*									*/
/*		HUFFMAN ENCODING					*/
/*									*/
/*----------------------------------------------------------------------*/

#define N_CHAR          (256 - THRESHOLD + F + 1) /* {code : 0 .. N_CHAR-1} */
#define T 		(N_CHAR * 2 - 1)	/* size of table */
#define R 		(T - 1)			/* root position */

#define MAX_FREQ        (unsigned)0x8000 /* tree update timing from frequency */

#define ENDOF           256

typedef unsigned char uchar;

/* TABLE OF ENCODE/DECODE for upper 6bits position information */

/* for encode */
uchar p_len[64] = {
	0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};

uchar p_code[64] = {
	0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,
	0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,
	0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,
	0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
	0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,
	0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

/* for decode */
uchar d_code[256] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
	0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
	0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
	0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
	0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
	0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
	0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
	0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
	0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
	0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
	0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
	0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
	0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
	0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
	0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};

uchar d_len[256] = {
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};

unsigned short freq[T + 1];    /* frequency table */

short    prnt[T + N_CHAR];    /* points to parent node */
/* notes :
   prnt[T .. T + N_CHAR - 1] used by
   indicates leaf position that corresponding to code */

short son[T];           /* points to son node (son[i],son[i+]) */

unsigned short getbuf = 0;
uchar    getlen = 0;

uchar corrupt_flag = 0;         /* If a file is corrupt, use fcat */

/* get one byte */
/* returning in Bit7...0 */
int GetByte ()
{
	register unsigned short int dx = getbuf;
	register unsigned int c;

	if (getlen <= 8) {
		c = getchar ();
		if ((int)c < 0) {
/* Frozen file is too short. This is fatal error.
 * Really the second absent byte indicates a error.
 * ("Packed file is corrupt." :-) )
 */
		    if (corrupt_flag)
			oops();
		    corrupt_flag = 1;
		    c = 0;
		}
		dx |= c << (8 - getlen);
		getlen += 8;
	}
	getbuf = dx << 8;
	getlen -= 8;
	return (dx >> 8) & 0xff;
}

/* get N bit */
/* returning in Bit(N-1)...Bit 0 */
int GetNBits (n)
	register unsigned int n;
{
	register unsigned int dx = getbuf;
	register unsigned int c;
	static int mask[17] = {
		0x0000,
		0x0001, 0x0003, 0x0007, 0x000f,
		0x001f, 0x003f, 0x007f, 0x00ff,
		0x01ff, 0x03ff, 0x07ff, 0x0fff,
		0x1fff, 0x3fff, 0x7fff, 0xffff };
	static int shift[17] = {
		16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };

	if (getlen <= 8)
		{
			c = getchar ();
			if ((int)c < 0) {
			    if (corrupt_flag)
				oops();
			    corrupt_flag = 1;
			    c = 0;
			}
			dx |= c << (8 - getlen);
			getlen += 8;
		}
	getbuf = dx << n;
	getlen -= n;
	return (dx >> shift[n]) & mask[n];
}

unsigned short putbuf = 0;
uchar putlen = 0;

/* output C bits */
Putcode (l, c)
	register int l;
	unsigned int c;
{
	register short len = putlen;
	register unsigned short b = putbuf;
	b |= c >> len;
	if ((len += l) >= 8) {
		if (putchar (b >> 8) == EOF) writeerr();
		if ((len -= 8) >= 8) {
			putchar (b);
			bytes_out += 2;
			len -= 8;
			b = c << (l - len);
		} else {
			b <<= 8;
			bytes_out++;
		}
	}
	putbuf = b;
	putlen = len;
}


/* Initialize tree */

StartHuff ()
{
	register int i, j;

	for (i = 0; i < N_CHAR; i++) {
		freq[i] = 1;
		son[i] = i + T;
		prnt[i + T] = i;
	}
	i = 0; j = N_CHAR;
	while (j <= R) {
		freq[j] = freq[i] + freq[i + 1];
		son[j] = i;
		prnt[i] = prnt[i + 1] = j;
		i += 2; j++;
	}
	freq[T] = 0xffff;
	prnt[R] = 0;
	in_count = 1;
	bytes_out = 2;
#ifdef DEBUG
	symbols_out = refers_out = 0;
#endif
	putlen = getlen = 0;
	putbuf = getbuf = 0;
	corrupt_flag = 0;
}


/* reconstruct tree */
reconst ()
{
	register int i, j, k;
	register unsigned f;
#ifdef DEBUG
	if (!quiet)
	  fprintf(stderr,
	    "Reconstructing Huffman tree: symbols: %ld, references: %ld\n",
	    symbols_out, refers_out);
#endif
	/* correct leaf node into of first half,
	   and set these freqency to (freq+1)/2       */
	j = 0;
	for (i = 0; i < T; i++) {
		if (son[i] >= T) {
			freq[j] = (freq[i] + 1) / 2;
			son[j] = son[i];
			j++;
		}
	}
	/* build tree.  Link sons first */
	for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
		k = i + 1;
		f = freq[j] = freq[i] + freq[k];
		for (k = j - 1; f < freq[k]; k--);
		k++;
		{       register unsigned short *p, *e;
			for (p = &freq[j], e = &freq[k]; p > e; p--)
				p[0] = p[-1];
			freq[k] = f;
		}
		{       register short *p, *e;
			for (p = &son[j], e = &son[k]; p > e; p--)
				p[0] = p[-1];
			son[k] = i;
		}
	}
	/* link parents */
	for (i = 0; i < T; i++) {
		if ((k = son[i]) >= T) {
			prnt[k] = i;
		} else {
			prnt[k] = prnt[k + 1] = i;
		}
	}
}


/* update given code's frequency, and update tree */

update (c)
	unsigned int	c;
{
	register unsigned short *p;
	register int i, j, k, l;

	if (freq[R] == MAX_FREQ) {
		reconst();
	}
	c = prnt[c + T];
	do {
		k = ++freq[c];

		/* swap nodes when become wrong frequency order. */
		if (k > freq[l = c + 1]) {
			for (p = freq+l+1; k > *p++; ) ;
			l = p - freq - 2;
			freq[c] = p[-2];
			p[-2] = k;

			i = son[c];
			prnt[i] = l;
			if (i < T) prnt[i + 1] = l;

			j = son[l];
			son[l] = i;

			prnt[j] = c;
			if (j < T) prnt[j + 1] = c;
			son[c] = j;

			c = l;
		}
	} while ((c = prnt[c]) != 0);	/* loop until reach to root */
}

/* unsigned code, len; */

EncodeChar (c)
	unsigned c;
{
	register short *p;
	unsigned long i;
	register int j, k;

	i = 0;
	j = 0;
	p = prnt;
	k = p[c + T];

	/* trace links from leaf node to root */
	do {
		i >>= 1;

		/* if node index is odd, trace larger of sons */
		if (k & 1) i += 0x80000000;

		j++;
	} while ((k = p[k]) != R) ;
	if (j > 16) {
		Putcode(16, (unsigned int)(i >> 16));
		Putcode(j - 16, (unsigned int)i);
	} else {
		Putcode(j, (unsigned int)(i >> 16));
	}
/*	code = i; */
/* 	len = j; */
	update(c);
}

EncodePosition (c)
	register unsigned c;
{
	register unsigned i;

	/* output upper 6bit from table */
	i = c >> 6;
	Putcode((int)(p_len[i]), (unsigned int)(p_code[i]) << 8);

	/* output lower 6 bit */
	Putcode(6, (unsigned int)(c & 0x3f) << 10);
}

EncodeEnd ()
{
	if (putlen) {
		putchar(putbuf >> 8);
		bytes_out++;
	}
}

int DecodeChar ()
{
	register unsigned c;
	register unsigned short dx;
	register unsigned short cc;
	c = son[R];

	/* trace from root to leaf,
	   got bit is 0 to small(son[]), 1 to large (son[]+1) son node */
	while (c < T) {
		dx = getbuf;
		if (getlen <= 8) {
		      if ((short)(cc = getchar()) < 0) {
			      if (corrupt_flag)
				      oops();
			      corrupt_flag = 1;
			      cc = 0;
		      }
			dx |= cc << (8 - getlen);
			getlen += 8;
		}
		getbuf = dx << 1;
		getlen--;
		c += (dx >> 15) & 1;
		c = son[c];
	}
	c -= T;
	update(c);
	return c;
}

int DecodePosition ()
{
	register unsigned short i, j, c;

	/* decode upper 6bit from table */
	i = GetByte();
	c = (unsigned)d_code[i] << 6;
	j = d_len[i];

	/* get lower 6bit */
	j -= 2;
	return c | (((i << j) | GetNBits (j)) & 0x3f);
}

/*
 * freeze stdin to stdout (as in compress.c & lzhuf.c)
 */

freeze ()
{
	register int  i, c, len, r, s, last_match_length;
	putchar(magic_header[0]);
	putchar(magic_header[1]);
	StartHuff();
	InitTree();
	s = 0;
	r = N - F;
	for (i = s; i < r; i++)
		text_buf[i] = ' ';
	for (len = 0; len < F && (c = getchar()) != EOF; len++)
		text_buf[r + len] = c;
	in_count = len;
	for (i = 1; i <= F; i++)
		InsertNode(r - i);
	InsertNode(r);
	while (len > 0) {
		if (match_length > len)
			match_length = len;
		if (match_length <= THRESHOLD) {
			match_length = 1;
			EncodeChar(text_buf[r]);
#ifdef DEBUG
			symbols_out ++;
			if (verbose)
				fprintf(stderr, "'%s'\n", pr_char(text_buf[r]));
#endif /* DEBUG */
		} else {
			EncodeChar(256 - THRESHOLD + match_length);
			EncodePosition(match_position);
#ifdef DEBUG
			refers_out ++;
			if (verbose) {
				register int pos = (r - 1 - match_position) & (N - 1),
				len = match_length;
				fputc('"', stderr);
				for(;len;len--, pos++)
					fprintf(stderr, "%s", pr_char(text_buf[pos]));
				fprintf(stderr, "\"\n");
			}
#endif /* DEBUG */
		}
		last_match_length = match_length;
		for (i = 0; i < last_match_length &&
				(c = getchar()) != EOF; i++) {
			DeleteNode(s);
			text_buf[s] = c;
			if (s < F - 1)
				text_buf[s + N] = c;
			s = (s + 1) & (N - 1);
			r = (r + 1) & (N - 1);
			InsertNode(r);
		}

		in_count += i;
		if ((in_count > indicator_count) && !quiet) {
			fprintf(stderr, "%5dK\b\b\b\b\b\b", in_count / 1024);
			fflush (stderr);
			indicator_count += indicator_threshold;
			indicator_threshold += 1024;
		}
		while (i++ < last_match_length) {
			DeleteNode(s);
			s = (s + 1) & (N - 1);
			r = (r + 1) & (N - 1);
			if (--len) InsertNode(r);
		}
	}
	EncodeChar(ENDOF);
#ifdef DEBUG
	symbols_out ++;
#endif
	EncodeEnd();
    /*
     * Print out stats on stderr
     */
    if(!quiet) {
#ifdef DEBUG
	fprintf( stderr,
		"%ld chars in, %ld codes (%ld bytes) out, freezing factor: ",
		in_count, symbols_out + refers_out, bytes_out);
	prratio( stderr, in_count, bytes_out );
	fprintf( stderr, "\n");
	fprintf( stderr, "\tFreezing as in compact: " );
	prratio( stderr, in_count-bytes_out, in_count );
	fprintf( stderr, "\n");
	fprintf( stderr, "\tSymbols: %ld; references: %ld.\n",
		symbols_out, refers_out);
#else /* !DEBUG */
	fprintf( stderr, "Freezing: " );
	prratio( stderr, in_count-bytes_out, in_count );
#endif /* DEBUG */
    }
    if(bytes_out > in_count)	/* exit(2) if no savings */
	exit_stat = 2;
    return;
}

/*
 * Melt stdin to stdout.
 * Works =~= 1.5 times faster than uncompress (relatively to decompressed
 * filesize) - on 16-bit machines.
 * !! DO NOT (!!) use arithmetic compression - it is extremely
 * slow when decompressing.
 */

melt ()
{
	register int	i, j, k, r, c;
	StartHuff();
	for (i = 0; i < N - F; i++)
		text_buf[i] = ' ';
	r = N - F;
	for (in_count = 0;; ) {
		c = DecodeChar();
		if (c == ENDOF)
			break;
		if (c < 256) {
			putchar (c);
			text_buf[r++] = c;
			r &= (N - 1);
			in_count++;
		} else {
			i = (r - DecodePosition() - 1) & (N - 1);
			j = c - 256 + THRESHOLD;
			for (k = 0; k < j; k++) {
				c = text_buf[(i + k) & (N - 1)];
				putchar (c);
				text_buf[r++] = c;
				r &= (N - 1);
				in_count++;
			}
		}

		if (!quiet && (in_count > indicator_count)) {
			fprintf(stderr, "%5dK\b\b\b\b\b\b", in_count / 1024);
			fflush (stderr);
			indicator_count += indicator_threshold;
			indicator_threshold += 1024;
		}
	}
}

char *
rindex(s, c)		/* For those who don't have it in libc.a */
register char *s, c;
{
	char *p;
	for (p = NULL; *s; s++)
	    if (*s == c)
		p = s;
	return(p);
}

#ifdef DEBUG
printcodes()
{
    /*
     * Just print out codes from input file.  For debugging.
     */
    register int    k, c, col = 0;
    StartHuff();
    for (;;) {
	    c = DecodeChar();
	    if (c == ENDOF)
		    break;
	    if (c < 256) {
		fprintf(stderr, "%5d%c", c, (col+=8) >= 74 ? (col = 0, '\n') : '\t' );
	    } else {
		c = c - 256 + THRESHOLD;
		k = DecodePosition();
		fprintf(stderr, "%2d-%d%c", c, k, (col+=8) >= 74 ? (col = 0, '\n') : '\t' );
	    }
    }
    putc( '\n', stderr );
    exit( 0 );
}

/* for pretty char printing */

char *
pr_char(c)
	register unsigned char c;
{
	static char buf[5];
	register i = 4;
	buf[4] = '\0';
	if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
	    buf[--i] = c;
	} else {
	    switch( c ) {
	    case '\n': buf[--i] = 'n'; break;
	    case '\t': buf[--i] = 't'; break;
	    case '\b': buf[--i] = 'b'; break;
	    case '\f': buf[--i] = 'f'; break;
	    case '\r': buf[--i] = 'r'; break;
	    case '\\': buf[--i] = '\\'; break;
	    default:
		buf[--i] = '0' + c % 8;
		buf[--i] = '0' + (c / 8) % 8;
		buf[--i] = '0' + c / 64;
		break;
	    }
	    buf[--i] = '\\';
	}
	return &buf[i];
}
#endif /* DEBUG */

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

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

#ifdef MSDOS
    if (_osmajor < 3) freopen("CON","at",stdout); else	  /* MS-DOS 2.xx bug */
#endif

    fclose(stdout);
    if (stat(ifname, &statbuf)) {		/* Get stat on input file */
	perror(ifname);
	return;
    }

#ifndef MSDOS
    if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
	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 = 1;
    } else if (exit_stat == 2 && (!force)) { /* No freezing: remove file.Z */
#else
    if (exit_stat == 2 && (!force)) { /* No freezing: remove file.Z */
#endif /* MSDOS */

	if(!quiet)
		fprintf(stderr, " file unchanged");
    } else {		    /* ***** Successful Freezing ***** */
	exit_stat = 0;
	mode = statbuf.st_mode & 07777;
	if (chmod(ofname, mode))		/* Copy modes */
	    perror(ofname);

#ifndef MSDOS
	chown(ofname, statbuf.st_uid, statbuf.st_gid);	/* Copy ownership */
#endif

	timep[0] = statbuf.st_atime;
	timep[1] = statbuf.st_mtime;
	utime(ofname, timep);	/* Update last accessed and modified times */
	precious = 1;
	if (unlink(ifname))	/* Remove input file */
	    perror(ifname);
	if(!quiet)
		fprintf(stderr, " -- replaced with %s", ofname);
	return;		/* Successful return */
    }

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

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

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

oops ( )        /* wild index -- assume bad input (or file too short) */
{
    if ( do_melt == 1 )
	fprintf ( stderr, "melt: corrupt input\n" );
    if ( fcat_flg == 1)
	fflush(stdout); /* Something is better than nothing */
    else
	unlink ( ofname );
    exit ( 1 );
}


prratio(stream, num, den)
FILE *stream;
long int num, den;
{

#ifdef DEBUG
	register long q;		/* permits |result| > 655.36% */
#else
	register int q;			/* Doesn't need to be long */
#endif
	if (!den) den++;

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

version()
{
	fprintf(stderr, "%s\n", ident);
	fprintf(stderr, "Options: ");
#ifdef DEBUG
	fprintf(stderr, "DEBUG, ");
#endif
#ifdef BSD4_2
	fprintf(stderr, "BSD4_2, ");
#endif
#ifdef  M_XENIX
	fprintf(stderr, "XENIX, ");
#endif
	fprintf(stderr, "LZSS: %d (table), %d (match length);\n", N, F);
	fprintf(stderr, "HUFFMAN: %ld (max frequency)\n", (long)MAX_FREQ);
}
----------------------- cut here -------------------------

teexdwu@ioe.lon.ac.uk (DOMINIK WUJASTYK) (11/26/90)

Um ... when water freezes, it expands.

Dominik

jms@cs.vu.nl (Jan-Mark) (11/28/90)

In article ...  teexdwu@ioe.lon.ac.uk (DOMINIK WUJASTYK) writes:
> Um ... when water freezes, it expands.
> 
> Dominik

	Not if it was 120 C ;-)

Jan-Mark

--

				 (:>	jms
				(_)
			========""======