|
|
@@ -1,7 +1,7 @@
|
|
|
/**
|
|
|
- * pugixml parser - version 1.10
|
|
|
+ * pugixml parser - version 1.12
|
|
|
* --------------------------------------------------------
|
|
|
- * Copyright (C) 2006-2019, by Arseny Kapoulkine ([email protected])
|
|
|
+ * Copyright (C) 2006-2022, by Arseny Kapoulkine ([email protected])
|
|
|
* Report bugs and download new versions at https://pugixml.org/
|
|
|
*
|
|
|
* This library is distributed under the MIT License. See notice at the end
|
|
|
@@ -132,8 +132,10 @@ using std::memset;
|
|
|
#endif
|
|
|
|
|
|
// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
|
|
|
-#if defined(_MSC_VER) && !defined(__S3E__)
|
|
|
+#if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE)
|
|
|
# define PUGI__MSVC_CRT_VERSION _MSC_VER
|
|
|
+#elif defined(_WIN32_WCE)
|
|
|
+# define PUGI__MSVC_CRT_VERSION 1310 // MSVC7.1
|
|
|
#endif
|
|
|
|
|
|
// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
|
|
|
@@ -378,7 +380,7 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
|
|
|
{
|
|
|
- unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));
|
|
|
+ unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key) & 0xffffffff);
|
|
|
|
|
|
// MurmurHash3 32-bit finalizer
|
|
|
h ^= h >> 16;
|
|
|
@@ -526,7 +528,8 @@ PUGI__NS_BEGIN
|
|
|
xml_memory_page* page = xml_memory_page::construct(memory);
|
|
|
assert(page);
|
|
|
|
|
|
- page->allocator = _root->allocator;
|
|
|
+ assert(this == _root->allocator);
|
|
|
+ page->allocator = this;
|
|
|
|
|
|
return page;
|
|
|
}
|
|
|
@@ -1273,12 +1276,14 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
child->parent = parent;
|
|
|
|
|
|
- if (node->next_sibling)
|
|
|
- node->next_sibling->prev_sibling_c = child;
|
|
|
+ xml_node_struct* next = node->next_sibling;
|
|
|
+
|
|
|
+ if (next)
|
|
|
+ next->prev_sibling_c = child;
|
|
|
else
|
|
|
parent->first_child->prev_sibling_c = child;
|
|
|
|
|
|
- child->next_sibling = node->next_sibling;
|
|
|
+ child->next_sibling = next;
|
|
|
child->prev_sibling_c = node;
|
|
|
|
|
|
node->next_sibling = child;
|
|
|
@@ -1290,12 +1295,14 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
child->parent = parent;
|
|
|
|
|
|
- if (node->prev_sibling_c->next_sibling)
|
|
|
- node->prev_sibling_c->next_sibling = child;
|
|
|
+ xml_node_struct* prev = node->prev_sibling_c;
|
|
|
+
|
|
|
+ if (prev->next_sibling)
|
|
|
+ prev->next_sibling = child;
|
|
|
else
|
|
|
parent->first_child = child;
|
|
|
|
|
|
- child->prev_sibling_c = node->prev_sibling_c;
|
|
|
+ child->prev_sibling_c = prev;
|
|
|
child->next_sibling = node;
|
|
|
|
|
|
node->prev_sibling_c = child;
|
|
|
@@ -1305,15 +1312,18 @@ PUGI__NS_BEGIN
|
|
|
{
|
|
|
xml_node_struct* parent = node->parent;
|
|
|
|
|
|
- if (node->next_sibling)
|
|
|
- node->next_sibling->prev_sibling_c = node->prev_sibling_c;
|
|
|
+ xml_node_struct* next = node->next_sibling;
|
|
|
+ xml_node_struct* prev = node->prev_sibling_c;
|
|
|
+
|
|
|
+ if (next)
|
|
|
+ next->prev_sibling_c = prev;
|
|
|
else
|
|
|
- parent->first_child->prev_sibling_c = node->prev_sibling_c;
|
|
|
+ parent->first_child->prev_sibling_c = prev;
|
|
|
|
|
|
- if (node->prev_sibling_c->next_sibling)
|
|
|
- node->prev_sibling_c->next_sibling = node->next_sibling;
|
|
|
+ if (prev->next_sibling)
|
|
|
+ prev->next_sibling = next;
|
|
|
else
|
|
|
- parent->first_child = node->next_sibling;
|
|
|
+ parent->first_child = next;
|
|
|
|
|
|
node->parent = 0;
|
|
|
node->prev_sibling_c = 0;
|
|
|
@@ -1357,39 +1367,46 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
|
|
|
{
|
|
|
- if (place->next_attribute)
|
|
|
- place->next_attribute->prev_attribute_c = attr;
|
|
|
+ xml_attribute_struct* next = place->next_attribute;
|
|
|
+
|
|
|
+ if (next)
|
|
|
+ next->prev_attribute_c = attr;
|
|
|
else
|
|
|
node->first_attribute->prev_attribute_c = attr;
|
|
|
|
|
|
- attr->next_attribute = place->next_attribute;
|
|
|
+ attr->next_attribute = next;
|
|
|
attr->prev_attribute_c = place;
|
|
|
place->next_attribute = attr;
|
|
|
}
|
|
|
|
|
|
inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
|
|
|
{
|
|
|
- if (place->prev_attribute_c->next_attribute)
|
|
|
- place->prev_attribute_c->next_attribute = attr;
|
|
|
+ xml_attribute_struct* prev = place->prev_attribute_c;
|
|
|
+
|
|
|
+ if (prev->next_attribute)
|
|
|
+ prev->next_attribute = attr;
|
|
|
else
|
|
|
node->first_attribute = attr;
|
|
|
|
|
|
- attr->prev_attribute_c = place->prev_attribute_c;
|
|
|
+ attr->prev_attribute_c = prev;
|
|
|
attr->next_attribute = place;
|
|
|
place->prev_attribute_c = attr;
|
|
|
}
|
|
|
|
|
|
inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)
|
|
|
{
|
|
|
- if (attr->next_attribute)
|
|
|
- attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
|
|
|
+ xml_attribute_struct* next = attr->next_attribute;
|
|
|
+ xml_attribute_struct* prev = attr->prev_attribute_c;
|
|
|
+
|
|
|
+ if (next)
|
|
|
+ next->prev_attribute_c = prev;
|
|
|
else
|
|
|
- node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
|
|
|
+ node->first_attribute->prev_attribute_c = prev;
|
|
|
|
|
|
- if (attr->prev_attribute_c->next_attribute)
|
|
|
- attr->prev_attribute_c->next_attribute = attr->next_attribute;
|
|
|
+ if (prev->next_attribute)
|
|
|
+ prev->next_attribute = next;
|
|
|
else
|
|
|
- node->first_attribute = attr->next_attribute;
|
|
|
+ node->first_attribute = next;
|
|
|
|
|
|
attr->prev_attribute_c = 0;
|
|
|
attr->next_attribute = 0;
|
|
|
@@ -4704,6 +4721,9 @@ PUGI__NS_BEGIN
|
|
|
// get actual encoding
|
|
|
xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
|
|
|
|
|
|
+ // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it
|
|
|
+ auto_deleter<void> contents_guard(own ? contents : 0, xml_memory::deallocate);
|
|
|
+
|
|
|
// get private buffer
|
|
|
char_t* buffer = 0;
|
|
|
size_t length = 0;
|
|
|
@@ -4711,6 +4731,9 @@ PUGI__NS_BEGIN
|
|
|
// coverity[var_deref_model]
|
|
|
if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
|
|
|
|
|
|
+ // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it
|
|
|
+ contents_guard.release();
|
|
|
+
|
|
|
// delete original buffer if we performed a conversion
|
|
|
if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
|
|
|
|
|
|
@@ -4732,7 +4755,7 @@ PUGI__NS_BEGIN
|
|
|
// we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
|
|
|
PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
|
|
|
{
|
|
|
- #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
|
|
|
+ #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
|
|
|
// there are 64-bit versions of fseek/ftell, let's use them
|
|
|
typedef __int64 length_type;
|
|
|
|
|
|
@@ -4981,7 +5004,12 @@ PUGI__NS_BEGIN
|
|
|
#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
|
|
|
PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
|
|
|
{
|
|
|
+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
|
|
|
+ FILE* file = 0;
|
|
|
+ return _wfopen_s(&file, path, mode) == 0 ? file : 0;
|
|
|
+#else
|
|
|
return _wfopen(path, mode);
|
|
|
+#endif
|
|
|
}
|
|
|
#else
|
|
|
PUGI__FN char* convert_path_heap(const wchar_t* str)
|
|
|
@@ -5025,6 +5053,16 @@ PUGI__NS_BEGIN
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+ PUGI__FN FILE* open_file(const char* path, const char* mode)
|
|
|
+ {
|
|
|
+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
|
|
|
+ FILE* file = 0;
|
|
|
+ return fopen_s(&file, path, mode) == 0 ? file : 0;
|
|
|
+#else
|
|
|
+ return fopen(path, mode);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
|
|
|
{
|
|
|
if (!file) return false;
|
|
|
@@ -5167,53 +5205,72 @@ namespace pugi
|
|
|
|
|
|
PUGI__FN xml_attribute xml_attribute::next_attribute() const
|
|
|
{
|
|
|
- return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
|
|
|
+ if (!_attr) return xml_attribute();
|
|
|
+ return xml_attribute(_attr->next_attribute);
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_attribute xml_attribute::previous_attribute() const
|
|
|
{
|
|
|
- return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();
|
|
|
+ if (!_attr) return xml_attribute();
|
|
|
+ xml_attribute_struct* prev = _attr->prev_attribute_c;
|
|
|
+ return prev->next_attribute ? xml_attribute(prev) : xml_attribute();
|
|
|
}
|
|
|
|
|
|
PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? _attr->value + 0 : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? value : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN int xml_attribute::as_int(int def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_int(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_uint(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN double xml_attribute::as_double(double def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_double(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN float xml_attribute::as_float(float def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_float(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN bool xml_attribute::as_bool(bool def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_bool(value) : def;
|
|
|
}
|
|
|
|
|
|
#ifdef PUGIXML_HAS_LONG_LONG
|
|
|
PUGI__FN long long xml_attribute::as_llong(long long def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_llong(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
|
|
|
+ if (!_attr) return def;
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? impl::get_value_ullong(value) : def;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
@@ -5224,12 +5281,16 @@ namespace pugi
|
|
|
|
|
|
PUGI__FN const char_t* xml_attribute::name() const
|
|
|
{
|
|
|
- return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
|
|
|
+ if (!_attr) return PUGIXML_TEXT("");
|
|
|
+ const char_t* name = _attr->name;
|
|
|
+ return name ? name : PUGIXML_TEXT("");
|
|
|
}
|
|
|
|
|
|
PUGI__FN const char_t* xml_attribute::value() const
|
|
|
{
|
|
|
- return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
|
|
|
+ if (!_attr) return PUGIXML_TEXT("");
|
|
|
+ const char_t* value = _attr->value;
|
|
|
+ return value ? value : PUGIXML_TEXT("");
|
|
|
}
|
|
|
|
|
|
PUGI__FN size_t xml_attribute::hash_value() const
|
|
|
@@ -5503,7 +5564,9 @@ namespace pugi
|
|
|
|
|
|
PUGI__FN const char_t* xml_node::name() const
|
|
|
{
|
|
|
- return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
|
|
|
+ if (!_root) return PUGIXML_TEXT("");
|
|
|
+ const char_t* name = _root->name;
|
|
|
+ return name ? name : PUGIXML_TEXT("");
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_node_type xml_node::type() const
|
|
|
@@ -5513,7 +5576,9 @@ namespace pugi
|
|
|
|
|
|
PUGI__FN const char_t* xml_node::value() const
|
|
|
{
|
|
|
- return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
|
|
|
+ if (!_root) return PUGIXML_TEXT("");
|
|
|
+ const char_t* value = _root->value;
|
|
|
+ return value ? value : PUGIXML_TEXT("");
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_node xml_node::child(const char_t* name_) const
|
|
|
@@ -5521,7 +5586,11 @@ namespace pugi
|
|
|
if (!_root) return xml_node();
|
|
|
|
|
|
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
|
|
|
- if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
+ return xml_node(i);
|
|
|
+ }
|
|
|
|
|
|
return xml_node();
|
|
|
}
|
|
|
@@ -5531,8 +5600,11 @@ namespace pugi
|
|
|
if (!_root) return xml_attribute();
|
|
|
|
|
|
for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
|
|
|
- if (i->name && impl::strequal(name_, i->name))
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
return xml_attribute(i);
|
|
|
+ }
|
|
|
|
|
|
return xml_attribute();
|
|
|
}
|
|
|
@@ -5542,7 +5614,11 @@ namespace pugi
|
|
|
if (!_root) return xml_node();
|
|
|
|
|
|
for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
|
|
|
- if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
+ return xml_node(i);
|
|
|
+ }
|
|
|
|
|
|
return xml_node();
|
|
|
}
|
|
|
@@ -5557,7 +5633,11 @@ namespace pugi
|
|
|
if (!_root) return xml_node();
|
|
|
|
|
|
for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
|
|
|
- if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
+ return xml_node(i);
|
|
|
+ }
|
|
|
|
|
|
return xml_node();
|
|
|
}
|
|
|
@@ -5573,24 +5653,30 @@ namespace pugi
|
|
|
|
|
|
// optimistically search from hint up until the end
|
|
|
for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
|
|
|
- if (i->name && impl::strequal(name_, i->name))
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
{
|
|
|
// update hint to maximize efficiency of searching for consecutive attributes
|
|
|
hint_._attr = i->next_attribute;
|
|
|
|
|
|
return xml_attribute(i);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
// wrap around and search from the first attribute until the hint
|
|
|
// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
|
|
|
for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
|
|
|
- if (j->name && impl::strequal(name_, j->name))
|
|
|
+ {
|
|
|
+ const char_t* jname = j->name;
|
|
|
+ if (jname && impl::strequal(name_, jname))
|
|
|
{
|
|
|
// update hint to maximize efficiency of searching for consecutive attributes
|
|
|
hint_._attr = j->next_attribute;
|
|
|
|
|
|
return xml_attribute(j);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
return xml_attribute();
|
|
|
}
|
|
|
@@ -5598,9 +5684,8 @@ namespace pugi
|
|
|
PUGI__FN xml_node xml_node::previous_sibling() const
|
|
|
{
|
|
|
if (!_root) return xml_node();
|
|
|
-
|
|
|
- if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
|
|
|
- else return xml_node();
|
|
|
+ xml_node_struct* prev = _root->prev_sibling_c;
|
|
|
+ return prev->next_sibling ? xml_node(prev) : xml_node();
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_node xml_node::parent() const
|
|
|
@@ -5627,8 +5712,11 @@ namespace pugi
|
|
|
return _root->value;
|
|
|
|
|
|
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
|
|
|
- if (impl::is_text_node(i) && i->value)
|
|
|
- return i->value;
|
|
|
+ {
|
|
|
+ const char_t* ivalue = i->value;
|
|
|
+ if (impl::is_text_node(i) && ivalue)
|
|
|
+ return ivalue;
|
|
|
+ }
|
|
|
|
|
|
return PUGIXML_TEXT("");
|
|
|
}
|
|
|
@@ -5640,22 +5728,28 @@ namespace pugi
|
|
|
|
|
|
PUGI__FN xml_attribute xml_node::first_attribute() const
|
|
|
{
|
|
|
- return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
|
|
|
+ if (!_root) return xml_attribute();
|
|
|
+ return xml_attribute(_root->first_attribute);
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_attribute xml_node::last_attribute() const
|
|
|
{
|
|
|
- return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();
|
|
|
+ if (!_root) return xml_attribute();
|
|
|
+ xml_attribute_struct* first = _root->first_attribute;
|
|
|
+ return first ? xml_attribute(first->prev_attribute_c) : xml_attribute();
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_node xml_node::first_child() const
|
|
|
{
|
|
|
- return _root ? xml_node(_root->first_child) : xml_node();
|
|
|
+ if (!_root) return xml_node();
|
|
|
+ return xml_node(_root->first_child);
|
|
|
}
|
|
|
|
|
|
PUGI__FN xml_node xml_node::last_child() const
|
|
|
{
|
|
|
- return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();
|
|
|
+ if (!_root) return xml_node();
|
|
|
+ xml_node_struct* first = _root->first_child;
|
|
|
+ return first ? xml_node(first->prev_sibling_c) : xml_node();
|
|
|
}
|
|
|
|
|
|
PUGI__FN bool xml_node::set_name(const char_t* rhs)
|
|
|
@@ -6127,13 +6221,13 @@ namespace pugi
|
|
|
impl::xml_allocator& alloc = impl::get_allocator(_root);
|
|
|
if (!alloc.reserve()) return false;
|
|
|
|
|
|
- for (xml_node_struct* child = _root->first_child; child; )
|
|
|
+ for (xml_node_struct* cur = _root->first_child; cur; )
|
|
|
{
|
|
|
- xml_node_struct* next = child->next_sibling;
|
|
|
+ xml_node_struct* next = cur->next_sibling;
|
|
|
|
|
|
- impl::destroy_node(child, alloc);
|
|
|
+ impl::destroy_node(cur, alloc);
|
|
|
|
|
|
- child = next;
|
|
|
+ cur = next;
|
|
|
}
|
|
|
|
|
|
_root->first_child = 0;
|
|
|
@@ -6181,12 +6275,22 @@ namespace pugi
|
|
|
if (!_root) return xml_node();
|
|
|
|
|
|
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
|
|
|
- if (i->name && impl::strequal(name_, i->name))
|
|
|
+ {
|
|
|
+ const char_t* iname = i->name;
|
|
|
+ if (iname && impl::strequal(name_, iname))
|
|
|
{
|
|
|
for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
|
|
|
- if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
|
|
|
- return xml_node(i);
|
|
|
+ {
|
|
|
+ const char_t* aname = a->name;
|
|
|
+ if (aname && impl::strequal(attr_name, aname))
|
|
|
+ {
|
|
|
+ const char_t* avalue = a->value;
|
|
|
+ if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT("")))
|
|
|
+ return xml_node(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
return xml_node();
|
|
|
}
|
|
|
@@ -6197,8 +6301,15 @@ namespace pugi
|
|
|
|
|
|
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
|
|
|
for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
|
|
|
- if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
|
|
|
- return xml_node(i);
|
|
|
+ {
|
|
|
+ const char_t* aname = a->name;
|
|
|
+ if (aname && impl::strequal(attr_name, aname))
|
|
|
+ {
|
|
|
+ const char_t* avalue = a->value;
|
|
|
+ if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT("")))
|
|
|
+ return xml_node(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return xml_node();
|
|
|
}
|
|
|
@@ -6212,8 +6323,9 @@ namespace pugi
|
|
|
|
|
|
for (xml_node_struct* i = _root; i; i = i->parent)
|
|
|
{
|
|
|
+ const char_t* iname = i->name;
|
|
|
offset += (i != _root);
|
|
|
- offset += i->name ? impl::strlength(i->name) : 0;
|
|
|
+ offset += iname ? impl::strlength(iname) : 0;
|
|
|
}
|
|
|
|
|
|
string_t result;
|
|
|
@@ -6224,12 +6336,13 @@ namespace pugi
|
|
|
if (j != _root)
|
|
|
result[--offset] = delimiter;
|
|
|
|
|
|
- if (j->name)
|
|
|
+ const char_t* jname = j->name;
|
|
|
+ if (jname)
|
|
|
{
|
|
|
- size_t length = impl::strlength(j->name);
|
|
|
+ size_t length = impl::strlength(jname);
|
|
|
|
|
|
offset -= length;
|
|
|
- memcpy(&result[offset], j->name, length * sizeof(char_t));
|
|
|
+ memcpy(&result[offset], jname, length * sizeof(char_t));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -6267,7 +6380,8 @@ namespace pugi
|
|
|
{
|
|
|
for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling)
|
|
|
{
|
|
|
- if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
|
|
|
+ const char_t* jname = j->name;
|
|
|
+ if (jname && impl::strequalrange(jname, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
|
|
|
{
|
|
|
xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
|
|
|
|
|
|
@@ -6459,65 +6573,74 @@ namespace pugi
|
|
|
PUGI__FN const char_t* xml_text::get() const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
|
|
|
+ if (!d) return PUGIXML_TEXT("");
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? value : PUGIXML_TEXT("");
|
|
|
}
|
|
|
|
|
|
PUGI__FN const char_t* xml_text::as_string(const char_t* def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? d->value + 0 : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? value : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN int xml_text::as_int(int def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_int(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_int(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_uint(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_uint(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN double xml_text::as_double(double def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_double(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_double(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN float xml_text::as_float(float def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_float(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_float(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN bool xml_text::as_bool(bool def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_bool(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_bool(value) : def;
|
|
|
}
|
|
|
|
|
|
#ifdef PUGIXML_HAS_LONG_LONG
|
|
|
PUGI__FN long long xml_text::as_llong(long long def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_llong(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_llong(value) : def;
|
|
|
}
|
|
|
|
|
|
PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
|
|
|
{
|
|
|
xml_node_struct* d = _data();
|
|
|
-
|
|
|
- return (d && d->value) ? impl::get_value_ullong(d->value) : def;
|
|
|
+ if (!d) return def;
|
|
|
+ const char_t* value = d->value;
|
|
|
+ return value ? impl::get_value_ullong(value) : def;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
@@ -6720,7 +6843,7 @@ namespace pugi
|
|
|
return const_cast<xml_node*>(&_wrap); // BCC5 workaround
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_node_iterator& xml_node_iterator::operator++()
|
|
|
+ PUGI__FN xml_node_iterator& xml_node_iterator::operator++()
|
|
|
{
|
|
|
assert(_wrap._root);
|
|
|
_wrap._root = _wrap._root->next_sibling;
|
|
|
@@ -6734,7 +6857,7 @@ namespace pugi
|
|
|
return temp;
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_node_iterator& xml_node_iterator::operator--()
|
|
|
+ PUGI__FN xml_node_iterator& xml_node_iterator::operator--()
|
|
|
{
|
|
|
_wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();
|
|
|
return *this;
|
|
|
@@ -6781,7 +6904,7 @@ namespace pugi
|
|
|
return const_cast<xml_attribute*>(&_wrap); // BCC5 workaround
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++()
|
|
|
+ PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator++()
|
|
|
{
|
|
|
assert(_wrap._attr);
|
|
|
_wrap._attr = _wrap._attr->next_attribute;
|
|
|
@@ -6795,7 +6918,7 @@ namespace pugi
|
|
|
return temp;
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--()
|
|
|
+ PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator--()
|
|
|
{
|
|
|
_wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();
|
|
|
return *this;
|
|
|
@@ -6842,7 +6965,7 @@ namespace pugi
|
|
|
return const_cast<xml_node*>(&_wrap); // BCC5 workaround
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
|
|
|
+ PUGI__FN xml_named_node_iterator& xml_named_node_iterator::operator++()
|
|
|
{
|
|
|
assert(_wrap._root);
|
|
|
_wrap = _wrap.next_sibling(_name);
|
|
|
@@ -6856,7 +6979,7 @@ namespace pugi
|
|
|
return temp;
|
|
|
}
|
|
|
|
|
|
- PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--()
|
|
|
+ PUGI__FN xml_named_node_iterator& xml_named_node_iterator::operator--()
|
|
|
{
|
|
|
if (_wrap._root)
|
|
|
_wrap = _wrap.previous_sibling(_name);
|
|
|
@@ -7076,8 +7199,12 @@ namespace pugi
|
|
|
#endif
|
|
|
|
|
|
// move allocation state
|
|
|
- doc->_root = other->_root;
|
|
|
- doc->_busy_size = other->_busy_size;
|
|
|
+ // note that other->_root may point to the embedded document page, in which case we should keep original (empty) state
|
|
|
+ if (other->_root != PUGI__GETPAGE(other))
|
|
|
+ {
|
|
|
+ doc->_root = other->_root;
|
|
|
+ doc->_busy_size = other->_busy_size;
|
|
|
+ }
|
|
|
|
|
|
// move buffer state
|
|
|
doc->buffer = other->buffer;
|
|
|
@@ -7187,7 +7314,7 @@ namespace pugi
|
|
|
reset();
|
|
|
|
|
|
using impl::auto_deleter; // MSVC7 workaround
|
|
|
- auto_deleter<FILE> file(fopen(path_, "rb"), impl::close_file);
|
|
|
+ auto_deleter<FILE> file(impl::open_file(path_, "rb"), impl::close_file);
|
|
|
|
|
|
return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
|
|
|
}
|
|
|
@@ -7270,7 +7397,7 @@ namespace pugi
|
|
|
PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
|
|
|
{
|
|
|
using impl::auto_deleter; // MSVC7 workaround
|
|
|
- auto_deleter<FILE> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
|
|
|
+ auto_deleter<FILE> file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
|
|
|
|
|
|
return impl::save_file_impl(*this, file.data, indent, flags, encoding);
|
|
|
}
|
|
|
@@ -8250,7 +8377,7 @@ PUGI__NS_BEGIN
|
|
|
}
|
|
|
|
|
|
// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
|
|
|
-#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
|
|
|
+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
|
|
|
PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
|
|
|
{
|
|
|
// get base values
|
|
|
@@ -11143,6 +11270,14 @@ PUGI__NS_BEGIN
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ static const size_t xpath_ast_depth_limit =
|
|
|
+ #ifdef PUGIXML_XPATH_DEPTH_LIMIT
|
|
|
+ PUGIXML_XPATH_DEPTH_LIMIT
|
|
|
+ #else
|
|
|
+ 1024
|
|
|
+ #endif
|
|
|
+ ;
|
|
|
+
|
|
|
struct xpath_parser
|
|
|
{
|
|
|
xpath_allocator* _alloc;
|
|
|
@@ -11155,6 +11290,8 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
char_t _scratch[32];
|
|
|
|
|
|
+ size_t _depth;
|
|
|
+
|
|
|
xpath_ast_node* error(const char* message)
|
|
|
{
|
|
|
_result->error = message;
|
|
|
@@ -11171,6 +11308,11 @@ PUGI__NS_BEGIN
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ xpath_ast_node* error_rec()
|
|
|
+ {
|
|
|
+ return error("Exceeded maximum allowed query depth");
|
|
|
+ }
|
|
|
+
|
|
|
void* alloc_node()
|
|
|
{
|
|
|
return _alloc->allocate(sizeof(xpath_ast_node));
|
|
|
@@ -11526,6 +11668,8 @@ PUGI__NS_BEGIN
|
|
|
return error("Unrecognized function call");
|
|
|
_lexer.next();
|
|
|
|
|
|
+ size_t old_depth = _depth;
|
|
|
+
|
|
|
while (_lexer.current() != lex_close_brace)
|
|
|
{
|
|
|
if (argc > 0)
|
|
|
@@ -11535,6 +11679,9 @@ PUGI__NS_BEGIN
|
|
|
_lexer.next();
|
|
|
}
|
|
|
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
xpath_ast_node* n = parse_expression();
|
|
|
if (!n) return 0;
|
|
|
|
|
|
@@ -11547,6 +11694,8 @@ PUGI__NS_BEGIN
|
|
|
|
|
|
_lexer.next();
|
|
|
|
|
|
+ _depth = old_depth;
|
|
|
+
|
|
|
return parse_function(function, argc, args);
|
|
|
}
|
|
|
|
|
|
@@ -11563,10 +11712,15 @@ PUGI__NS_BEGIN
|
|
|
xpath_ast_node* n = parse_primary_expression();
|
|
|
if (!n) return 0;
|
|
|
|
|
|
+ size_t old_depth = _depth;
|
|
|
+
|
|
|
while (_lexer.current() == lex_open_square_brace)
|
|
|
{
|
|
|
_lexer.next();
|
|
|
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
if (n->rettype() != xpath_type_node_set)
|
|
|
return error("Predicate has to be applied to node set");
|
|
|
|
|
|
@@ -11582,6 +11736,8 @@ PUGI__NS_BEGIN
|
|
|
_lexer.next();
|
|
|
}
|
|
|
|
|
|
+ _depth = old_depth;
|
|
|
+
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
@@ -11733,12 +11889,17 @@ PUGI__NS_BEGIN
|
|
|
xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy);
|
|
|
if (!n) return 0;
|
|
|
|
|
|
+ size_t old_depth = _depth;
|
|
|
+
|
|
|
xpath_ast_node* last = 0;
|
|
|
|
|
|
while (_lexer.current() == lex_open_square_brace)
|
|
|
{
|
|
|
_lexer.next();
|
|
|
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
xpath_ast_node* expr = parse_expression();
|
|
|
if (!expr) return 0;
|
|
|
|
|
|
@@ -11755,6 +11916,8 @@ PUGI__NS_BEGIN
|
|
|
last = pred;
|
|
|
}
|
|
|
|
|
|
+ _depth = old_depth;
|
|
|
+
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
@@ -11764,6 +11927,8 @@ PUGI__NS_BEGIN
|
|
|
xpath_ast_node* n = parse_step(set);
|
|
|
if (!n) return 0;
|
|
|
|
|
|
+ size_t old_depth = _depth;
|
|
|
+
|
|
|
while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
|
|
|
{
|
|
|
lexeme_t l = _lexer.current();
|
|
|
@@ -11773,12 +11938,19 @@ PUGI__NS_BEGIN
|
|
|
{
|
|
|
n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
|
|
|
if (!n) return 0;
|
|
|
+
|
|
|
+ ++_depth;
|
|
|
}
|
|
|
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
n = parse_step(n);
|
|
|
if (!n) return 0;
|
|
|
}
|
|
|
|
|
|
+ _depth = old_depth;
|
|
|
+
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
@@ -11964,6 +12136,9 @@ PUGI__NS_BEGIN
|
|
|
{
|
|
|
_lexer.next();
|
|
|
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
xpath_ast_node* rhs = parse_path_or_unary_expression();
|
|
|
if (!rhs) return 0;
|
|
|
|
|
|
@@ -12009,13 +12184,22 @@ PUGI__NS_BEGIN
|
|
|
// | MultiplicativeExpr 'mod' UnaryExpr
|
|
|
xpath_ast_node* parse_expression(int limit = 0)
|
|
|
{
|
|
|
+ size_t old_depth = _depth;
|
|
|
+
|
|
|
+ if (++_depth > xpath_ast_depth_limit)
|
|
|
+ return error_rec();
|
|
|
+
|
|
|
xpath_ast_node* n = parse_path_or_unary_expression();
|
|
|
if (!n) return 0;
|
|
|
|
|
|
- return parse_expression_rec(n, limit);
|
|
|
+ n = parse_expression_rec(n, limit);
|
|
|
+
|
|
|
+ _depth = old_depth;
|
|
|
+
|
|
|
+ return n;
|
|
|
}
|
|
|
|
|
|
- xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
|
|
|
+ xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
@@ -12024,6 +12208,8 @@ PUGI__NS_BEGIN
|
|
|
xpath_ast_node* n = parse_expression();
|
|
|
if (!n) return 0;
|
|
|
|
|
|
+ assert(_depth == 0);
|
|
|
+
|
|
|
// check if there are unparsed tokens left
|
|
|
if (_lexer.current() != lex_eof)
|
|
|
return error("Incorrect query");
|
|
|
@@ -12923,7 +13109,7 @@ namespace pugi
|
|
|
#endif
|
|
|
|
|
|
/**
|
|
|
- * Copyright (c) 2006-2019 Arseny Kapoulkine
|
|
|
+ * Copyright (c) 2006-2022 Arseny Kapoulkine
|
|
|
*
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
* obtaining a copy of this software and associated documentation
|