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 // 中 or 中
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