#include #ifdef _DEBUG static char s_aszModule[] = __FILE__; /* For error report */ #endif #include #include #include #include #include #include #include #include #include BOOL static IsInFilter(_LPGROUP qFilter, LONG lOffset); /***************************************************************************** * * WordWheelOpen * * @doc EXTERNAL * * @api HWHEEL | WordWheelOpen | This function opens a word wheel. * * * @parm LPCSTR | lpszName | Specifies the name of the word wheel. This name * should match the name used on the left side of the word-wheel entries * specified in the [WWHEEL] section of the project file. * * @parm PHRESULT | phr | Error return value if failed. * * @rdesc Returns a handle to the specified word wheel, or NULL if the * function fails. * * * Keyword indexes are stored as word wheels in the file system. Word wheels * for keyword indexes are named using the convention "\|c", where "c" is * the identification character specified in the "key=" entry in the * KEYINDEX section of the project file. For example, the default keyword * index is named "\|0". * * @xref WordWheelClose * */ HWHEEL FAR PASCAL EXPORT_API WordWheelOpen(IITDatabase* lpITDB, IStorage* pWWStorage, PHRESULT phr) { HWHEEL hWheel = NULL; // handle to the new structure PWHEEL pWheel = NULL; // pointer to locked-down structure BOOL fSuccess= FALSE; char szBtreeFormat[wMaxFormat + 1]; PWHEELINFO pInfo = NULL; WORD t; WORD wCount = 1; DWORD cbSize; DWORD cbRead; LARGE_INTEGER dlibMove; ULARGE_INTEGER libNewPos; HF pHdr = NULL; BTREE_PARAMS btp; BOOL fReadOnly = TRUE; ERRB errb; *phr = S_OK; // Assume success // allocate the structure. must be 0 initialized if ((hWheel = _GLOBALALLOC(GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof(WHEEL)+sizeof(WHEELINFO)*(wCount-1)))==NULL) { *phr = E_OUTOFMEMORY; return NULL; } pWheel = (PWHEEL)_GLOBALLOCK(hWheel); pWheel->magic = WHEEL_MAGIC; pWheel->pIITGroup = NULL; for (t = 0; t < wCount; t++) { pInfo = pWheel->pInfo + pWheel->wNumWheels; // just make sure these are NULL pInfo->pKeyHdr = NULL; pInfo->pOccHdr = NULL; pWheel->wNumWheels++; pInfo->magic = WHEEL_INFO_MAGIC; // open the subfiles if ((pInfo->hmapbt=HmapbtOpenHfs(pWWStorage, SZ_WORDWHEEL_MAP, &errb))==NULL) { SetErrCode(phr, errb); goto ignore_wheel; } if ((pInfo->hbt=HbtOpenBtreeSz(SZ_BTREE_BTREE, pWWStorage, fFSOpenReadOnly, &errb))==NULL) { SetErrCode(phr, errb); goto ignore_wheel; } GetBtreeParams(pInfo->hbt, &btp); if (btp.rgchFormat[0] == KT_EXTSORT) { IITSortKey *pITSortKey; ITASSERT(btp.dwExtSortInstID != IITDB_OBJINST_NULL); if (FAILED(*phr = lpITDB->GetObject(btp.dwExtSortInstID, IID_IITSortKey, (LPVOID *) &pITSortKey))) { goto ignore_wheel; } BtreeSetExtSort(pInfo->hbt, pITSortKey); // We release pITSortKey because BtreeSetExtSort // should've AddRef'ed it. pITSortKey->Release(); } // Open header and save out key/occurrence if ((pHdr = HfOpenHfs(pWWStorage, SZ_BTREE_HEADER, fFSOpenReadOnly, phr))==hfNil) goto ignore_wheel; // read global size and seek ahead to key header *phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup; dlibMove.QuadPart = cbSize; *phr = pHdr->Seek(dlibMove, STREAM_SEEK_CUR, &libNewPos); if (FAILED(*phr)) goto cleanup; // read key header size and read in key header info *phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup; // ALLOCATE memory if (cbSize) { if (NULL == (pInfo->pKeyHdr = new BYTE[cbSize])) goto cleanup; *phr = pHdr->Read(pInfo->pKeyHdr, cbSize, &cbRead); if (FAILED(*phr)) goto cleanup; } // read key def. data size and seek ahead to occurrence hdr. size *phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup; dlibMove.QuadPart = cbSize; *phr = pHdr->Seek(dlibMove, STREAM_SEEK_CUR, &libNewPos); if (FAILED(*phr)) goto cleanup; // read occ. header size and read in key header info *phr = pHdr->Read(&cbSize, sizeof(DWORD), &cbRead); if (FAILED(*phr)) goto cleanup; // ALLOCATE memory if (cbSize) { if (NULL == (pInfo->pOccHdr = new BYTE[cbSize])) goto cleanup; *phr = pHdr->Read(pInfo->pOccHdr, cbSize, &cbRead); if (FAILED(*phr)) goto cleanup; } // close header RcCloseHf(pHdr); // Open data file if ((pInfo->hf = HfOpenHfs(pWWStorage, SZ_BTREE_DATA, fFSOpenReadOnly,phr))==hfNil) goto ignore_wheel; // erinfox: comment out full-text index support temporarily // REVIEW(billa): If this is re-enabled, then the correct way to open an index // will be through IITIndex obtained from CoCreateInstance on CLSID_IITIndexLocal. // Any stop word information will get loaded automatically as part of the breaker // instance that was associated with the index at build time. #if 0 // if the index isn't there, that is okay. pInfo->lpIndex = MVIndexOpen(pTitle->hfpbSysFile, pstrI, phr); if (pInfo->lpIndex) { // if the stop file isn't there, that is okay. pWheel->lpsipb = MVStopListInitiate (0, phr); // If lpsipb is NULL MVStopListIndexLoad will // simply return ERR_BADARG, so we don't need to check it if (S_OK != MVStopListIndexLoad (pTitle->hfpbSysFile, pWheel->lpsipb, pstrS)) { MVStopListDispose (pWheel->lpsipb); pWheel->lpsipb = NULL; } } #endif // cache the number of keywords RcGetBtreeInfo(pInfo->hbt, (unsigned char*)szBtreeFormat, &pInfo->lNumKws, NULL); continue; ignore_wheel: // close the subfiles if (pHdr != NULL) RcCloseHf(pHdr); if (pInfo->hmapbt!=NULL) RcCloseHmapbt(pInfo->hmapbt); if (pInfo->hbt!=NULL) RcCloseBtreeHbt(pInfo->hbt); if (pInfo->hf != hfNil) RcCloseHf (pInfo->hf); #if 0 if (pInfo->lpIndex) MVIndexClose(pInfo->lpIndex); #endif pInfo->magic=0; pWheel->wNumWheels--; } if (!pWheel->wNumWheels) { // This can happen when we are looking for a-non existing word wheel goto cleanup; } if (pWheel->wNumWheels > 2) { *phr = E_TOOMANYTITLES; warning_abort; } if (pWheel->wNumWheels == 2) { *phr = E_NOMERGEDDATA; warning_abort; } else if (pWheel->wNumWheels==1) { // May STILL need to load from merge file!!!! pWheel->lNumRealEntries = pWheel->lNumEntries = pWheel->pInfo->lNumKws; } fSuccess = TRUE; cleanup: if (pWheel != NULL) _GLOBALUNLOCK(hWheel); // if we failed, then cleanup. if (!fSuccess && hWheel != NULL) { WordWheelClose(hWheel); hWheel = NULL; } return (hWheel); } /***************************************************************************** * * WordWheelClose * * * @doc EXTERNAL * * @api void | WordWheelClose | This function closes a word wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @comm After a word wheel is closed, the

handle is invalid and * should not be used. * * @xref WordWheelOpen * * *****************************************************************************/ PUBLIC VOID FAR PASCAL EXPORT_API WordWheelClose(HWHEEL hWheel) { PWHEEL pWheel = NULL; // pointer to locked-down structure WORD t; if (hWheel == NULL) return; // validate the parameters and lock down the structure if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) warning_abort; if (!PWHEEL_OK(pWheel)) warning_abort; for (t = 0; t < pWheel->wNumWheels; t++) { if (pWheel->pInfo[t].magic == WHEEL_INFO_MAGIC) { if (NULL != pWheel->pInfo[t].pOccHdr) delete pWheel->pInfo[t].pOccHdr; if (NULL != pWheel->pInfo[t].pKeyHdr) delete pWheel->pInfo[t].pKeyHdr; // close the subfiles and the file system if (pWheel->pInfo[t].hmapbt!=NULL) RcCloseHmapbt(pWheel->pInfo[t].hmapbt); if (pWheel->pInfo[t].hbt!=NULL) RcCloseBtreeHbt(pWheel->pInfo[t].hbt); if (pWheel->pInfo[t].hf != hfNil) RcCloseHf (pWheel->pInfo[t].hf); #if 0 // REVIEW(billa): If this is re-enabled, then the correct way // to close an index will be through IITIndex obtained from // CoCreateInstance on CLSID_IITIndexLocal at open time. // Any stop word information will get discarded automatically // when the index object releases its associated breaker. if (pWheel->pInfo[t].lpIndex) MVIndexClose(pWheel->pInfo[t].lpIndex); #endif pWheel->pInfo[t].magic=0; } } if (pWheel->hCacheData) _GLOBALFREE(pWheel->hCacheData); if (pWheel->lpszCacheData) DisposeMemory(pWheel->lpszCacheData); if (pWheel->hMergeData) _GLOBALFREE(pWheel->hMergeData); pWheel->magic = 0; #if 0 if (pWheel->lpsipb) MVStopListDispose (pWheel->lpsipb); #endif _GLOBALUNLOCK(hWheel); // release the memory used for the structure. _GLOBALFREE(hWheel); cleanup: return; } /***************************************************************************** * * WordWheelLength * * * @doc EXTERNAL * * @api long | WordWheelLength | This function returns the number of entries * in the word wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm PHRESULT | lpErrb | Error return value if failed. * * @rdesc Returns the number of entries in the word wheel, or -1 if an * error occurs. * * @xref WordWheelOpen * * *****************************************************************************/ PUBLIC long FAR PASCAL EXPORT_API WordWheelLength(HWHEEL hWheel, PHRESULT phr) { PWHEEL pWheel = NULL; // pointer to locked-down structure. long lRval = -1L; // -1 is the error condition. PWHEELINFO pInfo = NULL; // validate the parameters and lock down the structure if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) { *phr = E_HANDLE; warning_abort; } if (!PWHEEL_OK(pWheel)) { *phr = E_INVALIDARG; warning_abort; } lRval = pWheel->lNumEntries; //DPF3("...WordWheelLength returns, %ld\n", lRval); *phr = S_OK; cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel); return lRval; } /***************************************************************************** * * WordWheelLookup * * * @doc EXTERNAL * * @api HRESULT | WordWheelLookup | This function gets a string from a word * wheel. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm long | lIndex | Specifies an index to the word-wheel entry. * * @parm LPVOID | lpvKeyBuf | Specifies a far pointer to the buffer to * receive the text of the word-wheel entry. * * @parm DWORD | cbKeyBuf | Specifies the length of the buffer in bytes. * * @rdesc Returns S_OK if successful; otherwise returns error code * * @comm Word-wheel entries are numbered starting at zero. * * @xref WordWheelLength WordWheelOpen * * *****************************************************************************/ PUBLIC HRESULT FAR PASCAL EXPORT_API WordWheelLookup(HWHEEL hWheel, long lIndex, LPVOID lpvKeyBuf, DWORD cbKeyBuf) { PWHEEL pWheel = NULL; // pointer to locked-down structure HRESULT hr = E_INVALIDARG; // assume failure BYTE rgbKeyBuf[ITWW_CBKEY_MAX]; // validate the parameters and lock down the structure if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) warning_abort; if (!PWHEEL_OK(pWheel)) warning_abort; if (lIndex >= 0 && lIndex < pWheel->lNumEntries) { PWHEELINFO pInfo = NULL; LONG lWheelNum = 0; IITSortKey *pITSortKey = NULL; #ifdef MERGE_UPDATE VirtualToReal(lIndex,pWheel,&lIndex,&lWheelNum); #endif pInfo = pWheel->pInfo + lWheelNum; // If there's a filter, let's get the proper entry in the WW. if (pWheel->pIITGroup) { hr = (pWheel->pIITGroup)->FindTopicNum((DWORD)lIndex, (DWORD*)(&lIndex)); if (FAILED(hr)) goto cleanup; } // lookup the entry in the map file once we know _which_ map file to look in if (SUCCEEDED(hr = RcKeyFromIndexHbt(pInfo->hbt, pInfo->hmapbt, (KEY) &rgbKeyBuf[0], ITWW_CBKEY_MAX, lIndex)) && SUCCEEDED(hr = CheckWordWheelKeyType(pInfo->hbt, &pITSortKey))) { DWORD cbKey; // Check to see if the key we got back will fit in the caller's buffer. // If it will fit, copy it, otherwise return an error. if ((cbKey = CbKeyWordWheel((LPVOID) &rgbKeyBuf[0], pITSortKey)) <= cbKeyBuf) MEMCPY(lpvKeyBuf, (LPVOID) &rgbKeyBuf[0], cbKey); else hr = E_INVALIDARG; } if (pITSortKey != NULL) pITSortKey->Release(); #ifdef _MAC StringMapWinToMac (pInfo->hbt, lpBuf); #endif } else hr = E_OUTOFRANGE; cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel); return hr; } /***************************************************************************** * * WordWheelPrefix * * * @doc EXTERNAL * * @api long | WordWheelPrefix | This function locates a word-wheel entry * whose text starts with the specified prefix. * * @parm HWHEEL | hWheel | Specifies the handle to the word wheel. * * @parm LPCSTR | lpszPrefix | Specifies a far pointer to a string * containing the text of the prefix. * * @parm PHRESULT | lpErrb | Error return value if failed. * * @rdesc Returns the index of the first entry containing the specified * prefix. If no entry contains the prefix, the function returns the index * of the entry immediately prior to the point where a word with the * specified prefix would be found. The lowest index returned is zero. * * If an error occurs, the function returns -1. * * @comm Word-wheel entries are numbered starting at zero. * * @xref WordWheelLength WordWheelLookup WordWheelOpen * * *****************************************************************************/ PUBLIC long FAR PASCAL EXPORT_API WordWheelPrefix(HWHEEL hWheel, LPCVOID lpcvPrefix, BOOL fExactMatch, PHRESULT phr) { PWHEEL pWheel = NULL; // pointer to locked-down structure BTPOS btpos; // position in BTREE for prefix BYTE rgbKeyTemp[ITWW_CBKEY_MAX +1]; // holds key nearest prefix for single or Update long lRval = -1; // assume failure RC rc; // to catch return codes PWHEELINFO pInfo = NULL; BOOL ifOver1 = FALSE; #ifdef MERGE_UPDATE BYTE rgbKeyTemp2[ITWW_CBKEY_MAX +1]; // holds key nearest prefix for Main long lRval2 = -1; BOOL ifOver2 = FALSE; #endif #ifdef _MAC BYTE szSearchKey[ITWW_CBKEY_MAX+1]; LPBYTE lpb; LPCMAP lpCMap; LPCHARTAB FAR * lplpCharTab; #endif // validate the parameters and lock down the structure if ((pWheel = (PWHEEL)_GLOBALLOCK(hWheel))==NULL) { *phr = E_HANDLE; warning_abort; } if (!PWHEEL_OK(pWheel)) { *phr = E_INVALIDARG; warning_abort; } pInfo = pWheel->pInfo; #ifdef _MAC if (lplpCharTab = (LPCHARTAB FAR *)BtreeGetCMap (pInfo->hbt)) { lpCMap = (LPCMAP)lplpCharTab[0]->lpCMapTab; // Map Mac string back to Windows string for (lpb = szSearchKey; ; lpb++, lpstrPrefix++ ) { if ((*lpb = *lpstrPrefix) == EMBEDFONT_BYTE_TAG) { /* Load new table */ lpstrPrefix++; lpb++; lpCMap = lplpCharTab[*lpb = *lpstrPrefix]->lpCMapTab; lpstrPrefix++; lpb++; } *lpb = lpCMap[*lpstrPrefix].MacToWin; if (*lpstrPrefix == 0) break; } // lookup the entry in the btree file. rc=RcLookupByKey(pInfo->hbt, (KEY)szSearchKey, &btpos, NULL); } else #endif // look up the prefix in the btree. rc=RcLookupByKey(pInfo->hbt, (KEY)lpcvPrefix, &btpos, NULL); // if caller asked for exact match and it didn't happen, // return w/ an error if (fExactMatch) { if (rc != S_OK) { SetErrCode(phr, rc); warning_abort; } } if (rc != S_OK && rc != E_NOTEXIST) { SetErrCode(phr, rc); warning_abort; } // If we ran off the end, then position ourselves at the // last key in the btree. if (!FValidPos(&btpos)) { // maybe this was here for merge update; at any rate, // setting lRval to btpos.cKey is wrong #if 0 if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos))!= S_OK) { SetErrCode(phr, rc); warning_abort; } lRval = btpos.cKey; //ericjut: Putting the good value #endif if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos)) != S_OK) warning_abort; lRval = pInfo->lNumKws - 1; ifOver1 = TRUE; } else { // We are somewhere in the btree. We have either typed in // a string which is a prefix to a keyword in the btree or not. // See where we landed in the btree and compare the keyword in // the dialog with where we are. rc = RcLookupByPos(pInfo->hbt,&btpos, (KEY)rgbKeyTemp, ITWW_CBKEY_MAX ,NULL); if (rc == S_OK) rc = RcIndexFromKeyHbt(pInfo->hbt, pInfo->hmapbt, (LPLONG)&lRval, (KEY)rgbKeyTemp); if (rc != S_OK) { lRval = -1; SetErrCode(phr, rc); warning_abort; } // If the keyword we looked for is not a prefix of the // string at btpos, then we are positioned at the keyword // that would follow this keyword if it were in fact in the // btree. Back up one keyword to let him see the previous // one to give enough context so he sees his is not present. // If already at the first keyword, don't back up any farther. if (!FIsPrefix(pInfo->hbt, (KEY)lpcvPrefix, (KEY)rgbKeyTemp)) if (lRval > 0) lRval--; } #ifdef MERGE_UPDATE if (pWheel->wNumWheels==2) // Look up in update as well { pInfo = pWheel->pInfo+1; // Getting rid of the unwanted duplicates. if (ifOver1) lRval = WWDuplicateRemove(lRval, pWheel); #ifdef _MAC if (lplpCharTab = (LPCHARTAB FAR *)BtreeGetCMap (pInfo->hbt)) { lpCMap = (LPCMAP)lplpCharTab[0]->lpCMapTab; for (lpb = szSearchKey; ; lpb++, lpstrPrefix++ ) { if ((*lpb = *lpstrPrefix) == EMBEDFONT_BYTE_TAG) { /* Load new table */ lpstrPrefix++; lpb++; lpCMap = lplpCharTab[*lpb = *lpstrPrefix]->lpCMapTab; lpstrPrefix++; lpb++; } *lpb = lpCMap[*lpstrPrefix].MacToWin; if (*lpstrPrefix == 0) break; } rc=RcLookupByKey(pInfo->hbt, (KEY)szSearchKey, &btpos, NULL); } else #endif rc=RcLookupByKey(pInfo->hbt, (KEY)lpcvPrefix, &btpos, NULL); if (rc != S_OK && rc != ERR_NOTEXIST) { SetErrCode(phr, rc); warning_abort; } if (!FValidPos(&btpos)) { if ((rc = RcLastHbt(pInfo->hbt, (KEY)NULL, NULL, &btpos)) != S_OK) { SetErrCode(phr, rc); warning_abort; } lRval2 = btpos.cKey; //ericjut: Putting the good value ifOver2 = TRUE; } else { rc = RcLookupByPos(pInfo->hbt,&btpos,(KEY)rgbKeyTemp2,ITWW_CBKEY_MAX,NULL); if (rc == S_OK) rc = RcIndexFromKeyHbt(pInfo->hbt, pInfo->hmapbt, (LPLONG)&lRval2, (KEY)rgbKeyTemp2); if (rc != S_OK) { lRval2 = -1; SetErrCode(phr, rc); warning_abort; } if (!FIsPrefix(pInfo->hbt,(KEY)lpcvPrefix,(KEY)rgbKeyTemp2)) if (lRval2 > 0) lRval2--; } // Now find lowest virtual index for tree 0, index lRval or tree 1, index lRval2 lRval=RealToVirtual(lRval, 0, pWheel); lRval2=RealToVirtual(lRval2, 1, pWheel); if ((lRval2hMergeData) lRval=RealToVirtual(lRval, 0, pWheel); } #endif // MERGE_UPDATE if (pWheel->pIITGroup) { // In a filtered situation, let's find the proper offset. *phr = (pWheel->pIITGroup)->FindOffset((DWORD)lRval, (DWORD*)(&lRval)); if (FAILED(*phr) && (E_NOTEXIST != *phr)) goto cleanup; } *phr = S_OK; cleanup: if (pWheel!=NULL) _GLOBALUNLOCK(hWheel); return lRval; } // Little internal function to figure out if a UID is part of BOOL static IsInFilter(_LPGROUP qFilter, LONG lOffset) { LONG lNbBytes = lOffset / 8; LONG lNbBits = lOffset % 8; BYTE bMask = 1 << lNbBits; return ((BYTE)*(qFilter->lpbGrpBitVect+lNbBytes)) & bMask; } // Returns S_OK if the btree's key type is a valid one for the word wheel. // *ppITSortKey is always set based on one of the following cases: // // to NULL : if any error occurs, which includes an unsupported // key type or if the key type is KT_EXTSORT but no // pointer to the external sort instance has been specified. // // to non-NULL : the key type is KT_EXTSORT and a pointer to an // external sort instance has been specified. HRESULT FAR PASCAL CheckWordWheelKeyType(HBT hbt, IITSortKey **ppITSortKey) { BTREE_PARAMS btp; HRESULT hr = S_OK; if (ppITSortKey == NULL) return (E_POINTER); *ppITSortKey = NULL; GetBtreeParams(hbt, &btp); switch (btp.rgchFormat[0]) { case KT_EXTSORT: BtreeGetExtSort(hbt, ppITSortKey); if (*ppITSortKey == NULL) hr = E_INVALIDSTATE; break; case KT_SZ: case KT_SZMAP: break; default: // Word wheel doesn't support any other key types. hr = E_BADFORMAT; break; }; return (hr); } DWORD FAR PASCAL CbKeyWordWheel(LPVOID lpvKey, IITSortKey *pITSortKey) { DWORD cbKey; if (pITSortKey == NULL) { // Btree key type is a KT_SZx which we understand, so we'll determine // the key size ourselves. cbKey = (DWORD) STRLEN((char *) lpvKey) + 1; } else { // Get the key size from the sort object. if (FAILED(pITSortKey->GetSize(lpvKey, &cbKey))) { cbKey = 0; ITASSERT(FALSE); } } return (cbKey); }