/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Author: ShonK Project: Kauai Copyright (c) Microsoft Corporation Spell checker support using CSAPI compliant spell checkers. ***************************************************************************/ #include "frame.h" ASSERTNAME RTCLASS(SPLC) /*************************************************************************** Constructor for the spell checker. ***************************************************************************/ SPLC::SPLC(void) { } /*************************************************************************** Destructor for the spell checker. ***************************************************************************/ SPLC::~SPLC(void) { AssertThis(0); if (_fSplidValid) { if (_fMdrsValid) SpellCloseMdr(_splid, &_mdrs); if (_fUdrValid) SpellCloseUdr(_splid, _udr, fTrue); SpellTerminate(_splid, fTrue); } #ifdef WIN if (hNil != _hlib) FreeLibrary(_hlib); #endif //WIN } /*************************************************************************** Static method to create a new spell checker. ***************************************************************************/ PSPLC SPLC::PsplcNew(SC_LID sclid, PSTN pstnCustom) { PSPLC psplc; AssertNilOrPo(pstnCustom, 0); if (pvNil == (psplc = NewObj SPLC)) return pvNil; if (!psplc->_FInit(sclid, pstnCustom)) ReleasePpo(&psplc); AssertNilOrPo(psplc, 0); return psplc; } /*************************************************************************** Initializes the spell checker - finds the dll, loads the default dictionary. ***************************************************************************/ bool SPLC::_FInit(SC_LID sclid, PSTN pstnCustom) { AssertThis(0); AssertNilOrPo(pstnCustom, 0); SC_WSC wsc; FNI fni; if (!_FEnsureDll(sclid)) return fFalse; ClearPb(&wsc, size(wsc)); wsc.bHyphenHard = '-'; wsc.bEmDash = 151; wsc.bEnDash = 150; wsc.bEllipsis = 133; wsc.rgParaBreak[0] = kchReturn; Win( wsc.rgParaBreak[1] = kchLineFeed; ) if (secNOERRORS != SpellInit(&_splid, &wsc)) return fFalse; _fSplidValid = fTrue; if (!_FEnsureMainDict(sclid, &fni)) return fFalse; if (pvNil != pstnCustom && !_FEnsureUserDict(pstnCustom, &fni)) return fFalse; return fTrue; } /*************************************************************************** Find the spelling dll and link the functions we need. ***************************************************************************/ bool SPLC::_FEnsureDll(SC_LID sclid) { AssertThis(0); #ifdef WIN AssertVar(_hlib == hNil, "why is _hlib not nil?", &_hlib); HKEY hkey; long cb, lwType; STN stn; SZ sz; stn.FFormatSz( PszLit("SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools") PszLit("\\Spelling\\%d\\Normal"), sclid); if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, stn.Psz(), 0, KEY_QUERY_VALUE, &hkey)) { goto LError; } if (ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("Engine"), pvNil, (ulong *)&lwType, pvNil, (ulong *)&cb) || lwType != REG_SZ || cb >= size(sz) || ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("Engine"), pvNil, (ulong *)&lwType, (byte *)sz, (ulong *)&cb)) { RegCloseKey(hkey); goto LError; } RegCloseKey(hkey); if (hNil == (_hlib = LoadLibrary(sz))) goto LError; if (pvNil == (*(void **)&_pfnInit = (void *)GetProcAddress(_hlib, MIR(2))) || pvNil == (*(void **)&_pfnOptions = (void *)GetProcAddress(_hlib, MIR(3))) || pvNil == (*(void **)&_pfnCheck = (void *)GetProcAddress(_hlib, MIR(4))) || pvNil == (*(void **)&_pfnTerminate = (void *)GetProcAddress(_hlib, MIR(5))) || pvNil == (*(void **)&_pfnOpenMdr = (void *)GetProcAddress(_hlib, MIR(7))) || pvNil == (*(void **)&_pfnOpenUdr = (void *)GetProcAddress(_hlib, MIR(8))) || pvNil == (*(void **)&_pfnAddUdr = (void *)GetProcAddress(_hlib, MIR(9))) || pvNil == (*(void **)&_pfnAddChangeUdr = (void *)GetProcAddress(_hlib, MIR(10))) || pvNil == (*(void **)&_pfnClearUdr = (void *)GetProcAddress(_hlib, MIR(12))) || pvNil == (*(void **)&_pfnCloseMdr = (void *)GetProcAddress(_hlib, MIR(15))) || pvNil == (*(void **)&_pfnCloseUdr = (void *)GetProcAddress(_hlib, MIR(16)))) { goto LError; } AssertThis(0); return fTrue; LError: PushErc(ercSpellNoDll); return fFalse; #else //!WIN RawRtn(); //REVIEW shonk: Mac: implement _FEnsureDll return fFalse; #endif //!WIN } /*************************************************************************** Find the main dictionary and load it ***************************************************************************/ bool SPLC::_FEnsureMainDict(SC_LID sclid, PFNI pfni) { AssertThis(0); AssertNilOrPo(pfni, 0); #ifdef WIN HKEY hkey; long cb, lwType; STN stn; SZ sz; stn.FFormatSz( PszLit("SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools") PszLit("\\Spelling\\%d\\Normal"), sclid); if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, stn.Psz(), 0, KEY_QUERY_VALUE, &hkey)) { goto LError; } if (ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("Dictionary"), pvNil, (ulong *)&lwType, pvNil, (ulong *)&cb) || lwType != REG_SZ || cb >= size(sz) || ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("Dictionary"), pvNil, (ulong *)&lwType, (byte *)sz, (ulong *)&cb)) { RegCloseKey(hkey); goto LError; } RegCloseKey(hkey); if (pvNil != pfni) { STN stn = sz; if (!pfni->FBuildFromPath(&stn)) goto LError; } if (!_FLoadDictionary(sclid, sz, &_mdrs)) goto LError; _fMdrsValid = fTrue; AssertThis(0); return fTrue; LError: PushErc(ercSpellNoDict); return fFalse; #else //!WIN RawRtn(); //REVIEW shonk: Mac: implement _FEnsureMainDict return fFalse; #endif //!WIN } /*************************************************************************** Find the main dictionary and load it ***************************************************************************/ bool SPLC::_FEnsureUserDict(PSTN pstnCustom, PFNI pfniDef) { AssertThis(0); AssertPo(pstnCustom, 0); #ifdef WIN HKEY hkey; long cb, lwType; SZ sz; STN stn; FNI fni; if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, PszLit("SOFTWARE\\Microsoft\\Shared Tools Location"), 0, KEY_QUERY_VALUE, &hkey)) { goto LNoKey; } if (ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("PROOF"), pvNil, (ulong *)&lwType, pvNil, (ulong *)&cb) || lwType != REG_SZ || cb >= size(sz) || ERROR_SUCCESS != RegQueryValueEx(hkey, PszLit("PROOF"), pvNil, (ulong *)&lwType, (byte *)sz, (ulong *)&cb)) { RegCloseKey(hkey); LNoKey: if (pvNil == pfniDef) goto LError; fni = *pfniDef; } else { RegCloseKey(hkey); stn = sz; if (!fni.FBuildFromPath(&stn, kftgDir)) goto LError; } if (!fni.FSetLeaf(pstnCustom, kftgDictionary)) goto LError; fni.GetStnPath(&stn); if (!_FLoadUserDictionary(stn.Psz(), &_udr, fTrue)) goto LError; _fUdrValid = fTrue; AssertThis(0); return fTrue; LError: PushErc(ercSpellNoUserDict); return fFalse; #else //!WIN RawRtn(); //REVIEW shonk: Mac: implement _FEnsureUserDict return fFalse; #endif //!WIN } /*************************************************************************** Load a particular dictionary given its path. ***************************************************************************/ bool SPLC::_FLoadDictionary(SC_LID sclid, PSZ psz, SC_MDRS *pmdrs) { AssertThis(0); AssertSz(psz); AssertVarMem(pmdrs); if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } if (secNOERRORS != SpellOpenMdr(_splid, psz, pvNil, fFalse, fTrue, sclid, pmdrs)) { return fFalse; } return fTrue; } /*************************************************************************** Load a particular user dictionary given its path. ***************************************************************************/ bool SPLC::_FLoadUserDictionary(PSZ psz, SC_UDR *pudr, bool fCreate) { AssertThis(0); AssertSz(psz); AssertVarMem(pudr); SC_BOOL fReadOnly; if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } if (secNOERRORS != SpellOpenUdr(_splid, psz, FPure(fCreate), IgnoreAlwaysProp, pudr, &fReadOnly)) { return fFalse; } return fTrue; } /*************************************************************************** Set spelling options. ***************************************************************************/ bool SPLC::FSetOptions(ulong grfsplc) { AssertThis(0); if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } if (secNOERRORS != SpellOptions(_splid, grfsplc)) return fFalse; return fTrue; } /*************************************************************************** Check the spelling of stuff in the given buffer. ***************************************************************************/ bool SPLC::FCheck(achar *prgch, long cch, long *pichMinBad, long *pichLimBad, PSTN pstnReplace, long *pscrs) { AssertThis(0); AssertIn(cch, 0, ksuMax); AssertPvCb(prgch, cch * size(achar)); AssertVarMem(pichMinBad); AssertVarMem(pichLimBad); AssertPo(pstnReplace, 0); AssertVarMem(pscrs); SC_SIB sib; SC_SRB srb; SC_SEC sec; SZ sz; byte bRate; pstnReplace->SetNil(); *pichMinBad = *pichLimBad = cch; if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } ClearPb(&sib, size(sib)); sib.cch = (ushort)cch; sib.lrgch = prgch; sib.cMdr = 1; sib.lrgMdr = &_mdrs.mdr; if (_fUdrValid) { sib.cUdr = 1; sib.lrgUdr = &_udr; } ClearPb(&srb, size(srb)); srb.lrgsz = sz; srb.cch = size(sz) / size(achar); srb.lrgbRating = &bRate; srb.cbRate = 1; sec = SpellCheck(_splid, sccVerifyBuffer, &sib, &srb); if (sec != secNOERRORS) return fFalse; *pscrs = srb.scrs; switch (srb.scrs) { case scrsNoErrors: return fTrue; case scrsReturningChangeAlways: case scrsReturningChangeOnce: *pstnReplace = srb.lrgsz; break; } *pichMinBad = srb.ichError; *pichLimBad = srb.ichError + srb.cchError; return fTrue; } /*************************************************************************** Get the istn'th suggestion for the given word. ***************************************************************************/ bool SPLC::FSuggest(achar *prgch, long cch, bool fFirst, PSTN pstn) { AssertThis(0); AssertIn(cch, 1, ksuMax); AssertPvCb(prgch, cch * size(achar)); AssertPo(pstn, 0); SC_SIB sib; SC_SRB srb; SC_SEC sec; SC_CC sccc; pstn->SetNil(); if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } if (!fFirst && _ichSuggest < CvFromRgv(_rgchSuggest) && _rgchSuggest[_ichSuggest] != 0) { // have another suggestion in the buffer *pstn = _rgchSuggest + _ichSuggest; _ichSuggest += CchSz(_rgchSuggest + _ichSuggest) + 1; return fTrue; } ClearPb(&sib, size(sib)); sib.cch = (ushort)cch; sib.lrgch = prgch; sib.cMdr = 1; sib.lrgMdr = &_mdrs.mdr; if (_fUdrValid) { sib.cUdr = 1; sib.lrgUdr = &_udr; } ClearPb(&srb, size(srb)); srb.lrgsz = _rgchSuggest; srb.cch = CvFromRgv(_rgchSuggest); _ichSuggest = 0; sccc = fFirst ? sccSuggest : sccSuggestMore; for ( ; ; sccc = sccSuggestMore) { ClearPb(_rgchSuggest, size(_rgchSuggest)); sec = SpellCheck(_splid, sccc, &sib, &srb); if (sec != secNOERRORS) { // invalidate the buffer _ichSuggest = CvFromRgv(_rgchSuggest); return fFalse; } switch (srb.scrs) { case scrsNoMoreSuggestions: // invalidate the buffer _ichSuggest = CvFromRgv(_rgchSuggest); return fFalse; default: if (srb.csz > 0) { *pstn = _rgchSuggest; _ichSuggest = CchSz(_rgchSuggest) + 1; return fTrue; } break; } } } /*************************************************************************** Add this word to the ignore all list. ***************************************************************************/ bool SPLC::FIgnoreAll(PSTN pstn) { AssertThis(0); AssertPo(pstn, 0); if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } return secNOERRORS == SpellAddUdr(_splid, udrIgnoreAlways, pstn->Psz()); } /*************************************************************************** Add this word pair to the change once list. ***************************************************************************/ bool SPLC::FChange(PSTN pstnSrc, PSTN pstnDst, bool fAll) { AssertThis(0); AssertPo(pstnSrc, 0); AssertPo(pstnDst, 0); if (!_fSplidValid) { Bug("spell checker not initialized"); return fFalse; } return secNOERRORS == SpellAddChangeUdr(_splid, fAll ? udrChangeAlways : udrChangeOnce, pstnSrc->Psz(), pstnDst->Psz()); } /*************************************************************************** Add this word pair to the user dictionary. ***************************************************************************/ bool SPLC::FAddToUser(PSTN pstn) { AssertThis(0); AssertPo(pstn, 0); if (!_fUdrValid) { Bug("user dictionary not loaded"); return fFalse; } return secNOERRORS == SpellAddUdr(_splid, _udr, pstn->Psz()); } /*************************************************************************** Add this word to the ignore all list. ***************************************************************************/ void SPLC::FlushIgnoreList(void) { AssertThis(0); if (!_fSplidValid) { Bug("spell checker not initialized"); return; } SpellClearUdr(_splid, udrIgnoreAlways); } /*************************************************************************** Add this word pair to the change once list. ***************************************************************************/ void SPLC::FlushChangeList(bool fAll) { AssertThis(0); if (!_fSplidValid) { Bug("spell checker not initialized"); return; } SpellClearUdr(_splid, fAll ? udrChangeAlways : udrChangeOnce); } /*************************************************************************** These are the stubs for the dll entry points. ***************************************************************************/ #ifdef WIN /*************************************************************************** Stub for SpellInit ***************************************************************************/ SC_SEC SPLC::SpellInit(SC_SPLID *psplid, SC_WSC *pwsc) { AssertThis(0); if (pvNil == _pfnInit) { Bug("nil _pfnInit"); return secModuleError; } return (*_pfnInit)(psplid, pwsc); } /*************************************************************************** Stub for SpellOptions ***************************************************************************/ SC_SEC SPLC::SpellOptions(SC_SPLID splid, long grfso) { AssertThis(0); if (pvNil == _pfnOptions) { Bug("nil _pfnOptions"); return secModuleError; } return (*_pfnOptions)(splid, grfso); } /*************************************************************************** Stub for SpellCheck ***************************************************************************/ SC_SEC SPLC::SpellCheck(SC_SPLID splid, SC_CC sccc, LPSC_SIB psib, LPSC_SRB psrb) { AssertThis(0); if (pvNil == _pfnCheck) { Bug("nil _pfnCheck"); return secModuleError; } return (*_pfnCheck)(splid, sccc, psib, psrb); } /*************************************************************************** Stub for SpellTerminate ***************************************************************************/ SC_SEC SPLC::SpellTerminate(SC_SPLID splid, SC_BOOL fForce) { AssertThis(0); if (pvNil == _pfnTerminate) { Bug("nil _pfnTerminate"); return secModuleError; } return (*_pfnTerminate)(splid, fForce); } /*************************************************************************** Stub for SpellOpenMdr ***************************************************************************/ SC_SEC SPLC::SpellOpenMdr(SC_SPLID splid, LPSC_PATH ppath, LPSC_PATH ppathExclude, SC_BOOL fCreateExclude, SC_BOOL fCache, SC_LID sclidExpected, LPSC_MDRS pmdrs) { AssertThis(0); if (pvNil == _pfnOpenMdr) { Bug("nil _pfnOpenMdr"); return secModuleError; } return (*_pfnOpenMdr)(splid, ppath, ppathExclude, fCreateExclude, fCache, sclidExpected, pmdrs); } /*************************************************************************** Stub for SpellOpenUdr ***************************************************************************/ SC_SEC SPLC::SpellOpenUdr(SC_SPLID splid, LPSC_PATH ppath, SC_BOOL fCreate, SC_WORD udrprop, SC_UDR * pudr, SC_BOOL *pfReadOnly) { AssertThis(0); if (pvNil == _pfnOpenUdr) { Bug("nil _pfnOpenUdr"); return secModuleError; } return (*_pfnOpenUdr)(splid, ppath, fCreate, udrprop, pudr, pfReadOnly); } /*************************************************************************** Add a word to the given user dictionary ***************************************************************************/ SC_SEC SPLC::SpellAddUdr(SC_SPLID splid, SC_UDR udr, SC_CHAR *pszAdd) { AssertThis(0); if (pvNil == _pfnAddUdr) { Bug("nil _pfnAddUdr"); return secModuleError; } return (*_pfnAddUdr)(splid, udr, pszAdd); } /*************************************************************************** Add a word pair to the given user dictionary ***************************************************************************/ SC_SEC SPLC::SpellAddChangeUdr(SC_SPLID splid, SC_UDR udr, SC_CHAR *pszAdd, SC_CHAR *pszChange) { AssertThis(0); if (pvNil == _pfnAddChangeUdr) { Bug("nil _pfnAddChangeUdr"); return secModuleError; } return (*_pfnAddChangeUdr)(splid, udr, pszAdd, pszChange); } /*************************************************************************** Stub for SpellClearUdr ***************************************************************************/ SC_SEC SPLC::SpellClearUdr(SC_SPLID splid, SC_UDR udr) { AssertThis(0); if (pvNil == _pfnClearUdr) { Bug("nil _pfnClearUdr"); return secModuleError; } return (*_pfnClearUdr)(splid, udr); } /*************************************************************************** Stub for SpellCloseMdr ***************************************************************************/ SC_SEC SPLC::SpellCloseMdr(SC_SPLID splid, LPSC_MDRS pmdrs) { AssertThis(0); if (pvNil == _pfnCloseMdr) { Bug("nil _pfnCloseMdr"); return secModuleError; } return (*_pfnCloseMdr)(splid, pmdrs); } /*************************************************************************** Stub for SpellCloseUdr ***************************************************************************/ SC_SEC SPLC::SpellCloseUdr(SC_SPLID splid, SC_UDR udr, SC_BOOL fForce) { AssertThis(0); if (pvNil == _pfnCloseUdr) { Bug("nil _pfnCloseUdr"); return secModuleError; } return (*_pfnCloseUdr)(splid, udr, fForce); } #endif //WIN #ifdef DEBUG /*************************************************************************** Assert the validity of a SPLC. ***************************************************************************/ void SPLC::AssertValid(ulong grf) { SPLC_PAR::AssertValid(0); } /*************************************************************************** Mark memory for the SPLC. ***************************************************************************/ void SPLC::MarkMem(void) { AssertValid(0); SPLC_PAR::MarkMem(); } #endif //DEBUG