Fix missing break in switch
[gromacs.git] / src / external / tinyxml2 / tinyxml2.cpp
blobe53ac5691c6b9a360993f76568fec8457bd46982
1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source
21 distribution.
24 #include "tinyxml2.h"
26 #include <new> // yes, this one new style header, is in the Android SDK.
27 #if defined(ANDROID_NDK) || defined(__QNXNTO__)
28 # include <stddef.h>
29 #else
30 # include <cstddef>
31 #endif
33 #if defined(__GNUC__) && __GNUC__>=7
34 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
35 #endif
37 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
38 static const char LF = LINE_FEED;
39 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
40 static const char CR = CARRIAGE_RETURN;
41 static const char SINGLE_QUOTE = '\'';
42 static const char DOUBLE_QUOTE = '\"';
44 // Bunch of unicode info at:
45 // http://www.unicode.org/faq/utf_bom.html
46 // ef bb bf (Microsoft "lead bytes") - designates UTF-8
48 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
49 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
50 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
52 namespace tinyxml2
55 struct Entity {
56 const char* pattern;
57 int length;
58 char value;
61 static const int NUM_ENTITIES = 5;
62 static const Entity entities[NUM_ENTITIES] = {
63 { "quot", 4, DOUBLE_QUOTE },
64 { "amp", 3, '&' },
65 { "apos", 4, SINGLE_QUOTE },
66 { "lt", 2, '<' },
67 { "gt", 2, '>' }
71 StrPair::~StrPair()
73 Reset();
77 void StrPair::TransferTo( StrPair* other )
79 if ( this == other ) {
80 return;
82 // This in effect implements the assignment operator by "moving"
83 // ownership (as in auto_ptr).
85 TIXMLASSERT( other->_flags == 0 );
86 TIXMLASSERT( other->_start == 0 );
87 TIXMLASSERT( other->_end == 0 );
89 other->Reset();
91 other->_flags = _flags;
92 other->_start = _start;
93 other->_end = _end;
95 _flags = 0;
96 _start = 0;
97 _end = 0;
100 void StrPair::Reset()
102 if ( _flags & NEEDS_DELETE ) {
103 delete [] _start;
105 _flags = 0;
106 _start = 0;
107 _end = 0;
111 void StrPair::SetStr( const char* str, int flags )
113 Reset();
114 size_t len = strlen( str );
115 _start = new char[ len+1 ];
116 memcpy( _start, str, len+1 );
117 _end = _start + len;
118 _flags = flags | NEEDS_DELETE;
122 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
124 TIXMLASSERT( endTag && *endTag );
126 char* start = p;
127 char endChar = *endTag;
128 size_t length = strlen( endTag );
130 // Inner loop of text parsing.
131 while ( *p ) {
132 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
133 Set( start, p, strFlags );
134 return p + length;
136 ++p;
138 return 0;
142 char* StrPair::ParseName( char* p )
144 if ( !p || !(*p) ) {
145 return 0;
147 if ( !XMLUtil::IsNameStartChar( *p ) ) {
148 return 0;
151 char* const start = p;
152 ++p;
153 while ( *p && XMLUtil::IsNameChar( *p ) ) {
154 ++p;
157 Set( start, p, 0 );
158 return p;
162 void StrPair::CollapseWhitespace()
164 // Adjusting _start would cause undefined behavior on delete[]
165 TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
166 // Trim leading space.
167 _start = XMLUtil::SkipWhiteSpace( _start );
169 if ( *_start ) {
170 char* p = _start; // the read pointer
171 char* q = _start; // the write pointer
173 while( *p ) {
174 if ( XMLUtil::IsWhiteSpace( *p )) {
175 p = XMLUtil::SkipWhiteSpace( p );
176 if ( *p == 0 ) {
177 break; // don't write to q; this trims the trailing space.
179 *q = ' ';
180 ++q;
182 *q = *p;
183 ++q;
184 ++p;
186 *q = 0;
191 const char* StrPair::GetStr()
193 TIXMLASSERT( _start );
194 TIXMLASSERT( _end );
195 if ( _flags & NEEDS_FLUSH ) {
196 *_end = 0;
197 _flags ^= NEEDS_FLUSH;
199 if ( _flags ) {
200 char* p = _start; // the read pointer
201 char* q = _start; // the write pointer
203 while( p < _end ) {
204 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
205 // CR-LF pair becomes LF
206 // CR alone becomes LF
207 // LF-CR becomes LF
208 if ( *(p+1) == LF ) {
209 p += 2;
211 else {
212 ++p;
214 *q++ = LF;
216 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
217 if ( *(p+1) == CR ) {
218 p += 2;
220 else {
221 ++p;
223 *q++ = LF;
225 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
226 // Entities handled by tinyXML2:
227 // - special entities in the entity table [in/out]
228 // - numeric character reference [in]
229 // &#20013; or &#x4e2d;
231 if ( *(p+1) == '#' ) {
232 const int buflen = 10;
233 char buf[buflen] = { 0 };
234 int len = 0;
235 char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
236 if ( adjusted == 0 ) {
237 *q = *p;
238 ++p;
239 ++q;
241 else {
242 TIXMLASSERT( 0 <= len && len <= buflen );
243 TIXMLASSERT( q + len <= adjusted );
244 p = adjusted;
245 memcpy( q, buf, len );
246 q += len;
249 else {
250 int i=0;
251 for(; i<NUM_ENTITIES; ++i ) {
252 const Entity& entity = entities[i];
253 if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
254 && *( p + entity.length + 1 ) == ';' ) {
255 // Found an entity - convert.
256 *q = entity.value;
257 ++q;
258 p += entity.length + 2;
259 break;
262 if ( i == NUM_ENTITIES ) {
263 // fixme: treat as error?
264 ++p;
265 ++q;
269 else {
270 *q = *p;
271 ++p;
272 ++q;
275 *q = 0;
277 // The loop below has plenty going on, and this
278 // is a less useful mode. Break it out.
279 if ( _flags & COLLAPSE_WHITESPACE ) {
280 CollapseWhitespace();
282 _flags = (_flags & NEEDS_DELETE);
284 TIXMLASSERT( _start );
285 return _start;
291 // --------- XMLUtil ----------- //
293 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
295 TIXMLASSERT( p );
296 TIXMLASSERT( bom );
297 *bom = false;
298 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
299 // Check for BOM:
300 if ( *(pu+0) == TIXML_UTF_LEAD_0
301 && *(pu+1) == TIXML_UTF_LEAD_1
302 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
303 *bom = true;
304 p += 3;
306 TIXMLASSERT( p );
307 return p;
311 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
313 const unsigned long BYTE_MASK = 0xBF;
314 const unsigned long BYTE_MARK = 0x80;
315 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
317 if (input < 0x80) {
318 *length = 1;
320 else if ( input < 0x800 ) {
321 *length = 2;
323 else if ( input < 0x10000 ) {
324 *length = 3;
326 else if ( input < 0x200000 ) {
327 *length = 4;
329 else {
330 *length = 0; // This code won't covert this correctly anyway.
331 return;
334 output += *length;
336 // Scary scary fall throughs.
337 switch (*length) {
338 case 4:
339 --output;
340 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
341 input >>= 6;
342 case 3:
343 --output;
344 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
345 input >>= 6;
346 case 2:
347 --output;
348 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
349 input >>= 6;
350 case 1:
351 --output;
352 *output = (char)(input | FIRST_BYTE_MARK[*length]);
353 break;
354 default:
355 TIXMLASSERT( false );
360 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
362 // Presume an entity, and pull it out.
363 *length = 0;
365 if ( *(p+1) == '#' && *(p+2) ) {
366 unsigned long ucs = 0;
367 TIXMLASSERT( sizeof( ucs ) >= 4 );
368 ptrdiff_t delta = 0;
369 unsigned mult = 1;
370 static const char SEMICOLON = ';';
372 if ( *(p+2) == 'x' ) {
373 // Hexadecimal.
374 const char* q = p+3;
375 if ( !(*q) ) {
376 return 0;
379 q = strchr( q, SEMICOLON );
381 if ( !q ) {
382 return 0;
384 TIXMLASSERT( *q == SEMICOLON );
386 delta = q-p;
387 --q;
389 while ( *q != 'x' ) {
390 unsigned int digit = 0;
392 if ( *q >= '0' && *q <= '9' ) {
393 digit = *q - '0';
395 else if ( *q >= 'a' && *q <= 'f' ) {
396 digit = *q - 'a' + 10;
398 else if ( *q >= 'A' && *q <= 'F' ) {
399 digit = *q - 'A' + 10;
401 else {
402 return 0;
404 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
405 TIXMLASSERT( digit >= 0 && digit < 16);
406 const unsigned int digitScaled = mult * digit;
407 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
408 ucs += digitScaled;
409 TIXMLASSERT( mult <= UINT_MAX / 16 );
410 mult *= 16;
411 --q;
414 else {
415 // Decimal.
416 const char* q = p+2;
417 if ( !(*q) ) {
418 return 0;
421 q = strchr( q, SEMICOLON );
423 if ( !q ) {
424 return 0;
426 TIXMLASSERT( *q == SEMICOLON );
428 delta = q-p;
429 --q;
431 while ( *q != '#' ) {
432 if ( *q >= '0' && *q <= '9' ) {
433 const unsigned int digit = *q - '0';
434 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
435 const unsigned int digitScaled = mult * digit;
436 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
437 ucs += digitScaled;
439 else {
440 return 0;
442 TIXMLASSERT( mult <= UINT_MAX / 10 );
443 mult *= 10;
444 --q;
447 // convert the UCS to UTF-8
448 ConvertUTF32ToUTF8( ucs, value, length );
449 return p + delta + 1;
451 return p+1;
455 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
457 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
461 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
463 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
467 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
469 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
473 ToStr() of a number is a very tricky topic.
474 https://github.com/leethomason/tinyxml2/issues/106
476 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
478 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
482 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
484 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
488 bool XMLUtil::ToInt( const char* str, int* value )
490 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
491 return true;
493 return false;
496 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
498 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
499 return true;
501 return false;
504 bool XMLUtil::ToBool( const char* str, bool* value )
506 int ival = 0;
507 if ( ToInt( str, &ival )) {
508 *value = (ival==0) ? false : true;
509 return true;
511 if ( StringEqual( str, "true" ) ) {
512 *value = true;
513 return true;
515 else if ( StringEqual( str, "false" ) ) {
516 *value = false;
517 return true;
519 return false;
523 bool XMLUtil::ToFloat( const char* str, float* value )
525 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
526 return true;
528 return false;
531 bool XMLUtil::ToDouble( const char* str, double* value )
533 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
534 return true;
536 return false;
540 char* XMLDocument::Identify( char* p, XMLNode** node )
542 TIXMLASSERT( node );
543 TIXMLASSERT( p );
544 char* const start = p;
545 p = XMLUtil::SkipWhiteSpace( p );
546 if( !*p ) {
547 *node = 0;
548 TIXMLASSERT( p );
549 return p;
552 // What is this thing?
553 // These strings define the matching patters:
554 static const char* xmlHeader = { "<?" };
555 static const char* commentHeader = { "<!--" };
556 static const char* dtdHeader = { "<!" };
557 static const char* cdataHeader = { "<![CDATA[" };
558 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
560 static const int xmlHeaderLen = 2;
561 static const int commentHeaderLen = 4;
562 static const int dtdHeaderLen = 2;
563 static const int cdataHeaderLen = 9;
564 static const int elementHeaderLen = 1;
566 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
567 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
568 XMLNode* returnNode = 0;
569 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
570 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
571 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
572 returnNode->_memPool = &_commentPool;
573 p += xmlHeaderLen;
575 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
576 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
577 returnNode = new (_commentPool.Alloc()) XMLComment( this );
578 returnNode->_memPool = &_commentPool;
579 p += commentHeaderLen;
581 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
582 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
583 XMLText* text = new (_textPool.Alloc()) XMLText( this );
584 returnNode = text;
585 returnNode->_memPool = &_textPool;
586 p += cdataHeaderLen;
587 text->SetCData( true );
589 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
590 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
591 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
592 returnNode->_memPool = &_commentPool;
593 p += dtdHeaderLen;
595 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
596 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
597 returnNode = new (_elementPool.Alloc()) XMLElement( this );
598 returnNode->_memPool = &_elementPool;
599 p += elementHeaderLen;
601 else {
602 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
603 returnNode = new (_textPool.Alloc()) XMLText( this );
604 returnNode->_memPool = &_textPool;
605 p = start; // Back it up, all the text counts.
608 TIXMLASSERT( returnNode );
609 TIXMLASSERT( p );
610 *node = returnNode;
611 return p;
615 bool XMLDocument::Accept( XMLVisitor* visitor ) const
617 TIXMLASSERT( visitor );
618 if ( visitor->VisitEnter( *this ) ) {
619 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
620 if ( !node->Accept( visitor ) ) {
621 break;
625 return visitor->VisitExit( *this );
629 // --------- XMLNode ----------- //
631 XMLNode::XMLNode( XMLDocument* doc ) :
632 _document( doc ),
633 _parent( 0 ),
634 _firstChild( 0 ), _lastChild( 0 ),
635 _prev( 0 ), _next( 0 ),
636 _memPool( 0 )
641 XMLNode::~XMLNode()
643 DeleteChildren();
644 if ( _parent ) {
645 _parent->Unlink( this );
649 const char* XMLNode::Value() const
651 return _value.GetStr();
654 void XMLNode::SetValue( const char* str, bool staticMem )
656 if ( staticMem ) {
657 _value.SetInternedStr( str );
659 else {
660 _value.SetStr( str );
665 void XMLNode::DeleteChildren()
667 while( _firstChild ) {
668 TIXMLASSERT( _firstChild->_document == _document );
669 XMLNode* node = _firstChild;
670 Unlink( node );
672 DeleteNode( node );
674 _firstChild = _lastChild = 0;
678 void XMLNode::Unlink( XMLNode* child )
680 TIXMLASSERT( child );
681 TIXMLASSERT( child->_document == _document );
682 if ( child == _firstChild ) {
683 _firstChild = _firstChild->_next;
685 if ( child == _lastChild ) {
686 _lastChild = _lastChild->_prev;
689 if ( child->_prev ) {
690 child->_prev->_next = child->_next;
692 if ( child->_next ) {
693 child->_next->_prev = child->_prev;
695 child->_parent = 0;
699 void XMLNode::DeleteChild( XMLNode* node )
701 TIXMLASSERT( node );
702 TIXMLASSERT( node->_document == _document );
703 TIXMLASSERT( node->_parent == this );
704 DeleteNode( node );
708 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
710 TIXMLASSERT( addThis );
711 if ( addThis->_document != _document ) {
712 TIXMLASSERT( false );
713 return 0;
715 InsertChildPreamble( addThis );
717 if ( _lastChild ) {
718 TIXMLASSERT( _firstChild );
719 TIXMLASSERT( _lastChild->_next == 0 );
720 _lastChild->_next = addThis;
721 addThis->_prev = _lastChild;
722 _lastChild = addThis;
724 addThis->_next = 0;
726 else {
727 TIXMLASSERT( _firstChild == 0 );
728 _firstChild = _lastChild = addThis;
730 addThis->_prev = 0;
731 addThis->_next = 0;
733 addThis->_parent = this;
734 return addThis;
738 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
740 TIXMLASSERT( addThis );
741 if ( addThis->_document != _document ) {
742 TIXMLASSERT( false );
743 return 0;
745 InsertChildPreamble( addThis );
747 if ( _firstChild ) {
748 TIXMLASSERT( _lastChild );
749 TIXMLASSERT( _firstChild->_prev == 0 );
751 _firstChild->_prev = addThis;
752 addThis->_next = _firstChild;
753 _firstChild = addThis;
755 addThis->_prev = 0;
757 else {
758 TIXMLASSERT( _lastChild == 0 );
759 _firstChild = _lastChild = addThis;
761 addThis->_prev = 0;
762 addThis->_next = 0;
764 addThis->_parent = this;
765 return addThis;
769 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
771 TIXMLASSERT( addThis );
772 if ( addThis->_document != _document ) {
773 TIXMLASSERT( false );
774 return 0;
777 TIXMLASSERT( afterThis );
779 if ( afterThis->_parent != this ) {
780 TIXMLASSERT( false );
781 return 0;
784 if ( afterThis->_next == 0 ) {
785 // The last node or the only node.
786 return InsertEndChild( addThis );
788 InsertChildPreamble( addThis );
789 addThis->_prev = afterThis;
790 addThis->_next = afterThis->_next;
791 afterThis->_next->_prev = addThis;
792 afterThis->_next = addThis;
793 addThis->_parent = this;
794 return addThis;
800 const XMLElement* XMLNode::FirstChildElement( const char* value ) const
802 for( XMLNode* node=_firstChild; node; node=node->_next ) {
803 XMLElement* element = node->ToElement();
804 if ( element ) {
805 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
806 return element;
810 return 0;
814 const XMLElement* XMLNode::LastChildElement( const char* value ) const
816 for( XMLNode* node=_lastChild; node; node=node->_prev ) {
817 XMLElement* element = node->ToElement();
818 if ( element ) {
819 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
820 return element;
824 return 0;
828 const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
830 for( XMLNode* node=this->_next; node; node = node->_next ) {
831 const XMLElement* element = node->ToElement();
832 if ( element
833 && (!value || XMLUtil::StringEqual( value, node->Value() ))) {
834 return element;
837 return 0;
841 const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
843 for( XMLNode* node=_prev; node; node = node->_prev ) {
844 const XMLElement* element = node->ToElement();
845 if ( element
846 && (!value || XMLUtil::StringEqual( value, node->Value() ))) {
847 return element;
850 return 0;
854 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
856 // This is a recursive method, but thinking about it "at the current level"
857 // it is a pretty simple flat list:
858 // <foo/>
859 // <!-- comment -->
861 // With a special case:
862 // <foo>
863 // </foo>
864 // <!-- comment -->
866 // Where the closing element (/foo) *must* be the next thing after the opening
867 // element, and the names must match. BUT the tricky bit is that the closing
868 // element will be read by the child.
870 // 'endTag' is the end tag for this node, it is returned by a call to a child.
871 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
873 while( p && *p ) {
874 XMLNode* node = 0;
876 p = _document->Identify( p, &node );
877 if ( node == 0 ) {
878 break;
881 StrPair endTag;
882 p = node->ParseDeep( p, &endTag );
883 if ( !p ) {
884 DeleteNode( node );
885 if ( !_document->Error() ) {
886 _document->SetError( XML_ERROR_PARSING, 0, 0 );
888 break;
891 XMLElement* ele = node->ToElement();
892 if ( ele ) {
893 // We read the end tag. Return it to the parent.
894 if ( ele->ClosingType() == XMLElement::CLOSING ) {
895 if ( parentEnd ) {
896 ele->_value.TransferTo( parentEnd );
898 node->_memPool->SetTracked(); // created and then immediately deleted.
899 DeleteNode( node );
900 return p;
903 // Handle an end tag returned to this level.
904 // And handle a bunch of annoying errors.
905 bool mismatch = false;
906 if ( endTag.Empty() ) {
907 if ( ele->ClosingType() == XMLElement::OPEN ) {
908 mismatch = true;
911 else {
912 if ( ele->ClosingType() != XMLElement::OPEN ) {
913 mismatch = true;
915 else if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() ) ) {
916 mismatch = true;
919 if ( mismatch ) {
920 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
921 DeleteNode( node );
922 break;
925 InsertEndChild( node );
927 return 0;
930 void XMLNode::DeleteNode( XMLNode* node )
932 if ( node == 0 ) {
933 return;
935 MemPool* pool = node->_memPool;
936 node->~XMLNode();
937 pool->Free( node );
940 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
942 TIXMLASSERT( insertThis );
943 TIXMLASSERT( insertThis->_document == _document );
945 if ( insertThis->_parent )
946 insertThis->_parent->Unlink( insertThis );
947 else
948 insertThis->_memPool->SetTracked();
951 // --------- XMLText ---------- //
952 char* XMLText::ParseDeep( char* p, StrPair* )
954 const char* start = p;
955 if ( this->CData() ) {
956 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
957 if ( !p ) {
958 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
960 return p;
962 else {
963 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
964 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
965 flags |= StrPair::COLLAPSE_WHITESPACE;
968 p = _value.ParseText( p, "<", flags );
969 if ( p && *p ) {
970 return p-1;
972 if ( !p ) {
973 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
976 return 0;
980 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
982 if ( !doc ) {
983 doc = _document;
985 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
986 text->SetCData( this->CData() );
987 return text;
991 bool XMLText::ShallowEqual( const XMLNode* compare ) const
993 const XMLText* text = compare->ToText();
994 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
998 bool XMLText::Accept( XMLVisitor* visitor ) const
1000 TIXMLASSERT( visitor );
1001 return visitor->Visit( *this );
1005 // --------- XMLComment ---------- //
1007 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1012 XMLComment::~XMLComment()
1017 char* XMLComment::ParseDeep( char* p, StrPair* )
1019 // Comment parses as text.
1020 const char* start = p;
1021 p = _value.ParseText( p, "-->", StrPair::COMMENT );
1022 if ( p == 0 ) {
1023 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
1025 return p;
1029 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1031 if ( !doc ) {
1032 doc = _document;
1034 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
1035 return comment;
1039 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1041 TIXMLASSERT( compare );
1042 const XMLComment* comment = compare->ToComment();
1043 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1047 bool XMLComment::Accept( XMLVisitor* visitor ) const
1049 TIXMLASSERT( visitor );
1050 return visitor->Visit( *this );
1054 // --------- XMLDeclaration ---------- //
1056 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1061 XMLDeclaration::~XMLDeclaration()
1063 //printf( "~XMLDeclaration\n" );
1067 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
1069 // Declaration parses as text.
1070 const char* start = p;
1071 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1072 if ( p == 0 ) {
1073 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
1075 return p;
1079 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1081 if ( !doc ) {
1082 doc = _document;
1084 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1085 return dec;
1089 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1091 TIXMLASSERT( compare );
1092 const XMLDeclaration* declaration = compare->ToDeclaration();
1093 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1098 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1100 TIXMLASSERT( visitor );
1101 return visitor->Visit( *this );
1104 // --------- XMLUnknown ---------- //
1106 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1111 XMLUnknown::~XMLUnknown()
1116 char* XMLUnknown::ParseDeep( char* p, StrPair* )
1118 // Unknown parses as text.
1119 const char* start = p;
1121 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1122 if ( !p ) {
1123 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1125 return p;
1129 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1131 if ( !doc ) {
1132 doc = _document;
1134 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1135 return text;
1139 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1141 TIXMLASSERT( compare );
1142 const XMLUnknown* unknown = compare->ToUnknown();
1143 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1147 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1149 TIXMLASSERT( visitor );
1150 return visitor->Visit( *this );
1153 // --------- XMLAttribute ---------- //
1155 const char* XMLAttribute::Name() const
1157 return _name.GetStr();
1160 const char* XMLAttribute::Value() const
1162 return _value.GetStr();
1165 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1167 // Parse using the name rules: bug fix, was using ParseText before
1168 p = _name.ParseName( p );
1169 if ( !p || !*p ) {
1170 return 0;
1173 // Skip white space before =
1174 p = XMLUtil::SkipWhiteSpace( p );
1175 if ( *p != '=' ) {
1176 return 0;
1179 ++p; // move up to opening quote
1180 p = XMLUtil::SkipWhiteSpace( p );
1181 if ( *p != '\"' && *p != '\'' ) {
1182 return 0;
1185 char endTag[2] = { *p, 0 };
1186 ++p; // move past opening quote
1188 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1189 return p;
1193 void XMLAttribute::SetName( const char* n )
1195 _name.SetStr( n );
1199 XMLError XMLAttribute::QueryIntValue( int* value ) const
1201 if ( XMLUtil::ToInt( Value(), value )) {
1202 return XML_NO_ERROR;
1204 return XML_WRONG_ATTRIBUTE_TYPE;
1208 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1210 if ( XMLUtil::ToUnsigned( Value(), value )) {
1211 return XML_NO_ERROR;
1213 return XML_WRONG_ATTRIBUTE_TYPE;
1217 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1219 if ( XMLUtil::ToBool( Value(), value )) {
1220 return XML_NO_ERROR;
1222 return XML_WRONG_ATTRIBUTE_TYPE;
1226 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1228 if ( XMLUtil::ToFloat( Value(), value )) {
1229 return XML_NO_ERROR;
1231 return XML_WRONG_ATTRIBUTE_TYPE;
1235 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1237 if ( XMLUtil::ToDouble( Value(), value )) {
1238 return XML_NO_ERROR;
1240 return XML_WRONG_ATTRIBUTE_TYPE;
1244 void XMLAttribute::SetAttribute( const char* v )
1246 _value.SetStr( v );
1250 void XMLAttribute::SetAttribute( int v )
1252 char buf[BUF_SIZE];
1253 XMLUtil::ToStr( v, buf, BUF_SIZE );
1254 _value.SetStr( buf );
1258 void XMLAttribute::SetAttribute( unsigned v )
1260 char buf[BUF_SIZE];
1261 XMLUtil::ToStr( v, buf, BUF_SIZE );
1262 _value.SetStr( buf );
1266 void XMLAttribute::SetAttribute( bool v )
1268 char buf[BUF_SIZE];
1269 XMLUtil::ToStr( v, buf, BUF_SIZE );
1270 _value.SetStr( buf );
1273 void XMLAttribute::SetAttribute( double v )
1275 char buf[BUF_SIZE];
1276 XMLUtil::ToStr( v, buf, BUF_SIZE );
1277 _value.SetStr( buf );
1280 void XMLAttribute::SetAttribute( float v )
1282 char buf[BUF_SIZE];
1283 XMLUtil::ToStr( v, buf, BUF_SIZE );
1284 _value.SetStr( buf );
1288 // --------- XMLElement ---------- //
1289 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1290 _closingType( 0 ),
1291 _rootAttribute( 0 )
1296 XMLElement::~XMLElement()
1298 while( _rootAttribute ) {
1299 XMLAttribute* next = _rootAttribute->_next;
1300 DeleteAttribute( _rootAttribute );
1301 _rootAttribute = next;
1306 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1308 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1309 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1310 return a;
1313 return 0;
1317 const char* XMLElement::Attribute( const char* name, const char* value ) const
1319 const XMLAttribute* a = FindAttribute( name );
1320 if ( !a ) {
1321 return 0;
1323 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1324 return a->Value();
1326 return 0;
1330 const char* XMLElement::GetText() const
1332 if ( FirstChild() && FirstChild()->ToText() ) {
1333 return FirstChild()->Value();
1335 return 0;
1339 void XMLElement::SetText( const char* inText )
1341 if ( FirstChild() && FirstChild()->ToText() )
1342 FirstChild()->SetValue( inText );
1343 else {
1344 XMLText* theText = GetDocument()->NewText( inText );
1345 InsertFirstChild( theText );
1350 void XMLElement::SetText( int v )
1352 char buf[BUF_SIZE];
1353 XMLUtil::ToStr( v, buf, BUF_SIZE );
1354 SetText( buf );
1358 void XMLElement::SetText( unsigned v )
1360 char buf[BUF_SIZE];
1361 XMLUtil::ToStr( v, buf, BUF_SIZE );
1362 SetText( buf );
1366 void XMLElement::SetText( bool v )
1368 char buf[BUF_SIZE];
1369 XMLUtil::ToStr( v, buf, BUF_SIZE );
1370 SetText( buf );
1374 void XMLElement::SetText( float v )
1376 char buf[BUF_SIZE];
1377 XMLUtil::ToStr( v, buf, BUF_SIZE );
1378 SetText( buf );
1382 void XMLElement::SetText( double v )
1384 char buf[BUF_SIZE];
1385 XMLUtil::ToStr( v, buf, BUF_SIZE );
1386 SetText( buf );
1390 XMLError XMLElement::QueryIntText( int* ival ) const
1392 if ( FirstChild() && FirstChild()->ToText() ) {
1393 const char* t = FirstChild()->Value();
1394 if ( XMLUtil::ToInt( t, ival ) ) {
1395 return XML_SUCCESS;
1397 return XML_CAN_NOT_CONVERT_TEXT;
1399 return XML_NO_TEXT_NODE;
1403 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1405 if ( FirstChild() && FirstChild()->ToText() ) {
1406 const char* t = FirstChild()->Value();
1407 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1408 return XML_SUCCESS;
1410 return XML_CAN_NOT_CONVERT_TEXT;
1412 return XML_NO_TEXT_NODE;
1416 XMLError XMLElement::QueryBoolText( bool* bval ) const
1418 if ( FirstChild() && FirstChild()->ToText() ) {
1419 const char* t = FirstChild()->Value();
1420 if ( XMLUtil::ToBool( t, bval ) ) {
1421 return XML_SUCCESS;
1423 return XML_CAN_NOT_CONVERT_TEXT;
1425 return XML_NO_TEXT_NODE;
1429 XMLError XMLElement::QueryDoubleText( double* dval ) const
1431 if ( FirstChild() && FirstChild()->ToText() ) {
1432 const char* t = FirstChild()->Value();
1433 if ( XMLUtil::ToDouble( t, dval ) ) {
1434 return XML_SUCCESS;
1436 return XML_CAN_NOT_CONVERT_TEXT;
1438 return XML_NO_TEXT_NODE;
1442 XMLError XMLElement::QueryFloatText( float* fval ) const
1444 if ( FirstChild() && FirstChild()->ToText() ) {
1445 const char* t = FirstChild()->Value();
1446 if ( XMLUtil::ToFloat( t, fval ) ) {
1447 return XML_SUCCESS;
1449 return XML_CAN_NOT_CONVERT_TEXT;
1451 return XML_NO_TEXT_NODE;
1456 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1458 XMLAttribute* last = 0;
1459 XMLAttribute* attrib = 0;
1460 for( attrib = _rootAttribute;
1461 attrib;
1462 last = attrib, attrib = attrib->_next ) {
1463 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1464 break;
1467 if ( !attrib ) {
1468 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1469 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1470 attrib->_memPool = &_document->_attributePool;
1471 if ( last ) {
1472 last->_next = attrib;
1474 else {
1475 _rootAttribute = attrib;
1477 attrib->SetName( name );
1478 attrib->_memPool->SetTracked(); // always created and linked.
1480 return attrib;
1484 void XMLElement::DeleteAttribute( const char* name )
1486 XMLAttribute* prev = 0;
1487 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1488 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1489 if ( prev ) {
1490 prev->_next = a->_next;
1492 else {
1493 _rootAttribute = a->_next;
1495 DeleteAttribute( a );
1496 break;
1498 prev = a;
1503 char* XMLElement::ParseAttributes( char* p )
1505 const char* start = p;
1506 XMLAttribute* prevAttribute = 0;
1508 // Read the attributes.
1509 while( p ) {
1510 p = XMLUtil::SkipWhiteSpace( p );
1511 if ( !(*p) ) {
1512 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1513 return 0;
1516 // attribute.
1517 if (XMLUtil::IsNameStartChar( *p ) ) {
1518 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1519 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1520 attrib->_memPool = &_document->_attributePool;
1521 attrib->_memPool->SetTracked();
1523 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1524 if ( !p || Attribute( attrib->Name() ) ) {
1525 DeleteAttribute( attrib );
1526 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1527 return 0;
1529 // There is a minor bug here: if the attribute in the source xml
1530 // document is duplicated, it will not be detected and the
1531 // attribute will be doubly added. However, tracking the 'prevAttribute'
1532 // avoids re-scanning the attribute list. Preferring performance for
1533 // now, may reconsider in the future.
1534 if ( prevAttribute ) {
1535 prevAttribute->_next = attrib;
1537 else {
1538 _rootAttribute = attrib;
1540 prevAttribute = attrib;
1542 // end of the tag
1543 else if ( *p == '/' && *(p+1) == '>' ) {
1544 _closingType = CLOSED;
1545 return p+2; // done; sealed element.
1547 // end of the tag
1548 else if ( *p == '>' ) {
1549 ++p;
1550 break;
1552 else {
1553 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1554 return 0;
1557 return p;
1560 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1562 if ( attribute == 0 ) {
1563 return;
1565 MemPool* pool = attribute->_memPool;
1566 attribute->~XMLAttribute();
1567 pool->Free( attribute );
1571 // <ele></ele>
1572 // <ele>foo<b>bar</b></ele>
1574 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1576 // Read the element name.
1577 p = XMLUtil::SkipWhiteSpace( p );
1579 // The closing element is the </element> form. It is
1580 // parsed just like a regular element then deleted from
1581 // the DOM.
1582 if ( *p == '/' ) {
1583 _closingType = CLOSING;
1584 ++p;
1587 p = _value.ParseName( p );
1588 if ( _value.Empty() ) {
1589 return 0;
1592 p = ParseAttributes( p );
1593 if ( !p || !*p || _closingType ) {
1594 return p;
1597 p = XMLNode::ParseDeep( p, strPair );
1598 return p;
1603 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1605 if ( !doc ) {
1606 doc = _document;
1608 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1609 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1610 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1612 return element;
1616 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1618 TIXMLASSERT( compare );
1619 const XMLElement* other = compare->ToElement();
1620 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1622 const XMLAttribute* a=FirstAttribute();
1623 const XMLAttribute* b=other->FirstAttribute();
1625 while ( a && b ) {
1626 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1627 return false;
1629 a = a->Next();
1630 b = b->Next();
1632 if ( a || b ) {
1633 // different count
1634 return false;
1636 return true;
1638 return false;
1642 bool XMLElement::Accept( XMLVisitor* visitor ) const
1644 TIXMLASSERT( visitor );
1645 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1646 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1647 if ( !node->Accept( visitor ) ) {
1648 break;
1652 return visitor->VisitExit( *this );
1656 // --------- XMLDocument ----------- //
1658 // Warning: List must match 'enum XMLError'
1659 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1660 "XML_SUCCESS",
1661 "XML_NO_ATTRIBUTE",
1662 "XML_WRONG_ATTRIBUTE_TYPE",
1663 "XML_ERROR_FILE_NOT_FOUND",
1664 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1665 "XML_ERROR_FILE_READ_ERROR",
1666 "XML_ERROR_ELEMENT_MISMATCH",
1667 "XML_ERROR_PARSING_ELEMENT",
1668 "XML_ERROR_PARSING_ATTRIBUTE",
1669 "XML_ERROR_IDENTIFYING_TAG",
1670 "XML_ERROR_PARSING_TEXT",
1671 "XML_ERROR_PARSING_CDATA",
1672 "XML_ERROR_PARSING_COMMENT",
1673 "XML_ERROR_PARSING_DECLARATION",
1674 "XML_ERROR_PARSING_UNKNOWN",
1675 "XML_ERROR_EMPTY_DOCUMENT",
1676 "XML_ERROR_MISMATCHED_ELEMENT",
1677 "XML_ERROR_PARSING",
1678 "XML_CAN_NOT_CONVERT_TEXT",
1679 "XML_NO_TEXT_NODE"
1683 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1684 XMLNode( 0 ),
1685 _writeBOM( false ),
1686 _processEntities( processEntities ),
1687 _errorID( XML_NO_ERROR ),
1688 _whitespace( whitespace ),
1689 _errorStr1( 0 ),
1690 _errorStr2( 0 ),
1691 _charBuffer( 0 )
1693 _document = this; // avoid warning about 'this' in initializer list
1697 XMLDocument::~XMLDocument()
1699 Clear();
1703 void XMLDocument::Clear()
1705 DeleteChildren();
1707 #ifdef DEBUG
1708 const bool hadError = Error();
1709 #endif
1710 _errorID = XML_NO_ERROR;
1711 _errorStr1 = 0;
1712 _errorStr2 = 0;
1714 delete [] _charBuffer;
1715 _charBuffer = 0;
1717 #if 0
1718 _textPool.Trace( "text" );
1719 _elementPool.Trace( "element" );
1720 _commentPool.Trace( "comment" );
1721 _attributePool.Trace( "attribute" );
1722 #endif
1724 #ifdef DEBUG
1725 if ( !hadError ) {
1726 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
1727 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1728 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
1729 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
1731 #endif
1735 XMLElement* XMLDocument::NewElement( const char* name )
1737 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
1738 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1739 ele->_memPool = &_elementPool;
1740 ele->SetName( name );
1741 return ele;
1745 XMLComment* XMLDocument::NewComment( const char* str )
1747 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
1748 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1749 comment->_memPool = &_commentPool;
1750 comment->SetValue( str );
1751 return comment;
1755 XMLText* XMLDocument::NewText( const char* str )
1757 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
1758 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1759 text->_memPool = &_textPool;
1760 text->SetValue( str );
1761 return text;
1765 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1767 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
1768 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1769 dec->_memPool = &_commentPool;
1770 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1771 return dec;
1775 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1777 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
1778 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1779 unk->_memPool = &_commentPool;
1780 unk->SetValue( str );
1781 return unk;
1784 static FILE* callfopen( const char* filepath, const char* mode )
1786 TIXMLASSERT( filepath );
1787 TIXMLASSERT( mode );
1788 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
1789 FILE* fp = 0;
1790 errno_t err = fopen_s( &fp, filepath, mode );
1791 if ( err ) {
1792 return 0;
1794 #else
1795 FILE* fp = fopen( filepath, mode );
1796 #endif
1797 return fp;
1800 void XMLDocument::DeleteNode( XMLNode* node ) {
1801 TIXMLASSERT( node );
1802 TIXMLASSERT(node->_document == this );
1803 if (node->_parent) {
1804 node->_parent->DeleteChild( node );
1806 else {
1807 // Isn't in the tree.
1808 // Use the parent delete.
1809 // Also, we need to mark it tracked: we 'know'
1810 // it was never used.
1811 node->_memPool->SetTracked();
1812 // Call the static XMLNode version:
1813 XMLNode::DeleteNode(node);
1818 XMLError XMLDocument::LoadFile( const char* filename )
1820 Clear();
1821 FILE* fp = callfopen( filename, "rb" );
1822 if ( !fp ) {
1823 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1824 return _errorID;
1826 LoadFile( fp );
1827 fclose( fp );
1828 return _errorID;
1832 XMLError XMLDocument::LoadFile( FILE* fp )
1834 Clear();
1836 fseek( fp, 0, SEEK_SET );
1837 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
1838 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1839 return _errorID;
1842 fseek( fp, 0, SEEK_END );
1843 const long filelength = ftell( fp );
1844 fseek( fp, 0, SEEK_SET );
1845 if ( filelength == -1L ) {
1846 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1847 return _errorID;
1850 const size_t size = filelength;
1851 if ( size == 0 ) {
1852 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1853 return _errorID;
1856 _charBuffer = new char[size+1];
1857 size_t read = fread( _charBuffer, 1, size, fp );
1858 if ( read != size ) {
1859 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1860 return _errorID;
1863 _charBuffer[size] = 0;
1865 Parse();
1866 return _errorID;
1870 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1872 FILE* fp = callfopen( filename, "w" );
1873 if ( !fp ) {
1874 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1875 return _errorID;
1877 SaveFile(fp, compact);
1878 fclose( fp );
1879 return _errorID;
1883 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
1885 XMLPrinter stream( fp, compact );
1886 Print( &stream );
1887 return _errorID;
1891 XMLError XMLDocument::Parse( const char* p, size_t len )
1893 Clear();
1895 if ( len == 0 || !p || !*p ) {
1896 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1897 return _errorID;
1899 if ( len == (size_t)(-1) ) {
1900 len = strlen( p );
1902 _charBuffer = new char[ len+1 ];
1903 memcpy( _charBuffer, p, len );
1904 _charBuffer[len] = 0;
1906 Parse();
1907 if ( Error() ) {
1908 // clean up now essentially dangling memory.
1909 // and the parse fail can put objects in the
1910 // pools that are dead and inaccessible.
1911 DeleteChildren();
1912 _elementPool.Clear();
1913 _attributePool.Clear();
1914 _textPool.Clear();
1915 _commentPool.Clear();
1917 return _errorID;
1921 void XMLDocument::Print( XMLPrinter* streamer ) const
1923 XMLPrinter stdStreamer( stdout );
1924 if ( !streamer ) {
1925 streamer = &stdStreamer;
1927 Accept( streamer );
1931 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
1933 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
1934 _errorID = error;
1935 _errorStr1 = str1;
1936 _errorStr2 = str2;
1939 const char* XMLDocument::ErrorName() const
1941 TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
1942 return _errorNames[_errorID];
1945 void XMLDocument::PrintError() const
1947 if ( Error() ) {
1948 static const int LEN = 20;
1949 char buf1[LEN] = { 0 };
1950 char buf2[LEN] = { 0 };
1952 if ( _errorStr1 ) {
1953 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
1955 if ( _errorStr2 ) {
1956 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
1959 printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
1960 _errorID, ErrorName(), buf1, buf2 );
1964 void XMLDocument::Parse()
1966 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
1967 TIXMLASSERT( _charBuffer );
1968 char* p = _charBuffer;
1969 p = XMLUtil::SkipWhiteSpace( p );
1970 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
1971 if ( !*p ) {
1972 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1973 return;
1975 ParseDeep(p, 0 );
1978 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
1979 _elementJustOpened( false ),
1980 _firstElement( true ),
1981 _fp( file ),
1982 _depth( depth ),
1983 _textDepth( -1 ),
1984 _processEntities( true ),
1985 _compactMode( compact )
1987 for( int i=0; i<ENTITY_RANGE; ++i ) {
1988 _entityFlag[i] = false;
1989 _restrictedEntityFlag[i] = false;
1991 for( int i=0; i<NUM_ENTITIES; ++i ) {
1992 const char entityValue = entities[i].value;
1993 TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
1994 _entityFlag[ (unsigned char)entityValue ] = true;
1996 _restrictedEntityFlag[(unsigned char)'&'] = true;
1997 _restrictedEntityFlag[(unsigned char)'<'] = true;
1998 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
1999 _buffer.Push( 0 );
2003 void XMLPrinter::Print( const char* format, ... )
2005 va_list va;
2006 va_start( va, format );
2008 if ( _fp ) {
2009 vfprintf( _fp, format, va );
2011 else {
2012 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
2013 #if defined(WINCE)
2014 int len = 512;
2015 do {
2016 len = len*2;
2017 char* str = new char[len]();
2018 len = _vsnprintf(str, len, format, va);
2019 delete[] str;
2020 }while (len < 0);
2021 #else
2022 int len = _vscprintf( format, va );
2023 #endif
2024 #else
2025 int len = vsnprintf( 0, 0, format, va );
2026 #endif
2027 // Close out and re-start the va-args
2028 va_end( va );
2029 va_start( va, format );
2030 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2031 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2032 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
2033 #if defined(WINCE)
2034 _vsnprintf( p, len+1, format, va );
2035 #else
2036 vsnprintf_s( p, len+1, _TRUNCATE, format, va );
2037 #endif
2038 #else
2039 vsnprintf( p, len+1, format, va );
2040 #endif
2042 va_end( va );
2046 void XMLPrinter::PrintSpace( int depth )
2048 for( int i=0; i<depth; ++i ) {
2049 Print( " " );
2054 void XMLPrinter::PrintString( const char* p, bool restricted )
2056 // Look for runs of bytes between entities to print.
2057 const char* q = p;
2059 if ( _processEntities ) {
2060 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2061 while ( *q ) {
2062 // Remember, char is sometimes signed. (How many times has that bitten me?)
2063 if ( *q > 0 && *q < ENTITY_RANGE ) {
2064 // Check for entities. If one is found, flush
2065 // the stream up until the entity, write the
2066 // entity, and keep looking.
2067 if ( flag[(unsigned char)(*q)] ) {
2068 while ( p < q ) {
2069 Print( "%c", *p );
2070 ++p;
2072 for( int i=0; i<NUM_ENTITIES; ++i ) {
2073 if ( entities[i].value == *q ) {
2074 Print( "&%s;", entities[i].pattern );
2075 break;
2078 ++p;
2081 ++q;
2084 // Flush the remaining string. This will be the entire
2085 // string if an entity wasn't found.
2086 if ( !_processEntities || (q-p > 0) ) {
2087 Print( "%s", p );
2092 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2094 if ( writeBOM ) {
2095 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2096 Print( "%s", bom );
2098 if ( writeDec ) {
2099 PushDeclaration( "xml version=\"1.0\"" );
2104 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2106 SealElementIfJustOpened();
2107 _stack.Push( name );
2109 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2110 Print( "\n" );
2112 if ( !compactMode ) {
2113 PrintSpace( _depth );
2116 Print( "<%s", name );
2117 _elementJustOpened = true;
2118 _firstElement = false;
2119 ++_depth;
2123 void XMLPrinter::PushAttribute( const char* name, const char* value )
2125 TIXMLASSERT( _elementJustOpened );
2126 Print( " %s=\"", name );
2127 PrintString( value, false );
2128 Print( "\"" );
2132 void XMLPrinter::PushAttribute( const char* name, int v )
2134 char buf[BUF_SIZE];
2135 XMLUtil::ToStr( v, buf, BUF_SIZE );
2136 PushAttribute( name, buf );
2140 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2142 char buf[BUF_SIZE];
2143 XMLUtil::ToStr( v, buf, BUF_SIZE );
2144 PushAttribute( name, buf );
2148 void XMLPrinter::PushAttribute( const char* name, bool v )
2150 char buf[BUF_SIZE];
2151 XMLUtil::ToStr( v, buf, BUF_SIZE );
2152 PushAttribute( name, buf );
2156 void XMLPrinter::PushAttribute( const char* name, double v )
2158 char buf[BUF_SIZE];
2159 XMLUtil::ToStr( v, buf, BUF_SIZE );
2160 PushAttribute( name, buf );
2164 void XMLPrinter::CloseElement( bool compactMode )
2166 --_depth;
2167 const char* name = _stack.Pop();
2169 if ( _elementJustOpened ) {
2170 Print( "/>" );
2172 else {
2173 if ( _textDepth < 0 && !compactMode) {
2174 Print( "\n" );
2175 PrintSpace( _depth );
2177 Print( "</%s>", name );
2180 if ( _textDepth == _depth ) {
2181 _textDepth = -1;
2183 if ( _depth == 0 && !compactMode) {
2184 Print( "\n" );
2186 _elementJustOpened = false;
2190 void XMLPrinter::SealElementIfJustOpened()
2192 if ( !_elementJustOpened ) {
2193 return;
2195 _elementJustOpened = false;
2196 Print( ">" );
2200 void XMLPrinter::PushText( const char* text, bool cdata )
2202 _textDepth = _depth-1;
2204 SealElementIfJustOpened();
2205 if ( cdata ) {
2206 Print( "<![CDATA[" );
2207 Print( "%s", text );
2208 Print( "]]>" );
2210 else {
2211 PrintString( text, true );
2215 void XMLPrinter::PushText( int value )
2217 char buf[BUF_SIZE];
2218 XMLUtil::ToStr( value, buf, BUF_SIZE );
2219 PushText( buf, false );
2223 void XMLPrinter::PushText( unsigned value )
2225 char buf[BUF_SIZE];
2226 XMLUtil::ToStr( value, buf, BUF_SIZE );
2227 PushText( buf, false );
2231 void XMLPrinter::PushText( bool value )
2233 char buf[BUF_SIZE];
2234 XMLUtil::ToStr( value, buf, BUF_SIZE );
2235 PushText( buf, false );
2239 void XMLPrinter::PushText( float value )
2241 char buf[BUF_SIZE];
2242 XMLUtil::ToStr( value, buf, BUF_SIZE );
2243 PushText( buf, false );
2247 void XMLPrinter::PushText( double value )
2249 char buf[BUF_SIZE];
2250 XMLUtil::ToStr( value, buf, BUF_SIZE );
2251 PushText( buf, false );
2255 void XMLPrinter::PushComment( const char* comment )
2257 SealElementIfJustOpened();
2258 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2259 Print( "\n" );
2260 PrintSpace( _depth );
2262 _firstElement = false;
2263 Print( "<!--%s-->", comment );
2267 void XMLPrinter::PushDeclaration( const char* value )
2269 SealElementIfJustOpened();
2270 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2271 Print( "\n" );
2272 PrintSpace( _depth );
2274 _firstElement = false;
2275 Print( "<?%s?>", value );
2279 void XMLPrinter::PushUnknown( const char* value )
2281 SealElementIfJustOpened();
2282 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2283 Print( "\n" );
2284 PrintSpace( _depth );
2286 _firstElement = false;
2287 Print( "<!%s>", value );
2291 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2293 _processEntities = doc.ProcessEntities();
2294 if ( doc.HasBOM() ) {
2295 PushHeader( true, false );
2297 return true;
2301 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2303 const XMLElement* parentElem = element.Parent()->ToElement();
2304 bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode;
2305 OpenElement( element.Name(), compactMode );
2306 while ( attribute ) {
2307 PushAttribute( attribute->Name(), attribute->Value() );
2308 attribute = attribute->Next();
2310 return true;
2314 bool XMLPrinter::VisitExit( const XMLElement& element )
2316 CloseElement( CompactMode(element) );
2317 return true;
2321 bool XMLPrinter::Visit( const XMLText& text )
2323 PushText( text.Value(), text.CData() );
2324 return true;
2328 bool XMLPrinter::Visit( const XMLComment& comment )
2330 PushComment( comment.Value() );
2331 return true;
2334 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2336 PushDeclaration( declaration.Value() );
2337 return true;
2341 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2343 PushUnknown( unknown.Value() );
2344 return true;
2347 } // namespace tinyxml2