Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A Spell Checking Engine

, 5 Feb 2001
A free spell checking engine for use in your C++ applications. Includes the current US English dictionary
/*

  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;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Matt Gullett
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 6 Feb 2001
Article Copyright 2001 by Matt Gullett
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid