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.
1158 lines
37 KiB
1158 lines
37 KiB
/*++
|
|
|
|
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();
|
|
}
|
|
|