// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// EEConfigFactory.cpp
//
//
// Factory used to with the XML parser to read configuration files
//
#include "common.h"
#include "ngenoptout.h"
#include "eeconfigfactory.h"
#define ISWHITE(ch) ((ch) >= 0x09 && (ch) <= 0x0D || (ch) == 0x20)
#define CONST_STRING_AND_LEN(str) str, NumItems(str)-1
int EEXMLStringCompare(const WCHAR *pStr1,
DWORD cchStr1,
const WCHAR *pStr2,
DWORD cchStr2)
{
LIMITED_METHOD_CONTRACT;
if (cchStr1 != cchStr2)
return -1;
return wcsncmp(pStr1, pStr2, cchStr1);
}// EEXMLStringCompare
int EEXMLStringComparei(const WCHAR *pStr1,
DWORD cchStr1,
const WCHAR *pStr2,
DWORD cchStr2)
{
WRAPPER_NO_CONTRACT;
if (cchStr1 != cchStr2)
return -1;
return SString::_wcsnicmp(pStr1, pStr2, cchStr1);
}// EEXMLStringCompare
EEConfigFactory::EEConfigFactory(
ConfigStringHashtable* pTable,
LPCWSTR pString,
ParseCtl parseCtl)
{
LIMITED_METHOD_CONTRACT;
m_pTable = pTable;
m_pVersion = pString;
m_dwDepth = 0;
m_fUnderRuntimeElement = FALSE;
m_fDeveloperSettings = FALSE;
m_fVersionedRuntime= FALSE;
m_fOnEnabledAttribute = FALSE;
m_fOnValueAttribute = FALSE;
m_pCurrentRuntimeElement = m_pBuffer;
m_dwCurrentRuntimeElement = 0;
m_dwSize = CONFIG_KEY_SIZE;
m_parseCtl = parseCtl;
m_pActiveFactory = NULL;
}
EEConfigFactory::~EEConfigFactory()
{
LIMITED_METHOD_CONTRACT;
DeleteKey();
}
HRESULT STDMETHODCALLTYPE EEConfigFactory::NotifyEvent(
/* [in] */ IXMLNodeSource __RPC_FAR *pSource,
/* [in] */ XML_NODEFACTORY_EVENT iEvt)
{
LIMITED_METHOD_CONTRACT;
if(iEvt == XMLNF_ENDDOCUMENT) {
// add error handling.
}
if(m_pActiveFactory != NULL)
return m_pActiveFactory->NotifyEvent(pSource, iEvt);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE EEConfigFactory::BeginChildren(
/* [in] */ IXMLNodeSource __RPC_FAR *pSource,
/* [in] */ XML_NODE_INFO __RPC_FAR *pNodeInfo)
{
LIMITED_METHOD_CONTRACT;
m_dwDepth++;
if(m_pActiveFactory != NULL)
return m_pActiveFactory->BeginChildren(pSource, pNodeInfo);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE EEConfigFactory::EndChildren(
/* [in] */ IXMLNodeSource __RPC_FAR *pSource,
/* [in] */ BOOL fEmptyNode,
/* [in] */ XML_NODE_INFO __RPC_FAR *pNodeInfo)
{
LIMITED_METHOD_CONTRACT;
if ( fEmptyNode ) {
m_fDeveloperSettings = FALSE;
}
else {
m_dwDepth--;
}
if (m_pActiveFactory != NULL)
{
HRESULT hr = S_OK;
IfFailRet(m_pActiveFactory->EndChildren(pSource, fEmptyNode, pNodeInfo));
if(m_dwDepth == 2) // when generalizing: use the current active factory depth
{
m_pActiveFactory = NULL;
}
}
if (m_fUnderRuntimeElement && wcscmp(pNodeInfo->pwcText, W("runtime")) == 0) {
m_fUnderRuntimeElement = FALSE;
m_fVersionedRuntime = FALSE;
ClearKey();
// CLR_STARTUP_OPT:
// Early out if we only need to read section.
//
if (m_parseCtl == stopAfterRuntimeSection)
pSource->Abort(NULL/*unused*/);
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE EEConfigFactory::CreateNode(
/* [in] */ IXMLNodeSource __RPC_FAR *pSource,
/* [in] */ PVOID pNode,
/* [in] */ USHORT cNumRecs,
/* [in] */ XML_NODE_INFO* __RPC_FAR * __RPC_FAR apNodeInfo)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
INJECT_FAULT(return E_OUTOFMEMORY;);
}
CONTRACTL_END;
if(m_pActiveFactory != NULL)
return m_pActiveFactory->CreateNode(pSource, pNode, cNumRecs, apNodeInfo);
if(m_dwDepth > 3)
{
return S_OK;
}
HRESULT hr = S_OK;
DWORD dwStringSize = 0;
WCHAR* pszString = NULL;
DWORD i;
BOOL fRuntimeKey = FALSE;
BOOL fVersion = FALSE;
for( i = 0; i < cNumRecs; i++) {
CONTRACT_VIOLATION(ThrowsViolation); // Lots of stuff in here throws!
if(apNodeInfo[i]->dwType == XML_ELEMENT ||
apNodeInfo[i]->dwType == XML_ATTRIBUTE ||
apNodeInfo[i]->dwType == XML_PCDATA) {
dwStringSize = apNodeInfo[i]->ulLen;
pszString = (WCHAR*) apNodeInfo[i]->pwcText;
// Trim the value
// we should never decrement lgth if it's 0, because it's unsigned
for(;*pszString && ISWHITE(*pszString) && dwStringSize>0; pszString++, dwStringSize--);
while( dwStringSize > 0 && ISWHITE(pszString[dwStringSize-1]))
dwStringSize--;
// NOTE: pszString is not guaranteed to be null terminated. Use EEXMLStringCompare to do
// string comparisions on it
switch(apNodeInfo[i]->dwType) {
case XML_ELEMENT :
fRuntimeKey = FALSE;
ClearKey();
if (m_dwDepth == 1 && EEXMLStringCompare(pszString, dwStringSize, CONST_STRING_AND_LEN(W("runtime"))) == 0) {
m_fUnderRuntimeElement = TRUE;
fRuntimeKey = TRUE;
}
if(m_dwDepth == 2 && m_fUnderRuntimeElement) {
// Developer settings can look like
//
//
//
// or
//
//
//
// Neither one is your standard config setting.
if (!EEXMLStringCompare(pszString, dwStringSize, CONST_STRING_AND_LEN(W("developerSettings"))) ||
!EEXMLStringCompare(pszString, dwStringSize, CONST_STRING_AND_LEN(W("developmentMode"))))
{
m_fDeveloperSettings = TRUE;
}
else
// when generalizing: use map of (string, depth) -> class
if (!EEXMLStringCompare(pszString, dwStringSize, CONST_STRING_AND_LEN(W("disableNativeImageLoad"))))
{
m_pActiveFactory = new NativeImageOptOutConfigFactory();
m_pActiveFactory->AddRef();
}
else
{
// This is a standard element under the runtime node.... it could look like this
//
//
hr = CopyToKey(pszString, dwStringSize);
if(FAILED(hr)) return hr;
}
}
// If our depth isn't 2, and we're not under the runtime element....
else
ClearKey();
break ;
case XML_ATTRIBUTE :
if(fRuntimeKey && EEXMLStringCompare(pszString, dwStringSize, CONST_STRING_AND_LEN(W("version"))) == 0) {
fVersion = TRUE;
}
else
{
if (m_dwDepth == 2 && m_fUnderRuntimeElement)
{
if (!m_fDeveloperSettings)
{
_ASSERTE(m_dwCurrentRuntimeElement > 0);
// The standard model for runtime config settings is as follows
//
//
//
// or
//
// or
//
m_fOnEnabledAttribute = (EEXMLStringComparei(pszString, dwStringSize, CONST_STRING_AND_LEN(W("enabled"))) == 0);
m_fOnValueAttribute = (EEXMLStringComparei(pszString, dwStringSize, CONST_STRING_AND_LEN(W("value"))) == 0);
}
else // We're looking at developer settings
{
// Developer settings look like
//
//
// or
//
//
//
// The key name will actually be the attribute name
hr = CopyToKey(pszString, dwStringSize);
if(FAILED(hr)) return hr;
m_fOnEnabledAttribute = FALSE;
m_fOnValueAttribute = FALSE;
}
}
}
break;
case XML_PCDATA:
if(fVersion) {
// if this is not the right version
// then we are not interested
if(EEXMLStringCompare(pszString, dwStringSize, m_pVersion, (DWORD)wcslen(m_pVersion))) {
m_fUnderRuntimeElement = FALSE;
}
else {
// if it is the right version then overwrite
// all entries that exist in the hash table
m_fVersionedRuntime = TRUE;
}
fVersion = FALSE;
}
else if(fRuntimeKey) {
break; // Ignore all other attributes on
}
// m_dwCurrentRuntimeElement is set when we called CopyToKey in the XML_ELEMENT case
// section above.
else if(m_dwCurrentRuntimeElement > 0 && (m_fDeveloperSettings || m_fOnEnabledAttribute || m_fOnValueAttribute)) {
// This means that, either we are working on attribute values for the developer settings,
// or we've got what "enabled" is equal to, or we're reading a string for a value setting.
//
//
//
if (m_fOnEnabledAttribute) {
// For the enabled settings, let's convert all trues to 1s and the falses to 0s
if (EEXMLStringComparei(pszString, dwStringSize, CONST_STRING_AND_LEN(W("false"))) == 0) {
pszString = W("0");
dwStringSize = 1;
}
else if (EEXMLStringComparei(pszString, dwStringSize, CONST_STRING_AND_LEN(W("true"))) == 0) {
pszString = W("1");
dwStringSize = 1;
}
// Right now, if pString isn't 0 or 1, then the XML schema is bad.
// If we were to ever do schema validation, this would be a place to put it.
//
}
hr = AddKeyValuePair(pszString, dwStringSize, m_pCurrentRuntimeElement, m_dwCurrentRuntimeElement);
if(FAILED(hr)) { return hr; }
}
break ;
default:
;
} // end of switch
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE EEConfigFactory::AddKeyValuePair(
__in_ecount(dwStringSize) WCHAR * pszString,
/* [in] */ DWORD dwStringSize,
__in_ecount(m_dwCurrentRuntimeElement) WCHAR * m_pCurrentRuntimeElement,
/* [in] */ DWORD m_dwCurrentRuntimeElement
)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
INJECT_FAULT(return E_OUTOFMEMORY;);
}
CONTRACTL_END;
HRESULT hr = S_OK;
// verify we the size fields don't overflow
if (dwStringSize + 1 < dwStringSize) { return E_FAIL; }
if (m_dwCurrentRuntimeElement < m_dwCurrentRuntimeElement - 1) { return E_FAIL; }
EX_TRY
{
// Allocate memory that can store this setting
NewArrayHolder pStringToKeep(new WCHAR[dwStringSize+1]);
wcsncpy_s(pStringToKeep, dwStringSize + 1, pszString, dwStringSize);
// See if we've already picked up a value for this setting
ConfigStringKeyValuePair * pair = m_pTable->Lookup(m_pCurrentRuntimeElement);
if(pair != NULL) {
// If this is a config section for this runtime version, then it's allowed to overwrite
// previous settings that we've picked up
if(m_fVersionedRuntime) {
delete[] pair->value;
pair->value = pStringToKeep;
pStringToKeep.SuppressRelease();
}
}
else {
// We're adding a new config item
NewArrayHolder pKeyToKeep (new WCHAR[m_dwCurrentRuntimeElement]);
wcsncpy_s(pKeyToKeep, m_dwCurrentRuntimeElement, m_pCurrentRuntimeElement, m_dwCurrentRuntimeElement - 1);
ConfigStringKeyValuePair * newPair = new ConfigStringKeyValuePair();
newPair->key = pKeyToKeep;
newPair->value = pStringToKeep;
m_pTable->Add(newPair);
pKeyToKeep.SuppressRelease();
pStringToKeep.SuppressRelease();
}
}
EX_CATCH_HRESULT(hr);
return hr;
}