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 != 0 );
153     TIXMLASSERT( other->_flags == 0 );
154     TIXMLASSERT( other->_start == 0 );
155     TIXMLASSERT( other->_end == 0 );
156 
157     other->Reset();
158 
159     other->_flags = _flags;
160     other->_start = _start;
161     other->_end = _end;
162 
163     _flags = 0;
164     _start = 0;
165     _end = 0;
166 }
167 
168 
Reset()169 void StrPair::Reset()
170 {
171     if ( _flags & NEEDS_DELETE ) {
172         delete [] _start;
173     }
174     _flags = 0;
175     _start = 0;
176     _end = 0;
177 }
178 
179 
SetStr(const char * str,int flags)180 void StrPair::SetStr( const char* str, int flags )
181 {
182     TIXMLASSERT( str );
183     Reset();
184     size_t len = strlen( str );
185     TIXMLASSERT( _start == 0 );
186     _start = new char[ len+1 ];
187     memcpy( _start, str, len+1 );
188     _end = _start + len;
189     _flags = flags | NEEDS_DELETE;
190 }
191 
192 
ParseText(char * p,const char * endTag,int strFlags,int * curLineNumPtr)193 char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
194 {
195     TIXMLASSERT( p );
196     TIXMLASSERT( endTag && *endTag );
197 	TIXMLASSERT(curLineNumPtr);
198 
199     char* start = p;
200     char  endChar = *endTag;
201     size_t length = strlen( endTag );
202 
203     // Inner loop of text parsing.
204     while ( *p ) {
205         if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
206             Set( start, p, strFlags );
207             return p + length;
208         } else if (*p == '\n') {
209             ++(*curLineNumPtr);
210         }
211         ++p;
212         TIXMLASSERT( p );
213     }
214     return 0;
215 }
216 
217 
ParseName(char * p)218 char* StrPair::ParseName( char* p )
219 {
220     if ( !p || !(*p) ) {
221         return 0;
222     }
223     if ( !XMLUtil::IsNameStartChar( *p ) ) {
224         return 0;
225     }
226 
227     char* const start = p;
228     ++p;
229     while ( *p && XMLUtil::IsNameChar( *p ) ) {
230         ++p;
231     }
232 
233     Set( start, p, 0 );
234     return p;
235 }
236 
237 
CollapseWhitespace()238 void StrPair::CollapseWhitespace()
239 {
240     // Adjusting _start would cause undefined behavior on delete[]
241     TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
242     // Trim leading space.
243     _start = XMLUtil::SkipWhiteSpace( _start, 0 );
244 
245     if ( *_start ) {
246         const char* p = _start;	// the read pointer
247         char* q = _start;	// the write pointer
248 
249         while( *p ) {
250             if ( XMLUtil::IsWhiteSpace( *p )) {
251                 p = XMLUtil::SkipWhiteSpace( p, 0 );
252                 if ( *p == 0 ) {
253                     break;    // don't write to q; this trims the trailing space.
254                 }
255                 *q = ' ';
256                 ++q;
257             }
258             *q = *p;
259             ++q;
260             ++p;
261         }
262         *q = 0;
263     }
264 }
265 
266 
GetStr()267 const char* StrPair::GetStr()
268 {
269     TIXMLASSERT( _start );
270     TIXMLASSERT( _end );
271     if ( _flags & NEEDS_FLUSH ) {
272         *_end = 0;
273         _flags ^= NEEDS_FLUSH;
274 
275         if ( _flags ) {
276             const char* p = _start;	// the read pointer
277             char* q = _start;	// the write pointer
278 
279             while( p < _end ) {
280                 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
281                     // CR-LF pair becomes LF
282                     // CR alone becomes LF
283                     // LF-CR becomes LF
284                     if ( *(p+1) == LF ) {
285                         p += 2;
286                     }
287                     else {
288                         ++p;
289                     }
290                     *q = LF;
291                     ++q;
292                 }
293                 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
294                     if ( *(p+1) == CR ) {
295                         p += 2;
296                     }
297                     else {
298                         ++p;
299                     }
300                     *q = LF;
301                     ++q;
302                 }
303                 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
304                     // Entities handled by tinyXML2:
305                     // - special entities in the entity table [in/out]
306                     // - numeric character reference [in]
307                     //   &#20013; or &#x4e2d;
308 
309                     if ( *(p+1) == '#' ) {
310                         const int buflen = 10;
311                         char buf[buflen] = { 0 };
312                         int len = 0;
313                         char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
314                         if ( adjusted == 0 ) {
315                             *q = *p;
316                             ++p;
317                             ++q;
318                         }
319                         else {
320                             TIXMLASSERT( 0 <= len && len <= buflen );
321                             TIXMLASSERT( q + len <= adjusted );
322                             p = adjusted;
323                             memcpy( q, buf, len );
324                             q += len;
325                         }
326                     }
327                     else {
328                         bool entityFound = false;
329                         for( int i = 0; i < NUM_ENTITIES; ++i ) {
330                             const Entity& entity = entities[i];
331                             if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
332                                     && *( p + entity.length + 1 ) == ';' ) {
333                                 // Found an entity - convert.
334                                 *q = entity.value;
335                                 ++q;
336                                 p += entity.length + 2;
337                                 entityFound = true;
338                                 break;
339                             }
340                         }
341                         if ( !entityFound ) {
342                             // fixme: treat as error?
343                             ++p;
344                             ++q;
345                         }
346                     }
347                 }
348                 else {
349                     *q = *p;
350                     ++p;
351                     ++q;
352                 }
353             }
354             *q = 0;
355         }
356         // The loop below has plenty going on, and this
357         // is a less useful mode. Break it out.
358         if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
359             CollapseWhitespace();
360         }
361         _flags = (_flags & NEEDS_DELETE);
362     }
363     TIXMLASSERT( _start );
364     return _start;
365 }
366 
367 
368 
369 
370 // --------- XMLUtil ----------- //
371 
372 const char* XMLUtil::writeBoolTrue  = "true";
373 const char* XMLUtil::writeBoolFalse = "false";
374 
SetBoolSerialization(const char * writeTrue,const char * writeFalse)375 void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
376 {
377 	static const char* defTrue  = "true";
378 	static const char* defFalse = "false";
379 
380 	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
381 	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
382 }
383 
384 
ReadBOM(const char * p,bool * bom)385 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
386 {
387     TIXMLASSERT( p );
388     TIXMLASSERT( bom );
389     *bom = false;
390     const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
391     // Check for BOM:
392     if (    *(pu+0) == TIXML_UTF_LEAD_0
393             && *(pu+1) == TIXML_UTF_LEAD_1
394             && *(pu+2) == TIXML_UTF_LEAD_2 ) {
395         *bom = true;
396         p += 3;
397     }
398     TIXMLASSERT( p );
399     return p;
400 }
401 
402 
ConvertUTF32ToUTF8(unsigned long input,char * output,int * length)403 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
404 {
405     const unsigned long BYTE_MASK = 0xBF;
406     const unsigned long BYTE_MARK = 0x80;
407     const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
408 
409     if (input < 0x80) {
410         *length = 1;
411     }
412     else if ( input < 0x800 ) {
413         *length = 2;
414     }
415     else if ( input < 0x10000 ) {
416         *length = 3;
417     }
418     else if ( input < 0x200000 ) {
419         *length = 4;
420     }
421     else {
422         *length = 0;    // This code won't convert this correctly anyway.
423         return;
424     }
425 
426     output += *length;
427 
428     // Scary scary fall throughs are annotated with carefully designed comments
429     // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
430     switch (*length) {
431         case 4:
432             --output;
433             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
434             input >>= 6;
435             //fall through
436         case 3:
437             --output;
438             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
439             input >>= 6;
440             //fall through
441         case 2:
442             --output;
443             *output = (char)((input | BYTE_MARK) & BYTE_MASK);
444             input >>= 6;
445             //fall through
446         case 1:
447             --output;
448             *output = (char)(input | FIRST_BYTE_MARK[*length]);
449             break;
450         default:
451             TIXMLASSERT( false );
452     }
453 }
454 
455 
GetCharacterRef(const char * p,char * value,int * length)456 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
457 {
458     // Presume an entity, and pull it out.
459     *length = 0;
460 
461     if ( *(p+1) == '#' && *(p+2) ) {
462         unsigned long ucs = 0;
463         TIXMLASSERT( sizeof( ucs ) >= 4 );
464         ptrdiff_t delta = 0;
465         unsigned mult = 1;
466         static const char SEMICOLON = ';';
467 
468         if ( *(p+2) == 'x' ) {
469             // Hexadecimal.
470             const char* q = p+3;
471             if ( !(*q) ) {
472                 return 0;
473             }
474 
475             q = strchr( q, SEMICOLON );
476 
477             if ( !q ) {
478                 return 0;
479             }
480             TIXMLASSERT( *q == SEMICOLON );
481 
482             delta = q-p;
483             --q;
484 
485             while ( *q != 'x' ) {
486                 unsigned int digit = 0;
487 
488                 if ( *q >= '0' && *q <= '9' ) {
489                     digit = *q - '0';
490                 }
491                 else if ( *q >= 'a' && *q <= 'f' ) {
492                     digit = *q - 'a' + 10;
493                 }
494                 else if ( *q >= 'A' && *q <= 'F' ) {
495                     digit = *q - 'A' + 10;
496                 }
497                 else {
498                     return 0;
499                 }
500                 TIXMLASSERT( digit < 16 );
501                 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
502                 const unsigned int digitScaled = mult * digit;
503                 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
504                 ucs += digitScaled;
505                 TIXMLASSERT( mult <= UINT_MAX / 16 );
506                 mult *= 16;
507                 --q;
508             }
509         }
510         else {
511             // Decimal.
512             const char* q = p+2;
513             if ( !(*q) ) {
514                 return 0;
515             }
516 
517             q = strchr( q, SEMICOLON );
518 
519             if ( !q ) {
520                 return 0;
521             }
522             TIXMLASSERT( *q == SEMICOLON );
523 
524             delta = q-p;
525             --q;
526 
527             while ( *q != '#' ) {
528                 if ( *q >= '0' && *q <= '9' ) {
529                     const unsigned int digit = *q - '0';
530                     TIXMLASSERT( digit < 10 );
531                     TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
532                     const unsigned int digitScaled = mult * digit;
533                     TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
534                     ucs += digitScaled;
535                 }
536                 else {
537                     return 0;
538                 }
539                 TIXMLASSERT( mult <= UINT_MAX / 10 );
540                 mult *= 10;
541                 --q;
542             }
543         }
544         // convert the UCS to UTF-8
545         ConvertUTF32ToUTF8( ucs, value, length );
546         return p + delta + 1;
547     }
548     return p+1;
549 }
550 
551 
ToStr(int v,char * buffer,int bufferSize)552 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
553 {
554     TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
555 }
556 
557 
ToStr(unsigned v,char * buffer,int bufferSize)558 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
559 {
560     TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
561 }
562 
563 
ToStr(bool v,char * buffer,int bufferSize)564 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
565 {
566     TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
567 }
568 
569 /*
570 	ToStr() of a number is a very tricky topic.
571 	https://github.com/leethomason/tinyxml2/issues/106
572 */
ToStr(float v,char * buffer,int bufferSize)573 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
574 {
575     TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
576 }
577 
578 
ToStr(double v,char * buffer,int bufferSize)579 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
580 {
581     TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
582 }
583 
584 
ToStr(int64_t v,char * buffer,int bufferSize)585 void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
586 {
587 	// horrible syntax trick to make the compiler happy about %lld
588 	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
589 }
590 
591 
ToInt(const char * str,int * value)592 bool XMLUtil::ToInt( const char* str, int* value )
593 {
594     if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
595         return true;
596     }
597     return false;
598 }
599 
ToUnsigned(const char * str,unsigned * value)600 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
601 {
602     if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
603         return true;
604     }
605     return false;
606 }
607 
ToBool(const char * str,bool * value)608 bool XMLUtil::ToBool( const char* str, bool* value )
609 {
610     int ival = 0;
611     if ( ToInt( str, &ival )) {
612         *value = (ival==0) ? false : true;
613         return true;
614     }
615     if ( StringEqual( str, "true" ) ) {
616         *value = true;
617         return true;
618     }
619     else if ( StringEqual( str, "false" ) ) {
620         *value = false;
621         return true;
622     }
623     return false;
624 }
625 
626 
ToFloat(const char * str,float * value)627 bool XMLUtil::ToFloat( const char* str, float* value )
628 {
629     if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
630         return true;
631     }
632     return false;
633 }
634 
635 
ToDouble(const char * str,double * value)636 bool XMLUtil::ToDouble( const char* str, double* value )
637 {
638     if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
639         return true;
640     }
641     return false;
642 }
643 
644 
ToInt64(const char * str,int64_t * value)645 bool XMLUtil::ToInt64(const char* str, int64_t* value)
646 {
647 	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
648 	if (TIXML_SSCANF(str, "%lld", &v) == 1) {
649 		*value = (int64_t)v;
650 		return true;
651 	}
652 	return false;
653 }
654 
655 
Identify(char * p,XMLNode ** node)656 char* XMLDocument::Identify( char* p, XMLNode** node )
657 {
658     TIXMLASSERT( node );
659     TIXMLASSERT( p );
660     char* const start = p;
661     int const startLine = _parseCurLineNum;
662     p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
663     if( !*p ) {
664         *node = 0;
665         TIXMLASSERT( p );
666         return p;
667     }
668 
669     // These strings define the matching patterns:
670     static const char* xmlHeader		= { "<?" };
671     static const char* commentHeader	= { "<!--" };
672     static const char* cdataHeader		= { "<![CDATA[" };
673     static const char* dtdHeader		= { "<!" };
674     static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
675 
676     static const int xmlHeaderLen		= 2;
677     static const int commentHeaderLen	= 4;
678     static const int cdataHeaderLen		= 9;
679     static const int dtdHeaderLen		= 2;
680     static const int elementHeaderLen	= 1;
681 
682     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
683     TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
684     XMLNode* returnNode = 0;
685     if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
686         returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
687         returnNode->_parseLineNum = _parseCurLineNum;
688         p += xmlHeaderLen;
689     }
690     else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
691         returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
692         returnNode->_parseLineNum = _parseCurLineNum;
693         p += commentHeaderLen;
694     }
695     else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
696         XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
697         returnNode = text;
698         returnNode->_parseLineNum = _parseCurLineNum;
699         p += cdataHeaderLen;
700         text->SetCData( true );
701     }
702     else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
703         returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
704         returnNode->_parseLineNum = _parseCurLineNum;
705         p += dtdHeaderLen;
706     }
707     else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
708         returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
709         returnNode->_parseLineNum = _parseCurLineNum;
710         p += elementHeaderLen;
711     }
712     else {
713         returnNode = CreateUnlinkedNode<XMLText>( _textPool );
714         returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
715         p = start;	// Back it up, all the text counts.
716         _parseCurLineNum = startLine;
717     }
718 
719     TIXMLASSERT( returnNode );
720     TIXMLASSERT( p );
721     *node = returnNode;
722     return p;
723 }
724 
725 
Accept(XMLVisitor * visitor) const726 bool XMLDocument::Accept( XMLVisitor* visitor ) const
727 {
728     TIXMLASSERT( visitor );
729     if ( visitor->VisitEnter( *this ) ) {
730         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
731             if ( !node->Accept( visitor ) ) {
732                 break;
733             }
734         }
735     }
736     return visitor->VisitExit( *this );
737 }
738 
739 
740 // --------- XMLNode ----------- //
741 
XMLNode(XMLDocument * doc)742 XMLNode::XMLNode( XMLDocument* doc ) :
743     _document( doc ),
744     _parent( 0 ),
745     _value(),
746     _parseLineNum( 0 ),
747     _firstChild( 0 ), _lastChild( 0 ),
748     _prev( 0 ), _next( 0 ),
749 	_userData( 0 ),
750     _memPool( 0 )
751 {
752 }
753 
754 
~XMLNode()755 XMLNode::~XMLNode()
756 {
757     DeleteChildren();
758     if ( _parent ) {
759         _parent->Unlink( this );
760     }
761 }
762 
Value() const763 const char* XMLNode::Value() const
764 {
765     // Edge case: XMLDocuments don't have a Value. Return null.
766     if ( this->ToDocument() )
767         return 0;
768     return _value.GetStr();
769 }
770 
SetValue(const char * str,bool staticMem)771 void XMLNode::SetValue( const char* str, bool staticMem )
772 {
773     if ( staticMem ) {
774         _value.SetInternedStr( str );
775     }
776     else {
777         _value.SetStr( str );
778     }
779 }
780 
DeepClone(XMLDocument * target) const781 XMLNode* XMLNode::DeepClone(XMLDocument* target) const
782 {
783 	XMLNode* clone = this->ShallowClone(target);
784 	if (!clone) return 0;
785 
786 	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
787 		XMLNode* childClone = child->DeepClone(target);
788 		TIXMLASSERT(childClone);
789 		clone->InsertEndChild(childClone);
790 	}
791 	return clone;
792 }
793 
DeleteChildren()794 void XMLNode::DeleteChildren()
795 {
796     while( _firstChild ) {
797         TIXMLASSERT( _lastChild );
798         DeleteChild( _firstChild );
799     }
800     _firstChild = _lastChild = 0;
801 }
802 
803 
Unlink(XMLNode * child)804 void XMLNode::Unlink( XMLNode* child )
805 {
806     TIXMLASSERT( child );
807     TIXMLASSERT( child->_document == _document );
808     TIXMLASSERT( child->_parent == this );
809     if ( child == _firstChild ) {
810         _firstChild = _firstChild->_next;
811     }
812     if ( child == _lastChild ) {
813         _lastChild = _lastChild->_prev;
814     }
815 
816     if ( child->_prev ) {
817         child->_prev->_next = child->_next;
818     }
819     if ( child->_next ) {
820         child->_next->_prev = child->_prev;
821     }
822 	child->_next = 0;
823 	child->_prev = 0;
824 	child->_parent = 0;
825 }
826 
827 
DeleteChild(XMLNode * node)828 void XMLNode::DeleteChild( XMLNode* node )
829 {
830     TIXMLASSERT( node );
831     TIXMLASSERT( node->_document == _document );
832     TIXMLASSERT( node->_parent == this );
833     Unlink( node );
834 	TIXMLASSERT(node->_prev == 0);
835 	TIXMLASSERT(node->_next == 0);
836 	TIXMLASSERT(node->_parent == 0);
837     DeleteNode( node );
838 }
839 
840 
InsertEndChild(XMLNode * addThis)841 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
842 {
843     TIXMLASSERT( addThis );
844     if ( addThis->_document != _document ) {
845         TIXMLASSERT( false );
846         return 0;
847     }
848     InsertChildPreamble( addThis );
849 
850     if ( _lastChild ) {
851         TIXMLASSERT( _firstChild );
852         TIXMLASSERT( _lastChild->_next == 0 );
853         _lastChild->_next = addThis;
854         addThis->_prev = _lastChild;
855         _lastChild = addThis;
856 
857         addThis->_next = 0;
858     }
859     else {
860         TIXMLASSERT( _firstChild == 0 );
861         _firstChild = _lastChild = addThis;
862 
863         addThis->_prev = 0;
864         addThis->_next = 0;
865     }
866     addThis->_parent = this;
867     return addThis;
868 }
869 
870 
InsertFirstChild(XMLNode * addThis)871 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
872 {
873     TIXMLASSERT( addThis );
874     if ( addThis->_document != _document ) {
875         TIXMLASSERT( false );
876         return 0;
877     }
878     InsertChildPreamble( addThis );
879 
880     if ( _firstChild ) {
881         TIXMLASSERT( _lastChild );
882         TIXMLASSERT( _firstChild->_prev == 0 );
883 
884         _firstChild->_prev = addThis;
885         addThis->_next = _firstChild;
886         _firstChild = addThis;
887 
888         addThis->_prev = 0;
889     }
890     else {
891         TIXMLASSERT( _lastChild == 0 );
892         _firstChild = _lastChild = addThis;
893 
894         addThis->_prev = 0;
895         addThis->_next = 0;
896     }
897     addThis->_parent = this;
898     return addThis;
899 }
900 
901 
InsertAfterChild(XMLNode * afterThis,XMLNode * addThis)902 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
903 {
904     TIXMLASSERT( addThis );
905     if ( addThis->_document != _document ) {
906         TIXMLASSERT( false );
907         return 0;
908     }
909 
910     TIXMLASSERT( afterThis );
911 
912     if ( afterThis->_parent != this ) {
913         TIXMLASSERT( false );
914         return 0;
915     }
916     if ( afterThis == addThis ) {
917         // Current state: BeforeThis -> AddThis -> OneAfterAddThis
918         // Now AddThis must disappear from it's location and then
919         // reappear between BeforeThis and OneAfterAddThis.
920         // So just leave it where it is.
921         return addThis;
922     }
923 
924     if ( afterThis->_next == 0 ) {
925         // The last node or the only node.
926         return InsertEndChild( addThis );
927     }
928     InsertChildPreamble( addThis );
929     addThis->_prev = afterThis;
930     addThis->_next = afterThis->_next;
931     afterThis->_next->_prev = addThis;
932     afterThis->_next = addThis;
933     addThis->_parent = this;
934     return addThis;
935 }
936 
937 
938 
939 
FirstChildElement(const char * name) const940 const XMLElement* XMLNode::FirstChildElement( const char* name ) const
941 {
942     for( const XMLNode* node = _firstChild; node; node = node->_next ) {
943         const XMLElement* element = node->ToElementWithName( name );
944         if ( element ) {
945             return element;
946         }
947     }
948     return 0;
949 }
950 
951 
LastChildElement(const char * name) const952 const XMLElement* XMLNode::LastChildElement( const char* name ) const
953 {
954     for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
955         const XMLElement* element = node->ToElementWithName( name );
956         if ( element ) {
957             return element;
958         }
959     }
960     return 0;
961 }
962 
963 
NextSiblingElement(const char * name) const964 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
965 {
966     for( const XMLNode* node = _next; node; node = node->_next ) {
967         const XMLElement* element = node->ToElementWithName( name );
968         if ( element ) {
969             return element;
970         }
971     }
972     return 0;
973 }
974 
975 
PreviousSiblingElement(const char * name) const976 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
977 {
978     for( const XMLNode* node = _prev; node; node = node->_prev ) {
979         const XMLElement* element = node->ToElementWithName( name );
980         if ( element ) {
981             return element;
982         }
983     }
984     return 0;
985 }
986 
987 
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)988 char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
989 {
990     // This is a recursive method, but thinking about it "at the current level"
991     // it is a pretty simple flat list:
992     //		<foo/>
993     //		<!-- comment -->
994     //
995     // With a special case:
996     //		<foo>
997     //		</foo>
998     //		<!-- comment -->
999     //
1000     // Where the closing element (/foo) *must* be the next thing after the opening
1001     // element, and the names must match. BUT the tricky bit is that the closing
1002     // element will be read by the child.
1003     //
1004     // 'endTag' is the end tag for this node, it is returned by a call to a child.
1005     // 'parentEnd' is the end tag for the parent, which is filled in and returned.
1006 
1007 	XMLDocument::DepthTracker tracker(_document);
1008 	if (_document->Error())
1009 		return 0;
1010 
1011 	while( p && *p ) {
1012         XMLNode* node = 0;
1013 
1014         p = _document->Identify( p, &node );
1015         TIXMLASSERT( p );
1016         if ( node == 0 ) {
1017             break;
1018         }
1019 
1020         int initialLineNum = node->_parseLineNum;
1021 
1022         StrPair endTag;
1023         p = node->ParseDeep( p, &endTag, curLineNumPtr );
1024         if ( !p ) {
1025             DeleteNode( node );
1026             if ( !_document->Error() ) {
1027                 _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
1028             }
1029             break;
1030         }
1031 
1032         XMLDeclaration* decl = node->ToDeclaration();
1033         if ( decl ) {
1034             // Declarations are only allowed at document level
1035             bool wellLocated = ( ToDocument() != 0 );
1036             if ( wellLocated ) {
1037                 // Multiple declarations are allowed but all declarations
1038                 // must occur before anything else
1039                 for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {
1040                     if ( !existingNode->ToDeclaration() ) {
1041                         wellLocated = false;
1042                         break;
1043                     }
1044                 }
1045             }
1046             if ( !wellLocated ) {
1047                 _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1048                 DeleteNode( node );
1049                 break;
1050             }
1051         }
1052 
1053         XMLElement* ele = node->ToElement();
1054         if ( ele ) {
1055             // We read the end tag. Return it to the parent.
1056             if ( ele->ClosingType() == XMLElement::CLOSING ) {
1057                 if ( parentEndTag ) {
1058                     ele->_value.TransferTo( parentEndTag );
1059                 }
1060                 node->_memPool->SetTracked();   // created and then immediately deleted.
1061                 DeleteNode( node );
1062                 return p;
1063             }
1064 
1065             // Handle an end tag returned to this level.
1066             // And handle a bunch of annoying errors.
1067             bool mismatch = false;
1068             if ( endTag.Empty() ) {
1069                 if ( ele->ClosingType() == XMLElement::OPEN ) {
1070                     mismatch = true;
1071                 }
1072             }
1073             else {
1074                 if ( ele->ClosingType() != XMLElement::OPEN ) {
1075                     mismatch = true;
1076                 }
1077                 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1078                     mismatch = true;
1079                 }
1080             }
1081             if ( mismatch ) {
1082                 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1083                 DeleteNode( node );
1084                 break;
1085             }
1086         }
1087         InsertEndChild( node );
1088     }
1089     return 0;
1090 }
1091 
DeleteNode(XMLNode * node)1092 /*static*/ void XMLNode::DeleteNode( XMLNode* node )
1093 {
1094     if ( node == 0 ) {
1095         return;
1096     }
1097 	TIXMLASSERT(node->_document);
1098 	if (!node->ToDocument()) {
1099 		node->_document->MarkInUse(node);
1100 	}
1101 
1102     MemPool* pool = node->_memPool;
1103     node->~XMLNode();
1104     pool->Free( node );
1105 }
1106 
InsertChildPreamble(XMLNode * insertThis) const1107 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1108 {
1109     TIXMLASSERT( insertThis );
1110     TIXMLASSERT( insertThis->_document == _document );
1111 
1112 	if (insertThis->_parent) {
1113         insertThis->_parent->Unlink( insertThis );
1114 	}
1115 	else {
1116 		insertThis->_document->MarkInUse(insertThis);
1117         insertThis->_memPool->SetTracked();
1118 	}
1119 }
1120 
ToElementWithName(const char * name) const1121 const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1122 {
1123     const XMLElement* element = this->ToElement();
1124     if ( element == 0 ) {
1125         return 0;
1126     }
1127     if ( name == 0 ) {
1128         return element;
1129     }
1130     if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1131        return element;
1132     }
1133     return 0;
1134 }
1135 
1136 // --------- XMLText ---------- //
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1137 char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1138 {
1139     if ( this->CData() ) {
1140         p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1141         if ( !p ) {
1142             _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
1143         }
1144         return p;
1145     }
1146     else {
1147         int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1148         if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1149             flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1150         }
1151 
1152         p = _value.ParseText( p, "<", flags, curLineNumPtr );
1153         if ( p && *p ) {
1154             return p-1;
1155         }
1156         if ( !p ) {
1157             _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1158         }
1159     }
1160     return 0;
1161 }
1162 
1163 
ShallowClone(XMLDocument * doc) const1164 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1165 {
1166     if ( !doc ) {
1167         doc = _document;
1168     }
1169     XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
1170     text->SetCData( this->CData() );
1171     return text;
1172 }
1173 
1174 
ShallowEqual(const XMLNode * compare) const1175 bool XMLText::ShallowEqual( const XMLNode* compare ) const
1176 {
1177     TIXMLASSERT( compare );
1178     const XMLText* text = compare->ToText();
1179     return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1180 }
1181 
1182 
Accept(XMLVisitor * visitor) const1183 bool XMLText::Accept( XMLVisitor* visitor ) const
1184 {
1185     TIXMLASSERT( visitor );
1186     return visitor->Visit( *this );
1187 }
1188 
1189 
1190 // --------- XMLComment ---------- //
1191 
XMLComment(XMLDocument * doc)1192 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1193 {
1194 }
1195 
1196 
~XMLComment()1197 XMLComment::~XMLComment()
1198 {
1199 }
1200 
1201 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1202 char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1203 {
1204     // Comment parses as text.
1205     p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
1206     if ( p == 0 ) {
1207         _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
1208     }
1209     return p;
1210 }
1211 
1212 
ShallowClone(XMLDocument * doc) const1213 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1214 {
1215     if ( !doc ) {
1216         doc = _document;
1217     }
1218     XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
1219     return comment;
1220 }
1221 
1222 
ShallowEqual(const XMLNode * compare) const1223 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1224 {
1225     TIXMLASSERT( compare );
1226     const XMLComment* comment = compare->ToComment();
1227     return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1228 }
1229 
1230 
Accept(XMLVisitor * visitor) const1231 bool XMLComment::Accept( XMLVisitor* visitor ) const
1232 {
1233     TIXMLASSERT( visitor );
1234     return visitor->Visit( *this );
1235 }
1236 
1237 
1238 // --------- XMLDeclaration ---------- //
1239 
XMLDeclaration(XMLDocument * doc)1240 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1241 {
1242 }
1243 
1244 
~XMLDeclaration()1245 XMLDeclaration::~XMLDeclaration()
1246 {
1247     //printf( "~XMLDeclaration\n" );
1248 }
1249 
1250 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1251 char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1252 {
1253     // Declaration parses as text.
1254     p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1255     if ( p == 0 ) {
1256         _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
1257     }
1258     return p;
1259 }
1260 
1261 
ShallowClone(XMLDocument * doc) const1262 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1263 {
1264     if ( !doc ) {
1265         doc = _document;
1266     }
1267     XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
1268     return dec;
1269 }
1270 
1271 
ShallowEqual(const XMLNode * compare) const1272 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1273 {
1274     TIXMLASSERT( compare );
1275     const XMLDeclaration* declaration = compare->ToDeclaration();
1276     return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1277 }
1278 
1279 
1280 
Accept(XMLVisitor * visitor) const1281 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1282 {
1283     TIXMLASSERT( visitor );
1284     return visitor->Visit( *this );
1285 }
1286 
1287 // --------- XMLUnknown ---------- //
1288 
XMLUnknown(XMLDocument * doc)1289 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1290 {
1291 }
1292 
1293 
~XMLUnknown()1294 XMLUnknown::~XMLUnknown()
1295 {
1296 }
1297 
1298 
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1299 char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1300 {
1301     // Unknown parses as text.
1302     p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1303     if ( !p ) {
1304         _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
1305     }
1306     return p;
1307 }
1308 
1309 
ShallowClone(XMLDocument * doc) const1310 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1311 {
1312     if ( !doc ) {
1313         doc = _document;
1314     }
1315     XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
1316     return text;
1317 }
1318 
1319 
ShallowEqual(const XMLNode * compare) const1320 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1321 {
1322     TIXMLASSERT( compare );
1323     const XMLUnknown* unknown = compare->ToUnknown();
1324     return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1325 }
1326 
1327 
Accept(XMLVisitor * visitor) const1328 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1329 {
1330     TIXMLASSERT( visitor );
1331     return visitor->Visit( *this );
1332 }
1333 
1334 // --------- XMLAttribute ---------- //
1335 
Name() const1336 const char* XMLAttribute::Name() const
1337 {
1338     return _name.GetStr();
1339 }
1340 
Value() const1341 const char* XMLAttribute::Value() const
1342 {
1343     return _value.GetStr();
1344 }
1345 
ParseDeep(char * p,bool processEntities,int * curLineNumPtr)1346 char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
1347 {
1348     // Parse using the name rules: bug fix, was using ParseText before
1349     p = _name.ParseName( p );
1350     if ( !p || !*p ) {
1351         return 0;
1352     }
1353 
1354     // Skip white space before =
1355     p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1356     if ( *p != '=' ) {
1357         return 0;
1358     }
1359 
1360     ++p;	// move up to opening quote
1361     p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1362     if ( *p != '\"' && *p != '\'' ) {
1363         return 0;
1364     }
1365 
1366     char endTag[2] = { *p, 0 };
1367     ++p;	// move past opening quote
1368 
1369     p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
1370     return p;
1371 }
1372 
1373 
SetName(const char * n)1374 void XMLAttribute::SetName( const char* n )
1375 {
1376     _name.SetStr( n );
1377 }
1378 
1379 
QueryIntValue(int * value) const1380 XMLError XMLAttribute::QueryIntValue( int* value ) const
1381 {
1382     if ( XMLUtil::ToInt( Value(), value )) {
1383         return XML_SUCCESS;
1384     }
1385     return XML_WRONG_ATTRIBUTE_TYPE;
1386 }
1387 
1388 
QueryUnsignedValue(unsigned int * value) const1389 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1390 {
1391     if ( XMLUtil::ToUnsigned( Value(), value )) {
1392         return XML_SUCCESS;
1393     }
1394     return XML_WRONG_ATTRIBUTE_TYPE;
1395 }
1396 
1397 
QueryInt64Value(int64_t * value) const1398 XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
1399 {
1400 	if (XMLUtil::ToInt64(Value(), value)) {
1401 		return XML_SUCCESS;
1402 	}
1403 	return XML_WRONG_ATTRIBUTE_TYPE;
1404 }
1405 
1406 
QueryBoolValue(bool * value) const1407 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1408 {
1409     if ( XMLUtil::ToBool( Value(), value )) {
1410         return XML_SUCCESS;
1411     }
1412     return XML_WRONG_ATTRIBUTE_TYPE;
1413 }
1414 
1415 
QueryFloatValue(float * value) const1416 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1417 {
1418     if ( XMLUtil::ToFloat( Value(), value )) {
1419         return XML_SUCCESS;
1420     }
1421     return XML_WRONG_ATTRIBUTE_TYPE;
1422 }
1423 
1424 
QueryDoubleValue(double * value) const1425 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1426 {
1427     if ( XMLUtil::ToDouble( Value(), value )) {
1428         return XML_SUCCESS;
1429     }
1430     return XML_WRONG_ATTRIBUTE_TYPE;
1431 }
1432 
1433 
SetAttribute(const char * v)1434 void XMLAttribute::SetAttribute( const char* v )
1435 {
1436     _value.SetStr( v );
1437 }
1438 
1439 
SetAttribute(int v)1440 void XMLAttribute::SetAttribute( int v )
1441 {
1442     char buf[BUF_SIZE];
1443     XMLUtil::ToStr( v, buf, BUF_SIZE );
1444     _value.SetStr( buf );
1445 }
1446 
1447 
SetAttribute(unsigned v)1448 void XMLAttribute::SetAttribute( unsigned v )
1449 {
1450     char buf[BUF_SIZE];
1451     XMLUtil::ToStr( v, buf, BUF_SIZE );
1452     _value.SetStr( buf );
1453 }
1454 
1455 
SetAttribute(int64_t v)1456 void XMLAttribute::SetAttribute(int64_t v)
1457 {
1458 	char buf[BUF_SIZE];
1459 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1460 	_value.SetStr(buf);
1461 }
1462 
1463 
1464 
SetAttribute(bool v)1465 void XMLAttribute::SetAttribute( bool v )
1466 {
1467     char buf[BUF_SIZE];
1468     XMLUtil::ToStr( v, buf, BUF_SIZE );
1469     _value.SetStr( buf );
1470 }
1471 
SetAttribute(double v)1472 void XMLAttribute::SetAttribute( double v )
1473 {
1474     char buf[BUF_SIZE];
1475     XMLUtil::ToStr( v, buf, BUF_SIZE );
1476     _value.SetStr( buf );
1477 }
1478 
SetAttribute(float v)1479 void XMLAttribute::SetAttribute( float v )
1480 {
1481     char buf[BUF_SIZE];
1482     XMLUtil::ToStr( v, buf, BUF_SIZE );
1483     _value.SetStr( buf );
1484 }
1485 
1486 
1487 // --------- XMLElement ---------- //
XMLElement(XMLDocument * doc)1488 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1489     _closingType( OPEN ),
1490     _rootAttribute( 0 )
1491 {
1492 }
1493 
1494 
~XMLElement()1495 XMLElement::~XMLElement()
1496 {
1497     while( _rootAttribute ) {
1498         XMLAttribute* next = _rootAttribute->_next;
1499         DeleteAttribute( _rootAttribute );
1500         _rootAttribute = next;
1501     }
1502 }
1503 
1504 
FindAttribute(const char * name) const1505 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1506 {
1507     for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1508         if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1509             return a;
1510         }
1511     }
1512     return 0;
1513 }
1514 
1515 
Attribute(const char * name,const char * value) const1516 const char* XMLElement::Attribute( const char* name, const char* value ) const
1517 {
1518     const XMLAttribute* a = FindAttribute( name );
1519     if ( !a ) {
1520         return 0;
1521     }
1522     if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1523         return a->Value();
1524     }
1525     return 0;
1526 }
1527 
IntAttribute(const char * name,int defaultValue) const1528 int XMLElement::IntAttribute(const char* name, int defaultValue) const
1529 {
1530 	int i = defaultValue;
1531 	QueryIntAttribute(name, &i);
1532 	return i;
1533 }
1534 
UnsignedAttribute(const char * name,unsigned defaultValue) const1535 unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1536 {
1537 	unsigned i = defaultValue;
1538 	QueryUnsignedAttribute(name, &i);
1539 	return i;
1540 }
1541 
Int64Attribute(const char * name,int64_t defaultValue) const1542 int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1543 {
1544 	int64_t i = defaultValue;
1545 	QueryInt64Attribute(name, &i);
1546 	return i;
1547 }
1548 
BoolAttribute(const char * name,bool defaultValue) const1549 bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1550 {
1551 	bool b = defaultValue;
1552 	QueryBoolAttribute(name, &b);
1553 	return b;
1554 }
1555 
DoubleAttribute(const char * name,double defaultValue) const1556 double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1557 {
1558 	double d = defaultValue;
1559 	QueryDoubleAttribute(name, &d);
1560 	return d;
1561 }
1562 
FloatAttribute(const char * name,float defaultValue) const1563 float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1564 {
1565 	float f = defaultValue;
1566 	QueryFloatAttribute(name, &f);
1567 	return f;
1568 }
1569 
GetText() const1570 const char* XMLElement::GetText() const
1571 {
1572     if ( FirstChild() && FirstChild()->ToText() ) {
1573         return FirstChild()->Value();
1574     }
1575     return 0;
1576 }
1577 
1578 
SetText(const char * inText)1579 void	XMLElement::SetText( const char* inText )
1580 {
1581 	if ( FirstChild() && FirstChild()->ToText() )
1582 		FirstChild()->SetValue( inText );
1583 	else {
1584 		XMLText*	theText = GetDocument()->NewText( inText );
1585 		InsertFirstChild( theText );
1586 	}
1587 }
1588 
1589 
SetText(int v)1590 void XMLElement::SetText( int v )
1591 {
1592     char buf[BUF_SIZE];
1593     XMLUtil::ToStr( v, buf, BUF_SIZE );
1594     SetText( buf );
1595 }
1596 
1597 
SetText(unsigned v)1598 void XMLElement::SetText( unsigned v )
1599 {
1600     char buf[BUF_SIZE];
1601     XMLUtil::ToStr( v, buf, BUF_SIZE );
1602     SetText( buf );
1603 }
1604 
1605 
SetText(int64_t v)1606 void XMLElement::SetText(int64_t v)
1607 {
1608 	char buf[BUF_SIZE];
1609 	XMLUtil::ToStr(v, buf, BUF_SIZE);
1610 	SetText(buf);
1611 }
1612 
1613 
SetText(bool v)1614 void XMLElement::SetText( bool v )
1615 {
1616     char buf[BUF_SIZE];
1617     XMLUtil::ToStr( v, buf, BUF_SIZE );
1618     SetText( buf );
1619 }
1620 
1621 
SetText(float v)1622 void XMLElement::SetText( float v )
1623 {
1624     char buf[BUF_SIZE];
1625     XMLUtil::ToStr( v, buf, BUF_SIZE );
1626     SetText( buf );
1627 }
1628 
1629 
SetText(double v)1630 void XMLElement::SetText( double v )
1631 {
1632     char buf[BUF_SIZE];
1633     XMLUtil::ToStr( v, buf, BUF_SIZE );
1634     SetText( buf );
1635 }
1636 
1637 
QueryIntText(int * ival) const1638 XMLError XMLElement::QueryIntText( int* ival ) const
1639 {
1640     if ( FirstChild() && FirstChild()->ToText() ) {
1641         const char* t = FirstChild()->Value();
1642         if ( XMLUtil::ToInt( t, ival ) ) {
1643             return XML_SUCCESS;
1644         }
1645         return XML_CAN_NOT_CONVERT_TEXT;
1646     }
1647     return XML_NO_TEXT_NODE;
1648 }
1649 
1650 
QueryUnsignedText(unsigned * uval) const1651 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1652 {
1653     if ( FirstChild() && FirstChild()->ToText() ) {
1654         const char* t = FirstChild()->Value();
1655         if ( XMLUtil::ToUnsigned( t, uval ) ) {
1656             return XML_SUCCESS;
1657         }
1658         return XML_CAN_NOT_CONVERT_TEXT;
1659     }
1660     return XML_NO_TEXT_NODE;
1661 }
1662 
1663 
QueryInt64Text(int64_t * ival) const1664 XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1665 {
1666 	if (FirstChild() && FirstChild()->ToText()) {
1667 		const char* t = FirstChild()->Value();
1668 		if (XMLUtil::ToInt64(t, ival)) {
1669 			return XML_SUCCESS;
1670 		}
1671 		return XML_CAN_NOT_CONVERT_TEXT;
1672 	}
1673 	return XML_NO_TEXT_NODE;
1674 }
1675 
1676 
QueryBoolText(bool * bval) const1677 XMLError XMLElement::QueryBoolText( bool* bval ) const
1678 {
1679     if ( FirstChild() && FirstChild()->ToText() ) {
1680         const char* t = FirstChild()->Value();
1681         if ( XMLUtil::ToBool( t, bval ) ) {
1682             return XML_SUCCESS;
1683         }
1684         return XML_CAN_NOT_CONVERT_TEXT;
1685     }
1686     return XML_NO_TEXT_NODE;
1687 }
1688 
1689 
QueryDoubleText(double * dval) const1690 XMLError XMLElement::QueryDoubleText( double* dval ) const
1691 {
1692     if ( FirstChild() && FirstChild()->ToText() ) {
1693         const char* t = FirstChild()->Value();
1694         if ( XMLUtil::ToDouble( t, dval ) ) {
1695             return XML_SUCCESS;
1696         }
1697         return XML_CAN_NOT_CONVERT_TEXT;
1698     }
1699     return XML_NO_TEXT_NODE;
1700 }
1701 
1702 
QueryFloatText(float * fval) const1703 XMLError XMLElement::QueryFloatText( float* fval ) const
1704 {
1705     if ( FirstChild() && FirstChild()->ToText() ) {
1706         const char* t = FirstChild()->Value();
1707         if ( XMLUtil::ToFloat( t, fval ) ) {
1708             return XML_SUCCESS;
1709         }
1710         return XML_CAN_NOT_CONVERT_TEXT;
1711     }
1712     return XML_NO_TEXT_NODE;
1713 }
1714 
IntText(int defaultValue) const1715 int XMLElement::IntText(int defaultValue) const
1716 {
1717 	int i = defaultValue;
1718 	QueryIntText(&i);
1719 	return i;
1720 }
1721 
UnsignedText(unsigned defaultValue) const1722 unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1723 {
1724 	unsigned i = defaultValue;
1725 	QueryUnsignedText(&i);
1726 	return i;
1727 }
1728 
Int64Text(int64_t defaultValue) const1729 int64_t XMLElement::Int64Text(int64_t defaultValue) const
1730 {
1731 	int64_t i = defaultValue;
1732 	QueryInt64Text(&i);
1733 	return i;
1734 }
1735 
BoolText(bool defaultValue) const1736 bool XMLElement::BoolText(bool defaultValue) const
1737 {
1738 	bool b = defaultValue;
1739 	QueryBoolText(&b);
1740 	return b;
1741 }
1742 
DoubleText(double defaultValue) const1743 double XMLElement::DoubleText(double defaultValue) const
1744 {
1745 	double d = defaultValue;
1746 	QueryDoubleText(&d);
1747 	return d;
1748 }
1749 
FloatText(float defaultValue) const1750 float XMLElement::FloatText(float defaultValue) const
1751 {
1752 	float f = defaultValue;
1753 	QueryFloatText(&f);
1754 	return f;
1755 }
1756 
1757 
FindOrCreateAttribute(const char * name)1758 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1759 {
1760     XMLAttribute* last = 0;
1761     XMLAttribute* attrib = 0;
1762     for( attrib = _rootAttribute;
1763             attrib;
1764             last = attrib, attrib = attrib->_next ) {
1765         if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1766             break;
1767         }
1768     }
1769     if ( !attrib ) {
1770         attrib = CreateAttribute();
1771         TIXMLASSERT( attrib );
1772         if ( last ) {
1773             TIXMLASSERT( last->_next == 0 );
1774             last->_next = attrib;
1775         }
1776         else {
1777             TIXMLASSERT( _rootAttribute == 0 );
1778             _rootAttribute = attrib;
1779         }
1780         attrib->SetName( name );
1781     }
1782     return attrib;
1783 }
1784 
1785 
DeleteAttribute(const char * name)1786 void XMLElement::DeleteAttribute( const char* name )
1787 {
1788     XMLAttribute* prev = 0;
1789     for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1790         if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1791             if ( prev ) {
1792                 prev->_next = a->_next;
1793             }
1794             else {
1795                 _rootAttribute = a->_next;
1796             }
1797             DeleteAttribute( a );
1798             break;
1799         }
1800         prev = a;
1801     }
1802 }
1803 
1804 
ParseAttributes(char * p,int * curLineNumPtr)1805 char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
1806 {
1807     XMLAttribute* prevAttribute = 0;
1808 
1809     // Read the attributes.
1810     while( p ) {
1811         p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1812         if ( !(*p) ) {
1813             _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
1814             return 0;
1815         }
1816 
1817         // attribute.
1818         if (XMLUtil::IsNameStartChar( *p ) ) {
1819             XMLAttribute* attrib = CreateAttribute();
1820             TIXMLASSERT( attrib );
1821             attrib->_parseLineNum = _document->_parseCurLineNum;
1822 
1823             int attrLineNum = attrib->_parseLineNum;
1824 
1825             p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
1826             if ( !p || Attribute( attrib->Name() ) ) {
1827                 DeleteAttribute( attrib );
1828                 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
1829                 return 0;
1830             }
1831             // There is a minor bug here: if the attribute in the source xml
1832             // document is duplicated, it will not be detected and the
1833             // attribute will be doubly added. However, tracking the 'prevAttribute'
1834             // avoids re-scanning the attribute list. Preferring performance for
1835             // now, may reconsider in the future.
1836             if ( prevAttribute ) {
1837                 TIXMLASSERT( prevAttribute->_next == 0 );
1838                 prevAttribute->_next = attrib;
1839             }
1840             else {
1841                 TIXMLASSERT( _rootAttribute == 0 );
1842                 _rootAttribute = attrib;
1843             }
1844             prevAttribute = attrib;
1845         }
1846         // end of the tag
1847         else if ( *p == '>' ) {
1848             ++p;
1849             break;
1850         }
1851         // end of the tag
1852         else if ( *p == '/' && *(p+1) == '>' ) {
1853             _closingType = CLOSED;
1854             return p+2;	// done; sealed element.
1855         }
1856         else {
1857             _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
1858             return 0;
1859         }
1860     }
1861     return p;
1862 }
1863 
DeleteAttribute(XMLAttribute * attribute)1864 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1865 {
1866     if ( attribute == 0 ) {
1867         return;
1868     }
1869     MemPool* pool = attribute->_memPool;
1870     attribute->~XMLAttribute();
1871     pool->Free( attribute );
1872 }
1873 
CreateAttribute()1874 XMLAttribute* XMLElement::CreateAttribute()
1875 {
1876     TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1877     XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1878     TIXMLASSERT( attrib );
1879     attrib->_memPool = &_document->_attributePool;
1880     attrib->_memPool->SetTracked();
1881     return attrib;
1882 }
1883 
1884 //
1885 //	<ele></ele>
1886 //	<ele>foo<b>bar</b></ele>
1887 //
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)1888 char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
1889 {
1890     // Read the element name.
1891     p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1892 
1893     // The closing element is the </element> form. It is
1894     // parsed just like a regular element then deleted from
1895     // the DOM.
1896     if ( *p == '/' ) {
1897         _closingType = CLOSING;
1898         ++p;
1899     }
1900 
1901     p = _value.ParseName( p );
1902     if ( _value.Empty() ) {
1903         return 0;
1904     }
1905 
1906     p = ParseAttributes( p, curLineNumPtr );
1907     if ( !p || !*p || _closingType != OPEN ) {
1908         return p;
1909     }
1910 
1911     p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
1912     return p;
1913 }
1914 
1915 
1916 
ShallowClone(XMLDocument * doc) const1917 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1918 {
1919     if ( !doc ) {
1920         doc = _document;
1921     }
1922     XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
1923     for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1924         element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
1925     }
1926     return element;
1927 }
1928 
1929 
ShallowEqual(const XMLNode * compare) const1930 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1931 {
1932     TIXMLASSERT( compare );
1933     const XMLElement* other = compare->ToElement();
1934     if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
1935 
1936         const XMLAttribute* a=FirstAttribute();
1937         const XMLAttribute* b=other->FirstAttribute();
1938 
1939         while ( a && b ) {
1940             if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1941                 return false;
1942             }
1943             a = a->Next();
1944             b = b->Next();
1945         }
1946         if ( a || b ) {
1947             // different count
1948             return false;
1949         }
1950         return true;
1951     }
1952     return false;
1953 }
1954 
1955 
Accept(XMLVisitor * visitor) const1956 bool XMLElement::Accept( XMLVisitor* visitor ) const
1957 {
1958     TIXMLASSERT( visitor );
1959     if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1960         for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1961             if ( !node->Accept( visitor ) ) {
1962                 break;
1963             }
1964         }
1965     }
1966     return visitor->VisitExit( *this );
1967 }
1968 
1969 
1970 // --------- XMLDocument ----------- //
1971 
1972 // Warning: List must match 'enum XMLError'
1973 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1974     "XML_SUCCESS",
1975     "XML_NO_ATTRIBUTE",
1976     "XML_WRONG_ATTRIBUTE_TYPE",
1977     "XML_ERROR_FILE_NOT_FOUND",
1978     "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1979     "XML_ERROR_FILE_READ_ERROR",
1980     "UNUSED_XML_ERROR_ELEMENT_MISMATCH",
1981     "XML_ERROR_PARSING_ELEMENT",
1982     "XML_ERROR_PARSING_ATTRIBUTE",
1983     "UNUSED_XML_ERROR_IDENTIFYING_TAG",
1984     "XML_ERROR_PARSING_TEXT",
1985     "XML_ERROR_PARSING_CDATA",
1986     "XML_ERROR_PARSING_COMMENT",
1987     "XML_ERROR_PARSING_DECLARATION",
1988     "XML_ERROR_PARSING_UNKNOWN",
1989     "XML_ERROR_EMPTY_DOCUMENT",
1990     "XML_ERROR_MISMATCHED_ELEMENT",
1991     "XML_ERROR_PARSING",
1992     "XML_CAN_NOT_CONVERT_TEXT",
1993     "XML_NO_TEXT_NODE",
1994 	"XML_ELEMENT_DEPTH_EXCEEDED"
1995 };
1996 
1997 
XMLDocument(bool processEntities,Whitespace whitespaceMode)1998 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
1999     XMLNode( 0 ),
2000     _writeBOM( false ),
2001     _processEntities( processEntities ),
2002     _errorID(XML_SUCCESS),
2003     _whitespaceMode( whitespaceMode ),
2004     _errorStr(),
2005     _errorLineNum( 0 ),
2006     _charBuffer( 0 ),
2007     _parseCurLineNum( 0 ),
2008 	_parsingDepth(0),
2009     _unlinked(),
2010     _elementPool(),
2011     _attributePool(),
2012     _textPool(),
2013     _commentPool()
2014 {
2015     // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2016     _document = this;
2017 }
2018 
2019 
~XMLDocument()2020 XMLDocument::~XMLDocument()
2021 {
2022     Clear();
2023 }
2024 
2025 
MarkInUse(XMLNode * node)2026 void XMLDocument::MarkInUse(XMLNode* node)
2027 {
2028 	TIXMLASSERT(node);
2029 	TIXMLASSERT(node->_parent == 0);
2030 
2031 	for (int i = 0; i < _unlinked.Size(); ++i) {
2032 		if (node == _unlinked[i]) {
2033 			_unlinked.SwapRemove(i);
2034 			break;
2035 		}
2036 	}
2037 }
2038 
Clear()2039 void XMLDocument::Clear()
2040 {
2041     DeleteChildren();
2042 	while( _unlinked.Size()) {
2043 		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
2044 	}
2045 
2046 #ifdef TINYXML2_DEBUG
2047     const bool hadError = Error();
2048 #endif
2049     ClearError();
2050 
2051     delete [] _charBuffer;
2052     _charBuffer = 0;
2053 	_parsingDepth = 0;
2054 
2055 #if 0
2056     _textPool.Trace( "text" );
2057     _elementPool.Trace( "element" );
2058     _commentPool.Trace( "comment" );
2059     _attributePool.Trace( "attribute" );
2060 #endif
2061 
2062 #ifdef TINYXML2_DEBUG
2063     if ( !hadError ) {
2064         TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
2065         TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2066         TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
2067         TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
2068     }
2069 #endif
2070 }
2071 
2072 
DeepCopy(XMLDocument * target) const2073 void XMLDocument::DeepCopy(XMLDocument* target) const
2074 {
2075 	TIXMLASSERT(target);
2076     if (target == this) {
2077         return; // technically success - a no-op.
2078     }
2079 
2080 	target->Clear();
2081 	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2082 		target->InsertEndChild(node->DeepClone(target));
2083 	}
2084 }
2085 
NewElement(const char * name)2086 XMLElement* XMLDocument::NewElement( const char* name )
2087 {
2088     XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
2089     ele->SetName( name );
2090     return ele;
2091 }
2092 
2093 
NewComment(const char * str)2094 XMLComment* XMLDocument::NewComment( const char* str )
2095 {
2096     XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
2097     comment->SetValue( str );
2098     return comment;
2099 }
2100 
2101 
NewText(const char * str)2102 XMLText* XMLDocument::NewText( const char* str )
2103 {
2104     XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
2105     text->SetValue( str );
2106     return text;
2107 }
2108 
2109 
NewDeclaration(const char * str)2110 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2111 {
2112     XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
2113     dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2114     return dec;
2115 }
2116 
2117 
NewUnknown(const char * str)2118 XMLUnknown* XMLDocument::NewUnknown( const char* str )
2119 {
2120     XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
2121     unk->SetValue( str );
2122     return unk;
2123 }
2124 
callfopen(const char * filepath,const char * mode)2125 static FILE* callfopen( const char* filepath, const char* mode )
2126 {
2127     TIXMLASSERT( filepath );
2128     TIXMLASSERT( mode );
2129 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2130     FILE* fp = 0;
2131     errno_t err = fopen_s( &fp, filepath, mode );
2132     if ( err ) {
2133         return 0;
2134     }
2135 #else
2136     FILE* fp = fopen( filepath, mode );
2137 #endif
2138     return fp;
2139 }
2140 
DeleteNode(XMLNode * node)2141 void XMLDocument::DeleteNode( XMLNode* node )	{
2142     TIXMLASSERT( node );
2143     TIXMLASSERT(node->_document == this );
2144     if (node->_parent) {
2145         node->_parent->DeleteChild( node );
2146     }
2147     else {
2148         // Isn't in the tree.
2149         // Use the parent delete.
2150         // Also, we need to mark it tracked: we 'know'
2151         // it was never used.
2152         node->_memPool->SetTracked();
2153         // Call the static XMLNode version:
2154         XMLNode::DeleteNode(node);
2155     }
2156 }
2157 
2158 
LoadFile(const char * filename)2159 XMLError XMLDocument::LoadFile( const char* filename )
2160 {
2161     if ( !filename ) {
2162         TIXMLASSERT( false );
2163         SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2164         return _errorID;
2165     }
2166 
2167     Clear();
2168     FILE* fp = callfopen( filename, "rb" );
2169     if ( !fp ) {
2170         SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
2171         return _errorID;
2172     }
2173     LoadFile( fp );
2174     fclose( fp );
2175     return _errorID;
2176 }
2177 
2178 // This is likely overengineered template art to have a check that unsigned long value incremented
2179 // by one still fits into size_t. If size_t type is larger than unsigned long type
2180 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2181 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2182 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2183 // types sizes relate to each other.
2184 template
2185 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
2186 struct LongFitsIntoSizeTMinusOne {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2187     static bool Fits( unsigned long value )
2188     {
2189         return value < (size_t)-1;
2190     }
2191 };
2192 
2193 template <>
2194 struct LongFitsIntoSizeTMinusOne<false> {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2195     static bool Fits( unsigned long )
2196     {
2197         return true;
2198     }
2199 };
2200 
LoadFile(FILE * fp)2201 XMLError XMLDocument::LoadFile( FILE* fp )
2202 {
2203     Clear();
2204 
2205     fseek( fp, 0, SEEK_SET );
2206     if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2207         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2208         return _errorID;
2209     }
2210 
2211     fseek( fp, 0, SEEK_END );
2212     const long filelength = ftell( fp );
2213     fseek( fp, 0, SEEK_SET );
2214     if ( filelength == -1L ) {
2215         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2216         return _errorID;
2217     }
2218     TIXMLASSERT( filelength >= 0 );
2219 
2220     if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
2221         // Cannot handle files which won't fit in buffer together with null terminator
2222         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2223         return _errorID;
2224     }
2225 
2226     if ( filelength == 0 ) {
2227         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2228         return _errorID;
2229     }
2230 
2231     const size_t size = filelength;
2232     TIXMLASSERT( _charBuffer == 0 );
2233     _charBuffer = new char[size+1];
2234     size_t read = fread( _charBuffer, 1, size, fp );
2235     if ( read != size ) {
2236         SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2237         return _errorID;
2238     }
2239 
2240     _charBuffer[size] = 0;
2241 
2242     Parse();
2243     return _errorID;
2244 }
2245 
2246 
SaveFile(const char * filename,bool compact)2247 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
2248 {
2249     if ( !filename ) {
2250         TIXMLASSERT( false );
2251         SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2252         return _errorID;
2253     }
2254 
2255     FILE* fp = callfopen( filename, "w" );
2256     if ( !fp ) {
2257         SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
2258         return _errorID;
2259     }
2260     SaveFile(fp, compact);
2261     fclose( fp );
2262     return _errorID;
2263 }
2264 
2265 
SaveFile(FILE * fp,bool compact)2266 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2267 {
2268     // Clear any error from the last save, otherwise it will get reported
2269     // for *this* call.
2270     ClearError();
2271     XMLPrinter stream( fp, compact );
2272     Print( &stream );
2273     return _errorID;
2274 }
2275 
2276 
Parse(const char * p,size_t len)2277 XMLError XMLDocument::Parse( const char* p, size_t len )
2278 {
2279     Clear();
2280 
2281     if ( len == 0 || !p || !*p ) {
2282         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2283         return _errorID;
2284     }
2285     if ( len == (size_t)(-1) ) {
2286         len = strlen( p );
2287     }
2288     TIXMLASSERT( _charBuffer == 0 );
2289     _charBuffer = new char[ len+1 ];
2290     memcpy( _charBuffer, p, len );
2291     _charBuffer[len] = 0;
2292 
2293     Parse();
2294     if ( Error() ) {
2295         // clean up now essentially dangling memory.
2296         // and the parse fail can put objects in the
2297         // pools that are dead and inaccessible.
2298         DeleteChildren();
2299         _elementPool.Clear();
2300         _attributePool.Clear();
2301         _textPool.Clear();
2302         _commentPool.Clear();
2303     }
2304     return _errorID;
2305 }
2306 
2307 
Print(XMLPrinter * streamer) const2308 void XMLDocument::Print( XMLPrinter* streamer ) const
2309 {
2310     if ( streamer ) {
2311         Accept( streamer );
2312     }
2313     else {
2314         XMLPrinter stdoutStreamer( stdout );
2315         Accept( &stdoutStreamer );
2316     }
2317 }
2318 
2319 
SetError(XMLError error,int lineNum,const char * format,...)2320 void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
2321 {
2322     TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2323     _errorID = error;
2324     _errorLineNum = lineNum;
2325 	_errorStr.Reset();
2326 
2327     size_t BUFFER_SIZE = 1000;
2328     char* buffer = new char[BUFFER_SIZE];
2329 
2330     TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
2331 
2332 	if (format) {
2333 		size_t len = strlen(buffer);
2334 		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2335 		len = strlen(buffer);
2336 
2337 		va_list va;
2338 		va_start(va, format);
2339 		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2340 		va_end(va);
2341 	}
2342 	_errorStr.SetStr(buffer);
2343 	delete[] buffer;
2344 }
2345 
2346 
ErrorIDToName(XMLError errorID)2347 /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2348 {
2349 	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2350     const char* errorName = _errorNames[errorID];
2351     TIXMLASSERT( errorName && errorName[0] );
2352     return errorName;
2353 }
2354 
ErrorStr() const2355 const char* XMLDocument::ErrorStr() const
2356 {
2357 	return _errorStr.Empty() ? "" : _errorStr.GetStr();
2358 }
2359 
2360 
PrintError() const2361 void XMLDocument::PrintError() const
2362 {
2363     printf("%s\n", ErrorStr());
2364 }
2365 
ErrorName() const2366 const char* XMLDocument::ErrorName() const
2367 {
2368     return ErrorIDToName(_errorID);
2369 }
2370 
Parse()2371 void XMLDocument::Parse()
2372 {
2373     TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2374     TIXMLASSERT( _charBuffer );
2375     _parseCurLineNum = 1;
2376     _parseLineNum = 1;
2377     char* p = _charBuffer;
2378     p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2379     p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2380     if ( !*p ) {
2381         SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2382         return;
2383     }
2384     ParseDeep(p, 0, &_parseCurLineNum );
2385 }
2386 
PushDepth()2387 void XMLDocument::PushDepth()
2388 {
2389 	_parsingDepth++;
2390 	if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2391 		SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2392 	}
2393 }
2394 
PopDepth()2395 void XMLDocument::PopDepth()
2396 {
2397 	TIXMLASSERT(_parsingDepth > 0);
2398 	--_parsingDepth;
2399 }
2400 
XMLPrinter(FILE * file,bool compact,int depth)2401 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2402     _elementJustOpened( false ),
2403     _stack(),
2404     _firstElement( true ),
2405     _fp( file ),
2406     _depth( depth ),
2407     _textDepth( -1 ),
2408     _processEntities( true ),
2409     _compactMode( compact ),
2410     _buffer()
2411 {
2412     for( int i=0; i<ENTITY_RANGE; ++i ) {
2413         _entityFlag[i] = false;
2414         _restrictedEntityFlag[i] = false;
2415     }
2416     for( int i=0; i<NUM_ENTITIES; ++i ) {
2417         const char entityValue = entities[i].value;
2418         const unsigned char flagIndex = (unsigned char)entityValue;
2419         TIXMLASSERT( flagIndex < ENTITY_RANGE );
2420         _entityFlag[flagIndex] = true;
2421     }
2422     _restrictedEntityFlag[(unsigned char)'&'] = true;
2423     _restrictedEntityFlag[(unsigned char)'<'] = true;
2424     _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
2425     _buffer.Push( 0 );
2426 }
2427 
2428 
Print(const char * format,...)2429 void XMLPrinter::Print( const char* format, ... )
2430 {
2431     va_list     va;
2432     va_start( va, format );
2433 
2434     if ( _fp ) {
2435         vfprintf( _fp, format, va );
2436     }
2437     else {
2438         const int len = TIXML_VSCPRINTF( format, va );
2439         // Close out and re-start the va-args
2440         va_end( va );
2441         TIXMLASSERT( len >= 0 );
2442         va_start( va, format );
2443         TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2444         char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
2445 		TIXML_VSNPRINTF( p, len+1, format, va );
2446     }
2447     va_end( va );
2448 }
2449 
2450 
Write(const char * data,size_t size)2451 void XMLPrinter::Write( const char* data, size_t size )
2452 {
2453     if ( _fp ) {
2454         fwrite ( data , sizeof(char), size, _fp);
2455     }
2456     else {
2457         char* p = _buffer.PushArr( static_cast<int>(size) ) - 1;   // back up over the null terminator.
2458         memcpy( p, data, size );
2459         p[size] = 0;
2460     }
2461 }
2462 
2463 
Putc(char ch)2464 void XMLPrinter::Putc( char ch )
2465 {
2466     if ( _fp ) {
2467         fputc ( ch, _fp);
2468     }
2469     else {
2470         char* p = _buffer.PushArr( sizeof(char) ) - 1;   // back up over the null terminator.
2471         p[0] = ch;
2472         p[1] = 0;
2473     }
2474 }
2475 
2476 
PrintSpace(int depth)2477 void XMLPrinter::PrintSpace( int depth )
2478 {
2479     for( int i=0; i<depth; ++i ) {
2480         Write( "    " );
2481     }
2482 }
2483 
2484 
PrintString(const char * p,bool restricted)2485 void XMLPrinter::PrintString( const char* p, bool restricted )
2486 {
2487     // Look for runs of bytes between entities to print.
2488     const char* q = p;
2489 
2490     if ( _processEntities ) {
2491         const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2492         while ( *q ) {
2493             TIXMLASSERT( p <= q );
2494             // Remember, char is sometimes signed. (How many times has that bitten me?)
2495             if ( *q > 0 && *q < ENTITY_RANGE ) {
2496                 // Check for entities. If one is found, flush
2497                 // the stream up until the entity, write the
2498                 // entity, and keep looking.
2499                 if ( flag[(unsigned char)(*q)] ) {
2500                     while ( p < q ) {
2501                         const size_t delta = q - p;
2502                         const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2503                         Write( p, toPrint );
2504                         p += toPrint;
2505                     }
2506                     bool entityPatternPrinted = false;
2507                     for( int i=0; i<NUM_ENTITIES; ++i ) {
2508                         if ( entities[i].value == *q ) {
2509                             Putc( '&' );
2510                             Write( entities[i].pattern, entities[i].length );
2511                             Putc( ';' );
2512                             entityPatternPrinted = true;
2513                             break;
2514                         }
2515                     }
2516                     if ( !entityPatternPrinted ) {
2517                         // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2518                         TIXMLASSERT( false );
2519                     }
2520                     ++p;
2521                 }
2522             }
2523             ++q;
2524             TIXMLASSERT( p <= q );
2525         }
2526     }
2527     // Flush the remaining string. This will be the entire
2528     // string if an entity wasn't found.
2529     TIXMLASSERT( p <= q );
2530     if ( !_processEntities || ( p < q ) ) {
2531         const size_t delta = q - p;
2532         const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2533         Write( p, toPrint );
2534     }
2535 }
2536 
2537 
PushHeader(bool writeBOM,bool writeDec)2538 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2539 {
2540     if ( writeBOM ) {
2541         static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2542         Write( reinterpret_cast< const char* >( bom ) );
2543     }
2544     if ( writeDec ) {
2545         PushDeclaration( "xml version=\"1.0\"" );
2546     }
2547 }
2548 
2549 
OpenElement(const char * name,bool compactMode)2550 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2551 {
2552     SealElementIfJustOpened();
2553     _stack.Push( name );
2554 
2555     if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2556         Putc( '\n' );
2557     }
2558     if ( !compactMode ) {
2559         PrintSpace( _depth );
2560     }
2561 
2562     Write ( "<" );
2563     Write ( name );
2564 
2565     _elementJustOpened = true;
2566     _firstElement = false;
2567     ++_depth;
2568 }
2569 
2570 
PushAttribute(const char * name,const char * value)2571 void XMLPrinter::PushAttribute( const char* name, const char* value )
2572 {
2573     TIXMLASSERT( _elementJustOpened );
2574     Putc ( ' ' );
2575     Write( name );
2576     Write( "=\"" );
2577     PrintString( value, false );
2578     Putc ( '\"' );
2579 }
2580 
2581 
PushAttribute(const char * name,int v)2582 void XMLPrinter::PushAttribute( const char* name, int v )
2583 {
2584     char buf[BUF_SIZE];
2585     XMLUtil::ToStr( v, buf, BUF_SIZE );
2586     PushAttribute( name, buf );
2587 }
2588 
2589 
PushAttribute(const char * name,unsigned v)2590 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2591 {
2592     char buf[BUF_SIZE];
2593     XMLUtil::ToStr( v, buf, BUF_SIZE );
2594     PushAttribute( name, buf );
2595 }
2596 
2597 
PushAttribute(const char * name,int64_t v)2598 void XMLPrinter::PushAttribute(const char* name, int64_t v)
2599 {
2600 	char buf[BUF_SIZE];
2601 	XMLUtil::ToStr(v, buf, BUF_SIZE);
2602 	PushAttribute(name, buf);
2603 }
2604 
2605 
PushAttribute(const char * name,bool v)2606 void XMLPrinter::PushAttribute( const char* name, bool v )
2607 {
2608     char buf[BUF_SIZE];
2609     XMLUtil::ToStr( v, buf, BUF_SIZE );
2610     PushAttribute( name, buf );
2611 }
2612 
2613 
PushAttribute(const char * name,double v)2614 void XMLPrinter::PushAttribute( const char* name, double v )
2615 {
2616     char buf[BUF_SIZE];
2617     XMLUtil::ToStr( v, buf, BUF_SIZE );
2618     PushAttribute( name, buf );
2619 }
2620 
2621 
CloseElement(bool compactMode)2622 void XMLPrinter::CloseElement( bool compactMode )
2623 {
2624     --_depth;
2625     const char* name = _stack.Pop();
2626 
2627     if ( _elementJustOpened ) {
2628         Write( "/>" );
2629     }
2630     else {
2631         if ( _textDepth < 0 && !compactMode) {
2632             Putc( '\n' );
2633             PrintSpace( _depth );
2634         }
2635         Write ( "</" );
2636         Write ( name );
2637         Write ( ">" );
2638     }
2639 
2640     if ( _textDepth == _depth ) {
2641         _textDepth = -1;
2642     }
2643     if ( _depth == 0 && !compactMode) {
2644         Putc( '\n' );
2645     }
2646     _elementJustOpened = false;
2647 }
2648 
2649 
SealElementIfJustOpened()2650 void XMLPrinter::SealElementIfJustOpened()
2651 {
2652     if ( !_elementJustOpened ) {
2653         return;
2654     }
2655     _elementJustOpened = false;
2656     Putc( '>' );
2657 }
2658 
2659 
PushText(const char * text,bool cdata)2660 void XMLPrinter::PushText( const char* text, bool cdata )
2661 {
2662     _textDepth = _depth-1;
2663 
2664     SealElementIfJustOpened();
2665     if ( cdata ) {
2666         Write( "<![CDATA[" );
2667         Write( text );
2668         Write( "]]>" );
2669     }
2670     else {
2671         PrintString( text, true );
2672     }
2673 }
2674 
PushText(int64_t value)2675 void XMLPrinter::PushText( int64_t value )
2676 {
2677     char buf[BUF_SIZE];
2678     XMLUtil::ToStr( value, buf, BUF_SIZE );
2679     PushText( buf, false );
2680 }
2681 
PushText(int value)2682 void XMLPrinter::PushText( int value )
2683 {
2684     char buf[BUF_SIZE];
2685     XMLUtil::ToStr( value, buf, BUF_SIZE );
2686     PushText( buf, false );
2687 }
2688 
2689 
PushText(unsigned value)2690 void XMLPrinter::PushText( unsigned value )
2691 {
2692     char buf[BUF_SIZE];
2693     XMLUtil::ToStr( value, buf, BUF_SIZE );
2694     PushText( buf, false );
2695 }
2696 
2697 
PushText(bool value)2698 void XMLPrinter::PushText( bool value )
2699 {
2700     char buf[BUF_SIZE];
2701     XMLUtil::ToStr( value, buf, BUF_SIZE );
2702     PushText( buf, false );
2703 }
2704 
2705 
PushText(float value)2706 void XMLPrinter::PushText( float value )
2707 {
2708     char buf[BUF_SIZE];
2709     XMLUtil::ToStr( value, buf, BUF_SIZE );
2710     PushText( buf, false );
2711 }
2712 
2713 
PushText(double value)2714 void XMLPrinter::PushText( double value )
2715 {
2716     char buf[BUF_SIZE];
2717     XMLUtil::ToStr( value, buf, BUF_SIZE );
2718     PushText( buf, false );
2719 }
2720 
2721 
PushComment(const char * comment)2722 void XMLPrinter::PushComment( const char* comment )
2723 {
2724     SealElementIfJustOpened();
2725     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2726         Putc( '\n' );
2727         PrintSpace( _depth );
2728     }
2729     _firstElement = false;
2730 
2731     Write( "<!--" );
2732     Write( comment );
2733     Write( "-->" );
2734 }
2735 
2736 
PushDeclaration(const char * value)2737 void XMLPrinter::PushDeclaration( const char* value )
2738 {
2739     SealElementIfJustOpened();
2740     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2741         Putc( '\n' );
2742         PrintSpace( _depth );
2743     }
2744     _firstElement = false;
2745 
2746     Write( "<?" );
2747     Write( value );
2748     Write( "?>" );
2749 }
2750 
2751 
PushUnknown(const char * value)2752 void XMLPrinter::PushUnknown( const char* value )
2753 {
2754     SealElementIfJustOpened();
2755     if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2756         Putc( '\n' );
2757         PrintSpace( _depth );
2758     }
2759     _firstElement = false;
2760 
2761     Write( "<!" );
2762     Write( value );
2763     Putc( '>' );
2764 }
2765 
2766 
VisitEnter(const XMLDocument & doc)2767 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2768 {
2769     _processEntities = doc.ProcessEntities();
2770     if ( doc.HasBOM() ) {
2771         PushHeader( true, false );
2772     }
2773     return true;
2774 }
2775 
2776 
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2777 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2778 {
2779     const XMLElement* parentElem = 0;
2780     if ( element.Parent() ) {
2781         parentElem = element.Parent()->ToElement();
2782     }
2783     const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2784     OpenElement( element.Name(), compactMode );
2785     while ( attribute ) {
2786         PushAttribute( attribute->Name(), attribute->Value() );
2787         attribute = attribute->Next();
2788     }
2789     return true;
2790 }
2791 
2792 
VisitExit(const XMLElement & element)2793 bool XMLPrinter::VisitExit( const XMLElement& element )
2794 {
2795     CloseElement( CompactMode(element) );
2796     return true;
2797 }
2798 
2799 
Visit(const XMLText & text)2800 bool XMLPrinter::Visit( const XMLText& text )
2801 {
2802     PushText( text.Value(), text.CData() );
2803     return true;
2804 }
2805 
2806 
Visit(const XMLComment & comment)2807 bool XMLPrinter::Visit( const XMLComment& comment )
2808 {
2809     PushComment( comment.Value() );
2810     return true;
2811 }
2812 
Visit(const XMLDeclaration & declaration)2813 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2814 {
2815     PushDeclaration( declaration.Value() );
2816     return true;
2817 }
2818 
2819 
Visit(const XMLUnknown & unknown)2820 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2821 {
2822     PushUnknown( unknown.Value() );
2823     return true;
2824 }
2825 
2826 }   // namespace tinyxml2
2827