1450 lines
40 KiB
C++
1450 lines
40 KiB
C++
|
/////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// fusion\xmlparser\xmlparser.cxx
|
||
|
// just commend "SysFreeString" and SysAllocString()
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "stdinc.h"
|
||
|
#include "core.hxx"
|
||
|
#include "xmlhelper.hxx"
|
||
|
|
||
|
#pragma hdrstop
|
||
|
#include "xmlparser.hxx"
|
||
|
#include "xmlstream.hxx"
|
||
|
|
||
|
#include <objbase.h>
|
||
|
|
||
|
#define CRITICALSECTIONLOCK CSLock lock(&_cs);
|
||
|
const USHORT STACK_INCREMENT=10;
|
||
|
|
||
|
#define PUSHNODEINFO(pNodeInfo)\
|
||
|
if (_cNodeInfoAllocated == _cNodeInfoCurrent)\
|
||
|
{\
|
||
|
checkhr2(GrowNodeInfo());\
|
||
|
}\
|
||
|
_paNodeInfo[_cNodeInfoCurrent++] = _pCurrent;
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
class CSLock
|
||
|
{
|
||
|
public:
|
||
|
CSLock(CRITICAL_SECTION * pcs);
|
||
|
~CSLock();
|
||
|
|
||
|
private:
|
||
|
CRITICAL_SECTION * _pcs;
|
||
|
};
|
||
|
|
||
|
CSLock::CSLock(CRITICAL_SECTION * pcs){
|
||
|
_pcs = pcs;
|
||
|
::EnterCriticalSection(pcs);
|
||
|
}
|
||
|
CSLock::~CSLock(){
|
||
|
::LeaveCriticalSection(_pcs);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
XMLParser::XMLParser()
|
||
|
: _pDownloads(1), _pStack(STACK_INCREMENT)
|
||
|
{
|
||
|
ctorInit();
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
XMLParser::ctorInit()
|
||
|
{
|
||
|
InitializeCriticalSection(&_cs);
|
||
|
|
||
|
_pTokenizer = NULL;
|
||
|
_pCurrent = NULL;
|
||
|
_lCurrentElement = 0;
|
||
|
_paNodeInfo = NULL;
|
||
|
_cNodeInfoAllocated = _cNodeInfoCurrent = 0;
|
||
|
_pdc = NULL;
|
||
|
_usFlags = 0;
|
||
|
_fCaseInsensitive = false;
|
||
|
_bstrError = NULL;
|
||
|
// _fTokenizerChanged = false;
|
||
|
_fRunEntryCount = 0;
|
||
|
_pszSecureBaseURL = NULL;
|
||
|
_pszCurrentURL = NULL;
|
||
|
_pszBaseURL = NULL;
|
||
|
//_fInLoading = false;
|
||
|
_fInsideRun = false;
|
||
|
//_fFoundDTDAttribute = false;
|
||
|
_cAttributes = 0;
|
||
|
_pRoot = NULL;
|
||
|
//_fAttemptedURL = NULL;
|
||
|
_fLastError = S_OK;
|
||
|
_fStopped = false;
|
||
|
_fSuspended = false;
|
||
|
_fStarted = false;
|
||
|
_fWaiting = false;
|
||
|
_fIgnoreEncodingAttr = false;
|
||
|
_dwSafetyOptions = 0;
|
||
|
|
||
|
// rest of initialization done in the init() method.
|
||
|
|
||
|
//EnableTag(tagParserCallback, TRUE);
|
||
|
//EnableTag(tagParserError, TRUE);
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
XMLParser::~XMLParser()
|
||
|
{
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
Reset();
|
||
|
|
||
|
// Cleanup tagname buffers in context for good this time...
|
||
|
for (long i = _pStack.size()-1; i>=0; i--)
|
||
|
{
|
||
|
MY_XML_NODE_INFO* pNodeInfo = _pStack[i];
|
||
|
if (pNodeInfo->_pwcTagName != NULL)
|
||
|
{
|
||
|
delete [] pNodeInfo->_pwcTagName;
|
||
|
pNodeInfo->_pwcTagName = NULL;
|
||
|
pNodeInfo->_ulBufLen = 0;
|
||
|
}
|
||
|
// NULL out the node pointer in case it point's to a GC'd object :-)
|
||
|
pNodeInfo->pNode = NULL;
|
||
|
}
|
||
|
delete _pszSecureBaseURL;
|
||
|
delete _pszCurrentURL;
|
||
|
|
||
|
delete[] _paNodeInfo;
|
||
|
}
|
||
|
DeleteCriticalSection(&_cs);
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::QueryInterface(REFIID riid, void ** ppvObject)
|
||
|
{
|
||
|
//STACK_ENTRY; // xiaoyu : what it for?
|
||
|
|
||
|
// Since this one class implements both IXMLNodeSource and
|
||
|
// IXMLParser, we must override QueryInterface since the
|
||
|
// IUnknown template doesn't know about the IXMLNodeSource
|
||
|
// interface.
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
if (riid == IID_IXMLNodeSource || riid == IID_Parser)
|
||
|
{
|
||
|
*ppvObject = static_cast<IXMLNodeSource*>(this);
|
||
|
AddRef();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = _unknown<IXMLParser, &IID_IXMLParser>::QueryInterface(riid, ppvObject);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
ULONG STDMETHODCALLTYPE
|
||
|
XMLParser::AddRef(void)
|
||
|
{
|
||
|
//STACK_ENTRY;
|
||
|
return _unknown<IXMLParser, &IID_IXMLParser>::AddRef();
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
ULONG STDMETHODCALLTYPE
|
||
|
XMLParser::Release(void)
|
||
|
{
|
||
|
// STACK_ENTRY;
|
||
|
return _unknown<IXMLParser, &IID_IXMLParser>::Release();
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::SetInput(IUnknown *pStm)
|
||
|
{
|
||
|
if (pStm == NULL)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
//STACK_ENTRY_MODEL(_reThreadModel);
|
||
|
CRITICALSECTIONLOCK;
|
||
|
if (_pDownloads.used() == 0)
|
||
|
init();
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//checkhr2(PushTokenizer(NULL));
|
||
|
checkhr2(PushTokenizer());
|
||
|
|
||
|
// Get the url path
|
||
|
// Continue even if we cannot get it
|
||
|
// STATSTG stat;
|
||
|
IStream * pStream = NULL;
|
||
|
// memset(&stat, 0, sizeof(stat));
|
||
|
hr = pStm->QueryInterface(IID_IStream, (void**)&pStream);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = PushStream(pStream, false);
|
||
|
pStream->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::PushData(
|
||
|
/* [in] */ const char __RPC_FAR *pData,
|
||
|
/* [in] */ ULONG ulChars,
|
||
|
/* [in] */ BOOL fLastBuffer)
|
||
|
{
|
||
|
//STACK_ENTRY_MODEL(_reThreadModel);
|
||
|
CRITICALSECTIONLOCK;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if ((NULL == pData) && (ulChars != 0))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
if (_pTokenizer == NULL)
|
||
|
{
|
||
|
init();
|
||
|
//checkhr2(PushTokenizer(NULL));
|
||
|
checkhr2(PushTokenizer());
|
||
|
}
|
||
|
return _pTokenizer->AppendData((const BYTE*)pData, ulChars, fLastBuffer);
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::SetFactory(IXMLNodeFactory __RPC_FAR *pNodeFactory)
|
||
|
{
|
||
|
//STACK_ENTRY;
|
||
|
|
||
|
CRITICALSECTIONLOCK;
|
||
|
_pFactory = pNodeFactory;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::GetFactory(IXMLNodeFactory** ppNodeFactory)
|
||
|
{
|
||
|
if (ppNodeFactory == NULL) return E_INVALIDARG;
|
||
|
if (_pFactory)
|
||
|
{
|
||
|
*ppNodeFactory = _pFactory;
|
||
|
(*ppNodeFactory)->AddRef();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppNodeFactory = NULL;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::Run(long lChars)
|
||
|
{
|
||
|
HRESULT hr = NOERROR;
|
||
|
|
||
|
FN_TRACE_HR(hr);
|
||
|
|
||
|
//STACK_ENTRY_MODEL(_reThreadModel);
|
||
|
CRITICALSECTIONLOCK;
|
||
|
|
||
|
XML_NODE_INFO info;
|
||
|
XML_NODE_INFO* aNodeInfo[1];
|
||
|
|
||
|
USHORT numRecs;
|
||
|
|
||
|
bool fIsAttribute = false;
|
||
|
bool stop = false;
|
||
|
|
||
|
if (_fSuspended)
|
||
|
_fSuspended = FALSE; // caller must want to resume.
|
||
|
|
||
|
if (_pFactory == NULL)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XMLParser::Run() failing because _pFactory is NULL\n");
|
||
|
|
||
|
hr = E_FAIL;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (_fStopped)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XMLParser::Run() failing because _fStopped is set\n");
|
||
|
|
||
|
hr = XML_E_STOPPED;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (_pTokenizer == NULL)
|
||
|
{
|
||
|
if (_fLastError != S_OK)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XMLParser::Run() failing because _pTokenizer == NULL and _fLastError != S_OK (== 0x%08lx)\n", _fLastError);
|
||
|
|
||
|
hr = _fLastError;
|
||
|
goto Exit;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XMLParser::Run() failing because _pTokenizer == NULL and _fLastError == S_OK\n");
|
||
|
|
||
|
// must be _fStarted == false
|
||
|
hr = XMLPARSER_IDLE;
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for recurrsive entry and whether caller actually
|
||
|
// wants anything parsed.
|
||
|
if (_fInsideRun || lChars == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XMLParser::Run() failing because _fInsideRun is set or lChars == 0\n");
|
||
|
|
||
|
hr = E_PENDING;
|
||
|
goto Exit;
|
||
|
}
|
||
|
{
|
||
|
BoolLock flock(&_fInsideRun);
|
||
|
|
||
|
if (_fLastError != 0)
|
||
|
{
|
||
|
// one more chance to cleanup the parser stack.
|
||
|
hr = _fLastError;
|
||
|
goto cleanup_stack;
|
||
|
}
|
||
|
|
||
|
if (! _fStarted)
|
||
|
{
|
||
|
_fStarted = true;
|
||
|
hr = _pFactory->NotifyEvent(this, XMLNF_STARTDOCUMENT);
|
||
|
if (_fStopped){ // watch for onReadyStateChange handlers
|
||
|
hr = S_OK; // fussing with the parser state.
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_fWaiting = false;
|
||
|
if (_fPendingBeginChildren)
|
||
|
{
|
||
|
_fPendingBeginChildren = false;
|
||
|
hr = _pFactory->BeginChildren(this, (XML_NODE_INFO*)_pCurrent);
|
||
|
}
|
||
|
if (_fPendingEndChildren)
|
||
|
{
|
||
|
_fPendingEndChildren = false;
|
||
|
hr = _pFactory->EndChildren(this, TRUE, (XML_NODE_INFO*)_pCurrent);
|
||
|
if (FAILED(hr))
|
||
|
hr = pop(); // no match needed
|
||
|
}
|
||
|
|
||
|
info.dwSize = sizeof(XML_NODE_INFO);
|
||
|
info.dwType = XMLStream::XML_PENDING;
|
||
|
info.dwSubType = 0;
|
||
|
info.pwcText = NULL;
|
||
|
info.ulLen = 0;
|
||
|
info.ulNsPrefixLen = 0;
|
||
|
info.pNode = NULL;
|
||
|
info.pReserved = NULL;
|
||
|
aNodeInfo[0] = &info;
|
||
|
|
||
|
more:
|
||
|
_fRunEntryCount++; // count of callers inside this loop...
|
||
|
|
||
|
while (hr == 0 && ! _fSuspended)
|
||
|
{
|
||
|
info.dwSubType = 0;
|
||
|
|
||
|
// The XMLStream error codes have been aligned with the
|
||
|
// XMLParser error code so no mapping is necessary.
|
||
|
hr = _pTokenizer->GetNextToken(&info.dwType, (const WCHAR **)&info.pwcText, (long*)&info.ulLen, (long*)&info.ulNsPrefixLen);
|
||
|
if (hr == E_PENDING)
|
||
|
{
|
||
|
_fWaiting = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (! _fFoundNonWS &&
|
||
|
info.dwType != XMLStream::XML_PENDING &&
|
||
|
info.dwType != XML_WHITESPACE &&
|
||
|
info.dwType != XML_XMLDECL)
|
||
|
{
|
||
|
_fFoundNonWS = true;
|
||
|
}
|
||
|
|
||
|
// Now the NodeType is the same as the XMLToken value. We set
|
||
|
// this up by aligning the two enums.
|
||
|
switch (info.dwType)
|
||
|
{
|
||
|
case 0:
|
||
|
if (hr == XML_E_INVALIDSWITCH && _fIgnoreEncodingAttr)
|
||
|
{
|
||
|
hr = 0; // ignore it and continue on.
|
||
|
}
|
||
|
break;
|
||
|
// --------- Container Nodes -------------------
|
||
|
case XML_XMLDECL:
|
||
|
//if (_fFoundNonWS && ! _fIE4Mode) // IE4 allowed this...
|
||
|
if (_fFoundNonWS)
|
||
|
{
|
||
|
hr = XML_E_BADXMLDECL;
|
||
|
break;
|
||
|
}
|
||
|
// _fFoundNonWS = true;
|
||
|
goto containers;
|
||
|
|
||
|
case XML_ATTRIBUTE:
|
||
|
fIsAttribute = true;
|
||
|
goto containers;
|
||
|
|
||
|
case XML_VERSION:
|
||
|
info.dwSubType = info.dwType;
|
||
|
info.dwType = XML_ATTRIBUTE;
|
||
|
_fGotVersion = true;
|
||
|
fIsAttribute = true;
|
||
|
goto containers;
|
||
|
|
||
|
case XML_STANDALONE:
|
||
|
case XML_ENCODING:
|
||
|
if (! _fGotVersion && _pDownloads.used() == 1)
|
||
|
{
|
||
|
hr = XML_E_EXPECTING_VERSION;
|
||
|
break;
|
||
|
}
|
||
|
if (info.dwType == XML_STANDALONE)
|
||
|
{
|
||
|
if (_pDownloads.used() > 1)
|
||
|
{
|
||
|
hr = XML_E_UNEXPECTED_STANDALONE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
info.dwSubType = info.dwType;
|
||
|
info.dwType = XML_ATTRIBUTE;
|
||
|
fIsAttribute = true;
|
||
|
goto containers;
|
||
|
// fall through
|
||
|
case XML_ELEMENT:
|
||
|
containers:
|
||
|
if (_fRootLevel)
|
||
|
{
|
||
|
// Special rules apply for root level tags.
|
||
|
if (info.dwType == XML_ELEMENT)
|
||
|
{
|
||
|
// This is a root level element.
|
||
|
if (! _fFoundRoot)
|
||
|
{
|
||
|
_fFoundRoot = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser has found multiple roots in the document which is an error.\n");
|
||
|
|
||
|
hr = XML_E_MULTIPLEROOTS;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if (info.dwType != XML_PI &&
|
||
|
info.dwType != XML_XMLDECL &&
|
||
|
info.dwType != XML_DOCTYPE)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser has found an initial element which is not valid at the root level.\n");
|
||
|
|
||
|
hr = XML_E_INVALIDATROOTLEVEL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
info.fTerminal = FALSE;
|
||
|
|
||
|
if (fIsAttribute)
|
||
|
{
|
||
|
breakhr( pushAttribute(info));
|
||
|
fIsAttribute = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
breakhr( push(info));
|
||
|
}
|
||
|
break;
|
||
|
case XML_PCDATA:
|
||
|
case XML_CDATA:
|
||
|
terminals:
|
||
|
// Special rules apply for root level tags.
|
||
|
if (_fRootLevel)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser has found PCDATA at the root level which is not valid XML.\n");
|
||
|
|
||
|
hr = XML_E_INVALIDATROOTLEVEL;
|
||
|
break;
|
||
|
}
|
||
|
// fall through
|
||
|
case XML_COMMENT:
|
||
|
case XML_WHITESPACE:
|
||
|
tcreatenode:
|
||
|
info.fTerminal = TRUE;
|
||
|
if (_cAttributes != 0)
|
||
|
{
|
||
|
// We are inside the attribute list, so we need to push this.
|
||
|
hr = pushAttributeValue(info);
|
||
|
break;
|
||
|
}
|
||
|
hr = _pFactory->CreateNode(this, _pNode, 1, aNodeInfo);
|
||
|
info.pNode = NULL;
|
||
|
break;
|
||
|
|
||
|
case XML_ENTITYREF:
|
||
|
if (_fRootLevel)
|
||
|
{
|
||
|
hr = XML_E_INVALIDATROOTLEVEL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// We handle builtin entities and char entities in xmlstream
|
||
|
// so these must be user defined entity, so treat it like a regular terminal node.
|
||
|
goto terminals;
|
||
|
break;
|
||
|
|
||
|
case XMLStream::XML_BUILTINENTITYREF:
|
||
|
case XMLStream::XML_HEXENTITYREF:
|
||
|
case XMLStream::XML_NUMENTITYREF:
|
||
|
// pass real entityref type as subtype so we can publish these
|
||
|
// subtypes eventually.
|
||
|
info.dwSubType = info.dwType; // XML_ENTITYREF;
|
||
|
info.dwType = XML_PCDATA;
|
||
|
|
||
|
if (_cAttributes == 0)
|
||
|
{
|
||
|
goto tcreatenode;
|
||
|
}
|
||
|
|
||
|
// We are inside the attribute list, so we need to push this.
|
||
|
info.fTerminal = TRUE;
|
||
|
hr = pushAttributeValue(info);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CopyText(_pCurrent);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case XMLStream::XML_TAGEND: // ">"
|
||
|
numRecs = 1+_cAttributes;
|
||
|
if (_cAttributes != 0) // this is safe because _rawstack does NOT reclaim
|
||
|
{ // the popped stack entries.
|
||
|
popAttributes();
|
||
|
}
|
||
|
hr = _pFactory->CreateNode(this, _pNode, numRecs, (XML_NODE_INFO **)&_paNodeInfo[_lCurrentElement]);
|
||
|
_pNode = _pCurrent->pNode;
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
_fPendingBeginChildren = true;
|
||
|
break;
|
||
|
}
|
||
|
breakhr( _pFactory->BeginChildren(this, (XML_NODE_INFO*)_pCurrent));
|
||
|
break;
|
||
|
|
||
|
// The ENDXMLDECL is like EMPTYENDTAGs since we've been
|
||
|
// buffering up their attributes, and we have still got to call CreateNode.
|
||
|
case XMLStream::XML_ENDXMLDECL:
|
||
|
_fGotVersion = false; // reset back to initial state.
|
||
|
// fall through.
|
||
|
case XMLStream::XML_EMPTYTAGEND:
|
||
|
numRecs = 1+_cAttributes;
|
||
|
if (_cAttributes != 0)
|
||
|
{
|
||
|
popAttributes();
|
||
|
}
|
||
|
hr = _pFactory->CreateNode(this, _pNode, numRecs, (XML_NODE_INFO **)&_paNodeInfo[_lCurrentElement]);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
_fPendingEndChildren = true;
|
||
|
break;
|
||
|
}
|
||
|
breakhr(_pFactory->EndChildren(this, TRUE, (XML_NODE_INFO*)_pCurrent));
|
||
|
breakhr(pop()); // no match needed
|
||
|
break;
|
||
|
|
||
|
case XMLStream::XML_ENDTAG: // "</"
|
||
|
if (_pStack.used() == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser has found an unexpected end tag.\n");
|
||
|
|
||
|
hr = XML_E_UNEXPECTEDENDTAG;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
XML_NODE_INFO* pCurrent = (XML_NODE_INFO*)_pCurrent; // save current record
|
||
|
breakhr(pop(info.pwcText, info.ulLen)); // check tag/match
|
||
|
breakhr(_pFactory->EndChildren(this, FALSE, (XML_NODE_INFO*)pCurrent));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case XMLStream::XML_ENDPROLOG:
|
||
|
// For top level document only, (not for DTD's or
|
||
|
// entities), call EndProlog on the node factory.
|
||
|
if (_fRootLevel && ! _pdc->_fEntity && ! _pdc->_fDTD)
|
||
|
breakhr( _pFactory->NotifyEvent(this, XMLNF_ENDPROLOG));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
hr = E_FAIL;
|
||
|
break; // break from switch()
|
||
|
}
|
||
|
}
|
||
|
_fRunEntryCount--;
|
||
|
|
||
|
stop = false;
|
||
|
if (hr == XML_E_ENDOFINPUT)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
bool inDTD = _pdc->_fDTD;
|
||
|
bool inEntity = _pdc->_fEntity;
|
||
|
bool inPEReference = _pdc->_fPEReference;
|
||
|
|
||
|
if (inEntity && _pdc->_fDepth != _pStack.used())
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found unclosed tags at the end of the input stream.\n");
|
||
|
|
||
|
// Entity itself was unbalanced.
|
||
|
hr = ReportUnclosedTags(_pdc->_fDepth);
|
||
|
}
|
||
|
else if (PopDownload() == S_OK)
|
||
|
{
|
||
|
// then we must have just finished a DTD and we still have more to do
|
||
|
// BUGBUG -- need to check that entity is well formed, i.e. no tags
|
||
|
// left open.
|
||
|
|
||
|
if (!inPEReference)
|
||
|
{
|
||
|
if (inEntity)
|
||
|
{
|
||
|
hr = _pFactory->NotifyEvent(this, XMLNF_ENDENTITY);
|
||
|
}
|
||
|
else if (inDTD)
|
||
|
{
|
||
|
hr = _pFactory->NotifyEvent(this, XMLNF_ENDDTD);
|
||
|
}
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
goto cleanup_stack;
|
||
|
}
|
||
|
|
||
|
// In a synchronous DTD download, there is another parser
|
||
|
// parser Run() call on the stack above us, so let's return
|
||
|
// back to that Run method so we don't complete the parsing
|
||
|
// out from under it.
|
||
|
if (_fRunEntryCount > 0){
|
||
|
hr = S_OK;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (_fStopped){
|
||
|
hr = S_OK;
|
||
|
goto Exit;
|
||
|
}
|
||
|
goto more;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (_pStack.used() > 0)
|
||
|
{
|
||
|
hr = ReportUnclosedTags(0);
|
||
|
}
|
||
|
else if (! _fFoundRoot)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser has found no root in the document.\n");
|
||
|
|
||
|
hr = XML_E_MISSINGROOT;
|
||
|
}
|
||
|
stop = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cleanup_stack:
|
||
|
|
||
|
if (hr != S_OK && hr != E_PENDING)
|
||
|
{
|
||
|
stop = true;
|
||
|
_fLastError = hr;
|
||
|
|
||
|
// Pass all the XML_NODE_INFO structs to the Error function so the client
|
||
|
// gets a chance to cleanup the PVOID pNode fields.
|
||
|
HRESULT edr = _pFactory->Error(this, hr,
|
||
|
(USHORT)(_paNodeInfo ? _lCurrentElement+1 : 0), (XML_NODE_INFO**)_paNodeInfo);
|
||
|
if (edr != 0)
|
||
|
_fLastError = hr;
|
||
|
}
|
||
|
|
||
|
if (stop && ! _fStopped)
|
||
|
{
|
||
|
//TraceTag((tagParserError, "Parser stopping with hr %x", hr));
|
||
|
_fLastError = hr;
|
||
|
_fStopped = true;
|
||
|
_fStarted = false;
|
||
|
HRESULT edr;
|
||
|
edr = _pFactory->NotifyEvent(this, XMLNF_ENDDOCUMENT);
|
||
|
if (edr != 0)
|
||
|
{
|
||
|
hr = edr; // allow factory to change error code (except to S_OK)
|
||
|
if (S_OK == _fLastError)
|
||
|
{
|
||
|
// Make sure the node factory always finds out about errors.
|
||
|
edr = _pFactory->Error(this, hr, 0, NULL);
|
||
|
if (edr != 0)
|
||
|
hr = edr;
|
||
|
}
|
||
|
_fLastError = hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Exit:
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::popAttributes()
|
||
|
{
|
||
|
// Now I pop all the attributes that were pushed for this tag.
|
||
|
// I know we have at least one attribute.
|
||
|
|
||
|
while (_cAttributes > 0)
|
||
|
{
|
||
|
popAttribute(); // no match needed
|
||
|
}
|
||
|
Assert(_pStack.used() == _lCurrentElement+1);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::GetParserState(void)
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
|
||
|
if (_fLastError != 0)
|
||
|
return static_cast<HRESULT>(XMLPARSER_ERROR);
|
||
|
|
||
|
if (_fStopped)
|
||
|
return static_cast<HRESULT>(XMLPARSER_STOPPED);
|
||
|
|
||
|
if (_fSuspended)
|
||
|
return static_cast<HRESULT>(XMLPARSER_SUSPENDED);
|
||
|
|
||
|
if (! _fStarted)
|
||
|
return static_cast<HRESULT>(XMLPARSER_IDLE);
|
||
|
|
||
|
if (_fWaiting)
|
||
|
return static_cast<HRESULT>(XMLPARSER_WAITING);
|
||
|
|
||
|
return static_cast<HRESULT>(XMLPARSER_BUSY);
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::Abort(
|
||
|
/* [in] */ BSTR bstrErrorInfo)
|
||
|
{
|
||
|
//STACK_ENTRY_MODEL(_reThreadModel);
|
||
|
|
||
|
// Have to set these before Critical Section to notify Run()
|
||
|
_fStopped = true;
|
||
|
_fSuspended = true; // force Run to terminate...
|
||
|
|
||
|
CRITICALSECTIONLOCK;
|
||
|
//TraceTag((tagParserError, "Parser aborted - %ls", bstrErrorInfo));
|
||
|
|
||
|
//BUGBUG: may need to check bstrErrorInfo is NULL or not
|
||
|
// and the returned result so that we can report
|
||
|
// E_OUTOFMEMORY error
|
||
|
//if (_bstrError) ::SysFreeString(_bstrError);
|
||
|
//_bstrError = ::SysAllocString(bstrErrorInfo);
|
||
|
|
||
|
// abort all downloads
|
||
|
/* for (int i=_pDownloads.used()-1; i>=0; --i)
|
||
|
{
|
||
|
URLStream* stm = _pDownloads[i]->_pURLStream;
|
||
|
if (stm)
|
||
|
stm->Abort();
|
||
|
}
|
||
|
*/
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::Suspend( void)
|
||
|
{
|
||
|
_fSuspended = true; // force Run to suspend
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::Reset( void)
|
||
|
{
|
||
|
// STACK_ENTRY;
|
||
|
|
||
|
CRITICALSECTIONLOCK;
|
||
|
|
||
|
init();
|
||
|
|
||
|
delete _pszCurrentURL;
|
||
|
_pszCurrentURL = NULL;
|
||
|
delete _pszBaseURL;
|
||
|
_pszBaseURL = NULL;
|
||
|
_pRoot = NULL;
|
||
|
_pFactory = NULL;
|
||
|
_pNode = NULL;
|
||
|
//if (_bstrError != NULL) ::SysFreeString(_bstrError);
|
||
|
_bstrError = NULL;
|
||
|
//if (_fAttemptedURL != NULL) ::SysFreeString(_fAttemptedURL);
|
||
|
//_fAttemptedURL = NULL;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
ULONG STDMETHODCALLTYPE
|
||
|
XMLParser::GetLineNumber(void)
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
if (_pTokenizer) return _pTokenizer->GetLine();
|
||
|
else return 0;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
ULONG STDMETHODCALLTYPE
|
||
|
XMLParser::GetLinePosition( void)
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
if (_pTokenizer) return _pTokenizer->GetLinePosition();
|
||
|
else return 0;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
ULONG STDMETHODCALLTYPE
|
||
|
XMLParser::GetAbsolutePosition( void)
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
if (_pTokenizer) return _pTokenizer->GetInputPosition();
|
||
|
else return 0;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::GetLineBuffer(
|
||
|
/* [out] */ const WCHAR __RPC_FAR *__RPC_FAR *ppwcBuf,
|
||
|
/* [out] */ ULONG __RPC_FAR *pulLen,
|
||
|
/* [out] */ ULONG __RPC_FAR *pulStartPos)
|
||
|
{
|
||
|
if (pulLen == NULL || pulStartPos == NULL) return E_INVALIDARG;
|
||
|
|
||
|
//STACK_ENTRY;
|
||
|
|
||
|
CRITICALSECTIONLOCK;
|
||
|
if (_pTokenizer)
|
||
|
{
|
||
|
return _pTokenizer->GetLineBuffer(ppwcBuf, pulLen, pulStartPos);
|
||
|
}
|
||
|
*ppwcBuf = NULL;
|
||
|
*pulLen = 0;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT STDMETHODCALLTYPE
|
||
|
XMLParser::GetLastError( void)
|
||
|
{
|
||
|
return _fLastError;
|
||
|
}
|
||
|
|
||
|
//------------ PRIVATE METHODS --------------------------------------------------
|
||
|
HRESULT
|
||
|
//XMLParser::PushTokenizer(
|
||
|
// URLStream* stream)
|
||
|
XMLParser::PushTokenizer()
|
||
|
{
|
||
|
_pTokenizer = NEW (XMLStream(this));
|
||
|
if (_pTokenizer == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
_pTokenizer->SetFlags(_usFlags);
|
||
|
// _fTokenizerChanged = true;
|
||
|
|
||
|
//HRESULT hr= PushDownload(stream, _pTokenizer);
|
||
|
HRESULT hr= PushDownload(_pTokenizer);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
delete _pTokenizer;
|
||
|
_pTokenizer = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
//XMLParser::PushDownload(URLStream* stream, XMLStream* tokenizer)
|
||
|
XMLParser::PushDownload(XMLStream* tokenizer)
|
||
|
{
|
||
|
// NOTE: tokenizer can be null, in the case of a parameter entity download.
|
||
|
|
||
|
_pdc = _pDownloads.push();
|
||
|
if (_pdc == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
if (_pDownloads.used() > 1)
|
||
|
_fRootLevel = false;
|
||
|
|
||
|
_pdc->_pTokenizer = tokenizer;
|
||
|
_pdc->_fDTD = false;
|
||
|
_pdc->_fEntity = false;
|
||
|
_pdc->_fAsync = false;
|
||
|
_pdc->_fFoundNonWS = _fFoundNonWS;
|
||
|
_pdc->_fFoundRoot = _fFoundRoot;
|
||
|
_pdc->_fRootLevel = _fRootLevel;
|
||
|
_pdc->_fDepth = _pStack.used();
|
||
|
|
||
|
_fFoundNonWS = false;
|
||
|
_fFoundRoot = false;
|
||
|
|
||
|
_fRootLevel = (_pStack.used() == 0 && _pDownloads.used() == 1);
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::PushStream(IStream* pStm, bool fpe)
|
||
|
{
|
||
|
EncodingStream* stream = (EncodingStream*)EncodingStream::newEncodingStream(pStm); // refcount = 1
|
||
|
if (stream == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
/*
|
||
|
if (_usFlags & XMLFLAG_RUNBUFFERONLY)
|
||
|
stream->setReadStream(false);
|
||
|
*/
|
||
|
_pdc->_pEncodingStream = stream;
|
||
|
stream->Release(); // Smart pointer is holding a ref
|
||
|
|
||
|
HRESULT hr = _pTokenizer->PushStream(stream, fpe);
|
||
|
if (hr == E_PENDING)
|
||
|
{
|
||
|
_fWaiting = true;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::PopDownload()
|
||
|
{
|
||
|
// NOTE: tokenizer can be null, in the case of a parameter entity download.
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (_pdc != NULL)
|
||
|
{
|
||
|
if (_pdc->_pTokenizer)
|
||
|
{
|
||
|
_pdc->_pTokenizer->Reset();
|
||
|
delete _pdc->_pTokenizer;
|
||
|
_pdc->_pTokenizer = NULL;
|
||
|
}
|
||
|
_pdc->_pEncodingStream = NULL;
|
||
|
/*
|
||
|
if (_pdc->_pURLStream)
|
||
|
_pdc->_pURLStream->Reset();
|
||
|
|
||
|
_pdc->_pURLStream = NULL;
|
||
|
*/
|
||
|
// restore saved value of foundnonws.
|
||
|
_fFoundNonWS = _pdc->_fFoundNonWS;
|
||
|
_pdc = _pDownloads.pop();
|
||
|
}
|
||
|
if (_pdc != NULL)
|
||
|
{
|
||
|
if (_pdc->_pTokenizer != NULL)
|
||
|
{
|
||
|
_pTokenizer = _pdc->_pTokenizer;
|
||
|
}
|
||
|
/*
|
||
|
if (_pdc->_pURLStream != NULL)
|
||
|
{
|
||
|
hr = SetCurrentURL(_pdc->_pURLStream->GetURL()->getResolved());
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_pTokenizer = NULL;
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
|
||
|
if (_pStack.used() == 0 && _pDownloads.used() == 1)
|
||
|
_fRootLevel = true;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::GrowNodeInfo()
|
||
|
{
|
||
|
USHORT newsize = _cNodeInfoAllocated + STACK_INCREMENT;
|
||
|
MY_XML_NODE_INFO** pNewArray = NEW (PMY_XML_NODE_INFO[newsize]);
|
||
|
if (pNewArray == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
// Now since STACK_INCREMENT is the same for _pStack then _pStack
|
||
|
// has also re-allocated. Therefore we need to re-initialize all
|
||
|
// the pointers in this array - since they point into the _pStack's memory.
|
||
|
for (int i = _pStack.used() - 1; i >= 0; i--)
|
||
|
{
|
||
|
pNewArray[i] = _pStack[i];
|
||
|
}
|
||
|
delete[] _paNodeInfo;
|
||
|
_paNodeInfo = pNewArray;
|
||
|
_cNodeInfoAllocated = newsize;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::GrowBuffer(PMY_XML_NODE_INFO pNodeInfo, long newlen)
|
||
|
{
|
||
|
delete [] pNodeInfo->_pwcTagName;
|
||
|
pNodeInfo->_pwcTagName = NULL;
|
||
|
// add 50 characters to avoid too many reallocations.
|
||
|
pNodeInfo->_pwcTagName = NEW (WCHAR[ newlen ]);
|
||
|
if (pNodeInfo->_pwcTagName == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
pNodeInfo->_ulBufLen = newlen;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::push(XML_NODE_INFO& info)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
_lCurrentElement = _pStack.used();
|
||
|
|
||
|
_pCurrent = _pStack.push();
|
||
|
if (_pCurrent == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
*((XML_NODE_INFO*)_pCurrent) = info;
|
||
|
PUSHNODEINFO(_pCurrent);
|
||
|
|
||
|
_fRootLevel = false;
|
||
|
|
||
|
// Save the tag name into the private buffer so it sticks around until the
|
||
|
// close tag </foo> which could be anywhere down the road after the
|
||
|
// BufferedStream been overwritten
|
||
|
|
||
|
// THIS CODE IS OPTIMIZED FOR PERFORMANCE WHICH IS WHY IT IS NOT
|
||
|
// CALLING THE CopyText METHOD.
|
||
|
|
||
|
|
||
|
if (_pCurrent->_ulBufLen < info.ulLen+1)
|
||
|
{
|
||
|
checkhr2(GrowBuffer(_pCurrent, info.ulLen + 50));
|
||
|
}
|
||
|
Assert(info.ulLen >= 0);
|
||
|
::memcpy(_pCurrent->_pwcTagName, info.pwcText, info.ulLen*sizeof(WCHAR));
|
||
|
_pCurrent->_pwcTagName[info.ulLen] = L'\0';
|
||
|
|
||
|
// And make the XML_NODE_INFO point to private buffer.
|
||
|
_pCurrent->pwcText = _pCurrent->_pwcTagName;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::pushAttribute(XML_NODE_INFO& info)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
if (_cAttributes != 0)
|
||
|
{
|
||
|
// Attributes are special in that they are supposed to be unique.
|
||
|
// So here we actually check this.
|
||
|
for (long i = _pStack.used()-1; i > _lCurrentElement; i--)
|
||
|
{
|
||
|
XML_NODE_INFO* ptr = _pStack[i];
|
||
|
|
||
|
if (ptr->dwType != XML_ATTRIBUTE)
|
||
|
continue; // ignore attribute values.
|
||
|
|
||
|
if (ptr->ulLen != info.ulLen)
|
||
|
{
|
||
|
continue; // we're ok with this one
|
||
|
}
|
||
|
|
||
|
// Optimized for the normal case where there is no match
|
||
|
if (::memcmp(ptr->pwcText, info.pwcText, info.ulLen*sizeof(WCHAR)) == 0)
|
||
|
{
|
||
|
if (! _fCaseInsensitive)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found a duplicate attribute\n");
|
||
|
|
||
|
return XML_E_DUPLICATEATTRIBUTE;
|
||
|
}
|
||
|
//else if (StrCmpNI(ptr->pwcText, info.pwcText, info.ulLen) == 0)
|
||
|
else if (::FusionpCompareStrings(ptr->pwcText, ::wcslen(ptr->pwcText), info.pwcText, info.ulLen, true) == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found a duplicate attribute (#2)\n");
|
||
|
|
||
|
// Duplicate attributes are allowed in IE4 mode!!
|
||
|
// But only the latest one shows up
|
||
|
// So we have to delete the previous duplication
|
||
|
return XML_E_DUPLICATEATTRIBUTE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_cAttributes++;
|
||
|
|
||
|
_pCurrent = _pStack.push();
|
||
|
if (_pCurrent == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
*((XML_NODE_INFO*)_pCurrent) = info;
|
||
|
PUSHNODEINFO(_pCurrent);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::pushAttributeValue(XML_NODE_INFO& info)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
// Attributes are saved in the BufferedStream so we can point to the
|
||
|
// real text in the buffered stream instead of copying it !!
|
||
|
|
||
|
_pCurrent = _pStack.push();
|
||
|
if (_pCurrent == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
// store attribute value quote character in the pReserved field.
|
||
|
info.pReserved = (PVOID)_pTokenizer->getAttrValueQuoteChar();
|
||
|
|
||
|
*((XML_NODE_INFO*)_pCurrent) = info;
|
||
|
PUSHNODEINFO(_pCurrent);
|
||
|
|
||
|
// this is really the count of nodes on the stack, not just attributes.
|
||
|
_cAttributes++;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::pop(const WCHAR* tag, ULONG len)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (_pCurrent == NULL || _pStack.used() == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found an unexpected end tag.\n");
|
||
|
|
||
|
hr = XML_E_UNEXPECTEDENDTAG;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
if (len != 0)
|
||
|
{
|
||
|
if (_pCurrent->ulLen != len)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found an end tag mismatch\n");
|
||
|
|
||
|
hr = XML_E_ENDTAGMISMATCH;
|
||
|
}
|
||
|
// Optimized for the normal case where there is no match
|
||
|
else if (::memcmp(_pCurrent->pwcText, tag, len*sizeof(WCHAR)) != 0)
|
||
|
{
|
||
|
if (! _fCaseInsensitive)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: XML Parser found an end tag mismatch.\n");
|
||
|
|
||
|
hr = XML_E_ENDTAGMISMATCH;
|
||
|
}
|
||
|
//else if ( XML_StrCmpNI(_pCurrent->pwcText, tag, len) != 0 )
|
||
|
else if (::FusionpCompareStrings(_pCurrent->pwcText, len, tag, len, true) != 0)
|
||
|
{
|
||
|
hr = XML_E_ENDTAGMISMATCH;
|
||
|
}
|
||
|
}
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
/*
|
||
|
TRY
|
||
|
{
|
||
|
String* s = Resources::FormatMessage(hr, String::newString(_pCurrent->pwcText, 0, _pCurrent->ulLen),
|
||
|
String::newString(tag, 0, len), NULL);
|
||
|
_bstrError = s->getBSTR();
|
||
|
}
|
||
|
CATCH
|
||
|
{
|
||
|
hr = ERESULT;
|
||
|
}
|
||
|
ENDTRY
|
||
|
*/
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We don't delete the fTagName because we're going to reuse this field
|
||
|
// later to avoid lots of memory allocations.
|
||
|
|
||
|
_pCurrent = _pStack.pop();
|
||
|
_cNodeInfoCurrent--;
|
||
|
|
||
|
if (_pCurrent == 0)
|
||
|
{
|
||
|
_pNode = _pRoot;
|
||
|
if (_pDownloads.used() == 1)
|
||
|
_fRootLevel = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_pNode = _pCurrent->pNode;
|
||
|
}
|
||
|
|
||
|
Cleanup:
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT XMLParser::pop()
|
||
|
{
|
||
|
// We don't delete the fTagName because we're going to reuse this field
|
||
|
// later to avoid lots of memory allocations.
|
||
|
|
||
|
_pCurrent = _pStack.pop();
|
||
|
_cNodeInfoCurrent--;
|
||
|
|
||
|
if (_pCurrent == 0)
|
||
|
{
|
||
|
_pNode = _pRoot;
|
||
|
if (_pDownloads.used() == 1)
|
||
|
_fRootLevel = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_pNode = _pCurrent->pNode;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
void XMLParser::popAttribute()
|
||
|
{
|
||
|
Assert(_pStack.used() > 0);
|
||
|
|
||
|
_pCurrent = _pStack.pop();
|
||
|
_cNodeInfoCurrent--;
|
||
|
|
||
|
Assert(_pCurrent != 0);
|
||
|
|
||
|
_cAttributes--;
|
||
|
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::CopyText(PMY_XML_NODE_INFO pNodeInfo)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
if (pNodeInfo->_pwcTagName != pNodeInfo->pwcText)
|
||
|
{
|
||
|
ULONG len = pNodeInfo->ulLen;
|
||
|
|
||
|
// Copy the current text into the buffer.
|
||
|
if (pNodeInfo->_ulBufLen < len+1)
|
||
|
{
|
||
|
checkhr2(GrowBuffer(pNodeInfo, len + 50));
|
||
|
}
|
||
|
if (len > 0)
|
||
|
{
|
||
|
::memcpy(pNodeInfo->_pwcTagName, pNodeInfo->pwcText, len*sizeof(WCHAR));
|
||
|
}
|
||
|
pNodeInfo->_pwcTagName[len] = L'\0';
|
||
|
|
||
|
// And make the XML_NODE_INFO point to private buffer.
|
||
|
pNodeInfo->pwcText = pNodeInfo->_pwcTagName;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::CopyContext()
|
||
|
{
|
||
|
// For performance reasons we try not to copy the data for attributes
|
||
|
// and their values when we push them on the stack. We can do this
|
||
|
// because the tokenizer tries to freeze the internal buffers while
|
||
|
// parsing attributes and thereby guarentee that the pointers stay
|
||
|
// good. But occasionally the BufferedStream has to reallocate when
|
||
|
// the attributes are right at the end of the buffer.
|
||
|
|
||
|
long last = _pStack.used();
|
||
|
for (long i = _cAttributes; i > 0 ; i--)
|
||
|
{
|
||
|
long index = last - i;
|
||
|
MY_XML_NODE_INFO* ptr = _pStack[index];
|
||
|
CopyText(ptr);
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT XMLParser::ReportUnclosedTags(int start)
|
||
|
{
|
||
|
HRESULT hr = XML_E_UNCLOSEDTAG;
|
||
|
// Build a string containing the list of unclosed tags and format an error
|
||
|
// message containing this text.
|
||
|
int tags = _pStack.used();
|
||
|
|
||
|
WCHAR* buffer = NULL;
|
||
|
WCHAR* msgbuf = NULL;
|
||
|
unsigned long size = 0;
|
||
|
unsigned long used = 0;
|
||
|
|
||
|
for (long i = start; i < tags; i++)
|
||
|
{
|
||
|
XML_NODE_INFO* ptr = _pStack[i];
|
||
|
if (ptr->dwType == XML_ATTRIBUTE)
|
||
|
break;
|
||
|
|
||
|
if (used + ptr->ulLen + 3 > size) // +3 for '<','>' and '\0'
|
||
|
{
|
||
|
long newsize = used + ptr->ulLen + 500;
|
||
|
WCHAR* newbuf = NEW (WCHAR[newsize]);
|
||
|
if (newbuf == NULL)
|
||
|
{
|
||
|
goto nomem;
|
||
|
}
|
||
|
if (buffer != NULL)
|
||
|
{
|
||
|
::memcpy(newbuf, buffer, used);
|
||
|
delete[] buffer;
|
||
|
}
|
||
|
|
||
|
size = newsize;
|
||
|
buffer = newbuf;
|
||
|
}
|
||
|
if (i > start)
|
||
|
{
|
||
|
buffer[used++] = ',';
|
||
|
buffer[used++] = ' ';
|
||
|
}
|
||
|
::memcpy(&buffer[used], ptr->pwcText, sizeof(WCHAR) * ptr->ulLen);
|
||
|
used += ptr->ulLen;
|
||
|
buffer[used] = '\0';
|
||
|
}
|
||
|
goto cleanup;
|
||
|
|
||
|
//xiaoyu : SysAllocString and SysFreeString are commended off.
|
||
|
// msgbuf = ::FormatMessageInternal(g_hInstance, XML_E_UNCLOSEDTAG, buffer, NULL);
|
||
|
/*
|
||
|
TRY
|
||
|
{
|
||
|
String* s = Resources::FormatMessage(XML_E_UNCLOSEDTAG,
|
||
|
String::newString(buffer), NULL);
|
||
|
_bstrError = s->getBSTR();
|
||
|
goto cleanup;
|
||
|
}
|
||
|
CATCH
|
||
|
{
|
||
|
hr = ERESULT;
|
||
|
goto done;
|
||
|
}
|
||
|
ENDTRY
|
||
|
|
||
|
|
||
|
if (msgbuf == NULL)
|
||
|
goto nomem;
|
||
|
|
||
|
if (_bstrError) ::SysFreeString(_bstrError);
|
||
|
_bstrError = ::SysAllocString(msgbuf);
|
||
|
if (_bstrError == NULL)
|
||
|
goto nomem;
|
||
|
|
||
|
goto cleanup;
|
||
|
*/
|
||
|
nomem:
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
delete [] buffer;
|
||
|
delete [] msgbuf;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT XMLParser::init()
|
||
|
{
|
||
|
CRITICALSECTIONLOCK;
|
||
|
|
||
|
_fLastError = 0;
|
||
|
_fStopped = false;
|
||
|
_fSuspended = false;
|
||
|
_pNode = _pRoot;
|
||
|
_fStarted = false;
|
||
|
_fStopped = false;
|
||
|
_fWaiting = false;
|
||
|
_fFoundRoot = false;
|
||
|
_fFoundNonWS = false;
|
||
|
_pTokenizer = NULL;
|
||
|
_fGotVersion = false;
|
||
|
_fRootLevel = true;
|
||
|
_cAttributes = 0;
|
||
|
|
||
|
|
||
|
_fPendingBeginChildren = false;
|
||
|
_fPendingEndChildren = false;
|
||
|
|
||
|
while (_pCurrent != NULL)
|
||
|
{
|
||
|
_pCurrent = _pStack.pop();
|
||
|
}
|
||
|
|
||
|
_cNodeInfoCurrent = 0;
|
||
|
_lCurrentElement = 0;
|
||
|
|
||
|
// cleanup downloads
|
||
|
while (_pdc != NULL)
|
||
|
{
|
||
|
PopDownload();
|
||
|
}
|
||
|
|
||
|
_pCurrent = NULL;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
HRESULT
|
||
|
XMLParser::ErrorCallback(HRESULT hr)
|
||
|
{
|
||
|
Assert(hr == XMLStream::XML_DATAAVAILABLE ||
|
||
|
hr == XMLStream::XML_DATAREALLOCATE);
|
||
|
|
||
|
if (hr == XMLStream::XML_DATAREALLOCATE)
|
||
|
{
|
||
|
// This is more serious. We have to actually save away the
|
||
|
// context because the buffers are about to be reallocated.
|
||
|
checkhr2(CopyContext());
|
||
|
}
|
||
|
checkhr2(_pFactory->NotifyEvent(this, XMLNF_DATAAVAILABLE));
|
||
|
return hr;
|
||
|
}
|