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.

1247 lines
34 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. hlpfiles.c
  5. Abstract:
  6. Implements a function that is called for every file or directory,
  7. selects HLP files trying to detect help files that will not
  8. work after upgrade is complete.
  9. This code might run if _UNICODE is defined, although it processes only ANSI
  10. strings. The only exception is in it's entry point (ProcessHelpFile) and
  11. in function pCheckSubsystemMatch.
  12. Author:
  13. Calin Negreanu (calinn) 25-Oct-1997
  14. Revision History:
  15. calinn 23-Sep-1998 File mapping
  16. --*/
  17. #include "pch.h"
  18. //since we are reading from a file we need that sizeof to give us the accurate result
  19. #pragma pack(push,1)
  20. #define DBG_HELPFILES "HlpCheck"
  21. // Help 3.0 version and format numbers
  22. #define Version3_0 15
  23. #define Format3_0 1
  24. // Help 3.1 version and format numbers
  25. #define Version3_1 21
  26. #define Format3_5 1
  27. // Help 4.0 version and format numbers
  28. #define Version40 33
  29. #define Version41 34
  30. //magic numbers and various constants
  31. #define HF_MAGIC 0x5F3F
  32. #define HF_VERSION 0x03
  33. #define WhatWeKnow 'z'
  34. #define BT_MAGIC 0x293B
  35. #define BT_VERSION 0x02
  36. #define SYSTEM_FILE "|SYSTEM"
  37. #define MACRO_NEEDED_0 "RegisterRoutine(\""
  38. #define MACRO_NEEDED_1 "RR(\""
  39. //help file header
  40. typedef struct _HF_HEADER {
  41. WORD Magic; //magic number, for hlp files is 0x5F3F (?_ - the help icon (with shadow))
  42. BYTE Version; //version identification number, must be 0x03
  43. BYTE Flags; //file flags
  44. LONG Directory; //offset of directory block
  45. LONG FirstFree; //offset of head of free list
  46. LONG Eof; //virtual end of file
  47. } HF_HEADER, *PHF_HEADER;
  48. //internal file header
  49. typedef struct _IF_HEADER {
  50. LONG BlockSize; //block size (including header)
  51. LONG FileSize; //file size (not including header)
  52. BYTE Permission; //low byte of file permissions
  53. } IF_HEADER, *PIF_HEADER;
  54. //internal |SYSTEM file header
  55. typedef struct _SF_HEADER {
  56. LONG BlockSize; //block size (including header)
  57. LONG FileSize; //file size (not including header)
  58. BYTE Permission; //low byte of file permissions
  59. WORD Magic; //Magic word = 0x036C
  60. WORD VersionNo; //version : 15 - version 3.0
  61. // 21 - version 3.5
  62. // 33 - version 4.0
  63. // 34 - version 4.1
  64. WORD VersionFmt; //version format 1 - format 3.0
  65. // 1 - format 3.5
  66. LONG DateCreated; //creation date
  67. WORD Flags; //flags : fDEBUG 0x01, fBLOCK_COMPRESSION 0x4
  68. } SF_HEADER, *PSF_HEADER;
  69. #define MAX_FORMAT 15
  70. //btree header
  71. typedef struct _BT_HEADER {
  72. WORD Magic; //magic number = 0x293B
  73. BYTE Version; //version = 2
  74. BYTE Flags; //r/o, open r/o, dirty, isdir
  75. WORD BlockSize; //size of a block (bytes)
  76. CHAR Format[MAX_FORMAT+1];//key and record format string - MAXFORMAT=15
  77. //**WARNING! the first character should be z for the btree we are going to read**
  78. WORD First; //first leaf block in tree
  79. WORD Last; //last leaf block in tree
  80. WORD Root; //root block
  81. WORD Free; //head of free block list
  82. WORD Eof; //next bk to use if free list empty
  83. WORD Levels; //number of levels currently in tree
  84. LONG Entries; //number of keys in btree
  85. } BT_HEADER, *PBT_HEADER;
  86. //index page header
  87. typedef struct _IDX_PAGE {
  88. SHORT Slack; //unused space at the end of page (bytes)
  89. SHORT Keys; //# of keys in page
  90. WORD PreviousPage; //pointer to parent page (FFFF if it's root page)
  91. } IDX_PAGE, *PIDX_PAGE;
  92. //leaf page header
  93. typedef struct _LEAF_PAGE {
  94. SHORT Slack; //unused space at the end of page (bytes)
  95. SHORT Keys; //# of keys in page
  96. WORD PreviousPage; //pointer to previous page (FFFF if it's first)
  97. WORD NextPage; //pointer to next page (FFFF if it's last)
  98. } LEAF_PAGE, *PLEAF_PAGE;
  99. //format of info in |SYSTEM file
  100. typedef struct _DATA_HEADER {
  101. WORD InfoType; //info type
  102. WORD InfoLength; //# of bytes containing the info
  103. } DATA_HEADER, *PDATA_HEADER;
  104. //types of info in |SYSTEM file
  105. enum {
  106. tagFirst, // First tag in the list
  107. tagTitle, // Title for Help window (caption)
  108. tagCopyright, // Custom text for About box
  109. tagContents, // Address for contents topic
  110. tagConfig, // Macros to be run at load time
  111. tagIcon, // override of default help icon
  112. tagWindow, // secondary window info
  113. tagCS, // character set
  114. tagCitation, // Citation String
  115. // The following are new to 4.0
  116. tagLCID, // Locale ID and flags for CompareStringA
  117. tagCNT, // .CNT help file is associated with
  118. tagCHARSET, // charset of help file
  119. tagDefFont, // default font for keywords, topic titles, etc.
  120. tagPopupColor,// color of popups from a window
  121. tagIndexSep, // index separating characters
  122. tagLast // Last tag in the list
  123. };
  124. //
  125. // for tagLCID
  126. //
  127. typedef struct {
  128. DWORD fsCompareI;
  129. DWORD fsCompare;
  130. LANGID langid;
  131. } KEYWORD_LOCALE, *PKEYWORD_LOCALE;
  132. #define DOS_SIGNATURE 0x5A4D // MZ
  133. #define NE_SIGNATURE 0x454E // New Executable file format signature - NE
  134. #define PE_SIGNATURE 0x00004550 // Portable Executable file format signature - PE00
  135. #pragma pack(pop)
  136. ULONG
  137. pQueryBtreeForFile (
  138. IN PCSTR BtreeImage,
  139. IN PCSTR StringNeeded
  140. )
  141. /*++
  142. Routine Description:
  143. This routine will traverse a b tree trying to find the string passed as parameter.
  144. It will return the pointer associated with the passed string or NULLto the beginning of the internal |SYSTEM file of a HLP file.
  145. Arguments:
  146. BtreeImage - Pointer to the beginning of the b tree
  147. StringNeeded - String to find in b tree
  148. Return Value:
  149. It will return the pointer associated with the passed string or NULL if string could not be found or some error occured.
  150. --*/
  151. {
  152. PBT_HEADER pbt_Header;
  153. ULONG systemFileOffset = 0;
  154. WORD bt_Page;
  155. UINT bt_Deep;
  156. INT bt_KeysRead;
  157. PIDX_PAGE pbt_PageHeader;
  158. PCSTR pbt_LastString;
  159. PCSTR pbt_CurrentKey;
  160. LONG *pbt_LastLongOff = NULL;
  161. WORD *pbt_LastWordOff = NULL;
  162. BOOL found = FALSE;
  163. //let's read b tree header
  164. pbt_Header = (PBT_HEADER) BtreeImage;
  165. //check this b tree header to see if it's valid
  166. if ((pbt_Header->Magic != BT_MAGIC ) ||
  167. (pbt_Header->Version != BT_VERSION) ||
  168. (pbt_Header->Format [0] != WhatWeKnow)
  169. ) {
  170. //invalid b tree header.
  171. return 0;
  172. }
  173. //let's see if there is something in this b tree
  174. if ((pbt_Header->Levels == 0) ||
  175. (pbt_Header->Entries <= 0)
  176. ) {
  177. //nothing else to do
  178. return 0;
  179. }
  180. //now we are going to loop until we find our string or until we are sure that the string
  181. //does not exist. We are reffering all the time to a certain page from the b tree (starting
  182. //with root page.
  183. //initializing current processing page
  184. bt_Page = pbt_Header->Root;
  185. //initializing deep counter
  186. //we count how deep we are to know if we are processing an index page (deep < btree deepmax)
  187. //or a leaf page (deep == btree deepmax)
  188. bt_Deep = 1;
  189. //we are breaking the loop if:
  190. // 1. we reached the maximum deep level and we didn't find the string
  191. // 2. first key in the current page was already greater than our string and
  192. // there is no previous page.
  193. while (!found) {
  194. //for each page we are using three pointers:
  195. // one to the page header
  196. // one to the key currently beeing processed
  197. // one to the last string <= our string (this can be NULL)
  198. pbt_PageHeader = (PIDX_PAGE) (BtreeImage + sizeof (BT_HEADER) + (bt_Page * pbt_Header->BlockSize));
  199. pbt_CurrentKey = (PCSTR) pbt_PageHeader;
  200. pbt_CurrentKey += (bt_Deep == pbt_Header->Levels) ? sizeof (LEAF_PAGE) : sizeof (IDX_PAGE);
  201. pbt_LastString = NULL;
  202. //initializing number of keys read.
  203. bt_KeysRead = 0;
  204. //we are reading every key in this page until the we find one greater than our string
  205. //In the same time we try not to read too many keys
  206. while ((bt_KeysRead < pbt_PageHeader->Keys) &&
  207. (StringCompareA (StringNeeded, pbt_CurrentKey) >= 0)
  208. ) {
  209. pbt_LastString = pbt_CurrentKey;
  210. bt_KeysRead++;
  211. //passing the string in this key
  212. pbt_CurrentKey = GetEndOfStringA (pbt_CurrentKey) + 1;
  213. //read this key associated value
  214. pbt_LastLongOff = (LONG *)pbt_CurrentKey;
  215. pbt_LastWordOff = (WORD *)pbt_CurrentKey;
  216. //now if this is an index page then there is a WORD here, otherwise a LONG
  217. pbt_CurrentKey += (bt_Deep == pbt_Header->Levels) ? sizeof (LONG) : sizeof (WORD);
  218. }
  219. //OK, now we have passed the string we are looking for. If the last found value is valid
  220. //(is <= for an index page) (is == for a leaf page) then keep with it.
  221. if (!pbt_LastString) {
  222. //we found nothing. The first key was already greater that our string
  223. //we try to get to the previous page if we have one. If not, there is
  224. //nothing else to do
  225. if (pbt_PageHeader->PreviousPage != 0xFFFF) {
  226. bt_Deep++;
  227. bt_Page = pbt_PageHeader->PreviousPage;
  228. continue;
  229. }
  230. else {
  231. return 0;
  232. }
  233. }
  234. //Now in the string pointed by pbt_LastString we have something <= our string. If this is an index
  235. //page then we move on else those two strings should be equal.
  236. if (bt_Deep != pbt_Header->Levels) {
  237. //We are on an index page. Mark going deeper and moving on.
  238. bt_Deep++;
  239. bt_Page = *pbt_LastWordOff;
  240. continue;
  241. }
  242. if (!StringMatchA (StringNeeded, pbt_LastString)) {
  243. //We are on a leaf page and the strings are not equal. Our string does not exist.
  244. //nothing else to do
  245. return 0;
  246. }
  247. found = TRUE;
  248. systemFileOffset = *pbt_LastLongOff;
  249. }
  250. return systemFileOffset;
  251. }
  252. PCSTR
  253. pGetSystemFilePtr (
  254. IN PCSTR FileImage
  255. )
  256. /*++
  257. Routine Description:
  258. This routine will return a pointer to the beginning of the internal |SYSTEM file of a HLP file.
  259. Arguments:
  260. FileImage - Pointer to the beginning of the HLP file
  261. Return Value:
  262. NULL if an error occured, a valid pointer to the beginning of the |SYSTEM file otherwise
  263. --*/
  264. {
  265. PCSTR systemFileImage = NULL;
  266. PHF_HEADER phf_Header;
  267. //we are going to read from various portions of this memory mapped file. There
  268. //is no guarantee that we are going to keep our readings inside the file so let's
  269. //prevent any access violation.
  270. __try {
  271. //first check if we are really dealing with a HLP file
  272. phf_Header = (PHF_HEADER) FileImage;
  273. if ((phf_Header->Magic != HF_MAGIC ) ||
  274. (phf_Header->Version != HF_VERSION)
  275. ) {
  276. __leave;
  277. }
  278. //according to the hacked specs phf_header->Directory gives us the offset of
  279. //directory block relativ to the beginning of the HLP file. Here we find an
  280. //internal file header followed by a b tree.
  281. //now get the |SYSTEM internal file address passing the address of the b tree header.
  282. systemFileImage = FileImage + pQueryBtreeForFile (FileImage + phf_Header->Directory + sizeof (IF_HEADER), SYSTEM_FILE);
  283. }
  284. __except (EXCEPTION_EXECUTE_HANDLER) {
  285. return NULL;
  286. }
  287. return systemFileImage;
  288. }
  289. #define MODULE_OK 0
  290. #define MODULE_NOT_FOUND 1
  291. #define MODULE_BROKEN 2
  292. #define MODULE_MISMATCHED 3
  293. INT
  294. pCheckSubsystemByModule (
  295. IN PCSTR FileName,
  296. IN WORD VersionNo,
  297. IN PCSTR ModuleName
  298. )
  299. /*++
  300. Routine Description:
  301. Checks a help file and an extension module to see if are going to be loaded in the same
  302. subsystem while running in NT.
  303. Arguments:
  304. FileName - The help file (full path)
  305. VersionNo - version of help file
  306. ModuleName - contains module name
  307. Return Value:
  308. MODULE_OK - if both help file and module will be loaded in same subsystems in NT.
  309. MODULE_NOT_FOUND - if the needed module could not be located
  310. MODULE_BROKEN - if broken or not a windows module
  311. MODULE_MISMATCHED - if help file and module will be loaded in different subsystems in NT.
  312. --*/
  313. {
  314. PCSTR fileImage = NULL;
  315. HANDLE mapHandle = NULL;
  316. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  317. PDOS_HEADER pdos_Header;
  318. LONG *pPE_Signature;
  319. WORD *pNE_Signature;
  320. CHAR fullPath [MAX_MBCHAR_PATH];
  321. CHAR key [MEMDB_MAX];
  322. PSTR endPtr;
  323. PSTR dontCare;
  324. INT result = MODULE_BROKEN;
  325. MemDbBuildKey (key, MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, ModuleName, NULL, NULL);
  326. if (MemDbGetValue (key, NULL)) {
  327. return MODULE_OK;
  328. }
  329. //if out of memory we will not come back here so not checking this
  330. __try {
  331. //preparing fullPath to contain only the HelpFile path
  332. StringCopyA (fullPath, FileName);
  333. endPtr = (PSTR) GetFileNameFromPathA (fullPath);
  334. if (!endPtr) {
  335. result = MODULE_OK;
  336. __leave;
  337. }
  338. *endPtr = 0;
  339. if ((!SearchPathA (
  340. fullPath,
  341. ModuleName,
  342. ".EXE",
  343. MAX_MBCHAR_PATH,
  344. fullPath,
  345. &dontCare)) &&
  346. (!SearchPathA (
  347. fullPath,
  348. ModuleName,
  349. ".DLL",
  350. MAX_MBCHAR_PATH,
  351. fullPath,
  352. &dontCare)) &&
  353. (!SearchPathA (
  354. NULL,
  355. ModuleName,
  356. ".EXE",
  357. MAX_MBCHAR_PATH,
  358. fullPath,
  359. &dontCare)) &&
  360. (!SearchPathA (
  361. NULL,
  362. ModuleName,
  363. ".DLL",
  364. MAX_MBCHAR_PATH,
  365. fullPath,
  366. &dontCare))
  367. ) {
  368. result = MODULE_NOT_FOUND;
  369. __leave;
  370. }
  371. //map the file into memory and get a it's address
  372. fileImage = MapFileIntoMemory (fullPath, &fileHandle, &mapHandle);
  373. if (fileImage == NULL) {
  374. result = MODULE_NOT_FOUND;
  375. __leave;
  376. }
  377. //map dos header into view of file
  378. pdos_Header = (PDOS_HEADER) fileImage;
  379. //now see what kind of signature we have there
  380. pNE_Signature = (WORD *) (fileImage + pdos_Header->e_lfanew);
  381. pPE_Signature = (LONG *) (fileImage + pdos_Header->e_lfanew);
  382. if (*pNE_Signature == NE_SIGNATURE) {
  383. //this is New Executable format
  384. result = (VersionNo > Version3_1) ? MODULE_MISMATCHED : MODULE_OK;
  385. } else if (*pPE_Signature == PE_SIGNATURE) {
  386. //this is Portable Executable format
  387. result = (VersionNo <= Version3_1) ? MODULE_MISMATCHED : MODULE_OK;
  388. }
  389. }
  390. __finally {
  391. //unmap and close module
  392. UnmapFile ((PVOID) fileImage, mapHandle, fileHandle);
  393. }
  394. return result;
  395. }
  396. BOOL
  397. pCheckSubsystemMatch (
  398. IN PCSTR HlpName,
  399. IN PCSTR FriendlyName, OPTIONAL
  400. IN WORD VersionNo
  401. )
  402. /*++
  403. Routine Description:
  404. Checks all extension modules listed in MemDB category MEMDB_CATEGORY_HELP_FILES_DLLA
  405. to see if are going to be loaded in the same subsystem while running in NT.
  406. Arguments:
  407. HlpName - The help file (full path)
  408. VersionNo - version of help file
  409. KeyPath - contains category and module name (Ex:HelpFilesDll\foo.dll)
  410. Return Value:
  411. TRUE if successful, FALSE if at least one error occured
  412. --*/
  413. {
  414. INT result;
  415. MEMDB_ENUMA e;
  416. PCSTR moduleName;
  417. PCTSTR ArgList[3];
  418. PCTSTR Comp;
  419. //see if there is any aditional dll
  420. if (!MemDbEnumFirstValueA (
  421. &e,
  422. MEMDB_CATEGORY_HELP_FILES_DLLA,
  423. MEMDB_ALL_SUBLEVELS,
  424. MEMDB_ENDPOINTS_ONLY
  425. )) {
  426. return TRUE;
  427. }
  428. do {
  429. moduleName = _mbschr (e.szName, '\\');
  430. if (!moduleName) {
  431. continue;
  432. }
  433. moduleName = (PCSTR) _mbsinc (moduleName);
  434. result = pCheckSubsystemByModule (
  435. HlpName,
  436. VersionNo,
  437. moduleName
  438. );
  439. switch (result) {
  440. case MODULE_NOT_FOUND:
  441. #ifdef UNICODE
  442. ArgList[0] = ConvertAtoW (moduleName);
  443. ArgList[1] = ConvertAtoW (HlpName);
  444. #else
  445. ArgList[0] = moduleName;
  446. ArgList[1] = HlpName;
  447. #endif
  448. LOG ((LOG_WARNING, (PCSTR)MSG_HELPFILES_NOTFOUND_LOG, ArgList[0], ArgList[1]));
  449. #ifdef UNICODE
  450. FreeConvertedStr (ArgList[0]);
  451. FreeConvertedStr (ArgList[1]);
  452. #endif
  453. break;
  454. case MODULE_BROKEN:
  455. #ifdef UNICODE
  456. ArgList[0] = ConvertAtoW (moduleName);
  457. ArgList[1] = ConvertAtoW (HlpName);
  458. #else
  459. ArgList[0] = moduleName;
  460. ArgList[1] = HlpName;
  461. #endif
  462. LOG ((LOG_WARNING, (PCSTR)MSG_HELPFILES_BROKEN_LOG, ArgList[0], ArgList[1]));
  463. #ifdef UNICODE
  464. FreeConvertedStr (ArgList[0]);
  465. FreeConvertedStr (ArgList[1]);
  466. #endif
  467. break;
  468. case MODULE_MISMATCHED:
  469. if ((!FriendlyName) || (*FriendlyName == 0)) {
  470. FriendlyName = (PCSTR) GetFileNameFromPathA (HlpName);
  471. }
  472. #ifdef UNICODE
  473. ArgList[0] = ConvertAtoW (moduleName);
  474. ArgList[1] = ConvertAtoW (HlpName);
  475. ArgList[2] = ConvertAtoW (FriendlyName);
  476. #else
  477. ArgList[0] = moduleName;
  478. ArgList[1] = HlpName;
  479. ArgList[2] = FriendlyName;
  480. #endif
  481. Comp = BuildMessageGroup (MSG_MINOR_PROBLEM_ROOT, MSG_HELPFILES_SUBGROUP, ArgList[2]);
  482. MsgMgr_ObjectMsg_Add (HlpName, Comp, NULL);
  483. FreeText (Comp);
  484. LOG ((LOG_WARNING, (PCSTR)MSG_HELPFILES_MISMATCHED_LOG, ArgList[0], ArgList[1]));
  485. #ifdef UNICODE
  486. FreeConvertedStr (ArgList[0]);
  487. FreeConvertedStr (ArgList[1]);
  488. FreeConvertedStr (ArgList[2]);
  489. #endif
  490. break;
  491. }
  492. }
  493. while (MemDbEnumNextValueA (&e));
  494. MemDbDeleteTreeA (MEMDB_CATEGORY_HELP_FILES_DLLA);
  495. return TRUE;
  496. }
  497. BOOL
  498. pSkipPattern (
  499. IN PCSTR Source,
  500. IN OUT PCSTR *Result,
  501. IN PCSTR StrToSkip
  502. )
  503. /*++
  504. Routine Description:
  505. Skips a whole pattern. Usually when making simple parsers that are not supposed to
  506. raise an error message it's enough to know if the string that you parse is correct or not.
  507. For example if you want to see if a string complies with a pattern like "RR(\"" you don't
  508. have to scan for each symbol separately, just call this function with StrToSkip="RR(\""
  509. The good thing is that this function skips also the spaces for you so a string like
  510. " RR ( \" " will match the pattern above.
  511. ANSI only!!!
  512. Arguments:
  513. Source - String to scan
  514. Result - If not NULL it will point right after the pattern if successful
  515. StrToSkip - Pattern to match and skip
  516. Return Value:
  517. TRUE if was able to match the pattern, FALSE otherwise
  518. --*/
  519. {
  520. //first skip spaces
  521. Source = SkipSpaceA (Source );
  522. StrToSkip = SkipSpaceA (StrToSkip);
  523. //now try to see if the strings match
  524. while ((*Source ) &&
  525. (*StrToSkip) &&
  526. (_totlower (*Source) == _totlower (*StrToSkip))
  527. ) {
  528. Source = _mbsinc (Source );
  529. StrToSkip = _mbsinc (StrToSkip);
  530. Source = SkipSpaceA (Source );
  531. StrToSkip = SkipSpaceA (StrToSkip);
  532. }
  533. if (*StrToSkip) {
  534. return FALSE;
  535. }
  536. if (Result) {
  537. *Result = Source;
  538. }
  539. return TRUE;
  540. }
  541. BOOL
  542. pParseMacro (
  543. IN PCSTR FileName,
  544. IN WORD VersionNo,
  545. IN PCSTR MacroStr
  546. )
  547. /*++
  548. Routine Description:
  549. Parses a macro from |SYSTEM file inside a HLP file to see if there is a RegisterRoutine macro
  550. If true, then it will eventually do something with that information.
  551. Arguments:
  552. MacroStr - String to parse
  553. VersionNo - Version number for this help file (we will use this to identify the subsystem where
  554. this file is more likely to be loaded).
  555. Return Value:
  556. TRUE if successful, FALSE if at least one error occured
  557. --*/
  558. {
  559. BOOL result = TRUE;
  560. PCSTR endStr;
  561. char dllName[MAX_MBCHAR_PATH];
  562. char exportName[MAX_MBCHAR_PATH];
  563. PCSTR dllNameNoPath;
  564. //let's see if we have a pattern like RegisterRoutine(" or RR(" here
  565. if (!pSkipPattern (MacroStr, &MacroStr, MACRO_NEEDED_0)) {
  566. if (!pSkipPattern (MacroStr, &MacroStr, MACRO_NEEDED_1)) {
  567. return TRUE;
  568. }
  569. }
  570. //OK, we are ready to extract the dll name from the macro string
  571. endStr = _mbschr (MacroStr, '\"');
  572. if (!endStr) {
  573. return FALSE;
  574. }
  575. endStr = (PCSTR) _mbsinc (SkipSpaceRA (MacroStr, _mbsdec(MacroStr, endStr)));
  576. if (!endStr) {
  577. return FALSE;
  578. }
  579. //now we have the dll name between MacroStr and EndStr
  580. //a little safety check
  581. if ((endStr - MacroStr) >= MAX_MBCHAR_PATH-1) {
  582. return FALSE;
  583. }
  584. StringCopyABA (dllName, MacroStr, endStr);
  585. if (!dllName[0]) {
  586. return FALSE;
  587. }
  588. //now see if this is a full path file name or not
  589. dllNameNoPath = GetFileNameFromPathA (dllName);
  590. //ok, now the following pattern should be >>","<<
  591. if (!pSkipPattern (endStr, &MacroStr, "\",\"")) {
  592. return TRUE;
  593. }
  594. //OK, we are ready to extract the export function name from the macro string
  595. endStr = _mbschr (MacroStr, '\"');
  596. if (!endStr) {
  597. return FALSE;
  598. }
  599. endStr = (PCSTR) _mbsinc (SkipSpaceRA (MacroStr, _mbsdec(MacroStr, endStr)));
  600. if (!endStr) {
  601. return FALSE;
  602. }
  603. //now we have the dll name between MacroStr and EndStr
  604. //a little safety check
  605. if ((endStr - MacroStr) >= MAX_MBCHAR_PATH-1) {
  606. return FALSE;
  607. }
  608. StringCopyABA (exportName, MacroStr, endStr);
  609. if (!exportName[0]) {
  610. return FALSE;
  611. }
  612. //add to MemDb in HelpFilesDll category
  613. if (!MemDbSetValueExA (
  614. MEMDB_CATEGORY_HELP_FILES_DLLA,
  615. dllNameNoPath,
  616. NULL,
  617. NULL,
  618. 0,
  619. NULL
  620. )) {
  621. return FALSE;
  622. }
  623. return result;
  624. }
  625. BOOL
  626. pCheckDlls (
  627. IN PCSTR FileName,
  628. IN PCSTR SystemFileImage
  629. )
  630. /*++
  631. Routine Description:
  632. This routine checks the internal |SYSTEM file of a HLP file looking for additional
  633. Dll's. It does that trying to find either "RegisterRoutine" or "RR" macros.
  634. For every Dll found tries to match the Dll with the HLP file version.
  635. For every incompatibility found, adds an entry in the report presented to the user.
  636. Arguments:
  637. FileName - Full name of the help file
  638. SystemFileImage - Pointer to the beginning of the internal |SYSTEM file.
  639. Return Value:
  640. TRUE if successful, FALSE if at least one error occured
  641. --*/
  642. {
  643. PSF_HEADER psf_Header;
  644. PDATA_HEADER pdata_Header;
  645. PCSTR currImage;
  646. PCSTR friendlyName = NULL;
  647. LONG sf_BytesToRead;
  648. BOOL result = TRUE;
  649. BOOL bNoFriendlyName = FALSE;
  650. //we are going to read from various portions of this memory mapped file. There
  651. //is no guarantee that we are going to keep our readings inside the file so let's
  652. //prevent any access violation.
  653. __try {
  654. //first thing. Extract help file version
  655. psf_Header = (PSF_HEADER) SystemFileImage;
  656. //if file version is 3.0 or less we have nothing else to do
  657. if (psf_Header->VersionNo <= Version3_0) {
  658. __leave;
  659. }
  660. //Now scanning |SYSTEM file looking for macros. We must be careful to stop when
  661. //this internal file is over.
  662. sf_BytesToRead = psf_Header->FileSize + sizeof (IF_HEADER) - sizeof (SF_HEADER);
  663. currImage = SystemFileImage + sizeof (SF_HEADER);
  664. while (sf_BytesToRead > 0) {
  665. //map a data header
  666. pdata_Header = (PDATA_HEADER) currImage;
  667. currImage += sizeof (DATA_HEADER);
  668. sf_BytesToRead -= sizeof (DATA_HEADER);
  669. //see what kind of info we have here (macros are in tagConfig)
  670. if (pdata_Header->InfoType == tagConfig) {
  671. //parsing the string to see if there is a RegisterRoutine macro
  672. //If so we are going to store the Dll into MemDB.
  673. if (!pParseMacro(FileName, psf_Header->VersionNo, currImage)) {
  674. result = FALSE;
  675. __leave;
  676. }
  677. } else if (pdata_Header->InfoType == tagTitle) {
  678. //Now we have the help file friendly name. Map ANSI string
  679. if (!bNoFriendlyName) {
  680. friendlyName = currImage;
  681. }
  682. } else if (pdata_Header->InfoType == tagLCID) {
  683. if (pdata_Header->InfoLength == sizeof (KEYWORD_LOCALE)) {
  684. DWORD lcid;
  685. PKEYWORD_LOCALE pkl = (PKEYWORD_LOCALE)currImage;
  686. lcid = MAKELCID (pkl->langid, SORT_DEFAULT);
  687. if (!IsValidLocale (lcid, LCID_INSTALLED)) {
  688. //
  689. // the title is not friendly
  690. //
  691. bNoFriendlyName = TRUE;
  692. friendlyName = NULL;
  693. }
  694. }
  695. }
  696. currImage += pdata_Header->InfoLength;
  697. sf_BytesToRead -= pdata_Header->InfoLength;
  698. }
  699. //we finally finished scanning the help file. Let's take advantage of the __try __except block
  700. //and try to see if this help file and all it's extension dlls will run in the same
  701. //subsystem on NT.
  702. if (!pCheckSubsystemMatch (
  703. FileName,
  704. friendlyName,
  705. psf_Header->VersionNo
  706. )) {
  707. result = FALSE;
  708. }
  709. }
  710. __except (EXCEPTION_EXECUTE_HANDLER) {
  711. //if some exception occured maybe we managed to get something in MemDB
  712. //so let's make some cleanup
  713. MemDbDeleteTreeA (MEMDB_CATEGORY_HELP_FILES_DLLA);
  714. return FALSE;
  715. }
  716. return result;
  717. }
  718. BOOL
  719. pProcessHelpFile (
  720. IN PCSTR FileName
  721. )
  722. /*++
  723. Routine Description:
  724. This routine checks a HLP file looking for additional DLLs used. If such a DLL is found
  725. we will try to see if this combination will run on NT. The fact is that depending on
  726. the version of the HLP file it will be opened by WinHelp.EXE (16 bit app) or
  727. WinHlp32.EXE (32 bit app). Now suppose that a HLP file is opened by WinHlp32.EXE and
  728. it has an additional 16 bit DLL, this combination will not work on NT (WinHlp32.EXE
  729. and the aditional DLL are running in differend subsystems).
  730. For every incompatibility found, we will add an entry in the report presented to the
  731. user.
  732. Arguments:
  733. FileName - Full information about the location of the file
  734. Return Value:
  735. TRUE if file was processed successful, FALSE if at least one error occured
  736. --*/
  737. {
  738. PCSTR fileImage = NULL;
  739. HANDLE mapHandle = NULL;
  740. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  741. PCSTR systemFileImage = NULL;
  742. BOOL result = TRUE;
  743. //map the file into memory and get a it's address
  744. fileImage = MapFileIntoMemory (FileName, &fileHandle, &mapHandle);
  745. __try {
  746. if (fileImage == NULL) {
  747. result = FALSE;
  748. __leave;
  749. }
  750. //find the internal file |SYSTEM
  751. systemFileImage = pGetSystemFilePtr (fileImage);
  752. if (systemFileImage == fileImage) {
  753. result = FALSE;
  754. __leave;
  755. }
  756. //check every additional dll used by help file
  757. result = result && pCheckDlls (FileName, systemFileImage);
  758. }
  759. __finally {
  760. //unmap and close help file
  761. UnmapFile ((PVOID) fileImage, mapHandle, fileHandle);
  762. }
  763. return result;
  764. }
  765. PSTR
  766. pGetTitle (
  767. IN PCSTR FileName,
  768. IN PCSTR SystemFileImage
  769. )
  770. /*++
  771. Routine Description:
  772. This routine checks the internal |SYSTEM file of a HLP file looking for it's title
  773. Arguments:
  774. FileName - Full name of the help file
  775. SystemFileImage - Pointer to the beginning of the internal |SYSTEM file.
  776. Return Value:
  777. HLP file title (if available)
  778. --*/
  779. {
  780. PSF_HEADER psf_Header;
  781. PDATA_HEADER pdata_Header;
  782. PCSTR currImage;
  783. LONG sf_BytesToRead;
  784. PSTR result = NULL;
  785. //we are going to read from various portions of this memory mapped file. There
  786. //is no guarantee that we are going to keep our readings inside the file so let's
  787. //prevent any access violation.
  788. __try {
  789. //first thing. Extract help file version
  790. psf_Header = (PSF_HEADER) SystemFileImage;
  791. //if file version is 3.0 or less we have nothing else to do
  792. if (psf_Header->VersionNo <= Version3_0) {
  793. __leave;
  794. }
  795. //Now scanning |SYSTEM file looking for macros. We must be careful to stop when
  796. //this internal file is over.
  797. sf_BytesToRead = psf_Header->FileSize + sizeof (IF_HEADER) - sizeof (SF_HEADER);
  798. currImage = SystemFileImage + sizeof (SF_HEADER);
  799. while (sf_BytesToRead > 0) {
  800. //map a data header
  801. pdata_Header = (PDATA_HEADER) currImage;
  802. currImage += sizeof (DATA_HEADER);
  803. sf_BytesToRead -= sizeof (DATA_HEADER);
  804. if (pdata_Header->InfoType == tagTitle) {
  805. //Now we have the help file friendly name. Map ANSI string
  806. result = DuplicatePathStringA (currImage, 0);
  807. break;
  808. }
  809. currImage += pdata_Header->InfoLength;
  810. sf_BytesToRead -= pdata_Header->InfoLength;
  811. }
  812. }
  813. __except (EXCEPTION_EXECUTE_HANDLER) {
  814. result = NULL;
  815. }
  816. return result;
  817. }
  818. PSTR
  819. GetHlpFileTitle (
  820. IN PCSTR FileName
  821. )
  822. /*++
  823. Routine Description:
  824. This routine opens a HLP file looking for it's title.
  825. Arguments:
  826. FileName - Full information about the location of the file
  827. Return Value:
  828. The title of the HLP file if available
  829. --*/
  830. {
  831. PCSTR fileImage = NULL;
  832. HANDLE mapHandle = NULL;
  833. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  834. PCSTR systemFileImage = NULL;
  835. PSTR result = NULL;
  836. //map the file into memory and get a it's address
  837. fileImage = MapFileIntoMemory (FileName, &fileHandle, &mapHandle);
  838. __try {
  839. if (fileImage == NULL) {
  840. __leave;
  841. }
  842. //find the internal file |SYSTEM
  843. systemFileImage = pGetSystemFilePtr (fileImage);
  844. if (systemFileImage == fileImage) {
  845. __leave;
  846. }
  847. //check every additional dll used by help file
  848. result = pGetTitle (FileName, systemFileImage);
  849. }
  850. __finally {
  851. //unmap and close help file
  852. UnmapFile ((PVOID) fileImage, mapHandle, fileHandle);
  853. }
  854. return result;
  855. }
  856. BOOL
  857. ProcessHelpFile (
  858. IN PFILE_HELPER_PARAMS Params
  859. )
  860. /*++
  861. Routine Description:
  862. This routine is mainly a dispatcher. Will pass HLP files to routine pProcessHelpFile
  863. and modules to pProcessModule. The goal is to create two MemDb trees containing
  864. the export functions needed and provided to be able to estimate about some
  865. modules or help files not working after migration.
  866. Arguments:
  867. Params - Full information about the location of the file
  868. Return Value:
  869. TRUE if successful, FALSE otherwise
  870. --*/
  871. {
  872. PSTR fileName;
  873. TCHAR key[MEMDB_MAX];
  874. DWORD dontCare;
  875. //we are going to process this file if :
  876. // 1. has HLP extension
  877. // 2. is not marked as incompatible (this routine also checks for handled)
  878. if (!StringIMatch (Params->Extension, TEXT(".HLP"))||
  879. IsReportObjectIncompatible (Params->FullFileSpec)
  880. ) {
  881. return TRUE;
  882. }
  883. MemDbBuildKey (key, MEMDB_CATEGORY_COMPATIBLE_HLP, Params->FullFileSpec, NULL, NULL);
  884. if (MemDbGetValue (key, &dontCare)) {
  885. return TRUE;
  886. }
  887. #ifdef UNICODE
  888. fileName = ConvertWtoA (Params->FullFileSpec);
  889. #else
  890. fileName = (PSTR) Params->FullFileSpec;
  891. #endif
  892. if (!pProcessHelpFile (fileName)) {
  893. DEBUGMSG ((DBG_HELPFILES, "Error processing help file %s", fileName));
  894. }
  895. #ifdef UNICODE
  896. FreeConvertedStr (fileName);
  897. #endif
  898. return TRUE;
  899. }
  900. DWORD
  901. InitHlpProcessing (
  902. IN DWORD Request
  903. )
  904. {
  905. switch (Request) {
  906. case REQUEST_QUERYTICKS:
  907. return TICKS_INIT_HLP_PROCESSING;
  908. case REQUEST_RUN:
  909. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KERNEL", NULL, NULL, 0, NULL);
  910. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KERNEL.EXE", NULL, NULL, 0, NULL);
  911. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KERNEL32", NULL, NULL, 0, NULL);
  912. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KERNEL32.DLL", NULL, NULL, 0, NULL);
  913. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KRNL386", NULL, NULL, 0, NULL);
  914. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "KRNL386.EXE", NULL, NULL, 0, NULL);
  915. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "USER", NULL, NULL, 0, NULL);
  916. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "USER.EXE", NULL, NULL, 0, NULL);
  917. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "USER32", NULL, NULL, 0, NULL);
  918. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "USER32.DLL", NULL, NULL, 0, NULL);
  919. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "GDI", NULL, NULL, 0, NULL);
  920. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "GDI.EXE", NULL, NULL, 0, NULL);
  921. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "GDI32", NULL, NULL, 0, NULL);
  922. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "GDI32.DLL", NULL, NULL, 0, NULL);
  923. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "SHELL", NULL, NULL, 0, NULL);
  924. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "SHELL.DLL", NULL, NULL, 0, NULL);
  925. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "SHELL32", NULL, NULL, 0, NULL);
  926. MemDbSetValueEx (MEMDB_CATEGORY_GOOD_HLP_EXTENSIONS, "SHELL32.DLL", NULL, NULL, 0, NULL);
  927. return ERROR_SUCCESS;
  928. default:
  929. DEBUGMSG ((DBG_ERROR, "Bad parameter in InitHlpProcessing"));
  930. }
  931. return 0;
  932. }