Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

682 lines
19 KiB

  1. /*************************************************************************
  2. * @doc SHROOM EXTERNAL API *
  3. * *
  4. * INDEXIMP.CPP *
  5. * *
  6. * Copyright (C) Microsoft Corporation 1997 *
  7. * All Rights reserved. *
  8. * *
  9. * This file contains the implementation of the index object *
  10. * *
  11. * *
  12. **************************************************************************
  13. * *
  14. * Written By : Erin Foxford *
  15. * Current Owner: erinfox *
  16. * *
  17. **************************************************************************/
  18. #include <mvopsys.h>
  19. #ifdef _DEBUG
  20. static char s_aszModule[] = __FILE__; /* For error report */
  21. #endif
  22. #include <atlinc.h>
  23. // MediaView (InfoTech) includes
  24. #include <groups.h>
  25. #include <wwheel.h>
  26. #include <itquery.h>
  27. #include <itcat.h>
  28. #include <itwbrk.h>
  29. #include <ccfiles.h>
  30. #include <itwbrkid.h>
  31. #include "indeximp.h"
  32. #include "queryimp.h"
  33. #include "mvsearch.h"
  34. #include <ITDB.h>
  35. #include <itcc.h> // for STDPROP_UID def.
  36. #include <itrs.h> // for IITResultSet def.
  37. #include <itgroup.h>
  38. #define QUERYRESULT_GROUPCREATE 0x0800
  39. //----------------------------------------------------------------------
  40. // REVIEW (billa): Need to add critical section locking to all methods
  41. // that reference member variables.
  42. //----------------------------------------------------------------------
  43. /********************************************************************
  44. * @method STDMETHODIMP | IITIndex | Open |
  45. * Opens a full-text index, which can reside in the database's
  46. * storage or as a Win32 file.
  47. *
  48. * @parm IITDatabase* | pITDB | Pointer to database associated with
  49. * full-text index
  50. * @parm LPCWSTR | lpszIndexMoniker | Name of full-text index to open.
  51. * If index resides outside database (as a file), this should include
  52. * the full path to the index.
  53. * @parm BOOL | fInside | If TRUE, index resides inside of database;
  54. * otherwise, index resides outside of database.
  55. *
  56. * @rvalue S_OK | The index was successfully opened
  57. ********************************************************************/
  58. STDMETHODIMP CITIndexLocal::Open(IITDatabase* pITDB, LPCWSTR lpszIndexMoniker,
  59. BOOL fInside)
  60. {
  61. HFPB hfpb = NULL;
  62. HRESULT hr;
  63. INDEXINFO indexinfo;
  64. char szFileName[_MAX_PATH + 1] = SZ_FI_STREAM_A;
  65. if (m_idx)
  66. return (SetErrReturn(E_ALREADYINIT));
  67. // We have to have a database for charmap (and stoplist and
  68. // operator table eventually)
  69. if (NULL == pITDB || NULL == lpszIndexMoniker)
  70. return (SetErrReturn(E_INVALIDARG));
  71. m_cs.Lock();
  72. // if index is inside storage, need to get hfpb
  73. if (fInside)
  74. {
  75. WCHAR rgwch[1];
  76. IStorage *pStorageDBRoot = NULL;
  77. // Get root storage from database.
  78. rgwch[0] = (WCHAR) NULL;
  79. if (FAILED(hr = pITDB->GetObjectPersistence(rgwch, IITDB_OBJINST_NULL,
  80. (LPVOID *)&pStorageDBRoot, FALSE)) ||
  81. (hfpb = (HFPB)FpbFromHfs(pStorageDBRoot, &hr)) == NULL)
  82. {
  83. if (pStorageDBRoot != NULL)
  84. pStorageDBRoot->Release();
  85. m_cs.Unlock();
  86. return (hr);
  87. }
  88. }
  89. // TODO: make MVIndexOpen take Unicode file name. This might take a little
  90. // work because it depends on FileOpen, which has a call to one of the Fm
  91. // functions...
  92. DWORD dwSize = (DWORD) STRLEN(szFileName);
  93. WideCharToMultiByte(CP_ACP, 0, lpszIndexMoniker, -1,
  94. szFileName + dwSize, _MAX_PATH + 1 - dwSize, NULL, NULL);
  95. if (NULL == (m_idx = MVIndexOpen(hfpb, (LSZ) szFileName, &hr)))
  96. goto cleanup;
  97. MVGetIndexInfoLpidx(m_idx, &indexinfo);
  98. if (SUCCEEDED(hr = pITDB->GetObject(indexinfo.dwBreakerInstID,
  99. IID_IWordBreaker, (LPVOID *) &m_piwbrk)))
  100. {
  101. BOOL fLicense;
  102. hr = m_piwbrk->Init(TRUE, CB_MAX_WORD_LEN, &fLicense);
  103. }
  104. if (FAILED(hr))
  105. goto cleanup;
  106. // Open catalog object - we only need one instance
  107. // TODO (evaluate): how bad of hit is this going to be?
  108. hr = CoCreateInstance(CLSID_IITCatalogLocal, NULL, CLSCTX_INPROC_SERVER, IID_IITCatalog,
  109. (VOID **) &m_pCatalog);
  110. if (FAILED(hr))
  111. goto cleanup;
  112. // if it fails, there is no catalog which we can run without.
  113. if (FAILED(m_pCatalog->Open(pITDB)))
  114. {
  115. m_pCatalog->Release();
  116. m_pCatalog = NULL;
  117. }
  118. cleanup:
  119. if (FAILED(hr))
  120. Close();
  121. // If we have an HFPB for the DB's root storage, we need to release the
  122. // storage pointer and free the HFPB. FileClose takes care of everything.
  123. if (hfpb)
  124. FileClose(hfpb);
  125. m_cs.Unlock();
  126. return hr;
  127. }
  128. /********************************************************************
  129. * @method STDMETHODIMP | IITIndex | CreateQueryInstance |
  130. * Creates a query object
  131. *
  132. * @parm IITQuery** | ppITQuery | Indirect pointer to query object
  133. *
  134. * @rvalue S_OK | The query object was successfully returned
  135. *
  136. ********************************************************************/
  137. STDMETHODIMP CITIndexLocal::CreateQueryInstance(IITQuery** ppITQuery)
  138. {
  139. // TODO: possible optimization in case where user specifies multiple
  140. // query objects... get class factory pointer once; then call CreateInstance
  141. // Free CF when all done w/ index object.
  142. return CoCreateInstance(CLSID_IITQuery, NULL, CLSCTX_INPROC_SERVER, IID_IITQuery,
  143. (VOID **) ppITQuery);
  144. }
  145. /********************************************************************
  146. * @method STDMETHODIMP | IITIndex | Search |
  147. * Performs a full-text search on the open index, returning the
  148. * results in a result set object.
  149. *
  150. * @parm IITQuery* | pITQuery | Pointer to query object
  151. * @parm IITResultSet* | pITResult | Pointer to result set object
  152. * containing search results. Caller is responsible for initializing
  153. * the result set with the properties to be returned.
  154. *
  155. * @rvalue S_FALSE | The search was successful, but returned no hits.
  156. * @rvalue S_OK | The search was successfully performed.
  157. * @rvalue E_NOTOPEN | The index object is not open.
  158. * @rvalue E_INVALIDARG | One or both parameters is NULL.
  159. * @rvalue E_OUTOFMEMORY | There was not enough memory to perform this function.
  160. * @rvalue E_NULLQUERY | The query consisted of no terms, or is all stopwords.
  161. * @rvalue E_STOPWORD | A stopword was one of the terms in the query.
  162. * @rvalue E_* | An error occurred during the search. Check iterror.h for the possible error codes.
  163. *
  164. * @comm The caller is responsible for setting the proper options
  165. * through the query object before calling this function.
  166. ********************************************************************/
  167. STDMETHODIMP CITIndexLocal::Search(IITQuery* pITQuery, IITResultSet* pITResult)
  168. {
  169. HRESULT hr;
  170. CITIndexObjBridge *pidxobr = NULL;
  171. LPQT pQueryTree = NULL; // Pointer to query tree
  172. LPHL pHitList = NULL; // Pointer to hit list
  173. IITGroup* piitGroup = NULL;
  174. _LPGROUP lpGroup;
  175. if (NULL == pITQuery || NULL == pITResult)
  176. return (SetErrReturn(E_INVALIDARG));
  177. if (m_idx == NULL)
  178. return (SetErrReturn(E_NOTOPEN));
  179. if ((pidxobr = new CITIndexObjBridge) != NULL)
  180. {
  181. pidxobr->AddRef();
  182. hr = pidxobr->SetWordBreaker(m_piwbrk);
  183. }
  184. else
  185. hr = E_OUTOFMEMORY;
  186. if (SUCCEEDED(hr) &&
  187. SUCCEEDED(hr = QueryParse(pITQuery, &pQueryTree, pidxobr)))
  188. {
  189. SRCHINFO SrchInfo; // Search parameters
  190. SrchInfo.dwMemAllowed = 0;
  191. pITQuery->GetResultCount((LONG &)SrchInfo.dwTopicCount);
  192. pITQuery->GetOptions(SrchInfo.Flag);
  193. SrchInfo.dwValue = 0;
  194. SrchInfo.dwTopicFullCalc = 0;
  195. SrchInfo.lpvIndexObjBridge = (LPVOID) pidxobr;
  196. pITQuery->GetGroup(&piitGroup);
  197. if (piitGroup)
  198. lpGroup = (_LPGROUP)piitGroup->GetLocalImageOfGroup();
  199. else
  200. lpGroup = NULL;
  201. // Perform search
  202. pHitList = MVIndexSearch(m_idx, pQueryTree, &SrchInfo, lpGroup, &hr);
  203. // Massage hitlist into a result set.
  204. if (pHitList)
  205. {
  206. hr = HitListToResultSet(pHitList, pITResult, pidxobr);
  207. MVHitListDispose(pHitList);
  208. }
  209. }
  210. if (pQueryTree)
  211. MVQueryFree(pQueryTree);
  212. // We don't want to delete pidxobr if HitListToResultSet AddRef'ed it
  213. // so that the result set can hold onto a term string heap via pidxobr.
  214. if (pidxobr && pidxobr->Release() == 0)
  215. delete pidxobr;
  216. return hr;
  217. }
  218. /********************************************************************
  219. * @method STDMETHODIMP | IITIndex | Search |
  220. * Performs a full-text search on the open index, returning the
  221. * results in a group object.
  222. *
  223. * @parm IITQuery* | pITQuery | Pointer to query object
  224. * @parm IITGroup* | pITGroup | Pointer to group object. The caller
  225. * is responsible for initializing this object before passing it.
  226. *
  227. * @rvalue S_OK | The search was successfully performed
  228. *
  229. * @comm The caller is responsible for setting the proper options
  230. * through the query object before calling this function.
  231. ********************************************************************/
  232. STDMETHODIMP CITIndexLocal::Search(IITQuery* pITQuery, IITGroup* pITGroup)
  233. {
  234. HRESULT hr = S_OK;
  235. CITIndexObjBridge *pidxobr = NULL;
  236. LPQT pQueryTree = NULL; // Pointer to query tree
  237. LPHL pHitList = NULL; // Pointer to hit list
  238. if (NULL == pITQuery || NULL == pITGroup)
  239. return (SetErrReturn(E_INVALIDARG));
  240. if (m_idx == NULL)
  241. return (SetErrReturn(E_NOTOPEN));
  242. // TODO: MVIndexSearch would take IITGroup*, not _LPGROUP
  243. _LPGROUP lpGroup = (_LPGROUP) pITGroup->GetLocalImageOfGroup();
  244. if ((pidxobr = new CITIndexObjBridge) != NULL)
  245. hr = pidxobr->SetWordBreaker(m_piwbrk);
  246. else
  247. hr = E_OUTOFMEMORY;
  248. if (SUCCEEDED(hr) &&
  249. SUCCEEDED(hr = QueryParse(pITQuery, &pQueryTree, pidxobr)))
  250. {
  251. SRCHINFO SrchInfo; // Search parameters
  252. SrchInfo.dwMemAllowed = 0;
  253. pITQuery->GetResultCount((LONG &)SrchInfo.dwTopicCount);
  254. pITQuery->GetOptions(SrchInfo.Flag);
  255. SrchInfo.Flag |= QUERYRESULT_GROUPCREATE;
  256. SrchInfo.dwValue = 0;
  257. SrchInfo.dwTopicFullCalc = 0;
  258. SrchInfo.lpvIndexObjBridge = (LPVOID) pidxobr;
  259. // Perform search - if pHitList comes back NULL, we will return hr
  260. if (pHitList = MVIndexSearch(m_idx, pQueryTree, &SrchInfo, lpGroup, &hr))
  261. MVHitListDispose(pHitList);
  262. }
  263. if (pQueryTree)
  264. MVQueryFree(pQueryTree);
  265. if (pidxobr)
  266. delete pidxobr;
  267. return hr;
  268. }
  269. // This is private - it encapsulates the query parsing needed
  270. // in all searches
  271. STDMETHODIMP CITIndexLocal::QueryParse(IITQuery* pITQuery, LPQT* pQueryTree,
  272. CITIndexObjBridge *pidxobr)
  273. {
  274. HRESULT hr = S_OK;
  275. EXBRKPM exbrkpm;
  276. PARSE_PARMS ParseParm;
  277. ITASSERT(pITQuery != NULL && pQueryTree != NULL && pidxobr != NULL);
  278. // Fill PARSE_PARMS structure
  279. DWORD dwFlags;
  280. pITQuery->GetOptions(dwFlags);
  281. if (dwFlags & QUERYRESULT_SKIPOCCINFO)
  282. m_fSkipOcc = TRUE;
  283. ParseParm.cDefOp = (WORD)(dwFlags & IMPLICIT_OR);
  284. ParseParm.wCompoundWord = (WORD)(dwFlags & COMPOUNDWORD_PHRASE);
  285. pITQuery->GetProximity(ParseParm.cProxDist);
  286. IITGroup* ITGroup;
  287. pITQuery->GetGroup(&ITGroup);
  288. if (ITGroup)
  289. {
  290. _LPGROUP lpGroup = (_LPGROUP) ITGroup->GetLocalImageOfGroup();
  291. ParseParm.lpGroup = lpGroup;
  292. }
  293. else
  294. ParseParm.lpGroup = NULL;
  295. // Breaker bridge setup
  296. exbrkpm.lpvIndexObjBridge = (LPVOID)pidxobr;
  297. ParseParm.pexbrkpm = &exbrkpm;
  298. // TODO: provide the right stuff
  299. ParseParm.lpOpTab = NULL;
  300. LPSTR lpszQuery = NULL; // Pointer to query buffer
  301. DWORD cbQuery; // Query buffer's length
  302. DWORD dwCodePageID;
  303. LCID lcid;
  304. if (FAILED(GetLocaleInfo(&dwCodePageID, &lcid)))
  305. {
  306. ITASSERT(FALSE);
  307. dwCodePageID = CP_ACP;
  308. }
  309. LPCWSTR lpszwQuery;
  310. pITQuery->GetCommand(lpszwQuery);
  311. if (NULL == lpszwQuery)
  312. return E_NULLQUERY;
  313. // Query comes in as Unicode, but the FTI still uses MBCS.
  314. cbQuery = WideCharToMultiByte
  315. (dwCodePageID, 0, lpszwQuery, -1, NULL, 0, NULL, NULL);
  316. if ((lpszQuery = new char[cbQuery]) != NULL)
  317. {
  318. WideCharToMultiByte(dwCodePageID, 0, lpszwQuery, -1, lpszQuery, cbQuery,
  319. NULL, NULL);
  320. ParseParm.cbQuery = cbQuery - 1;
  321. ParseParm.lpbQuery = (const char*) lpszQuery;
  322. }
  323. else
  324. hr = E_OUTOFMEMORY;
  325. // Parse query
  326. if (SUCCEEDED(hr))
  327. {
  328. FCALLBACK_MSG fcbkmsg;
  329. *pQueryTree = MVQueryParse (&ParseParm, &hr);
  330. if (SUCCEEDED(hr) && SUCCEEDED(pITQuery->GetResultCallback(&fcbkmsg)))
  331. MVSearchSetCallback(*pQueryTree, &fcbkmsg);
  332. }
  333. if (lpszQuery)
  334. delete lpszQuery;
  335. return hr;
  336. }
  337. /********************************************************************
  338. * @method STDMETHODIMP | IITIndex | Close |
  339. * Closes the full-text index.
  340. *
  341. * @rvalue S_OK | The index was successfully closed
  342. *
  343. ********************************************************************/
  344. STDMETHODIMP CITIndexLocal::Close()
  345. {
  346. m_cs.Lock();
  347. if (m_idx)
  348. {
  349. MVIndexClose(m_idx);
  350. m_idx = NULL;
  351. }
  352. if (m_pCatalog)
  353. {
  354. m_pCatalog->Close();
  355. m_pCatalog->Release();
  356. m_pCatalog = NULL;
  357. }
  358. if (m_piwbrk != NULL)
  359. {
  360. m_piwbrk->Release();
  361. m_piwbrk = NULL;
  362. }
  363. m_cs.Unlock();
  364. return S_OK;
  365. }
  366. /********************************************************************
  367. * @method STDMETHODIMP | IITIndex | GetLocaleInfo |
  368. * Gets locale info that the full text index was built with.
  369. * @parm DWORD* | pdwCodePageID | On exit, pointer to code page ID.
  370. * @parm LCID* | plcid | On exit, pointer to locale ID.
  371. *
  372. * @rvalue S_OK | The locale info was successfully retrieved.
  373. *
  374. ********************************************************************/
  375. STDMETHODIMP CITIndexLocal::GetLocaleInfo(DWORD *pdwCodePageID, LCID *plcid)
  376. {
  377. INDEXINFO indexinfo;
  378. if (pdwCodePageID == NULL || plcid == NULL)
  379. return (SetErrReturn(E_POINTER));
  380. if (m_idx == NULL)
  381. return (SetErrReturn(E_NOTOPEN));
  382. MVGetIndexInfoLpidx(m_idx, &indexinfo);
  383. *pdwCodePageID = indexinfo.dwCodePageID;
  384. *plcid = indexinfo.lcid;
  385. return (S_OK);
  386. }
  387. /********************************************************************
  388. * @method STDMETHODIMP | IITIndex | GetWordBreakerInstance |
  389. * Gets the ID of the word breaker instance that the full text
  390. * index was built with.
  391. * @parm DWORD* | pdwObjInstance | On exit, pointer to word breaker instance.
  392. *
  393. * @rvalue S_OK | The word breaker instance ID was successfully retrieved.
  394. *
  395. ********************************************************************/
  396. STDMETHODIMP CITIndexLocal::GetWordBreakerInstance(DWORD *pdwObjInstance)
  397. {
  398. INDEXINFO indexinfo;
  399. if (pdwObjInstance == NULL)
  400. return (SetErrReturn(E_POINTER));
  401. if (m_idx == NULL)
  402. return (SetErrReturn(E_NOTOPEN));
  403. MVGetIndexInfoLpidx(m_idx, &indexinfo);
  404. *pdwObjInstance = indexinfo.dwBreakerInstID;
  405. return (S_OK);
  406. }
  407. // Private function - passed as a parameter by
  408. // CITIndexLocal::HitListToResultSet.
  409. SCODE __stdcall FreeRSColumnHeap(LPVOID lpvIndexObjBridge)
  410. {
  411. CITIndexObjBridge *pidxobr;
  412. if (lpvIndexObjBridge == NULL)
  413. return (SetErrReturn(E_POINTER));
  414. pidxobr = (CITIndexObjBridge *) lpvIndexObjBridge;
  415. pidxobr->Release();
  416. delete pidxobr;
  417. return (S_OK);
  418. }
  419. // Private function - one grand hack to provide a result set from a hit list
  420. STDMETHODIMP CITIndexLocal::HitListToResultSet(LPHL pHitList, IITResultSet* pRS,
  421. CITIndexObjBridge *pidxobr)
  422. {
  423. DWORD cEntry; // Number of entries
  424. HIT HitInfo;
  425. TOPICINFO TopicInfo;
  426. LONG lColumnUID = -1;
  427. LONG lColumnOccInfo[5];
  428. DWORD iTopic, iHit, iColumn; // Loop indices
  429. LONG lRow = 0;
  430. HRESULT hr;
  431. ITASSERT(pRS != NULL && pidxobr != NULL);
  432. // Number of entries in hit list - if 0, just return FALSE
  433. if (0 == (cEntry = MVHitListEntries(pHitList)))
  434. return S_FALSE;
  435. hr = pRS->GetColumnFromPropID(STDPROP_UID, lColumnUID);
  436. if (!m_fSkipOcc)
  437. {
  438. for (iColumn = 0; iColumn < 5; iColumn++)
  439. lColumnOccInfo[iColumn] = -1;
  440. pRS->GetColumnFromPropID(STDPROP_FIELD, lColumnOccInfo[0]);
  441. pRS->GetColumnFromPropID(STDPROP_LENGTH, lColumnOccInfo[1]);
  442. pRS->GetColumnFromPropID(STDPROP_COUNT, lColumnOccInfo[2]);
  443. pRS->GetColumnFromPropID(STDPROP_OFFSET, lColumnOccInfo[3]);
  444. pRS->GetColumnFromPropID(STDPROP_TERM_UNICODE_ST, lColumnOccInfo[4]);
  445. }
  446. // Loop over all the topics in the hit list
  447. for (iTopic = 0; iTopic < cEntry; iTopic++)
  448. {
  449. hr = MVHitListGetTopic(pHitList, iTopic, &TopicInfo);
  450. if (FAILED(hr))
  451. return hr; // or do we continue?
  452. if (m_fSkipOcc)
  453. {
  454. // No occurrence info
  455. if (-1 != lColumnUID)
  456. pRS->Set(lRow, lColumnUID, TopicInfo.dwTopicId);
  457. lRow++;
  458. }
  459. else
  460. {
  461. // Requested occurence info, so loop
  462. // over all the hits in this topic
  463. for (iHit = 0; iHit < TopicInfo.lcHits; iHit++)
  464. {
  465. if (-1 != lColumnUID)
  466. pRS->Set(lRow, lColumnUID, TopicInfo.dwTopicId);
  467. hr = MVHitListGetHit(pHitList, &TopicInfo, iHit, &HitInfo);
  468. if (FAILED(hr))
  469. continue;
  470. if (-1 != lColumnOccInfo[0])
  471. pRS->Set(lRow, lColumnOccInfo[0], HitInfo.dwFieldId);
  472. if (-1 != lColumnOccInfo[1])
  473. pRS->Set(lRow, lColumnOccInfo[1], HitInfo.dwLength);
  474. if (-1 != lColumnOccInfo[2])
  475. pRS->Set(lRow, lColumnOccInfo[2], HitInfo.dwCount);
  476. if (-1 != lColumnOccInfo[3])
  477. pRS->Set(lRow, lColumnOccInfo[3], HitInfo.dwOffset);
  478. if (-1 != lColumnOccInfo[4])
  479. pRS->Set(lRow, lColumnOccInfo[4], (DWORD_PTR) HitInfo.lpvTerm);
  480. lRow++;
  481. }
  482. }
  483. }
  484. // Fill in rest of properties from catalog (like IITWordWheel::GetData)
  485. if (m_pCatalog)
  486. {
  487. hr = m_pCatalog->Lookup(pRS);
  488. if (S_FALSE == hr)
  489. hr = S_OK; // don't report S_FALSE
  490. }
  491. // If the caller requested Unicode term STs, then we need to give the result
  492. // set the string heap and adjust the string lengths in the heap. Otherwise,
  493. // we will just let the heap get freed whenever pidxobr gets deleted.
  494. if (-1 != lColumnOccInfo[4])
  495. {
  496. pidxobr->AdjustQueryResultTerms();
  497. pRS->SetColumnHeap(lColumnOccInfo[4], (LPVOID) pidxobr, FreeRSColumnHeap);
  498. // Tell our caller not to delete pidxobr because the result set is
  499. // holding onto it.
  500. pidxobr->AddRef();
  501. }
  502. return S_OK;
  503. }
  504. // Need to export these without decoration to the linker so they can be called
  505. // from the old .c files.
  506. extern "C" {
  507. PUBLIC HRESULT EXPORT_API FAR PASCAL
  508. ExtBreakText(PEXBRKPM pexbrkpm)
  509. {
  510. CITIndexObjBridge *pidxobr;
  511. if (pexbrkpm == NULL || pexbrkpm->lpvIndexObjBridge == NULL)
  512. return (SetErrReturn(E_POINTER));
  513. pidxobr = (CITIndexObjBridge *) pexbrkpm->lpvIndexObjBridge;
  514. return (pidxobr->BreakText(pexbrkpm));
  515. }
  516. PUBLIC HRESULT EXPORT_API FAR PASCAL
  517. ExtStemWord(LPVOID lpvIndexObjBridge, LPBYTE lpbStemWord, LPBYTE lpbRawWord)
  518. {
  519. CITIndexObjBridge *pidxobr;
  520. if (lpvIndexObjBridge == NULL ||
  521. lpbStemWord == NULL || lpbRawWord == NULL)
  522. return (SetErrReturn(E_POINTER));
  523. pidxobr = (CITIndexObjBridge *) lpvIndexObjBridge;
  524. return (pidxobr->StemWord(lpbStemWord, lpbRawWord));
  525. }
  526. PUBLIC HRESULT EXPORT_API FAR PASCAL
  527. ExtLookupStopWord(LPVOID lpvIndexObjBridge, LPBYTE lpbStopWord)
  528. {
  529. CITIndexObjBridge *pidxobr;
  530. if (lpvIndexObjBridge == NULL || lpbStopWord == NULL)
  531. return (SetErrReturn(E_POINTER));
  532. pidxobr = (CITIndexObjBridge *) lpvIndexObjBridge;
  533. return (pidxobr->LookupStopWord(lpbStopWord));
  534. }
  535. PUBLIC HRESULT EXPORT_API FAR PASCAL
  536. ExtAddQueryResultTerm(LPVOID lpvIndexObjBridge, LPBYTE lpbTermHit,
  537. LPVOID *ppvTermHit)
  538. {
  539. CITIndexObjBridge *pidxobr;
  540. if (lpvIndexObjBridge == NULL || lpbTermHit == NULL || ppvTermHit == NULL)
  541. return (SetErrReturn(E_POINTER));
  542. pidxobr = (CITIndexObjBridge *) lpvIndexObjBridge;
  543. return (pidxobr->AddQueryResultTerm(lpbTermHit, ppvTermHit));
  544. }
  545. } // End extern "C"