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.

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