Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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