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.

2890 lines
80 KiB

  1. // FTS.CPP : implementation file
  2. //
  3. // by DougO
  4. //
  5. #include "header.h"
  6. #include "stdio.h"
  7. #include "string.h"
  8. #include "TCHAR.h"
  9. #ifdef HHCTRL
  10. #include "parserhh.h"
  11. #else
  12. #include "parser.h"
  13. #endif
  14. #include "collect.h"
  15. #include "hhtypes.h"
  16. #include "toc.h"
  17. #include "highlite.h"
  18. #include "lockout.h"
  19. #include "userwait.h"
  20. #include "fts.h"
  21. #include "hhfinder.h"
  22. #include "csubset.h"
  23. #include "subset.h"
  24. #ifdef _DEBUG
  25. #undef THIS_FILE
  26. static const char THIS_FILE[] = __FILE__;
  27. #endif
  28. #include "ftsnames.cpp"
  29. #define CENTAUR_TERMS 1
  30. /*************************************************************************
  31. *
  32. * SINGLE TO DOUBLE-WIDTH KATAKANA MAPPING ARRAY
  33. *
  34. *************************************************************************/
  35. // Single-Width to Double-Width Mapping Array
  36. //
  37. static const int mtable[][2]={
  38. {129,66},{129,117},{129,118},{129,65},{129,69},{131,146},{131,64},
  39. {131,66},{131,68},{131,70},{131,72},{131,131},{131,133},{131,135},
  40. {131,98},{129,91},{131,65},{131,67},{131,69},{131,71},{131,73},
  41. {131,74},{131,76},{131,78},{131,80},{131,82},{131,84},{131,86},
  42. {131,88},{131,90},{131,92},{131,94},{131,96},{131,99},{131,101},
  43. {131,103},{131,105},{131,106},{131,107},{131,108},{131,109},
  44. {131,110},{131,113},{131,116},{131,119},{131,122},{131,125},
  45. {131,126},{131,128},{131,129},{131,130},{131,132},{131,134},
  46. {131,136},{131,137},{131,138},{131,139},{131,140},{131,141},
  47. {131,143},{131,147},{129,74},{129,75} };
  48. // note, cannot put in .text since the pointers themselves are uninitialized
  49. static const char* pJOperatorList[] = {"","?�?�??","?�??","?�?�??","?�???�??","?m?d?`?q","?n?q","?`?m?c","?m?n?s",""};
  50. static const char* pEnglishOperator[] = {"","and " ,"or " ,"not " ,"near " ,"NEAR " ,"OR " ,"AND " ,"NOT " ,""};
  51. int FASTCALL CompareVolumeOrder( const void* p1, const void* p2 )
  52. {
  53. int iReturn;
  54. TITLE_ENTRY* pEntry1= (TITLE_ENTRY*) p1;
  55. TITLE_ENTRY* pEntry2= (TITLE_ENTRY*) p2;
  56. if( pEntry1->uiVolumeOrder < pEntry2->uiVolumeOrder )
  57. iReturn = -1;
  58. else if ( pEntry1->uiVolumeOrder > pEntry2->uiVolumeOrder )
  59. iReturn = 1;
  60. else
  61. iReturn = 0;
  62. return iReturn;
  63. }
  64. /////////////////////////////////////////////////////////////////////////////
  65. // CFullTextSearch Class
  66. //
  67. // This class provides full-text search functionality to HTML Help. This
  68. // class encapsulates multi-title searches and combined indexes.
  69. //
  70. /////////////////////////////////////////////////////////////////////////////
  71. // CFullTextSearch class constructor
  72. //
  73. CFullTextSearch::CFullTextSearch(CExCollection *pTitleCollection)
  74. {
  75. m_bInit = FALSE;
  76. m_bTitleArrayInit = FALSE;
  77. m_SearchActive = FALSE;
  78. m_InitFailed = FALSE;
  79. m_pTitleCollection = pTitleCollection;
  80. m_pTitleArray = NULL;
  81. m_bMergedChmSetWithCHQ = FALSE;
  82. m_SystemLangID = PRIMARYLANGID(GetSystemDefaultLangID());
  83. }
  84. /////////////////////////////////////////////////////////////////////////////
  85. // CFullTextSearch class destructor
  86. //
  87. CFullTextSearch::~CFullTextSearch()
  88. {
  89. TermListRemoveAll();
  90. INT iTitle, iTitle2;
  91. // Delete the CCombinedFTS objects
  92. //
  93. if(m_pTitleArray)
  94. {
  95. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  96. {
  97. if(m_pTitleArray[iTitle].pCombinedFTI)
  98. {
  99. CCombinedFTS *pObject = m_pTitleArray[iTitle].pCombinedFTI;
  100. for(iTitle2 = 0; iTitle2 < m_TitleArraySize; ++iTitle2)
  101. {
  102. if(m_pTitleArray[iTitle2].pCombinedFTI == pObject)
  103. m_pTitleArray[iTitle2].pCombinedFTI = NULL;
  104. }
  105. delete pObject;
  106. }
  107. // cleanup strings in title array
  108. CHECK_AND_FREE( m_pTitleArray[iTitle].pszQueryName );
  109. CHECK_AND_FREE( m_pTitleArray[iTitle].pszIndexName );
  110. CHECK_AND_FREE( m_pTitleArray[iTitle].pszShortName );
  111. }
  112. }
  113. if(m_pTitleArray)
  114. lcFree(m_pTitleArray);
  115. }
  116. /////////////////////////////////////////////////////////////////////////////
  117. // CFullTextSearch initialization
  118. //
  119. BOOL CFullTextSearch::Initialize()
  120. {
  121. if(m_InitFailed)
  122. return FALSE;
  123. if(m_bInit)
  124. return TRUE;
  125. m_InitFailed = TRUE;
  126. // init here
  127. m_bInit = TRUE;
  128. m_InitFailed = FALSE;
  129. ZeroMemory(m_HLTermArray, sizeof(m_HLTermArray));
  130. m_iHLIndex = 0;
  131. m_lMaxRowCount = 500;
  132. m_wQueryProximity = 8;
  133. // DOUGO - insert code here to init system language member
  134. return TRUE;
  135. }
  136. /////////////////////////////////////////////////////////////////////////////
  137. // CFullTextSearch simple query
  138. //
  139. // pszQuery pointer to null terminated query string.
  140. //
  141. // pcResultCount pointer to variable to receive results count.
  142. //
  143. // ppSearchResults pointer to a SEARCH_RESULT pointer to receive
  144. // results list. Upon return, this pointer will
  145. // point to an array of SEARCH_RESULTS structures
  146. // of size pcResultCount.
  147. //
  148. HRESULT CFullTextSearch::SimpleQuery(WCHAR *pszQuery, int *pcResultCount, SEARCH_RESULT **ppSearchResults)
  149. {
  150. if(!m_bInit)
  151. return FTS_NOT_INITIALIZED;
  152. return ComplexQuery(pszQuery, FTS_ENABLE_STEMMING, pcResultCount, ppSearchResults, NULL);
  153. }
  154. /////////////////////////////////////////////////////////////////////////////
  155. // CFullTextSearch complex query
  156. //
  157. HRESULT CFullTextSearch::ComplexQuery(WCHAR *pszQuery, DWORD dwFlags, int *pcResultCount, SEARCH_RESULT **ppSearchResults, CSubSet *pSubSet)
  158. {
  159. int wcb = 0, cb = 0;
  160. HRESULT hr;
  161. *ppSearchResults = NULL;
  162. *pcResultCount = 0;
  163. if(!m_bInit)
  164. return E_FAIL;
  165. if(!pszQuery)
  166. return E_FAIL;
  167. CUWait cWaitDlg(GetActiveWindow());
  168. // no chars in string
  169. //
  170. if(!*pszQuery)
  171. return S_OK;
  172. // Initialize the title array
  173. //
  174. if(!m_bTitleArrayInit) {
  175. InitTitleArray();
  176. }
  177. // Make sure title array exists
  178. //
  179. if(!m_pTitleArray)
  180. return E_FAIL;
  181. INT iTitle;
  182. // reset the results flags (this flag is set when a title has results to collect)
  183. //
  184. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  185. {
  186. m_pTitleArray[iTitle].bHasResults = FALSE;
  187. m_pTitleArray[iTitle].bAlreadyQueried = FALSE;
  188. }
  189. BOOL bDbcsQuery = FALSE;
  190. char *pszDbcsQuery = NULL;
  191. WCHAR *pwsBuffer = NULL;
  192. pwsBuffer = lcStrDupW(pszQuery);
  193. if(!pwsBuffer)
  194. return E_FAIL;
  195. TermListRemoveAll();
  196. AddQueryToTermList(pwsBuffer);
  197. // Add field identifier to query (VFLD 0 = full content, VFLD 1 = title only)
  198. //
  199. wcb = lstrlenW(pwsBuffer)*2;
  200. WCHAR *pwsField = (WCHAR *) lcMalloc(wcb+22);
  201. if(!pwsField)
  202. return E_FAIL;
  203. // check for title only search
  204. //
  205. wcscpy(pwsField,L"(");
  206. if(dwFlags & FTS_TITLE_ONLY)
  207. wcscat(pwsField,L"VFLD 1 ");
  208. else
  209. wcscat(pwsField,L"VFLD 0 ");
  210. wcscat(pwsField,pwsBuffer);
  211. wcscat(pwsField,L")");
  212. lcFree(pwsBuffer);
  213. pwsBuffer = pwsField;
  214. // Append the subset filter query to the user query
  215. //
  216. // Construct the subset query based on the current FTS subset.
  217. //
  218. WCHAR szSubsetFilter[65534];
  219. DWORD dwSubsetTitleCount = 0;
  220. CStructuralSubset *pSubset;
  221. // pre-load combined index for merged chm sets (NT5)
  222. //
  223. if(m_bMergedChmSetWithCHQ)
  224. LoadCombinedIndex(0);
  225. // copy the user query into buffer larger enough for subset add-on query
  226. //
  227. wcscpy(szSubsetFilter, pwsBuffer);
  228. // Create subset for titles in current merged chm set when running with
  229. // combined index outside of collection (NT5 feature).
  230. //
  231. if(!(dwFlags & FTS_SEARCH_PREVIOUS) && m_bMergedChmSetWithCHQ)
  232. {
  233. wcscat(szSubsetFilter,L" AND (VFLD 1 ");
  234. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  235. {
  236. if(!m_pTitleArray[iTitle].bCombinedIndex)
  237. continue;
  238. if(dwSubsetTitleCount != 0)
  239. wcscat(szSubsetFilter,L" OR ");
  240. // Convert title name to Unicode
  241. //
  242. char szTempIdentifier[256], szTempLang[20];
  243. WCHAR wszTempIdentifier[256];
  244. // generate the magic title identifier
  245. //
  246. strcpy(szTempIdentifier,"HHTitleID");
  247. strcat(szTempIdentifier,m_pTitleArray[iTitle].pszShortName);
  248. szTempLang[0] = 0;
  249. Itoa(m_pTitleArray[iTitle].language, szTempLang);
  250. strcat(szTempIdentifier,szTempLang);
  251. MultiByteToWideChar(CP_ACP, 0, szTempIdentifier, -1, wszTempIdentifier, sizeof(wszTempIdentifier));
  252. // Append the title identifier to the subset query
  253. //
  254. wcscat(szSubsetFilter,wszTempIdentifier);
  255. dwSubsetTitleCount++;
  256. }
  257. // Close the expression
  258. //
  259. wcscat(szSubsetFilter,L")");
  260. }
  261. else
  262. // Get the current CStructuralSubset and check if "search previous" is active.
  263. // When "search previous" is on, we don't use subsetting (we query within the previous results)
  264. //
  265. if(m_pTitleCollection && m_pTitleCollection->m_pSSList)
  266. {
  267. if( (pSubset = m_pTitleCollection->m_pSSList->GetFTS()) && !(dwFlags & FTS_SEARCH_PREVIOUS) && !pSubset->IsEntire() )
  268. {
  269. wcscat(szSubsetFilter,L" AND (VFLD 1 ");
  270. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  271. {
  272. // Check if this title is part of the current subset
  273. //
  274. if(pSubset->IsTitleInSubset(m_pTitleArray[iTitle].pExTitle))
  275. {
  276. if(dwSubsetTitleCount != 0)
  277. wcscat(szSubsetFilter,L" OR ");
  278. // Convert title name to Unicode
  279. //
  280. char szTempIdentifier[256], szTempLang[20];
  281. WCHAR wszTempIdentifier[256];
  282. // generate the magic title identifier
  283. //
  284. strcpy(szTempIdentifier,"HHTitleID");
  285. strcat(szTempIdentifier,m_pTitleArray[iTitle].pszShortName);
  286. szTempLang[0] = 0;
  287. Itoa(m_pTitleArray[iTitle].language, szTempLang);
  288. strcat(szTempIdentifier,szTempLang);
  289. MultiByteToWideChar(CP_ACP, 0, szTempIdentifier, -1, wszTempIdentifier, sizeof(wszTempIdentifier));
  290. // Append the title identifier to the subset query
  291. //
  292. wcscat(szSubsetFilter,wszTempIdentifier);
  293. dwSubsetTitleCount++;
  294. }
  295. }
  296. // Close the expression
  297. //
  298. wcscat(szSubsetFilter,L")");
  299. }
  300. }
  301. IITResultSet* pITRS = NULL;
  302. LONG CombinedResultCount = 0;
  303. SEARCH_RESULT *pNextResult;
  304. BOOL bNeverPrompt;
  305. int QueryPhase;
  306. // Query Phase #1 - submit queries to any titles currently available (in CD drive, or on HD)
  307. // Query Phase #2 - submit queries to all titles in CD order
  308. //
  309. for(QueryPhase = 0; QueryPhase < 2; QueryPhase++)
  310. {
  311. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  312. {
  313. if(QueryPhase)
  314. bNeverPrompt = FALSE;
  315. else
  316. bNeverPrompt = TRUE;
  317. if(m_pTitleArray[iTitle].bSearch && !m_pTitleArray[iTitle].bAlreadyQueried)
  318. {
  319. if(m_pTitleArray[iTitle].bCombinedIndex)
  320. {
  321. // Query combined index
  322. //
  323. CExTitle* pTitle = m_pTitleArray[iTitle].pExTitle;
  324. // skip title if bad pTitle or we already have results for this query
  325. //
  326. if(!pTitle | m_pTitleArray[iTitle].bHasResults | m_pTitleArray[iTitle].bAlreadyQueried)
  327. continue;
  328. // Make sure the storage is available
  329. //
  330. if(!m_bMergedChmSetWithCHQ && m_pTitleCollection && !(m_pTitleCollection->IsSingleTitle()))
  331. if( FAILED(hr = EnsureStorageAvailability( pTitle, HHRMS_TYPE_COMBINED_QUERY, FALSE, FALSE, bNeverPrompt ) ) )
  332. continue;
  333. // create combined index object if it doesn't exist
  334. //
  335. if(!m_pTitleArray[iTitle].pCombinedFTI)
  336. {
  337. // [paulti] must re-init query location before calling LoadCombinedIndex if dirty
  338. if(m_pTitleArray[iTitle].pExTitle->m_pCollection->m_Collection.IsDirty())
  339. {
  340. lcFree( m_pTitleArray[iTitle].pszQueryName );
  341. m_pTitleArray[iTitle].pszQueryName = lcStrDup(
  342. m_pTitleArray[iTitle].pExTitle->GetUsedLocation()->QueryFileName );
  343. }
  344. if(!m_bMergedChmSetWithCHQ)
  345. {
  346. if(!LoadCombinedIndex(iTitle))
  347. {
  348. // Load failed - not part of specified combined index.
  349. // This title is now disabled for this session.
  350. //
  351. m_pTitleArray[iTitle].bSearch = FALSE;
  352. continue;
  353. }
  354. }
  355. }
  356. // All is happy - set options and query the combined index
  357. //
  358. m_pTitleArray[iTitle].pCombinedFTI->UpdateOptions(m_wQueryProximity,m_lMaxRowCount);
  359. if(FAILED(hr = m_pTitleArray[iTitle].pCombinedFTI->Query(szSubsetFilter, dwFlags, &pITRS, this, &cWaitDlg, iTitle)))
  360. {
  361. if(hr == FTS_INVALID_SYNTAX || hr == FTS_CANCELED)
  362. {
  363. m_iLastResultCount = 0;
  364. return hr;
  365. }
  366. // error accessing title
  367. m_pTitleArray[iTitle].bSearch = FALSE;
  368. continue;
  369. }
  370. m_pTitleArray[iTitle].bHasResults = TRUE;
  371. m_pTitleArray[iTitle].bAlreadyQueried = TRUE;
  372. CombinedResultCount+=ComputeResultCount(pITRS);
  373. // Mark all other titles that use this combined index as having been queried
  374. //
  375. INT iTempTitle;
  376. for(iTempTitle = 0; iTempTitle < m_TitleArraySize; ++iTempTitle)
  377. if(m_pTitleArray[iTempTitle].pCombinedFTI == m_pTitleArray[iTitle].pCombinedFTI)
  378. m_pTitleArray[iTempTitle].bAlreadyQueried = TRUE;
  379. }
  380. else
  381. {
  382. // Query index in individual title
  383. //
  384. CExTitle* pTitle = m_pTitleArray[iTitle].pExTitle;
  385. // skip title if bad pTitle or we already have results for this query
  386. //
  387. if(!pTitle || m_pTitleArray[iTitle].bHasResults || m_pTitleArray[iTitle].bAlreadyQueried )
  388. continue;
  389. CTitleInformation *pTitleInfo = m_pTitleArray[iTitle].pExTitle->GetInfo();
  390. if(!(pTitleInfo && pTitleInfo->IsFullTextSearch()))
  391. {
  392. // This title turned out to not contain a FTI. We delay this check until now
  393. // for performance reasons.
  394. //
  395. m_pTitleArray[iTitle].bSearch = FALSE;
  396. continue;
  397. }
  398. if(!m_bMergedChmSetWithCHQ && m_pTitleCollection && !(m_pTitleCollection->IsSingleTitle()))
  399. if( FAILED(hr = EnsureStorageAvailability( pTitle, HHRMS_TYPE_TITLE, TRUE, FALSE, bNeverPrompt ) ) )
  400. continue;
  401. m_pTitleArray[iTitle].pExTitle->m_pTitleFTS->UpdateOptions(m_wQueryProximity,m_lMaxRowCount);
  402. if(FAILED(hr = m_pTitleArray[iTitle].pExTitle->m_pTitleFTS->Query(pwsBuffer, dwFlags, &pITRS, this, &cWaitDlg, iTitle)))
  403. {
  404. m_pTitleArray[iTitle].bAlreadyQueried = TRUE;
  405. if(hr == FTS_INVALID_SYNTAX || hr == FTS_CANCELED)
  406. {
  407. m_iLastResultCount = 0;
  408. return hr;
  409. }
  410. continue;
  411. }
  412. m_pTitleArray[iTitle].bHasResults = TRUE;
  413. m_pTitleArray[iTitle].bAlreadyQueried = TRUE;
  414. CombinedResultCount+=ComputeResultCount(pITRS);
  415. }
  416. }
  417. }
  418. }
  419. lcFree(pwsBuffer);
  420. // check for zero results
  421. //
  422. if(!CombinedResultCount)
  423. {
  424. *ppSearchResults = NULL;
  425. m_iLastResultCount = 0;
  426. return S_OK;
  427. }
  428. // compute the max size of the results structure
  429. //
  430. cb = ((CombinedResultCount) * sizeof(SEARCH_RESULT));
  431. // allocate the results structure
  432. //
  433. *ppSearchResults = pNextResult = (SEARCH_RESULT *) lcMalloc(cb);
  434. // Clear the structure
  435. //
  436. memset(pNextResult,0,cb);
  437. int cFilteredResultCount = 0;
  438. long lRowCount;
  439. // Query Phase #3 - collect the query results from each search object
  440. //
  441. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  442. {
  443. if(m_pTitleArray[iTitle].bHasResults)
  444. {
  445. if(m_pTitleArray[iTitle].bCombinedIndex)
  446. pITRS = m_pTitleArray[iTitle].pCombinedFTI->GetResultsSet();
  447. else if( m_pTitleArray[iTitle].pExTitle->m_pTitleFTS )
  448. pITRS = m_pTitleArray[iTitle].pExTitle->m_pTitleFTS->GetResultsSet();
  449. else
  450. continue;
  451. if( !pITRS )
  452. continue;
  453. // Get the results row count
  454. //
  455. pITRS->GetRowCount(lRowCount);
  456. // Make sure we have results
  457. //
  458. if(lRowCount)
  459. {
  460. int i,rank = 1;
  461. CProperty Prop;
  462. DWORD dwLastTopic = 0xffffffff;
  463. // walk through the results
  464. //
  465. for (i = 0; i < lRowCount; i++)
  466. {
  467. pITRS->Get(i, 0, Prop);
  468. if(Prop.dwValue != dwLastTopic)
  469. {
  470. if(m_pTitleArray[iTitle].bCombinedIndex)
  471. {
  472. pNextResult->dwTopicNumber = TOPIC_NUM(Prop.dwValue);
  473. pNextResult->pTitle = LookupTitle(m_pTitleArray[iTitle].pCombinedFTI,CHM_ID(Prop.dwValue));
  474. if(!pNextResult->pTitle)
  475. continue; // skip results from title that are out of date or not loaded
  476. if(!pSubSet || pSubSet->m_bIsEntireCollection ) // no subset specified, add the topic
  477. {
  478. pNextResult->dwRank = rank++;
  479. pNextResult++;
  480. ++cFilteredResultCount;
  481. dwLastTopic = Prop.dwValue;
  482. }
  483. else
  484. if(pNextResult->pTitle->InfoTypeFilter(pSubSet,pNextResult->dwTopicNumber))
  485. {
  486. pNextResult->dwRank = rank++;
  487. pNextResult++;
  488. ++cFilteredResultCount;
  489. dwLastTopic = Prop.dwValue;
  490. }
  491. }
  492. else
  493. {
  494. pNextResult->dwTopicNumber = Prop.dwValue;
  495. pNextResult->pTitle = m_pTitleArray[iTitle].pExTitle;
  496. if(!pSubSet || pSubSet->m_bIsEntireCollection )
  497. {
  498. pNextResult->dwRank = rank++;
  499. pNextResult++;
  500. ++cFilteredResultCount;
  501. dwLastTopic = Prop.dwValue;
  502. }
  503. else
  504. if(pNextResult->pTitle->InfoTypeFilter(pSubSet,pNextResult->dwTopicNumber))
  505. {
  506. pNextResult->dwRank = rank++;
  507. pNextResult++;
  508. ++cFilteredResultCount;
  509. dwLastTopic = Prop.dwValue;
  510. }
  511. }
  512. }
  513. }
  514. }
  515. }
  516. }
  517. // Save the result count for client
  518. //
  519. if(cFilteredResultCount > m_lMaxRowCount)
  520. *pcResultCount = m_lMaxRowCount;
  521. else
  522. *pcResultCount = cFilteredResultCount;
  523. m_iLastResultCount = *pcResultCount;
  524. #ifdef _DEBUG
  525. OutputDebugString("FTS: Full-Text Index Query Array\n\n");
  526. OutputDebugString("Entry Title Enabled CHQ Results\n");
  527. OutputDebugString("================================================\n");
  528. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  529. {
  530. char szTemp[500];
  531. wsprintf(szTemp,"%03d %16s %1d %1d %1d\n",iTitle, m_pTitleArray[iTitle].pszShortName,m_pTitleArray[iTitle].bSearch,m_pTitleArray[iTitle].bCombinedIndex,m_pTitleArray[iTitle].bHasResults);
  532. OutputDebugString(szTemp);
  533. }
  534. OutputDebugString("================================================\n\n");
  535. #endif
  536. // Below is some test code that dumps the results list to the debug window
  537. //
  538. // int x;
  539. // char szTemp[100];
  540. // for(x=0;x<cFilteredResultCount;++x)
  541. // {
  542. // wsprintf(szTemp,"Title=%0x Rank=%03d\n",(*ppSearchResults)[x].pTitle,(*ppSearchResults)[x].dwRank);
  543. // OutputDebugString(szTemp);
  544. // }
  545. QSort(*ppSearchResults,cFilteredResultCount,sizeof(SEARCH_RESULT),CompareIntValues);
  546. return S_OK;
  547. }
  548. int FASTCALL CompareIntValues(const void *pval1, const void *pval2)
  549. {
  550. return ((SEARCH_RESULT *)pval1)->dwRank - ((SEARCH_RESULT *)pval2)->dwRank;
  551. }
  552. long CFullTextSearch::ComputeResultCount(IITResultSet *pResultSet)
  553. {
  554. long count = 0, uniqueCount = 0;
  555. if(!pResultSet)
  556. return 0;
  557. if(FAILED(pResultSet->GetRowCount(count)))
  558. return 0;
  559. if(count)
  560. {
  561. int i;
  562. CProperty Prop;
  563. DWORD dwLastTopic = 0xffffffff;
  564. // walk through the results
  565. //
  566. for (i = 0; i < count; i++)
  567. {
  568. pResultSet->Get(i, 0, Prop);
  569. if(Prop.dwValue != dwLastTopic)
  570. {
  571. ++uniqueCount;
  572. dwLastTopic = Prop.dwValue;
  573. }
  574. }
  575. }
  576. return uniqueCount;
  577. }
  578. /////////////////////////////////////////////////////////////////////////////
  579. // CFullTextSearch lookup title
  580. //
  581. CExTitle *CFullTextSearch::LookupTitle(CCombinedFTS *pCombinedFTS, DWORD dwValue)
  582. {
  583. static CCombinedFTS *pCacheObject= NULL;
  584. static DWORD pCacheValue = NULL;
  585. static CExTitle *pPreviousResult;
  586. // see if we are looking up the same title again
  587. //
  588. if(pCacheObject == pCombinedFTS && dwValue == pCacheValue)
  589. return pPreviousResult;
  590. INT iTitle;
  591. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  592. {
  593. if(m_pTitleArray[iTitle].pCombinedFTI == pCombinedFTS &&
  594. m_pTitleArray[iTitle].iTitleIndex == dwValue && m_pTitleArray[iTitle].bCombinedIndex)
  595. {
  596. pCacheObject = pCombinedFTS;
  597. pCacheValue = dwValue;
  598. pPreviousResult = m_pTitleArray[iTitle].pExTitle;
  599. return m_pTitleArray[iTitle].pExTitle;
  600. }
  601. }
  602. return NULL;
  603. }
  604. /////////////////////////////////////////////////////////////////////////////
  605. // CFullTextSearch Initialize title array
  606. //
  607. void CFullTextSearch::InitTitleArray()
  608. {
  609. if(m_bTitleArrayInit)
  610. return;
  611. m_TitleArraySize = 0;
  612. // Iterate through the titles
  613. //
  614. CExTitle *pTitle = m_pTitleCollection->GetFirstTitle();
  615. // Count the entries in the title list
  616. //
  617. while(pTitle)
  618. {
  619. pTitle = pTitle->GetNext();
  620. ++m_TitleArraySize;
  621. }
  622. int cb = m_TitleArraySize * sizeof(TITLE_ENTRY);
  623. // allocate the results structure
  624. //
  625. m_pTitleArray = (TITLE_ENTRY *) lcMalloc(cb);
  626. if(!m_pTitleArray)
  627. return;
  628. // Clear the structure
  629. //
  630. memset(m_pTitleArray,0,cb);
  631. // Iterate through the titles and init members
  632. //
  633. pTitle = m_pTitleCollection->GetFirstTitle();
  634. INT iTitle;
  635. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  636. {
  637. if(pTitle == NULL)
  638. {
  639. lcFree(m_pTitleArray);
  640. m_pTitleArray = NULL;
  641. return;
  642. }
  643. m_pTitleArray[iTitle].pExTitle = pTitle;
  644. char *pszQueryName = (char *) pTitle->GetQueryName();
  645. if(pszQueryName && *pszQueryName)
  646. {
  647. m_pTitleArray[iTitle].pszQueryName = lcStrDup(pszQueryName);
  648. m_pTitleArray[iTitle].bCombinedIndex = TRUE;
  649. }
  650. else
  651. m_pTitleArray[iTitle].bCombinedIndex = FALSE;
  652. // Check if combined index for master chm exists
  653. // This feature was added for NT5
  654. //
  655. if(iTitle == 0 && m_pTitleCollection->IsSingleTitle())
  656. {
  657. char *pszFilePath = lcStrDup((char *) pTitle->GetIndexFileName());
  658. if(pszFilePath)
  659. {
  660. _strupr(pszFilePath);
  661. char *pszTemp = strstr(pszFilePath,".CHM");
  662. if(pszTemp)
  663. {
  664. pszTemp[3] = 'Q';
  665. // Check if combined index exists
  666. //
  667. if(IsFile(pszFilePath))
  668. {
  669. m_pTitleArray[iTitle].pszQueryName = lcStrDup(pszFilePath);
  670. m_pTitleArray[iTitle].bCombinedIndex = TRUE;
  671. m_bMergedChmSetWithCHQ = TRUE; // This disables CD swapping
  672. }
  673. }
  674. if(pszFilePath)
  675. lcFree(pszFilePath);
  676. }
  677. }
  678. if(!m_pTitleCollection->IsSingleTitle())
  679. m_bMergedChmSetWithCHQ = FALSE;
  680. // Get version and name info from the index file
  681. //
  682. m_pTitleArray[iTitle].pszIndexName = lcStrDup((char *) pTitle->GetIndexFileName());
  683. CTitleInformation2 Info2(m_pTitleArray[iTitle].pszIndexName);
  684. m_pTitleArray[iTitle].pszShortName = lcStrDup(Info2.GetShortName());
  685. m_pTitleArray[iTitle].versioninfo = Info2.GetFileTime();
  686. m_pTitleArray[iTitle].language = Info2.GetLanguage();
  687. // Mark title as having a internal FTI for now. If the title is not part of a combined index
  688. // I'll check for FTI before querying (this prevents opening all the titles during this
  689. // initialization (which saves about 80% on init time).
  690. //
  691. m_pTitleArray[iTitle].bSearch = TRUE;
  692. WORD wTitlePrimaryLang = PRIMARYLANGID(LANGIDFROMLCID(m_pTitleArray[iTitle].language));
  693. if(m_SystemLangID == LANG_JAPANESE || m_SystemLangID == LANG_KOREAN ||
  694. m_SystemLangID == LANG_CHINESE)
  695. {
  696. // Disable a DBCS title not running on a corresponding DBCS OS
  697. //
  698. if(m_SystemLangID != wTitlePrimaryLang && (wTitlePrimaryLang == LANG_JAPANESE
  699. || wTitlePrimaryLang == LANG_KOREAN || wTitlePrimaryLang == LANG_CHINESE))
  700. m_pTitleArray[iTitle].bSearch = FALSE;
  701. }
  702. // get the volume order
  703. pTitle->GetVolumeOrder( &(m_pTitleArray[iTitle].uiVolumeOrder),
  704. pszQueryName ? HHRMS_TYPE_COMBINED_QUERY : HHRMS_TYPE_TITLE );
  705. pTitle = pTitle->GetNext();
  706. }
  707. // sort the title list based on volume order
  708. if(!m_bMergedChmSetWithCHQ)
  709. QSort( m_pTitleArray, m_TitleArraySize, sizeof(TITLE_ENTRY), CompareVolumeOrder );
  710. m_bTitleArrayInit = TRUE;
  711. }
  712. /////////////////////////////////////////////////////////////////////////////
  713. // CFullTextSearch GetPreviousInstance
  714. //
  715. CCombinedFTS *CFullTextSearch::GetPreviousInstance(char *pszQueryName)
  716. {
  717. static char *pCacheName = NULL;
  718. static CCombinedFTS *pCacheObject = NULL;
  719. if(pCacheName)
  720. if(!strcmp(pszQueryName,pCacheName))
  721. return pCacheObject;
  722. INT iTitle;
  723. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  724. {
  725. if(!strcmp(m_pTitleArray[iTitle].pszQueryName,pszQueryName) && m_pTitleArray[iTitle].pCombinedFTI)
  726. {
  727. pCacheName = pszQueryName;
  728. pCacheObject = m_pTitleArray[iTitle].pCombinedFTI;
  729. return m_pTitleArray[iTitle].pCombinedFTI;
  730. }
  731. }
  732. return NULL;
  733. }
  734. /////////////////////////////////////////////////////////////////////////////
  735. // CFullTextSearch LoadCombinedIndex
  736. //
  737. BOOL CFullTextSearch::LoadCombinedIndex(DWORD iCurTitle)
  738. {
  739. HRESULT hr;
  740. CCombinedFTS *pCombinedFTS = NULL;
  741. CFileSystem* pDatabase = new CFileSystem;
  742. char *pszQueryName = m_pTitleArray[iCurTitle].pszQueryName;
  743. if( SUCCEEDED(hr = pDatabase->Init()) && SUCCEEDED(hr = pDatabase->Open(pszQueryName)))
  744. {
  745. CSubFileSystem* pTitleMap = new CSubFileSystem( pDatabase );
  746. if( SUCCEEDED(hr = pTitleMap->OpenSub( "$TitleMap" ) ) )
  747. {
  748. ULONG cbRead = 0;
  749. WORD wCount = 0;
  750. pTitleMap->ReadSub( (void*) &wCount, sizeof(wCount), &cbRead );
  751. for( int iCount = 0; iCount < (int) wCount; iCount++ )
  752. {
  753. CHM_MAP_ENTRY mapEntry;
  754. pTitleMap->ReadSub( (void*) &mapEntry, sizeof(mapEntry), &cbRead );
  755. if(cbRead != sizeof(mapEntry))
  756. return FALSE;
  757. INT iTitle;
  758. // walk through the title array and set title index for matching titles
  759. //
  760. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  761. {
  762. // if a combined index wasn't specified for the title, skip it
  763. //
  764. if(!m_pTitleArray[iTitle].pszQueryName && !m_bMergedChmSetWithCHQ)
  765. continue;
  766. FILETIME titleTime = m_pTitleArray[iTitle].versioninfo;
  767. char *pszShortName = m_pTitleArray[iTitle].pszShortName;
  768. if(!strcmpi(mapEntry.szChmName,pszShortName) && !m_pTitleArray[iTitle].pCombinedFTI &&
  769. (!strcmpi(pszQueryName,m_pTitleArray[iTitle].pszQueryName) || m_bMergedChmSetWithCHQ) &&
  770. mapEntry.language == m_pTitleArray[iTitle].language &&
  771. (mapEntry.versioninfo.dwLowDateTime == titleTime.dwLowDateTime &&
  772. mapEntry.versioninfo.dwHighDateTime == titleTime.dwHighDateTime ))
  773. {
  774. if(!pCombinedFTS)
  775. {
  776. pCombinedFTS = m_pTitleArray[iTitle].pCombinedFTI =
  777. new CCombinedFTS(m_pTitleArray[iTitle].pExTitle,
  778. m_pTitleArray[iTitle].pExTitle->GetInfo2()->GetLanguage(), this);
  779. // This is the master title for this combined index
  780. //
  781. m_pTitleArray[iTitle].bSearch = TRUE;
  782. }
  783. else
  784. {
  785. m_pTitleArray[iTitle].pCombinedFTI = pCombinedFTS;
  786. m_pTitleArray[iTitle].bSearch = TRUE;
  787. }
  788. m_pTitleArray[iTitle].bCombinedIndex = TRUE;
  789. m_pTitleArray[iTitle].versioninfo = mapEntry.versioninfo;
  790. m_pTitleArray[iTitle].iTitleIndex = mapEntry.iIndex;
  791. m_pTitleArray[iTitle].dwTopicCount = mapEntry.dwTopicCount;
  792. }
  793. }
  794. }
  795. }
  796. delete pTitleMap;
  797. }
  798. delete pDatabase;
  799. // special case where the master chm is out of date in respects to combined index (NT5)
  800. //
  801. if(m_bMergedChmSetWithCHQ && m_pTitleArray[0].bCombinedIndex && !m_pTitleArray[0].pCombinedFTI)
  802. m_pTitleArray[0].bCombinedIndex = 0;
  803. #ifdef _DEBUG
  804. /*
  805. INT iTitle;
  806. OutputDebugString("Full-Text Search Query Map Array:\n");
  807. for(iTitle = 0; iTitle < m_TitleArraySize; ++iTitle)
  808. {
  809. char szTemp[500];
  810. wsprintf(szTemp," %d bSearch=%d bCombinedIndex=%d iIndex=%02d pExTitle=%d pCombinedFTI=%d\n",iTitle,
  811. m_pTitleArray[iTitle].bSearch,m_pTitleArray[iTitle].bCombinedIndex,m_pTitleArray[iTitle].iTitleIndex,
  812. m_pTitleArray[iTitle].pExTitle,m_pTitleArray[iTitle].pCombinedFTI);
  813. OutputDebugString(szTemp);
  814. }
  815. */
  816. #endif
  817. // Check to see if the title that initiated the loading of this combined index
  818. // was validated for this index.
  819. if(m_pTitleArray[iCurTitle].pCombinedFTI)
  820. return TRUE;
  821. else
  822. return FALSE;
  823. }
  824. /////////////////////////////////////////////////////////////////////////////
  825. // CFullTextSearch abort query function
  826. //
  827. HRESULT CFullTextSearch::AbortQuery()
  828. {
  829. if(!m_bInit)
  830. return E_FAIL;
  831. CExTitle *pTitle = m_pTitleCollection->GetFirstTitle();
  832. while(pTitle)
  833. {
  834. // BUGBUG: If this is the first title in a collection, it will
  835. // reread the system data.
  836. CTitleInformation *pTitleInfo = pTitle->GetInfo();
  837. if(pTitleInfo && pTitleInfo->IsFullTextSearch())
  838. {
  839. pTitle->m_pTitleFTS->AbortQuery();
  840. }
  841. pTitle = pTitle->GetNext();
  842. }
  843. return S_OK;
  844. }
  845. /////////////////////////////////////////////////////////////////////////////
  846. // CFullTextSearch set query options
  847. //
  848. // dwFlag can be one or more of the following:
  849. //
  850. // IMPLICIT_AND
  851. // Search terms are AND'd if no operator is specified
  852. //
  853. // IMPLICIT_OR
  854. // Search terms are OR'd if no operator is specified
  855. //
  856. // COMPOUNDWORD_PHRASE
  857. // Use PHRASE operator for compound words
  858. //
  859. // QUERYRESULT_RANK
  860. // Results are returned in ranked order
  861. //
  862. // QUERYRESULT_UIDSORT
  863. // Results are returned in UID order
  864. //
  865. // QUERYRESULT_SKIPOCCINFO
  866. // Only topic-level hit information is returned
  867. //
  868. // STEMMED_SEARCH
  869. // The search returns stemmed results
  870. //
  871. HRESULT CFullTextSearch::SetOptions(DWORD dwFlag)
  872. {
  873. if(!m_bInit)
  874. return E_FAIL;
  875. m_dwQueryFlags = dwFlag;
  876. CExTitle *pTitle = m_pTitleCollection->GetFirstTitle();
  877. while(pTitle)
  878. {
  879. // BUGBUG: If this is the first title in a collection, it will
  880. // reread the system data.
  881. CTitleInformation *pTitleInfo = pTitle->GetInfo();
  882. if(pTitleInfo && pTitleInfo->IsFullTextSearch())
  883. {
  884. pTitle->m_pTitleFTS->SetOptions(dwFlag);
  885. }
  886. pTitle = pTitle->GetNext();
  887. }
  888. return S_OK;
  889. }
  890. /////////////////////////////////////////////////////////////////////////////
  891. // CFullTextSearch set proximity
  892. //
  893. HRESULT CFullTextSearch::SetProximity(WORD wNear)
  894. {
  895. if(!m_bInit)
  896. return E_FAIL;
  897. CExTitle *pTitle = m_pTitleCollection->GetFirstTitle();
  898. while(pTitle)
  899. {
  900. // BUGBUG: If this is the first title in a collection, it will
  901. // reread the system data.
  902. CTitleInformation *pTitleInfo = pTitle->GetInfo();
  903. if(pTitleInfo && pTitleInfo->IsFullTextSearch())
  904. {
  905. pTitle->m_pTitleFTS->SetProximity(wNear);
  906. }
  907. pTitle = pTitle->GetNext();
  908. }
  909. return S_OK;
  910. }
  911. /////////////////////////////////////////////////////////////////////////////
  912. // CFullTextSearch set result count
  913. // This sets max number of rows to be returned by query
  914. //
  915. HRESULT CFullTextSearch::SetResultCount(LONG cRows)
  916. {
  917. if(!m_bInit)
  918. return E_FAIL;
  919. CExTitle *pTitle = m_pTitleCollection->GetFirstTitle();
  920. while(pTitle)
  921. {
  922. // BUGBUG: If this is the first title in a collection, it will
  923. // reread the system data.
  924. CTitleInformation *pTitleInfo = pTitle->GetInfo();
  925. if(pTitleInfo && pTitleInfo->IsFullTextSearch())
  926. {
  927. pTitle->m_pTitleFTS->SetResultCount(cRows);
  928. }
  929. pTitle = pTitle->GetNext();
  930. }
  931. return S_OK;
  932. }
  933. /////////////////////////////////////////////////////////////////////////////
  934. // CFullTextSearch free results data structure
  935. //
  936. VOID CFullTextSearch::FreeResults(SEARCH_RESULT *pResults)
  937. {
  938. if(!m_bInit)
  939. return;
  940. if(pResults)
  941. lcFree(pResults);
  942. }
  943. /*
  944. /////////////////////////////////////////////////////////////////////////////
  945. // CFullTextSearch AddHLTerm
  946. // Add a term to the highlight list
  947. //
  948. HRESULT CFullTextSearch::AddHLTerm(WCHAR *pwcTerm)
  949. {
  950. if(!pwcTerm || !*pwcTerm)
  951. return E_FAIL;
  952. if(m_iHLIndex < MAX_HIGHLIGHT_TERMS)
  953. return E_FAIL;
  954. int len = (wcslen(pwcTerm) + 1) * sizeof(WCHAR);
  955. m_HLTermArray[m_iHLIndex] = (WCHAR *) lcMalloc(len);
  956. wcscpy(m_HLTermArray[m_iHLIndex],pwcTerm);
  957. m_iHLIndex++;
  958. return S_OK;
  959. }
  960. */
  961. /////////////////////////////////////////////////////////////////////////////
  962. // CFullTextSearch AddHLTerm
  963. // Add a term to the highlight list
  964. //
  965. HRESULT CFullTextSearch::AddHLTerm(WCHAR *pwcTerm, int cTermLength)
  966. {
  967. if(!pwcTerm || !*pwcTerm || !cTermLength)
  968. return E_FAIL;
  969. if(*pwcTerm == L')' || *pwcTerm == L'(' || *pwcTerm == L'\"')
  970. return S_OK;
  971. if(m_iHLIndex >= MAX_HIGHLIGHT_TERMS)
  972. return E_FAIL;
  973. if(!wcsnicmp(pwcTerm,L"HHTitleID",9))
  974. return S_OK;
  975. int i;
  976. // do not accept duplicates
  977. //
  978. for(i=0;i<m_iHLIndex;++i)
  979. {
  980. int cLen = wcslen(m_HLTermArray[i]);
  981. if(cTermLength == cLen && !wcsnicmp(pwcTerm, m_HLTermArray[i], cLen))
  982. return S_OK;
  983. }
  984. int len = (cTermLength + 1) * sizeof(WCHAR);
  985. m_HLTermArray[m_iHLIndex] = (WCHAR *) lcMalloc(len);
  986. CopyMemory(m_HLTermArray[m_iHLIndex],pwcTerm,len-sizeof(WCHAR));
  987. *(m_HLTermArray[m_iHLIndex]+cTermLength) = NULL;
  988. m_iHLIndex++;
  989. return S_OK;
  990. }
  991. /////////////////////////////////////////////////////////////////////////////
  992. // CFullTextSearch AddHLTerm
  993. // Add a term to the highlight list
  994. //
  995. HRESULT CFullTextSearch::AddQueryToTermList(WCHAR *pwsBuffer)
  996. {
  997. WCHAR *pwsStart, *pwsTemp = pwsBuffer;
  998. if(!pwsBuffer || !*pwsBuffer)
  999. return E_FAIL;
  1000. while(*pwsTemp)
  1001. {
  1002. // Remove white space
  1003. //
  1004. while(*pwsTemp && (*pwsTemp == L' ' || *pwsTemp == L'(' || *pwsTemp == L')'))
  1005. ++pwsTemp;
  1006. if(!*pwsTemp)
  1007. continue;
  1008. pwsStart = pwsTemp;
  1009. if(*pwsTemp == L'"')
  1010. {
  1011. pwsStart++;
  1012. pwsTemp++;
  1013. while(*pwsTemp && *pwsTemp != L'"')
  1014. ++pwsTemp;
  1015. }
  1016. else
  1017. {
  1018. while(*pwsTemp && !(*pwsTemp == L' ' || *pwsTemp == L'(' || *pwsTemp == L')'))
  1019. ++pwsTemp;
  1020. }
  1021. if(!*pwsTemp)
  1022. continue;
  1023. if(pwsStart != pwsTemp)
  1024. {
  1025. if(wcsnicmp(pwsStart,L"and",(int)(pwsTemp-pwsStart)) && wcsnicmp(pwsStart,L"or",(int)(pwsTemp-pwsStart))
  1026. && wcsnicmp(pwsStart,L"near",(int)(pwsTemp-pwsStart)) && wcsnicmp(pwsStart,L"not",(int)(pwsTemp-pwsStart))
  1027. && wcsnicmp(pwsStart,L"VFLD 0",(int)(pwsTemp-pwsStart)) && wcsnicmp(pwsStart,L"VFLD 1",(int)(pwsTemp-pwsStart))
  1028. && wcsnicmp(pwsStart,L"HHTitleID",9))
  1029. AddHLTerm(pwsStart,(int)(pwsTemp-pwsStart));
  1030. }
  1031. if(!wcsnicmp(pwsStart,L"HHTitleID",(int)(pwsTemp-pwsStart)))
  1032. {
  1033. while(*pwsTemp && !(*pwsTemp == L' ' || *pwsTemp == L'(' || *pwsTemp == L')'))
  1034. ++pwsTemp;
  1035. }
  1036. if(!wcsnicmp(pwsStart,L"VFLD 0",(int)(pwsTemp-pwsStart)) && !wcsnicmp(pwsStart,L"VFLD 1",(int)(pwsTemp-pwsStart)))
  1037. pwsTemp+=2;
  1038. if(*pwsTemp == L'"')
  1039. ++pwsTemp;
  1040. }
  1041. return S_OK;
  1042. }
  1043. /////////////////////////////////////////////////////////////////////////////
  1044. // CFullTextSearch TermListRemoveAll
  1045. // Delete all entries in the highlight term list
  1046. //
  1047. HRESULT CFullTextSearch::TermListRemoveAll(void)
  1048. {
  1049. int i;
  1050. for(i=0;i<m_iHLIndex;++i)
  1051. {
  1052. if(m_HLTermArray[i])
  1053. {
  1054. lcFree(m_HLTermArray[i]);
  1055. m_HLTermArray[i] = NULL;
  1056. }
  1057. }
  1058. m_iHLIndex = 0;
  1059. return S_OK;
  1060. }
  1061. /////////////////////////////////////////////////////////////////////////////
  1062. // CFullTextSearch GetHLTerm
  1063. // Retrieve a term from the highlight term list
  1064. //
  1065. WCHAR * CFullTextSearch::GetHLTermAt(int index)
  1066. {
  1067. if(index >= m_iHLIndex)
  1068. return NULL;
  1069. return m_HLTermArray[index];
  1070. }
  1071. /////////////////////////////////////////////////////////////////////////////
  1072. // CFullTextSearch GetHLTermCount
  1073. // Get the count of entries in the highlight term list
  1074. //
  1075. INT CFullTextSearch::GetHLTermCount(void)
  1076. {
  1077. return m_iHLIndex;
  1078. }
  1079. /////////////////////////////////////////////////////////////////////////////
  1080. // CTitleFTS Class
  1081. //
  1082. // This class provides full-text search functionality for a single open
  1083. // title. In a multi-title environment, there will be an instance of this
  1084. // class for each open title.
  1085. //
  1086. // This class is intended only for use by the CFullTextSearch class which
  1087. // provides full-text search services for both single and multi-title
  1088. // configurations.
  1089. /////////////////////////////////////////////////////////////////////////////
  1090. // CTitleFTS class constructor
  1091. //
  1092. CTitleFTS::CTitleFTS( PCSTR pszTitlePath, LCID lcid, CExTitle *pTitle)
  1093. {
  1094. MultiByteToWideChar(CP_ACP, 0, pszTitlePath, -1, m_tcTitlePath, sizeof(m_tcTitlePath) );
  1095. m_lcid = lcid;
  1096. m_langid = LANGIDFROMLCID(lcid);
  1097. m_codepage = CodePageFromLCID(lcid);
  1098. m_bInit = FALSE;
  1099. m_SearchActive = FALSE;
  1100. m_pIndex = NULL;
  1101. m_pQuery = NULL;
  1102. m_pITResultSet = NULL;
  1103. m_pITDB = NULL;
  1104. m_InitFailed = FALSE;
  1105. m_InitError = E_FAIL;
  1106. m_pTitle = pTitle;
  1107. m_pPrevQuery = NULL;
  1108. m_SystemLangID = PRIMARYLANGID(GetSystemDefaultLangID());
  1109. m_fDBCS = FALSE;
  1110. m_lMaxRowCount = 500;
  1111. m_wQueryProximity = 8;
  1112. m_iLastResultCount = 0;
  1113. }
  1114. /////////////////////////////////////////////////////////////////////////////
  1115. // CTitleFTS class destructor
  1116. //
  1117. CTitleFTS::~CTitleFTS()
  1118. {
  1119. ReleaseObjects();
  1120. if(m_pPrevQuery)
  1121. lcFree(m_pPrevQuery);
  1122. }
  1123. /////////////////////////////////////////////////////////////////////////////
  1124. // CTitleFTS release objects
  1125. //
  1126. void CTitleFTS::ReleaseObjects()
  1127. {
  1128. if(!m_bInit)
  1129. return;
  1130. if (m_pITResultSet)
  1131. {
  1132. m_pITResultSet->Clear();
  1133. m_pITResultSet->Release();
  1134. }
  1135. if(m_pQuery)
  1136. m_pQuery->Release();
  1137. if(m_pIndex)
  1138. {
  1139. m_pIndex->Close();
  1140. m_pIndex->Release();
  1141. }
  1142. if(m_pITDB)
  1143. {
  1144. m_pITDB->Close();
  1145. m_pITDB->Release();
  1146. }
  1147. m_pITResultSet = NULL;
  1148. m_pQuery = NULL;
  1149. m_pIndex = NULL;
  1150. m_pITDB = NULL;
  1151. }
  1152. /////////////////////////////////////////////////////////////////////////////
  1153. // CTitleFTS class initialization
  1154. //
  1155. HRESULT CTitleFTS::Initialize()
  1156. {
  1157. if(m_InitFailed)
  1158. return E_FAIL;
  1159. if(m_bInit)
  1160. return S_OK;
  1161. m_InitFailed = TRUE;
  1162. WORD PrimaryLang = PRIMARYLANGID(m_langid);
  1163. if(PrimaryLang == LANG_JAPANESE || PrimaryLang == LANG_CHINESE || PrimaryLang == LANG_KOREAN)
  1164. m_fDBCS = TRUE;
  1165. else
  1166. m_fDBCS = FALSE;
  1167. if(m_lcid == 1033)
  1168. m_dwQueryFlags = IMPLICIT_AND | QUERY_GETTERMS | STEMMED_SEARCH; // QUERYRESULT_RANK
  1169. else
  1170. m_dwQueryFlags = IMPLICIT_AND | QUERY_GETTERMS;
  1171. // char szLangID[20];
  1172. //GetLocaleInfo(m_lcid,LOCALE_ILANGUAGE,szLangID,sizeof(szLangID));
  1173. // Make sure we have a path
  1174. //
  1175. if(!*m_tcTitlePath)
  1176. {
  1177. m_InitError = FTS_NOT_INITIALIZED;
  1178. return E_FAIL;
  1179. }
  1180. // DOUGO - insert code here to initialize language information
  1181. // Get IITIndex pointer
  1182. //
  1183. HRESULT hr = CoCreateInstance(CLSID_IITIndexLocal, NULL, CLSCTX_INPROC_SERVER,
  1184. IID_IITIndex, (VOID**)&m_pIndex);
  1185. if (FAILED(hr))
  1186. {
  1187. m_InitError = FTS_NOT_INITIALIZED;
  1188. return E_FAIL;
  1189. }
  1190. // Get IITDatabase pointer
  1191. //
  1192. hr = CoCreateInstance(CLSID_IITDatabaseLocal, NULL, CLSCTX_INPROC_SERVER,
  1193. IID_IITDatabase, (VOID**)&m_pITDB);
  1194. if (FAILED(hr))
  1195. {
  1196. m_InitError = FTS_NOT_INITIALIZED;
  1197. return E_FAIL;
  1198. }
  1199. // Open the storage system
  1200. //
  1201. hr = m_pITDB->Open(NULL, m_tcTitlePath, NULL);
  1202. if (FAILED(hr))
  1203. {
  1204. m_InitError = FTS_NO_INDEX;
  1205. return E_FAIL;
  1206. }
  1207. // open the index.
  1208. //
  1209. hr = m_pIndex->Open(m_pITDB, txtwFtiMain, TRUE);
  1210. if (FAILED(hr))
  1211. {
  1212. m_InitError = FTS_NO_INDEX;
  1213. return E_FAIL;
  1214. }
  1215. // Create query instance
  1216. //
  1217. hr = m_pIndex->CreateQueryInstance(&m_pQuery);
  1218. if (FAILED(hr))
  1219. {
  1220. m_InitError = FTS_NOT_INITIALIZED;
  1221. return E_FAIL;
  1222. }
  1223. // set search options
  1224. //
  1225. hr = m_pQuery->SetOptions(m_dwQueryFlags);
  1226. if (FAILED(hr))
  1227. {
  1228. m_InitError = FTS_NOT_INITIALIZED;
  1229. return E_FAIL;
  1230. }
  1231. // Create Result Set object
  1232. //
  1233. hr = CoCreateInstance(CLSID_IITResultSet, NULL, CLSCTX_INPROC_SERVER,
  1234. IID_IITResultSet, (VOID**) &m_pITResultSet);
  1235. if (FAILED(hr))
  1236. {
  1237. m_InitError = FTS_NOT_INITIALIZED;
  1238. return E_FAIL;
  1239. }
  1240. m_bInit = TRUE;
  1241. m_InitFailed = FALSE;
  1242. return S_OK;
  1243. }
  1244. /////////////////////////////////////////////////////////////////////////////
  1245. // CTitleFTS query function
  1246. //
  1247. HRESULT CTitleFTS::Query(WCHAR *pwcQuery, DWORD dwFlags, IITResultSet **ppITRS, CFullTextSearch *pFullTextSearch, CUWait *pWaitDlg, int iTitle)
  1248. {
  1249. HRESULT hr;
  1250. WCHAR *pNewQuery = NULL;
  1251. if(!Init())
  1252. return m_InitError;
  1253. // Reinit the object if the dirty bit is set
  1254. //
  1255. if(m_pTitle->m_pCollection->m_Collection.IsDirty())
  1256. {
  1257. ReleaseObjects();
  1258. MultiByteToWideChar(CP_ACP, 0, m_pTitle->GetContentFileName(), -1, m_tcTitlePath, sizeof(m_tcTitlePath) );
  1259. m_InitFailed = FALSE;
  1260. m_bInit = FALSE;
  1261. m_InitError = E_FAIL;
  1262. if(!Init())
  1263. return m_InitError;
  1264. }
  1265. WCHAR *pszQuery = pwcQuery;
  1266. pszQuery = PreProcessQuery(pwcQuery, m_codepage);
  1267. if(!pszQuery)
  1268. return E_FAIL;
  1269. *ppITRS = NULL;
  1270. // return if search previous set, but no previous query
  1271. //
  1272. if((dwFlags & FTS_SEARCH_PREVIOUS) && !m_pPrevQuery)
  1273. return S_OK;
  1274. // If this title resulted in no hits last query, but the global result count was non-zero, then
  1275. // return no hits. The only time we want to query this title using the last known good query is
  1276. // when the global query count (combined results) was also zero. In that case, we want to
  1277. // revert all titles and combined indexes back to the last known good query. Otherwise, we skip
  1278. // this title because the last query (which resulted in some results globally) is still valid.
  1279. //
  1280. if((dwFlags & FTS_SEARCH_PREVIOUS) && pFullTextSearch->m_iLastResultCount && !m_iLastResultCount)
  1281. {
  1282. lcFree(m_pPrevQuery);
  1283. m_pPrevQuery = NULL;
  1284. return S_OK;
  1285. }
  1286. WCHAR *pPrevQuerySaved = NULL;
  1287. if(m_pPrevQuery)
  1288. {
  1289. pPrevQuerySaved = (WCHAR *) lcMalloc((wcslen(m_pPrevQuery)+ 2) * sizeof(WCHAR));
  1290. wcscpy(pPrevQuerySaved,m_pPrevQuery);
  1291. }
  1292. CStructuralSubset *pSubset;
  1293. // Check if this title is part of the current subset
  1294. //
  1295. if(m_pTitle->m_pCollection && m_pTitle->m_pCollection->m_pSSList)
  1296. if( (pSubset = m_pTitle->m_pCollection->m_pSSList->GetFTS()) && !pSubset->IsEntire() )
  1297. if(!pSubset->IsTitleInSubset(m_pTitle) && !(dwFlags & FTS_SEARCH_PREVIOUS) )
  1298. {
  1299. // clear the previous results
  1300. //
  1301. hr = m_pITResultSet->Clear();
  1302. m_iLastResultCount = 0;
  1303. if(m_pPrevQuery)
  1304. lcFree(m_pPrevQuery);
  1305. m_pPrevQuery = NULL;
  1306. if(pPrevQuerySaved)
  1307. lcFree(pPrevQuerySaved);
  1308. return S_OK;
  1309. }
  1310. if(dwFlags & FTS_ENABLE_STEMMING)
  1311. m_dwQueryFlags |= STEMMED_SEARCH;
  1312. else
  1313. m_dwQueryFlags &= (~STEMMED_SEARCH);
  1314. // Set previous options
  1315. //
  1316. m_pQuery->ReInit();
  1317. m_pQuery->SetOptions(m_dwQueryFlags);
  1318. m_pQuery->SetResultCount(m_lMaxRowCount);
  1319. m_pQuery->SetProximity(m_wQueryProximity);
  1320. FCALLBACK_MSG fCallBackMsg;
  1321. fCallBackMsg.MessageFunc = SearchMessageFunc;
  1322. fCallBackMsg.pUserData = (PVOID)pWaitDlg;
  1323. fCallBackMsg.dwFlags = 0; // not recommended for use
  1324. m_pQuery->SetResultCallback(&fCallBackMsg);
  1325. // Search Previous
  1326. //
  1327. if(dwFlags & FTS_SEARCH_PREVIOUS)
  1328. {
  1329. // append new query onto old query for "search previous"
  1330. //
  1331. int cQuery = wcslen(pszQuery);
  1332. int cPrevQuery = wcslen(m_pPrevQuery);
  1333. pNewQuery = (WCHAR *) lcMalloc((cQuery+cPrevQuery+20) * sizeof(WCHAR));
  1334. if(!pNewQuery)
  1335. return E_FAIL;
  1336. *pNewQuery = 0;
  1337. wcscat(pNewQuery,m_pPrevQuery);
  1338. wcscat(pNewQuery,L" and ");
  1339. wcscat(pNewQuery,pszQuery);
  1340. wcscat(pNewQuery,L" ");
  1341. }
  1342. // free the previous prev query
  1343. //
  1344. if(m_pPrevQuery)
  1345. {
  1346. lcFree(m_pPrevQuery);
  1347. m_pPrevQuery = NULL;
  1348. }
  1349. // Save the new query for next time
  1350. //
  1351. if(pNewQuery)
  1352. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pNewQuery)+ 2) * sizeof(WCHAR));
  1353. else
  1354. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pszQuery)+ 2) * sizeof(WCHAR));
  1355. if(pNewQuery)
  1356. wcscpy(m_pPrevQuery,pNewQuery);
  1357. else
  1358. wcscpy(m_pPrevQuery,pszQuery);
  1359. // clear the previous results
  1360. //
  1361. hr = m_pITResultSet->Clear();
  1362. if (FAILED(hr))
  1363. {
  1364. // Error clearing results set
  1365. if(pPrevQuerySaved)
  1366. lcFree(pPrevQuerySaved);
  1367. return E_FAIL;
  1368. }
  1369. // we want topic numbers back
  1370. //
  1371. hr = m_pITResultSet->Add(STDPROP_UID, (DWORD) 0, PRIORITY_NORMAL);
  1372. if (FAILED(hr))
  1373. {
  1374. // Error adding result property
  1375. m_InitError = FTS_NOT_INITIALIZED;
  1376. if(pPrevQuerySaved)
  1377. lcFree(pPrevQuerySaved);
  1378. return E_FAIL;
  1379. }
  1380. hr = m_pITResultSet->Add(STDPROP_TERM_UNICODE_ST, (DWORD)NULL, PRIORITY_NORMAL);
  1381. if (FAILED(hr))
  1382. {
  1383. // Error adding result property
  1384. m_InitError = FTS_NOT_INITIALIZED;
  1385. if(pPrevQuerySaved)
  1386. lcFree(pPrevQuerySaved);
  1387. return E_FAIL;
  1388. }
  1389. hr = m_pITResultSet->Add(STDPROP_COUNT, (DWORD)NULL, PRIORITY_NORMAL);
  1390. if (FAILED(hr))
  1391. {
  1392. // Error adding result property
  1393. m_InitError = FTS_NOT_INITIALIZED;
  1394. if(pPrevQuerySaved)
  1395. lcFree(pPrevQuerySaved);
  1396. return E_FAIL;
  1397. }
  1398. // Set the query
  1399. //
  1400. if(pNewQuery)
  1401. {
  1402. pFullTextSearch->AddQueryToTermList(pNewQuery);
  1403. hr = m_pQuery->SetCommand(pNewQuery);
  1404. // MessageBoxW(NULL,pNewQuery,L"Query",MB_OK);
  1405. }
  1406. else
  1407. {
  1408. pFullTextSearch->AddQueryToTermList(pszQuery);
  1409. hr = m_pQuery->SetCommand(pszQuery);
  1410. // MessageBoxW(NULL,pwcQuery,L"Query",MB_OK);
  1411. }
  1412. if (FAILED(hr))
  1413. {
  1414. // Error setting query
  1415. if(pPrevQuerySaved)
  1416. lcFree(pPrevQuerySaved);
  1417. return E_FAIL;
  1418. }
  1419. if(pNewQuery)
  1420. lcFree(pNewQuery);
  1421. // Execute the query
  1422. //
  1423. hr = m_pIndex->Search(m_pQuery, m_pITResultSet);
  1424. // if we receive a no stemmer error, re-query with stemming turned off
  1425. //
  1426. if(hr == E_NOSTEMMER)
  1427. {
  1428. m_dwQueryFlags &= (~STEMMED_SEARCH);
  1429. m_pQuery->SetOptions(m_dwQueryFlags);
  1430. m_pQuery->SetCommand(pszQuery);
  1431. hr = m_pIndex->Search(m_pQuery, m_pITResultSet);
  1432. }
  1433. long lRowCount;
  1434. m_pITResultSet->GetRowCount(lRowCount);
  1435. m_iLastResultCount = lRowCount;
  1436. // If query failed, then restore the previous query (for next search previous)
  1437. //
  1438. if((FAILED(hr) || !lRowCount) && pPrevQuerySaved)
  1439. {
  1440. if(m_pPrevQuery)
  1441. lcFree(m_pPrevQuery);
  1442. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pPrevQuerySaved)+ 2) * sizeof(WCHAR));
  1443. wcscpy(m_pPrevQuery,pPrevQuerySaved);
  1444. }
  1445. if(pPrevQuerySaved)
  1446. lcFree(pPrevQuerySaved);
  1447. if (hr ==E_NULLQUERY || hr == E_MISSQUOTE || hr == E_EXPECTEDTERM || hr == E_MISSLPAREN
  1448. || hr == E_MISSRPAREN || hr == E_ALL_WILD)
  1449. {
  1450. // Error invalid syntax
  1451. //
  1452. return FTS_INVALID_SYNTAX;
  1453. }
  1454. if(hr == FTS_CANCELED)
  1455. return FTS_CANCELED;
  1456. // Generic error
  1457. //
  1458. if (FAILED(hr))
  1459. return E_FAIL;
  1460. // Add search terms to term list
  1461. //
  1462. // Make sure we have results
  1463. //
  1464. if(lRowCount)
  1465. {
  1466. WCHAR wcBuffer[2048];
  1467. int i;
  1468. CProperty Prop, Prop2, Prop3;
  1469. DWORD dwLastTopic = 0xffffffff, dwNextWordCount;
  1470. WCHAR *pwcTemp = wcBuffer;
  1471. *pwcTemp = 0;
  1472. // prime the topic number
  1473. //
  1474. m_pITResultSet->Get(0,0, Prop3);
  1475. dwLastTopic = Prop3.dwValue;
  1476. // walk through the results
  1477. //
  1478. for (i = 0; i < lRowCount; i++)
  1479. {
  1480. m_pITResultSet->Get(i,0, Prop3);
  1481. if(i<lRowCount)
  1482. {
  1483. m_pITResultSet->Get(i+1,2, Prop2);
  1484. dwNextWordCount = Prop2.dwValue;
  1485. }
  1486. else
  1487. dwNextWordCount = 0;
  1488. m_pITResultSet->Get(i,1, Prop);
  1489. m_pITResultSet->Get(i,2, Prop2);
  1490. if(!Prop.dwValue)
  1491. continue;
  1492. WORD wFlags[2];
  1493. wFlags[0]=0;
  1494. // Convert term from Unicode to ANSI because GetStringTypeW is not supported on Win95.
  1495. //
  1496. WORD dwStrLen = (WORD)(wcslen(&Prop.lpszwData[1]) * sizeof(WCHAR));
  1497. if(dwStrLen)
  1498. {
  1499. char *pAnsiString = (char *) lcMalloc(dwStrLen);
  1500. DWORD dwTermLen = 1;
  1501. //UNICODE WORK: will have to use title cp when doing this conversion to ANSI (Damn Win98 and no Unicode support!!).
  1502. WideCharToMultiByte(m_codepage, 0, Prop.lpszwData + 1, -1, pAnsiString, dwStrLen, NULL, NULL);
  1503. if(IsDBCSLeadByteEx(m_codepage, *pAnsiString))
  1504. dwTermLen=2;
  1505. GetStringTypeA(m_lcid, CT_CTYPE3, pAnsiString, dwTermLen, wFlags);
  1506. lcFree(pAnsiString);
  1507. }
  1508. // skip DB chars (DB words were already added)
  1509. //
  1510. if(wFlags[0] & C3_FULLWIDTH || wFlags[0] & C3_KATAKANA || wFlags[0] & C3_HIRAGANA)
  1511. {
  1512. if(*pwcTemp)
  1513. {
  1514. pFullTextSearch->AddHLTerm(wcBuffer,wcslen(wcBuffer));
  1515. pwcTemp = wcBuffer;
  1516. *pwcTemp = 0;
  1517. }
  1518. dwLastTopic = Prop3.dwValue;
  1519. continue;
  1520. }
  1521. CopyMemory(pwcTemp, Prop.lpszwData + 1, (*((WORD *)Prop.lpszwData) * sizeof(WCHAR)));
  1522. pwcTemp+=*((WORD *)Prop.lpszwData);
  1523. *pwcTemp = 0;
  1524. if(dwNextWordCount != (Prop2.dwValue+1) || (wcslen(wcBuffer) > 500))
  1525. {
  1526. pFullTextSearch->AddHLTerm(wcBuffer,wcslen(wcBuffer));
  1527. pwcTemp = wcBuffer;
  1528. *pwcTemp = 0;
  1529. }
  1530. else
  1531. {
  1532. *pwcTemp++ = L' ';
  1533. *pwcTemp = 0;
  1534. }
  1535. dwLastTopic = Prop3.dwValue;
  1536. }
  1537. }
  1538. // Send the results set back
  1539. //
  1540. *ppITRS = m_pITResultSet;
  1541. return S_OK;
  1542. }
  1543. /////////////////////////////////////////////////////////////////////////////
  1544. // CTitleFTS abort current query
  1545. //
  1546. HRESULT CTitleFTS::AbortQuery()
  1547. {
  1548. if(!Init())
  1549. return E_FAIL;
  1550. // DOUGO - insert abort code here when Centaur support is available
  1551. return S_OK;
  1552. }
  1553. /////////////////////////////////////////////////////////////////////////////
  1554. // CTitleFTS update FTS options without calling into Centaur
  1555. //
  1556. HRESULT CTitleFTS::UpdateOptions(WORD wNear, LONG cRows)
  1557. {
  1558. m_wQueryProximity = wNear;
  1559. m_lMaxRowCount = cRows;
  1560. return S_OK;
  1561. }
  1562. /////////////////////////////////////////////////////////////////////////////
  1563. // CTitleFTS set title FTS options
  1564. //
  1565. HRESULT CTitleFTS::SetOptions(DWORD dwFlag)
  1566. {
  1567. HRESULT hr;
  1568. if(!Init())
  1569. return E_FAIL;
  1570. m_dwQueryFlags = dwFlag;
  1571. hr = m_pQuery->SetOptions(dwFlag);
  1572. if (FAILED(hr))
  1573. return E_FAIL;
  1574. else
  1575. return S_OK;
  1576. }
  1577. /////////////////////////////////////////////////////////////////////////////
  1578. // CTitleFTS set proximity for title
  1579. //
  1580. HRESULT CTitleFTS::SetProximity(WORD wNear)
  1581. {
  1582. if(!Init())
  1583. return E_FAIL;
  1584. m_wQueryProximity = wNear;
  1585. return m_pQuery->SetProximity(wNear);
  1586. }
  1587. /////////////////////////////////////////////////////////////////////////////
  1588. // CTitleFTS set max result count for title query
  1589. //
  1590. HRESULT CTitleFTS::SetResultCount(LONG cRows)
  1591. {
  1592. if(!Init())
  1593. return E_FAIL;
  1594. m_lMaxRowCount = cRows;
  1595. return m_pQuery->SetResultCount(cRows);
  1596. }
  1597. // Han2Zen
  1598. //
  1599. // This function converts half-width katakana character to their
  1600. // full-width equivalents while taking into account the nigori
  1601. // and maru marks.
  1602. //
  1603. DWORD Han2Zen(unsigned char *lpInBuffer, unsigned char *lpOutBuffer, UINT codepage )
  1604. {
  1605. // Note: The basic algorithm (including the mapping table) used here to
  1606. // convert half-width Katakana characters to full-width Katakana appears
  1607. // in the book "Understanding Japanese Information Systems" by
  1608. // O'Reily & Associates.
  1609. while(*lpInBuffer)
  1610. {
  1611. if(*lpInBuffer >= 161 && *lpInBuffer <= 223)
  1612. {
  1613. // We have a half-width Katakana character. Now compute the equivalent
  1614. // full-width character via the mapping table.
  1615. //
  1616. *lpOutBuffer = (unsigned char)mtable[*lpInBuffer-161][0];
  1617. *(lpOutBuffer+1) = (unsigned char)mtable[*lpInBuffer-161][1];
  1618. lpInBuffer++;
  1619. // check if the second character is nigori mark.
  1620. //
  1621. if(*lpInBuffer == 222)
  1622. {
  1623. // see if we have a half-width katakana that can be modified by nigori.
  1624. //
  1625. if((*(lpInBuffer-1) >= 182 && *(lpInBuffer-1) <= 196) ||
  1626. (*(lpInBuffer-1) >= 202 && *(lpInBuffer-1) <= 206) || (*(lpInBuffer-1) == 179))
  1627. {
  1628. // transform kana into kana with maru
  1629. //
  1630. if((*(lpOutBuffer+1) >= 74 && *(lpOutBuffer+1) <= 103) ||
  1631. (*(lpOutBuffer+1) >= 110 && *(lpOutBuffer+1) <= 122))
  1632. {
  1633. (*(lpOutBuffer+1))++;
  1634. ++lpInBuffer;
  1635. }
  1636. else if(*lpOutBuffer == 131 && *(lpOutBuffer+1) == 69)
  1637. {
  1638. *(lpOutBuffer+1) = 148;
  1639. ++lpInBuffer;
  1640. }
  1641. }
  1642. }
  1643. else if(*lpInBuffer==223) // check if following character is maru mark
  1644. {
  1645. // see if we have a half-width katakana that can be modified by maru.
  1646. //
  1647. if((*(lpInBuffer-1) >= 202 && *(lpInBuffer-1) <= 206))
  1648. {
  1649. // transform kana into kana with nigori
  1650. //
  1651. if(*(lpOutBuffer+1) >= 110 && *(lpOutBuffer+1) <= 122)
  1652. {
  1653. *(lpOutBuffer+1)+=2;
  1654. ++lpInBuffer;
  1655. }
  1656. }
  1657. }
  1658. lpOutBuffer+=2;
  1659. }
  1660. else
  1661. {
  1662. if(IsDBCSLeadByteEx(codepage, *lpInBuffer))
  1663. {
  1664. *lpOutBuffer++ = *lpInBuffer++;
  1665. if(*lpInBuffer)
  1666. *lpOutBuffer++ = *lpInBuffer++;
  1667. }
  1668. else
  1669. *lpOutBuffer++ = *lpInBuffer++;
  1670. }
  1671. }
  1672. *lpOutBuffer = 0;
  1673. return TRUE;
  1674. }
  1675. WCHAR* PreProcessQuery(WCHAR *pwcQuery, UINT codepage)
  1676. {
  1677. if(!pwcQuery)
  1678. return NULL;
  1679. char *pszQuery = NULL;
  1680. // compute max length for ANSI/DBCS conversion buffer
  1681. //
  1682. DWORD dwTempLen = ((wcslen(pwcQuery)*2)+4);
  1683. // allocate buffer for ANSI/DBCS version of query string
  1684. //
  1685. char *pszTempQuery1 = (char *) lcMalloc(dwTempLen);
  1686. // return on fail
  1687. //
  1688. if(!pszTempQuery1)
  1689. return NULL;
  1690. // Convert our Unicode query to ANSI/DBCS
  1691. //
  1692. int ret = WideCharToMultiByte(codepage, 0, pwcQuery, -1, pszTempQuery1, dwTempLen, "%", NULL);
  1693. // return on fail
  1694. //
  1695. if(!ret || !pszTempQuery1)
  1696. return NULL;
  1697. int cUnmappedChars = 0;
  1698. char *pszTempQuery5 = pszTempQuery1;
  1699. // Count the number of unmappable characters
  1700. //
  1701. while(*pszTempQuery5)
  1702. {
  1703. if(*pszTempQuery5 == '%')
  1704. ++cUnmappedChars;
  1705. if(IsDBCSLeadByteEx(codepage, *pszTempQuery5))
  1706. {
  1707. pszTempQuery5++;
  1708. if(*pszTempQuery5)
  1709. pszTempQuery5++;
  1710. }
  1711. else
  1712. ++pszTempQuery5;
  1713. }
  1714. // allocate a new buffer large enough for unmapped character place holders plus original query
  1715. //
  1716. DWORD dwTranslatedLen = (DWORD)strlen(pszTempQuery1) + (cUnmappedChars * 4) + 16;
  1717. char *pszTempQuery6 = (char *)lcMalloc(dwTranslatedLen);
  1718. char *pszTempQuery7 = pszTempQuery6;
  1719. if(!pszTempQuery6)
  1720. return NULL;
  1721. pszTempQuery5 = pszTempQuery1;
  1722. // construct the new query string (inserting unmappable character place holders)
  1723. //
  1724. while(*pszTempQuery5)
  1725. {
  1726. if(*pszTempQuery5 == '%')
  1727. {
  1728. ++pszTempQuery5;
  1729. *pszTempQuery7++='D';
  1730. *pszTempQuery7++='X';
  1731. *pszTempQuery7++='O';
  1732. continue;
  1733. }
  1734. if(IsDBCSLeadByteEx(codepage, *pszTempQuery5))
  1735. {
  1736. *pszTempQuery7++ = *pszTempQuery5++;
  1737. if(*pszTempQuery5)
  1738. *pszTempQuery7++ = *pszTempQuery5++;
  1739. }
  1740. else
  1741. *pszTempQuery7++ = *pszTempQuery5++;
  1742. }
  1743. *pszTempQuery7 = 0;
  1744. lcFree(pszTempQuery1);
  1745. char *pszTempQuery2 = pszTempQuery6;
  1746. // If we are running a Japanese title then we nomalize Katakana characters
  1747. // by converting half-width Katakana characters to full-width Katakana.
  1748. // This allows the user to receive hits for both the full and half-width
  1749. // versions of the character regardless of which version they type in the
  1750. // query string.
  1751. //
  1752. if(codepage == 932)
  1753. {
  1754. int cb = (int)strlen(pszTempQuery2)+1;
  1755. // allocate new buffer for converted query
  1756. //
  1757. char *pszTempQuery3 = (char *) lcMalloc(cb*2);
  1758. // convert half-width katakana to full-width
  1759. //
  1760. Han2Zen((unsigned char *)pszTempQuery2,(unsigned char *)pszTempQuery3, codepage);
  1761. if(pszTempQuery2)
  1762. lcFree(pszTempQuery2);
  1763. pszTempQuery2 = pszTempQuery3;
  1764. }
  1765. // done half-width normalization
  1766. // For Japanese queries, convert all double-byte quotes into single byte quotes
  1767. //
  1768. if(codepage == 932)
  1769. {
  1770. char *pszTemp = pszTempQuery2;
  1771. while(*pszTemp)
  1772. {
  1773. if(*pszTemp == '' && (*(pszTemp+1) == 'h' || *(pszTemp+1) == 'g' || *(pszTemp+1) == 'J') )
  1774. {
  1775. *pszTemp = ' ';
  1776. *(pszTemp+1) = '\"';
  1777. }
  1778. pszTemp = CharNext(pszTemp);
  1779. }
  1780. }
  1781. // done convert quotes
  1782. // This section converts contigious blocks of DBCS characters into phrases (enclosed in double quotes).
  1783. // Converting DBCS words into phrases is required with the character based DBCS indexer we use.
  1784. //
  1785. int i, cb = (int)strlen(pszTempQuery2);
  1786. // allocate new buffer for processed query
  1787. //
  1788. char *pszDest, *pszTemp;
  1789. char *pszTempQuery4 = (char *) lcMalloc(cb*8);
  1790. if(!pszTempQuery4)
  1791. return NULL;
  1792. pszTemp = pszTempQuery2;
  1793. pszDest = pszTempQuery4;
  1794. while(*pszTemp)
  1795. {
  1796. // check for quoted string - if found, copy it
  1797. if(*pszTemp == '"')
  1798. {
  1799. *pszDest++=*pszTemp++;
  1800. while(*pszTemp && *pszTemp != '"')
  1801. {
  1802. if(IsDBCSLeadByteEx(codepage, *pszTemp))
  1803. {
  1804. *pszDest++=*pszTemp++;
  1805. *pszDest++=*pszTemp++;
  1806. }
  1807. else
  1808. *pszDest++=*pszTemp++;
  1809. }
  1810. if(*pszTemp == '"')
  1811. *pszDest++=*pszTemp++;
  1812. continue;
  1813. }
  1814. // Convert Japanese operators to English operators
  1815. //
  1816. if(IsDBCSLeadByteEx(codepage, *pszTemp))
  1817. {
  1818. // check for full-width operator, if found, convert to ANSI
  1819. if(i = IsJOperator(pszTemp))
  1820. {
  1821. strcpy(pszDest,pEnglishOperator[i]);
  1822. pszDest+=strlen(pEnglishOperator[i]);
  1823. pszTemp+=strlen(pJOperatorList[i]);
  1824. continue;
  1825. }
  1826. *pszDest++=' ';
  1827. *pszDest++='"';
  1828. while(*pszTemp && *pszTemp !='"' && IsDBCSLeadByteEx(codepage, *pszTemp))
  1829. {
  1830. *pszDest++=*pszTemp++;
  1831. *pszDest++=*pszTemp++;
  1832. }
  1833. *pszDest++='"';
  1834. *pszDest++=' ';
  1835. continue;
  1836. }
  1837. *pszDest++=*pszTemp++;
  1838. }
  1839. *pszDest = 0;
  1840. if(pszTempQuery2)
  1841. lcFree(pszTempQuery2);
  1842. // compute size of Unicode buffer;
  1843. int cbUnicodeSize = ((MultiByteToWideChar(codepage, 0, pszTempQuery4, -1, NULL, 0) + 2) *2);
  1844. WCHAR *pszUnicodeBuffer = (WCHAR *) lcMalloc(cbUnicodeSize);
  1845. ret = MultiByteToWideChar(codepage, 0, pszTempQuery4, -1, pszUnicodeBuffer, cbUnicodeSize);
  1846. if(!ret)
  1847. return NULL;
  1848. if(pszTempQuery4)
  1849. lcFree(pszTempQuery4);
  1850. return (WCHAR *) pszUnicodeBuffer;
  1851. }
  1852. // This function computes if pszQuery is a FTS operator in full-width alphanumeric.
  1853. //
  1854. // return value
  1855. //
  1856. // 0 = not operator
  1857. // n = index into pEnglishOperator array of translated English operator
  1858. //
  1859. int IsJOperator(char *pszQuery)
  1860. {
  1861. if((PRIMARYLANGID(GetSystemDefaultLangID())) != LANG_JAPANESE)
  1862. return FALSE;
  1863. if(!pszQuery)
  1864. return 0;
  1865. int i = 1;
  1866. char *pTerm = (char*)pJOperatorList[i];
  1867. while(*pTerm)
  1868. {
  1869. if(compareOperator(pszQuery,pTerm))
  1870. return i;
  1871. pTerm = (char*)pJOperatorList[++i];
  1872. }
  1873. return 0;
  1874. }
  1875. // Compare operator to query. This is similar to a stricmp.
  1876. //
  1877. BOOL compareOperator(char *pszQuery, char *pszTerm)
  1878. {
  1879. if(!*pszQuery || !*pszTerm)
  1880. return FALSE;
  1881. while(*pszQuery && *pszTerm)
  1882. {
  1883. if(*pszQuery != *pszTerm)
  1884. return FALSE;
  1885. ++pszQuery;
  1886. ++pszTerm;
  1887. }
  1888. if(*pszTerm)
  1889. return FALSE;
  1890. return TRUE;
  1891. }
  1892. /////////////////////////////////////////////////////////////////////////////
  1893. // CCombinedFTS Class
  1894. //
  1895. // This class provides full-text search functionality for multiple titles.
  1896. //
  1897. // This class is intended only for use by the CFullTextSearch class which
  1898. // provides full-text search services for both single and multi-title
  1899. // configurations.
  1900. /////////////////////////////////////////////////////////////////////////////
  1901. // CCombinedFTS class constructor
  1902. //
  1903. CCombinedFTS::CCombinedFTS(CExTitle *pExTitle, LCID lcid, CFullTextSearch *pFTS)
  1904. {
  1905. m_lcid = lcid;
  1906. m_langid = LANGIDFROMLCID(lcid);
  1907. m_codepage = CodePageFromLCID(lcid);
  1908. m_pTitle = pExTitle;
  1909. m_SearchActive = FALSE;
  1910. m_pIndex = NULL;
  1911. m_pQuery = NULL;
  1912. m_pITResultSet = NULL;
  1913. m_pITDB = NULL;
  1914. m_pFullTextSearch = pFTS;
  1915. m_pPrevQuery = NULL;
  1916. m_SystemLangID = PRIMARYLANGID(GetSystemDefaultLangID());
  1917. m_fDBCS = FALSE;
  1918. m_lMaxRowCount = 500;
  1919. m_wQueryProximity = 8;
  1920. m_iLastResultCount = 0;
  1921. }
  1922. /////////////////////////////////////////////////////////////////////////////
  1923. // CCombinedFTS class destructor
  1924. //
  1925. CCombinedFTS::~CCombinedFTS()
  1926. {
  1927. if (m_pITResultSet)
  1928. {
  1929. m_pITResultSet->Clear();
  1930. m_pITResultSet->Release();
  1931. }
  1932. if(m_pPrevQuery)
  1933. lcFree(m_pPrevQuery);
  1934. }
  1935. /////////////////////////////////////////////////////////////////////////////
  1936. // CCombinedFTS class destructor
  1937. //
  1938. void CCombinedFTS::ReleaseObjects()
  1939. {
  1940. if(m_pQuery)
  1941. {
  1942. m_pQuery->Release();
  1943. m_pQuery = NULL;
  1944. }
  1945. if(m_pIndex)
  1946. {
  1947. m_pIndex->Close();
  1948. m_pIndex->Release();
  1949. m_pIndex = NULL;
  1950. }
  1951. if(m_pITDB)
  1952. {
  1953. m_pITDB->Close();
  1954. m_pITDB->Release();
  1955. m_pITDB = NULL;
  1956. }
  1957. }
  1958. /////////////////////////////////////////////////////////////////////////////
  1959. // CCombinedFTS class initialization
  1960. //
  1961. HRESULT CCombinedFTS::Initialize()
  1962. {
  1963. const char *pszQueryName;
  1964. if(m_pFullTextSearch && m_pFullTextSearch->m_bMergedChmSetWithCHQ)
  1965. pszQueryName = m_pFullTextSearch->m_pTitleArray[0].pszQueryName;
  1966. else
  1967. pszQueryName = m_pTitle->GetQueryName();
  1968. if(!pszQueryName)
  1969. return E_FAIL;
  1970. MultiByteToWideChar(CP_ACP, 0, pszQueryName, -1, m_tcTitlePath, sizeof(m_tcTitlePath) );
  1971. WORD PrimaryLang = PRIMARYLANGID(m_langid);
  1972. if(PrimaryLang == LANG_JAPANESE || PrimaryLang == LANG_CHINESE || PrimaryLang == LANG_KOREAN)
  1973. m_fDBCS = TRUE;
  1974. else
  1975. m_fDBCS = FALSE;
  1976. if(m_lcid == 1033)
  1977. m_dwQueryFlags = IMPLICIT_AND | QUERY_GETTERMS | STEMMED_SEARCH;
  1978. else
  1979. m_dwQueryFlags = IMPLICIT_AND | QUERY_GETTERMS;
  1980. // char szLangID[20];
  1981. //GetLocaleInfo(m_lcid,LOCALE_ILANGUAGE,szLangID,sizeof(szLangID));
  1982. // Make sure we have a path
  1983. //
  1984. if(!*m_tcTitlePath)
  1985. {
  1986. return E_FAIL;
  1987. }
  1988. // Get IITIndex pointer
  1989. //
  1990. HRESULT hr = CoCreateInstance(CLSID_IITIndexLocal, NULL, CLSCTX_INPROC_SERVER,
  1991. IID_IITIndex, (VOID**)&m_pIndex);
  1992. if (FAILED(hr))
  1993. {
  1994. return E_FAIL;
  1995. }
  1996. // Get IITDatabase pointer
  1997. //
  1998. hr = CoCreateInstance(CLSID_IITDatabaseLocal, NULL, CLSCTX_INPROC_SERVER,
  1999. IID_IITDatabase, (VOID**)&m_pITDB);
  2000. if (FAILED(hr))
  2001. {
  2002. return E_FAIL;
  2003. }
  2004. // Open the storage system
  2005. //
  2006. hr = m_pITDB->Open(NULL, m_tcTitlePath, NULL);
  2007. if (FAILED(hr))
  2008. {
  2009. return E_FAIL;
  2010. }
  2011. // open the index.
  2012. //
  2013. hr = m_pIndex->Open(m_pITDB, txtwFtiMain, TRUE);
  2014. if (FAILED(hr))
  2015. {
  2016. return E_FAIL;
  2017. }
  2018. // Create query instance
  2019. //
  2020. hr = m_pIndex->CreateQueryInstance(&m_pQuery);
  2021. if (FAILED(hr))
  2022. {
  2023. return E_FAIL;
  2024. }
  2025. // set search options
  2026. //
  2027. hr = m_pQuery->SetOptions(m_dwQueryFlags);
  2028. if (FAILED(hr))
  2029. {
  2030. return E_FAIL;
  2031. }
  2032. // Create Result Set object
  2033. //
  2034. if(!m_pITResultSet)
  2035. {
  2036. hr = CoCreateInstance(CLSID_IITResultSet, NULL, CLSCTX_INPROC_SERVER,
  2037. IID_IITResultSet, (VOID**) &m_pITResultSet);
  2038. if (FAILED(hr))
  2039. {
  2040. return E_FAIL;
  2041. }
  2042. }
  2043. m_pITResultSet->Clear();
  2044. // we want topic numbers back
  2045. //
  2046. hr = m_pITResultSet->Add(STDPROP_UID, (DWORD) 0, PRIORITY_NORMAL);
  2047. if (FAILED(hr))
  2048. {
  2049. // Error adding result property
  2050. return E_FAIL;
  2051. }
  2052. hr = m_pITResultSet->Add(STDPROP_TERM_UNICODE_ST, (DWORD)NULL, PRIORITY_NORMAL);
  2053. if (FAILED(hr))
  2054. {
  2055. // Error adding result property
  2056. return E_FAIL;
  2057. }
  2058. hr = m_pITResultSet->Add(STDPROP_COUNT, (DWORD)NULL, PRIORITY_NORMAL);
  2059. if (FAILED(hr))
  2060. {
  2061. // Error adding result property
  2062. return E_FAIL;
  2063. }
  2064. return S_OK;
  2065. }
  2066. /////////////////////////////////////////////////////////////////////////////
  2067. // CCombinedFTS query function
  2068. //
  2069. HRESULT CCombinedFTS::Query(WCHAR *pwcQuery, DWORD dwFlags, IITResultSet **ppITRS, CFullTextSearch *pFullTextSearch, CUWait *pWaitDlg, int iTitle)
  2070. {
  2071. HRESULT hr;
  2072. WCHAR *pNewQuery = NULL;
  2073. if(FAILED(hr = Initialize()))
  2074. return hr;
  2075. *ppITRS = NULL;
  2076. // return if search previous set, but no previous query
  2077. //
  2078. if((dwFlags & FTS_SEARCH_PREVIOUS) && !m_pPrevQuery)
  2079. {
  2080. ReleaseObjects();
  2081. return S_OK;
  2082. }
  2083. WCHAR *pszQuery = pwcQuery;
  2084. pszQuery = PreProcessQuery(pwcQuery, m_codepage);
  2085. if(!pszQuery)
  2086. return E_FAIL;
  2087. // If this combined index resulted in no hits last query, but the global result count was non-zero, then
  2088. // return no hits. The only time we want to query this combined index using the last known good query is
  2089. // when the global query count (combined results) was also zero. In that case, we want to
  2090. // revert all titles and combined indexes back to the last known good query. Otherwise, we skip
  2091. // this combined index because the last query (which resulted in some results globally) is still valid.
  2092. //
  2093. if((dwFlags & FTS_SEARCH_PREVIOUS) && pFullTextSearch->m_iLastResultCount && !m_iLastResultCount)
  2094. {
  2095. lcFree(m_pPrevQuery);
  2096. m_pPrevQuery = NULL;
  2097. return S_OK;
  2098. }
  2099. WCHAR *pPrevQuerySaved = NULL;
  2100. if(m_pPrevQuery)
  2101. {
  2102. pPrevQuerySaved = (WCHAR *) lcMalloc((wcslen(m_pPrevQuery)+ 2) * sizeof(WCHAR));
  2103. wcscpy(pPrevQuerySaved,m_pPrevQuery);
  2104. }
  2105. if(dwFlags & FTS_ENABLE_STEMMING)
  2106. m_dwQueryFlags |= STEMMED_SEARCH;
  2107. else
  2108. m_dwQueryFlags &= (~STEMMED_SEARCH);
  2109. // Set previous options
  2110. //
  2111. m_pQuery->ReInit();
  2112. m_pQuery->SetOptions(m_dwQueryFlags);
  2113. m_pQuery->SetResultCount(m_lMaxRowCount);
  2114. m_pQuery->SetProximity(m_wQueryProximity);
  2115. FCALLBACK_MSG fCallBackMsg;
  2116. fCallBackMsg.MessageFunc = SearchMessageFunc;
  2117. fCallBackMsg.pUserData = (PVOID)pWaitDlg; // used to pass back userdata
  2118. fCallBackMsg.dwFlags = 0; // not recommended for use
  2119. m_pQuery->SetResultCallback(&fCallBackMsg);
  2120. // create group if doing "search previous results"
  2121. //
  2122. if(dwFlags & FTS_SEARCH_PREVIOUS)
  2123. {
  2124. // append new query onto old query for "search previous"
  2125. //
  2126. int cQuery = wcslen(pszQuery);
  2127. int cPrevQuery = wcslen(m_pPrevQuery);
  2128. pNewQuery = (WCHAR *) lcMalloc((cQuery+cPrevQuery+20) * sizeof(WCHAR));
  2129. if(!pNewQuery)
  2130. {
  2131. ReleaseObjects();
  2132. return E_FAIL;
  2133. }
  2134. *pNewQuery = 0;
  2135. wcscat(pNewQuery,m_pPrevQuery);
  2136. wcscat(pNewQuery,L" and ");
  2137. wcscat(pNewQuery,pszQuery);
  2138. wcscat(pNewQuery,L" ");
  2139. }
  2140. // free the previous prev query
  2141. //
  2142. if(m_pPrevQuery)
  2143. lcFree(m_pPrevQuery);
  2144. // Save the new query for next time
  2145. //
  2146. if(pNewQuery)
  2147. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pNewQuery)+ 2) * sizeof(WCHAR));
  2148. else
  2149. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pszQuery)+ 2) * sizeof(WCHAR));
  2150. if(!m_pPrevQuery)
  2151. {
  2152. ReleaseObjects();
  2153. return E_FAIL;
  2154. }
  2155. if(pNewQuery)
  2156. wcscpy(m_pPrevQuery,pNewQuery);
  2157. else
  2158. wcscpy(m_pPrevQuery,pszQuery);
  2159. // clear the previous results
  2160. //
  2161. hr = m_pITResultSet->ClearRows();
  2162. if (FAILED(hr))
  2163. {
  2164. if(pPrevQuerySaved)
  2165. lcFree(pPrevQuerySaved);
  2166. ReleaseObjects();
  2167. return E_FAIL;
  2168. }
  2169. // Set the query
  2170. //
  2171. if(pNewQuery)
  2172. {
  2173. pFullTextSearch->AddQueryToTermList(pNewQuery);
  2174. hr = m_pQuery->SetCommand(pNewQuery);
  2175. }
  2176. else
  2177. {
  2178. pFullTextSearch->AddQueryToTermList(pszQuery);
  2179. hr = m_pQuery->SetCommand(pszQuery);
  2180. }
  2181. if (FAILED(hr))
  2182. {
  2183. if(pPrevQuerySaved)
  2184. lcFree(pPrevQuerySaved);
  2185. ReleaseObjects();
  2186. return E_FAIL;
  2187. }
  2188. if(pNewQuery)
  2189. lcFree(pNewQuery);
  2190. // Execute the query
  2191. //
  2192. hr = m_pIndex->Search(m_pQuery, m_pITResultSet);
  2193. if(hr == E_NOSTEMMER)
  2194. {
  2195. m_dwQueryFlags &= (~STEMMED_SEARCH);
  2196. m_pQuery->SetOptions(m_dwQueryFlags);
  2197. m_pQuery->SetCommand(pszQuery);
  2198. hr = m_pIndex->Search(m_pQuery, m_pITResultSet);
  2199. }
  2200. long lRowCount;
  2201. m_pITResultSet->GetRowCount(lRowCount);
  2202. m_iLastResultCount = lRowCount;
  2203. // If query failed, then restore the previous query (for next search previous)
  2204. //
  2205. if((FAILED(hr) || !lRowCount) && pPrevQuerySaved)
  2206. {
  2207. if(m_pPrevQuery)
  2208. lcFree(m_pPrevQuery);
  2209. m_pPrevQuery = (WCHAR *) lcMalloc((wcslen(pPrevQuerySaved)+ 2) * sizeof(WCHAR));
  2210. wcscpy(m_pPrevQuery,pPrevQuerySaved);
  2211. }
  2212. if(pPrevQuerySaved)
  2213. lcFree(pPrevQuerySaved);
  2214. if (hr ==E_NULLQUERY || hr == E_MISSQUOTE || hr == E_EXPECTEDTERM || hr == E_MISSLPAREN
  2215. || hr == E_MISSRPAREN || hr == E_ALL_WILD)
  2216. {
  2217. // Error invalid syntax
  2218. //
  2219. ReleaseObjects();
  2220. return FTS_INVALID_SYNTAX;
  2221. }
  2222. if(hr == FTS_CANCELED)
  2223. {
  2224. ReleaseObjects();
  2225. return FTS_CANCELED;
  2226. }
  2227. // Generic error
  2228. //
  2229. if (FAILED(hr))
  2230. {
  2231. ReleaseObjects();
  2232. return E_FAIL;
  2233. }
  2234. // Add search terms to term list
  2235. //
  2236. // Make sure we have results
  2237. //
  2238. if(lRowCount)
  2239. {
  2240. WCHAR wcBuffer[2048];
  2241. int i;
  2242. CProperty Prop, Prop2, Prop3;
  2243. DWORD dwLastTopic = 0xffffffff, dwNextWordCount;
  2244. WCHAR *pwcTemp = wcBuffer;
  2245. *pwcTemp = 0;
  2246. // prime the topic number
  2247. //
  2248. m_pITResultSet->Get(0,0, Prop3);
  2249. dwLastTopic = Prop3.dwValue;
  2250. // walk through the results
  2251. //
  2252. for (i = 0; i < lRowCount; i++)
  2253. {
  2254. m_pITResultSet->Get(i,0, Prop3);
  2255. if(i<lRowCount)
  2256. {
  2257. m_pITResultSet->Get(i+1,2, Prop2);
  2258. dwNextWordCount = Prop2.dwValue;
  2259. }
  2260. else
  2261. dwNextWordCount = 0;
  2262. m_pITResultSet->Get(i,1, Prop);
  2263. m_pITResultSet->Get(i,2, Prop2);
  2264. if(!Prop.dwValue)
  2265. continue;
  2266. WORD wFlags[2];
  2267. wFlags[0]=0;
  2268. // Convert term from Unicode to ANSI because GetStringTypeW is not supported on Win95.
  2269. //
  2270. WORD dwStrLen = (WORD)(wcslen(Prop.lpszwData + 1) * sizeof(WCHAR));
  2271. if(dwStrLen)
  2272. {
  2273. char *pAnsiString = (char *) lcMalloc(dwStrLen);
  2274. DWORD dwTermLen = 1;
  2275. WideCharToMultiByte(m_codepage, 0, Prop.lpszwData + 1, -1, pAnsiString, dwStrLen, NULL, NULL);
  2276. if(IsDBCSLeadByteEx(m_codepage, *pAnsiString))
  2277. dwTermLen=2;
  2278. GetStringTypeA(m_lcid, CT_CTYPE3, pAnsiString, dwTermLen, wFlags);
  2279. lcFree(pAnsiString);
  2280. }
  2281. // skip DB chars (DB words were already added)
  2282. //
  2283. if(wFlags[0] & C3_FULLWIDTH || wFlags[0] & C3_KATAKANA || wFlags[0] & C3_HIRAGANA)
  2284. {
  2285. if(*pwcTemp)
  2286. {
  2287. pFullTextSearch->AddHLTerm(wcBuffer,wcslen(wcBuffer));
  2288. pwcTemp = wcBuffer;
  2289. *pwcTemp = 0;
  2290. }
  2291. dwLastTopic = Prop3.dwValue;
  2292. continue;
  2293. }
  2294. CopyMemory(pwcTemp, Prop.lpszwData + 1, (*((WORD *)Prop.lpszwData) * sizeof(WCHAR)));
  2295. pwcTemp+=*((WORD *)Prop.lpszwData);
  2296. *pwcTemp = 0;
  2297. if(dwNextWordCount != (Prop2.dwValue+1) || (wcslen(wcBuffer) > 500))
  2298. {
  2299. pFullTextSearch->AddHLTerm(wcBuffer,wcslen(wcBuffer));
  2300. pwcTemp = wcBuffer;
  2301. }
  2302. else
  2303. {
  2304. *pwcTemp++ = L' ';
  2305. *pwcTemp = 0;
  2306. }
  2307. dwLastTopic = Prop3.dwValue;
  2308. }
  2309. }
  2310. // Send the results set back
  2311. //
  2312. *ppITRS = m_pITResultSet;
  2313. ReleaseObjects();
  2314. return S_OK;
  2315. }
  2316. /////////////////////////////////////////////////////////////////////////////
  2317. // CCombinedFTS abort current query
  2318. //
  2319. HRESULT CCombinedFTS::AbortQuery()
  2320. {
  2321. // DOUGO - insert abort code here when Centaur support is available
  2322. return S_OK;
  2323. }
  2324. /////////////////////////////////////////////////////////////////////////////
  2325. // CCombinedFTS update FTS options without calling into Centaur
  2326. //
  2327. HRESULT CCombinedFTS::UpdateOptions(WORD wNear, LONG cRows)
  2328. {
  2329. m_wQueryProximity = wNear;
  2330. m_lMaxRowCount = cRows;
  2331. return S_OK;
  2332. }
  2333. /////////////////////////////////////////////////////////////////////////////
  2334. // CCombinedFTS set title FTS options
  2335. //
  2336. HRESULT CCombinedFTS::SetOptions(DWORD dwFlag)
  2337. {
  2338. HRESULT hr = E_FAIL;
  2339. m_dwQueryFlags = dwFlag;
  2340. if(m_pQuery)
  2341. hr = m_pQuery->SetOptions(dwFlag);
  2342. if (FAILED(hr))
  2343. return E_FAIL;
  2344. else
  2345. return S_OK;
  2346. }
  2347. /////////////////////////////////////////////////////////////////////////////
  2348. // CCombinedFTS set proximity for title
  2349. //
  2350. HRESULT CCombinedFTS::SetProximity(WORD wNear)
  2351. {
  2352. HRESULT hr = E_FAIL;
  2353. m_wQueryProximity = wNear;
  2354. if(m_pQuery)
  2355. hr = m_pQuery->SetProximity(wNear);
  2356. return hr;
  2357. }
  2358. /////////////////////////////////////////////////////////////////////////////
  2359. // CCombinedFTS set max result count for title query
  2360. //
  2361. HRESULT CCombinedFTS::SetResultCount(LONG cRows)
  2362. {
  2363. HRESULT hr = E_FAIL;
  2364. m_lMaxRowCount = cRows;
  2365. if(m_pQuery)
  2366. hr = m_pQuery->SetResultCount(cRows);
  2367. return hr;
  2368. }
  2369. ERR SearchMessageFunc(DWORD dwFlag, LPVOID pUserData, LPVOID pMessage)
  2370. {
  2371. MSG msg;
  2372. CUWait *pUW = (CUWait *)pUserData;
  2373. if(!pUW->m_bVisable)
  2374. {
  2375. ShowWindow(pUW->m_hwndUWait, SW_SHOW);
  2376. pUW->m_bVisable = TRUE;
  2377. }
  2378. while (PeekMessage(&msg, pUW->m_hwndUWait, 0, 0, PM_REMOVE))
  2379. {
  2380. if(!IsDialogMessage(pUW->m_hwndUWait, &msg))
  2381. {
  2382. TranslateMessage(&msg);
  2383. DispatchMessage(&msg);
  2384. }
  2385. }
  2386. if (pUW->m_bUserCancel == TRUE)
  2387. return FTS_CANCELED;
  2388. return S_OK; // return something else to abort
  2389. }