/*---------------------------------------------------------------------

  LZin modules written by L. Granroth  07-19-90
  to provide binary stream input from compressed files
  using on-the-fly LZW decompression.  The input files must be
  compatible with standard Unix compress using the 12-bit option.
  The entry points are designed to be callable from VAX/VMS and MS
  FORTRAN.
  
  ---------------------------------------------------------------------*/

/* Get the usual include files */

#include <stdio.h>

/* Define some useful constants */

#define BITS          12
#define HSIZE       5003            /* 80% occupancy */
#define BIT_MASK    0x1F
#define INIT_BITS      9            /* initial number of bits/code */
#define BLOCK_MASK  0x80
#define FIRST        257            /* first free entry */
#define CLEAR        256            /* table clear output code */

/* Define some useful macros */

#define MAXCODE(n_bits)     ((1 << (n_bits)) - 1)
#define htabof(i)           htab[i]
#define codetabof(i)        codetab[i]
#define tab_prefixof(i)     codetabof (i)
#define tab_suffixof(i)     ((char_type *) (htab))[i]
#define de_stack            ((char_type *) &tab_suffixof (1 << BITS))

/* Set up our typedefs */

typedef short int code_int;
typedef short int count_int;
typedef unsigned char char_type;

/* Declare the global variables */

static char_type rmask[9] = {
  (char_type) 0x00,
  (char_type) 0x01,
  (char_type) 0x03,
  (char_type) 0x07,
  (char_type) 0x0F,
  (char_type) 0x1F,
  (char_type) 0x3F,
  (char_type) 0x7F,
  (char_type) 0xFF};

static int n_bits;                      /* number of bits/code */
static int maxbits = BITS;              /* user settable max # bits/code */
static code_int maxcode;                /* maximum code, given n_bits */
static code_int maxmaxcode = 1 << BITS; /* should NEVER generate this code */

static count_int htab [HSIZE];
static unsigned short codetab [HSIZE];

static code_int free_ent = 0;           /* first unused entry */

extern FILE *datunit;
static int initialize = 0;

/* Define our function prototypes */

code_int getcode(void);

/*
 *  Block compression parameters -- after all codes are used up,
 *  and compression rate changes, start over.
 *
 */

static int block_compress = BLOCK_MASK;
static int clear_flg = 0;

/*---------------------------------------------------------------------*/

int LZOPEN (fname)

const char *fname;

{
    /* Open input file */

	if (datunit) fclose (datunit);
	if (!(datunit = fopen (fname, "rb"))) return (0);

    /* Check the magic number */

	if ((fgetc (datunit) != 0x1F) || (fgetc (datunit) != 0x9D)) {
        fprintf (stderr, "%s: not in compressed format\n", fname);
        return (0);
    }

	maxbits = fgetc (datunit);        /* set bits from file */
    block_compress = maxbits & BLOCK_MASK;
    maxbits &= BIT_MASK;
    maxmaxcode = 1 << maxbits;

    if (maxbits > BITS) {
        fprintf (stderr,
	  "%s: compressed with %d bits, can only handle %d bits\n",
          fname, maxbits, BITS);
        return (0);
    }

    initialize = 1;
    return (1);
}

/*---------------------------------------------------------------------*/

int LZREAD (bufp, request)

char_type *bufp;
const int *request;
{
    static char_type *stackp;
    static code_int code, oldcode, incode, finchar;
    int count;

    count = 0;
	if (ferror (datunit)) perror ("LZread");
        
    if (initialize) {
        initialize = 0;
      
        /* Initialize the first 256 entries in the table */

        maxcode = MAXCODE (n_bits = INIT_BITS);

        for (code = 255; code >= 0; code--) {
            tab_prefixof (code) = 0;
            tab_suffixof (code) = (char_type) code;
        }

        free_ent = ((block_compress) ? FIRST : 256);

        finchar = oldcode = getcode();

        stackp = de_stack;

        if (oldcode == -1) return (0);	/* EOF already??? */

	*bufp++ = (char_type) oldcode;
	count = 1;

    } /* if initialize */

    while (stackp > de_stack) {
	*bufp++ = (char_type) *--stackp;
	if (++count >= *request) return (count);
    }

    while ((code = getcode ( )) > -1) {

        if ((code == CLEAR) && block_compress) {

            for (code = 255; code >= 0; code--)
                tab_prefixof (code) = 0;

            clear_flg = 1;
            free_ent = FIRST - 1;

            if ((code = getcode ( )) == -1)     /* O, untimely death! */
                break;
        }

        incode = code;

        /* Special case for KwKwK string */

        if (code >= free_ent) {
            *stackp++ = finchar;
            code = oldcode;
        }

        /* Generate output characters in reverse order */

        while (code >= 256) {
            *stackp++ = tab_suffixof (code);
            code = tab_prefixof (code);
        }

        *stackp++ = finchar = tab_suffixof (code);

        /* Generate the new entry */

        if ((code = free_ent) < maxmaxcode) {
            tab_prefixof (code) = (unsigned short) oldcode;
            tab_suffixof (code) = finchar;
            free_ent = code + 1;
        } 

        /* Remember previous code */

        oldcode = incode;

        /* return characters in forward order */

        while (stackp > de_stack) {
	    *bufp++ = (char_type) *--stackp;
	    if (++count >= *request) return (count);
        }
    }

    return (count);
    
}

/*
 *  g e t c o d e
 *
 *  Read one code from the standard input.  If EOF, return -1.
 *
 */

code_int getcode ( ) 
{
    register code_int code;
    static int offset = 0, size = 0;
    static char_type buf[BITS];
    register int r_off, bits;
    register char_type *bp = buf;

    if (clear_flg > 0 || 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 any bigger now */
            else
                maxcode = MAXCODE (n_bits);
        }

        if (clear_flg > 0) {
            maxcode = MAXCODE (n_bits = INIT_BITS);
            clear_flg = 0;
        }

	size = fread (buf, 1, n_bits, datunit);

/*	printf ("fread %d\n", size);	 DEBUG */

        if (size <= 0)
            return (-1);                /* End of file */

        offset = 0;

        /* Round size down to an integral number of codes */

        size = (size << 3) - (n_bits - 1);
    }

    r_off = offset;
    bits = n_bits;

    /* Get to the first byte */

    bp += (r_off >> 3);
    r_off &= 7;

    /* Get first part (low order bits) */

    code = (*bp++ >> r_off);
    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) {
        code |= *bp++ << r_off;
        r_off += 8;
        bits -= 8;
    }

    /* Handle the high order bits */

    code |= (*bp & rmask[bits]) << r_off;
    offset += n_bits;

    return (code);
}

/*---------------------------------------------------------------------*/

int LZCLOSE ( )

{
	return (fclose (datunit));
}
