1 #include "Macros.h"
2 
3 #if IS_GNUC_AND_GNUC_VERSION_LT(5,1,1)
4 #include <cstring>
5 #endif
6 
7 #include "XmlDoc.h"
8 #include "XmlNode.h"
9 #include "XmlNamespace.h"
10 
11 #include "no_warning_fstream"
12 #include "no_warning_vector"
13 #include <sys/stat.h>
14 
15 using namespace std;
16 
17 #include "zlib.h"
18 
19 namespace opencollada
20 {
XmlDoc(XmlDoc && other)21 	XmlDoc::XmlDoc(XmlDoc && other)
22 	{
23 		*this = move(other);
24 	}
25 
~XmlDoc()26 	XmlDoc::~XmlDoc()
27 	{
28 		reset();
29 	}
30 
operator =(XmlDoc && other)31 	const XmlDoc & XmlDoc::operator = (XmlDoc && other)
32 	{
33 		swap(mDoc, other.mDoc);
34 		return *this;
35 	}
36 
readFile(const string & path)37 	void XmlDoc::readFile(const string & path)
38 	{
39 		reset();
40 
41 		ifstream ifile(path, ios_base::binary);
42 
43 		if (!ifile.is_open()) return;
44 
45 		struct stat st;
46 		int r = stat(path.c_str(), &st);
47 		auto size = 0;
48 		if (r == 0)
49 			size = st.st_size;
50 
51 		if (size <= 4) return;
52 
53 		vector<char> content(static_cast<size_t>(size));
54 		ifile.read(content.data(), size);
55 
56 #if IS_MSVC_AND_MSVC_VERSION_LT(1900)
57 		typedef unsigned int uint32_t;
58 #endif
59 		uint32_t signature = *reinterpret_cast<const uint32_t*>(content.data());
60 
61 		// Uncompressed document
62 		// 1f8b08
63 		if ((signature & 0x00FFFFFF) != 0x00088b1f)
64 		{
65 			mDoc = xmlReadMemory(content.data(), size, path.c_str(), NULL, 0);
66 		}
67 		// Compressed document (gzip only)
68 		else
69 		{
70 			vector<char> decompressed_content(*reinterpret_cast<const uint32_t*>(content.data() + content.size() - 4));
71 #if IS_GNUC_AND_GNUC_VERSION_LT(5,1,1)
72 			z_stream zInfo;
73 			memset(&zInfo, 0, sizeof(zInfo));
74 #else
75 			z_stream zInfo {};
76 #endif
77 			zInfo.total_in = zInfo.avail_in = static_cast<uInt>(content.size());
78 			zInfo.total_out = zInfo.avail_out = static_cast<uInt>(decompressed_content.size());
79 			zInfo.next_in = reinterpret_cast<Bytef*>(content.data());
80 			zInfo.next_out = reinterpret_cast<Bytef*>(decompressed_content.data());
81 
82 			int nErr = inflateInit2(&zInfo, 16 + MAX_WBITS);
83 			if (nErr == Z_OK)
84 			{
85 				nErr = inflate(&zInfo, Z_FINISH);
86 			}
87 			inflateEnd(&zInfo);
88 
89 			if (nErr == Z_STREAM_END)
90 				mDoc = xmlReadMemory(decompressed_content.data(), static_cast<int>(decompressed_content.size()), path.c_str(), NULL, 0);
91 		}
92 
93 		if (mDoc)
94 			mDoc->_private = this;
95 	}
96 
operator bool() const97 	XmlDoc::operator bool() const
98 	{
99 		return mDoc != nullptr;
100 	}
101 
reset()102 	void XmlDoc::reset()
103 	{
104 		mXPathCache.clear();
105 
106 		if (mDoc)
107 		{
108 			xmlFreeDoc(mDoc);
109 			mDoc = nullptr;
110 		}
111 	}
112 
root() const113 	XmlNode XmlDoc::root() const
114 	{
115 		return xmlDocGetRootElement(mDoc);
116 	}
117 
getRootNamespace() const118 	string XmlDoc::getRootNamespace() const
119 	{
120 		if (auto r = root())
121 			if (auto n = r.ns())
122 				return n.href();
123 		return "";
124 	}
125 
setTempRoot(const XmlNode & node) const126 	XmlDoc::TempRootMod XmlDoc::setTempRoot(const XmlNode & node) const
127 	{
128 		TempRootMod trm(mDoc->children);
129 		mDoc->children = node.mNode;
130 		mDoc->last = mDoc->children;
131 		return trm;
132 	}
133 
TempRootMod(const XmlNode & old_root)134 	XmlDoc::TempRootMod::TempRootMod(const XmlNode & old_root)
135 		: mOldDocChildren(old_root.mNode->doc->children)
136 		, mOldDocLast(old_root.mNode->doc->last)
137 	{}
138 
TempRootMod(TempRootMod && other)139 	XmlDoc::TempRootMod::TempRootMod(TempRootMod && other)
140 	{
141 		swap(mOldDocChildren, other.mOldDocChildren);
142 		swap(mOldDocLast, other.mOldDocLast);
143 	}
144 
~TempRootMod()145 	XmlDoc::TempRootMod::~TempRootMod()
146 	{
147 		// Restore old root
148 		if (mOldDocChildren)
149 		{
150 			const XmlDoc & doc = mOldDocChildren.doc();
151 			doc.mDoc->children = mOldDocChildren.mNode;
152 			doc.mDoc->last = mOldDocLast.mNode;
153 		}
154 	}
155 
GetXmlDoc(xmlDocPtr doc)156 	XmlDoc & XmlDoc::GetXmlDoc(xmlDocPtr doc)
157 	{
158 		return *static_cast<XmlDoc*>(doc->_private);
159 	}
160 }
161