2 Original code by Lee Thomason (www.grinninglizard.com)
\r
4 This software is provided 'as-is', without any express or implied
\r
5 warranty. In no event will the authors be held liable for any
\r
6 damages arising from the use of this software.
\r
8 Permission is granted to anyone to use this software for any
\r
9 purpose, including commercial applications, and to alter it and
\r
10 redistribute it freely, subject to the following restrictions:
\r
12 1. The origin of this software must not be misrepresented; you must
\r
13 not claim that you wrote the original software. If you use this
\r
14 software in a product, an acknowledgment in the product documentation
\r
15 would be appreciated but is not required.
\r
17 2. Altered source versions must be plainly marked as such, and
\r
18 must not be misrepresented as being the original software.
\r
20 3. This notice may not be removed or altered from any source
\r
24 #include "tinyxml2.h"
\r
26 #include <new> // yes, this one new style header, is in the Android SDK.
\r
27 #if defined(ANDROID_NDK) || defined(__QNXNTO__)
\r
28 # include <stddef.h>
\r
29 # include <stdarg.h>
\r
35 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
\r
36 // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
\r
39 size_t sizeOfBuffer,
\r
41 const char *format [,
\r
44 static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
\r
47 va_start( va, format );
\r
48 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
\r
53 static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
\r
55 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
\r
59 #define TIXML_VSCPRINTF _vscprintf
\r
60 #define TIXML_SSCANF sscanf_s
\r
61 #elif defined _MSC_VER
\r
62 // Microsoft Visual Studio 2003 and earlier or WinCE
\r
63 #define TIXML_SNPRINTF _snprintf
\r
64 #define TIXML_VSNPRINTF _vsnprintf
\r
65 #define TIXML_SSCANF sscanf
\r
66 #if (_MSC_VER < 1400 ) && (!defined WINCE)
\r
67 // Microsoft Visual Studio 2003 and not WinCE.
\r
68 #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
\r
70 // Microsoft Visual Studio 2003 and earlier or WinCE.
\r
71 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
\r
76 char* str = new char[len]();
\r
77 const int required = _vsnprintf(str, len, format, va);
\r
79 if ( required != -1 ) {
\r
80 TIXMLASSERT( required >= 0 );
\r
85 TIXMLASSERT( len >= 0 );
\r
90 // GCC version 3 and higher
\r
91 //#warning( "Using sn* functions." )
\r
92 #define TIXML_SNPRINTF snprintf
\r
93 #define TIXML_VSNPRINTF vsnprintf
\r
94 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
\r
96 int len = vsnprintf( 0, 0, format, va );
\r
97 TIXMLASSERT( len >= 0 );
\r
100 #define TIXML_SSCANF sscanf
\r
104 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
\r
105 static const char LF = LINE_FEED;
\r
106 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
\r
107 static const char CR = CARRIAGE_RETURN;
\r
108 static const char SINGLE_QUOTE = '\'';
\r
109 static const char DOUBLE_QUOTE = '\"';
\r
111 // Bunch of unicode info at:
\r
112 // http://www.unicode.org/faq/utf_bom.html
\r
113 // ef bb bf (Microsoft "lead bytes") - designates UTF-8
\r
115 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
\r
116 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
\r
117 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
\r
123 const char* pattern;
\r
128 static const int NUM_ENTITIES = 5;
\r
129 static const Entity entities[NUM_ENTITIES] = {
\r
130 { "quot", 4, DOUBLE_QUOTE },
\r
132 { "apos", 4, SINGLE_QUOTE },
\r
138 StrPair::~StrPair()
\r
144 void StrPair::TransferTo( StrPair* other )
\r
146 if ( this == other ) {
\r
149 // This in effect implements the assignment operator by "moving"
\r
150 // ownership (as in auto_ptr).
\r
152 TIXMLASSERT( other->_flags == 0 );
\r
153 TIXMLASSERT( other->_start == 0 );
\r
154 TIXMLASSERT( other->_end == 0 );
\r
158 other->_flags = _flags;
\r
159 other->_start = _start;
\r
160 other->_end = _end;
\r
167 void StrPair::Reset()
\r
169 if ( _flags & NEEDS_DELETE ) {
\r
178 void StrPair::SetStr( const char* str, int flags )
\r
180 TIXMLASSERT( str );
\r
182 size_t len = strlen( str );
\r
183 TIXMLASSERT( _start == 0 );
\r
184 _start = new char[ len+1 ];
\r
185 memcpy( _start, str, len+1 );
\r
186 _end = _start + len;
\r
187 _flags = flags | NEEDS_DELETE;
\r
191 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
\r
193 TIXMLASSERT( endTag && *endTag );
\r
196 char endChar = *endTag;
\r
197 size_t length = strlen( endTag );
\r
199 // Inner loop of text parsing.
\r
201 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
\r
202 Set( start, p, strFlags );
\r
211 char* StrPair::ParseName( char* p )
\r
213 if ( !p || !(*p) ) {
\r
216 if ( !XMLUtil::IsNameStartChar( *p ) ) {
\r
220 char* const start = p;
\r
222 while ( *p && XMLUtil::IsNameChar( *p ) ) {
\r
226 Set( start, p, 0 );
\r
231 void StrPair::CollapseWhitespace()
\r
233 // Adjusting _start would cause undefined behavior on delete[]
\r
234 TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
\r
235 // Trim leading space.
\r
236 _start = XMLUtil::SkipWhiteSpace( _start );
\r
239 char* p = _start; // the read pointer
\r
240 char* q = _start; // the write pointer
\r
243 if ( XMLUtil::IsWhiteSpace( *p )) {
\r
244 p = XMLUtil::SkipWhiteSpace( p );
\r
246 break; // don't write to q; this trims the trailing space.
\r
260 const char* StrPair::GetStr()
\r
262 TIXMLASSERT( _start );
\r
263 TIXMLASSERT( _end );
\r
264 if ( _flags & NEEDS_FLUSH ) {
\r
266 _flags ^= NEEDS_FLUSH;
\r
269 char* p = _start; // the read pointer
\r
270 char* q = _start; // the write pointer
\r
272 while( p < _end ) {
\r
273 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
\r
274 // CR-LF pair becomes LF
\r
275 // CR alone becomes LF
\r
276 // LF-CR becomes LF
\r
277 if ( *(p+1) == LF ) {
\r
285 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
\r
286 if ( *(p+1) == CR ) {
\r
294 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
\r
295 // Entities handled by tinyXML2:
\r
296 // - special entities in the entity table [in/out]
\r
297 // - numeric character reference [in]
\r
298 // 中 or 中
\r
300 if ( *(p+1) == '#' ) {
\r
301 const int buflen = 10;
\r
302 char buf[buflen] = { 0 };
\r
304 char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
\r
305 if ( adjusted == 0 ) {
\r
311 TIXMLASSERT( 0 <= len && len <= buflen );
\r
312 TIXMLASSERT( q + len <= adjusted );
\r
314 memcpy( q, buf, len );
\r
319 bool entityFound = false;
\r
320 for( int i = 0; i < NUM_ENTITIES; ++i ) {
\r
321 const Entity& entity = entities[i];
\r
322 if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
\r
323 && *( p + entity.length + 1 ) == ';' ) {
\r
324 // Found an entity - convert.
\r
327 p += entity.length + 2;
\r
328 entityFound = true;
\r
332 if ( !entityFound ) {
\r
333 // fixme: treat as error?
\r
347 // The loop below has plenty going on, and this
\r
348 // is a less useful mode. Break it out.
\r
349 if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
\r
350 CollapseWhitespace();
\r
352 _flags = (_flags & NEEDS_DELETE);
\r
354 TIXMLASSERT( _start );
\r
361 // --------- XMLUtil ----------- //
\r
363 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
\r
366 TIXMLASSERT( bom );
\r
368 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
\r
370 if ( *(pu+0) == TIXML_UTF_LEAD_0
\r
371 && *(pu+1) == TIXML_UTF_LEAD_1
\r
372 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
\r
381 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
\r
383 const unsigned long BYTE_MASK = 0xBF;
\r
384 const unsigned long BYTE_MARK = 0x80;
\r
385 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
\r
387 if (input < 0x80) {
\r
390 else if ( input < 0x800 ) {
\r
393 else if ( input < 0x10000 ) {
\r
396 else if ( input < 0x200000 ) {
\r
400 *length = 0; // This code won't convert this correctly anyway.
\r
406 // Scary scary fall throughs.
\r
410 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
\r
414 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
\r
418 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
\r
422 *output = (char)(input | FIRST_BYTE_MARK[*length]);
\r
425 TIXMLASSERT( false );
\r
430 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
\r
432 // Presume an entity, and pull it out.
\r
435 if ( *(p+1) == '#' && *(p+2) ) {
\r
436 unsigned long ucs = 0;
\r
437 TIXMLASSERT( sizeof( ucs ) >= 4 );
\r
438 ptrdiff_t delta = 0;
\r
440 static const char SEMICOLON = ';';
\r
442 if ( *(p+2) == 'x' ) {
\r
444 const char* q = p+3;
\r
449 q = strchr( q, SEMICOLON );
\r
454 TIXMLASSERT( *q == SEMICOLON );
\r
459 while ( *q != 'x' ) {
\r
460 unsigned int digit = 0;
\r
462 if ( *q >= '0' && *q <= '9' ) {
\r
465 else if ( *q >= 'a' && *q <= 'f' ) {
\r
466 digit = *q - 'a' + 10;
\r
468 else if ( *q >= 'A' && *q <= 'F' ) {
\r
469 digit = *q - 'A' + 10;
\r
474 TIXMLASSERT( digit >= 0 && digit < 16);
\r
475 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
\r
476 const unsigned int digitScaled = mult * digit;
\r
477 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
\r
478 ucs += digitScaled;
\r
479 TIXMLASSERT( mult <= UINT_MAX / 16 );
\r
486 const char* q = p+2;
\r
491 q = strchr( q, SEMICOLON );
\r
496 TIXMLASSERT( *q == SEMICOLON );
\r
501 while ( *q != '#' ) {
\r
502 if ( *q >= '0' && *q <= '9' ) {
\r
503 const unsigned int digit = *q - '0';
\r
504 TIXMLASSERT( digit >= 0 && digit < 10);
\r
505 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
\r
506 const unsigned int digitScaled = mult * digit;
\r
507 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
\r
508 ucs += digitScaled;
\r
513 TIXMLASSERT( mult <= UINT_MAX / 10 );
\r
518 // convert the UCS to UTF-8
\r
519 ConvertUTF32ToUTF8( ucs, value, length );
\r
520 return p + delta + 1;
\r
526 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
\r
528 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
\r
532 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
\r
534 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
\r
538 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
\r
540 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
\r
544 ToStr() of a number is a very tricky topic.
\r
545 https://github.com/leethomason/tinyxml2/issues/106
\r
547 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
\r
549 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
\r
553 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
\r
555 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
\r
559 bool XMLUtil::ToInt( const char* str, int* value )
\r
561 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
\r
567 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
\r
569 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
\r
575 bool XMLUtil::ToBool( const char* str, bool* value )
\r
578 if ( ToInt( str, &ival )) {
\r
579 *value = (ival==0) ? false : true;
\r
582 if ( StringEqual( str, "true" ) ) {
\r
586 else if ( StringEqual( str, "false" ) ) {
\r
594 bool XMLUtil::ToFloat( const char* str, float* value )
\r
596 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
\r
602 bool XMLUtil::ToDouble( const char* str, double* value )
\r
604 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
\r
611 char* XMLDocument::Identify( char* p, XMLNode** node )
\r
613 TIXMLASSERT( node );
\r
615 char* const start = p;
\r
616 p = XMLUtil::SkipWhiteSpace( p );
\r
623 // These strings define the matching patterns:
\r
624 static const char* xmlHeader = { "<?" };
\r
625 static const char* commentHeader = { "<!--" };
\r
626 static const char* cdataHeader = { "<![CDATA[" };
\r
627 static const char* dtdHeader = { "<!" };
\r
628 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
\r
630 static const int xmlHeaderLen = 2;
\r
631 static const int commentHeaderLen = 4;
\r
632 static const int cdataHeaderLen = 9;
\r
633 static const int dtdHeaderLen = 2;
\r
634 static const int elementHeaderLen = 1;
\r
636 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
\r
637 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
\r
638 XMLNode* returnNode = 0;
\r
639 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
\r
640 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
\r
641 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
\r
642 returnNode->_memPool = &_commentPool;
\r
645 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
\r
646 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
\r
647 returnNode = new (_commentPool.Alloc()) XMLComment( this );
\r
648 returnNode->_memPool = &_commentPool;
\r
649 p += commentHeaderLen;
\r
651 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
\r
652 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
\r
653 XMLText* text = new (_textPool.Alloc()) XMLText( this );
\r
655 returnNode->_memPool = &_textPool;
\r
656 p += cdataHeaderLen;
\r
657 text->SetCData( true );
\r
659 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
\r
660 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
\r
661 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
\r
662 returnNode->_memPool = &_commentPool;
\r
665 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
\r
666 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
\r
667 returnNode = new (_elementPool.Alloc()) XMLElement( this );
\r
668 returnNode->_memPool = &_elementPool;
\r
669 p += elementHeaderLen;
\r
672 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
\r
673 returnNode = new (_textPool.Alloc()) XMLText( this );
\r
674 returnNode->_memPool = &_textPool;
\r
675 p = start; // Back it up, all the text counts.
\r
678 TIXMLASSERT( returnNode );
\r
680 *node = returnNode;
\r
685 bool XMLDocument::Accept( XMLVisitor* visitor ) const
\r
687 TIXMLASSERT( visitor );
\r
688 if ( visitor->VisitEnter( *this ) ) {
\r
689 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
\r
690 if ( !node->Accept( visitor ) ) {
\r
695 return visitor->VisitExit( *this );
\r
699 // --------- XMLNode ----------- //
\r
701 XMLNode::XMLNode( XMLDocument* doc ) :
\r
704 _firstChild( 0 ), _lastChild( 0 ),
\r
705 _prev( 0 ), _next( 0 ),
\r
711 XMLNode::~XMLNode()
\r
715 _parent->Unlink( this );
\r
719 const char* XMLNode::Value() const
\r
721 // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.
\r
722 if ( this->ToDocument() )
\r
724 return _value.GetStr();
\r
727 void XMLNode::SetValue( const char* str, bool staticMem )
\r
730 _value.SetInternedStr( str );
\r
733 _value.SetStr( str );
\r
738 void XMLNode::DeleteChildren()
\r
740 while( _firstChild ) {
\r
741 TIXMLASSERT( _lastChild );
\r
742 TIXMLASSERT( _firstChild->_document == _document );
\r
743 XMLNode* node = _firstChild;
\r
746 DeleteNode( node );
\r
748 _firstChild = _lastChild = 0;
\r
752 void XMLNode::Unlink( XMLNode* child )
\r
754 TIXMLASSERT( child );
\r
755 TIXMLASSERT( child->_document == _document );
\r
756 TIXMLASSERT( child->_parent == this );
\r
757 if ( child == _firstChild ) {
\r
758 _firstChild = _firstChild->_next;
\r
760 if ( child == _lastChild ) {
\r
761 _lastChild = _lastChild->_prev;
\r
764 if ( child->_prev ) {
\r
765 child->_prev->_next = child->_next;
\r
767 if ( child->_next ) {
\r
768 child->_next->_prev = child->_prev;
\r
770 child->_parent = 0;
\r
774 void XMLNode::DeleteChild( XMLNode* node )
\r
776 TIXMLASSERT( node );
\r
777 TIXMLASSERT( node->_document == _document );
\r
778 TIXMLASSERT( node->_parent == this );
\r
780 DeleteNode( node );
\r
784 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
\r
786 TIXMLASSERT( addThis );
\r
787 if ( addThis->_document != _document ) {
\r
788 TIXMLASSERT( false );
\r
791 InsertChildPreamble( addThis );
\r
793 if ( _lastChild ) {
\r
794 TIXMLASSERT( _firstChild );
\r
795 TIXMLASSERT( _lastChild->_next == 0 );
\r
796 _lastChild->_next = addThis;
\r
797 addThis->_prev = _lastChild;
\r
798 _lastChild = addThis;
\r
800 addThis->_next = 0;
\r
803 TIXMLASSERT( _firstChild == 0 );
\r
804 _firstChild = _lastChild = addThis;
\r
806 addThis->_prev = 0;
\r
807 addThis->_next = 0;
\r
809 addThis->_parent = this;
\r
814 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
\r
816 TIXMLASSERT( addThis );
\r
817 if ( addThis->_document != _document ) {
\r
818 TIXMLASSERT( false );
\r
821 InsertChildPreamble( addThis );
\r
823 if ( _firstChild ) {
\r
824 TIXMLASSERT( _lastChild );
\r
825 TIXMLASSERT( _firstChild->_prev == 0 );
\r
827 _firstChild->_prev = addThis;
\r
828 addThis->_next = _firstChild;
\r
829 _firstChild = addThis;
\r
831 addThis->_prev = 0;
\r
834 TIXMLASSERT( _lastChild == 0 );
\r
835 _firstChild = _lastChild = addThis;
\r
837 addThis->_prev = 0;
\r
838 addThis->_next = 0;
\r
840 addThis->_parent = this;
\r
845 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
\r
847 TIXMLASSERT( addThis );
\r
848 if ( addThis->_document != _document ) {
\r
849 TIXMLASSERT( false );
\r
853 TIXMLASSERT( afterThis );
\r
855 if ( afterThis->_parent != this ) {
\r
856 TIXMLASSERT( false );
\r
860 if ( afterThis->_next == 0 ) {
\r
861 // The last node or the only node.
\r
862 return InsertEndChild( addThis );
\r
864 InsertChildPreamble( addThis );
\r
865 addThis->_prev = afterThis;
\r
866 addThis->_next = afterThis->_next;
\r
867 afterThis->_next->_prev = addThis;
\r
868 afterThis->_next = addThis;
\r
869 addThis->_parent = this;
\r
876 const XMLElement* XMLNode::FirstChildElement( const char* name ) const
\r
878 for( const XMLNode* node = _firstChild; node; node = node->_next ) {
\r
879 const XMLElement* element = node->ToElement();
\r
881 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
\r
890 const XMLElement* XMLNode::LastChildElement( const char* name ) const
\r
892 for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
\r
893 const XMLElement* element = node->ToElement();
\r
895 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
\r
904 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
\r
906 for( const XMLNode* node = _next; node; node = node->_next ) {
\r
907 const XMLElement* element = node->ToElement();
\r
909 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
\r
917 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
\r
919 for( const XMLNode* node = _prev; node; node = node->_prev ) {
\r
920 const XMLElement* element = node->ToElement();
\r
922 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
\r
930 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
\r
932 // This is a recursive method, but thinking about it "at the current level"
\r
933 // it is a pretty simple flat list:
\r
935 // <!-- comment -->
\r
937 // With a special case:
\r
940 // <!-- comment -->
\r
942 // Where the closing element (/foo) *must* be the next thing after the opening
\r
943 // element, and the names must match. BUT the tricky bit is that the closing
\r
944 // element will be read by the child.
\r
946 // 'endTag' is the end tag for this node, it is returned by a call to a child.
\r
947 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
\r
952 p = _document->Identify( p, &node );
\r
958 p = node->ParseDeep( p, &endTag );
\r
960 DeleteNode( node );
\r
961 if ( !_document->Error() ) {
\r
962 _document->SetError( XML_ERROR_PARSING, 0, 0 );
\r
967 XMLDeclaration* decl = node->ToDeclaration();
\r
969 // A declaration can only be the first child of a document.
\r
970 // Set error, if document already has children.
\r
971 if ( !_document->NoChildren() ) {
\r
972 _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);
\r
973 DeleteNode( decl );
\r
978 XMLElement* ele = node->ToElement();
\r
980 // We read the end tag. Return it to the parent.
\r
981 if ( ele->ClosingType() == XMLElement::CLOSING ) {
\r
983 ele->_value.TransferTo( parentEnd );
\r
985 node->_memPool->SetTracked(); // created and then immediately deleted.
\r
986 DeleteNode( node );
\r
990 // Handle an end tag returned to this level.
\r
991 // And handle a bunch of annoying errors.
\r
992 bool mismatch = false;
\r
993 if ( endTag.Empty() ) {
\r
994 if ( ele->ClosingType() == XMLElement::OPEN ) {
\r
999 if ( ele->ClosingType() != XMLElement::OPEN ) {
\r
1002 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
\r
1007 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );
\r
1008 DeleteNode( node );
\r
1012 InsertEndChild( node );
\r
1017 void XMLNode::DeleteNode( XMLNode* node )
\r
1019 if ( node == 0 ) {
\r
1022 MemPool* pool = node->_memPool;
\r
1024 pool->Free( node );
\r
1027 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
\r
1029 TIXMLASSERT( insertThis );
\r
1030 TIXMLASSERT( insertThis->_document == _document );
\r
1032 if ( insertThis->_parent )
\r
1033 insertThis->_parent->Unlink( insertThis );
\r
1035 insertThis->_memPool->SetTracked();
\r
1038 // --------- XMLText ---------- //
\r
1039 char* XMLText::ParseDeep( char* p, StrPair* )
\r
1041 const char* start = p;
\r
1042 if ( this->CData() ) {
\r
1043 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
\r
1045 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
\r
1050 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
\r
1051 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
\r
1052 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
\r
1055 p = _value.ParseText( p, "<", flags );
\r
1060 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
\r
1067 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
\r
1072 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
\r
1073 text->SetCData( this->CData() );
\r
1078 bool XMLText::ShallowEqual( const XMLNode* compare ) const
\r
1080 const XMLText* text = compare->ToText();
\r
1081 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
\r
1085 bool XMLText::Accept( XMLVisitor* visitor ) const
\r
1087 TIXMLASSERT( visitor );
\r
1088 return visitor->Visit( *this );
\r
1092 // --------- XMLComment ---------- //
\r
1094 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
\r
1099 XMLComment::~XMLComment()
\r
1104 char* XMLComment::ParseDeep( char* p, StrPair* )
\r
1106 // Comment parses as text.
\r
1107 const char* start = p;
\r
1108 p = _value.ParseText( p, "-->", StrPair::COMMENT );
\r
1110 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
\r
1116 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
\r
1121 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
\r
1126 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
\r
1128 TIXMLASSERT( compare );
\r
1129 const XMLComment* comment = compare->ToComment();
\r
1130 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
\r
1134 bool XMLComment::Accept( XMLVisitor* visitor ) const
\r
1136 TIXMLASSERT( visitor );
\r
1137 return visitor->Visit( *this );
\r
1141 // --------- XMLDeclaration ---------- //
\r
1143 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
\r
1148 XMLDeclaration::~XMLDeclaration()
\r
1150 //printf( "~XMLDeclaration\n" );
\r
1154 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
\r
1156 // Declaration parses as text.
\r
1157 const char* start = p;
\r
1158 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
\r
1160 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
\r
1166 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
\r
1171 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
\r
1176 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
\r
1178 TIXMLASSERT( compare );
\r
1179 const XMLDeclaration* declaration = compare->ToDeclaration();
\r
1180 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
\r
1185 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
\r
1187 TIXMLASSERT( visitor );
\r
1188 return visitor->Visit( *this );
\r
1191 // --------- XMLUnknown ---------- //
\r
1193 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
\r
1198 XMLUnknown::~XMLUnknown()
\r
1203 char* XMLUnknown::ParseDeep( char* p, StrPair* )
\r
1205 // Unknown parses as text.
\r
1206 const char* start = p;
\r
1208 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
\r
1210 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
\r
1216 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
\r
1221 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
\r
1226 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
\r
1228 TIXMLASSERT( compare );
\r
1229 const XMLUnknown* unknown = compare->ToUnknown();
\r
1230 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
\r
1234 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
\r
1236 TIXMLASSERT( visitor );
\r
1237 return visitor->Visit( *this );
\r
1240 // --------- XMLAttribute ---------- //
\r
1242 const char* XMLAttribute::Name() const
\r
1244 return _name.GetStr();
\r
1247 const char* XMLAttribute::Value() const
\r
1249 return _value.GetStr();
\r
1252 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
\r
1254 // Parse using the name rules: bug fix, was using ParseText before
\r
1255 p = _name.ParseName( p );
\r
1256 if ( !p || !*p ) {
\r
1260 // Skip white space before =
\r
1261 p = XMLUtil::SkipWhiteSpace( p );
\r
1262 if ( *p != '=' ) {
\r
1266 ++p; // move up to opening quote
\r
1267 p = XMLUtil::SkipWhiteSpace( p );
\r
1268 if ( *p != '\"' && *p != '\'' ) {
\r
1272 char endTag[2] = { *p, 0 };
\r
1273 ++p; // move past opening quote
\r
1275 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
\r
1280 void XMLAttribute::SetName( const char* n )
\r
1282 _name.SetStr( n );
\r
1286 XMLError XMLAttribute::QueryIntValue( int* value ) const
\r
1288 if ( XMLUtil::ToInt( Value(), value )) {
\r
1289 return XML_NO_ERROR;
\r
1291 return XML_WRONG_ATTRIBUTE_TYPE;
\r
1295 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
\r
1297 if ( XMLUtil::ToUnsigned( Value(), value )) {
\r
1298 return XML_NO_ERROR;
\r
1300 return XML_WRONG_ATTRIBUTE_TYPE;
\r
1304 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
\r
1306 if ( XMLUtil::ToBool( Value(), value )) {
\r
1307 return XML_NO_ERROR;
\r
1309 return XML_WRONG_ATTRIBUTE_TYPE;
\r
1313 XMLError XMLAttribute::QueryFloatValue( float* value ) const
\r
1315 if ( XMLUtil::ToFloat( Value(), value )) {
\r
1316 return XML_NO_ERROR;
\r
1318 return XML_WRONG_ATTRIBUTE_TYPE;
\r
1322 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
\r
1324 if ( XMLUtil::ToDouble( Value(), value )) {
\r
1325 return XML_NO_ERROR;
\r
1327 return XML_WRONG_ATTRIBUTE_TYPE;
\r
1331 void XMLAttribute::SetAttribute( const char* v )
\r
1333 _value.SetStr( v );
\r
1337 void XMLAttribute::SetAttribute( int v )
\r
1339 char buf[BUF_SIZE];
\r
1340 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1341 _value.SetStr( buf );
\r
1345 void XMLAttribute::SetAttribute( unsigned v )
\r
1347 char buf[BUF_SIZE];
\r
1348 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1349 _value.SetStr( buf );
\r
1353 void XMLAttribute::SetAttribute( bool v )
\r
1355 char buf[BUF_SIZE];
\r
1356 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1357 _value.SetStr( buf );
\r
1360 void XMLAttribute::SetAttribute( double v )
\r
1362 char buf[BUF_SIZE];
\r
1363 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1364 _value.SetStr( buf );
\r
1367 void XMLAttribute::SetAttribute( float v )
\r
1369 char buf[BUF_SIZE];
\r
1370 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1371 _value.SetStr( buf );
\r
1375 // --------- XMLElement ---------- //
\r
1376 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
\r
1377 _closingType( 0 ),
\r
1378 _rootAttribute( 0 )
\r
1383 XMLElement::~XMLElement()
\r
1385 while( _rootAttribute ) {
\r
1386 XMLAttribute* next = _rootAttribute->_next;
\r
1387 DeleteAttribute( _rootAttribute );
\r
1388 _rootAttribute = next;
\r
1393 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
\r
1395 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
\r
1396 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
\r
1404 const char* XMLElement::Attribute( const char* name, const char* value ) const
\r
1406 const XMLAttribute* a = FindAttribute( name );
\r
1410 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
\r
1411 return a->Value();
\r
1417 const char* XMLElement::GetText() const
\r
1419 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1420 return FirstChild()->Value();
\r
1426 void XMLElement::SetText( const char* inText )
\r
1428 if ( FirstChild() && FirstChild()->ToText() )
\r
1429 FirstChild()->SetValue( inText );
\r
1431 XMLText* theText = GetDocument()->NewText( inText );
\r
1432 InsertFirstChild( theText );
\r
1437 void XMLElement::SetText( int v )
\r
1439 char buf[BUF_SIZE];
\r
1440 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1445 void XMLElement::SetText( unsigned v )
\r
1447 char buf[BUF_SIZE];
\r
1448 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1453 void XMLElement::SetText( bool v )
\r
1455 char buf[BUF_SIZE];
\r
1456 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1461 void XMLElement::SetText( float v )
\r
1463 char buf[BUF_SIZE];
\r
1464 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1469 void XMLElement::SetText( double v )
\r
1471 char buf[BUF_SIZE];
\r
1472 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
1477 XMLError XMLElement::QueryIntText( int* ival ) const
\r
1479 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1480 const char* t = FirstChild()->Value();
\r
1481 if ( XMLUtil::ToInt( t, ival ) ) {
\r
1482 return XML_SUCCESS;
\r
1484 return XML_CAN_NOT_CONVERT_TEXT;
\r
1486 return XML_NO_TEXT_NODE;
\r
1490 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
\r
1492 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1493 const char* t = FirstChild()->Value();
\r
1494 if ( XMLUtil::ToUnsigned( t, uval ) ) {
\r
1495 return XML_SUCCESS;
\r
1497 return XML_CAN_NOT_CONVERT_TEXT;
\r
1499 return XML_NO_TEXT_NODE;
\r
1503 XMLError XMLElement::QueryBoolText( bool* bval ) const
\r
1505 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1506 const char* t = FirstChild()->Value();
\r
1507 if ( XMLUtil::ToBool( t, bval ) ) {
\r
1508 return XML_SUCCESS;
\r
1510 return XML_CAN_NOT_CONVERT_TEXT;
\r
1512 return XML_NO_TEXT_NODE;
\r
1516 XMLError XMLElement::QueryDoubleText( double* dval ) const
\r
1518 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1519 const char* t = FirstChild()->Value();
\r
1520 if ( XMLUtil::ToDouble( t, dval ) ) {
\r
1521 return XML_SUCCESS;
\r
1523 return XML_CAN_NOT_CONVERT_TEXT;
\r
1525 return XML_NO_TEXT_NODE;
\r
1529 XMLError XMLElement::QueryFloatText( float* fval ) const
\r
1531 if ( FirstChild() && FirstChild()->ToText() ) {
\r
1532 const char* t = FirstChild()->Value();
\r
1533 if ( XMLUtil::ToFloat( t, fval ) ) {
\r
1534 return XML_SUCCESS;
\r
1536 return XML_CAN_NOT_CONVERT_TEXT;
\r
1538 return XML_NO_TEXT_NODE;
\r
1543 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
\r
1545 XMLAttribute* last = 0;
\r
1546 XMLAttribute* attrib = 0;
\r
1547 for( attrib = _rootAttribute;
\r
1549 last = attrib, attrib = attrib->_next ) {
\r
1550 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
\r
1555 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
\r
1556 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
\r
1557 attrib->_memPool = &_document->_attributePool;
\r
1559 last->_next = attrib;
\r
1562 _rootAttribute = attrib;
\r
1564 attrib->SetName( name );
\r
1565 attrib->_memPool->SetTracked(); // always created and linked.
\r
1571 void XMLElement::DeleteAttribute( const char* name )
\r
1573 XMLAttribute* prev = 0;
\r
1574 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
\r
1575 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
\r
1577 prev->_next = a->_next;
\r
1580 _rootAttribute = a->_next;
\r
1582 DeleteAttribute( a );
\r
1590 char* XMLElement::ParseAttributes( char* p )
\r
1592 const char* start = p;
\r
1593 XMLAttribute* prevAttribute = 0;
\r
1595 // Read the attributes.
\r
1597 p = XMLUtil::SkipWhiteSpace( p );
\r
1599 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
\r
1604 if (XMLUtil::IsNameStartChar( *p ) ) {
\r
1605 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
\r
1606 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
\r
1607 attrib->_memPool = &_document->_attributePool;
\r
1608 attrib->_memPool->SetTracked();
\r
1610 p = attrib->ParseDeep( p, _document->ProcessEntities() );
\r
1611 if ( !p || Attribute( attrib->Name() ) ) {
\r
1612 DeleteAttribute( attrib );
\r
1613 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
\r
1616 // There is a minor bug here: if the attribute in the source xml
\r
1617 // document is duplicated, it will not be detected and the
\r
1618 // attribute will be doubly added. However, tracking the 'prevAttribute'
\r
1619 // avoids re-scanning the attribute list. Preferring performance for
\r
1620 // now, may reconsider in the future.
\r
1621 if ( prevAttribute ) {
\r
1622 prevAttribute->_next = attrib;
\r
1625 _rootAttribute = attrib;
\r
1627 prevAttribute = attrib;
\r
1630 else if ( *p == '>' ) {
\r
1635 else if ( *p == '/' && *(p+1) == '>' ) {
\r
1636 _closingType = CLOSED;
\r
1637 return p+2; // done; sealed element.
\r
1640 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
\r
1647 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
\r
1649 if ( attribute == 0 ) {
\r
1652 MemPool* pool = attribute->_memPool;
\r
1653 attribute->~XMLAttribute();
\r
1654 pool->Free( attribute );
\r
1659 // <ele>foo<b>bar</b></ele>
\r
1661 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
\r
1663 // Read the element name.
\r
1664 p = XMLUtil::SkipWhiteSpace( p );
\r
1666 // The closing element is the </element> form. It is
\r
1667 // parsed just like a regular element then deleted from
\r
1669 if ( *p == '/' ) {
\r
1670 _closingType = CLOSING;
\r
1674 p = _value.ParseName( p );
\r
1675 if ( _value.Empty() ) {
\r
1679 p = ParseAttributes( p );
\r
1680 if ( !p || !*p || _closingType ) {
\r
1684 p = XMLNode::ParseDeep( p, strPair );
\r
1690 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
\r
1695 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
\r
1696 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
\r
1697 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
\r
1703 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
\r
1705 TIXMLASSERT( compare );
\r
1706 const XMLElement* other = compare->ToElement();
\r
1707 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
\r
1709 const XMLAttribute* a=FirstAttribute();
\r
1710 const XMLAttribute* b=other->FirstAttribute();
\r
1712 while ( a && b ) {
\r
1713 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
\r
1720 // different count
\r
1729 bool XMLElement::Accept( XMLVisitor* visitor ) const
\r
1731 TIXMLASSERT( visitor );
\r
1732 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
\r
1733 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
\r
1734 if ( !node->Accept( visitor ) ) {
\r
1739 return visitor->VisitExit( *this );
\r
1743 // --------- XMLDocument ----------- //
\r
1745 // Warning: List must match 'enum XMLError'
\r
1746 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
\r
1748 "XML_NO_ATTRIBUTE",
\r
1749 "XML_WRONG_ATTRIBUTE_TYPE",
\r
1750 "XML_ERROR_FILE_NOT_FOUND",
\r
1751 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
\r
1752 "XML_ERROR_FILE_READ_ERROR",
\r
1753 "XML_ERROR_ELEMENT_MISMATCH",
\r
1754 "XML_ERROR_PARSING_ELEMENT",
\r
1755 "XML_ERROR_PARSING_ATTRIBUTE",
\r
1756 "XML_ERROR_IDENTIFYING_TAG",
\r
1757 "XML_ERROR_PARSING_TEXT",
\r
1758 "XML_ERROR_PARSING_CDATA",
\r
1759 "XML_ERROR_PARSING_COMMENT",
\r
1760 "XML_ERROR_PARSING_DECLARATION",
\r
1761 "XML_ERROR_PARSING_UNKNOWN",
\r
1762 "XML_ERROR_EMPTY_DOCUMENT",
\r
1763 "XML_ERROR_MISMATCHED_ELEMENT",
\r
1764 "XML_ERROR_PARSING",
\r
1765 "XML_CAN_NOT_CONVERT_TEXT",
\r
1766 "XML_NO_TEXT_NODE"
\r
1770 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
\r
1772 _writeBOM( false ),
\r
1773 _processEntities( processEntities ),
\r
1774 _errorID( XML_NO_ERROR ),
\r
1775 _whitespace( whitespace ),
\r
1780 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
\r
1785 XMLDocument::~XMLDocument()
\r
1791 void XMLDocument::Clear()
\r
1796 const bool hadError = Error();
\r
1798 _errorID = XML_NO_ERROR;
\r
1802 delete [] _charBuffer;
\r
1806 _textPool.Trace( "text" );
\r
1807 _elementPool.Trace( "element" );
\r
1808 _commentPool.Trace( "comment" );
\r
1809 _attributePool.Trace( "attribute" );
\r
1813 if ( !hadError ) {
\r
1814 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
\r
1815 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
\r
1816 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
\r
1817 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
\r
1823 XMLElement* XMLDocument::NewElement( const char* name )
\r
1825 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
\r
1826 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
\r
1827 ele->_memPool = &_elementPool;
\r
1828 ele->SetName( name );
\r
1833 XMLComment* XMLDocument::NewComment( const char* str )
\r
1835 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
\r
1836 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
\r
1837 comment->_memPool = &_commentPool;
\r
1838 comment->SetValue( str );
\r
1843 XMLText* XMLDocument::NewText( const char* str )
\r
1845 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
\r
1846 XMLText* text = new (_textPool.Alloc()) XMLText( this );
\r
1847 text->_memPool = &_textPool;
\r
1848 text->SetValue( str );
\r
1853 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
\r
1855 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
\r
1856 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
\r
1857 dec->_memPool = &_commentPool;
\r
1858 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
\r
1863 XMLUnknown* XMLDocument::NewUnknown( const char* str )
\r
1865 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
\r
1866 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
\r
1867 unk->_memPool = &_commentPool;
\r
1868 unk->SetValue( str );
\r
1872 static FILE* callfopen( const char* filepath, const char* mode )
\r
1874 TIXMLASSERT( filepath );
\r
1875 TIXMLASSERT( mode );
\r
1876 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
\r
1878 errno_t err = fopen_s( &fp, filepath, mode );
\r
1883 FILE* fp = fopen( filepath, mode );
\r
1888 void XMLDocument::DeleteNode( XMLNode* node ) {
\r
1889 TIXMLASSERT( node );
\r
1890 TIXMLASSERT(node->_document == this );
\r
1891 if (node->_parent) {
\r
1892 node->_parent->DeleteChild( node );
\r
1895 // Isn't in the tree.
\r
1896 // Use the parent delete.
\r
1897 // Also, we need to mark it tracked: we 'know'
\r
1898 // it was never used.
\r
1899 node->_memPool->SetTracked();
\r
1900 // Call the static XMLNode version:
\r
1901 XMLNode::DeleteNode(node);
\r
1906 XMLError XMLDocument::LoadFile( const char* filename )
\r
1909 FILE* fp = callfopen( filename, "rb" );
\r
1911 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
\r
1919 // This is likely overengineered template art to have a check that unsigned long value incremented
\r
1920 // by one still fits into size_t. If size_t type is larger than unsigned long type
\r
1921 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
\r
1922 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
\r
1923 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
\r
1924 // types sizes relate to each other.
\r
1926 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
\r
1927 struct LongFitsIntoSizeTMinusOne {
\r
1928 static bool Fits( unsigned long value )
\r
1930 return value < (size_t)-1;
\r
1935 bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ )
\r
1940 XMLError XMLDocument::LoadFile( FILE* fp )
\r
1944 fseek( fp, 0, SEEK_SET );
\r
1945 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
\r
1946 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
\r
1950 fseek( fp, 0, SEEK_END );
\r
1951 const long filelength = ftell( fp );
\r
1952 fseek( fp, 0, SEEK_SET );
\r
1953 if ( filelength == -1L ) {
\r
1954 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
\r
1957 TIXMLASSERT( filelength >= 0 );
\r
1959 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
\r
1960 // Cannot handle files which won't fit in buffer together with null terminator
\r
1961 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
\r
1965 if ( filelength == 0 ) {
\r
1966 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
\r
1970 const size_t size = filelength;
\r
1971 TIXMLASSERT( _charBuffer == 0 );
\r
1972 _charBuffer = new char[size+1];
\r
1973 size_t read = fread( _charBuffer, 1, size, fp );
\r
1974 if ( read != size ) {
\r
1975 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
\r
1979 _charBuffer[size] = 0;
\r
1986 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
\r
1988 FILE* fp = callfopen( filename, "w" );
\r
1990 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
\r
1993 SaveFile(fp, compact);
\r
1999 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
\r
2001 // Clear any error from the last save, otherwise it will get reported
\r
2002 // for *this* call.
\r
2003 SetError( XML_NO_ERROR, 0, 0 );
\r
2004 XMLPrinter stream( fp, compact );
\r
2010 XMLError XMLDocument::Parse( const char* p, size_t len )
\r
2014 if ( len == 0 || !p || !*p ) {
\r
2015 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
\r
2018 if ( len == (size_t)(-1) ) {
\r
2019 len = strlen( p );
\r
2021 TIXMLASSERT( _charBuffer == 0 );
\r
2022 _charBuffer = new char[ len+1 ];
\r
2023 memcpy( _charBuffer, p, len );
\r
2024 _charBuffer[len] = 0;
\r
2028 // clean up now essentially dangling memory.
\r
2029 // and the parse fail can put objects in the
\r
2030 // pools that are dead and inaccessible.
\r
2032 _elementPool.Clear();
\r
2033 _attributePool.Clear();
\r
2034 _textPool.Clear();
\r
2035 _commentPool.Clear();
\r
2041 void XMLDocument::Print( XMLPrinter* streamer ) const
\r
2044 Accept( streamer );
\r
2047 XMLPrinter stdoutStreamer( stdout );
\r
2048 Accept( &stdoutStreamer );
\r
2053 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
\r
2055 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
\r
2057 _errorStr1 = str1;
\r
2058 _errorStr2 = str2;
\r
2061 const char* XMLDocument::ErrorName() const
\r
2063 TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
\r
2064 const char* errorName = _errorNames[_errorID];
\r
2065 TIXMLASSERT( errorName && errorName[0] );
\r
2069 void XMLDocument::PrintError() const
\r
2072 static const int LEN = 20;
\r
2073 char buf1[LEN] = { 0 };
\r
2074 char buf2[LEN] = { 0 };
\r
2076 if ( _errorStr1 ) {
\r
2077 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
\r
2079 if ( _errorStr2 ) {
\r
2080 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
\r
2083 // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
\r
2084 // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
\r
2085 TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
\r
2086 printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
\r
2087 static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );
\r
2091 void XMLDocument::Parse()
\r
2093 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
\r
2094 TIXMLASSERT( _charBuffer );
\r
2095 char* p = _charBuffer;
\r
2096 p = XMLUtil::SkipWhiteSpace( p );
\r
2097 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
\r
2099 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
\r
2105 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
\r
2106 _elementJustOpened( false ),
\r
2107 _firstElement( true ),
\r
2111 _processEntities( true ),
\r
2112 _compactMode( compact )
\r
2114 for( int i=0; i<ENTITY_RANGE; ++i ) {
\r
2115 _entityFlag[i] = false;
\r
2116 _restrictedEntityFlag[i] = false;
\r
2118 for( int i=0; i<NUM_ENTITIES; ++i ) {
\r
2119 const char entityValue = entities[i].value;
\r
2120 TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
\r
2121 _entityFlag[ (unsigned char)entityValue ] = true;
\r
2123 _restrictedEntityFlag[(unsigned char)'&'] = true;
\r
2124 _restrictedEntityFlag[(unsigned char)'<'] = true;
\r
2125 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
\r
2126 _buffer.Push( 0 );
\r
2130 void XMLPrinter::Print( const char* format, ... )
\r
2133 va_start( va, format );
\r
2136 vfprintf( _fp, format, va );
\r
2139 const int len = TIXML_VSCPRINTF( format, va );
\r
2140 // Close out and re-start the va-args
\r
2142 TIXMLASSERT( len >= 0 );
\r
2143 va_start( va, format );
\r
2144 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
\r
2145 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
\r
2146 TIXML_VSNPRINTF( p, len+1, format, va );
\r
2152 void XMLPrinter::PrintSpace( int depth )
\r
2154 for( int i=0; i<depth; ++i ) {
\r
2160 void XMLPrinter::PrintString( const char* p, bool restricted )
\r
2162 // Look for runs of bytes between entities to print.
\r
2163 const char* q = p;
\r
2165 if ( _processEntities ) {
\r
2166 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
\r
2168 TIXMLASSERT( p <= q );
\r
2169 // Remember, char is sometimes signed. (How many times has that bitten me?)
\r
2170 if ( *q > 0 && *q < ENTITY_RANGE ) {
\r
2171 // Check for entities. If one is found, flush
\r
2172 // the stream up until the entity, write the
\r
2173 // entity, and keep looking.
\r
2174 if ( flag[(unsigned char)(*q)] ) {
\r
2176 const size_t delta = q - p;
\r
2177 // %.*s accepts type int as "precision"
\r
2178 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
\r
2179 Print( "%.*s", toPrint, p );
\r
2182 bool entityPatternPrinted = false;
\r
2183 for( int i=0; i<NUM_ENTITIES; ++i ) {
\r
2184 if ( entities[i].value == *q ) {
\r
2185 Print( "&%s;", entities[i].pattern );
\r
2186 entityPatternPrinted = true;
\r
2190 if ( !entityPatternPrinted ) {
\r
2191 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
\r
2192 TIXMLASSERT( false );
\r
2198 TIXMLASSERT( p <= q );
\r
2201 // Flush the remaining string. This will be the entire
\r
2202 // string if an entity wasn't found.
\r
2203 TIXMLASSERT( p <= q );
\r
2204 if ( !_processEntities || ( p < q ) ) {
\r
2210 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
\r
2213 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
\r
2214 Print( "%s", bom );
\r
2217 PushDeclaration( "xml version=\"1.0\"" );
\r
2222 void XMLPrinter::OpenElement( const char* name, bool compactMode )
\r
2224 SealElementIfJustOpened();
\r
2225 _stack.Push( name );
\r
2227 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
\r
2230 if ( !compactMode ) {
\r
2231 PrintSpace( _depth );
\r
2234 Print( "<%s", name );
\r
2235 _elementJustOpened = true;
\r
2236 _firstElement = false;
\r
2241 void XMLPrinter::PushAttribute( const char* name, const char* value )
\r
2243 TIXMLASSERT( _elementJustOpened );
\r
2244 Print( " %s=\"", name );
\r
2245 PrintString( value, false );
\r
2250 void XMLPrinter::PushAttribute( const char* name, int v )
\r
2252 char buf[BUF_SIZE];
\r
2253 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
2254 PushAttribute( name, buf );
\r
2258 void XMLPrinter::PushAttribute( const char* name, unsigned v )
\r
2260 char buf[BUF_SIZE];
\r
2261 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
2262 PushAttribute( name, buf );
\r
2266 void XMLPrinter::PushAttribute( const char* name, bool v )
\r
2268 char buf[BUF_SIZE];
\r
2269 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
2270 PushAttribute( name, buf );
\r
2274 void XMLPrinter::PushAttribute( const char* name, double v )
\r
2276 char buf[BUF_SIZE];
\r
2277 XMLUtil::ToStr( v, buf, BUF_SIZE );
\r
2278 PushAttribute( name, buf );
\r
2282 void XMLPrinter::CloseElement( bool compactMode )
\r
2285 const char* name = _stack.Pop();
\r
2287 if ( _elementJustOpened ) {
\r
2291 if ( _textDepth < 0 && !compactMode) {
\r
2293 PrintSpace( _depth );
\r
2295 Print( "</%s>", name );
\r
2298 if ( _textDepth == _depth ) {
\r
2301 if ( _depth == 0 && !compactMode) {
\r
2304 _elementJustOpened = false;
\r
2308 void XMLPrinter::SealElementIfJustOpened()
\r
2310 if ( !_elementJustOpened ) {
\r
2313 _elementJustOpened = false;
\r
2318 void XMLPrinter::PushText( const char* text, bool cdata )
\r
2320 _textDepth = _depth-1;
\r
2322 SealElementIfJustOpened();
\r
2324 Print( "<![CDATA[%s]]>", text );
\r
2327 PrintString( text, true );
\r
2331 void XMLPrinter::PushText( int value )
\r
2333 char buf[BUF_SIZE];
\r
2334 XMLUtil::ToStr( value, buf, BUF_SIZE );
\r
2335 PushText( buf, false );
\r
2339 void XMLPrinter::PushText( unsigned value )
\r
2341 char buf[BUF_SIZE];
\r
2342 XMLUtil::ToStr( value, buf, BUF_SIZE );
\r
2343 PushText( buf, false );
\r
2347 void XMLPrinter::PushText( bool value )
\r
2349 char buf[BUF_SIZE];
\r
2350 XMLUtil::ToStr( value, buf, BUF_SIZE );
\r
2351 PushText( buf, false );
\r
2355 void XMLPrinter::PushText( float value )
\r
2357 char buf[BUF_SIZE];
\r
2358 XMLUtil::ToStr( value, buf, BUF_SIZE );
\r
2359 PushText( buf, false );
\r
2363 void XMLPrinter::PushText( double value )
\r
2365 char buf[BUF_SIZE];
\r
2366 XMLUtil::ToStr( value, buf, BUF_SIZE );
\r
2367 PushText( buf, false );
\r
2371 void XMLPrinter::PushComment( const char* comment )
\r
2373 SealElementIfJustOpened();
\r
2374 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
\r
2376 PrintSpace( _depth );
\r
2378 _firstElement = false;
\r
2379 Print( "<!--%s-->", comment );
\r
2383 void XMLPrinter::PushDeclaration( const char* value )
\r
2385 SealElementIfJustOpened();
\r
2386 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
\r
2388 PrintSpace( _depth );
\r
2390 _firstElement = false;
\r
2391 Print( "<?%s?>", value );
\r
2395 void XMLPrinter::PushUnknown( const char* value )
\r
2397 SealElementIfJustOpened();
\r
2398 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
\r
2400 PrintSpace( _depth );
\r
2402 _firstElement = false;
\r
2403 Print( "<!%s>", value );
\r
2407 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
\r
2409 _processEntities = doc.ProcessEntities();
\r
2410 if ( doc.HasBOM() ) {
\r
2411 PushHeader( true, false );
\r
2417 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
\r
2419 const XMLElement* parentElem = 0;
\r
2420 if ( element.Parent() ) {
\r
2421 parentElem = element.Parent()->ToElement();
\r
2423 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
\r
2424 OpenElement( element.Name(), compactMode );
\r
2425 while ( attribute ) {
\r
2426 PushAttribute( attribute->Name(), attribute->Value() );
\r
2427 attribute = attribute->Next();
\r
2433 bool XMLPrinter::VisitExit( const XMLElement& element )
\r
2435 CloseElement( CompactMode(element) );
\r
2440 bool XMLPrinter::Visit( const XMLText& text )
\r
2442 PushText( text.Value(), text.CData() );
\r
2447 bool XMLPrinter::Visit( const XMLComment& comment )
\r
2449 PushComment( comment.Value() );
\r
2453 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
\r
2455 PushDeclaration( declaration.Value() );
\r
2460 bool XMLPrinter::Visit( const XMLUnknown& unknown )
\r
2462 PushUnknown( unknown.Value() );
\r
2466 } // namespace tinyxml2
\r