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

1158 lines
37 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. TstINIConfig.cxx
  5. Abstract:
  6. Class that manages the reading of the test scenario INI file.
  7. Author:
  8. Stefan R. Steiner [ssteiner] 05-16-2000
  9. Revision History:
  10. --*/
  11. #include <windows.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include "vststtools.hxx"
  15. #include "vs_hash.hxx"
  16. #include "tstiniconfig.hxx"
  17. #include "tstiniconfigpriv.hxx" // has layouts of all the possible INI sections
  18. static VOID
  19. pVsTstWrapOutput(
  20. IN FILE *pfOut,
  21. IN LPCWSTR pwszBeginString,
  22. IN CBsString& cwsToBeWrapped,
  23. IN SIZE_T cWrapWidth
  24. );
  25. static LPCWSTR x_wszDefaultINIPath = L"%SystemRoot%\\VsTestHarness.ini";
  26. struct SVsTstSection
  27. {
  28. LPWSTR m_pwszSectionTypeName;
  29. SVsTstINISectionDef *m_psSectionDef;
  30. };
  31. //
  32. // This array must match the EVsTstINISectionType enum in tstiniconfigpriv.h.
  33. // These strings are the strings found in the INI file in the section headers. E.g.
  34. // for section name [VssTestHarness.XXXX]
  35. // the XXXX is the section qualifier for the section type VssTestHarness.
  36. // See tstiniconfigpriv.hxx for definitions of sVsTstINISectionDefXXXX variables.
  37. //
  38. static SVsTstSection x_sSectionDefArr[] =
  39. {
  40. { L"INVALID", NULL },
  41. { L"VssTestController", sVsTstINISectionDefController },
  42. { L"VssTestRequestor", sVsTstINISectionDefRequester },
  43. { L"VssTestWriter", sVsTstINISectionDefWriter },
  44. { L"VssTestProvider", sVsTstINISectionDefProvider }
  45. };
  46. static LPCWSTR x_wszDefaultSectionName = L"DEFAULT";
  47. //
  48. // Range delimiter
  49. //
  50. static LPCWSTR x_wszRangeString = L"...";
  51. //
  52. // The following constants define the boolean values when writing to the
  53. // ini files.
  54. //
  55. static LPWSTR const x_pwszBooleanValueNames[] =
  56. {
  57. L"No", // eVsTstBool_False
  58. L"Yes", // eVsTstBool_True
  59. L"Random" // eVsTstBool_Random
  60. };
  61. //
  62. // The valid true values that can be specified as a boolean value.
  63. //
  64. static LPWSTR const x_pwszValidBooleanTrueValues[] =
  65. {
  66. L"YES",
  67. L"TRUE",
  68. L"1",
  69. L"JA",
  70. L"SI",
  71. L"OUI",
  72. NULL
  73. };
  74. //
  75. // The valid false values that can be specified as a boolean value.
  76. //
  77. static LPWSTR const x_pwszValidBooleanFalseValues[] =
  78. {
  79. L"NO",
  80. L"FALSE",
  81. L"0",
  82. L"NEIN",
  83. L"NON",
  84. NULL
  85. };
  86. //
  87. // The valid strings to specify random values.
  88. //
  89. static LPWSTR const x_pwszValidBooleanRandomValues[] =
  90. {
  91. L"RANDOM",
  92. L"-1",
  93. NULL
  94. };
  95. //
  96. // Function that finds a match within a string array
  97. //
  98. static BOOL
  99. IsInArray(
  100. IN const CBsString& cwsString,
  101. IN LPWSTR const *ppwszStringMatchArray
  102. )
  103. {
  104. VSTST_ASSERT( ppwszStringMatchArray != NULL );
  105. while ( *ppwszStringMatchArray != NULL )
  106. {
  107. if ( cwsString == *ppwszStringMatchArray )
  108. return TRUE;
  109. ++ppwszStringMatchArray;
  110. }
  111. return FALSE;
  112. }
  113. //
  114. // Parent class the all in memory options subclass from
  115. //
  116. class CVsTstSectionOptionBase
  117. {
  118. public:
  119. CVsTstSectionOptionBase(
  120. IN EVsTstINIOptionType eOptionType
  121. ) : m_eOptionType( eOptionType ),
  122. m_bDefaultOverridden( FALSE ) { };
  123. virtual ~CVsTstSectionOptionBase() {};
  124. EVsTstINIOptionType GetOptionType() { return m_eOptionType; }
  125. //
  126. // The value after the "KeyName=" in the INI file
  127. //
  128. virtual VOID SetValueFromINIValue(
  129. IN CBsString cwsINIValue
  130. ) = 0;
  131. //
  132. // Returns true if the default value was overridden by the INI
  133. // file or a call to a SetValueXXXX method in derived classes.
  134. //
  135. BOOL IsDefaultOverridden() { return m_bDefaultOverridden; }
  136. protected:
  137. VOID SetDefaultOverriden() { m_bDefaultOverridden = TRUE; }
  138. private:
  139. EVsTstINIOptionType m_eOptionType;
  140. BOOL m_bDefaultOverridden; // TRUE if default value was overridden
  141. };
  142. //
  143. // In-memory boolean option. This maintains the state of
  144. // one option. It takes care of using the option definition in
  145. // tstiniconfigpriv.hxx to initialize the option with its default value
  146. // and if the option value changes, makes sure it matches what
  147. // is allowed by the definition.
  148. //
  149. class CVsTstSectionOptionBoolean : public CVsTstSectionOptionBase
  150. {
  151. public:
  152. CVsTstSectionOptionBoolean(
  153. IN SVsTstINIBooleanDef& rsBoolDef
  154. ) : CVsTstSectionOptionBase( eVsTstOptType_Boolean )
  155. {
  156. // Set up option definition
  157. m_psBoolDef = &rsBoolDef;
  158. // Set up default values
  159. m_eBoolValue = m_psBoolDef->m_eBoolDefault;
  160. };
  161. virtual ~CVsTstSectionOptionBoolean() {};
  162. virtual VOID SetValueFromINIValue(
  163. IN CBsString cwsINIValue
  164. )
  165. {
  166. cwsINIValue.TrimLeft();
  167. cwsINIValue.TrimRight();
  168. cwsINIValue.MakeUpper();
  169. if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanTrueValues ) )
  170. SetValue( eVsTstBool_True );
  171. else if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanFalseValues ) )
  172. SetValue( eVsTstBool_False );
  173. else if ( ::IsInArray( cwsINIValue, x_pwszValidBooleanRandomValues ) )
  174. SetValue( eVsTstBool_Random );
  175. else
  176. {
  177. CBsString cwsThrow;
  178. VSTST_THROW( cwsThrow.Format( L"Invalid boolean value '%s'", cwsINIValue.c_str() ) );
  179. }
  180. }
  181. VOID SetValue(
  182. IN EVsTstINIBoolType eBoolValue
  183. )
  184. {
  185. CBsString cwsThrow;
  186. VSTST_ASSERT( eBoolValue == eVsTstBool_False || eBoolValue == eVsTstBool_True ||
  187. eBoolValue == eVsTstBool_Random );
  188. if ( GetOptionType() != eVsTstOptType_Boolean )
  189. VSTST_THROW( E_INVALIDARG );
  190. if ( eBoolValue == eVsTstBool_Random && !m_psBoolDef->m_bCanHaveRandom )
  191. VSTST_THROW( cwsThrow.Format( L"Value 'Random' not allowed for this keyword" ) );
  192. m_eBoolValue = eBoolValue;
  193. SetDefaultOverriden();
  194. }
  195. EVsTstINIBoolType GetValue()
  196. {
  197. if ( GetOptionType() != eVsTstOptType_Boolean )
  198. VSTST_THROW( E_INVALIDARG );
  199. return m_eBoolValue;
  200. }
  201. private:
  202. EVsTstINIBoolType m_eBoolValue;
  203. SVsTstINIBooleanDef *m_psBoolDef;
  204. };
  205. //
  206. // In-memory number option. This maintains the state of
  207. // one option. It takes care of using the option definition in
  208. // tstiniconfigpriv.hxx to initialize the option with its default value
  209. // and if the option value changes, makes sure it matches what
  210. // is allowed by the definition.
  211. //
  212. class CVsTstSectionOptionNumber : public CVsTstSectionOptionBase
  213. {
  214. public:
  215. CVsTstSectionOptionNumber(
  216. IN SVsTstININumberDef& rsNumDef
  217. ) : CVsTstSectionOptionBase( eVsTstOptType_Number )
  218. {
  219. // Set up option definition
  220. m_psNumDef = &rsNumDef;
  221. // Set up default values
  222. m_llMinNumberValue = m_psNumDef->m_llDefaultMinNumber;
  223. if ( m_psNumDef->m_bCanHaveRange )
  224. m_llMaxNumberValue = m_psNumDef->m_llDefaultMaxNumber;
  225. else
  226. m_llMaxNumberValue = m_psNumDef->m_llDefaultMinNumber;
  227. };
  228. virtual ~CVsTstSectionOptionNumber() {};
  229. virtual VOID SetValueFromINIValue(
  230. IN CBsString cwsINIValue
  231. )
  232. {
  233. INT iFind;
  234. LONGLONG llMinNumberValue;
  235. LONGLONG llMaxNumberValue;
  236. //
  237. // See if the range characters are in the value
  238. //
  239. iFind = cwsINIValue.Find( x_wszRangeString );
  240. if ( iFind == -1 )
  241. {
  242. //
  243. // Not a range
  244. //
  245. llMinNumberValue = _wtoi64( cwsINIValue );
  246. SetValue( llMinNumberValue, 0, FALSE );
  247. }
  248. else
  249. {
  250. CBsString cwsTemp( cwsINIValue );
  251. llMinNumberValue = _wtoi64( cwsTemp ); // Will stop at ...
  252. cwsTemp = cwsINIValue.Mid( iFind + (INT)::wcslen( x_wszRangeString ) );
  253. llMaxNumberValue = _wtoi64( cwsTemp );
  254. SetValue( llMinNumberValue, llMaxNumberValue, TRUE );
  255. }
  256. }
  257. VOID SetValue(
  258. IN LONGLONG llMinNumberValue,
  259. IN LONGLONG llMaxNumberValue,
  260. IN BOOL bRange
  261. )
  262. {
  263. CBsString cwsThrow;
  264. if ( GetOptionType() != eVsTstOptType_Number )
  265. VSTST_THROW( E_INVALIDARG );
  266. if ( bRange && llMinNumberValue != llMaxNumberValue &&
  267. !m_psNumDef->m_bCanHaveRange )
  268. VSTST_THROW( cwsThrow.Format( L"%s number range not allowed in value", x_wszRangeString ) );
  269. if ( llMinNumberValue < m_psNumDef->m_llMinNumber ||
  270. llMinNumberValue > m_psNumDef->m_llMaxNumber )
  271. VSTST_THROW( cwsThrow.Format( L"%I64d not within valid min (%I64d) and max (%I64d) number values",
  272. llMinNumberValue, m_psNumDef->m_llMinNumber, m_psNumDef->m_llMaxNumber ) );
  273. if ( bRange )
  274. {
  275. if ( llMaxNumberValue < m_psNumDef->m_llMinNumber ||
  276. llMaxNumberValue > m_psNumDef->m_llMaxNumber )
  277. VSTST_THROW( cwsThrow.Format( L"%I64d not within valid min (%I64d) and max (%I64d) number values",
  278. llMaxNumberValue, m_psNumDef->m_llMinNumber, m_psNumDef->m_llMaxNumber ) );
  279. else if ( llMinNumberValue > llMaxNumberValue )
  280. VSTST_THROW( cwsThrow.Format( L"Min value larger than max value" ) );
  281. }
  282. m_llMinNumberValue = llMinNumberValue;
  283. if ( bRange )
  284. m_llMaxNumberValue = llMaxNumberValue;
  285. else
  286. m_llMaxNumberValue = llMinNumberValue;
  287. SetDefaultOverriden();
  288. }
  289. VOID GetValue(
  290. OUT LONGLONG *pllMinNumberValue,
  291. OUT LONGLONG *pllMaxNumberValue,
  292. OUT BOOL *pbRange
  293. )
  294. {
  295. if ( GetOptionType() != eVsTstOptType_Number )
  296. VSTST_THROW( E_INVALIDARG );
  297. *pllMinNumberValue = m_llMinNumberValue;
  298. *pllMaxNumberValue = m_llMaxNumberValue;
  299. if ( m_llMinNumberValue == m_llMaxNumberValue )
  300. *pbRange = FALSE;
  301. else
  302. *pbRange = TRUE;
  303. }
  304. private:
  305. LONGLONG m_llMinNumberValue;
  306. LONGLONG m_llMaxNumberValue;
  307. SVsTstININumberDef *m_psNumDef;
  308. };
  309. //
  310. // In-memory string option. This maintains the state of
  311. // one option. It takes care of using the option definition in
  312. // tstiniconfigpriv.hxx to initialize the option with its default value
  313. // and if the option value changes, makes sure it matches what
  314. // is allowed by the definition.
  315. //
  316. class CVsTstSectionOptionString : public CVsTstSectionOptionBase
  317. {
  318. public:
  319. CVsTstSectionOptionString(
  320. IN SVsTstINIStringDef& rsStringDef
  321. ) : CVsTstSectionOptionBase( eVsTstOptType_String )
  322. {
  323. // Set up option definition
  324. m_psStringDef = &rsStringDef;
  325. // Set up default values
  326. m_wsStringValue = m_psStringDef->m_pwszDefaultString;
  327. };
  328. virtual ~CVsTstSectionOptionString() {};
  329. virtual VOID SetValueFromINIValue(
  330. IN CBsString cwsINIValue
  331. )
  332. {
  333. SetValue( cwsINIValue );
  334. }
  335. VOID SetValue(
  336. IN const CBsString& rwsStringValue
  337. )
  338. {
  339. //
  340. // If the PossibleValues field is NULL in the definition
  341. // then, any string is allowed in the option.
  342. //
  343. if ( m_psStringDef->m_pwszPossibleValues != NULL )
  344. {
  345. //
  346. // See if this string is part of the set of possible
  347. // values. The values are in a string delimited by '|' chars.
  348. //
  349. LPWSTR pwszPossibleValues = ::_wcsdup( m_psStringDef->m_pwszPossibleValues );
  350. if ( pwszPossibleValues == NULL )
  351. VSTST_THROW( E_OUTOFMEMORY );
  352. LPWSTR pwszToken;
  353. pwszToken = ::wcstok( pwszPossibleValues, L"|" );
  354. while ( pwszToken != NULL )
  355. {
  356. if ( ::_wcsicmp( pwszToken, rwsStringValue.c_str() ) == 0 )
  357. break;
  358. pwszToken = ::wcstok( NULL, L"|" );
  359. }
  360. free( pwszPossibleValues );
  361. if ( pwszToken == NULL )
  362. {
  363. // Not a string in the PossibleValues array, throw string
  364. CBsString cwsThrow;
  365. VSTST_THROW( cwsThrow.Format( L"Invalid value '%s', possible values are '%s'",
  366. rwsStringValue.c_str(), m_psStringDef->m_pwszPossibleValues ) );
  367. }
  368. }
  369. m_wsStringValue = rwsStringValue;
  370. SetDefaultOverriden();
  371. }
  372. CBsString GetValue()
  373. {
  374. if ( GetOptionType() != eVsTstOptType_String )
  375. VSTST_THROW( E_INVALIDARG );
  376. return m_wsStringValue;
  377. }
  378. private:
  379. CBsString m_wsStringValue;
  380. SVsTstINIStringDef *m_psStringDef;
  381. };
  382. //
  383. // Definition of the hash table that maintains the option name to
  384. // option class instance mapping. This will efficiently allow
  385. // many options to be used in a section. Pointer to instances
  386. // of this class are stored in the m_pvOptionsList field of
  387. // CVsTstINIConfig.
  388. //
  389. typedef TBsHashMap< CBsString, CVsTstSectionOptionBase * > CVsTstOptionsList;
  390. //
  391. // The equality test
  392. //
  393. inline BOOL AreKeysEqual( const CBsString& lhK, const CBsString& rhK )
  394. {
  395. //
  396. // Do a case independent compare
  397. //
  398. return ( lhK.CompareNoCase( rhK ) == 0 );
  399. }
  400. static LONG CBsStringHashFunc( const CBsString& Key, LONG NumBuckets )
  401. {
  402. //
  403. // Need a temp string to uppercase
  404. //
  405. CBsString cwsTemp( Key );
  406. cwsTemp.MakeUpper();
  407. const BYTE *pByteKey = (const BYTE *)cwsTemp.c_str();
  408. LONG dwHashVal = 0;
  409. SIZE_T cKeyLen = cwsTemp.GetLength() * sizeof WCHAR;
  410. for ( SIZE_T i = 0; i < cKeyLen; ++i )
  411. {
  412. dwHashVal += pByteKey[i];
  413. }
  414. return dwHashVal % NumBuckets;
  415. }
  416. /*++
  417. Routine Description:
  418. Constructor for the CVsTstINIConfig class.
  419. Arguments:
  420. eSectionType - The section type of the section to read.
  421. pwszSectionQualifier - The qualifier of the section in the INI file, the XXX in [SectionType.XXX]
  422. bWriteINIFile - If true and the INI file doesn't exist, the INI file
  423. will be created with the default values filled in.
  424. pwszINIFileName - The full path to the INI file. If NULL, the default
  425. INI file location is used.
  426. bContinueOnINIFileErrors - If FALSE, an CVsTstINIConfigException class is thrown
  427. when an error is found in the ini file. If TRUE, they are silently skipped; however,
  428. HRESULT's may be still thrown if programatic or memory errors occur.
  429. Return Value:
  430. NONE
  431. May throw HRESULT and CVsTstINIConfigException exceptions.
  432. --*/
  433. CVsTstINIConfig::CVsTstINIConfig(
  434. IN EVsTstINISectionType eSectionType,
  435. IN LPCWSTR pwszSectionQualifier,
  436. IN BOOL bWriteINIFile,
  437. IN LPCWSTR pwszINIFileName,
  438. IN BOOL bContinueOnINIFileErrors
  439. ) : m_bWriteINIFile( bWriteINIFile ),
  440. m_eSectionType( eSectionType ),
  441. m_pvOptionsList( NULL ),
  442. m_bContinueOnINIFileErrors( bContinueOnINIFileErrors )
  443. {
  444. VSTST_ASSERT( pwszSectionQualifier != NULL );
  445. VSTST_ASSERT( pwszSectionQualifier[ 0 ] != L'\0' );
  446. VSTST_ASSERT( eSectionType > eVsTstSectionType_UNKNOWN &&
  447. eSectionType < eVsTstSectionType_SENTINEL );
  448. //
  449. // Set up the hash table to be used to store the option values
  450. //
  451. m_pvOptionsList = new CVsTstOptionsList( BSHASHMAP_SMALL, CBsStringHashFunc );
  452. if ( m_pvOptionsList == NULL )
  453. VSTST_THROW( E_OUTOFMEMORY );
  454. //
  455. // Set up the section that will be read
  456. //
  457. m_wsSectionName = x_sSectionDefArr[ m_eSectionType ].m_pwszSectionTypeName;
  458. m_wsSectionName += L".";
  459. m_wsSectionName += pwszSectionQualifier;
  460. //
  461. // Set up the INI file path. If pwszINIFileName is NULL, use the default
  462. // INI file name. The paths can have environment variables that need
  463. // to be expanded.
  464. //
  465. DWORD dwRet;
  466. dwRet = ::ExpandEnvironmentStringsW(
  467. pwszINIFileName == NULL ? x_wszDefaultINIPath : pwszINIFileName,
  468. m_wsINIFileName.GetBuffer( MAX_PATH ),
  469. MAX_PATH );
  470. m_wsINIFileName.ReleaseBuffer();
  471. if ( dwRet == 0 )
  472. VSTST_THROW( E_UNEXPECTED );
  473. HRESULT hr;
  474. //
  475. // First initialize all options with their default values
  476. //
  477. hr = SetupDefaultValues();
  478. if ( FAILED( hr ) )
  479. {
  480. VSTST_THROW( hr );
  481. }
  482. //
  483. // Now open the ini file. If the file is not there and the caller wants a
  484. // default INI file created, then what are we waiting for, create it.
  485. //
  486. hr = LoadINIFileData();
  487. if ( hr == STG_E_FILENOTFOUND && bWriteINIFile )
  488. {
  489. hr = CreateDefaultINIFile();
  490. }
  491. if ( FAILED( hr ) )
  492. {
  493. VSTST_THROW( hr );
  494. }
  495. }
  496. CVsTstINIConfig::~CVsTstINIConfig()
  497. {
  498. //
  499. // Clean up the options list if necessary
  500. //
  501. if ( m_pvOptionsList != NULL )
  502. {
  503. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * )m_pvOptionsList;
  504. CBsString wsOptionName;
  505. CVsTstSectionOptionBase *pcSectionOptionBase;
  506. pcOptionsList->StartEnum();
  507. while ( pcOptionsList->GetNextEnum( &wsOptionName, &pcSectionOptionBase ) )
  508. {
  509. delete pcSectionOptionBase;
  510. }
  511. pcOptionsList->EndEnum();
  512. delete pcOptionsList;
  513. m_pvOptionsList = NULL;
  514. }
  515. }
  516. HRESULT
  517. CVsTstINIConfig::LoadINIFileData()
  518. {
  519. DWORD dwSectionBufferSize = 1024;
  520. DWORD dwRet;
  521. LPWSTR pwszSectionBuffer = NULL;
  522. //
  523. // See if the INI file exists, if not return file not found
  524. //
  525. if ( ::GetFileAttributesW( m_wsINIFileName ) == -1 )
  526. {
  527. if ( ::GetLastError() != ERROR_FILE_NOT_FOUND &&
  528. ::GetLastError() != ERROR_PATH_NOT_FOUND )
  529. VSTST_THROW( HRESULT_FROM_WIN32( ::GetLastError() ) );
  530. else
  531. return STG_E_FILENOTFOUND;
  532. }
  533. //
  534. // First get the entire section from the INI file by using the funky
  535. // GetPrivateProfileSection API.
  536. //
  537. do
  538. {
  539. if ( pwszSectionBuffer )
  540. {
  541. free( pwszSectionBuffer );
  542. dwSectionBufferSize <<= 2; // bump up the size by a power of two and try again
  543. }
  544. pwszSectionBuffer = ( LPWSTR )malloc( sizeof( WCHAR ) * dwSectionBufferSize );
  545. if ( pwszSectionBuffer == NULL )
  546. VSTST_THROW( E_OUTOFMEMORY );
  547. dwRet = ::GetPrivateProfileSectionW(
  548. m_wsSectionName,
  549. pwszSectionBuffer,
  550. dwSectionBufferSize,
  551. m_wsINIFileName );
  552. } while ( dwRet == dwSectionBufferSize - 2 ); // who came up with this API ???
  553. if ( dwRet > 0 )
  554. {
  555. // Section is found and not empty
  556. //
  557. // Now go through the section buffer, one option at a time, replacing defaults
  558. // with the specified options.
  559. //
  560. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList;
  561. LPWSTR pwszCurrOption = pwszSectionBuffer;
  562. while ( true )
  563. {
  564. SIZE_T cOptionLen = ::wcslen( pwszCurrOption );
  565. LPWSTR pwszValue = ::wcschr( pwszCurrOption, L'=' );
  566. if ( pwszValue != NULL )
  567. {
  568. pwszValue[ 0 ] = '\0'; // blast away =
  569. ++pwszValue; // Skip over blasted =
  570. //
  571. // Now pwszCurrOption only contains the key name and pwszValue contains
  572. // the value
  573. // Find the key in the option list and set the value
  574. //
  575. CVsTstSectionOptionBase *pcSectionOptionBase;
  576. if ( pcOptionsList->Find( pwszCurrOption, &pcSectionOptionBase ) )
  577. {
  578. //
  579. // Key found, set it. Note, the SetValue methods
  580. // can throw CBsStrings when an INI file error is
  581. // found.
  582. //
  583. try
  584. {
  585. pcSectionOptionBase->SetValueFromINIValue( pwszValue );
  586. }
  587. catch ( CBsString cwsExcept )
  588. {
  589. if ( !m_bContinueOnINIFileErrors )
  590. {
  591. CVsTstINIConfigException cExcept;
  592. cExcept.m_cwsExceptionString.Format( L"(%s), keyword '%s', section '%s', INI file '%s'",
  593. cwsExcept.c_str(), pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() );
  594. free( pwszSectionBuffer );
  595. VSTST_THROW( cExcept );
  596. }
  597. }
  598. }
  599. else
  600. {
  601. if ( !m_bContinueOnINIFileErrors )
  602. {
  603. //
  604. // Keyword not found, throw an error. We might not want to do this
  605. // in the future.
  606. //
  607. CVsTstINIConfigException cExcept;
  608. cExcept.m_cwsExceptionString.Format( L"Unknown keyword '%s', section '%s', INI file '%s'",
  609. pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() );
  610. VSTST_THROW( cExcept );
  611. }
  612. }
  613. }
  614. else
  615. {
  616. if ( !m_bContinueOnINIFileErrors )
  617. {
  618. CVsTstINIConfigException cExcept;
  619. cExcept.m_cwsExceptionString.Format( L"No '=' in line '%s', section '%s' of INI file '%s'",
  620. pwszCurrOption, m_wsSectionName.c_str(), m_wsINIFileName.c_str() );
  621. free( pwszSectionBuffer );
  622. VSTST_THROW( cExcept );
  623. }
  624. }
  625. pwszCurrOption += cOptionLen;
  626. if ( pwszCurrOption[ 0 ] == L'\0' &&
  627. pwszCurrOption[ 1 ] == L'\0' )
  628. break;
  629. ++pwszCurrOption; // Skip null char
  630. }
  631. }
  632. free( pwszSectionBuffer );
  633. return S_OK;
  634. }
  635. HRESULT
  636. CVsTstINIConfig::SetupDefaultValues()
  637. {
  638. VSTST_ASSERT( m_pvOptionsList != NULL );
  639. HRESULT hr = S_OK;
  640. //
  641. // Initialize all of the section options with the hardwired option types,
  642. // max sizes and default values.
  643. //
  644. SVsTstINISectionDef *psSectionDef = x_sSectionDefArr[ m_eSectionType ].m_psSectionDef;
  645. if ( psSectionDef == NULL )
  646. // No section definition, return
  647. return S_OK;
  648. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList;
  649. //
  650. // Iterate through the list of options in the definition.
  651. //
  652. for( SIZE_T cOptionIdx = 0;
  653. psSectionDef[ cOptionIdx ].m_pwszKeyName != NULL;
  654. ++cOptionIdx )
  655. {
  656. //
  657. // Skip comments in definition
  658. //
  659. if ( psSectionDef[ cOptionIdx ].m_eOptionType == eVsTstOptType_Comment )
  660. continue;
  661. CVsTstSectionOptionBase *pcOptionBase = NULL;
  662. //
  663. // Depending on type of option, create the correct
  664. // object and place it into the hash table. Yes,
  665. // these new()'s can throw exceptions, not a
  666. // problem here, things will clean up properly.
  667. //
  668. switch ( psSectionDef[ cOptionIdx ].m_eOptionType )
  669. {
  670. case eVsTstOptType_Boolean:
  671. pcOptionBase = new CVsTstSectionOptionBoolean(
  672. psSectionDef[ cOptionIdx ].m_sBooleanDef );
  673. break;
  674. case eVsTstOptType_String:
  675. pcOptionBase = new CVsTstSectionOptionString(
  676. psSectionDef[ cOptionIdx ].m_sStringDef );
  677. break;
  678. case eVsTstOptType_Number:
  679. pcOptionBase = new CVsTstSectionOptionNumber(
  680. psSectionDef[ cOptionIdx ].m_sNumberDef );
  681. break;
  682. default:
  683. VSTST_ASSERT( "Invalid option type in definition array" && FALSE );
  684. VSTST_THROW( E_INVALIDARG );
  685. break;
  686. }
  687. if ( pcOptionBase == NULL )
  688. VSTST_THROW( E_OUTOFMEMORY );
  689. //
  690. // Now insert the option object into the hash table.
  691. //
  692. try
  693. {
  694. LONG lRet;
  695. CBsString cwsKeyName( psSectionDef[ cOptionIdx ].m_pwszKeyName );
  696. //
  697. // Store key names in uppercase
  698. //
  699. lRet = pcOptionsList->Insert( cwsKeyName, pcOptionBase );
  700. if ( lRet == BSHASHMAP_ALREADY_EXISTS )
  701. {
  702. VSTST_ASSERT( "Option name defined twice in definition array" && FALSE );
  703. VSTST_THROW( E_INVALIDARG );
  704. }
  705. }
  706. VSTST_STANDARD_CATCH();
  707. if ( FAILED( hr ) )
  708. {
  709. delete pcOptionBase;
  710. VSTST_THROW( hr );
  711. }
  712. }
  713. return S_OK;
  714. }
  715. #define VSTST_WRAP_WIDTH 97
  716. /*++
  717. Routine Description:
  718. Creates a default INI file that specifies all sections, keys
  719. and default values including comments about what each key
  720. is for.
  721. Arguments:
  722. None
  723. Return Value:
  724. <Enter return values here>
  725. --*/
  726. HRESULT
  727. CVsTstINIConfig::CreateDefaultINIFile()
  728. {
  729. FILE *pfINIFile = NULL;
  730. HRESULT hr = S_OK;
  731. try
  732. {
  733. // Open the INI file
  734. pfINIFile = ::_wfopen( m_wsINIFileName, L"w" );
  735. if ( pfINIFile == NULL )
  736. {
  737. CVsTstINIConfigException cExcept;
  738. cExcept.m_cwsExceptionString.Format( L"Unable to open INI file '%s' for write",
  739. m_wsINIFileName.c_str() );
  740. VSTST_THROW( cExcept );
  741. }
  742. // Write out all known options for all sections.
  743. for ( SIZE_T idx = ( SIZE_T ) eVsTstSectionType_UNKNOWN + 1;
  744. idx < ( ( SIZE_T )eVsTstSectionType_SENTINEL );
  745. ++idx )
  746. {
  747. fwprintf( pfINIFile, L"[%s.%s]\n",
  748. x_sSectionDefArr[ idx ].m_pwszSectionTypeName, x_wszDefaultSectionName );
  749. SVsTstINISectionDef *psSectionDef = x_sSectionDefArr[ idx ].m_psSectionDef;
  750. if ( psSectionDef != NULL )
  751. {
  752. CBsString cwsToBeWrapped;
  753. // Go through each option in the sections.
  754. for ( SIZE_T cSect = 0; psSectionDef[ cSect ].m_pwszKeyName != NULL; ++cSect )
  755. {
  756. if ( psSectionDef[ cSect ].m_eOptionType != eVsTstOptType_Comment )
  757. {
  758. cwsToBeWrapped.Format( L"%s - %s", psSectionDef[ cSect ].m_pwszKeyName,
  759. psSectionDef[ cSect ].m_pwszDescription );
  760. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  761. }
  762. switch ( psSectionDef[ cSect ].m_eOptionType )
  763. {
  764. case eVsTstOptType_Comment:
  765. if ( psSectionDef[ cSect ].m_pwszDescription == NULL )
  766. fwprintf( pfINIFile, L"\n" );
  767. else
  768. {
  769. fwprintf( pfINIFile, L";\n" );
  770. cwsToBeWrapped.Format( L"%s\n", psSectionDef[ cSect ].m_pwszDescription );
  771. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  772. fwprintf( pfINIFile, L";\n" );
  773. }
  774. break;
  775. case eVsTstOptType_String:
  776. if ( psSectionDef[ cSect ].m_sStringDef.m_pwszPossibleValues == NULL )
  777. cwsToBeWrapped.Format( L"Default value: '%s'\n",
  778. psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString );
  779. else
  780. {
  781. CBsString cwsPossibleValuesConverted( psSectionDef[ cSect ].m_sStringDef.m_pwszPossibleValues );
  782. cwsPossibleValuesConverted.Replace( L'|', L',' );
  783. cwsToBeWrapped.Format( L"Default value: '%s', possible values: '%s'\n",
  784. psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString,
  785. cwsPossibleValuesConverted.c_str() );
  786. }
  787. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  788. fwprintf( pfINIFile, L";%s = %s\n\n",
  789. psSectionDef[ cSect ].m_pwszKeyName,
  790. psSectionDef[ cSect ].m_sStringDef.m_pwszDefaultString );
  791. break;
  792. case eVsTstOptType_Boolean:
  793. cwsToBeWrapped.Format( L"Default value: '%s'%s",
  794. x_pwszBooleanValueNames[ psSectionDef[ cSect ].m_sBooleanDef.m_eBoolDefault ],
  795. ( psSectionDef[ cSect ].m_sBooleanDef.m_bCanHaveRandom )
  796. ? L", can have 'Random' value\n" : L"" );
  797. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  798. fwprintf( pfINIFile, L";%s = %s\n\n",
  799. psSectionDef[ cSect ].m_pwszKeyName,
  800. x_pwszBooleanValueNames[ psSectionDef[ cSect ].m_sBooleanDef.m_eBoolDefault ] );
  801. break;
  802. case eVsTstOptType_Number:
  803. {
  804. SVsTstININumberDef *psNumDef = &( psSectionDef[ cSect ].m_sNumberDef );
  805. if ( psNumDef->m_bCanHaveRange )
  806. {
  807. cwsToBeWrapped.Format( L"Default value: %I64d%s%I64d, Min value: %I64d, Max value: %I64d, can be a range\n",
  808. psNumDef->m_llDefaultMinNumber,
  809. x_wszRangeString,
  810. psNumDef->m_llDefaultMaxNumber,
  811. psNumDef->m_llMinNumber,
  812. psNumDef->m_llMaxNumber );
  813. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  814. fwprintf( pfINIFile, L";%s = %I64d%s%I64d\n\n",
  815. psSectionDef[ cSect ].m_pwszKeyName,
  816. psNumDef->m_llDefaultMinNumber,
  817. x_wszRangeString,
  818. psNumDef->m_llDefaultMaxNumber );
  819. }
  820. else
  821. {
  822. cwsToBeWrapped.Format( L"Default value: %I64d, Min value: %I64d, Max value: %I64d\n",
  823. psNumDef->m_llDefaultMinNumber,
  824. psNumDef->m_llMinNumber,
  825. psNumDef->m_llMaxNumber );
  826. ::pVsTstWrapOutput( pfINIFile, L"; ", cwsToBeWrapped, VSTST_WRAP_WIDTH );
  827. fwprintf( pfINIFile, L";%s = %I64d\n\n",
  828. psSectionDef[ cSect ].m_pwszKeyName,
  829. psNumDef->m_llDefaultMinNumber );
  830. }
  831. }
  832. break;
  833. }
  834. }
  835. }
  836. fwprintf( pfINIFile, L"; ==================================================================\n" );
  837. }
  838. }
  839. VSTST_STANDARD_CATCH();
  840. if ( pfINIFile != NULL )
  841. ::fclose( pfINIFile );
  842. return S_OK;
  843. }
  844. // Gets a string value
  845. VOID
  846. CVsTstINIConfig::GetOptionValue(
  847. IN LPCWSTR pwszOptionName,
  848. OUT CBsString *pwsOptionValue,
  849. OUT BOOL *pbOverridden
  850. )
  851. {
  852. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList;
  853. CVsTstSectionOptionBase *pcSectionOptionBase;
  854. if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) )
  855. {
  856. if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_String )
  857. {
  858. VSTST_ASSERT( FALSE );
  859. VSTST_THROW( E_INVALIDARG );
  860. }
  861. CVsTstSectionOptionString *pcOptionString;
  862. pcOptionString = ( CVsTstSectionOptionString * )pcSectionOptionBase;
  863. *pwsOptionValue = pcOptionString->GetValue();
  864. if ( pbOverridden != NULL )
  865. *pbOverridden = pcOptionString->IsDefaultOverridden();
  866. return;
  867. }
  868. VSTST_THROW( E_INVALIDARG );
  869. }
  870. // Gets a boolean value
  871. VOID
  872. CVsTstINIConfig::GetOptionValue(
  873. IN LPCWSTR pwszOptionName,
  874. OUT EVsTstINIBoolType *peOptionValue,
  875. OUT BOOL *pbOverridden
  876. )
  877. {
  878. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList;
  879. CVsTstSectionOptionBase *pcSectionOptionBase;
  880. if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) )
  881. {
  882. if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_Boolean )
  883. {
  884. VSTST_ASSERT( FALSE );
  885. VSTST_THROW( E_INVALIDARG );
  886. }
  887. CVsTstSectionOptionBoolean *pcOptionBoolean;
  888. pcOptionBoolean = ( CVsTstSectionOptionBoolean * )pcSectionOptionBase;
  889. *peOptionValue = pcOptionBoolean->GetValue();
  890. if ( pbOverridden != NULL )
  891. *pbOverridden = pcOptionBoolean->IsDefaultOverridden();
  892. return;
  893. }
  894. VSTST_THROW( E_INVALIDARG );
  895. }
  896. // Get a number value
  897. VOID
  898. CVsTstINIConfig::GetOptionValue(
  899. IN LPCWSTR pwszOptionName,
  900. OUT LONGLONG *pllOptionMinValue,
  901. OUT LONGLONG *pllOptionMaxValue,
  902. OUT BOOL *pbOverridden
  903. )
  904. {
  905. CVsTstOptionsList *pcOptionsList = ( CVsTstOptionsList * ) m_pvOptionsList;
  906. CVsTstSectionOptionBase *pcSectionOptionBase;
  907. if ( pcOptionsList->Find( pwszOptionName, &pcSectionOptionBase ) )
  908. {
  909. if ( pcSectionOptionBase->GetOptionType() != eVsTstOptType_Number )
  910. {
  911. VSTST_ASSERT( FALSE );
  912. VSTST_THROW( E_INVALIDARG );
  913. }
  914. CVsTstSectionOptionNumber *pcOptionNumber;
  915. pcOptionNumber = ( CVsTstSectionOptionNumber * )pcSectionOptionBase;
  916. BOOL bHasRange;
  917. pcOptionNumber->GetValue( pllOptionMinValue, pllOptionMaxValue, &bHasRange );
  918. if ( pbOverridden != NULL )
  919. *pbOverridden = pcOptionNumber->IsDefaultOverridden();
  920. return;
  921. }
  922. VSTST_THROW( E_INVALIDARG );
  923. }
  924. static VOID
  925. pVsTstWrapOutput(
  926. IN FILE *pfOut,
  927. IN LPCWSTR pwszBeginString,
  928. IN CBsString& cwsToBeWrapped,
  929. IN SIZE_T cWrapWidth
  930. )
  931. {
  932. cwsToBeWrapped.TrimLeft();
  933. cwsToBeWrapped.TrimRight();
  934. LPWSTR pwszCurrPosition = cwsToBeWrapped.GetBuffer( cwsToBeWrapped.GetLength() );
  935. LPWSTR pwszNextLine = NULL;
  936. LPWSTR pwszSpaces = L"";
  937. while( *pwszCurrPosition != L'\0' )
  938. {
  939. SIZE_T cLen;
  940. cLen = ::wcslen( pwszCurrPosition );
  941. //
  942. // Get rid of the easy case
  943. //
  944. if ( cLen <= cWrapWidth )
  945. {
  946. fwprintf( pfOut, L"%s%s%s\n", pwszBeginString, pwszSpaces, pwszCurrPosition );
  947. break;
  948. }
  949. pwszNextLine = pwszCurrPosition + cWrapWidth - ::wcslen( pwszSpaces );
  950. while ( pwszNextLine > pwszCurrPosition )
  951. {
  952. if ( *pwszNextLine == L' ' )
  953. break;
  954. --pwszNextLine;
  955. }
  956. if ( pwszNextLine == pwszCurrPosition )
  957. {
  958. //
  959. // No spaces within margin, move forward to first space.
  960. //
  961. pwszNextLine = ::wcschr( pwszCurrPosition, L' ' );
  962. //
  963. // If pwszNextLine is NULL, then it means the entire rest of the line has no spaces
  964. //
  965. if ( pwszNextLine != NULL )
  966. *pwszNextLine = '\0';
  967. }
  968. else
  969. {
  970. *pwszNextLine = '\0';
  971. }
  972. fwprintf( pfOut, L"%s%s%s\n", pwszBeginString, pwszSpaces, pwszCurrPosition );
  973. //
  974. // If special case where pwszNextLine is NULL, we are done
  975. //
  976. if ( pwszNextLine == NULL )
  977. break;
  978. pwszCurrPosition = pwszNextLine + 1;
  979. pwszSpaces = L" ";
  980. }
  981. cwsToBeWrapped.ReleaseBuffer();
  982. }