windows-nt/Source/XPSP1/NT/admin/pchealth/client/lamebtn/simplexml.cpp
2020-09-26 16:20:57 +08:00

456 lines
13 KiB
C++

/*
Copyright 1999 Microsoft Corporation
Simplified XML "DOM"
Walter Smith (wsmith)
Rajesh Soy (nsoy) - modified - 4/13/2000
*/
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__;
#define THIS_FILE __szTraceSourceFile
#include "stdafx.h"
#define NOTRACE
#include "simplexml.h"
#ifdef SIMPLEXML_WANTPARSER
class __declspec(uuid("7887F5E7-640C-45A5-B98B-1E8645E2F7BA")) DataParser;
interface __declspec(uuid("9EFEB013-4E9C-42aa-8354-C5508E352346")) IDataParser;
struct IDataParser : public IUnknown {
virtual HRESULT STDMETHODCALLTYPE GetTopNode(/*OUT*/ SimpleXMLNode** ppNode) = 0;
};
class ATL_NO_VTABLE DataParser :
public CComObjectRoot,
public CComCoClass<DataParser, &__uuidof(DataParser)>,
public IXMLNodeFactory,
public IDataParser
{
public:
public:
DataParser()
{
m_pNodeHolder = auto_ptr<SimpleXMLNode>(new SimpleXMLNode);
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(DataParser)
COM_INTERFACE_ENTRY(IXMLNodeFactory)
COM_INTERFACE_ENTRY(IDataParser)
END_COM_MAP()
//private:
public:
auto_ptr<SimpleXMLNode> m_pNodeHolder;
public:
// IXMLNodeFactory
STDMETHOD(NotifyEvent)(IXMLNodeSource* pSource,
XML_NODEFACTORY_EVENT iEvt);
STDMETHOD(BeginChildren)(IXMLNodeSource* pSource,
XML_NODE_INFO* pNodeInfo);
STDMETHOD(EndChildren)(IXMLNodeSource* pSource,
BOOL fEmpty,
XML_NODE_INFO* pNodeInfo);
STDMETHOD(Error)(IXMLNodeSource* pSource,
HRESULT hrErrorCode,
USHORT cNumRecs,
XML_NODE_INFO** aNodeInfo);
STDMETHOD(CreateNode)(IXMLNodeSource* pSource,
PVOID pNodeParent,
USHORT cNumRecs,
XML_NODE_INFO** aNodeInfo);
// IDataParser
// Get a pointer to the "node holder" (a node whose children
// are the parsed document) -- caller gets ownership of the
// pointer and must delete when finished.
STDMETHOD(GetTopNode)(/*OUT*/ SimpleXMLNode** ppNode)
{
*ppNode = m_pNodeHolder.get();
m_pNodeHolder.release();
return S_OK;
}
};
HRESULT DataParser::NotifyEvent(IXMLNodeSource* pSource,
XML_NODEFACTORY_EVENT iEvt)
{
switch (iEvt) {
case XMLNF_STARTDOCUMENT:
{
CComQIPtr<IXMLParser> pParser(pSource);
ThrowIfTrue(!pParser);
pParser->SetRoot(m_pNodeHolder.get());
}
break;
default:
break;
}
return S_OK;
}
HRESULT DataParser::BeginChildren(IXMLNodeSource* pSource,
XML_NODE_INFO* pNodeInfo)
{
return S_OK;
}
HRESULT DataParser::EndChildren(IXMLNodeSource* pSource,
BOOL fEmpty,
XML_NODE_INFO* pNodeInfo)
{
return S_OK;
}
HRESULT DataParser::Error(IXMLNodeSource* pSource,
HRESULT hrErrorCode,
USHORT cNumRecs,
XML_NODE_INFO** aNodeInfo)
{
throw hrErrorCode;
return S_OK;
}
HRESULT DataParser::CreateNode(IXMLNodeSource* pSource,
PVOID pNodeParent,
USHORT cNumRecs,
XML_NODE_INFO** aNodeInfo)
{
switch (aNodeInfo[0]->dwType) {
case XML_ELEMENT:
{
// Make a new node and add it to the parent node
XML_NODE_INFO* pTagInfo = aNodeInfo[0];
wstring tag(pTagInfo->pwcText, pTagInfo->ulLen);
SimpleXMLNode* pNode = ((SimpleXMLNode*) pNodeParent)->AppendChild(tag);
pTagInfo->pNode = pNode;
// Collect attributes, if any
if (cNumRecs > 1) {
wstring sName;
wstring sValue;
for (int i = 1; i < cNumRecs; i++) {
XML_NODE_INFO* pInfo = aNodeInfo[i];
switch (pInfo->dwType) {
case XML_ATTRIBUTE:
if (!sName.empty()) {
pNode->SetAttribute(sName, sValue);
sName.erase();
sValue.erase();
}
sName = wstring(pInfo->pwcText, pInfo->ulLen);
break;
case XML_PCDATA:
sValue.append(pInfo->pwcText, pInfo->ulLen);
break;
case XML_ENTITYREF:
// We don't deal with non-predefined entities, so if we see
// an ENTITYREF we're guaranteed to parse incorrectly.
throw E_FAIL;
break;
default:
break;
}
}
if (!sName.empty()) {
pNode->SetAttribute(sName, sValue);
}
}
}
break;
case XML_PCDATA:
{
_ASSERT(pNodeParent != NULL);
((SimpleXMLNode*) pNodeParent)->text.append(aNodeInfo[0]->pwcText, aNodeInfo[0]->ulLen);
}
break;
}
return S_OK;
}
class FileMapping {
public:
FileMapping()
: hFile(NULL), hMapping(NULL), pView(NULL)
{
}
~FileMapping()
{
if (pView != NULL)
UnmapViewOfFile(pView);
if (hMapping != NULL)
CloseHandle(hMapping);
if (hFile != NULL)
CloseHandle(hFile);
}
void Open(LPCTSTR szFile)
{
// Sometimes the file is still in use by the upload library when we find
// it in the directory -- not sure how this can be true, but it seems to
// be short-lived, so we just give it a chance to finish up and retry a
// couple of times.
TraceFunctEnter(_T("FileMapping"));
for (int tries = 0; tries < 3; tries++) {
hFile = CreateFile(szFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
// BUGBUG: use FILE_FLAG_DELETE_ON_CLOSE when this is working OK
NULL);
if (hFile != INVALID_HANDLE_VALUE)
break;
Sleep(50);
}
ThrowIfTrue(hFile == INVALID_HANDLE_VALUE);
_RPT1(_CRT_WARN, "CreateFile: %d retries\n", tries);
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
ThrowIfTrue(hMapping == INVALID_HANDLE_VALUE);
pView = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
TraceFunctLeave();
}
PVOID GetDataPtr()
{
_ASSERT(pView != NULL);
return pView;
}
DWORD GetSize()
{
_ASSERT(hFile != NULL);
return GetFileSize(hFile, NULL);
}
private:
HANDLE hFile;
HANDLE hMapping;
PVOID pView;
};
void DumpNodes(SimpleXMLNode* pNode, int indent = 4)
{
for (vector< auto_ptr<SimpleXMLNode> >::const_iterator it = pNode->children.begin();it != pNode->children.end();it++)
{
_RPT3(_CRT_WARN, "%*s<%ls", indent, _T(""), (*it)->tag.c_str());
for (SimpleXMLNode::attrs_t::const_iterator itattr = (*it)->attrs.begin();itattr != (*it)->attrs.end();itattr++)
{
_RPT2(_CRT_WARN, " %ls=\"%ls\"", (*itattr).first.c_str(), (*itattr).second.c_str());
}
_RPT0(_CRT_WARN, ">\n");
DumpNodes((*it).get(), indent + 4);
if (!(*it)->text.empty())
{
_RPT1(_CRT_WARN, "%ls", (*it)->text.c_str());
}
_RPT3(_CRT_WARN, "%*s</%ls>\n", indent, _T(""), (*it)->tag.c_str());
}
}
void SimpleXMLDocument::ParseFile(LPCTSTR szFilename)
{
TraceFunctEnter(_T("SimpleXMLDocument::ParseFile"));
DebugTrace(TRACE_ID, _T("Processing: %s"), szFilename);
_RPTF1(_CRT_WARN, "Processing %s\n", szFilename);
CComPtr<IXMLParser> pParser;
ThrowIfFail( pParser.CoCreateInstance(__uuidof(XMLParser)) );
_ASSERT(_CrtCheckMemory());
CComPtr<IXMLNodeFactory> pFactory;
DataParser::CreateInstance(&pFactory);
_ASSERT(_CrtCheckMemory());
ThrowIfFail( pParser->SetFactory(pFactory) );
ThrowIfFail( pParser->SetFlags(XMLFLAG_NOWHITESPACE) ) ;
FileMapping mapping;
mapping.Open(szFilename);
ThrowIfFail( pParser->PushData((const char*) mapping.GetDataPtr(), mapping.GetSize(), TRUE) );
ThrowIfFail( pParser->Run(-1) );
pParser.Release();
CComQIPtr<IDataParser> pFactoryPrivate(pFactory);
// We get ownership of the parser's node -- we carefully pass it
// along to m_pTopNode (not mixing auto_ptrs and real ptrs would
// probably be the idiomatic way to do this).
SimpleXMLNode* pTopNode;
pFactoryPrivate->GetTopNode(&pTopNode);
m_pTopNode = auto_ptr<SimpleXMLNode>(pTopNode);
// DumpNodes(m_pTopNode.get());
pFactory.Release();
TraceFunctLeave();
}
#endif // SIMPLEXML_WANTPARSER
// This is ATL's stuff slightly modified to pass CP_UTF8 to WideCharToMultiByte
inline LPSTR WINAPI W2Helper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp)
{
ATLASSERT(lpw != NULL);
ATLASSERT(lpa != NULL);
// verify that no illegal character present
// since lpa was allocated based on the size of lpw
// don't worry about the number of chars
lpa[0] = '\0';
WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL);
return lpa;
}
#define W2UTF8(lpw) (\
((_lpw = lpw) == NULL) ? NULL : (\
_convert = (lstrlenW(_lpw)+1)*2,\
W2Helper((LPSTR) alloca(_convert), _lpw, _convert, CP_UTF8)))
#define USES_UTF8_CONVERSION int _convert = 0; _convert; LPCWSTR _lpw = NULL; _lpw; LPCSTR _lpa = NULL; _lpa
void OutUTF8String(ostream& out, LPCWSTR wstr, int nChars)
{
_ASSERT(wstr != NULL);
int nBuf = (nChars + 1) * 3; // UTF-8 can convert one WCHAR into 3 bytes
char* buf;
if (nBuf >= 1024)
buf = (char*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, nBuf);
else
buf = (char*) alloca(nBuf);
int nWritten = WideCharToMultiByte(CP_UTF8, 0, wstr, nChars, buf, nBuf, NULL, NULL);
// REVIEW: WideCharToMultiByte is documented as writing a null terminator to buf,
// but it doesn't seem to be doing it.
buf[nWritten] = 0;
out << buf;
if (nBuf >= 1024)
HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, buf);
}
void OutXMLString(ostream& out, const wstring& str)
{
wstring::size_type iRemainder = 0;
wstring::size_type iSpecial = str.find_first_of(L"<>&'\"");
while (iSpecial < str.size()) {
if (iSpecial > iRemainder)
OutUTF8String(out, str.c_str() + iRemainder, iSpecial - iRemainder);
switch (str.at(iSpecial)) {
case '<': out << "&lt;"; break;
case '>': out << "&gt;"; break;
case '&': out << "&amp;"; break;
case '\'': out << "&apos;"; break;
case '"': out << "&quot;"; break;
default: _ASSERT(0); break;
}
iRemainder = iSpecial + 1;
iSpecial = str.find_first_of(L"<>&'\"", iRemainder);
}
if (iRemainder < str.size())
OutUTF8String(out, str.c_str() + iRemainder, str.size() - iRemainder);
}
void SaveNodes(ostream& out, SimpleXMLNode* pNode)
{
USES_UTF8_CONVERSION;
for (vector< auto_ptr<SimpleXMLNode> >::const_iterator it = pNode->children.begin();
it != pNode->children.end();
it++) {
out << '<';
OutXMLString(out, (*it)->tag);
for (SimpleXMLNode::attrs_t::const_iterator itattr = (*it)->attrs.begin();
itattr != (*it)->attrs.end();
itattr++) {
out << ' ';
OutXMLString(out, (*itattr).first);
out << "=\"";
OutXMLString(out, (*itattr).second);
out << '"';
}
if ((*it)->children.empty() && (*it)->text.empty()) {
out << " />";
}
else {
out << ">";
SaveNodes(out, (*it).get());
if (!(*it)->text.empty())
OutXMLString(out, (*it)->text);
out << "</";
OutXMLString(out, (*it)->tag);
out << ">";
}
}
}
void SimpleXMLDocument::SaveFile(LPCTSTR szFilename) const
{
USES_CONVERSION;
ofstream out(T2CA(szFilename), ios::out | ios::trunc | ios::binary);
if (!out.is_open())
ThrowLastError();
out << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
SaveNodes(out, m_pTopNode.get());
}