|
|
//=============================================================================
// File: tmplfile.cpp
// Author: a-jammar
// Covers: CTemplateFileFunctions
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// This file contains the functions necessary to read in the MSInfo template
// file. The template file is a text file with an NFT extension. Reading
// multiple template files is allowed - the contents are merged together into
// the category tree maintained by the snap-in.
//=============================================================================
#include "stdafx.h"
#include "gather.h"
#include "gathint.h"
//-----------------------------------------------------------------------------
// This function is the main entry point for reading the template file into
// the category structure used by the CDataGatherer object. If there is no
// tree when this function is called, the tree is created from the contents
// of the file. If there is a tree already in place, then the contents of the
// file are loaded under the existing root node.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ReadTemplateFile(CFile *pFile, CDataGatherer *pGatherer) { ASSERT(pFile && pGatherer);
if (!VerifyUNICODEFile(pFile)) { TRACE0("-- CTemplateFileFunctions::ReadTemplateFile() passed non-UNICODE file\n"); return FALSE; }
if (!ReadHeaderInfo(pFile, pGatherer)) { TRACE0("-- CTemplateFileFunctions::ReadTemplateFile() failed from ReadHeaderInfo\n"); return FALSE; }
if (pGatherer->m_dwRootID) { // There is already a tree present. Insert the contents of the file under
// the root node, after the last first level node. Walk through the first
// level of the internal category tree. TBD: add a way to extend a specified node
INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(pGatherer->m_dwRootID); ASSERT(pInternal);
if (pInternal) { pInternal = pGatherer->GetInternalRep(pInternal->m_dwChildID); while (pInternal && pInternal->m_dwNextID) pInternal = pGatherer->GetInternalRep(pInternal->m_dwNextID); }
DWORD dwPrevID = (pInternal) ? pInternal->m_dwID : 0; if (ReadNodeRecursive(pFile, pGatherer, pGatherer->m_dwRootID, dwPrevID) == 0) return FALSE; return TRUE; } else { pGatherer->m_dwRootID = ReadNodeRecursive(pFile, pGatherer, 0, 0); if (pGatherer->m_dwRootID == 0) return FALSE; return TRUE; } }
//-----------------------------------------------------------------------------
// This method reads the header information from the file before the recursive
// category descriptions. Note: since this is the first and only version of
// the template file, we take the easy way out and just make sure that the
// identifier and version are there.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ReadHeaderInfo(CFile *pFile, CDataGatherer * /* pGatherer */) { return VerifyAndAdvanceFile(pFile, CString(_T(TEMPLATE_FILE_TAG))); }
//-----------------------------------------------------------------------------
// This method verifies that the passed file is a UNICODE file, by reading the
// value 0xFEFF from the file. It also leaves the file pointer past this word.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::VerifyUNICODEFile(CFile *pFile) { WORD verify;
if (pFile->Read((void *) &verify, sizeof(WORD)) != sizeof(WORD)) { TRACE0("-- CTemplateFileFunctions::VerifyUNICODEFile() couldn't read WORD\n"); return FALSE; }
return (verify == 0xFEFF); }
//-----------------------------------------------------------------------------
// This is the recursive function to read a node. It reads the information
// from the node parameters, creates the node, and processes the contents of
// the block following the node (contained within "{}"'s). It's called
// recursively if there are any nodes in that block.
//-----------------------------------------------------------------------------
DWORD CTemplateFileFunctions::ReadNodeRecursive(CFile *pFile, CDataGatherer *pGatherer, DWORD dwParentID, DWORD dwPrevID) { // Determine if we need to create a new category for this node. Search through the
// other sibling nodes to see if there is one which matches this category's name.
// If there is, just use that category, and don't create a new one. Read the
// information from the file to determine the identifier for the new category.
CString strEnumerateClass, strIdentifier;
if (!VerifyAndAdvanceFile(pFile, CString(NODE_KEYWORD) + CString("("))) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on node keyword\n"); return 0; }
if (!ReadArgument(pFile, strEnumerateClass)) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on enumerate class\n"); return 0; }
if (!ReadArgument(pFile, strIdentifier)) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on identifier\n"); return 0; }
// Look for a node among the siblings which has a matching strIdentifier.
INTERNAL_CATEGORY * pInternal; DWORD dwSearchID = dwPrevID, dwMatchingID = 0;
while (dwMatchingID == 0 && dwSearchID != 0) { pInternal = pGatherer->GetInternalRep(dwSearchID);
if (pInternal) { if (pInternal->m_strIdentifier.CompareNoCase(strIdentifier) == 0) dwMatchingID = dwSearchID; dwSearchID = pInternal->m_dwPrevID; } else { ASSERT(pInternal); break; } }
INTERNAL_CATEGORY * pCategory = ((dwMatchingID) ? pInternal : NULL); DWORD dwID = dwMatchingID;
if (pCategory == NULL) { // Create the category for the node.
dwID = CreateCategory(pGatherer, dwParentID, dwPrevID); pCategory = pGatherer->GetInternalRep(dwID); if (!pCategory) return 0;
// Read the contents of the node argument list ("node(enum, identifier, field(source, formatstr, arg...))")
// We've already read up to and including the identifier.
pCategory->m_strEnumerateClass = strEnumerateClass; pCategory->m_strIdentifier = strIdentifier;
if (!ReadField(pFile, pCategory->m_fieldName)) return 0;
// Copy the field name to the name of the category (they are two different
// member variables to allow for dynamically refreshed names, which turns
// out to be unnecessary in this version).
pCategory->m_categoryName.m_strText = pCategory->m_fieldName.m_strFormat;
if (!ReadArgument(pFile, pCategory->m_strNoInstances)) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on no instance message\n"); return 0; } } else { // This node already existed, and we just want to read past the rest of
// it's description without changing the existing node.
GATH_FIELD fieldTemp; CString strTemp;
if (!ReadField(pFile, fieldTemp)) return 0;
if (!ReadArgument(pFile, strTemp)) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() ReadArgument failed on no instance message\n"); return 0; } }
if (!VerifyAndAdvanceFile(pFile, CString("){"))) { TRACE1("-- CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on node \"){\" (%s)\n", pCategory->m_strIdentifier); return 0; }
// Process the contents of the block (enclosed in "{}") for this node.
DWORD dwSubNodePrev = 0, dwNewNode = 0; CString strKeyword;
// If this new category isn't actually new (i.e. it is being read from a
// template and overlaps an existing category) see if there are any
// existing children.
if (pCategory->m_dwChildID) { pInternal = pGatherer->GetInternalRep(pCategory->m_dwChildID); ASSERT(pInternal); while (pInternal && pInternal->m_dwNextID) pInternal = pGatherer->GetInternalRep(pInternal->m_dwNextID);
if (pInternal) dwSubNodePrev = pInternal->m_dwID; }
while (GetKeyword(pFile, strKeyword)) { if (strKeyword.CompareNoCase(CString(NODE_KEYWORD)) == 0) { dwNewNode = ReadNodeRecursive(pFile, pGatherer, dwID, dwSubNodePrev); if (dwNewNode == 0) return 0;
// If this is the first child node we've read, save its ID.
if (pCategory->m_dwChildID == 0) pCategory->m_dwChildID = dwNewNode;
// If we've read another child node, set its next field appropriately.
if (dwSubNodePrev) { INTERNAL_CATEGORY * pPrevCategory = pGatherer->GetInternalRep(dwSubNodePrev); if (pPrevCategory) pPrevCategory->m_dwNextID = dwNewNode; } dwSubNodePrev = dwNewNode; } else if (strKeyword.CompareNoCase(CString(COLUMN_KEYWORD)) == 0) { if (!ReadColumnInfo(pFile, pGatherer, dwID)) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadColumnInfo\n"); return FALSE; } } else if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewLineSpec = ReadLineInfo(pFile, pGatherer); if (pNewLineSpec == NULL) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineInfo\n"); return FALSE; }
// Add the line we just read in to the end of the list of line specs for this
// internal category.
if (pCategory->m_pLineSpec == NULL) pCategory->m_pLineSpec = pNewLineSpec; else { GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewLineSpec; } } else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewLineSpec = ReadLineEnumRecursive(pFile, pGatherer); if (pNewLineSpec == NULL) { TRACE0("-- CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineEnumRecursive\n"); return FALSE; }
// Add the line we just read in to the end of the list of line specs for this
// internal category.
if (pCategory->m_pLineSpec == NULL) pCategory->m_pLineSpec = pNewLineSpec; else { GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewLineSpec; }
//if (!ReadLineEnumRecursive(pFile, pGatherer, dwID))
//{
// TRACE0("CTemplateFileFunctions::ReadNodeRecursive() failed on ReadLineEnumRecursive\n");
// return FALSE;
//}
} else { ASSERT(FALSE); VerifyAndAdvanceFile(pFile, strKeyword); } }
if (!VerifyAndAdvanceFile(pFile, CString("}"))) { TRACE0("CTemplateFileFunctions::ReadNodeRecursive() Verify.. failed on \"}\"\n"); return 0; }
return dwID; }
//-----------------------------------------------------------------------------
// This method verifies that the text in strVerify comes next in the file (not
// including case or whitespace differences) and advances the file past that
// text. If the text was the next content in the file, TRUE is returned,
// otherwise FALSE. If FALSE is returned, the file is backed up to where it
// was when this method was called.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::VerifyAndAdvanceFile(CFile * pFile, const CString &strVerify) { DWORD dwPosition = pFile->GetPosition(); TCHAR cLastChar, cCurrentChar = _T('\0'); BOOL fInComment = FALSE; int iCharIndex = 0, iStringLen = strVerify.GetLength();
while (iCharIndex < iStringLen) { // Save the last character read, since the comment token ("//") is
// two characters long.
cLastChar = cCurrentChar;
// Read the next character in the file.
if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR)) { TRACE0("-- CTemplateFileFunctions::VerifyAndAdvanceFile() couldn't read character\n"); return FALSE; }
// If we're in a comment, and the character we just read isn't a new line,
// we want to ignore it.
if (fInComment) { if (cCurrentChar == _T('\n')) fInComment = FALSE; continue; }
// Check to see if we've started into a comment. Note that we ignore
// the first '/' also by continuing.
if (cCurrentChar == _T('/')) { if (cLastChar == _T('/')) fInComment = TRUE; continue; } // Skip whitespace, and also leading commas.
if (_istspace(cCurrentChar) || (cCurrentChar == _T(',') && iCharIndex == 0)) continue;
if (cCurrentChar != strVerify[iCharIndex]) { pFile->Seek((LONG)dwPosition, CFile::begin); return FALSE; }
iCharIndex++; }
return TRUE; }
//-----------------------------------------------------------------------------
// Create a new category, and return the ID for the category.
//-----------------------------------------------------------------------------
DWORD CTemplateFileFunctions::CreateCategory(CDataGatherer * pGatherer, DWORD dwParentID, DWORD dwPrevID) { DWORD dwID = pGatherer->m_dwNextFreeID; INTERNAL_CATEGORY * pInternalCat; INTERNAL_CATEGORY * pPreviousCat; CString strName;
pInternalCat = new INTERNAL_CATEGORY; if (!pInternalCat) return 0;
pInternalCat->m_fListView = TRUE; pInternalCat->m_dwID = dwID; pInternalCat->m_dwParentID = dwParentID; pInternalCat->m_dwPrevID = dwPrevID;
if (dwPrevID) { pPreviousCat = pGatherer->GetInternalRep(dwPrevID); if (pPreviousCat) pPreviousCat->m_dwNextID = dwID; }
pGatherer->m_mapCategories.SetAt((WORD)dwID, (void *) pInternalCat); pGatherer->m_dwNextFreeID++;
return dwID; }
//-----------------------------------------------------------------------------
// This method returns the next keyword in the file. Any whitespace or
// punctuation is skipped until an alphanumeric character is read. The keyword
// returned is the string starting with this character until whitespace or
// punctuation is encountered. Note: it is very important that this function
// returns the file to the state it was in when the function started, with
// the current position restored.
//
// TBD: inefficient
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::GetKeyword(CFile * pFile, CString & strKeyword) { CString strTemp = CString(""); DWORD dwPosition = pFile->GetPosition(); TCHAR cLastChar, cCurrentChar = _T('\0'); BOOL fInComment = FALSE;
// Skip over whitespace characters until we reach an alphanumeric char.
do { // Save the last character read, since the comment token ("//") is
// two characters long.
cLastChar = cCurrentChar;
// Read the next character in the file.
if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR)) return FALSE;
// If we're in a comment, and the character we just read isn't a new line,
// we want to ignore it.
if (fInComment) { if (cCurrentChar == _T('\n')) fInComment = FALSE; continue; }
// Check to see if we've started into a comment.
if (cCurrentChar == _T('/')) { if (cLastChar == _T('/')) fInComment = TRUE; continue; } } while (_istspace(cCurrentChar) || cCurrentChar == _T('/') || fInComment); // Read the keyword while it's alphanumeric.
if (_istalnum(cCurrentChar)) do { strTemp += CString(cCurrentChar);
if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR)) return FALSE; } while (_istalnum(cCurrentChar));
// Reset the file, set the keyword and return.
pFile->Seek((LONG)dwPosition, CFile::begin); strKeyword = strTemp; return !strTemp.IsEmpty(); }
//-----------------------------------------------------------------------------
// This method reads in a "column" line from the file, adding the appropriate
// entries for the columns into the category referenced by dwID. The column
// line contains a bunch of fields in a list.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ReadColumnInfo(CFile * pFile, CDataGatherer * pGatherer, DWORD dwID) { CString strTemp;
if (!VerifyAndAdvanceFile(pFile, CString(COLUMN_KEYWORD) + CString("("))) { TRACE0("CTemplateFileFunctions::ReadColumnInfo() Verify.. failed on column keyword\n"); return FALSE; }
// Get the internal category referenced by dwID.
INTERNAL_CATEGORY * pCategory = pGatherer->GetInternalRep(dwID); if (!pCategory) return FALSE;
// We only allow one column specifier list per node.
if (pCategory->m_pColSpec) { TRACE0("CTemplateFileFunctions::ReadColumnInfo() already column information present\n"); return FALSE; }
// While we are still reading fields from the file, keep adding to the column list.
GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) return FALSE;
while (ReadField(pFile, *pNewField)) { if (pCategory->m_pColSpec == NULL) pCategory->m_pColSpec = pNewField; else { // Scan to the last field in the linespec.m_pFields list, and insert the new field.
GATH_FIELD * pFieldScan = pCategory->m_pColSpec; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; }
// Parse the width out of the column caption.
if (pNewField->m_strFormat.ReverseFind(_T(',')) != -1) { strTemp = pNewField->m_strFormat.Right(pNewField->m_strFormat.GetLength() - pNewField->m_strFormat.ReverseFind(_T(',')) - 1); pNewField->m_usWidth = (unsigned short) atoi(strTemp); pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.GetLength() - strTemp.GetLength() - 1); } else { ASSERT(FALSE); pNewField->m_usWidth = (unsigned short) 80; } // Parse off any remaining information in the column label (the label ends
// with [name, n], when n is the width, and name is the ID for the column
// which should not be displayed).
if (pNewField->m_strFormat.ReverseFind(_T('[')) != -1) pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.ReverseFind(_T('[')) - 1);
// Read the sorting type from the file.
if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(SORT_LEXICAL))) == 0) pNewField->m_sort = LEXICAL; else if (strTemp.CompareNoCase(CString(_T(SORT_VALUE))) == 0) pNewField->m_sort = BYVALUE; else pNewField->m_sort = NOSORT; } else { TRACE0("CTemplateFileFunctions::ReadColumnInfo() couldn't read column sorting\n"); return FALSE; }
// Read the complexity (BASIC or ADVANCED) from the file.
if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0) pNewField->m_datacomplexity = ADVANCED; else pNewField->m_datacomplexity = BASIC; } else { TRACE0("CTemplateFileFunctions::ReadColumnInfo() couldn't read data complexity\n"); return FALSE; }
pNewField = new GATH_FIELD; if (pNewField == NULL) return FALSE; }
delete pNewField;
if (!VerifyAndAdvanceFile(pFile, CString(")"))) { TRACE0("CTemplateFileFunctions::ReadColumnInfo() Verify.. failed on \")\"\n"); return FALSE; }
return TRUE; }
//-----------------------------------------------------------------------------
// Read in the information for a single line. Add the line to the internal
// representation of the category. TBD: inefficient, since this will be
// called multiple times and the line list will need to be scanned to the
// end each time.
//-----------------------------------------------------------------------------
GATH_LINESPEC * CTemplateFileFunctions::ReadLineInfo(CFile * pFile, CDataGatherer * /* pGatherer */) {
if (!VerifyAndAdvanceFile(pFile, CString(LINE_KEYWORD) + CString("("))) { TRACE0("CTemplateFileFunctions::ReadLineInfo() Verify.. failed on line keyword\n"); return NULL; }
// Declare a line specification variable to store the line info.
GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC; if (pNewLineSpec == NULL) return NULL;
// While we are still reading fields from the file, keep adding to the column list.
// TBD: inefficient, repeated scans through linespec.m_pFields list.
GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) { delete pNewLineSpec; return NULL; }
// Read in the complexity (BASIC or ADVANCED) for this line.
CString strTemp; if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0) pNewLineSpec->m_datacomplexity = ADVANCED; else pNewLineSpec->m_datacomplexity = BASIC; } else { TRACE0("CTemplateFileFunctions::ReadLineInfo() couldn't read complexity\n"); return FALSE; }
while (ReadField(pFile, *pNewField)) { if (pNewLineSpec->m_pFields == NULL) pNewLineSpec->m_pFields = pNewField; else { // Scan to the last field in the linespec.m_pFields list, and insert the new field.
GATH_FIELD * pFieldScan = pNewLineSpec->m_pFields; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; }
pNewField = new GATH_FIELD; if (pNewField == NULL) { delete pNewLineSpec; return NULL; } }
delete pNewField;
if (!VerifyAndAdvanceFile(pFile, CString(")"))) { TRACE0("CTemplateFileFunctions::ReadLineInfo() Verify.. failed on \")\"\n"); delete pNewLineSpec; return NULL; }
return pNewLineSpec; }
//-----------------------------------------------------------------------------
// This method simply reads an argument (as string) from the file, until a
// punctuation or whitespace character is found. If a quote mark is found,
// all characters are included in the string until another quote is found.
// TBD: currently no way to have a quote mark in the string.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ReadArgument(CFile * pFile, CString & strSource) { BOOL fInQuote = FALSE, fInComment = FALSE; CString strTemp; TCHAR cLastChar, cCurrentChar = _T('\0');
// Skip over characters until we reach an alphanumeric char. If we find
// a close paren, then we've reached the end of the argument list and
// should return FALSE.
do { // Save the last character read, since the comment token ("//") is
// two characters long.
cLastChar = cCurrentChar;
// Read the next character in the file.
if (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) != sizeof(TCHAR)) { TRACE0("CTemplateFileFunctions::ReadArgument() couldn't read character\n"); return FALSE; }
// If we're in a comment, and the character we just read isn't a new line,
// we want to ignore it.
if (fInComment) { if (cCurrentChar == _T('\n')) fInComment = FALSE; continue; }
// Check to see if we've started into a comment.
if (cCurrentChar == _T('/')) { if (cLastChar == _T('/')) fInComment = TRUE; continue; }
if (cCurrentChar == _T(')')) return FALSE; } while (!_istalnum(cCurrentChar) && cCurrentChar != _T('"'));
// Read characters into the string until we find whitespace or punctuation.
do { if (cCurrentChar == _T('"')) { fInQuote = !fInQuote; continue; }
if (_istalnum(cCurrentChar) || fInQuote) strTemp += CString(cCurrentChar); else break; } while (pFile->Read((void *) &cCurrentChar, sizeof(TCHAR)) == sizeof(TCHAR));
// If the last character read (the one which terminated this argument) was
// not a comma, then back the file up so that the character can be re-read
// and interpreted.
if (cCurrentChar != _T(',')) pFile->Seek(-(LONG)sizeof(TCHAR), CFile::current);
strSource = strTemp; return TRUE; }
//-----------------------------------------------------------------------------
// A field consists of a source string, followed by a format string, followed
// by a list of zero or more arguments.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ReadField(CFile * pFile, GATH_FIELD & field) { // Advance past the field keyword and read the two source and format strings.
if (!VerifyAndAdvanceFile(pFile, CString(FIELD_KEYWORD) + CString("("))) return FALSE;
if (!ReadArgument(pFile, field.m_strSource)) { TRACE0("CTemplateFileFunctions::ReadField() ReadArgument failed on source\n"); return FALSE; }
if (!ReadArgument(pFile, field.m_strFormat)) { TRACE0("CTemplateFileFunctions::ReadField() ReadArgument failed on format\n"); return FALSE; }
// Read arguments until there are no more, building them into a list of
// arguments stored by the FIELD struct.
GATH_VALUE arg; GATH_VALUE * pArg = NULL;
while (ReadArgument(pFile, arg.m_strText)) { if (pArg == NULL) { field.m_pArgs = new GATH_VALUE; if (field.m_pArgs == NULL) { TRACE0("CTemplateFileFunctions::ReadField() field.m_pArgs allocation failed\n"); return FALSE; } *field.m_pArgs = arg; pArg = field.m_pArgs; } else { pArg->m_pNext = new GATH_VALUE; if (pArg->m_pNext == NULL) { TRACE0("CTemplateFileFunctions::ReadField() pArg->m_pNext allocation failed\n"); return FALSE; } *pArg->m_pNext = arg; pArg = pArg->m_pNext; } }
return TRUE; }
//-----------------------------------------------------------------------------
// Read an enumline(){} block. This construct is used to group lines together
// which are enumerated for each instance of a class. A line is added to
// the parent node's list of lines with a m_strEnumerateClass equal to the
// class to be enumerated. The added line structure will have children lines
// (the lines to be enumerated) referenced by m_pEnumeratedGroup.
//-----------------------------------------------------------------------------
GATH_LINESPEC * CTemplateFileFunctions::ReadLineEnumRecursive(CFile * pFile, CDataGatherer * pGatherer) { if (!VerifyAndAdvanceFile(pFile, CString(ENUMLINE_KEYWORD) + CString("("))) { TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on enum line keyword\n"); return NULL; }
// Declare a line specification variable to store the line info.
GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC; if (pNewLineSpec == NULL) return NULL;
// Read in the enumerated class variable.
if (!ReadArgument(pFile, pNewLineSpec->m_strEnumerateClass)) { delete pNewLineSpec; TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() ReadArgument failed on enumerate class\n"); return NULL; }
// Read in the variable (zero or more) number of fields for the constraints.
GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) return NULL;
while (ReadField(pFile, *pNewField)) { if (pNewLineSpec->m_pConstraintFields == NULL) pNewLineSpec->m_pConstraintFields = pNewField; else { // Add the newly read field to the end of the field list. Note,
// this is inefficient, and should be fixed. (TBD)
GATH_FIELD * pFieldScan = pNewLineSpec->m_pConstraintFields; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; }
pNewField = new GATH_FIELD; if (pNewField == NULL) return NULL; }
delete pNewField;
// Advance past the close paren and the (necessary) open bracket.
if (!VerifyAndAdvanceFile(pFile, CString("){"))) { TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on \"){\"\n"); delete pNewLineSpec; return NULL; }
// Read the contents of the block (should be all lines or enumlines).
CString strKeyword; while (GetKeyword(pFile, strKeyword)) { if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewSubLine = ReadLineInfo(pFile, pGatherer); if (pNewSubLine == NULL) { delete pNewLineSpec; return NULL; }
if (pNewLineSpec->m_pEnumeratedGroup == NULL) pNewLineSpec->m_pEnumeratedGroup = pNewSubLine; else { GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewSubLine; } } else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewSubLine = ReadLineEnumRecursive(pFile, pGatherer); if (pNewSubLine == NULL) { delete pNewLineSpec; return NULL; }
if (pNewLineSpec->m_pEnumeratedGroup == NULL) pNewLineSpec->m_pEnumeratedGroup = pNewSubLine; else { GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewSubLine; } } else { TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive(), bad keyword in enumlines block\n"); delete pNewLineSpec; return NULL; } }
if (!VerifyAndAdvanceFile(pFile, CString("}"))) { TRACE0("CTemplateFileFunctions::ReadLineEnumRecursive() Verify.. failed on \"}\"\n"); delete pNewLineSpec; return NULL; }
return pNewLineSpec; }
//-----------------------------------------------------------------------------
// This function is used to adjust the tree of loaded categories based on
// a string (which indicates what categories should be included). The
// following rules are applied:
//
// 1. By default, no categories are included.
// 2. If "+all" is in the string, all categories are included.
// 3. If "+cat" is in the string, the cat, all its children and ancestors
// are included.
// 4. If "-cat" is in the string, the cat and all its children are excluded.
//
// First this function must recurse through the tree, marking each node
// with whether it should be deleted or not. Then the nodes are actually
// removed from the tree. Yippee skip.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::ApplyCategories(const CString & strCategories, CDataGatherer * pGatherer) { CString strLoweredCats(strCategories); strLoweredCats.MakeLower(); BOOL fDefaultAdd = (strLoweredCats.Find(CString(_T("+all"))) > -1); RecurseTreeCategories(fDefaultAdd, pGatherer->m_dwRootID, strLoweredCats, pGatherer); RemoveExtraCategories(pGatherer->m_dwRootID, pGatherer);
return TRUE; }
//-----------------------------------------------------------------------------
// Remove all the categories from the tree which aren't marked as "include".
//-----------------------------------------------------------------------------
void CTemplateFileFunctions::RemoveExtraCategories(DWORD dwID, CDataGatherer * pGatherer) { if (dwID == 0) return;
INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(dwID); if (pInternal == NULL) return;
// If this category is not marked as included, delete it and
// all the children.
if (!pInternal->m_fIncluded) { DWORD dwChildID = pInternal->m_dwChildID; DWORD dwNextChild = 0; while (dwChildID) { INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID); if (pChild) dwNextChild = pChild->m_dwNextID; else dwNextChild = 0; RemoveExtraCategories(dwChildID, pGatherer); dwChildID = dwNextChild; } pGatherer->m_mapCategories.SetAt((WORD)pInternal->m_dwID, (void *) NULL); delete pInternal;
return; }
// Otherwise, if we are to save this category, scan through all the
// children, recursively calling this function on each one, and
// constructing a new list of children which are included.
INTERNAL_CATEGORY * pLastGood = NULL; DWORD dwChildID = pInternal->m_dwChildID; DWORD dwNextChild = 0; while (dwChildID) { INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID); if (pChild) { dwNextChild = pChild->m_dwNextID; if (!pChild->m_fIncluded) { // We're removing this child. If this is the first child,
// set the pInternal field, otherwise, remove it from
// the list of children.
if (dwChildID == pInternal->m_dwChildID) pInternal->m_dwChildID = dwNextChild; else if (pLastGood) // this better be true
pLastGood->m_dwNextID = dwNextChild; } else pLastGood = pChild; RemoveExtraCategories(dwChildID, pGatherer); } else dwNextChild = 0; dwChildID = dwNextChild; } }
//-----------------------------------------------------------------------------
// This function recursively processes the categories to determine which
// ones should be included.
//-----------------------------------------------------------------------------
BOOL CTemplateFileFunctions::RecurseTreeCategories(BOOL fParentOK, DWORD dwID, const CString & strCategories, CDataGatherer * pGatherer) { if (dwID == 0) return FALSE;
INTERNAL_CATEGORY *pInternal = pGatherer->GetInternalRep(dwID); if (pInternal == NULL) return FALSE;
// Default to using the same status as the parent category.
pInternal->m_fIncluded = fParentOK;
// If we are added or removed by the category string, change our status.
CString strCategoryID(pInternal->m_strIdentifier); strCategoryID.MakeLower(); int iIndex = strCategories.Find(strCategoryID); if (iIndex > 0) { // Make sure that we aren't matching part of a longer string,
// by making sure this is either the last string, or a + or -
// immediately follows.
if ((iIndex + strCategoryID.GetLength()) >= strCategories.GetLength() || strCategories[iIndex + strCategoryID.GetLength()] == _T('+') || strCategories[iIndex + strCategoryID.GetLength()] == _T('-')) { if (strCategories[iIndex - 1] == _T('+')) pInternal->m_fIncluded = TRUE; else if (strCategories[iIndex - 1] == _T('-')) pInternal->m_fIncluded = FALSE; } }
// Now, for each child of this node, recurse using this node's status.
// If any of the children return TRUE for an included status, we must
// modify this node to TRUE.
DWORD dwChildID = pInternal->m_dwChildID; BOOL fChildIncluded = FALSE; while (dwChildID) { fChildIncluded |= RecurseTreeCategories(pInternal->m_fIncluded, dwChildID, strCategories, pGatherer); INTERNAL_CATEGORY *pChild = pGatherer->GetInternalRep(dwChildID); if (pChild) dwChildID = pChild->m_dwNextID; else dwChildID = 0; }
pInternal->m_fIncluded |= fChildIncluded; return pInternal->m_fIncluded; }
//-----------------------------------------------------------------------------
// This function is used to load template information from DLLs (the new
// method, to allow resources to be selected on the fly). The HKEY passed in
// is the base key for the entries which describe the DLLs containing template
// information. It's enumerated for subkeys, each of which is used to load a
// DLL. A standard entry point for the DLL is used, and the template
// information retrieved and passed into the file parsing functions.
//-----------------------------------------------------------------------------
typedef DWORD (__cdecl *pfuncGetTemplate)(void ** ppBuffer); extern "C" DWORD __cdecl GetTemplate(void ** ppBuffer);
BOOL CTemplateFileFunctions::LoadTemplateDLLs(HKEY hkeyBase, CDataGatherer * pGatherer) { CStringList strlistTemplates;
// Add a keyword to the list of DLLs which indicates that we should add
// information from ourselves (we don't want to just add ourselves normally,
// since we would do a LoadLibrary on ourselves, which opens up can of
// unnecessary initializion worms). So, we'll just add "this" to the string list.
strlistTemplates.AddTail(_T("this"));
// Enumerate the registry key, adding each subkey to a list of DLL names to
// process (the DLL path is in the default value of the subkey).
if (hkeyBase) { TCHAR szName[64], szValue[MAX_PATH]; DWORD dwIndex = 0; DWORD dwLength = sizeof(szName) / sizeof(TCHAR); while (ERROR_SUCCESS == RegEnumKeyEx(hkeyBase, dwIndex++, szName, &dwLength, NULL, NULL, NULL, NULL)) { dwLength = sizeof(szValue) / sizeof(TCHAR); if (ERROR_SUCCESS == RegQueryValue(hkeyBase, szName, szValue, (long *)&dwLength)) if (*szValue) strlistTemplates.AddTail(szValue); dwLength = sizeof(szName) / sizeof(TCHAR); } }
// For each DLL in the list of templates, we'll attempt to get the template info.
CString strFileName; HINSTANCE hinst; DWORD dwBufferSize; pfuncGetTemplate pfunc; unsigned char * pBuffer; CMemFile memfile;
while (!strlistTemplates.IsEmpty()) { strFileName = strlistTemplates.RemoveHead();
// Try to load the library, and get a pointer to the entry point.
if (strFileName.Compare(_T("this")) == 0) { hinst = NULL; pfunc = &GetTemplate; } else { hinst = LoadLibrary(strFileName); if (hinst == NULL) continue;
pfunc = (pfuncGetTemplate) GetProcAddress(hinst, "GetTemplate"); if (pfunc == NULL) { FreeLibrary(hinst); continue; } }
// Call the DLL function with a NULL parameter to get the size of the buffer.
dwBufferSize = (*pfunc)((void **)&pBuffer); if (dwBufferSize && pBuffer) { memfile.Attach((BYTE *)pBuffer, dwBufferSize, 0); CTemplateFileFunctions::ReadTemplateFile(&memfile, pGatherer); memfile.Detach(); (void)(*pfunc)(NULL); // calling the exported DLL function with NULL frees its buffers
}
if (hinst != NULL) { FreeLibrary(hinst); hinst = NULL; } }
return TRUE; }
|