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.

1229 lines
36 KiB

  1. //=============================================================================
  2. // File: tmplfile.cpp
  3. // Author: a-jammar
  4. // Covers: CTemplateFileFunctions
  5. //
  6. // Copyright (c) 1998-1999 Microsoft Corporation
  7. //
  8. // This file contains the functions necessary to read in the MSInfo template
  9. // file. The template file is a text file with an NFT extension. Reading
  10. // multiple template files is allowed - the contents are merged together into
  11. // the category tree maintained by the snap-in.
  12. //=============================================================================
  13. #include "stdafx.h"
  14. #include "gather.h"
  15. #include "gathint.h"
  16. //-----------------------------------------------------------------------------
  17. // This function is the main entry point for reading the template file into
  18. // the category structure used by the CDataGatherer object. If there is no
  19. // tree when this function is called, the tree is created from the contents
  20. // of the file. If there is a tree already in place, then the contents of the
  21. // file are loaded under the existing root node.
  22. //-----------------------------------------------------------------------------
  23. BOOL CTemplateFileFunctions::ReadTemplateFile(CFile *pFile, CDataGatherer *pGatherer)
  24. {
  25. ASSERT(pFile && pGatherer);
  26. if (!VerifyUNICODEFile(pFile))
  27. {
  28. TRACE0("-- CTemplateFileFunctions::ReadTemplateFile() passed non-UNICODE file\n");
  29. return FALSE;
  30. }
  31. if (!ReadHeaderInfo(pFile, pGatherer))
  32. {
  33. TRACE0("-- CTemplateFileFunctions::ReadTemplateFile() failed from ReadHeaderInfo\n");
  34. return FALSE;
  35. }
  36. if (pGatherer->m_dwRootID)
  37. {
  38. // There is already a tree present. Insert the contents of the file under
  39. // the root node, after the last first level node. Walk through the first
  40. // level of the internal category tree. TBD: add a way to extend a specified node
  41. INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(pGatherer->m_dwRootID);
  42. ASSERT(pInternal);
  43. if (pInternal)
  44. {
  45. pInternal = pGatherer->GetInternalRep(pInternal->m_dwChildID);
  46. while (pInternal && pInternal->m_dwNextID)
  47. pInternal = pGatherer->GetInternalRep(pInternal->m_dwNextID);
  48. }
  49. DWORD dwPrevID = (pInternal) ? pInternal->m_dwID : 0;
  50. if (ReadNodeRecursive(pFile, pGatherer, pGatherer->m_dwRootID, dwPrevID) == 0)
  51. return FALSE;
  52. return TRUE;
  53. }
  54. else
  55. {
  56. pGatherer->m_dwRootID = ReadNodeRecursive(pFile, pGatherer, 0, 0);
  57. if (pGatherer->m_dwRootID == 0)
  58. return FALSE;
  59. return TRUE;
  60. }
  61. }
  62. //-----------------------------------------------------------------------------
  63. // This method reads the header information from the file before the recursive
  64. // category descriptions. Note: since this is the first and only version of
  65. // the template file, we take the easy way out and just make sure that the
  66. // identifier and version are there.
  67. //-----------------------------------------------------------------------------
  68. BOOL CTemplateFileFunctions::ReadHeaderInfo(CFile *pFile, CDataGatherer * /* pGatherer */)
  69. {
  70. return VerifyAndAdvanceFile(pFile, CString(_T(TEMPLATE_FILE_TAG)));
  71. }
  72. //-----------------------------------------------------------------------------
  73. // This method verifies that the passed file is a UNICODE file, by reading the
  74. // value 0xFEFF from the file. It also leaves the file pointer past this word.
  75. //-----------------------------------------------------------------------------
  76. BOOL CTemplateFileFunctions::VerifyUNICODEFile(CFile *pFile)
  77. {
  78. WORD verify;
  79. if (pFile->Read((void *) &verify, sizeof(WORD)) != sizeof(WORD))
  80. {
  81. TRACE0("-- CTemplateFileFunctions::VerifyUNICODEFile() couldn't read WORD\n");
  82. return FALSE;
  83. }
  84. return (verify == 0xFEFF);
  85. }
  86. //-----------------------------------------------------------------------------
  87. // This is the recursive function to read a node. It reads the information
  88. // from the node parameters, creates the node, and processes the contents of
  89. // the block following the node (contained within "{}"'s). It's called
  90. // recursively if there are any nodes in that block.
  91. //-----------------------------------------------------------------------------
  92. DWORD CTemplateFileFunctions::ReadNodeRecursive(CFile *pFile, CDataGatherer *pGatherer, DWORD dwParentID, DWORD dwPrevID)
  93. {
  94. // Determine if we need to create a new category for this node. Search through the
  95. // other sibling nodes to see if there is one which matches this category's name.
  96. // If there is, just use that category, and don't create a new one. Read the
  97. // information from the file to determine the identifier for the new category.
  98. CString strEnumerateClass, strIdentifier;
  99. if (!VerifyAndAdvanceFile(pFile, CString(NODE_KEYWORD) + CString("(")))
  100. {
  101. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on node keyword\n");
  102. return 0;
  103. }
  104. if (!ReadArgument(pFile, strEnumerateClass))
  105. {
  106. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on enumerate class\n");
  107. return 0;
  108. }
  109. if (!ReadArgument(pFile, strIdentifier))
  110. {
  111. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on identifier\n");
  112. return 0;
  113. }
  114. // Look for a node among the siblings which has a matching strIdentifier.
  115. INTERNAL_CATEGORY * pInternal;
  116. DWORD dwSearchID = dwPrevID, dwMatchingID = 0;
  117. while (dwMatchingID == 0 && dwSearchID != 0)
  118. {
  119. pInternal = pGatherer->GetInternalRep(dwSearchID);
  120. if (pInternal)
  121. {
  122. if (pInternal->m_strIdentifier.CompareNoCase(strIdentifier) == 0)
  123. dwMatchingID = dwSearchID;
  124. dwSearchID = pInternal->m_dwPrevID;
  125. }
  126. else
  127. {
  128. ASSERT(pInternal);
  129. break;
  130. }
  131. }
  132. INTERNAL_CATEGORY * pCategory = ((dwMatchingID) ? pInternal : NULL);
  133. DWORD dwID = dwMatchingID;
  134. if (pCategory == NULL)
  135. {
  136. // Create the category for the node.
  137. dwID = CreateCategory(pGatherer, dwParentID, dwPrevID);
  138. pCategory = pGatherer->GetInternalRep(dwID);
  139. if (!pCategory)
  140. return 0;
  141. // Read the contents of the node argument list ("node(enum, identifier, field(source, formatstr, arg...))")
  142. // We've already read up to and including the identifier.
  143. pCategory->m_strEnumerateClass = strEnumerateClass;
  144. pCategory->m_strIdentifier = strIdentifier;
  145. if (!ReadField(pFile, pCategory->m_fieldName))
  146. return 0;
  147. // Copy the field name to the name of the category (they are two different
  148. // member variables to allow for dynamically refreshed names, which turns
  149. // out to be unnecessary in this version).
  150. pCategory->m_categoryName.m_strText = pCategory->m_fieldName.m_strFormat;
  151. if (!ReadArgument(pFile, pCategory->m_strNoInstances))
  152. {
  153. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on no instance message\n");
  154. return 0;
  155. }
  156. }
  157. else
  158. {
  159. // This node already existed, and we just want to read past the rest of
  160. // it's description without changing the existing node.
  161. GATH_FIELD fieldTemp;
  162. CString strTemp;
  163. if (!ReadField(pFile, fieldTemp))
  164. return 0;
  165. if (!ReadArgument(pFile, strTemp))
  166. {
  167. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on no instance message\n");
  168. return 0;
  169. }
  170. }
  171. if (!VerifyAndAdvanceFile(pFile, CString("){")))
  172. {
  173. TRACE1("-- CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on node \"){\" (%s)\n", pCategory->m_strIdentifier);
  174. return 0;
  175. }
  176. // Process the contents of the block (enclosed in "{}") for this node.
  177. DWORD dwSubNodePrev = 0, dwNewNode = 0;
  178. CString strKeyword;
  179. // If this new category isn't actually new (i.e. it is being read from a
  180. // template and overlaps an existing category) see if there are any
  181. // existing children.
  182. if (pCategory->m_dwChildID)
  183. {
  184. pInternal = pGatherer->GetInternalRep(pCategory->m_dwChildID); ASSERT(pInternal);
  185. while (pInternal && pInternal->m_dwNextID)
  186. pInternal = pGatherer->GetInternalRep(pInternal->m_dwNextID);
  187. if (pInternal)
  188. dwSubNodePrev = pInternal->m_dwID;
  189. }
  190. while (GetKeyword(pFile, strKeyword))
  191. {
  192. if (strKeyword.CompareNoCase(CString(NODE_KEYWORD)) == 0)
  193. {
  194. dwNewNode = ReadNodeRecursive(pFile, pGatherer, dwID, dwSubNodePrev);
  195. if (dwNewNode == 0)
  196. return 0;
  197. // If this is the first child node we've read, save its ID.
  198. if (pCategory->m_dwChildID == 0)
  199. pCategory->m_dwChildID = dwNewNode;
  200. // If we've read another child node, set its next field appropriately.
  201. if (dwSubNodePrev)
  202. {
  203. INTERNAL_CATEGORY * pPrevCategory = pGatherer->GetInternalRep(dwSubNodePrev);
  204. if (pPrevCategory)
  205. pPrevCategory->m_dwNextID = dwNewNode;
  206. }
  207. dwSubNodePrev = dwNewNode;
  208. }
  209. else if (strKeyword.CompareNoCase(CString(COLUMN_KEYWORD)) == 0)
  210. {
  211. if (!ReadColumnInfo(pFile, pGatherer, dwID))
  212. {
  213. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadColumnInfo\n");
  214. return FALSE;
  215. }
  216. }
  217. else if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0)
  218. {
  219. GATH_LINESPEC * pNewLineSpec = ReadLineInfo(pFile, pGatherer);
  220. if (pNewLineSpec == NULL)
  221. {
  222. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineInfo\n");
  223. return FALSE;
  224. }
  225. // Add the line we just read in to the end of the list of line specs for this
  226. // internal category.
  227. if (pCategory->m_pLineSpec == NULL)
  228. pCategory->m_pLineSpec = pNewLineSpec;
  229. else
  230. {
  231. GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec;
  232. while (pLineSpec->m_pNext)
  233. pLineSpec = pLineSpec->m_pNext;
  234. pLineSpec->m_pNext = pNewLineSpec;
  235. }
  236. }
  237. else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0)
  238. {
  239. GATH_LINESPEC * pNewLineSpec = ReadLineEnumRecursive(pFile, pGatherer);
  240. if (pNewLineSpec == NULL)
  241. {
  242. TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineEnumRecursive\n");
  243. return FALSE;
  244. }
  245. // Add the line we just read in to the end of the list of line specs for this
  246. // internal category.
  247. if (pCategory->m_pLineSpec == NULL)
  248. pCategory->m_pLineSpec = pNewLineSpec;
  249. else
  250. {
  251. GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec;
  252. while (pLineSpec->m_pNext)
  253. pLineSpec = pLineSpec->m_pNext;
  254. pLineSpec->m_pNext = pNewLineSpec;
  255. }
  256. //if (!ReadLineEnumRecursive(pFile, pGatherer, dwID))
  257. //{
  258. // TRACE0("CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineEnumRecursive\n");
  259. // return FALSE;
  260. //}
  261. }
  262. else
  263. {
  264. ASSERT(FALSE);
  265. VerifyAndAdvanceFile(pFile, strKeyword);
  266. }
  267. }
  268. if (!VerifyAndAdvanceFile(pFile, CString("}")))
  269. {
  270. TRACE0("CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on \"}\"\n");
  271. return 0;
  272. }
  273. return dwID;
  274. }
  275. //-----------------------------------------------------------------------------
  276. // This method verifies that the text in strVerify comes next in the file (not
  277. // including case or whitespace differences) and advances the file past that
  278. // text. If the text was the next content in the file, TRUE is returned,
  279. // otherwise FALSE. If FALSE is returned, the file is backed up to where it
  280. // was when this method was called.
  281. //-----------------------------------------------------------------------------
  282. BOOL CTemplateFileFunctions::VerifyAndAdvanceFile(CFile * pFile, const CString &strVerify)
  283. {
  284. DWORD dwPosition = pFile->GetPosition();
  285. TCHAR cLastChar, cCurrentChar = _T('\0');
  286. BOOL fInComment = FALSE;
  287. int iCharIndex = 0, iStringLen = strVerify.GetLength();
  288. while (iCharIndex < iStringLen)
  289. {
  290. // Save the last character read, since the comment token ("//") is
  291. // two characters long.
  292. cLastChar = cCurrentChar;
  293. // Read the next character in the file.
  294. if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR))
  295. {
  296. TRACE0("-- CTemplateFileFunctions::VerifyAndAdvanceFile() couldn't read character\n");
  297. return FALSE;
  298. }
  299. // If we're in a comment, and the character we just read isn't a new line,
  300. // we want to ignore it.
  301. if (fInComment)
  302. {
  303. if (cCurrentChar == _T('\n'))
  304. fInComment = FALSE;
  305. continue;
  306. }
  307. // Check to see if we've started into a comment. Note that we ignore
  308. // the first '/' also by continuing.
  309. if (cCurrentChar == _T('/'))
  310. {
  311. if (cLastChar == _T('/'))
  312. fInComment = TRUE;
  313. continue;
  314. }
  315. // Skip whitespace, and also leading commas.
  316. if (_istspace(cCurrentChar) || (cCurrentChar == _T(',') && iCharIndex == 0))
  317. continue;
  318. if (cCurrentChar != strVerify[iCharIndex])
  319. {
  320. pFile->Seek((LONG)dwPosition, CFile::begin);
  321. return FALSE;
  322. }
  323. iCharIndex++;
  324. }
  325. return TRUE;
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Create a new category, and return the ID for the category.
  329. //-----------------------------------------------------------------------------
  330. DWORD CTemplateFileFunctions::CreateCategory(CDataGatherer * pGatherer, DWORD dwParentID, DWORD dwPrevID)
  331. {
  332. DWORD dwID = pGatherer->m_dwNextFreeID;
  333. INTERNAL_CATEGORY * pInternalCat;
  334. INTERNAL_CATEGORY * pPreviousCat;
  335. CString strName;
  336. pInternalCat = new INTERNAL_CATEGORY;
  337. if (!pInternalCat)
  338. return 0;
  339. pInternalCat->m_fListView = TRUE;
  340. pInternalCat->m_dwID = dwID;
  341. pInternalCat->m_dwParentID = dwParentID;
  342. pInternalCat->m_dwPrevID = dwPrevID;
  343. if (dwPrevID)
  344. {
  345. pPreviousCat = pGatherer->GetInternalRep(dwPrevID);
  346. if (pPreviousCat)
  347. pPreviousCat->m_dwNextID = dwID;
  348. }
  349. pGatherer->m_mapCategories.SetAt((WORD)dwID, (void *) pInternalCat);
  350. pGatherer->m_dwNextFreeID++;
  351. return dwID;
  352. }
  353. //-----------------------------------------------------------------------------
  354. // This method returns the next keyword in the file. Any whitespace or
  355. // punctuation is skipped until an alphanumeric character is read. The keyword
  356. // returned is the string starting with this character until whitespace or
  357. // punctuation is encountered. Note: it is very important that this function
  358. // returns the file to the state it was in when the function started, with
  359. // the current position restored.
  360. //
  361. // TBD: inefficient
  362. //-----------------------------------------------------------------------------
  363. BOOL CTemplateFileFunctions::GetKeyword(CFile * pFile, CString & strKeyword)
  364. {
  365. CString strTemp = CString("");
  366. DWORD dwPosition = pFile->GetPosition();
  367. TCHAR cLastChar, cCurrentChar = _T('\0');
  368. BOOL fInComment = FALSE;
  369. // Skip over whitespace characters until we reach an alphanumeric char.
  370. do
  371. {
  372. // Save the last character read, since the comment token ("//") is
  373. // two characters long.
  374. cLastChar = cCurrentChar;
  375. // Read the next character in the file.
  376. if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR))
  377. return FALSE;
  378. // If we're in a comment, and the character we just read isn't a new line,
  379. // we want to ignore it.
  380. if (fInComment)
  381. {
  382. if (cCurrentChar == _T('\n'))
  383. fInComment = FALSE;
  384. continue;
  385. }
  386. // Check to see if we've started into a comment.
  387. if (cCurrentChar == _T('/'))
  388. {
  389. if (cLastChar == _T('/'))
  390. fInComment = TRUE;
  391. continue;
  392. }
  393. } while (_istspace(cCurrentChar) || cCurrentChar == _T('/') || fInComment);
  394. // Read the keyword while it's alphanumeric.
  395. if (_istalnum(cCurrentChar))
  396. do
  397. {
  398. strTemp += CString(cCurrentChar);
  399. if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR))
  400. return FALSE;
  401. } while (_istalnum(cCurrentChar));
  402. // Reset the file, set the keyword and return.
  403. pFile->Seek((LONG)dwPosition, CFile::begin);
  404. strKeyword = strTemp;
  405. return !strTemp.IsEmpty();
  406. }
  407. //-----------------------------------------------------------------------------
  408. // This method reads in a "column" line from the file, adding the appropriate
  409. // entries for the columns into the category referenced by dwID. The column
  410. // line contains a bunch of fields in a list.
  411. //-----------------------------------------------------------------------------
  412. BOOL CTemplateFileFunctions::ReadColumnInfo(CFile * pFile, CDataGatherer * pGatherer, DWORD dwID)
  413. {
  414. CString strTemp;
  415. if (!VerifyAndAdvanceFile(pFile, CString(COLUMN_KEYWORD) + CString("(")))
  416. {
  417. TRACE0("CTemplateFileFunctions::ReadColumnInfo() Verify.. failed on column keyword\n");
  418. return FALSE;
  419. }
  420. // Get the internal category referenced by dwID.
  421. INTERNAL_CATEGORY * pCategory = pGatherer->GetInternalRep(dwID);
  422. if (!pCategory)
  423. return FALSE;
  424. // We only allow one column specifier list per node.
  425. if (pCategory->m_pColSpec)
  426. {
  427. TRACE0("CTemplateFileFunctions::ReadColumnInfo() already column information present\n");
  428. return FALSE;
  429. }
  430. // While we are still reading fields from the file, keep adding to the column list.
  431. GATH_FIELD * pNewField = new GATH_FIELD;
  432. if (pNewField == NULL)
  433. return FALSE;
  434. while (ReadField(pFile, *pNewField))
  435. {
  436. if (pCategory->m_pColSpec == NULL)
  437. pCategory->m_pColSpec = pNewField;
  438. else
  439. {
  440. // Scan to the last field in the linespec.m_pFields list, and insert the new field.
  441. GATH_FIELD * pFieldScan = pCategory->m_pColSpec;
  442. while (pFieldScan->m_pNext)
  443. pFieldScan = pFieldScan->m_pNext;
  444. pFieldScan->m_pNext = pNewField;
  445. }
  446. // Parse the width out of the column caption.
  447. if (pNewField->m_strFormat.ReverseFind(_T(',')) != -1)
  448. {
  449. strTemp = pNewField->m_strFormat.Right(pNewField->m_strFormat.GetLength() - pNewField->m_strFormat.ReverseFind(_T(',')) - 1);
  450. pNewField->m_usWidth = (unsigned short) atoi(strTemp);
  451. pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.GetLength() - strTemp.GetLength() - 1);
  452. }
  453. else
  454. {
  455. ASSERT(FALSE);
  456. pNewField->m_usWidth = (unsigned short) 80;
  457. }
  458. // Parse off any remaining information in the column label (the label ends
  459. // with [name, n], when n is the width, and name is the ID for the column
  460. // which should not be displayed).
  461. if (pNewField->m_strFormat.ReverseFind(_T('[')) != -1)
  462. pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.ReverseFind(_T('[')) - 1);
  463. // Read the sorting type from the file.
  464. if (ReadArgument(pFile, strTemp))
  465. {
  466. if (strTemp.CompareNoCase(CString(_T(SORT_LEXICAL))) == 0)
  467. pNewField->m_sort = LEXICAL;
  468. else if (strTemp.CompareNoCase(CString(_T(SORT_VALUE))) == 0)
  469. pNewField->m_sort = BYVALUE;
  470. else
  471. pNewField->m_sort = NOSORT;
  472. }
  473. else
  474. {
  475. TRACE0("CTemplateFileFunctions::ReadColumnInfo() couldn't read column sorting\n");
  476. return FALSE;
  477. }
  478. // Read the complexity (BASIC or ADVANCED) from the file.
  479. if (ReadArgument(pFile, strTemp))
  480. {
  481. if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0)
  482. pNewField->m_datacomplexity = ADVANCED;
  483. else
  484. pNewField->m_datacomplexity = BASIC;
  485. }
  486. else
  487. {
  488. TRACE0("CTemplateFileFunctions::ReadColumnInfo() couldn't read data complexity\n");
  489. return FALSE;
  490. }
  491. pNewField = new GATH_FIELD;
  492. if (pNewField == NULL)
  493. return FALSE;
  494. }
  495. delete pNewField;
  496. if (!VerifyAndAdvanceFile(pFile, CString(")")))
  497. {
  498. TRACE0("CTemplateFileFunctions::ReadColumnInfo() Verify.. failed on \")\"\n");
  499. return FALSE;
  500. }
  501. return TRUE;
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Read in the information for a single line. Add the line to the internal
  505. // representation of the category. TBD: inefficient, since this will be
  506. // called multiple times and the line list will need to be scanned to the
  507. // end each time.
  508. //-----------------------------------------------------------------------------
  509. GATH_LINESPEC * CTemplateFileFunctions::ReadLineInfo(CFile * pFile, CDataGatherer * /* pGatherer */)
  510. {
  511. if (!VerifyAndAdvanceFile(pFile, CString(LINE_KEYWORD) + CString("(")))
  512. {
  513. TRACE0("CTemplateFileFunctions::ReadLineInfo() Verify.. failed on line keyword\n");
  514. return NULL;
  515. }
  516. // Declare a line specification variable to store the line info.
  517. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC;
  518. if (pNewLineSpec == NULL)
  519. return NULL;
  520. // While we are still reading fields from the file, keep adding to the column list.
  521. // TBD: inefficient, repeated scans through linespec.m_pFields list.
  522. GATH_FIELD * pNewField = new GATH_FIELD;
  523. if (pNewField == NULL)
  524. {
  525. delete pNewLineSpec;
  526. return NULL;
  527. }
  528. // Read in the complexity (BASIC or ADVANCED) for this line.
  529. CString strTemp;
  530. if (ReadArgument(pFile, strTemp))
  531. {
  532. if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0)
  533. pNewLineSpec->m_datacomplexity = ADVANCED;
  534. else
  535. pNewLineSpec->m_datacomplexity = BASIC;
  536. }
  537. else
  538. {
  539. TRACE0("CTemplateFileFunctions::ReadLineInfo() couldn't read complexity\n");
  540. return FALSE;
  541. }
  542. while (ReadField(pFile, *pNewField))
  543. {
  544. if (pNewLineSpec->m_pFields == NULL)
  545. pNewLineSpec->m_pFields = pNewField;
  546. else
  547. {
  548. // Scan to the last field in the linespec.m_pFields list, and insert the new field.
  549. GATH_FIELD * pFieldScan = pNewLineSpec->m_pFields;
  550. while (pFieldScan->m_pNext)
  551. pFieldScan = pFieldScan->m_pNext;
  552. pFieldScan->m_pNext = pNewField;
  553. }
  554. pNewField = new GATH_FIELD;
  555. if (pNewField == NULL)
  556. {
  557. delete pNewLineSpec;
  558. return NULL;
  559. }
  560. }
  561. delete pNewField;
  562. if (!VerifyAndAdvanceFile(pFile, CString(")")))
  563. {
  564. TRACE0("CTemplateFileFunctions::ReadLineInfo() Verify.. failed on \")\"\n");
  565. delete pNewLineSpec;
  566. return NULL;
  567. }
  568. return pNewLineSpec;
  569. }
  570. //-----------------------------------------------------------------------------
  571. // This method simply reads an argument (as string) from the file, until a
  572. // punctuation or whitespace character is found. If a quote mark is found,
  573. // all characters are included in the string until another quote is found.
  574. // TBD: currently no way to have a quote mark in the string.
  575. //-----------------------------------------------------------------------------
  576. BOOL CTemplateFileFunctions::ReadArgument(CFile * pFile, CString & strSource)
  577. {
  578. BOOL fInQuote = FALSE, fInComment = FALSE;
  579. CString strTemp;
  580. TCHAR cLastChar, cCurrentChar = _T('\0');
  581. // Skip over characters until we reach an alphanumeric char. If we find
  582. // a close paren, then we've reached the end of the argument list and
  583. // should return FALSE.
  584. do
  585. {
  586. // Save the last character read, since the comment token ("//") is
  587. // two characters long.
  588. cLastChar = cCurrentChar;
  589. // Read the next character in the file.
  590. if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR))
  591. {
  592. TRACE0("CTemplateFileFunctions::ReadArgument() couldn't read character\n");
  593. return FALSE;
  594. }
  595. // If we're in a comment, and the character we just read isn't a new line,
  596. // we want to ignore it.
  597. if (fInComment)
  598. {
  599. if (cCurrentChar == _T('\n'))
  600. fInComment = FALSE;
  601. continue;
  602. }
  603. // Check to see if we've started into a comment.
  604. if (cCurrentChar == _T('/'))
  605. {
  606. if (cLastChar == _T('/'))
  607. fInComment = TRUE;
  608. continue;
  609. }
  610. if (cCurrentChar == _T(')'))
  611. return FALSE;
  612. } while (!_istalnum(cCurrentChar) && cCurrentChar != _T('"'));
  613. // Read characters into the string until we find whitespace or punctuation.
  614. do
  615. {
  616. if (cCurrentChar == _T('"'))
  617. {
  618. fInQuote = !fInQuote;
  619. continue;
  620. }
  621. if (_istalnum(cCurrentChar) || fInQuote)
  622. strTemp += CString(cCurrentChar);
  623. else
  624. break;
  625. } while (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) == sizeof(TCHAR));
  626. // If the last character read (the one which terminated this argument) was
  627. // not a comma, then back the file up so that the character can be re-read
  628. // and interpreted.
  629. if (cCurrentChar != _T(','))
  630. pFile->Seek(-(LONG)sizeof(TCHAR), CFile::current);
  631. strSource = strTemp;
  632. return TRUE;
  633. }
  634. //-----------------------------------------------------------------------------
  635. // A field consists of a source string, followed by a format string, followed
  636. // by a list of zero or more arguments.
  637. //-----------------------------------------------------------------------------
  638. BOOL CTemplateFileFunctions::ReadField(CFile * pFile, GATH_FIELD & field)
  639. {
  640. // Advance past the field keyword and read the two source and format strings.
  641. if (!VerifyAndAdvanceFile(pFile, CString(FIELD_KEYWORD) + CString("(")))
  642. return FALSE;
  643. if (!ReadArgument(pFile, field.m_strSource))
  644. {
  645. TRACE0("CTemplateFileFunctions::ReadField() ReadArgument failed on source\n");
  646. return FALSE;
  647. }
  648. if (!ReadArgument(pFile, field.m_strFormat))
  649. {
  650. TRACE0("CTemplateFileFunctions::ReadField() ReadArgument failed on format\n");
  651. return FALSE;
  652. }
  653. // Read arguments until there are no more, building them into a list of
  654. // arguments stored by the FIELD struct.
  655. GATH_VALUE arg;
  656. GATH_VALUE * pArg = NULL;
  657. while (ReadArgument(pFile, arg.m_strText))
  658. {
  659. if (pArg == NULL)
  660. {
  661. field.m_pArgs = new GATH_VALUE;
  662. if (field.m_pArgs == NULL)
  663. {
  664. TRACE0("CTemplateFileFunctions::ReadField() field.m_pArgs allocation failed\n");
  665. return FALSE;
  666. }
  667. *field.m_pArgs = arg;
  668. pArg = field.m_pArgs;
  669. }
  670. else
  671. {
  672. pArg->m_pNext = new GATH_VALUE;
  673. if (pArg->m_pNext == NULL)
  674. {
  675. TRACE0("CTemplateFileFunctions::ReadField() pArg->m_pNext allocation failed\n");
  676. return FALSE;
  677. }
  678. *pArg->m_pNext = arg;
  679. pArg = pArg->m_pNext;
  680. }
  681. }
  682. return TRUE;
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Read an enumline(){} block. This construct is used to group lines together
  686. // which are enumerated for each instance of a class. A line is added to
  687. // the parent node's list of lines with a m_strEnumerateClass equal to the
  688. // class to be enumerated. The added line structure will have children lines
  689. // (the lines to be enumerated) referenced by m_pEnumeratedGroup.
  690. //-----------------------------------------------------------------------------
  691. GATH_LINESPEC * CTemplateFileFunctions::ReadLineEnumRecursive(CFile * pFile, CDataGatherer * pGatherer)
  692. {
  693. if (!VerifyAndAdvanceFile(pFile, CString(ENUMLINE_KEYWORD) + CString("(")))
  694. {
  695. TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on enum line keyword\n");
  696. return NULL;
  697. }
  698. // Declare a line specification variable to store the line info.
  699. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC;
  700. if (pNewLineSpec == NULL)
  701. return NULL;
  702. // Read in the enumerated class variable.
  703. if (!ReadArgument(pFile, pNewLineSpec->m_strEnumerateClass))
  704. {
  705. delete pNewLineSpec;
  706. TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() ReadArgument failed on enumerate class\n");
  707. return NULL;
  708. }
  709. // Read in the variable (zero or more) number of fields for the constraints.
  710. GATH_FIELD * pNewField = new GATH_FIELD;
  711. if (pNewField == NULL)
  712. return NULL;
  713. while (ReadField(pFile, *pNewField))
  714. {
  715. if (pNewLineSpec->m_pConstraintFields == NULL)
  716. pNewLineSpec->m_pConstraintFields = pNewField;
  717. else
  718. {
  719. // Add the newly read field to the end of the field list. Note,
  720. // this is inefficient, and should be fixed. (TBD)
  721. GATH_FIELD * pFieldScan = pNewLineSpec->m_pConstraintFields;
  722. while (pFieldScan->m_pNext)
  723. pFieldScan = pFieldScan->m_pNext;
  724. pFieldScan->m_pNext = pNewField;
  725. }
  726. pNewField = new GATH_FIELD;
  727. if (pNewField == NULL)
  728. return NULL;
  729. }
  730. delete pNewField;
  731. // Advance past the close paren and the (necessary) open bracket.
  732. if (!VerifyAndAdvanceFile(pFile, CString("){")))
  733. {
  734. TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on \"){\"\n");
  735. delete pNewLineSpec;
  736. return NULL;
  737. }
  738. // Read the contents of the block (should be all lines or enumlines).
  739. CString strKeyword;
  740. while (GetKeyword(pFile, strKeyword))
  741. {
  742. if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0)
  743. {
  744. GATH_LINESPEC * pNewSubLine = ReadLineInfo(pFile, pGatherer);
  745. if (pNewSubLine == NULL)
  746. {
  747. delete pNewLineSpec;
  748. return NULL;
  749. }
  750. if (pNewLineSpec->m_pEnumeratedGroup == NULL)
  751. pNewLineSpec->m_pEnumeratedGroup = pNewSubLine;
  752. else
  753. {
  754. GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup;
  755. while (pLineSpec->m_pNext)
  756. pLineSpec = pLineSpec->m_pNext;
  757. pLineSpec->m_pNext = pNewSubLine;
  758. }
  759. }
  760. else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0)
  761. {
  762. GATH_LINESPEC * pNewSubLine = ReadLineEnumRecursive(pFile, pGatherer);
  763. if (pNewSubLine == NULL)
  764. {
  765. delete pNewLineSpec;
  766. return NULL;
  767. }
  768. if (pNewLineSpec->m_pEnumeratedGroup == NULL)
  769. pNewLineSpec->m_pEnumeratedGroup = pNewSubLine;
  770. else
  771. {
  772. GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup;
  773. while (pLineSpec->m_pNext)
  774. pLineSpec = pLineSpec->m_pNext;
  775. pLineSpec->m_pNext = pNewSubLine;
  776. }
  777. }
  778. else
  779. {
  780. TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive(), bad keyword in enumlines block\n");
  781. delete pNewLineSpec;
  782. return NULL;
  783. }
  784. }
  785. if (!VerifyAndAdvanceFile(pFile, CString("}")))
  786. {
  787. TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on \"}\"\n");
  788. delete pNewLineSpec;
  789. return NULL;
  790. }
  791. return pNewLineSpec;
  792. }
  793. //-----------------------------------------------------------------------------
  794. // This function is used to adjust the tree of loaded categories based on
  795. // a string (which indicates what categories should be included). The
  796. // following rules are applied:
  797. //
  798. // 1. By default, no categories are included.
  799. // 2. If "+all" is in the string, all categories are included.
  800. // 3. If "+cat" is in the string, the cat, all its children and ancestors
  801. // are included.
  802. // 4. If "-cat" is in the string, the cat and all its children are excluded.
  803. //
  804. // First this function must recurse through the tree, marking each node
  805. // with whether it should be deleted or not. Then the nodes are actually
  806. // removed from the tree. Yippee skip.
  807. //-----------------------------------------------------------------------------
  808. BOOL CTemplateFileFunctions::ApplyCategories(const CString & strCategories, CDataGatherer * pGatherer)
  809. {
  810. CString strLoweredCats(strCategories);
  811. strLoweredCats.MakeLower();
  812. BOOL fDefaultAdd = (strLoweredCats.Find(CString(_T("+all"))) > -1);
  813. RecurseTreeCategories(fDefaultAdd, pGatherer->m_dwRootID, strLoweredCats, pGatherer);
  814. RemoveExtraCategories(pGatherer->m_dwRootID, pGatherer);
  815. return TRUE;
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Remove all the categories from the tree which aren't marked as "include".
  819. //-----------------------------------------------------------------------------
  820. void CTemplateFileFunctions::RemoveExtraCategories(DWORD dwID, CDataGatherer * pGatherer)
  821. {
  822. if (dwID == 0)
  823. return;
  824. INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(dwID);
  825. if (pInternal == NULL)
  826. return;
  827. // If this category is not marked as included, delete it and
  828. // all the children.
  829. if (!pInternal->m_fIncluded)
  830. {
  831. DWORD dwChildID = pInternal->m_dwChildID;
  832. DWORD dwNextChild = 0;
  833. while (dwChildID)
  834. {
  835. INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID);
  836. if (pChild)
  837. dwNextChild = pChild->m_dwNextID;
  838. else
  839. dwNextChild = 0;
  840. RemoveExtraCategories(dwChildID, pGatherer);
  841. dwChildID = dwNextChild;
  842. }
  843. pGatherer->m_mapCategories.SetAt((WORD)pInternal->m_dwID, (void *) NULL);
  844. delete pInternal;
  845. return;
  846. }
  847. // Otherwise, if we are to save this category, scan through all the
  848. // children, recursively calling this function on each one, and
  849. // constructing a new list of children which are included.
  850. INTERNAL_CATEGORY * pLastGood = NULL;
  851. DWORD dwChildID = pInternal->m_dwChildID;
  852. DWORD dwNextChild = 0;
  853. while (dwChildID)
  854. {
  855. INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID);
  856. if (pChild)
  857. {
  858. dwNextChild = pChild->m_dwNextID;
  859. if (!pChild->m_fIncluded)
  860. {
  861. // We're removing this child. If this is the first child,
  862. // set the pInternal field, otherwise, remove it from
  863. // the list of children.
  864. if (dwChildID == pInternal->m_dwChildID)
  865. pInternal->m_dwChildID = dwNextChild;
  866. else if (pLastGood) // this better be true
  867. pLastGood->m_dwNextID = dwNextChild;
  868. }
  869. else
  870. pLastGood = pChild;
  871. RemoveExtraCategories(dwChildID, pGatherer);
  872. }
  873. else
  874. dwNextChild = 0;
  875. dwChildID = dwNextChild;
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // This function recursively processes the categories to determine which
  880. // ones should be included.
  881. //-----------------------------------------------------------------------------
  882. BOOL CTemplateFileFunctions::RecurseTreeCategories(BOOL fParentOK, DWORD dwID, const CString & strCategories, CDataGatherer * pGatherer)
  883. {
  884. if (dwID == 0)
  885. return FALSE;
  886. INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(dwID);
  887. if (pInternal == NULL)
  888. return FALSE;
  889. // Default to using the same status as the parent category.
  890. pInternal->m_fIncluded = fParentOK;
  891. // If we are added or removed by the category string, change our status.
  892. CString strCategoryID(pInternal->m_strIdentifier);
  893. strCategoryID.MakeLower();
  894. int iIndex = strCategories.Find(strCategoryID);
  895. if (iIndex > 0)
  896. {
  897. // Make sure that we aren't matching part of a longer string,
  898. // by making sure this is either the last string, or a + or -
  899. // immediately follows.
  900. if ((iIndex + strCategoryID.GetLength()) >= strCategories.GetLength() ||
  901. strCategories[iIndex + strCategoryID.GetLength()] == _T('+') ||
  902. strCategories[iIndex + strCategoryID.GetLength()] == _T('-'))
  903. {
  904. if (strCategories[iIndex - 1] == _T('+'))
  905. pInternal->m_fIncluded = TRUE;
  906. else if (strCategories[iIndex - 1] == _T('-'))
  907. pInternal->m_fIncluded = FALSE;
  908. }
  909. }
  910. // Now, for each child of this node, recurse using this node's status.
  911. // If any of the children return TRUE for an included status, we must
  912. // modify this node to TRUE.
  913. DWORD dwChildID = pInternal->m_dwChildID;
  914. BOOL fChildIncluded = FALSE;
  915. while (dwChildID)
  916. {
  917. fChildIncluded |= RecurseTreeCategories(pInternal->m_fIncluded, dwChildID, strCategories, pGatherer);
  918. INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID);
  919. if (pChild)
  920. dwChildID = pChild->m_dwNextID;
  921. else
  922. dwChildID = 0;
  923. }
  924. pInternal->m_fIncluded |= fChildIncluded;
  925. return pInternal->m_fIncluded;
  926. }
  927. //-----------------------------------------------------------------------------
  928. // This function is used to load template information from DLLs (the new
  929. // method, to allow resources to be selected on the fly). The HKEY passed in
  930. // is the base key for the entries which describe the DLLs containing template
  931. // information. It's enumerated for subkeys, each of which is used to load a
  932. // DLL. A standard entry point for the DLL is used, and the template
  933. // information retrieved and passed into the file parsing functions.
  934. //-----------------------------------------------------------------------------
  935. typedef DWORD (__cdecl *pfuncGetTemplate)(void ** ppBuffer);
  936. extern "C" DWORD __cdecl GetTemplate(void ** ppBuffer);
  937. BOOL CTemplateFileFunctions::LoadTemplateDLLs(HKEY hkeyBase, CDataGatherer * pGatherer)
  938. {
  939. CStringList strlistTemplates;
  940. // Add a keyword to the list of DLLs which indicates that we should add
  941. // information from ourselves (we don't want to just add ourselves normally,
  942. // since we would do a LoadLibrary on ourselves, which opens up can of
  943. // unnecessary initializion worms). So, we'll just add "this" to the string list.
  944. strlistTemplates.AddTail(_T("this"));
  945. // Enumerate the registry key, adding each subkey to a list of DLL names to
  946. // process (the DLL path is in the default value of the subkey).
  947. if (hkeyBase)
  948. {
  949. TCHAR szName[64], szValue[MAX_PATH];
  950. DWORD dwIndex = 0;
  951. DWORD dwLength = sizeof(szName) / sizeof(TCHAR);
  952. while (ERROR_SUCCESS == RegEnumKeyEx(hkeyBase, dwIndex++, szName, &dwLength, NULL, NULL, NULL, NULL))
  953. {
  954. dwLength = sizeof(szValue) / sizeof(TCHAR);
  955. if (ERROR_SUCCESS == RegQueryValue(hkeyBase, szName, szValue, (long *)&dwLength))
  956. if (*szValue)
  957. strlistTemplates.AddTail(szValue);
  958. dwLength = sizeof(szName) / sizeof(TCHAR);
  959. }
  960. }
  961. // For each DLL in the list of templates, we'll attempt to get the template info.
  962. CString strFileName;
  963. HINSTANCE hinst;
  964. DWORD dwBufferSize;
  965. pfuncGetTemplate pfunc;
  966. unsigned char * pBuffer;
  967. CMemFile memfile;
  968. while (!strlistTemplates.IsEmpty())
  969. {
  970. strFileName = strlistTemplates.RemoveHead();
  971. // Try to load the library, and get a pointer to the entry point.
  972. if (strFileName.Compare(_T("this")) == 0)
  973. {
  974. hinst = NULL;
  975. pfunc = &GetTemplate;
  976. }
  977. else
  978. {
  979. hinst = LoadLibrary(strFileName);
  980. if (hinst == NULL)
  981. continue;
  982. pfunc = (pfuncGetTemplate) GetProcAddress(hinst, "GetTemplate");
  983. if (pfunc == NULL)
  984. {
  985. FreeLibrary(hinst);
  986. continue;
  987. }
  988. }
  989. // Call the DLL function with a NULL parameter to get the size of the buffer.
  990. dwBufferSize = (*pfunc)((void **)&pBuffer);
  991. if (dwBufferSize && pBuffer)
  992. {
  993. memfile.Attach((BYTE *)pBuffer, dwBufferSize, 0);
  994. CTemplateFileFunctions::ReadTemplateFile(&memfile, pGatherer);
  995. memfile.Detach();
  996. (void)(*pfunc)(NULL); // calling the exported DLL function with NULL frees its buffers
  997. }
  998. if (hinst != NULL)
  999. {
  1000. FreeLibrary(hinst);
  1001. hinst = NULL;
  1002. }
  1003. }
  1004. return TRUE;
  1005. }