904 lines
28 KiB
C
904 lines
28 KiB
C
/*************************************************************************
|
|
* *
|
|
* STOP.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990-1994 *
|
|
* All Rights reserved. *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* Stop list indexing and retrieval *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* Written By : Binh Nguyen *
|
|
* Current Owner: Binh Nguyen *
|
|
* *
|
|
*************************************************************************/
|
|
#include <mvopsys.h>
|
|
#include <orkin.h>
|
|
#include <mem.h>
|
|
#include <memory.h>
|
|
#include <io.h>
|
|
#include <mvsearch.h>
|
|
#include "common.h"
|
|
|
|
#ifdef _DEBUG
|
|
static BYTE NEAR s_aszModule[] = __FILE__; /* Used by error return functions.*/
|
|
#endif
|
|
|
|
#define cbSTOP_BUF ((CB)512) // Number of bytes read at a time
|
|
// from the stop-word file.
|
|
/*************************************************************************
|
|
*
|
|
* API FUNCTIONS
|
|
* Those functions should be exported in a .DEF file
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListAddWord(LPSIPB, LST);
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListIndexLoad (HFPB, LPSIPB, LSZ);
|
|
PUBLIC LPSIPB EXPORT_API FAR PASCAL MVStopListInitiate(WORD, PHRESULT);
|
|
PUBLIC void EXPORT_API FAR PASCAL MVStopListDispose(LPSIPB);
|
|
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListLoad(HFPB, LPSIPB, LSZ,
|
|
BREAKER_FUNC, LPV);
|
|
PUBLIC HRESULT EXPORT_API PASCAL FAR MVStopFileBuild (HFPB, LPSIPB, LSZ);
|
|
PUBLIC LPCHAIN EXPORT_API FAR PASCAL MVStopListFind(_LPSIPB lpsipb, LST lstWord);
|
|
|
|
/*************************************************************************
|
|
*
|
|
* INTERNAL PRIVATE FUNCTIONS
|
|
* All of them should be declared near
|
|
*************************************************************************/
|
|
|
|
PRIVATE WORD NEAR PASCAL GetHashKey (WORD, LST);
|
|
|
|
/*************************************************************************
|
|
*
|
|
* INTERNAL PUBLIC FUNCTIONS
|
|
* All of them should be declared far, and included in some include file
|
|
*************************************************************************/
|
|
|
|
PUBLIC HRESULT FAR PASCAL FStopCallback(LST, LST, LFO, LPV);
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func LPSIPB FAR PASCAL | MVStopListInitiate |
|
|
* Create and initiate a stop-word information structure
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Pointer to error buffer.
|
|
*
|
|
* @parm WORD | wTabSize |
|
|
* Table size in DWORD. The process of stop word checking will
|
|
* be faster with larger values of dwTabSize.
|
|
*
|
|
* @rdesc the pointer to the stop-list structure if succeeded,
|
|
* NULL if failed. The error buffer will contain descriptions about
|
|
* the cause of the failure
|
|
*************************************************************************/
|
|
|
|
PUBLIC LPSIPB EXPORT_API FAR PASCAL MVStopListInitiate(WORD wTabSize,
|
|
PHRESULT phr)
|
|
{
|
|
_LPSIPB lpsipb;
|
|
|
|
if (wTabSize < HASH_SIZE)
|
|
wTabSize = HASH_SIZE;
|
|
|
|
/* Allocate a StopInfo structure */
|
|
if ((lpsipb = (_LPSIPB)GLOBALLOCKEDSTRUCTMEMALLOC(sizeof(SIPB) +
|
|
wTabSize * sizeof(LPB))) == NULL)
|
|
{
|
|
exit00:
|
|
SetErrCode(phr, E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
lpsipb->HashTab = (LPCHAIN FAR *)((LPB)lpsipb + sizeof(SIPB));
|
|
|
|
/* Allocate a word block buffer */
|
|
if ((lpsipb->lpBlkMgr = BlockInitiate (WORDBUF_SIZE, 0, 0, 0)) == NULL)
|
|
{
|
|
GlobalLockedStructMemFree((LPV)lpsipb);
|
|
goto exit00;
|
|
}
|
|
|
|
lpsipb->wTabSize = wTabSize; /* Size of hash table */
|
|
lpsipb->lpfnStopListLookup = MVStopListLookup;
|
|
return (LPSIPB)lpsipb;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListAddWord |
|
|
* Add a word to a stop list
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LST | lstWord |
|
|
* Pointer to 2-byte length preceded Pascal word to be added
|
|
* into the stop-word list
|
|
*
|
|
* @rdesc S_OK if succeeded
|
|
*************************************************************************/
|
|
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListAddWord(_LPSIPB lpsipb, LST lstWord)
|
|
{
|
|
WORD wHash;
|
|
LPCHAIN lpChain;
|
|
WORD wByteUsed;
|
|
|
|
// Sanity check
|
|
if (lpsipb == NULL || lstWord == NULL)
|
|
return(E_INVALIDARG);
|
|
|
|
/* Look for the word. If it is already there then just
|
|
* return S_OK, don't add it into the list
|
|
*/
|
|
if (lpChain = MVStopListFind (lpsipb, lstWord))
|
|
{
|
|
// Don't add if already there.
|
|
lpChain->dwCount++;
|
|
return S_OK;
|
|
}
|
|
|
|
wByteUsed = *(LPUW)lstWord + 2;
|
|
|
|
#ifndef _32BIT
|
|
if (lpsipb->cbTextUsed + wByteUsed > MAX_STOPWORD_BUFSIZE) {
|
|
/* There are too many stop words */
|
|
return ERR_TOOMANYSTOPS;
|
|
}
|
|
#endif
|
|
|
|
lpsipb->cbTextUsed += wByteUsed ;
|
|
|
|
/* Copy the word into the word buffer block */
|
|
if ((lpChain = (LPCHAIN)BlockCopy (lpsipb->lpBlkMgr, lstWord, wByteUsed,
|
|
sizeof(CHAIN) - 1)) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
lpChain->dwCount = 0;
|
|
|
|
/* Compute hash key */
|
|
wHash = GetHashKey(lpsipb->wTabSize, lstWord);
|
|
|
|
/* Add the word to the hash table */
|
|
CH_NEXT(lpChain) = lpsipb->HashTab[wHash];
|
|
lpsipb->HashTab[wHash] = lpChain;
|
|
|
|
return S_OK; // Function worked.
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func void FAR PASCAL | MVStopListDispose |
|
|
* Frees memory associated with a stop list.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*************************************************************************/
|
|
|
|
PUBLIC void EXPORT_API FAR PASCAL MVStopListDispose (_LPSIPB lpsipb)
|
|
{
|
|
if (lpsipb == NULL)
|
|
return;
|
|
|
|
/* Free the word buffer */
|
|
BlockFree(lpsipb->lpBlkMgr);
|
|
|
|
/* Free the stop info structure */
|
|
GlobalLockedStructMemFree((LPV)lpsipb);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListIndexLoad |
|
|
* Read a stop-word list stored in the subfile/dos file.
|
|
*
|
|
* @parm HFPB | hfpb |
|
|
* Handle to input file. Can be mvfs subfile or separate dos file.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LPIDX | lpidx |
|
|
* Pointer to index structure
|
|
*
|
|
* @parm LSZ | lszWordBreaker |
|
|
* Word breaker to be used
|
|
*
|
|
* @rdesc S_OK if succeeded, other errors if failed.
|
|
*************************************************************************/
|
|
/*
|
|
The strings are stored in the file in a sequence of pascal strings
|
|
*/
|
|
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListIndexLoad (HFPB hfpbSysFile,
|
|
_LPSIPB lpsipb, LSZ lszStopFile)
|
|
{
|
|
BYTE argbInBuf[CB_STOP_BUF];
|
|
FILEOFFSET lfo;
|
|
FILEOFFSET foStart;
|
|
HFPB hfpbSubFile;
|
|
BOOL fOpenedFile;
|
|
HRESULT fRet = S_OK;
|
|
WORD cbRead;
|
|
int fLast;
|
|
LPSTOP lpStopHdr;
|
|
LPB lpWord;
|
|
WORD wOffsetInBuf;
|
|
WORD wLen;
|
|
ERRB errb;
|
|
|
|
/* Sanity check */
|
|
if (lpsipb == NULL)
|
|
return SetErrCode (NULL, E_INVALIDARG);
|
|
|
|
/* Open the subfile */
|
|
if ((fOpenedFile =
|
|
FsTypeFromHfpb(hfpbSubFile = hfpbSysFile) != FS_SUBFILE) &&
|
|
(hfpbSubFile = FileOpen
|
|
(hfpbSysFile, lszStopFile, hfpbSysFile ? FS_SUBFILE : REGULAR_FILE,
|
|
READ, &errb)) == NULL)
|
|
{
|
|
return errb;
|
|
}
|
|
|
|
// If we didn't open the file, we need to find out where the file seek
|
|
// pointer is initially so that we only seek relative to that starting
|
|
// position (i.e. the caller owns the part of the file that comes before).
|
|
foStart = (fOpenedFile ? MakeFo(0,0) :
|
|
FileSeek (hfpbSubFile, MakeFo (0, 0), wFSSeekCur, &fRet));
|
|
|
|
/* Read and check the file validity */
|
|
if (FAILED(fRet) ||
|
|
(cbRead = (WORD)FileSeekRead
|
|
(hfpbSubFile, (LPV)(lpStopHdr = (LPSTOP)argbInBuf),
|
|
FoAddFo(foStart, MakeFo(0, 0)), sizeof(STOP_HDR), &fRet))
|
|
!= sizeof(STOP_HDR))
|
|
{
|
|
exit01:
|
|
// Close file only if we were the one's who opened it.
|
|
if (fOpenedFile)
|
|
(void)FileClose(hfpbSubFile); // Return value not checked
|
|
// because the file is open
|
|
// for read-only.
|
|
return fRet;
|
|
}
|
|
|
|
/* MAC codes. They will be eliminated through optimization */
|
|
|
|
lpStopHdr->FileStamp = SWAPWORD(lpStopHdr->FileStamp);
|
|
lpStopHdr->version = SWAPWORD(lpStopHdr->version);
|
|
lpStopHdr->dwFileSize = SWAPLONG(lpStopHdr->dwFileSize);
|
|
|
|
|
|
if (lpStopHdr->FileStamp != STOP_STAMP ||
|
|
lpStopHdr->version != VERCURRENT)
|
|
{
|
|
fRet = SetErrCode(&errb, E_BADVERSION);
|
|
goto exit01;
|
|
}
|
|
|
|
/* Start at the beginning of the buffer */
|
|
wOffsetInBuf = 0;
|
|
|
|
for (lfo = FoAddFo(foStart, MakeFo(STOP_HDR_SIZE, 0));;)
|
|
{
|
|
LPB lpbCur;
|
|
WORD cbReadOurs = 0;
|
|
|
|
if ((cbRead = (WORD)FileSeekRead(hfpbSubFile,
|
|
lpbCur = ((LPB)argbInBuf + wOffsetInBuf), lfo,
|
|
CB_STOP_BUF - wOffsetInBuf, &errb)) == cbIO_ERROR)
|
|
{
|
|
SetErrCode(&errb, fRet = E_FILEREAD);
|
|
goto exit01;
|
|
}
|
|
|
|
lfo = FoAddDw(lfo, (DWORD)cbRead);
|
|
|
|
while (cbRead - cbReadOurs++ >= sizeof(WORD))
|
|
{
|
|
if (*((WORD UNALIGNED * UNALIGNED)lpbCur) == 0)
|
|
{
|
|
FILEOFFSET foCur;
|
|
|
|
// Get our current seek position.
|
|
foCur = FileSeek (hfpbSubFile, MakeFo (0, 0), wFSSeekCur, &fRet);
|
|
|
|
// We already advanced cbReadOurs by one in the loop
|
|
// condition; advance it by one more to account for
|
|
// the second byte of the NULL word. Then we move
|
|
// the seek pointer back by the difference so that we
|
|
// don't leave it past the end of our data.
|
|
FileSeek (hfpbSubFile,
|
|
FoSubFo(foCur, MakeFo(cbRead - ++cbReadOurs, 0)),
|
|
wFSSeekSet, &fRet);
|
|
ITASSERT(SUCCEEDED(fRet));
|
|
cbRead = cbReadOurs;
|
|
fLast = TRUE;
|
|
}
|
|
else
|
|
lpbCur++;
|
|
}
|
|
|
|
cbRead += wOffsetInBuf; // Catch what's left from previous scan
|
|
wOffsetInBuf = 0;
|
|
|
|
/* Add the word into the stop word list */
|
|
for (lpWord = argbInBuf; cbRead > 0;)
|
|
{
|
|
|
|
/* If the whole word has been read in, just add it to the
|
|
stop list, else we have to "reconstruct" it
|
|
*/
|
|
// erinfox: we have to byte-swap on Mac
|
|
*(WORD UNALIGNED * UNALIGNED)lpWord = SWAPWORD(*(WORD UNALIGNED * UNALIGNED)lpWord);
|
|
|
|
wLen = *(LPUW)(lpWord) + 2;
|
|
if (wLen <= cbRead)
|
|
{
|
|
|
|
/* Everything fits */
|
|
if ((fRet = MVStopListAddWord(lpsipb, lpWord)) != S_OK)
|
|
goto exit01;
|
|
cbRead -= wLen;
|
|
lpWord += wLen; /* Move to next word */
|
|
}
|
|
else
|
|
{
|
|
/* Copy the word to the beginning of the buffer */
|
|
MEMCPY(argbInBuf, lpWord, cbRead);
|
|
wOffsetInBuf = cbRead;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fLast)
|
|
break;
|
|
}
|
|
fRet = S_OK; // Succeeded
|
|
goto exit01;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API INDEX RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListLoad |
|
|
* Read a stop-word list from an external file. The file must have
|
|
* only one stop word per line, or else there is potential loss
|
|
* of stop words.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LSZ | lszStopFile |
|
|
* Stop word filename. This is a simple ASCII text file
|
|
*
|
|
* @parm BREAKER_FUNC | lpfnBreakFunc |
|
|
* Word breaker to be used
|
|
*
|
|
* @parm PHRESULT | phr |
|
|
* Pointer to error buffer.
|
|
*
|
|
* @rdesc S_OK if succeeded, other errors failed.
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListLoad(HFPB hfpbIn, _LPSIPB lpsipb,
|
|
LSZ lszStopFile, BREAKER_FUNC lpfnBreakFunc,
|
|
LPCHARTAB lpCharTab)
|
|
{
|
|
BYTE argbInBuf[cbSTOP_BUF]; // IO buffer
|
|
HFPB hfpb; // File handle
|
|
BOOL fOpenedFile;
|
|
_LPIBI lpibi; // Pointer to internal breaker info
|
|
HANDLE hbi; // Handle to internal brekaer info
|
|
HRESULT fRet; // Returned value
|
|
BRK_PARMS brkParms; // Breaker parameters structure
|
|
LPB lpStart; // Beginning of strings to be parsed
|
|
LPB lpEnd; // End of strings to be parsed
|
|
WORD wStrLength; // Bytes in string
|
|
CB cbTobeRead; // Bytes to be read
|
|
CB cbRead; // Bytes actually read
|
|
int fLast; // TRUE if this is the last read
|
|
int fGetWord; // TRUE if we get a whole word
|
|
|
|
/* Sanity check */
|
|
if (lpsipb == NULL || (lszStopFile == NULL && hfpbIn == NULL)
|
|
|| lpfnBreakFunc == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
if ((fOpenedFile = FsTypeFromHfpb(hfpb = hfpbIn) != FS_SUBFILE) &&
|
|
(hfpb = (HANDLE)FileOpen
|
|
(hfpbIn, lszStopFile, hfpbIn ? FS_SUBFILE : REGULAR_FILE,
|
|
READ, &fRet)) == 0)
|
|
{
|
|
return (fRet);
|
|
}
|
|
|
|
/* Allocate a breaker info block */
|
|
|
|
if ((hbi = _GLOBALALLOC(DLLGMEM_ZEROINIT, (LCB)sizeof(IBI))) == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
lpibi = (_LPIBI)_GLOBALLOCK(hbi);
|
|
|
|
/* Initialize variables */
|
|
brkParms.lcbBufOffset = 0L;
|
|
brkParms.lpInternalBreakInfo = lpibi;
|
|
brkParms.lpvUser = lpsipb;
|
|
brkParms.lpfnOutWord = (FWORDCB)FStopCallback;
|
|
brkParms.lpStopInfoBlock = NULL;
|
|
brkParms.lpCharTab = lpCharTab;
|
|
|
|
cbTobeRead = cbSTOP_BUF; // Read in a buffer whole
|
|
lpStart = lpEnd = (LPB)argbInBuf; // Start & End of string
|
|
fGetWord = FALSE; // We didn't get any word yet
|
|
wStrLength = 0;
|
|
|
|
/* The idea is to break the file into sequences of lines, and pass
|
|
* each line to the word breaker. The assumption made is that we
|
|
* should only have one word per line, since various type breakers
|
|
* can only handle one word a type.
|
|
*/
|
|
|
|
for (;;)
|
|
{
|
|
cbRead = (WORD)FileRead(hfpb, lpEnd, cbTobeRead, &fRet);
|
|
if (FAILED(fRet))
|
|
{
|
|
exit01:
|
|
/* Free breaker info block */
|
|
_GLOBALUNLOCK(hbi);
|
|
_GLOBALFREE(hbi);
|
|
|
|
/* Close the file */
|
|
if (fOpenedFile)
|
|
FileClose(hfpb);
|
|
return fRet;
|
|
}
|
|
else
|
|
fLast = (cbRead != cbTobeRead);
|
|
|
|
lpEnd = lpStart;
|
|
cbRead += wStrLength; // Get what left in buffer
|
|
wStrLength = 0;
|
|
|
|
while (cbRead != (CB)-1)
|
|
{
|
|
/* Break the buffer into lines */
|
|
|
|
if (*lpEnd == '\r' || *lpEnd == '\n' || !cbRead)
|
|
{
|
|
if (wStrLength)
|
|
{
|
|
|
|
/* Process the word we got */
|
|
brkParms.lpbBuf = lpStart;
|
|
brkParms.cbBufCount = wStrLength;
|
|
|
|
if ((fRet = (*lpfnBreakFunc)((LPBRK_PARMS)&brkParms))
|
|
!= S_OK)
|
|
goto exit01;
|
|
|
|
/* Flush the breaker buffer */
|
|
brkParms.lpbBuf = NULL;
|
|
brkParms.cbBufCount = 0;
|
|
if ((fRet = (*lpfnBreakFunc)((LPBRK_PARMS)&brkParms))
|
|
!= S_OK)
|
|
goto exit01;
|
|
|
|
wStrLength = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Update the pointer to the new word */
|
|
if (wStrLength == 0)
|
|
lpStart = lpEnd;
|
|
wStrLength++; // Increase string's length
|
|
}
|
|
|
|
cbRead--;
|
|
lpEnd++;
|
|
}
|
|
|
|
|
|
if (fLast)
|
|
break;
|
|
|
|
/* Now copy the partial string to the beginning of the buffer */
|
|
MEMCPY(argbInBuf, lpStart, wStrLength);
|
|
lpEnd = (lpStart = argbInBuf) + wStrLength;
|
|
cbTobeRead = cbSTOP_BUF - wStrLength; // Read in a buffer whole
|
|
}
|
|
|
|
if (wStrLength)
|
|
{
|
|
/* Flush the breaker buffer */
|
|
brkParms.lpbBuf = NULL;
|
|
brkParms.cbBufCount = 0;
|
|
if ((fRet = (*lpfnBreakFunc)((LPBRK_PARMS)&brkParms)) != S_OK)
|
|
goto exit01;
|
|
}
|
|
fRet = S_OK; // Succeeded
|
|
goto exit01;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @func WORD NEAR PASCAL | GetHashKey |
|
|
* Compute the hash key of a string. This key is used for indexing
|
|
* into the stop word hash table
|
|
*
|
|
* @parm LST | lstWord |
|
|
* Pointer to a 2-byte length preceded Pascal-type string
|
|
*
|
|
* @rdesc
|
|
* Return the index into the stop words hash table
|
|
*************************************************************************/
|
|
|
|
PRIVATE WORD NEAR PASCAL GetHashKey (WORD hashSize, LST lstWord)
|
|
{
|
|
register unsigned int wHash;
|
|
register unsigned int nLength;
|
|
|
|
wHash = 0;
|
|
nLength = *(LPUW)lstWord;
|
|
lstWord += sizeof(WORD);
|
|
for (; nLength; nLength--)
|
|
{
|
|
wHash = (wHash << 1) | (wHash >> 15);
|
|
wHash ^= *lstWord++;
|
|
}
|
|
wHash %= hashSize;
|
|
return ((WORD)wHash);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL INDEX
|
|
*
|
|
* @func LPCHAIN FAR PASCAL | MVStopListFind |
|
|
* This looks for a word (lstWord) in a stop-word (lpsipb)
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word list structure
|
|
*
|
|
* @parm LST | lstWord |
|
|
* Pointer to string to be looked for
|
|
*
|
|
* @rdesc Pointer to the node if found, NULL otherwise
|
|
*************************************************************************/
|
|
|
|
PUBLIC LPCHAIN EXPORT_API FAR PASCAL MVStopListFind(_LPSIPB lpsipb, LST lstWord)
|
|
{
|
|
WORD wHash; // Hash key
|
|
LPCHAIN lpChain; // Pointer to the word chain
|
|
|
|
// Sanity check
|
|
if (lpsipb == NULL || lstWord == NULL)
|
|
return(NULL);
|
|
|
|
/* Compute hash key */
|
|
wHash = GetHashKey(lpsipb->wTabSize, lstWord);
|
|
lpChain = lpsipb->HashTab[wHash];
|
|
|
|
while (lpChain)
|
|
{
|
|
if (!StringDiff2 (&CH_WORD(lpChain), lstWord))
|
|
return (lpChain);
|
|
lpChain = CH_NEXT(lpChain);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL INDEX
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListLookup |
|
|
* This looks for a word (lstWord) in a stop-word (lpsipb)
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word list structure
|
|
*
|
|
* @parm LST | lstWord |
|
|
* Pointer to string to be looked for
|
|
*
|
|
* @rdesc S_OK if found, E_FAIL if not, or other errors
|
|
*************************************************************************/
|
|
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListLookup(_LPSIPB lpsipb, LST lstWord)
|
|
{
|
|
WORD wHash; // Hash key
|
|
LPCHAIN lpChain; // Pointer to the word chain
|
|
|
|
|
|
// Sanity check
|
|
if (lpsipb == NULL || lstWord == NULL)
|
|
return(E_INVALIDARG);
|
|
|
|
/* Compute hash key */
|
|
wHash = GetHashKey(lpsipb->wTabSize, lstWord);
|
|
lpChain = lpsipb->HashTab[wHash];
|
|
|
|
while (lpChain)
|
|
{
|
|
if (!StringDiff2 (&CH_WORD(lpChain), lstWord))
|
|
return (S_OK);
|
|
lpChain = CH_NEXT(lpChain);
|
|
}
|
|
return (E_FAIL);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API INDEX
|
|
*
|
|
* @func HRESULT PASCAL FAR | MVStopFileBuild |
|
|
* Incorporate the stop word list into the system file
|
|
*
|
|
* @parm HFPB | hpfbSysFile |
|
|
* If non-zero, handle to an opened system file.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LSZ | lszFilename |
|
|
* If hpfbSysFile is non-zero, this is the name of the stop's subfile
|
|
* else this is a regular DOS file
|
|
*
|
|
* @rdesc S_OK if succeeded, E_FAIL if tehre is nothing to build
|
|
* or other errors
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API PASCAL FAR MVStopFileBuild (HFPB hfpbSysFile,
|
|
_LPSIPB lpsipb, LSZ lszFilename)
|
|
{
|
|
HFPB hfpbStop; // Pointer to final index file info.
|
|
HRESULT fRet = S_OK;
|
|
STOP_HDR Stop_hdr;
|
|
HFPB hfpb = 0;
|
|
BOOL fCreatedFile;
|
|
BYTE Dummy[STOP_HDR_SIZE]; // Dummy buffer to write 0
|
|
int i;
|
|
LPCHAIN lpChain;
|
|
LST lstWord;
|
|
WORD wLen;
|
|
CB cbByteLeft;
|
|
GHANDLE hBuf;
|
|
LPB lpbBuf;
|
|
LPB lpbStart;
|
|
LPB lpbLimit;
|
|
ERRB errb;
|
|
FILEOFFSET fo;
|
|
FILEOFFSET foStart;
|
|
|
|
/* Sanity check */
|
|
if (lpsipb == NULL || (lszFilename == NULL && hfpbSysFile == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
if (lpsipb->cbTextUsed == 0)
|
|
return E_FAIL; /* Nothing to build */
|
|
|
|
if ((fCreatedFile =
|
|
FsTypeFromHfpb(hfpbStop = hfpbSysFile) != FS_SUBFILE) &&
|
|
(hfpbStop = FileCreate(hfpbSysFile, lszFilename,
|
|
hfpbSysFile ? FS_SUBFILE: REGULAR_FILE, &errb)) == 0)
|
|
return errb;
|
|
|
|
// If we didn't open the file, we need to find out where the file seek
|
|
// pointer is initially so that we only seek relative to that starting
|
|
// position (i.e. the caller owns the part of the file that comes before).
|
|
foStart = (fCreatedFile ? MakeFo(0,0) :
|
|
FileSeek (hfpbStop, MakeFo (0, 0), wFSSeekCur, &fRet));
|
|
|
|
if (FAILED(fRet))
|
|
goto exit01;
|
|
|
|
/* Write out the stop file header */
|
|
Stop_hdr.FileStamp = STOP_STAMP;
|
|
Stop_hdr.version = VERCURRENT;
|
|
Stop_hdr.dwFileSize = lpsipb->cbTextUsed;
|
|
|
|
MEMSET(Dummy, 0, STOP_HDR_SIZE);
|
|
|
|
/* Write all zeroes to the header area, which is larger than the
|
|
* STOP_HDR structure.
|
|
*/
|
|
if (FileSeekWrite (hfpbStop, Dummy, FoAddFo(foStart, MakeFo (0, 0)),
|
|
STOP_HDR_SIZE, &errb) != STOP_HDR_SIZE)
|
|
{
|
|
fRet = errb;
|
|
exit01:
|
|
if (fCreatedFile)
|
|
FileClose (hfpbStop);
|
|
return(fRet);
|
|
|
|
}
|
|
|
|
if (FileSeekWrite (hfpbStop, &Stop_hdr, FoAddFo(foStart, MakeFo (0, 0)),
|
|
sizeof (STOP_HDR), &errb) != sizeof (STOP_HDR))
|
|
{
|
|
fRet = errb;
|
|
goto exit01;
|
|
}
|
|
|
|
|
|
/* Allocate a buffer to flush the data */
|
|
if ((hBuf = _GLOBALALLOC (DLLGMEM, cbByteLeft = CB_HUGE_BUF)) == NULL)
|
|
{
|
|
SetErrCode (&errb, fRet = E_OUTOFMEMORY);
|
|
goto exit01;
|
|
}
|
|
|
|
lpbBuf = lpbStart = (LPB)_GLOBALLOCK(hBuf);
|
|
lpbLimit = lpbStart + CB_HUGE_BUF - CB_MAX_WORD_LEN;
|
|
|
|
/* Seek the file to the correct offset */
|
|
fo = FoAddFo(foStart, MakeFo (STOP_HDR_SIZE, 0));
|
|
|
|
if (!FoEquals (FileSeek (hfpbStop, fo, 0, &errb), fo))
|
|
{
|
|
fRet = E_FILESEEK;
|
|
exit02:
|
|
_GLOBALUNLOCK(hBuf);
|
|
_GLOBALFREE(hBuf);
|
|
goto exit01;
|
|
}
|
|
|
|
/* Write out the buffer */
|
|
for (i = lpsipb->wTabSize - 1; i >= 0; i--)
|
|
{
|
|
for (lpChain = lpsipb->HashTab[i]; lpChain;
|
|
lpChain = CH_NEXT(lpChain))
|
|
{
|
|
lstWord = &CH_WORD (lpChain);
|
|
MEMCPY (lpbBuf, lstWord, wLen = *(WORD FAR *)lstWord + 2);
|
|
lpbBuf += wLen;
|
|
if (lpbBuf >= lpbLimit)
|
|
{
|
|
/* No more room, just flush the buffer */
|
|
|
|
FileWrite(hfpbStop, lpbStart, (DWORD)(lpbBuf - lpbStart), &errb);
|
|
if ((fRet = errb) != S_OK)
|
|
goto exit02;
|
|
|
|
lpbBuf = lpbStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Flush the buffer */
|
|
FileWrite (hfpbStop, lpbStart, (DWORD)(lpbBuf - lpbStart), &errb);
|
|
|
|
if ((fRet = errb) == S_OK)
|
|
{
|
|
/* Write a trailing 0 word (i.e. a NULL st) to mark
|
|
* the end of the word list.
|
|
*/
|
|
*((WORD *)lpbStart) = 0;
|
|
FileWrite (hfpbStop, lpbStart, sizeof(WORD), &errb);
|
|
fRet = errb;
|
|
}
|
|
|
|
goto exit02;
|
|
}
|
|
|
|
PUBLIC HRESULT FAR PASCAL FStopCallback(
|
|
LST lstRawWord,
|
|
LST lstNormWord,
|
|
LFO lfoWordOffset,
|
|
_LPSIPB lpsipb)
|
|
{
|
|
return MVStopListAddWord(lpsipb, lstNormWord);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListEnumWords |
|
|
* Enumerate the words in a stop list, getting a pointer to each.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LST* | plstWord |
|
|
* Indirect Pointer to 2-byte length preceded Pascal word that is
|
|
* the next word identified by *pdwWordInfo and *ppvWordInfo.
|
|
*
|
|
* @parm LONG* | plWordInfo |
|
|
* Pointer to information used to determine what the next word is
|
|
* in the stop word list. Passing -1 along with NULL for *ppvWordInfo
|
|
* means start at the beginning. On exit, this contains an appropriate
|
|
* value that can be passed in again to get the next word, provided
|
|
* that no intervening calls have been made to MVStopListAddWord.
|
|
*
|
|
* @parm LPVOID* | ppvWordInfo |
|
|
* Indirect pointer to information used to determine what the next word is
|
|
* in the stop word list. Passing NULL along with -1 for *plWordInfo
|
|
* means start at the beginning. On exit, this contains an appropriate
|
|
* value that can be passed in again to get the next word, provided
|
|
* that no intervening calls have been made to MVStopListAddWord.
|
|
*
|
|
* @rdesc S_OK if succeeded
|
|
* @rdesc E_OUTOFRANGE if there are no more words in the stop list.
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListEnumWords(_LPSIPB lpsipb,
|
|
LST *plstWord, LONG *plWordInfo, LPVOID *ppvWordInfo)
|
|
{
|
|
LPCHAIN lpchain = NULL;
|
|
LONG iHashChain;
|
|
|
|
if (lpsipb == NULL || plstWord == NULL ||
|
|
plWordInfo == NULL || ppvWordInfo == NULL)
|
|
return (SetErrReturn(E_POINTER));
|
|
|
|
iHashChain = *plWordInfo;
|
|
|
|
// If after the last call to us, we were left sitting on a hash chain
|
|
// element, just advance to the next one (which may be NULL).
|
|
if ((lpchain = (LPCHAIN) *ppvWordInfo) != NULL)
|
|
lpchain = CH_NEXT(lpchain);
|
|
|
|
// If we're now sitting on a NULL hash chain (initial condition or we
|
|
// reached the end of a previous chain), we need to find the beginning
|
|
// of the next chain in the hash table.
|
|
while (iHashChain < lpsipb->wTabSize - 1 && lpchain == NULL)
|
|
lpchain = lpsipb->HashTab[++iHashChain];
|
|
|
|
if (iHashChain >= lpsipb->wTabSize - 1 && lpchain == NULL)
|
|
return (SetErrReturn(E_OUTOFRANGE));
|
|
|
|
*plstWord = &CH_WORD(lpchain);
|
|
*ppvWordInfo = (LPVOID)lpchain;
|
|
*plWordInfo = iHashChain;
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVStopListFindWordPtr |
|
|
* Find a word in the stop list and return a pointer to it.
|
|
*
|
|
* @parm LPSIPB | lpsipb |
|
|
* Pointer to stop-word information structure
|
|
*
|
|
* @parm LST | lstWord |
|
|
* Pointer to a 2-byte length preceded Pascal
|
|
* string containing the word to find.
|
|
*
|
|
* @parm LST* | plstWordInList |
|
|
* On exit, indirect pointer to 2-byte length preceded Pascal
|
|
* string for the word that was found.
|
|
*
|
|
* @rdesc S_OK if succeeded
|
|
* @rdesc E_NOTFOUND if the word isn't in the stop list
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVStopListFindWordPtr(_LPSIPB lpsipb,
|
|
LST lstWord, LST *plstWordInList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPCHAIN lpchain;
|
|
|
|
if ((lpchain = MVStopListFind(lpsipb, lstWord)) != NULL)
|
|
*(LST UNALIGNED * UNALIGNED)plstWordInList = &CH_WORD(lpchain);
|
|
else
|
|
hr = E_NOTFOUND;
|
|
|
|
return (hr);
|
|
}
|