diff --git a/.travis.yml b/.travis.yml index 48c02f7..5e6a043 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,7 @@ notifications: - "irc.esper.net#bitblot" skip_join: true use_notice: true + on_success: change template: #- "[%{commit}: %{author}] %{message}" #- "%{build_url}" diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 19f3b74..e06aaf9 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -9611,7 +9611,7 @@ luaFunc(loadXMLTable) tinyxml2::XMLDocument xml; tinyxml2::XMLError err = readXML(fn, xml); - if(err != tinyxml2::XML_NO_ERROR) + if(err != tinyxml2::XML_SUCCESS) { lua_pushboolean(L, false); lua_pushinteger(L, err); diff --git a/ExternalLibs/tinyxml2.cpp b/ExternalLibs/tinyxml2.cpp index bb5fadb..295a3ab 100644 --- a/ExternalLibs/tinyxml2.cpp +++ b/ExternalLibs/tinyxml2.cpp @@ -21,18 +21,86 @@ must not be misrepresented as being the original software. distribution. */ -// Disabled LoadFile() functions to catch misuse - Aquaria uses its own VFS, not FILE* -- fg - - #include "tinyxml2.h" #include // yes, this one new style header, is in the Android SDK. -# ifdef ANDROID_NDK +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include +# include #else # include +# include #endif +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + + static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF static const char LF = LINE_FEED; static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out @@ -48,22 +116,6 @@ static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -#define DELETE_NODE( node ) { \ - if ( node ) { \ - MemPool* pool = node->_memPool; \ - node->~XMLNode(); \ - pool->Free( node ); \ - } \ - } -#define DELETE_ATTRIBUTE( attrib ) { \ - if ( attrib ) { \ - MemPool* pool = attrib->_memPool; \ - attrib->~XMLAttribute(); \ - pool->Free( attrib ); \ - } \ - } - namespace tinyxml2 { @@ -89,6 +141,29 @@ StrPair::~StrPair() } +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + void StrPair::Reset() { if ( _flags & NEEDS_DELETE ) { @@ -102,8 +177,10 @@ void StrPair::Reset() void StrPair::SetStr( const char* str, int flags ) { + TIXMLASSERT( str ); Reset(); size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); _start = new char[ len+1 ]; memcpy( _start, str, len+1 ); _end = _start + len; @@ -115,7 +192,7 @@ char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) { TIXMLASSERT( endTag && *endTag ); - char* start = p; // fixme: hides a member + char* start = p; char endChar = *endTag; size_t length = strlen( endTag ); @@ -133,30 +210,32 @@ char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) char* StrPair::ParseName( char* p ) { - char* start = p; - - if ( !start || !(*start) ) { + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( *p ) ) { return 0; } - while( *p && ( p == start ? XMLUtil::IsNameStartChar( *p ) : XMLUtil::IsNameChar( *p ) )) { + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( *p ) ) { ++p; } - if ( p > start ) { - Set( start, p, 0 ); - return p; - } - return 0; + Set( start, p, 0 ); + return p; } void StrPair::CollapseWhitespace() { + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); // Trim leading space. _start = XMLUtil::SkipWhiteSpace( _start ); - if ( _start && *_start ) { + if ( *_start ) { char* p = _start; // the read pointer char* q = _start; // the write pointer @@ -180,6 +259,8 @@ void StrPair::CollapseWhitespace() const char* StrPair::GetStr() { + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); if ( _flags & NEEDS_FLUSH ) { *_end = 0; _flags ^= NEEDS_FLUSH; @@ -217,27 +298,38 @@ const char* StrPair::GetStr() // 中 or 中 if ( *(p+1) == '#' ) { - char buf[10] = { 0 }; - int len; - p = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); - for( int i=0; i( XMLUtil::GetCharacterRef( p, buf, &len ) ); + if ( adjusted == 0 ) { + *q = *p; + ++p; + ++q; + } + else { + TIXMLASSERT( 0 <= len && len <= buflen ); + TIXMLASSERT( q + len <= adjusted ); + p = adjusted; + memcpy( q, buf, len ); + q += len; } - TIXMLASSERT( q <= p ); } else { - int i=0; - for(; i(p); // Check for BOM: @@ -278,6 +373,7 @@ const char* XMLUtil::ReadBOM( const char* p, bool* bom ) *bom = true; p += 3; } + TIXMLASSERT( p ); return p; } @@ -301,7 +397,7 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length *length = 4; } else { - *length = 0; // This code won't covert this correctly anyway. + *length = 0; // This code won't convert this correctly anyway. return; } @@ -324,8 +420,9 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); - default: break; + default: + TIXMLASSERT( false ); } } @@ -337,65 +434,83 @@ const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) if ( *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; + TIXMLASSERT( sizeof( ucs ) >= 4 ); ptrdiff_t delta = 0; unsigned mult = 1; + static const char SEMICOLON = ';'; if ( *(p+2) == 'x' ) { // Hexadecimal. - if ( !*(p+3) ) { - return 0; - } - const char* q = p+3; - q = strchr( q, ';' ); - - if ( !q || !*q ) { + if ( !(*q) ) { return 0; } + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + delta = q-p; --q; while ( *q != 'x' ) { + unsigned int digit = 0; + if ( *q >= '0' && *q <= '9' ) { - ucs += mult * (*q - '0'); + digit = *q - '0'; } else if ( *q >= 'a' && *q <= 'f' ) { - ucs += mult * (*q - 'a' + 10); + digit = *q - 'a' + 10; } else if ( *q >= 'A' && *q <= 'F' ) { - ucs += mult * (*q - 'A' + 10 ); + digit = *q - 'A' + 10; } else { return 0; } + TIXMLASSERT( digit < 16 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + TIXMLASSERT( mult <= UINT_MAX / 16 ); mult *= 16; --q; } } else { // Decimal. - if ( !*(p+2) ) { - return 0; - } - const char* q = p+2; - q = strchr( q, ';' ); - - if ( !q || !*q ) { + if ( !(*q) ) { return 0; } + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) { - ucs += mult * (*q - '0'); + const unsigned int digit = *q - '0'; + TIXMLASSERT( digit < 10 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; } else { return 0; } + TIXMLASSERT( mult <= UINT_MAX / 10 ); mult *= 10; --q; } @@ -441,6 +556,13 @@ void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) } +void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) +{ + // horrible syntax trick to make the compiler happy about %lld + TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); +} + + bool XMLUtil::ToInt( const char* str, int* value ) { if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { @@ -484,6 +606,7 @@ bool XMLUtil::ToFloat( const char* str, float* value ) return false; } + bool XMLUtil::ToDouble( const char* str, double* value ) { if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { @@ -493,49 +616,59 @@ bool XMLUtil::ToDouble( const char* str, double* value ) } +bool XMLUtil::ToInt64(const char* str, int64_t* value) +{ + long long v = 0; // horrible syntax trick to make the compiler happy about %lld + if (TIXML_SSCANF(str, "%lld", &v) == 1) { + *value = (int64_t)v; + return true; + } + return false; +} + + char* XMLDocument::Identify( char* p, XMLNode** node ) { - XMLNode* returnNode = 0; - char* start = p; + TIXMLASSERT( node ); + TIXMLASSERT( p ); + char* const start = p; p = XMLUtil::SkipWhiteSpace( p ); - if( !p || !*p ) { + if( !*p ) { + *node = 0; + TIXMLASSERT( p ); return p; } - // What is this thing? - // These strings define the matching patters: + // These strings define the matching patterns: static const char* xmlHeader = { "_memPool = &_commentPool; p += xmlHeaderLen; } else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); returnNode = new (_commentPool.Alloc()) XMLComment( this ); returnNode->_memPool = &_commentPool; p += commentHeaderLen; } else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); XMLText* text = new (_textPool.Alloc()) XMLText( this ); returnNode = text; returnNode->_memPool = &_textPool; @@ -543,21 +676,26 @@ char* XMLDocument::Identify( char* p, XMLNode** node ) text->SetCData( true ); } else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); returnNode->_memPool = &_commentPool; p += dtdHeaderLen; } else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); returnNode = new (_elementPool.Alloc()) XMLElement( this ); returnNode->_memPool = &_elementPool; p += elementHeaderLen; } else { + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); returnNode = new (_textPool.Alloc()) XMLText( this ); returnNode->_memPool = &_textPool; p = start; // Back it up, all the text counts. } + TIXMLASSERT( returnNode ); + TIXMLASSERT( p ); *node = returnNode; return p; } @@ -565,6 +703,7 @@ char* XMLDocument::Identify( char* p, XMLNode** node ) bool XMLDocument::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { @@ -583,6 +722,7 @@ XMLNode::XMLNode( XMLDocument* doc ) : _parent( 0 ), _firstChild( 0 ), _lastChild( 0 ), _prev( 0 ), _next( 0 ), + _userData( 0 ), _memPool( 0 ) { } @@ -598,6 +738,9 @@ XMLNode::~XMLNode() const char* XMLNode::Value() const { + // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr. + if ( this->ToDocument() ) + return 0; return _value.GetStr(); } @@ -615,10 +758,12 @@ void XMLNode::SetValue( const char* str, bool staticMem ) void XMLNode::DeleteChildren() { while( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_document == _document ); XMLNode* node = _firstChild; Unlink( node ); - DELETE_NODE( node ); + DeleteNode( node ); } _firstChild = _lastChild = 0; } @@ -626,6 +771,9 @@ void XMLNode::DeleteChildren() void XMLNode::Unlink( XMLNode* child ) { + TIXMLASSERT( child ); + TIXMLASSERT( child->_document == _document ); + TIXMLASSERT( child->_parent == this ); if ( child == _firstChild ) { _firstChild = _firstChild->_next; } @@ -645,23 +793,22 @@ void XMLNode::Unlink( XMLNode* child ) void XMLNode::DeleteChild( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT( node->_document == _document ); TIXMLASSERT( node->_parent == this ); - DELETE_NODE( node ); + Unlink( node ); + DeleteNode( node ); } XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) { - if (addThis->_document != _document) - { - TIXMLASSERT( false ); - return 0; - } - - if (addThis->_parent) - addThis->_parent->Unlink( addThis ); - else - addThis->_memPool->SetTracked(); + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); if ( _lastChild ) { TIXMLASSERT( _firstChild ); @@ -686,16 +833,12 @@ XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) { - if (addThis->_document != _document) - { - TIXMLASSERT( false ); - return 0; - } - - if (addThis->_parent) - addThis->_parent->Unlink( addThis ); - else - addThis->_memPool->SetTracked(); + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); if ( _firstChild ) { TIXMLASSERT( _lastChild ); @@ -715,22 +858,22 @@ XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) addThis->_next = 0; } addThis->_parent = this; - return addThis; + return addThis; } XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) { - if (addThis->_document != _document) - { - TIXMLASSERT( false ); - return 0; - } + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } - TIXMLASSERT( afterThis->_parent == this ); + TIXMLASSERT( afterThis ); if ( afterThis->_parent != this ) { - TIXMLASSERT( false ); + TIXMLASSERT( false ); return 0; } @@ -738,10 +881,7 @@ XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) // The last node or the only node. return InsertEndChild( addThis ); } - if (addThis->_parent) - addThis->_parent->Unlink( addThis ); - else - addThis->_memPool->SetTracked(); + InsertChildPreamble( addThis ); addThis->_prev = afterThis; addThis->_next = afterThis->_next; afterThis->_next->_prev = addThis; @@ -753,12 +893,12 @@ XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) -const XMLElement* XMLNode::FirstChildElement( const char* value ) const +const XMLElement* XMLNode::FirstChildElement( const char* name ) const { - for( XMLNode* node=_firstChild; node; node=node->_next ) { - XMLElement* element = node->ToElement(); + for( const XMLNode* node = _firstChild; node; node = node->_next ) { + const XMLElement* element = node->ToElement(); if ( element ) { - if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { + if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { return element; } } @@ -767,12 +907,12 @@ const XMLElement* XMLNode::FirstChildElement( const char* value ) const } -const XMLElement* XMLNode::LastChildElement( const char* value ) const +const XMLElement* XMLNode::LastChildElement( const char* name ) const { - for( XMLNode* node=_lastChild; node; node=node->_prev ) { - XMLElement* element = node->ToElement(); + for( const XMLNode* node = _lastChild; node; node = node->_prev ) { + const XMLElement* element = node->ToElement(); if ( element ) { - if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { + if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { return element; } } @@ -781,24 +921,26 @@ const XMLElement* XMLNode::LastChildElement( const char* value ) const } -const XMLElement* XMLNode::NextSiblingElement( const char* value ) const +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const { - for( XMLNode* element=this->_next; element; element = element->_next ) { - if ( element->ToElement() - && (!value || XMLUtil::StringEqual( value, element->Value() ))) { - return element->ToElement(); + for( const XMLNode* node = _next; node; node = node->_next ) { + const XMLElement* element = node->ToElement(); + if ( element + && (!name || XMLUtil::StringEqual( name, element->Name() ))) { + return element; } } return 0; } -const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const { - for( XMLNode* element=_prev; element; element = element->_prev ) { - if ( element->ToElement() - && (!value || XMLUtil::StringEqual( value, element->Value() ))) { - return element->ToElement(); + for( const XMLNode* node = _prev; node; node = node->_prev ) { + const XMLElement* element = node->ToElement(); + if ( element + && (!name || XMLUtil::StringEqual( name, element->Name() ))) { + return element; } } return 0; @@ -828,61 +970,91 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) XMLNode* node = 0; p = _document->Identify( p, &node ); - if ( p == 0 || node == 0 ) { + if ( node == 0 ) { break; } StrPair endTag; p = node->ParseDeep( p, &endTag ); if ( !p ) { - DELETE_NODE( node ); - node = 0; + DeleteNode( node ); if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, 0, 0 ); } break; } - // We read the end tag. Return it to the parent. - if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { - if ( parentEnd ) { - *parentEnd = static_cast(node)->_value; - } - node->_memPool->SetTracked(); // created and then immediately deleted. - DELETE_NODE( node ); - return p; + XMLDeclaration* decl = node->ToDeclaration(); + if ( decl ) { + // A declaration can only be the first child of a document. + // Set error, if document already has children. + if ( !_document->NoChildren() ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0); + DeleteNode( decl ); + break; + } } - // Handle an end tag returned to this level. - // And handle a bunch of annoying errors. XMLElement* ele = node->ToElement(); if ( ele ) { - if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); - p = 0; + // We read the end tag. Return it to the parent. + if ( ele->ClosingType() == XMLElement::CLOSING ) { + if ( parentEnd ) { + ele->_value.TransferTo( parentEnd ); + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DeleteNode( node ); + return p; } - else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); - p = 0; - } - else if ( !endTag.Empty() ) { - if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); - p = 0; + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + bool mismatch = false; + if ( endTag.Empty() ) { + if ( ele->ClosingType() == XMLElement::OPEN ) { + mismatch = true; } } + else { + if ( ele->ClosingType() != XMLElement::OPEN ) { + mismatch = true; + } + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { + mismatch = true; + } + } + if ( mismatch ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 ); + DeleteNode( node ); + break; + } } - if ( p == 0 ) { - DELETE_NODE( node ); - node = 0; - } - if ( node ) { - this->InsertEndChild( node ); - } + InsertEndChild( node ); } return 0; } +void XMLNode::DeleteNode( XMLNode* node ) +{ + if ( node == 0 ) { + return; + } + MemPool* pool = node->_memPool; + node->~XMLNode(); + pool->Free( node ); +} + +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const +{ + TIXMLASSERT( insertThis ); + TIXMLASSERT( insertThis->_document == _document ); + + if ( insertThis->_parent ) + insertThis->_parent->Unlink( insertThis ); + else + insertThis->_memPool->SetTracked(); +} + // --------- XMLText ---------- // char* XMLText::ParseDeep( char* p, StrPair* ) { @@ -897,16 +1069,16 @@ char* XMLText::ParseDeep( char* p, StrPair* ) else { int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { - flags |= StrPair::COLLAPSE_WHITESPACE; + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; } p = _value.ParseText( p, "<", flags ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); - } if ( p && *p ) { return p-1; } + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); + } } return 0; } @@ -925,12 +1097,14 @@ XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const bool XMLText::ShallowEqual( const XMLNode* compare ) const { - return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); + const XMLText* text = compare->ToText(); + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); } bool XMLText::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); return visitor->Visit( *this ); } @@ -971,12 +1145,15 @@ XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const bool XMLComment::ShallowEqual( const XMLNode* compare ) const { - return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); + TIXMLASSERT( compare ); + const XMLComment* comment = compare->ToComment(); + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); } bool XMLComment::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); return visitor->Visit( *this ); } @@ -1018,13 +1195,16 @@ XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const { - return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); + TIXMLASSERT( compare ); + const XMLDeclaration* declaration = compare->ToDeclaration(); + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); } bool XMLDeclaration::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); return visitor->Visit( *this ); } @@ -1065,12 +1245,15 @@ XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const { - return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); + TIXMLASSERT( compare ); + const XMLUnknown* unknown = compare->ToUnknown(); + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); } bool XMLUnknown::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); return visitor->Visit( *this ); } @@ -1096,7 +1279,7 @@ char* XMLAttribute::ParseDeep( char* p, bool processEntities ) // Skip white space before = p = XMLUtil::SkipWhiteSpace( p ); - if ( !p || *p != '=' ) { + if ( *p != '=' ) { return 0; } @@ -1123,7 +1306,7 @@ void XMLAttribute::SetName( const char* n ) XMLError XMLAttribute::QueryIntValue( int* value ) const { if ( XMLUtil::ToInt( Value(), value )) { - return XML_NO_ERROR; + return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } @@ -1132,16 +1315,25 @@ XMLError XMLAttribute::QueryIntValue( int* value ) const XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { if ( XMLUtil::ToUnsigned( Value(), value )) { - return XML_NO_ERROR; + return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const +{ + if (XMLUtil::ToInt64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { - return XML_NO_ERROR; + return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } @@ -1150,7 +1342,7 @@ XMLError XMLAttribute::QueryBoolValue( bool* value ) const XMLError XMLAttribute::QueryFloatValue( float* value ) const { if ( XMLUtil::ToFloat( Value(), value )) { - return XML_NO_ERROR; + return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } @@ -1159,7 +1351,7 @@ XMLError XMLAttribute::QueryFloatValue( float* value ) const XMLError XMLAttribute::QueryDoubleValue( double* value ) const { if ( XMLUtil::ToDouble( Value(), value )) { - return XML_NO_ERROR; + return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } @@ -1187,6 +1379,15 @@ void XMLAttribute::SetAttribute( unsigned v ) } +void XMLAttribute::SetAttribute(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + + + void XMLAttribute::SetAttribute( bool v ) { char buf[BUF_SIZE]; @@ -1221,28 +1422,15 @@ XMLElement::~XMLElement() { while( _rootAttribute ) { XMLAttribute* next = _rootAttribute->_next; - DELETE_ATTRIBUTE( _rootAttribute ); + DeleteAttribute( _rootAttribute ); _rootAttribute = next; } } -XMLAttribute* XMLElement::FindAttribute( const char* name ) -{ - XMLAttribute* a = 0; - for( a=_rootAttribute; a; a = a->_next ) { - if ( XMLUtil::StringEqual( a->Name(), name ) ) { - return a; - } - } - return 0; -} - - const XMLAttribute* XMLElement::FindAttribute( const char* name ) const { - XMLAttribute* a = 0; - for( a=_rootAttribute; a; a = a->_next ) { + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } @@ -1267,7 +1455,7 @@ const char* XMLElement::Attribute( const char* name, const char* value ) const const char* XMLElement::GetText() const { if ( FirstChild() && FirstChild()->ToText() ) { - return FirstChild()->ToText()->Value(); + return FirstChild()->Value(); } return 0; } @@ -1300,7 +1488,15 @@ void XMLElement::SetText( unsigned v ) } -void XMLElement::SetText( bool v ) +void XMLElement::SetText(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + + +void XMLElement::SetText( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); @@ -1327,7 +1523,7 @@ void XMLElement::SetText( double v ) XMLError XMLElement::QueryIntText( int* ival ) const { if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->ToText()->Value(); + const char* t = FirstChild()->Value(); if ( XMLUtil::ToInt( t, ival ) ) { return XML_SUCCESS; } @@ -1340,7 +1536,7 @@ XMLError XMLElement::QueryIntText( int* ival ) const XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const { if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->ToText()->Value(); + const char* t = FirstChild()->Value(); if ( XMLUtil::ToUnsigned( t, uval ) ) { return XML_SUCCESS; } @@ -1350,10 +1546,23 @@ XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const } +XMLError XMLElement::QueryInt64Text(int64_t* ival) const +{ + if (FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if (XMLUtil::ToInt64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + XMLError XMLElement::QueryBoolText( bool* bval ) const { if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->ToText()->Value(); + const char* t = FirstChild()->Value(); if ( XMLUtil::ToBool( t, bval ) ) { return XML_SUCCESS; } @@ -1366,7 +1575,7 @@ XMLError XMLElement::QueryBoolText( bool* bval ) const XMLError XMLElement::QueryDoubleText( double* dval ) const { if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->ToText()->Value(); + const char* t = FirstChild()->Value(); if ( XMLUtil::ToDouble( t, dval ) ) { return XML_SUCCESS; } @@ -1379,7 +1588,7 @@ XMLError XMLElement::QueryDoubleText( double* dval ) const XMLError XMLElement::QueryFloatText( float* fval ) const { if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->ToText()->Value(); + const char* t = FirstChild()->Value(); if ( XMLUtil::ToFloat( t, fval ) ) { return XML_SUCCESS; } @@ -1402,6 +1611,7 @@ XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) } } if ( !attrib ) { + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; if ( last ) { @@ -1428,7 +1638,7 @@ void XMLElement::DeleteAttribute( const char* name ) else { _rootAttribute = a->_next; } - DELETE_ATTRIBUTE( a ); + DeleteAttribute( a ); break; } prev = a; @@ -1444,20 +1654,21 @@ char* XMLElement::ParseAttributes( char* p ) // Read the attributes. while( p ) { p = XMLUtil::SkipWhiteSpace( p ); - if ( !p || !(*p) ) { + if ( !(*p) ) { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); return 0; } // attribute. if (XMLUtil::IsNameStartChar( *p ) ) { + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; attrib->_memPool->SetTracked(); p = attrib->ParseDeep( p, _document->ProcessEntities() ); if ( !p || Attribute( attrib->Name() ) ) { - DELETE_ATTRIBUTE( attrib ); + DeleteAttribute( attrib ); _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); return 0; } @@ -1475,15 +1686,15 @@ char* XMLElement::ParseAttributes( char* p ) prevAttribute = attrib; } // end of the tag - else if ( *p == '/' && *(p+1) == '>' ) { - _closingType = CLOSED; - return p+2; // done; sealed element. - } - // end of the tag else if ( *p == '>' ) { ++p; break; } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } else { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); return 0; @@ -1492,6 +1703,15 @@ char* XMLElement::ParseAttributes( char* p ) return p; } +void XMLElement::DeleteAttribute( XMLAttribute* attribute ) +{ + if ( attribute == 0 ) { + return; + } + MemPool* pool = attribute->_memPool; + attribute->~XMLAttribute(); + pool->Free( attribute ); +} // // @@ -1501,9 +1721,6 @@ char* XMLElement::ParseDeep( char* p, StrPair* strPair ) { // Read the element name. p = XMLUtil::SkipWhiteSpace( p ); - if ( !p ) { - return 0; - } // The closing element is the form. It is // parsed just like a regular element then deleted from @@ -1544,8 +1761,9 @@ XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const bool XMLElement::ShallowEqual( const XMLNode* compare ) const { + TIXMLASSERT( compare ); const XMLElement* other = compare->ToElement(); - if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { const XMLAttribute* a=FirstAttribute(); const XMLAttribute* b=other->FirstAttribute(); @@ -1569,6 +1787,7 @@ bool XMLElement::ShallowEqual( const XMLNode* compare ) const bool XMLElement::Accept( XMLVisitor* visitor ) const { + TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this, _rootAttribute ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { @@ -1581,41 +1800,50 @@ bool XMLElement::Accept( XMLVisitor* visitor ) const // --------- XMLDocument ----------- // + +// Warning: List must match 'enum XMLError' +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { + "XML_SUCCESS", + "XML_NO_ATTRIBUTE", + "XML_WRONG_ATTRIBUTE_TYPE", + "XML_ERROR_FILE_NOT_FOUND", + "XML_ERROR_FILE_COULD_NOT_BE_OPENED", + "XML_ERROR_FILE_READ_ERROR", + "XML_ERROR_ELEMENT_MISMATCH", + "XML_ERROR_PARSING_ELEMENT", + "XML_ERROR_PARSING_ATTRIBUTE", + "XML_ERROR_IDENTIFYING_TAG", + "XML_ERROR_PARSING_TEXT", + "XML_ERROR_PARSING_CDATA", + "XML_ERROR_PARSING_COMMENT", + "XML_ERROR_PARSING_DECLARATION", + "XML_ERROR_PARSING_UNKNOWN", + "XML_ERROR_EMPTY_DOCUMENT", + "XML_ERROR_MISMATCHED_ELEMENT", + "XML_ERROR_PARSING", + "XML_CAN_NOT_CONVERT_TEXT", + "XML_NO_TEXT_NODE" +}; + + XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : XMLNode( 0 ), _writeBOM( false ), _processEntities( processEntities ), - _errorID( XML_NO_ERROR ), + _errorID(XML_SUCCESS), _whitespace( whitespace ), _errorStr1( 0 ), _errorStr2( 0 ), _charBuffer( 0 ) { - _document = this; // avoid warning about 'this' in initializer list + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) + _document = this; } XMLDocument::~XMLDocument() { - DeleteChildren(); - delete [] _charBuffer; - -#if 0 - _textPool.Trace( "text" ); - _elementPool.Trace( "element" ); - _commentPool.Trace( "comment" ); - _attributePool.Trace( "attribute" ); -#endif -/* // Geez, this is broken -- fg -#ifdef DEBUG - if ( Error() == false ) { - TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); - TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); - TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); - TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); - } -#endif -*/ + Clear(); } @@ -1623,17 +1851,37 @@ void XMLDocument::Clear() { DeleteChildren(); - _errorID = XML_NO_ERROR; +#ifdef DEBUG + const bool hadError = Error(); +#endif + _errorID = XML_SUCCESS; _errorStr1 = 0; _errorStr2 = 0; delete [] _charBuffer; _charBuffer = 0; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef DEBUG + if ( !hadError ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif } XMLElement* XMLDocument::NewElement( const char* name ) { + TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); ele->_memPool = &_elementPool; ele->SetName( name ); @@ -1643,6 +1891,7 @@ XMLElement* XMLDocument::NewElement( const char* name ) XMLComment* XMLDocument::NewComment( const char* str ) { + TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); comment->_memPool = &_commentPool; comment->SetValue( str ); @@ -1652,6 +1901,7 @@ XMLComment* XMLDocument::NewComment( const char* str ) XMLText* XMLDocument::NewText( const char* str ) { + TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); XMLText* text = new (_textPool.Alloc()) XMLText( this ); text->_memPool = &_textPool; text->SetValue( str ); @@ -1661,6 +1911,7 @@ XMLText* XMLDocument::NewText( const char* str ) XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) { + TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); dec->_memPool = &_commentPool; dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); @@ -1670,25 +1921,52 @@ XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) XMLUnknown* XMLDocument::NewUnknown( const char* str ) { + TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); unk->_memPool = &_commentPool; unk->SetValue( str ); return unk; } -/* +static FILE* callfopen( const char* filepath, const char* mode ) +{ + TIXMLASSERT( filepath ); + TIXMLASSERT( mode ); +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filepath, mode ); + if ( err ) { + return 0; + } +#else + FILE* fp = fopen( filepath, mode ); +#endif + return fp; +} + +void XMLDocument::DeleteNode( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT(node->_document == this ); + if (node->_parent) { + node->_parent->DeleteChild( node ); + } + else { + // Isn't in the tree. + // Use the parent delete. + // Also, we need to mark it tracked: we 'know' + // it was never used. + node->_memPool->SetTracked(); + // Call the static XMLNode version: + XMLNode::DeleteNode(node); + } +} + + XMLError XMLDocument::LoadFile( const char* filename ) { Clear(); - FILE* fp = 0; - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - errno_t err = fopen_s(&fp, filename, "rb" ); - if ( !fp || err) { -#else - fp = fopen( filename, "rb" ); - if ( !fp) { -#endif + FILE* fp = callfopen( filename, "rb" ); + if ( !fp ) { SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); return _errorID; } @@ -1697,27 +1975,61 @@ XMLError XMLDocument::LoadFile( const char* filename ) return _errorID; } +// This is likely overengineered template art to have a check that unsigned long value incremented +// by one still fits into size_t. If size_t type is larger than unsigned long type +// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit +// -Wtype-limits warning. This piece makes the compiler select code with a check when a check +// is useful and code with no check when a check is redundant depending on how size_t and unsigned long +// types sizes relate to each other. +template += sizeof(size_t))> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long value ) + { + return value < (size_t)-1; + } +}; + +template <> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long ) + { + return true; + } +}; XMLError XMLDocument::LoadFile( FILE* fp ) { Clear(); fseek( fp, 0, SEEK_SET ); - fgetc( fp ); - if ( ferror( fp ) != 0 ) { + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } fseek( fp, 0, SEEK_END ); - size_t size = ftell( fp ); + const long filelength = ftell( fp ); fseek( fp, 0, SEEK_SET ); + if ( filelength == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + TIXMLASSERT( filelength >= 0 ); - if ( size == 0 ) { + if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { + // Cannot handle files which won't fit in buffer together with null terminator + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + if ( filelength == 0 ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } + const size_t size = filelength; + TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[size+1]; size_t read = fread( _charBuffer, 1, size, fp ); if ( read != size ) { @@ -1727,29 +2039,15 @@ XMLError XMLDocument::LoadFile( FILE* fp ) _charBuffer[size] = 0; - const char* p = _charBuffer; - p = XMLUtil::SkipWhiteSpace( p ); - p = XMLUtil::ReadBOM( p, &_writeBOM ); - if ( !p || !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - - ParseDeep( _charBuffer + (p-_charBuffer), 0 ); + Parse(); return _errorID; } -*/ + XMLError XMLDocument::SaveFile( const char* filename, bool compact ) { - FILE* fp = 0; -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - errno_t err = fopen_s(&fp, filename, "w" ); - if ( !fp || err) { -#else - fp = fopen( filename, "w" ); - if ( !fp) { -#endif + FILE* fp = callfopen( filename, "w" ); + if ( !fp ) { SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); return _errorID; } @@ -1761,6 +2059,9 @@ XMLError XMLDocument::SaveFile( const char* filename, bool compact ) XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) { + // Clear any error from the last save, otherwise it will get reported + // for *this* call. + SetError(XML_SUCCESS, 0, 0); XMLPrinter stream( fp, compact ); Print( &stream ); return _errorID; @@ -1769,7 +2070,6 @@ XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) XMLError XMLDocument::Parse( const char* p, size_t len ) { - const char* start = p; Clear(); if ( len == 0 || !p || !*p ) { @@ -1779,44 +2079,57 @@ XMLError XMLDocument::Parse( const char* p, size_t len ) if ( len == (size_t)(-1) ) { len = strlen( p ); } + TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[ len+1 ]; memcpy( _charBuffer, p, len ); _charBuffer[len] = 0; - p = XMLUtil::SkipWhiteSpace( p ); - p = XMLUtil::ReadBOM( p, &_writeBOM ); - if ( !p || !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; + Parse(); + if ( Error() ) { + // clean up now essentially dangling memory. + // and the parse fail can put objects in the + // pools that are dead and inaccessible. + DeleteChildren(); + _elementPool.Clear(); + _attributePool.Clear(); + _textPool.Clear(); + _commentPool.Clear(); } - - ptrdiff_t delta = p - start; // skip initial whitespace, BOM, etc. - ParseDeep( _charBuffer+delta, 0 ); return _errorID; } void XMLDocument::Print( XMLPrinter* streamer ) const { - XMLPrinter stdStreamer( stdout ); - if ( !streamer ) { - streamer = &stdStreamer; + if ( streamer ) { + Accept( streamer ); + } + else { + XMLPrinter stdoutStreamer( stdout ); + Accept( &stdoutStreamer ); } - Accept( streamer ); } void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) { + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); _errorID = error; _errorStr1 = str1; _errorStr2 = str2; } +const char* XMLDocument::ErrorName() const +{ + TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT ); + const char* errorName = _errorNames[_errorID]; + TIXMLASSERT( errorName && errorName[0] ); + return errorName; +} void XMLDocument::PrintError() const { - if ( _errorID ) { + if ( Error() ) { static const int LEN = 20; char buf1[LEN] = { 0 }; char buf2[LEN] = { 0 }; @@ -1828,11 +2141,27 @@ void XMLDocument::PrintError() const TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); } - printf( "XMLDocument error id=%d str1=%s str2=%s\n", - _errorID, buf1, buf2 ); + // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that + // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning + TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX ); + printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n", + static_cast( _errorID ), ErrorName(), buf1, buf2 ); } } +void XMLDocument::Parse() +{ + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously + TIXMLASSERT( _charBuffer ); + char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p ); + p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); + if ( !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return; + } + ParseDeep(p, 0 ); +} XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _elementJustOpened( false ), @@ -1848,14 +2177,13 @@ XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _restrictedEntityFlag[i] = false; } for( int i=0; i'] = true; // not required, but consistency is nice + _restrictedEntityFlag[(unsigned char)'&'] = true; + _restrictedEntityFlag[(unsigned char)'<'] = true; + _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice _buffer.Push( 0 ); } @@ -1869,20 +2197,14 @@ void XMLPrinter::Print( const char* format, ... ) vfprintf( _fp, format, va ); } else { -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - int len = _vscprintf( format, va ); -#else - int len = vsnprintf( 0, 0, format, va ); -#endif + const int len = TIXML_VSCPRINTF( format, va ); // Close out and re-start the va-args va_end( va ); + TIXMLASSERT( len >= 0 ); va_start( va, format ); + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - vsnprintf_s( p, len+1, _TRUNCATE, format, va ); -#else - vsnprintf( p, len+1, format, va ); -#endif + TIXML_VSNPRINTF( p, len+1, format, va ); } va_end( va ); } @@ -1900,35 +2222,47 @@ void XMLPrinter::PrintString( const char* p, bool restricted ) { // Look for runs of bytes between entities to print. const char* q = p; - const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; if ( _processEntities ) { + const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; while ( *q ) { + TIXMLASSERT( p <= q ); // Remember, char is sometimes signed. (How many times has that bitten me?) if ( *q > 0 && *q < ENTITY_RANGE ) { // Check for entities. If one is found, flush // the stream up until the entity, write the // entity, and keep looking. - if ( flag[(unsigned)(*q)] ) { + if ( flag[(unsigned char)(*q)] ) { while ( p < q ) { - Print( "%c", *p ); - ++p; + const size_t delta = q - p; + // %.*s accepts type int as "precision" + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; + Print( "%.*s", toPrint, p ); + p += toPrint; } + bool entityPatternPrinted = false; for( int i=0; i 0) ) { + TIXMLASSERT( p <= q ); + if ( !_processEntities || ( p < q ) ) { Print( "%s", p ); } } @@ -1948,9 +2282,7 @@ void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) void XMLPrinter::OpenElement( const char* name, bool compactMode ) { - if ( _elementJustOpened ) { - SealElement(); - } + SealElementIfJustOpened(); _stack.Push( name ); if ( _textDepth < 0 && !_firstElement && !compactMode ) { @@ -1992,6 +2324,14 @@ void XMLPrinter::PushAttribute( const char* name, unsigned v ) } +void XMLPrinter::PushAttribute(const char* name, int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; @@ -2034,8 +2374,11 @@ void XMLPrinter::CloseElement( bool compactMode ) } -void XMLPrinter::SealElement() +void XMLPrinter::SealElementIfJustOpened() { + if ( !_elementJustOpened ) { + return; + } _elementJustOpened = false; Print( ">" ); } @@ -2045,13 +2388,9 @@ void XMLPrinter::PushText( const char* text, bool cdata ) { _textDepth = _depth-1; - if ( _elementJustOpened ) { - SealElement(); - } + SealElementIfJustOpened(); if ( cdata ) { - Print( "" ); + Print( "", text ); } else { PrintString( text, true ); @@ -2100,9 +2439,7 @@ void XMLPrinter::PushText( double value ) void XMLPrinter::PushComment( const char* comment ) { - if ( _elementJustOpened ) { - SealElement(); - } + SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); @@ -2114,9 +2451,7 @@ void XMLPrinter::PushComment( const char* comment ) void XMLPrinter::PushDeclaration( const char* value ) { - if ( _elementJustOpened ) { - SealElement(); - } + SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); @@ -2128,9 +2463,7 @@ void XMLPrinter::PushDeclaration( const char* value ) void XMLPrinter::PushUnknown( const char* value ) { - if ( _elementJustOpened ) { - SealElement(); - } + SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); @@ -2152,8 +2485,11 @@ bool XMLPrinter::VisitEnter( const XMLDocument& doc ) bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) { - const XMLElement* parentElem = element.Parent()->ToElement(); - bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode; + const XMLElement* parentElem = 0; + if ( element.Parent() ) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; OpenElement( element.Name(), compactMode ); while ( attribute ) { PushAttribute( attribute->Name(), attribute->Value() ); diff --git a/ExternalLibs/tinyxml2.h b/ExternalLibs/tinyxml2.h index 9043e26..60280c0 100644 --- a/ExternalLibs/tinyxml2.h +++ b/ExternalLibs/tinyxml2.h @@ -14,7 +14,6 @@ not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. @@ -25,21 +24,23 @@ distribution. #ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED -#if defined(ANDROID_NDK) || defined(__BORLANDC__) +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include # include # include # include # include -# include +# if defined(__PS3__) +# include +# endif #else # include # include # include # include # include -# include #endif +#include /* TODO: intern strings instead of allocation. @@ -78,7 +79,8 @@ distribution. #if defined(DEBUG) # if defined(_MSC_VER) -# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like +# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } # elif defined (ANDROID_NDK) # include # define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } @@ -86,42 +88,17 @@ distribution. # include # define TIXMLASSERT assert # endif -# else -# define TIXMLASSERT( x ) {} -#endif - - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) -// Microsoft visual studio, version 2005 and higher. -/*int _snprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format [, - argument] ... -);*/ -inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) -{ - va_list va; - va_start( va, format ); - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; -} -#define TIXML_SSCANF sscanf_s #else -// GCC version 3 and higher -//#warning( "Using sn* functions." ) -#define TIXML_SNPRINTF snprintf -#define TIXML_SSCANF sscanf +# define TIXMLASSERT( x ) {} #endif + /* Versioning, past 1.0.14: http://semver.org/ */ -static const int TIXML2_MAJOR_VERSION = 2; -static const int TIXML2_MINOR_VERSION = 1; -static const int TIXML2_PATCH_VERSION = 0; +static const int TIXML2_MAJOR_VERSION = 4; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 1; namespace tinyxml2 { @@ -146,7 +123,7 @@ public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02, - COLLAPSE_WHITESPACE = 0x04, + NEEDS_WHITESPACE_COLLAPSING = 0x04, TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, @@ -182,6 +159,8 @@ public: char* ParseText( char* in, const char* endTag, int strFlags ); char* ParseName( char* in ); + void TransferTo( StrPair* other ); + private: void Reset(); void CollapseWhitespace(); @@ -191,10 +170,12 @@ private: NEEDS_DELETE = 0x200 }; - // After parsing, if *_end != 0, it can be set to zero. int _flags; char* _start; char* _end; + + StrPair( const StrPair& other ); // not supported + void operator=( StrPair& other ); // not supported, use TransferTo() }; @@ -203,13 +184,13 @@ private: Has a small initial memory pool, so that low or no usage will not cause a call to new/delete */ -template +template class DynArray { public: - DynArray< T, INIT >() { + DynArray() { _mem = _pool; - _allocated = INIT; + _allocated = INITIAL_SIZE; _size = 0; } @@ -224,11 +205,14 @@ public: } void Push( T t ) { + TIXMLASSERT( _size < INT_MAX ); EnsureCapacity( _size+1 ); _mem[_size++] = t; } T* PushArr( int count ) { + TIXMLASSERT( count >= 0 ); + TIXMLASSERT( _size <= INT_MAX - count ); EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; @@ -236,6 +220,7 @@ public: } T Pop() { + TIXMLASSERT( _size > 0 ); return _mem[--_size]; } @@ -258,30 +243,39 @@ public: return _mem[i]; } - const T& PeekTop() const { + const T& PeekTop() const { TIXMLASSERT( _size > 0 ); return _mem[ _size - 1]; } int Size() const { + TIXMLASSERT( _size >= 0 ); return _size; } int Capacity() const { + TIXMLASSERT( _allocated >= INITIAL_SIZE ); return _allocated; } const T* Mem() const { + TIXMLASSERT( _mem ); return _mem; } T* Mem() { + TIXMLASSERT( _mem ); return _mem; } private: + DynArray( const DynArray& ); // not supported + void operator=( const DynArray& ); // not supported + void EnsureCapacity( int cap ) { + TIXMLASSERT( cap > 0 ); if ( cap > _allocated ) { + TIXMLASSERT( cap <= INT_MAX / 2 ); int newAllocated = cap * 2; T* newMem = new T[newAllocated]; memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs @@ -294,7 +288,7 @@ private: } T* _mem; - T _pool[INIT]; + T _pool[INITIAL_SIZE]; int _allocated; // objects allocated int _size; // number objects in use }; @@ -314,6 +308,7 @@ public: virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; + virtual void Clear() = 0; }; @@ -326,10 +321,20 @@ class MemPoolT : public MemPool public: MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { + Clear(); + } + + void Clear() { // Delete the blocks. - for( int i=0; i<_blockPtrs.Size(); ++i ) { - delete _blockPtrs[i]; + while( !_blockPtrs.Empty()) { + Block* b = _blockPtrs.Pop(); + delete b; } + _root = 0; + _currentAllocs = 0; + _nAllocs = 0; + _maxAllocs = 0; + _nUntracked = 0; } virtual int ItemSize() const { @@ -362,12 +367,13 @@ public: _nUntracked++; return result; } + virtual void Free( void* mem ) { if ( !mem ) { return; } --_currentAllocs; - Chunk* chunk = (Chunk*)mem; + Chunk* chunk = static_cast( mem ); #ifdef DEBUG memset( chunk, 0xfe, sizeof(Chunk) ); #endif @@ -399,6 +405,9 @@ public: enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private private: + MemPoolT( const MemPoolT& ); // not supported + void operator=( const MemPoolT& ); // not supported + union Chunk { Chunk* next; char mem[SIZE]; @@ -477,6 +486,32 @@ public: } }; +// WARNING: must match XMLDocument::_errorNames[] +enum XMLError { + XML_SUCCESS = 0, + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_ELEMENT_MISMATCH, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_IDENTIFYING_TAG, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE, + + XML_ERROR_COUNT +}; + /* Utility functionality. @@ -484,28 +519,33 @@ public: class XMLUtil { public: - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. static const char* SkipWhiteSpace( const char* p ) { - while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + TIXMLASSERT( p ); + while( IsWhiteSpace(*p) ) { ++p; } + TIXMLASSERT( p ); return p; } static char* SkipWhiteSpace( char* p ) { - while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { - ++p; - } - return p; + return const_cast( SkipWhiteSpace( const_cast(p) ) ); } + + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } inline static bool IsNameStartChar( unsigned char ch ) { - return ( ( ch < 128 ) ? isalpha( ch ) : 1 ) - || ch == ':' - || ch == '_'; + if ( ch >= 128 ) { + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() + return true; + } + if ( isalpha( ch ) ) { + return true; + } + return ch == ':' || ch == '_'; } inline static bool IsNameChar( unsigned char ch ) { @@ -516,23 +556,14 @@ public: } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - int n = 0; if ( p == q ) { return true; } - while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); + XMLElement* FirstChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->FirstChildElement( name )); } /// Get the last child node, or null if none exists. @@ -695,16 +730,16 @@ public: } XMLNode* LastChild() { - return const_cast(const_cast(this)->LastChild() ); + return _lastChild; } /** Get the last child element or optionally the last child element with the specified name. */ - const XMLElement* LastChildElement( const char* value=0 ) const; + const XMLElement* LastChildElement( const char* name = 0 ) const; - XMLElement* LastChildElement( const char* value=0 ) { - return const_cast(const_cast(this)->LastChildElement(value) ); + XMLElement* LastChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->LastChildElement(name) ); } /// Get the previous (left) sibling node of this node. @@ -717,10 +752,10 @@ public: } /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; - XMLElement* PreviousSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); + XMLElement* PreviousSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); } /// Get the next (right) sibling node of this node. @@ -733,10 +768,10 @@ public: } /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const char* value=0 ) const; + const XMLElement* NextSiblingElement( const char* name = 0 ) const; - XMLElement* NextSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->NextSiblingElement( value ) ); + XMLElement* NextSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->NextSiblingElement( name ) ); } /** @@ -822,14 +857,25 @@ public: */ virtual bool Accept( XMLVisitor* visitor ) const = 0; - // internal - virtual char* ParseDeep( char*, StrPair* ); + /** + Set user data into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void SetUserData(void* userData) { _userData = userData; } + + /** + Get user data set into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void* GetUserData() const { return _userData; } protected: XMLNode( XMLDocument* ); virtual ~XMLNode(); - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported + + virtual char* ParseDeep( char*, StrPair* ); XMLDocument* _document; XMLNode* _parent; @@ -841,9 +887,16 @@ protected: XMLNode* _prev; XMLNode* _next; + void* _userData; + private: MemPool* _memPool; void Unlink( XMLNode* child ); + static void DeleteNode( XMLNode* node ); + void InsertChildPreamble( XMLNode* insertThis ) const; + + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported }; @@ -861,7 +914,6 @@ private: */ class TINYXML2_LIB XMLText : public XMLNode { - friend class XMLBase; friend class XMLDocument; public: virtual bool Accept( XMLVisitor* visitor ) const; @@ -882,18 +934,20 @@ public: return _isCData; } - char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported + + char* ParseDeep( char*, StrPair* endTag ); private: bool _isCData; + + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported }; @@ -911,17 +965,18 @@ public: virtual bool Accept( XMLVisitor* visitor ) const; - char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported + + char* ParseDeep( char*, StrPair* endTag ); private: + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported }; @@ -949,13 +1004,16 @@ public: virtual bool Accept( XMLVisitor* visitor ) const; - char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); + + char* ParseDeep( char*, StrPair* endTag ); + +private: XMLDeclaration( const XMLDeclaration& ); // not supported XMLDeclaration& operator=( const XMLDeclaration& ); // not supported }; @@ -981,45 +1039,21 @@ public: virtual bool Accept( XMLVisitor* visitor ) const; - char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); + + char* ParseDeep( char*, StrPair* endTag ); + +private: XMLUnknown( const XMLUnknown& ); // not supported XMLUnknown& operator=( const XMLUnknown& ); // not supported }; -enum XMLError { - XML_NO_ERROR = 0, - XML_SUCCESS = 0, - - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - XML_ERROR_ELEMENT_MISMATCH, - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - XML_ERROR_IDENTIFYING_TAG, - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE -}; - /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @@ -1046,11 +1080,18 @@ public: If the value isn't an integer, 0 will be returned. There is no error checking; use QueryIntValue() if you need error checking. */ - int IntValue() const { - int i=0; - QueryIntValue( &i ); - return i; - } + int IntValue() const { + int i = 0; + QueryIntValue(&i); + return i; + } + + int64_t Int64Value() const { + int64_t i = 0; + QueryInt64Value(&i); + return i; + } + /// Query as an unsigned integer. See IntValue() unsigned UnsignedValue() const { unsigned i=0; @@ -1083,7 +1124,9 @@ public: XMLError QueryIntValue( int* value ) const; /// See QueryIntValue XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue + /// See QueryIntValue + XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue XMLError QueryBoolValue( bool* value ) const; /// See QueryIntValue XMLError QueryDoubleValue( double* value ) const; @@ -1096,7 +1139,9 @@ public: void SetAttribute( int value ); /// Set the attribute to value. void SetAttribute( unsigned value ); - /// Set the attribute to value. + /// Set the attribute to value. + void SetAttribute(int64_t value); + /// Set the attribute to value. void SetAttribute( bool value ); /// Set the attribute to value. void SetAttribute( double value ); @@ -1128,7 +1173,6 @@ private: */ class TINYXML2_LIB XMLElement : public XMLNode { - friend class XMLBase; friend class XMLDocument; public: /// Get the name of an element (which is the Value() of the node.) @@ -1183,26 +1227,35 @@ public: QueryIntAttribute( name, &i ); return i; } + /// See IntAttribute() unsigned UnsignedAttribute( const char* name ) const { unsigned i=0; QueryUnsignedAttribute( name, &i ); return i; } - /// See IntAttribute() - bool BoolAttribute( const char* name ) const { + + /// See IntAttribute() + int64_t Int64Attribute(const char* name) const { + int64_t i = 0; + QueryInt64Attribute(name, &i); + return i; + } + + /// See IntAttribute() + bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( name, &b ); return b; } /// See IntAttribute() - double DoubleAttribute( const char* name ) const { + double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( name, &d ); return d; } /// See IntAttribute() - float FloatAttribute( const char* name ) const { + float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( name, &f ); return f; @@ -1228,7 +1281,8 @@ public: } return a->QueryIntValue( value ); } - /// See QueryIntAttribute() + + /// See QueryIntAttribute() XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { @@ -1236,7 +1290,17 @@ public: } return a->QueryUnsignedValue( value ); } - /// See QueryIntAttribute() + + /// See QueryIntAttribute() + XMLError QueryInt64Attribute(const char* name, int64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryInt64Value(value); + } + + /// See QueryIntAttribute() XMLError QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { @@ -1287,6 +1351,10 @@ public: return QueryUnsignedAttribute( name, value ); } + int QueryAttribute(const char* name, int64_t* value) const { + return QueryInt64Attribute(name, value); + } + int QueryAttribute( const char* name, bool* value ) const { return QueryBoolAttribute( name, value ); } @@ -1314,7 +1382,14 @@ public: XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } - /// Sets the named attribute to value. + + /// Sets the named attribute to value. + void SetAttribute(const char* name, int64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); @@ -1407,15 +1482,17 @@ public: @endverbatim */ void SetText( const char* inText ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. + /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( int value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. + /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( unsigned value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( bool value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. + /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( double value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. + /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( float value ); /** @@ -1447,7 +1524,9 @@ public: XMLError QueryIntText( int* ival ) const; /// See QueryIntText() XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() + /// See QueryIntText() + XMLError QueryInt64Text(int64_t* uval) const; + /// See QueryIntText() XMLError QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; @@ -1463,20 +1542,25 @@ public: int ClosingType() const { return _closingType; } - char* ParseDeep( char* p, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; +protected: + char* ParseDeep( char* p, StrPair* endTag ); + private: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported - XMLAttribute* FindAttribute( const char* name ); + XMLAttribute* FindAttribute( const char* name ) { + return const_cast(const_cast(this)->FindAttribute( name )); + } XMLAttribute* FindOrCreateAttribute( const char* name ); //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p ); + static void DeleteAttribute( XMLAttribute* attribute ); enum { BUF_SIZE = 200 }; int _closingType; @@ -1507,9 +1591,11 @@ public: ~XMLDocument(); virtual XMLDocument* ToDocument() { + TIXMLASSERT( this == _document ); return this; } virtual const XMLDocument* ToDocument() const { + TIXMLASSERT( this == _document ); return this; } @@ -1530,16 +1616,20 @@ public: Returns XML_NO_ERROR (0) on success, or an errorID. */ - //XMLError LoadFile( const char* filename ); + XMLError LoadFile( const char* filename ); /** Load an XML file from disk. You are responsible - for providing and closing the FILE*. + for providing and closing the FILE*. + + NOTE: The file should be opened as binary ("rb") + not text in order for TinyXML-2 to correctly + do newline normalization. Returns XML_NO_ERROR (0) on success, or an errorID. */ - //XMLError LoadFile( FILE* ); + XMLError LoadFile( FILE* ); /** Save the XML file to disk. @@ -1644,20 +1734,20 @@ public: Delete a node associated with this document. It will be unlinked from the DOM. */ - void DeleteNode( XMLNode* node ) { - node->_parent->DeleteChild( node ); - } + void DeleteNode( XMLNode* node ); void SetError( XMLError error, const char* str1, const char* str2 ); /// Return true if there was an error parsing the document. bool Error() const { - return _errorID != XML_NO_ERROR; + return _errorID != XML_SUCCESS; } /// Return the errorID. XMLError ErrorID() const { return _errorID; } + const char* ErrorName() const; + /// Return a possibly helpful diagnostic location or string. const char* GetErrorStr1() const { return _errorStr1; @@ -1698,6 +1788,10 @@ private: MemPoolT< sizeof(XMLAttribute) > _attributePool; MemPoolT< sizeof(XMLText) > _textPool; MemPoolT< sizeof(XMLComment) > _commentPool; + + static const char* _errorNames[XML_ERROR_COUNT]; + + void Parse(); }; @@ -1741,7 +1835,7 @@ private: @verbatim XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); if ( child2 ) { // do something useful @@ -1782,32 +1876,32 @@ public: return XMLHandle( _node ? _node->FirstChild() : 0 ); } /// Get the first child element of this handle. - XMLHandle FirstChildElement( const char* value=0 ) { - return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); + XMLHandle FirstChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); } /// Get the last child of this handle. XMLHandle LastChild() { return XMLHandle( _node ? _node->LastChild() : 0 ); } /// Get the last child element of this handle. - XMLHandle LastChildElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); + XMLHandle LastChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); } /// Get the previous sibling of this handle. XMLHandle PreviousSibling() { return XMLHandle( _node ? _node->PreviousSibling() : 0 ); } /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + XMLHandle PreviousSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } /// Get the next sibling of this handle. XMLHandle NextSibling() { return XMLHandle( _node ? _node->NextSibling() : 0 ); } /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + XMLHandle NextSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } /// Safe cast to XMLNode. This can return null. @@ -1816,19 +1910,19 @@ public: } /// Safe cast to XMLElement. This can return null. XMLElement* ToElement() { - return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToElement() ); } /// Safe cast to XMLText. This can return null. XMLText* ToText() { - return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToText() ); } /// Safe cast to XMLUnknown. This can return null. XMLUnknown* ToUnknown() { - return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToUnknown() ); } /// Safe cast to XMLDeclaration. This can return null. XMLDeclaration* ToDeclaration() { - return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() ); } private: @@ -1861,26 +1955,26 @@ public: const XMLConstHandle FirstChild() const { return XMLConstHandle( _node ? _node->FirstChild() : 0 ); } - const XMLConstHandle FirstChildElement( const char* value=0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); + const XMLConstHandle FirstChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); } const XMLConstHandle LastChild() const { return XMLConstHandle( _node ? _node->LastChild() : 0 ); } - const XMLConstHandle LastChildElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); + const XMLConstHandle LastChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); } const XMLConstHandle PreviousSibling() const { return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); } - const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } const XMLConstHandle NextSibling() const { return XMLConstHandle( _node ? _node->NextSibling() : 0 ); } - const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } @@ -1888,16 +1982,16 @@ public: return _node; } const XMLElement* ToElement() const { - return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToElement() ); } const XMLText* ToText() const { - return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToText() ); } const XMLUnknown* ToUnknown() const { - return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToUnknown() ); } const XMLDeclaration* ToDeclaration() const { - return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() ); } private: @@ -1969,7 +2063,8 @@ public: void PushAttribute( const char* name, const char* value ); void PushAttribute( const char* name, int value ); void PushAttribute( const char* name, unsigned value ); - void PushAttribute( const char* name, bool value ); + void PushAttribute(const char* name, int64_t value); + void PushAttribute( const char* name, bool value ); void PushAttribute( const char* name, double value ); /// If streaming, close the Element. virtual void CloseElement( bool compactMode=false ); @@ -1980,7 +2075,9 @@ public: void PushText( int value ); /// Add a text node from an unsigned. void PushText( unsigned value ); - /// Add a text node from a bool. + /// Add a text node from an unsigned. + void PushText(int64_t value); + /// Add a text node from a bool. void PushText( bool value ); /// Add a text node from a float. void PushText( float value ); @@ -2039,7 +2136,7 @@ protected: virtual void PrintSpace( int depth ); void Print( const char* format, ... ); - void SealElement(); + void SealElementIfJustOpened(); bool _elementJustOpened; DynArray< const char*, 10 > _stack; @@ -2061,9 +2158,6 @@ private: bool _restrictedEntityFlag[ENTITY_RANGE]; DynArray< char, 20 > _buffer; -#ifdef _MSC_VER - DynArray< char, 20 > _accumulator; -#endif };