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 //
1036 // Multiple declarations are allowed but all declarations
1037 // must occur before anything else.
1038 //
1039 // Optimized due to a security test case. If the first node is
1040 // a declaration, and the last node is a declaration, then only
1041 // declarations have so far been addded.
1042 bool wellLocated = false;
1043
1044 if (ToDocument()) {
1045 if (FirstChild()) {
1046 wellLocated =
1047 FirstChild() &&
1048 FirstChild()->ToDeclaration() &&
1049 LastChild() &&
1050 LastChild()->ToDeclaration();
1051 }
1052 else {
1053 wellLocated = true;
1054 }
1055 }
1056 if ( !wellLocated ) {
1057 _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1058 DeleteNode( node );
1059 break;
1060 }
1061 }
1062
1063 XMLElement* ele = node->ToElement();
1064 if ( ele ) {
1065 // We read the end tag. Return it to the parent.
1066 if ( ele->ClosingType() == XMLElement::CLOSING ) {
1067 if ( parentEndTag ) {
1068 ele->_value.TransferTo( parentEndTag );
1069 }
1070 node->_memPool->SetTracked(); // created and then immediately deleted.
1071 DeleteNode( node );
1072 return p;
1073 }
1074
1075 // Handle an end tag returned to this level.
1076 // And handle a bunch of annoying errors.
1077 bool mismatch = false;
1078 if ( endTag.Empty() ) {
1079 if ( ele->ClosingType() == XMLElement::OPEN ) {
1080 mismatch = true;
1081 }
1082 }
1083 else {
1084 if ( ele->ClosingType() != XMLElement::OPEN ) {
1085 mismatch = true;
1086 }
1087 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1088 mismatch = true;
1089 }
1090 }
1091 if ( mismatch ) {
1092 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1093 DeleteNode( node );
1094 break;
1095 }
1096 }
1097 InsertEndChild( node );
1098 }
1099 return 0;
1100 }
1101
DeleteNode(XMLNode * node)1102 /*static*/ void XMLNode::DeleteNode( XMLNode* node )
1103 {
1104 if ( node == 0 ) {
1105 return;
1106 }
1107 TIXMLASSERT(node->_document);
1108 if (!node->ToDocument()) {
1109 node->_document->MarkInUse(node);
1110 }
1111
1112 MemPool* pool = node->_memPool;
1113 node->~XMLNode();
1114 pool->Free( node );
1115 }
1116
InsertChildPreamble(XMLNode * insertThis) const1117 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1118 {
1119 TIXMLASSERT( insertThis );
1120 TIXMLASSERT( insertThis->_document == _document );
1121
1122 if (insertThis->_parent) {
1123 insertThis->_parent->Unlink( insertThis );
1124 }
1125 else {
1126 insertThis->_document->MarkInUse(insertThis);
1127 insertThis->_memPool->SetTracked();
1128 }
1129 }
1130
ToElementWithName(const char * name) const1131 const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1132 {
1133 const XMLElement* element = this->ToElement();
1134 if ( element == 0 ) {
1135 return 0;
1136 }
1137 if ( name == 0 ) {
1138 return element;
1139 }
1140 if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1141 return element;
1142 }
1143 return 0;
1144 }
1145
1146 // --------- XMLText ---------- //
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1147 char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1148 {
1149 if ( this->CData() ) {
1150 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1151 if ( !p ) {
1152 _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
1153 }
1154 return p;
1155 }
1156 else {
1157 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1158 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1159 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1160 }
1161
1162 p = _value.ParseText( p, "<", flags, curLineNumPtr );
1163 if ( p && *p ) {
1164 return p-1;
1165 }
1166 if ( !p ) {
1167 _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1168 }
1169 }
1170 return 0;
1171 }
1172
1173
ShallowClone(XMLDocument * doc) const1174 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1175 {
1176 if ( !doc ) {
1177 doc = _document;
1178 }
1179 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
1180 text->SetCData( this->CData() );
1181 return text;
1182 }
1183
1184
ShallowEqual(const XMLNode * compare) const1185 bool XMLText::ShallowEqual( const XMLNode* compare ) const
1186 {
1187 TIXMLASSERT( compare );
1188 const XMLText* text = compare->ToText();
1189 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1190 }
1191
1192
Accept(XMLVisitor * visitor) const1193 bool XMLText::Accept( XMLVisitor* visitor ) const
1194 {
1195 TIXMLASSERT( visitor );
1196 return visitor->Visit( *this );
1197 }
1198
1199
1200 // --------- XMLComment ---------- //
1201
XMLComment(XMLDocument * doc)1202 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1203 {
1204 }
1205
1206
~XMLComment()1207 XMLComment::~XMLComment()
1208 {
1209 }
1210
1211
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1212 char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1213 {
1214 // Comment parses as text.
1215 p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
1216 if ( p == 0 ) {
1217 _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
1218 }
1219 return p;
1220 }
1221
1222
ShallowClone(XMLDocument * doc) const1223 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1224 {
1225 if ( !doc ) {
1226 doc = _document;
1227 }
1228 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
1229 return comment;
1230 }
1231
1232
ShallowEqual(const XMLNode * compare) const1233 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1234 {
1235 TIXMLASSERT( compare );
1236 const XMLComment* comment = compare->ToComment();
1237 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1238 }
1239
1240
Accept(XMLVisitor * visitor) const1241 bool XMLComment::Accept( XMLVisitor* visitor ) const
1242 {
1243 TIXMLASSERT( visitor );
1244 return visitor->Visit( *this );
1245 }
1246
1247
1248 // --------- XMLDeclaration ---------- //
1249
XMLDeclaration(XMLDocument * doc)1250 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1251 {
1252 }
1253
1254
~XMLDeclaration()1255 XMLDeclaration::~XMLDeclaration()
1256 {
1257 //printf( "~XMLDeclaration\n" );
1258 }
1259
1260
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1261 char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1262 {
1263 // Declaration parses as text.
1264 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1265 if ( p == 0 ) {
1266 _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
1267 }
1268 return p;
1269 }
1270
1271
ShallowClone(XMLDocument * doc) const1272 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1273 {
1274 if ( !doc ) {
1275 doc = _document;
1276 }
1277 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1278 return dec;
1279 }
1280
1281
ShallowEqual(const XMLNode * compare) const1282 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1283 {
1284 TIXMLASSERT( compare );
1285 const XMLDeclaration* declaration = compare->ToDeclaration();
1286 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1287 }
1288
1289
1290
Accept(XMLVisitor * visitor) const1291 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1292 {
1293 TIXMLASSERT( visitor );
1294 return visitor->Visit( *this );
1295 }
1296
1297 // --------- XMLUnknown ---------- //
1298
XMLUnknown(XMLDocument * doc)1299 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1300 {
1301 }
1302
1303
~XMLUnknown()1304 XMLUnknown::~XMLUnknown()
1305 {
1306 }
1307
1308
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1309 char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1310 {
1311 // Unknown parses as text.
1312 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1313 if ( !p ) {
1314 _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
1315 }
1316 return p;
1317 }
1318
1319
ShallowClone(XMLDocument * doc) const1320 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1321 {
1322 if ( !doc ) {
1323 doc = _document;
1324 }
1325 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1326 return text;
1327 }
1328
1329
ShallowEqual(const XMLNode * compare) const1330 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1331 {
1332 TIXMLASSERT( compare );
1333 const XMLUnknown* unknown = compare->ToUnknown();
1334 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1335 }
1336
1337
Accept(XMLVisitor * visitor) const1338 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1339 {
1340 TIXMLASSERT( visitor );
1341 return visitor->Visit( *this );
1342 }
1343
1344 // --------- XMLAttribute ---------- //
1345
Name() const1346 const char* XMLAttribute::Name() const
1347 {
1348 return _name.GetStr();
1349 }
1350
Value() const1351 const char* XMLAttribute::Value() const
1352 {
1353 return _value.GetStr();
1354 }
1355
ParseDeep(char * p,bool processEntities,int * curLineNumPtr)1356 char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
1357 {
1358 // Parse using the name rules: bug fix, was using ParseText before
1359 p = _name.ParseName( p );
1360 if ( !p || !*p ) {
1361 return 0;
1362 }
1363
1364 // Skip white space before =
1365 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1366 if ( *p != '=' ) {
1367 return 0;
1368 }
1369
1370 ++p; // move up to opening quote
1371 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1372 if ( *p != '\"' && *p != '\'' ) {
1373 return 0;
1374 }
1375
1376 char endTag[2] = { *p, 0 };
1377 ++p; // move past opening quote
1378
1379 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
1380 return p;
1381 }
1382
1383
SetName(const char * n)1384 void XMLAttribute::SetName( const char* n )
1385 {
1386 _name.SetStr( n );
1387 }
1388
1389
QueryIntValue(int * value) const1390 XMLError XMLAttribute::QueryIntValue( int* value ) const
1391 {
1392 if ( XMLUtil::ToInt( Value(), value )) {
1393 return XML_SUCCESS;
1394 }
1395 return XML_WRONG_ATTRIBUTE_TYPE;
1396 }
1397
1398
QueryUnsignedValue(unsigned int * value) const1399 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1400 {
1401 if ( XMLUtil::ToUnsigned( Value(), value )) {
1402 return XML_SUCCESS;
1403 }
1404 return XML_WRONG_ATTRIBUTE_TYPE;
1405 }
1406
1407
QueryInt64Value(int64_t * value) const1408 XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
1409 {
1410 if (XMLUtil::ToInt64(Value(), value)) {
1411 return XML_SUCCESS;
1412 }
1413 return XML_WRONG_ATTRIBUTE_TYPE;
1414 }
1415
1416
QueryBoolValue(bool * value) const1417 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1418 {
1419 if ( XMLUtil::ToBool( Value(), value )) {
1420 return XML_SUCCESS;
1421 }
1422 return XML_WRONG_ATTRIBUTE_TYPE;
1423 }
1424
1425
QueryFloatValue(float * value) const1426 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1427 {
1428 if ( XMLUtil::ToFloat( Value(), value )) {
1429 return XML_SUCCESS;
1430 }
1431 return XML_WRONG_ATTRIBUTE_TYPE;
1432 }
1433
1434
QueryDoubleValue(double * value) const1435 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1436 {
1437 if ( XMLUtil::ToDouble( Value(), value )) {
1438 return XML_SUCCESS;
1439 }
1440 return XML_WRONG_ATTRIBUTE_TYPE;
1441 }
1442
1443
SetAttribute(const char * v)1444 void XMLAttribute::SetAttribute( const char* v )
1445 {
1446 _value.SetStr( v );
1447 }
1448
1449
SetAttribute(int v)1450 void XMLAttribute::SetAttribute( int v )
1451 {
1452 char buf[BUF_SIZE];
1453 XMLUtil::ToStr( v, buf, BUF_SIZE );
1454 _value.SetStr( buf );
1455 }
1456
1457
SetAttribute(unsigned v)1458 void XMLAttribute::SetAttribute( unsigned v )
1459 {
1460 char buf[BUF_SIZE];
1461 XMLUtil::ToStr( v, buf, BUF_SIZE );
1462 _value.SetStr( buf );
1463 }
1464
1465
SetAttribute(int64_t v)1466 void XMLAttribute::SetAttribute(int64_t v)
1467 {
1468 char buf[BUF_SIZE];
1469 XMLUtil::ToStr(v, buf, BUF_SIZE);
1470 _value.SetStr(buf);
1471 }
1472
1473
1474
SetAttribute(bool v)1475 void XMLAttribute::SetAttribute( bool v )
1476 {
1477 char buf[BUF_SIZE];
1478 XMLUtil::ToStr( v, buf, BUF_SIZE );
1479 _value.SetStr( buf );
1480 }
1481
SetAttribute(double v)1482 void XMLAttribute::SetAttribute( double v )
1483 {
1484 char buf[BUF_SIZE];
1485 XMLUtil::ToStr( v, buf, BUF_SIZE );
1486 _value.SetStr( buf );
1487 }
1488
SetAttribute(float v)1489 void XMLAttribute::SetAttribute( float v )
1490 {
1491 char buf[BUF_SIZE];
1492 XMLUtil::ToStr( v, buf, BUF_SIZE );
1493 _value.SetStr( buf );
1494 }
1495
1496
1497 // --------- XMLElement ---------- //
XMLElement(XMLDocument * doc)1498 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1499 _closingType( OPEN ),
1500 _rootAttribute( 0 )
1501 {
1502 }
1503
1504
~XMLElement()1505 XMLElement::~XMLElement()
1506 {
1507 while( _rootAttribute ) {
1508 XMLAttribute* next = _rootAttribute->_next;
1509 DeleteAttribute( _rootAttribute );
1510 _rootAttribute = next;
1511 }
1512 }
1513
1514
FindAttribute(const char * name) const1515 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1516 {
1517 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1518 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1519 return a;
1520 }
1521 }
1522 return 0;
1523 }
1524
1525
Attribute(const char * name,const char * value) const1526 const char* XMLElement::Attribute( const char* name, const char* value ) const
1527 {
1528 const XMLAttribute* a = FindAttribute( name );
1529 if ( !a ) {
1530 return 0;
1531 }
1532 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1533 return a->Value();
1534 }
1535 return 0;
1536 }
1537
IntAttribute(const char * name,int defaultValue) const1538 int XMLElement::IntAttribute(const char* name, int defaultValue) const
1539 {
1540 int i = defaultValue;
1541 QueryIntAttribute(name, &i);
1542 return i;
1543 }
1544
UnsignedAttribute(const char * name,unsigned defaultValue) const1545 unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1546 {
1547 unsigned i = defaultValue;
1548 QueryUnsignedAttribute(name, &i);
1549 return i;
1550 }
1551
Int64Attribute(const char * name,int64_t defaultValue) const1552 int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1553 {
1554 int64_t i = defaultValue;
1555 QueryInt64Attribute(name, &i);
1556 return i;
1557 }
1558
BoolAttribute(const char * name,bool defaultValue) const1559 bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1560 {
1561 bool b = defaultValue;
1562 QueryBoolAttribute(name, &b);
1563 return b;
1564 }
1565
DoubleAttribute(const char * name,double defaultValue) const1566 double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1567 {
1568 double d = defaultValue;
1569 QueryDoubleAttribute(name, &d);
1570 return d;
1571 }
1572
FloatAttribute(const char * name,float defaultValue) const1573 float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1574 {
1575 float f = defaultValue;
1576 QueryFloatAttribute(name, &f);
1577 return f;
1578 }
1579
GetText() const1580 const char* XMLElement::GetText() const
1581 {
1582 if ( FirstChild() && FirstChild()->ToText() ) {
1583 return FirstChild()->Value();
1584 }
1585 return 0;
1586 }
1587
1588
SetText(const char * inText)1589 void XMLElement::SetText( const char* inText )
1590 {
1591 if ( FirstChild() && FirstChild()->ToText() )
1592 FirstChild()->SetValue( inText );
1593 else {
1594 XMLText* theText = GetDocument()->NewText( inText );
1595 InsertFirstChild( theText );
1596 }
1597 }
1598
1599
SetText(int v)1600 void XMLElement::SetText( int v )
1601 {
1602 char buf[BUF_SIZE];
1603 XMLUtil::ToStr( v, buf, BUF_SIZE );
1604 SetText( buf );
1605 }
1606
1607
SetText(unsigned v)1608 void XMLElement::SetText( unsigned v )
1609 {
1610 char buf[BUF_SIZE];
1611 XMLUtil::ToStr( v, buf, BUF_SIZE );
1612 SetText( buf );
1613 }
1614
1615
SetText(int64_t v)1616 void XMLElement::SetText(int64_t v)
1617 {
1618 char buf[BUF_SIZE];
1619 XMLUtil::ToStr(v, buf, BUF_SIZE);
1620 SetText(buf);
1621 }
1622
1623
SetText(bool v)1624 void XMLElement::SetText( bool v )
1625 {
1626 char buf[BUF_SIZE];
1627 XMLUtil::ToStr( v, buf, BUF_SIZE );
1628 SetText( buf );
1629 }
1630
1631
SetText(float v)1632 void XMLElement::SetText( float v )
1633 {
1634 char buf[BUF_SIZE];
1635 XMLUtil::ToStr( v, buf, BUF_SIZE );
1636 SetText( buf );
1637 }
1638
1639
SetText(double v)1640 void XMLElement::SetText( double v )
1641 {
1642 char buf[BUF_SIZE];
1643 XMLUtil::ToStr( v, buf, BUF_SIZE );
1644 SetText( buf );
1645 }
1646
1647
QueryIntText(int * ival) const1648 XMLError XMLElement::QueryIntText( int* ival ) const
1649 {
1650 if ( FirstChild() && FirstChild()->ToText() ) {
1651 const char* t = FirstChild()->Value();
1652 if ( XMLUtil::ToInt( t, ival ) ) {
1653 return XML_SUCCESS;
1654 }
1655 return XML_CAN_NOT_CONVERT_TEXT;
1656 }
1657 return XML_NO_TEXT_NODE;
1658 }
1659
1660
QueryUnsignedText(unsigned * uval) const1661 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1662 {
1663 if ( FirstChild() && FirstChild()->ToText() ) {
1664 const char* t = FirstChild()->Value();
1665 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1666 return XML_SUCCESS;
1667 }
1668 return XML_CAN_NOT_CONVERT_TEXT;
1669 }
1670 return XML_NO_TEXT_NODE;
1671 }
1672
1673
QueryInt64Text(int64_t * ival) const1674 XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1675 {
1676 if (FirstChild() && FirstChild()->ToText()) {
1677 const char* t = FirstChild()->Value();
1678 if (XMLUtil::ToInt64(t, ival)) {
1679 return XML_SUCCESS;
1680 }
1681 return XML_CAN_NOT_CONVERT_TEXT;
1682 }
1683 return XML_NO_TEXT_NODE;
1684 }
1685
1686
QueryBoolText(bool * bval) const1687 XMLError XMLElement::QueryBoolText( bool* bval ) const
1688 {
1689 if ( FirstChild() && FirstChild()->ToText() ) {
1690 const char* t = FirstChild()->Value();
1691 if ( XMLUtil::ToBool( t, bval ) ) {
1692 return XML_SUCCESS;
1693 }
1694 return XML_CAN_NOT_CONVERT_TEXT;
1695 }
1696 return XML_NO_TEXT_NODE;
1697 }
1698
1699
QueryDoubleText(double * dval) const1700 XMLError XMLElement::QueryDoubleText( double* dval ) const
1701 {
1702 if ( FirstChild() && FirstChild()->ToText() ) {
1703 const char* t = FirstChild()->Value();
1704 if ( XMLUtil::ToDouble( t, dval ) ) {
1705 return XML_SUCCESS;
1706 }
1707 return XML_CAN_NOT_CONVERT_TEXT;
1708 }
1709 return XML_NO_TEXT_NODE;
1710 }
1711
1712
QueryFloatText(float * fval) const1713 XMLError XMLElement::QueryFloatText( float* fval ) const
1714 {
1715 if ( FirstChild() && FirstChild()->ToText() ) {
1716 const char* t = FirstChild()->Value();
1717 if ( XMLUtil::ToFloat( t, fval ) ) {
1718 return XML_SUCCESS;
1719 }
1720 return XML_CAN_NOT_CONVERT_TEXT;
1721 }
1722 return XML_NO_TEXT_NODE;
1723 }
1724
IntText(int defaultValue) const1725 int XMLElement::IntText(int defaultValue) const
1726 {
1727 int i = defaultValue;
1728 QueryIntText(&i);
1729 return i;
1730 }
1731
UnsignedText(unsigned defaultValue) const1732 unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1733 {
1734 unsigned i = defaultValue;
1735 QueryUnsignedText(&i);
1736 return i;
1737 }
1738
Int64Text(int64_t defaultValue) const1739 int64_t XMLElement::Int64Text(int64_t defaultValue) const
1740 {
1741 int64_t i = defaultValue;
1742 QueryInt64Text(&i);
1743 return i;
1744 }
1745
BoolText(bool defaultValue) const1746 bool XMLElement::BoolText(bool defaultValue) const
1747 {
1748 bool b = defaultValue;
1749 QueryBoolText(&b);
1750 return b;
1751 }
1752
DoubleText(double defaultValue) const1753 double XMLElement::DoubleText(double defaultValue) const
1754 {
1755 double d = defaultValue;
1756 QueryDoubleText(&d);
1757 return d;
1758 }
1759
FloatText(float defaultValue) const1760 float XMLElement::FloatText(float defaultValue) const
1761 {
1762 float f = defaultValue;
1763 QueryFloatText(&f);
1764 return f;
1765 }
1766
1767
FindOrCreateAttribute(const char * name)1768 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1769 {
1770 XMLAttribute* last = 0;
1771 XMLAttribute* attrib = 0;
1772 for( attrib = _rootAttribute;
1773 attrib;
1774 last = attrib, attrib = attrib->_next ) {
1775 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1776 break;
1777 }
1778 }
1779 if ( !attrib ) {
1780 attrib = CreateAttribute();
1781 TIXMLASSERT( attrib );
1782 if ( last ) {
1783 TIXMLASSERT( last->_next == 0 );
1784 last->_next = attrib;
1785 }
1786 else {
1787 TIXMLASSERT( _rootAttribute == 0 );
1788 _rootAttribute = attrib;
1789 }
1790 attrib->SetName( name );
1791 }
1792 return attrib;
1793 }
1794
1795
DeleteAttribute(const char * name)1796 void XMLElement::DeleteAttribute( const char* name )
1797 {
1798 XMLAttribute* prev = 0;
1799 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1800 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1801 if ( prev ) {
1802 prev->_next = a->_next;
1803 }
1804 else {
1805 _rootAttribute = a->_next;
1806 }
1807 DeleteAttribute( a );
1808 break;
1809 }
1810 prev = a;
1811 }
1812 }
1813
1814
ParseAttributes(char * p,int * curLineNumPtr)1815 char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
1816 {
1817 XMLAttribute* prevAttribute = 0;
1818
1819 // Read the attributes.
1820 while( p ) {
1821 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1822 if ( !(*p) ) {
1823 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
1824 return 0;
1825 }
1826
1827 // attribute.
1828 if (XMLUtil::IsNameStartChar( *p ) ) {
1829 XMLAttribute* attrib = CreateAttribute();
1830 TIXMLASSERT( attrib );
1831 attrib->_parseLineNum = _document->_parseCurLineNum;
1832
1833 int attrLineNum = attrib->_parseLineNum;
1834
1835 p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
1836 if ( !p || Attribute( attrib->Name() ) ) {
1837 DeleteAttribute( attrib );
1838 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
1839 return 0;
1840 }
1841 // There is a minor bug here: if the attribute in the source xml
1842 // document is duplicated, it will not be detected and the
1843 // attribute will be doubly added. However, tracking the 'prevAttribute'
1844 // avoids re-scanning the attribute list. Preferring performance for
1845 // now, may reconsider in the future.
1846 if ( prevAttribute ) {
1847 TIXMLASSERT( prevAttribute->_next == 0 );
1848 prevAttribute->_next = attrib;
1849 }
1850 else {
1851 TIXMLASSERT( _rootAttribute == 0 );
1852 _rootAttribute = attrib;
1853 }
1854 prevAttribute = attrib;
1855 }
1856 // end of the tag
1857 else if ( *p == '>' ) {
1858 ++p;
1859 break;
1860 }
1861 // end of the tag
1862 else if ( *p == '/' && *(p+1) == '>' ) {
1863 _closingType = CLOSED;
1864 return p+2; // done; sealed element.
1865 }
1866 else {
1867 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
1868 return 0;
1869 }
1870 }
1871 return p;
1872 }
1873
DeleteAttribute(XMLAttribute * attribute)1874 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1875 {
1876 if ( attribute == 0 ) {
1877 return;
1878 }
1879 MemPool* pool = attribute->_memPool;
1880 attribute->~XMLAttribute();
1881 pool->Free( attribute );
1882 }
1883
CreateAttribute()1884 XMLAttribute* XMLElement::CreateAttribute()
1885 {
1886 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1887 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1888 TIXMLASSERT( attrib );
1889 attrib->_memPool = &_document->_attributePool;
1890 attrib->_memPool->SetTracked();
1891 return attrib;
1892 }
1893
1894 //
1895 // <ele></ele>
1896 // <ele>foo<b>bar</b></ele>
1897 //
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)1898 char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
1899 {
1900 // Read the element name.
1901 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1902
1903 // The closing element is the </element> form. It is
1904 // parsed just like a regular element then deleted from
1905 // the DOM.
1906 if ( *p == '/' ) {
1907 _closingType = CLOSING;
1908 ++p;
1909 }
1910
1911 p = _value.ParseName( p );
1912 if ( _value.Empty() ) {
1913 return 0;
1914 }
1915
1916 p = ParseAttributes( p, curLineNumPtr );
1917 if ( !p || !*p || _closingType != OPEN ) {
1918 return p;
1919 }
1920
1921 p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
1922 return p;
1923 }
1924
1925
1926
ShallowClone(XMLDocument * doc) const1927 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1928 {
1929 if ( !doc ) {
1930 doc = _document;
1931 }
1932 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1933 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1934 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1935 }
1936 return element;
1937 }
1938
1939
ShallowEqual(const XMLNode * compare) const1940 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1941 {
1942 TIXMLASSERT( compare );
1943 const XMLElement* other = compare->ToElement();
1944 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
1945
1946 const XMLAttribute* a=FirstAttribute();
1947 const XMLAttribute* b=other->FirstAttribute();
1948
1949 while ( a && b ) {
1950 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1951 return false;
1952 }
1953 a = a->Next();
1954 b = b->Next();
1955 }
1956 if ( a || b ) {
1957 // different count
1958 return false;
1959 }
1960 return true;
1961 }
1962 return false;
1963 }
1964
1965
Accept(XMLVisitor * visitor) const1966 bool XMLElement::Accept( XMLVisitor* visitor ) const
1967 {
1968 TIXMLASSERT( visitor );
1969 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1970 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1971 if ( !node->Accept( visitor ) ) {
1972 break;
1973 }
1974 }
1975 }
1976 return visitor->VisitExit( *this );
1977 }
1978
1979
1980 // --------- XMLDocument ----------- //
1981
1982 // Warning: List must match 'enum XMLError'
1983 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1984 "XML_SUCCESS",
1985 "XML_NO_ATTRIBUTE",
1986 "XML_WRONG_ATTRIBUTE_TYPE",
1987 "XML_ERROR_FILE_NOT_FOUND",
1988 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1989 "XML_ERROR_FILE_READ_ERROR",
1990 "XML_ERROR_PARSING_ELEMENT",
1991 "XML_ERROR_PARSING_ATTRIBUTE",
1992 "XML_ERROR_PARSING_TEXT",
1993 "XML_ERROR_PARSING_CDATA",
1994 "XML_ERROR_PARSING_COMMENT",
1995 "XML_ERROR_PARSING_DECLARATION",
1996 "XML_ERROR_PARSING_UNKNOWN",
1997 "XML_ERROR_EMPTY_DOCUMENT",
1998 "XML_ERROR_MISMATCHED_ELEMENT",
1999 "XML_ERROR_PARSING",
2000 "XML_CAN_NOT_CONVERT_TEXT",
2001 "XML_NO_TEXT_NODE",
2002 "XML_ELEMENT_DEPTH_EXCEEDED"
2003 };
2004
2005
XMLDocument(bool processEntities,Whitespace whitespaceMode)2006 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
2007 XMLNode( 0 ),
2008 _writeBOM( false ),
2009 _processEntities( processEntities ),
2010 _errorID(XML_SUCCESS),
2011 _whitespaceMode( whitespaceMode ),
2012 _errorStr(),
2013 _errorLineNum( 0 ),
2014 _charBuffer( 0 ),
2015 _parseCurLineNum( 0 ),
2016 _parsingDepth(0),
2017 _unlinked(),
2018 _elementPool(),
2019 _attributePool(),
2020 _textPool(),
2021 _commentPool()
2022 {
2023 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2024 _document = this;
2025 }
2026
2027
~XMLDocument()2028 XMLDocument::~XMLDocument()
2029 {
2030 Clear();
2031 }
2032
2033
MarkInUse(XMLNode * node)2034 void XMLDocument::MarkInUse(XMLNode* node)
2035 {
2036 TIXMLASSERT(node);
2037 TIXMLASSERT(node->_parent == 0);
2038
2039 for (int i = 0; i < _unlinked.Size(); ++i) {
2040 if (node == _unlinked[i]) {
2041 _unlinked.SwapRemove(i);
2042 break;
2043 }
2044 }
2045 }
2046
Clear()2047 void XMLDocument::Clear()
2048 {
2049 DeleteChildren();
2050 while( _unlinked.Size()) {
2051 DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2052 }
2053
2054 #ifdef TINYXML2_DEBUG
2055 const bool hadError = Error();
2056 #endif
2057 ClearError();
2058
2059 delete [] _charBuffer;
2060 _charBuffer = 0;
2061 _parsingDepth = 0;
2062
2063 #if 0
2064 _textPool.Trace( "text" );
2065 _elementPool.Trace( "element" );
2066 _commentPool.Trace( "comment" );
2067 _attributePool.Trace( "attribute" );
2068 #endif
2069
2070 #ifdef TINYXML2_DEBUG
2071 if ( !hadError ) {
2072 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
2073 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2074 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
2075 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
2076 }
2077 #endif
2078 }
2079
2080
DeepCopy(XMLDocument * target) const2081 void XMLDocument::DeepCopy(XMLDocument* target) const
2082 {
2083 TIXMLASSERT(target);
2084 if (target == this) {
2085 return; // technically success - a no-op.
2086 }
2087
2088 target->Clear();
2089 for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2090 target->InsertEndChild(node->DeepClone(target));
2091 }
2092 }
2093
NewElement(const char * name)2094 XMLElement* XMLDocument::NewElement( const char* name )
2095 {
2096 XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
2097 ele->SetName( name );
2098 return ele;
2099 }
2100
2101
NewComment(const char * str)2102 XMLComment* XMLDocument::NewComment( const char* str )
2103 {
2104 XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
2105 comment->SetValue( str );
2106 return comment;
2107 }
2108
2109
NewText(const char * str)2110 XMLText* XMLDocument::NewText( const char* str )
2111 {
2112 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
2113 text->SetValue( str );
2114 return text;
2115 }
2116
2117
NewDeclaration(const char * str)2118 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2119 {
2120 XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
2121 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2122 return dec;
2123 }
2124
2125
NewUnknown(const char * str)2126 XMLUnknown* XMLDocument::NewUnknown( const char* str )
2127 {
2128 XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
2129 unk->SetValue( str );
2130 return unk;
2131 }
2132
callfopen(const char * filepath,const char * mode)2133 static FILE* callfopen( const char* filepath, const char* mode )
2134 {
2135 TIXMLASSERT( filepath );
2136 TIXMLASSERT( mode );
2137 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2138 FILE* fp = 0;
2139 errno_t err = fopen_s( &fp, filepath, mode );
2140 if ( err ) {
2141 return 0;
2142 }
2143 #else
2144 FILE* fp = fopen( filepath, mode );
2145 #endif
2146 return fp;
2147 }
2148
DeleteNode(XMLNode * node)2149 void XMLDocument::DeleteNode( XMLNode* node ) {
2150 TIXMLASSERT( node );
2151 TIXMLASSERT(node->_document == this );
2152 if (node->_parent) {
2153 node->_parent->DeleteChild( node );
2154 }
2155 else {
2156 // Isn't in the tree.
2157 // Use the parent delete.
2158 // Also, we need to mark it tracked: we 'know'
2159 // it was never used.
2160 node->_memPool->SetTracked();
2161 // Call the static XMLNode version:
2162 XMLNode::DeleteNode(node);
2163 }
2164 }
2165
2166
LoadFile(const char * filename)2167 XMLError XMLDocument::LoadFile( const char* filename )
2168 {
2169 if ( !filename ) {
2170 TIXMLASSERT( false );
2171 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2172 return _errorID;
2173 }
2174
2175 Clear();
2176 FILE* fp = callfopen( filename, "rb" );
2177 if ( !fp ) {
2178 SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
2179 return _errorID;
2180 }
2181 LoadFile( fp );
2182 fclose( fp );
2183 return _errorID;
2184 }
2185
2186 // This is likely overengineered template art to have a check that unsigned long value incremented
2187 // by one still fits into size_t. If size_t type is larger than unsigned long type
2188 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2189 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2190 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2191 // types sizes relate to each other.
2192 template
2193 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
2194 struct LongFitsIntoSizeTMinusOne {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2195 static bool Fits( unsigned long value )
2196 {
2197 return value < (size_t)-1;
2198 }
2199 };
2200
2201 template <>
2202 struct LongFitsIntoSizeTMinusOne<false> {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2203 static bool Fits( unsigned long )
2204 {
2205 return true;
2206 }
2207 };
2208
LoadFile(FILE * fp)2209 XMLError XMLDocument::LoadFile( FILE* fp )
2210 {
2211 Clear();
2212
2213 fseek( fp, 0, SEEK_SET );
2214 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2215 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2216 return _errorID;
2217 }
2218
2219 fseek( fp, 0, SEEK_END );
2220 const long filelength = ftell( fp );
2221 fseek( fp, 0, SEEK_SET );
2222 if ( filelength == -1L ) {
2223 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2224 return _errorID;
2225 }
2226 TIXMLASSERT( filelength >= 0 );
2227
2228 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
2229 // Cannot handle files which won't fit in buffer together with null terminator
2230 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2231 return _errorID;
2232 }
2233
2234 if ( filelength == 0 ) {
2235 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2236 return _errorID;
2237 }
2238
2239 const size_t size = filelength;
2240 TIXMLASSERT( _charBuffer == 0 );
2241 _charBuffer = new char[size+1];
2242 size_t read = fread( _charBuffer, 1, size, fp );
2243 if ( read != size ) {
2244 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2245 return _errorID;
2246 }
2247
2248 _charBuffer[size] = 0;
2249
2250 Parse();
2251 return _errorID;
2252 }
2253
2254
SaveFile(const char * filename,bool compact)2255 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
2256 {
2257 if ( !filename ) {
2258 TIXMLASSERT( false );
2259 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2260 return _errorID;
2261 }
2262
2263 FILE* fp = callfopen( filename, "w" );
2264 if ( !fp ) {
2265 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
2266 return _errorID;
2267 }
2268 SaveFile(fp, compact);
2269 fclose( fp );
2270 return _errorID;
2271 }
2272
2273
SaveFile(FILE * fp,bool compact)2274 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2275 {
2276 // Clear any error from the last save, otherwise it will get reported
2277 // for *this* call.
2278 ClearError();
2279 XMLPrinter stream( fp, compact );
2280 Print( &stream );
2281 return _errorID;
2282 }
2283
2284
Parse(const char * p,size_t len)2285 XMLError XMLDocument::Parse( const char* p, size_t len )
2286 {
2287 Clear();
2288
2289 if ( len == 0 || !p || !*p ) {
2290 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2291 return _errorID;
2292 }
2293 if ( len == (size_t)(-1) ) {
2294 len = strlen( p );
2295 }
2296 TIXMLASSERT( _charBuffer == 0 );
2297 _charBuffer = new char[ len+1 ];
2298 memcpy( _charBuffer, p, len );
2299 _charBuffer[len] = 0;
2300
2301 Parse();
2302 if ( Error() ) {
2303 // clean up now essentially dangling memory.
2304 // and the parse fail can put objects in the
2305 // pools that are dead and inaccessible.
2306 DeleteChildren();
2307 _elementPool.Clear();
2308 _attributePool.Clear();
2309 _textPool.Clear();
2310 _commentPool.Clear();
2311 }
2312 return _errorID;
2313 }
2314
2315
Print(XMLPrinter * streamer) const2316 void XMLDocument::Print( XMLPrinter* streamer ) const
2317 {
2318 if ( streamer ) {
2319 Accept( streamer );
2320 }
2321 else {
2322 XMLPrinter stdoutStreamer( stdout );
2323 Accept( &stdoutStreamer );
2324 }
2325 }
2326
2327
SetError(XMLError error,int lineNum,const char * format,...)2328 void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
2329 {
2330 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2331 _errorID = error;
2332 _errorLineNum = lineNum;
2333 _errorStr.Reset();
2334
2335 size_t BUFFER_SIZE = 1000;
2336 char* buffer = new char[BUFFER_SIZE];
2337
2338 TIXMLASSERT(sizeof(error) <= sizeof(int));
2339 TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
2340
2341 if (format) {
2342 size_t len = strlen(buffer);
2343 TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2344 len = strlen(buffer);
2345
2346 va_list va;
2347 va_start(va, format);
2348 TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2349 va_end(va);
2350 }
2351 _errorStr.SetStr(buffer);
2352 delete[] buffer;
2353 }
2354
2355
ErrorIDToName(XMLError errorID)2356 /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2357 {
2358 TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2359 const char* errorName = _errorNames[errorID];
2360 TIXMLASSERT( errorName && errorName[0] );
2361 return errorName;
2362 }
2363
ErrorStr() const2364 const char* XMLDocument::ErrorStr() const
2365 {
2366 return _errorStr.Empty() ? "" : _errorStr.GetStr();
2367 }
2368
2369
PrintError() const2370 void XMLDocument::PrintError() const
2371 {
2372 printf("%s\n", ErrorStr());
2373 }
2374
ErrorName() const2375 const char* XMLDocument::ErrorName() const
2376 {
2377 return ErrorIDToName(_errorID);
2378 }
2379
Parse()2380 void XMLDocument::Parse()
2381 {
2382 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2383 TIXMLASSERT( _charBuffer );
2384 _parseCurLineNum = 1;
2385 _parseLineNum = 1;
2386 char* p = _charBuffer;
2387 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2388 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2389 if ( !*p ) {
2390 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2391 return;
2392 }
2393 ParseDeep(p, 0, &_parseCurLineNum );
2394 }
2395
PushDepth()2396 void XMLDocument::PushDepth()
2397 {
2398 _parsingDepth++;
2399 if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2400 SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2401 }
2402 }
2403
PopDepth()2404 void XMLDocument::PopDepth()
2405 {
2406 TIXMLASSERT(_parsingDepth > 0);
2407 --_parsingDepth;
2408 }
2409
XMLPrinter(FILE * file,bool compact,int depth)2410 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2411 _elementJustOpened( false ),
2412 _stack(),
2413 _firstElement( true ),
2414 _fp( file ),
2415 _depth( depth ),
2416 _textDepth( -1 ),
2417 _processEntities( true ),
2418 _compactMode( compact ),
2419 _buffer()
2420 {
2421 for( int i=0; i<ENTITY_RANGE; ++i ) {
2422 _entityFlag[i] = false;
2423 _restrictedEntityFlag[i] = false;
2424 }
2425 for( int i=0; i<NUM_ENTITIES; ++i ) {
2426 const char entityValue = entities[i].value;
2427 const unsigned char flagIndex = (unsigned char)entityValue;
2428 TIXMLASSERT( flagIndex < ENTITY_RANGE );
2429 _entityFlag[flagIndex] = true;
2430 }
2431 _restrictedEntityFlag[(unsigned char)'&'] = true;
2432 _restrictedEntityFlag[(unsigned char)'<'] = true;
2433 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
2434 _buffer.Push( 0 );
2435 }
2436
2437
Print(const char * format,...)2438 void XMLPrinter::Print( const char* format, ... )
2439 {
2440 va_list va;
2441 va_start( va, format );
2442
2443 if ( _fp ) {
2444 vfprintf( _fp, format, va );
2445 }
2446 else {
2447 const int len = TIXML_VSCPRINTF( format, va );
2448 // Close out and re-start the va-args
2449 va_end( va );
2450 TIXMLASSERT( len >= 0 );
2451 va_start( va, format );
2452 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2453 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2454 TIXML_VSNPRINTF( p, len+1, format, va );
2455 }
2456 va_end( va );
2457 }
2458
2459
Write(const char * data,size_t size)2460 void XMLPrinter::Write( const char* data, size_t size )
2461 {
2462 if ( _fp ) {
2463 fwrite ( data , sizeof(char), size, _fp);
2464 }
2465 else {
2466 char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
2467 memcpy( p, data, size );
2468 p[size] = 0;
2469 }
2470 }
2471
2472
Putc(char ch)2473 void XMLPrinter::Putc( char ch )
2474 {
2475 if ( _fp ) {
2476 fputc ( ch, _fp);
2477 }
2478 else {
2479 char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
2480 p[0] = ch;
2481 p[1] = 0;
2482 }
2483 }
2484
2485
PrintSpace(int depth)2486 void XMLPrinter::PrintSpace( int depth )
2487 {
2488 for( int i=0; i<depth; ++i ) {
2489 Write( " " );
2490 }
2491 }
2492
2493
PrintString(const char * p,bool restricted)2494 void XMLPrinter::PrintString( const char* p, bool restricted )
2495 {
2496 // Look for runs of bytes between entities to print.
2497 const char* q = p;
2498
2499 if ( _processEntities ) {
2500 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2501 while ( *q ) {
2502 TIXMLASSERT( p <= q );
2503 // Remember, char is sometimes signed. (How many times has that bitten me?)
2504 if ( *q > 0 && *q < ENTITY_RANGE ) {
2505 // Check for entities. If one is found, flush
2506 // the stream up until the entity, write the
2507 // entity, and keep looking.
2508 if ( flag[(unsigned char)(*q)] ) {
2509 while ( p < q ) {
2510 const size_t delta = q - p;
2511 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2512 Write( p, toPrint );
2513 p += toPrint;
2514 }
2515 bool entityPatternPrinted = false;
2516 for( int i=0; i<NUM_ENTITIES; ++i ) {
2517 if ( entities[i].value == *q ) {
2518 Putc( '&' );
2519 Write( entities[i].pattern, entities[i].length );
2520 Putc( ';' );
2521 entityPatternPrinted = true;
2522 break;
2523 }
2524 }
2525 if ( !entityPatternPrinted ) {
2526 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2527 TIXMLASSERT( false );
2528 }
2529 ++p;
2530 }
2531 }
2532 ++q;
2533 TIXMLASSERT( p <= q );
2534 }
2535 // Flush the remaining string. This will be the entire
2536 // string if an entity wasn't found.
2537 if ( p < q ) {
2538 const size_t delta = q - p;
2539 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2540 Write( p, toPrint );
2541 }
2542 }
2543 else {
2544 Write( p );
2545 }
2546 }
2547
2548
PushHeader(bool writeBOM,bool writeDec)2549 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2550 {
2551 if ( writeBOM ) {
2552 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2553 Write( reinterpret_cast< const char* >( bom ) );
2554 }
2555 if ( writeDec ) {
2556 PushDeclaration( "xml version=\"1.0\"" );
2557 }
2558 }
2559
2560
OpenElement(const char * name,bool compactMode)2561 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2562 {
2563 SealElementIfJustOpened();
2564 _stack.Push( name );
2565
2566 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2567 Putc( '\n' );
2568 }
2569 if ( !compactMode ) {
2570 PrintSpace( _depth );
2571 }
2572
2573 Write ( "<" );
2574 Write ( name );
2575
2576 _elementJustOpened = true;
2577 _firstElement = false;
2578 ++_depth;
2579 }
2580
2581
PushAttribute(const char * name,const char * value)2582 void XMLPrinter::PushAttribute( const char* name, const char* value )
2583 {
2584 TIXMLASSERT( _elementJustOpened );
2585 Putc ( ' ' );
2586 Write( name );
2587 Write( "=\"" );
2588 PrintString( value, false );
2589 Putc ( '\"' );
2590 }
2591
2592
PushAttribute(const char * name,int v)2593 void XMLPrinter::PushAttribute( const char* name, int v )
2594 {
2595 char buf[BUF_SIZE];
2596 XMLUtil::ToStr( v, buf, BUF_SIZE );
2597 PushAttribute( name, buf );
2598 }
2599
2600
PushAttribute(const char * name,unsigned v)2601 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2602 {
2603 char buf[BUF_SIZE];
2604 XMLUtil::ToStr( v, buf, BUF_SIZE );
2605 PushAttribute( name, buf );
2606 }
2607
2608
PushAttribute(const char * name,int64_t v)2609 void XMLPrinter::PushAttribute(const char* name, int64_t v)
2610 {
2611 char buf[BUF_SIZE];
2612 XMLUtil::ToStr(v, buf, BUF_SIZE);
2613 PushAttribute(name, buf);
2614 }
2615
2616
PushAttribute(const char * name,bool v)2617 void XMLPrinter::PushAttribute( const char* name, bool v )
2618 {
2619 char buf[BUF_SIZE];
2620 XMLUtil::ToStr( v, buf, BUF_SIZE );
2621 PushAttribute( name, buf );
2622 }
2623
2624
PushAttribute(const char * name,double v)2625 void XMLPrinter::PushAttribute( const char* name, double v )
2626 {
2627 char buf[BUF_SIZE];
2628 XMLUtil::ToStr( v, buf, BUF_SIZE );
2629 PushAttribute( name, buf );
2630 }
2631
2632
CloseElement(bool compactMode)2633 void XMLPrinter::CloseElement( bool compactMode )
2634 {
2635 --_depth;
2636 const char* name = _stack.Pop();
2637
2638 if ( _elementJustOpened ) {
2639 Write( "/>" );
2640 }
2641 else {
2642 if ( _textDepth < 0 && !compactMode) {
2643 Putc( '\n' );
2644 PrintSpace( _depth );
2645 }
2646 Write ( "</" );
2647 Write ( name );
2648 Write ( ">" );
2649 }
2650
2651 if ( _textDepth == _depth ) {
2652 _textDepth = -1;
2653 }
2654 if ( _depth == 0 && !compactMode) {
2655 Putc( '\n' );
2656 }
2657 _elementJustOpened = false;
2658 }
2659
2660
SealElementIfJustOpened()2661 void XMLPrinter::SealElementIfJustOpened()
2662 {
2663 if ( !_elementJustOpened ) {
2664 return;
2665 }
2666 _elementJustOpened = false;
2667 Putc( '>' );
2668 }
2669
2670
PushText(const char * text,bool cdata)2671 void XMLPrinter::PushText( const char* text, bool cdata )
2672 {
2673 _textDepth = _depth-1;
2674
2675 SealElementIfJustOpened();
2676 if ( cdata ) {
2677 Write( "<![CDATA[" );
2678 Write( text );
2679 Write( "]]>" );
2680 }
2681 else {
2682 PrintString( text, true );
2683 }
2684 }
2685
PushText(int64_t value)2686 void XMLPrinter::PushText( int64_t value )
2687 {
2688 char buf[BUF_SIZE];
2689 XMLUtil::ToStr( value, buf, BUF_SIZE );
2690 PushText( buf, false );
2691 }
2692
PushText(int value)2693 void XMLPrinter::PushText( int value )
2694 {
2695 char buf[BUF_SIZE];
2696 XMLUtil::ToStr( value, buf, BUF_SIZE );
2697 PushText( buf, false );
2698 }
2699
2700
PushText(unsigned value)2701 void XMLPrinter::PushText( unsigned value )
2702 {
2703 char buf[BUF_SIZE];
2704 XMLUtil::ToStr( value, buf, BUF_SIZE );
2705 PushText( buf, false );
2706 }
2707
2708
PushText(bool value)2709 void XMLPrinter::PushText( bool value )
2710 {
2711 char buf[BUF_SIZE];
2712 XMLUtil::ToStr( value, buf, BUF_SIZE );
2713 PushText( buf, false );
2714 }
2715
2716
PushText(float value)2717 void XMLPrinter::PushText( float value )
2718 {
2719 char buf[BUF_SIZE];
2720 XMLUtil::ToStr( value, buf, BUF_SIZE );
2721 PushText( buf, false );
2722 }
2723
2724
PushText(double value)2725 void XMLPrinter::PushText( double value )
2726 {
2727 char buf[BUF_SIZE];
2728 XMLUtil::ToStr( value, buf, BUF_SIZE );
2729 PushText( buf, false );
2730 }
2731
2732
PushComment(const char * comment)2733 void XMLPrinter::PushComment( const char* comment )
2734 {
2735 SealElementIfJustOpened();
2736 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2737 Putc( '\n' );
2738 PrintSpace( _depth );
2739 }
2740 _firstElement = false;
2741
2742 Write( "<!--" );
2743 Write( comment );
2744 Write( "-->" );
2745 }
2746
2747
PushDeclaration(const char * value)2748 void XMLPrinter::PushDeclaration( const char* value )
2749 {
2750 SealElementIfJustOpened();
2751 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2752 Putc( '\n' );
2753 PrintSpace( _depth );
2754 }
2755 _firstElement = false;
2756
2757 Write( "<?" );
2758 Write( value );
2759 Write( "?>" );
2760 }
2761
2762
PushUnknown(const char * value)2763 void XMLPrinter::PushUnknown( const char* value )
2764 {
2765 SealElementIfJustOpened();
2766 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2767 Putc( '\n' );
2768 PrintSpace( _depth );
2769 }
2770 _firstElement = false;
2771
2772 Write( "<!" );
2773 Write( value );
2774 Putc( '>' );
2775 }
2776
2777
VisitEnter(const XMLDocument & doc)2778 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2779 {
2780 _processEntities = doc.ProcessEntities();
2781 if ( doc.HasBOM() ) {
2782 PushHeader( true, false );
2783 }
2784 return true;
2785 }
2786
2787
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2788 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2789 {
2790 const XMLElement* parentElem = 0;
2791 if ( element.Parent() ) {
2792 parentElem = element.Parent()->ToElement();
2793 }
2794 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2795 OpenElement( element.Name(), compactMode );
2796 while ( attribute ) {
2797 PushAttribute( attribute->Name(), attribute->Value() );
2798 attribute = attribute->Next();
2799 }
2800 return true;
2801 }
2802
2803
VisitExit(const XMLElement & element)2804 bool XMLPrinter::VisitExit( const XMLElement& element )
2805 {
2806 CloseElement( CompactMode(element) );
2807 return true;
2808 }
2809
2810
Visit(const XMLText & text)2811 bool XMLPrinter::Visit( const XMLText& text )
2812 {
2813 PushText( text.Value(), text.CData() );
2814 return true;
2815 }
2816
2817
Visit(const XMLComment & comment)2818 bool XMLPrinter::Visit( const XMLComment& comment )
2819 {
2820 PushComment( comment.Value() );
2821 return true;
2822 }
2823
Visit(const XMLDeclaration & declaration)2824 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2825 {
2826 PushDeclaration( declaration.Value() );
2827 return true;
2828 }
2829
2830
Visit(const XMLUnknown & unknown)2831 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2832 {
2833 PushUnknown( unknown.Value() );
2834 return true;
2835 }
2836
2837 } // namespace tinyxml2
2838