/* This STANDALONE variable doesn't work to build a Standalone LZHUF executable. */
#undef  STANDALONE
/*
 ************************************************************
 lzhuf.c
 written by Haruyasu Yoshizaki 11/20/1988
 some minor changes 4/6/1989
 comments translated by Haruhiko Okumura 4/7/1989
 ************************************************************
 */
#ifndef MSDOS
#include "ctype.h"
#endif
#ifndef STANDALONE
#include "global.h"
#include <time.h>
#include "forward.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LZHUF 1
#define mallocw malloc
#endif

#if defined(LZHUF) || defined(FBBCMP)
#ifndef MSDOS
#include "session.h"
#endif

#undef  DEBUG
#undef  DEBUG2

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: lzhuf.c,v 1.17 2001/05/06 16:32:56 brian Exp $";
#endif

#if defined(CATALOG) && !defined(STANDALONE)
#include "catalog.h"

#define CAT lzhuf_catalog

#define ErrorEncode	__STR(0)
#define lostremote	__STR(1)
#define ErrorDecode	__STR(2)
#define Erroropening	__STR(3)
#define ENCODEratio	__STR(4)
#define DECODEratio	__STR(5)

#else /* CATALOG */
static const char ErrorEncode[] = "FWDCMP: Error from Encode() rc = %d";
static const char lostremote[] = "FBBFWD: Lost the remote connection in recv_yapp()";
static const char ErrorDecode[] = "FWDCMP: Error from Decode() rc = %d";
static const char Erroropening[] = "FBBFWD: Error opening %s in %s\n";
static const char ENCODEratio[] = "FWDCMP: ENCODE:  In: %-8ld  Out: %-8ld (%ld%%)\n";
static const char DECODEratio[] = "FWDCMP: DECODE:  In: %-8ld  Out: %-8ld (%ld%%)\n";

#endif /* CATALOG */


static const char Encodestr[] = "Encode()";
static const char Decodestr[] = "Decode()";

#ifdef STANDALONE
#undef free
#undef malloc
#define tcmdprintf printf
int FBBtrace = 1;
#endif



static void 
AllocDataBuffers (struct fwd *f)
{
	f->lzhuf = mallocw (sizeof (struct lzhufstruct));
#if 0
	f->tmpBuffer = mallocw (260);
#endif

	f->lzhuf->data_type = 0;		/* 0 means not allocated yet. */
	f->lzhuf->data = mallocw (sizeof (struct lzhufdata));

	if (f->lzhuf->data != NULLLZHUFDATA)
		f->lzhuf->data_type = 2;	/* 2 means big buffer + lower memory. */

	if (f->lzhuf->data_type == 0) {
		f->lzhuf->data_type = 1;	/* 1 means small buffers + lower memory. */
		f->lzhuf->dad = mallocw ((N + 1) * sizeof (int));
		f->lzhuf->lson = mallocw ((N + 1) * sizeof (int));
		f->lzhuf->rson = mallocw ((N + 257) * sizeof (int));
		f->lzhuf->text_buf = mallocw ((N + F - 1) * sizeof (unsigned char));
		f->lzhuf->freq = mallocw ((T + 1) * sizeof (unsigned));
		f->lzhuf->prnt = mallocw ((T + N_CHAR) * sizeof (int));
		f->lzhuf->son = mallocw ((T) * sizeof (int));
	} else {
		/* point pointers to correct spot in large buffer. */
		f->lzhuf->dad = f->lzhuf->data->dad;
		f->lzhuf->rson = f->lzhuf->data->rson;
		f->lzhuf->lson = f->lzhuf->data->lson;
		f->lzhuf->text_buf = f->lzhuf->data->text_buf;
		f->lzhuf->freq = f->lzhuf->data->freq;
		f->lzhuf->prnt = f->lzhuf->data->prnt;
		f->lzhuf->son = f->lzhuf->data->son;
	}
	f->lzhuf->codesize = 0;
	f->lzhuf->getbuf = 0;
	f->lzhuf->getlen = 0;
	f->lzhuf->putbuf = 0;
	f->lzhuf->putlen = 0;
	f->lzhuf->code = 0;
	f->lzhuf->len = 0;
}



static void 
FreeDataBuffers (struct fwd *f)
{
#if 0
	free (f->tmpBuffer);
#endif

	if (f->lzhuf->data_type == 1) {
		/* Free lower memory blocks. */
		free (f->lzhuf->dad);
		free (f->lzhuf->lson);
		free (f->lzhuf->rson);
		free (f->lzhuf->text_buf);
		free (f->lzhuf->freq);
		free (f->lzhuf->prnt);
		free (f->lzhuf->son);
	} else if (f->lzhuf->data_type == 2) {
		/* Free lower memory block. */
		free (f->lzhuf->data);
	}
	free (f->lzhuf);
	f->lzhuf = NULLLZHUFSTRUCT;
}



static int Encode (int, char *, char *, struct lzhufstruct *, int trace);
static int Decode (int, char *, char *, struct lzhufstruct *, int addex, int trace);
static int GetBit (struct lzhufstruct *);
static unsigned short GetByte (struct lzhufstruct *);
static void Putcode (struct lzhufstruct *, int, unsigned);
static void EncodeEnd (struct lzhufstruct *);
static int DecodeChar (struct lzhufstruct *);


static void InitTree (struct lzhufstruct *);
static void InsertNode (struct lzhufstruct *, int);
static void StartHuff (struct lzhufstruct *);
static void reconst (struct lzhufstruct *);
static void lzhuf_update (struct lzhufstruct *, int);
static void EncodeChar (struct lzhufstruct *, unsigned);
static void EncodePosition (struct lzhufstruct *, unsigned);
static int DecodePosition (struct lzhufstruct *);
static void DeleteNode (struct lzhufstruct *, int);
static int recvbuf (int, char *, unsigned);

/********** LZSS compression **********/



static void 
InitTree (struct lzhufstruct *lzhuf)
{			/* initialize trees */
int i;

	for (i = N + 1; i <= N + 256; i++)
		lzhuf->rson[i] = NIL;	/* root */
	for (i = 0; i < N; i++)
		lzhuf->dad[i] = NIL;	/* node */
}



static void 
InsertNode (struct lzhufstruct *lzhuf, int r)
{			/* insert to tree */
int i, p, cmp;
unsigned char *key;
unsigned c;

	cmp = 1;
	key = &lzhuf->text_buf[r];
	p = N + 1 + key[0];
	lzhuf->rson[r] = lzhuf->lson[r] = NIL;
	lzhuf->match_length = 0;
	for (;;) {
		if (cmp >= 0) {
			if (lzhuf->rson[p] != NIL)
				p = lzhuf->rson[p];
			else {
				lzhuf->rson[p] = r;
				lzhuf->dad[r] = p;
				return;
			}
		} else {
			if (lzhuf->lson[p] != NIL)
				p = lzhuf->lson[p];
			else {
				lzhuf->lson[p] = r;
				lzhuf->dad[r] = p;
				return;
			}
		}
		for (i = 1; i < F; i++)
			if ((cmp = key[i] - lzhuf->text_buf[p + i]) != 0)
				break;
		if (i > THRESHOLD) {
			if (i > lzhuf->match_length) {
				lzhuf->match_position = ((r - p) & (N - 1)) - 1;
				if ((lzhuf->match_length = i) >= F)
					break;
			}
			if (i == lzhuf->match_length) {
				if ((c = ((r - p) & (N - 1)) - 1) < (unsigned) lzhuf->match_position)
					lzhuf->match_position = (int) c;
			}
		}
	}
	lzhuf->dad[r] = lzhuf->dad[p];
	lzhuf->lson[r] = lzhuf->lson[p];
	lzhuf->rson[r] = lzhuf->rson[p];
	lzhuf->dad[lzhuf->lson[p]] = r;
	lzhuf->dad[lzhuf->rson[p]] = r;
	if (lzhuf->rson[lzhuf->dad[p]] == p)
		lzhuf->rson[lzhuf->dad[p]] = r;
	else
		lzhuf->lson[lzhuf->dad[p]] = r;
	lzhuf->dad[p] = NIL;	/* remove p */
}



static void 
DeleteNode (struct lzhufstruct *lzhuf, int p)
{			/* remove from tree */
int q;

	if (lzhuf->dad[p] == NIL)
		return;		/* not registered */
	if (lzhuf->rson[p] == NIL)
		q = lzhuf->lson[p];
	else if (lzhuf->lson[p] == NIL)
		q = lzhuf->rson[p];
	else {
		q = lzhuf->lson[p];
		if (lzhuf->rson[q] != NIL) {
			do {
				q = lzhuf->rson[q];
			} while (lzhuf->rson[q] != NIL);
			lzhuf->rson[lzhuf->dad[q]] = lzhuf->lson[q];
			lzhuf->dad[lzhuf->lson[q]] = lzhuf->dad[q];
			lzhuf->lson[q] = lzhuf->lson[p];
			lzhuf->dad[lzhuf->lson[p]] = q;
		}
		lzhuf->rson[q] = lzhuf->rson[p];
		lzhuf->dad[lzhuf->rson[p]] = q;
	}
	lzhuf->dad[q] = lzhuf->dad[p];
	if (lzhuf->rson[lzhuf->dad[p]] == p)
		lzhuf->rson[lzhuf->dad[p]] = q;
	else
		lzhuf->lson[lzhuf->dad[p]] = q;
	lzhuf->dad[p] = NIL;
}



/* Huffman coding */

/* table for encoding and decoding the upper 6 bits of position */

/* for encoding */
#define MAX_P_LEN 64
static uint8 p_len[MAX_P_LEN] =
{
	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
};

static uint8 p_code[MAX_P_LEN] =
{
	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 decoding */
#define MAX_D_LEN 256
static uint8 d_code[MAX_D_LEN] =
{
	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,
};

static uint8 d_len[MAX_D_LEN] =
{
	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,
};



static int 
GetBit (struct lzhufstruct *lzhuf)
{			/* get one bit */
register unsigned i;
register unsigned dx = lzhuf->getbuf;
register unsigned char glen = lzhuf->getlen;

	while (glen <= 8) {
		i = (unsigned) getc (lzhuf->iFile);
		if ((int) i < 0)
			i = 0;
		dx |= i << (8 - glen);
		glen += 8;
	}
	lzhuf->getbuf = dx << 1;
	lzhuf->getlen = glen - 1;
	return (dx & 0x8000) ? 1 : 0;
}



static unsigned short 
GetByte (struct lzhufstruct *lzhuf)
{			/* get one byte */
register unsigned i;
register unsigned dx = lzhuf->getbuf;
register unsigned char glen = lzhuf->getlen;

	while (glen <= 8) {
		i = (unsigned) getc (lzhuf->iFile);
		if ((int) i < 0)
			i = 0;
		dx |= i << (8 - glen);
		glen += 8;
	}
	lzhuf->getbuf = dx << 8;
	lzhuf->getlen = glen - 8;
	return (dx >> 8) & 0xff;
}



static void 
Putcode (struct lzhufstruct *lzhuf, int l, unsigned c)
{			/* output c bits of code */
	lzhuf->putbuf |= c >> lzhuf->putlen;
	if ((lzhuf->putlen += uchar (l)) >= 8) {
		if (putc (lzhuf->putbuf >> 8, lzhuf->oFile) == EOF)
			return;

		if ((lzhuf->putlen -= 8) >= 8) {
			if (putc ((int) lzhuf->putbuf, lzhuf->oFile) == EOF)
				return;

			lzhuf->codesize += 2;
			lzhuf->putlen -= 8;
			lzhuf->putbuf = c << (l - lzhuf->putlen);
		} else {
			lzhuf->putbuf <<= 8;
			lzhuf->codesize++;
		}
	}
}



/* initialization of tree */

static void 
StartHuff (struct lzhufstruct *lzhuf)
{
int i, j;

	for (i = 0; i < N_CHAR; i++) {
		lzhuf->freq[i] = 1;
		lzhuf->son[i] = i + T;
		lzhuf->prnt[i + T] = i;
	}
	i = 0;
	j = N_CHAR;
	while (j <= R) {
		lzhuf->freq[j] = lzhuf->freq[i] + lzhuf->freq[i + 1];
		lzhuf->son[j] = i;
		lzhuf->prnt[i] = lzhuf->prnt[i + 1] = j;
		i += 2;
		j++;
	}
	lzhuf->freq[T] = 0xffff;
	lzhuf->prnt[R] = 0;
	lzhuf->putlen = lzhuf->getlen = 0;
	lzhuf->putbuf = lzhuf->getbuf = 0;
}



/* reconstruction of tree */
static void 
reconst (struct lzhufstruct *lzhuf)
{
int i, j, k;
unsigned first;

	/* collect leaf nodes in the first half of the table */
	/* and replace the freq by (freq + 1) / 2. */
	j = 0;
	for (i = 0; i < T; i++) {
		if (lzhuf->son[i] >= T) {
			lzhuf->freq[j] = (lzhuf->freq[i] + 1) / 2;
			lzhuf->son[j] = lzhuf->son[i];
			j++;
		}
	}
	/* begin constructing tree by connecting sons */
	for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
		k = i + 1;
		first = lzhuf->freq[j] = lzhuf->freq[i] + lzhuf->freq[k];
		for (k = j - 1; first < lzhuf->freq[k]; k--)
			;
		k++;
		{
			register unsigned *p, *e;

			for (p = &lzhuf->freq[j], e = &lzhuf->freq[k]; p > e; p--)
				p[0] = p[-1];
			lzhuf->freq[k] = first;
		}
		{
			register int *p, *e;

			for (p = &lzhuf->son[j], e = &lzhuf->son[k]; p > e; p--)
				p[0] = p[-1];
			lzhuf->son[k] = i;
		}
	}
	/* connect prnt */
	for (i = 0; i < T; i++) {
		if ((k = lzhuf->son[i]) >= T)
			lzhuf->prnt[k] = i;
		else
			lzhuf->prnt[k] = lzhuf->prnt[k + 1] = i;
	}
}



/* increment frequency of given code by one, and update tree */
static void 
lzhuf_update (struct lzhufstruct *lzhuf, int c)
{
int i, j, k, l;

	if (lzhuf->freq[R] == MAX_FREQ)
		reconst (lzhuf);

	c = lzhuf->prnt[c + T];
	do {
		k = (int) ++lzhuf->freq[c];

		/* if the order is disturbed, exchange nodes */
		if ((unsigned) k > lzhuf->freq[l = c + 1]) {
			while ((unsigned) k > lzhuf->freq[++l])
				;
			l--;
			lzhuf->freq[c] = lzhuf->freq[l];
			lzhuf->freq[l] = (unsigned int) k;

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

			j = lzhuf->son[l];
			lzhuf->son[l] = i;

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

			c = l;
		}
	} while ((c = lzhuf->prnt[c]) != 0);	/* repeat up to root */
}



static void 
EncodeChar (struct lzhufstruct *lzhuf, unsigned c)
{
unsigned i;
int j, k;

	i = 0;
	j = 0;
	k = lzhuf->prnt[c + T];

	/* travel from leaf to root */
	do {
		i >>= 1;

		/* if node's address is odd-numbered, choose bigger brother node */
		if (k & 1)
			i += 0x8000;

		j++;
	} while ((k = lzhuf->prnt[k]) != R);
	Putcode (lzhuf, j, i);
	lzhuf->code = i;
	lzhuf->len = (unsigned int) j;
	lzhuf_update (lzhuf, (int) c);
}



static void 
EncodePosition (struct lzhufstruct *lzhuf, unsigned c)
{
unsigned i;

	/* output upper 6 bits by table lookup */
	i = c >> 6;
#ifdef DEBUG
	if (i >= MAX_P_LEN)
		tcmdprintf ("Ah....FYI...you've just exceded the MAX_P_LEN variable ( %d ) with %d\n", MAX_P_LEN, i);
#endif
	Putcode (lzhuf, p_len[i], (unsigned) p_code[i] << 8);

	/* output lower 6 bits verbatim */
	Putcode (lzhuf, 6, (c & 0x3f) << 10);
}



static void 
EncodeEnd (struct lzhufstruct *lzhuf)
{
	if (lzhuf->putlen) {
		if (putc (lzhuf->putbuf >> 8, lzhuf->oFile) == EOF)
			return;
		lzhuf->codesize++;
	}
}



static int 
DecodeChar (struct lzhufstruct *lzhuf)
{
unsigned c;

	c = (unsigned) lzhuf->son[R];

	/* travel from root to leaf, */
	/* choosing the smaller child node (son[]) if the read bit is 0, */
	/* the bigger (son[]+1} if 1 */
	while (c < T) {
		c += (unsigned) GetBit (lzhuf);
		c = (unsigned) lzhuf->son[c];
	}
	c -= T;
	lzhuf_update (lzhuf, (int) c);
	return (int) c;
}



static int 
DecodePosition (struct lzhufstruct *lzhuf)
{
unsigned j, c;
unsigned short i;

	/* recover upper 6 bits from table */
	i = GetByte (lzhuf);
#ifdef DEBUG
	if (i >= MAX_D_LEN)
		tcmdprintf ("Ah....FYI...you've just exceded the MAX_D_LEN variable ( %u ) with %u\n", MAX_D_LEN, i);
#endif
	c = (unsigned) d_code[i] << 6;
	j = d_len[i];

	/* read lower 6 bits verbatim */
	j -= 2;
	while (j--)
		i = (int16) ((i << 1) + GetBit (lzhuf));

	return (int) (c | (i & 0x3f));
}



/* compression */
static int 
Encode (int usock OPTIONAL, char *txtFileName, char *binFileName, struct lzhufstruct *lzhuf, int trace)
{
int i, c, len, r, s, last_match_length;
unsigned long int filesize = 0;

#ifdef DEBUG
	tcmdprintf ("Encoding %s into %s\n", txtFileName, binFileName);
#endif

	/* Open input and output files. */
	if ((lzhuf->iFile = fopen (txtFileName, "rb")) == NULLFILE) {
		if (trace)
			tcmdprintf (Erroropening, txtFileName, Encodestr);
		return 0;
	}
	if ((lzhuf->oFile = fopen (binFileName, "wb")) == NULLFILE) {
		if (trace)
			tcmdprintf (Erroropening, binFileName, Encodestr);
		return 0;
	}
	fseek (lzhuf->iFile, 0L, 2);
	filesize = (unsigned long) ftell (lzhuf->iFile);
	if (filesize == 0)
		return 0;
	/* output size of text */
	if (fwrite (&filesize, sizeof (filesize), 1, lzhuf->oFile) < 1)
		return 0;
	rewind (lzhuf->iFile);

	lzhuf->iFileSize = filesize;
	filesize = 0;		/* rewind and re-read */

	StartHuff (lzhuf);
	InitTree (lzhuf);

	s = 0;
	r = N - F;
	for (i = s; i < r; i++)
		lzhuf->text_buf[i] = ' ';
	for (len = 0; len < F && (c = getc (lzhuf->iFile)) != EOF; len++)
		lzhuf->text_buf[r + len] = uchar (c);
	filesize = (unsigned long) len;
	for (i = 1; i <= F; i++)
		InsertNode (lzhuf, r - i);
	InsertNode (lzhuf, r);
	do {
		kwait (NULL);
		if (lzhuf->match_length > len)
			lzhuf->match_length = len;
		if (lzhuf->match_length <= THRESHOLD) {
			lzhuf->match_length = 1;
			EncodeChar (lzhuf, lzhuf->text_buf[r]);
		} else {
			EncodeChar (lzhuf, (unsigned) (255 - THRESHOLD + lzhuf->match_length));
			EncodePosition (lzhuf, (unsigned) lzhuf->match_position);
		}
		last_match_length = lzhuf->match_length;
		for (i = 0; i < last_match_length && (c = getc (lzhuf->iFile)) != EOF; i++) {
			DeleteNode (lzhuf, s);
			lzhuf->text_buf[s] = uchar (c);
			if (s < F - 1)
				lzhuf->text_buf[s + N] = uchar (c);
			s = (s + 1) & (N - 1);
			r = (r + 1) & (N - 1);
			InsertNode (lzhuf, r);
		}
		while (i++ < last_match_length) {
			DeleteNode (lzhuf, s);
			s = (s + 1) & (N - 1);
			r = (r + 1) & (N - 1);
			if (--len)
				InsertNode (lzhuf, r);
		}
	} while (len > 0);
	EncodeEnd (lzhuf);
	(void) fclose (lzhuf->iFile);
	(void) fclose (lzhuf->oFile);

	if (lzhuf->iFileSize == 0)
		lzhuf->iFileSize = 1;
	if (trace)
		tcmdprintf (ENCODEratio, lzhuf->iFileSize, lzhuf->codesize,
		(100 - ((lzhuf->codesize * 100) / (long) lzhuf->iFileSize)));
	return 1;
}



static int 
Decode (int usock OPTIONAL, char *iFile, char *oFile, struct lzhufstruct *lzhuf, int addex, int trace)
{
int i = 0;
int j = 0;
int k = 0;
int r = 0;
int c = 0;
unsigned long int count = 0;
long int filesize = 0;

#ifdef DEBUG
	tcmdprintf ("Decoding %s into %s\n", iFile, oFile);
#endif

	/* Open input and output files. */
	if ((lzhuf->iFile = fopen (iFile, "rb")) == NULLFILE) {
		if (trace)
			tcmdprintf (Erroropening, iFile, Decodestr);
		return 0;
	}
	if ((lzhuf->oFile = fopen (oFile, "wb")) == NULLFILE) {
		if (trace)
			tcmdprintf (Erroropening, oFile, Decodestr);
		return 0;
	}
	fseek (lzhuf->iFile, 0L, 2);
	filesize = ftell (lzhuf->iFile);
	if (filesize == 0)
		return 0;
	lzhuf->iFileSize = (unsigned long) filesize;
	rewind (lzhuf->iFile);

	if (fread (&filesize, sizeof (filesize), 1, lzhuf->iFile) < 1)
		return 0;
	if (filesize == 0)
		return 0;

	StartHuff (lzhuf);
	for (i = 0; i < N - F; i++)
		lzhuf->text_buf[i] = ' ';

	r = N - F;
	for (count = 0; count < (unsigned long) filesize;) {
		kwait (NULL);
		c = DecodeChar (lzhuf);
		if (c < 256) {
			if (putc (c, lzhuf->oFile) == EOF)
				return 0;

			lzhuf->text_buf[r++] = uchar (c);
			r &= (N - 1);
			count++;
		} else {
			i = (r - DecodePosition (lzhuf) - 1) & (N - 1);
			j = c - 255 + THRESHOLD;
			for (k = 0; k < j; k++) {
				c = lzhuf->text_buf[(i + k) & (N - 1)];
				if (putc (c, lzhuf->oFile) == EOF)
					return 0;

				lzhuf->text_buf[r++] = uchar (c);
				r &= (N - 1);
				count++;
			}
		}
	}

#ifndef STANDALONE
	if (addex)
		/* This terminates a note. */
		fputs ("\n/EX\n", lzhuf->oFile);
#endif

	(void) fclose (lzhuf->iFile);
	(void) fclose (lzhuf->oFile);

	if (count == 0)
		count = 1;
	if (trace)
		tcmdprintf (DECODEratio, lzhuf->iFileSize, count,
			    (100 - ((lzhuf->iFileSize * 100) / count)));
	return 1;
}



#ifndef STANDALONE
int 
send_yapp (struct fwd *f, char *txtFileName, char *subj)
{
#define SLEN 79		/* Maximum subject     */
int oldmode;		/* Socket Mode holder. */
char *buffer;		/* buffer data.        */
int buffer_len;		/* buffer Length.      */
int x;			/* misc counter.       */
int cnt;		/* misc counter.       */
int rc;
int Error;
int usock;
short b_checksum;	/* buffer checksum.    */
short f_checksum;	/* file checksum.      */
FILE *binFile;
char binFileName[80];
#ifdef DEBUG2
FILE *debug;
#endif

	/* Debug info. */
#ifdef DEBUG2
	if (((debug = fopen (tmpnam (NULL), "wb")) == NULLFILE)) {
		printf ("Error opening input file.\n");
		return 0;
	}
#endif

	/* User socket */
	usock = f->m->user;

	/* Encode code. */
	(void) tmpnam (binFileName);

	Error = FALSE;

	kwait (NULL);
	AllocDataBuffers (f);
	rc = Encode (usock, txtFileName, binFileName, f->lzhuf, FBBtrace);
	FreeDataBuffers (f);
	kwait (NULL);
	if (!rc) {
		if (FBBtrace)
			tcmdprintf (ErrorEncode, rc);
		log (f->m->user, ErrorEncode, rc);
		Error = TRUE;
		unlink (binFileName);	/* just in case */
	}
	unlink (txtFileName);

	if (Error)
		return 0;

	/* open the compressed data file. */
	/* Now... we're going to read from the file and close it when we exit. */

	/* Open the input file. */
	binFile = fopen (binFileName, "rb");
	if (binFile == NULLFILE)
		return 0;

	/* Debug info. */
#ifdef DEBUG
	tcmdprintf ("we opened %s for input.\n", f->oFile);
#endif

	/* Grab some space. Largest YAPP packet is 250+ bytes. */
	f->tmpBuffer = mallocw (260);
	buffer = f->tmpBuffer;

	/* Set the socket to Binary mode since we'll be sending Binary data. */
	oldmode = sockmode (usock, SOCK_BINARY);

	/* Send the subject buffer
         The buffer is setup as follows:
         Pos Data
           1 SOH
           2 Length of entire buffer ( 8 bytes + strlen(subject) )
           3 Null terminated Subject string.
           x Null terminated '0' string.
	 */

	/* Make sure that the subject strlen() is equal to or less than SLEN */
	x = (int) strlen (subj);
	if (x >= SLEN) {
		x = SLEN;
		subj[SLEN] = '\0';
	}
	/* length of subject + NULL + length of "     0" + NULL */
	buffer_len = x + 1 + 6 + 1;

	/* Build the buffer. */
	buffer[0] = SOH;	/* buffer_Type                */
	buffer[1] = (char) buffer_len;	/* buffer_Len                 */
	strcpy (&buffer[2], subj);	/* Subject info.              */
	strcpy (&buffer[x + 3], "     0");	/* Always 0 for FBB Messages. */

	/* Now we can send it.                       */
	/* buffer_len + 2 ( for the first two bytes. */
	(void) usputbuf (usock, (unsigned char *) buffer, buffer_len + 2);
#ifdef DEBUG2
	fwrite (buffer, buffer_len + 2, 1, debug);
#endif

	/* Send the data buffers. */
	f_checksum = 0;
	/* fill buffer with data. Bytes 0 and 1 are reserved. */
	while ((x = (int) fread (&buffer[2], 1, 250, binFile)) > 0) {
		/* prepare the buffer. */
		buffer[0] = STX;/* buffer_Type       */
		buffer[1] = (char) x;	/* buffer_Len        */

		b_checksum = 0;
		for (cnt = 0; cnt < x; cnt++)
			b_checksum += buffer[2 + cnt];	/* buffer checksum.  */

#if 0				/* must be for FBB 4.15C ?? */
		buffer[x + 2] = ((-b_checksum) & 0xff);	/* Store b_checksum. */
#endif

		/* and send it. */
		(void) usputbuf (usock, (unsigned char *) buffer, (x + 2));
#ifdef DEBUG2
		fwrite (buffer, x + 2, 1, debug);
#endif

		f_checksum += b_checksum;
	}			/* endwhile */

	/* Send the EOT */
	/* Prepare the buffer. */
	buffer[0] = EOT;	/* buffer_Type */
	buffer[1] = (char) ((-f_checksum) & 0xff);	/* Checksum.   */

	/* and send it. */
	(void) usputbuf (usock, (unsigned char *) buffer, 2);
#ifdef DEBUG2
	fwrite (buffer, 2, 1, debug);
#endif

	/* Terminate. */
	(void) fclose (binFile);

	/* Delete binFileName */
	unlink (binFileName);

#ifdef DEBUG2
	(void) fclose (debug);
#endif
	free (f->tmpBuffer);
	/* Set the socket back to it's orginal mode. */
	(void) sockmode (usock, oldmode);
	return 1;
}


int 
recv_yapp (struct fwd *f)
{
int recvcnt;
int packet_type;
unsigned int packet_size;
char packet_data[258];
int GetSubject;
int NoteDone;
int NoteError;
unsigned int rx_checksumctr;
unsigned int rx_checksum;
int rc;
int oldmode;		/* Socket Mode holder. */
FILE *iFile = NULLFILE;
int usock;

	/* User socket */
	usock = f->m->user;

	/* Set the socket to Binary mode since we'll be sending Binary data. */
	oldmode = sockmode (usock, SOCK_BINARY);

	GetSubject = TRUE;
	NoteDone = FALSE;
	NoteError = FALSE;
	rx_checksum = 0;

	while (!NoteDone) {
		/* Get the data packets. */
		packet_type = recvchar (usock);

		if (GetSubject) {
			if (packet_type != SOH) {
				FBBerror (0, usock, f->m);
				NoteDone = NoteError = TRUE;
				continue;
			}
		} else if ((packet_type != STX) && (packet_type != EOT)) {
			/* Get the packet size. */
			packet_size = (unsigned int) recvchar (usock);
			FBBerror (3, usock, f->m);
			NoteDone = NoteError = TRUE;
			continue;
		}
		/* Get the packet size. */
		packet_size = (unsigned int) recvchar (usock);
		if (!packet_size)
			packet_size = 256;	/* 0x00 always means 256 */

		if (packet_type == SOH) {
			/* This is the subject. Reset the flag so we don't */
			/* come here again.                                */
			GetSubject = FALSE;

			/* Open the output file. */
			if (iFile == NULLFILE)	/* shouldn't be needed, but just in case */
				if ((iFile = fopen (f->iFile, "wb")) == NULLFILE)
					return 0;

			/* This is a subject packet (with the offset hiding behind it). */
			recvcnt = recvbuf (usock, &packet_data[0], packet_size + 1);
			/* we have now read the subject (which we'll use) and the
			   offset (which we are assuming to be zero), which we ignore */
			if (recvcnt == -1) {
				if (FBBtrace) {
					tcmdprintf (lostremote);
					tcmdprintf ("\n");
				}
				log (f->m->user, lostremote);
				/* We've lost the connection.... */
				NoteDone = NoteError = TRUE;
			} else
				f->m->subject = strdup (packet_data);
		}
		/* endif */ 
		else if (packet_type == STX) {
			/* Validate the packet       */
			/* and write it to the file. */
			recvcnt = recvbuf (usock, &packet_data[0], packet_size + 1);
			if (recvcnt == -1) {
				if (FBBtrace) {
					tcmdprintf (lostremote);
					tcmdprintf ("\n");
				}
				log (f->m->user, lostremote);
				/* We've lost the connection.... */
				NoteDone = NoteError = TRUE;
			} else {
				/* Write to disk */
				if (iFile)
					fwrite (packet_data, (unsigned int) recvcnt, 1, iFile);

				/* add the data to the rx_checksum count. */
				for (rx_checksumctr = 0; rx_checksumctr < packet_size; rx_checksumctr++)
					rx_checksum += (unsigned char) packet_data[rx_checksumctr];
			}
		}
		/* endif */ 
		else if (packet_type == EOT) {
			/* Close the file.                  */
			/* Validate the Checksum.           */
			/* erase the file if Checksum fails.*/
			/* Get ready to read next message.  */

			if (packet_size == 256)
				packet_size = 0;
			if (packet_size != (unsigned int) ((-(int) rx_checksum) & 0xff)) {
				NoteDone = NoteError = TRUE;
				usputs (usock, "*** CHECKSUM ERROR\n");
				if (FBBtrace)
					tcmdprintf ("FBBCMP: Checksum Error in message data\n");
				log (f->m->user, "Checksum Error in message data");
			} else if (FBBtrace)
				tcmdprintf ("FBBCMP: YAPP Message Checksum verified\n");
			/* Close the data file. */
			if (iFile)
				(void) fclose (iFile);
			iFile = NULLFILE;

			/* We're done with this message. Return. */
			NoteDone = TRUE;
		}		/* endif */
	}			/* End while !NoteDone */

	if (!NoteError) {
		kwait (NULL);
		AllocDataBuffers (f);
		rc = Decode (usock, f->iFile, f->oFile, f->lzhuf, 1, FBBtrace);
		FreeDataBuffers (f);
		kwait (NULL);
		if (!rc) {
			if (FBBtrace)
				tcmdprintf (ErrorDecode, rc);
			log (f->m->user, ErrorDecode, rc);
			NoteError = TRUE;
		}
	}
	if (iFile != NULLFILE)
		(void) fclose (iFile);

	/* delete compressed file. */
	unlink (f->iFile);

	/* Set the socket back to it's orginal mode. */
	(void) sockmode (usock, oldmode);
	if (!NoteError)
		return 1;
	else
		return 0;
}



/* Receive a buffer from a socket, returning # chars read.
 */
static int 
recvbuf (int s, char *buf, unsigned len)
{
int c;
int cnt = 0;

	while (len-- > 1) {
		if ((c = recvchar (s)) == EOF) {
			cnt = -1;
			break;
		}
		if (buf != NULLCHAR)
			*buf++ = (char) c;
		cnt++;
	}
	return cnt;
}
#endif



#ifdef XFWD
int 
send_lzhuf (struct fwd *f, char *txtFileName)
{
int oldmode;		/* Socket Mode holder. */
int rc, c;
int Error;
int usock;
FILE *binFile;
char binFileName[80];
int16 newchecksum = 0;

	/* User socket */
	usock = f->m->user;

	/* Encode code. */
	(void) tmpnam (binFileName);

	Error = FALSE;

	kwait (NULL);
	AllocDataBuffers (f);
	rc = Encode (usock, txtFileName, binFileName, f->lzhuf, Xtrace);
	FreeDataBuffers (f);
	kwait (NULL);
	if (!rc) {
		if (Xtrace)
			tcmdprintf (ErrorEncode, rc);
		log (f->m->user, ErrorEncode, rc);
		Error = TRUE;
		unlink (binFileName);	/* just in case */
	}
	unlink (txtFileName);

	if (Error) {
		tprintf ("SS 0\n");
		return 0;
	}
	/* open the compressed data file. */
	/* Now... we're going to read from the file and close it when we exit. */

	kwait (NULL);

	/* Open the input file. */
	binFile = fopen (binFileName, "rb");
	if (binFile == NULLFILE)
		return 0;

	kwait (NULL);

	/* Calculate checksum */
	while ((c = fgetc (binFile)) != EOF)
		newchecksum += (int16) c;

	rewind (binFile);
	kwait (NULL);

	tprintf ("SS %ld %u\n", filelength (fileno (binFile)), newchecksum);
	if (Xtrace)
		tcmdprintf ("XFWD: Sending 'SS %ld %u'\n", filelength (fileno (binFile)), newchecksum);

	/* Set the socket to Binary mode since we'll be sending Binary data. */
	oldmode = sockmode (usock, SOCK_BINARY);

	while ((c = fgetc (binFile)) != EOF)
		tputc (uchar (c));

	/* Terminate. */
	(void) fclose (binFile);

	/* Delete binFileName */
	unlink (binFileName);

	/* Set the socket back to it's orginal mode. */
	(void) sockmode (usock, oldmode);
	return 1;
}



int 
recv_lzhuf (struct fwd *f, int msgsize, int16 * newchecksum)
{
int c;
int Error = FALSE;
int rc;
int oldmode;		/* Socket Mode holder. */
FILE *iFile = NULLFILE;
int usock;

	*newchecksum = 0;

	/* User socket */
	usock = f->m->user;

	/* Set the socket to Binary mode since we'll be sending Binary data. */
	oldmode = sockmode (usock, SOCK_BINARY);

	iFile = fopen (f->iFile, "wb");
	if (iFile == NULLFILE)
		return 0;

	while (msgsize--) {
		/* Get the data. */
		c = recvchar (usock);
		*newchecksum += (int16) c;
		fputc (c, iFile);
	}

	(void) fclose (iFile);
	AllocDataBuffers (f);
	rc = Decode (usock, f->iFile, f->oFile, f->lzhuf, 0, Xtrace);
	FreeDataBuffers (f);
	if (!rc) {
		if (Xtrace)
			tcmdprintf (ErrorDecode, rc);
		log (f->m->user, ErrorDecode, rc);
		Error = TRUE;
	}
	/* delete compressed file. */
	unlink (f->iFile);

	kwait (NULL);

	/* Set the socket back to it's orginal mode. */
	(void) sockmode (usock, oldmode);
	if (!Error)
		return 1;
	else
		return 0;
}
#endif



#ifdef STANDALONE
int 
kwait (volatile void *event)
{
}



void
main (argc, argv, envp)
int argc;
char *argv[];
char *envp[];
{
	struct fwd f;

	AllocDataBuffers (&f);
	if (argv[1][0] == 'd')
		Decode (0, argv[2], argv[3], f.lzhuf, 0, FBBtrace);
	else
		Encode (0, argv[2], argv[3], f.lzhuf, FBBtrace);
}
#endif
#endif
