goat3d

view libs/tinyxml2/tinyxml2.cpp @ 93:07c0ec4a410d

[goatview] line endings fix and missing post_redisplay in load_scene
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 18 May 2014 06:20:20 +0300
parents
children
line source
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.
22 */
24 #include "tinyxml2.h"
26 #include <new> // yes, this one new style header, is in the Android SDK.
27 # ifdef ANDROID_NDK
28 # include <stddef.h>
29 #else
30 # include <cstddef>
31 #endif
33 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
34 static const char LF = LINE_FEED;
35 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
36 static const char CR = CARRIAGE_RETURN;
37 static const char SINGLE_QUOTE = '\'';
38 static const char DOUBLE_QUOTE = '\"';
40 // Bunch of unicode info at:
41 // http://www.unicode.org/faq/utf_bom.html
42 // ef bb bf (Microsoft "lead bytes") - designates UTF-8
44 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
45 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
46 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
49 #define DELETE_NODE( node ) { \
50 if ( node ) { \
51 MemPool* pool = node->_memPool; \
52 node->~XMLNode(); \
53 pool->Free( node ); \
54 } \
55 }
56 #define DELETE_ATTRIBUTE( attrib ) { \
57 if ( attrib ) { \
58 MemPool* pool = attrib->_memPool; \
59 attrib->~XMLAttribute(); \
60 pool->Free( attrib ); \
61 } \
62 }
64 namespace tinyxml2
65 {
67 struct Entity {
68 const char* pattern;
69 int length;
70 char value;
71 };
73 static const int NUM_ENTITIES = 5;
74 static const Entity entities[NUM_ENTITIES] = {
75 { "quot", 4, DOUBLE_QUOTE },
76 { "amp", 3, '&' },
77 { "apos", 4, SINGLE_QUOTE },
78 { "lt", 2, '<' },
79 { "gt", 2, '>' }
80 };
83 StrPair::~StrPair()
84 {
85 Reset();
86 }
89 void StrPair::Reset()
90 {
91 if ( _flags & NEEDS_DELETE ) {
92 delete [] _start;
93 }
94 _flags = 0;
95 _start = 0;
96 _end = 0;
97 }
100 void StrPair::SetStr( const char* str, int flags )
101 {
102 Reset();
103 size_t len = strlen( str );
104 _start = new char[ len+1 ];
105 memcpy( _start, str, len+1 );
106 _end = _start + len;
107 _flags = flags | NEEDS_DELETE;
108 }
111 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
112 {
113 TIXMLASSERT( endTag && *endTag );
115 char* start = p; // fixme: hides a member
116 char endChar = *endTag;
117 size_t length = strlen( endTag );
119 // Inner loop of text parsing.
120 while ( *p ) {
121 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
122 Set( start, p, strFlags );
123 return p + length;
124 }
125 ++p;
126 }
127 return 0;
128 }
131 char* StrPair::ParseName( char* p )
132 {
133 char* start = p;
135 if ( !start || !(*start) ) {
136 return 0;
137 }
139 while( *p && ( p == start ? XMLUtil::IsNameStartChar( *p ) : XMLUtil::IsNameChar( *p ) )) {
140 ++p;
141 }
143 if ( p > start ) {
144 Set( start, p, 0 );
145 return p;
146 }
147 return 0;
148 }
151 void StrPair::CollapseWhitespace()
152 {
153 // Trim leading space.
154 _start = XMLUtil::SkipWhiteSpace( _start );
156 if ( _start && *_start ) {
157 char* p = _start; // the read pointer
158 char* q = _start; // the write pointer
160 while( *p ) {
161 if ( XMLUtil::IsWhiteSpace( *p )) {
162 p = XMLUtil::SkipWhiteSpace( p );
163 if ( *p == 0 ) {
164 break; // don't write to q; this trims the trailing space.
165 }
166 *q = ' ';
167 ++q;
168 }
169 *q = *p;
170 ++q;
171 ++p;
172 }
173 *q = 0;
174 }
175 }
178 const char* StrPair::GetStr()
179 {
180 if ( _flags & NEEDS_FLUSH ) {
181 *_end = 0;
182 _flags ^= NEEDS_FLUSH;
184 if ( _flags ) {
185 char* p = _start; // the read pointer
186 char* q = _start; // the write pointer
188 while( p < _end ) {
189 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
190 // CR-LF pair becomes LF
191 // CR alone becomes LF
192 // LF-CR becomes LF
193 if ( *(p+1) == LF ) {
194 p += 2;
195 }
196 else {
197 ++p;
198 }
199 *q++ = LF;
200 }
201 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
202 if ( *(p+1) == CR ) {
203 p += 2;
204 }
205 else {
206 ++p;
207 }
208 *q++ = LF;
209 }
210 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
211 // Entities handled by tinyXML2:
212 // - special entities in the entity table [in/out]
213 // - numeric character reference [in]
214 // &#20013; or &#x4e2d;
216 if ( *(p+1) == '#' ) {
217 char buf[10] = { 0 };
218 int len;
219 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
220 for( int i=0; i<len; ++i ) {
221 *q++ = buf[i];
222 }
223 TIXMLASSERT( q <= p );
224 }
225 else {
226 int i=0;
227 for(; i<NUM_ENTITIES; ++i ) {
228 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
229 && *(p+entities[i].length+1) == ';' ) {
230 // Found an entity convert;
231 *q = entities[i].value;
232 ++q;
233 p += entities[i].length + 2;
234 break;
235 }
236 }
237 if ( i == NUM_ENTITIES ) {
238 // fixme: treat as error?
239 ++p;
240 ++q;
241 }
242 }
243 }
244 else {
245 *q = *p;
246 ++p;
247 ++q;
248 }
249 }
250 *q = 0;
251 }
252 // The loop below has plenty going on, and this
253 // is a less useful mode. Break it out.
254 if ( _flags & COLLAPSE_WHITESPACE ) {
255 CollapseWhitespace();
256 }
257 _flags = (_flags & NEEDS_DELETE);
258 }
259 return _start;
260 }
265 // --------- XMLUtil ----------- //
267 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
268 {
269 *bom = false;
270 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
271 // Check for BOM:
272 if ( *(pu+0) == TIXML_UTF_LEAD_0
273 && *(pu+1) == TIXML_UTF_LEAD_1
274 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
275 *bom = true;
276 p += 3;
277 }
278 return p;
279 }
282 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
283 {
284 const unsigned long BYTE_MASK = 0xBF;
285 const unsigned long BYTE_MARK = 0x80;
286 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
288 if (input < 0x80) {
289 *length = 1;
290 }
291 else if ( input < 0x800 ) {
292 *length = 2;
293 }
294 else if ( input < 0x10000 ) {
295 *length = 3;
296 }
297 else if ( input < 0x200000 ) {
298 *length = 4;
299 }
300 else {
301 *length = 0; // This code won't covert this correctly anyway.
302 return;
303 }
305 output += *length;
307 // Scary scary fall throughs.
308 switch (*length) {
309 case 4:
310 --output;
311 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
312 input >>= 6;
313 case 3:
314 --output;
315 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
316 input >>= 6;
317 case 2:
318 --output;
319 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
320 input >>= 6;
321 case 1:
322 --output;
323 *output = (char)(input | FIRST_BYTE_MARK[*length]);
324 default:
325 break;
326 }
327 }
330 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
331 {
332 // Presume an entity, and pull it out.
333 *length = 0;
335 if ( *(p+1) == '#' && *(p+2) ) {
336 unsigned long ucs = 0;
337 ptrdiff_t delta = 0;
338 unsigned mult = 1;
340 if ( *(p+2) == 'x' ) {
341 // Hexadecimal.
342 if ( !*(p+3) ) {
343 return 0;
344 }
346 const char* q = p+3;
347 q = strchr( q, ';' );
349 if ( !q || !*q ) {
350 return 0;
351 }
353 delta = q-p;
354 --q;
356 while ( *q != 'x' ) {
357 if ( *q >= '0' && *q <= '9' ) {
358 ucs += mult * (*q - '0');
359 }
360 else if ( *q >= 'a' && *q <= 'f' ) {
361 ucs += mult * (*q - 'a' + 10);
362 }
363 else if ( *q >= 'A' && *q <= 'F' ) {
364 ucs += mult * (*q - 'A' + 10 );
365 }
366 else {
367 return 0;
368 }
369 mult *= 16;
370 --q;
371 }
372 }
373 else {
374 // Decimal.
375 if ( !*(p+2) ) {
376 return 0;
377 }
379 const char* q = p+2;
380 q = strchr( q, ';' );
382 if ( !q || !*q ) {
383 return 0;
384 }
386 delta = q-p;
387 --q;
389 while ( *q != '#' ) {
390 if ( *q >= '0' && *q <= '9' ) {
391 ucs += mult * (*q - '0');
392 }
393 else {
394 return 0;
395 }
396 mult *= 10;
397 --q;
398 }
399 }
400 // convert the UCS to UTF-8
401 ConvertUTF32ToUTF8( ucs, value, length );
402 return p + delta + 1;
403 }
404 return p+1;
405 }
408 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
409 {
410 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
411 }
414 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
415 {
416 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
417 }
420 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
421 {
422 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
423 }
426 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
427 {
428 TIXML_SNPRINTF( buffer, bufferSize, "%f", v );
429 }
432 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
433 {
434 TIXML_SNPRINTF( buffer, bufferSize, "%f", v );
435 }
438 bool XMLUtil::ToInt( const char* str, int* value )
439 {
440 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
441 return true;
442 }
443 return false;
444 }
446 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
447 {
448 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
449 return true;
450 }
451 return false;
452 }
454 bool XMLUtil::ToBool( const char* str, bool* value )
455 {
456 int ival = 0;
457 if ( ToInt( str, &ival )) {
458 *value = (ival==0) ? false : true;
459 return true;
460 }
461 if ( StringEqual( str, "true" ) ) {
462 *value = true;
463 return true;
464 }
465 else if ( StringEqual( str, "false" ) ) {
466 *value = false;
467 return true;
468 }
469 return false;
470 }
473 bool XMLUtil::ToFloat( const char* str, float* value )
474 {
475 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
476 return true;
477 }
478 return false;
479 }
481 bool XMLUtil::ToDouble( const char* str, double* value )
482 {
483 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
484 return true;
485 }
486 return false;
487 }
490 char* XMLDocument::Identify( char* p, XMLNode** node )
491 {
492 XMLNode* returnNode = 0;
493 char* start = p;
494 p = XMLUtil::SkipWhiteSpace( p );
495 if( !p || !*p ) {
496 return p;
497 }
499 // What is this thing?
500 // - Elements start with a letter or underscore, but xml is reserved.
501 // - Comments: <!--
502 // - Declaration: <?
503 // - Everything else is unknown to tinyxml.
504 //
506 static const char* xmlHeader = { "<?" };
507 static const char* commentHeader = { "<!--" };
508 static const char* dtdHeader = { "<!" };
509 static const char* cdataHeader = { "<![CDATA[" };
510 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
512 static const int xmlHeaderLen = 2;
513 static const int commentHeaderLen = 4;
514 static const int dtdHeaderLen = 2;
515 static const int cdataHeaderLen = 9;
516 static const int elementHeaderLen = 1;
518 #if defined(_MSC_VER)
519 #pragma warning ( push )
520 #pragma warning ( disable : 4127 )
521 #endif
522 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
523 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
524 #if defined(_MSC_VER)
525 #pragma warning (pop)
526 #endif
527 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
528 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
529 returnNode->_memPool = &_commentPool;
530 p += xmlHeaderLen;
531 }
532 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
533 returnNode = new (_commentPool.Alloc()) XMLComment( this );
534 returnNode->_memPool = &_commentPool;
535 p += commentHeaderLen;
536 }
537 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
538 XMLText* text = new (_textPool.Alloc()) XMLText( this );
539 returnNode = text;
540 returnNode->_memPool = &_textPool;
541 p += cdataHeaderLen;
542 text->SetCData( true );
543 }
544 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
545 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
546 returnNode->_memPool = &_commentPool;
547 p += dtdHeaderLen;
548 }
549 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
550 returnNode = new (_elementPool.Alloc()) XMLElement( this );
551 returnNode->_memPool = &_elementPool;
552 p += elementHeaderLen;
553 }
554 else {
555 returnNode = new (_textPool.Alloc()) XMLText( this );
556 returnNode->_memPool = &_textPool;
557 p = start; // Back it up, all the text counts.
558 }
560 *node = returnNode;
561 return p;
562 }
565 bool XMLDocument::Accept( XMLVisitor* visitor ) const
566 {
567 if ( visitor->VisitEnter( *this ) ) {
568 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
569 if ( !node->Accept( visitor ) ) {
570 break;
571 }
572 }
573 }
574 return visitor->VisitExit( *this );
575 }
578 // --------- XMLNode ----------- //
580 XMLNode::XMLNode( XMLDocument* doc ) :
581 _document( doc ),
582 _parent( 0 ),
583 _firstChild( 0 ), _lastChild( 0 ),
584 _prev( 0 ), _next( 0 ),
585 _memPool( 0 )
586 {
587 }
590 XMLNode::~XMLNode()
591 {
592 DeleteChildren();
593 if ( _parent ) {
594 _parent->Unlink( this );
595 }
596 }
599 void XMLNode::SetValue( const char* str, bool staticMem )
600 {
601 if ( staticMem ) {
602 _value.SetInternedStr( str );
603 }
604 else {
605 _value.SetStr( str );
606 }
607 }
610 void XMLNode::DeleteChildren()
611 {
612 while( _firstChild ) {
613 XMLNode* node = _firstChild;
614 Unlink( node );
616 DELETE_NODE( node );
617 }
618 _firstChild = _lastChild = 0;
619 }
622 void XMLNode::Unlink( XMLNode* child )
623 {
624 TIXMLASSERT( child->_parent == this );
625 if ( child == _firstChild ) {
626 _firstChild = _firstChild->_next;
627 }
628 if ( child == _lastChild ) {
629 _lastChild = _lastChild->_prev;
630 }
632 if ( child->_prev ) {
633 child->_prev->_next = child->_next;
634 }
635 if ( child->_next ) {
636 child->_next->_prev = child->_prev;
637 }
638 child->_parent = 0;
639 }
642 void XMLNode::DeleteChild( XMLNode* node )
643 {
644 TIXMLASSERT( node->_parent == this );
645 DELETE_NODE( node );
646 }
649 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
650 {
651 if ( _lastChild ) {
652 TIXMLASSERT( _firstChild );
653 TIXMLASSERT( _lastChild->_next == 0 );
654 _lastChild->_next = addThis;
655 addThis->_prev = _lastChild;
656 _lastChild = addThis;
658 addThis->_next = 0;
659 }
660 else {
661 TIXMLASSERT( _firstChild == 0 );
662 _firstChild = _lastChild = addThis;
664 addThis->_prev = 0;
665 addThis->_next = 0;
666 }
667 addThis->_parent = this;
668 addThis->_memPool->SetTracked();
669 return addThis;
670 }
673 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
674 {
675 if ( _firstChild ) {
676 TIXMLASSERT( _lastChild );
677 TIXMLASSERT( _firstChild->_prev == 0 );
679 _firstChild->_prev = addThis;
680 addThis->_next = _firstChild;
681 _firstChild = addThis;
683 addThis->_prev = 0;
684 }
685 else {
686 TIXMLASSERT( _lastChild == 0 );
687 _firstChild = _lastChild = addThis;
689 addThis->_prev = 0;
690 addThis->_next = 0;
691 }
692 addThis->_parent = this;
693 addThis->_memPool->SetTracked();
694 return addThis;
695 }
698 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
699 {
700 TIXMLASSERT( afterThis->_parent == this );
701 if ( afterThis->_parent != this ) {
702 return 0;
703 }
705 if ( afterThis->_next == 0 ) {
706 // The last node or the only node.
707 return InsertEndChild( addThis );
708 }
709 addThis->_prev = afterThis;
710 addThis->_next = afterThis->_next;
711 afterThis->_next->_prev = addThis;
712 afterThis->_next = addThis;
713 addThis->_parent = this;
714 addThis->_memPool->SetTracked();
715 return addThis;
716 }
721 const XMLElement* XMLNode::FirstChildElement( const char* value ) const
722 {
723 for( XMLNode* node=_firstChild; node; node=node->_next ) {
724 XMLElement* element = node->ToElement();
725 if ( element ) {
726 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
727 return element;
728 }
729 }
730 }
731 return 0;
732 }
735 const XMLElement* XMLNode::LastChildElement( const char* value ) const
736 {
737 for( XMLNode* node=_lastChild; node; node=node->_prev ) {
738 XMLElement* element = node->ToElement();
739 if ( element ) {
740 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
741 return element;
742 }
743 }
744 }
745 return 0;
746 }
749 const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
750 {
751 for( XMLNode* element=this->_next; element; element = element->_next ) {
752 if ( element->ToElement()
753 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
754 return element->ToElement();
755 }
756 }
757 return 0;
758 }
761 const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
762 {
763 for( XMLNode* element=_prev; element; element = element->_prev ) {
764 if ( element->ToElement()
765 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
766 return element->ToElement();
767 }
768 }
769 return 0;
770 }
773 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
774 {
775 // This is a recursive method, but thinking about it "at the current level"
776 // it is a pretty simple flat list:
777 // <foo/>
778 // <!-- comment -->
779 //
780 // With a special case:
781 // <foo>
782 // </foo>
783 // <!-- comment -->
784 //
785 // Where the closing element (/foo) *must* be the next thing after the opening
786 // element, and the names must match. BUT the tricky bit is that the closing
787 // element will be read by the child.
788 //
789 // 'endTag' is the end tag for this node, it is returned by a call to a child.
790 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
792 while( p && *p ) {
793 XMLNode* node = 0;
795 p = _document->Identify( p, &node );
796 if ( p == 0 || node == 0 ) {
797 break;
798 }
800 StrPair endTag;
801 p = node->ParseDeep( p, &endTag );
802 if ( !p ) {
803 DELETE_NODE( node );
804 node = 0;
805 if ( !_document->Error() ) {
806 _document->SetError( XML_ERROR_PARSING, 0, 0 );
807 }
808 break;
809 }
811 // We read the end tag. Return it to the parent.
812 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
813 if ( parentEnd ) {
814 *parentEnd = static_cast<XMLElement*>(node)->_value;
815 }
816 node->_memPool->SetTracked(); // created and then immediately deleted.
817 DELETE_NODE( node );
818 return p;
819 }
821 // Handle an end tag returned to this level.
822 // And handle a bunch of annoying errors.
823 XMLElement* ele = node->ToElement();
824 if ( ele ) {
825 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
826 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
827 p = 0;
828 }
829 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
830 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
831 p = 0;
832 }
833 else if ( !endTag.Empty() ) {
834 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
835 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
836 p = 0;
837 }
838 }
839 }
840 if ( p == 0 ) {
841 DELETE_NODE( node );
842 node = 0;
843 }
844 if ( node ) {
845 this->InsertEndChild( node );
846 }
847 }
848 return 0;
849 }
851 // --------- XMLText ---------- //
852 char* XMLText::ParseDeep( char* p, StrPair* )
853 {
854 const char* start = p;
855 if ( this->CData() ) {
856 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
857 if ( !p ) {
858 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
859 }
860 return p;
861 }
862 else {
863 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
864 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
865 flags |= StrPair::COLLAPSE_WHITESPACE;
866 }
868 p = _value.ParseText( p, "<", flags );
869 if ( !p ) {
870 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
871 }
872 if ( p && *p ) {
873 return p-1;
874 }
875 }
876 return 0;
877 }
880 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
881 {
882 if ( !doc ) {
883 doc = _document;
884 }
885 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
886 text->SetCData( this->CData() );
887 return text;
888 }
891 bool XMLText::ShallowEqual( const XMLNode* compare ) const
892 {
893 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
894 }
897 bool XMLText::Accept( XMLVisitor* visitor ) const
898 {
899 return visitor->Visit( *this );
900 }
903 // --------- XMLComment ---------- //
905 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
906 {
907 }
910 XMLComment::~XMLComment()
911 {
912 }
915 char* XMLComment::ParseDeep( char* p, StrPair* )
916 {
917 // Comment parses as text.
918 const char* start = p;
919 p = _value.ParseText( p, "-->", StrPair::COMMENT );
920 if ( p == 0 ) {
921 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
922 }
923 return p;
924 }
927 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
928 {
929 if ( !doc ) {
930 doc = _document;
931 }
932 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
933 return comment;
934 }
937 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
938 {
939 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
940 }
943 bool XMLComment::Accept( XMLVisitor* visitor ) const
944 {
945 return visitor->Visit( *this );
946 }
949 // --------- XMLDeclaration ---------- //
951 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
952 {
953 }
956 XMLDeclaration::~XMLDeclaration()
957 {
958 //printf( "~XMLDeclaration\n" );
959 }
962 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
963 {
964 // Declaration parses as text.
965 const char* start = p;
966 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
967 if ( p == 0 ) {
968 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
969 }
970 return p;
971 }
974 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
975 {
976 if ( !doc ) {
977 doc = _document;
978 }
979 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
980 return dec;
981 }
984 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
985 {
986 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
987 }
991 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
992 {
993 return visitor->Visit( *this );
994 }
996 // --------- XMLUnknown ---------- //
998 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
999 {
1003 XMLUnknown::~XMLUnknown()
1008 char* XMLUnknown::ParseDeep( char* p, StrPair* )
1010 // Unknown parses as text.
1011 const char* start = p;
1013 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1014 if ( !p ) {
1015 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1017 return p;
1021 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1023 if ( !doc ) {
1024 doc = _document;
1026 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1027 return text;
1031 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1033 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
1037 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1039 return visitor->Visit( *this );
1042 // --------- XMLAttribute ---------- //
1043 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1045 // Parse using the name rules: bug fix, was using ParseText before
1046 p = _name.ParseName( p );
1047 if ( !p || !*p ) {
1048 return 0;
1051 // Skip white space before =
1052 p = XMLUtil::SkipWhiteSpace( p );
1053 if ( !p || *p != '=' ) {
1054 return 0;
1057 ++p; // move up to opening quote
1058 p = XMLUtil::SkipWhiteSpace( p );
1059 if ( *p != '\"' && *p != '\'' ) {
1060 return 0;
1063 char endTag[2] = { *p, 0 };
1064 ++p; // move past opening quote
1066 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1067 return p;
1071 void XMLAttribute::SetName( const char* n )
1073 _name.SetStr( n );
1077 XMLError XMLAttribute::QueryIntValue( int* value ) const
1079 if ( XMLUtil::ToInt( Value(), value )) {
1080 return XML_NO_ERROR;
1082 return XML_WRONG_ATTRIBUTE_TYPE;
1086 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1088 if ( XMLUtil::ToUnsigned( Value(), value )) {
1089 return XML_NO_ERROR;
1091 return XML_WRONG_ATTRIBUTE_TYPE;
1095 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1097 if ( XMLUtil::ToBool( Value(), value )) {
1098 return XML_NO_ERROR;
1100 return XML_WRONG_ATTRIBUTE_TYPE;
1104 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1106 if ( XMLUtil::ToFloat( Value(), value )) {
1107 return XML_NO_ERROR;
1109 return XML_WRONG_ATTRIBUTE_TYPE;
1113 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1115 if ( XMLUtil::ToDouble( Value(), value )) {
1116 return XML_NO_ERROR;
1118 return XML_WRONG_ATTRIBUTE_TYPE;
1122 void XMLAttribute::SetAttribute( const char* v )
1124 _value.SetStr( v );
1128 void XMLAttribute::SetAttribute( int v )
1130 char buf[BUF_SIZE];
1131 XMLUtil::ToStr( v, buf, BUF_SIZE );
1132 _value.SetStr( buf );
1136 void XMLAttribute::SetAttribute( unsigned v )
1138 char buf[BUF_SIZE];
1139 XMLUtil::ToStr( v, buf, BUF_SIZE );
1140 _value.SetStr( buf );
1144 void XMLAttribute::SetAttribute( bool v )
1146 char buf[BUF_SIZE];
1147 XMLUtil::ToStr( v, buf, BUF_SIZE );
1148 _value.SetStr( buf );
1151 void XMLAttribute::SetAttribute( double v )
1153 char buf[BUF_SIZE];
1154 XMLUtil::ToStr( v, buf, BUF_SIZE );
1155 _value.SetStr( buf );
1158 void XMLAttribute::SetAttribute( float v )
1160 char buf[BUF_SIZE];
1161 XMLUtil::ToStr( v, buf, BUF_SIZE );
1162 _value.SetStr( buf );
1166 // --------- XMLElement ---------- //
1167 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1168 _closingType( 0 ),
1169 _rootAttribute( 0 )
1174 XMLElement::~XMLElement()
1176 while( _rootAttribute ) {
1177 XMLAttribute* next = _rootAttribute->_next;
1178 DELETE_ATTRIBUTE( _rootAttribute );
1179 _rootAttribute = next;
1184 XMLAttribute* XMLElement::FindAttribute( const char* name )
1186 XMLAttribute* a = 0;
1187 for( a=_rootAttribute; a; a = a->_next ) {
1188 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1189 return a;
1192 return 0;
1196 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1198 XMLAttribute* a = 0;
1199 for( a=_rootAttribute; a; a = a->_next ) {
1200 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1201 return a;
1204 return 0;
1208 const char* XMLElement::Attribute( const char* name, const char* value ) const
1210 const XMLAttribute* a = FindAttribute( name );
1211 if ( !a ) {
1212 return 0;
1214 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1215 return a->Value();
1217 return 0;
1221 const char* XMLElement::GetText() const
1223 if ( FirstChild() && FirstChild()->ToText() ) {
1224 return FirstChild()->ToText()->Value();
1226 return 0;
1230 XMLError XMLElement::QueryIntText( int* ival ) const
1232 if ( FirstChild() && FirstChild()->ToText() ) {
1233 const char* t = FirstChild()->ToText()->Value();
1234 if ( XMLUtil::ToInt( t, ival ) ) {
1235 return XML_SUCCESS;
1237 return XML_CAN_NOT_CONVERT_TEXT;
1239 return XML_NO_TEXT_NODE;
1243 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1245 if ( FirstChild() && FirstChild()->ToText() ) {
1246 const char* t = FirstChild()->ToText()->Value();
1247 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1248 return XML_SUCCESS;
1250 return XML_CAN_NOT_CONVERT_TEXT;
1252 return XML_NO_TEXT_NODE;
1256 XMLError XMLElement::QueryBoolText( bool* bval ) const
1258 if ( FirstChild() && FirstChild()->ToText() ) {
1259 const char* t = FirstChild()->ToText()->Value();
1260 if ( XMLUtil::ToBool( t, bval ) ) {
1261 return XML_SUCCESS;
1263 return XML_CAN_NOT_CONVERT_TEXT;
1265 return XML_NO_TEXT_NODE;
1269 XMLError XMLElement::QueryDoubleText( double* dval ) const
1271 if ( FirstChild() && FirstChild()->ToText() ) {
1272 const char* t = FirstChild()->ToText()->Value();
1273 if ( XMLUtil::ToDouble( t, dval ) ) {
1274 return XML_SUCCESS;
1276 return XML_CAN_NOT_CONVERT_TEXT;
1278 return XML_NO_TEXT_NODE;
1282 XMLError XMLElement::QueryFloatText( float* fval ) const
1284 if ( FirstChild() && FirstChild()->ToText() ) {
1285 const char* t = FirstChild()->ToText()->Value();
1286 if ( XMLUtil::ToFloat( t, fval ) ) {
1287 return XML_SUCCESS;
1289 return XML_CAN_NOT_CONVERT_TEXT;
1291 return XML_NO_TEXT_NODE;
1296 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1298 XMLAttribute* last = 0;
1299 XMLAttribute* attrib = 0;
1300 for( attrib = _rootAttribute;
1301 attrib;
1302 last = attrib, attrib = attrib->_next ) {
1303 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1304 break;
1307 if ( !attrib ) {
1308 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1309 attrib->_memPool = &_document->_attributePool;
1310 if ( last ) {
1311 last->_next = attrib;
1313 else {
1314 _rootAttribute = attrib;
1316 attrib->SetName( name );
1317 attrib->_memPool->SetTracked(); // always created and linked.
1319 return attrib;
1323 void XMLElement::DeleteAttribute( const char* name )
1325 XMLAttribute* prev = 0;
1326 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1327 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1328 if ( prev ) {
1329 prev->_next = a->_next;
1331 else {
1332 _rootAttribute = a->_next;
1334 DELETE_ATTRIBUTE( a );
1335 break;
1337 prev = a;
1342 char* XMLElement::ParseAttributes( char* p )
1344 const char* start = p;
1345 XMLAttribute* prevAttribute = 0;
1347 // Read the attributes.
1348 while( p ) {
1349 p = XMLUtil::SkipWhiteSpace( p );
1350 if ( !p || !(*p) ) {
1351 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1352 return 0;
1355 // attribute.
1356 if (XMLUtil::IsNameStartChar( *p ) ) {
1357 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1358 attrib->_memPool = &_document->_attributePool;
1359 attrib->_memPool->SetTracked();
1361 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1362 if ( !p || Attribute( attrib->Name() ) ) {
1363 DELETE_ATTRIBUTE( attrib );
1364 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1365 return 0;
1367 // There is a minor bug here: if the attribute in the source xml
1368 // document is duplicated, it will not be detected and the
1369 // attribute will be doubly added. However, tracking the 'prevAttribute'
1370 // avoids re-scanning the attribute list. Preferring performance for
1371 // now, may reconsider in the future.
1372 if ( prevAttribute ) {
1373 prevAttribute->_next = attrib;
1375 else {
1376 _rootAttribute = attrib;
1378 prevAttribute = attrib;
1380 // end of the tag
1381 else if ( *p == '/' && *(p+1) == '>' ) {
1382 _closingType = CLOSED;
1383 return p+2; // done; sealed element.
1385 // end of the tag
1386 else if ( *p == '>' ) {
1387 ++p;
1388 break;
1390 else {
1391 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1392 return 0;
1395 return p;
1399 //
1400 // <ele></ele>
1401 // <ele>foo<b>bar</b></ele>
1402 //
1403 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1405 // Read the element name.
1406 p = XMLUtil::SkipWhiteSpace( p );
1407 if ( !p ) {
1408 return 0;
1411 // The closing element is the </element> form. It is
1412 // parsed just like a regular element then deleted from
1413 // the DOM.
1414 if ( *p == '/' ) {
1415 _closingType = CLOSING;
1416 ++p;
1419 p = _value.ParseName( p );
1420 if ( _value.Empty() ) {
1421 return 0;
1424 p = ParseAttributes( p );
1425 if ( !p || !*p || _closingType ) {
1426 return p;
1429 p = XMLNode::ParseDeep( p, strPair );
1430 return p;
1435 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1437 if ( !doc ) {
1438 doc = _document;
1440 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1441 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1442 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1444 return element;
1448 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1450 const XMLElement* other = compare->ToElement();
1451 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1453 const XMLAttribute* a=FirstAttribute();
1454 const XMLAttribute* b=other->FirstAttribute();
1456 while ( a && b ) {
1457 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1458 return false;
1460 a = a->Next();
1461 b = b->Next();
1463 if ( a || b ) {
1464 // different count
1465 return false;
1467 return true;
1469 return false;
1473 bool XMLElement::Accept( XMLVisitor* visitor ) const
1475 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1476 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1477 if ( !node->Accept( visitor ) ) {
1478 break;
1482 return visitor->VisitExit( *this );
1486 // --------- XMLDocument ----------- //
1487 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1488 XMLNode( 0 ),
1489 _writeBOM( false ),
1490 _processEntities( processEntities ),
1491 _errorID( XML_NO_ERROR ),
1492 _whitespace( whitespace ),
1493 _errorStr1( 0 ),
1494 _errorStr2( 0 ),
1495 _charBuffer( 0 )
1497 _document = this; // avoid warning about 'this' in initializer list
1501 XMLDocument::~XMLDocument()
1503 DeleteChildren();
1504 delete [] _charBuffer;
1506 #if 0
1507 _textPool.Trace( "text" );
1508 _elementPool.Trace( "element" );
1509 _commentPool.Trace( "comment" );
1510 _attributePool.Trace( "attribute" );
1511 #endif
1513 #ifdef DEBUG
1514 if ( Error() == false ) {
1515 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
1516 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1517 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
1518 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
1520 #endif
1524 void XMLDocument::Clear()
1526 DeleteChildren();
1528 _errorID = XML_NO_ERROR;
1529 _errorStr1 = 0;
1530 _errorStr2 = 0;
1532 delete [] _charBuffer;
1533 _charBuffer = 0;
1537 XMLElement* XMLDocument::NewElement( const char* name )
1539 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1540 ele->_memPool = &_elementPool;
1541 ele->SetName( name );
1542 return ele;
1546 XMLComment* XMLDocument::NewComment( const char* str )
1548 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1549 comment->_memPool = &_commentPool;
1550 comment->SetValue( str );
1551 return comment;
1555 XMLText* XMLDocument::NewText( const char* str )
1557 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1558 text->_memPool = &_textPool;
1559 text->SetValue( str );
1560 return text;
1564 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1566 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1567 dec->_memPool = &_commentPool;
1568 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1569 return dec;
1573 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1575 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1576 unk->_memPool = &_commentPool;
1577 unk->SetValue( str );
1578 return unk;
1582 XMLError XMLDocument::LoadFile( const char* filename )
1584 Clear();
1585 FILE* fp = 0;
1587 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1588 errno_t err = fopen_s(&fp, filename, "rb" );
1589 if ( !fp || err) {
1590 #else
1591 fp = fopen( filename, "rb" );
1592 if ( !fp) {
1593 #endif
1594 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1595 return _errorID;
1597 LoadFile( fp );
1598 fclose( fp );
1599 return _errorID;
1603 XMLError XMLDocument::LoadFile( FILE* fp )
1605 Clear();
1607 fseek( fp, 0, SEEK_END );
1608 size_t size = ftell( fp );
1609 fseek( fp, 0, SEEK_SET );
1611 if ( size == 0 ) {
1612 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1613 return _errorID;
1616 _charBuffer = new char[size+1];
1617 size_t read = fread( _charBuffer, 1, size, fp );
1618 if ( read != size ) {
1619 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1620 return _errorID;
1623 _charBuffer[size] = 0;
1625 const char* p = _charBuffer;
1626 p = XMLUtil::SkipWhiteSpace( p );
1627 p = XMLUtil::ReadBOM( p, &_writeBOM );
1628 if ( !p || !*p ) {
1629 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1630 return _errorID;
1633 ParseDeep( _charBuffer + (p-_charBuffer), 0 );
1634 return _errorID;
1638 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1640 FILE* fp = 0;
1641 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1642 errno_t err = fopen_s(&fp, filename, "w" );
1643 if ( !fp || err) {
1644 #else
1645 fp = fopen( filename, "w" );
1646 if ( !fp) {
1647 #endif
1648 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1649 return _errorID;
1651 SaveFile(fp, compact);
1652 fclose( fp );
1653 return _errorID;
1657 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
1659 XMLPrinter stream( fp, compact );
1660 Print( &stream );
1661 return _errorID;
1665 XMLError XMLDocument::Parse( const char* p, size_t len )
1667 const char* start = p;
1668 Clear();
1670 if ( !p || !*p ) {
1671 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1672 return _errorID;
1674 if ( len == (size_t)(-1) ) {
1675 len = strlen( p );
1677 _charBuffer = new char[ len+1 ];
1678 memcpy( _charBuffer, p, len );
1679 _charBuffer[len] = 0;
1681 p = XMLUtil::SkipWhiteSpace( p );
1682 p = XMLUtil::ReadBOM( p, &_writeBOM );
1683 if ( !p || !*p ) {
1684 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1685 return _errorID;
1688 ptrdiff_t delta = p - start; // skip initial whitespace, BOM, etc.
1689 ParseDeep( _charBuffer+delta, 0 );
1690 return _errorID;
1694 void XMLDocument::Print( XMLPrinter* streamer ) const
1696 XMLPrinter stdStreamer( stdout );
1697 if ( !streamer ) {
1698 streamer = &stdStreamer;
1700 Accept( streamer );
1704 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
1706 _errorID = error;
1707 _errorStr1 = str1;
1708 _errorStr2 = str2;
1712 void XMLDocument::PrintError() const
1714 if ( _errorID ) {
1715 static const int LEN = 20;
1716 char buf1[LEN] = { 0 };
1717 char buf2[LEN] = { 0 };
1719 if ( _errorStr1 ) {
1720 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
1722 if ( _errorStr2 ) {
1723 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
1726 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1727 _errorID, buf1, buf2 );
1732 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
1733 _elementJustOpened( false ),
1734 _firstElement( true ),
1735 _fp( file ),
1736 _depth( depth ),
1737 _textDepth( -1 ),
1738 _processEntities( true ),
1739 _compactMode( compact )
1741 for( int i=0; i<ENTITY_RANGE; ++i ) {
1742 _entityFlag[i] = false;
1743 _restrictedEntityFlag[i] = false;
1745 for( int i=0; i<NUM_ENTITIES; ++i ) {
1746 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1747 if ( entities[i].value < ENTITY_RANGE ) {
1748 _entityFlag[ (int)entities[i].value ] = true;
1751 _restrictedEntityFlag[(int)'&'] = true;
1752 _restrictedEntityFlag[(int)'<'] = true;
1753 _restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
1754 _buffer.Push( 0 );
1758 void XMLPrinter::Print( const char* format, ... )
1760 va_list va;
1761 va_start( va, format );
1763 if ( _fp ) {
1764 vfprintf( _fp, format, va );
1766 else {
1767 // This seems brutally complex. Haven't figured out a better
1768 // way on windows.
1769 #ifdef _MSC_VER
1770 int len = -1;
1771 int expand = 1000;
1772 while ( len < 0 ) {
1773 len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va );
1774 if ( len < 0 ) {
1775 expand *= 3/2;
1776 _accumulator.PushArr( expand );
1779 char* p = _buffer.PushArr( len ) - 1;
1780 memcpy( p, _accumulator.Mem(), len+1 );
1781 #else
1782 int len = vsnprintf( 0, 0, format, va );
1783 // Close out and re-start the va-args
1784 va_end( va );
1785 va_start( va, format );
1786 char* p = _buffer.PushArr( len ) - 1;
1787 vsnprintf( p, len+1, format, va );
1788 #endif
1790 va_end( va );
1794 void XMLPrinter::PrintSpace( int depth )
1796 for( int i=0; i<depth; ++i ) {
1797 Print( " " );
1802 void XMLPrinter::PrintString( const char* p, bool restricted )
1804 // Look for runs of bytes between entities to print.
1805 const char* q = p;
1806 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
1808 if ( _processEntities ) {
1809 while ( *q ) {
1810 // Remember, char is sometimes signed. (How many times has that bitten me?)
1811 if ( *q > 0 && *q < ENTITY_RANGE ) {
1812 // Check for entities. If one is found, flush
1813 // the stream up until the entity, write the
1814 // entity, and keep looking.
1815 if ( flag[(unsigned)(*q)] ) {
1816 while ( p < q ) {
1817 Print( "%c", *p );
1818 ++p;
1820 for( int i=0; i<NUM_ENTITIES; ++i ) {
1821 if ( entities[i].value == *q ) {
1822 Print( "&%s;", entities[i].pattern );
1823 break;
1826 ++p;
1829 ++q;
1832 // Flush the remaining string. This will be the entire
1833 // string if an entity wasn't found.
1834 if ( !_processEntities || (q-p > 0) ) {
1835 Print( "%s", p );
1840 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
1842 if ( writeBOM ) {
1843 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1844 Print( "%s", bom );
1846 if ( writeDec ) {
1847 PushDeclaration( "xml version=\"1.0\"" );
1852 void XMLPrinter::OpenElement( const char* name )
1854 if ( _elementJustOpened ) {
1855 SealElement();
1857 _stack.Push( name );
1859 if ( _textDepth < 0 && !_firstElement && !_compactMode ) {
1860 Print( "\n" );
1862 if ( !_compactMode ) {
1863 PrintSpace( _depth );
1866 Print( "<%s", name );
1867 _elementJustOpened = true;
1868 _firstElement = false;
1869 ++_depth;
1873 void XMLPrinter::PushAttribute( const char* name, const char* value )
1875 TIXMLASSERT( _elementJustOpened );
1876 Print( " %s=\"", name );
1877 PrintString( value, false );
1878 Print( "\"" );
1882 void XMLPrinter::PushAttribute( const char* name, int v )
1884 char buf[BUF_SIZE];
1885 XMLUtil::ToStr( v, buf, BUF_SIZE );
1886 PushAttribute( name, buf );
1890 void XMLPrinter::PushAttribute( const char* name, unsigned v )
1892 char buf[BUF_SIZE];
1893 XMLUtil::ToStr( v, buf, BUF_SIZE );
1894 PushAttribute( name, buf );
1898 void XMLPrinter::PushAttribute( const char* name, bool v )
1900 char buf[BUF_SIZE];
1901 XMLUtil::ToStr( v, buf, BUF_SIZE );
1902 PushAttribute( name, buf );
1906 void XMLPrinter::PushAttribute( const char* name, double v )
1908 char buf[BUF_SIZE];
1909 XMLUtil::ToStr( v, buf, BUF_SIZE );
1910 PushAttribute( name, buf );
1914 void XMLPrinter::CloseElement()
1916 --_depth;
1917 const char* name = _stack.Pop();
1919 if ( _elementJustOpened ) {
1920 Print( "/>" );
1922 else {
1923 if ( _textDepth < 0 && !_compactMode) {
1924 Print( "\n" );
1925 PrintSpace( _depth );
1927 Print( "</%s>", name );
1930 if ( _textDepth == _depth ) {
1931 _textDepth = -1;
1933 if ( _depth == 0 && !_compactMode) {
1934 Print( "\n" );
1936 _elementJustOpened = false;
1940 void XMLPrinter::SealElement()
1942 _elementJustOpened = false;
1943 Print( ">" );
1947 void XMLPrinter::PushText( const char* text, bool cdata )
1949 _textDepth = _depth-1;
1951 if ( _elementJustOpened ) {
1952 SealElement();
1954 if ( cdata ) {
1955 Print( "<![CDATA[" );
1956 Print( "%s", text );
1957 Print( "]]>" );
1959 else {
1960 PrintString( text, true );
1964 void XMLPrinter::PushText( int value )
1966 char buf[BUF_SIZE];
1967 XMLUtil::ToStr( value, buf, BUF_SIZE );
1968 PushText( buf, false );
1972 void XMLPrinter::PushText( unsigned value )
1974 char buf[BUF_SIZE];
1975 XMLUtil::ToStr( value, buf, BUF_SIZE );
1976 PushText( buf, false );
1980 void XMLPrinter::PushText( bool value )
1982 char buf[BUF_SIZE];
1983 XMLUtil::ToStr( value, buf, BUF_SIZE );
1984 PushText( buf, false );
1988 void XMLPrinter::PushText( float value )
1990 char buf[BUF_SIZE];
1991 XMLUtil::ToStr( value, buf, BUF_SIZE );
1992 PushText( buf, false );
1996 void XMLPrinter::PushText( double value )
1998 char buf[BUF_SIZE];
1999 XMLUtil::ToStr( value, buf, BUF_SIZE );
2000 PushText( buf, false );
2004 void XMLPrinter::PushComment( const char* comment )
2006 if ( _elementJustOpened ) {
2007 SealElement();
2009 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2010 Print( "\n" );
2011 PrintSpace( _depth );
2013 _firstElement = false;
2014 Print( "<!--%s-->", comment );
2018 void XMLPrinter::PushDeclaration( const char* value )
2020 if ( _elementJustOpened ) {
2021 SealElement();
2023 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2024 Print( "\n" );
2025 PrintSpace( _depth );
2027 _firstElement = false;
2028 Print( "<?%s?>", value );
2032 void XMLPrinter::PushUnknown( const char* value )
2034 if ( _elementJustOpened ) {
2035 SealElement();
2037 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2038 Print( "\n" );
2039 PrintSpace( _depth );
2041 _firstElement = false;
2042 Print( "<!%s>", value );
2046 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2048 _processEntities = doc.ProcessEntities();
2049 if ( doc.HasBOM() ) {
2050 PushHeader( true, false );
2052 return true;
2056 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2058 OpenElement( element.Name() );
2059 while ( attribute ) {
2060 PushAttribute( attribute->Name(), attribute->Value() );
2061 attribute = attribute->Next();
2063 return true;
2067 bool XMLPrinter::VisitExit( const XMLElement& )
2069 CloseElement();
2070 return true;
2074 bool XMLPrinter::Visit( const XMLText& text )
2076 PushText( text.Value(), text.CData() );
2077 return true;
2081 bool XMLPrinter::Visit( const XMLComment& comment )
2083 PushComment( comment.Value() );
2084 return true;
2087 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2089 PushDeclaration( declaration.Value() );
2090 return true;
2094 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2096 PushUnknown( unknown.Value() );
2097 return true;
2100 } // namespace tinyxml2