antoine-source/appleworksgs/Spell/Src/SP.C
2023-03-04 03:45:20 +01:00

1 line
19 KiB
C
Executable File
Raw Permalink Blame History

/* SP.C Robert A. Hearn */
#include <types.h>
#include <memory.h>
#include "sp.h"
#include "spdef.h"
#include "sc.h"
#include "scparam.h"
#include "string.h"
/*#include "ctype.h"*/
#include "stdio.h"
#include "query.h"
#include "clam.h"
#include "lex.h"
#include "corelex.h"
#include "driver.h"
/* SP.C: This file contains the SP routines, which interface to the Proximity
routines. The assembly language interface to these routines is in spint.src.
Unless I hand-code some of these, which seems fairly probable. */
extern char *SPJumpLoc[];
extern unsigned _SPActive[];
extern unsigned _THActive;
/* SP private globals */
unsigned _SPListAlts;
unsigned _SPXVal;
unsigned _SPID;
long _SPHandler;
char *_SPdir;
char *WcurDoc; /* so it can be updated */
char *SPCancelled = "\pSpell checking was cancelled.";
char *SPBroken = "\pCould not open the user dictionary.\rIt may be corrupt.";
/* Stuff local to sp.c */
static unsigned inhyph; /* In hypenated word to scan word-by-word */
static unsigned mustbecap; /* This word must be capitalized */
static unsigned repeating; /* Calling GetWord at same place as before */
static unsigned wantperiod; /* GetWord should attach ending pd to word */
static unsigned firsttime; /* First time through SPCheck? */
static char *cmndtab; /* Command char table */
static char lastword[MAXWORD]; /* Previous word */
static unsigned lastflags; /* Previous word's flags */
static unsigned cancelled; /* Cancelled? */
static unsigned errcode; /* What spelling error occurred? */
static unsigned result;
/* These are maintained by lots of things. */
static char *sp; /* Word start ptr */
static char *ep; /* Word end ptr */
static char *rp; /* Ptr to rest of text (after endpunc) */
char *prevp; /* pointer to the end of the previous word */
/* These are maintained by GetWord. */
static char wbuf[MAXWORD]; /* Word buffer */
static unsigned flags; /* Word characteristics */
static char epfirst; /* First chararacter in end punctuation */
static char eplast; /* Last character in end punctuation */
static char epnum; /* Number of end punctuation chars */
/* This is maintained by CheckWord. */
static char corbuf[MAXWORD]; /* Corrected word if SPCMISPUNC or SPCHYPH. */
/* This is for the udict stuff */
CLAM *_SPudtable[SPMAXUDICT]; /* CLAM ptrs */
char **_SPudpn[SPMAXUDICT]; /* Pathname handles */
/* Static declarations for wimpy C compiler */
static int NextChar();
static unsigned EatWhite();
static unsigned GetWord();
static unsigned CheckWord();
static void MakeLower();
static unsigned GetUDSlot();
extern pascal unsigned _SPMetaHandler();
/* Startup the spell checker. */
pascal void _SPStartUp(mmid, spdir)
unsigned mmid;
char *spdir;
{
unsigned len;
unsigned x;
if (_SPActive[0])
SPErr2(SPACTIVE, 0);
_SPErrNum[0] = 0;
_SPID = _SPTHID[0] = mmid + SPTHID;
toolErr[0] = &_toolErr; /* &%^$%$!@@!@!! */
if (!meminit())
SPErr2(SPNOMEM, 0);
len = *(unsigned *) spdir;
_SPdir = spdir+2;
_SPdir[len] = '\0';
if (!clxopen(1, SP_CORRECT + SP_DETECT))
if (*toolErr[0])
{
SPErr2(SPPRODOS, *toolErr[0]);
}
else
{
SPErr2(SPCANTDOIT, 0);
}
strcat(_SPdir, "Dictionary");
if (!lexopen(2, SP_CORRECT + SP_DETECT, _SPdir))
{
if (*toolErr[0])
{
SPErr2(SPPRODOS, *toolErr[0]);
}
else
{
SPErr2(SPCANTDOIT, 0);
}
}
for (x = 0; x < SPMAXUDICT; ++x)
_SPudtable[x] = NULL;
_SPActive[0] = 1;
}
/* Shut down SP */
pascal void _SPShutDown()
{
unsigned x;
if (!_SPActive[0])
SPErr2(SPINACTIVE, 0);
/* Close the dictionaries */
for (x = 0; x < SPMAXUDICT; ++x)
if (_SPudtable[x])
{
clamclose(_SPudtable[x]);
DisposeHandle(_SPudpn[x]);
}
clxclose();
lexclose();
_SPErrNum[0] = 0;
_SPActive[0] = 0;
}
/* Spell-check the text at tp. */
pascal void _SPCheck(tp, ct, mode, h, curdoc, wcancel)
char *tp; /* Ptr to to text */
char *ct; /* Ptr to command table */
unsigned mode; /* Initial mode */
long h; /* Error handler - must lie to the compiler
about type. 'pascal' keyword not valid here. */
char *curdoc; /* in case it has to be updated */
unsigned *wcancel; /* if you cancel, let WP know about it */
{
if (!_SPActive[0])
SPErr2(SPINACTIVE, 0);
_SPErrNum[0] = 0;
WcurDoc = curdoc;
if (!*Clxdata)
{
clxfree();
if (!getacorelex())
SPErr2(SPNOMEM, 0);
}
if (mode == SPMSENT)
mustbecap = 1;
else if (mode == SPMNOSENT)
mustbecap = 0;
else
SPErr2(SPBADMODE, 0);
inhyph = repeating = wantperiod = 0;
lastword[0] = 0;
flags = 0;
sp = tp;
_SPHandler = h; /* make it global */
cmndtab = ct ? ct : _SPdeftab;
while (1)
{
asm {
jsl D_BEACHBALL
bcc OK
}
*wcancel = 1;
(void) D_ALERTBOX(okBox,SPCancelled);
return;
OK:
if (!EatWhite())
SPErr2(_SPErrNum[0], _SPXVal); /* _SPErrNum may be 0 */
/* GetWord gets a word and determines its flags. */
prevp = ep;
if (!GetWord())
{
if (_SPErrNum[0])
SPErr2(_SPErrNum[0], _SPXVal);
/* Hit a final break... quit if cancel or buf empty. */
if (cancelled || !wbuf[0])
return;
}
/* See if adding a period is required and we have one to attach
(determined in CheckWord). If so, there may still be other
punctuation errors. */
if ((errcode = CheckWord()) == WANTPD)
{
strcat(wbuf, ".");
if (isword(wbuf))
{
/* Rescan the word, taking final period this time. May seem
a little extreme, but the state information is needed, and
other errors may occur. */
wantperiod = repeating = 1;
continue;
}
else
{
wbuf[strlen(wbuf) - 1] = 0;
errcode = SPCMISSPELL;
}
}
if (errcode)
{
/* If it has hyphens and its unhyphenated version is not a
word, try looking at it word by word. */
if (flags & THYPHEN && errcode != SPCHYPH)
{
repeating = inhyph = 1;
continue;
}
else
{
switch((result = _SPMetaHandler(errcode, sp, ep, corbuf, prevp)) &
CODEMASK)
{
case SPHACCEPT:
break;
case SPHREPLACE:
repeating = 1;
case SPHJUMP:
if (SPJumpLoc[0])
{
sp = SPJumpLoc[0];
SPJumpLoc[0] = NULL;
}
continue;
case SPHCANCEL:
case SPHFINISH:
return;
default:
SPErr2(SPBADRESULT, result);
}
}
}
strcpy(lastword, wbuf);
MakeLower(lastword);
lastflags = flags;
inhyph = inhyph && flags & BEFOREHYPH;
mustbecap = !(flags & BEFOREHYPH + BEFORESLASH) && (eplast == '.' ||
eplast == '?' || eplast == '!') && (epnum == 1);
repeating = 0;
sp = rp;
}
}
/* Suggest correct spellings for wptr (presumed incorrect). Return corrections
in Pascal format. */
static char *justcor[2];
pascal char **_SPSuggest(wptr, listalts)
char *wptr;
unsigned listalts;
{
char **result;
char **rp;
if (!_SPActive[0])
SPErr(SPINACTIVE, 0, NULL);
_SPErrNum[0] = 0;
if (!*Clxdata)
{
clxfree();
if (!getacorelex())
SPErr(SPNOMEM, 0, NULL);
}
if ((_SPListAlts = listalts) > MAXLISTALTS)
SPErr(SPTOOMANY, 0, NULL);
if (wptr == NULL)
{
/* Use the word SPCheck gave to the handler. If SPCMISPUNC or
SPCHYPH, just give corbuf (already in Pascal format). */
if ((errcode == SPCMISPUNC) || (errcode == SPCHYPH) || (errcode ==
SPCCAP))
{
justcor[0] = corbuf;
justcor[1] = NULL;
return(justcor);
}
result = correct(wbuf);
}
else
{
p2cstr(wptr);
result = correct(wptr);
c2pstr(wptr);
}
for (rp = result; *rp; ++rp)
c2pstr(*rp);
return(result);
}
/* Open a previously created user dictionary */
pascal unsigned _SPOpenUDict(name)
char *name; /* this is already a C string */
{
unsigned refnum;
if (!_SPActive[0])
SPErr(SPINACTIVE, 0, 0);
_SPErrNum[0] = 0;
_SPTHID[0] = _SPID;
/* Find an unused udict slot if there is one */
for (refnum = 0; refnum < SPMAXUDICT; ++refnum)
if (!_SPudtable[refnum])
break;
if (_SPudtable[refnum])
SPErr(SPTOOMANY, 0, 0);
if (!(_SPudtable[refnum] = clamopen(refnum + 3, SP_CORRECT + SP_DETECT,
name, 0, 0L)))
{
if (*toolErr[0])
{
SPErr(SPPRODOS, *toolErr[0], 0);
}
else
{
SPErr(SPCANTDOIT, 0, 0);
}
}
_SPudpn[refnum] = (char **) NewHandle((long) strlen(name) + 1, _SPID,
0x0010, 0L);
strcpy(*_SPudpn[refnum], name);
return(refnum + 1); /* Publicly, refnums begin at 1 */
}
/* <20><><EFBFBD> Dead Code
/* Close a user dictionary *
pascal void _SPCloseUDict(refnum)
unsigned refnum;
{
if (!_SPActive[0])
SPErr2(SPINACTIVE, 0);
_SPErrNum[0] = 0;
--refnum;
if (refnum < 0 || refnum >= SPMAXUDICT)
SPErr2(SPBADREF, 0);
if (!_SPudtable[refnum])
SPErr2(SPBADREF, 0);
clamclose(_SPudtable[refnum]);
_SPudtable[refnum] = NULL;
DisposeHandle(_SPudpn[refnum]);
}
*/
/* Create a new user dictionary */
pascal void _SPNewUDict(name, ftype, atype)
char *name; /* already a C string */
unsigned ftype;
long atype;
{
CLAM *cp;
if (!_SPActive[0])
SPErr2(SPINACTIVE, 0);
_SPErrNum[0] = 0;
_SPTHID[0] = _SPID;
/* Possible list #s are 1 - 10; use 11 */
if (!(cp = clamopen(11, CL_INIT, name, ftype, atype)))
{
SPErr2(SPPRODOS, *toolErr[0]);
}
clamclose(cp);
}
/* Add a word to a user dictionary */
pascal void _SPAddWord(refnum, word)
unsigned refnum;
char *word;
{
char buf[2*MAXWORD];
unsigned flags;
char *thePath, *pos;
HANDLE f;
extern char *c2pstr1();
extern HANDLE stdcreate();
extern void stdclose();
if (!_SPActive[0])
SPErr2(SPINACTIVE, 0);
_SPErrNum[0] = 0;
_SPTHID[0] = _SPID;
if (refnum < 1 || refnum > SPMAXUDICT)
SPErr2(SPBADREF, 0);
if (!_SPudtable[refnum - 1])
SPErr2(SPBADREF, 0);
/* see if we have some disk space */
thePath = *_SPudpn[refnum-1];
if (pos = strrchr(thePath,':'))
pos++;
else
pos = thePath;
strcpy(pos, "bobo.temp");
if ((f = stdcreate(thePath, 0, 0L)) != H_ERROR) {
char *path1;
stdclose(f);
f = 0;
if ((path1 = c2pstr1(thePath)) != (char *) 0xFFFFFFFFL) {
D_DESTROY2(path1);
D_DISPOSEPTR(path1);
}
} else
D_ALERTBOX(okBox,"\pThe word could not be added.");
strcpy(pos,"User.Dictionary");
if (f)
return;
p2cstr(word);
flags = doflags(word, buf, MAXWORD);
if (clamadd(buf, flags, _SPudtable[refnum - 1]) != OKAY)
{
c2pstr(word);
if (*toolErr[0])
{
_SPErrNum[0] = SPPRODOS;
_SPXVal = *toolErr[0];
}
else
{
_SPErrNum[0] = SPCANTDOIT;
_SPXVal = 0;
}
}
c2pstr(word);
/* force a flush of any buffers */
clamclose(_SPudtable[refnum-1]);
if (!(_SPudtable[refnum-1] = clamopen(refnum+2, SP_DETECT+SP_CORRECT, *_SPudpn[refnum-1], 0xD0, 0x8001L)))
D_ALERTBOX(okBox,SPCancelled);
}
/* */
/* Support routines follow */
/* */
/* NextChar finds the next real character from a specified position. Positions
the pointer there and returns the char, or -1 if nonrecoverable break.
Char may be different from what is pointed to (if SPHSUBST). Sets cancelled
if cancelled. */
static int NextChar(tpp)
char **tpp;
{
unsigned isreal;
while (1)
{
if ((isreal = cmndtab[(unsigned) **tpp]) > 1)
{
*tpp += isreal - 1;
continue;
}
if (isreal)
return((unsigned) **tpp);
switch ((result = _SPMetaHandler(SPCBREAK, *tpp, 0L, NULL, 0L)) & CODEMASK)
{
case SPHJUMP:
*tpp = SPJumpLoc[0];
SPJumpLoc[0] = NULL;
continue;
case SPHSUBST: /* this shouldn't be possible. The handler in WP isn't capable */
return (result & DATAMASK);
case SPHCANCEL:
cancelled = 1;
case SPHFINISH:
return(-1);
default:
SPErr(SPBADRESULT, 0, -1);
}
}
}
/* Eat (optional) white space characters. Leave sp pointing to the char we
stopped on. If we run into a break we can't skip, return 0. */
static unsigned EatWhite()
{
int c;
while (1)
{
if ((c = NextChar(&sp)) == -1)
return (0);
if (!(chartype[c] & TWSPACE))
return (1);
++sp;
}
}
/* Put the next word into wbuf. Set flags. */
static unsigned GetWord()
{
int c;
unsigned buflen;
unsigned endpos;
unsigned stop;
unsigned sawchar;
unsigned endtypes = 0;
unsigned finished = 0;
unsigned ctype;
unsigned apost=0;
if (repeating)
flags &= AFTERSLASH + AFTERHYPH + HASBEGINPUNC;
else if (flags & BEFORESLASH)
flags = AFTERSLASH;
else if (flags & BEFOREHYPH)
flags = AFTERHYPH;
else flags = 0;
/* Empty the buffer now, in case we exit early */
wbuf[0] = 0;
/* Skip beginning punctuation. OK if we end here, because isolated
punctuation is a special case of a nonalphanumeric word, which we
accept. */
for (c = NextChar(&sp); c != -1 && chartype[c] & TBPUNC; ++sp, c =
NextChar(&sp))
flags |= HASBEGINPUNC;
if (c == -1)
return (0);
ctype = chartype[c];
stop = TWSPACE + TSLASH + (inhyph ? THYPHEN : 0);
rp = sp;
sawchar = 0;
ep = NULL;
buflen = 0;
/* Find the end of the word (ep) and of the token (rp), building the
buffer and the flags. */
while (!(ctype & stop))
{
if (!sawchar && ctype & (TLETTER + TDIGIT))
{
ep = NULL;
epfirst = eplast = epnum = 0;
sawchar = 1;
flags |= endtypes;
}
else if (sawchar && (ctype & TEPUNC) && !(wantperiod && c == '.'))
{
ep = rp;
if (buflen >= MAXWORD)
endpos = MAXWORD-1;
else
endpos = buflen;
epfirst = eplast = c;
epnum = 1;
sawchar = endtypes = 0;
}
else if (ctype & TEPUNC)
{
eplast = c;
++epnum;
endtypes |= ctype;
}
if (buflen < MAXWORD)
wbuf[buflen] = (char) c;
++buflen;
if (c == '\'' || c == '<EFBFBD>') {
apost = 1; /* we've seen "<22>" */
} else if (c == 's' && apost == 1) {
apost = 2; /* we've seen "<22>s" */
} else if (apost == 2) {
if (!(ctype & TEPUNC)) {
apost = 0; /* we've seen "<22>sx", so blow it off */
flags |= chartype['\''] | chartype['s'];
} else
apost = 2;
} else /* nothin... */
apost = 0;
if (sawchar)
{
if (!apost) /* This way, the flags for "<22>" and "s" don't get */
flags |= ctype; /* erroneously set. E.g., "123<32>s" */
if (buflen >= MAXWORD)
flags |= OVERFLOW;
}
++rp;
if ((c = NextChar(&rp)) == -1)
if (cancelled)
return(0);
else
{
++finished;
break;
}
ctype = chartype[c];
}
if (!ep)
{
ep = rp;
if (buflen >= MAXWORD)
endpos = MAXWORD-1;
else
endpos = buflen;
}
else
flags |= HASENDPUNC;
if (c == '/' || c == '<EFBFBD>' || c == '<EFBFBD>')
{
++rp;
flags |= BEFORESLASH;
}
else if (c == '-' || c == '<EFBFBD>' || c == '<EFBFBD>')
{
flags |= BEFOREHYPH;
++rp;
}
flags &= ~(TBPUNC + TEPUNC);
wbuf[endpos] = '\0';
/* Strip 's and set HASPOSS */
if (endpos > 2 && ((c = wbuf[endpos - 2]) == '\'' || c == '<EFBFBD>') && wbuf[endpos - 1] == 's')
{
flags |= HASPOSS;
ep -= 2;
wbuf[endpos - 2] = '\0';
}
wantperiod = 0;
return (!finished);
}
static unsigned CheckWord()
{
char *p, *q, c;
/* Pass it if it's non-alphabetic */
if (!(flags & TLETTER))
return(0);
if (flags & OVERFLOW)
{
wbuf[MAXWORD-1] = 0;
return (SPCMISSPELL);
}
strcpy(corbuf, wbuf);
if (!(flags & (BEFORESLASH + AFTERSLASH + BEFOREHYPH + AFTERHYPH +
HASBEGINPUNC + OVERFLOW)) && !(lastflags & (BEFORESLASH + AFTERSLASH
+ BEFOREHYPH + AFTERHYPH + HASENDPUNC + OVERFLOW)))
{
MakeLower(corbuf);
if (!strcmp(lastword, corbuf))
return(SPCREPEAT);
}
if (strlen(wbuf) == 1 && (epfirst == '.' || epfirst == ')'))
return(0);
if (!isword(wbuf))
{
/* See if it has spurious hyphens */
if (flags & THYPHEN)
{
for (p = wbuf, q = corbuf; *p; ++p)
if ((c = *p) != '-' && c != '<EFBFBD>' && c != '<EFBFBD>')
*q++ = *p;
*q = 0;
if (isword(corbuf))
{
c2pstr(corbuf);
return(SPCHYPH);
}
}
/* See if we just need to add a period */
if (!(Quflags & (IW_ENDDOT + IW_ALLDOT)) && epfirst == '.')
return (WANTPD);
else if (Quwlist > 0)
{
undoflags(Qulookup, Quwflags, corbuf);
if ((Quwflags & IW_CASE) < (Quflags & IW_CASE))
undoflags(corbuf, Quflags & IW_CASE, corbuf);
c2pstr(corbuf);
return(SPCMISPUNC);
}
return(SPCMISSPELL);
}
if (mustbecap && islower(wbuf[0]))
{
corbuf[0] = _toupper(corbuf[0]);
c2pstr(corbuf);
return(SPCCAP);
}
return(SPNOERR);
}
static void MakeLower(s)
char *s;
{
--s;
while (*++s)
*s = tolower(*s);
}
SPCloseFiles()
{
unsigned x;
setbank;
/* Close main dict */
stdclose(Lexfile);
/* Close the udicts */
for (x = 0; x < SPMAXUDICT; ++x)
if (_SPudtable[x])
stdclose(_SPudtable[x]->cl_file);
restorebank;
}