Source code of Windows XP (NT5)
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.

455 lines
13 KiB

  1. /*
  2. Copyright 1999 Microsoft Corporation
  3. Simplified XML "DOM"
  4. Walter Smith (wsmith)
  5. Rajesh Soy (nsoy) - modified - 4/13/2000
  6. */
  7. #ifdef THIS_FILE
  8. #undef THIS_FILE
  9. #endif
  10. static char __szTraceSourceFile[] = __FILE__;
  11. #define THIS_FILE __szTraceSourceFile
  12. #include "stdafx.h"
  13. #define NOTRACE
  14. #include "simplexml.h"
  15. #ifdef SIMPLEXML_WANTPARSER
  16. class __declspec(uuid("7887F5E7-640C-45A5-B98B-1E8645E2F7BA")) DataParser;
  17. interface __declspec(uuid("9EFEB013-4E9C-42aa-8354-C5508E352346")) IDataParser;
  18. struct IDataParser : public IUnknown {
  19. virtual HRESULT STDMETHODCALLTYPE GetTopNode(/*OUT*/ SimpleXMLNode** ppNode) = 0;
  20. };
  21. class ATL_NO_VTABLE DataParser :
  22. public CComObjectRoot,
  23. public CComCoClass<DataParser, &__uuidof(DataParser)>,
  24. public IXMLNodeFactory,
  25. public IDataParser
  26. {
  27. public:
  28. public:
  29. DataParser()
  30. {
  31. m_pNodeHolder = auto_ptr<SimpleXMLNode>(new SimpleXMLNode);
  32. }
  33. DECLARE_PROTECT_FINAL_CONSTRUCT()
  34. BEGIN_COM_MAP(DataParser)
  35. COM_INTERFACE_ENTRY(IXMLNodeFactory)
  36. COM_INTERFACE_ENTRY(IDataParser)
  37. END_COM_MAP()
  38. //private:
  39. public:
  40. auto_ptr<SimpleXMLNode> m_pNodeHolder;
  41. public:
  42. // IXMLNodeFactory
  43. STDMETHOD(NotifyEvent)(IXMLNodeSource* pSource,
  44. XML_NODEFACTORY_EVENT iEvt);
  45. STDMETHOD(BeginChildren)(IXMLNodeSource* pSource,
  46. XML_NODE_INFO* pNodeInfo);
  47. STDMETHOD(EndChildren)(IXMLNodeSource* pSource,
  48. BOOL fEmpty,
  49. XML_NODE_INFO* pNodeInfo);
  50. STDMETHOD(Error)(IXMLNodeSource* pSource,
  51. HRESULT hrErrorCode,
  52. USHORT cNumRecs,
  53. XML_NODE_INFO** aNodeInfo);
  54. STDMETHOD(CreateNode)(IXMLNodeSource* pSource,
  55. PVOID pNodeParent,
  56. USHORT cNumRecs,
  57. XML_NODE_INFO** aNodeInfo);
  58. // IDataParser
  59. // Get a pointer to the "node holder" (a node whose children
  60. // are the parsed document) -- caller gets ownership of the
  61. // pointer and must delete when finished.
  62. STDMETHOD(GetTopNode)(/*OUT*/ SimpleXMLNode** ppNode)
  63. {
  64. *ppNode = m_pNodeHolder.get();
  65. m_pNodeHolder.release();
  66. return S_OK;
  67. }
  68. };
  69. HRESULT DataParser::NotifyEvent(IXMLNodeSource* pSource,
  70. XML_NODEFACTORY_EVENT iEvt)
  71. {
  72. switch (iEvt) {
  73. case XMLNF_STARTDOCUMENT:
  74. {
  75. CComQIPtr<IXMLParser> pParser(pSource);
  76. ThrowIfTrue(!pParser);
  77. pParser->SetRoot(m_pNodeHolder.get());
  78. }
  79. break;
  80. default:
  81. break;
  82. }
  83. return S_OK;
  84. }
  85. HRESULT DataParser::BeginChildren(IXMLNodeSource* pSource,
  86. XML_NODE_INFO* pNodeInfo)
  87. {
  88. return S_OK;
  89. }
  90. HRESULT DataParser::EndChildren(IXMLNodeSource* pSource,
  91. BOOL fEmpty,
  92. XML_NODE_INFO* pNodeInfo)
  93. {
  94. return S_OK;
  95. }
  96. HRESULT DataParser::Error(IXMLNodeSource* pSource,
  97. HRESULT hrErrorCode,
  98. USHORT cNumRecs,
  99. XML_NODE_INFO** aNodeInfo)
  100. {
  101. throw hrErrorCode;
  102. return S_OK;
  103. }
  104. HRESULT DataParser::CreateNode(IXMLNodeSource* pSource,
  105. PVOID pNodeParent,
  106. USHORT cNumRecs,
  107. XML_NODE_INFO** aNodeInfo)
  108. {
  109. switch (aNodeInfo[0]->dwType) {
  110. case XML_ELEMENT:
  111. {
  112. // Make a new node and add it to the parent node
  113. XML_NODE_INFO* pTagInfo = aNodeInfo[0];
  114. wstring tag(pTagInfo->pwcText, pTagInfo->ulLen);
  115. SimpleXMLNode* pNode = ((SimpleXMLNode*) pNodeParent)->AppendChild(tag);
  116. pTagInfo->pNode = pNode;
  117. // Collect attributes, if any
  118. if (cNumRecs > 1) {
  119. wstring sName;
  120. wstring sValue;
  121. for (int i = 1; i < cNumRecs; i++) {
  122. XML_NODE_INFO* pInfo = aNodeInfo[i];
  123. switch (pInfo->dwType) {
  124. case XML_ATTRIBUTE:
  125. if (!sName.empty()) {
  126. pNode->SetAttribute(sName, sValue);
  127. sName.erase();
  128. sValue.erase();
  129. }
  130. sName = wstring(pInfo->pwcText, pInfo->ulLen);
  131. break;
  132. case XML_PCDATA:
  133. sValue.append(pInfo->pwcText, pInfo->ulLen);
  134. break;
  135. case XML_ENTITYREF:
  136. // We don't deal with non-predefined entities, so if we see
  137. // an ENTITYREF we're guaranteed to parse incorrectly.
  138. throw E_FAIL;
  139. break;
  140. default:
  141. break;
  142. }
  143. }
  144. if (!sName.empty()) {
  145. pNode->SetAttribute(sName, sValue);
  146. }
  147. }
  148. }
  149. break;
  150. case XML_PCDATA:
  151. {
  152. _ASSERT(pNodeParent != NULL);
  153. ((SimpleXMLNode*) pNodeParent)->text.append(aNodeInfo[0]->pwcText, aNodeInfo[0]->ulLen);
  154. }
  155. break;
  156. }
  157. return S_OK;
  158. }
  159. class FileMapping {
  160. public:
  161. FileMapping()
  162. : hFile(NULL), hMapping(NULL), pView(NULL)
  163. {
  164. }
  165. ~FileMapping()
  166. {
  167. if (pView != NULL)
  168. UnmapViewOfFile(pView);
  169. if (hMapping != NULL)
  170. CloseHandle(hMapping);
  171. if (hFile != NULL)
  172. CloseHandle(hFile);
  173. }
  174. void Open(LPCTSTR szFile)
  175. {
  176. // Sometimes the file is still in use by the upload library when we find
  177. // it in the directory -- not sure how this can be true, but it seems to
  178. // be short-lived, so we just give it a chance to finish up and retry a
  179. // couple of times.
  180. TraceFunctEnter(_T("FileMapping"));
  181. for (int tries = 0; tries < 3; tries++) {
  182. hFile = CreateFile(szFile,
  183. GENERIC_READ,
  184. FILE_SHARE_READ,
  185. NULL,
  186. OPEN_EXISTING,
  187. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  188. // BUGBUG: use FILE_FLAG_DELETE_ON_CLOSE when this is working OK
  189. NULL);
  190. if (hFile != INVALID_HANDLE_VALUE)
  191. break;
  192. Sleep(50);
  193. }
  194. ThrowIfTrue(hFile == INVALID_HANDLE_VALUE);
  195. _RPT1(_CRT_WARN, "CreateFile: %d retries\n", tries);
  196. hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  197. ThrowIfTrue(hMapping == INVALID_HANDLE_VALUE);
  198. pView = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
  199. TraceFunctLeave();
  200. }
  201. PVOID GetDataPtr()
  202. {
  203. _ASSERT(pView != NULL);
  204. return pView;
  205. }
  206. DWORD GetSize()
  207. {
  208. _ASSERT(hFile != NULL);
  209. return GetFileSize(hFile, NULL);
  210. }
  211. private:
  212. HANDLE hFile;
  213. HANDLE hMapping;
  214. PVOID pView;
  215. };
  216. void DumpNodes(SimpleXMLNode* pNode, int indent = 4)
  217. {
  218. for (vector< auto_ptr<SimpleXMLNode> >::const_iterator it = pNode->children.begin();it != pNode->children.end();it++)
  219. {
  220. _RPT3(_CRT_WARN, "%*s<%ls", indent, _T(""), (*it)->tag.c_str());
  221. for (SimpleXMLNode::attrs_t::const_iterator itattr = (*it)->attrs.begin();itattr != (*it)->attrs.end();itattr++)
  222. {
  223. _RPT2(_CRT_WARN, " %ls=\"%ls\"", (*itattr).first.c_str(), (*itattr).second.c_str());
  224. }
  225. _RPT0(_CRT_WARN, ">\n");
  226. DumpNodes((*it).get(), indent + 4);
  227. if (!(*it)->text.empty())
  228. {
  229. _RPT1(_CRT_WARN, "%ls", (*it)->text.c_str());
  230. }
  231. _RPT3(_CRT_WARN, "%*s</%ls>\n", indent, _T(""), (*it)->tag.c_str());
  232. }
  233. }
  234. void SimpleXMLDocument::ParseFile(LPCTSTR szFilename)
  235. {
  236. TraceFunctEnter(_T("SimpleXMLDocument::ParseFile"));
  237. DebugTrace(TRACE_ID, _T("Processing: %s"), szFilename);
  238. _RPTF1(_CRT_WARN, "Processing %s\n", szFilename);
  239. CComPtr<IXMLParser> pParser;
  240. ThrowIfFail( pParser.CoCreateInstance(__uuidof(XMLParser)) );
  241. _ASSERT(_CrtCheckMemory());
  242. CComPtr<IXMLNodeFactory> pFactory;
  243. DataParser::CreateInstance(&pFactory);
  244. _ASSERT(_CrtCheckMemory());
  245. ThrowIfFail( pParser->SetFactory(pFactory) );
  246. ThrowIfFail( pParser->SetFlags(XMLFLAG_NOWHITESPACE) ) ;
  247. FileMapping mapping;
  248. mapping.Open(szFilename);
  249. ThrowIfFail( pParser->PushData((const char*) mapping.GetDataPtr(), mapping.GetSize(), TRUE) );
  250. ThrowIfFail( pParser->Run(-1) );
  251. pParser.Release();
  252. CComQIPtr<IDataParser> pFactoryPrivate(pFactory);
  253. // We get ownership of the parser's node -- we carefully pass it
  254. // along to m_pTopNode (not mixing auto_ptrs and real ptrs would
  255. // probably be the idiomatic way to do this).
  256. SimpleXMLNode* pTopNode;
  257. pFactoryPrivate->GetTopNode(&pTopNode);
  258. m_pTopNode = auto_ptr<SimpleXMLNode>(pTopNode);
  259. // DumpNodes(m_pTopNode.get());
  260. pFactory.Release();
  261. TraceFunctLeave();
  262. }
  263. #endif // SIMPLEXML_WANTPARSER
  264. // This is ATL's stuff slightly modified to pass CP_UTF8 to WideCharToMultiByte
  265. inline LPSTR WINAPI W2Helper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp)
  266. {
  267. ATLASSERT(lpw != NULL);
  268. ATLASSERT(lpa != NULL);
  269. // verify that no illegal character present
  270. // since lpa was allocated based on the size of lpw
  271. // don't worry about the number of chars
  272. lpa[0] = '\0';
  273. WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL);
  274. return lpa;
  275. }
  276. #define W2UTF8(lpw) (\
  277. ((_lpw = lpw) == NULL) ? NULL : (\
  278. _convert = (lstrlenW(_lpw)+1)*2,\
  279. W2Helper((LPSTR) alloca(_convert), _lpw, _convert, CP_UTF8)))
  280. #define USES_UTF8_CONVERSION int _convert = 0; _convert; LPCWSTR _lpw = NULL; _lpw; LPCSTR _lpa = NULL; _lpa
  281. void OutUTF8String(ostream& out, LPCWSTR wstr, int nChars)
  282. {
  283. _ASSERT(wstr != NULL);
  284. int nBuf = (nChars + 1) * 3; // UTF-8 can convert one WCHAR into 3 bytes
  285. char* buf;
  286. if (nBuf >= 1024)
  287. buf = (char*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, nBuf);
  288. else
  289. buf = (char*) alloca(nBuf);
  290. int nWritten = WideCharToMultiByte(CP_UTF8, 0, wstr, nChars, buf, nBuf, NULL, NULL);
  291. // REVIEW: WideCharToMultiByte is documented as writing a null terminator to buf,
  292. // but it doesn't seem to be doing it.
  293. buf[nWritten] = 0;
  294. out << buf;
  295. if (nBuf >= 1024)
  296. HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, buf);
  297. }
  298. void OutXMLString(ostream& out, const wstring& str)
  299. {
  300. wstring::size_type iRemainder = 0;
  301. wstring::size_type iSpecial = str.find_first_of(L"<>&'\"");
  302. while (iSpecial < str.size()) {
  303. if (iSpecial > iRemainder)
  304. OutUTF8String(out, str.c_str() + iRemainder, iSpecial - iRemainder);
  305. switch (str.at(iSpecial)) {
  306. case '<': out << "&lt;"; break;
  307. case '>': out << "&gt;"; break;
  308. case '&': out << "&amp;"; break;
  309. case '\'': out << "&apos;"; break;
  310. case '"': out << "&quot;"; break;
  311. default: _ASSERT(0); break;
  312. }
  313. iRemainder = iSpecial + 1;
  314. iSpecial = str.find_first_of(L"<>&'\"", iRemainder);
  315. }
  316. if (iRemainder < str.size())
  317. OutUTF8String(out, str.c_str() + iRemainder, str.size() - iRemainder);
  318. }
  319. void SaveNodes(ostream& out, SimpleXMLNode* pNode)
  320. {
  321. USES_UTF8_CONVERSION;
  322. for (vector< auto_ptr<SimpleXMLNode> >::const_iterator it = pNode->children.begin();
  323. it != pNode->children.end();
  324. it++) {
  325. out << '<';
  326. OutXMLString(out, (*it)->tag);
  327. for (SimpleXMLNode::attrs_t::const_iterator itattr = (*it)->attrs.begin();
  328. itattr != (*it)->attrs.end();
  329. itattr++) {
  330. out << ' ';
  331. OutXMLString(out, (*itattr).first);
  332. out << "=\"";
  333. OutXMLString(out, (*itattr).second);
  334. out << '"';
  335. }
  336. if ((*it)->children.empty() && (*it)->text.empty()) {
  337. out << " />";
  338. }
  339. else {
  340. out << ">";
  341. SaveNodes(out, (*it).get());
  342. if (!(*it)->text.empty())
  343. OutXMLString(out, (*it)->text);
  344. out << "</";
  345. OutXMLString(out, (*it)->tag);
  346. out << ">";
  347. }
  348. }
  349. }
  350. void SimpleXMLDocument::SaveFile(LPCTSTR szFilename) const
  351. {
  352. USES_CONVERSION;
  353. ofstream out(T2CA(szFilename), ios::out | ios::trunc | ios::binary);
  354. if (!out.is_open())
  355. ThrowLastError();
  356. out << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n";
  357. SaveNodes(out, m_pTopNode.get());
  358. }