Leaked source code of windows server 2003
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.

967 lines
28 KiB

  1. #include "stdafx.h"
  2. #include "ftsobj.h"
  3. #include "fs.h"
  4. //
  5. // The high 10 bits will be used as an CHM ID.
  6. // Conversion from DWORD to CHM_ID and Topic Number.
  7. //
  8. #define CHM_ID(exp) (0x000003ff & (exp >> 22))
  9. #define TOPIC_NUM(exp) (0x003fffff & exp)
  10. // Single-Width to Double-Width Mapping Array
  11. //
  12. static const unsigned char mtable[][2]={
  13. {129,66},{129,117},{129,118},{129,65},{129,69},{131,146},{131,64},
  14. {131,66},{131,68},{131,70},{131,72},{131,131},{131,133},{131,135},
  15. {131,98},{129,91},{131,65},{131,67},{131,69},{131,71},{131,73},
  16. {131,74},{131,76},{131,78},{131,80},{131,82},{131,84},{131,86},
  17. {131,88},{131,90},{131,92},{131,94},{131,96},{131,99},{131,101},
  18. {131,103},{131,105},{131,106},{131,107},{131,108},{131,109},
  19. {131,110},{131,113},{131,116},{131,119},{131,122},{131,125},
  20. {131,126},{131,128},{131,129},{131,130},{131,132},{131,134},
  21. {131,136},{131,137},{131,138},{131,139},{131,140},{131,141},
  22. {131,143},{131,147},{129,74},{129,75} };
  23. // note, cannot put in .text since the pointers themselves are uninitialized
  24. static const char* pJOperatorList[] = {"","?�?�??","?�??","?�?�??","?�???�??","?m?d?`?q","?n?q","?`?m?c","?m?n?s",""};
  25. static const char* pEnglishOperator[] = {"","and " ,"or " ,"not " ,"near " ,"NEAR " ,"OR " ,"AND " ,"NOT " ,""};
  26. UINT WINAPI CodePageFromLCID(LCID lcid)
  27. {
  28. char wchLocale[10];
  29. UINT cp;
  30. if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, wchLocale, sizeof wchLocale))
  31. {
  32. cp = strtoul(wchLocale, NULL, 10);
  33. if (cp)
  34. return cp;
  35. }
  36. return GetACP();
  37. }
  38. // Compare operator to query. This is similar to a stricmp.
  39. //
  40. BOOL compareOperator(char *pszQuery, char *pszTerm)
  41. {
  42. if(!*pszQuery || !*pszTerm)
  43. return FALSE;
  44. while(*pszQuery && *pszTerm)
  45. {
  46. if(*pszQuery != *pszTerm)
  47. return FALSE;
  48. ++pszQuery;
  49. ++pszTerm;
  50. }
  51. if(*pszTerm)
  52. return FALSE;
  53. return TRUE;
  54. }
  55. // This function computes if pszQuery is a FTS operator in full-width alphanumeric.
  56. //
  57. // return value
  58. //
  59. // 0 = not operator
  60. // n = index into pEnglishOperator array of translated English operator
  61. //
  62. int IsJOperator(char *pszQuery)
  63. {
  64. if((PRIMARYLANGID(GetSystemDefaultLangID())) != LANG_JAPANESE)
  65. return FALSE;
  66. if(!pszQuery)
  67. return 0;
  68. int i = 1;
  69. char *pTerm = (char*)pJOperatorList[i];
  70. while(*pTerm)
  71. {
  72. if(compareOperator(pszQuery,pTerm))
  73. return i;
  74. pTerm = (char*)pJOperatorList[++i];
  75. }
  76. return 0;
  77. }
  78. // Han2Zen
  79. //
  80. // This function converts half-width katakana character to their
  81. // full-width equivalents while taking into account the nigori
  82. // and maru marks.
  83. //
  84. DWORD Han2Zen(unsigned char *lpInBuffer, unsigned char *lpOutBuffer, UINT codepage )
  85. {
  86. // Note: The basic algorithm (including the mapping table) used here to
  87. // convert half-width Katakana characters to full-width Katakana appears
  88. // in the book "Understanding Japanese Information Systems" by
  89. // O'Reily & Associates.
  90. while(*lpInBuffer)
  91. {
  92. if(*lpInBuffer >= 161 && *lpInBuffer <= 223)
  93. {
  94. // We have a half-width Katakana character. Now compute the equivalent
  95. // full-width character via the mapping table.
  96. //
  97. *lpOutBuffer = mtable[*lpInBuffer-161][0];
  98. *(lpOutBuffer+1) = mtable[*lpInBuffer-161][1];
  99. lpInBuffer++;
  100. // check if the second character is nigori mark.
  101. //
  102. if(*lpInBuffer == 222)
  103. {
  104. // see if we have a half-width katakana that can be modified by nigori.
  105. //
  106. if((*(lpInBuffer-1) >= 182 && *(lpInBuffer-1) <= 196) ||
  107. (*(lpInBuffer-1) >= 202 && *(lpInBuffer-1) <= 206) || (*(lpInBuffer-1) == 179))
  108. {
  109. // transform kana into kana with maru
  110. //
  111. if((*(lpOutBuffer+1) >= 74 && *(lpOutBuffer+1) <= 103) ||
  112. (*(lpOutBuffer+1) >= 110 && *(lpOutBuffer+1) <= 122))
  113. {
  114. (*(lpOutBuffer+1))++;
  115. ++lpInBuffer;
  116. }
  117. else if(*lpOutBuffer == 131 && *(lpOutBuffer+1) == 69)
  118. {
  119. *(lpOutBuffer+1) = 148;
  120. ++lpInBuffer;
  121. }
  122. }
  123. }
  124. else if(*lpInBuffer==223) // check if following character is maru mark
  125. {
  126. // see if we have a half-width katakana that can be modified by maru.
  127. //
  128. if((*(lpInBuffer-1) >= 202 && *(lpInBuffer-1) <= 206))
  129. {
  130. // transform kana into kana with nigori
  131. //
  132. if(*(lpOutBuffer+1) >= 110 && *(lpOutBuffer+1) <= 122)
  133. {
  134. *(lpOutBuffer+1)+=2;
  135. ++lpInBuffer;
  136. }
  137. }
  138. }
  139. lpOutBuffer+=2;
  140. }
  141. else
  142. {
  143. if(IsDBCSLeadByteEx(codepage, *lpInBuffer))
  144. {
  145. *lpOutBuffer++ = *lpInBuffer++;
  146. if(*lpInBuffer)
  147. *lpOutBuffer++ = *lpInBuffer++;
  148. }
  149. else
  150. *lpOutBuffer++ = *lpInBuffer++;
  151. }
  152. }
  153. *lpOutBuffer = 0;
  154. return TRUE;
  155. }
  156. LPWSTR PreProcessQuery(LPCWSTR pwcQuery, UINT codepage)
  157. {
  158. WCHAR* pszUnicodeBuffer = NULL;
  159. char* pszTempQuery1 = NULL;
  160. char* pszTempQuery2 ;
  161. char* pszTempQuery3 = NULL;
  162. char* pszTempQuery4 = NULL;
  163. char* pszTempQuery5 ;
  164. char* pszTempQuery6 = NULL;
  165. char* pszTempQuery7 ;
  166. char* pszDest;
  167. char* pszTemp;
  168. int cUnmappedChars = 0;
  169. int cbUnicodeSize;
  170. int cb;
  171. DWORD dwTempLen;
  172. DWORD dwTranslatedLen;
  173. if(!pwcQuery) goto end;
  174. // compute max length for ANSI/DBCS conversion buffer
  175. //
  176. dwTempLen = ((wcslen(pwcQuery)*2)+4);
  177. // allocate buffer for ANSI/DBCS version of query string
  178. //
  179. pszTempQuery1 = new char[dwTempLen]; if(!pszTempQuery1) goto end;
  180. // Convert our Unicode query to ANSI/DBCS
  181. if(!WideCharToMultiByte(codepage, 0, pwcQuery, -1, pszTempQuery1, dwTempLen, "%", NULL)) goto end;
  182. // Count the number of unmappable characters
  183. //
  184. pszTempQuery5 = pszTempQuery1;
  185. while(*pszTempQuery5)
  186. {
  187. if(*pszTempQuery5 == '%') ++cUnmappedChars;
  188. if(IsDBCSLeadByteEx(codepage, *pszTempQuery5))
  189. {
  190. pszTempQuery5++;
  191. if(*pszTempQuery5) pszTempQuery5++;
  192. }
  193. else
  194. {
  195. ++pszTempQuery5;
  196. }
  197. }
  198. // allocate a new buffer large enough for unmapped character place holders plus original query
  199. //
  200. dwTranslatedLen = strlen(pszTempQuery1) + (cUnmappedChars * 4) + 16;
  201. pszTempQuery6 = new char[dwTranslatedLen]; if(!pszTempQuery6) goto end;
  202. pszTempQuery7 = pszTempQuery6;
  203. pszTempQuery5 = pszTempQuery1;
  204. // construct the new query string (inserting unmappable character place holders)
  205. //
  206. while(*pszTempQuery5)
  207. {
  208. if(*pszTempQuery5 == '%')
  209. {
  210. ++pszTempQuery5;
  211. *pszTempQuery7++='D';
  212. *pszTempQuery7++='X';
  213. *pszTempQuery7++='O';
  214. continue;
  215. }
  216. if(IsDBCSLeadByteEx(codepage, *pszTempQuery5))
  217. {
  218. *pszTempQuery7++ = *pszTempQuery5++;
  219. if(*pszTempQuery5)
  220. *pszTempQuery7++ = *pszTempQuery5++;
  221. }
  222. else
  223. *pszTempQuery7++ = *pszTempQuery5++;
  224. }
  225. *pszTempQuery7 = 0;
  226. pszTempQuery2 = pszTempQuery6;
  227. // If we are running a Japanese title then we nomalize Katakana characters
  228. // by converting half-width Katakana characters to full-width Katakana.
  229. // This allows the user to receive hits for both the full and half-width
  230. // versions of the character regardless of which version they type in the
  231. // query string.
  232. //
  233. if(codepage == 932)
  234. {
  235. cb = strlen(pszTempQuery2)+1;
  236. // allocate new buffer for converted query
  237. //
  238. pszTempQuery3 = new char[cb*2]; if(!pszTempQuery3) goto end;
  239. // convert half-width katakana to full-width
  240. //
  241. Han2Zen((unsigned char *)pszTempQuery2,(unsigned char *)pszTempQuery3, codepage);
  242. pszTempQuery2 = pszTempQuery3;
  243. }
  244. // done half-width normalization
  245. // For Japanese queries, convert all double-byte quotes into single byte quotes
  246. //
  247. if(codepage == 932)
  248. {
  249. pszTemp = pszTempQuery2;
  250. while(*pszTemp)
  251. {
  252. if(*pszTemp == '' && (*(pszTemp+1) == 'h' || *(pszTemp+1) == 'g' || *(pszTemp+1) == 'J') )
  253. {
  254. *pszTemp = ' ';
  255. *(pszTemp+1) = '\"'; //"
  256. }
  257. pszTemp = ::CharNextA(pszTemp);
  258. }
  259. }
  260. // done convert quotes
  261. // This section converts contigious blocks of DBCS characters into phrases (enclosed in double quotes).
  262. // Converting DBCS words into phrases is required with the character based DBCS indexer we use.
  263. //
  264. cb = strlen(pszTempQuery2);
  265. // allocate new buffer for processed query
  266. //
  267. pszTempQuery4 = new char[cb*8]; if(!pszTempQuery4) goto end;
  268. pszTemp = pszTempQuery2;
  269. pszDest = pszTempQuery4;
  270. while(*pszTemp)
  271. {
  272. // check for quoted string - if found, copy it
  273. if(*pszTemp == '"')
  274. {
  275. *pszDest++=*pszTemp++;
  276. while(*pszTemp && *pszTemp != '"')
  277. {
  278. if(IsDBCSLeadByteEx(codepage, *pszTemp))
  279. {
  280. *pszDest++=*pszTemp++;
  281. *pszDest++=*pszTemp++;
  282. }
  283. else
  284. *pszDest++=*pszTemp++;
  285. }
  286. if(*pszTemp == '"')
  287. *pszDest++=*pszTemp++;
  288. continue;
  289. }
  290. // Convert Japanese operators to English operators
  291. //
  292. if(IsDBCSLeadByteEx(codepage, *pszTemp))
  293. {
  294. int i;
  295. // check for full-width operator, if found, convert to ANSI
  296. if((i = IsJOperator(pszTemp)))
  297. {
  298. StringCchCopyA(pszDest, (cb*8) - (pszDest - pszTempQuery4), pEnglishOperator[i]);
  299. pszDest+=strlen(pEnglishOperator[i]);
  300. pszTemp+=strlen(pJOperatorList[i]);
  301. continue;
  302. }
  303. *pszDest++=' ';
  304. *pszDest++='"';
  305. while(*pszTemp && *pszTemp !='"' && IsDBCSLeadByteEx(codepage, *pszTemp))
  306. {
  307. *pszDest++=*pszTemp++;
  308. *pszDest++=*pszTemp++;
  309. }
  310. *pszDest++='"';
  311. *pszDest++=' ';
  312. continue;
  313. }
  314. *pszDest++=*pszTemp++;
  315. }
  316. *pszDest = 0;
  317. // compute size of Unicode buffer;
  318. cbUnicodeSize = ((MultiByteToWideChar(codepage, 0, pszTempQuery4, -1, NULL, 0) + 2) *2);
  319. pszUnicodeBuffer = new WCHAR[cbUnicodeSize]; if(!pszUnicodeBuffer) goto end;
  320. if(!MultiByteToWideChar(codepage, 0, pszTempQuery4, -1, pszUnicodeBuffer, cbUnicodeSize))
  321. {
  322. delete [] pszUnicodeBuffer; pszUnicodeBuffer = NULL;
  323. goto end;
  324. }
  325. end:
  326. delete [] pszTempQuery1;
  327. delete [] pszTempQuery3;
  328. delete [] pszTempQuery4;
  329. delete [] pszTempQuery6;
  330. return pszUnicodeBuffer;
  331. }
  332. ////////////////////////////////////////////////////////////////////////////////
  333. ////////////////////////////////////////////////////////////////////////////////
  334. CFTSObject::CFTSObject()
  335. {
  336. // Config m_cfg;
  337. //
  338. m_fInitialized = false; // bool m_fInitialized;
  339. // MPC::wstring m_strCHQPath;
  340. //
  341. // LCID m_lcidLang;
  342. // FILETIME m_ftVersionInfo;
  343. // DWORD m_dwTopicCount;
  344. // WORD m_wIndex;
  345. //
  346. m_fOutDated = false; // bool m_fOutDated;
  347. m_cmeCHMInfo = NULL; // CHM_MAP_ENTRY* m_cmeCHMInfo;
  348. m_wCHMInfoCount = 0; // WORD m_wCHMInfoCount;
  349. //
  350. // CComPtr<IITIndex> m_pIndex;
  351. // CComPtr<IITQuery> m_pQuery;
  352. // CComPtr<IITResultSet> m_pITResultSet;
  353. // CComPtr<IITDatabase> m_pITDB;
  354. }
  355. CFTSObject::~CFTSObject()
  356. {
  357. if(m_pITResultSet) m_pITResultSet->Clear();
  358. if(m_pIndex ) m_pIndex ->Close();
  359. if(m_pITDB ) m_pITDB ->Close();
  360. delete [] m_cmeCHMInfo;
  361. }
  362. ////////////////////
  363. void CFTSObject::BuildChmPath( /*[in/out]*/ MPC::wstring& strPath, /*[in]*/ LPCSTR szChmName )
  364. {
  365. WCHAR rgBuf[MAX_PATH];
  366. ::MultiByteToWideChar( CP_ACP, 0, szChmName, -1, rgBuf, MAXSTRLEN(rgBuf) );
  367. strPath = m_strCHQPath;
  368. strPath += rgBuf;
  369. strPath += L".chm";
  370. }
  371. HRESULT CFTSObject::Initialize()
  372. {
  373. __HCP_FUNC_ENTRY( "CFTSObject::Initialize" );
  374. USES_CONVERSION;
  375. HRESULT hr;
  376. LPCWSTR szFile;
  377. HANDLE hFile = INVALID_HANDLE_VALUE;
  378. if(m_fInitialized == false)
  379. {
  380. /*DWORD dwFileStamp;
  381. DWORD dwRead;*/
  382. //
  383. // Check if it is a CHQ
  384. //
  385. if(m_cfg.m_fCombined)
  386. {
  387. LPCWSTR szStart = m_cfg.m_strCHQFilename.c_str();
  388. LPCWSTR szEnd = wcsrchr( szStart, '\\' );
  389. m_strCHQPath.assign( szStart, szEnd ? ((szEnd+1) - szStart) : m_cfg.m_strCHQFilename.size() );
  390. __MPC_EXIT_IF_METHOD_FAILS(hr, LoadCombinedIndex());
  391. szFile = m_cfg.m_strCHQFilename.c_str();
  392. }
  393. else
  394. {
  395. szFile = m_cfg.m_strCHMFilename.c_str();
  396. }
  397. hFile = CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL );
  398. if(hFile == INVALID_HANDLE_VALUE)
  399. {
  400. __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
  401. }
  402. /*::SetFilePointer( hFile, 4*sizeof(UINT), NULL, FILE_BEGIN );
  403. __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::ReadFile( hFile, (void*) &dwFileStamp, sizeof( dwFileStamp ), &dwRead, NULL ));
  404. __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::ReadFile( hFile, (void*) &m_lcidLang , sizeof( m_lcidLang ), &dwRead, NULL ));*/
  405. ::CloseHandle( hFile ); hFile = INVALID_HANDLE_VALUE;
  406. //
  407. // Get IITIndex pointer
  408. //
  409. __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_IITIndexLocal, NULL, CLSCTX_INPROC_SERVER, IID_IITIndex, (VOID**)&m_pIndex ));
  410. //
  411. // Get IITDatabase pointer
  412. //
  413. __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_IITDatabaseLocal, NULL, CLSCTX_INPROC_SERVER, IID_IITDatabase, (VOID**)&m_pITDB ));
  414. //
  415. // Open the storage system
  416. //
  417. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITDB->Open( NULL, szFile, NULL));
  418. //
  419. // open the index.
  420. //
  421. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pIndex->Open( m_pITDB, L"ftiMain", TRUE ));
  422. //
  423. // Create query instance
  424. //
  425. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pIndex->CreateQueryInstance( &m_pQuery ));
  426. //
  427. // Create Result Set object
  428. //
  429. __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_IITResultSet, NULL, CLSCTX_INPROC_SERVER, IID_IITResultSet, (VOID**)&m_pITResultSet ));
  430. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->ClearRows());
  431. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->Add( STDPROP_UID , (DWORD)0 , PRIORITY_NORMAL ));
  432. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->Add( STDPROP_TERM_UNICODE_ST, (DWORD)NULL, PRIORITY_NORMAL ));
  433. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->Add( STDPROP_COUNT , (DWORD)NULL, PRIORITY_NORMAL ));
  434. m_fInitialized = true;
  435. }
  436. hr = S_OK;
  437. __MPC_FUNC_CLEANUP;
  438. if(hFile != INVALID_HANDLE_VALUE)
  439. {
  440. ::CloseHandle( hFile );
  441. }
  442. __MPC_FUNC_EXIT(hr);
  443. }
  444. HRESULT CFTSObject::LoadCombinedIndex()
  445. {
  446. __HCP_FUNC_ENTRY( "CFTSObject::LoadCombinedIndex" );
  447. USES_CONVERSION;
  448. HRESULT hr;
  449. MPC::wstring strCHMPathName;
  450. CFileSystem* pDatabase = NULL;
  451. CSubFileSystem* pTitleMap = NULL;
  452. ULONG cbRead = 0;
  453. HANDLE hFile = INVALID_HANDLE_VALUE;
  454. //
  455. // Open the CHQ
  456. //
  457. __MPC_EXIT_IF_ALLOC_FAILS(hr, pDatabase, new CFileSystem);
  458. __MPC_EXIT_IF_METHOD_FAILS(hr, pDatabase->Init( ));
  459. __MPC_EXIT_IF_METHOD_FAILS(hr, pDatabase->Open( (LPWSTR)m_cfg.m_strCHQFilename.c_str() ));
  460. //
  461. // Open the TitleMap that contains all the CHM indexes
  462. //
  463. __MPC_EXIT_IF_ALLOC_FAILS(hr, pTitleMap, new CSubFileSystem( pDatabase ));
  464. __MPC_EXIT_IF_METHOD_FAILS(hr, pTitleMap->OpenSub( "$TitleMap" ));
  465. __MPC_EXIT_IF_METHOD_FAILS(hr, pTitleMap->ReadSub( &m_wCHMInfoCount, sizeof(m_wCHMInfoCount), &cbRead ));
  466. //
  467. // Allocate the CHM MAP
  468. //
  469. delete [] m_cmeCHMInfo;
  470. __MPC_EXIT_IF_ALLOC_FAILS(hr, m_cmeCHMInfo, new CHM_MAP_ENTRY[m_wCHMInfoCount]);
  471. //
  472. // Read in all the CHM Maps
  473. //
  474. for(int iCount = 0; iCount < (int)m_wCHMInfoCount; iCount++)
  475. {
  476. /*DWORD dwFileStamp = 0;
  477. LCID FileLocale = 0;
  478. DWORD dwRead = 0;*/
  479. if(hFile != INVALID_HANDLE_VALUE)
  480. {
  481. ::CloseHandle( hFile ); hFile = INVALID_HANDLE_VALUE;
  482. }
  483. //
  484. // Read in the CHM Map Entry
  485. //
  486. __MPC_EXIT_IF_METHOD_FAILS(hr, pTitleMap->ReadSub( &m_cmeCHMInfo[iCount], sizeof(CHM_MAP_ENTRY), &cbRead ));
  487. //
  488. // Open the CHM in the same folder as the CHQ folder
  489. //
  490. BuildChmPath( strCHMPathName, m_cmeCHMInfo[iCount].szChmName );
  491. hFile = ::CreateFileW( strCHMPathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL );
  492. //
  493. // If cannot open the file, just resume to the next one
  494. //
  495. if(hFile == INVALID_HANDLE_VALUE) continue;
  496. //
  497. // Read in the timestamp and locale
  498. //
  499. /*::SetFilePointer( hFile, 4*sizeof(UINT), NULL, FILE_BEGIN );
  500. if(::ReadFile( hFile, (void*) &dwFileStamp, sizeof( dwFileStamp ), &dwRead, NULL ) == FALSE) continue;
  501. if(::ReadFile( hFile, (void*) &FileLocale , sizeof( FileLocale ), &dwRead, NULL ) == FALSE) continue;*/
  502. ::CloseHandle( hFile ); hFile = INVALID_HANDLE_VALUE;
  503. //
  504. // Check if CHQ index has different version of index than CHM or different language
  505. //
  506. /*if ((m_cmeCHMInfo[iCount].versioninfo.dwLowDateTime != dwFileStamp) ||
  507. (m_cmeCHMInfo[iCount].versioninfo.dwHighDateTime != dwFileStamp) ||
  508. (m_cmeCHMInfo[iCount].language != FileLocale))
  509. {
  510. //
  511. // If it is outdated, mark it
  512. //
  513. m_fOutDated = TRUE;
  514. m_cmeCHMInfo[iCount].dwOutDated = 1;
  515. }
  516. else
  517. {
  518. //
  519. // Otherwise it is good
  520. //
  521. m_cmeCHMInfo[iCount].dwOutDated = 0;
  522. }*/
  523. }
  524. hr = S_OK;
  525. __HCP_FUNC_CLEANUP;
  526. if(hFile != INVALID_HANDLE_VALUE)
  527. {
  528. ::CloseHandle( hFile );
  529. }
  530. delete pTitleMap;
  531. delete pDatabase;
  532. __HCP_FUNC_EXIT(hr);
  533. }
  534. HRESULT CFTSObject::ResetQuery( LPCWSTR wszQuery )
  535. {
  536. __HCP_FUNC_ENTRY( "CFTSObject::ResetQuery" );
  537. HRESULT hr;
  538. __MPC_EXIT_IF_METHOD_FAILS(hr, Initialize());
  539. //
  540. // Setup result set
  541. // we want topic numbers back
  542. //
  543. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->ClearRows());
  544. //
  545. // Set up query parameters
  546. //
  547. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->ReInit());
  548. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetResultCount( m_cfg.m_dwMaxResult ));
  549. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetProximity ( m_cfg.m_wQueryProximity ));
  550. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetCommand ( wszQuery ));
  551. hr = S_OK;
  552. __HCP_FUNC_CLEANUP;
  553. __HCP_FUNC_EXIT(hr);
  554. }
  555. HRESULT CFTSObject::ProcessResult( /*[in/out]*/ SEARCH_RESULT_SET& results, /*[in/out]*/ MPC::WStringSet& words, UINT cp)
  556. {
  557. __HCP_FUNC_ENTRY( "CFTSObject::ProcessResult" );
  558. HRESULT hr;
  559. CProperty Prop;
  560. CProperty HLProp;
  561. MPC::wstring strCHMPathName;
  562. SEARCH_RESULT* pPrevResult = NULL;
  563. CTitleInfo* pTitleInfo = NULL;
  564. DWORD dwPrevCHMID = 0xffffffff;
  565. DWORD dwPrevValue = 0xffffffff;
  566. long lRowCount = 0;
  567. long lLoop;
  568. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pITResultSet->GetRowCount( lRowCount ));
  569. //
  570. // loop through all the results
  571. //
  572. for(lLoop = 0; lLoop < lRowCount; lLoop++)
  573. {
  574. WCHAR rgTitle [1024];
  575. WCHAR rgLocation[1024];
  576. char rgURL [1024];
  577. HLProp.dwType = TYPE_STRING;
  578. if(FAILED(m_pITResultSet->Get( lLoop, 0, Prop ))) continue;
  579. if(FAILED(m_pITResultSet->Get( lLoop, 1, HLProp ))) continue;
  580. //
  581. // Add to highlight word list
  582. //
  583. words.insert( HLProp.lpszwData + 1 );
  584. //
  585. // Check if it is a duplicate
  586. //
  587. if(Prop.dwValue == dwPrevValue)
  588. {
  589. // increment the previous rank
  590. if(pPrevResult) pPrevResult->dwRank++;
  591. continue;
  592. }
  593. dwPrevValue = Prop.dwValue;
  594. //
  595. // If it is a CHQ result
  596. //
  597. if(m_cfg.m_fCombined)
  598. {
  599. //
  600. // If TitleInfo not already opened before
  601. //
  602. if((dwPrevCHMID != CHM_ID(Prop.dwValue)) || !pTitleInfo)
  603. {
  604. //
  605. // save the previous CHMID
  606. //
  607. dwPrevCHMID = CHM_ID(Prop.dwValue);
  608. //
  609. // Hunt for the correct CHMID
  610. //
  611. for(int iCHMInfo = 0; iCHMInfo < m_wCHMInfoCount; iCHMInfo++)
  612. {
  613. //
  614. // Check if the CHM index matches
  615. //
  616. if(m_cmeCHMInfo[iCHMInfo].iIndex == dwPrevCHMID)
  617. {
  618. delete pTitleInfo; pTitleInfo = NULL;
  619. //
  620. // Check if outdated
  621. //
  622. //if(m_cmeCHMInfo[iCHMInfo].dwOutDated == 0)
  623. {
  624. //
  625. // Create a new one
  626. //
  627. __MPC_EXIT_IF_ALLOC_FAILS(hr, pTitleInfo, new CTitleInfo);
  628. //
  629. // Create the chm pathname
  630. //
  631. BuildChmPath( strCHMPathName, m_cmeCHMInfo[iCHMInfo].szChmName );
  632. //
  633. // Open the CHM file
  634. //
  635. if(!pTitleInfo->OpenTitle( (LPWSTR)strCHMPathName.c_str() ))
  636. {
  637. delete pTitleInfo; pTitleInfo = NULL;
  638. }
  639. }
  640. break;
  641. }
  642. }
  643. }
  644. }
  645. else
  646. {
  647. //
  648. // Open the chm
  649. //
  650. if(!pTitleInfo)
  651. {
  652. //
  653. // Create a new one
  654. //
  655. __MPC_EXIT_IF_ALLOC_FAILS(hr, pTitleInfo, new CTitleInfo);
  656. if(!pTitleInfo->OpenTitle( (LPWSTR)m_cfg.m_strCHMFilename.c_str() ))
  657. {
  658. delete pTitleInfo; pTitleInfo = NULL;
  659. }
  660. }
  661. }
  662. if(pTitleInfo)
  663. {
  664. SEARCH_RESULT res;
  665. //
  666. // Get the topic title
  667. //
  668. if(SUCCEEDED(pTitleInfo->GetTopicName( TOPIC_NUM(Prop.dwValue), rgTitle, MAXSTRLEN(rgTitle), cp )))
  669. {
  670. res.bstrTopicName = rgTitle;
  671. }
  672. //
  673. // Get the topic location
  674. //
  675. if(SUCCEEDED(pTitleInfo->GetLocationName( rgLocation, MAXSTRLEN(rgLocation), cp )))
  676. {
  677. res.bstrLocation = rgLocation;
  678. }
  679. //
  680. // Get the topic URL
  681. //
  682. if(SUCCEEDED(pTitleInfo->GetTopicURL( TOPIC_NUM(Prop.dwValue), rgURL, MAXSTRLEN(rgURL) )))
  683. {
  684. res.bstrTopicURL = rgURL;
  685. }
  686. if(res.bstrTopicURL.Length() > 0)
  687. {
  688. std::pair<SEARCH_RESULT_SET_ITER,bool> ins = results.insert( res );
  689. ins.first->dwRank++;
  690. pPrevResult = &(*ins.first);
  691. }
  692. }
  693. }
  694. __MPC_FUNC_CLEANUP;
  695. delete pTitleInfo;
  696. __MPC_FUNC_EXIT(hr);
  697. }
  698. ////////////////////////////////////////////////////////////////////////////////
  699. HRESULT CFTSObject::Query( /*[in]*/ LPCWSTR wszQuery, /*[in]*/ bool bTitle, /*[in]*/ bool bStemming, /*[in/out]*/ SEARCH_RESULT_SET& results, /*[in/out]*/ MPC::WStringSet& words, UINT cp )
  700. {
  701. __HCP_FUNC_ENTRY( "CFTSObject::Query" );
  702. HRESULT hr;
  703. MPC::wstring strFormatQuery;
  704. LPWSTR wszProcessedQuery = NULL;
  705. static bool fSkipFirstTime = false;
  706. __MPC_PARAMCHECK_BEGIN(hr);
  707. __MPC_PARAMCHECK_POINTER(wszQuery);
  708. __MPC_PARAMCHECK_END();
  709. //
  710. // Add field identifier to query (VFLD 0 = full content, VFLD 1 = title only)
  711. //
  712. if(bTitle) strFormatQuery = L"(VFLD 1 ";
  713. else strFormatQuery = L"(VFLD 0 ";
  714. strFormatQuery += wszQuery;
  715. strFormatQuery += L")";
  716. //
  717. // Process query
  718. //
  719. wszProcessedQuery = PreProcessQuery( strFormatQuery.c_str(), CodePageFromLCID(m_lcidLang) );
  720. //
  721. // Execute the search on the CHQ
  722. //
  723. __MPC_EXIT_IF_METHOD_FAILS(hr, Initialize());
  724. __MPC_EXIT_IF_METHOD_FAILS(hr, ResetQuery( wszProcessedQuery ));
  725. if(bStemming)
  726. {
  727. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetOptions( IMPLICIT_AND | QUERY_GETTERMS | STEMMED_SEARCH ));
  728. }
  729. else
  730. {
  731. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetOptions( IMPLICIT_AND | QUERY_GETTERMS ));
  732. }
  733. if(fSkipFirstTime)
  734. {
  735. hr = m_pIndex->Search( m_pQuery, m_pITResultSet );
  736. if(hr == E_NOSTEMMER && bStemming)
  737. {
  738. //
  739. // If won't allow stemmed search, take it out and requery
  740. //
  741. __MPC_EXIT_IF_METHOD_FAILS(hr, ResetQuery( wszProcessedQuery ));
  742. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pQuery->SetOptions( IMPLICIT_AND | QUERY_GETTERMS ));
  743. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pIndex->Search( m_pQuery, m_pITResultSet ));
  744. }
  745. __MPC_EXIT_IF_METHOD_FAILS(hr, ProcessResult( results, words, cp ));
  746. }
  747. //
  748. // Check if we have any outdated chms
  749. //
  750. //if(m_fOutDated)
  751. {
  752. //
  753. // search the chm that is outdated
  754. //
  755. for(int iCount = 0; iCount < (int)m_wCHMInfoCount; iCount++)
  756. {
  757. //
  758. // If the CHM is outdated
  759. //
  760. //if(m_cmeCHMInfo[iCount].dwOutDated == 1)
  761. {
  762. CFTSObject cftsoCHM;
  763. //
  764. // Initialize the sub-object.
  765. //
  766. BuildChmPath( cftsoCHM.m_cfg.m_strCHMFilename, m_cmeCHMInfo[iCount].szChmName );
  767. cftsoCHM.m_cfg.m_dwMaxResult = m_cfg.m_dwMaxResult ;
  768. cftsoCHM.m_cfg.m_wQueryProximity = m_cfg.m_wQueryProximity;
  769. cftsoCHM.m_lcidLang = m_lcidLang;
  770. fSkipFirstTime = true;
  771. //
  772. // Execute query
  773. //
  774. (void)cftsoCHM.Query( wszQuery, bTitle, bStemming, results, words, cp );
  775. }
  776. }
  777. }
  778. hr = S_OK;
  779. __HCP_FUNC_CLEANUP;
  780. delete [] wszProcessedQuery;
  781. __HCP_FUNC_EXIT(hr);
  782. }