/***********************************************************************\

    Filename: unfexc.c

\***********************************************************************/

#include <proxio.h>
#include "thesmisc.h"
#include "th.h"

#define GETNIB() ((nb = ~nb) ? (bt = ctoi(*pdptr++)) >> 4 : bt & 0x0F)

#define PXHEADER 8

static unfbin();

/* structure to handle the inflection exceptions processing */

static struct
{
    int   px_rev;
    int   px_nlet;
    int   px_wbin;
    int   px_wlbin;
    int   px_nbin;
    int   px_nwords;
    int   px_nbytes;
    int   px_dummy;
    int   *px_bintab;
    char  *px_lettab;
    char  *px_data;
} Pxinfo;

unfopen(ioptr)
HANDLE  ioptr;
{
char    *zalloc();
long    thesbit();

int i;
char *strptr;
int   *pxptr;
int     offset;
char   str[sizeof(unsigned) * PXHEADER];


    i = PXHEADER * sizeof(int);
    strptr = str;
    offset = i;
    while (--i >= 0)
        *strptr++ = thesbit(ioptr, 8);

    thintmv(str, (int *) &Pxinfo, PXHEADER);

    if ((Pxinfo.px_lettab = zalloc(_THID, (unsigned) Pxinfo.px_nlet)) == NULL
      || (Pxinfo.px_data = (char *) zalloc(_THID, (unsigned) Pxinfo.px_nbytes))
        == NULL || (Pxinfo.px_bintab = (int *) zalloc(_THID,
        (unsigned) Pxinfo.px_nbin * sizeof(int))) == NULL)
        return (FALSE);

    strptr = (char *) Pxinfo.px_lettab;
    i = Pxinfo.px_nlet;
    offset += i;
    while (--i >= 0)
        *strptr++ = thesbit(ioptr, 8);

    offset += (i = Pxinfo.px_nbin) * sizeof(int);
    pxptr = Pxinfo.px_bintab;
    while (--i >= 0)
    {
        *pxptr = thesbit(ioptr, 8);
        *pxptr |= thesbit(ioptr, 8) << 8;
        ++pxptr;
    }

    i = Pxinfo.px_nbytes;
    strptr = Pxinfo.px_data;
    offset += i;
    while (--i >= 0)
        *strptr++ = thesbit(ioptr, 8);

    return (offset);
}

unfclose()
{
    nzfree((char *)Pxinfo.px_data);
    nzfree((char *)Pxinfo.px_bintab);
    nzfree((char *)Pxinfo.px_lettab);
}

unfexc(word, excword, inftype)
char    *word;
char    *excword;
int     inftype;
{
int    nb;     /* flag set when nibble-getter needs a byte.*/
int    c;      /* index into px_lettab[] (see environ.h)   */
char *pdptr;
char *strptr; /* pointer to end of decompressed word */

char   *saveptr;       /* pointer to end of decompressed word */
int     bt;             /* latest byte read from corelex array */
int     chind;          /* index into px_lettab[] (see environ.h) */
int     cmp;
int     bs;
int     bin;
int     binwords;
int     refcnt;
int     refindex;
int     refind;
int     refeof;
int     reftype;
int     skip;
int     retval;
int     refiarr[MAXREF];
int     reftarr[MAXREF];
char   clword[MAXWORD];  /* holds word during decompression */

    bin = unfbin(word);
    binwords = bin < Pxinfo.px_nbin - 1 ? Pxinfo.px_wbin : Pxinfo.px_wlbin;
    pdptr = Pxinfo.px_data + Pxinfo.px_bintab[bin];
    strptr = clword + 1;

    retval = nb = FALSE;

    /* decompress until the word is found. If the decompressed word
       is greater than word or if at the end of the bin return ERROR */

    do
    {
        if (!binwords--)
            return (ERROR);
        bs = GETNIB() + 1;
        strptr -= bs;
        do
        {
            chind = 0;
            while ((c = GETNIB()) == 0xF)
                chind += 0xF;
            chind += c;
        }
        while (*strptr++ = Pxinfo.px_lettab[chind]);
        refcnt = 0;
        if ((cmp = strcmp(clword, word)) > 0)
            return (ERROR);
        do
        {
            refindex = GETNIB();
            refeof = refindex & 0x8;
            if (cmp == 0)
            {
                refindex = ((refindex & 0x7) << 4) + GETNIB();
                refindex = (refindex << 4) + GETNIB();
                reftype = GETNIB();
                reftype = ((reftype & 0x7) << 4) + GETNIB();
                refiarr[refcnt] = refindex;
                reftarr[refcnt++] = reftype;
            }
            else
            {
                GETNIB();
                GETNIB();
                GETNIB();
                GETNIB();
            }
        }
        while (!refeof);
    }
    while (cmp);

    strptr = excword + 1;
    for (refind = 0; refind < refcnt; refind++)
    {
        refindex = refind;
        skip = FALSE;

        /* If inftype is UNF_IR retain only unflection entries.
           If a certain type of inflection is specified, discard
           elements in reftarr that do not match inftype. */

        if ((inftype == UNF_IR && reftarr[refindex] & 1)
            || (inftype != UNF_IR && reftarr[refindex] != inftype))
            continue;
        if (retval != 0 && retval != reftarr[refindex])
            break;
        if ((retval = reftarr[refindex]) <= 0)
            return (retval);
        saveptr = strptr;
        bin = refiarr[refindex] / Pxinfo.px_wbin;
        binwords = refiarr[refindex] % Pxinfo.px_wbin + 1;
        pdptr = Pxinfo.px_data + Pxinfo.px_bintab[bin];
        nb = FALSE;
        while (binwords--)
        {
            bs = GETNIB() + 1;
            strptr -= bs;
            do
            {
                chind = 0;
                while ((c = GETNIB()) == 0xF)
                    chind += 0xF;
                chind += c;
            }
            while (*strptr++ = Pxinfo.px_lettab[chind]);
            do
            {
                refindex = GETNIB();
                refeof = refindex & 0x8;
                GETNIB();
                GETNIB();
                GETNIB();
                GETNIB();
            }
            while (!refeof);
        }

        /* Take care of multiple unflections. */

        if (!skip)
        {
            *(strptr - 1) = UNFSEP;
            *strptr++ = '\0';
        }
        else
            strptr = saveptr;
    }
    if (strptr == &excword[1])
        return (ERROR);
    *(strptr - 2) = '\0';
    return (retval);
}

static unfbin(word0)
char   *word0;
{
char *word;           /* actual word scanner */
char *pdptr;          /* pointer into Pxinfo.px_data[] */
int   bt;              /* holds a byte read from px_data */

int     bin;        /* number of bin to be compared with input word */
int     nb;         /* nibble-getter flag */
int     frindex;    /* index into px_lettab[] for actual character */
int     curindex; /* temporary index for processing hex-F sequences */
int     minval;
int     maxval;

    minval = 0;
    maxval = Pxinfo.px_nbin - 1;
    while (minval < maxval)
    {
        bin = (minval + maxval + 1) / 2;

        /* px_bintab[bin] is byte offset into corelex array
           (px_data[]) of start of next bin to be checked. */

        pdptr = Pxinfo.px_data + Pxinfo.px_bintab[bin];

        /* Decompress the first word in the bin, comparing it with
           the input word to see if the input word is in the bin. */

        nb = FALSE;

        /* get the backspace */

        while ((curindex = GETNIB()) == 0xF)
            ;

        word = word0 - 1;
        do
        {
            /* read nibbles to form the character index; */
            /* as in coreword(), sequence of hex-F's */
            /* can be arbitrarily long */

            frindex = curindex = GETNIB();
            while (curindex == 0x0F)
            {
                curindex = GETNIB();
                frindex += curindex;
            }
            if (*++word != Pxinfo.px_lettab[frindex])
                break;
        }
        while (*word);

        /* update the binary search limits; test depends on */
        /* string terminator ('\0') being the character with the */
        /* smallest value */

        if (ctoi(*word) >= ctoi(Pxinfo.px_lettab[frindex]))
            minval = bin;
        else
            maxval = bin - 1;
    }

    return (maxval);
}
