/*
Copyright: 2000
Author: Matthew T Gullett
Email: gullettm@yahoo.com
Name: CFPSDictionary
Part of: Spell Checking Engine
Requires:
DESCRIPTION
----------------------------------------------------------
This class is designed to implement dictionary support for
the spell checking engine.
INFO:
----------------------------------------------------------
This class is provided -as is-. No warranty as to the
function or performance of this class is provided either
written or implied.
You may freely use this code and modify it as necessary,
as long as this header is unmodified and credit is given
to the author in the application(s) in which it is
incorporated.
*/
#include "stdafx.h"
#include "FPSSpellChecker.h"
#include "FPSDictionary.h"
#include "io.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// declare and implement function to sort dictionary
int FPSDicSortWords(const void* elem1, const void* elem2 );
int FPSDicSortWords(const void* elem1, const void* elem2 )
{
FPSDICWORD* p1 = (FPSDICWORD*)elem1;
FPSDICWORD* p2 = (FPSDICWORD*)elem2;
ASSERT(p1);
ASSERT(p2);
return stricmp(p1->szWord, p2->szWord);
}
CFPSDictionary::CFPSDictionary()
{
// initialize member variables
m_pWords = NULL;
m_lRecordCount = 0;
// intialize our file header work area
memset(&m_FileHeader, 0, sizeof(FPSDICHEADER));
}
CFPSDictionary::~CFPSDictionary()
{
// cleanup memory
if (m_pWords)
delete[] m_pWords;
m_pWords = NULL;
RemoveExtraWords();
}
// this function opens the dictionary file and caches the dictionary data
int CFPSDictionary::Open(LPCSTR lpszDicFile)
{
ASSERT(lpszDicFile);
ASSERT(AfxIsValidString(lpszDicFile));
TRACE("Open dictionary: '%s'\n", lpszDicFile);
int iReturn = FPSSPELLCHECK_ERROR_NONE;
int iHandle = -1;
long lLen = 0;
long lRecords = 0;
// save file name
m_strFileName = lpszDicFile;
// intitialize header
memset(&m_FileHeader, 0, sizeof(FPSDICHEADER));
memset(&m_iRecords, 0, sizeof(m_iRecords));
// make sure the word array is not already allocted
if (m_pWords)
{
try
{
delete[] m_pWords;
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
TRACE("CFPSDictionary::Open(%s) failed to free m_pWords\n",lpszDicFile);
}
}
m_pWords = NULL;
// open the dictionary file specified
iHandle = _open(lpszDicFile, _O_BINARY);
// check that the handle is valid
if (iHandle != -1)
{
// determine length of file
lseek(iHandle, 0, SEEK_END); // move to end
lLen = tell(iHandle); // check position
lseek(iHandle, 0, SEEK_SET); // move to start
// calculate # of records in file (records = (size of file - header size) / size of each record)
lRecords = (lLen - sizeof(FPSDICHEADER)) / sizeof(FPSDICWORD);
TRACE("CFPSDictionary::Open :> file len = %d\n", lLen);
TRACE("CFPSDictionary::Open :> records in file = %d\n", lRecords);
// allocate memory for dictionary
try
{
m_pWords = new FPSDICWORD[lRecords+1];
}
catch(...)
{
m_pWords = NULL;
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
ASSERT(m_pWords);
// if words array allocated, cache word array
if (m_pWords)
{
// read in our file header
_read(iHandle, &m_FileHeader, sizeof(FPSDICHEADER));
// validate the file header
if (IsValidHeader(m_FileHeader))
{
// read the word list in from the file
_read(iHandle, m_pWords, lLen);
// set the record count
m_lRecordCount = lRecords;
// cache word locations
CacheWordLocations();
}
else
{
iReturn = CFPSDictionary_ERROR_INVLD_HDR;
TRACE("CFPSDictionary::Open(%s) invalid header\n",lpszDicFile);
}
}
else
{
TRACE("Failed to allocate memory for CFPSDictionary::m_pWords\n");
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
// close the dictionary file
_close(iHandle);
}
else
{
iReturn = CFPSDictionary_ERROR_DIC_OPEN_FAILED;
TRACE("CFPSDictionary::Open(%s) failed to open file\n",lpszDicFile);
}
ASSERT(IsInOrder());
return iReturn;
}
// this function checks the dictionary for the specified word
BOOL CFPSDictionary::IsWordInDictionary(LPCSTR lpszWord)
{
ASSERT(lpszWord);
ASSERT(AfxIsValidString(lpszWord));
BOOL bFound = FALSE;
int iRecord = 0;
char szWordCopy[50];
int iLen = lstrlen(lpszWord);
ASSERT(iLen <= 50);
ASSERT(m_pWords);
// make a copy of word and trim it
strcpy(szWordCopy, lpszWord);
TrimRight(szWordCopy);
// search from first position of char
char cSearchChar = GetUpperChar(szWordCopy[0]);
ASSERT(cSearchChar >= 0);
ASSERT(cSearchChar <= 254);
iRecord = m_iRecords[cSearchChar];
while (iRecord < m_lRecordCount && !bFound)
{
FPSDICWORD& Word = m_pWords[iRecord];
int iMatch = 0;
if (Word.bCaseSensitive)
{
iMatch = strcmp(Word.szWord, szWordCopy);
}
else
{
iMatch = stricmp(Word.szWord, szWordCopy);
}
if (iMatch == 0)
bFound = TRUE;
if (iMatch > 0)
iRecord = m_lRecordCount;
iRecord++;
}
// use a binary search to search the dictionary
//if (m_lRecordCount > 0)
// bFound = FindWord(szWordCopy, 0, m_lRecordCount);
// if word is not found, look in extras
if (!bFound)
{
POSITION Pos = NULL;
FPSDICWORD* pEntry = NULL;
Pos = m_ExtraWords.GetHeadPosition();
while (Pos && !bFound)
{
pEntry = m_ExtraWords.GetNext(Pos);
ASSERT(pEntry);
if (pEntry->bCaseSensitive)
{
if (strcmp(szWordCopy, pEntry->szWord) == 0)
bFound = TRUE;
}
else
{
if (stricmp(szWordCopy, pEntry->szWord) == 0)
bFound = TRUE;
}
}
}
return bFound;
}
// this function searches the dictionary for all suggestions based on
// an input word. the result is returned in a CStringList
BOOL CFPSDictionary::FindMatches(LPCSTR lpszWord, CStringList& Matches)
{
ASSERT(lpszWord);
ASSERT(AfxIsValidString(lpszWord));
ASSERT(m_pWords);
BOOL bReturn = TRUE;
int iRecord = 0;
char szWordCopy[50];
char szWordMetaphone[10];
int iLen = lstrlen(lpszWord);
POSITION Pos = NULL;
FPSDICWORD* pEntry = NULL;
int iVowelCount = GetVowelCount(lpszWord);
char szTemp[50];
char szOutWord[50];
// make sure there are no entries in matches list
Matches.RemoveAll();
ASSERT(iLen <= 50);
// copy the input word, trim it and make it upper-case
strcpy(szWordCopy, lpszWord);
TrimRight(szWordCopy);
CharUpper(szWordCopy);
iLen = lstrlen(szWordCopy);
memset(szTemp, 0, 50);
memset(szOutWord, 0, 50);
// create a metaphone representation of word
MetaphoneEx(szWordCopy, szWordMetaphone, FPSSPELLCHECK_MAXMETALEN);
// if the word to search for has no vowels,
if (iVowelCount == 0)
{
int iPos = 0;
int iMaxVowelCount = 1;
if (lpszWord[iLen-1] == 'R' || lpszWord[iLen-1] == 'S' || lpszWord[iLen-1] == 'D')
iMaxVowelCount = 2;
RemoveVowelsAndDups(lpszWord, szTemp);
while (iPos < m_lRecordCount)
{
FPSDICWORD& Word = m_pWords[iPos];
if (IsWordMatch(lpszWord, iLen, Word.szWord, Word.iWordLen))
{
Matches.AddTail(Word.szWord);
}
else
{
memset(szOutWord, 0, 50);
RemoveVowelsAndDups(Word.szWord, szOutWord);
if (stricmp(szOutWord, szTemp) == 0)
{
if (abs(iLen - Word.iWordLen) < 3)
Matches.AddTail(Word.szWord);
}
}
iPos++;
}
}
else
{
// search the entire dictionary
while (iRecord < m_lRecordCount)
{
FPSDICWORD& Word = m_pWords[iRecord];
// if metaphone matches, add to suggestion list, otherwise, check
// to see if IsWordMatch returns TRUE.
if (strcmp(Word.szMetaphone, szWordMetaphone) == 0)
{
Matches.AddTail(Word.szWord);
}
else if (IsWordMatch(Word.szWord, Word.iWordLen, szWordCopy, iLen))
{
Matches.AddTail(Word.szWord);
}
iRecord++;
}
// look in extra words list
Pos = m_ExtraWords.GetHeadPosition();
while (Pos)
{
pEntry = m_ExtraWords.GetNext(Pos);
ASSERT(pEntry);
// if metaphone matches, add to suggestion list, otherwise, check
// to see if IsWordMatch returns TRUE.
if (strcmp(pEntry->szMetaphone, szWordMetaphone) == 0)
{
Matches.AddTail(pEntry->szWord);
}
else if (IsWordMatch(pEntry->szWord, pEntry->iWordLen, szWordCopy, iLen))
{
Matches.AddTail(pEntry->szWord);
}
}
}
return bReturn;
}
// this function reads in a word list and builds the dictionary
int CFPSDictionary::BuildFromWordList(LPCSTR lpszWordFile, LPCSTR lpszDicFile)
{
ASSERT(lpszWordFile);
ASSERT(AfxIsValidString(lpszWordFile));
ASSERT(lpszDicFile);
ASSERT(AfxIsValidString(lpszDicFile));
TRACE("CFPSDictionary::BuildFromWordList(%s,%s)\n", lpszWordFile, lpszDicFile);
int iReturn = FPSSPELLCHECK_ERROR_NONE;
CStdioFile WordFile;
CFile DicFile;
CString strWord;
long lCount = 0;
// if words are already allocated, return FALSE;
if (m_pWords)
{
TRACE("CFPSDictionary::BuildFromWordList :> m_pWords NOT NULL\n");
iReturn = CFPSDictionary_ERROR_DIC_ALREADY_OPEN;
}
// only continue if necessary
try
{
if (IsOK(iReturn))
{
// first, we read in the word file to count the records
if (WordFile.Open(lpszWordFile, CFile::modeRead | CFile::shareExclusive))
{
while (WordFile.ReadString(strWord))
lCount++;
WordFile.Close();
}
else
{
TRACE("CFPSDictionary::BuildFromWordList failed to open word file\n");
iReturn = CFPSDictionary_ERROR_WORD_OPEN_FAILED;
}
// re-open word file to build main dic
if (IsOK(iReturn))
{
// allocate memory for word list
try
{
m_pWords = new FPSDICWORD[lCount+1];
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
m_pWords = NULL;
}
ASSERT(m_pWords);
if (m_pWords && IsOK(iReturn))
{
if (WordFile.Open(lpszWordFile, CFile::modeRead | CFile::shareExclusive))
{
// open dictionary file (create a new file and overwrite any existing one)
if (DicFile.Open(lpszDicFile, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive))
{
long lPos = 0;
while (WordFile.ReadString(strWord))
{
FPSDICWORD& Word = m_pWords[lPos];
strWord.TrimRight();
strWord.TrimLeft();
if (strWord.GetLength() <= FPSSPELLCHECK_MAXWORDLEN)
{
Word.bCaseSensitive = ConatainsUpperCase(strWord);
strcpy(Word.szWord, strWord);
Word.iWordLen = strWord.GetLength();
MetaphoneEx(Word.szWord, Word.szMetaphone, FPSSPELLCHECK_MAXMETALEN);
memset(Word.szReduced, 0, FPSSPELLCHECK_MAXREDUCEDLEN);
lPos++;
}
}
// sort word list
qsort(m_pWords, lCount, sizeof(FPSDICWORD), FPSDicSortWords);
// prepare a file header
PrepareHeader(m_FileHeader);
// write file header
DicFile.Write(&m_FileHeader, sizeof(FPSDICHEADER));
// write word list to dictionary file
DicFile.WriteHuge(m_pWords, sizeof(FPSDICWORD)*lCount);
// close dictionary file
DicFile.Close();
}
else
{
TRACE("CFPSDictionary::BuildFromWordList failed to open dictionary file\n");
iReturn = CFPSDictionary_ERROR_DIC_OPEN_FAILED;
}
// close word file
WordFile.Close();
}
else
{
TRACE("CFPSDictionary::BuildFromWordList failed to open word file for 2nd time\n");
iReturn = CFPSDictionary_ERROR_WORD_OPEN_FAILED;
}
}
else
{
TRACE("CFPSDictionary::BuildFromWordList m_pWords failed to allocate\n");
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
}
}
}
catch(...)
{
TRACE("CFPSDictionary::BuildFromWordList EXCEPTION!\n");
iReturn = FPSSPELLCHECK_ERROR_EXCEPTION;
}
if (IsOK(iReturn))
{
m_lRecordCount = lCount;
}
else if (m_pWords)
{
try
{
delete[] m_pWords;
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
TRACE("CFPSDictionary::BuildFromWordList(%s,%s) unable to free array\n",lpszWordFile, lpszDicFile);
}
m_pWords = NULL;
}
return iReturn;
}
// add a word to the dictionary extra list. It will be saved back
// to the file at a later time
int CFPSDictionary::AddWord(LPCSTR lpszWord)
{
ASSERT(lpszWord);
ASSERT(AfxIsValidString(lpszWord));
int iReturn = FPSSPELLCHECK_ERROR_NONE;
FPSDICWORD* pEntry = NULL;
CString strWord = lpszWord;
strWord.TrimLeft(); strWord.TrimRight();
if (strWord.GetLength() <= FPSSPELLCHECK_MAXWORDLEN)
{
try
{
pEntry = new FPSDICWORD;
}
catch(...)
{
pEntry = NULL;
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
ASSERT(pEntry);
if (pEntry)
{
m_ExtraWords.AddTail(pEntry);
pEntry->bCaseSensitive = ConatainsUpperCase(strWord);
strcpy(pEntry->szWord, strWord);
pEntry->iWordLen = strWord.GetLength();
MetaphoneEx(pEntry->szWord, pEntry->szMetaphone, FPSSPELLCHECK_MAXMETALEN);
memset(pEntry->szReduced, 0, FPSSPELLCHECK_MAXREDUCEDLEN);
}
else
{
TRACE("CFPSDictionary::AddWord(%s) :. failed to allocate memory for word\n",lpszWord);
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
}
else
{
iReturn = CFPSDictionary_ERROR_WORD_TOOLONG;
TRACE("CFPSDictionary::AddWord(%s) : word too long\n", lpszWord);
}
return iReturn;
}
// removes a word from the dictionary
int CFPSDictionary::RemoveWord(LPCSTR lpszWord)
{
ASSERT(lpszWord);
ASSERT(AfxIsValidString(lpszWord));
ASSERT(m_pWords);
int iReturn = FPSSPELLCHECK_ERROR_NONE;
BOOL bFound = FALSE;
POSITION Pos = NULL;
FPSDICWORD* pEntry = NULL;
CString strWord = lpszWord;
int iPos = 0;
strWord.TrimLeft(); strWord.TrimRight();
// first, search the extra words list for a match
Pos = m_ExtraWords.GetHeadPosition();
while (Pos && !bFound)
{
POSITION LastPos = Pos;
pEntry = m_ExtraWords.GetNext(Pos);
if (stricmp(strWord, pEntry->szWord) == 0)
{
try
{
delete pEntry;
m_ExtraWords.RemoveAt(LastPos);
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
TRACE("CFPSDictionary::RemoveWord(%s) failed to delete pEntry\n", lpszWord);
}
bFound = TRUE;
}
}
// if match not found in extra word list, search main array
while (iPos < m_lRecordCount && !bFound)
{
FPSDICWORD& Word = m_pWords[iPos];
if (stricmp(strWord, Word.szWord) == 0)
{
// we just reset the array, it will be cleaned
// up at a later time
memset(Word.szWord, 0, FPSSPELLCHECK_MAXWORDLEN);
memset(Word.szMetaphone, 0, FPSSPELLCHECK_MAXMETALEN);
memset(Word.szReduced, 0, FPSSPELLCHECK_MAXREDUCEDLEN);
Word.bCaseSensitive = FALSE;
Word.iWordLen = 0;
bFound = TRUE;
}
iPos++;
}
if (!bFound)
iReturn = CFPSDictionary_ERROR_NOMATCH;
return iReturn;
}
int CFPSDictionary::Save(LPCSTR lpszDicFile)
{
ASSERT(lpszDicFile);
ASSERT(AfxIsValidString(lpszDicFile));
ASSERT(m_pWords);
TRACE("CFPSDictionary::Save(%s)\n",lpszDicFile);
int iReturn = FPSSPELLCHECK_ERROR_NONE;
CFile DicFile;
FPSDICWORD* pArray = NULL;
int iCount = m_lRecordCount + m_ExtraWords.GetCount() + 1;
int iArrayPos = 0;
int iWordPos = 0;
POSITION Pos = NULL;
FPSDICWORD* pEntry = NULL;
try
{
if (DicFile.Open(lpszDicFile, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive))
{
try
{
pArray = new FPSDICWORD[iCount];
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
pArray = NULL;
}
ASSERT(pArray);
// if the array is allocated, lets populate it
if (pArray && IsOK(iReturn))
{
// start with extra words
Pos = m_ExtraWords.GetHeadPosition();
while (Pos)
{
pEntry = m_ExtraWords.GetNext(Pos);
ASSERT(pEntry);
FPSDICWORD& Word = pArray[iArrayPos];
Word.bCaseSensitive = pEntry->bCaseSensitive;
Word.iWordLen = pEntry->iWordLen;
strcpy(Word.szMetaphone, pEntry->szMetaphone);
strcpy(Word.szReduced, pEntry->szReduced);
strcpy(Word.szWord, pEntry->szWord);
iArrayPos++;
}
// now, populate from m_pWords
while (iWordPos < m_lRecordCount)
{
FPSDICWORD& CopyTo = pArray[iArrayPos];
FPSDICWORD& CopyFrom = m_pWords[iWordPos];
// RemoveWord function sets szWord to all NULLS, ignore these
// words
if (strcmp(CopyFrom.szWord, "") != 0)
{
CopyTo.bCaseSensitive = CopyFrom.bCaseSensitive;
CopyTo.iWordLen = CopyFrom.iWordLen;
strcpy(CopyTo.szMetaphone, CopyFrom.szMetaphone);
strcpy(CopyTo.szReduced, CopyFrom.szReduced);
strcpy(CopyTo.szWord, CopyFrom.szWord);
iArrayPos++;
}
iWordPos++;
}
// sort array
if (iArrayPos > 1)
qsort(pArray, iArrayPos, sizeof(FPSDICWORD), FPSDicSortWords);
// write array to file
try
{
// prepare a file header
PrepareHeader(m_FileHeader);
// write the file header out
DicFile.Write(&m_FileHeader, sizeof(FPSDICHEADER));
// write the dictionary data out
DicFile.WriteHuge(pArray, sizeof(FPSDICWORD)*iArrayPos);
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_EXCEPTION;
TRACE("CFPSDictionary::Save(%s) EXCEPTION\n",lpszDicFile);
}
// delete temporary working array
try
{
delete[] pArray;
}
catch(...)
{
TRACE("CFPSDictionary::Save(%s) failed to destroy array\n",lpszDicFile);
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
pArray = NULL;
}
else
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
TRACE("CFPSDictionary::Save(%s) failed to allocate array of size %d\n",lpszDicFile,iCount);
}
// close dictionary file
DicFile.Close();
}
else
{
// file failed to open dictionary file
iReturn = CFPSDictionary_ERROR_DIC_OPEN_FAILED;
TRACE("CFPSDictionary::Save(%s) failed to open file\n",lpszDicFile);
}
}
catch(...)
{
// an exception occred
iReturn = FPSSPELLCHECK_ERROR_EXCEPTION;
TRACE("CFPSDictionary::Save(%s) EXCEPTION\n",lpszDicFile);
}
ASSERT(!pArray);
if (pArray)
{
try
{
delete[] pArray;
}
catch(...)
{
TRACE("CFPSDictionary::Save(%s) failed to destroy array\n",lpszDicFile);
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
}
pArray = NULL;
}
return iReturn;
}
// close the dictionary, this involves cleanuping the extra word list
// and freeing memory used by words
int CFPSDictionary::Close()
{
TRACE("CFPSDictionary::Close()\n");
int iReturn = FPSSPELLCHECK_ERROR_NONE;
if (m_pWords)
{
try
{
delete[] m_pWords;
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
TRACE("CFPSDictionary::Close() failed to destroy m_pWords\n");
}
m_pWords = NULL;
RemoveExtraWords();
m_lRecordCount = 0;
memset(&m_FileHeader, 0, sizeof(FPSDICHEADER));
}
else
{
iReturn = CFPSDictionary_ERROR_NOT_OPEN;
}
return iReturn;
}
// this function removes the entries from the m_ExtraWords list
void CFPSDictionary::RemoveExtraWords()
{
POSITION Pos;
FPSDICWORD* pEntry;
Pos = m_ExtraWords.GetHeadPosition();
while (Pos)
{
pEntry = m_ExtraWords.GetNext(Pos);
ASSERT(pEntry);
try
{
delete pEntry;
}
catch(...)
{
TRACE("CFPSDictionary::RemoveExtraWords() failed to destroy pEntry\n");
}
pEntry = NULL;
}
m_ExtraWords.RemoveAll();
}
// this function is called by IsWordInDictionary function to
// perform a binary search of the dictionary
BOOL CFPSDictionary::FindWord(LPCSTR lpszWord, int iMin, int iMax)
{
if (iMax < iMin)
iMax = iMin;
if (iMin >= m_lRecordCount)
iMin = m_lRecordCount-1;
if (iMax >= m_lRecordCount)
iMax = m_lRecordCount-1;
ASSERT(lpszWord);
ASSERT(AfxIsValidString(lpszWord));
ASSERT(m_pWords);
ASSERT(iMin >= 0);
ASSERT(iMin < m_lRecordCount);
ASSERT(iMax >= 0);
ASSERT(iMax < m_lRecordCount);
// if iMin=iMax, just check the record and return (no recursion)
if (iMin == iMax)
{
FPSDICWORD& Word = m_pWords[iMin];
#ifdef _DEBUG
CString strTest = Word.szWord;
strTest.TrimLeft(); strTest.TrimRight(); strTest.MakeUpper();
if (strTest == "IF")
AfxMessageBox(Word.szWord);
#endif
if (Word.bCaseSensitive)
{
return (strcmp(Word.szWord, lpszWord) == 0);
}
else
{
return (stricmp(Word.szWord, lpszWord) == 0);
}
}
else
{
double dblRecord = (double)((double)iMin + (double)iMax) / (double)2;
int iRecord = (iMin + iMax) / 2;
int iMatch = 0;
FPSDICWORD& Word = m_pWords[iRecord];
if (dblRecord != (double)iRecord)
iRecord++;
#ifdef _DEBUG
CString strTest = Word.szWord;
strTest.TrimLeft(); strTest.TrimRight(); strTest.MakeUpper();
if (strTest == "IF")
AfxMessageBox(Word.szWord);
#endif
if (Word.bCaseSensitive)
{
iMatch = strcmp(Word.szWord, lpszWord);
}
else
{
iMatch = stricmp(Word.szWord, lpszWord);
}
if (iMatch == 0)
return TRUE;
// if iRecord-word < lpszWord search upper region, otherwise,
// search lower region
if (iMatch < 0)
{
return FindWord(lpszWord, iRecord+1, iMax);
}
else
{
return FindWord(lpszWord, iMin, iRecord-1);
}
}
}
BOOL CFPSDictionary::IsValidHeader(FPSDICHEADER &Header)
{
ASSERT(AfxIsValidAddress(&Header, sizeof(FPSDICHEADER), TRUE));
BOOL bReturn = TRUE;
// make sure the start and end codes are intact
if (strcmp(Header.szBeginCode, FPSDICHEADER_CODE) != 0)
bReturn = FALSE;
if (strcmp(Header.szEndCode, FPSDICHEADER_CODE) != 0)
bReturn = FALSE;
// make sure the compression option is valid
if (Header.uiCompress != FPSDICHEADER_COMPRESS_NONE)
bReturn = FALSE;
return bReturn;
}
void CFPSDictionary::PrepareHeader(FPSDICHEADER &Header)
{
ASSERT(AfxIsValidAddress(&Header, sizeof(FPSDICHEADER), TRUE));
FPSDICHEADER Temp;
CString strDate = COleDateTime::GetCurrentTime().Format("%m-%d-%Y");
// intialize our temporary header for testing it against passed in header
memset(&Temp, 0, sizeof(FPSDICHEADER));
// if passed in header is all zeroes, we need to completely set it up
if (memcmp(&Header, &Temp, sizeof(FPSDICHEADER)) == 0)
{
strcpy(Header.szBeginCode, FPSDICHEADER_CODE); // begin code
strcpy(Header.szEndCode, FPSDICHEADER_CODE); // end code
strcpy(Header.szDateCreated, strDate); // set date created
strcpy(Header.szAuthor, FPSDICHEADER_AUTHOR); // set authod
strcpy(Header.szLanguage, FPSDICHEADER_LANG_ENGLISH); // set language
Header.uiCompress = FPSDICHEADER_COMPRESS_NONE; // set compression option
}
// set date updated
strcpy(Header.szDateUpdated, strDate);
}
CString CFPSDictionary::GetWords()
{
ASSERT(m_pWords);
CString strReturn;
POSITION Pos = NULL;
FPSDICWORD* pEntry = NULL;
int iPos = 0;
// get words from main word list
while (iPos < m_lRecordCount)
{
FPSDICWORD& Word = m_pWords[iPos];
strReturn += Word.szWord;
strReturn += "\r\n";
iPos++;
}
// get words from extra words list
Pos = m_ExtraWords.GetHeadPosition();
while (Pos)
{
pEntry = m_ExtraWords.GetNext(Pos);
ASSERT(pEntry);
strReturn += pEntry->szWord;
strReturn += "\r\n";
}
return strReturn;
}
void CFPSDictionary::SetWords(LPCSTR lpszWords)
{
ASSERT(lpszWords);
ASSERT(AfxIsValidString(lpszWords));
int iLen = lstrlen(lpszWords);
int iPos = 0;
CString strWord;
CString strFile = m_strFileName;
// close currently opened dictionary
Close();
// create new dictionary
CreateNew();
// extract words from lpszWords
while (iPos < iLen)
{
if (strWord == '\n')
{
AddWord(strWord);
strWord = "";
}
else if (lpszWords[iPos] != '\r')
{
strWord += lpszWords[iPos];
}
iPos++;
}
if (strWord != "")
AddWord(strWord);
// save dictionary
Save(strFile);
// close dictionary
Close();
// re-open dictionary
Open(strFile);
}
int CFPSDictionary::CreateNew()
{
TRACE("CFPSDictionary::CreateNew()\n");
ASSERT(!m_pWords);
int iReturn = FPSSPELLCHECK_ERROR_NONE;
try
{
m_pWords = new FPSDICWORD[1];
}
catch(...)
{
iReturn = FPSSPELLCHECK_ERROR_MEMORY;
m_pWords = NULL;
TRACE("CFPSDictionary::CreateNew() error allocating memory for words\n");
}
ASSERT(m_pWords);
m_lRecordCount = 0;
return iReturn;
}
BOOL CFPSDictionary::IsInOrder()
{
ASSERT(m_pWords);
BOOL bReturn = TRUE;
int iPos = 0;
char szLastWord[50];
memset(szLastWord, 0, 50);
while (iPos < m_lRecordCount && bReturn)
{
FPSDICWORD& Word = m_pWords[iPos];
if (stricmp(szLastWord, Word.szWord) > 0)
bReturn = FALSE;
iPos++;
}
return bReturn;
}
void CFPSDictionary::CacheWordLocations()
{
ASSERT(m_pWords);
char cLastChar = 0;
int iPos = 0;
while (iPos < m_lRecordCount)
{
FPSDICWORD& Word = m_pWords[iPos];
char cThisChar = GetUpperChar(m_pWords[iPos].szWord[0]);
if (cThisChar >= 0 && cThisChar <= 254)
{
if (cThisChar > cLastChar)
m_iRecords[cThisChar] = iPos;
}
cLastChar = cThisChar;
iPos++;
}
}
char CFPSDictionary::GetUpperChar(char cInput)
{
char cReturn = cInput;
if (cInput >= 'a' && cInput <= 'z')
cReturn = cInput - ('a' - 'A');
return cReturn;
}