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
26 #include <new> // yes, this one new style header, is in the Android SDK.
27 #if defined(ANDROID_NDK) || defined(__QNXNTO__)
33 #if defined(__GNUC__) && __GNUC__>=7
34 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
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
;
61 static const int NUM_ENTITIES
= 5;
62 static const Entity entities
[NUM_ENTITIES
] = {
63 { "quot", 4, DOUBLE_QUOTE
},
65 { "apos", 4, SINGLE_QUOTE
},
77 void StrPair::TransferTo( StrPair
* other
)
79 if ( this == other
) {
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 );
91 other
->_flags
= _flags
;
92 other
->_start
= _start
;
100 void StrPair::Reset()
102 if ( _flags
& NEEDS_DELETE
) {
111 void StrPair::SetStr( const char* str
, int flags
)
114 size_t len
= strlen( str
);
115 _start
= new char[ len
+1 ];
116 memcpy( _start
, str
, len
+1 );
118 _flags
= flags
| NEEDS_DELETE
;
122 char* StrPair::ParseText( char* p
, const char* endTag
, int strFlags
)
124 TIXMLASSERT( endTag
&& *endTag
);
127 char endChar
= *endTag
;
128 size_t length
= strlen( endTag
);
130 // Inner loop of text parsing.
132 if ( *p
== endChar
&& strncmp( p
, endTag
, length
) == 0 ) {
133 Set( start
, p
, strFlags
);
142 char* StrPair::ParseName( char* p
)
147 if ( !XMLUtil::IsNameStartChar( *p
) ) {
151 char* const start
= p
;
153 while ( *p
&& XMLUtil::IsNameChar( *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
);
170 char* p
= _start
; // the read pointer
171 char* q
= _start
; // the write pointer
174 if ( XMLUtil::IsWhiteSpace( *p
)) {
175 p
= XMLUtil::SkipWhiteSpace( p
);
177 break; // don't write to q; this trims the trailing space.
191 const char* StrPair::GetStr()
193 TIXMLASSERT( _start
);
195 if ( _flags
& NEEDS_FLUSH
) {
197 _flags
^= NEEDS_FLUSH
;
200 char* p
= _start
; // the read pointer
201 char* q
= _start
; // the write pointer
204 if ( (_flags
& NEEDS_NEWLINE_NORMALIZATION
) && *p
== CR
) {
205 // CR-LF pair becomes LF
206 // CR alone becomes LF
208 if ( *(p
+1) == LF
) {
216 else if ( (_flags
& NEEDS_NEWLINE_NORMALIZATION
) && *p
== LF
) {
217 if ( *(p
+1) == CR
) {
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 // 中 or 中
231 if ( *(p
+1) == '#' ) {
232 const int buflen
= 10;
233 char buf
[buflen
] = { 0 };
235 char* adjusted
= const_cast<char*>( XMLUtil::GetCharacterRef( p
, buf
, &len
) );
236 if ( adjusted
== 0 ) {
242 TIXMLASSERT( 0 <= len
&& len
<= buflen
);
243 TIXMLASSERT( q
+ len
<= adjusted
);
245 memcpy( q
, buf
, len
);
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.
258 p
+= entity
.length
+ 2;
262 if ( i
== NUM_ENTITIES
) {
263 // fixme: treat as error?
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
);
291 // --------- XMLUtil ----------- //
293 const char* XMLUtil::ReadBOM( const char* p
, bool* bom
)
298 const unsigned char* pu
= reinterpret_cast<const unsigned char*>(p
);
300 if ( *(pu
+0) == TIXML_UTF_LEAD_0
301 && *(pu
+1) == TIXML_UTF_LEAD_1
302 && *(pu
+2) == TIXML_UTF_LEAD_2
) {
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 };
320 else if ( input
< 0x800 ) {
323 else if ( input
< 0x10000 ) {
326 else if ( input
< 0x200000 ) {
330 *length
= 0; // This code won't covert this correctly anyway.
336 // Scary scary fall throughs.
340 *output
= (char)((input
| BYTE_MARK
) & BYTE_MASK
);
344 *output
= (char)((input
| BYTE_MARK
) & BYTE_MASK
);
348 *output
= (char)((input
| BYTE_MARK
) & BYTE_MASK
);
352 *output
= (char)(input
| FIRST_BYTE_MARK
[*length
]);
355 TIXMLASSERT( false );
360 const char* XMLUtil::GetCharacterRef( const char* p
, char* value
, int* length
)
362 // Presume an entity, and pull it out.
365 if ( *(p
+1) == '#' && *(p
+2) ) {
366 unsigned long ucs
= 0;
367 TIXMLASSERT( sizeof( ucs
) >= 4 );
370 static const char SEMICOLON
= ';';
372 if ( *(p
+2) == 'x' ) {
379 q
= strchr( q
, SEMICOLON
);
384 TIXMLASSERT( *q
== SEMICOLON
);
389 while ( *q
!= 'x' ) {
390 unsigned int digit
= 0;
392 if ( *q
>= '0' && *q
<= '9' ) {
395 else if ( *q
>= 'a' && *q
<= 'f' ) {
396 digit
= *q
- 'a' + 10;
398 else if ( *q
>= 'A' && *q
<= 'F' ) {
399 digit
= *q
- 'A' + 10;
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
);
409 TIXMLASSERT( mult
<= UINT_MAX
/ 16 );
421 q
= strchr( q
, SEMICOLON
);
426 TIXMLASSERT( *q
== SEMICOLON
);
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
);
442 TIXMLASSERT( mult
<= UINT_MAX
/ 10 );
447 // convert the UCS to UTF-8
448 ConvertUTF32ToUTF8( ucs
, value
, length
);
449 return p
+ delta
+ 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 ) {
496 bool XMLUtil::ToUnsigned( const char* str
, unsigned *value
)
498 if ( TIXML_SSCANF( str
, "%u", value
) == 1 ) {
504 bool XMLUtil::ToBool( const char* str
, bool* value
)
507 if ( ToInt( str
, &ival
)) {
508 *value
= (ival
==0) ? false : true;
511 if ( StringEqual( str
, "true" ) ) {
515 else if ( StringEqual( str
, "false" ) ) {
523 bool XMLUtil::ToFloat( const char* str
, float* value
)
525 if ( TIXML_SSCANF( str
, "%f", value
) == 1 ) {
531 bool XMLUtil::ToDouble( const char* str
, double* value
)
533 if ( TIXML_SSCANF( str
, "%lf", value
) == 1 ) {
540 char* XMLDocument::Identify( char* p
, XMLNode
** node
)
544 char* const start
= p
;
545 p
= XMLUtil::SkipWhiteSpace( 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
;
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 );
585 returnNode
->_memPool
= &_textPool
;
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
;
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
;
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
);
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
) ) {
625 return visitor
->VisitExit( *this );
629 // --------- XMLNode ----------- //
631 XMLNode::XMLNode( XMLDocument
* doc
) :
634 _firstChild( 0 ), _lastChild( 0 ),
635 _prev( 0 ), _next( 0 ),
645 _parent
->Unlink( this );
649 const char* XMLNode::Value() const
651 return _value
.GetStr();
654 void XMLNode::SetValue( const char* str
, bool staticMem
)
657 _value
.SetInternedStr( str
);
660 _value
.SetStr( str
);
665 void XMLNode::DeleteChildren()
667 while( _firstChild
) {
668 TIXMLASSERT( _firstChild
->_document
== _document
);
669 XMLNode
* node
= _firstChild
;
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
;
699 void XMLNode::DeleteChild( XMLNode
* node
)
702 TIXMLASSERT( node
->_document
== _document
);
703 TIXMLASSERT( node
->_parent
== this );
708 XMLNode
* XMLNode::InsertEndChild( XMLNode
* addThis
)
710 TIXMLASSERT( addThis
);
711 if ( addThis
->_document
!= _document
) {
712 TIXMLASSERT( false );
715 InsertChildPreamble( addThis
);
718 TIXMLASSERT( _firstChild
);
719 TIXMLASSERT( _lastChild
->_next
== 0 );
720 _lastChild
->_next
= addThis
;
721 addThis
->_prev
= _lastChild
;
722 _lastChild
= addThis
;
727 TIXMLASSERT( _firstChild
== 0 );
728 _firstChild
= _lastChild
= addThis
;
733 addThis
->_parent
= this;
738 XMLNode
* XMLNode::InsertFirstChild( XMLNode
* addThis
)
740 TIXMLASSERT( addThis
);
741 if ( addThis
->_document
!= _document
) {
742 TIXMLASSERT( false );
745 InsertChildPreamble( addThis
);
748 TIXMLASSERT( _lastChild
);
749 TIXMLASSERT( _firstChild
->_prev
== 0 );
751 _firstChild
->_prev
= addThis
;
752 addThis
->_next
= _firstChild
;
753 _firstChild
= addThis
;
758 TIXMLASSERT( _lastChild
== 0 );
759 _firstChild
= _lastChild
= addThis
;
764 addThis
->_parent
= this;
769 XMLNode
* XMLNode::InsertAfterChild( XMLNode
* afterThis
, XMLNode
* addThis
)
771 TIXMLASSERT( addThis
);
772 if ( addThis
->_document
!= _document
) {
773 TIXMLASSERT( false );
777 TIXMLASSERT( afterThis
);
779 if ( afterThis
->_parent
!= this ) {
780 TIXMLASSERT( false );
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;
800 const XMLElement
* XMLNode::FirstChildElement( const char* value
) const
802 for( XMLNode
* node
=_firstChild
; node
; node
=node
->_next
) {
803 XMLElement
* element
= node
->ToElement();
805 if ( !value
|| XMLUtil::StringEqual( element
->Name(), value
) ) {
814 const XMLElement
* XMLNode::LastChildElement( const char* value
) const
816 for( XMLNode
* node
=_lastChild
; node
; node
=node
->_prev
) {
817 XMLElement
* element
= node
->ToElement();
819 if ( !value
|| XMLUtil::StringEqual( element
->Name(), value
) ) {
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();
833 && (!value
|| XMLUtil::StringEqual( value
, node
->Value() ))) {
841 const XMLElement
* XMLNode::PreviousSiblingElement( const char* value
) const
843 for( XMLNode
* node
=_prev
; node
; node
= node
->_prev
) {
844 const XMLElement
* element
= node
->ToElement();
846 && (!value
|| XMLUtil::StringEqual( value
, node
->Value() ))) {
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:
861 // With a special case:
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.
876 p
= _document
->Identify( p
, &node
);
882 p
= node
->ParseDeep( p
, &endTag
);
885 if ( !_document
->Error() ) {
886 _document
->SetError( XML_ERROR_PARSING
, 0, 0 );
891 XMLElement
* ele
= node
->ToElement();
893 // We read the end tag. Return it to the parent.
894 if ( ele
->ClosingType() == XMLElement::CLOSING
) {
896 ele
->_value
.TransferTo( parentEnd
);
898 node
->_memPool
->SetTracked(); // created and then immediately deleted.
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
) {
912 if ( ele
->ClosingType() != XMLElement::OPEN
) {
915 else if ( !XMLUtil::StringEqual( endTag
.GetStr(), node
->Value() ) ) {
920 _document
->SetError( XML_ERROR_MISMATCHED_ELEMENT
, node
->Value(), 0 );
925 InsertEndChild( node
);
930 void XMLNode::DeleteNode( XMLNode
* node
)
935 MemPool
* pool
= node
->_memPool
;
940 void XMLNode::InsertChildPreamble( XMLNode
* insertThis
) const
942 TIXMLASSERT( insertThis
);
943 TIXMLASSERT( insertThis
->_document
== _document
);
945 if ( insertThis
->_parent
)
946 insertThis
->_parent
->Unlink( insertThis
);
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
);
958 _document
->SetError( XML_ERROR_PARSING_CDATA
, start
, 0 );
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
);
973 _document
->SetError( XML_ERROR_PARSING_TEXT
, start
, 0 );
980 XMLNode
* XMLText::ShallowClone( XMLDocument
* doc
) const
985 XMLText
* text
= doc
->NewText( Value() ); // fixme: this will always allocate memory. Intern?
986 text
->SetCData( this->CData() );
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
);
1023 _document
->SetError( XML_ERROR_PARSING_COMMENT
, start
, 0 );
1029 XMLNode
* XMLComment::ShallowClone( XMLDocument
* doc
) const
1034 XMLComment
* comment
= doc
->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
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
);
1073 _document
->SetError( XML_ERROR_PARSING_DECLARATION
, start
, 0 );
1079 XMLNode
* XMLDeclaration::ShallowClone( XMLDocument
* doc
) const
1084 XMLDeclaration
* dec
= doc
->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
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
);
1123 _document
->SetError( XML_ERROR_PARSING_UNKNOWN
, start
, 0 );
1129 XMLNode
* XMLUnknown::ShallowClone( XMLDocument
* doc
) const
1134 XMLUnknown
* text
= doc
->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
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
);
1173 // Skip white space before =
1174 p
= XMLUtil::SkipWhiteSpace( p
);
1179 ++p
; // move up to opening quote
1180 p
= XMLUtil::SkipWhiteSpace( p
);
1181 if ( *p
!= '\"' && *p
!= '\'' ) {
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
);
1193 void XMLAttribute::SetName( const char* 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
)
1250 void XMLAttribute::SetAttribute( int v
)
1253 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1254 _value
.SetStr( buf
);
1258 void XMLAttribute::SetAttribute( unsigned v
)
1261 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1262 _value
.SetStr( buf
);
1266 void XMLAttribute::SetAttribute( bool v
)
1269 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1270 _value
.SetStr( buf
);
1273 void XMLAttribute::SetAttribute( double v
)
1276 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1277 _value
.SetStr( buf
);
1280 void XMLAttribute::SetAttribute( float v
)
1283 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1284 _value
.SetStr( buf
);
1288 // --------- XMLElement ---------- //
1289 XMLElement::XMLElement( XMLDocument
* doc
) : XMLNode( doc
),
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
) ) {
1317 const char* XMLElement::Attribute( const char* name
, const char* value
) const
1319 const XMLAttribute
* a
= FindAttribute( name
);
1323 if ( !value
|| XMLUtil::StringEqual( a
->Value(), value
)) {
1330 const char* XMLElement::GetText() const
1332 if ( FirstChild() && FirstChild()->ToText() ) {
1333 return FirstChild()->Value();
1339 void XMLElement::SetText( const char* inText
)
1341 if ( FirstChild() && FirstChild()->ToText() )
1342 FirstChild()->SetValue( inText
);
1344 XMLText
* theText
= GetDocument()->NewText( inText
);
1345 InsertFirstChild( theText
);
1350 void XMLElement::SetText( int v
)
1353 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1358 void XMLElement::SetText( unsigned v
)
1361 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1366 void XMLElement::SetText( bool v
)
1369 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1374 void XMLElement::SetText( float v
)
1377 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1382 void XMLElement::SetText( double v
)
1385 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
1390 XMLError
XMLElement::QueryIntText( int* ival
) const
1392 if ( FirstChild() && FirstChild()->ToText() ) {
1393 const char* t
= FirstChild()->Value();
1394 if ( XMLUtil::ToInt( t
, ival
) ) {
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
) ) {
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
) ) {
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
) ) {
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
) ) {
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
;
1462 last
= attrib
, attrib
= attrib
->_next
) {
1463 if ( XMLUtil::StringEqual( attrib
->Name(), name
) ) {
1468 TIXMLASSERT( sizeof( XMLAttribute
) == _document
->_attributePool
.ItemSize() );
1469 attrib
= new (_document
->_attributePool
.Alloc() ) XMLAttribute();
1470 attrib
->_memPool
= &_document
->_attributePool
;
1472 last
->_next
= attrib
;
1475 _rootAttribute
= attrib
;
1477 attrib
->SetName( name
);
1478 attrib
->_memPool
->SetTracked(); // always created and linked.
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() ) ) {
1490 prev
->_next
= a
->_next
;
1493 _rootAttribute
= a
->_next
;
1495 DeleteAttribute( a
);
1503 char* XMLElement::ParseAttributes( char* p
)
1505 const char* start
= p
;
1506 XMLAttribute
* prevAttribute
= 0;
1508 // Read the attributes.
1510 p
= XMLUtil::SkipWhiteSpace( p
);
1512 _document
->SetError( XML_ERROR_PARSING_ELEMENT
, start
, Name() );
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
);
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
;
1538 _rootAttribute
= attrib
;
1540 prevAttribute
= attrib
;
1543 else if ( *p
== '/' && *(p
+1) == '>' ) {
1544 _closingType
= CLOSED
;
1545 return p
+2; // done; sealed element.
1548 else if ( *p
== '>' ) {
1553 _document
->SetError( XML_ERROR_PARSING_ELEMENT
, start
, p
);
1560 void XMLElement::DeleteAttribute( XMLAttribute
* attribute
)
1562 if ( attribute
== 0 ) {
1565 MemPool
* pool
= attribute
->_memPool
;
1566 attribute
->~XMLAttribute();
1567 pool
->Free( attribute
);
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
1583 _closingType
= CLOSING
;
1587 p
= _value
.ParseName( p
);
1588 if ( _value
.Empty() ) {
1592 p
= ParseAttributes( p
);
1593 if ( !p
|| !*p
|| _closingType
) {
1597 p
= XMLNode::ParseDeep( p
, strPair
);
1603 XMLNode
* XMLElement::ShallowClone( XMLDocument
* doc
) const
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?
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();
1626 if ( !XMLUtil::StringEqual( a
->Value(), b
->Value() ) ) {
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
) ) {
1652 return visitor
->VisitExit( *this );
1656 // --------- XMLDocument ----------- //
1658 // Warning: List must match 'enum XMLError'
1659 const char* XMLDocument::_errorNames
[XML_ERROR_COUNT
] = {
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",
1683 XMLDocument::XMLDocument( bool processEntities
, Whitespace whitespace
) :
1686 _processEntities( processEntities
),
1687 _errorID( XML_NO_ERROR
),
1688 _whitespace( whitespace
),
1693 _document
= this; // avoid warning about 'this' in initializer list
1697 XMLDocument::~XMLDocument()
1703 void XMLDocument::Clear()
1708 const bool hadError
= Error();
1710 _errorID
= XML_NO_ERROR
;
1714 delete [] _charBuffer
;
1718 _textPool
.Trace( "text" );
1719 _elementPool
.Trace( "element" );
1720 _commentPool
.Trace( "comment" );
1721 _attributePool
.Trace( "attribute" );
1726 TIXMLASSERT( _elementPool
.CurrentAllocs() == _elementPool
.Untracked() );
1727 TIXMLASSERT( _attributePool
.CurrentAllocs() == _attributePool
.Untracked() );
1728 TIXMLASSERT( _textPool
.CurrentAllocs() == _textPool
.Untracked() );
1729 TIXMLASSERT( _commentPool
.CurrentAllocs() == _commentPool
.Untracked() );
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
);
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
);
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
);
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\"" );
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
);
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)
1790 errno_t err
= fopen_s( &fp
, filepath
, mode
);
1795 FILE* fp
= fopen( filepath
, mode
);
1800 void XMLDocument::DeleteNode( XMLNode
* node
) {
1801 TIXMLASSERT( node
);
1802 TIXMLASSERT(node
->_document
== this );
1803 if (node
->_parent
) {
1804 node
->_parent
->DeleteChild( node
);
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
)
1821 FILE* fp
= callfopen( filename
, "rb" );
1823 SetError( XML_ERROR_FILE_NOT_FOUND
, filename
, 0 );
1832 XMLError
XMLDocument::LoadFile( FILE* fp
)
1836 fseek( fp
, 0, SEEK_SET
);
1837 if ( fgetc( fp
) == EOF
&& ferror( fp
) != 0 ) {
1838 SetError( XML_ERROR_FILE_READ_ERROR
, 0, 0 );
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 );
1850 const size_t size
= filelength
;
1852 SetError( XML_ERROR_EMPTY_DOCUMENT
, 0, 0 );
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 );
1863 _charBuffer
[size
] = 0;
1870 XMLError
XMLDocument::SaveFile( const char* filename
, bool compact
)
1872 FILE* fp
= callfopen( filename
, "w" );
1874 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED
, filename
, 0 );
1877 SaveFile(fp
, compact
);
1883 XMLError
XMLDocument::SaveFile( FILE* fp
, bool compact
)
1885 XMLPrinter
stream( fp
, compact
);
1891 XMLError
XMLDocument::Parse( const char* p
, size_t len
)
1895 if ( len
== 0 || !p
|| !*p
) {
1896 SetError( XML_ERROR_EMPTY_DOCUMENT
, 0, 0 );
1899 if ( len
== (size_t)(-1) ) {
1902 _charBuffer
= new char[ len
+1 ];
1903 memcpy( _charBuffer
, p
, len
);
1904 _charBuffer
[len
] = 0;
1908 // clean up now essentially dangling memory.
1909 // and the parse fail can put objects in the
1910 // pools that are dead and inaccessible.
1912 _elementPool
.Clear();
1913 _attributePool
.Clear();
1915 _commentPool
.Clear();
1921 void XMLDocument::Print( XMLPrinter
* streamer
) const
1923 XMLPrinter
stdStreamer( stdout
);
1925 streamer
= &stdStreamer
;
1931 void XMLDocument::SetError( XMLError error
, const char* str1
, const char* str2
)
1933 TIXMLASSERT( error
>= 0 && error
< XML_ERROR_COUNT
);
1939 const char* XMLDocument::ErrorName() const
1941 TIXMLASSERT( _errorID
>= 0 && _errorID
< XML_ERROR_COUNT
);
1942 return _errorNames
[_errorID
];
1945 void XMLDocument::PrintError() const
1948 static const int LEN
= 20;
1949 char buf1
[LEN
] = { 0 };
1950 char buf2
[LEN
] = { 0 };
1953 TIXML_SNPRINTF( buf1
, LEN
, "%s", _errorStr1
);
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
) );
1972 SetError( XML_ERROR_EMPTY_DOCUMENT
, 0, 0 );
1978 XMLPrinter::XMLPrinter( FILE* file
, bool compact
, int depth
) :
1979 _elementJustOpened( false ),
1980 _firstElement( true ),
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
2003 void XMLPrinter::Print( const char* format
, ... )
2006 va_start( va
, format
);
2009 vfprintf( _fp
, format
, va
);
2012 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
2017 char* str
= new char[len
]();
2018 len
= _vsnprintf(str
, len
, format
, va
);
2022 int len
= _vscprintf( format
, va
);
2025 int len
= vsnprintf( 0, 0, format
, va
);
2027 // Close out and re-start the va-args
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 )
2034 _vsnprintf( p
, len
+1, format
, va
);
2036 vsnprintf_s( p
, len
+1, _TRUNCATE
, format
, va
);
2039 vsnprintf( p
, len
+1, format
, va
);
2046 void XMLPrinter::PrintSpace( int depth
)
2048 for( int i
=0; i
<depth
; ++i
) {
2054 void XMLPrinter::PrintString( const char* p
, bool restricted
)
2056 // Look for runs of bytes between entities to print.
2059 if ( _processEntities
) {
2060 const bool* flag
= restricted
? _restrictedEntityFlag
: _entityFlag
;
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
)] ) {
2072 for( int i
=0; i
<NUM_ENTITIES
; ++i
) {
2073 if ( entities
[i
].value
== *q
) {
2074 Print( "&%s;", entities
[i
].pattern
);
2084 // Flush the remaining string. This will be the entire
2085 // string if an entity wasn't found.
2086 if ( !_processEntities
|| (q
-p
> 0) ) {
2092 void XMLPrinter::PushHeader( bool writeBOM
, bool writeDec
)
2095 static const unsigned char bom
[] = { TIXML_UTF_LEAD_0
, TIXML_UTF_LEAD_1
, TIXML_UTF_LEAD_2
, 0 };
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
) {
2112 if ( !compactMode
) {
2113 PrintSpace( _depth
);
2116 Print( "<%s", name
);
2117 _elementJustOpened
= true;
2118 _firstElement
= false;
2123 void XMLPrinter::PushAttribute( const char* name
, const char* value
)
2125 TIXMLASSERT( _elementJustOpened
);
2126 Print( " %s=\"", name
);
2127 PrintString( value
, false );
2132 void XMLPrinter::PushAttribute( const char* name
, int v
)
2135 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
2136 PushAttribute( name
, buf
);
2140 void XMLPrinter::PushAttribute( const char* name
, unsigned v
)
2143 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
2144 PushAttribute( name
, buf
);
2148 void XMLPrinter::PushAttribute( const char* name
, bool v
)
2151 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
2152 PushAttribute( name
, buf
);
2156 void XMLPrinter::PushAttribute( const char* name
, double v
)
2159 XMLUtil::ToStr( v
, buf
, BUF_SIZE
);
2160 PushAttribute( name
, buf
);
2164 void XMLPrinter::CloseElement( bool compactMode
)
2167 const char* name
= _stack
.Pop();
2169 if ( _elementJustOpened
) {
2173 if ( _textDepth
< 0 && !compactMode
) {
2175 PrintSpace( _depth
);
2177 Print( "</%s>", name
);
2180 if ( _textDepth
== _depth
) {
2183 if ( _depth
== 0 && !compactMode
) {
2186 _elementJustOpened
= false;
2190 void XMLPrinter::SealElementIfJustOpened()
2192 if ( !_elementJustOpened
) {
2195 _elementJustOpened
= false;
2200 void XMLPrinter::PushText( const char* text
, bool cdata
)
2202 _textDepth
= _depth
-1;
2204 SealElementIfJustOpened();
2206 Print( "<![CDATA[" );
2207 Print( "%s", text
);
2211 PrintString( text
, true );
2215 void XMLPrinter::PushText( int value
)
2218 XMLUtil::ToStr( value
, buf
, BUF_SIZE
);
2219 PushText( buf
, false );
2223 void XMLPrinter::PushText( unsigned value
)
2226 XMLUtil::ToStr( value
, buf
, BUF_SIZE
);
2227 PushText( buf
, false );
2231 void XMLPrinter::PushText( bool value
)
2234 XMLUtil::ToStr( value
, buf
, BUF_SIZE
);
2235 PushText( buf
, false );
2239 void XMLPrinter::PushText( float value
)
2242 XMLUtil::ToStr( value
, buf
, BUF_SIZE
);
2243 PushText( buf
, false );
2247 void XMLPrinter::PushText( double value
)
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
) {
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
) {
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
) {
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 );
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();
2314 bool XMLPrinter::VisitExit( const XMLElement
& element
)
2316 CloseElement( CompactMode(element
) );
2321 bool XMLPrinter::Visit( const XMLText
& text
)
2323 PushText( text
.Value(), text
.CData() );
2328 bool XMLPrinter::Visit( const XMLComment
& comment
)
2330 PushComment( comment
.Value() );
2334 bool XMLPrinter::Visit( const XMLDeclaration
& declaration
)
2336 PushDeclaration( declaration
.Value() );
2341 bool XMLPrinter::Visit( const XMLUnknown
& unknown
)
2343 PushUnknown( unknown
.Value() );
2347 } // namespace tinyxml2