/*
 *			l z d c m 2 . c
 *
 * Actual decompression code
 */

#include "lz.h"

/*
 * These global parameters are read from the compressed file.
 * The decompressor needs them.
 */
extern short	maxbits;		/* settable max # bits/code	*/
extern short	block_compress;
extern code_int	maxmaxcode;

/*
 * Local variables to the decompressor.
 */
code_int	maxcode;
int		n_bits;
flag		clear_flg = FALSE;
int		offset = 0;
int		size = 0;

#if !vax_asm
char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
#endif

/*
 * Big data storage stuff
 */

char_type	stack[MAXSTACK];
#define	STACK_TOP	(&stack[MAXSTACK])
U_short		tab_prefix[1<<BITS];	/* prefix code for this entry	*/
char_type  	tab_suffix[1<<BITS];	/* last char in this entry	*/
code_int	free_ent;

decompress()
/*
 * Decompress stdin to stdout.
 */
{
	register char_type	*stp;		/* Stack pointer	*/
	register code_int code, oldcode, incode;
	register int		finchar;	/* End of a sequence?	*/
	extern code_int		getcode();

	stp = STACK_TOP;
	clear_flg = FALSE;
	offset = 0;
	size = 0;
	/*
	 * As above, initialize the first 256 entries in the table.
	 */
	maxcode = MAXCODE(n_bits = INIT_BITS);
	for (code = 255; code >= 0; code--) {
	    tab_prefix[code] = 0;
	    tab_suffix[code] = (char_type)code;
	}
	free_ent = ((block_compress != 0) ? FIRST : 256);
	finchar = oldcode = getcode();
	PUT((char) finchar);		/* first code must be 8 bits = char */

	while ((code = getcode()) != -1) {
	    if ((code == CLEAR) && block_compress != 0) {
		for (code = 255; code > 0; code -= 4) {
		    tab_prefix[code-3] = 0;
		    tab_prefix[code-2] = 0;
		    tab_prefix[code-1] = 0;
		    tab_prefix[code] = 0;
		}
		clear_flg = TRUE;
		free_ent = FIRST - 1;
		if ((code = getcode()) == -1	/* O, untimely death!	*/
		 || code == CLEAR) {		/* Endfile signal	*/
		    break;
		}
	    }
	    incode = code;
	    /*
	     * Special case for KwKwK string.
	     */
	    if (code >= free_ent) {
#ifdef DEBUG
		if (stp <= stack) {
		    fprintf(stderr, "Stack overflow in KwKwK\n");
		    abort();
		}
#endif
		*--stp = finchar;
		code = oldcode;
	    }
	    /*
	     * Generate output characters in reverse order
	     */
#ifdef interdata
	    while (((unsigned long)code) >= ((unsigned long)256)) {
#else
	    while (code >= 256) {
#endif
#ifdef DEBUG
		if (stp <= stack) {
		    fprintf(stderr, "Stack overflow in generate output\n");
		    abort();
		}
#endif
		*--stp = tab_suffix[code];
		code = tab_prefix[code];
	    }
#ifdef DEBUG
	    if (stp <= stack) {
		fprintf(stderr, "Stack overflow final char\n");
		abort();
	    }
#endif
	    *--stp = finchar = tab_suffix[code];
	    /*
	     * And put them out in forward order
	     */
	    PUTBUF(stp, STACK_TOP - stp);
	    stp = STACK_TOP;
	    /*
	     * Generate the new entry.
	     */
	    if ((code = free_ent) < maxmaxcode) {
		tab_prefix[code] = (U_short) oldcode;
		tab_suffix[code] = finchar;
		free_ent = code + 1;
	    } 
	    /*
	     * Remember previous code.
	     */
	    oldcode = incode;
	}
	LZ_FLUSH();
}

/*
 * getcode()
 *
 * Read one code from the standard input.  If EOF, return -1.
 * Inputs:
 * 	stdin (via GET)
 * Outputs:
 * 	code or -1 is returned.
 */

static char_type	buf[BITS];

code_int
getcode() {
	/*
	 * On the VAX (4.2 bsd), it is important to have the register
	 * declarations in exactly the order given, or the asm will break.
	 */
	register code_int 	code;
	register int		r_off, bits;
	register char_type	*bp;

	bp = buf;
	if (clear_flg || offset >= size || free_ent > maxcode) {
	    /*
	     * If the next entry will be too big for the current code
	     * size, then we must increase the size.  This implies reading
	     * a new buffer full, too.
	     */
	    if (free_ent > maxcode) {
		n_bits++;
		if (n_bits == maxbits)
		    maxcode = maxmaxcode;	/* won't get bigger now	*/
		else
		    maxcode = MAXCODE(n_bits);
	    }
	    if (clear_flg) {
		maxcode = MAXCODE(n_bits = INIT_BITS);
		clear_flg = FALSE;
	    }
	    size = GETBUF(buf, n_bits);
#ifdef DEBUG
	    if (debug > 2)
		dumphex(buf, size);
#endif
	    if (size <= 0) {
		return (-1);			/* end of file		*/
	    }
	    offset = 0;
	    /*
	     * Round size down to integral number of codes
	     */
	    size = (size << 3) - (n_bits - 1);
	}
	r_off = offset;
	bits = n_bits;
#if vax_asm
	asm("extzv   r10,r9,(r8),r11");
#else
	/*
	 * Get to the first byte.
	 */
	bp += (r_off >> 3);
	r_off &= 7;
	/*
	 * Get first part (low order bits)
	 */
#if UCHAR
	code = (*bp++ >> r_off);
#else
	/*
	 * Don't touch the 0xFF.
	 */
	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xFF;
#endif
	bits -= (8 - r_off);
	r_off = 8 - r_off;		/* now, offset into code word	*/
	/*
	 * Get any 8 bit parts in the middle (<=1 for up to 16 bits).
	 */
	if (bits >= 8) {
#if UCHAR
	    code |= *bp++ << r_off;
#else
	    code |= (*bp++ & 0xFF) << r_off;
#endif
	    r_off += 8;
	    bits -= 8;
	}
	/* high order bits. */
#if UCHAR
	code |= (*bp & rmask[bits]) << r_off;
#else
	code |= (*bp & rmask[bits]) << r_off;
#endif
	/*
	 * End of non-vax (Unix) specific code.
	 */
#endif
	offset += n_bits;
	return (code);
}

#ifdef DEBUG

printcodes(dumpfd)
FILE		*dumpfd;
/*
 * Just print out codes from input file.  Mostly for debugging.
 */
{
	code_int code;
	int col, bits;

	col = 0;
	bits = n_bits = INIT_BITS;
	maxcode = MAXCODE(n_bits);
	free_ent = ((block_compress != 0) ? FIRST : 256);
	while ((code = getcode()) >= 0) {
	    if ((code == CLEAR) && block_compress) {
		free_ent = FIRST - 1;
		clear_flg = TRUE;
	    }
	    else if (free_ent < maxmaxcode)
		free_ent++;
	    if (bits != n_bits) {
		fprintf(dumpfd, "\nChange to %d bits\n", n_bits);
		bits = n_bits;
		col = 0;
	    }
	    fprintf(dumpfd, "%5d%c",
		code, (col += 6) >= 74 ? (col = 0, '\n') : ' ');
	}
	putc('\n', dumpfd);
}

dump_tab(dumpfd)
FILE		*dumpfd;
/*
 * dump string table
 */
{
	register char_type	*stp;	/* Stack pointer		*/
	register int		i;
	register int		ent;

	stp = STACK_TOP;
	fprintf(dumpfd, "%d %s in string table\n",
	    free_ent, (free_ent == 1) ? "entry" : "entries");
	for (i = 0; i < free_ent; i++) {
	    if (isascii(tab_suffix[i]) && isprint(tab_suffix[i])) {
		fprintf(dumpfd, "%5d: %5d/'%c'  \"",
		    i, tab_prefix[i], tab_suffix[i]);
	    }
	    else {
		fprintf(dumpfd, "%5d: %5d/\\%03o \"",
		    i, tab_prefix[i], tab_suffix[i]);
	    }
	    for (ent = i;;) {
		*--stp = tab_suffix[ent];
		if (ent < FIRST)
		    break;
		ent = tab_prefix[ent];
	    }
	    putc('"', dumpfd);
	    while (stp < STACK_TOP) {
		ent = *stp++ & 0xFF;
		if (isascii(ent) && isprint(ent))
		    putc(ent, dumpfd);
		else {
		    switch (ent) {
		    case '\n':	fputs("\\n", dumpfd);	break;
		    case '\t':	fputs("\\t", dumpfd);	break;
		    case '\b':	fputs("\\b", dumpfd);	break;
		    case '\f':	fputs("\\f", dumpfd);	break;
		    case '\r':	fputs("\\r", dumpfd);	break;
		    default:
			fprintf(dumpfd, "\x%02X", ent);
			break;
		    }
		}
	    }
	    fputs("\"\n", dumpfd);
	    stp = STACK_TOP;
	}
}

static FILE	*foo_fd = NULL;

dumphex(buffer, count)
register char_type	*buffer;
register int		count;
{
	if (foo_fd == NULL
	 && (foo_fd = fopen("lzdcmp.dmp", "w")) == NULL)
	    abort();
	fprintf(foo_fd, "%2d:", count);
	while (--count >= 0) {
	    fprintf(foo_fd, " %02x", *buffer++ & 0xFF);
	}
	fprintf(foo_fd, "\n");
}

dumptext(buffer, count)
register char_type	*buffer;
int			count;
{
	register int	c;

	if (foo_fd == NULL
	 && (foo_fd = fopen("lzdcmp.dmp", "w")) == NULL)
	    abort();
	fprintf(foo_fd, "%2d: \"", count);
	while (--count >= 0) {
	    c = *buffer++ & 0xFF;
	    if (isascii(c) && isprint(c))
		putc(c, foo_fd);
	    else {
		switch (c) {
		case '\n':	fputs("\\n", foo_fd);	break;
		case '\t':	fputs("\\t", foo_fd);	break;
		case '\b':	fputs("\\b", foo_fd);	break;
		case '\f':	fputs("\\f", foo_fd);	break;
		case '\r':	fputs("\\r", foo_fd);	break;
		default:
		    fprintf(foo_fd, "<\x%02X>", c);
		    break;
		}
	    }
	}
	fputs("\"\n", foo_fd);
}
#endif
