874 lines
29 KiB
C
874 lines
29 KiB
C
#include <mvopsys.h>
|
|
#include <mem.h>
|
|
#if DOS_ONLY
|
|
#include <io.h>
|
|
#include <string.h>
|
|
#endif // DOS_ONLY
|
|
#include <mvsearch.h>
|
|
#include <groups.h>
|
|
#include "common.h"
|
|
#include "search.h"
|
|
#include "httpsrch.h"
|
|
|
|
#ifdef _DEBUG
|
|
static BYTE NEAR s_aszModule[] = __FILE__; /* Used by error return functions.*/
|
|
#endif
|
|
|
|
#define NOT_OPENED NULL
|
|
#define CACHE_EMPTY ((DWORD)-1)
|
|
|
|
/*************************************************************************
|
|
*
|
|
* API FUNCTIONS
|
|
* Those functions should be exported in a .DEF file
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGetTopic (LPHL, DWORD, PTOPICINFO);
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGetHit (LPHL, PTOPICINFO, DWORD,
|
|
LPHIT);
|
|
PUBLIC DWORD EXPORT_API PASCAL FAR MVHitListEntries (LPHL);
|
|
PUBLIC LONG EXPORT_API PASCAL FAR MVHitListMax(LPHL);
|
|
PUBLIC VOID EXPORT_API FAR PASCAL MVHitListDispose(LPHL);
|
|
|
|
/*************************************************************************
|
|
*
|
|
* INTERNAL PUBLIC FUNCTIONS
|
|
* Those functions should be prototyped in some include files
|
|
*************************************************************************/
|
|
PUBLIC HRESULT PASCAL FAR OccListSave (LPITOPIC, LPDW, LPDW, HFPB, HFPB, int);
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVHitListGetTopic |
|
|
* The function will return the data of the nth TOPIC. The total
|
|
* of TOPIC is given by lpHitList->lcReturnedTopics
|
|
*
|
|
* @parm LPHL | lpHitList |
|
|
* Pointer to a hitlist structure. This structure contains all
|
|
* the information necessary for the retrieval
|
|
*
|
|
* @parm PTOPICINFO | lpTopic |
|
|
* Pointer to a TOPIC structure to be filled
|
|
*
|
|
* @parm DWORD | TopicNumber |
|
|
* Which TOPIC we want to get. The number starts at 0
|
|
*
|
|
* @rdesc S_OK if ERR_SUCCESSed, other errors if failed
|
|
*************************************************************************/
|
|
#define CNODE_IN_CACHE 100
|
|
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGetTopic (_LPHL lpHitList,
|
|
DWORD TopicNumber, PTOPICINFO lpTopic)
|
|
{
|
|
LPITOPIC lpTopicList;
|
|
DWORD TopicNumSaved = TopicNumber;
|
|
ERRB errb;
|
|
DWORD dwByteRead;
|
|
|
|
if (lpHitList == NULL || lpTopic == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
if (lpHitList->lcReturnedTopics == 0 ||
|
|
lpHitList->lcReturnedTopics < TopicNumber)
|
|
return E_INVALIDARG;
|
|
|
|
if (lpHitList->hTopic == 0)
|
|
{
|
|
/* Everything is in memory */
|
|
|
|
/* Find the starting pointer */
|
|
if (lpHitList->lLastTopicId <= TopicNumber) {
|
|
TopicNumber -= lpHitList->lLastTopicId;
|
|
lpTopicList = lpHitList->lpLastTopic;
|
|
}
|
|
else
|
|
lpTopicList = lpHitList->lpTopicList;
|
|
|
|
if (lpTopicList == NULL)
|
|
return E_ASSERT;
|
|
|
|
/* Traverse to the right TopicList*/
|
|
if (TopicNumber != 0)
|
|
{
|
|
for (;lpTopicList && TopicNumber > 0;
|
|
lpTopicList = lpTopicList->pNext, TopicNumber--);
|
|
}
|
|
|
|
if (lpTopicList == NULL)
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Everything is saved in a temp file */
|
|
|
|
/* Allocate a cache if needed */
|
|
if (lpHitList->lpTopicCache == NULL)
|
|
{
|
|
if ((lpHitList->hTopicCache = _GLOBALALLOC(DLLGMEM,
|
|
sizeof(TOPIC_LIST)*CNODE_IN_CACHE)) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
lpHitList->lpTopicCache = (LPITOPIC)_GLOBALLOCK(lpHitList->hTopicCache);
|
|
lpHitList->dwTopicCacheStart = lpHitList->dwOccCacheStart =
|
|
lpHitList->dwCurTopic = CACHE_EMPTY; // The cache is empty
|
|
lpHitList->dwTopicInCacheCount = 0;
|
|
}
|
|
|
|
/* Fill up the cache if it is empty, or out of range */
|
|
if (lpHitList->dwTopicCacheStart > TopicNumber ||
|
|
(lpHitList->dwTopicCacheStart + lpHitList->dwTopicInCacheCount)
|
|
<= TopicNumber)
|
|
{
|
|
|
|
/* Fill up the cache */
|
|
if ((dwByteRead = FileSeekRead(lpHitList->hTopic,
|
|
lpHitList->lpTopicCache,
|
|
MakeFo (sizeof (TOPIC_LIST)*TopicNumber, 0),
|
|
sizeof(TOPIC_LIST) * CNODE_IN_CACHE, &errb)) == cbIO_ERROR)
|
|
return errb;
|
|
lpHitList->dwTopicInCacheCount = dwByteRead / sizeof(TOPIC_LIST);
|
|
lpHitList->dwTopicCacheStart = TopicNumber;
|
|
}
|
|
lpTopicList = lpHitList->lpTopicCache + TopicNumber -
|
|
lpHitList->dwTopicCacheStart;
|
|
}
|
|
|
|
/* Save the TopicList node address */
|
|
lpTopic->lpTopicList = (LPV)lpTopicList;
|
|
|
|
/* Copy the structure */
|
|
lpTopic->dwTopicId = lpTopicList->dwTopicId;
|
|
lpTopic->lcHits = lpTopicList->lcOccur;
|
|
lpTopic->wWeight = lpTopicList->wWeight;
|
|
|
|
/* Remember the last accessed topic */
|
|
lpHitList->lpLastTopic = lpTopicList;
|
|
lpHitList->lLastTopicId = TopicNumSaved;
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVHitListGetTopicId |
|
|
* The function will return the data of the topic with doc id given.
|
|
* If it is in the hitlist.
|
|
* @parm LPHL | lpHitList |
|
|
* Pointer to a hitlist structure. This structure contains all
|
|
* the information necessary for the retrieval
|
|
*
|
|
* @parm PTOPICINFO | lpTopic |
|
|
* Pointer to a TOPIC structure to be filled
|
|
*
|
|
* @parm DWORD | TopicId |
|
|
* Which TOPIC we want to get.
|
|
*
|
|
* @rdesc S_OK if ERR_SUCCESSed, other errors if failed
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGetTopicID (_LPHL lpHitList,
|
|
DWORD TopicID, PTOPICINFO lpTopic)
|
|
{
|
|
LPITOPIC lpTopicList;
|
|
|
|
if (lpHitList == NULL || lpTopic == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
/* Traverse to the right TopicList*/
|
|
for ( lpTopicList = lpHitList->lpTopicList;
|
|
(lpTopicList && (lpTopicList->dwTopicId != TopicID)) ;
|
|
lpTopicList = lpTopicList->pNext);
|
|
if (lpTopicList == NULL) return E_INVALIDARG;
|
|
/* Save the TopicList node address */
|
|
lpTopic->lpTopicList = (LPV)lpTopicList;
|
|
|
|
/* Copy the structure */
|
|
|
|
lpTopic->dwTopicId = lpTopicList->dwTopicId;
|
|
lpTopic->lcHits = lpTopicList->lcOccur;
|
|
#ifdef _DEBUG
|
|
lpTopic->wWeight = lpTopicList->wWeight;
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVHitListGetHit |
|
|
* The function will return the data of the nth hit.
|
|
*
|
|
* @parm LPHL | lpHitList |
|
|
* Pointer to a hitlist structure. This structure contains all
|
|
* the information necessary for the retrieval
|
|
*
|
|
* @parm PTOPICINFO | lpTopic |
|
|
* Pointer to the Topic list
|
|
*
|
|
* @parm LPHIT | lpHit |
|
|
* Pointer to a HIT structure to be filled
|
|
*
|
|
* @parm DWORD | HitNumber |
|
|
* Which HIT we want to get. The number starts at 0
|
|
*
|
|
* @rdesc S_OK if ERR_SUCCESSed, E_FAIL if no such hit exists, or other
|
|
* errors if failed
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGetHit (_LPHL lpHitList, PTOPICINFO lpTopic,
|
|
DWORD HitNumber, LPHIT lpHit)
|
|
{
|
|
LPIOCC lpOccur;
|
|
LPITOPIC lpTopicList;
|
|
ERRB errb;
|
|
|
|
if (lpHitList == NULL || lpTopic == NULL || lpHit == NULL )
|
|
return SetErrCode (&errb, E_INVALIDARG);
|
|
|
|
if ((lpTopicList = lpTopic->lpTopicList) == NULL)
|
|
return E_FAIL;
|
|
|
|
if (HitNumber > lpTopicList->lcOccur)
|
|
return SetErrCode (&errb, E_INVALIDARG);
|
|
|
|
if (lpHitList->hOcc == NULL) {
|
|
/* Everything is still in memory */
|
|
if ((lpOccur = DL_OCCUR(lpTopicList)) == NULL)
|
|
return SetErrCode (&errb, E_ASSERT);
|
|
|
|
/* Traverse to the right occurrence */
|
|
if (HitNumber != 0) {
|
|
for (;lpOccur && HitNumber > 0; lpOccur = lpOccur->pNext,
|
|
HitNumber--);
|
|
}
|
|
|
|
if (lpOccur == NULL)
|
|
return E_FAIL;
|
|
}
|
|
else {
|
|
|
|
/* Allocate a cache if needed */
|
|
|
|
if (lpHitList->lpOccCache == NULL) {
|
|
if ((lpHitList->hOccCache = _GLOBALALLOC(DLLGMEM,
|
|
sizeof(OCCURENCE) * CNODE_IN_CACHE)) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
lpHitList->lpOccCache = (LPIOCC)_GLOBALLOCK(lpHitList->hOccCache);
|
|
lpHitList->dwOccCacheStart =
|
|
lpHitList->dwCurTopic = CACHE_EMPTY; // The cache is empty
|
|
}
|
|
|
|
/* Fill up the cache if it is empty, or out of range */
|
|
if (lpHitList->dwCurTopic != lpTopicList->dwTopicId ||
|
|
lpHitList->dwOccCacheStart > HitNumber ||
|
|
(lpHitList->dwOccCacheStart + CNODE_IN_CACHE) <= HitNumber) {
|
|
|
|
/* Fill up the cache */
|
|
/* NOTE: The following calculation uses the lpOccur fields as a DWORD not a POINTER */
|
|
/* the original author should have used a union to make the code clear. Once I*/
|
|
/* have converted the source to Win64 I should take time to clean this up RAK */
|
|
// Win64 caution
|
|
if ((FileSeekRead(lpHitList->hOcc, lpHitList->lpOccCache,
|
|
MakeFo (HitNumber * sizeof(OCCURENCE) + (DWORD)
|
|
(DWORD_PTR) (DL_OCCUR(lpTopicList)), 0), sizeof(OCCURENCE) * CNODE_IN_CACHE,
|
|
&errb)) == cbIO_ERROR)
|
|
return errb;
|
|
|
|
/* Update the fields */
|
|
|
|
lpHitList->dwOccCacheStart = HitNumber;
|
|
lpHitList->dwCurTopic = lpTopicList->dwTopicId;
|
|
}
|
|
lpOccur = lpHitList->lpOccCache + HitNumber -
|
|
lpHitList->dwOccCacheStart;
|
|
}
|
|
|
|
/* Copy the Occurrence structure */
|
|
|
|
lpHit->dwOffset = lpOccur->dwOffset;
|
|
lpHit->dwLength = lpOccur->cLength;
|
|
lpHit->dwCount = lpOccur->dwCount;
|
|
lpHit->dwFieldId = lpOccur->dwFieldId;
|
|
lpHit->lpvTerm = lpOccur->lpvTerm;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func VOID FAR PASCAL | MVHitListDispose |
|
|
* Free all the memory associated with the hitlist
|
|
*
|
|
* @parm lpHitList | lpHitList |
|
|
* Pointer to a HitList (HL) structure
|
|
*************************************************************************/
|
|
PUBLIC VOID EXPORT_API FAR PASCAL MVHitListDispose(_LPHL lpHitList)
|
|
{
|
|
HANDLE hCache;
|
|
|
|
if (lpHitList == NULL)
|
|
return;
|
|
|
|
if (lpHitList->lpMainHitList && lpHitList->lpUpdateHitList)
|
|
{
|
|
if (lpHitList->lpMainHitList->lpTopicMemBlock ==
|
|
lpHitList->lpUpdateHitList->lpTopicMemBlock)
|
|
lpHitList->lpMainHitList->lpTopicMemBlock = NULL;
|
|
|
|
if (lpHitList->lpMainHitList->lpOccMemBlock ==
|
|
lpHitList->lpUpdateHitList->lpOccMemBlock)
|
|
lpHitList->lpMainHitList->lpOccMemBlock = NULL;
|
|
}
|
|
|
|
MVHitListDispose ((_LPHL)lpHitList->lpMainHitList);
|
|
MVHitListDispose ((_LPHL)lpHitList->lpUpdateHitList);
|
|
|
|
/* Free the Topic block */
|
|
BlockFree ((LPV)lpHitList->lpTopicMemBlock);
|
|
|
|
/* Free the occurrences block */
|
|
BlockFree ((LPV)lpHitList->lpOccMemBlock);
|
|
|
|
/* Free the Topic cache */
|
|
if (hCache = lpHitList->hTopicCache) {
|
|
_GLOBALUNLOCK(hCache);
|
|
_GLOBALFREE(hCache);
|
|
}
|
|
|
|
/* Free Occ cache */
|
|
if (hCache = lpHitList->hOccCache) {
|
|
_GLOBALUNLOCK(hCache);
|
|
_GLOBALFREE(hCache);
|
|
}
|
|
|
|
/* Kill the temp files */
|
|
if (lpHitList->hTopic) {
|
|
FileClose (lpHitList->hTopic);
|
|
FileUnlink (NULL, lpHitList->lszTopicName, REGULAR_FILE);
|
|
}
|
|
|
|
if (lpHitList->hOcc) {
|
|
FileClose (lpHitList->hOcc);
|
|
FileUnlink (NULL, lpHitList->lszOccName, REGULAR_FILE);
|
|
}
|
|
|
|
/* Free HttpQ structure */
|
|
if (lpHitList->lpHttpQ)
|
|
{
|
|
_GLOBALUNLOCK(((LPHTTPQ)(lpHitList->lpHttpQ))->hStrQuery);
|
|
_GLOBALFREE(((LPHTTPQ)(lpHitList->lpHttpQ))->hStrQuery);
|
|
GlobalLockedStructMemFree((LPV)lpHitList->lpHttpQ);
|
|
}
|
|
|
|
/* Free the structure */
|
|
GlobalLockedStructMemFree((LPV)lpHitList);
|
|
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func DWORD FAR PASCAL | MVHitListEntries |
|
|
* This funtion returns the number of entries of a hitlist
|
|
*
|
|
* @parm lpHitList | lpHitList |
|
|
* Pointer to hit list being queried.
|
|
*
|
|
* @rdesc Number of hitlist entries
|
|
*************************************************************************/
|
|
|
|
PUBLIC DWORD EXPORT_API PASCAL FAR MVHitListEntries (_LPHL lpHitList)
|
|
{
|
|
if (lpHitList == NULL)
|
|
return 0;
|
|
return lpHitList->lcReturnedTopics;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVHitListFlush |
|
|
* This function saves all the hitlist info onto the disk. The
|
|
* number of topics saved is indicated by the parameter. The remaining
|
|
* will be discarded
|
|
*
|
|
* @parm LPHL | lpHitList |
|
|
* Pointer to hit list being queried.
|
|
*
|
|
* @parm DWORD | cTopic |
|
|
* How many topics we want to save. If cTopic == 0 then we want to
|
|
* save all
|
|
*
|
|
* @rdesc S_OK, or other errors
|
|
*
|
|
* @comm The I/O buffers are allocated on the stack instead from
|
|
* a regular _GlobalAlloc. The reason is that for large query (but
|
|
* necessary large result, such as A AND (B THRU Z)), we may run
|
|
* out of memory. Using stack space ensure that we have enough
|
|
* memory to do the write
|
|
*************************************************************************/
|
|
|
|
#define IO_OCC_CNT 100
|
|
#define IO_TOPIC_CNT 100
|
|
|
|
HRESULT HttpSaveToBuffers(_LPHL lpHitList, DWORD cTopic, LPHTTPQ lpHttpQ);
|
|
|
|
PUBLIC HRESULT EXPORT_API PASCAL FAR MVHitListFlush (_LPHL lpHitList, DWORD cTopic)
|
|
{
|
|
ERRB errb; /* Error buffer */
|
|
HFPB hTopic; /* Topic's file handle */
|
|
GHANDLE hTopicBuf; /* Handle to Topic buffer */
|
|
LPITOPIC lpTopicBuf; /* Pointer to Topic buffer */
|
|
LPITOPIC lpDestTopic; /* Destination Topic */
|
|
HFPB hOcc; /* Occurrences' file handle */
|
|
GHANDLE hOccBuf; /* Handle to occurence buffer */
|
|
LPIOCC lpOccBuf; /* Pointer to Occurrence buffer */
|
|
LPIOCC lpDestOcc; /* Destination occ */
|
|
LPIOCC lpCurOcc; /* Current Occ ptr */
|
|
DWORD lfoOcc; /* Starting offset of the occurrences */
|
|
LPITOPIC lpCurTopic; /* Topic's current pointer */
|
|
HRESULT fRet; /* Returned value */
|
|
int cOccCnt; /* Counter */
|
|
int cTopicCnt; /* Counter */
|
|
DWORD cTopicSaved = cTopic;
|
|
DWORD cbWrite;
|
|
|
|
#ifdef DOS_ONLY
|
|
LPB lpTmp;
|
|
#endif
|
|
|
|
|
|
// Sanity check
|
|
if (lpHitList == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Search performed locally...
|
|
if (lpHitList->lcReturnedTopics == 0)
|
|
return S_OK;
|
|
|
|
#ifdef DOS_ONLY
|
|
if (lpTmp = _mktemp ("ocXXXXXX"))
|
|
_fstrcpy (lpHitList->lszOccName, lpTmp);
|
|
#else
|
|
/* Create a temporary filename for occurrence file */
|
|
(void)GETTEMPFILENAME((char)0, (LPB)"occ", (WORD)0, lpHitList->lszOccName);
|
|
#endif
|
|
|
|
/* Open the occurrence temp file */
|
|
if ((lpHitList->hOcc = hOcc = FileOpen (NULL, lpHitList->lszOccName,
|
|
REGULAR_FILE, OF_WRITE, &errb)) == 0)
|
|
{
|
|
return errb;
|
|
}
|
|
|
|
#ifdef DOS_ONLY
|
|
if (lpTmp = _mktemp ("doXXXXXX"))
|
|
_fstrcpy (lpHitList->lszTopicName, lpTmp);
|
|
#else
|
|
/* Create a temporary filename for doc file */
|
|
(void)GETTEMPFILENAME((char)0, (LPB)"Topic", (WORD)0, lpHitList->lszTopicName);
|
|
#endif
|
|
|
|
/* Open the doc temp file */
|
|
if ((lpHitList->hTopic = hTopic = FileOpen (NULL, lpHitList->lszTopicName,
|
|
REGULAR_FILE, OF_WRITE, &errb)) == 0)
|
|
{
|
|
fRet = errb;
|
|
exit0:
|
|
if (fRet != S_OK)
|
|
{
|
|
FileClose (hOcc);
|
|
FileUnlink (NULL, lpHitList->lszOccName, REGULAR_FILE);
|
|
lpHitList->hOcc = 0;
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
/* Allocate I/O buffers for Topic and Occ lists. Since we seldom
|
|
* used near memory, the following LocalAlloc should S_OK.
|
|
* The reason we don't want to use far memory is that there is
|
|
* a possibility that we ran out of far memory when we came to this
|
|
* point (partial hit list). If so, we have to ensure that everything
|
|
* get written to the disk so that memory can be freed, else nothing
|
|
* will work in an OOM state
|
|
*/
|
|
if ((hTopicBuf = LocalAlloc(LMEM_MOVEABLE,
|
|
sizeof(TOPIC_LIST) * IO_TOPIC_CNT)) == NULL)
|
|
{
|
|
fRet = E_OUTOFMEMORY;
|
|
exit01:
|
|
if (fRet != S_OK)
|
|
{
|
|
FileClose (hTopic);
|
|
FileUnlink (NULL, lpHitList->lszTopicName, REGULAR_FILE);
|
|
lpHitList->hTopic = 0;
|
|
}
|
|
goto exit0;
|
|
}
|
|
|
|
lpTopicBuf = (LPITOPIC)LocalLock(hTopicBuf);
|
|
|
|
if ((hOccBuf = LocalAlloc(LMEM_MOVEABLE, sizeof(OCCURENCE) * IO_OCC_CNT))
|
|
== NULL)
|
|
{
|
|
fRet = E_OUTOFMEMORY;
|
|
exit1:
|
|
LocalUnlock(hTopicBuf);
|
|
LocalFree(hTopicBuf);
|
|
goto exit01;
|
|
}
|
|
|
|
lpOccBuf = (LPIOCC)LocalLock(hOccBuf);
|
|
|
|
/* The remaining of the code consists of writing the info to the disk */
|
|
|
|
/* Initialize the variables */
|
|
|
|
lfoOcc = 0;
|
|
lpCurTopic = lpHitList->lpTopicList;
|
|
lpDestOcc = lpOccBuf;
|
|
lpDestTopic = lpTopicBuf;
|
|
cTopicCnt = IO_TOPIC_CNT;
|
|
cOccCnt = IO_OCC_CNT;
|
|
|
|
/* If cTopic == 0, decrementing it will change it to 0xffffffff,
|
|
* which means take the whole list.
|
|
*/
|
|
do
|
|
{
|
|
DWORD_PTR dwStartOff;
|
|
|
|
lpCurOcc = DL_OCCUR(lpCurTopic);
|
|
dwStartOff = lfoOcc;
|
|
|
|
for (; lpCurOcc; lpCurOcc = lpCurOcc->pNext)
|
|
{
|
|
*lpDestOcc++ = *lpCurOcc;
|
|
if (--cOccCnt <= 0)
|
|
{
|
|
cbWrite = (DWORD)((LPB)lpDestOcc - (LPB)lpOccBuf);
|
|
if ((DWORD)FileWrite (hOcc, lpOccBuf, cbWrite,
|
|
&errb) != cbWrite)
|
|
{
|
|
fRet = errb;
|
|
exit2:
|
|
LocalUnlock(hOccBuf);
|
|
LocalFree(hOccBuf);
|
|
goto exit1;
|
|
}
|
|
cOccCnt = IO_OCC_CNT;
|
|
lpDestOcc = lpOccBuf;
|
|
}
|
|
|
|
/* Update the offset */
|
|
lfoOcc += sizeof(OCCURENCE);
|
|
}
|
|
|
|
/* The next steps involves the saving of the lpOcc's offset
|
|
* Note that the pointer must be saved and restored to ensure
|
|
* that everything is intact in memory in case writing fails
|
|
*/
|
|
lpCurOcc = DL_OCCUR(lpCurTopic);
|
|
DL_OCCUR(lpCurTopic) = (LPIOCC)dwStartOff;
|
|
*lpDestTopic++ = *lpCurTopic;
|
|
DL_OCCUR(lpCurTopic) = lpCurOcc;
|
|
|
|
|
|
if (--cTopicCnt <= 0) {
|
|
cbWrite = (DWORD)((LPB)lpDestTopic - (LPB)lpTopicBuf);
|
|
if ((DWORD)FileWrite(hTopic, lpTopicBuf, cbWrite, &errb) != cbWrite)
|
|
{
|
|
fRet = errb;
|
|
goto exit2;
|
|
}
|
|
cTopicCnt = IO_TOPIC_CNT;
|
|
lpDestTopic = lpTopicBuf;
|
|
}
|
|
|
|
if ((lpCurTopic = lpCurTopic->pNext) == NULL)
|
|
break;
|
|
} while (--cTopic > 0);
|
|
|
|
/* Write the remaining data to the disk */
|
|
cbWrite = (DWORD)((LPB)lpDestTopic - (LPB)lpTopicBuf);
|
|
if ((DWORD)FileWrite(hTopic, lpTopicBuf, cbWrite, &errb) != cbWrite)
|
|
{
|
|
fRet = errb;
|
|
goto exit2;
|
|
}
|
|
|
|
cbWrite = (DWORD)((LPB)lpDestOcc - (LPB)lpOccBuf);
|
|
if ((DWORD)FileWrite(hOcc, lpOccBuf, cbWrite, &errb) != cbWrite)
|
|
{
|
|
fRet = errb;
|
|
goto exit2;
|
|
}
|
|
/* At this point everything is saved to the disk, we can
|
|
* get rid of all the memory block, and reset the fields
|
|
* to NULL
|
|
*/
|
|
BlockFree(lpHitList->lpOccMemBlock); // Pointer to Occ memory block
|
|
BlockFree(lpHitList->lpTopicMemBlock); // Pointer to Topic memory block
|
|
lpHitList->lpTopicMemBlock = lpHitList->lpOccMemBlock = NULL;
|
|
lpHitList->lpTopicList = NULL;
|
|
|
|
/* Update the number of topics left */
|
|
|
|
if (cTopicSaved && lpHitList->lcReturnedTopics > cTopicSaved) {
|
|
lpHitList->lcReturnedTopics = cTopicSaved;
|
|
}
|
|
|
|
fRet = S_OK;
|
|
goto exit2;
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
PUBLIC HRESULT PASCAL FAR OccListSave (LPIOCC lpCurOcc, LPIOCC lpOccBuf,
|
|
HFPB hOcc, LPW lpwOccCnt, WORD MaxOcc, int fFlag)
|
|
{
|
|
LPIOCC lpDestOcc;
|
|
DWORD cbWritten = 0;
|
|
WORD wOccCnt = *lpwOccCnt;
|
|
HRESULT fRet;
|
|
|
|
lpDestOcc = lpOccBuf + wOccCnt;
|
|
|
|
for (lpCurOcc = DL_OCCUR(lpCurTopic); lpCurOcc; lpCurOcc =
|
|
lpCurOcc->pNext) {
|
|
|
|
*lpDestOcc++ = *lpCurOcc;
|
|
if (++wOccCnt == MaxOcc) {
|
|
if ((fRet = FileWrite(hOcc, lpOccBuf,
|
|
(LPB)lpDestOcc - lpOccBuf)) != S_OK) {
|
|
return fRet;
|
|
}
|
|
wOccCnt = 0;
|
|
lpDestOcc = lpOccBuf;
|
|
}
|
|
|
|
/* Update number of bytes written */
|
|
cbWritten += sizeof (OCCURENCE);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*************************************************************************
|
|
* @doc API RETRIEVAL
|
|
*
|
|
* @func int FAR PASCAL | MVHitListMax |
|
|
* This funtion returns the highest numbered entry
|
|
*
|
|
* @parm lpHitList | lpHitList |
|
|
* Pointer to hit list being queried.
|
|
*
|
|
*************************************************************************/
|
|
PUBLIC LONG EXPORT_API FAR PASCAL MVHitListMax(_LPHL lpHitList)
|
|
{
|
|
if (lpHitList == NULL)
|
|
return 0;
|
|
return lpHitList->lcMaxTopic;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc RETRIEVAL
|
|
*
|
|
* @func HRESULT FAR PASCAL | MVHitListGroup |
|
|
* Given a hitlist, this function will create a group that contains
|
|
* all the TopicIds in the hitlist.
|
|
* @parm _LPGROUP | lpGroup |
|
|
* Pointer to a group tobe filled with data
|
|
* @parm LPHL | lpHitList |
|
|
* Pointer to hitlist
|
|
* @rdesc The function will return S_OK if ERR_SUCCESSed, else errors
|
|
* In case of S_OK, the group will be filled with new data
|
|
*************************************************************************/
|
|
PUBLIC HRESULT EXPORT_API FAR PASCAL MVHitListGroup(_LPGROUP lpGroup,
|
|
_LPHL lpHitList)
|
|
{
|
|
register long i;
|
|
long cTopicCount;
|
|
PTOPICINFO lpTopic;
|
|
HANDLE hTopic;
|
|
DWORD dwTopicId ;
|
|
LPB lpbGrpBitVect;
|
|
HRESULT fRet = S_OK;
|
|
ERRB errb;
|
|
|
|
/* Sanity check */
|
|
if (lpHitList == NULL || lpGroup==NULL)
|
|
{
|
|
return SetErrCode (&errb, E_INVALIDARG);
|
|
}
|
|
|
|
/* Allow empty hitlist */
|
|
cTopicCount = lpHitList->lcReturnedTopics;
|
|
|
|
/* Allocate a Topic structure */
|
|
if ((hTopic = _GLOBALALLOC(DLLGMEM_ZEROINIT,
|
|
sizeof(TOPICINFO))) == 0)
|
|
{
|
|
return SetErrCode(&errb, E_OUTOFMEMORY);
|
|
}
|
|
|
|
lpTopic = (PTOPICINFO)_GLOBALLOCK(hTopic);
|
|
|
|
lpGroup->lcItem = cTopicCount;
|
|
|
|
/* Initialize variables */
|
|
lpbGrpBitVect = lpGroup->lpbGrpBitVect;
|
|
|
|
for ( i = 0; i < cTopicCount; i++)
|
|
{
|
|
/* Get the Topic list */
|
|
if ((fRet = MVHitListGetTopic(lpHitList, (DWORD)i, lpTopic)) != S_OK)
|
|
break;
|
|
|
|
dwTopicId = lpTopic->dwTopicId;
|
|
|
|
/* Set the bit */
|
|
lpbGrpBitVect[dwTopicId / 8] |= 1 << (dwTopicId % 8);
|
|
}
|
|
|
|
/* Free allocated memory */
|
|
_GLOBALUNLOCK(hTopic);
|
|
_GLOBALFREE(hTopic);
|
|
return fRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @doc RETRIEVAL
|
|
*
|
|
* @func LPHL FAR PASCAL | MVHitListMerge |
|
|
* Given two hitlists, this function will merge them, as if lpHitList1
|
|
* came from a Main title, and lpHitList2 from an Update title, so all
|
|
* hits in lpHitList2 take priority over those in lpHitList1.
|
|
* @parm PSCHRINFO | pSrchInfo |
|
|
* Search information
|
|
* @parm LPHL | lpHitList1 |
|
|
* Pointer to hitlist (Main)
|
|
* @parm LPHL | lpHitList1 |
|
|
* Pointer to another hitlist (Update)
|
|
* @rdesc The function will return a merged hitlist, or NULL if error.
|
|
*************************************************************************/
|
|
|
|
PUBLIC LPHL EXPORT_API FAR PASCAL MVHitListMerge(PSRCHINFO pSrchInfo,
|
|
_LPHL lpHitListMain, _LPHL lpHitListUpdate, _LPGROUP lpIgnoreTopicGrp,
|
|
DWORD fFlag, PHRESULT phr)
|
|
{
|
|
_LPHL lpHitListResult;
|
|
LPITOPIC lpUpdateTopic; /* Topic's current pointer */
|
|
LPITOPIC lpMainTopic; /* Topic's current pointer */
|
|
LPITOPIC lpResultTopic; /* Topic's current pointer */
|
|
HRESULT fRet;
|
|
QTNODE qtNode;
|
|
|
|
if (lpHitListMain == NULL || lpHitListUpdate == NULL)
|
|
{
|
|
SetErrCode (phr, E_INVALIDARG);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Allocate hitlist */
|
|
if ((lpHitListResult = (_LPHL)GLOBALLOCKEDSTRUCTMEMALLOC(sizeof (HL))) == NULL)
|
|
{
|
|
SetErrCode(phr, E_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
lpUpdateTopic = lpHitListUpdate->lpTopicList;
|
|
lpMainTopic = lpHitListMain->lpTopicList;
|
|
lpResultTopic = NULL;
|
|
lpHitListResult->lpMainHitList = lpHitListMain;
|
|
lpHitListResult->lpUpdateHitList = lpHitListUpdate;
|
|
lpHitListResult->lpOccMemBlock =
|
|
lpHitListResult->lpMainHitList->lpOccMemBlock;
|
|
lpHitListResult->lpMainHitList->lpOccMemBlock =
|
|
lpHitListResult->lpUpdateHitList->lpOccMemBlock = NULL;
|
|
lpHitListResult->lpTopicMemBlock =
|
|
lpHitListResult->lpUpdateHitList->lpTopicMemBlock;
|
|
lpHitListResult->lpMainHitList->lpTopicMemBlock =
|
|
lpHitListResult->lpUpdateHitList->lpTopicMemBlock = NULL;
|
|
|
|
for (; lpMainTopic; )
|
|
{
|
|
if (lpUpdateTopic == NULL ||
|
|
lpMainTopic->dwTopicId < lpUpdateTopic->dwTopicId)
|
|
{
|
|
if (lpIgnoreTopicGrp && FGroupLookup(lpIgnoreTopicGrp,
|
|
lpMainTopic->dwTopicId))
|
|
{
|
|
// Skip this unwanted old topic
|
|
lpMainTopic = lpMainTopic->pNext;
|
|
continue;
|
|
}
|
|
// Add the main list
|
|
if (lpResultTopic == NULL)
|
|
{
|
|
lpHitListResult->lpTopicList = lpResultTopic = lpMainTopic;
|
|
}
|
|
else
|
|
lpResultTopic->pNext = lpMainTopic;
|
|
lpResultTopic = lpMainTopic;
|
|
lpMainTopic = lpMainTopic->pNext;
|
|
lpResultTopic->pNext = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Add the update list
|
|
if (lpResultTopic == NULL)
|
|
lpHitListResult->lpTopicList = lpUpdateTopic;
|
|
else
|
|
lpResultTopic->pNext = lpUpdateTopic;
|
|
|
|
if (lpMainTopic->dwTopicId == lpUpdateTopic->dwTopicId)
|
|
lpMainTopic = lpMainTopic->pNext;
|
|
lpResultTopic = lpUpdateTopic;
|
|
lpUpdateTopic = lpUpdateTopic->pNext;
|
|
lpResultTopic->pNext = NULL;
|
|
}
|
|
}
|
|
|
|
// Attach to the remaining list
|
|
if (lpResultTopic == NULL)
|
|
lpHitListResult->lpTopicList = lpUpdateTopic;
|
|
else
|
|
lpResultTopic->pNext = lpUpdateTopic;
|
|
|
|
// Calculate the number of returned topics and the total of topics
|
|
for (lpResultTopic = lpHitListResult->lpTopicList; lpResultTopic;
|
|
lpResultTopic = lpResultTopic->pNext)
|
|
{
|
|
lpHitListResult->lcTotalNumOfTopics++;
|
|
if (lpResultTopic->dwTopicId > lpHitListResult->lcMaxTopic)
|
|
lpHitListResult->lcMaxTopic = lpResultTopic->dwTopicId;
|
|
}
|
|
|
|
lpHitListResult->lcReturnedTopics = lpHitListResult->lcTotalNumOfTopics;
|
|
|
|
// Remap the sorting order based on the number of hits
|
|
qtNode.lpTopicList = lpHitListResult->lpTopicList;
|
|
qtNode.cTopic = lpHitListResult->lcTotalNumOfTopics;
|
|
TopicListSort (&qtNode, HIT_COUNT_BASED);
|
|
lpHitListResult->lpTopicList = qtNode.lpTopicList;
|
|
|
|
// Only return the number of topics that the user requested
|
|
if (pSrchInfo->dwTopicCount> 0 &&
|
|
pSrchInfo->dwTopicCount < lpHitListResult->lcReturnedTopics)
|
|
lpHitListResult->lcReturnedTopics = pSrchInfo->dwTopicCount;
|
|
|
|
if ((fFlag & QUERYRESULT_IN_MEM) == 0)
|
|
{
|
|
if ((fRet = MVHitListFlush (lpHitListResult,
|
|
lpHitListResult->lcReturnedTopics)) != S_OK)
|
|
{
|
|
SetErrCode (phr, fRet);
|
|
}
|
|
}
|
|
return lpHitListResult;
|
|
}
|