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.

3676 lines
98 KiB

  1. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // File: toc.cpp
  4. // Author: Donald Drake
  5. // Purpose: Implements classes to support the table of contents
  6. #include "header.h"
  7. #include "stdio.h"
  8. #include "string.h"
  9. #ifdef HHCTRL
  10. #include "parserhh.h"
  11. #else
  12. #include "windows.h"
  13. #include "parser.h"
  14. #endif
  15. #include "collect.h"
  16. #include "hhtypes.h"
  17. #include "wwheel.h"
  18. #include "toc.h"
  19. #include "fts.h"
  20. #include "subfile.h"
  21. #include "fs.h"
  22. #include "sysnames.h"
  23. #include "highlite.h"
  24. #include "hhfinder.h"
  25. #include "csubset.h"
  26. #include "hherror.h"
  27. // NormalizeUrlInPlace
  28. #include "util.h"
  29. #include "subset.h"
  30. // typed -- just use ANSI for now
  31. #undef _tcsicmp
  32. #undef _tcstok
  33. #define _tcsicmp strcmpi
  34. #define _tcstok StrToken
  35. #ifdef _DEBUG
  36. #undef THIS_FILE
  37. static const char THIS_FILE[] = __FILE__;
  38. #endif
  39. // Persist keys. No need for these to be localized so I place them here.
  40. //
  41. const char g_szFTSKey[] = "ssv1\\FTS";
  42. const char g_szIndexKey[] = "ssv1\\Index";
  43. const char g_szTOCKey[] = "ssv1\\TOC";
  44. const char g_szUDSKey[] = "ssv2\\UDS"; // User Defined Subsets
  45. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  46. // global helper functions
  47. CExCollection* g_pCurrentCollection = NULL;
  48. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  49. // CExCollection implementation
  50. CExCollection::CExCollection(CHmData* phmData, const CHAR* pszFile, BOOL bSingleTitle)
  51. {
  52. m_pstate = new CState(pszFile);
  53. m_phmData = phmData;
  54. m_bSingleTitle = bSingleTitle;
  55. m_pHeadTitles = NULL;
  56. m_csFile = pszFile;
  57. m_pFullTextSearch = NULL;
  58. m_pSearchHighlight = NULL;
  59. m_pDatabase = NULL;
  60. m_szWordWheelPathname = NULL;
  61. m_dwCurrSlot = 0;
  62. m_pCurrTitle = NULL;
  63. m_pSubSets = NULL;
  64. m_pMasterTitle = NULL ; // HH BUG 2428: Always initialize your variables!!!
  65. m_dwLastSlot = 0;
  66. m_pSSList = NULL;
  67. for (int i = 0; i < MAX_OPEN_TITLES; i++)
  68. m_MaxOpenTitles[i] = NULL;
  69. if (! bSingleTitle )
  70. {
  71. //
  72. // If this is ever not the case then we have a situation where we are trying to init more than a single
  73. // collection. This is a very bad thing and must be avoided.
  74. //
  75. ASSERT(g_pCurrentCollection == NULL);
  76. g_pCurrentCollection = NULL;
  77. }
  78. m_pCSlt = NULL;
  79. }
  80. CExCollection::~CExCollection()
  81. {
  82. if ( m_phmData->m_sysflags.fDoSS && m_pSSList )
  83. {
  84. m_pSSList->PersistSubsets(this);
  85. delete m_pSSList;
  86. }
  87. if( m_pDatabase )
  88. delete m_pDatabase;
  89. if (m_pFullTextSearch)
  90. delete m_pFullTextSearch;
  91. if ( m_pSearchHighlight )
  92. delete m_pSearchHighlight;
  93. CExTitle *p, *pNext;
  94. p = m_pHeadTitles;
  95. while (p)
  96. {
  97. pNext = p->GetNext();
  98. delete p;
  99. p = pNext;
  100. }
  101. if (m_Collection.IsDirty())
  102. m_Collection.Save();
  103. m_Collection.Close();
  104. if( m_szWordWheelPathname ) {
  105. delete [] (CHAR*) m_szWordWheelPathname;
  106. m_szWordWheelPathname = NULL;
  107. }
  108. // Persist subset selections.
  109. //
  110. #if 0
  111. CSubSet* pSS;
  112. #endif
  113. if ( m_pSubSets )
  114. {
  115. #if 0
  116. if ( (pSS = m_pSubSets->GetFTSSubset()) && SUCCEEDED(m_pstate->Open(g_szFTSKey,STGM_WRITE)) )
  117. {
  118. m_pstate->Write(pSS->m_cszSubSetName, strlen(pSS->m_cszSubSetName)+1);
  119. m_pstate->Close();
  120. }
  121. if ( (pSS = m_pSubSets->GetIndexSubset()) && SUCCEEDED(m_pstate->Open(g_szIndexKey,STGM_WRITE)) )
  122. {
  123. m_pstate->Write(pSS->m_cszSubSetName, strlen(pSS->m_cszSubSetName)+1);
  124. m_pstate->Close();
  125. }
  126. if ( (pSS = m_pSubSets->GetTocSubset()) && SUCCEEDED(m_pstate->Open(g_szTOCKey,STGM_WRITE)) )
  127. {
  128. m_pstate->Write(pSS->m_cszSubSetName, strlen(pSS->m_cszSubSetName)+1);
  129. m_pstate->Close();
  130. }
  131. #ifdef _DEBUG
  132. /* Output all the user defined subsets to the state store.
  133. *******************/
  134. int nKey;
  135. char buf[5];
  136. CStr cszKey;
  137. extern const char txtSSInclusive[]; // "Inclusive";
  138. extern const char txtSSExclusive[]; // "Exclusive";
  139. static const int MAX_PARAM = 4096;
  140. static const char txtSSConvString[] = "%s:%s:%s"; // Exclusive|Inclusive:SetName:TypeName
  141. CMem memParam( MAX_PARAM );
  142. CHAR* pszParam = (CHAR*)memParam; // for notational convenience
  143. for(int i=0; i<m_pSubSets->HowManySubSets(); i++ )
  144. {
  145. pSS = m_pSubSets->GetSubSet(i);
  146. if ( pSS->m_bPredefined )
  147. continue;
  148. int type = pSS->GetFirstExcITinSubSet();
  149. nKey=1;
  150. while (type != -1 && pSS->m_pIT->GetInfoTypeName(type) && (type <= pSS->m_pIT->HowManyInfoTypes()))
  151. {
  152. // even though we don't define exclusive filters, for user defined, it will be
  153. // here when we do.
  154. wsprintf(pszParam, txtSSConvString, txtSSExclusive,
  155. pSS->m_cszSubSetName.psz,
  156. pSS->m_pIT->GetInfoTypeName(type) );
  157. cszKey = g_szUDSKey;
  158. wsprintf(buf,"%d",nKey++);
  159. cszKey += buf;
  160. if ( SUCCEEDED( m_pstate->Open(cszKey, STGM_WRITE)) )
  161. m_pstate->Write(pszParam, strlen(pszParam)+1);
  162. m_pstate->Close();
  163. type = pSS->GetNextExcITinSubSet();
  164. }
  165. type = pSS->GetFirstIncITinSubSet();
  166. nKey=1;
  167. while (type != -1 && pSS->m_pIT->GetInfoTypeName(type) && (type <= pSS->m_pIT->HowManyInfoTypes()))
  168. {
  169. wsprintf(pszParam, txtSSConvString, txtSSInclusive,
  170. pSS->m_cszSubSetName.psz,
  171. pSS->m_pIT->GetInfoTypeName(type) );
  172. cszKey = g_szUDSKey;
  173. wsprintf(buf,"%d",nKey++);
  174. cszKey += buf;
  175. if ( SUCCEEDED( m_pstate->Open(cszKey, STGM_WRITE)) )
  176. m_pstate->Write(pszParam, strlen(pszParam)+1);
  177. m_pstate->Close();
  178. type = pSS->GetNextIncITinSubSet();
  179. }
  180. }
  181. #endif
  182. #endif
  183. delete m_pSubSets;
  184. }
  185. if (m_pCSlt)
  186. delete m_pCSlt; // leak fix
  187. if ( m_pstate )
  188. delete m_pstate;
  189. if (! m_bSingleTitle )
  190. g_pCurrentCollection = NULL;
  191. }
  192. /* The FullPath parameter is the name of a directory. The directory is suppose to contain files
  193. to add to the collection.
  194. */
  195. #ifdef CHIINDEX
  196. #include <io.h>
  197. BOOL CExCollection::InitCollection( const TCHAR * FullPath, const TCHAR * szMasterChmFn )
  198. {
  199. char szExt[2][5] = {".chm", ".chi"};
  200. BOOL ret = FALSE;
  201. CStr filespec;
  202. long hSrch;
  203. struct _finddata_t fd_t;
  204. CStr szAdd;
  205. HRESULT hr;
  206. m_pMasterTitle = NULL;
  207. for(int i=0; i<2; i++)
  208. {
  209. filespec = FullPath;
  210. filespec += "\\*";
  211. filespec += szExt[i];
  212. if ( (hSrch = _findfirst( filespec, &fd_t ) ) == -1 )
  213. continue;
  214. else
  215. ret = TRUE;
  216. do
  217. {
  218. CExTitle *pExTitle = NULL;
  219. szAdd = FullPath;
  220. szAdd += "\\";
  221. szAdd += fd_t.name;
  222. // only check for the existance of a index for chm files
  223. if (0==i)
  224. {
  225. CFileSystem* pFileSystem = new CFileSystem;
  226. pFileSystem->Init();
  227. if (!(SUCCEEDED(pFileSystem->Open( szAdd ))))
  228. {
  229. delete pFileSystem;
  230. continue;
  231. }
  232. CSubFileSystem* pSubFileSystem = new CSubFileSystem(pFileSystem);
  233. hr = pSubFileSystem->OpenSub("$WWKeywordLinks\\btree");
  234. if (FAILED(hr))
  235. {
  236. hr = pSubFileSystem->OpenSub("$WWAssociativeLinks\\btree");
  237. if (FAILED(hr))
  238. {
  239. pFileSystem->Close();
  240. delete pFileSystem;
  241. delete pSubFileSystem;
  242. continue;
  243. }
  244. }
  245. delete pSubFileSystem;
  246. pFileSystem->Close();
  247. delete pFileSystem;
  248. }
  249. pExTitle = new CExTitle( szAdd , this );
  250. pExTitle->SetNext(m_pHeadTitles);
  251. m_pHeadTitles = pExTitle;
  252. if ( (m_pMasterTitle == NULL) && (strnicmp( szMasterChmFn, fd_t.name, strlen(szMasterChmFn)) == 0) )
  253. {
  254. m_pMasterTitle = m_pHeadTitles;
  255. m_phmData->SetCompiledFile(szAdd);
  256. }
  257. m_Collection.IncrementRefTitleCount();
  258. } while( _findnext( hSrch, &fd_t ) == 0 );
  259. }
  260. if ( (ret == TRUE) && (m_pMasterTitle == NULL) )
  261. {
  262. m_pMasterTitle = m_pHeadTitles;
  263. m_phmData->SetCompiledFile(szAdd);
  264. }
  265. return ret;
  266. }
  267. #endif
  268. BOOL CExCollection::InitCollection()
  269. {
  270. if (m_bSingleTitle)
  271. {
  272. m_pHeadTitles = new CExTitle(m_csFile, this);
  273. m_pMasterTitle = m_pHeadTitles;
  274. m_Collection.IncrementRefTitleCount();
  275. GetMergedTitles(m_pHeadTitles);
  276. CStr cszCompiledFile;
  277. const CHAR* pszFilePortion = GetCompiledName(m_csFile, &cszCompiledFile);
  278. m_phmData->SetCompiledFile(cszCompiledFile);
  279. #ifdef DUMPTOC
  280. m_fh = fopen("c:\\toc_dump.txt", "w");
  281. m_bRoot = TRUE;
  282. m_dwLevel = 0;
  283. CTreeNode *pNode = GetRootNode();
  284. DumpNode(&pNode);
  285. fclose(m_fh);
  286. #endif
  287. }
  288. else
  289. {
  290. m_Collection.ConfirmTitles();
  291. m_Collection.m_bFailNoFile = TRUE;
  292. if (m_Collection.Open(m_csFile) != F_OK)
  293. return FALSE;
  294. // Create an CExTitle for each referanced title
  295. LANGID LangId;
  296. CHAR* pszTitle;
  297. CTitle *pTitle;
  298. CFolder *p;
  299. LISTITEM *pItem;
  300. CExTitle *pExTitle;
  301. m_pCSlt = new CSlotLookupTable(); // start a new slot lookup table.
  302. pItem = m_Collection.m_RefTitles.First();
  303. while (pItem)
  304. {
  305. p = (CFolder *)pItem->pItem;
  306. pszTitle = p->GetTitle() + 1;
  307. LangId = p->GetLanguage();
  308. // check if extitle already exist, title referanced twice in the collection
  309. if ( (pExTitle = FindTitle(pszTitle, LangId)) )
  310. {
  311. m_Collection.DecrementRefTitleCount();
  312. pItem = m_Collection.m_RefTitles.Next(pItem);
  313. p->SetExTitlePtr(pExTitle);
  314. continue;
  315. }
  316. // find the title
  317. pTitle = m_Collection.FindTitle(pszTitle, LangId);
  318. if (pTitle == NULL)
  319. {
  320. m_Collection.DecrementRefTitleCount();
  321. pItem = m_Collection.m_RefTitles.Next(pItem);
  322. continue;
  323. }
  324. //create CExTitle
  325. pExTitle = new CExTitle(pTitle, m_Collection.GetColNo(), this);
  326. if (ValidateTitle(pExTitle) == FALSE)
  327. {
  328. pItem = m_Collection.m_RefTitles.Next(pItem);
  329. continue;
  330. }
  331. // add the title
  332. pExTitle->SetNext(m_pHeadTitles);
  333. m_pHeadTitles = pExTitle;
  334. // Wire up the CFolder to the CExTitle, also, generate the Title Hash identifier.
  335. //
  336. p->SetExTitlePtr(pExTitle);
  337. char szBuf[20];
  338. char szID[MAX_PATH + 20];
  339. Itoa(p->GetLanguage(), szBuf);
  340. strcpy(szID, p->GetTitle()+1); // Don't hash the '='
  341. strcat(szID, szBuf);
  342. pExTitle->m_dwHash = HashFromSz(szID);
  343. m_pCSlt->AddValue(p);
  344. if (pExTitle->GetUsedLocation()->bSupportsMerge)
  345. {
  346. GetMergedTitles(pExTitle);
  347. }
  348. pItem = m_Collection.m_RefTitles.Next(pItem);
  349. }
  350. //
  351. // check for a master chm
  352. //
  353. CHAR* pszName;
  354. LANGID LangId2;
  355. if (m_Collection.GetMasterCHM(&pszName, &LangId2))
  356. {
  357. m_pMasterTitle = FindTitle(pszName, LangId2);
  358. }
  359. if (!m_pMasterTitle)
  360. {
  361. // default to first title
  362. m_pMasterTitle = GetFirstTitle();
  363. }
  364. if (!m_pMasterTitle)
  365. return FALSE;
  366. g_pCurrentCollection = this;
  367. m_pCSlt->SortAndAssignSlots(); // Complete construction of the slot lookup table.
  368. while (TRUE)
  369. {
  370. if (m_pMasterTitle->OpenTitle() == FALSE)
  371. {
  372. m_pMasterTitle = m_pMasterTitle->GetNext();
  373. if (m_pMasterTitle == NULL)
  374. return FALSE;
  375. continue;
  376. }
  377. CStr cszCompiledFile;
  378. GetCompiledName(m_pMasterTitle->GetPathName(), &cszCompiledFile);
  379. m_phmData->SetCompiledFile(cszCompiledFile);
  380. return TRUE;
  381. }
  382. }
  383. return TRUE;
  384. }
  385. void CExCollection::InitStructuralSubsets(void)
  386. {
  387. if ( m_bSingleTitle )
  388. {
  389. m_phmData->m_sysflags.fDoSS = 0;
  390. return;
  391. }
  392. if ( m_phmData->m_sysflags.fDoSS )
  393. {
  394. // Initilize the structural subset list and the "new" and "entire contents" subsets.
  395. //
  396. CStructuralSubset* pSS;
  397. CHAR szBuf[50];
  398. m_pSSList = new CSSList;
  399. strncpy(szBuf,GetStringResource(IDS_ADVSEARCH_SEARCHIN_ENTIRE), sizeof(szBuf));
  400. szBuf[49] = 0;
  401. pSS = new CStructuralSubset(szBuf);
  402. pSS->SetEntire();
  403. pSS->SetReadOnly();
  404. m_pSSList->AddSubset(pSS);
  405. m_pSSList->SetEC(pSS);
  406. strncpy(szBuf,GetStringResource(IDS_NEW), sizeof(szBuf));
  407. szBuf[49] = 0;
  408. pSS = new CStructuralSubset(szBuf);
  409. pSS->SetEmpty();
  410. pSS->SetReadOnly();
  411. m_pSSList->AddSubset(pSS);
  412. m_pSSList->SetNew(pSS);
  413. m_pSSList->RestoreSubsets(this, szBuf);
  414. m_pSSList->ReadPreDefinedSubsets(this, szBuf);
  415. //
  416. // If a subset has been selected via SetGlobalProperties() via HH_GPROPID_CURRENT_SUBSET then use that as an override...
  417. //
  418. if ( _Module.szCurSS[0] )
  419. {
  420. pSS = NULL;
  421. while ( (pSS = m_pSSList->GetNextSubset(pSS)) )
  422. {
  423. if (! strcmpi(_Module.szCurSS, pSS->GetID()) )
  424. {
  425. m_pSSList->SetFTS(pSS);
  426. m_pSSList->SetF1(pSS);
  427. m_pSSList->SetTOC(pSS);
  428. break;
  429. }
  430. }
  431. }
  432. }
  433. }
  434. void CExCollection::GetOpenSlot(CExTitle *p)
  435. {
  436. if (m_MaxOpenTitles[m_dwLastSlot])
  437. m_MaxOpenTitles[m_dwLastSlot]->CloseTitle();
  438. m_MaxOpenTitles[m_dwLastSlot] = p;
  439. m_dwLastSlot++;
  440. if (m_dwLastSlot == MAX_OPEN_TITLES)
  441. m_dwLastSlot = 0;
  442. }
  443. BOOL CExCollection::ValidateTitle(CExTitle *pExTitle, BOOL bDupCheckOnly)
  444. {
  445. // make sure the collection does not already contain a chm with same location MMC
  446. CExTitle *pEnumTitle;
  447. pEnumTitle = GetFirstTitle();
  448. while (pEnumTitle)
  449. {
  450. if (lstrcmp(pEnumTitle->GetContentFileName(), pExTitle->GetContentFileName()) == 0)
  451. {
  452. delete pExTitle;
  453. m_Collection.DecrementRefTitleCount();
  454. return FALSE;
  455. }
  456. pEnumTitle = pEnumTitle->GetNext();
  457. }
  458. if (bDupCheckOnly == TRUE)
  459. return TRUE;
  460. // before adding to title list confirm that files exist
  461. pExTitle->FindUsedLocation();
  462. if (pExTitle->GetUsedLocation() == NULL)
  463. {
  464. delete pExTitle;
  465. m_Collection.DecrementRefTitleCount();
  466. return FALSE;
  467. }
  468. if (pExTitle->GetUsedLocation()->IndexFileName && pExTitle->GetUsedLocation()->IndexFileName[0])
  469. {
  470. if (GetFileAttributes(pExTitle->GetUsedLocation()->IndexFileName) == HFILE_ERROR)
  471. {
  472. delete pExTitle;
  473. m_Collection.DecrementRefTitleCount();
  474. return FALSE;
  475. }
  476. }
  477. return TRUE;
  478. }
  479. BOOL CExCollection::UpdateLocation( const CHAR* pszLocId, const CHAR* pszNewPath, const CHAR* pszNewVolume, const CHAR* pszNewTitle )
  480. {
  481. // find the location itself
  482. CLocation *pLocation = FindLocation( (CHAR*)pszLocId);
  483. if (pLocation == NULL)
  484. return FALSE;
  485. // make sure new path has trailing backslash
  486. CStr cStrPath = pszNewPath;
  487. if (pszNewPath[strlen(pszNewPath) - 1] != '\\')
  488. cStrPath += "\\";
  489. // get the current (old) location path
  490. CStr cStrOldPath = pLocation->GetPath();
  491. if (cStrOldPath.psz[strlen(cStrOldPath.psz) - 1] != '\\')
  492. cStrOldPath += "\\";
  493. // if new and old paths are the some just bail out
  494. if( lstrcmpi( cStrPath.psz, cStrOldPath.psz ) == 0 )
  495. return FALSE;
  496. // determine what has changed in this path (was it just the drive letter?)
  497. // note we must compare from the tail end and thus we have to be careful when
  498. // dealing with DBCS strings
  499. CHAR* pszOldPathHead = cStrOldPath.psz;
  500. CHAR* pszOldPathTail = CharPrev(pszOldPathHead, pszOldPathHead + strlen(pszOldPathHead));
  501. CHAR* pszPathHead = cStrPath.psz;
  502. CHAR* pszPathTail = CharPrev(pszPathHead, pszPathHead + strlen(pszPathHead));
  503. while( (pszOldPathTail >= pszOldPathHead) && (pszPathTail >= pszPathHead) ) {
  504. BOOL bOldLB = IsDBCSLeadByte(*pszOldPathTail);
  505. BOOL bLB = IsDBCSLeadByte(*pszPathTail);
  506. if( bOldLB && bLB ) {
  507. if( !((*pszOldPathTail == *pszPathTail) && (*(pszOldPathTail+1) == *(pszPathTail+1))) )
  508. break;
  509. }
  510. else if( bOldLB || bLB ) {
  511. break;
  512. }
  513. else if( ToLower(*pszOldPathTail) != ToLower(*pszPathTail) ) {
  514. break;
  515. }
  516. // bail if we compared all chars
  517. if( (pszOldPathTail == pszOldPathHead) || (pszPathTail == pszPathHead) )
  518. break;
  519. // advance to previous char
  520. pszOldPathTail = CharPrev(pszOldPathHead, pszOldPathTail);
  521. pszPathTail = CharPrev(pszPathHead, pszPathTail);
  522. }
  523. if( IsDBCSLeadByte(*pszOldPathTail) )
  524. pszOldPathTail++;
  525. if( IsDBCSLeadByte(*pszPathTail) )
  526. pszPathTail++;
  527. char szOldPathPrefix[MAX_PATH];
  528. int iLen = (int)(((DWORD_PTR)pszOldPathTail)-((DWORD_PTR)pszOldPathHead)+1); // always at least one
  529. lstrcpyn( szOldPathPrefix, pszOldPathHead, iLen+1 );
  530. char szPathPrefix[MAX_PATH];
  531. iLen = (int)(((DWORD_PTR)pszPathTail)-((DWORD_PTR)pszPathHead)+1); // always at least one
  532. lstrcpyn( szPathPrefix, pszPathHead, iLen+1 );
  533. // update title if specified
  534. if (pszNewTitle)
  535. pLocation->SetTitle(pszNewTitle);
  536. // get the volume that will be updated
  537. CStr cStrVolume = pLocation->GetVolume();
  538. // first, update each pathname in the titles that have the same volume label
  539. CExTitle *pExTitle = GetFirstTitle();
  540. LOCATIONHISTORY *pLH;
  541. CHAR* pszFilePortion;
  542. CStr cFileName;
  543. while (pExTitle)
  544. {
  545. // if the used location for this title is the new location update it
  546. pLH = pExTitle->GetUsedLocation();
  547. CLocation *pLoc = FindLocation( (CHAR*)pLH->LocationId );
  548. CStr cStrVol = pLoc->GetVolume();
  549. // get the old location path
  550. CStr cStrOldPath = pLoc->GetPath();
  551. if (cStrOldPath.psz[strlen(cStrOldPath.psz) - 1] != '\\')
  552. cStrOldPath += "\\";
  553. if (pExTitle->GetContentFileName().psz && pLH)
  554. {
  555. if (strcmpi( cStrVol, cStrVolume ) == 0)
  556. {
  557. // make sure that it did point to the old location
  558. pszFilePortion = (CHAR*)FindFilePortion(pExTitle->GetContentFileName());
  559. cFileName = cStrOldPath.psz;
  560. cFileName += pszFilePortion;
  561. pszFilePortion = (CHAR*)FindFilePortion(cFileName);
  562. if (strcmpi(cFileName, pExTitle->GetContentFileName()) == 0)
  563. {
  564. // find the ending location of the old prefix
  565. CHAR* pszOldPathNameEnd = pExTitle->GetContentFileName().psz + strlen(szOldPathPrefix);
  566. // create the new pathname using the prefix of the new location and the
  567. // ending string of the old pathname
  568. CStr cStrPathName = szPathPrefix;
  569. cStrPathName += pszOldPathNameEnd;
  570. // update the pathname
  571. pExTitle->GetContentFileName() = cStrPathName.psz;
  572. }
  573. }
  574. }
  575. // check each location for this title
  576. pLH = pExTitle->m_pTitle->m_pHead;
  577. while (pLH)
  578. {
  579. // chm file location information
  580. CLocation *pLoc = FindLocation( (CHAR*)pLH->LocationId );
  581. // if the location is NULL, this indications that we are looking at a title
  582. // that belongs to a different collection and thus we should skip it
  583. if( pLoc ) {
  584. CStr cStrVol = pLoc->GetVolume();
  585. // get the old location path
  586. CStr cStrOldPath = pLoc->GetPath();
  587. if (cStrOldPath.psz[strlen(cStrOldPath.psz) - 1] != '\\')
  588. cStrOldPath += "\\";
  589. // chm file
  590. if (strcmpi( cStrVol, cStrVolume ) == 0)
  591. {
  592. pszFilePortion = (CHAR*)FindFilePortion(pLH->FileName);
  593. cFileName = cStrOldPath.psz;
  594. cFileName += pszFilePortion;
  595. if (strcmpi(cFileName, pLH->FileName) == 0)
  596. {
  597. // find the ending location of the old prefix
  598. CHAR* pszOldPathNameEnd = pLH->FileName + strlen(szOldPathPrefix);
  599. // create the new pathname using the prefix of the new location and the
  600. // ending string of the old pathname
  601. CStr cStrPathName = szPathPrefix;
  602. cStrPathName += pszOldPathNameEnd;
  603. // update the pathname
  604. AllocSetValue(cStrPathName.psz, &pLH->FileName);
  605. }
  606. }
  607. // chq file
  608. if( pLH->QueryFileName && *pLH->QueryFileName ) {
  609. // chq file location information
  610. // (use the same as the chm file if QueryLocation is not set)
  611. if( pLH->QueryLocation && *(pLH->QueryLocation) ) {
  612. pLoc = FindLocation( (CHAR*) pLH->QueryLocation );
  613. cStrVol = pLoc->GetVolume();
  614. // get the old location path
  615. cStrOldPath = pLoc->GetPath();
  616. if (cStrOldPath.psz[strlen(cStrOldPath.psz) - 1] != '\\')
  617. cStrOldPath += "\\";
  618. }
  619. // chq file
  620. if( strcmpi( cStrVol, cStrVolume ) == 0 )
  621. {
  622. pszFilePortion = (CHAR*)FindFilePortion(pLH->QueryFileName);
  623. cFileName = cStrOldPath.psz;
  624. cFileName += pszFilePortion;
  625. if (strcmpi(cFileName, pLH->QueryFileName) == 0)
  626. {
  627. // find the ending location of the old prefix
  628. CHAR* pszOldPathNameEnd = pLH->QueryFileName + strlen(szOldPathPrefix);
  629. // create the new pathname using the prefix of the new location and the
  630. // ending string of the old pathname
  631. CStr cStrPathName = szPathPrefix;
  632. cStrPathName += pszOldPathNameEnd;
  633. // update the pathname
  634. AllocSetValue(cStrPathName.psz, &pLH->QueryFileName);
  635. }
  636. }
  637. }
  638. }
  639. pLH = pLH->pNext;
  640. }
  641. pExTitle = pExTitle->GetNext();
  642. }
  643. // and finally, update any location identifier that has the same volume label
  644. //
  645. // note: we must do this list since the individual title updating relies on the fact
  646. // that we can fetch the "old" location information to validate the file pathing before
  647. // we update it
  648. CLocation *pLoc = m_Collection.FirstLocation();
  649. while( pLoc ) {
  650. CStr cStrVol = pLoc->GetVolume();
  651. if( strcmpi( cStrVol, cStrVolume ) == 0 ) {
  652. // get the old location path
  653. CStr cStrOldPath = pLoc->GetPath();
  654. if (cStrOldPath.psz[strlen(cStrOldPath.psz) - 1] != '\\')
  655. cStrOldPath += "\\";
  656. // find the ending location of the old prefix
  657. CHAR* pszOldPathEnd = cStrOldPath.psz + strlen(szOldPathPrefix);
  658. // create the new path using the prefix of the new location and the
  659. // ending string of the old path
  660. CStr cStrPath = szPathPrefix;
  661. cStrPath += pszOldPathEnd;
  662. // make sure the path aways ends with a backslash
  663. if (cStrPath.psz[strlen(cStrPath.psz) - 1] != '\\')
  664. cStrPath += "\\";
  665. // update the path
  666. pLoc->SetPath(cStrPath);
  667. // update the volume label if specified
  668. if( pszNewVolume )
  669. pLoc->SetVolume( pszNewVolume );
  670. }
  671. pLoc = pLoc->GetNextLocation();
  672. }
  673. // set the collection dirty bit so the hhcolreg.dat will get updated on shutdown
  674. m_Collection.Dirty();
  675. return FALSE;
  676. }
  677. void CExCollection::GetChildURLS(CTreeNode *pNode, CTable *pTable)
  678. {
  679. CTreeNode *pParents[50];
  680. CTreeNode *pCur, *pNext;
  681. CHAR* pszFind;
  682. DWORD dwCurLevel = 0;
  683. CHAR szURL[MAX_URL];
  684. for (int i = 0; i < 50; i++)
  685. pParents[i] = NULL;
  686. // add the URL for this node
  687. if (pNode->GetURL(szURL, sizeof(szURL), TRUE))
  688. {
  689. // Truncate the strings at the # character if there is one.
  690. pszFind = StrChr((const CHAR*)szURL, '#');
  691. if (pszFind != NULL)
  692. *pszFind = '\0';
  693. if (pszFind = StrChr((const CHAR*)szURL, '\\'))
  694. {
  695. while (*pszFind != '\0')
  696. {
  697. if (*pszFind == '\\')
  698. *pszFind = '/';
  699. pszFind = CharNext(pszFind);
  700. }
  701. }
  702. if (pTable->IsStringInTable(szURL) == 0)
  703. pTable->AddString(szURL);
  704. }
  705. if (pNode->HasChildren())
  706. {
  707. pParents[dwCurLevel] = pNode;
  708. dwCurLevel++;
  709. pCur = pNode->GetFirstChild();
  710. while (pCur)
  711. {
  712. if (pCur->GetURL(szURL, sizeof(szURL), TRUE))
  713. {
  714. // Truncate the strings at the # character if there is one.
  715. pszFind = StrChr((const CHAR*)szURL, '#');
  716. if (pszFind != NULL)
  717. *pszFind = '\0';
  718. if (pszFind = StrChr((const CHAR*)szURL, '\\'))
  719. {
  720. while (*pszFind != '\0')
  721. {
  722. if (*pszFind == '\\')
  723. *pszFind = '/';
  724. pszFind = CharNext(pszFind);
  725. }
  726. }
  727. if (pTable->IsStringInTable(szURL) == 0)
  728. pTable->AddString(szURL);
  729. }
  730. if (pNext = pCur->GetFirstChild())
  731. {
  732. if (pParents[dwCurLevel])
  733. delete pParents[dwCurLevel];
  734. pParents[dwCurLevel] = pCur;
  735. dwCurLevel++;
  736. pCur = pNext;
  737. }
  738. else if (pNext = pCur->GetNextSibling())
  739. {
  740. delete pCur;
  741. pCur = pNext;
  742. }
  743. else
  744. {
  745. delete pCur;
  746. while (TRUE)
  747. {
  748. dwCurLevel--;
  749. if (dwCurLevel == 0)
  750. {
  751. if (pParents[dwCurLevel+1])
  752. delete pParents[dwCurLevel+1];
  753. pCur = NULL;
  754. break;
  755. }
  756. pCur = pParents[dwCurLevel];
  757. if (pNext = pCur->GetNextSibling())
  758. {
  759. pCur = pNext;
  760. break;
  761. }
  762. delete pCur;
  763. pParents[dwCurLevel] = NULL;
  764. }
  765. }
  766. }
  767. }
  768. return;
  769. }
  770. BOOL CExCollection::InitFTSKeyword()
  771. {
  772. // BUGBUG: we shouldn't do this until we know we have full-text search
  773. // in the file (which will usually NOT be the case).
  774. // Create full-text search object
  775. //
  776. if (m_phmData->m_sysflags.fFTI) {
  777. m_pFullTextSearch = new CFullTextSearch(this);
  778. m_pFullTextSearch->Initialize();
  779. // Create search highlight object
  780. //
  781. m_pSearchHighlight = new CSearchHighlight(this);
  782. }
  783. // create word wheels (they self initialize themselves upon use)
  784. //
  785. // TODO: move full text search shared code to CTitleDatabase
  786. //
  787. m_pDatabase = new CTitleDatabase( this );
  788. return TRUE;
  789. }
  790. #ifdef DUMPTOC
  791. void CExCollection::DumpNode(CTreeNode **p)
  792. {
  793. char sz[256];
  794. if (m_bRoot == TRUE)
  795. m_bRoot = FALSE;
  796. else
  797. {
  798. for (DWORD i = 1; i < m_dwLevel; i++)
  799. fprintf(m_fh, " ");
  800. (*p)->GetTopicName(sz, sizeof(sz));
  801. fprintf(m_fh, "%s\n", sz);
  802. }
  803. CTreeNode *pNode;
  804. if (pNode = (*p)->GetFirstChild())
  805. {
  806. m_dwLevel++;
  807. DumpNode(&pNode);
  808. m_dwLevel--;
  809. }
  810. pNode = (*p)->GetNextSibling();
  811. delete (*p);
  812. *p = NULL;
  813. do
  814. {
  815. if (pNode)
  816. DumpNode(&pNode);
  817. } while (pNode && (pNode = pNode->GetNextSibling()));
  818. }
  819. #endif
  820. void CExCollection::GetMergedTitles(CExTitle *pTitle)
  821. {
  822. CStr cStrFile;
  823. CStr cStrFullPath;
  824. CExTitle *pNewTitle = NULL;
  825. CExTitle *pPreviousNewTitle = NULL;
  826. CExTitle *pParent = pTitle;
  827. char szMasterPath[MAX_PATH];
  828. char szTmp[MAX_PATH];
  829. BOOL bOk2Add = FALSE;
  830. LCID TitleLocale = NULL;
  831. if( !(pTitle->Init()) )
  832. return;
  833. //[ZERO IDXHDR]
  834. if (!pTitle->IsIdxHeaderValid())
  835. {
  836. return ;
  837. }
  838. if (pTitle->GetInfo())
  839. {
  840. TitleLocale = pTitle->GetInfo()->GetLanguage();
  841. }
  842. DWORD dwCount = pTitle->GetIdxHeaderStruct()->dwCntMergedTitles;
  843. for (DWORD i = 0; i < dwCount; i++)
  844. {
  845. // read string table for this string
  846. if (FAILED(pTitle->GetString((((DWORD*)(&(pTitle->GetIdxHeaderStruct()->pad)))[i]), &cStrFile)))
  847. continue;
  848. // if this is not a single title (.col file) search collection registry
  849. if (m_bSingleTitle == FALSE)
  850. {
  851. // get base name
  852. SplitPath((CHAR*)cStrFile, NULL, NULL, szTmp, NULL);
  853. // search
  854. CTitle *pCTitle = m_Collection.FindTitle(szTmp, (LANGID)TitleLocale);
  855. if (pCTitle == NULL && TitleLocale != ENGLANGID) // for localized merged chms if we can't find a child with the same lang look for englist look for english titles in hhcolreg
  856. {
  857. pCTitle = m_Collection.FindTitle(szTmp, (LANGID)ENGLANGID);
  858. }
  859. if (pCTitle)
  860. {
  861. // create CExTitle
  862. pNewTitle = new CExTitle(pCTitle, m_Collection.GetColNo(), this);
  863. m_Collection.IncrementRefTitleCount();
  864. if (ValidateTitle(pNewTitle) == TRUE)
  865. {
  866. CExTitle* pTitle = m_pHeadTitles;
  867. while(pTitle->GetNext())
  868. pTitle = pTitle->GetNext();
  869. pTitle->SetNext(pNewTitle);
  870. //
  871. // Sync/Next/Prev and subsetting spupport for "merged" chms...
  872. //
  873. pNewTitle->m_pParent = pParent; // Set parent pointer.
  874. if (! pParent->m_pKid ) // Set parent titles kid pointer to first kid.
  875. pParent->m_pKid = pNewTitle;
  876. if ( pPreviousNewTitle )
  877. pPreviousNewTitle->m_pNextKid = pNewTitle;
  878. pPreviousNewTitle = pNewTitle;
  879. //
  880. // Create a hash for these...
  881. //
  882. char szBuf[20];
  883. char szID[MAX_PATH + 20];
  884. Itoa(pCTitle->GetLanguage(), szBuf);
  885. strcpy(szID, pCTitle->GetId());
  886. strcat(szID, szBuf);
  887. pNewTitle->m_dwHash = HashFromSz(szID);
  888. continue;
  889. }
  890. }
  891. else if (m_Collection.GetFindMergedCHMS())
  892. {
  893. if ( FindThisFile(NULL, cStrFile, &cStrFullPath) )
  894. {
  895. // if found add to title list (add to the end of the list)
  896. pNewTitle = new CExTitle(cStrFullPath, this);
  897. m_Collection.IncrementRefTitleCount();
  898. if (ValidateTitle(pNewTitle, TRUE) == TRUE)
  899. {
  900. CExTitle* pTitle = m_pHeadTitles;
  901. while(pTitle->GetNext())
  902. pTitle = pTitle->GetNext();
  903. pTitle->SetNext(pNewTitle);
  904. //
  905. // Sync/Next/Prev and subsetting spupport for "merged" chms...
  906. //
  907. pNewTitle->m_pParent = pParent; // Set parent pointer.
  908. if (! pParent->m_pKid ) // Set parent titles kid pointer to first kid.
  909. pParent->m_pKid = pNewTitle;
  910. if ( pPreviousNewTitle )
  911. pPreviousNewTitle->m_pNextKid = pNewTitle;
  912. pPreviousNewTitle = pNewTitle;
  913. //
  914. // Create a hash for these...
  915. //
  916. char szBuf[20];
  917. char szID[MAX_PATH + 20];
  918. Itoa(0, szBuf);
  919. strcpy(szID, szTmp);
  920. strcat(szID, szBuf);
  921. pNewTitle->m_dwHash = HashFromSz(szID);
  922. continue;
  923. }
  924. }
  925. }
  926. }
  927. else
  928. {
  929. //
  930. // First check location of this file
  931. //
  932. if ((CHAR*)m_csFile )
  933. {
  934. SplitPath((CHAR*)m_csFile, szMasterPath, szTmp, NULL, NULL);
  935. CatPath(szMasterPath, szTmp);
  936. CatPath(szMasterPath, (CHAR*)cStrFile);
  937. bOk2Add = ( GetFileAttributes(szMasterPath) != HFILE_ERROR );
  938. }
  939. // search for file
  940. //
  941. if (! bOk2Add )
  942. bOk2Add = FindThisFile(NULL, cStrFile, &cStrFullPath);
  943. else
  944. cStrFullPath = (const CHAR*)szMasterPath;
  945. if ( bOk2Add )
  946. {
  947. // if found add to title list (add to the end of the list)
  948. pNewTitle = new CExTitle(cStrFullPath, this);
  949. pNewTitle->m_pParent = pParent; // Set parent pointer.
  950. CExTitle* pTitle = m_pHeadTitles;
  951. while(pTitle->GetNext())
  952. pTitle = pTitle->GetNext();
  953. pTitle->SetNext(pNewTitle);
  954. m_Collection.IncrementRefTitleCount();
  955. }
  956. }
  957. }
  958. }
  959. // BUGBUG: <mikecole> dondr review, could this go away in-lu of checking the f_IsOrphan bit ?
  960. CTreeNode * CExCollection::CheckForTitleNode(CFolder *p)
  961. {
  962. if (p == NULL)
  963. return NULL;
  964. CHAR* pszTitle = p->GetTitle();
  965. if (pszTitle && pszTitle[0] == '=')
  966. {
  967. CExTitle *pt = FindTitle(pszTitle+1, p->GetLanguage());
  968. if (pt == NULL)
  969. return NULL;
  970. CExTitleNode *pext = new CExTitleNode(pt, p);
  971. return pext;
  972. }
  973. else
  974. {
  975. // Check if this folder has a title below it somewhere
  976. BOOL bFound = FALSE;
  977. CFolder *pFolder;
  978. if (pFolder = p->GetFirstChildFolder())
  979. {
  980. CheckForTitleChild(pFolder, &bFound);
  981. }
  982. if (bFound == FALSE)
  983. {
  984. return NULL;
  985. }
  986. CExFolderNode *pf = new CExFolderNode(p, this);
  987. return pf;
  988. }
  989. return NULL;
  990. }
  991. // BUGBUG: <mikecole> dondr review, could this go away in-lu of checking the f_IsOrphan bit ?
  992. void CExCollection::CheckForTitleChild(CFolder *p, BOOL *pbFound)
  993. {
  994. if (*pbFound == TRUE)
  995. return;
  996. CHAR* pszTitle = p->GetTitle();
  997. CFolder *pF;
  998. if (pszTitle && pszTitle[0] == '=')
  999. {
  1000. *pbFound = TRUE;
  1001. return;
  1002. }
  1003. if (pF = p->GetFirstChildFolder())
  1004. {
  1005. CheckForTitleChild(pF, pbFound);
  1006. if (*pbFound == TRUE)
  1007. return;
  1008. }
  1009. pF = p->GetNextFolder();
  1010. while (pF)
  1011. {
  1012. CheckForTitleChild(pF, pbFound);
  1013. if (*pbFound == TRUE)
  1014. return;
  1015. pF = pF->GetNextFolder();
  1016. }
  1017. }
  1018. DWORD CExCollection::GetRefedTitleCount()
  1019. {
  1020. return m_Collection.GetRefTitleCount();
  1021. }
  1022. BOOL CExCollection::IsBinaryTOC(const CHAR* pszToc)
  1023. {
  1024. if (! m_phmData || !pszToc)
  1025. return FALSE;
  1026. return (HashFromSz(FindFilePortion(pszToc)) == m_phmData->m_hashBinaryTocName);
  1027. }
  1028. CTreeNode * CExCollection::GetRootNode()
  1029. {
  1030. CStructuralSubset* pSS = NULL;
  1031. if (m_bSingleTitle)
  1032. {
  1033. if (!m_pHeadTitles)
  1034. m_pHeadTitles = new CExTitle(GetPathName(), this);
  1035. TOC_FOLDERNODE Node;
  1036. if ( !SUCCEEDED(m_pHeadTitles->GetRootNode(&Node)) )
  1037. return NULL;
  1038. CExTitleNode *pext = new CExTitleNode(m_pHeadTitles, NULL);
  1039. return pext;
  1040. }
  1041. else
  1042. {
  1043. CFolder *p = m_Collection.GetRootFolder();
  1044. CTreeNode *pN;
  1045. // implement structural subset filtering for TOC here.
  1046. if( m_pSSList )
  1047. pSS = m_pSSList->GetTOC();
  1048. while (p)
  1049. {
  1050. if ((pN = CheckForTitleNode(p)))
  1051. {
  1052. if ( !pSS || pSS->IsEntire() || p->bIsVisable() )
  1053. return pN;
  1054. else
  1055. delete pN; // leak fix
  1056. }
  1057. p = p->GetNextFolder();
  1058. }
  1059. return NULL;
  1060. }
  1061. }
  1062. //////////////////////////////////////////////////////////////////////////
  1063. //
  1064. // Ignore LangId if its Zero.
  1065. //
  1066. CExTitle * CExCollection::FindTitle(const CHAR* pszId, LANGID LangId)
  1067. {
  1068. // look in list of titles
  1069. CExTitle *p = m_pHeadTitles;
  1070. while (p)
  1071. {
  1072. if( p->GetCTitle() )
  1073. if( _tcsicmp(p->GetCTitle()->GetId(), pszId) == 0 )
  1074. if( (LangId == 0 || p->GetCTitle()->GetLanguage() == LangId) ) // If LangId == 0, we ignore the lang id.
  1075. {
  1076. return p;
  1077. }
  1078. p = p->GetNext();
  1079. }
  1080. return NULL;
  1081. }
  1082. // Try multiple LangIds before failing
  1083. CExTitle * CExCollection::FindTitleNonExact(const CHAR* pszId, LANGID DesiredLangId)
  1084. {
  1085. CExTitle* pTitle = NULL ;
  1086. CLanguageEnum* pEnum = _Module.m_Language.GetEnumerator(DesiredLangId) ;
  1087. ASSERT(pEnum) ;
  1088. LANGID LangId = pEnum->start() ;
  1089. while (LangId != c_LANGID_ENUM_EOF)
  1090. {
  1091. pTitle = FindTitle(pszId, LangId);
  1092. if (pTitle)
  1093. {
  1094. break ; // Found it!
  1095. }
  1096. LangId = pEnum->next() ;
  1097. }
  1098. // Cleanup.
  1099. if (pEnum)
  1100. {
  1101. delete pEnum ;
  1102. }
  1103. return pTitle;
  1104. }
  1105. //
  1106. CExTitle * CExCollection::TitleFromChmName(const CHAR* pszChmName)
  1107. {
  1108. CExTitle *p = m_pHeadTitles;
  1109. CHAR szFN[MAX_PATH];
  1110. CHAR szExt[MAX_PATH];
  1111. while (p)
  1112. {
  1113. SplitPath(p->GetContentFileName(), NULL, NULL, szFN, szExt);
  1114. strcat(szFN, szExt);
  1115. if (! _tcsicmp(szFN, pszChmName) )
  1116. return p;
  1117. p = p->GetNext();
  1118. }
  1119. return NULL;
  1120. }
  1121. CLocation * CExCollection::FindLocation(CHAR* pszId)
  1122. {
  1123. return m_Collection.FindLocation(pszId);
  1124. }
  1125. // GetNext()
  1126. //
  1127. // Returns the next physical TOC node irregaurdless of its type. If a node is a container, it's
  1128. // next is considered to be it's child.
  1129. //
  1130. // Note that the caller will be responsible for deleting the returned CTreeNode object.
  1131. //
  1132. CTreeNode * CExCollection::GetNext(CTreeNode* pTreeNode, DWORD* pdwSlot)
  1133. {
  1134. CTreeNode *pTreeNext = NULL, *pTreeParent = NULL, *pSaveNode = NULL;
  1135. DWORD dwObjType;
  1136. dwObjType = pTreeNode->GetType();
  1137. if ( ((dwObjType == FOLDER) || (dwObjType == CONTAINER)) && (pTreeNext = pTreeNode->GetFirstChild(pdwSlot)) )
  1138. return pTreeNext;
  1139. else
  1140. {
  1141. if ( (pTreeNext = pTreeNode->GetNextSibling(NULL, pdwSlot)) )
  1142. return pTreeNext;
  1143. else
  1144. {
  1145. pSaveNode = pTreeNode;
  1146. do
  1147. {
  1148. pTreeParent = pTreeNode->GetParent(pdwSlot, TRUE);
  1149. if ( pSaveNode != pTreeNode )
  1150. delete pTreeNode;
  1151. if (! (pTreeNode = pTreeParent) )
  1152. return NULL;
  1153. } while (!(pTreeNext = pTreeNode->GetNextSibling(NULL, pdwSlot)));
  1154. }
  1155. return pTreeNext;
  1156. }
  1157. }
  1158. // GetNext()
  1159. //
  1160. // Returns the next TOC node that represents a displayable topic.
  1161. //
  1162. // Note that the caller will be responsible for deleting the returned CTreeNode object.
  1163. //
  1164. CTreeNode * CExCollection::GetNextTopicNode(CTreeNode* pTreeNode, DWORD* pdwSlot)
  1165. {
  1166. CTreeNode *pTocNext = NULL, *pTocKid = NULL;
  1167. DWORD dwObjType;
  1168. try_again:
  1169. if ( (pTocNext = GetNext(pTreeNode, pdwSlot)) )
  1170. {
  1171. dwObjType = pTocNext->GetType();
  1172. if ( (dwObjType != TOPIC) && (dwObjType != CONTAINER) )
  1173. {
  1174. // We need to drill down!
  1175. //
  1176. do
  1177. {
  1178. if ( (pTocKid = pTocNext->GetFirstChild(pdwSlot)) )
  1179. {
  1180. dwObjType = pTocKid->GetType();
  1181. delete pTocNext;
  1182. pTocNext = pTocKid;
  1183. }
  1184. else
  1185. {
  1186. // This is the case of the book that contains no kids! For this case, we'll skip this dirty node!
  1187. //
  1188. pTreeNode = pTocNext;
  1189. goto try_again;
  1190. }
  1191. } while ( (dwObjType != TOPIC) && (dwObjType != CONTAINER) );
  1192. }
  1193. return pTocNext;
  1194. }
  1195. return NULL;
  1196. }
  1197. // GetPrev()
  1198. //
  1199. // Returns the previous physical TOC node irregardless of its type.
  1200. //
  1201. // Note that the caller will be responsible for deleting the returned CTreeNode object.
  1202. //
  1203. CTreeNode * CExCollection::GetPrev(CTreeNode* pTreeNode, DWORD* pdwSlot)
  1204. {
  1205. CTreeNode *pTreeNext = NULL, *pTreeParent = NULL , *pSaveNode = NULL, *pTmpNode = NULL;
  1206. DWORD dwObjType;
  1207. DWORD dwTmpSlot;
  1208. pSaveNode = pTreeNode;
  1209. if (! (pTreeParent = pTreeNode->GetParent(pdwSlot, TRUE)) )
  1210. {
  1211. // Could this be a single title with multiple roots ?
  1212. //
  1213. if ( pTreeNode->GetObjType() == EXNODE )
  1214. {
  1215. CExTitle* pTitle;
  1216. pTitle = ((CExNode*)pTreeNode)->GetTitle();
  1217. if ( pTitle->m_pCollection->IsSingleTitle() )
  1218. {
  1219. TOC_FOLDERNODE Node;
  1220. pTitle->GetRootNode(&Node);
  1221. pTreeNext = new CExNode(&Node, pTitle);
  1222. if ( pTreeNext->Compare(pSaveNode) ) {
  1223. delete pTreeNext; // leak fix
  1224. return NULL; // No prev to be found!
  1225. }
  1226. if ( pdwSlot )
  1227. *pdwSlot = pTitle->GetRootSlot();
  1228. goto find_it;
  1229. }
  1230. }
  1231. else
  1232. {
  1233. CFolder *p = m_Collection.GetRootFolder();
  1234. if( !p )
  1235. return NULL;
  1236. p = p->GetFirstChildFolder();
  1237. //
  1238. // Is it visable ?
  1239. //
  1240. CStructuralSubset* pSS = NULL;
  1241. if( m_pSSList )
  1242. pSS = m_pSSList->GetTOC();
  1243. if ( pSS && !pSS->IsEntire() && !p->bIsVisable() )
  1244. return NULL;
  1245. pTreeNext = new CExFolderNode(p, this);
  1246. goto find_it;
  1247. }
  1248. return NULL;
  1249. }
  1250. if (! (pTreeNext = pTreeParent->GetFirstChild(&dwTmpSlot)) ) {
  1251. delete pTreeParent; // leak fix
  1252. return NULL;
  1253. }
  1254. // ---LEAK!: At this point we have a pTreeNext and pTreeParent ---
  1255. if ( pTreeNext->Compare(pSaveNode) )
  1256. {
  1257. dwObjType = pTreeParent->GetType();
  1258. if ( dwObjType == CONTAINER )
  1259. return pTreeParent;
  1260. else
  1261. {
  1262. pTmpNode = GetPrev(pTreeParent, pdwSlot);
  1263. delete pTreeParent;
  1264. if (! pTmpNode )
  1265. {
  1266. delete pTreeNext ; // Fix Leak.
  1267. return NULL;
  1268. }
  1269. if ( pTmpNode->GetType() == CONTAINER )
  1270. return pTmpNode;
  1271. else
  1272. {
  1273. pTreeParent = GetLastChild(pTmpNode, pdwSlot);
  1274. if ( pTreeParent != pTmpNode )
  1275. delete pTmpNode;
  1276. return pTreeParent;
  1277. }
  1278. }
  1279. }
  1280. delete pTreeParent;
  1281. if ( pdwSlot )
  1282. *pdwSlot = dwTmpSlot;
  1283. find_it:
  1284. pTmpNode = pTreeNext->GetNextSibling(NULL, &dwTmpSlot);
  1285. while ( pTmpNode && !pTmpNode->Compare(pSaveNode) )
  1286. {
  1287. delete pTreeNext; pTreeNext = NULL; // leak fix
  1288. pTreeNext = pTmpNode;
  1289. if ( pdwSlot )
  1290. *pdwSlot = dwTmpSlot;
  1291. pTmpNode = pTreeNext->GetNextSibling(NULL, &dwTmpSlot);
  1292. }
  1293. if (pTmpNode && pTmpNode->Compare(pSaveNode) )
  1294. {
  1295. delete pTmpNode;
  1296. if ( pTreeNext->GetType() == BOGUS_FOLDER )
  1297. {
  1298. pTmpNode = GetPrev(pTreeNext, pdwSlot);
  1299. if ( pTmpNode != pTreeNext )
  1300. {
  1301. delete pTreeNext; pTreeNext = NULL; // leak fix
  1302. pTreeNext = pTmpNode;
  1303. }
  1304. }
  1305. pTmpNode = GetLastChild(pTreeNext, pdwSlot);
  1306. if ( pTmpNode != pTreeNext )
  1307. delete pTreeNext;
  1308. return pTmpNode;
  1309. }
  1310. else if( pTmpNode )
  1311. delete pTmpNode; // leak fix
  1312. if( pTreeNext && (pTreeNext != pTmpNode) )
  1313. delete pTreeNext; // leak fix
  1314. return NULL;
  1315. }
  1316. CTreeNode * CExCollection::GetLastChild(CTreeNode* pTreeNode, DWORD* pdwSlot)
  1317. {
  1318. CTreeNode *pTreeTmp = NULL, *pTreeKid = NULL, *pSiblingNode = NULL;
  1319. DWORD dwObjType;
  1320. if (! pTreeNode )
  1321. return NULL;
  1322. dwObjType = pTreeNode->GetType();
  1323. if ( dwObjType != TOPIC )
  1324. {
  1325. // We need to drill down.
  1326. //
  1327. if ( (pTreeKid = pTreeNode->GetFirstChild(pdwSlot)) )
  1328. {
  1329. while ( (pSiblingNode = pTreeKid->GetNextSibling(NULL, pdwSlot)) )
  1330. {
  1331. while ( pSiblingNode->GetType() == BOGUS_FOLDER )
  1332. {
  1333. if ( ! (pTreeTmp = pSiblingNode->GetNextSibling(NULL, pdwSlot)) )
  1334. break;
  1335. else
  1336. {
  1337. delete pSiblingNode;
  1338. pSiblingNode = pTreeTmp;
  1339. }
  1340. }
  1341. delete pTreeKid;
  1342. pTreeKid = pSiblingNode;
  1343. }
  1344. if ( pTreeKid->GetType() != TOPIC )
  1345. {
  1346. pTreeTmp = GetLastChild(pTreeKid, pdwSlot);
  1347. if (pTreeTmp == pTreeKid)
  1348. {
  1349. delete pTreeKid;
  1350. return NULL;
  1351. }
  1352. delete pTreeKid;
  1353. pTreeKid = pTreeTmp;
  1354. }
  1355. return pTreeKid;
  1356. }
  1357. }
  1358. return pTreeNode;
  1359. }
  1360. HRESULT CExCollection::URL2ExTitle(const CHAR* pszURL, CExTitle **ppTitle)
  1361. {
  1362. // get the title from the URL
  1363. CStr cStr;
  1364. const CHAR* pszThisFileName;
  1365. GetCompiledName(pszURL, &cStr);
  1366. if( !cStr.psz )
  1367. return E_FAIL;
  1368. const CHAR* pszFileName = FindFilePortion( cStr.psz );
  1369. CExTitle *pTitle = GetFirstTitle();
  1370. while (pTitle)
  1371. {
  1372. pszThisFileName = pTitle->GetFileName();
  1373. if (pszThisFileName == NULL)
  1374. {
  1375. pTitle = pTitle->GetNext();
  1376. continue;
  1377. }
  1378. if( StrCmpIA(pszThisFileName, pszFileName) == 0 )
  1379. {
  1380. *ppTitle = pTitle;
  1381. return S_OK;
  1382. }
  1383. pTitle = pTitle->GetNext();
  1384. }
  1385. return E_FAIL;
  1386. }
  1387. //
  1388. // The following bit of code attempts to detect if we have arrived at a topic that is referenced in
  1389. // more than one place in the TOC. This is done by comparing the TOC location we lookup for the URL with
  1390. // the "m_dwCurrSlot" which is updated here and when any navigation call is made. If these TOC locations
  1391. // are different yet they reference the same topic, we utilize the "m_dwCurrSlot" TOC location for syncing
  1392. // needs.
  1393. //
  1394. // UpdateTopicSlot()
  1395. //
  1396. // Called ONLY from BeforeNavigate() before a navigation in allowed to proceed.
  1397. //
  1398. void CExCollection::UpdateTopicSlot(DWORD dwSlot, DWORD dwTN, CExTitle* pTitle)
  1399. {
  1400. if ( m_dwCurrSlot && dwSlot != m_dwCurrSlot )
  1401. {
  1402. // if ( (dwTN != m_dwCurrTN) && dwSlot )
  1403. if ( (dwTN != m_dwCurrTN) )
  1404. m_dwCurrSlot = dwSlot;
  1405. else
  1406. return;
  1407. }
  1408. else
  1409. m_dwCurrSlot = dwSlot;
  1410. m_dwCurrTN = dwTN;
  1411. m_pCurrTitle = pTitle;
  1412. }
  1413. HRESULT CExCollection::Sync(CPointerList *pHier, const CHAR* pszURL)
  1414. {
  1415. CTreeNode* pThis = NULL;
  1416. CExTitle *pTitle = NULL;
  1417. CTreeNode *pParent = NULL;
  1418. if ( m_dwCurrSlot )
  1419. {
  1420. if( IsBadReadPtr(m_pCurrTitle, sizeof(CExTitle)) )
  1421. return E_FAIL;
  1422. if (! SUCCEEDED(m_pCurrTitle->Slot2TreeNode(m_dwCurrSlot, &pThis)) )
  1423. return E_FAIL;
  1424. }
  1425. else if (pszURL) {
  1426. if (! SUCCEEDED(URL2ExTitle(pszURL, &pTitle)) )
  1427. return E_FAIL;
  1428. // MAJOR HACK to fix a show stopper for nt5. This code assumes nt's superchm authoring of URLS's as follows
  1429. // MS-ITS:dkconcepts.chm::/defrag_overview_01.htm. This code assumes ansi URL strings
  1430. if (pTitle->m_pParent)
  1431. {
  1432. char szBuf[MAX_URL];
  1433. strcpy(szBuf, pszURL);
  1434. char *pszLastWak, *pszCurChar;
  1435. pszLastWak = NULL;
  1436. pszCurChar = szBuf;
  1437. while (*pszCurChar)
  1438. {
  1439. // if we get to the :: we are at the end of the pathing information
  1440. if (*pszCurChar == ':' && *(pszCurChar+1) == ':')
  1441. break;
  1442. if (*pszCurChar == '\\' || *pszCurChar == '/')
  1443. pszLastWak = pszCurChar;
  1444. pszCurChar++;
  1445. }
  1446. if (pszLastWak)
  1447. {
  1448. char szBuf2[MAX_URL];
  1449. pszLastWak ++;
  1450. strcpy(szBuf2, pszLastWak);
  1451. strcpy(szBuf, txtMsItsMoniker);
  1452. pszCurChar = szBuf2;
  1453. strcat(szBuf, szBuf2);
  1454. if (! SUCCEEDED(pTitle->m_pParent->GetURLTreeNode(szBuf, &pThis, FALSE)) )
  1455. return E_FAIL;
  1456. }
  1457. }
  1458. else if (! SUCCEEDED(pTitle->GetURLTreeNode(pszURL, &pThis)) )
  1459. {
  1460. return E_FAIL;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. return E_FAIL;
  1466. }
  1467. // Make sure we have a valid CTreeNode pointer (Whistler bug #8112 & 8101).
  1468. // The above code below "MAJOR HACK" doesn't appear to work because:
  1469. // 1) we fall out of the parsing loop before pszLastWak is set, and
  1470. // 2) even if pszLastWak was set, GetURLTreeNode() fails on the
  1471. // generated URL.
  1472. //
  1473. // In the case of bug #8112 & 8101, we get to this point and pThis has not
  1474. // been set to a valid CTreeNode (thus the crash). The safest thing to do
  1475. // is simply call GetURLTreeNode here and get the CTreeNode pointer.
  1476. //
  1477. if(!pThis)
  1478. {
  1479. if (!SUCCEEDED(pTitle->GetURLTreeNode(pszURL, &pThis)))
  1480. return E_FAIL;
  1481. }
  1482. pHier->Add(pThis);
  1483. // now get all of the parents
  1484. for (pParent = pThis->GetParent(); pParent; pParent = pParent->GetParent())
  1485. pHier->Add(pParent);
  1486. return S_OK;
  1487. }
  1488. CFolder *CExCollection::FindTitleFolder(CExTitle *pTitle)
  1489. {
  1490. CFolder *p;
  1491. LISTITEM *pItem;
  1492. pItem = m_Collection.m_RefTitles.First();
  1493. while (pItem)
  1494. {
  1495. p = (CFolder *)pItem->pItem;
  1496. if (pTitle->GetCTitle() && _tcsicmp(pTitle->GetCTitle()->GetId(), p->GetTitle() + 1) == 0 &&
  1497. pTitle->GetCTitle()->GetLanguage() == p->GetLanguage())
  1498. return p;
  1499. pItem = m_Collection.m_RefTitles.Next(pItem);
  1500. }
  1501. return NULL;
  1502. }
  1503. // <mc>
  1504. // I've modified this function be be more generic. It returns a local storage path according to
  1505. // the same rules as always:
  1506. //
  1507. // 1.) location of local .COL
  1508. // 2.) "windir"\profiles\"user"\application data\microsoft\htmlhelp directory.
  1509. //
  1510. // The function mow takes an extension name which will be used to qualify the requested storage
  1511. // pathname.
  1512. //
  1513. // **** WARNING ****
  1514. // We return a pointer from this function. Callers should make a copy of the string immeadiatly
  1515. // after they call this function rather than placing any reliance of the integrity of the pointer returned.
  1516. //
  1517. // </mc>
  1518. const CHAR* CExCollection::GetLocalStoragePathname(const CHAR* pszExt)
  1519. {
  1520. // TODO: read this in from the XML file
  1521. // (for now base it on the collection pathname)
  1522. //
  1523. // TODO: check write permission of destination, if not allowed
  1524. // the set the path to the system directory or some
  1525. // other default writable location
  1526. //
  1527. // TODO: check for sufficient disk space.
  1528. static CHAR szPathname[MAX_PATH];
  1529. CHAR szFileName[MAX_PATH];
  1530. CHAR* pszExtension;
  1531. UINT uiDt = DRIVE_UNKNOWN;
  1532. if( !pszExt && *pszExt )
  1533. return NULL;
  1534. // if we want the chw and it is already set then return it and bail out
  1535. if( !strcmp( pszExt, ".chw" ) ) {
  1536. if( m_szWordWheelPathname )
  1537. return m_szWordWheelPathname;
  1538. }
  1539. // If we're operating a collection, use the location of the collection file and the collection
  1540. // file root name as the .chm name.
  1541. //
  1542. if (! m_bSingleTitle )
  1543. strcpy(szPathname, m_Collection.GetCollectionFileName());
  1544. else // else, in single title mode, use master .chm name and location.
  1545. strcpy(szPathname ,GetPathName());
  1546. CHAR* pszFilePart = NULL;
  1547. GetFullPathName( szPathname, sizeof(szPathname), szPathname, &pszFilePart );
  1548. // get the drive of the path
  1549. CHAR szDriveRoot[4];
  1550. strncpy( szDriveRoot, szPathname, 4 );
  1551. szDriveRoot[3] = '\0';
  1552. // make sure to add a backslash if the second char is a colon
  1553. // sometimes we will get just "d:" instead of "d:\" and thus
  1554. // GetDriveType will fail under this circumstance
  1555. if( szDriveRoot[1] == ':' ) {
  1556. szDriveRoot[2] = '\\';
  1557. szDriveRoot[3] = 0;
  1558. }
  1559. // Get media type
  1560. if( szDriveRoot[1] == ':' && szDriveRoot[2] == '\\' ) {
  1561. uiDt = GetDriveType(szDriveRoot);
  1562. }
  1563. else if( szDriveRoot[0] == '\\' && szDriveRoot[1] == '\\' ) {
  1564. uiDt = DRIVE_REMOTE;
  1565. }
  1566. // If removable media or not write access then write to the %windir%\profiles... directory
  1567. if( !( ((uiDt == DRIVE_FIXED) || (uiDt == DRIVE_REMOTE) || (uiDt == DRIVE_RAMDISK)) ) ) {
  1568. strcpy(szFileName, FindFilePortion(szPathname));
  1569. HHGetUserDataPath( szPathname );
  1570. CatPath(szPathname, szFileName);
  1571. }
  1572. // for chw files this location must be writeable
  1573. if( !strcmp( pszExt, ".chw" ) )
  1574. {
  1575. if( (pszExtension = StrRChr( szPathname, '.' )) )
  1576. *pszExtension = '\0';
  1577. strcat( szPathname, ".foo" );
  1578. HANDLE hFile = CreateFile(szPathname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, 0);
  1579. if (INVALID_HANDLE_VALUE == hFile)
  1580. {
  1581. strcpy(szFileName, FindFilePortion(szPathname));
  1582. HHGetUserDataPath( szPathname );
  1583. CatPath(szPathname, szFileName);
  1584. }
  1585. else
  1586. {
  1587. CloseHandle(hFile);
  1588. DeleteFile(szPathname);
  1589. }
  1590. }
  1591. // now update the extension
  1592. if( (pszExtension = StrRChr( szPathname, '.' )) )
  1593. *pszExtension = '\0';
  1594. strcat( szPathname, pszExt );
  1595. // for the chw file, save it
  1596. if( !strcmp( pszExt, ".chw" ) )
  1597. SetWordWheelPathname( szPathname );
  1598. return( szPathname );
  1599. }
  1600. const CHAR* CExCollection::GetUserCHSLocalStoragePathnameByLanguage()
  1601. {
  1602. // TODO: check for sufficient disk space.
  1603. static CHAR szPathname[MAX_PATH];
  1604. CHAR* pszExtension;
  1605. // If we're operating a collection, use the location of the collection file and the collection
  1606. // file root name as the .chm name.
  1607. //
  1608. if (! m_bSingleTitle )
  1609. strcpy(szPathname, m_Collection.GetCollectionFileName());
  1610. else // else, in single title mode, use master .chm name and location.
  1611. strcpy(szPathname ,GetPathName());
  1612. CHAR* pszFilePart = NULL;
  1613. GetFullPathName( szPathname, sizeof(szPathname), szPathname, &pszFilePart );
  1614. // now get the users path name
  1615. char szUserPath[MAX_PATH];
  1616. if (HHGetCurUserDataPath( szUserPath ) == S_OK)
  1617. {
  1618. // append language id
  1619. CHAR szTemp[5];
  1620. CHAR* pszName;
  1621. LANGID LangId;
  1622. m_Collection.GetMasterCHM(&pszName, &LangId);
  1623. wsprintf(szTemp, "%d", LangId);
  1624. CatPath(szUserPath, szTemp);
  1625. if( !IsDirectory(szUserPath) )
  1626. CreateDirectory( szUserPath, NULL );
  1627. CatPath(szUserPath, pszFilePart);
  1628. strcpy(szPathname, szUserPath);
  1629. }
  1630. // now update the extension
  1631. if( (pszExtension = StrRChr( szPathname, '.' )) )
  1632. *pszExtension = '\0';
  1633. strcat( szPathname, ".chs" );
  1634. return( szPathname );
  1635. }
  1636. const CHAR* CExCollection::GetUserCHSLocalStoragePathname()
  1637. {
  1638. // TODO: check for sufficient disk space.
  1639. static CHAR szPathname[MAX_PATH];
  1640. CHAR* pszExtension;
  1641. // If we're operating a collection, use the location of the collection file and the collection
  1642. // file root name as the .chm name.
  1643. //
  1644. if (! m_bSingleTitle )
  1645. strcpy(szPathname, m_Collection.GetCollectionFileName());
  1646. else // else, in single title mode, use master .chm name and location.
  1647. strcpy(szPathname ,GetPathName());
  1648. CHAR* pszFilePart = NULL;
  1649. GetFullPathName( szPathname, sizeof(szPathname), szPathname, &pszFilePart );
  1650. // now get the users path name
  1651. char szUserPath[MAX_PATH];
  1652. if (HHGetCurUserDataPath( szUserPath ) == S_OK)
  1653. {
  1654. CatPath(szUserPath, pszFilePart);
  1655. strcpy(szPathname, szUserPath);
  1656. }
  1657. // now update the extension
  1658. if( (pszExtension = StrRChr( szPathname, '.' )) )
  1659. *pszExtension = '\0';
  1660. strcat( szPathname, ".chs" );
  1661. return( szPathname );
  1662. }
  1663. const CHAR* CExCollection::SetWordWheelPathname( const CHAR* pszWordWheelPathname )
  1664. {
  1665. // if we already have one, free it
  1666. if( m_szWordWheelPathname ) {
  1667. delete [] (CHAR*) m_szWordWheelPathname;
  1668. m_szWordWheelPathname = NULL;
  1669. }
  1670. // allocate a new once based on the size of the input buffer
  1671. int iLen = (int)strlen( pszWordWheelPathname );
  1672. m_szWordWheelPathname = new char[iLen+10]; // Add 10 for future extension additions.
  1673. strcpy( (CHAR*) m_szWordWheelPathname, pszWordWheelPathname );
  1674. return m_szWordWheelPathname;
  1675. }
  1676. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1677. // CExTitle implementation
  1678. void CExTitle::_CExTitle() // <--- put all shared initialization here
  1679. {
  1680. m_pUsedLocation = NULL;
  1681. m_bOpen = FALSE;
  1682. m_dwNodeOffsetInParentTitle = 0;
  1683. m_pTitleFTS = NULL;
  1684. m_pCFileSystem = NULL;
  1685. m_pTocNodes = m_pTopics = m_pStrTbl = m_pUrlTbl = m_pUrlStrings = m_pITBits = NULL;
  1686. m_pNext = NULL;
  1687. m_pHeader = NULL;
  1688. m_pInfo = NULL;
  1689. m_pInfo2 = NULL;
  1690. m_fIsChiFile = FALSE;
  1691. m_uiVolumeOrder = (UINT) -1;
  1692. m_bChiChmChecked = FALSE;
  1693. m_bIsValidTitle = FALSE;
  1694. m_cMapIds = 0;
  1695. m_pMapIds = NULL;
  1696. //[ZERO IDXHDR] Mark the m_IdxHeader as being uninitialized.
  1697. m_pIdxHeader = NULL;
  1698. m_pKid = m_pNextKid = m_pParent = NULL;
  1699. m_szAttachmentPathName[0] = 0;
  1700. }
  1701. CExTitle::CExTitle(const CHAR* pszFileName, CExCollection *pCollection)
  1702. {
  1703. _CExTitle();
  1704. m_ContentFileName = pszFileName;
  1705. m_dwColNo = 0;
  1706. m_pTitle = NULL;
  1707. m_pCollection = pCollection;
  1708. m_pInfo2 = new CTitleInformation2( GetIndexFileName() );
  1709. }
  1710. CExTitle::CExTitle(CTitle *p, DWORD ColNo, CExCollection *pCollection)
  1711. {
  1712. _CExTitle();
  1713. m_dwColNo = ColNo;
  1714. m_pTitle = p;
  1715. m_pCollection = pCollection;
  1716. const CHAR* pFileName = NULL ;
  1717. ASSERT(p) ;
  1718. FindUsedLocation();
  1719. if (GetUsedLocation() == NULL)
  1720. return;
  1721. if (GetUsedLocation()->IndexFileName)
  1722. pFileName = GetUsedLocation()->IndexFileName ;
  1723. else
  1724. pFileName = GetUsedLocation()->FileName ;
  1725. m_pInfo2 = new CTitleInformation2(pFileName);
  1726. }
  1727. CExTitle::~CExTitle()
  1728. {
  1729. CloseTitle();
  1730. if ( m_pTitleFTS )
  1731. delete m_pTitleFTS;
  1732. m_pTitleFTS = NULL;
  1733. if ( m_pInfo2 )
  1734. delete m_pInfo2;
  1735. m_pInfo2 = NULL;
  1736. if ( m_pInfo )
  1737. delete m_pInfo;
  1738. m_pInfo = NULL;
  1739. }
  1740. void CExTitle::CloseTitle()
  1741. {
  1742. if ( m_pTocNodes )
  1743. delete m_pTocNodes;
  1744. m_pTocNodes = NULL;
  1745. if ( m_pTopics )
  1746. delete m_pTopics;
  1747. m_pTopics = NULL;
  1748. if ( m_pStrTbl )
  1749. delete m_pStrTbl;
  1750. m_pStrTbl = NULL;
  1751. if ( m_pUrlTbl )
  1752. delete m_pUrlTbl;
  1753. m_pUrlTbl = NULL;
  1754. if ( m_pUrlStrings )
  1755. delete m_pUrlStrings;
  1756. m_pUrlStrings = NULL;
  1757. if ( m_pITBits )
  1758. delete m_pITBits;
  1759. m_pITBits = NULL;
  1760. if ( m_pHeader )
  1761. lcFree(m_pHeader);
  1762. m_pHeader = NULL;
  1763. if ( m_pCFileSystem ) {
  1764. m_pCFileSystem->Close();
  1765. delete m_pCFileSystem;
  1766. }
  1767. m_pCFileSystem = NULL;
  1768. m_bOpen = FALSE;
  1769. }
  1770. static const char txtChiFile[] = ".chi";
  1771. BOOL CExTitle::OpenTitle()
  1772. {
  1773. if (m_bOpen)
  1774. return TRUE;
  1775. // get our title locations
  1776. const CHAR* pszContentFilename = GetPathName();
  1777. const CHAR* pszIndexFilename = GetIndexFileName();
  1778. // bail out if neither file specified
  1779. if( !pszContentFilename && !pszIndexFilename )
  1780. return FALSE;
  1781. // for the single title case, the command line is simply
  1782. // the chm file so we will have to see if the chi files lives in the
  1783. // same location. If it does not, then we have a monolithic title
  1784. // condition.
  1785. char szIndexFilename[_MAX_PATH];
  1786. if( m_pCollection && m_pCollection->IsSingleTitle() && pszContentFilename ) {
  1787. strcpy( szIndexFilename, pszContentFilename );
  1788. CHAR* psz;
  1789. if( (psz = StrRChr(szIndexFilename, '.')) )
  1790. strcpy(psz, txtChiFile);
  1791. else
  1792. strcat(szIndexFilename, txtChiFile);
  1793. if (GetFileAttributes(szIndexFilename) != HFILE_ERROR)
  1794. pszIndexFilename = szIndexFilename;
  1795. }
  1796. // monolithic title?
  1797. BOOL bMonolithic = FALSE;
  1798. if( !pszIndexFilename || !pszContentFilename ||
  1799. (strcmpi( pszContentFilename, pszIndexFilename ) == 0) ) {
  1800. bMonolithic = TRUE;
  1801. }
  1802. // bail out if no content file
  1803. if( !pszContentFilename )
  1804. return FALSE;
  1805. // now open the title files
  1806. return exOpenFile( pszContentFilename, (bMonolithic || pszIndexFilename[0] == NULL) ? NULL : pszIndexFilename );
  1807. }
  1808. BOOL CExTitle::FindUsedLocation()
  1809. {
  1810. if (m_pUsedLocation)
  1811. return TRUE;
  1812. char drive[_MAX_DRIVE];
  1813. char dir[_MAX_DIR];
  1814. char fname[_MAX_FNAME];
  1815. char ext[_MAX_EXT];
  1816. // find the correct location to use
  1817. // first look for the newest local and entry for this collection
  1818. m_pUsedLocation = m_pTitle->m_pHead;
  1819. DWORD dwNewestLocal = 0;
  1820. DWORD dwNewest = 0;
  1821. LOCATIONHISTORY *pNewestLocal = NULL;
  1822. LOCATIONHISTORY *pNewest = NULL;
  1823. LOCATIONHISTORY *pThisCol = NULL;
  1824. while (m_pUsedLocation)
  1825. {
  1826. _splitpath( m_pUsedLocation->FileName, drive, dir, fname, ext );
  1827. strcpy(dir, drive);
  1828. if (dir[0] != NULL)
  1829. {
  1830. dir[1] = ':';
  1831. dir[2] = '\\';
  1832. dir[3] = NULL;
  1833. }
  1834. if (GetDriveType(dir) == DRIVE_FIXED)
  1835. {
  1836. if (m_pUsedLocation->Version > dwNewestLocal)
  1837. {
  1838. dwNewestLocal = m_pUsedLocation->Version;
  1839. pNewestLocal = m_pUsedLocation;
  1840. }
  1841. }
  1842. if (m_pUsedLocation->Version >= dwNewest)
  1843. {
  1844. dwNewest = m_pUsedLocation->Version;
  1845. pNewest = m_pUsedLocation;
  1846. }
  1847. if (m_pUsedLocation->CollectionNumber == m_dwColNo)
  1848. {
  1849. if (pThisCol)
  1850. {
  1851. if (pThisCol->Version <= m_pUsedLocation->Version)
  1852. pThisCol = m_pUsedLocation;
  1853. }
  1854. else
  1855. pThisCol = m_pUsedLocation;
  1856. }
  1857. m_pUsedLocation = m_pUsedLocation->pNext;
  1858. }
  1859. m_pUsedLocation = pThisCol;
  1860. if (m_pUsedLocation == NULL)
  1861. return FALSE;
  1862. m_ContentFileName = m_pUsedLocation->FileName;
  1863. m_IndexFileName = m_pUsedLocation->IndexFileName;
  1864. return TRUE;
  1865. }
  1866. BOOL CExTitle::exOpenFile(const CHAR* pszContent, const CHAR* pszIndex)
  1867. {
  1868. HRESULT hr;
  1869. if (m_bOpen)
  1870. return TRUE;
  1871. ASSERT_COMMENT(!m_pCFileSystem, "exOpenFile already called once");
  1872. m_pCFileSystem = new CFileSystem();
  1873. if ( m_pCFileSystem->Init() != S_OK )
  1874. goto failure;
  1875. if ( pszIndex )
  1876. {
  1877. hr = m_pCFileSystem->Open(pszIndex);
  1878. m_IndexFileName = pszIndex;
  1879. m_fIsChiFile = TRUE;
  1880. }
  1881. else
  1882. hr = m_pCFileSystem->Open(pszContent);
  1883. if (FAILED(hr))
  1884. goto failure;
  1885. // title information pointer
  1886. if (! m_pInfo )
  1887. {
  1888. m_pInfo = new CTitleInformation( m_pCFileSystem );
  1889. if (! m_pInfo->GetIdxHeader(&m_pIdxHeader) )
  1890. {
  1891. //
  1892. // Open and read the idxheader subifle.
  1893. //
  1894. CPagedSubfile* pHdrSubFile;
  1895. BYTE* pb;
  1896. pHdrSubFile = new CPagedSubfile;
  1897. if ( SUCCEEDED(hr = pHdrSubFile->Open(this, txtIdxHdrFile)) )
  1898. {
  1899. if ( (pb = (BYTE*)pHdrSubFile->Offset(0)) )
  1900. {
  1901. CopyMemory((PVOID)m_pIdxHeader, (LPCVOID)pb, sizeof(IDXHEADER));
  1902. // m_pIdxHeader->dwOffsMergedTitles = (DWORD*)&m_pIdxHeader->pad; // 64bit overwrite
  1903. }
  1904. }
  1905. if( pHdrSubFile )
  1906. delete pHdrSubFile;
  1907. }
  1908. }
  1909. m_bOpen = TRUE;
  1910. //
  1911. // Init full-text for title
  1912. //
  1913. if(!m_pTitleFTS && GetInfo()->IsFullTextSearch())
  1914. {
  1915. m_pTitleFTS = new CTitleFTS( pszContent, GetInfo()->GetLanguage(), this );
  1916. }
  1917. m_ITCnt = GetInfo()->GetInfoTypeCount();
  1918. if( m_pCollection )
  1919. {
  1920. if ( m_pCollection->GetMasterTitle() != this ) // ALWAYS cache the CExTitle data for the master title. i.e.
  1921. m_pCollection->GetOpenSlot(this);
  1922. }
  1923. // BUGBUG - return a value based on hr when these subfiles exist
  1924. return(TRUE); // TRUE on success.
  1925. failure:
  1926. delete m_pCFileSystem;
  1927. m_pCFileSystem = NULL;
  1928. return FALSE;
  1929. }
  1930. BOOL CExTitle::EnsureChmChiMatch( CHAR* pszChm )
  1931. {
  1932. if (! m_fIsChiFile )
  1933. return TRUE;
  1934. if ( !pszChm && m_bChiChmChecked )
  1935. return m_bIsValidTitle;
  1936. if( pszChm ) {
  1937. m_bChiChmChecked = FALSE;
  1938. m_bIsValidTitle = FALSE;
  1939. }
  1940. else
  1941. m_bChiChmChecked = TRUE;
  1942. FILETIME ftChi, ftChm;
  1943. CTitleInformation* pChmInfo = NULL;
  1944. HRESULT hr;
  1945. ftChi = GetInfo()->GetFileTime();
  1946. CFileSystem* pCFileSysChm = new CFileSystem();
  1947. if ( pCFileSysChm->Init() == S_OK )
  1948. {
  1949. const CHAR* pszChmTry;
  1950. if( pszChm )
  1951. pszChmTry = pszChm;
  1952. else
  1953. pszChmTry = GetPathName();
  1954. if ( SUCCEEDED((hr = pCFileSysChm->Open(pszChmTry))) )
  1955. {
  1956. pChmInfo = new CTitleInformation(pCFileSysChm);
  1957. ftChm = pChmInfo->GetFileTime();
  1958. if ( (ftChm.dwHighDateTime != ftChi.dwHighDateTime) || (ftChm.dwLowDateTime != ftChi.dwLowDateTime) )
  1959. {
  1960. if( pszChm )
  1961. m_bIsValidTitle = FALSE;
  1962. else if ( MsgBox(IDS_CHM_CHI_MISMATCH, GetPathName(), MB_OKCANCEL | MB_ICONWARNING | MB_TASKMODAL) == IDOK )
  1963. m_bIsValidTitle = TRUE;
  1964. else
  1965. m_bIsValidTitle = FALSE;
  1966. }
  1967. else
  1968. m_bIsValidTitle = TRUE;
  1969. }
  1970. }
  1971. delete pCFileSysChm;
  1972. if ( pChmInfo )
  1973. delete pChmInfo;
  1974. return m_bIsValidTitle;
  1975. }
  1976. const CHAR* CExTitle::GetFileName()
  1977. {
  1978. if (m_pTitle && GetUsedLocation() == NULL)
  1979. return NULL;
  1980. if (GetUsedLocation())
  1981. return FindFilePortion(GetUsedLocation()->FileName);
  1982. else
  1983. return FindFilePortion(m_ContentFileName);
  1984. }
  1985. const CHAR* CExTitle::GetPathName()
  1986. {
  1987. if (m_pTitle && GetUsedLocation() == NULL)
  1988. return NULL;
  1989. if (GetUsedLocation())
  1990. return GetUsedLocation()->FileName;
  1991. else
  1992. return m_ContentFileName;
  1993. }
  1994. const CHAR* CExTitle::GetQueryName()
  1995. {
  1996. if (m_pTitle && GetUsedLocation() == NULL)
  1997. return NULL;
  1998. if (GetUsedLocation())
  1999. {
  2000. char *pszQueryName = GetUsedLocation()->QueryFileName;
  2001. if( pszQueryName && *pszQueryName) // don't want a null string
  2002. return pszQueryName;
  2003. else
  2004. return NULL;
  2005. }
  2006. else
  2007. {
  2008. // DON: Is this right?
  2009. //
  2010. return NULL;
  2011. }
  2012. }
  2013. const CHAR* CExTitle::GetIndexFileName()
  2014. {
  2015. if (m_pTitle && GetUsedLocation() == NULL)
  2016. return NULL;
  2017. if (GetUsedLocation())
  2018. {
  2019. return GetUsedLocation()->IndexFileName;
  2020. }
  2021. else
  2022. {
  2023. if ( (CHAR*)m_IndexFileName )
  2024. return m_IndexFileName;
  2025. else
  2026. return GetPathName();
  2027. }
  2028. }
  2029. const CHAR* CExTitle::GetCurrentAttachmentName()
  2030. {
  2031. return (const CHAR*) &m_szAttachmentPathName;
  2032. }
  2033. const CHAR* CExTitle::SetCurrentAttachmentName( const CHAR* pszAttachmentPathName )
  2034. {
  2035. if( pszAttachmentPathName && pszAttachmentPathName[0] )
  2036. strcpy( m_szAttachmentPathName, pszAttachmentPathName );
  2037. return (const CHAR*) &m_szAttachmentPathName;
  2038. }
  2039. DWORD CExTitle::GetRootSlot(void)
  2040. {
  2041. BYTE* pb;
  2042. if (m_bOpen == FALSE)
  2043. {
  2044. if (OpenTitle() == FALSE)
  2045. return 0;
  2046. }
  2047. if (!m_pTocNodes)
  2048. {
  2049. m_pTocNodes = new CPagedSubfile;
  2050. if (FAILED(m_pTocNodes->Open(this, txtTocIdxFile)))
  2051. {
  2052. delete m_pTocNodes;
  2053. m_pTocNodes = NULL;
  2054. return 0;
  2055. }
  2056. }
  2057. if (! m_pHeader )
  2058. {
  2059. m_pHeader = (TOCIDX_HDR*)lcCalloc(sizeof(TOCIDX_HDR));
  2060. if (! (pb = (BYTE*)m_pTocNodes->Offset(0)) )
  2061. return 0; // m_pHeader will be freeed in destructor.
  2062. CopyMemory((PVOID)m_pHeader, (LPCVOID)pb, sizeof(TOCIDX_HDR));
  2063. }
  2064. return(m_pHeader->dwOffsRootNode);
  2065. }
  2066. HRESULT CExTitle::GetRootNode(TOC_FOLDERNODE *pNode)
  2067. {
  2068. HRESULT hr = E_FAIL;
  2069. DWORD dwNode;
  2070. unsigned int uiWidth;
  2071. const unsigned int *pdwITBits;
  2072. CSubSet* pSS;
  2073. dwNode = GetRootSlot();
  2074. do
  2075. {
  2076. if ( !SUCCEEDED(GetNode(dwNode, pNode)) )
  2077. return hr;
  2078. if ( pNode->dwFlags & TOC_HAS_UNTYPED )
  2079. break;
  2080. if ( (pSS = m_pCollection->m_pSubSets->GetTocSubset()) && pSS->m_bIsEntireCollection )
  2081. break;
  2082. if ( pNode->dwFlags & TOC_FOLDER )
  2083. {
  2084. if ( m_ITCnt > 31 )
  2085. {
  2086. uiWidth = (((m_ITCnt / 32) + 1) * 4);
  2087. pdwITBits = GetITBits(pNode->dwIT_Idx * uiWidth);
  2088. }
  2089. else
  2090. pdwITBits = (const unsigned int *)&pNode->dwIT_Idx;
  2091. }
  2092. else
  2093. pdwITBits = GetTopicITBits(pNode->dwOffsTopic);
  2094. } while ( !m_pCollection->m_pSubSets->fTOCFilter(pdwITBits) && (dwNode = pNode->dwOffsNext) );
  2095. if ( dwNode )
  2096. hr = S_OK;
  2097. return hr;
  2098. }
  2099. HRESULT CExTitle::GetNode(DWORD iNode, TOC_FOLDERNODE *pNode)
  2100. {
  2101. BYTE* pb;
  2102. TOC_FOLDERNODE* pLocalNode;
  2103. HRESULT hr = E_FAIL;
  2104. if ( !iNode )
  2105. return hr;
  2106. if (m_bOpen == FALSE)
  2107. {
  2108. if (OpenTitle() == FALSE)
  2109. return hr;
  2110. }
  2111. if (!m_pTocNodes)
  2112. {
  2113. m_pTocNodes = new CPagedSubfile;
  2114. if (FAILED(hr = m_pTocNodes->Open(this, txtTocIdxFile)))
  2115. {
  2116. delete m_pTocNodes;
  2117. m_pTocNodes = NULL;
  2118. return hr;
  2119. }
  2120. }
  2121. if ( (pb = (BYTE*)m_pTocNodes->Offset(iNode)) )
  2122. {
  2123. pLocalNode = (TOC_FOLDERNODE*)pb;
  2124. if ( pLocalNode->dwFlags & TOC_FOLDER )
  2125. CopyMemory((PVOID)pNode, (LPCVOID)pb, sizeof(TOC_FOLDERNODE));
  2126. else
  2127. {
  2128. CopyMemory((PVOID)pNode, (LPCVOID)pb, sizeof(TOC_LEAFNODE));
  2129. pNode->dwOffsChild = 0;
  2130. }
  2131. hr = S_OK;
  2132. }
  2133. return hr;
  2134. }
  2135. // CExTitle::GetString()
  2136. //
  2137. const CHAR* CExTitle::GetString( DWORD dwOffset )
  2138. {
  2139. HRESULT hr;
  2140. const CHAR* pStr;
  2141. if( !m_bOpen )
  2142. if( !OpenTitle() )
  2143. return NULL;
  2144. if( !m_pStrTbl ) {
  2145. m_pStrTbl = new CPagedSubfile;
  2146. if( FAILED(hr = m_pStrTbl->Open(this,txtStringsFile)) ) {
  2147. delete m_pStrTbl;
  2148. m_pStrTbl = NULL;
  2149. return NULL;
  2150. }
  2151. }
  2152. pStr = (const CHAR*) m_pStrTbl->Offset( dwOffset );
  2153. return pStr;
  2154. }
  2155. // CExTitle::GetString()
  2156. //
  2157. HRESULT CExTitle::GetString( DWORD dwOffset, CHAR* psz, int cb )
  2158. {
  2159. const CHAR* pStr = GetString( dwOffset );
  2160. if( pStr ) {
  2161. strncpy( psz, pStr, cb );
  2162. psz[cb-1] = 0;
  2163. return S_OK;
  2164. }
  2165. else
  2166. return E_FAIL;
  2167. }
  2168. // CExTitle::GetString()
  2169. //
  2170. HRESULT CExTitle::GetString( DWORD dwOffset, CStr* pcsz )
  2171. {
  2172. const CHAR* pStr = GetString( dwOffset );
  2173. if( pStr ) {
  2174. *pcsz = pStr;
  2175. return S_OK;
  2176. }
  2177. else
  2178. return E_FAIL;
  2179. }
  2180. // CExTitle::GetString()
  2181. //
  2182. HRESULT CExTitle::GetString( DWORD dwOffset, WCHAR* pwsz, int cch )
  2183. {
  2184. const CHAR* pStr = GetString( dwOffset );
  2185. if( pStr ) {
  2186. MultiByteToWideChar( GetInfo()->GetCodePage(), 0, pStr, -1, pwsz, cch );
  2187. return S_OK;
  2188. }
  2189. else
  2190. return E_FAIL;
  2191. }
  2192. // CExTitle::GetString()
  2193. //
  2194. HRESULT CExTitle::GetString( DWORD dwOffset, CWStr* pcwsz )
  2195. {
  2196. const CHAR* pStr = GetString( dwOffset );
  2197. if( pStr ) {
  2198. MultiByteToWideChar( GetInfo()->GetCodePage(), 0, pStr, -1, *pcwsz, -1 );
  2199. return S_OK;
  2200. }
  2201. else
  2202. return E_FAIL;
  2203. }
  2204. //
  2205. // CExTitle::InfoTypeFilter()
  2206. //
  2207. // Function determines if the given topic id (topic number) is part of the currently
  2208. // selected InfoType profile.
  2209. //
  2210. // ENTRY:
  2211. // dwTopic - Topic number.
  2212. //
  2213. // EXIT:
  2214. // BOOL - TRUE if topic is part of currently selected infotype profile. FALSE if not.
  2215. //
  2216. BOOL CExTitle::InfoTypeFilter(CSubSet* pSubSet, DWORD dwTopic)
  2217. {
  2218. const unsigned int *pdwITBits;
  2219. if (m_bOpen == FALSE)
  2220. {
  2221. if (OpenTitle() == FALSE)
  2222. return FALSE;
  2223. }
  2224. pdwITBits = GetTopicITBits(dwTopic);
  2225. return pSubSet->Filter(pdwITBits);
  2226. }
  2227. //
  2228. // Given a topic number, fetch and return a pointer to the itbits
  2229. // in the form of a DWORD*
  2230. //
  2231. const unsigned int * CExTitle::GetTopicITBits(DWORD dwTN)
  2232. {
  2233. TOC_TOPIC TopicData;
  2234. unsigned int uiWidth;
  2235. static unsigned int dwITBits;
  2236. GetTopicData(dwTN, &TopicData);
  2237. if ( m_ITCnt > 15 )
  2238. {
  2239. uiWidth = (((m_ITCnt / 32) + 1) * 4);
  2240. return (GetITBits(TopicData.wIT_Idx * uiWidth));
  2241. }
  2242. else
  2243. {
  2244. dwITBits = 0;
  2245. dwITBits = TopicData.wIT_Idx;
  2246. return &dwITBits;
  2247. }
  2248. }
  2249. //
  2250. // Given an offset into the ITBITS subfile, fetch and return a pointer to the itbits
  2251. // in the form of a DWORD*
  2252. //
  2253. const unsigned int * CExTitle::GetITBits(DWORD dwOffsBits)
  2254. {
  2255. if (! m_pITBits )
  2256. {
  2257. m_pITBits = new CPagedSubfile;
  2258. if (FAILED(m_pITBits->Open(this, txtITBits)))
  2259. {
  2260. delete m_pITBits;
  2261. m_pITBits = NULL;
  2262. return NULL;
  2263. }
  2264. }
  2265. //
  2266. // Compute the width in DWORDS and get the bits.
  2267. //
  2268. return (const unsigned int *)m_pITBits->Offset(dwOffsBits);
  2269. }
  2270. // CExTitle::GetTopicData()
  2271. //
  2272. HRESULT CExTitle::GetTopicData(DWORD dwTopic, TOC_TOPIC * pTopicData)
  2273. {
  2274. HRESULT hr;
  2275. BYTE * pb;
  2276. if (m_bOpen == FALSE)
  2277. {
  2278. if (OpenTitle() == FALSE)
  2279. return E_FAIL;
  2280. }
  2281. if (!m_pTopics)
  2282. {
  2283. m_pTopics = new CPagedSubfile;
  2284. if (FAILED(hr = m_pTopics->Open(this, txtTopicsFile)))
  2285. {
  2286. delete m_pTopics;
  2287. m_pTopics = NULL;
  2288. return hr;
  2289. }
  2290. }
  2291. pb = (BYTE*)m_pTopics->Offset(dwTopic * sizeof(TOC_TOPIC));
  2292. if (pb)
  2293. {
  2294. memcpy(pTopicData, pb, sizeof(TOC_TOPIC));
  2295. return S_OK;
  2296. }
  2297. else
  2298. return E_FAIL;
  2299. }
  2300. HRESULT CExTitle::GetTopicName(DWORD dwTopic, CHAR* pszTitle, int cb)
  2301. {
  2302. TOC_TOPIC topic;
  2303. HRESULT hr;
  2304. if (SUCCEEDED(hr = GetTopicData(dwTopic, &topic)))
  2305. return GetString(topic.dwOffsTitle, pszTitle, cb);
  2306. else
  2307. return hr;
  2308. }
  2309. HRESULT CExTitle::GetTopicName(DWORD dwTopic, WCHAR* pwszTitle, int cch)
  2310. {
  2311. TOC_TOPIC topic;
  2312. HRESULT hr;
  2313. if (SUCCEEDED(hr = GetTopicData(dwTopic, &topic)))
  2314. return GetString(topic.dwOffsTitle, pwszTitle, cch);
  2315. else
  2316. return hr;
  2317. }
  2318. HRESULT CExTitle::GetTopicLocation(DWORD dwTopic, CHAR* pszLocation, int cb)
  2319. {
  2320. CTitleInformation* pInfo = GetInfo();
  2321. if( pInfo ) {
  2322. const CHAR* psz = NULL;
  2323. psz = pInfo->GetDefaultCaption();
  2324. if( !psz || !*psz )
  2325. psz = pInfo->GetShortName();
  2326. if( psz && *psz ) {
  2327. strncpy( pszLocation, psz, cb );
  2328. pszLocation[cb-1] = 0;
  2329. return S_OK;
  2330. }
  2331. }
  2332. return E_FAIL;
  2333. }
  2334. HRESULT CExTitle::GetTopicLocation(DWORD dwTopic, WCHAR* pwszLocation, int cch)
  2335. {
  2336. CTitleInformation* pInfo = GetInfo();
  2337. if( pInfo ) {
  2338. const CHAR* psz = NULL;
  2339. psz = pInfo->GetDefaultCaption();
  2340. if( !psz || !*psz )
  2341. psz = pInfo->GetShortName();
  2342. if( psz && *psz ) {
  2343. MultiByteToWideChar( GetInfo()->GetCodePage(), 0, psz, -1, pwszLocation, cch );
  2344. return S_OK;
  2345. }
  2346. }
  2347. return E_FAIL;
  2348. }
  2349. //
  2350. // GetUrlTocSlot();
  2351. //
  2352. // Translates a URL from this title into it's corisponding offset into the TOC.
  2353. // Function will also return the Topic number if desired in reference pdwTopicNumber.
  2354. //
  2355. HRESULT CExTitle::GetUrlTocSlot(const CHAR* pszURL, DWORD* pdwSlot, DWORD* pdwTopicNumber)
  2356. {
  2357. char szURL[MAX_URL];
  2358. HRESULT hr = E_FAIL;
  2359. strncpy( szURL, pszURL, MAX_URL );
  2360. szURL[MAX_URL-1] = 0;
  2361. // Remove all of the stuff from the url.
  2362. NormalizeUrlInPlace(szURL) ;
  2363. TOC_TOPIC Topic;
  2364. if (SUCCEEDED(URL2Topic(szURL, &Topic, pdwTopicNumber)))
  2365. {
  2366. *pdwSlot = Topic.dwOffsTOC_Node;
  2367. return S_OK;
  2368. }
  2369. CHAR* pszInterTopic = NULL;
  2370. if (pszInterTopic = StrChr(szURL, '#'))
  2371. {
  2372. *pszInterTopic = NULL;
  2373. if (SUCCEEDED(URL2Topic(szURL, &Topic, pdwTopicNumber)))
  2374. {
  2375. *pdwSlot = Topic.dwOffsTOC_Node;
  2376. return S_OK;
  2377. }
  2378. }
  2379. return E_FAIL;
  2380. }
  2381. //
  2382. // GetURLTreeNode()
  2383. //
  2384. // Translates a URL from this title into it's corisponding node in the TOC.
  2385. //
  2386. HRESULT CExTitle::GetURLTreeNode(const CHAR* pszURL, CTreeNode** ppTreeNode, BOOL bNormalize /* = TRUE */)
  2387. {
  2388. char szURL[MAX_URL];
  2389. HRESULT hr = E_FAIL;
  2390. strncpy( szURL, pszURL, MAX_URL );
  2391. szURL[MAX_URL-1] = 0;
  2392. // Remove all of the stuff from the url.
  2393. if (bNormalize)
  2394. NormalizeUrlInPlace(szURL) ;
  2395. TOC_TOPIC Topic;
  2396. if (SUCCEEDED(URL2Topic(szURL, &Topic)))
  2397. {
  2398. TOC_FOLDERNODE Node;
  2399. if (SUCCEEDED(GetNode(Topic.dwOffsTOC_Node, &Node)))
  2400. {
  2401. *ppTreeNode = new CExNode(&Node, this);
  2402. hr = S_OK;
  2403. }
  2404. }
  2405. return hr;
  2406. }
  2407. //
  2408. // Slot2TreeNode(DWORD dwSlot)
  2409. //
  2410. // Returns a tree node given a TOC "slot" number. A slot number is just the
  2411. // offset into the #TOCIDX subfile.
  2412. //
  2413. HRESULT CExTitle::Slot2TreeNode(DWORD dwSlot, CTreeNode** ppTreeNode)
  2414. {
  2415. TOC_FOLDERNODE Node;
  2416. if ( !IsBadReadPtr(this, sizeof(CExTitle)) && SUCCEEDED(GetNode(dwSlot, &Node)))
  2417. {
  2418. // *ppTreeNode = new CExNode(&Node, this, dwSlot);
  2419. *ppTreeNode = new CExNode(&Node, this);
  2420. return S_OK;
  2421. }
  2422. return E_FAIL;
  2423. }
  2424. HRESULT CExTitle::GetTopicURL(DWORD dwTopic, CHAR* pszURL, int cb, BOOL bFull )
  2425. {
  2426. TOC_TOPIC topic;
  2427. HRESULT hr;
  2428. CExTitle* pTitle;
  2429. CHAR* psz;
  2430. if (m_bOpen == FALSE)
  2431. {
  2432. if (OpenTitle() == FALSE)
  2433. return E_FAIL;
  2434. }
  2435. if (!m_pUrlTbl)
  2436. {
  2437. m_pUrlTbl = new CPagedSubfile;
  2438. if (FAILED(hr = m_pUrlTbl->Open(this, txtUrlTblFile)))
  2439. {
  2440. delete m_pUrlTbl;
  2441. m_pUrlTbl = NULL;
  2442. return hr;
  2443. }
  2444. }
  2445. if (!m_pUrlStrings)
  2446. {
  2447. m_pUrlStrings = new CPagedSubfile;
  2448. if (FAILED(hr = m_pUrlStrings->Open(this, txtUrlStrFile)))
  2449. {
  2450. delete m_pUrlStrings;
  2451. m_pUrlStrings = NULL;
  2452. return hr;
  2453. }
  2454. }
  2455. if ( (hr = GetTopicData(dwTopic, &topic)) == S_OK )
  2456. {
  2457. PCURL pUrlTbl;
  2458. if ( (pUrlTbl = (PCURL)m_pUrlTbl->Offset(topic.dwOffsURL)) )
  2459. {
  2460. PURLSTR purl = (PURLSTR) m_pUrlStrings->Offset(pUrlTbl->dwOffsURL);
  2461. if (purl)
  2462. {
  2463. // If not an interfile jump, the create the full URL
  2464. //
  2465. if (! StrChr(purl->szURL, ':'))
  2466. {
  2467. /*
  2468. * 22-Oct-1997 [ralphw] ASSERT on this rather then
  2469. * checking in retail, because if the caller is using
  2470. * MAX_URL or INTERNET_MAX_URL_LENGTH then an overflow
  2471. * will never happen.
  2472. */
  2473. pTitle = this;
  2474. psz = purl->szURL;
  2475. qualify:
  2476. ASSERT((strlen(psz) + strlen((g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore)) + strlen(pTitle->GetPathName()) + 7) < (size_t) cb);
  2477. if ((int) (strlen(psz) + strlen((g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore)) + strlen(pTitle->GetPathName()) + 7) > cb )
  2478. return E_OUTOFMEMORY;
  2479. strncpy(pszURL, (g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore), cb);
  2480. pszURL[cb-1] = 0;
  2481. if( bFull )
  2482. strcat(pszURL, pTitle->GetPathName());
  2483. else
  2484. strcat(pszURL, pTitle->GetFileName());
  2485. if (*psz != '/')
  2486. strcat(pszURL, txtSepBack);
  2487. else
  2488. strcat(pszURL, txtDoubleColonSep);
  2489. strcat(pszURL, psz);
  2490. }
  2491. else
  2492. {
  2493. // Check for a URL of this type: vb98.chm::\foo\bar\cat.htm
  2494. //
  2495. if ( psz = StrStr(purl->szURL, txtChmColon) )
  2496. {
  2497. psz += 4;
  2498. *psz = '\0';
  2499. pTitle = m_pCollection->TitleFromChmName(purl->szURL);
  2500. *psz = ':';
  2501. if ( pTitle )
  2502. {
  2503. psz += 2;
  2504. goto qualify;
  2505. }
  2506. }
  2507. // BUGBUG: we do the check here to see if the CHM exists,
  2508. // and if not, switch to the alternate URL (if there is one).
  2509. strncpy(pszURL, purl->szURL, cb);
  2510. pszURL[cb-1] = 0;
  2511. }
  2512. hr = S_OK;
  2513. }
  2514. }
  2515. }
  2516. return hr;
  2517. }
  2518. //
  2519. //
  2520. //
  2521. // we now have to support a new URL format for compiled files. The format is:
  2522. //
  2523. // mk:@MSITStore:mytitle.chm::/dir/mytopic.htm
  2524. //
  2525. // where "mk:@MSITStore:" can take any one of the many forms of our URL
  2526. // prefix and it may be optional. The "mytitle.chm" substring may or
  2527. // may not be a full pathname to the title. The remaining part is simply
  2528. // the pathname inside of the compiled title.
  2529. //
  2530. // When the URL is in the format, we need to change it to the fully
  2531. // qualified URL format which is:
  2532. //
  2533. // mk:@MSITStore:c:\titles\mytitle.chm::/dir/mytopic.htm
  2534. //
  2535. HRESULT CExTitle::ConvertURL( const CHAR* pszURLIn, CHAR* pszURLOut )
  2536. {
  2537. HRESULT hr = S_OK;
  2538. // prefix
  2539. if( IsSamePrefix(pszURLIn, txtMkStore, (int)strlen(txtMkStore) - 1) )
  2540. strcpy( pszURLOut, txtMkStore );
  2541. else if( IsSamePrefix(pszURLIn, txtMsItsMoniker, (int)strlen(txtMsItsMoniker) - 1) )
  2542. strcpy( pszURLOut, txtMsItsMoniker );
  2543. else if( IsSamePrefix(pszURLIn, txtItsMoniker, (int)strlen(txtItsMoniker) - 1) )
  2544. strcpy( pszURLOut, txtItsMoniker );
  2545. else {
  2546. strcpy( pszURLOut, pszURLIn );
  2547. return hr;
  2548. }
  2549. // title full pathname
  2550. CStr szPathName;
  2551. ConvertSpacesToEscapes( m_ContentFileName.psz, &szPathName );
  2552. strcat( pszURLOut, szPathName.psz );
  2553. // subfile pathname
  2554. char* pszTail = strstr( pszURLIn, "::" );
  2555. if( pszTail )
  2556. strcat( pszURLOut, pszTail );
  2557. else
  2558. strcpy( pszURLOut, pszURLIn );
  2559. return hr;
  2560. }
  2561. // CExTitle::URL2TopicNumber
  2562. //
  2563. // Translate a URL into a topic number.
  2564. //
  2565. HRESULT CExTitle::URL2Topic(const CHAR* pszURL, TOC_TOPIC* pTopic, DWORD* pdwTN)
  2566. {
  2567. static int iPageCnt = (PAGE_SIZE / sizeof(CURL));
  2568. DWORD dwOffs;
  2569. HRESULT hr = E_FAIL;
  2570. HASH dwHash;
  2571. PCURL pCurl;
  2572. int mid,low = 0;
  2573. if (m_bOpen == FALSE)
  2574. {
  2575. if (OpenTitle() == FALSE)
  2576. return E_FAIL;
  2577. }
  2578. //[ZERO IDXHDR]
  2579. if (!IsIdxHeaderValid())
  2580. {
  2581. return E_FAIL;
  2582. }
  2583. int high = m_pIdxHeader->cTopics - 1;
  2584. if (!m_pUrlTbl)
  2585. {
  2586. m_pUrlTbl = new CPagedSubfile;
  2587. if (FAILED(hr = m_pUrlTbl->Open(this,txtUrlTblFile)))
  2588. {
  2589. delete m_pUrlTbl;
  2590. m_pUrlTbl = NULL;
  2591. return hr;
  2592. }
  2593. }
  2594. //
  2595. // Hash it and find it!
  2596. dwHash = HashFromSz(pszURL);
  2597. while ( low <= high )
  2598. {
  2599. mid = ((low + high) / 2);
  2600. dwOffs = (((mid / iPageCnt) * PAGE_SIZE) + ((mid % iPageCnt) * sizeof(CURL)));
  2601. if (! (pCurl = (PCURL)m_pUrlTbl->Offset(dwOffs)) ) // Read the data in.
  2602. return hr;
  2603. if ( pCurl->dwHash == dwHash )
  2604. {
  2605. if ( pdwTN )
  2606. {
  2607. *pdwTN = pCurl->dwTopicNumber;
  2608. hr = S_OK ; // This part succeeded. we may fail the GetTopicData below.
  2609. }
  2610. if ( pTopic ) // Found it!
  2611. hr = GetTopicData(pCurl->dwTopicNumber, pTopic);
  2612. break;
  2613. }
  2614. else if ( pCurl->dwHash > dwHash )
  2615. high = mid - 1;
  2616. else
  2617. low = mid + 1;
  2618. }
  2619. return hr;
  2620. }
  2621. ////////////////////////////////////////////////////////
  2622. // GetVolumeOrder
  2623. //
  2624. // returned value in puiVolumeOrder is:
  2625. // 0 = local drive
  2626. // 1-N = Removable media order (CD1, CD2, etc.)
  2627. // -1 = error
  2628. HRESULT CExTitle::GetVolumeOrder( UINT* puiVolumeOrder, UINT uiFileType )
  2629. {
  2630. HRESULT hr = S_OK;
  2631. if( m_uiVolumeOrder == (UINT) -1 ) {
  2632. if( m_pCollection->IsSingleTitle() )
  2633. return E_FAIL;
  2634. // Get the location information
  2635. LOCATIONHISTORY* pLocationHistory = GetUsedLocation();
  2636. // Get the pathname
  2637. const CHAR* pszPathname = NULL;
  2638. if( uiFileType == HHRMS_TYPE_TITLE )
  2639. pszPathname = GetPathName();
  2640. else if( uiFileType == HHRMS_TYPE_COMBINED_QUERY )
  2641. pszPathname = GetQueryName();
  2642. else
  2643. return E_FAIL;
  2644. // Get the location identifier
  2645. const CHAR* pszLocation = NULL;
  2646. CLocation* pLocation = NULL;
  2647. if( pLocationHistory ) {
  2648. pszLocation = pLocationHistory->LocationId;
  2649. pLocation = m_pCollection->m_Collection.FindLocation( pszLocation, puiVolumeOrder );
  2650. m_uiVolumeOrder = *puiVolumeOrder;
  2651. }
  2652. // Get the location path
  2653. const CHAR* pszPath = NULL;
  2654. CHAR szPath[MAX_PATH];
  2655. szPath[0]= 0;
  2656. if( pLocation ) {
  2657. pszPath = pLocation->GetPath();
  2658. strcpy( szPath, pszPath );
  2659. }
  2660. // get the drive of the path
  2661. CHAR szDriveRoot[4];
  2662. strncpy( szDriveRoot, szPath, 4 );
  2663. szDriveRoot[3] = '\0';
  2664. // make sure to add a backslash if the second char is a colon
  2665. // sometimes we will get just "d:" instead of "d:\" and thus
  2666. // GetDriveType will fail under this circumstance
  2667. if( szDriveRoot[1] == ':' ) {
  2668. szDriveRoot[2] = '\\';
  2669. szDriveRoot[3] = 0;
  2670. }
  2671. // Get media type
  2672. UINT uiDriveType = DRIVE_UNKNOWN;
  2673. if( szDriveRoot[1] == ':' && szDriveRoot[2] == '\\' ) {
  2674. uiDriveType = GetDriveType(szDriveRoot);
  2675. }
  2676. else if( szDriveRoot[0] == '\\' && szDriveRoot[1] == '\\' ) {
  2677. uiDriveType = DRIVE_REMOTE;
  2678. }
  2679. // handle the drive types
  2680. switch( uiDriveType ) {
  2681. case DRIVE_REMOTE:
  2682. case DRIVE_FIXED:
  2683. case DRIVE_RAMDISK:
  2684. m_uiVolumeOrder = 0;
  2685. break;
  2686. case DRIVE_REMOVABLE:
  2687. case DRIVE_CDROM:
  2688. //m_uiVolumeOrder = 1; // set to one for now
  2689. break;
  2690. case DRIVE_UNKNOWN:
  2691. case DRIVE_NO_ROOT_DIR:
  2692. return E_FAIL;
  2693. break;
  2694. default:
  2695. return E_FAIL;
  2696. }
  2697. }
  2698. *puiVolumeOrder = m_uiVolumeOrder;
  2699. if( m_uiVolumeOrder == (UINT) -1 )
  2700. hr = E_FAIL;
  2701. return hr;
  2702. }
  2703. //////////////////////////////////////////////////////////////////////////
  2704. //
  2705. // Translate a ContextID into a URL.
  2706. //
  2707. //
  2708. HRESULT CExTitle::ResolveContextId(DWORD id, CHAR** ppszURL)
  2709. {
  2710. CSubFileSystem* pCSubFS;
  2711. int cbMapIds, cbRead;
  2712. if (!m_bOpen)
  2713. {
  2714. if (OpenTitle() == FALSE)
  2715. return E_FAIL;
  2716. }
  2717. if ( !m_pMapIds )
  2718. {
  2719. pCSubFS = new CSubFileSystem(m_pCFileSystem);
  2720. if(FAILED(pCSubFS->OpenSub(txtMAP)))
  2721. {
  2722. delete pCSubFS;
  2723. return HH_E_NOCONTEXTIDS;
  2724. }
  2725. if ( (pCSubFS->ReadSub(&cbMapIds, sizeof(DWORD), (ULONG*)&cbRead) != S_OK) || (cbRead != sizeof(DWORD)) )
  2726. {
  2727. delete pCSubFS;
  2728. return HH_E_NOCONTEXTIDS;
  2729. }
  2730. if (! (m_pMapIds = (MAPPED_ID*)lcMalloc(cbMapIds)) )
  2731. {
  2732. delete pCSubFS;
  2733. return E_FAIL;
  2734. }
  2735. if ( (pCSubFS->ReadSub(m_pMapIds, cbMapIds, (ULONG*)&cbRead) != S_OK) || (cbRead != cbMapIds) )
  2736. {
  2737. delete pCSubFS;
  2738. lcFree(m_pMapIds);
  2739. m_pMapIds = NULL;
  2740. return HH_E_NOCONTEXTIDS;
  2741. }
  2742. m_cMapIds = cbMapIds / sizeof(MAPPED_ID);
  2743. }
  2744. //
  2745. // Translate the id into a URL...
  2746. //
  2747. for (int i = 0; i < m_cMapIds; i++)
  2748. {
  2749. if ( id == m_pMapIds[i].idTopic )
  2750. {
  2751. *ppszURL = (CHAR*)GetString(m_pMapIds[i].offUrl);
  2752. return S_OK;
  2753. }
  2754. }
  2755. return HH_E_CONTEXTIDDOESNTEXIT;
  2756. }
  2757. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2758. // CTreeNode implementation
  2759. CTreeNode::CTreeNode()
  2760. {
  2761. m_Expanded = FALSE;
  2762. SetError(0);
  2763. }
  2764. CTreeNode::~CTreeNode()
  2765. {
  2766. }
  2767. BOOL CTreeNode::Compare(CTreeNode *pOtherNode)
  2768. {
  2769. if (m_ObjType != pOtherNode->GetObjType())
  2770. return FALSE;
  2771. switch (m_ObjType)
  2772. {
  2773. case EXFOLDERNODE:
  2774. if (((CExFolderNode *)this)->GetFolder() == ((CExFolderNode *)pOtherNode)->GetFolder())
  2775. return TRUE;
  2776. return FALSE;
  2777. case EXTITLENODE:
  2778. if (((CExTitleNode *)this)->GetFolder() == ((CExTitleNode *)pOtherNode)->GetFolder() &&
  2779. ((CExTitleNode *)this)->GetTitle() == ((CExTitleNode *)pOtherNode)->GetTitle())
  2780. return TRUE;
  2781. return FALSE;
  2782. case EXNODE:
  2783. case EXMERGEDNODE:
  2784. CExTitle *p1, *p2;
  2785. p1 = ((CExNode *)this)->GetTitle();
  2786. p2 =((CExNode *)pOtherNode)->GetTitle();
  2787. if (p1 == p2 &&
  2788. ((CExNode *)this)->m_Node.dwFlags == ((CExNode *)pOtherNode)->m_Node.dwFlags &&
  2789. ((CExNode *)this)->m_Node.dwOffsTopic == ((CExNode *)pOtherNode)->m_Node.dwOffsTopic &&
  2790. ((CExNode *)this)->m_Node.dwOffsParent == ((CExNode *)pOtherNode)->m_Node.dwOffsParent &&
  2791. ((CExNode *)this)->m_Node.dwOffsNext == ((CExNode *)pOtherNode)->m_Node.dwOffsNext &&
  2792. ((CExNode *)this)->m_Node.dwOffsChild == ((CExNode *)pOtherNode)->m_Node.dwOffsChild)
  2793. return TRUE;
  2794. return FALSE;
  2795. }
  2796. return FALSE;
  2797. }
  2798. CTreeNode *CTreeNode::GetExNode(TOC_FOLDERNODE *pNode, CExTitle *pTitle)
  2799. {
  2800. if (pNode->dwFlags & TOC_MERGED_REF)
  2801. {
  2802. // read chm file from string file
  2803. CStr cStr;
  2804. if (SUCCEEDED(pTitle->GetString(pNode->dwOffsChild, &cStr)))
  2805. {
  2806. // search title list
  2807. CExTitle *pCur = pTitle->m_pCollection->GetFirstTitle();
  2808. while (pCur)
  2809. {
  2810. if (strcmp(cStr, FindFilePortion(pCur->GetPathName())) == 0)
  2811. {
  2812. CExMergedTitleNode *pT;
  2813. pT = new CExMergedTitleNode(pNode, pCur);
  2814. if (pT != NULL)
  2815. return pT;
  2816. }
  2817. pCur = pCur->GetNext();
  2818. }
  2819. }
  2820. return GetNextSibling(pNode);
  2821. }
  2822. else
  2823. {
  2824. CExNode *p = new CExNode(pNode, pTitle);
  2825. if (p == NULL)
  2826. return GetNextSibling(pNode);
  2827. return p;
  2828. }
  2829. }
  2830. void CTreeNode::SetError(DWORD dw)
  2831. {
  2832. m_Error = dw;
  2833. }
  2834. DWORD CTreeNode::GetLastError()
  2835. {
  2836. return m_Error;
  2837. }
  2838. BOOL CTreeNode::GetURL(CHAR* pszURL, unsigned cb, BOOL bFull)
  2839. {
  2840. return FALSE;
  2841. }
  2842. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2843. // CExFolderNode implementation
  2844. CExFolderNode::CExFolderNode(CFolder *p, CExCollection *pCollection)
  2845. {
  2846. SetObjType(EXFOLDERNODE);
  2847. m_pCollection = pCollection;
  2848. m_pFolder = p;
  2849. }
  2850. CExFolderNode::~CExFolderNode()
  2851. {
  2852. }
  2853. CTreeNode *CExFolderNode::GetFirstChild(DWORD* pdwSlot)
  2854. {
  2855. CStructuralSubset* pSS = NULL;
  2856. CFolder *p = m_pFolder->GetFirstChildFolder();
  2857. CTreeNode *pN;
  2858. // implement structural subsetting here.
  2859. if( m_pCollection && m_pCollection->m_pSSList )
  2860. pSS = m_pCollection->m_pSSList->GetTOC();
  2861. while (p)
  2862. {
  2863. if ((pN = m_pCollection->CheckForTitleNode(p)))
  2864. {
  2865. if ( !pSS || pSS->IsEntire() || p->bIsVisable() )
  2866. return pN;
  2867. else
  2868. delete pN; // leak fix
  2869. }
  2870. p = p->GetNextFolder();
  2871. }
  2872. return NULL;
  2873. }
  2874. CTreeNode *CExFolderNode::GetNextSibling(TOC_FOLDERNODE *pAltNode, DWORD* pdwSlot)
  2875. {
  2876. CStructuralSubset* pSS = NULL;
  2877. CFolder *p = m_pFolder->GetNextFolder();
  2878. CTreeNode *pN;
  2879. // implement structural subsetting here.
  2880. if( m_pCollection && m_pCollection->m_pSSList )
  2881. pSS = m_pCollection->m_pSSList->GetTOC();
  2882. while (p)
  2883. {
  2884. if ((pN = m_pCollection->CheckForTitleNode(p)))
  2885. {
  2886. if ( !pSS || pSS->IsEntire() || p->bIsVisable() )
  2887. return pN;
  2888. else
  2889. delete pN; // leak fix
  2890. }
  2891. p = p->GetNextFolder();
  2892. }
  2893. return NULL;
  2894. }
  2895. CTreeNode *CExFolderNode::GetParent(DWORD* pdwSlot, BOOL bDirectParent)
  2896. {
  2897. CFolder *p = m_pFolder->GetParent();
  2898. if (p->GetTitle() == NULL)
  2899. return NULL;
  2900. if (p)
  2901. {
  2902. return m_pCollection->CheckForTitleNode(p);
  2903. }
  2904. return NULL;
  2905. }
  2906. HRESULT CExFolderNode::GetTopicName(CHAR* pszTitle, int cb)
  2907. {
  2908. CHAR* psz = m_pFolder->GetTitle();
  2909. if (strlen(psz) + 1 > (size_t) cb)
  2910. return E_FAIL;
  2911. else
  2912. strcpy(pszTitle, psz);
  2913. return S_OK;
  2914. }
  2915. HRESULT CExFolderNode::GetTopicName( WCHAR* pwszTitle, int cch )
  2916. {
  2917. CHAR* psz = m_pFolder->GetTitle();
  2918. if( (2*(strlen(psz) + 1)) > (size_t) cch )
  2919. return E_FAIL;
  2920. else {
  2921. UINT CodePage = m_pCollection->GetMasterTitle()->GetInfo()->GetCodePage();
  2922. MultiByteToWideChar( CodePage, 0, psz, -1, pwszTitle, cch );
  2923. }
  2924. return S_OK;
  2925. }
  2926. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2927. // CExTitleNode implementation
  2928. CExTitleNode::CExTitleNode(CExTitle *pTitle, CFolder *p)
  2929. {
  2930. SetObjType(EXTITLENODE);
  2931. m_pTitle = pTitle;
  2932. m_pFolder = p;
  2933. }
  2934. #if 0
  2935. CExTitleNode::~CExTitleNode()
  2936. {
  2937. }
  2938. #endif
  2939. CTreeNode * CExTitleNode::GetFirstChild(DWORD* pdwSlot)
  2940. {
  2941. TOC_FOLDERNODE Node;
  2942. if ( !SUCCEEDED(m_pTitle->GetRootNode(&Node)) )
  2943. return NULL;
  2944. if ( pdwSlot )
  2945. *pdwSlot = m_pTitle->GetRootSlot();
  2946. return GetExNode(&Node, m_pTitle);
  2947. }
  2948. CTreeNode * CExTitleNode::GetNextSibling(TOC_FOLDERNODE *pAltNode, DWORD* pdwSlot)
  2949. {
  2950. CTreeNode *pN;
  2951. CStructuralSubset* pSS = NULL;
  2952. if (m_pFolder == NULL)
  2953. return NULL;
  2954. CFolder *p = m_pFolder->GetNextFolder();
  2955. // implement structural subsetting here.
  2956. if( m_pTitle && m_pTitle->m_pCollection && m_pTitle->m_pCollection->m_pSSList )
  2957. pSS = m_pTitle->m_pCollection->m_pSSList->GetTOC();
  2958. while (p)
  2959. {
  2960. if ((pN = m_pTitle->m_pCollection->CheckForTitleNode(p)))
  2961. {
  2962. if ( !pSS || pSS->IsEntire() || p->bIsVisable() )
  2963. return pN;
  2964. else
  2965. delete pN; // leak fix
  2966. }
  2967. p = p->GetNextFolder();
  2968. }
  2969. return NULL;
  2970. }
  2971. CTreeNode * CExTitleNode::GetParent(DWORD* pdwSlot, BOOL bDirectParent)
  2972. {
  2973. CFolder *p = m_pFolder->GetParent();
  2974. if (p->GetTitle() == NULL)
  2975. return NULL;
  2976. if (p)
  2977. {
  2978. return m_pTitle->m_pCollection->CheckForTitleNode(p);
  2979. }
  2980. return NULL;
  2981. }
  2982. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2983. // CExMergedTitleNode implementation
  2984. CExMergedTitleNode::CExMergedTitleNode(TOC_FOLDERNODE *p, CExTitle *pTitle) : CExNode(p, pTitle)
  2985. {
  2986. SetObjType(EXMERGEDNODE);
  2987. }
  2988. CExMergedTitleNode::~CExMergedTitleNode()
  2989. {
  2990. }
  2991. CTreeNode * CExMergedTitleNode::GetFirstChild(DWORD* pdwSlot)
  2992. {
  2993. TOC_FOLDERNODE Node;
  2994. if (FAILED(GetTitle()->GetRootNode(&Node)))
  2995. return NULL;
  2996. return GetExNode(&Node, GetTitle());
  2997. }
  2998. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2999. // CExNode implementation
  3000. CExNode::CExNode(TOC_FOLDERNODE *p, CExTitle *pTitle)
  3001. {
  3002. SetObjType(EXNODE);
  3003. if (p)
  3004. memcpy(&m_Node, p, sizeof(TOC_FOLDERNODE));
  3005. m_pTitle = pTitle;
  3006. }
  3007. #if 0
  3008. CExNode::~CExNode()
  3009. {
  3010. }
  3011. #endif
  3012. DWORD CExNode::GetType()
  3013. {
  3014. if ( (m_Node.dwFlags & TOC_FOLDER) )
  3015. {
  3016. if ( (m_Node.dwFlags & TOC_TOPIC_NODE) )
  3017. return CONTAINER; // Topic Folder.
  3018. else
  3019. {
  3020. if ( m_Node.dwOffsChild )
  3021. return FOLDER;
  3022. else
  3023. return BOGUS_FOLDER;
  3024. }
  3025. } // Folder Only.
  3026. return TOPIC; // Topic Only.
  3027. }
  3028. HRESULT CExNode::GetTopicName(CHAR* pszTitle, int cb)
  3029. {
  3030. HRESULT hr = S_OK;
  3031. if (! (m_Node.dwFlags & TOC_TOPIC_NODE) )
  3032. hr |= m_pTitle->GetString(m_Node.dwOffsTopic, pszTitle, cb);
  3033. else
  3034. hr |= m_pTitle->GetTopicName(m_Node.dwOffsTopic, pszTitle, cb);
  3035. return hr;
  3036. }
  3037. HRESULT CExNode::GetTopicName( WCHAR* pwszTitle, int cch )
  3038. {
  3039. HRESULT hr = S_OK;
  3040. if (! (m_Node.dwFlags & TOC_TOPIC_NODE) )
  3041. hr |= m_pTitle->GetString(m_Node.dwOffsTopic, pwszTitle, cch);
  3042. else
  3043. hr |= m_pTitle->GetTopicName(m_Node.dwOffsTopic, pwszTitle, cch);
  3044. return hr;
  3045. }
  3046. CTreeNode *CExNode::GetFirstChild(DWORD* pdwSlot)
  3047. {
  3048. unsigned int uiWidth;
  3049. const unsigned int *pdwITBits;
  3050. DWORD dwOffsChild;
  3051. CSubSet* pSS;
  3052. if (m_Node.dwFlags & TOC_HAS_CHILDREN)
  3053. {
  3054. TOC_FOLDERNODE Node;
  3055. dwOffsChild = m_Node.dwOffsChild;
  3056. do
  3057. {
  3058. if ( !SUCCEEDED(m_pTitle->GetNode(dwOffsChild, &Node)) )
  3059. return NULL;
  3060. if ( Node.dwFlags & TOC_HAS_UNTYPED )
  3061. break;
  3062. if ( (pSS = m_pTitle->m_pCollection->m_pSubSets->GetTocSubset()) && pSS->m_bIsEntireCollection )
  3063. break;
  3064. if ( Node.dwFlags & TOC_FOLDER )
  3065. {
  3066. if ( m_pTitle->m_ITCnt > 31 )
  3067. {
  3068. uiWidth = (((m_pTitle->m_ITCnt / 32) + 1) * 4);
  3069. pdwITBits = m_pTitle->GetITBits(Node.dwIT_Idx * uiWidth);
  3070. }
  3071. else
  3072. pdwITBits = (const unsigned int *)&Node.dwIT_Idx;
  3073. }
  3074. else
  3075. pdwITBits = m_pTitle->GetTopicITBits(Node.dwOffsTopic);
  3076. } while ( !m_pTitle->m_pCollection->m_pSubSets->fTOCFilter(pdwITBits) && (dwOffsChild = Node.dwOffsNext) );
  3077. if ( dwOffsChild )
  3078. {
  3079. if ( pdwSlot )
  3080. *pdwSlot = dwOffsChild;
  3081. return GetExNode(&Node, m_pTitle);
  3082. }
  3083. }
  3084. return NULL;
  3085. }
  3086. CTreeNode *CExNode::GetNextSibling(TOC_FOLDERNODE *pAltNode, DWORD* pdwSlot)
  3087. {
  3088. TOC_FOLDERNODE Node;
  3089. DWORD dwNext = (pAltNode ? pAltNode->dwOffsNext : m_Node.dwOffsNext);
  3090. unsigned int uiWidth;
  3091. const unsigned int *pdwITBits;
  3092. CSubSet* pSS;
  3093. do
  3094. {
  3095. if ( !SUCCEEDED(m_pTitle->GetNode(dwNext, &Node)) )
  3096. return NULL;
  3097. if ( Node.dwFlags & TOC_HAS_UNTYPED )
  3098. break;
  3099. if ( (pSS = m_pTitle->m_pCollection->m_pSubSets->GetTocSubset()) && pSS->m_bIsEntireCollection )
  3100. break;
  3101. if ( Node.dwFlags & TOC_FOLDER )
  3102. {
  3103. if ( m_pTitle->m_ITCnt > 31 )
  3104. {
  3105. uiWidth = (((m_pTitle->m_ITCnt / 32) + 1) * 4);
  3106. pdwITBits = m_pTitle->GetITBits(Node.dwIT_Idx * uiWidth);
  3107. }
  3108. else
  3109. pdwITBits = (const unsigned int *)&Node.dwIT_Idx;
  3110. }
  3111. else
  3112. pdwITBits = m_pTitle->GetTopicITBits(Node.dwOffsTopic);
  3113. } while ( !m_pTitle->m_pCollection->m_pSubSets->fTOCFilter(pdwITBits) && (dwNext = Node.dwOffsNext) );
  3114. if ( dwNext )
  3115. {
  3116. if ( pdwSlot )
  3117. *pdwSlot = dwNext;
  3118. return GetExNode(&Node, m_pTitle);
  3119. }
  3120. return NULL;
  3121. }
  3122. CTreeNode *CExNode::GetParent(DWORD* pdwSlot, BOOL bDirectParent)
  3123. {
  3124. TOC_FOLDERNODE Node;
  3125. if (m_Node.dwOffsParent)
  3126. {
  3127. if ( !SUCCEEDED(m_pTitle->GetNode(m_Node.dwOffsParent, &Node)) )
  3128. return NULL;
  3129. if ( pdwSlot )
  3130. *pdwSlot = m_Node.dwOffsParent;
  3131. return GetExNode(&Node, m_pTitle);
  3132. }
  3133. else
  3134. {
  3135. // check if this is a merged title
  3136. if (m_pTitle->GetNodeOffsetInParentTitle())
  3137. {
  3138. // assume that the first title in the collection is the master title
  3139. CExTitle * pParent;
  3140. pParent = m_pTitle->m_pCollection->GetFirstTitle();
  3141. if (!pParent)
  3142. return NULL;
  3143. if ( !SUCCEEDED(pParent->GetNode(m_pTitle->GetNodeOffsetInParentTitle(), &Node)) )
  3144. return NULL;
  3145. if ( pdwSlot )
  3146. *pdwSlot = m_pTitle->GetNodeOffsetInParentTitle();
  3147. return GetExNode(&Node, pParent);
  3148. }
  3149. else
  3150. {
  3151. // is this a collection
  3152. if (m_pTitle->m_pCollection->IsSingleTitle() == FALSE)
  3153. {
  3154. CFolder *p = m_pTitle->m_pCollection->FindTitleFolder(m_pTitle);
  3155. if (!p)
  3156. return NULL;
  3157. CTreeNode *pTitle;
  3158. if (pTitle = m_pTitle->m_pCollection->CheckForTitleNode(p))
  3159. {
  3160. if (bDirectParent == TRUE)
  3161. return pTitle;
  3162. CTreeNode *p = pTitle->GetParent(pdwSlot);
  3163. delete pTitle;
  3164. return p;
  3165. }
  3166. }
  3167. }
  3168. }
  3169. return NULL;
  3170. }
  3171. BOOL CExNode::GetURL(CHAR* pszURL, unsigned cb, BOOL bFull)
  3172. {
  3173. if ( (m_Node.dwFlags & TOC_TOPIC_NODE) )
  3174. if ( SUCCEEDED(m_pTitle->GetTopicURL(m_Node.dwOffsTopic, pszURL, cb, bFull)) )
  3175. return TRUE;
  3176. return FALSE;
  3177. }