1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
3 
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.
7 
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:
11 
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.
16 
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
19 
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
23 
24 #include "tinyxml2.h"
25 
26 #include <new>		// yes, this one new style header, is in the Android SDK.
27 #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
28 #   include <stddef.h>
29 #   include <stdarg.h>
30 #else
31 #   include <cstddef>
32 #   include <cstdarg>
33 #endif
34 
35 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
36 	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
37 	/*int _snprintf_s(
38 	   char *buffer,
39 	   size_t sizeOfBuffer,
40 	   size_t count,
41 	   const char *format [,
42 		  argument] ...
43 	);*/
TIXML_SNPRINTF(char * buffer,size_t size,const char * format,...)44 	static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
45 	{
46 		va_list va;
47 		va_start( va, format );
48 		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
49 		va_end( va );
50 		return result;
51 	}
52 
TIXML_VSNPRINTF(char * buffer,size_t size,const char * format,va_list va)53 	static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
54 	{
55 		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
56 		return result;
57 	}
58 
59 	#define TIXML_VSCPRINTF	_vscprintf
60 	#define TIXML_SSCANF	sscanf_s
61 #elif defined _MSC_VER
62 	// Microsoft Visual Studio 2003 and earlier or WinCE
63 	#define TIXML_SNPRINTF	_snprintf
64 	#define TIXML_VSNPRINTF _vsnprintf
65 	#define TIXML_SSCANF	sscanf
66 	#if (_MSC_VER < 1400 ) && (!defined WINCE)
67 		// Microsoft Visual Studio 2003 and not WinCE.
68 		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
69 	#else
70 		// Microsoft Visual Studio 2003 and earlier or WinCE.
TIXML_VSCPRINTF(const char * format,va_list va)71 		static inline int TIXML_VSCPRINTF( const char* format, va_list va )
72 		{
73 			int len = 512;
74 			for (;;) {
75 				len = len*2;
76 				char* str = new char[len]();
77 				const int required = _vsnprintf(str, len, format, va);
78 				delete[] str;
79 				if ( required != -1 ) {
80 					TIXMLASSERT( required >= 0 );
81 					len = required;
82 					break;
83 				}
84 			}
85 			TIXMLASSERT( len >= 0 );
86 			return len;
87 		}
88 	#endif
89 #else
90 	// GCC version 3 and higher
91 	//#warning( "Using sn* functions." )
92 	#define TIXML_SNPRINTF	snprintf
93 	#define TIXML_VSNPRINTF	vsnprintf
TIXML_VSCPRINTF(const char * format,va_list va)94 	static inline int TIXML_VSCPRINTF( const char* format, va_list va )
95 	{
96 		int len = vsnprintf( 0, 0, format, va );
97 		TIXMLASSERT( len >= 0 );
98 		return len;
99 	}
100 	#define TIXML_SSCANF   sscanf
101 #endif
102 
103 
104 static const char LINE_FEED				= (char)0x0a;			// all line endings are normalized to LF
105 static const char LF = LINE_FEED;
106 static const char CARRIAGE_RETURN		= (char)0x0d;			// CR gets filtered out
107 static const char CR = CARRIAGE_RETURN;
108 static const char SINGLE_QUOTE			= '\'';
109 static const char DOUBLE_QUOTE			= '\"';
110 
111 // Bunch of unicode info at:
112 //		http://www.unicode.org/faq/utf_bom.html
113 //	ef bb bf (Microsoft "lead bytes") - designates UTF-8
114 
115 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
116 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
117 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
118 
119 namespace tinyxml2
120 {
121 
122 struct Entity {
123     const char* pattern;
124     int length;
125     char value;
126 };
127 
128 static const int NUM_ENTITIES = 5;
129 static const Entity entities[NUM_ENTITIES] = {
130     { "quot", 4,	DOUBLE_QUOTE },
131     { "amp", 3,		'&'  },
132     { "apos", 4,	SINGLE_QUOTE },
133     { "lt",	2, 		'<'	 },
134     { "gt",	2,		'>'	 }
135 };
136 
137 
~StrPair()138 StrPair::~StrPair()
139 {
140     Reset();
141 }
142 
143 
TransferTo(StrPair * other)144 void StrPair::TransferTo( StrPair* other )
145 {
146     if ( this == other ) {
147         return;
148     }
149     // This in effect implements the assignment operator by "moving"
150     // ownership (as in auto_ptr).
151 
152     TIXMLASSERT( other->_flags == 0 );
153     TIXMLASSERT( other->_start == 0 );
154     TIXMLASSERT( other->_end == 0 );
155 
156     other->Reset();
157 
158     other->_flags = _flags;
159     other->_start = _start;
160     other->_end = _end;
161 
162     _flags = 0;
163     _start = 0;
164     _end = 0;
165 }
166 
Reset()167 void StrPair::Reset()
168 {
169     if ( _flags & NEEDS_DELETE ) {
170         delete [] _start;
171     }
172     _flags = 0;
173     _start = 0;
174     _end = 0;
175 }
176 
177 
SetStr(const char * str,int flags)178 void StrPair::SetStr( const char* str, int flags )
179 {
180     TIXMLASSERT( str );
181     Reset();
182     size_t len = strlen( str );
183     TIXMLASSERT( _start == 0 );
184     _start = new char[ len+1 ];
185     memcpy( _start, str, len+1 );
186     _end = _start + len;
187     _flags = flags | NEEDS_DELETE;
188 }
189 
190 
ParseText(char * p,const char * endTag,int strFlags)191 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
192 {
193     TIXMLASSERT( endTag && *endTag );
194 
195     char* start = p;
196     char  endChar = *endTag;
197     size_t length = strlen( endTag );
198 
199     // Inner loop of text parsing.
200     while ( *p ) {
201         if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
202             Set( start, p, strFlags );
203             return p + length;
204         }
205         ++p;
206     }
207     return 0;
208 }
209 
210 
ParseName(char * p)211 char* StrPair::ParseName( char* p )
212 {
213     if ( !p || !(*p) ) {
214         return 0;
215     }
216     if ( !XMLUtil::IsNameStartChar( *p ) ) {
217         return 0;
218     }
219 
220     char* const start = p;
221     ++p;
222     while ( *p && XMLUtil::IsNameChar( *p ) ) {
223         ++p;
224     }
225 
226     Set( start, p, 0 );
227     return p;
228 }
229 
230 
CollapseWhitespace()231 void StrPair::CollapseWhitespace()
232 {
233     // Adjusting _start would cause undefined behavior on delete[]
234     TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
235     // Trim leading space.
236     _start = XMLUtil::SkipWhiteSpace( _start );
237 
238     if ( *_start ) {
239         char* p = _start;	// the read pointer
240         char* q = _start;	// the write pointer
241 
242         while( *p ) {
243             if ( XMLUtil::IsWhiteSpace( *p )) {
244                 p = XMLUtil::SkipWhiteSpace( p );
245                 if ( *p == 0 ) {
246                     break;    // don't write to q; this trims the trailing space.
247                 }
248                 *q = ' ';
249                 ++q;
250             }
251             *q = *p;
252             ++q;
253             ++p;
254         }
255         *q = 0;
256     }
257 }
258 
259 
GetStr()260 const char* StrPair::GetStr()
261 {
262     TIXMLASSERT( _start );
263     TIXMLASSERT( _end );
264     if ( _flags & NEEDS_FLUSH ) {
265         *_end = 0;
266         _flags ^= NEEDS_FLUSH;
267 
268         if ( _flags ) {
269             char* p = _start;	// the read pointer
270             char* q = _start;	// the write pointer
271 
272             while( p < _end ) {
273                 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
274                     // CR-LF pair becomes LF
275                     // CR alone becomes LF
276                     // LF-CR becomes LF
277                     if ( *(p+1) == LF ) {
278                         p += 2;
279                     }
280                     else {
281                         ++p;
282                     }
283                     *q++ = LF;
284                 }
285                 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
286                     if ( *(p+1) == CR ) {
287                         p += 2;
288                     }
289                     else {
290                         ++p;
291                     }
292                     *q++ = LF;
293                 }
294                 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
295                     // Entities handled by tinyXML2:
296                     // - special entities in the entity table [in/out]
297                     // - numeric character reference [in]
298                     //   &#20013; or &#x4e2d;
299 
300                     if ( *(p+1) == '#' ) {
301                         const int buflen = 10;
302                         char buf[buflen] = { 0 };
303                         int len = 0;
304                         char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
305                         if ( adjusted == 0 ) {
306                             *q = *p;
307                             ++p;
308                             ++q;
309                         }
310                         else {
311                             TIXMLASSERT( 0 <= len && len <= buflen );
312                             TIXMLASSERT( q + len <= adjusted );
313                             p = adjusted;
314                             memcpy( q, buf, len );
315                             q += len;
316                         }
317                     }
318                     else {
319                         bool entityFound = false;
320                         for( int i = 0; i < NUM_ENTITIES; ++i ) {
321                             const Entity& entity = entities[i];
322                             if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
323                                     && *( p + entity.length + 1 ) == ';' ) {
324                                 // Found an entity - convert.
325                                 *q = entity.value;
326                                 ++q;
327                                 p += entity.length + 2;
328                                 entityFound = true;
329                                 break;
330                             }
331                         }
332                         if ( !entityFound ) {
333                             // fixme: treat as error?
334                             ++p;
335                             ++q;
336                         }
337                     }
338                 }
339                 else {
340                     *q = *p;
341                     ++p;
342                     ++q;
343                 }
344             }
345             *q = 0;
346         }
347         // The loop below has plenty going on, and this
348         // is a less useful mode. Break it out.
349         if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
350             CollapseWhitespace();
351         }
352         _flags = (_flags & NEEDS_DELETE);
353     }
354     TIXMLASSERT( _start );
355     return _start;
356 }
357 
358 
359 
360 
361 // --------- XMLUtil ----------- //
362 
ReadBOM(const char * p,bool * bom)363 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
364 {
365     TIXMLASSERT( p );
366     TIXMLASSERT( bom );
367     *bom = false;
368     const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
369     // Check for BOM:
370     if (    *(pu+0) == TIXML_UTF_LEAD_0
371             && *(pu+1) == TIXML_UTF_LEAD_1
372             && *(pu+2) == TIXML_UTF_LEAD_2 ) {
373         *bom = true;
374         p += 3;
375     }
376     TIXMLASSERT( p );
377     return p;
378 }
379 
380 
ConvertUTF32ToUTF8(unsigned long input,char * output,int * length)381 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
382 {
383     const unsigned long BYTE_MASK = 0xBF;
384     const unsigned long BYTE_MARK = 0x80;
385     const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
386 
387     if (input < 0x80) {
388         *length = 1;
389     }
390     else if ( input < 0x800 ) {
391         *length = 2;
392     }
393     else if ( input < 0x10000 ) {
394         *length = 3;
395     }
396     else if ( input < 0x200000 ) {
397         *length = 4;
398     }
399     else {
400         *length = 0;    // This code won't convert this correctly anyway.
401         return;
402     }
403 
404     output += *length;
405 
406     // Scary scary fall throughs.
407     switch (*length) {
408         case 4:
409             --output;
410             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
411             input >>= 6;
412         case 3:
413             --output;
414             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
415             input >>= 6;
416         case 2:
417             --output;
418             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
419             input >>= 6;
420         case 1:
421             --output;
422             *output = (char)(input | FIRST_BYTE_MARK[*length]);
423             break;
424         default:
425             TIXMLASSERT( false );
426     }
427 }
428 
429 
GetCharacterRef(const char * p,char * value,int * length)430 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
431 {
432     // Presume an entity, and pull it out.
433     *length = 0;
434 
435     if ( *(p+1) == '#' && *(p+2) ) {
436         unsigned long ucs = 0;
437         TIXMLASSERT( sizeof( ucs ) >= 4 );
438         ptrdiff_t delta = 0;
439         unsigned mult = 1;
440         static const char SEMICOLON = ';';
441 
442         if ( *(p+2) == 'x' ) {
443             // Hexadecimal.
444             const char* q = p+3;
445             if ( !(*q) ) {
446                 return 0;
447             }
448 
449             q = strchr( q, SEMICOLON );
450 
451             if ( !q ) {
452                 return 0;
453             }
454             TIXMLASSERT( *q == SEMICOLON );
455 
456             delta = q-p;
457             --q;
458 
459             while ( *q != 'x' ) {
460                 unsigned int digit = 0;
461 
462                 if ( *q >= '0' && *q <= '9' ) {
463                     digit = *q - '0';
464                 }
465                 else if ( *q >= 'a' && *q <= 'f' ) {
466                     digit = *q - 'a' + 10;
467                 }
468                 else if ( *q >= 'A' && *q <= 'F' ) {
469                     digit = *q - 'A' + 10;
470                 }
471                 else {
472                     return 0;
473                 }
474                 TIXMLASSERT( digit < 16 );
475                 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
476                 const unsigned int digitScaled = mult * digit;
477                 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
478                 ucs += digitScaled;
479                 TIXMLASSERT( mult <= UINT_MAX / 16 );
480                 mult *= 16;
481                 --q;
482             }
483         }
484         else {
485             // Decimal.
486             const char* q = p+2;
487             if ( !(*q) ) {
488                 return 0;
489             }
490 
491             q = strchr( q, SEMICOLON );
492 
493             if ( !q ) {
494                 return 0;
495             }
496             TIXMLASSERT( *q == SEMICOLON );
497 
498             delta = q-p;
499             --q;
500 
501             while ( *q != '#' ) {
502                 if ( *q >= '0' && *q <= '9' ) {
503                     const unsigned int digit = *q - '0';
504                     TIXMLASSERT( digit < 10 );
505                     TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
506                     const unsigned int digitScaled = mult * digit;
507                     TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
508                     ucs += digitScaled;
509                 }
510                 else {
511                     return 0;
512                 }
513                 TIXMLASSERT( mult <= UINT_MAX / 10 );
514                 mult *= 10;
515                 --q;
516             }
517         }
518         // convert the UCS to UTF-8
519         ConvertUTF32ToUTF8( ucs, value, length );
520         return p + delta + 1;
521     }
522     return p+1;
523 }
524 
525 
ToStr(int v,char * buffer,int bufferSize)526 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
527 {
528     TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
529 }
530 
531 
ToStr(unsigned v,char * buffer,int bufferSize)532 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
533 {
534     TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
535 }
536 
537 
ToStr(bool v,char * buffer,int bufferSize)538 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
539 {
540     TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
541 }
542 
543 /*
544 	ToStr() of a number is a very tricky topic.
545 	https://github.com/leethomason/tinyxml2/issues/106
546 */
ToStr(float v,char * buffer,int bufferSize)547 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
548 {
549     TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
550 }
551 
552 
ToStr(double v,char * buffer,int bufferSize)553 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
554 {
555     TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
556 }
557 
558 
ToInt(const char * str,int * value)559 bool XMLUtil::ToInt( const char* str, int* value )
560 {
561     if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
562         return true;
563     }
564     return false;
565 }
566 
ToUnsigned(const char * str,unsigned * value)567 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
568 {
569     if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
570         return true;
571     }
572     return false;
573 }
574 
ToBool(const char * str,bool * value)575 bool XMLUtil::ToBool( const char* str, bool* value )
576 {
577     int ival = 0;
578     if ( ToInt( str, &ival )) {
579         *value = (ival==0) ? false : true;
580         return true;
581     }
582     if ( StringEqual( str, "true" ) ) {
583         *value = true;
584         return true;
585     }
586     else if ( StringEqual( str, "false" ) ) {
587         *value = false;
588         return true;
589     }
590     return false;
591 }
592 
593 
ToFloat(const char * str,float * value)594 bool XMLUtil::ToFloat( const char* str, float* value )
595 {
596     if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
597         return true;
598     }
599     return false;
600 }
601 
ToDouble(const char * str,double * value)602 bool XMLUtil::ToDouble( const char* str, double* value )
603 {
604     if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
605         return true;
606     }
607     return false;
608 }
609 
610 
Identify(char * p,XMLNode ** node)611 char* XMLDocument::Identify( char* p, XMLNode** node )
612 {
613     TIXMLASSERT( node );
614     TIXMLASSERT( p );
615     char* const start = p;
616     p = XMLUtil::SkipWhiteSpace( p );
617     if( !*p ) {
618         *node = 0;
619         TIXMLASSERT( p );
620         return p;
621     }
622 
623     // These strings define the matching patterns:
624     static const char* xmlHeader		= { "<?" };
625     static const char* commentHeader	= { "<!--" };
626     static const char* cdataHeader		= { "<![CDATA[" };
627     static const char* dtdHeader		= { "<!" };
628     static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
629 
630     static const int xmlHeaderLen		= 2;
631     static const int commentHeaderLen	= 4;
632     static const int cdataHeaderLen		= 9;
633     static const int dtdHeaderLen		= 2;
634     static const int elementHeaderLen	= 1;
635 
636     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
637     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
638     XMLNode* returnNode = 0;
639     if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
640         TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
641         returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
642         returnNode->_memPool = &_commentPool;
643         p += xmlHeaderLen;
644     }
645     else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
646         TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
647         returnNode = new (_commentPool.Alloc()) XMLComment( this );
648         returnNode->_memPool = &_commentPool;
649         p += commentHeaderLen;
650     }
651     else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
652         TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
653         XMLText* text = new (_textPool.Alloc()) XMLText( this );
654         returnNode = text;
655         returnNode->_memPool = &_textPool;
656         p += cdataHeaderLen;
657         text->SetCData( true );
658     }
659     else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
660         TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
661         returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
662         returnNode->_memPool = &_commentPool;
663         p += dtdHeaderLen;
664     }
665     else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
666         TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
667         returnNode = new (_elementPool.Alloc()) XMLElement( this );
668         returnNode->_memPool = &_elementPool;
669         p += elementHeaderLen;
670     }
671     else {
672         TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
673         returnNode = new (_textPool.Alloc()) XMLText( this );
674         returnNode->_memPool = &_textPool;
675         p = start;	// Back it up, all the text counts.
676     }
677 
678     TIXMLASSERT( returnNode );
679     TIXMLASSERT( p );
680     *node = returnNode;
681     return p;
682 }
683 
684 
Accept(XMLVisitor * visitor) const685 bool XMLDocument::Accept( XMLVisitor* visitor ) const
686 {
687     TIXMLASSERT( visitor );
688     if ( visitor->VisitEnter( *this ) ) {
689         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
690             if ( !node->Accept( visitor ) ) {
691                 break;
692             }
693         }
694     }
695     return visitor->VisitExit( *this );
696 }
697 
698 
699 // --------- XMLNode ----------- //
700 
XMLNode(XMLDocument * doc)701 XMLNode::XMLNode( XMLDocument* doc ) :
702     _document( doc ),
703     _parent( 0 ),
704     _firstChild( 0 ), _lastChild( 0 ),
705     _prev( 0 ), _next( 0 ),
706     _memPool( 0 )
707 {
708 }
709 
710 
~XMLNode()711 XMLNode::~XMLNode()
712 {
713     DeleteChildren();
714     if ( _parent ) {
715         _parent->Unlink( this );
716     }
717 }
718 
Value() const719 const char* XMLNode::Value() const
720 {
721     // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.
722     if ( this->ToDocument() )
723         return 0;
724     return _value.GetStr();
725 }
726 
SetValue(const char * str,bool staticMem)727 void XMLNode::SetValue( const char* str, bool staticMem )
728 {
729     if ( staticMem ) {
730         _value.SetInternedStr( str );
731     }
732     else {
733         _value.SetStr( str );
734     }
735 }
736 
737 
DeleteChildren()738 void XMLNode::DeleteChildren()
739 {
740     while( _firstChild ) {
741         TIXMLASSERT( _lastChild );
742         TIXMLASSERT( _firstChild->_document == _document );
743         XMLNode* node = _firstChild;
744         Unlink( node );
745 
746         DeleteNode( node );
747     }
748     _firstChild = _lastChild = 0;
749 }
750 
751 
Unlink(XMLNode * child)752 void XMLNode::Unlink( XMLNode* child )
753 {
754     TIXMLASSERT( child );
755     TIXMLASSERT( child->_document == _document );
756     TIXMLASSERT( child->_parent == this );
757     if ( child == _firstChild ) {
758         _firstChild = _firstChild->_next;
759     }
760     if ( child == _lastChild ) {
761         _lastChild = _lastChild->_prev;
762     }
763 
764     if ( child->_prev ) {
765         child->_prev->_next = child->_next;
766     }
767     if ( child->_next ) {
768         child->_next->_prev = child->_prev;
769     }
770 	child->_parent = 0;
771 }
772 
773 
DeleteChild(XMLNode * node)774 void XMLNode::DeleteChild( XMLNode* node )
775 {
776     TIXMLASSERT( node );
777     TIXMLASSERT( node->_document == _document );
778     TIXMLASSERT( node->_parent == this );
779     Unlink( node );
780     DeleteNode( node );
781 }
782 
783 
InsertEndChild(XMLNode * addThis)784 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
785 {
786     TIXMLASSERT( addThis );
787     if ( addThis->_document != _document ) {
788         TIXMLASSERT( false );
789         return 0;
790     }
791     InsertChildPreamble( addThis );
792 
793     if ( _lastChild ) {
794         TIXMLASSERT( _firstChild );
795         TIXMLASSERT( _lastChild->_next == 0 );
796         _lastChild->_next = addThis;
797         addThis->_prev = _lastChild;
798         _lastChild = addThis;
799 
800         addThis->_next = 0;
801     }
802     else {
803         TIXMLASSERT( _firstChild == 0 );
804         _firstChild = _lastChild = addThis;
805 
806         addThis->_prev = 0;
807         addThis->_next = 0;
808     }
809     addThis->_parent = this;
810     return addThis;
811 }
812 
813 
InsertFirstChild(XMLNode * addThis)814 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
815 {
816     TIXMLASSERT( addThis );
817     if ( addThis->_document != _document ) {
818         TIXMLASSERT( false );
819         return 0;
820     }
821     InsertChildPreamble( addThis );
822 
823     if ( _firstChild ) {
824         TIXMLASSERT( _lastChild );
825         TIXMLASSERT( _firstChild->_prev == 0 );
826 
827         _firstChild->_prev = addThis;
828         addThis->_next = _firstChild;
829         _firstChild = addThis;
830 
831         addThis->_prev = 0;
832     }
833     else {
834         TIXMLASSERT( _lastChild == 0 );
835         _firstChild = _lastChild = addThis;
836 
837         addThis->_prev = 0;
838         addThis->_next = 0;
839     }
840     addThis->_parent = this;
841     return addThis;
842 }
843 
844 
InsertAfterChild(XMLNode * afterThis,XMLNode * addThis)845 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
846 {
847     TIXMLASSERT( addThis );
848     if ( addThis->_document != _document ) {
849         TIXMLASSERT( false );
850         return 0;
851     }
852 
853     TIXMLASSERT( afterThis );
854 
855     if ( afterThis->_parent != this ) {
856         TIXMLASSERT( false );
857         return 0;
858     }
859 
860     if ( afterThis->_next == 0 ) {
861         // The last node or the only node.
862         return InsertEndChild( addThis );
863     }
864     InsertChildPreamble( addThis );
865     addThis->_prev = afterThis;
866     addThis->_next = afterThis->_next;
867     afterThis->_next->_prev = addThis;
868     afterThis->_next = addThis;
869     addThis->_parent = this;
870     return addThis;
871 }
872 
873 
874 
875 
FirstChildElement(const char * name) const876 const XMLElement* XMLNode::FirstChildElement( const char* name ) const
877 {
878     for( const XMLNode* node = _firstChild; node; node = node->_next ) {
879         const XMLElement* element = node->ToElement();
880         if ( element ) {
881             if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
882                 return element;
883             }
884         }
885     }
886     return 0;
887 }
888 
889 
LastChildElement(const char * name) const890 const XMLElement* XMLNode::LastChildElement( const char* name ) const
891 {
892     for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
893         const XMLElement* element = node->ToElement();
894         if ( element ) {
895             if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
896                 return element;
897             }
898         }
899     }
900     return 0;
901 }
902 
903 
NextSiblingElement(const char * name) const904 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
905 {
906     for( const XMLNode* node = _next; node; node = node->_next ) {
907         const XMLElement* element = node->ToElement();
908         if ( element
909                 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
910             return element;
911         }
912     }
913     return 0;
914 }
915 
916 
PreviousSiblingElement(const char * name) const917 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
918 {
919     for( const XMLNode* node = _prev; node; node = node->_prev ) {
920         const XMLElement* element = node->ToElement();
921         if ( element
922                 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
923             return element;
924         }
925     }
926     return 0;
927 }
928 
929 
ParseDeep(char * p,StrPair * parentEnd)930 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
931 {
932     // This is a recursive method, but thinking about it "at the current level"
933     // it is a pretty simple flat list:
934     //		<foo/>
935     //		<!-- comment -->
936     //
937     // With a special case:
938     //		<foo>
939     //		</foo>
940     //		<!-- comment -->
941     //
942     // Where the closing element (/foo) *must* be the next thing after the opening
943     // element, and the names must match. BUT the tricky bit is that the closing
944     // element will be read by the child.
945     //
946     // 'endTag' is the end tag for this node, it is returned by a call to a child.
947     // 'parentEnd' is the end tag for the parent, which is filled in and returned.
948 
949     while( p && *p ) {
950         XMLNode* node = 0;
951 
952         p = _document->Identify( p, &node );
953         if ( node == 0 ) {
954             break;
955         }
956 
957         StrPair endTag;
958         p = node->ParseDeep( p, &endTag );
959         if ( !p ) {
960             DeleteNode( node );
961             if ( !_document->Error() ) {
962                 _document->SetError( XML_ERROR_PARSING, 0, 0 );
963             }
964             break;
965         }
966 
967         XMLDeclaration* decl = node->ToDeclaration();
968         if ( decl ) {
969                 // A declaration can only be the first child of a document.
970                 // Set error, if document already has children.
971                 if ( !_document->NoChildren() ) {
972                         _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);
973                         DeleteNode( decl );
974                         break;
975                 }
976         }
977 
978         XMLElement* ele = node->ToElement();
979         if ( ele ) {
980             // We read the end tag. Return it to the parent.
981             if ( ele->ClosingType() == XMLElement::CLOSING ) {
982                 if ( parentEnd ) {
983                     ele->_value.TransferTo( parentEnd );
984                 }
985                 node->_memPool->SetTracked();   // created and then immediately deleted.
986                 DeleteNode( node );
987                 return p;
988             }
989 
990             // Handle an end tag returned to this level.
991             // And handle a bunch of annoying errors.
992             bool mismatch = false;
993             if ( endTag.Empty() ) {
994                 if ( ele->ClosingType() == XMLElement::OPEN ) {
995                     mismatch = true;
996                 }
997             }
998             else {
999                 if ( ele->ClosingType() != XMLElement::OPEN ) {
1000                     mismatch = true;
1001                 }
1002                 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1003                     mismatch = true;
1004                 }
1005             }
1006             if ( mismatch ) {
1007                 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );
1008                 DeleteNode( node );
1009                 break;
1010             }
1011         }
1012         InsertEndChild( node );
1013     }
1014     return 0;
1015 }
1016 
DeleteNode(XMLNode * node)1017 void XMLNode::DeleteNode( XMLNode* node )
1018 {
1019     if ( node == 0 ) {
1020         return;
1021     }
1022     MemPool* pool = node->_memPool;
1023     node->~XMLNode();
1024     pool->Free( node );
1025 }
1026 
InsertChildPreamble(XMLNode * insertThis) const1027 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1028 {
1029     TIXMLASSERT( insertThis );
1030     TIXMLASSERT( insertThis->_document == _document );
1031 
1032     if ( insertThis->_parent )
1033         insertThis->_parent->Unlink( insertThis );
1034     else
1035         insertThis->_memPool->SetTracked();
1036 }
1037 
1038 // --------- XMLText ---------- //
ParseDeep(char * p,StrPair *)1039 char* XMLText::ParseDeep( char* p, StrPair* )
1040 {
1041     const char* start = p;
1042     if ( this->CData() ) {
1043         p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1044         if ( !p ) {
1045             _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
1046         }
1047         return p;
1048     }
1049     else {
1050         int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1051         if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1052             flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1053         }
1054 
1055         p = _value.ParseText( p, "<", flags );
1056         if ( p && *p ) {
1057             return p-1;
1058         }
1059         if ( !p ) {
1060             _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
1061         }
1062     }
1063     return 0;
1064 }
1065 
1066 
ShallowClone(XMLDocument * doc) const1067 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1068 {
1069     if ( !doc ) {
1070         doc = _document;
1071     }
1072     XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
1073     text->SetCData( this->CData() );
1074     return text;
1075 }
1076 
1077 
ShallowEqual(const XMLNode * compare) const1078 bool XMLText::ShallowEqual( const XMLNode* compare ) const
1079 {
1080     const XMLText* text = compare->ToText();
1081     return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1082 }
1083 
1084 
Accept(XMLVisitor * visitor) const1085 bool XMLText::Accept( XMLVisitor* visitor ) const
1086 {
1087     TIXMLASSERT( visitor );
1088     return visitor->Visit( *this );
1089 }
1090 
1091 
1092 // --------- XMLComment ---------- //
1093 
XMLComment(XMLDocument * doc)1094 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1095 {
1096 }
1097 
1098 
~XMLComment()1099 XMLComment::~XMLComment()
1100 {
1101 }
1102 
1103 
ParseDeep(char * p,StrPair *)1104 char* XMLComment::ParseDeep( char* p, StrPair* )
1105 {
1106     // Comment parses as text.
1107     const char* start = p;
1108     p = _value.ParseText( p, "-->", StrPair::COMMENT );
1109     if ( p == 0 ) {
1110         _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
1111     }
1112     return p;
1113 }
1114 
1115 
ShallowClone(XMLDocument * doc) const1116 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1117 {
1118     if ( !doc ) {
1119         doc = _document;
1120     }
1121     XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
1122     return comment;
1123 }
1124 
1125 
ShallowEqual(const XMLNode * compare) const1126 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1127 {
1128     TIXMLASSERT( compare );
1129     const XMLComment* comment = compare->ToComment();
1130     return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1131 }
1132 
1133 
Accept(XMLVisitor * visitor) const1134 bool XMLComment::Accept( XMLVisitor* visitor ) const
1135 {
1136     TIXMLASSERT( visitor );
1137     return visitor->Visit( *this );
1138 }
1139 
1140 
1141 // --------- XMLDeclaration ---------- //
1142 
XMLDeclaration(XMLDocument * doc)1143 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1144 {
1145 }
1146 
1147 
~XMLDeclaration()1148 XMLDeclaration::~XMLDeclaration()
1149 {
1150     //printf( "~XMLDeclaration\n" );
1151 }
1152 
1153 
ParseDeep(char * p,StrPair *)1154 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
1155 {
1156     // Declaration parses as text.
1157     const char* start = p;
1158     p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1159     if ( p == 0 ) {
1160         _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
1161     }
1162     return p;
1163 }
1164 
1165 
ShallowClone(XMLDocument * doc) const1166 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1167 {
1168     if ( !doc ) {
1169         doc = _document;
1170     }
1171     XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
1172     return dec;
1173 }
1174 
1175 
ShallowEqual(const XMLNode * compare) const1176 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1177 {
1178     TIXMLASSERT( compare );
1179     const XMLDeclaration* declaration = compare->ToDeclaration();
1180     return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1181 }
1182 
1183 
1184 
Accept(XMLVisitor * visitor) const1185 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1186 {
1187     TIXMLASSERT( visitor );
1188     return visitor->Visit( *this );
1189 }
1190 
1191 // --------- XMLUnknown ---------- //
1192 
XMLUnknown(XMLDocument * doc)1193 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1194 {
1195 }
1196 
1197 
~XMLUnknown()1198 XMLUnknown::~XMLUnknown()
1199 {
1200 }
1201 
1202 
ParseDeep(char * p,StrPair *)1203 char* XMLUnknown::ParseDeep( char* p, StrPair* )
1204 {
1205     // Unknown parses as text.
1206     const char* start = p;
1207 
1208     p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1209     if ( !p ) {
1210         _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1211     }
1212     return p;
1213 }
1214 
1215 
ShallowClone(XMLDocument * doc) const1216 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1217 {
1218     if ( !doc ) {
1219         doc = _document;
1220     }
1221     XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
1222     return text;
1223 }
1224 
1225 
ShallowEqual(const XMLNode * compare) const1226 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1227 {
1228     TIXMLASSERT( compare );
1229     const XMLUnknown* unknown = compare->ToUnknown();
1230     return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1231 }
1232 
1233 
Accept(XMLVisitor * visitor) const1234 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1235 {
1236     TIXMLASSERT( visitor );
1237     return visitor->Visit( *this );
1238 }
1239 
1240 // --------- XMLAttribute ---------- //
1241 
Name() const1242 const char* XMLAttribute::Name() const
1243 {
1244     return _name.GetStr();
1245 }
1246 
Value() const1247 const char* XMLAttribute::Value() const
1248 {
1249     return _value.GetStr();
1250 }
1251 
ParseDeep(char * p,bool processEntities)1252 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1253 {
1254     // Parse using the name rules: bug fix, was using ParseText before
1255     p = _name.ParseName( p );
1256     if ( !p || !*p ) {
1257         return 0;
1258     }
1259 
1260     // Skip white space before =
1261     p = XMLUtil::SkipWhiteSpace( p );
1262     if ( *p != '=' ) {
1263         return 0;
1264     }
1265 
1266     ++p;	// move up to opening quote
1267     p = XMLUtil::SkipWhiteSpace( p );
1268     if ( *p != '\"' && *p != '\'' ) {
1269         return 0;
1270     }
1271 
1272     char endTag[2] = { *p, 0 };
1273     ++p;	// move past opening quote
1274 
1275     p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1276     return p;
1277 }
1278 
1279 
SetName(const char * n)1280 void XMLAttribute::SetName( const char* n )
1281 {
1282     _name.SetStr( n );
1283 }
1284 
1285 
QueryIntValue(int * value) const1286 XMLError XMLAttribute::QueryIntValue( int* value ) const
1287 {
1288     if ( XMLUtil::ToInt( Value(), value )) {
1289         return XML_NO_ERROR;
1290     }
1291     return XML_WRONG_ATTRIBUTE_TYPE;
1292 }
1293 
1294 
QueryUnsignedValue(unsigned int * value) const1295 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1296 {
1297     if ( XMLUtil::ToUnsigned( Value(), value )) {
1298         return XML_NO_ERROR;
1299     }
1300     return XML_WRONG_ATTRIBUTE_TYPE;
1301 }
1302 
1303 
QueryBoolValue(bool * value) const1304 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1305 {
1306     if ( XMLUtil::ToBool( Value(), value )) {
1307         return XML_NO_ERROR;
1308     }
1309     return XML_WRONG_ATTRIBUTE_TYPE;
1310 }
1311 
1312 
QueryFloatValue(float * value) const1313 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1314 {
1315     if ( XMLUtil::ToFloat( Value(), value )) {
1316         return XML_NO_ERROR;
1317     }
1318     return XML_WRONG_ATTRIBUTE_TYPE;
1319 }
1320 
1321 
QueryDoubleValue(double * value) const1322 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1323 {
1324     if ( XMLUtil::ToDouble( Value(), value )) {
1325         return XML_NO_ERROR;
1326     }
1327     return XML_WRONG_ATTRIBUTE_TYPE;
1328 }
1329 
1330 
SetAttribute(const char * v)1331 void XMLAttribute::SetAttribute( const char* v )
1332 {
1333     _value.SetStr( v );
1334 }
1335 
1336 
SetAttribute(int v)1337 void XMLAttribute::SetAttribute( int v )
1338 {
1339     char buf[BUF_SIZE];
1340     XMLUtil::ToStr( v, buf, BUF_SIZE );
1341     _value.SetStr( buf );
1342 }
1343 
1344 
SetAttribute(unsigned v)1345 void XMLAttribute::SetAttribute( unsigned v )
1346 {
1347     char buf[BUF_SIZE];
1348     XMLUtil::ToStr( v, buf, BUF_SIZE );
1349     _value.SetStr( buf );
1350 }
1351 
1352 
SetAttribute(bool v)1353 void XMLAttribute::SetAttribute( bool v )
1354 {
1355     char buf[BUF_SIZE];
1356     XMLUtil::ToStr( v, buf, BUF_SIZE );
1357     _value.SetStr( buf );
1358 }
1359 
SetAttribute(double v)1360 void XMLAttribute::SetAttribute( double v )
1361 {
1362     char buf[BUF_SIZE];
1363     XMLUtil::ToStr( v, buf, BUF_SIZE );
1364     _value.SetStr( buf );
1365 }
1366 
SetAttribute(float v)1367 void XMLAttribute::SetAttribute( float v )
1368 {
1369     char buf[BUF_SIZE];
1370     XMLUtil::ToStr( v, buf, BUF_SIZE );
1371     _value.SetStr( buf );
1372 }
1373 
1374 
1375 // --------- XMLElement ---------- //
XMLElement(XMLDocument * doc)1376 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1377     _closingType( 0 ),
1378     _rootAttribute( 0 )
1379 {
1380 }
1381 
1382 
~XMLElement()1383 XMLElement::~XMLElement()
1384 {
1385     while( _rootAttribute ) {
1386         XMLAttribute* next = _rootAttribute->_next;
1387         DeleteAttribute( _rootAttribute );
1388         _rootAttribute = next;
1389     }
1390 }
1391 
1392 
FindAttribute(const char * name) const1393 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1394 {
1395     for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1396         if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1397             return a;
1398         }
1399     }
1400     return 0;
1401 }
1402 
1403 
Attribute(const char * name,const char * value) const1404 const char* XMLElement::Attribute( const char* name, const char* value ) const
1405 {
1406     const XMLAttribute* a = FindAttribute( name );
1407     if ( !a ) {
1408         return 0;
1409     }
1410     if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1411         return a->Value();
1412     }
1413     return 0;
1414 }
1415 
1416 
GetText() const1417 const char* XMLElement::GetText() const
1418 {
1419     if ( FirstChild() && FirstChild()->ToText() ) {
1420         return FirstChild()->Value();
1421     }
1422     return 0;
1423 }
1424 
1425 
SetText(const char * inText)1426 void	XMLElement::SetText( const char* inText )
1427 {
1428 	if ( FirstChild() && FirstChild()->ToText() )
1429 		FirstChild()->SetValue( inText );
1430 	else {
1431 		XMLText*	theText = GetDocument()->NewText( inText );
1432 		InsertFirstChild( theText );
1433 	}
1434 }
1435 
1436 
SetText(int v)1437 void XMLElement::SetText( int v )
1438 {
1439     char buf[BUF_SIZE];
1440     XMLUtil::ToStr( v, buf, BUF_SIZE );
1441     SetText( buf );
1442 }
1443 
1444 
SetText(unsigned v)1445 void XMLElement::SetText( unsigned v )
1446 {
1447     char buf[BUF_SIZE];
1448     XMLUtil::ToStr( v, buf, BUF_SIZE );
1449     SetText( buf );
1450 }
1451 
1452 
SetText(bool v)1453 void XMLElement::SetText( bool v )
1454 {
1455     char buf[BUF_SIZE];
1456     XMLUtil::ToStr( v, buf, BUF_SIZE );
1457     SetText( buf );
1458 }
1459 
1460 
SetText(float v)1461 void XMLElement::SetText( float v )
1462 {
1463     char buf[BUF_SIZE];
1464     XMLUtil::ToStr( v, buf, BUF_SIZE );
1465     SetText( buf );
1466 }
1467 
1468 
SetText(double v)1469 void XMLElement::SetText( double v )
1470 {
1471     char buf[BUF_SIZE];
1472     XMLUtil::ToStr( v, buf, BUF_SIZE );
1473     SetText( buf );
1474 }
1475 
1476 
QueryIntText(int * ival) const1477 XMLError XMLElement::QueryIntText( int* ival ) const
1478 {
1479     if ( FirstChild() && FirstChild()->ToText() ) {
1480         const char* t = FirstChild()->Value();
1481         if ( XMLUtil::ToInt( t, ival ) ) {
1482             return XML_SUCCESS;
1483         }
1484         return XML_CAN_NOT_CONVERT_TEXT;
1485     }
1486     return XML_NO_TEXT_NODE;
1487 }
1488 
1489 
QueryUnsignedText(unsigned * uval) const1490 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1491 {
1492     if ( FirstChild() && FirstChild()->ToText() ) {
1493         const char* t = FirstChild()->Value();
1494         if ( XMLUtil::ToUnsigned( t, uval ) ) {
1495             return XML_SUCCESS;
1496         }
1497         return XML_CAN_NOT_CONVERT_TEXT;
1498     }
1499     return XML_NO_TEXT_NODE;
1500 }
1501 
1502 
QueryBoolText(bool * bval) const1503 XMLError XMLElement::QueryBoolText( bool* bval ) const
1504 {
1505     if ( FirstChild() && FirstChild()->ToText() ) {
1506         const char* t = FirstChild()->Value();
1507         if ( XMLUtil::ToBool( t, bval ) ) {
1508             return XML_SUCCESS;
1509         }
1510         return XML_CAN_NOT_CONVERT_TEXT;
1511     }
1512     return XML_NO_TEXT_NODE;
1513 }
1514 
1515 
QueryDoubleText(double * dval) const1516 XMLError XMLElement::QueryDoubleText( double* dval ) const
1517 {
1518     if ( FirstChild() && FirstChild()->ToText() ) {
1519         const char* t = FirstChild()->Value();
1520         if ( XMLUtil::ToDouble( t, dval ) ) {
1521             return XML_SUCCESS;
1522         }
1523         return XML_CAN_NOT_CONVERT_TEXT;
1524     }
1525     return XML_NO_TEXT_NODE;
1526 }
1527 
1528 
QueryFloatText(float * fval) const1529 XMLError XMLElement::QueryFloatText( float* fval ) const
1530 {
1531     if ( FirstChild() && FirstChild()->ToText() ) {
1532         const char* t = FirstChild()->Value();
1533         if ( XMLUtil::ToFloat( t, fval ) ) {
1534             return XML_SUCCESS;
1535         }
1536         return XML_CAN_NOT_CONVERT_TEXT;
1537     }
1538     return XML_NO_TEXT_NODE;
1539 }
1540 
1541 
1542 
FindOrCreateAttribute(const char * name)1543 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1544 {
1545     XMLAttribute* last = 0;
1546     XMLAttribute* attrib = 0;
1547     for( attrib = _rootAttribute;
1548             attrib;
1549             last = attrib, attrib = attrib->_next ) {
1550         if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1551             break;
1552         }
1553     }
1554     if ( !attrib ) {
1555         TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1556         attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1557         attrib->_memPool = &_document->_attributePool;
1558         if ( last ) {
1559             last->_next = attrib;
1560         }
1561         else {
1562             _rootAttribute = attrib;
1563         }
1564         attrib->SetName( name );
1565         attrib->_memPool->SetTracked(); // always created and linked.
1566     }
1567     return attrib;
1568 }
1569 
1570 
DeleteAttribute(const char * name)1571 void XMLElement::DeleteAttribute( const char* name )
1572 {
1573     XMLAttribute* prev = 0;
1574     for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1575         if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1576             if ( prev ) {
1577                 prev->_next = a->_next;
1578             }
1579             else {
1580                 _rootAttribute = a->_next;
1581             }
1582             DeleteAttribute( a );
1583             break;
1584         }
1585         prev = a;
1586     }
1587 }
1588 
1589 
ParseAttributes(char * p)1590 char* XMLElement::ParseAttributes( char* p )
1591 {
1592     const char* start = p;
1593     XMLAttribute* prevAttribute = 0;
1594 
1595     // Read the attributes.
1596     while( p ) {
1597         p = XMLUtil::SkipWhiteSpace( p );
1598         if ( !(*p) ) {
1599             _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1600             return 0;
1601         }
1602 
1603         // attribute.
1604         if (XMLUtil::IsNameStartChar( *p ) ) {
1605             TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1606             XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1607             attrib->_memPool = &_document->_attributePool;
1608 			attrib->_memPool->SetTracked();
1609 
1610             p = attrib->ParseDeep( p, _document->ProcessEntities() );
1611             if ( !p || Attribute( attrib->Name() ) ) {
1612                 DeleteAttribute( attrib );
1613                 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1614                 return 0;
1615             }
1616             // There is a minor bug here: if the attribute in the source xml
1617             // document is duplicated, it will not be detected and the
1618             // attribute will be doubly added. However, tracking the 'prevAttribute'
1619             // avoids re-scanning the attribute list. Preferring performance for
1620             // now, may reconsider in the future.
1621             if ( prevAttribute ) {
1622                 prevAttribute->_next = attrib;
1623             }
1624             else {
1625                 _rootAttribute = attrib;
1626             }
1627             prevAttribute = attrib;
1628         }
1629         // end of the tag
1630         else if ( *p == '>' ) {
1631             ++p;
1632             break;
1633         }
1634         // end of the tag
1635         else if ( *p == '/' && *(p+1) == '>' ) {
1636             _closingType = CLOSED;
1637             return p+2;	// done; sealed element.
1638         }
1639         else {
1640             _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1641             return 0;
1642         }
1643     }
1644     return p;
1645 }
1646 
DeleteAttribute(XMLAttribute * attribute)1647 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1648 {
1649     if ( attribute == 0 ) {
1650         return;
1651     }
1652     MemPool* pool = attribute->_memPool;
1653     attribute->~XMLAttribute();
1654     pool->Free( attribute );
1655 }
1656 
1657 //
1658 //	<ele></ele>
1659 //	<ele>foo<b>bar</b></ele>
1660 //
ParseDeep(char * p,StrPair * strPair)1661 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1662 {
1663     // Read the element name.
1664     p = XMLUtil::SkipWhiteSpace( p );
1665 
1666     // The closing element is the </element> form. It is
1667     // parsed just like a regular element then deleted from
1668     // the DOM.
1669     if ( *p == '/' ) {
1670         _closingType = CLOSING;
1671         ++p;
1672     }
1673 
1674     p = _value.ParseName( p );
1675     if ( _value.Empty() ) {
1676         return 0;
1677     }
1678 
1679     p = ParseAttributes( p );
1680     if ( !p || !*p || _closingType ) {
1681         return p;
1682     }
1683 
1684     p = XMLNode::ParseDeep( p, strPair );
1685     return p;
1686 }
1687 
1688 
1689 
ShallowClone(XMLDocument * doc) const1690 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1691 {
1692     if ( !doc ) {
1693         doc = _document;
1694     }
1695     XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
1696     for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1697         element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
1698     }
1699     return element;
1700 }
1701 
1702 
ShallowEqual(const XMLNode * compare) const1703 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1704 {
1705     TIXMLASSERT( compare );
1706     const XMLElement* other = compare->ToElement();
1707     if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
1708 
1709         const XMLAttribute* a=FirstAttribute();
1710         const XMLAttribute* b=other->FirstAttribute();
1711 
1712         while ( a && b ) {
1713             if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1714                 return false;
1715             }
1716             a = a->Next();
1717             b = b->Next();
1718         }
1719         if ( a || b ) {
1720             // different count
1721             return false;
1722         }
1723         return true;
1724     }
1725     return false;
1726 }
1727 
1728 
Accept(XMLVisitor * visitor) const1729 bool XMLElement::Accept( XMLVisitor* visitor ) const
1730 {
1731     TIXMLASSERT( visitor );
1732     if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1733         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1734             if ( !node->Accept( visitor ) ) {
1735                 break;
1736             }
1737         }
1738     }
1739     return visitor->VisitExit( *this );
1740 }
1741 
1742 
1743 // --------- XMLDocument ----------- //
1744 
1745 // Warning: List must match 'enum XMLError'
1746 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1747     "XML_SUCCESS",
1748     "XML_NO_ATTRIBUTE",
1749     "XML_WRONG_ATTRIBUTE_TYPE",
1750     "XML_ERROR_FILE_NOT_FOUND",
1751     "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1752     "XML_ERROR_FILE_READ_ERROR",
1753     "XML_ERROR_ELEMENT_MISMATCH",
1754     "XML_ERROR_PARSING_ELEMENT",
1755     "XML_ERROR_PARSING_ATTRIBUTE",
1756     "XML_ERROR_IDENTIFYING_TAG",
1757     "XML_ERROR_PARSING_TEXT",
1758     "XML_ERROR_PARSING_CDATA",
1759     "XML_ERROR_PARSING_COMMENT",
1760     "XML_ERROR_PARSING_DECLARATION",
1761     "XML_ERROR_PARSING_UNKNOWN",
1762     "XML_ERROR_EMPTY_DOCUMENT",
1763     "XML_ERROR_MISMATCHED_ELEMENT",
1764     "XML_ERROR_PARSING",
1765     "XML_CAN_NOT_CONVERT_TEXT",
1766     "XML_NO_TEXT_NODE"
1767 };
1768 
1769 
XMLDocument(bool processEntities,Whitespace whitespace)1770 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1771     XMLNode( 0 ),
1772     _writeBOM( false ),
1773     _processEntities( processEntities ),
1774     _errorID( XML_NO_ERROR ),
1775     _whitespace( whitespace ),
1776     _errorStr1( 0 ),
1777     _errorStr2( 0 ),
1778     _charBuffer( 0 )
1779 {
1780     // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
1781     _document = this;
1782 }
1783 
1784 
~XMLDocument()1785 XMLDocument::~XMLDocument()
1786 {
1787     Clear();
1788 }
1789 
1790 
Clear()1791 void XMLDocument::Clear()
1792 {
1793     DeleteChildren();
1794 
1795 #ifdef DEBUG
1796     const bool hadError = Error();
1797 #endif
1798     _errorID = XML_NO_ERROR;
1799     _errorStr1 = 0;
1800     _errorStr2 = 0;
1801 
1802     delete [] _charBuffer;
1803     _charBuffer = 0;
1804 
1805 #if 0
1806     _textPool.Trace( "text" );
1807     _elementPool.Trace( "element" );
1808     _commentPool.Trace( "comment" );
1809     _attributePool.Trace( "attribute" );
1810 #endif
1811 
1812 #ifdef DEBUG
1813     if ( !hadError ) {
1814         TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
1815         TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1816         TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
1817         TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
1818     }
1819 #endif
1820 }
1821 
1822 
NewElement(const char * name)1823 XMLElement* XMLDocument::NewElement( const char* name )
1824 {
1825     TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
1826     XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1827     ele->_memPool = &_elementPool;
1828     ele->SetName( name );
1829     return ele;
1830 }
1831 
1832 
NewComment(const char * str)1833 XMLComment* XMLDocument::NewComment( const char* str )
1834 {
1835     TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
1836     XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1837     comment->_memPool = &_commentPool;
1838     comment->SetValue( str );
1839     return comment;
1840 }
1841 
1842 
NewText(const char * str)1843 XMLText* XMLDocument::NewText( const char* str )
1844 {
1845     TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
1846     XMLText* text = new (_textPool.Alloc()) XMLText( this );
1847     text->_memPool = &_textPool;
1848     text->SetValue( str );
1849     return text;
1850 }
1851 
1852 
NewDeclaration(const char * str)1853 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1854 {
1855     TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
1856     XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1857     dec->_memPool = &_commentPool;
1858     dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1859     return dec;
1860 }
1861 
1862 
NewUnknown(const char * str)1863 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1864 {
1865     TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
1866     XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1867     unk->_memPool = &_commentPool;
1868     unk->SetValue( str );
1869     return unk;
1870 }
1871 
callfopen(const char * filepath,const char * mode)1872 static FILE* callfopen( const char* filepath, const char* mode )
1873 {
1874     TIXMLASSERT( filepath );
1875     TIXMLASSERT( mode );
1876 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
1877     FILE* fp = 0;
1878     errno_t err = fopen_s( &fp, filepath, mode );
1879     if ( err ) {
1880         return 0;
1881     }
1882 #else
1883     FILE* fp = fopen( filepath, mode );
1884 #endif
1885     return fp;
1886 }
1887 
DeleteNode(XMLNode * node)1888 void XMLDocument::DeleteNode( XMLNode* node )	{
1889     TIXMLASSERT( node );
1890     TIXMLASSERT(node->_document == this );
1891     if (node->_parent) {
1892         node->_parent->DeleteChild( node );
1893     }
1894     else {
1895         // Isn't in the tree.
1896         // Use the parent delete.
1897         // Also, we need to mark it tracked: we 'know'
1898         // it was never used.
1899         node->_memPool->SetTracked();
1900         // Call the static XMLNode version:
1901         XMLNode::DeleteNode(node);
1902     }
1903 }
1904 
1905 
LoadFile(const char * filename)1906 XMLError XMLDocument::LoadFile( const char* filename )
1907 {
1908     Clear();
1909     FILE* fp = callfopen( filename, "rb" );
1910     if ( !fp ) {
1911         SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1912         return _errorID;
1913     }
1914     LoadFile( fp );
1915     fclose( fp );
1916     return _errorID;
1917 }
1918 
1919 // This is likely overengineered template art to have a check that unsigned long value incremented
1920 // by one still fits into size_t. If size_t type is larger than unsigned long type
1921 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
1922 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
1923 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
1924 // types sizes relate to each other.
1925 template
1926 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
1927 struct LongFitsIntoSizeTMinusOne {
Fitstinyxml2::LongFitsIntoSizeTMinusOne1928     static bool Fits( unsigned long value )
1929     {
1930         return value < (size_t)-1;
1931     }
1932 };
1933 
1934 template <>
Fits(unsigned long)1935 bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ )
1936 {
1937     return true;
1938 }
1939 
LoadFile(FILE * fp)1940 XMLError XMLDocument::LoadFile( FILE* fp )
1941 {
1942     Clear();
1943 
1944     fseek( fp, 0, SEEK_SET );
1945     if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
1946         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1947         return _errorID;
1948     }
1949 
1950     fseek( fp, 0, SEEK_END );
1951     const long filelength = ftell( fp );
1952     fseek( fp, 0, SEEK_SET );
1953     if ( filelength == -1L ) {
1954         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1955         return _errorID;
1956     }
1957     TIXMLASSERT( filelength >= 0 );
1958 
1959     if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
1960         // Cannot handle files which won't fit in buffer together with null terminator
1961         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1962         return _errorID;
1963     }
1964 
1965     if ( filelength == 0 ) {
1966         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1967         return _errorID;
1968     }
1969 
1970     const size_t size = filelength;
1971     TIXMLASSERT( _charBuffer == 0 );
1972     _charBuffer = new char[size+1];
1973     size_t read = fread( _charBuffer, 1, size, fp );
1974     if ( read != size ) {
1975         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1976         return _errorID;
1977     }
1978 
1979     _charBuffer[size] = 0;
1980 
1981     Parse();
1982     return _errorID;
1983 }
1984 
1985 
SaveFile(const char * filename,bool compact)1986 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1987 {
1988     FILE* fp = callfopen( filename, "w" );
1989     if ( !fp ) {
1990         SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1991         return _errorID;
1992     }
1993     SaveFile(fp, compact);
1994     fclose( fp );
1995     return _errorID;
1996 }
1997 
1998 
SaveFile(FILE * fp,bool compact)1999 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2000 {
2001     // Clear any error from the last save, otherwise it will get reported
2002     // for *this* call.
2003     SetError( XML_NO_ERROR, 0, 0 );
2004     XMLPrinter stream( fp, compact );
2005     Print( &stream );
2006     return _errorID;
2007 }
2008 
2009 
Parse(const char * p,size_t len)2010 XMLError XMLDocument::Parse( const char* p, size_t len )
2011 {
2012     Clear();
2013 
2014     if ( len == 0 || !p || !*p ) {
2015         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2016         return _errorID;
2017     }
2018     if ( len == (size_t)(-1) ) {
2019         len = strlen( p );
2020     }
2021     TIXMLASSERT( _charBuffer == 0 );
2022     _charBuffer = new char[ len+1 ];
2023     memcpy( _charBuffer, p, len );
2024     _charBuffer[len] = 0;
2025 
2026     Parse();
2027     if ( Error() ) {
2028         // clean up now essentially dangling memory.
2029         // and the parse fail can put objects in the
2030         // pools that are dead and inaccessible.
2031         DeleteChildren();
2032         _elementPool.Clear();
2033         _attributePool.Clear();
2034         _textPool.Clear();
2035         _commentPool.Clear();
2036     }
2037     return _errorID;
2038 }
2039 
2040 
Print(XMLPrinter * streamer) const2041 void XMLDocument::Print( XMLPrinter* streamer ) const
2042 {
2043     if ( streamer ) {
2044         Accept( streamer );
2045     }
2046     else {
2047         XMLPrinter stdoutStreamer( stdout );
2048         Accept( &stdoutStreamer );
2049     }
2050 }
2051 
2052 
SetError(XMLError error,const char * str1,const char * str2)2053 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
2054 {
2055     TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2056     _errorID = error;
2057     _errorStr1 = str1;
2058     _errorStr2 = str2;
2059 }
2060 
ErrorName() const2061 const char* XMLDocument::ErrorName() const
2062 {
2063 	TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
2064     const char* errorName = _errorNames[_errorID];
2065     TIXMLASSERT( errorName && errorName[0] );
2066     return errorName;
2067 }
2068 
PrintError() const2069 void XMLDocument::PrintError() const
2070 {
2071     if ( Error() ) {
2072         static const int LEN = 20;
2073         char buf1[LEN] = { 0 };
2074         char buf2[LEN] = { 0 };
2075 
2076         if ( _errorStr1 ) {
2077             TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
2078         }
2079         if ( _errorStr2 ) {
2080             TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
2081         }
2082 
2083         // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
2084         // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
2085         TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
2086         printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
2087                 static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );
2088     }
2089 }
2090 
Parse()2091 void XMLDocument::Parse()
2092 {
2093     TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2094     TIXMLASSERT( _charBuffer );
2095     char* p = _charBuffer;
2096     p = XMLUtil::SkipWhiteSpace( p );
2097     p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2098     if ( !*p ) {
2099         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2100         return;
2101     }
2102     ParseDeep(p, 0 );
2103 }
2104 
XMLPrinter(FILE * file,bool compact,int depth)2105 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2106     _elementJustOpened( false ),
2107     _firstElement( true ),
2108     _fp( file ),
2109     _depth( depth ),
2110     _textDepth( -1 ),
2111     _processEntities( true ),
2112     _compactMode( compact )
2113 {
2114     for( int i=0; i<ENTITY_RANGE; ++i ) {
2115         _entityFlag[i] = false;
2116         _restrictedEntityFlag[i] = false;
2117     }
2118     for( int i=0; i<NUM_ENTITIES; ++i ) {
2119         const char entityValue = entities[i].value;
2120         TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
2121         _entityFlag[ (unsigned char)entityValue ] = true;
2122     }
2123     _restrictedEntityFlag[(unsigned char)'&'] = true;
2124     _restrictedEntityFlag[(unsigned char)'<'] = true;
2125     _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
2126     _buffer.Push( 0 );
2127 }
2128 
2129 
Print(const char * format,...)2130 void XMLPrinter::Print( const char* format, ... )
2131 {
2132     va_list     va;
2133     va_start( va, format );
2134 
2135     if ( _fp ) {
2136         vfprintf( _fp, format, va );
2137     }
2138     else {
2139         const int len = TIXML_VSCPRINTF( format, va );
2140         // Close out and re-start the va-args
2141         va_end( va );
2142         TIXMLASSERT( len >= 0 );
2143         va_start( va, format );
2144         TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2145         char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
2146 		TIXML_VSNPRINTF( p, len+1, format, va );
2147     }
2148     va_end( va );
2149 }
2150 
2151 
PrintSpace(int depth)2152 void XMLPrinter::PrintSpace( int depth )
2153 {
2154     for( int i=0; i<depth; ++i ) {
2155         Print( "    " );
2156     }
2157 }
2158 
2159 
PrintString(const char * p,bool restricted)2160 void XMLPrinter::PrintString( const char* p, bool restricted )
2161 {
2162     // Look for runs of bytes between entities to print.
2163     const char* q = p;
2164 
2165     if ( _processEntities ) {
2166         const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2167         while ( *q ) {
2168             TIXMLASSERT( p <= q );
2169             // Remember, char is sometimes signed. (How many times has that bitten me?)
2170             if ( *q > 0 && *q < ENTITY_RANGE ) {
2171                 // Check for entities. If one is found, flush
2172                 // the stream up until the entity, write the
2173                 // entity, and keep looking.
2174                 if ( flag[(unsigned char)(*q)] ) {
2175                     while ( p < q ) {
2176                         const size_t delta = q - p;
2177                         // %.*s accepts type int as "precision"
2178                         const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2179                         Print( "%.*s", toPrint, p );
2180                         p += toPrint;
2181                     }
2182                     bool entityPatternPrinted = false;
2183                     for( int i=0; i<NUM_ENTITIES; ++i ) {
2184                         if ( entities[i].value == *q ) {
2185                             Print( "&%s;", entities[i].pattern );
2186                             entityPatternPrinted = true;
2187                             break;
2188                         }
2189                     }
2190                     if ( !entityPatternPrinted ) {
2191                         // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2192                         TIXMLASSERT( false );
2193                     }
2194                     ++p;
2195                 }
2196             }
2197             ++q;
2198             TIXMLASSERT( p <= q );
2199         }
2200     }
2201     // Flush the remaining string. This will be the entire
2202     // string if an entity wasn't found.
2203     TIXMLASSERT( p <= q );
2204     if ( !_processEntities || ( p < q ) ) {
2205         Print( "%s", p );
2206     }
2207 }
2208 
2209 
PushHeader(bool writeBOM,bool writeDec)2210 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2211 {
2212     if ( writeBOM ) {
2213         static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2214         Print( "%s", bom );
2215     }
2216     if ( writeDec ) {
2217         PushDeclaration( "xml version=\"1.0\"" );
2218     }
2219 }
2220 
2221 
OpenElement(const char * name,bool compactMode)2222 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2223 {
2224     SealElementIfJustOpened();
2225     _stack.Push( name );
2226 
2227     if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2228         Print( "\n" );
2229     }
2230     if ( !compactMode ) {
2231         PrintSpace( _depth );
2232     }
2233 
2234     Print( "<%s", name );
2235     _elementJustOpened = true;
2236     _firstElement = false;
2237     ++_depth;
2238 }
2239 
2240 
PushAttribute(const char * name,const char * value)2241 void XMLPrinter::PushAttribute( const char* name, const char* value )
2242 {
2243     TIXMLASSERT( _elementJustOpened );
2244     Print( " %s=\"", name );
2245     PrintString( value, false );
2246     Print( "\"" );
2247 }
2248 
2249 
PushAttribute(const char * name,int v)2250 void XMLPrinter::PushAttribute( const char* name, int v )
2251 {
2252     char buf[BUF_SIZE];
2253     XMLUtil::ToStr( v, buf, BUF_SIZE );
2254     PushAttribute( name, buf );
2255 }
2256 
2257 
PushAttribute(const char * name,unsigned v)2258 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2259 {
2260     char buf[BUF_SIZE];
2261     XMLUtil::ToStr( v, buf, BUF_SIZE );
2262     PushAttribute( name, buf );
2263 }
2264 
2265 
PushAttribute(const char * name,bool v)2266 void XMLPrinter::PushAttribute( const char* name, bool v )
2267 {
2268     char buf[BUF_SIZE];
2269     XMLUtil::ToStr( v, buf, BUF_SIZE );
2270     PushAttribute( name, buf );
2271 }
2272 
2273 
PushAttribute(const char * name,double v)2274 void XMLPrinter::PushAttribute( const char* name, double v )
2275 {
2276     char buf[BUF_SIZE];
2277     XMLUtil::ToStr( v, buf, BUF_SIZE );
2278     PushAttribute( name, buf );
2279 }
2280 
2281 
CloseElement(bool compactMode)2282 void XMLPrinter::CloseElement( bool compactMode )
2283 {
2284     --_depth;
2285     const char* name = _stack.Pop();
2286 
2287     if ( _elementJustOpened ) {
2288         Print( "/>" );
2289     }
2290     else {
2291         if ( _textDepth < 0 && !compactMode) {
2292             Print( "\n" );
2293             PrintSpace( _depth );
2294         }
2295         Print( "</%s>", name );
2296     }
2297 
2298     if ( _textDepth == _depth ) {
2299         _textDepth = -1;
2300     }
2301     if ( _depth == 0 && !compactMode) {
2302         Print( "\n" );
2303     }
2304     _elementJustOpened = false;
2305 }
2306 
2307 
SealElementIfJustOpened()2308 void XMLPrinter::SealElementIfJustOpened()
2309 {
2310     if ( !_elementJustOpened ) {
2311         return;
2312     }
2313     _elementJustOpened = false;
2314     Print( ">" );
2315 }
2316 
2317 
PushText(const char * text,bool cdata)2318 void XMLPrinter::PushText( const char* text, bool cdata )
2319 {
2320     _textDepth = _depth-1;
2321 
2322     SealElementIfJustOpened();
2323     if ( cdata ) {
2324         Print( "<![CDATA[%s]]>", text );
2325     }
2326     else {
2327         PrintString( text, true );
2328     }
2329 }
2330 
PushText(int value)2331 void XMLPrinter::PushText( int value )
2332 {
2333     char buf[BUF_SIZE];
2334     XMLUtil::ToStr( value, buf, BUF_SIZE );
2335     PushText( buf, false );
2336 }
2337 
2338 
PushText(unsigned value)2339 void XMLPrinter::PushText( unsigned value )
2340 {
2341     char buf[BUF_SIZE];
2342     XMLUtil::ToStr( value, buf, BUF_SIZE );
2343     PushText( buf, false );
2344 }
2345 
2346 
PushText(bool value)2347 void XMLPrinter::PushText( bool value )
2348 {
2349     char buf[BUF_SIZE];
2350     XMLUtil::ToStr( value, buf, BUF_SIZE );
2351     PushText( buf, false );
2352 }
2353 
2354 
PushText(float value)2355 void XMLPrinter::PushText( float value )
2356 {
2357     char buf[BUF_SIZE];
2358     XMLUtil::ToStr( value, buf, BUF_SIZE );
2359     PushText( buf, false );
2360 }
2361 
2362 
PushText(double value)2363 void XMLPrinter::PushText( double value )
2364 {
2365     char buf[BUF_SIZE];
2366     XMLUtil::ToStr( value, buf, BUF_SIZE );
2367     PushText( buf, false );
2368 }
2369 
2370 
PushComment(const char * comment)2371 void XMLPrinter::PushComment( const char* comment )
2372 {
2373     SealElementIfJustOpened();
2374     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2375         Print( "\n" );
2376         PrintSpace( _depth );
2377     }
2378     _firstElement = false;
2379     Print( "<!--%s-->", comment );
2380 }
2381 
2382 
PushDeclaration(const char * value)2383 void XMLPrinter::PushDeclaration( const char* value )
2384 {
2385     SealElementIfJustOpened();
2386     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2387         Print( "\n" );
2388         PrintSpace( _depth );
2389     }
2390     _firstElement = false;
2391     Print( "<?%s?>", value );
2392 }
2393 
2394 
PushUnknown(const char * value)2395 void XMLPrinter::PushUnknown( const char* value )
2396 {
2397     SealElementIfJustOpened();
2398     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2399         Print( "\n" );
2400         PrintSpace( _depth );
2401     }
2402     _firstElement = false;
2403     Print( "<!%s>", value );
2404 }
2405 
2406 
VisitEnter(const XMLDocument & doc)2407 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2408 {
2409     _processEntities = doc.ProcessEntities();
2410     if ( doc.HasBOM() ) {
2411         PushHeader( true, false );
2412     }
2413     return true;
2414 }
2415 
2416 
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2417 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2418 {
2419     const XMLElement* parentElem = 0;
2420     if ( element.Parent() ) {
2421         parentElem = element.Parent()->ToElement();
2422     }
2423     const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2424     OpenElement( element.Name(), compactMode );
2425     while ( attribute ) {
2426         PushAttribute( attribute->Name(), attribute->Value() );
2427         attribute = attribute->Next();
2428     }
2429     return true;
2430 }
2431 
2432 
VisitExit(const XMLElement & element)2433 bool XMLPrinter::VisitExit( const XMLElement& element )
2434 {
2435     CloseElement( CompactMode(element) );
2436     return true;
2437 }
2438 
2439 
Visit(const XMLText & text)2440 bool XMLPrinter::Visit( const XMLText& text )
2441 {
2442     PushText( text.Value(), text.CData() );
2443     return true;
2444 }
2445 
2446 
Visit(const XMLComment & comment)2447 bool XMLPrinter::Visit( const XMLComment& comment )
2448 {
2449     PushComment( comment.Value() );
2450     return true;
2451 }
2452 
Visit(const XMLDeclaration & declaration)2453 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2454 {
2455     PushDeclaration( declaration.Value() );
2456     return true;
2457 }
2458 
2459 
Visit(const XMLUnknown & unknown)2460 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2461 {
2462     PushUnknown( unknown.Value() );
2463     return true;
2464 }
2465 
2466 }   // namespace tinyxml2
2467 
2468