/* SP.C Robert A. Hearn */ #include #include #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 */ } /* ¥¥¥ 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 == 'Õ') { apost = 1; /* we've seen "Õ" */ } else if (c == 's' && apost == 1) { apost = 2; /* we've seen "Õs" */ } else if (apost == 2) { if (!(ctype & TEPUNC)) { apost = 0; /* we've seen "Õ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 "Õ" and "s" don't get */ flags |= ctype; /* erroneously set. E.g., "123Õ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 == 'Ú' || c == 'Ö') { ++rp; flags |= BEFORESLASH; } else if (c == '-' || c == 'Ð' || c == 'Ñ') { flags |= BEFOREHYPH; ++rp; } flags &= ~(TBPUNC + TEPUNC); wbuf[endpos] = '\0'; /* Strip 's and set HASPOSS */ if (endpos > 2 && ((c = wbuf[endpos - 2]) == '\'' || c == 'Õ') && 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 != 'Ð' && c != 'Ñ') *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; }