|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
TstINIConfig.cxx
Abstract:
Class that manages the reading of the test scenario INI file.
Author:
Stefan R. Steiner [ssteiner] 05-16-2000
Revision History:
--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "vststtools.hxx"
#include "vs_hash.hxx"
#include "tstiniconfig.hxx"
#include "tstiniconfigpriv.hxx" // has layouts of all the possible INI sections
static VOID pVsTstWrapOutput( IN FILE *pfOut, IN LPCWSTR pwszBeginString, IN CBsString& cwsToBeWrapped, IN SIZE_T cWrapWidth );
static LPCWSTR x_wszDefaultINIPath = L"%SystemRoot%\\VsTestHarness.ini";
struct SVsTstSection { LPWSTR m_pwszSectionTypeName; SVsTstINISectionDef *m_psSectionDef; }; //
// This array must match the EVsTstINISectionType enum in tstiniconfigpriv.h.
// These strings are the strings found in the INI file in the section headers. E.g.
// for section name [VssTestHarness.XXXX]
// the XXXX is the section qualifier for the section type VssTestHarness.
// See tstiniconfigpriv.hxx for definitions of sVsTstINISectionDefXXXX variables.
//
static SVsTstSection x_sSectionDefArr[] = { { L"INVALID", NULL }, { L"VssTestController", sVsTstINISectionDefController }, { L"VssTestRequestor", sVsTstINISectionDefRequester }, { L"VssTestWriter", sVsTstINISectionDefWriter }, { L"VssTestProvider", sVsTstINISectionDefProvider } };
static LPCWSTR x_wszDefaultSectionName = L"DEFAULT";
//
// Range delimiter
//
static LPCWSTR x_wszRangeString = L"...";
//
// The following constants define the boolean values when writing to the
// ini files.
//
static LPWSTR const x_pwszBooleanValueNames[] = { L"No", // eVsTstBool_False
L"Yes", // eVsTstBool_True
L"Random" // eVsTstBool_Random
};
//
// The valid true values that can be specified as a boolean value.
//
static LPWSTR const x_pwszValidBooleanTrueValues[] = { L"YES", L"TRUE", L"1", L"JA", L"SI", L"OUI", NULL };
//
// The valid false values that can be specified as a boolean value.
//
static LPWSTR const x_pwszValidBooleanFalseValues[] = { L"NO", L"FALSE", L"0", L"NEIN", L"NON", NULL };
//
// The valid strings to specify random values.
//
static LPWSTR const x_pwszValidBooleanRandomValues[] = { L"RANDOM", L"-1", NULL };
//
// Function that finds a match within a string array
//
static BOOL IsInArray( IN const CBsString& cwsString, IN LPWSTR const *ppwszStringMatchArray ) { VSTST_ASSERT( ppwszStringMatchArray != NULL ); while ( *ppwszStringMatchArray != NULL ) { if ( cwsString == *ppwszStringMatchArray ) return TRUE; ++ppwszStringMatchArray; }
return FALSE; }
//
// Parent class the all in memory options subclass from
//
class CVsTstSectionOptionBase { public: CVsTstSectionOptionBase( IN EVsTstINIOptionType eOptionType ) : m_eOptionType( eOptionType ), m_bDefaultOverridden( FALSE ) { }; virtual ~CVsTstSectionOptionBase() {};
EVsTstINIOptionType GetOptionType() { return m_eOptionType; }
//
// The value after the "KeyName=" in the INI file
//
virtual VOID SetValueFromINIValue( IN CBsString cwsINIValue ) = 0;
//
// Returns true if the default value was overridden by the INI
// file or a call to a SetValueXXXX method in derived classes.
//
BOOL IsDefaultOverridden() { return m_bDefaultOverridden; } protected: VOID SetDefaultOverriden() { m_bDefaultOverridden = TRUE; } private: EVsTstINIOptionType m_eOptionType; BOOL m_bDefaultOverridden; // TRUE if default value was overridden
};
//
// In-memory boolean option. This maintains the state of
// one option. It takes care of using the option definition in
// tstiniconfigpriv.hxx to initialize the option with its default value
// and if the option value changes, makes sure it matches what
// is allowed by the definition.
//
class CVsTstSectionOptionBoolean : public CVsTstSectionOptionBase { public: CVsTstSectionOptionBoolean( IN SVsTstINIBooleanDef& rsBoolDef ) : CVsTstSectionOptionBase( eVsTstOptType_Boolean ) { // Set up option definition
m_psBoolDef = &rsBoolDef;
// Set up default values
m_eBoolValue = m_psBoolDef->m_eBoolDefault; }; virtual ~CVsTstSectionOptionBoolean() {};
virtual VOID SetValueFromINIValue( IN CBsString cwsINIValue ) { cwsINIValue.TrimLeft(); cwsINIValue.TrimRight(); cwsINIValue.MakeUpper(); if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanTrueValues ) ) SetValue( eVsTstBool_True ); else if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanFalseValues ) ) SetValue( eVsTstBool_False ); else if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanRandomValues ) ) SetValue( eVsTstBool_Random ); else { CBsString cwsThrow; VSTST_THROW( cwsThrow.Format( L"Invalid boolean value '%s'", cwsINIValue.c_str() ) ); } }
VOID SetValue( IN EVsTstINIBoolType eBoolValue ) { CBsString cwsThrow; VSTST_ASSERT( eBoolValue == eVsTstBool_False || eBoolValue == eVsTstBool_True || eBoolValue == eVsTstBool_Random ); if ( GetOptionType() != eVsTstOptType_Boolean ) VSTST_THROW( E_INVALIDARG ); if ( eBoolValue == eVsTstBool_Random && !m_psBoolDef->m_bCanHaveRandom ) VSTST_THROW( cwsThrow.Format( L"Value 'Random' not allowed for this keyword" ) ); m_eBoolValue = eBoolValue; SetDefaultOverriden(); }
EVsTstINIBoolType GetValue() { if ( GetOptionType() != eVsTstOptType_Boolean ) VSTST_THROW( E_INVALIDARG ); return m_eBoolValue; } private: EVsTstINIBoolType m_eBoolValue; SVsTstINIBooleanDef *m_psBoolDef; };
//
// In-memory number option. This maintains the state of
// one option. It takes care of using the option definition in
// tstiniconfigpriv.hxx to initialize the option with its default value
// and if the option value changes, makes sure it matches what
// is allowed by the definition.
//
class CVsTstSectionOptionNumber : public CVsTstSectionOptionBase { public: CVsTstSectionOptionNumber( IN SVsTstININumberDef& rsNumDef ) : CVsTstSectionOptionBase( eVsTstOptType_Number ) { // Set up option definition
m_psNumDef = &rsNumDef;
// Set up default values
m_llMinNumberValue = m_psNumDef->m_llDefaultMinNumber; if ( m_psNumDef->m_bCanHaveRange ) m_llMaxNumberValue = m_psNumDef->m_llDefaultMaxNumber; else m_llMaxNumberValue = m_psNumDef->m_llDefaultMinNumber; }; virtual ~CVsTstSectionOptionNumber() {}; virtual VOID SetValueFromINIValue( IN CBsString cwsINIValue ) { INT iFind; LONGLONG llMinNumberValue; LONGLONG llMaxNumberValue; //
// See if the range characters are in the value
//
iFind = cwsINIValue.Find( x_wszRangeString ); if ( iFind == -1 ) { //
// Not a range
//
llMinNumberValue = _wtoi64( cwsINIValue ); SetValue( llMinNumberValue, 0, FALSE ); } else { CBsString cwsTemp( cwsINIValue ); llMinNumberValue = _wtoi64( cwsTemp ); // Will stop at ...
cwsTemp = cwsINIValue.Mid( iFind + (INT)::wcslen( x_wszRangeString ) ); llMaxNumberValue = _wtoi64( cwsTemp ); SetValue( llMinNumberValue, llMaxNumberValue, TRUE ); } }
VOID SetValue( IN LONGLONG llMinNumberValue, IN LONGLONG llMaxNumberValue, IN BOOL bRange ) { CBsString cwsThrow; if ( GetOptionType() != eVsTstOptType_Number ) VSTST_THROW( E_INVALIDARG );
if ( bRange && llMinNumberValue != llMaxNumberValue && !m_psNumDef->m_bCanHaveRange ) VSTST_THROW( cwsThrow.Format( L"%s number range not allowed in value", x_wszRangeString ) );
if ( llMinNumberValue < m_psNumDef->m_llMinNumber || llMinNumberValue > m_psNumDef->m_llMaxNumber ) VSTST_THROW( cwsThrow.Format( L"%I64d not within valid min (%I64d) and max (%I64d) number values", llMinNumberValue, m_psNumDef->m_llMinNumber, m_psNumDef->m_llMaxNumber ) );
if ( bRange ) { if ( llMaxNumberValue < m_psNumDef->m_llMinNumber || llMaxNumberValue > m_psNumDef->m_llMaxNumber ) VSTST_THROW( cwsThrow.Format( L"%I64d not within valid min (%I64d) and max (%I64d) number values", llMaxNumberValue, m_psNumDef->m_llMinNumber, m_psNumDef->m_llMaxNumber ) ); else if ( llMinNumberValue > llMaxNumberValue ) VSTST_THROW( cwsThrow.Format( L"Min value larger than max value" ) ); } m_llMinNumberValue = llMinNumberValue; if ( bRange ) m_llMaxNumberValue = llMaxNumberValue; else m_llMaxNumberValue = llMinNumberValue; SetDefaultOverriden(); }
VOID GetValue( OUT LONGLONG *pllMinNumberValue, OUT LONGLONG *pllMaxNumberValue, OUT BOOL *pbRange ) { if ( GetOptionType() != eVsTstOptType_Number ) VSTST_THROW( E_INVALIDARG ); *pllMinNumberValue = m_llMinNumberValue; *pllMaxNumberValue = m_llMaxNumberValue;
if ( m_llMinNumberValue == m_llMaxNumberValue ) *pbRange = FALSE; else *pbRange = TRUE; }
private: LONGLONG m_llMinNumberValue; LONGLONG m_llMaxNumberValue; SVsTstININumberDef *m_psNumDef; };
//
// In-memory string option. This maintains the state of
// one option. It takes care of using the option definition in
// tstiniconfigpriv.hxx to initialize the option with its default value
// and if the option value changes, makes sure it matches what
// is allowed by the definition.
//
class CVsTstSectionOptionString : public CVsTstSectionOptionBase { public: CVsTstSectionOptionString( IN SVsTstINIStringDef& rsStringDef ) : CVsTstSectionOptionBase( eVsTstOptType_String ) { // Set up option definition
m_psStringDef = &rsStringDef;
// Set up default values
m_wsStringValue = m_psStringDef->m_pwszDefaultString; }; virtual ~CVsTstSectionOptionString() {};
virtual VOID SetValueFromINIValue( IN CBsString cwsINIValue ) { SetValue( cwsINIValue ); }
VOID SetValue( IN const CBsString& rwsStringValue ) { //
// If the PossibleValues field is NULL in the definition
// then, any string is allowed in the option.
//
if ( m_psStringDef->m_pwszPossibleValues != NULL ) { //
// See if this string is part of the set of possible
// values. The values are in a string delimited by '|' chars.
//
LPWSTR pwszPossibleValues = ::_wcsdup( m_psStringDef->m_pwszPossibleValues ); if ( pwszPossibleValues == NULL ) VSTST_THROW( E_OUTOFMEMORY );
LPWSTR pwszToken; pwszToken = ::wcstok( pwszPossibleValues, L"|" ); while ( pwszToken != NULL ) { if ( ::_wcsicmp( pwszToken, rwsStringValue.c_str() ) == 0 ) break; pwszToken = ::wcstok( NULL, L"|" ); }
free( pwszPossibleValues ); if ( pwszToken == NULL ) { // Not a string in the PossibleValues array, throw string
CBsString cwsThrow; VSTST_THROW( cwsThrow.Format( L"Invalid value '%s', possible values are '%s'", rwsStringValue.c_str(), m_psStringDef->m_pwszPossibleValues ) ); } }
m_wsStringValue = rwsStringValue; SetDefaultOverriden(); }
CBsString GetValue() { if ( GetOptionType() != eVsTstOptType_String ) VSTST_THROW( E_INVALIDARG ); return m_wsStringValue; } private: CBsString m_wsStringValue; SVsTstINIStringDef *m_psStringDef; };
//
// Definition of the hash table that maintains the option name to
// option class instance mapping. This will efficiently allow
// many options to be used in a section. Pointer to instances
// of this class are stored in the m_pvOptionsList field of
// CVsTstINIConfig.
//
typedef TBsHashMap< CBsString, CVsTstSectionOptionBase * > CVsTstOptionsList;
//
// The equality test
//
inline BOOL AreKeysEqual( const CBsString& lhK, const CBsString& rhK ) { //
// Do a case independent compare
//
return ( lhK.CompareNoCase( rhK ) == 0 ); }
static LONG CBsStringHashFunc( const CBsString& Key, LONG NumBuckets ) { //
// Need a temp string to uppercase
//
CBsString cwsTemp( Key ); cwsTemp.MakeUpper(); const BYTE *pByteKey = (const BYTE *)cwsTemp.c_str(); LONG dwHashVal = 0; SIZE_T cKeyLen = cwsTemp.GetLength() * sizeof WCHAR; for ( SIZE_T i = 0; i < cKeyLen; ++i ) { dwHashVal += pByteKey[i]; } return dwHashVal % NumBuckets; }
/*++
Routine Description:
Constructor for the CVsTstINIConfig class.
Arguments:
eSectionType - The section type of the section to read. pwszSectionQualifier - The qualifier of the section in the INI file, the XXX in [SectionType.XXX] bWriteINIFile - If true and the INI file doesn't exist, the INI file will be created with the default values filled in. pwszINIFileName - The full path to the INI file. If NULL, the default INI file location is used. bContinueOnINIFileErrors - If FALSE, an CVsTstINIConfigException class is thrown when an error is found in the ini file. If TRUE, they are silently skipped; however, HRESULT's may be still thrown if programatic or memory errors occur. Return Value:
NONE May throw HRESULT and CVsTstINIConfigException exceptions.
--*/ CVsTstINIConfig::CVsTstINIConfig( IN EVsTstINISectionType eSectionType, IN LPCWSTR pwszSectionQualifier, IN BOOL bWriteINIFile, IN LPCWSTR pwszINIFileName, IN BOOL bContinueOnINIFileErrors ) : m_bWriteINIFile( bWriteINIFile ), m_eSectionType( eSectionType ), m_pvOptionsList( NULL ), m_bContinueOnINIFileErrors( bContinueOnINIFileErrors ) { VSTST_ASSERT( pwszSectionQualifier != NULL ); VSTST_ASSERT( pwszSectionQualifier[ 0 ] != L'\0' ); VSTST_ASSERT( eSectionType > eVsTstSectionType_UNKNOWN && eSectionType < eVsTstSectionType_SENTINEL );
//
// Set up the hash table to be used to store the option values
//
m_pvOptionsList = new CVsTstOptionsList( BSHASHMAP_SMALL, CBsStringHashFunc ); if ( m_pvOptionsList == NULL ) VSTST_THROW( E_OUTOFMEMORY ); //
// Set up the section that will be read
//
m_wsSectionName = x_sSectionDefArr[ m_eSectionType ].m_pwszSectionTypeName; m_wsSectionName += L"."; m_wsSectionName += pwszSectionQualifier;
//
// Set up the INI file path. If pwszINIFileName is NULL, use the default
// INI file name. The paths can have environment variables that need
// to be expanded.
//
DWORD dwRet; dwRet = ::ExpandEnvironmentStringsW( pwszINIFileName == NULL ? x_wszDefaultINIPath : pwszINIFileName, m_wsINIFileName.GetBuffer( MAX_PATH ), MAX_PATH ); m_wsINIFileName.ReleaseBuffer(); if ( dwRet == 0 ) VSTST_THROW( E_UNEXPECTED );
HRESULT hr;
//
// First initialize all options with their default values
//
hr = SetupDefaultValues(); if ( FAILED( hr ) ) { VSTST_THROW( hr ); } //
// Now open the ini file. If the file is not there and the caller wants a
// default INI file created, then what are we waiting for, create it.
//
hr = LoadINIFileData(); if ( hr == STG_E_FILENOTFOUND && bWriteINIFile ) { hr = CreateDefaultINIFile(); } if ( FAILED( hr ) ) { VSTST_THROW( hr ); } }
CVsTstINIConfig::~CVsTstINIConfig() { //
// Clean up the options list if necessary
//
if ( m_pvOptionsList != NULL ) { CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * )m_pvOptionsList; CBsString wsOptionName; CVsTstSectionOptionBase *pcSectionOptionBase; pcOptionsList->StartEnum(); while ( pcOptionsList->GetNextEnum( &wsOptionName, &pcSectionOptionBase ) ) { delete pcSectionOptionBase; } pcOptionsList->EndEnum(); delete pcOptionsList; m_pvOptionsList = NULL; } }
HRESULT CVsTstINIConfig::LoadINIFileData() { DWORD dwSectionBufferSize = 1024; DWORD dwRet; LPWSTR pwszSectionBuffer = NULL;
//
// See if the INI file exists, if not return file not found
//
if ( ::GetFileAttributesW( m_wsINIFileName ) == -1 ) { if ( ::GetLastError() != ERROR_FILE_NOT_FOUND && ::GetLastError() != ERROR_PATH_NOT_FOUND ) VSTST_THROW( HRESULT_FROM_WIN32( ::GetLastError() ) ); else return STG_E_FILENOTFOUND; } //
// First get the entire section from the INI file by using the funky
// GetPrivateProfileSection API.
//
do { if ( pwszSectionBuffer ) { free( pwszSectionBuffer ); dwSectionBufferSize <<= 2; // bump up the size by a power of two and try again
} pwszSectionBuffer = ( LPWSTR )malloc( sizeof( WCHAR ) * dwSectionBufferSize ); if ( pwszSectionBuffer == NULL ) VSTST_THROW( E_OUTOFMEMORY ); dwRet = ::GetPrivateProfileSectionW( m_wsSectionName, pwszSectionBuffer, dwSectionBufferSize, m_wsINIFileName ); } while ( dwRet == dwSectionBufferSize - 2 ); // who came up with this API ???
if ( dwRet > 0 ) { // Section is found and not empty
//
// Now go through the section buffer, one option at a time, replacing defaults
// with the specified options.
//
CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList; LPWSTR pwszCurrOption = pwszSectionBuffer; while ( true ) { SIZE_T cOptionLen = ::wcslen( pwszCurrOption ); LPWSTR pwszValue = ::wcschr( pwszCurrOption, L'=' ); if ( pwszValue != NULL ) { pwszValue[ 0 ] = '\0'; // blast away =
++pwszValue; // Skip over blasted =
//
// Now pwszCurrOption only contains the key name and pwszValue contains
// the value
// Find the key in the option list and set the value
//
CVsTstSectionOptionBase *pcSectionOptionBase; if ( pcOptionsList->Find( pwszCurrOption, &pcSectionOptionBase ) ) { //
// Key found, set it. Note, the SetValue methods
// can throw CBsStrings when an INI file error is
// found.
//
try { pcSectionOptionBase->SetValueFromINIValue( pwszValue ); } catch ( CBsString cwsExcept ) { if ( !m_bContinueOnINIFileErrors ) { CVsTstINIConfigException cExcept; cExcept.m_cwsExceptionString.Format( L"(%s), keyword '%s', section '%s', INI file '%s'", cwsExcept.c_str(), pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() ); free( pwszSectionBuffer ); VSTST_THROW( cExcept ); } } } else { if ( !m_bContinueOnINIFileErrors ) { //
// Keyword not found, throw an error. We might not want to do this
// in the future.
//
CVsTstINIConfigException cExcept; cExcept.m_cwsExceptionString.Format( L"Unknown keyword '%s', section '%s', INI file '%s'", pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() ); VSTST_THROW( cExcept ); } } } else { if ( !m_bContinueOnINIFileErrors ) { CVsTstINIConfigException cExcept; cExcept.m_cwsExceptionString.Format( L"No '=' in line '%s', section '%s' of INI file '%s'", pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() ); free( pwszSectionBuffer ); VSTST_THROW( cExcept ); } } pwszCurrOption += cOptionLen;
if ( pwszCurrOption[ 0 ] == L'\0' && pwszCurrOption[ 1 ] == L'\0' ) break; ++pwszCurrOption; // Skip null char
} } free( pwszSectionBuffer ); return S_OK; }
HRESULT CVsTstINIConfig::SetupDefaultValues() { VSTST_ASSERT( m_pvOptionsList != NULL ); HRESULT hr = S_OK; //
// Initialize all of the section options with the hardwired option types,
// max sizes and default values.
//
SVsTstINISectionDef *psSectionDef = x_sSectionDefArr[ m_eSectionType ].m_psSectionDef;
if ( psSectionDef == NULL ) // No section definition, return
return S_OK;
CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList; //
// Iterate through the list of options in the definition.
//
for( SIZE_T cOptionIdx = 0; psSectionDef[ cOptionIdx ].m_pwszKeyName != NULL; ++cOptionIdx ) { //
// Skip comments in definition
//
if ( psSectionDef[ cOptionIdx ].m_eOptionType == eVsTstOptType_Comment ) continue; CVsTstSectionOptionBase *pcOptionBase = NULL;
//
// Depending on type of option, create the correct
// object and place it into the hash table. Yes,
// these new()'s can throw exceptions, not a
// problem here, things will clean up properly.
//
switch ( psSectionDef[ cOptionIdx ].m_eOptionType ) { case eVsTstOptType_Boolean: pcOptionBase = new CVsTstSectionOptionBoolean( psSectionDef[ cOptionIdx ].m_sBooleanDef ); break; case eVsTstOptType_String: pcOptionBase = new CVsTstSectionOptionString( psSectionDef[ cOptionIdx ].m_sStringDef ); break; case eVsTstOptType_Number: pcOptionBase = new CVsTstSectionOptionNumber( psSectionDef[ cOptionIdx ].m_sNumberDef ); break; default: VSTST_ASSERT( "Invalid option type in definition array" && FALSE ); VSTST_THROW( E_INVALIDARG ); break; }
if ( pcOptionBase == NULL ) VSTST_THROW( E_OUTOFMEMORY ); //
// Now insert the option object into the hash table.
//
try { LONG lRet; CBsString cwsKeyName( psSectionDef[ cOptionIdx ].m_pwszKeyName );
//
// Store key names in uppercase
//
lRet = pcOptionsList->Insert( cwsKeyName, pcOptionBase ); if ( lRet == BSHASHMAP_ALREADY_EXISTS ) { VSTST_ASSERT( "Option name defined twice in definition array" && FALSE ); VSTST_THROW( E_INVALIDARG ); } } VSTST_STANDARD_CATCH(); if ( FAILED( hr ) ) { delete pcOptionBase; VSTST_THROW( hr ); } }
return S_OK; }
#define VSTST_WRAP_WIDTH 97
/*++
Routine Description:
Creates a default INI file that specifies all sections, keys and default values including comments about what each key is for.
Arguments:
None
Return Value:
<Enter return values here>
--*/ HRESULT CVsTstINIConfig::CreateDefaultINIFile() { FILE *pfINIFile = NULL; HRESULT hr = S_OK; try { // Open the INI file
pfINIFile = ::_wfopen( m_wsINIFileName, L"w" ); if ( pfINIFile == NULL ) { CVsTstINIConfigException cExcept; cExcept.m_cwsExceptionString.Format( L"Unable to open INI file '%s' for write", m_wsINIFileName.c_str() ); VSTST_THROW( cExcept ); } // Write out all known options for all sections.
for ( SIZE_T idx = ( SIZE_T ) eVsTstSectionType_UNKNOWN + 1; idx < ( ( SIZE_T )eVsTstSectionType_SENTINEL ); ++idx ) { fwprintf( pfINIFile, L"[%s.%s]\n", x_sSectionDefArr[ idx ].m_pwszSectionTypeName, x_wszDefaultSectionName ); SVsTstINISectionDef *psSectionDef = x_sSectionDefArr[ idx ].m_psSectionDef; if ( psSectionDef != NULL ) { CBsString cwsToBeWrapped; // Go through each option in the sections.
for ( SIZE_T cSect = 0; psSectionDef[ cSect ].m_pwszKeyName != NULL; ++cSect ) { if ( psSectionDef[ cSect ].m_eOptionType != eVsTstOptType_Comment ) { cwsToBeWrapped.Format( L"%s - %s", psSectionDef[ cSect ].m_pwszKeyName, psSectionDef[ cSect ].m_pwszDescription ); ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); } switch ( psSectionDef[ cSect ].m_eOptionType ) { case eVsTstOptType_Comment: if ( psSectionDef[ cSect ].m_pwszDescription == NULL ) fwprintf( pfINIFile, L"\n" ); else { fwprintf( pfINIFile, L";\n" ); cwsToBeWrapped.Format( L"%s\n", psSectionDef[ cSect ].m_pwszDescription ); ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); fwprintf( pfINIFile, L";\n" ); } break; case eVsTstOptType_String: if ( psSectionDef[ cSect ].m_sStringDef.m_pwszPossibleValues == NULL ) cwsToBeWrapped.Format( L"Default value: '%s'\n", psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString ); else { CBsString cwsPossibleValuesConverted( psSectionDef[ cSect ].m_sStringDef.m_pwszPossibleValues ); cwsPossibleValuesConverted.Replace( L'|', L',' ); cwsToBeWrapped.Format( L"Default value: '%s', possible values: '%s'\n", psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString, cwsPossibleValuesConverted.c_str() ); } ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); fwprintf( pfINIFile, L";%s = %s\n\n", psSectionDef[ cSect ].m_pwszKeyName, psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString ); break; case eVsTstOptType_Boolean: cwsToBeWrapped.Format( L"Default value: '%s'%s", x_pwszBooleanValueNames[ psSectionDef[ cSect ].m_sBooleanDef.m_eBoolDefault ], ( psSectionDef[ cSect ].m_sBooleanDef.m_bCanHaveRandom ) ? L", can have 'Random' value\n" : L"" ); ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); fwprintf( pfINIFile, L";%s = %s\n\n", psSectionDef[ cSect ].m_pwszKeyName, x_pwszBooleanValueNames[ psSectionDef[ cSect ].m_sBooleanDef.m_eBoolDefault ] ); break; case eVsTstOptType_Number: { SVsTstININumberDef *psNumDef = &( psSectionDef[ cSect ].m_sNumberDef ); if ( psNumDef->m_bCanHaveRange ) { cwsToBeWrapped.Format( L"Default value: %I64d%s%I64d, Min value: %I64d, Max value: %I64d, can be a range\n", psNumDef->m_llDefaultMinNumber, x_wszRangeString, psNumDef->m_llDefaultMaxNumber, psNumDef->m_llMinNumber, psNumDef->m_llMaxNumber ); ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); fwprintf( pfINIFile, L";%s = %I64d%s%I64d\n\n", psSectionDef[ cSect ].m_pwszKeyName, psNumDef->m_llDefaultMinNumber, x_wszRangeString, psNumDef->m_llDefaultMaxNumber ); } else { cwsToBeWrapped.Format( L"Default value: %I64d, Min value: %I64d, Max value: %I64d\n", psNumDef->m_llDefaultMinNumber, psNumDef->m_llMinNumber, psNumDef->m_llMaxNumber ); ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH ); fwprintf( pfINIFile, L";%s = %I64d\n\n", psSectionDef[ cSect ].m_pwszKeyName, psNumDef->m_llDefaultMinNumber ); } } break; } } } fwprintf( pfINIFile, L"; ==================================================================\n" ); } } VSTST_STANDARD_CATCH();
if ( pfINIFile != NULL ) ::fclose( pfINIFile ); return S_OK; }
// Gets a string value
VOID CVsTstINIConfig::GetOptionValue( IN LPCWSTR pwszOptionName, OUT CBsString *pwsOptionValue, OUT BOOL *pbOverridden ) { CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList; CVsTstSectionOptionBase *pcSectionOptionBase; if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) ) { if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_String ) { VSTST_ASSERT( FALSE ); VSTST_THROW( E_INVALIDARG ); } CVsTstSectionOptionString *pcOptionString; pcOptionString = ( CVsTstSectionOptionString * )pcSectionOptionBase; *pwsOptionValue = pcOptionString->GetValue(); if ( pbOverridden != NULL ) *pbOverridden = pcOptionString->IsDefaultOverridden(); return; }
VSTST_THROW( E_INVALIDARG ); }
// Gets a boolean value
VOID CVsTstINIConfig::GetOptionValue( IN LPCWSTR pwszOptionName, OUT EVsTstINIBoolType *peOptionValue, OUT BOOL *pbOverridden ) { CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList; CVsTstSectionOptionBase *pcSectionOptionBase; if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) ) { if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_Boolean ) { VSTST_ASSERT( FALSE ); VSTST_THROW( E_INVALIDARG ); } CVsTstSectionOptionBoolean *pcOptionBoolean; pcOptionBoolean = ( CVsTstSectionOptionBoolean * )pcSectionOptionBase; *peOptionValue = pcOptionBoolean->GetValue(); if ( pbOverridden != NULL ) *pbOverridden = pcOptionBoolean->IsDefaultOverridden(); return; }
VSTST_THROW( E_INVALIDARG ); }
// Get a number value
VOID CVsTstINIConfig::GetOptionValue( IN LPCWSTR pwszOptionName, OUT LONGLONG *pllOptionMinValue, OUT LONGLONG *pllOptionMaxValue, OUT BOOL *pbOverridden ) { CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList; CVsTstSectionOptionBase *pcSectionOptionBase; if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) ) { if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_Number ) { VSTST_ASSERT( FALSE ); VSTST_THROW( E_INVALIDARG ); } CVsTstSectionOptionNumber *pcOptionNumber; pcOptionNumber = ( CVsTstSectionOptionNumber * )pcSectionOptionBase;
BOOL bHasRange; pcOptionNumber->GetValue( pllOptionMinValue, pllOptionMaxValue, &bHasRange ); if ( pbOverridden != NULL ) *pbOverridden = pcOptionNumber->IsDefaultOverridden(); return; }
VSTST_THROW( E_INVALIDARG ); }
static VOID pVsTstWrapOutput( IN FILE *pfOut, IN LPCWSTR pwszBeginString, IN CBsString& cwsToBeWrapped, IN SIZE_T cWrapWidth ) { cwsToBeWrapped.TrimLeft(); cwsToBeWrapped.TrimRight(); LPWSTR pwszCurrPosition = cwsToBeWrapped.GetBuffer( cwsToBeWrapped.GetLength() ); LPWSTR pwszNextLine = NULL; LPWSTR pwszSpaces = L""; while( *pwszCurrPosition != L'\0' ) { SIZE_T cLen; cLen = ::wcslen( pwszCurrPosition );
//
// Get rid of the easy case
//
if ( cLen <= cWrapWidth ) { fwprintf( pfOut, L"%s%s%s\n", pwszBeginString, pwszSpaces, pwszCurrPosition ); break; } pwszNextLine = pwszCurrPosition + cWrapWidth - ::wcslen( pwszSpaces ); while ( pwszNextLine > pwszCurrPosition ) { if ( *pwszNextLine == L' ' ) break; --pwszNextLine; }
if ( pwszNextLine == pwszCurrPosition ) { //
// No spaces within margin, move forward to first space.
//
pwszNextLine = ::wcschr( pwszCurrPosition, L' ' );
//
// If pwszNextLine is NULL, then it means the entire rest of the line has no spaces
//
if ( pwszNextLine != NULL ) *pwszNextLine = '\0'; } else { *pwszNextLine = '\0'; } fwprintf( pfOut, L"%s%s%s\n", pwszBeginString, pwszSpaces, pwszCurrPosition );
//
// If special case where pwszNextLine is NULL, we are done
//
if ( pwszNextLine == NULL ) break; pwszCurrPosition = pwszNextLine + 1; pwszSpaces = L" "; }
cwsToBeWrapped.ReleaseBuffer(); }
|