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.

2373 lines
65 KiB

  1. /******************************************************************************
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. MergedHHK.cpp
  5. Abstract:
  6. This file contains the implementation of the classes used to parse and
  7. process HHK files.
  8. Revision History:
  9. Davide Massarenti (Dmassare) 12/18/99
  10. created
  11. ******************************************************************************/
  12. #include <stdafx.h>
  13. ////////////////////////////////////////////////////////////////////////////////
  14. ////////////////////////////////////////////////////////////////////////////////
  15. #ifdef DEBUG
  16. #define DEBUG_REGKEY HC_REGISTRY_HELPSVC L"\\Debug"
  17. #define DEBUG_DUMPHHK L"DUMPHHK"
  18. static bool m_fInitialized = false;
  19. static bool m_fDumpHHK = false;
  20. static void Local_ReadDebugSettings()
  21. {
  22. __HCP_FUNC_ENTRY( "Local_ReadDebugSettings" );
  23. HRESULT hr;
  24. MPC::RegKey rkBase;
  25. bool fFound;
  26. if(m_fInitialized) __MPC_FUNC_LEAVE;
  27. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.SetRoot( HKEY_LOCAL_MACHINE ));
  28. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.Attach ( DEBUG_REGKEY ));
  29. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.Exists ( fFound ));
  30. if(fFound)
  31. {
  32. CComVariant vValue;
  33. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.get_Value( vValue, fFound, DEBUG_DUMPHHK ));
  34. if(fFound && vValue.vt == VT_I4)
  35. {
  36. m_fDumpHHK = vValue.lVal ? true : false;
  37. }
  38. }
  39. m_fInitialized = true;
  40. __HCP_FUNC_CLEANUP;
  41. }
  42. static void Local_DumpStream( /*[in]*/ LPCWSTR szFile, /*[in]*/ IStream* streamIN, /*[in]*/ HRESULT hrIN )
  43. {
  44. __HCP_FUNC_ENTRY( "Local_DumpStream" );
  45. static int iSeq = 0;
  46. HRESULT hr;
  47. CComPtr<MPC::FileStream> streamOUT;
  48. Local_ReadDebugSettings();
  49. if(m_fDumpHHK)
  50. {
  51. USES_CONVERSION;
  52. WCHAR rgBuf [MAX_PATH];
  53. CHAR rgBuf2[ 64];
  54. ULARGE_INTEGER liWritten;
  55. swprintf( rgBuf , L"C:\\TMP\\dump_%d.hhk", iSeq++ );
  56. sprintf ( rgBuf2, "%s\n" , W2A( szFile ) );
  57. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &streamOUT ));
  58. __MPC_EXIT_IF_METHOD_FAILS(hr, streamOUT->InitForWrite( rgBuf ));
  59. streamOUT->Write( rgBuf2, strlen(rgBuf2), &liWritten.LowPart );
  60. if(SUCCEEDED(hrIN) && streamIN)
  61. {
  62. STATSTG statstg;
  63. LARGE_INTEGER li;
  64. ULARGE_INTEGER liRead;
  65. streamIN->Stat( &statstg, STATFLAG_NONAME );
  66. streamIN->CopyTo( streamOUT, statstg.cbSize, &liRead, &liWritten );
  67. li.LowPart = 0;
  68. li.HighPart = 0;
  69. streamIN->Seek( li, STREAM_SEEK_SET, NULL );
  70. }
  71. }
  72. __HCP_FUNC_CLEANUP;
  73. }
  74. #endif
  75. ////////////////////////////////////////////////////////////////////////////////
  76. ////////////////////////////////////////////////////////////////////////////////
  77. // ENTITY TABLE SWIPED FROM IE
  78. static const struct
  79. {
  80. const char* szName;
  81. int ch;
  82. } rgEntities[] =
  83. {
  84. "AElig", '\306', // capital AE diphthong (ligature)
  85. "Aacute", '\301', // capital A, acute accent
  86. "Acirc", '\302', // capital A, circumflex accent
  87. "Agrave", '\300', // capital A, grave accent
  88. "Aring", '\305', // capital A, ring
  89. "Atilde", '\303', // capital A, tilde
  90. "Auml", '\304', // capital A, dieresis or umlaut mark
  91. "Ccedil", '\307', // capital C, cedilla
  92. "Dstrok", '\320', // capital Eth, Icelandic
  93. "ETH", '\320', // capital Eth, Icelandic
  94. "Eacute", '\311', // capital E, acute accent
  95. "Ecirc", '\312', // capital E, circumflex accent
  96. "Egrave", '\310', // capital E, grave accent
  97. "Euml", '\313', // capital E, dieresis or umlaut mark
  98. "Iacute", '\315', // capital I, acute accent
  99. "Icirc", '\316', // capital I, circumflex accent
  100. "Igrave", '\314', // capital I, grave accent
  101. "Iuml", '\317', // capital I, dieresis or umlaut mark
  102. "Ntilde", '\321', // capital N, tilde
  103. "Oacute", '\323', // capital O, acute accent
  104. "Ocirc", '\324', // capital O, circumflex accent
  105. "Ograve", '\322', // capital O, grave accent
  106. "Oslash", '\330', // capital O, slash
  107. "Otilde", '\325', // capital O, tilde
  108. "Ouml", '\326', // capital O, dieresis or umlaut mark
  109. "THORN", '\336', // capital THORN, Icelandic
  110. "Uacute", '\332', // capital U, acute accent
  111. "Ucirc", '\333', // capital U, circumflex accent
  112. "Ugrave", '\331', // capital U, grave accent
  113. "Uuml", '\334', // capital U, dieresis or umlaut mark
  114. "Yacute", '\335', // capital Y, acute accent
  115. "aacute", '\341', // small a, acute accent
  116. "acirc", '\342', // small a, circumflex accent
  117. "acute", '\264', // acute accent
  118. "aelig", '\346', // small ae diphthong (ligature)
  119. "agrave", '\340', // small a, grave accent
  120. "amp", '\046', // ampersand
  121. "aring", '\345', // small a, ring
  122. "atilde", '\343', // small a, tilde
  123. "auml", '\344', // small a, dieresis or umlaut mark
  124. "brkbar", '\246', // broken vertical bar
  125. "brvbar", '\246', // broken vertical bar
  126. "ccedil", '\347', // small c, cedilla
  127. "cedil", '\270', // cedilla
  128. "cent", '\242', // small c, cent
  129. "copy", '\251', // copyright symbol (proposed 2.0)
  130. "curren", '\244', // currency symbol
  131. "deg", '\260', // degree sign
  132. "die", '\250', // umlaut (dieresis)
  133. "divide", '\367', // divide sign
  134. "eacute", '\351', // small e, acute accent
  135. "ecirc", '\352', // small e, circumflex accent
  136. "egrave", '\350', // small e, grave accent
  137. "eth", '\360', // small eth, Icelandic
  138. "euml", '\353', // small e, dieresis or umlaut mark
  139. "frac12", '\275', // fraction 1/2
  140. "frac14", '\274', // fraction 1/4
  141. "frac34", '\276', // fraction 3/4*/
  142. "gt", '\076', // greater than
  143. "hibar", '\257', // macron accent
  144. "iacute", '\355', // small i, acute accent
  145. "icirc", '\356', // small i, circumflex accent
  146. "iexcl", '\241', // inverted exclamation
  147. "igrave", '\354', // small i, grave accent
  148. "iquest", '\277', // inverted question mark
  149. "iuml", '\357', // small i, dieresis or umlaut mark
  150. "laquo", '\253', // left angle quote
  151. "lt", '\074', // less than
  152. "macr", '\257', // macron accent
  153. "micro", '\265', // micro sign
  154. "middot", '\267', // middle dot
  155. "nbsp", '\240', // non-breaking space (proposed 2.0)
  156. "not", '\254', // not sign
  157. "ntilde", '\361', // small n, tilde
  158. "oacute", '\363', // small o, acute accent
  159. "ocirc", '\364', // small o, circumflex accent
  160. "ograve", '\362', // small o, grave accent
  161. "ordf", '\252', // feminine ordinal
  162. "ordm", '\272', // masculine ordinal
  163. "oslash", '\370', // small o, slash
  164. "otilde", '\365', // small o, tilde
  165. "ouml", '\366', // small o, dieresis or umlaut mark
  166. "para", '\266', // paragraph sign
  167. "plusmn", '\261', // plus minus
  168. "pound", '\243', // pound sterling
  169. "quot", '"', // double quote
  170. "raquo", '\273', // right angle quote
  171. "reg", '\256', // registered trademark (proposed 2.0)
  172. "sect", '\247', // section sign
  173. "shy", '\255', // soft hyphen (proposed 2.0)
  174. "sup1", '\271', // superscript 1
  175. "sup2", '\262', // superscript 2
  176. "sup3", '\263', // superscript 3
  177. "szlig", '\337', // small sharp s, German (sz ligature)
  178. "thorn", '\376', // small thorn, Icelandic
  179. "times", '\327', // times sign
  180. "trade", '\231', // trademark sign
  181. "uacute", '\372', // small u, acute accent
  182. "ucirc", '\373', // small u, circumflex accent
  183. "ugrave", '\371', // small u, grave accent
  184. "uml", '\250', // umlaut (dieresis)
  185. "uuml", '\374', // small u, dieresis or umlaut mark
  186. "yacute", '\375', // small y, acute accent
  187. "yen", '\245', // yen
  188. "yuml", '\377', // small y, dieresis or umlaut mark
  189. 0, 0
  190. };
  191. static BOOL ReplaceEscapes( PCSTR pszSrc, PSTR pszDst )
  192. {
  193. if(StrChrA( pszSrc, '&' ) == NULL)
  194. {
  195. // If we get here, there are no escape sequences, so copy the string and return.
  196. if(pszDst != pszSrc) strcpy( pszDst, pszSrc );
  197. return FALSE; // nothing changed
  198. }
  199. while(*pszSrc)
  200. {
  201. if(IsDBCSLeadByte(*pszSrc))
  202. {
  203. if(pszSrc[1])
  204. {
  205. *pszDst++ = *pszSrc++;
  206. *pszDst++ = *pszSrc++;
  207. }
  208. else
  209. {
  210. // leadbyte followed by 0; invalid!
  211. *pszDst++ = '?';
  212. break;
  213. }
  214. }
  215. else if(*pszSrc == '&')
  216. {
  217. pszSrc++;
  218. if(*pszSrc == '#')
  219. {
  220. // SGML/HTML character entity (decimal)
  221. pszSrc++;
  222. for(int val = 0; *pszSrc && *pszSrc != ';'; pszSrc++)
  223. {
  224. if(*pszSrc >= '0' && *pszSrc <= '9')
  225. {
  226. val = val * 10 + *pszSrc - '0';
  227. }
  228. else
  229. {
  230. while(*pszSrc && *pszSrc != ';')
  231. {
  232. pszSrc++;
  233. }
  234. break;
  235. }
  236. }
  237. if(val)
  238. {
  239. *pszDst++ = (char)val;
  240. }
  241. }
  242. else if(*pszSrc)
  243. {
  244. char szEntityName[256];
  245. int count = 0;
  246. for(PSTR p = szEntityName; *pszSrc && *pszSrc != ';' && *pszSrc != ' ' && count < sizeof(szEntityName);)
  247. {
  248. *p++ = *pszSrc++;
  249. count++;
  250. }
  251. *p = 0;
  252. if(*pszSrc == ';') pszSrc++;
  253. for(int i = 0; rgEntities[i].szName; i++)
  254. {
  255. if(!strcmp(szEntityName, rgEntities[i].szName))
  256. {
  257. if(rgEntities[i].ch)
  258. {
  259. *pszDst++ = (char)rgEntities[i].ch;
  260. }
  261. break;
  262. }
  263. }
  264. if(!rgEntities[i].szName)
  265. {
  266. // illegal entity name, put in a block character
  267. *pszDst++ = '?';
  268. }
  269. }
  270. }
  271. else
  272. {
  273. // just your usual character...
  274. *pszDst++ = *pszSrc++;
  275. }
  276. }
  277. *pszDst = 0;
  278. return TRUE;
  279. }
  280. static void ReplaceCharactersWithEntity( /*[out]*/ MPC::string& strValue ,
  281. /*[out]*/ MPC::string& strBuffer )
  282. {
  283. LPCSTR szToEscape = strValue.c_str();
  284. CHAR ch;
  285. strBuffer.erase();
  286. while((ch = *szToEscape++))
  287. {
  288. switch(ch)
  289. {
  290. case '&': strBuffer += "&amp;" ; break;
  291. case '"': strBuffer += "&quot;"; break;
  292. case '<': strBuffer += "&lt;" ; break;
  293. case '>': strBuffer += "&gt;" ; break;
  294. default: strBuffer += ch ; break;
  295. }
  296. }
  297. strValue = strBuffer;
  298. }
  299. ////////////////////////////////////////////////////////////////////////////////
  300. ////////////////////////////////////////////////////////////////////////////////
  301. ////////////////////////////////////////////////////////////////////////////////
  302. static const char txtBeginList [] = "UL>";
  303. static const char txtEndList [] = "/UL>";
  304. static const char txtBeginListItem [] = "LI";
  305. static const char txtBeginObject [] = "OBJECT";
  306. static const char txtEndObject [] = "/OBJECT";
  307. static const char txtParam [] = "param name";
  308. static const char txtValue [] = "value";
  309. static const char txtParamKeyword [] = "Keyword";
  310. static const char txtParamName [] = "Name";
  311. static const char txtParamSeeAlso [] = "See Also";
  312. static const char txtParamLocal [] = "Local";
  313. static const char txtType [] = "type";
  314. static const char txtSiteMapObject [] = "text/sitemap";
  315. /////////////////////////////////////////////////////////////////////////////
  316. static const char txtHeader[] = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n" \
  317. "<HTML>\n" \
  318. "<HEAD>\n" \
  319. "<meta name=\"GENERATOR\" content=\"Microsoft&reg; HTML Help Workshop 4.1\">\n" \
  320. "<!-- Sitemap 1.0 -->\n" \
  321. "</HEAD><BODY>\n" \
  322. "<OBJECT type=\"text/site properties\">\n" \
  323. "\t<param name=\"FrameName\" value=\"HelpCtrContents\">\n" \
  324. "</OBJECT>\n" \
  325. "<UL>\n";
  326. static const char txtTail[] = "</UL>\n" \
  327. "</BODY></HTML>\n";
  328. /////////////////////////////////////////////////////////////////////////////
  329. static const char txtIndent [] = "\t";
  330. static const char txtNewSection_Open [] = "\t<LI> <OBJECT type=\"text/sitemap\">\n";
  331. static const char txtNewSection_Close [] = "\t\t</OBJECT>\n";
  332. static const char txtNewSubSection_Open [] = "\t<UL>\n";
  333. static const char txtNewSubSection_Close[] = "\t</UL>\n";
  334. static const char txtNewParam_Name [] = "\t\t<param name=\"Name\" value=\"";
  335. static const char txtNewParam_Local [] = "\t\t<param name=\"Local\" value=\"";
  336. static const char txtNewParam_SeeAlso [] = "\t\t<param name=\"See Also\" value=\"";
  337. static const char txtNewParam_Close [] = "\">\n";
  338. static const char txtIndexFirstLevel [] = "hcp://system/errors/indexfirstlevel.htm";
  339. /////////////////////////////////////////////////////////////////////////////
  340. BOOL HHK::Reader::s_fDBCSSystem = (BOOL)::GetSystemMetrics( SM_DBCSENABLED );
  341. LCID HHK::Reader::s_lcidSystem = ::GetUserDefaultLCID();
  342. /////////////////////////////////////////////////////////////////////////////
  343. static void ConvertToAnsi( MPC::string& strANSI, const MPC::wstring& strUNICODE )
  344. {
  345. USES_CONVERSION;
  346. strANSI = W2A( strUNICODE.c_str() );
  347. }
  348. ////////////////////////////////////////////////////////////////////////////////
  349. void HHK::Entry::MergeURLs( const HHK::Entry& entry )
  350. {
  351. Entry::UrlIterConst itUrlNew;
  352. Entry::UrlIter itUrlOld;
  353. //
  354. // Just copy unique URLs.
  355. //
  356. for(itUrlNew = entry.m_lstUrl.begin(); itUrlNew != entry.m_lstUrl.end(); itUrlNew++)
  357. {
  358. bool fInsert = true;
  359. for(itUrlOld = m_lstUrl.begin(); itUrlOld != m_lstUrl.end(); itUrlOld++)
  360. {
  361. int res = Reader::StrColl( (*itUrlOld).c_str(), (*itUrlNew).c_str() );
  362. if(res == 0)
  363. {
  364. // Same URL, skip it.
  365. fInsert = false;
  366. break;
  367. }
  368. if(res > 0)
  369. {
  370. //
  371. // Old > New, insert New before Old.
  372. //
  373. break;
  374. }
  375. }
  376. //
  377. // If fInsert is set, we need to insert "New" just before "Old".
  378. //
  379. // This work also in the case itUrlOld == end().
  380. //
  381. if(fInsert) m_lstUrl.insert( itUrlOld, *itUrlNew );
  382. }
  383. }
  384. HHK::Section::Section()
  385. {
  386. }
  387. HHK::Section::~Section()
  388. {
  389. MPC::CallDestructorForAll( m_lstSeeAlso );
  390. }
  391. void HHK::Section::MergeURLs( const Entry& entry )
  392. {
  393. Section::EntryIter itEntry;
  394. bool fInsert = true;
  395. for(itEntry = m_lstEntries.begin(); itEntry != m_lstEntries.end(); itEntry++)
  396. {
  397. Entry& entryOld = *itEntry;
  398. int res = Reader::StrColl( entryOld.m_strTitle.c_str(), entry.m_strTitle.c_str() );
  399. if(res == 0)
  400. {
  401. // Same title, just merge the URLs.
  402. entryOld.MergeURLs( entry );
  403. fInsert = false;
  404. break;
  405. }
  406. if(res > 0)
  407. {
  408. //
  409. // Old > New, insert New before Old.
  410. //
  411. break;
  412. }
  413. }
  414. //
  415. // Make a copy, insert it at the right position...
  416. //
  417. if(fInsert) m_lstEntries.insert( itEntry, entry );
  418. }
  419. void HHK::Section::MergeSeeAlso( const Section& sec )
  420. {
  421. Section::SectionIterConst itSec;
  422. Section::SectionIter itSecOld;
  423. Section* subsec;
  424. Section* subsecOld;
  425. int res;
  426. for(itSec = sec.m_lstSeeAlso.begin(); itSec != sec.m_lstSeeAlso.end(); itSec++)
  427. {
  428. bool fInsert = true;
  429. subsec = *itSec;
  430. for(itSecOld = m_lstSeeAlso.begin(); itSecOld != m_lstSeeAlso.end(); itSecOld++)
  431. {
  432. subsecOld = *itSecOld;
  433. res = Reader::StrColl( subsecOld->m_strTitle.c_str(), subsec->m_strTitle.c_str() );
  434. if(res == 0)
  435. {
  436. //
  437. // Same title, merge the entries.
  438. //
  439. Section::EntryIterConst itEntry;
  440. for(itEntry = subsec->m_lstEntries.begin(); itEntry != subsec->m_lstEntries.end(); itEntry++)
  441. {
  442. subsecOld->MergeURLs( *itEntry );
  443. }
  444. fInsert = false;
  445. break;
  446. }
  447. if(res > 0)
  448. {
  449. //
  450. // Old > New, insert New before Old.
  451. //
  452. break;
  453. }
  454. }
  455. if(fInsert)
  456. {
  457. if((subsecOld = new Section()))
  458. {
  459. //
  460. // Copy everything, except "see also" list.
  461. //
  462. *subsecOld = *subsec;
  463. subsecOld->m_lstSeeAlso.clear();
  464. m_lstSeeAlso.insert( itSecOld, subsecOld );
  465. }
  466. }
  467. }
  468. }
  469. void HHK::Section::CleanEntries( EntryList& lstEntries )
  470. {
  471. Section::EntryIterConst itEntry;
  472. for(itEntry = lstEntries.begin(); itEntry != lstEntries.end(); )
  473. {
  474. const Entry& entry = *itEntry;
  475. if(entry.m_strTitle.length() == 0 ||
  476. entry.m_lstUrl.size() == 0 )
  477. {
  478. lstEntries.erase( itEntry );
  479. itEntry = lstEntries.begin();
  480. }
  481. else
  482. {
  483. itEntry++;
  484. }
  485. }
  486. }
  487. /////////////////////////////////////////////////////////////////////////////
  488. LPCSTR HHK::Reader::StrChr( LPCSTR szString, CHAR cSearch )
  489. {
  490. if(s_fDBCSSystem)
  491. {
  492. CHAR c;
  493. while((c = *szString))
  494. {
  495. while(::IsDBCSLeadByte( c ))
  496. {
  497. szString++;
  498. if( *szString++ == 0) return NULL;
  499. if((c = *szString ) == 0) return NULL;
  500. }
  501. if(c == cSearch) return szString;
  502. szString++;
  503. }
  504. return NULL;
  505. }
  506. return ::strchr( szString, cSearch );
  507. }
  508. LPCSTR HHK::Reader::StriStr( LPCSTR szString, LPCSTR szSearch )
  509. {
  510. if(!szString || !szSearch) return NULL;
  511. LPCSTR szCur = szString;
  512. CHAR ch = (int)tolower(*szSearch);
  513. int cb = strlen ( szSearch);
  514. for(;;)
  515. {
  516. while(tolower(*szCur) != ch && *szCur)
  517. {
  518. szCur = s_fDBCSSystem ? ::CharNextA( szCur ) : szCur + 1;
  519. }
  520. if(!*szCur) return NULL;
  521. if(::CompareStringA( s_lcidSystem, NORM_IGNORECASE, szCur, cb, szSearch, cb ) == 2) return szCur;
  522. szCur = s_fDBCSSystem ? ::CharNextA( szCur ) : szCur + 1;
  523. }
  524. }
  525. int HHK::Reader::StrColl( LPCSTR szLeft, LPCSTR szRight )
  526. {
  527. LPSTR szLeftCopy = (LPSTR)_alloca( strlen( szLeft ) + 2 );
  528. LPSTR szRightCopy = (LPSTR)_alloca( strlen( szRight ) + 2 );
  529. ReplaceEscapes( szLeft , szLeftCopy );
  530. ReplaceEscapes( szRight, szRightCopy );
  531. switch(::CompareStringA( s_lcidSystem, NORM_IGNORECASE, szLeftCopy, -1, szRightCopy, -1 ))
  532. {
  533. case CSTR_LESS_THAN : return -1;
  534. case CSTR_EQUAL : return 0;
  535. case CSTR_GREATER_THAN: return 1;
  536. }
  537. return _stricmp( szLeftCopy, szRightCopy );
  538. }
  539. LPCSTR HHK::Reader::ComparePrefix( LPCSTR szString, LPCSTR szPrefix )
  540. {
  541. int cb = strlen( szPrefix );
  542. if(_strnicoll( szString, szPrefix, cb ) == 0) return &szString[cb];
  543. return NULL;
  544. }
  545. /////////////////////////////////////////////////////////////////////////////
  546. HHK::Reader::Reader()
  547. {
  548. // CComPtr<IStream> m_stream;
  549. // CHAR m_rgBuf[HHK_BUF_SIZE];
  550. m_szBuf_Pos = NULL; // LPSTR m_szBuf_Pos;
  551. m_szBuf_End = NULL; // LPSTR m_szBuf_End;
  552. //
  553. // MPC::string m_strLine;
  554. m_szLine_Pos = NULL; // LPCSTR m_szLine_Pos;
  555. m_szLine_End = NULL; // LPCSTR m_szLine_End;
  556. m_iLevel = 0; // int m_iLevel;
  557. m_fOpeningBraceSeen = false; // bool m_fOpeningBraceSeen;
  558. }
  559. HHK::Reader::~Reader()
  560. {
  561. }
  562. /*
  563. HRESULT HHK::Reader::Init( LPCWSTR szFile )
  564. Initializes the Reader as either a stream coming from a CHM of a plain file.
  565. */
  566. HRESULT HHK::Reader::Init( LPCWSTR szFile )
  567. {
  568. __HCP_FUNC_ENTRY( "HHK::Reader::Init" );
  569. HRESULT hr;
  570. CComBSTR bstrStorageName;
  571. CComBSTR bstrFilePath;
  572. if(MPC::MSITS::IsCHM( szFile, &bstrStorageName, &bstrFilePath ))
  573. {
  574. USES_CONVERSION;
  575. m_strStorage = "ms-its:";
  576. m_strStorage += OLE2A( SAFEBSTR( bstrStorageName ) );
  577. m_strStorage += "::/";
  578. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MSITS::OpenAsStream( bstrStorageName, bstrFilePath, &m_stream ));
  579. }
  580. else
  581. {
  582. CComPtr<MPC::FileStream> fsStream;
  583. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &fsStream ));
  584. __MPC_EXIT_IF_METHOD_FAILS(hr, fsStream->InitForRead( szFile ));
  585. __MPC_EXIT_IF_METHOD_FAILS(hr, fsStream.QueryInterface( &m_stream ));
  586. }
  587. hr = S_OK;
  588. __HCP_FUNC_CLEANUP;
  589. #ifdef DEBUG
  590. Local_DumpStream( szFile, m_stream, hr );
  591. #endif
  592. __HCP_FUNC_EXIT(hr);
  593. }
  594. /////////////////////////////////////////////////////////////////////////////
  595. /*
  596. bool HHK::Reader::ReadNextBuffer()
  597. Reads the Next Input Buffer into m_rgBuf then resets member variable m_szBuf_Pos to point at the beginning
  598. of the Buffer and m_szBuf_end to point at the end of the Buffer.
  599. Returns: true - If it could read a buffer
  600. false - When at End of File (EOF), or on read error condition.
  601. */
  602. bool HHK::Reader::ReadNextBuffer()
  603. {
  604. bool fRes = false;
  605. if(m_stream)
  606. {
  607. HRESULT hr;
  608. ULONG cbRead;
  609. hr = m_stream->Read( m_rgBuf, sizeof(m_rgBuf)-1, &cbRead );
  610. if(SUCCEEDED(hr) && cbRead)
  611. {
  612. m_szBuf_Pos = m_rgBuf;
  613. m_szBuf_End = &m_rgBuf[cbRead]; m_szBuf_End[0] = 0; // So it's a string...
  614. fRes = true;
  615. }
  616. }
  617. return fRes;
  618. }
  619. /*
  620. bool HHK::Reader::GetLine():
  621. Reads the Next Text Line from reader Input Stream
  622. returns: true - if it could read information
  623. false - if at Enf of File (EOF).
  624. */
  625. bool HHK::Reader::GetLine( MPC::wstring* pstrString )
  626. {
  627. LPSTR szEnd;
  628. LPSTR szMatch1;
  629. LPSTR szMatch2;
  630. int cb;
  631. bool fRes = false;
  632. bool fSkip = true;
  633. m_strLine.erase();
  634. for(;;)
  635. {
  636. //
  637. // Make sure the buffer has data, otherwise exit.
  638. //
  639. if(IsEndOfBuffer())
  640. {
  641. if(ReadNextBuffer() == false)
  642. {
  643. //
  644. // End of file: return 'true' if we got any text.
  645. //
  646. if(m_strLine.length()) fRes = true;
  647. break;
  648. }
  649. }
  650. //
  651. // Skip initial end of lines...
  652. //
  653. if(fSkip)
  654. {
  655. if(m_szBuf_Pos[0] == '\r' ||
  656. m_szBuf_Pos[0] == '\n' )
  657. {
  658. m_szBuf_Pos++;
  659. continue;
  660. }
  661. fSkip = false;
  662. }
  663. szMatch1 = (LPSTR)StrChr( m_szBuf_Pos, '\r' );
  664. szMatch2 = (LPSTR)StrChr( m_szBuf_Pos, '\n' );
  665. if(szMatch1 == NULL || (szMatch2 && szMatch1 > szMatch2)) szMatch1 = szMatch2; // Pick the first to appear, between \r and \n.
  666. if(szMatch1 == NULL)
  667. {
  668. //
  669. // End of line not found, save all the buffer.
  670. //
  671. cb = m_szBuf_End - m_szBuf_Pos;
  672. if(cb) fRes = true;
  673. m_strLine.append( m_szBuf_Pos, cb );
  674. m_szBuf_Pos = m_szBuf_End;
  675. }
  676. else
  677. {
  678. cb = szMatch1 - m_szBuf_Pos;
  679. if(cb) fRes = true;
  680. m_strLine.append( m_szBuf_Pos, cb );
  681. m_szBuf_Pos = szMatch1;
  682. break;
  683. }
  684. }
  685. if(fRes)
  686. {
  687. m_szLine_Pos = m_strLine.begin();
  688. m_szLine_End = m_strLine.end ();
  689. //
  690. // Remove trailing spaces.
  691. //
  692. while(m_szLine_End > m_szLine_Pos && m_szLine_End[-1] == ' ')
  693. {
  694. --m_szLine_End;
  695. }
  696. if(m_szLine_End != m_strLine.end())
  697. {
  698. ;
  699. }
  700. }
  701. else
  702. {
  703. m_szLine_Pos = NULL;
  704. m_szLine_End = NULL;
  705. }
  706. if(pstrString)
  707. {
  708. USES_CONVERSION;
  709. *pstrString = A2W( m_strLine.c_str() );
  710. }
  711. return fRes;
  712. }
  713. /////////////////////////////////////////////////////////////////////////////
  714. /*
  715. bool HHK::Reader::FirstNonSpace( bool fWrap )
  716. This function sets the current Reader position to the First non space character it finds.
  717. If fWrap is set, it can go over End Of Line (EOL) markers.
  718. Return Value: It reports back whether there is or not a non space character forward from
  719. the current Reader stream position.
  720. */
  721. bool HHK::Reader::FirstNonSpace( bool fWrap )
  722. {
  723. for(;;)
  724. {
  725. LPCSTR szMatch;
  726. while(IsEndOfLine())
  727. {
  728. if(fWrap == false) return false;
  729. if(GetLine() == false) return false;
  730. }
  731. if(s_fDBCSSystem)
  732. {
  733. if(IsDBCSLeadByte( *m_szLine_Pos )) break;
  734. }
  735. if(m_szLine_Pos[0] != ' ' &&
  736. m_szLine_Pos[0] != '\t' )
  737. {
  738. break;
  739. }
  740. m_szLine_Pos++;
  741. }
  742. return true;
  743. }
  744. /*
  745. HHK::Reader::FindCharacter( CHAR ch, bool fSkip, bool fWrap ):
  746. Finds a character within a given Reader Stream. if fWrap is set it goes beyond End of Line characters
  747. If fSkip is set it instructs the routine to not only find the character, but also to skip it and
  748. return the first non space character.
  749. */
  750. bool HHK::Reader::FindCharacter( CHAR ch, bool fSkip, bool fWrap )
  751. {
  752. for(;;)
  753. {
  754. LPCSTR szMatch;
  755. while(IsEndOfLine())
  756. {
  757. if(fWrap == false) return false;
  758. if(GetLine() == false) return false;
  759. }
  760. szMatch = StrChr( m_szLine_Pos, ch );
  761. if(szMatch)
  762. {
  763. m_szLine_Pos = szMatch;
  764. if(fSkip) m_szLine_Pos++;
  765. break;
  766. }
  767. m_szLine_Pos = m_szLine_End; // Skip the whole line.
  768. }
  769. return fSkip ? FirstNonSpace( fWrap ) : true;
  770. }
  771. bool HHK::Reader::FindDblQuote ( bool fSkip, bool fWrap ) { return FindCharacter( '"', fSkip, fWrap ); }
  772. bool HHK::Reader::FindOpeningBrace( bool fSkip, bool fWrap ) { return FindCharacter( '<', fSkip, fWrap ); }
  773. bool HHK::Reader::FindClosingBrace( bool fSkip, bool fWrap ) { return FindCharacter( '>', fSkip, fWrap ); }
  774. /////////////////////////////////////////////////////////////////////////////
  775. /////////////////////////////////////////////////////////////////////////////
  776. //
  777. // We need to extract <Value> from "<Value>".
  778. //
  779. /////////////////////////////////////////////////////////////////////////////
  780. bool HHK::Reader::GetQuotedString( MPC::string& strString )
  781. {
  782. LPCSTR szPos;
  783. strString.erase();
  784. //
  785. // Skip past beginning quote.
  786. //
  787. if(FindDblQuote() == false) return false;
  788. for(;;)
  789. {
  790. szPos = m_szLine_Pos;
  791. //
  792. // Find ending quote of parameter value, but don't skip it.
  793. //
  794. if(FindDblQuote( false, false ))
  795. {
  796. strString.append( szPos, m_szLine_Pos - szPos );
  797. break;
  798. }
  799. else
  800. {
  801. strString.append( szPos, m_szLine_End - szPos );
  802. if(GetLine() == false) return false;
  803. }
  804. }
  805. //
  806. // Skip past ending quote.
  807. //
  808. return FindDblQuote();
  809. }
  810. /*
  811. bool HHK::Reader::GetValue( MPC::string& strName, MPC::string& strValue )
  812. We are after '<param name=', we need to extract <Name> and <Value> from '"<Name>" value="<Value>">' ... "
  813. portion of the line.
  814. returns: true - If syntax is correct and everything was as expected.
  815. false - Some unexpected syntactitc error occured.
  816. */
  817. bool HHK::Reader::GetValue( MPC::string& strName, MPC::string& strValue )
  818. {
  819. LPCSTR szPos;
  820. strValue.erase();
  821. if(GetQuotedString( strName ) == false) return false;
  822. //
  823. // Find parameter value.
  824. //
  825. for(;;)
  826. {
  827. while(IsEndOfLine())
  828. {
  829. if(GetLine() == false) return false;
  830. }
  831. szPos = StriStr( m_szLine_Pos, txtValue );
  832. if(szPos)
  833. {
  834. m_szLine_Pos = szPos + MAXSTRLEN(txtValue);
  835. break;
  836. }
  837. }
  838. if(GetQuotedString( strValue ) == false) return false;
  839. return FindClosingBrace();
  840. }
  841. /////////////////////////////////////////////////////////////////////////////
  842. //
  843. // We are after '<OBJECT', we need to extract <Type> from ' type="<Type>">'
  844. //
  845. /////////////////////////////////////////////////////////////////////////////
  846. bool HHK::Reader::GetType( MPC::string& strType )
  847. {
  848. LPCSTR szPos;
  849. strType.erase();
  850. //
  851. // Find type text.
  852. //
  853. for(;;)
  854. {
  855. while(IsEndOfLine())
  856. {
  857. if(GetLine() == false) return false;
  858. }
  859. szPos = StriStr( m_szLine_Pos, txtType );
  860. if(szPos)
  861. {
  862. m_szLine_Pos = szPos + MAXSTRLEN(txtValue);
  863. break;
  864. }
  865. }
  866. if(GetQuotedString( strType ) == false) return false;
  867. return FindClosingBrace();
  868. }
  869. /////////////////////////////////////////////////////////////////////////////
  870. /////////////////////////////////////////////////////////////////////////////
  871. /////////////////////////////////////////////////////////////////////////////
  872. /////////////////////////////////////////////////////////////////////////////
  873. /////////////////////////////////////////////////////////////////////////////
  874. HHK::Section* HHK::Reader::Parse()
  875. {
  876. MPC::string strName;
  877. MPC::string strValue;
  878. MPC::string strType;
  879. LPCSTR szPos;
  880. Section* section = NULL;
  881. Section* sectionCurrent = NULL;
  882. bool fComplete = false;
  883. for(;;)
  884. {
  885. if(m_fOpeningBraceSeen)
  886. {
  887. m_fOpeningBraceSeen = false;
  888. }
  889. else
  890. {
  891. if(FindOpeningBrace() == false) break;
  892. }
  893. if((szPos = ComparePrefix( m_szLine_Pos, txtParam )))
  894. {
  895. m_szLine_Pos = szPos;
  896. if(GetValue( strName, strValue ) == false) break;
  897. {
  898. if(sectionCurrent)
  899. {
  900. if(!StrColl( strName.c_str(), txtParamKeyword ))
  901. {
  902. sectionCurrent->m_strTitle = strValue;
  903. }
  904. else if(!StrColl( strName.c_str(), txtParamName ))
  905. {
  906. if(sectionCurrent->m_strTitle.length() == 0) // Title of the section.
  907. {
  908. sectionCurrent->m_strTitle = strValue;
  909. }
  910. else // Title of the entry.
  911. {
  912. Section::EntryIter it = sectionCurrent->m_lstEntries.insert( sectionCurrent->m_lstEntries.end() );
  913. it->m_strTitle = strValue;
  914. }
  915. }
  916. else if(!StrColl( strName.c_str(), txtParamLocal )) // URL of the entry.
  917. {
  918. Section::EntryIter it;
  919. if(sectionCurrent->m_lstEntries.size())
  920. {
  921. it = sectionCurrent->m_lstEntries.end();
  922. it--;
  923. }
  924. else
  925. {
  926. //
  927. // No title for this entry, so let's create it without title...
  928. //
  929. it = sectionCurrent->m_lstEntries.insert( sectionCurrent->m_lstEntries.end() );
  930. //
  931. // If it's the first entry, use the keyword as a title.
  932. //
  933. if(sectionCurrent->m_lstEntries.size())
  934. {
  935. it->m_strTitle = sectionCurrent->m_strTitle;
  936. }
  937. }
  938. if(m_strStorage.length())
  939. {
  940. MPC::string strFullUrl( m_strStorage );
  941. LPCSTR szValue = strValue.c_str();
  942. //
  943. // If the entry in the HHK is in the form: <file>::/<stream>, drop the last component of the storage base.
  944. //
  945. if(strValue.find( "::/" ) != strValue.npos)
  946. {
  947. LPCSTR szStart;
  948. LPCSTR szEnd;
  949. szStart = strFullUrl.c_str();
  950. szEnd = strrchr( szStart, '\\' );
  951. if(szEnd)
  952. {
  953. strFullUrl.resize( (szEnd - szStart) + 1 );
  954. }
  955. //
  956. // Handle the case for "MS-ITS:<file>::/<stream>"
  957. //
  958. szStart = strchr( szValue, ':' );
  959. if(szStart && szStart[1] != ':') szValue = szStart+1;
  960. }
  961. else if(strValue.find( ":/" ) != strValue.npos) // If it's a full URL (with a protocol), just add the value.
  962. {
  963. strFullUrl = "";
  964. }
  965. strFullUrl += szValue;
  966. it->m_lstUrl.push_back( strFullUrl );
  967. }
  968. else
  969. {
  970. it->m_lstUrl.push_back( strValue );
  971. }
  972. }
  973. else if(!StrColl( strName.c_str(), txtParamSeeAlso )) // See Also
  974. {
  975. if(sectionCurrent)
  976. {
  977. sectionCurrent->m_strSeeAlso = strValue;
  978. }
  979. }
  980. }
  981. }
  982. }
  983. else if((szPos = ComparePrefix( m_szLine_Pos, txtBeginList )))
  984. {
  985. m_szLine_Pos = szPos;
  986. m_iLevel++;
  987. if(FirstNonSpace() == false) break;
  988. }
  989. else if((szPos = ComparePrefix( m_szLine_Pos, txtEndList )))
  990. {
  991. m_szLine_Pos = szPos;
  992. m_iLevel--;
  993. if(FirstNonSpace() == false) break;
  994. }
  995. else if((szPos = ComparePrefix( m_szLine_Pos, txtBeginListItem )))
  996. {
  997. if(section)
  998. {
  999. if(m_iLevel == 1)
  1000. {
  1001. //
  1002. // Ok, the node is really closed.
  1003. //
  1004. // Since we have already read the opening brace for the NEXT node, set the flag.
  1005. //
  1006. m_fOpeningBraceSeen = true;
  1007. return section;
  1008. }
  1009. }
  1010. m_szLine_Pos = szPos;
  1011. if(FindClosingBrace() == false) break;
  1012. if(FindOpeningBrace() == false) break;
  1013. if((szPos = ComparePrefix( m_szLine_Pos, txtBeginObject )))
  1014. {
  1015. m_szLine_Pos = szPos;
  1016. if(GetType( strType ) == false) break;
  1017. //////////////////// New Node ////////////////////
  1018. if(!StrColl( strType.c_str(), txtSiteMapObject ))
  1019. {
  1020. if(m_iLevel == 1)
  1021. {
  1022. section = new Section(); if(section == NULL) break;
  1023. sectionCurrent = section;
  1024. }
  1025. else if(section)
  1026. {
  1027. sectionCurrent = new Section(); if(sectionCurrent == NULL) break;
  1028. section->m_lstSeeAlso.push_back( sectionCurrent );
  1029. }
  1030. fComplete = false; // Start of a section/subsection.
  1031. }
  1032. }
  1033. }
  1034. else if((szPos = ComparePrefix( m_szLine_Pos, txtEndObject )))
  1035. {
  1036. m_szLine_Pos = szPos;
  1037. //////////////////// End Node ////////////////////
  1038. if(m_iLevel == 1) // Normal section
  1039. {
  1040. //
  1041. // Ok, node complete, but it's possible to have a <UL> subnode, so wait before exiting.
  1042. //
  1043. }
  1044. else if(m_iLevel == 2) // See Also section
  1045. {
  1046. sectionCurrent = section;
  1047. }
  1048. fComplete = true; // End of a subsection.
  1049. }
  1050. }
  1051. if(section)
  1052. {
  1053. //
  1054. // End of File, but a section has already been parsed, so return it.
  1055. //
  1056. if(fComplete) return section;
  1057. delete section;
  1058. }
  1059. return NULL;
  1060. }
  1061. /////////////////////////////////////////////////////////////////////////////
  1062. /////////////////////////////////////////////////////////////////////////////
  1063. /////////////////////////////////////////////////////////////////////////////
  1064. HHK::Writer::Writer()
  1065. {
  1066. // CComPtr<MPC::FileStream> m_stream;
  1067. // CHAR m_rgBuf[HHK_BUF_SIZE];
  1068. m_szBuf_Pos = m_rgBuf; // LPSTR m_szBuf_Pos;
  1069. }
  1070. HHK::Writer::~Writer()
  1071. {
  1072. Close();
  1073. }
  1074. HRESULT HHK::Writer::Init( LPCWSTR szFile )
  1075. {
  1076. __HCP_FUNC_ENTRY( "HHK::Writer::Init" );
  1077. HRESULT hr;
  1078. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &m_stream ));
  1079. __MPC_EXIT_IF_METHOD_FAILS(hr, m_stream->InitForWrite( szFile ));
  1080. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtHeader ));
  1081. hr = S_OK;
  1082. __HCP_FUNC_CLEANUP;
  1083. __HCP_FUNC_EXIT(hr);
  1084. }
  1085. HRESULT HHK::Writer::Close()
  1086. {
  1087. __HCP_FUNC_ENTRY( "HHK::Writer::Close" );
  1088. HRESULT hr;
  1089. if(m_stream)
  1090. {
  1091. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtTail ));
  1092. __MPC_EXIT_IF_METHOD_FAILS(hr, FlushBuffer());
  1093. __MPC_EXIT_IF_METHOD_FAILS(hr, m_stream->Close());
  1094. }
  1095. hr = S_OK;
  1096. __HCP_FUNC_CLEANUP;
  1097. __HCP_FUNC_EXIT(hr);
  1098. }
  1099. /////////////////////////////////////////////////////////////////////////////
  1100. HRESULT HHK::Writer::FlushBuffer()
  1101. {
  1102. HRESULT hr;
  1103. ULONG cbWrite = (m_szBuf_Pos - m_rgBuf);
  1104. ULONG cbWrote;
  1105. if(m_stream)
  1106. {
  1107. if(cbWrite)
  1108. {
  1109. hr = m_stream->Write( m_szBuf_Pos = m_rgBuf, cbWrite, &cbWrote );
  1110. }
  1111. else
  1112. {
  1113. hr = S_OK;
  1114. }
  1115. }
  1116. else
  1117. {
  1118. hr = E_FAIL;
  1119. }
  1120. return hr;
  1121. }
  1122. HRESULT HHK::Writer::OutputLine( LPCSTR szLine )
  1123. {
  1124. HRESULT hr = S_OK;
  1125. if(szLine)
  1126. {
  1127. size_t iLen = strlen( szLine );
  1128. while(iLen)
  1129. {
  1130. size_t iCopy = min( iLen, Available() );
  1131. ::CopyMemory( m_szBuf_Pos, szLine, iCopy );
  1132. m_szBuf_Pos += iCopy;
  1133. szLine += iCopy;
  1134. iLen -= iCopy;
  1135. if(iLen)
  1136. {
  1137. if(FAILED(hr = FlushBuffer())) break;
  1138. }
  1139. }
  1140. }
  1141. return hr;
  1142. }
  1143. HRESULT HHK::Writer::OutputSection( Section* sec )
  1144. {
  1145. __HCP_FUNC_ENTRY( "HHK::Writer::OutputSection" );
  1146. HRESULT hr;
  1147. Section::SectionIterConst itSec;
  1148. Section* subsec;
  1149. Section::EntryIterConst itEntry;
  1150. Entry::UrlIterConst itUrl;
  1151. // BUG 135252 - Help Center Content: Broken link from Adapters topic on Help Index
  1152. // This is a UA specific tweak. The condition is present ONLY on single entry
  1153. // Keyword links coming from the DB, which means that their URI does not point
  1154. // inside a .CHM.
  1155. if(sec->m_lstEntries.size() != 0 &&
  1156. sec->m_lstSeeAlso.size() != 0 )
  1157. {
  1158. Section Sec1;
  1159. for(itEntry = sec->m_lstEntries.begin(); itEntry != sec->m_lstEntries.end(); itEntry++)
  1160. {
  1161. itUrl = itEntry->m_lstUrl.begin();
  1162. if(itUrl != itEntry->m_lstUrl.end())
  1163. {
  1164. Section* SubSec1;
  1165. __MPC_EXIT_IF_ALLOC_FAILS(hr, SubSec1, new Section);
  1166. SubSec1->m_strTitle = itEntry->m_strTitle;
  1167. SubSec1->m_lstEntries.push_back( *itEntry );
  1168. Sec1.m_lstSeeAlso.push_back( SubSec1 );
  1169. }
  1170. }
  1171. sec->MergeSeeAlso( Sec1 );
  1172. sec->m_lstEntries.clear();
  1173. }
  1174. // END Fix BUG 135252
  1175. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSection_Open ));
  1176. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Name ));
  1177. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( sec->m_strTitle.c_str() ));
  1178. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1179. sec->CleanEntries( sec->m_lstEntries );
  1180. for(itEntry = sec->m_lstEntries.begin(); itEntry != sec->m_lstEntries.end(); itEntry++)
  1181. {
  1182. const Entry& entry = *itEntry;
  1183. if(entry.m_strTitle.length())
  1184. {
  1185. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Name ));
  1186. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( entry.m_strTitle.c_str() ));
  1187. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1188. }
  1189. for(itUrl = entry.m_lstUrl.begin(); itUrl != entry.m_lstUrl.end(); itUrl++)
  1190. {
  1191. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Local ));
  1192. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( itUrl->c_str() ));
  1193. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1194. }
  1195. }
  1196. if ( sec->m_lstEntries.size() == 0 )
  1197. {
  1198. if(sec->m_strSeeAlso.length())
  1199. {
  1200. if (sec->m_strSeeAlso == sec->m_strTitle)
  1201. {
  1202. // Bug 278906: If this is a first level index entry, with no associated
  1203. // topic, then the See Also will be the same as the Title. Replace the
  1204. // See Also by a pointer to an HTM that asks the user to click on a
  1205. // lower level index entry.
  1206. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Local ));
  1207. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndexFirstLevel ));
  1208. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1209. }
  1210. else
  1211. {
  1212. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_SeeAlso ));
  1213. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( sec->m_strSeeAlso.c_str() ));
  1214. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1215. }
  1216. }
  1217. }
  1218. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSection_Close ));
  1219. ////////////////////
  1220. if(sec->m_lstSeeAlso.size())
  1221. {
  1222. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSubSection_Open ));
  1223. for(itSec = sec->m_lstSeeAlso.begin(); itSec != sec->m_lstSeeAlso.end(); itSec++)
  1224. {
  1225. subsec = *itSec;
  1226. subsec->CleanEntries( subsec->m_lstEntries );
  1227. if(subsec->m_strSeeAlso.length() == 0 &&
  1228. subsec->m_lstEntries.size() == 0 ) continue;
  1229. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1230. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSection_Open ));
  1231. if(subsec->m_strTitle.length())
  1232. {
  1233. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1234. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Name ));
  1235. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( subsec->m_strTitle.c_str() ));
  1236. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1237. }
  1238. if(subsec->m_strSeeAlso.length())
  1239. {
  1240. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1241. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_SeeAlso ));
  1242. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( subsec->m_strSeeAlso.c_str() ));
  1243. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1244. }
  1245. else
  1246. {
  1247. for(itEntry = subsec->m_lstEntries.begin(); itEntry != subsec->m_lstEntries.end(); itEntry++)
  1248. {
  1249. const Entry& entry = *itEntry;
  1250. if(entry.m_strTitle.length() == 0 ||
  1251. entry.m_lstUrl.size() == 0 ) continue;
  1252. if(entry.m_strTitle.length())
  1253. {
  1254. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1255. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Name ));
  1256. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( entry.m_strTitle.c_str() ));
  1257. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1258. }
  1259. for(itUrl = entry.m_lstUrl.begin(); itUrl != entry.m_lstUrl.end(); itUrl++)
  1260. {
  1261. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1262. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Local ));
  1263. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( itUrl->c_str() ));
  1264. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewParam_Close ));
  1265. }
  1266. }
  1267. }
  1268. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtIndent ));
  1269. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSection_Close ));
  1270. }
  1271. __MPC_EXIT_IF_METHOD_FAILS(hr, OutputLine( txtNewSubSection_Close ));
  1272. }
  1273. hr = S_OK;
  1274. __HCP_FUNC_CLEANUP;
  1275. __HCP_FUNC_EXIT(hr);
  1276. }
  1277. /////////////////////////////////////////////////////////////////////////////
  1278. /////////////////////////////////////////////////////////////////////////////
  1279. /////////////////////////////////////////////////////////////////////////////
  1280. HHK::Merger::Entity::Entity()
  1281. {
  1282. m_Section = NULL; // Section* m_Section;
  1283. }
  1284. HHK::Merger::Entity::~Entity()
  1285. {
  1286. if(m_Section)
  1287. {
  1288. delete m_Section;
  1289. }
  1290. }
  1291. void HHK::Merger::Entity::SetSection( HHK::Section* sec )
  1292. {
  1293. if(m_Section)
  1294. {
  1295. delete m_Section;
  1296. }
  1297. m_Section = sec;
  1298. }
  1299. HHK::Section* HHK::Merger::Entity::GetSection()
  1300. {
  1301. return m_Section;
  1302. }
  1303. HHK::Section* HHK::Merger::Entity::Detach()
  1304. {
  1305. HHK::Section* sec = m_Section;
  1306. m_Section = NULL;
  1307. return sec;
  1308. }
  1309. /////////////////////////////////////////////////////////////////////////////
  1310. HHK::Merger::FileEntity::FileEntity( LPCWSTR szFile )
  1311. {
  1312. m_strFile = szFile; // MPC::wstring m_strFile;
  1313. // Reader m_Input;
  1314. }
  1315. HHK::Merger::FileEntity::~FileEntity()
  1316. {
  1317. SetSection( NULL );
  1318. }
  1319. HRESULT HHK::Merger::FileEntity::Init()
  1320. {
  1321. return m_Input.Init( m_strFile.c_str() );
  1322. }
  1323. bool HHK::Merger::FileEntity::MoveNext()
  1324. {
  1325. HHK::Section* sec = m_Input.Parse();
  1326. SetSection( sec );
  1327. return sec != NULL;
  1328. }
  1329. long HHK::Merger::FileEntity::Size() const
  1330. {
  1331. return 0;
  1332. }
  1333. /////////////////////////////////////////////////////////////////////////////
  1334. bool HHK::Merger::DbEntity::CompareMatches::operator()( /*[in]*/ const HHK::Merger::DbEntity::match* left ,
  1335. /*[in]*/ const HHK::Merger::DbEntity::match* right ) const
  1336. {
  1337. int res = Reader::StrColl( left->strKeyword.c_str(), right->strKeyword.c_str() );
  1338. if(res == 0)
  1339. {
  1340. res = Reader::StrColl( left->strTitle.c_str(), right->strTitle.c_str() );
  1341. }
  1342. return (res < 0);
  1343. }
  1344. HHK::Merger::DbEntity::DbEntity( /*[in]*/ Taxonomy::Updater& updater, /*[in]*/ Taxonomy::WordSet& setCHM ) : m_updater( updater )
  1345. {
  1346. // Section::SectionList m_lst;
  1347. // Taxonomy::Updater& m_updater;
  1348. m_setCHM = setCHM; // Taxonomy::WordSet m_setCHM;
  1349. }
  1350. HHK::Merger::DbEntity::~DbEntity()
  1351. {
  1352. MPC::CallDestructorForAll( m_lst );
  1353. }
  1354. HRESULT HHK::Merger::DbEntity::Init()
  1355. {
  1356. __HCP_FUNC_ENTRY( "HHK::Merger::DbEntity::Init" );
  1357. HRESULT hr;
  1358. MatchList lstMatches;
  1359. MatchIter itMatches;
  1360. KeywordMap mapKeywords;
  1361. KeywordIterConst itKeywords;
  1362. TopicMap mapTopics;
  1363. SortMap mapSorted;
  1364. SortIter itSorted;
  1365. bool fFound;
  1366. //
  1367. // Load all the matches.
  1368. //
  1369. {
  1370. Taxonomy::RS_Matches* rs;
  1371. __MPC_EXIT_IF_METHOD_FAILS(hr, m_updater.GetMatches( &rs ));
  1372. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveFirst, &fFound ));
  1373. while(fFound)
  1374. {
  1375. if(rs->m_fHHK)
  1376. {
  1377. itMatches = lstMatches.insert( lstMatches.end() );
  1378. itMatches->ID_keyword = rs->m_ID_keyword;
  1379. itMatches->ID_topic = rs->m_ID_topic;
  1380. mapTopics[rs->m_ID_topic] = &(*itMatches);
  1381. }
  1382. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveNext, &fFound ));
  1383. }
  1384. }
  1385. //
  1386. // Load all the keywords.
  1387. //
  1388. {
  1389. Taxonomy::RS_Keywords* rs;
  1390. __MPC_EXIT_IF_METHOD_FAILS(hr, m_updater.GetKeywords( &rs ));
  1391. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveFirst, &fFound ));
  1392. while(fFound)
  1393. {
  1394. MPC::string strKeyword; ConvertToAnsi( strKeyword, rs->m_strKeyword );
  1395. mapKeywords[rs->m_ID_keyword] = strKeyword;
  1396. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveNext, &fFound ));
  1397. }
  1398. }
  1399. //
  1400. // Lookup the keyword's strings.
  1401. //
  1402. {
  1403. match* lastKeyword = NULL;
  1404. for(itMatches = lstMatches.begin(); itMatches != lstMatches.end(); itMatches++)
  1405. {
  1406. if(lastKeyword && lastKeyword->ID_keyword == itMatches->ID_keyword)
  1407. {
  1408. itMatches->strKeyword = lastKeyword->strKeyword;
  1409. }
  1410. else
  1411. {
  1412. itKeywords = mapKeywords.find( itMatches->ID_keyword );
  1413. if(itKeywords == mapKeywords.end())
  1414. {
  1415. ; // This should NOT happen...
  1416. }
  1417. else
  1418. {
  1419. itMatches->strKeyword = itKeywords->second;
  1420. }
  1421. lastKeyword = &(*itMatches);
  1422. }
  1423. }
  1424. }
  1425. //
  1426. // Lookup topics.
  1427. //
  1428. {
  1429. Taxonomy::RS_Topics* rs;
  1430. __MPC_EXIT_IF_METHOD_FAILS(hr, m_updater.GetTopics( &rs ));
  1431. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveFirst, &fFound ));
  1432. while(fFound)
  1433. {
  1434. match* elem;
  1435. if(rs->m_fValid__URI && (elem = mapTopics[rs->m_ID_topic]))
  1436. {
  1437. bool fSkip = false;
  1438. //
  1439. // If the link points to a CHM and it's one of those already merged, skip the topic.
  1440. //
  1441. {
  1442. CComBSTR bstrStorageName;
  1443. if(MPC::MSITS::IsCHM( rs->m_strURI.c_str(), &bstrStorageName ) && bstrStorageName)
  1444. {
  1445. LPCWSTR szEnd = wcsrchr( bstrStorageName, '\\' );
  1446. if(szEnd && m_setCHM.count( MPC::wstring( szEnd+1 ) ) > 0)
  1447. {
  1448. fSkip = true;
  1449. }
  1450. }
  1451. }
  1452. if(fSkip == false)
  1453. {
  1454. __MPC_EXIT_IF_METHOD_FAILS(hr, m_updater.ExpandURL( rs->m_strURI ));
  1455. ConvertToAnsi( elem->strTitle, rs->m_strTitle );
  1456. ConvertToAnsi( elem->strURI , rs->m_strURI );
  1457. }
  1458. }
  1459. __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveNext, &fFound ));
  1460. }
  1461. }
  1462. //
  1463. // Sort topics.
  1464. //
  1465. {
  1466. for(itMatches = lstMatches.begin(); itMatches != lstMatches.end(); itMatches++)
  1467. {
  1468. match* elem = &(*itMatches);
  1469. //
  1470. // We keep a one-to-one association between ID_topic and match, in order to resolve the Title/URI of a keyword.
  1471. // However, there can be multiple keywords pointing to the same topic.
  1472. // So we need to copy title/URI from the element in the one-to-one association to all the others.
  1473. //
  1474. if(elem->strTitle.length() == 0 ||
  1475. elem->strURI .length() == 0 )
  1476. {
  1477. match* elem2 = mapTopics[elem->ID_topic];
  1478. if(elem2 && elem2 != elem)
  1479. {
  1480. elem->strTitle = elem2->strTitle;
  1481. elem->strURI = elem2->strURI ;
  1482. }
  1483. }
  1484. if(elem->strKeyword.length() == 0) continue;
  1485. if(elem->strTitle .length() == 0) continue;
  1486. if(elem->strURI .length() == 0) continue;
  1487. mapSorted[elem] = elem;
  1488. }
  1489. }
  1490. //
  1491. // Generate sections.
  1492. //
  1493. {
  1494. HHK::Section* sec = NULL;
  1495. HHK::Section* secsub = NULL;
  1496. HHK::Section::EntryIter it;
  1497. MPC::string strBuffer;
  1498. for(itSorted = mapSorted.begin(); itSorted != mapSorted.end(); itSorted++)
  1499. {
  1500. match* elem = itSorted->first;
  1501. //
  1502. // Escape all the values, before generating the sections.
  1503. //
  1504. ReplaceCharactersWithEntity( elem->strKeyword, strBuffer );
  1505. ReplaceCharactersWithEntity( elem->strTitle , strBuffer );
  1506. ReplaceCharactersWithEntity( elem->strURI , strBuffer );
  1507. if(sec == NULL || sec->m_strTitle != elem->strKeyword)
  1508. {
  1509. if(sec)
  1510. {
  1511. m_lst.push_back( sec );
  1512. }
  1513. __MPC_EXIT_IF_ALLOC_FAILS(hr, sec, new HHK::Section());
  1514. secsub = NULL;
  1515. it = sec->m_lstEntries.insert( sec->m_lstEntries.end() );
  1516. sec->m_strTitle = elem->strKeyword;
  1517. it->m_strTitle = elem->strTitle;
  1518. it->m_lstUrl.push_back( elem->strURI );
  1519. }
  1520. else
  1521. {
  1522. if(secsub == NULL)
  1523. {
  1524. secsub = sec;
  1525. __MPC_EXIT_IF_ALLOC_FAILS(hr, sec, new HHK::Section());
  1526. sec->m_lstSeeAlso.push_back( secsub );
  1527. sec->m_strTitle = elem->strKeyword;
  1528. sec->m_strSeeAlso = elem->strKeyword;
  1529. secsub->m_strTitle = it->m_strTitle;
  1530. }
  1531. if(secsub->m_strTitle != elem->strTitle)
  1532. {
  1533. __MPC_EXIT_IF_ALLOC_FAILS(hr, secsub, new HHK::Section());
  1534. sec->m_lstSeeAlso.push_back( secsub );
  1535. secsub->m_strTitle = elem->strTitle;
  1536. it = secsub->m_lstEntries.insert( secsub->m_lstEntries.end() );
  1537. it->m_strTitle = elem->strTitle;
  1538. }
  1539. it->m_lstUrl.push_back( elem->strURI );
  1540. }
  1541. }
  1542. if(sec)
  1543. {
  1544. m_lst.push_back( sec );
  1545. }
  1546. }
  1547. hr = S_OK;
  1548. __HCP_FUNC_CLEANUP;
  1549. __HCP_FUNC_EXIT(hr);
  1550. }
  1551. bool HHK::Merger::DbEntity::MoveNext()
  1552. {
  1553. Section::SectionIterConst it = m_lst.begin();
  1554. if(it != m_lst.end())
  1555. {
  1556. SetSection( *it );
  1557. m_lst.erase( it );
  1558. return true;
  1559. }
  1560. else
  1561. {
  1562. SetSection( NULL );
  1563. return false;
  1564. }
  1565. }
  1566. long HHK::Merger::DbEntity::Size() const
  1567. {
  1568. return m_lst.size();
  1569. }
  1570. /////////////////////////////////////////////////////////////////////////////
  1571. class Compare
  1572. {
  1573. public:
  1574. bool operator()( /*[in]*/ HHK::Section* const &left, /*[in]*/ HHK::Section* const &right ) const
  1575. {
  1576. return HHK::Reader::StrColl( left->m_strTitle.c_str(), right->m_strTitle.c_str() ) < 0;
  1577. }
  1578. };
  1579. HHK::Merger::SortingFileEntity::SortingFileEntity( LPCWSTR szFile ) : m_in( szFile )
  1580. {
  1581. // Section::SectionList m_lst;
  1582. // FileEntity m_in;
  1583. }
  1584. HHK::Merger::SortingFileEntity::~SortingFileEntity()
  1585. {
  1586. MPC::CallDestructorForAll( m_lst );
  1587. }
  1588. HRESULT HHK::Merger::SortingFileEntity::Init()
  1589. {
  1590. HRESULT hr;
  1591. if(SUCCEEDED(hr = m_in.Init()))
  1592. {
  1593. Compare Pr;
  1594. SectionVec vec;
  1595. SectionIter itLast;
  1596. SectionIter it;
  1597. Section* secLast = NULL;
  1598. Section* sec;
  1599. //
  1600. // Parse the whole file.
  1601. //
  1602. while(m_in.MoveNext())
  1603. {
  1604. vec.push_back( m_in.Detach() );
  1605. }
  1606. //
  1607. // Sort all the sections.
  1608. //
  1609. std::sort( vec.begin(), vec.end(), Pr );
  1610. //
  1611. // Walk through the sections, looking for duplicate keywords to merge.
  1612. //
  1613. // Each section encountered is not added immediately, but kept in "secLast" for comparison with the next one.
  1614. //
  1615. for(it=vec.begin(); it!=vec.end();)
  1616. {
  1617. sec = *it;
  1618. if(secLast)
  1619. {
  1620. if(Reader::StrColl( sec->m_strTitle.c_str(), secLast->m_strTitle.c_str() ) == 0)
  1621. {
  1622. Section::SectionList lst;
  1623. //
  1624. // Collate all the sections with the same keyword in a list and merge them.
  1625. //
  1626. lst.push_back( secLast );
  1627. while(it != vec.end() && Reader::StrColl( (*it)->m_strTitle.c_str(), secLast->m_strTitle.c_str() ) == 0)
  1628. {
  1629. lst.push_back( *it++ );
  1630. }
  1631. sec = Merger::MergeSections( lst );
  1632. if(sec == NULL)
  1633. {
  1634. hr = E_OUTOFMEMORY;
  1635. break;
  1636. }
  1637. //
  1638. // Queue the merged section and loop.
  1639. //
  1640. m_lst.push_back( sec );
  1641. secLast = NULL;
  1642. continue;
  1643. }
  1644. else
  1645. {
  1646. m_lst.push_back( secLast ); *itLast = NULL;
  1647. }
  1648. }
  1649. itLast = it;
  1650. secLast = sec; it++;
  1651. }
  1652. if(secLast)
  1653. {
  1654. m_lst.push_back( secLast ); *itLast = NULL;
  1655. }
  1656. MPC::CallDestructorForAll( vec );
  1657. }
  1658. return hr;
  1659. }
  1660. bool HHK::Merger::SortingFileEntity::MoveNext()
  1661. {
  1662. Section::SectionIterConst it = m_lst.begin();
  1663. if(it != m_lst.end())
  1664. {
  1665. SetSection( *it );
  1666. m_lst.erase( it );
  1667. return true;
  1668. }
  1669. else
  1670. {
  1671. SetSection( NULL );
  1672. return false;
  1673. }
  1674. }
  1675. long HHK::Merger::SortingFileEntity::Size() const
  1676. {
  1677. return m_lst.size();
  1678. }
  1679. /////////////////////////////////////////////////////////////////////////////
  1680. HHK::Merger::Merger()
  1681. {
  1682. // EntityList m_lst;
  1683. // EntityList m_lstSelected;
  1684. m_SectionTemp = NULL; // Section* m_SectionTemp;
  1685. }
  1686. HHK::Merger::~Merger()
  1687. {
  1688. MPC::CallDestructorForAll( m_lst );
  1689. if(m_SectionTemp)
  1690. {
  1691. delete m_SectionTemp;
  1692. }
  1693. }
  1694. HRESULT HHK::Merger::AddFile( Entity* ent, bool fIgnoreMissing )
  1695. {
  1696. HRESULT hr;
  1697. if(ent == NULL)
  1698. {
  1699. return E_OUTOFMEMORY;
  1700. }
  1701. if(SUCCEEDED(hr = ent->Init()))
  1702. {
  1703. m_lst.push_back( ent );
  1704. }
  1705. else
  1706. {
  1707. delete ent;
  1708. if(fIgnoreMissing)
  1709. {
  1710. hr = S_OK;
  1711. }
  1712. }
  1713. return hr;
  1714. }
  1715. HHK::Section* HHK::Merger::MergeSections( Section::SectionList& lst )
  1716. {
  1717. HHK::Section* secMerged = new HHK::Section();
  1718. if(secMerged)
  1719. {
  1720. if(lst.size())
  1721. {
  1722. HHK::Section* sec = *(lst.begin());
  1723. secMerged->m_strTitle = sec->m_strTitle;
  1724. }
  1725. //
  1726. // Move than one section with the same title, need to merge...
  1727. //
  1728. for(HHK::Section::SectionIter it=lst.begin(); it!=lst.end(); it++)
  1729. {
  1730. HHK::Section* sec = *it;
  1731. for(HHK::Section::EntryIterConst itEntry = sec->m_lstEntries.begin(); itEntry != sec->m_lstEntries.end(); itEntry++)
  1732. {
  1733. secMerged->MergeURLs( *itEntry );
  1734. }
  1735. if(sec ->m_strSeeAlso.length() > 0 &&
  1736. secMerged->m_strSeeAlso.length() == 0 )
  1737. {
  1738. secMerged->m_strSeeAlso = sec->m_strSeeAlso;
  1739. }
  1740. ////////////////////
  1741. secMerged->MergeSeeAlso( *sec );
  1742. }
  1743. }
  1744. return secMerged;
  1745. }
  1746. bool HHK::Merger::MoveNext()
  1747. {
  1748. HHK::Section* secLowest = NULL;
  1749. EntityIterConst it;
  1750. EntityIterConst it2;
  1751. if(m_SectionTemp)
  1752. {
  1753. delete m_SectionTemp;
  1754. m_SectionTemp = NULL;
  1755. }
  1756. //
  1757. // If this is the first round ever, select all the entities.
  1758. //
  1759. if(m_lstSelected.size() == 0)
  1760. {
  1761. m_lstSelected = m_lst;
  1762. }
  1763. //
  1764. // First of all, advance all the entity selected in the previous round.
  1765. //
  1766. for(it=m_lstSelected.begin(); it!=m_lstSelected.end(); it++)
  1767. {
  1768. Entity* ent = *it;
  1769. if(ent->MoveNext() == false)
  1770. {
  1771. //
  1772. // End of File for this entity, remove it from the system.
  1773. //
  1774. delete ent;
  1775. it2 = std::find( m_lst.begin(), m_lst.end(), ent );
  1776. if(it2 != m_lst.end())
  1777. {
  1778. m_lst.erase( it2 );
  1779. }
  1780. continue;
  1781. }
  1782. }
  1783. m_lstSelected.clear();
  1784. //
  1785. // No more entities, abort.
  1786. //
  1787. if(m_lst.size() == 0) return false;
  1788. //
  1789. // Select a section with the lowest title.
  1790. //
  1791. for(it=m_lst.begin(); it!=m_lst.end(); it++)
  1792. {
  1793. Entity* ent = *it;
  1794. HHK::Section* sec = ent->GetSection();
  1795. if(secLowest == NULL || Reader::StrColl( sec->m_strTitle.c_str(), secLowest->m_strTitle.c_str() ) < 0)
  1796. {
  1797. secLowest = sec;
  1798. }
  1799. }
  1800. //
  1801. // Find all the sections with the lowest title.
  1802. //
  1803. for(it=m_lst.begin(); it!=m_lst.end(); it++)
  1804. {
  1805. Entity* ent = *it;
  1806. HHK::Section* sec = ent->GetSection();
  1807. if(Reader::StrColl( sec->m_strTitle.c_str(), secLowest->m_strTitle.c_str() ) == 0)
  1808. {
  1809. m_lstSelected.push_back( ent );
  1810. }
  1811. }
  1812. if(m_lstSelected.size() > 1)
  1813. {
  1814. Section::SectionList lst;
  1815. for(it=m_lstSelected.begin(); it!=m_lstSelected.end(); it++)
  1816. {
  1817. HHK::Section* sec = (*it)->GetSection();
  1818. lst.push_back( sec );
  1819. }
  1820. m_SectionTemp = MergeSections( lst );
  1821. }
  1822. return true;
  1823. }
  1824. HHK::Section* HHK::Merger::GetSection()
  1825. {
  1826. if(m_SectionTemp)
  1827. {
  1828. return m_SectionTemp;
  1829. }
  1830. if(m_lstSelected.size())
  1831. {
  1832. return (*m_lstSelected.begin())->GetSection();
  1833. }
  1834. return NULL;
  1835. }
  1836. long HHK::Merger::Size() const
  1837. {
  1838. long lTotal = 0;
  1839. EntityIterConst it;
  1840. for(it=m_lst.begin(); it!=m_lst.end(); it++)
  1841. {
  1842. lTotal += (*it)->Size();
  1843. }
  1844. return lTotal;
  1845. }
  1846. HRESULT HHK::Merger::PrepareMergedHhk( Writer& writer ,
  1847. Taxonomy::Updater& updater ,
  1848. Taxonomy::WordSet& setCHM ,
  1849. MPC::WStringList& lst ,
  1850. LPCWSTR szOutputHHK )
  1851. {
  1852. __HCP_FUNC_ENTRY( "HHK::Merger::PrepareMergedHhk" );
  1853. HRESULT hr;
  1854. MPC::WStringIter it;
  1855. //
  1856. // Enumerate all the HHKs to merge.
  1857. //
  1858. for(it=lst.begin(); it!=lst.end(); it++)
  1859. {
  1860. __MPC_EXIT_IF_METHOD_FAILS(hr, AddFile( new SortingFileEntity( it->c_str() ) ));
  1861. }
  1862. //// Keyword are not merged in the HHK.
  1863. //// __MPC_EXIT_IF_METHOD_FAILS(hr, AddFile( new DbEntity( updater, setCHM ) ));
  1864. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MakeDir( szOutputHHK ));
  1865. __MPC_EXIT_IF_METHOD_FAILS(hr, writer.Init( szOutputHHK ));
  1866. hr = S_OK;
  1867. __HCP_FUNC_CLEANUP;
  1868. __HCP_FUNC_EXIT(hr);
  1869. }
  1870. HRESULT HHK::Merger::PrepareSortingOfHhk( HHK::Writer& writer ,
  1871. LPCWSTR szInputHHK ,
  1872. LPCWSTR szOutputHHK )
  1873. {
  1874. __HCP_FUNC_ENTRY( "HHK::Merger::PrepareSortingOfHhk" );
  1875. HRESULT hr;
  1876. __MPC_EXIT_IF_METHOD_FAILS(hr, AddFile( new SortingFileEntity( szInputHHK ) ));
  1877. __MPC_EXIT_IF_METHOD_FAILS(hr, writer.Init( szOutputHHK ));
  1878. hr = S_OK;
  1879. __HCP_FUNC_CLEANUP;
  1880. __HCP_FUNC_EXIT(hr);
  1881. }