/* ufind.c -  find words in dict using wildcards */

#include "stdlib.h"
#include "exec/types.h"
#include "hardware/blit.h"
#include "exec/execbase.h"
#include "intuition/intuitionbase.h"

#include "workbench/startup.h"
#include "workbench/workbench.h"

#include "exec/memory.h"
#include "exec/io.h"
#include <exec/ports.h>
#include "devices/printer.h"
#include <libraries/dosextens.h>
#include "string.h"
#include "fcntl.h"

#include "functions.h"


#define UCHAR unsigned char

long lseek();
UCHAR               mask[66],word[66],wword[66],buf[66];
int                 maskLen,all;
unsigned short      ctable[28][28];
UCHAR               stat[456];
unsigned long       tb[4000];
int fdout;
UCHAR *uout = NULL;

UCHAR zword[80];


#define MINSTAT 0
#define MAXSTAT 227
#define MAXNULL 16
#define ADDSTAT 28
#define ZERO    0
#define APOSTROPHE 27
#define MINALPHA 1
#define MAXALPHA 26

short   doCaps = 0;

toLower()
{
register UCHAR *x,c;

        x = wword;
        strcpy(x,word);
        while (1) {
            c = *x;
            if (c==0) break;
            if (!doCaps) {
                if (c>='A' && c<='Z') *x |= 32;
            }
            x++;
        }
}

main(argc,argv)
int argc;
char **argv;
{
int                     fd;
UCHAR                   *q,*udtable,*udict,*pbuf;
register UCHAR          *p;
register unsigned long  *s;
register long           nextloc,loc;
register short          c1,c2,n,jj;
int                     len,letter,glen,letter2,m,k,kk,mm;
int                     tbsize;
UCHAR                   ch;

    fdout = NULL;
    fd = -1;
    if (argc<2) {
        puts("Uedit 2.0 word finder, (C) 1991 by Rick Stiles");
        puts("  usage: Ufind w?rd*mask* <-tTable> <-dDict> <-oOutFile>");
        puts("         (for uppercase output use a Capital Letter in mask)");
        exit(0);
    }
    strcpy(mask,argv[1]);
    maskLen = strlen(mask);
    glen = maskLen;
    for (n=0; n<maskLen; n++) {
        c1 = mask[n];
        if (c1>='A' && c1<='Z') doCaps = TRUE;
        if (c1>='a' && c1<='z') c1 &= ~32;
        if (c1>='A' && c1<='Z') { mask[n]=c1; continue; }
        if (c1=='?' || c1=='*') { if (n<3 && c1=='*') glen = 4; continue; }
        if (c1==39) continue;
        puts("Bad arg");
        exit(0);
    }
    all = FALSE;
    letter = mask[0];
    if (letter == 0) all = TRUE;
    if (letter=='*' && mask[1]==0x00) all = TRUE;
    if (all || letter=='*' || letter == '?') letter = 0;

    letter2 = mask[1];
    udtable = (UCHAR *)"udtable";
    udict = (UCHAR *)"udict";
    if (argc>1) {
        for (c1=1; c1<argc; c1++) {
            p = (UCHAR *)argv[c1];
            if (*p=='-') {
                p++;
                ch = *(p++);
                if (*p<'0') continue;
                if (ch=='t' || ch=='T') udtable = p;
                if (ch=='d' || ch=='D') udict = p;
                if (ch=='o' || ch=='O') uout = p;
            }
        }
    }

    fd = open(udtable,O_RDONLY);
    if (fd==-1) { puts("Can't find table"); goto BADOUT; }
    tbsize = lseek(fd,0L,2);
    lseek(fd,0L,0);
    if (tbsize<1000) goto BADOUT;

    if (read(fd,ctable,sizeof(ctable))!=sizeof(ctable)) goto BADOUT;
    if (ctable[0][0]!=65533) { puts("Not a dictionary"); goto BADOUT; }

    if (read(fd,stat,(size_t)456)!=456) goto BADOUT;

    tbsize -= (456 + sizeof(ctable));

    if (tbsize>16000) { puts("Table is too big!"); goto BADOUT; }

    if (read(fd,tb,(size_t)tbsize)!=tbsize) goto BADOUT;
    close(fd);

    fd = open(udict,O_RDONLY);
    if (fd==-1) { puts("Can't find dictionary"); goto BADOUT; }

    fdout = -1;
    if (uout!=NULL) {
        unlink(uout);
        fdout = open(uout,O_CREAT | O_RDWR);
        if (fdout==-1) { puts("Can't create output file"); goto BADOUT; }
    }

    for (k=1; k<28; k++) {
        kk = k + 63;
        if (k==1) kk = 39;
        for (m=0; m<28; m++) {
            mm = m + 63;
            if (m==1) mm = 39;
            n = ctable[k][m];
            if (n==0) continue;
            s = tb + n - 1;

            if (!all && letter) {
                if (kk<letter) continue;
                if (kk==letter && letter2>='A' && mm>letter2) goto BADOUT;
                if (kk>letter) goto BADOUT; /* done */
            }
            q = word;
            q[0] = kk;
            q[1] = mm;

            n = *s & 0x0700;
            while (1) {
                q = word;
                jj = *s & 0x00ff;
                if (jj == 1) jj = 39;
                else if (jj) jj += 63;
                q[2] = jj;
                q[3] = 0x00;
                loc = *s >> 11L;
                nextloc = *(s+1) >> 11L;
                if (n  & 0x0600) {                              /* short word */
                    if (matchMask(mask,word)) { toLower(); puts(wword); writeWord(); }
                    if (n==0x0400) goto BUMP;   /* no longer words using it */
                }
                if (glen<4) goto BUMP;                      /* word len <= 3 */
                p = &(word[3]);     /* will build words & compare with mask */
                lseek(fd,loc,0);
                pbuf = buf;
                len = read(fd,pbuf,(size_t)64) + 1;
                if (len<2) goto BADOUT;
                while (1) {                                     /* grab a words */
                    if (loc>=nextloc) break;
                    if (--len<=0) {
                        pbuf = buf;
                        len = read(fd,pbuf,(size_t)64);
                        if (len<1) break;
                    }
                    loc++;
                    c1 = *(pbuf++);
                    c2 = 256;
                    if (c1>=ADDSTAT) {
                        c1 -= ADDSTAT;
                        q = stat + (c1 << 1);
                        c1= *(q++);
                        c2 = *q;
                    }
                    if (c1>1) c1 += 63;
                    else if (c1==1) c1 = 39;
                    *(p++) = c1;
                    if (c2!=256) {
                        if (c2>1) c2 += 63;
                        else if (c2==1) c2 = 39;
                        *(p++) = c2;
                    }
                    if (c1==0 || c2==0) {
                        *p = 0x00;
                        if (matchMask(mask,word)) {
                                toLower();
                                puts(wword);
                                writeWord();
                        }
                        p = &(word[3]);
                    }
                }
BUMP:
                s++;
                n = *s & 0x0700;
                if (n & 0x0100) break;
            }
        }
    }

BADOUT:
    if (fd!=-1) close(fd);
    if (fdout!=-1) close(fdout);
    exit(0);
}

int written = 0;
UCHAR temp[64];

writeWord()
{
char ch;

    if (fdout!=-1) {
        ch = 10;
        lseek(fdout,0L,2);
        write(fdout,wword,(size_t)strlen(wword));
        write(fdout,&ch,(size_t)1);
        written = TRUE;
    }
}

matchMask(mm,zz)
register UCHAR *mm,*zz;
{
UCHAR *oldmm;
register int rtn;

    if (all) { rtn = TRUE; goto RTN; }
    rtn = FALSE;
    while (1) {
        if (*mm==0x00) {
            if (*zz==0x00) rtn = TRUE;
            goto RTN;
        }
        if (*zz==0x00) {
            if (*mm=='*' && *(mm+1)==0x00) rtn = TRUE;
            goto RTN;
        }
        if (*mm=='*') {
                oldmm = mm;
                while (*mm=='*' || *mm=='?') mm++;
                if (*mm==0x00) { rtn = TRUE; goto RTN; }
HITMORE:
                while (*zz!=*mm) {
                    zz++;
                    if (*zz==0x00) goto RTN;    /* word run out */
                }
                rtn = matchMask(mm,zz);
                if (rtn) goto RTN;
                zz++;
                if (*zz==0x00) goto RTN;
                goto HITMORE;
        }
        if (*mm=='?') {
                if (*zz==0x00) goto RTN;
                goto BUMP;
        }
        if (*mm!=*zz) goto RTN;
 BUMP:
        mm++;
        zz++;
    }
 RTN:
    return(rtn);
}
