/*----------------------------------------------------------------------*/
/* Copyright (c) 1987													*/
/* by CompuServe Inc., Columbus, Ohio.  All Rights Reserved				*/
/* 89.6.8	FM-TOWNS Model Convert By Ken								*/
/*----------------------------------------------------------------------*/
#include 	<stdio.h>
#include	<stdlib.h>

#define largest_code	4095

struct code_entry {
    short prefix;			/* prefix code */
    char suffix;			/* suffix character */
    char stack;
};

static short code_size;
static short clear_code;
static short eof_code;
static short first_free;
static short bit_offset;
static short byte_offset, bits_left;
static short max_code;
static short free_code;
static short old_code;
static short input_code;
static short code;
static short suffix_char;
static short final_char;
static short bytes_unread;
static short min_code_size;
static unsigned char code_buffer[64];
static struct code_entry *code_table;
static char *dmy_buf=NULL;
static short dmy_size,dmy_ptr,dmy_read;

static short mask[12] =
    {0x001, 0x003, 0x007, 0x00F,
     0x01F, 0x03F, 0x07F, 0x0FF,
     0x1FF, 0x3FF, 0x7FF, 0xFFF};

static void dmy_putc(short ch)
{
	dmy_buf[dmy_ptr++] = ch;
	dmy_ptr &= 4095;
	dmy_size++;
}
static void init_table(short min_code_size)
{
    code_size = min_code_size + 1;
    clear_code = 1 << min_code_size;
    eof_code = clear_code + 1;
    first_free = clear_code + 2;
    free_code = first_free;
    max_code = 1 << code_size;
}

static long read_code(FILE *fp)
{
    short bytes_to_move, i, ch;
    long temp;

    byte_offset = bit_offset >> 3;
    bits_left = bit_offset & 7;

    if (byte_offset >= 61) {
		bytes_to_move = 64 - byte_offset;
		for (i = 0; i < bytes_to_move; i++)
	    	code_buffer[i] = code_buffer[byte_offset + i];
		while (i < 64) {
	    	if (bytes_unread == 0) {
				if ( (bytes_unread = getc(fp)) == EOF )
					return EOF;
	    		if ( bytes_unread == 0 )	/* end of data */
					break;
			}
		    if ( (ch = getc(fp)) == EOF )
				return EOF;
		    code_buffer[i++] = ch;
		    bytes_unread--;
	    }
		bit_offset = bits_left;
		byte_offset = 0;
	}

    bit_offset += code_size;
    temp = (long) code_buffer[byte_offset]
		 | (long) code_buffer[byte_offset + 1] << 8
		 | (long) code_buffer[byte_offset + 2] << 16;

    if (bits_left > 0)
		temp >>= bits_left;

    return temp & mask[code_size - 1];
}

static short Expand_init(FILE *fp)
{
    if ( (code_table = (struct code_entry *)
			malloc(sizeof(struct code_entry)*(largest_code + 1))) == NULL )
		return EOF;

    if ( (min_code_size = getc(fp)) < 0 )
		return EOF;
    else if ( min_code_size < 2 || min_code_size > 9 )
		return EOF;

    init_table(min_code_size);
    bit_offset = 64*8;			/* force "read_code" to start a new */
    bytes_unread = 0;			/* record */
	return 0;
}
static short Expand_Data(FILE *fp)
{
	short sp = 0;

    if ( (code = read_code(fp)) == eof_code )
		return EOF;

	if ( code == clear_code ) {
    	init_table(min_code_size);
    	code = read_code(fp);
    	old_code = code;
    	suffix_char = code;
    	final_char = code;
    	dmy_putc(suffix_char);
		return 0;
    }

   	input_code = code;
    if ( code >= free_code ) {
		code = old_code;
		code_table[sp++].stack = final_char;
	}
    while (code >= first_free) {
		code_table[sp++].stack = code_table[code].suffix;
		code = code_table[code].prefix;
	}

   	final_char = code;
   	suffix_char = code;
   	code_table[sp++].stack = final_char;

   	while (sp > 0)
		dmy_putc(code_table[--sp].stack);

    code_table[free_code].suffix = suffix_char;
    code_table[free_code].prefix = old_code;
   	free_code++;
   	old_code = input_code;

   	if ( free_code >= max_code && code_size < 12 ) {
    	code_size++;
    	max_code <<= 1;
    }
	return 0;
}
void	dmy_close(void)
{
	if ( dmy_buf != NULL ) { free(dmy_buf); dmy_buf = NULL; }
	if ( code_table != NULL ) { free(code_table); code_table = NULL; }
}
int		dmy_getc(FILE *fp)
{
	int		ch;

	if ( dmy_buf == NULL ) {
		if ( Expand_init(fp) != 0 )
			goto ERROR;
		if ( (dmy_buf = malloc(4096)) == NULL )
			goto ERROR;		
		dmy_size = dmy_ptr = dmy_read = 0;
	}
	if ( dmy_size <= 0 && Expand_Data(fp) != 0 )
		goto ERROR;
	ch = (unsigned char)dmy_buf[dmy_read++];
	dmy_read &= 4095;
	dmy_size--;
	return ch;

ERROR:
	dmy_close();
	return EOF;
}
