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 const 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 const 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 = static_cast<char>(0x0a); // all line endings are normalized to LF
105 static const char LF = LINE_FEED;
106 static const char CARRIAGE_RETURN = static_cast<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 const 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 const 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 = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
434 input >>= 6;
435 //fall through
436 case 3:
437 --output;
438 *output = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
439 input >>= 6;
440 //fall through
441 case 2:
442 --output;
443 *output = static_cast<char>((input | BYTE_MARK) & BYTE_MASK);
444 input >>= 6;
445 //fall through
446 case 1:
447 --output;
448 *output = static_cast<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", static_cast<long long>(v));
589 }
590
ToStr(uint64_t v,char * buffer,int bufferSize)591 void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize )
592 {
593 // horrible syntax trick to make the compiler happy about %llu
594 TIXML_SNPRINTF(buffer, bufferSize, "%llu", (long long)v);
595 }
596
ToInt(const char * str,int * value)597 bool XMLUtil::ToInt( const char* str, int* value )
598 {
599 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
600 return true;
601 }
602 return false;
603 }
604
ToUnsigned(const char * str,unsigned * value)605 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
606 {
607 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
608 return true;
609 }
610 return false;
611 }
612
ToBool(const char * str,bool * value)613 bool XMLUtil::ToBool( const char* str, bool* value )
614 {
615 int ival = 0;
616 if ( ToInt( str, &ival )) {
617 *value = (ival==0) ? false : true;
618 return true;
619 }
620 static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 };
621 static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 };
622
623 for (int i = 0; TRUE_VALS[i]; ++i) {
624 if (StringEqual(str, TRUE_VALS[i])) {
625 *value = true;
626 return true;
627 }
628 }
629 for (int i = 0; FALSE_VALS[i]; ++i) {
630 if (StringEqual(str, FALSE_VALS[i])) {
631 *value = false;
632 return true;
633 }
634 }
635 return false;
636 }
637
638
ToFloat(const char * str,float * value)639 bool XMLUtil::ToFloat( const char* str, float* value )
640 {
641 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
642 return true;
643 }
644 return false;
645 }
646
647
ToDouble(const char * str,double * value)648 bool XMLUtil::ToDouble( const char* str, double* value )
649 {
650 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
651 return true;
652 }
653 return false;
654 }
655
656
ToInt64(const char * str,int64_t * value)657 bool XMLUtil::ToInt64(const char* str, int64_t* value)
658 {
659 long long v = 0; // horrible syntax trick to make the compiler happy about %lld
660 if (TIXML_SSCANF(str, "%lld", &v) == 1) {
661 *value = static_cast<int64_t>(v);
662 return true;
663 }
664 return false;
665 }
666
667
ToUnsigned64(const char * str,uint64_t * value)668 bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) {
669 unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu
670 if(TIXML_SSCANF(str, "%llu", &v) == 1) {
671 *value = (uint64_t)v;
672 return true;
673 }
674 return false;
675 }
676
677
Identify(char * p,XMLNode ** node)678 char* XMLDocument::Identify( char* p, XMLNode** node )
679 {
680 TIXMLASSERT( node );
681 TIXMLASSERT( p );
682 char* const start = p;
683 int const startLine = _parseCurLineNum;
684 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
685 if( !*p ) {
686 *node = 0;
687 TIXMLASSERT( p );
688 return p;
689 }
690
691 // These strings define the matching patterns:
692 static const char* xmlHeader = { "<?" };
693 static const char* commentHeader = { "<!--" };
694 static const char* cdataHeader = { "<![CDATA[" };
695 static const char* dtdHeader = { "<!" };
696 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
697
698 static const int xmlHeaderLen = 2;
699 static const int commentHeaderLen = 4;
700 static const int cdataHeaderLen = 9;
701 static const int dtdHeaderLen = 2;
702 static const int elementHeaderLen = 1;
703
704 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
705 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
706 XMLNode* returnNode = 0;
707 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
708 returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
709 returnNode->_parseLineNum = _parseCurLineNum;
710 p += xmlHeaderLen;
711 }
712 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
713 returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
714 returnNode->_parseLineNum = _parseCurLineNum;
715 p += commentHeaderLen;
716 }
717 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
718 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
719 returnNode = text;
720 returnNode->_parseLineNum = _parseCurLineNum;
721 p += cdataHeaderLen;
722 text->SetCData( true );
723 }
724 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
725 returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
726 returnNode->_parseLineNum = _parseCurLineNum;
727 p += dtdHeaderLen;
728 }
729 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
730 returnNode = CreateUnlinkedNode<XMLElement>( _elementPool );
731 returnNode->_parseLineNum = _parseCurLineNum;
732 p += elementHeaderLen;
733 }
734 else {
735 returnNode = CreateUnlinkedNode<XMLText>( _textPool );
736 returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
737 p = start; // Back it up, all the text counts.
738 _parseCurLineNum = startLine;
739 }
740
741 TIXMLASSERT( returnNode );
742 TIXMLASSERT( p );
743 *node = returnNode;
744 return p;
745 }
746
747
Accept(XMLVisitor * visitor) const748 bool XMLDocument::Accept( XMLVisitor* visitor ) const
749 {
750 TIXMLASSERT( visitor );
751 if ( visitor->VisitEnter( *this ) ) {
752 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
753 if ( !node->Accept( visitor ) ) {
754 break;
755 }
756 }
757 }
758 return visitor->VisitExit( *this );
759 }
760
761
762 // --------- XMLNode ----------- //
763
XMLNode(XMLDocument * doc)764 XMLNode::XMLNode( XMLDocument* doc ) :
765 _document( doc ),
766 _parent( 0 ),
767 _value(),
768 _parseLineNum( 0 ),
769 _firstChild( 0 ), _lastChild( 0 ),
770 _prev( 0 ), _next( 0 ),
771 _userData( 0 ),
772 _memPool( 0 )
773 {
774 }
775
776
~XMLNode()777 XMLNode::~XMLNode()
778 {
779 DeleteChildren();
780 if ( _parent ) {
781 _parent->Unlink( this );
782 }
783 }
784
Value() const785 const char* XMLNode::Value() const
786 {
787 // Edge case: XMLDocuments don't have a Value. Return null.
788 if ( this->ToDocument() )
789 return 0;
790 return _value.GetStr();
791 }
792
SetValue(const char * str,bool staticMem)793 void XMLNode::SetValue( const char* str, bool staticMem )
794 {
795 if ( staticMem ) {
796 _value.SetInternedStr( str );
797 }
798 else {
799 _value.SetStr( str );
800 }
801 }
802
DeepClone(XMLDocument * target) const803 XMLNode* XMLNode::DeepClone(XMLDocument* target) const
804 {
805 XMLNode* clone = this->ShallowClone(target);
806 if (!clone) return 0;
807
808 for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
809 XMLNode* childClone = child->DeepClone(target);
810 TIXMLASSERT(childClone);
811 clone->InsertEndChild(childClone);
812 }
813 return clone;
814 }
815
DeleteChildren()816 void XMLNode::DeleteChildren()
817 {
818 while( _firstChild ) {
819 TIXMLASSERT( _lastChild );
820 DeleteChild( _firstChild );
821 }
822 _firstChild = _lastChild = 0;
823 }
824
825
Unlink(XMLNode * child)826 void XMLNode::Unlink( XMLNode* child )
827 {
828 TIXMLASSERT( child );
829 TIXMLASSERT( child->_document == _document );
830 TIXMLASSERT( child->_parent == this );
831 if ( child == _firstChild ) {
832 _firstChild = _firstChild->_next;
833 }
834 if ( child == _lastChild ) {
835 _lastChild = _lastChild->_prev;
836 }
837
838 if ( child->_prev ) {
839 child->_prev->_next = child->_next;
840 }
841 if ( child->_next ) {
842 child->_next->_prev = child->_prev;
843 }
844 child->_next = 0;
845 child->_prev = 0;
846 child->_parent = 0;
847 }
848
849
DeleteChild(XMLNode * node)850 void XMLNode::DeleteChild( XMLNode* node )
851 {
852 TIXMLASSERT( node );
853 TIXMLASSERT( node->_document == _document );
854 TIXMLASSERT( node->_parent == this );
855 Unlink( node );
856 TIXMLASSERT(node->_prev == 0);
857 TIXMLASSERT(node->_next == 0);
858 TIXMLASSERT(node->_parent == 0);
859 DeleteNode( node );
860 }
861
862
InsertEndChild(XMLNode * addThis)863 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
864 {
865 TIXMLASSERT( addThis );
866 if ( addThis->_document != _document ) {
867 TIXMLASSERT( false );
868 return 0;
869 }
870 InsertChildPreamble( addThis );
871
872 if ( _lastChild ) {
873 TIXMLASSERT( _firstChild );
874 TIXMLASSERT( _lastChild->_next == 0 );
875 _lastChild->_next = addThis;
876 addThis->_prev = _lastChild;
877 _lastChild = addThis;
878
879 addThis->_next = 0;
880 }
881 else {
882 TIXMLASSERT( _firstChild == 0 );
883 _firstChild = _lastChild = addThis;
884
885 addThis->_prev = 0;
886 addThis->_next = 0;
887 }
888 addThis->_parent = this;
889 return addThis;
890 }
891
892
InsertFirstChild(XMLNode * addThis)893 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
894 {
895 TIXMLASSERT( addThis );
896 if ( addThis->_document != _document ) {
897 TIXMLASSERT( false );
898 return 0;
899 }
900 InsertChildPreamble( addThis );
901
902 if ( _firstChild ) {
903 TIXMLASSERT( _lastChild );
904 TIXMLASSERT( _firstChild->_prev == 0 );
905
906 _firstChild->_prev = addThis;
907 addThis->_next = _firstChild;
908 _firstChild = addThis;
909
910 addThis->_prev = 0;
911 }
912 else {
913 TIXMLASSERT( _lastChild == 0 );
914 _firstChild = _lastChild = addThis;
915
916 addThis->_prev = 0;
917 addThis->_next = 0;
918 }
919 addThis->_parent = this;
920 return addThis;
921 }
922
923
InsertAfterChild(XMLNode * afterThis,XMLNode * addThis)924 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
925 {
926 TIXMLASSERT( addThis );
927 if ( addThis->_document != _document ) {
928 TIXMLASSERT( false );
929 return 0;
930 }
931
932 TIXMLASSERT( afterThis );
933
934 if ( afterThis->_parent != this ) {
935 TIXMLASSERT( false );
936 return 0;
937 }
938 if ( afterThis == addThis ) {
939 // Current state: BeforeThis -> AddThis -> OneAfterAddThis
940 // Now AddThis must disappear from it's location and then
941 // reappear between BeforeThis and OneAfterAddThis.
942 // So just leave it where it is.
943 return addThis;
944 }
945
946 if ( afterThis->_next == 0 ) {
947 // The last node or the only node.
948 return InsertEndChild( addThis );
949 }
950 InsertChildPreamble( addThis );
951 addThis->_prev = afterThis;
952 addThis->_next = afterThis->_next;
953 afterThis->_next->_prev = addThis;
954 afterThis->_next = addThis;
955 addThis->_parent = this;
956 return addThis;
957 }
958
959
960
961
FirstChildElement(const char * name) const962 const XMLElement* XMLNode::FirstChildElement( const char* name ) const
963 {
964 for( const XMLNode* node = _firstChild; node; node = node->_next ) {
965 const XMLElement* element = node->ToElementWithName( name );
966 if ( element ) {
967 return element;
968 }
969 }
970 return 0;
971 }
972
973
LastChildElement(const char * name) const974 const XMLElement* XMLNode::LastChildElement( const char* name ) const
975 {
976 for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
977 const XMLElement* element = node->ToElementWithName( name );
978 if ( element ) {
979 return element;
980 }
981 }
982 return 0;
983 }
984
985
NextSiblingElement(const char * name) const986 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
987 {
988 for( const XMLNode* node = _next; node; node = node->_next ) {
989 const XMLElement* element = node->ToElementWithName( name );
990 if ( element ) {
991 return element;
992 }
993 }
994 return 0;
995 }
996
997
PreviousSiblingElement(const char * name) const998 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
999 {
1000 for( const XMLNode* node = _prev; node; node = node->_prev ) {
1001 const XMLElement* element = node->ToElementWithName( name );
1002 if ( element ) {
1003 return element;
1004 }
1005 }
1006 return 0;
1007 }
1008
1009
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)1010 char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
1011 {
1012 // This is a recursive method, but thinking about it "at the current level"
1013 // it is a pretty simple flat list:
1014 // <foo/>
1015 // <!-- comment -->
1016 //
1017 // With a special case:
1018 // <foo>
1019 // </foo>
1020 // <!-- comment -->
1021 //
1022 // Where the closing element (/foo) *must* be the next thing after the opening
1023 // element, and the names must match. BUT the tricky bit is that the closing
1024 // element will be read by the child.
1025 //
1026 // 'endTag' is the end tag for this node, it is returned by a call to a child.
1027 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
1028
1029 XMLDocument::DepthTracker tracker(_document);
1030 if (_document->Error())
1031 return 0;
1032
1033 while( p && *p ) {
1034 XMLNode* node = 0;
1035
1036 p = _document->Identify( p, &node );
1037 TIXMLASSERT( p );
1038 if ( node == 0 ) {
1039 break;
1040 }
1041
1042 const int initialLineNum = node->_parseLineNum;
1043
1044 StrPair endTag;
1045 p = node->ParseDeep( p, &endTag, curLineNumPtr );
1046 if ( !p ) {
1047 DeleteNode( node );
1048 if ( !_document->Error() ) {
1049 _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
1050 }
1051 break;
1052 }
1053
1054 const XMLDeclaration* const decl = node->ToDeclaration();
1055 if ( decl ) {
1056 // Declarations are only allowed at document level
1057 //
1058 // Multiple declarations are allowed but all declarations
1059 // must occur before anything else.
1060 //
1061 // Optimized due to a security test case. If the first node is
1062 // a declaration, and the last node is a declaration, then only
1063 // declarations have so far been added.
1064 bool wellLocated = false;
1065
1066 if (ToDocument()) {
1067 if (FirstChild()) {
1068 wellLocated =
1069 FirstChild() &&
1070 FirstChild()->ToDeclaration() &&
1071 LastChild() &&
1072 LastChild()->ToDeclaration();
1073 }
1074 else {
1075 wellLocated = true;
1076 }
1077 }
1078 if ( !wellLocated ) {
1079 _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
1080 DeleteNode( node );
1081 break;
1082 }
1083 }
1084
1085 XMLElement* ele = node->ToElement();
1086 if ( ele ) {
1087 // We read the end tag. Return it to the parent.
1088 if ( ele->ClosingType() == XMLElement::CLOSING ) {
1089 if ( parentEndTag ) {
1090 ele->_value.TransferTo( parentEndTag );
1091 }
1092 node->_memPool->SetTracked(); // created and then immediately deleted.
1093 DeleteNode( node );
1094 return p;
1095 }
1096
1097 // Handle an end tag returned to this level.
1098 // And handle a bunch of annoying errors.
1099 bool mismatch = false;
1100 if ( endTag.Empty() ) {
1101 if ( ele->ClosingType() == XMLElement::OPEN ) {
1102 mismatch = true;
1103 }
1104 }
1105 else {
1106 if ( ele->ClosingType() != XMLElement::OPEN ) {
1107 mismatch = true;
1108 }
1109 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1110 mismatch = true;
1111 }
1112 }
1113 if ( mismatch ) {
1114 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
1115 DeleteNode( node );
1116 break;
1117 }
1118 }
1119 InsertEndChild( node );
1120 }
1121 return 0;
1122 }
1123
DeleteNode(XMLNode * node)1124 /*static*/ void XMLNode::DeleteNode( XMLNode* node )
1125 {
1126 if ( node == 0 ) {
1127 return;
1128 }
1129 TIXMLASSERT(node->_document);
1130 if (!node->ToDocument()) {
1131 node->_document->MarkInUse(node);
1132 }
1133
1134 MemPool* pool = node->_memPool;
1135 node->~XMLNode();
1136 pool->Free( node );
1137 }
1138
InsertChildPreamble(XMLNode * insertThis) const1139 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1140 {
1141 TIXMLASSERT( insertThis );
1142 TIXMLASSERT( insertThis->_document == _document );
1143
1144 if (insertThis->_parent) {
1145 insertThis->_parent->Unlink( insertThis );
1146 }
1147 else {
1148 insertThis->_document->MarkInUse(insertThis);
1149 insertThis->_memPool->SetTracked();
1150 }
1151 }
1152
ToElementWithName(const char * name) const1153 const XMLElement* XMLNode::ToElementWithName( const char* name ) const
1154 {
1155 const XMLElement* element = this->ToElement();
1156 if ( element == 0 ) {
1157 return 0;
1158 }
1159 if ( name == 0 ) {
1160 return element;
1161 }
1162 if ( XMLUtil::StringEqual( element->Name(), name ) ) {
1163 return element;
1164 }
1165 return 0;
1166 }
1167
1168 // --------- XMLText ---------- //
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1169 char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1170 {
1171 if ( this->CData() ) {
1172 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1173 if ( !p ) {
1174 _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
1175 }
1176 return p;
1177 }
1178 else {
1179 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1180 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1181 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1182 }
1183
1184 p = _value.ParseText( p, "<", flags, curLineNumPtr );
1185 if ( p && *p ) {
1186 return p-1;
1187 }
1188 if ( !p ) {
1189 _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
1190 }
1191 }
1192 return 0;
1193 }
1194
1195
ShallowClone(XMLDocument * doc) const1196 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1197 {
1198 if ( !doc ) {
1199 doc = _document;
1200 }
1201 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
1202 text->SetCData( this->CData() );
1203 return text;
1204 }
1205
1206
ShallowEqual(const XMLNode * compare) const1207 bool XMLText::ShallowEqual( const XMLNode* compare ) const
1208 {
1209 TIXMLASSERT( compare );
1210 const XMLText* text = compare->ToText();
1211 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1212 }
1213
1214
Accept(XMLVisitor * visitor) const1215 bool XMLText::Accept( XMLVisitor* visitor ) const
1216 {
1217 TIXMLASSERT( visitor );
1218 return visitor->Visit( *this );
1219 }
1220
1221
1222 // --------- XMLComment ---------- //
1223
XMLComment(XMLDocument * doc)1224 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1225 {
1226 }
1227
1228
~XMLComment()1229 XMLComment::~XMLComment()
1230 {
1231 }
1232
1233
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1234 char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1235 {
1236 // Comment parses as text.
1237 p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
1238 if ( p == 0 ) {
1239 _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
1240 }
1241 return p;
1242 }
1243
1244
ShallowClone(XMLDocument * doc) const1245 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1246 {
1247 if ( !doc ) {
1248 doc = _document;
1249 }
1250 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
1251 return comment;
1252 }
1253
1254
ShallowEqual(const XMLNode * compare) const1255 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1256 {
1257 TIXMLASSERT( compare );
1258 const XMLComment* comment = compare->ToComment();
1259 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1260 }
1261
1262
Accept(XMLVisitor * visitor) const1263 bool XMLComment::Accept( XMLVisitor* visitor ) const
1264 {
1265 TIXMLASSERT( visitor );
1266 return visitor->Visit( *this );
1267 }
1268
1269
1270 // --------- XMLDeclaration ---------- //
1271
XMLDeclaration(XMLDocument * doc)1272 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1273 {
1274 }
1275
1276
~XMLDeclaration()1277 XMLDeclaration::~XMLDeclaration()
1278 {
1279 //printf( "~XMLDeclaration\n" );
1280 }
1281
1282
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1283 char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1284 {
1285 // Declaration parses as text.
1286 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1287 if ( p == 0 ) {
1288 _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
1289 }
1290 return p;
1291 }
1292
1293
ShallowClone(XMLDocument * doc) const1294 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1295 {
1296 if ( !doc ) {
1297 doc = _document;
1298 }
1299 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1300 return dec;
1301 }
1302
1303
ShallowEqual(const XMLNode * compare) const1304 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1305 {
1306 TIXMLASSERT( compare );
1307 const XMLDeclaration* declaration = compare->ToDeclaration();
1308 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1309 }
1310
1311
1312
Accept(XMLVisitor * visitor) const1313 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1314 {
1315 TIXMLASSERT( visitor );
1316 return visitor->Visit( *this );
1317 }
1318
1319 // --------- XMLUnknown ---------- //
1320
XMLUnknown(XMLDocument * doc)1321 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1322 {
1323 }
1324
1325
~XMLUnknown()1326 XMLUnknown::~XMLUnknown()
1327 {
1328 }
1329
1330
ParseDeep(char * p,StrPair *,int * curLineNumPtr)1331 char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
1332 {
1333 // Unknown parses as text.
1334 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
1335 if ( !p ) {
1336 _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
1337 }
1338 return p;
1339 }
1340
1341
ShallowClone(XMLDocument * doc) const1342 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1343 {
1344 if ( !doc ) {
1345 doc = _document;
1346 }
1347 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1348 return text;
1349 }
1350
1351
ShallowEqual(const XMLNode * compare) const1352 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1353 {
1354 TIXMLASSERT( compare );
1355 const XMLUnknown* unknown = compare->ToUnknown();
1356 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1357 }
1358
1359
Accept(XMLVisitor * visitor) const1360 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1361 {
1362 TIXMLASSERT( visitor );
1363 return visitor->Visit( *this );
1364 }
1365
1366 // --------- XMLAttribute ---------- //
1367
Name() const1368 const char* XMLAttribute::Name() const
1369 {
1370 return _name.GetStr();
1371 }
1372
Value() const1373 const char* XMLAttribute::Value() const
1374 {
1375 return _value.GetStr();
1376 }
1377
ParseDeep(char * p,bool processEntities,int * curLineNumPtr)1378 char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
1379 {
1380 // Parse using the name rules: bug fix, was using ParseText before
1381 p = _name.ParseName( p );
1382 if ( !p || !*p ) {
1383 return 0;
1384 }
1385
1386 // Skip white space before =
1387 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1388 if ( *p != '=' ) {
1389 return 0;
1390 }
1391
1392 ++p; // move up to opening quote
1393 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1394 if ( *p != '\"' && *p != '\'' ) {
1395 return 0;
1396 }
1397
1398 const char endTag[2] = { *p, 0 };
1399 ++p; // move past opening quote
1400
1401 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
1402 return p;
1403 }
1404
1405
SetName(const char * n)1406 void XMLAttribute::SetName( const char* n )
1407 {
1408 _name.SetStr( n );
1409 }
1410
1411
QueryIntValue(int * value) const1412 XMLError XMLAttribute::QueryIntValue( int* value ) const
1413 {
1414 if ( XMLUtil::ToInt( Value(), value )) {
1415 return XML_SUCCESS;
1416 }
1417 return XML_WRONG_ATTRIBUTE_TYPE;
1418 }
1419
1420
QueryUnsignedValue(unsigned int * value) const1421 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1422 {
1423 if ( XMLUtil::ToUnsigned( Value(), value )) {
1424 return XML_SUCCESS;
1425 }
1426 return XML_WRONG_ATTRIBUTE_TYPE;
1427 }
1428
1429
QueryInt64Value(int64_t * value) const1430 XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
1431 {
1432 if (XMLUtil::ToInt64(Value(), value)) {
1433 return XML_SUCCESS;
1434 }
1435 return XML_WRONG_ATTRIBUTE_TYPE;
1436 }
1437
1438
QueryUnsigned64Value(uint64_t * value) const1439 XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const
1440 {
1441 if(XMLUtil::ToUnsigned64(Value(), value)) {
1442 return XML_SUCCESS;
1443 }
1444 return XML_WRONG_ATTRIBUTE_TYPE;
1445 }
1446
1447
QueryBoolValue(bool * value) const1448 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1449 {
1450 if ( XMLUtil::ToBool( Value(), value )) {
1451 return XML_SUCCESS;
1452 }
1453 return XML_WRONG_ATTRIBUTE_TYPE;
1454 }
1455
1456
QueryFloatValue(float * value) const1457 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1458 {
1459 if ( XMLUtil::ToFloat( Value(), value )) {
1460 return XML_SUCCESS;
1461 }
1462 return XML_WRONG_ATTRIBUTE_TYPE;
1463 }
1464
1465
QueryDoubleValue(double * value) const1466 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1467 {
1468 if ( XMLUtil::ToDouble( Value(), value )) {
1469 return XML_SUCCESS;
1470 }
1471 return XML_WRONG_ATTRIBUTE_TYPE;
1472 }
1473
1474
SetAttribute(const char * v)1475 void XMLAttribute::SetAttribute( const char* v )
1476 {
1477 _value.SetStr( v );
1478 }
1479
1480
SetAttribute(int v)1481 void XMLAttribute::SetAttribute( int v )
1482 {
1483 char buf[BUF_SIZE];
1484 XMLUtil::ToStr( v, buf, BUF_SIZE );
1485 _value.SetStr( buf );
1486 }
1487
1488
SetAttribute(unsigned v)1489 void XMLAttribute::SetAttribute( unsigned v )
1490 {
1491 char buf[BUF_SIZE];
1492 XMLUtil::ToStr( v, buf, BUF_SIZE );
1493 _value.SetStr( buf );
1494 }
1495
1496
SetAttribute(int64_t v)1497 void XMLAttribute::SetAttribute(int64_t v)
1498 {
1499 char buf[BUF_SIZE];
1500 XMLUtil::ToStr(v, buf, BUF_SIZE);
1501 _value.SetStr(buf);
1502 }
1503
SetAttribute(uint64_t v)1504 void XMLAttribute::SetAttribute(uint64_t v)
1505 {
1506 char buf[BUF_SIZE];
1507 XMLUtil::ToStr(v, buf, BUF_SIZE);
1508 _value.SetStr(buf);
1509 }
1510
1511
SetAttribute(bool v)1512 void XMLAttribute::SetAttribute( bool v )
1513 {
1514 char buf[BUF_SIZE];
1515 XMLUtil::ToStr( v, buf, BUF_SIZE );
1516 _value.SetStr( buf );
1517 }
1518
SetAttribute(double v)1519 void XMLAttribute::SetAttribute( double v )
1520 {
1521 char buf[BUF_SIZE];
1522 XMLUtil::ToStr( v, buf, BUF_SIZE );
1523 _value.SetStr( buf );
1524 }
1525
SetAttribute(float v)1526 void XMLAttribute::SetAttribute( float v )
1527 {
1528 char buf[BUF_SIZE];
1529 XMLUtil::ToStr( v, buf, BUF_SIZE );
1530 _value.SetStr( buf );
1531 }
1532
1533
1534 // --------- XMLElement ---------- //
XMLElement(XMLDocument * doc)1535 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1536 _closingType( OPEN ),
1537 _rootAttribute( 0 )
1538 {
1539 }
1540
1541
~XMLElement()1542 XMLElement::~XMLElement()
1543 {
1544 while( _rootAttribute ) {
1545 XMLAttribute* next = _rootAttribute->_next;
1546 DeleteAttribute( _rootAttribute );
1547 _rootAttribute = next;
1548 }
1549 }
1550
1551
FindAttribute(const char * name) const1552 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1553 {
1554 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1555 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1556 return a;
1557 }
1558 }
1559 return 0;
1560 }
1561
1562
Attribute(const char * name,const char * value) const1563 const char* XMLElement::Attribute( const char* name, const char* value ) const
1564 {
1565 const XMLAttribute* a = FindAttribute( name );
1566 if ( !a ) {
1567 return 0;
1568 }
1569 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1570 return a->Value();
1571 }
1572 return 0;
1573 }
1574
IntAttribute(const char * name,int defaultValue) const1575 int XMLElement::IntAttribute(const char* name, int defaultValue) const
1576 {
1577 int i = defaultValue;
1578 QueryIntAttribute(name, &i);
1579 return i;
1580 }
1581
UnsignedAttribute(const char * name,unsigned defaultValue) const1582 unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1583 {
1584 unsigned i = defaultValue;
1585 QueryUnsignedAttribute(name, &i);
1586 return i;
1587 }
1588
Int64Attribute(const char * name,int64_t defaultValue) const1589 int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1590 {
1591 int64_t i = defaultValue;
1592 QueryInt64Attribute(name, &i);
1593 return i;
1594 }
1595
Unsigned64Attribute(const char * name,uint64_t defaultValue) const1596 uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const
1597 {
1598 uint64_t i = defaultValue;
1599 QueryUnsigned64Attribute(name, &i);
1600 return i;
1601 }
1602
BoolAttribute(const char * name,bool defaultValue) const1603 bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1604 {
1605 bool b = defaultValue;
1606 QueryBoolAttribute(name, &b);
1607 return b;
1608 }
1609
DoubleAttribute(const char * name,double defaultValue) const1610 double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1611 {
1612 double d = defaultValue;
1613 QueryDoubleAttribute(name, &d);
1614 return d;
1615 }
1616
FloatAttribute(const char * name,float defaultValue) const1617 float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1618 {
1619 float f = defaultValue;
1620 QueryFloatAttribute(name, &f);
1621 return f;
1622 }
1623
GetText() const1624 const char* XMLElement::GetText() const
1625 {
1626 if ( FirstChild() && FirstChild()->ToText() ) {
1627 return FirstChild()->Value();
1628 }
1629 return 0;
1630 }
1631
1632
SetText(const char * inText)1633 void XMLElement::SetText( const char* inText )
1634 {
1635 if ( FirstChild() && FirstChild()->ToText() )
1636 FirstChild()->SetValue( inText );
1637 else {
1638 XMLText* theText = GetDocument()->NewText( inText );
1639 InsertFirstChild( theText );
1640 }
1641 }
1642
1643
SetText(int v)1644 void XMLElement::SetText( int v )
1645 {
1646 char buf[BUF_SIZE];
1647 XMLUtil::ToStr( v, buf, BUF_SIZE );
1648 SetText( buf );
1649 }
1650
1651
SetText(unsigned v)1652 void XMLElement::SetText( unsigned v )
1653 {
1654 char buf[BUF_SIZE];
1655 XMLUtil::ToStr( v, buf, BUF_SIZE );
1656 SetText( buf );
1657 }
1658
1659
SetText(int64_t v)1660 void XMLElement::SetText(int64_t v)
1661 {
1662 char buf[BUF_SIZE];
1663 XMLUtil::ToStr(v, buf, BUF_SIZE);
1664 SetText(buf);
1665 }
1666
SetText(uint64_t v)1667 void XMLElement::SetText(uint64_t v) {
1668 char buf[BUF_SIZE];
1669 XMLUtil::ToStr(v, buf, BUF_SIZE);
1670 SetText(buf);
1671 }
1672
1673
SetText(bool v)1674 void XMLElement::SetText( bool v )
1675 {
1676 char buf[BUF_SIZE];
1677 XMLUtil::ToStr( v, buf, BUF_SIZE );
1678 SetText( buf );
1679 }
1680
1681
SetText(float v)1682 void XMLElement::SetText( float v )
1683 {
1684 char buf[BUF_SIZE];
1685 XMLUtil::ToStr( v, buf, BUF_SIZE );
1686 SetText( buf );
1687 }
1688
1689
SetText(double v)1690 void XMLElement::SetText( double v )
1691 {
1692 char buf[BUF_SIZE];
1693 XMLUtil::ToStr( v, buf, BUF_SIZE );
1694 SetText( buf );
1695 }
1696
1697
QueryIntText(int * ival) const1698 XMLError XMLElement::QueryIntText( int* ival ) const
1699 {
1700 if ( FirstChild() && FirstChild()->ToText() ) {
1701 const char* t = FirstChild()->Value();
1702 if ( XMLUtil::ToInt( t, ival ) ) {
1703 return XML_SUCCESS;
1704 }
1705 return XML_CAN_NOT_CONVERT_TEXT;
1706 }
1707 return XML_NO_TEXT_NODE;
1708 }
1709
1710
QueryUnsignedText(unsigned * uval) const1711 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1712 {
1713 if ( FirstChild() && FirstChild()->ToText() ) {
1714 const char* t = FirstChild()->Value();
1715 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1716 return XML_SUCCESS;
1717 }
1718 return XML_CAN_NOT_CONVERT_TEXT;
1719 }
1720 return XML_NO_TEXT_NODE;
1721 }
1722
1723
QueryInt64Text(int64_t * ival) const1724 XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1725 {
1726 if (FirstChild() && FirstChild()->ToText()) {
1727 const char* t = FirstChild()->Value();
1728 if (XMLUtil::ToInt64(t, ival)) {
1729 return XML_SUCCESS;
1730 }
1731 return XML_CAN_NOT_CONVERT_TEXT;
1732 }
1733 return XML_NO_TEXT_NODE;
1734 }
1735
1736
QueryUnsigned64Text(uint64_t * ival) const1737 XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const
1738 {
1739 if(FirstChild() && FirstChild()->ToText()) {
1740 const char* t = FirstChild()->Value();
1741 if(XMLUtil::ToUnsigned64(t, ival)) {
1742 return XML_SUCCESS;
1743 }
1744 return XML_CAN_NOT_CONVERT_TEXT;
1745 }
1746 return XML_NO_TEXT_NODE;
1747 }
1748
1749
QueryBoolText(bool * bval) const1750 XMLError XMLElement::QueryBoolText( bool* bval ) const
1751 {
1752 if ( FirstChild() && FirstChild()->ToText() ) {
1753 const char* t = FirstChild()->Value();
1754 if ( XMLUtil::ToBool( t, bval ) ) {
1755 return XML_SUCCESS;
1756 }
1757 return XML_CAN_NOT_CONVERT_TEXT;
1758 }
1759 return XML_NO_TEXT_NODE;
1760 }
1761
1762
QueryDoubleText(double * dval) const1763 XMLError XMLElement::QueryDoubleText( double* dval ) const
1764 {
1765 if ( FirstChild() && FirstChild()->ToText() ) {
1766 const char* t = FirstChild()->Value();
1767 if ( XMLUtil::ToDouble( t, dval ) ) {
1768 return XML_SUCCESS;
1769 }
1770 return XML_CAN_NOT_CONVERT_TEXT;
1771 }
1772 return XML_NO_TEXT_NODE;
1773 }
1774
1775
QueryFloatText(float * fval) const1776 XMLError XMLElement::QueryFloatText( float* fval ) const
1777 {
1778 if ( FirstChild() && FirstChild()->ToText() ) {
1779 const char* t = FirstChild()->Value();
1780 if ( XMLUtil::ToFloat( t, fval ) ) {
1781 return XML_SUCCESS;
1782 }
1783 return XML_CAN_NOT_CONVERT_TEXT;
1784 }
1785 return XML_NO_TEXT_NODE;
1786 }
1787
IntText(int defaultValue) const1788 int XMLElement::IntText(int defaultValue) const
1789 {
1790 int i = defaultValue;
1791 QueryIntText(&i);
1792 return i;
1793 }
1794
UnsignedText(unsigned defaultValue) const1795 unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1796 {
1797 unsigned i = defaultValue;
1798 QueryUnsignedText(&i);
1799 return i;
1800 }
1801
Int64Text(int64_t defaultValue) const1802 int64_t XMLElement::Int64Text(int64_t defaultValue) const
1803 {
1804 int64_t i = defaultValue;
1805 QueryInt64Text(&i);
1806 return i;
1807 }
1808
Unsigned64Text(uint64_t defaultValue) const1809 uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const
1810 {
1811 uint64_t i = defaultValue;
1812 QueryUnsigned64Text(&i);
1813 return i;
1814 }
1815
BoolText(bool defaultValue) const1816 bool XMLElement::BoolText(bool defaultValue) const
1817 {
1818 bool b = defaultValue;
1819 QueryBoolText(&b);
1820 return b;
1821 }
1822
DoubleText(double defaultValue) const1823 double XMLElement::DoubleText(double defaultValue) const
1824 {
1825 double d = defaultValue;
1826 QueryDoubleText(&d);
1827 return d;
1828 }
1829
FloatText(float defaultValue) const1830 float XMLElement::FloatText(float defaultValue) const
1831 {
1832 float f = defaultValue;
1833 QueryFloatText(&f);
1834 return f;
1835 }
1836
1837
FindOrCreateAttribute(const char * name)1838 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1839 {
1840 XMLAttribute* last = 0;
1841 XMLAttribute* attrib = 0;
1842 for( attrib = _rootAttribute;
1843 attrib;
1844 last = attrib, attrib = attrib->_next ) {
1845 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1846 break;
1847 }
1848 }
1849 if ( !attrib ) {
1850 attrib = CreateAttribute();
1851 TIXMLASSERT( attrib );
1852 if ( last ) {
1853 TIXMLASSERT( last->_next == 0 );
1854 last->_next = attrib;
1855 }
1856 else {
1857 TIXMLASSERT( _rootAttribute == 0 );
1858 _rootAttribute = attrib;
1859 }
1860 attrib->SetName( name );
1861 }
1862 return attrib;
1863 }
1864
1865
DeleteAttribute(const char * name)1866 void XMLElement::DeleteAttribute( const char* name )
1867 {
1868 XMLAttribute* prev = 0;
1869 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1870 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1871 if ( prev ) {
1872 prev->_next = a->_next;
1873 }
1874 else {
1875 _rootAttribute = a->_next;
1876 }
1877 DeleteAttribute( a );
1878 break;
1879 }
1880 prev = a;
1881 }
1882 }
1883
1884
ParseAttributes(char * p,int * curLineNumPtr)1885 char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
1886 {
1887 XMLAttribute* prevAttribute = 0;
1888
1889 // Read the attributes.
1890 while( p ) {
1891 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1892 if ( !(*p) ) {
1893 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
1894 return 0;
1895 }
1896
1897 // attribute.
1898 if (XMLUtil::IsNameStartChar( *p ) ) {
1899 XMLAttribute* attrib = CreateAttribute();
1900 TIXMLASSERT( attrib );
1901 attrib->_parseLineNum = _document->_parseCurLineNum;
1902
1903 const int attrLineNum = attrib->_parseLineNum;
1904
1905 p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
1906 if ( !p || Attribute( attrib->Name() ) ) {
1907 DeleteAttribute( attrib );
1908 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
1909 return 0;
1910 }
1911 // There is a minor bug here: if the attribute in the source xml
1912 // document is duplicated, it will not be detected and the
1913 // attribute will be doubly added. However, tracking the 'prevAttribute'
1914 // avoids re-scanning the attribute list. Preferring performance for
1915 // now, may reconsider in the future.
1916 if ( prevAttribute ) {
1917 TIXMLASSERT( prevAttribute->_next == 0 );
1918 prevAttribute->_next = attrib;
1919 }
1920 else {
1921 TIXMLASSERT( _rootAttribute == 0 );
1922 _rootAttribute = attrib;
1923 }
1924 prevAttribute = attrib;
1925 }
1926 // end of the tag
1927 else if ( *p == '>' ) {
1928 ++p;
1929 break;
1930 }
1931 // end of the tag
1932 else if ( *p == '/' && *(p+1) == '>' ) {
1933 _closingType = CLOSED;
1934 return p+2; // done; sealed element.
1935 }
1936 else {
1937 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
1938 return 0;
1939 }
1940 }
1941 return p;
1942 }
1943
DeleteAttribute(XMLAttribute * attribute)1944 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1945 {
1946 if ( attribute == 0 ) {
1947 return;
1948 }
1949 MemPool* pool = attribute->_memPool;
1950 attribute->~XMLAttribute();
1951 pool->Free( attribute );
1952 }
1953
CreateAttribute()1954 XMLAttribute* XMLElement::CreateAttribute()
1955 {
1956 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1957 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1958 TIXMLASSERT( attrib );
1959 attrib->_memPool = &_document->_attributePool;
1960 attrib->_memPool->SetTracked();
1961 return attrib;
1962 }
1963
1964
InsertNewChildElement(const char * name)1965 XMLElement* XMLElement::InsertNewChildElement(const char* name)
1966 {
1967 XMLElement* node = _document->NewElement(name);
1968 return InsertEndChild(node) ? node : 0;
1969 }
1970
InsertNewComment(const char * comment)1971 XMLComment* XMLElement::InsertNewComment(const char* comment)
1972 {
1973 XMLComment* node = _document->NewComment(comment);
1974 return InsertEndChild(node) ? node : 0;
1975 }
1976
InsertNewText(const char * text)1977 XMLText* XMLElement::InsertNewText(const char* text)
1978 {
1979 XMLText* node = _document->NewText(text);
1980 return InsertEndChild(node) ? node : 0;
1981 }
1982
InsertNewDeclaration(const char * text)1983 XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text)
1984 {
1985 XMLDeclaration* node = _document->NewDeclaration(text);
1986 return InsertEndChild(node) ? node : 0;
1987 }
1988
InsertNewUnknown(const char * text)1989 XMLUnknown* XMLElement::InsertNewUnknown(const char* text)
1990 {
1991 XMLUnknown* node = _document->NewUnknown(text);
1992 return InsertEndChild(node) ? node : 0;
1993 }
1994
1995
1996
1997 //
1998 // <ele></ele>
1999 // <ele>foo<b>bar</b></ele>
2000 //
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)2001 char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
2002 {
2003 // Read the element name.
2004 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
2005
2006 // The closing element is the </element> form. It is
2007 // parsed just like a regular element then deleted from
2008 // the DOM.
2009 if ( *p == '/' ) {
2010 _closingType = CLOSING;
2011 ++p;
2012 }
2013
2014 p = _value.ParseName( p );
2015 if ( _value.Empty() ) {
2016 return 0;
2017 }
2018
2019 p = ParseAttributes( p, curLineNumPtr );
2020 if ( !p || !*p || _closingType != OPEN ) {
2021 return p;
2022 }
2023
2024 p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
2025 return p;
2026 }
2027
2028
2029
ShallowClone(XMLDocument * doc) const2030 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
2031 {
2032 if ( !doc ) {
2033 doc = _document;
2034 }
2035 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
2036 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
2037 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
2038 }
2039 return element;
2040 }
2041
2042
ShallowEqual(const XMLNode * compare) const2043 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
2044 {
2045 TIXMLASSERT( compare );
2046 const XMLElement* other = compare->ToElement();
2047 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
2048
2049 const XMLAttribute* a=FirstAttribute();
2050 const XMLAttribute* b=other->FirstAttribute();
2051
2052 while ( a && b ) {
2053 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
2054 return false;
2055 }
2056 a = a->Next();
2057 b = b->Next();
2058 }
2059 if ( a || b ) {
2060 // different count
2061 return false;
2062 }
2063 return true;
2064 }
2065 return false;
2066 }
2067
2068
Accept(XMLVisitor * visitor) const2069 bool XMLElement::Accept( XMLVisitor* visitor ) const
2070 {
2071 TIXMLASSERT( visitor );
2072 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
2073 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
2074 if ( !node->Accept( visitor ) ) {
2075 break;
2076 }
2077 }
2078 }
2079 return visitor->VisitExit( *this );
2080 }
2081
2082
2083 // --------- XMLDocument ----------- //
2084
2085 // Warning: List must match 'enum XMLError'
2086 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
2087 "XML_SUCCESS",
2088 "XML_NO_ATTRIBUTE",
2089 "XML_WRONG_ATTRIBUTE_TYPE",
2090 "XML_ERROR_FILE_NOT_FOUND",
2091 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
2092 "XML_ERROR_FILE_READ_ERROR",
2093 "XML_ERROR_PARSING_ELEMENT",
2094 "XML_ERROR_PARSING_ATTRIBUTE",
2095 "XML_ERROR_PARSING_TEXT",
2096 "XML_ERROR_PARSING_CDATA",
2097 "XML_ERROR_PARSING_COMMENT",
2098 "XML_ERROR_PARSING_DECLARATION",
2099 "XML_ERROR_PARSING_UNKNOWN",
2100 "XML_ERROR_EMPTY_DOCUMENT",
2101 "XML_ERROR_MISMATCHED_ELEMENT",
2102 "XML_ERROR_PARSING",
2103 "XML_CAN_NOT_CONVERT_TEXT",
2104 "XML_NO_TEXT_NODE",
2105 "XML_ELEMENT_DEPTH_EXCEEDED"
2106 };
2107
2108
XMLDocument(bool processEntities,Whitespace whitespaceMode)2109 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
2110 XMLNode( 0 ),
2111 _writeBOM( false ),
2112 _processEntities( processEntities ),
2113 _errorID(XML_SUCCESS),
2114 _whitespaceMode( whitespaceMode ),
2115 _errorStr(),
2116 _errorLineNum( 0 ),
2117 _charBuffer( 0 ),
2118 _parseCurLineNum( 0 ),
2119 _parsingDepth(0),
2120 _unlinked(),
2121 _elementPool(),
2122 _attributePool(),
2123 _textPool(),
2124 _commentPool()
2125 {
2126 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2127 _document = this;
2128 }
2129
2130
~XMLDocument()2131 XMLDocument::~XMLDocument()
2132 {
2133 Clear();
2134 }
2135
2136
MarkInUse(const XMLNode * const node)2137 void XMLDocument::MarkInUse(const XMLNode* const node)
2138 {
2139 TIXMLASSERT(node);
2140 TIXMLASSERT(node->_parent == 0);
2141
2142 for (int i = 0; i < _unlinked.Size(); ++i) {
2143 if (node == _unlinked[i]) {
2144 _unlinked.SwapRemove(i);
2145 break;
2146 }
2147 }
2148 }
2149
Clear()2150 void XMLDocument::Clear()
2151 {
2152 DeleteChildren();
2153 while( _unlinked.Size()) {
2154 DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2155 }
2156
2157 #ifdef TINYXML2_DEBUG
2158 const bool hadError = Error();
2159 #endif
2160 ClearError();
2161
2162 delete [] _charBuffer;
2163 _charBuffer = 0;
2164 _parsingDepth = 0;
2165
2166 #if 0
2167 _textPool.Trace( "text" );
2168 _elementPool.Trace( "element" );
2169 _commentPool.Trace( "comment" );
2170 _attributePool.Trace( "attribute" );
2171 #endif
2172
2173 #ifdef TINYXML2_DEBUG
2174 if ( !hadError ) {
2175 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
2176 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2177 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
2178 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
2179 }
2180 #endif
2181 }
2182
2183
DeepCopy(XMLDocument * target) const2184 void XMLDocument::DeepCopy(XMLDocument* target) const
2185 {
2186 TIXMLASSERT(target);
2187 if (target == this) {
2188 return; // technically success - a no-op.
2189 }
2190
2191 target->Clear();
2192 for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2193 target->InsertEndChild(node->DeepClone(target));
2194 }
2195 }
2196
NewElement(const char * name)2197 XMLElement* XMLDocument::NewElement( const char* name )
2198 {
2199 XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
2200 ele->SetName( name );
2201 return ele;
2202 }
2203
2204
NewComment(const char * str)2205 XMLComment* XMLDocument::NewComment( const char* str )
2206 {
2207 XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
2208 comment->SetValue( str );
2209 return comment;
2210 }
2211
2212
NewText(const char * str)2213 XMLText* XMLDocument::NewText( const char* str )
2214 {
2215 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
2216 text->SetValue( str );
2217 return text;
2218 }
2219
2220
NewDeclaration(const char * str)2221 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2222 {
2223 XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
2224 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2225 return dec;
2226 }
2227
2228
NewUnknown(const char * str)2229 XMLUnknown* XMLDocument::NewUnknown( const char* str )
2230 {
2231 XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
2232 unk->SetValue( str );
2233 return unk;
2234 }
2235
callfopen(const char * filepath,const char * mode)2236 static FILE* callfopen( const char* filepath, const char* mode )
2237 {
2238 TIXMLASSERT( filepath );
2239 TIXMLASSERT( mode );
2240 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2241 FILE* fp = 0;
2242 const errno_t err = fopen_s( &fp, filepath, mode );
2243 if ( err ) {
2244 return 0;
2245 }
2246 #else
2247 FILE* fp = fopen( filepath, mode );
2248 #endif
2249 return fp;
2250 }
2251
DeleteNode(XMLNode * node)2252 void XMLDocument::DeleteNode( XMLNode* node ) {
2253 TIXMLASSERT( node );
2254 TIXMLASSERT(node->_document == this );
2255 if (node->_parent) {
2256 node->_parent->DeleteChild( node );
2257 }
2258 else {
2259 // Isn't in the tree.
2260 // Use the parent delete.
2261 // Also, we need to mark it tracked: we 'know'
2262 // it was never used.
2263 node->_memPool->SetTracked();
2264 // Call the static XMLNode version:
2265 XMLNode::DeleteNode(node);
2266 }
2267 }
2268
2269
LoadFile(const char * filename)2270 XMLError XMLDocument::LoadFile( const char* filename )
2271 {
2272 if ( !filename ) {
2273 TIXMLASSERT( false );
2274 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2275 return _errorID;
2276 }
2277
2278 Clear();
2279 FILE* fp = callfopen( filename, "rb" );
2280 if ( !fp ) {
2281 SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
2282 return _errorID;
2283 }
2284 LoadFile( fp );
2285 fclose( fp );
2286 return _errorID;
2287 }
2288
2289 // This is likely overengineered template art to have a check that unsigned long value incremented
2290 // by one still fits into size_t. If size_t type is larger than unsigned long type
2291 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
2292 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
2293 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
2294 // types sizes relate to each other.
2295 template
2296 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
2297 struct LongFitsIntoSizeTMinusOne {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2298 static bool Fits( unsigned long value )
2299 {
2300 return value < static_cast<size_t>(-1);
2301 }
2302 };
2303
2304 template <>
2305 struct LongFitsIntoSizeTMinusOne<false> {
Fitstinyxml2::LongFitsIntoSizeTMinusOne2306 static bool Fits( unsigned long )
2307 {
2308 return true;
2309 }
2310 };
2311
LoadFile(FILE * fp)2312 XMLError XMLDocument::LoadFile( FILE* fp )
2313 {
2314 Clear();
2315
2316 fseek( fp, 0, SEEK_SET );
2317 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2318 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2319 return _errorID;
2320 }
2321
2322 fseek( fp, 0, SEEK_END );
2323 const long filelength = ftell( fp );
2324 fseek( fp, 0, SEEK_SET );
2325 if ( filelength == -1L ) {
2326 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2327 return _errorID;
2328 }
2329 TIXMLASSERT( filelength >= 0 );
2330
2331 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
2332 // Cannot handle files which won't fit in buffer together with null terminator
2333 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2334 return _errorID;
2335 }
2336
2337 if ( filelength == 0 ) {
2338 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2339 return _errorID;
2340 }
2341
2342 const size_t size = filelength;
2343 TIXMLASSERT( _charBuffer == 0 );
2344 _charBuffer = new char[size+1];
2345 const size_t read = fread( _charBuffer, 1, size, fp );
2346 if ( read != size ) {
2347 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2348 return _errorID;
2349 }
2350
2351 _charBuffer[size] = 0;
2352
2353 Parse();
2354 return _errorID;
2355 }
2356
2357
SaveFile(const char * filename,bool compact)2358 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
2359 {
2360 if ( !filename ) {
2361 TIXMLASSERT( false );
2362 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2363 return _errorID;
2364 }
2365
2366 FILE* fp = callfopen( filename, "w" );
2367 if ( !fp ) {
2368 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
2369 return _errorID;
2370 }
2371 SaveFile(fp, compact);
2372 fclose( fp );
2373 return _errorID;
2374 }
2375
2376
SaveFile(FILE * fp,bool compact)2377 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2378 {
2379 // Clear any error from the last save, otherwise it will get reported
2380 // for *this* call.
2381 ClearError();
2382 XMLPrinter stream( fp, compact );
2383 Print( &stream );
2384 return _errorID;
2385 }
2386
2387
Parse(const char * p,size_t len)2388 XMLError XMLDocument::Parse( const char* p, size_t len )
2389 {
2390 Clear();
2391
2392 if ( len == 0 || !p || !*p ) {
2393 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2394 return _errorID;
2395 }
2396 if ( len == static_cast<size_t>(-1) ) {
2397 len = strlen( p );
2398 }
2399 TIXMLASSERT( _charBuffer == 0 );
2400 _charBuffer = new char[ len+1 ];
2401 memcpy( _charBuffer, p, len );
2402 _charBuffer[len] = 0;
2403
2404 Parse();
2405 if ( Error() ) {
2406 // clean up now essentially dangling memory.
2407 // and the parse fail can put objects in the
2408 // pools that are dead and inaccessible.
2409 DeleteChildren();
2410 _elementPool.Clear();
2411 _attributePool.Clear();
2412 _textPool.Clear();
2413 _commentPool.Clear();
2414 }
2415 return _errorID;
2416 }
2417
2418
Print(XMLPrinter * streamer) const2419 void XMLDocument::Print( XMLPrinter* streamer ) const
2420 {
2421 if ( streamer ) {
2422 Accept( streamer );
2423 }
2424 else {
2425 XMLPrinter stdoutStreamer( stdout );
2426 Accept( &stdoutStreamer );
2427 }
2428 }
2429
2430
SetError(XMLError error,int lineNum,const char * format,...)2431 void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
2432 {
2433 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2434 _errorID = error;
2435 _errorLineNum = lineNum;
2436 _errorStr.Reset();
2437
2438 const size_t BUFFER_SIZE = 1000;
2439 char* buffer = new char[BUFFER_SIZE];
2440
2441 TIXMLASSERT(sizeof(error) <= sizeof(int));
2442 TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
2443
2444 if (format) {
2445 size_t len = strlen(buffer);
2446 TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2447 len = strlen(buffer);
2448
2449 va_list va;
2450 va_start(va, format);
2451 TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2452 va_end(va);
2453 }
2454 _errorStr.SetStr(buffer);
2455 delete[] buffer;
2456 }
2457
2458
ErrorIDToName(XMLError errorID)2459 /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2460 {
2461 TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2462 const char* errorName = _errorNames[errorID];
2463 TIXMLASSERT( errorName && errorName[0] );
2464 return errorName;
2465 }
2466
ErrorStr() const2467 const char* XMLDocument::ErrorStr() const
2468 {
2469 return _errorStr.Empty() ? "" : _errorStr.GetStr();
2470 }
2471
2472
PrintError() const2473 void XMLDocument::PrintError() const
2474 {
2475 printf("%s\n", ErrorStr());
2476 }
2477
ErrorName() const2478 const char* XMLDocument::ErrorName() const
2479 {
2480 return ErrorIDToName(_errorID);
2481 }
2482
Parse()2483 void XMLDocument::Parse()
2484 {
2485 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2486 TIXMLASSERT( _charBuffer );
2487 _parseCurLineNum = 1;
2488 _parseLineNum = 1;
2489 char* p = _charBuffer;
2490 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2491 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2492 if ( !*p ) {
2493 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2494 return;
2495 }
2496 ParseDeep(p, 0, &_parseCurLineNum );
2497 }
2498
PushDepth()2499 void XMLDocument::PushDepth()
2500 {
2501 _parsingDepth++;
2502 if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2503 SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2504 }
2505 }
2506
PopDepth()2507 void XMLDocument::PopDepth()
2508 {
2509 TIXMLASSERT(_parsingDepth > 0);
2510 --_parsingDepth;
2511 }
2512
XMLPrinter(FILE * file,bool compact,int depth)2513 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2514 _elementJustOpened( false ),
2515 _stack(),
2516 _firstElement( true ),
2517 _fp( file ),
2518 _depth( depth ),
2519 _textDepth( -1 ),
2520 _processEntities( true ),
2521 _compactMode( compact ),
2522 _buffer()
2523 {
2524 for( int i=0; i<ENTITY_RANGE; ++i ) {
2525 _entityFlag[i] = false;
2526 _restrictedEntityFlag[i] = false;
2527 }
2528 for( int i=0; i<NUM_ENTITIES; ++i ) {
2529 const char entityValue = entities[i].value;
2530 const unsigned char flagIndex = static_cast<unsigned char>(entityValue);
2531 TIXMLASSERT( flagIndex < ENTITY_RANGE );
2532 _entityFlag[flagIndex] = true;
2533 }
2534 _restrictedEntityFlag[static_cast<unsigned char>('&')] = true;
2535 _restrictedEntityFlag[static_cast<unsigned char>('<')] = true;
2536 _restrictedEntityFlag[static_cast<unsigned char>('>')] = true; // not required, but consistency is nice
2537 _buffer.Push( 0 );
2538 }
2539
2540
Print(const char * format,...)2541 void XMLPrinter::Print( const char* format, ... )
2542 {
2543 va_list va;
2544 va_start( va, format );
2545
2546 if ( _fp ) {
2547 vfprintf( _fp, format, va );
2548 }
2549 else {
2550 const int len = TIXML_VSCPRINTF( format, va );
2551 // Close out and re-start the va-args
2552 va_end( va );
2553 TIXMLASSERT( len >= 0 );
2554 va_start( va, format );
2555 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2556 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2557 TIXML_VSNPRINTF( p, len+1, format, va );
2558 }
2559 va_end( va );
2560 }
2561
2562
Write(const char * data,size_t size)2563 void XMLPrinter::Write( const char* data, size_t size )
2564 {
2565 if ( _fp ) {
2566 fwrite ( data , sizeof(char), size, _fp);
2567 }
2568 else {
2569 char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
2570 memcpy( p, data, size );
2571 p[size] = 0;
2572 }
2573 }
2574
2575
Putc(char ch)2576 void XMLPrinter::Putc( char ch )
2577 {
2578 if ( _fp ) {
2579 fputc ( ch, _fp);
2580 }
2581 else {
2582 char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
2583 p[0] = ch;
2584 p[1] = 0;
2585 }
2586 }
2587
2588
PrintSpace(int depth)2589 void XMLPrinter::PrintSpace( int depth )
2590 {
2591 for( int i=0; i<depth; ++i ) {
2592 Write( " " );
2593 }
2594 }
2595
2596
PrintString(const char * p,bool restricted)2597 void XMLPrinter::PrintString( const char* p, bool restricted )
2598 {
2599 // Look for runs of bytes between entities to print.
2600 const char* q = p;
2601
2602 if ( _processEntities ) {
2603 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2604 while ( *q ) {
2605 TIXMLASSERT( p <= q );
2606 // Remember, char is sometimes signed. (How many times has that bitten me?)
2607 if ( *q > 0 && *q < ENTITY_RANGE ) {
2608 // Check for entities. If one is found, flush
2609 // the stream up until the entity, write the
2610 // entity, and keep looking.
2611 if ( flag[static_cast<unsigned char>(*q)] ) {
2612 while ( p < q ) {
2613 const size_t delta = q - p;
2614 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2615 Write( p, toPrint );
2616 p += toPrint;
2617 }
2618 bool entityPatternPrinted = false;
2619 for( int i=0; i<NUM_ENTITIES; ++i ) {
2620 if ( entities[i].value == *q ) {
2621 Putc( '&' );
2622 Write( entities[i].pattern, entities[i].length );
2623 Putc( ';' );
2624 entityPatternPrinted = true;
2625 break;
2626 }
2627 }
2628 if ( !entityPatternPrinted ) {
2629 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2630 TIXMLASSERT( false );
2631 }
2632 ++p;
2633 }
2634 }
2635 ++q;
2636 TIXMLASSERT( p <= q );
2637 }
2638 // Flush the remaining string. This will be the entire
2639 // string if an entity wasn't found.
2640 if ( p < q ) {
2641 const size_t delta = q - p;
2642 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2643 Write( p, toPrint );
2644 }
2645 }
2646 else {
2647 Write( p );
2648 }
2649 }
2650
2651
PushHeader(bool writeBOM,bool writeDec)2652 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2653 {
2654 if ( writeBOM ) {
2655 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2656 Write( reinterpret_cast< const char* >( bom ) );
2657 }
2658 if ( writeDec ) {
2659 PushDeclaration( "xml version=\"1.0\"" );
2660 }
2661 }
2662
2663
OpenElement(const char * name,bool compactMode)2664 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2665 {
2666 SealElementIfJustOpened();
2667 _stack.Push( name );
2668
2669 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2670 Putc( '\n' );
2671 PrintSpace( _depth );
2672 }
2673
2674 Write ( "<" );
2675 Write ( name );
2676
2677 _elementJustOpened = true;
2678 _firstElement = false;
2679 ++_depth;
2680 }
2681
2682
PushAttribute(const char * name,const char * value)2683 void XMLPrinter::PushAttribute( const char* name, const char* value )
2684 {
2685 TIXMLASSERT( _elementJustOpened );
2686 Putc ( ' ' );
2687 Write( name );
2688 Write( "=\"" );
2689 PrintString( value, false );
2690 Putc ( '\"' );
2691 }
2692
2693
PushAttribute(const char * name,int v)2694 void XMLPrinter::PushAttribute( const char* name, int v )
2695 {
2696 char buf[BUF_SIZE];
2697 XMLUtil::ToStr( v, buf, BUF_SIZE );
2698 PushAttribute( name, buf );
2699 }
2700
2701
PushAttribute(const char * name,unsigned v)2702 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2703 {
2704 char buf[BUF_SIZE];
2705 XMLUtil::ToStr( v, buf, BUF_SIZE );
2706 PushAttribute( name, buf );
2707 }
2708
2709
PushAttribute(const char * name,int64_t v)2710 void XMLPrinter::PushAttribute(const char* name, int64_t v)
2711 {
2712 char buf[BUF_SIZE];
2713 XMLUtil::ToStr(v, buf, BUF_SIZE);
2714 PushAttribute(name, buf);
2715 }
2716
2717
PushAttribute(const char * name,uint64_t v)2718 void XMLPrinter::PushAttribute(const char* name, uint64_t v)
2719 {
2720 char buf[BUF_SIZE];
2721 XMLUtil::ToStr(v, buf, BUF_SIZE);
2722 PushAttribute(name, buf);
2723 }
2724
2725
PushAttribute(const char * name,bool v)2726 void XMLPrinter::PushAttribute( const char* name, bool v )
2727 {
2728 char buf[BUF_SIZE];
2729 XMLUtil::ToStr( v, buf, BUF_SIZE );
2730 PushAttribute( name, buf );
2731 }
2732
2733
PushAttribute(const char * name,double v)2734 void XMLPrinter::PushAttribute( const char* name, double v )
2735 {
2736 char buf[BUF_SIZE];
2737 XMLUtil::ToStr( v, buf, BUF_SIZE );
2738 PushAttribute( name, buf );
2739 }
2740
2741
CloseElement(bool compactMode)2742 void XMLPrinter::CloseElement( bool compactMode )
2743 {
2744 --_depth;
2745 const char* name = _stack.Pop();
2746
2747 if ( _elementJustOpened ) {
2748 Write( "/>" );
2749 }
2750 else {
2751 if ( _textDepth < 0 && !compactMode) {
2752 Putc( '\n' );
2753 PrintSpace( _depth );
2754 }
2755 Write ( "</" );
2756 Write ( name );
2757 Write ( ">" );
2758 }
2759
2760 if ( _textDepth == _depth ) {
2761 _textDepth = -1;
2762 }
2763 if ( _depth == 0 && !compactMode) {
2764 Putc( '\n' );
2765 }
2766 _elementJustOpened = false;
2767 }
2768
2769
SealElementIfJustOpened()2770 void XMLPrinter::SealElementIfJustOpened()
2771 {
2772 if ( !_elementJustOpened ) {
2773 return;
2774 }
2775 _elementJustOpened = false;
2776 Putc( '>' );
2777 }
2778
2779
PushText(const char * text,bool cdata)2780 void XMLPrinter::PushText( const char* text, bool cdata )
2781 {
2782 _textDepth = _depth-1;
2783
2784 SealElementIfJustOpened();
2785 if ( cdata ) {
2786 Write( "<![CDATA[" );
2787 Write( text );
2788 Write( "]]>" );
2789 }
2790 else {
2791 PrintString( text, true );
2792 }
2793 }
2794
2795
PushText(int64_t value)2796 void XMLPrinter::PushText( int64_t value )
2797 {
2798 char buf[BUF_SIZE];
2799 XMLUtil::ToStr( value, buf, BUF_SIZE );
2800 PushText( buf, false );
2801 }
2802
2803
PushText(uint64_t value)2804 void XMLPrinter::PushText( uint64_t value )
2805 {
2806 char buf[BUF_SIZE];
2807 XMLUtil::ToStr(value, buf, BUF_SIZE);
2808 PushText(buf, false);
2809 }
2810
2811
PushText(int value)2812 void XMLPrinter::PushText( int value )
2813 {
2814 char buf[BUF_SIZE];
2815 XMLUtil::ToStr( value, buf, BUF_SIZE );
2816 PushText( buf, false );
2817 }
2818
2819
PushText(unsigned value)2820 void XMLPrinter::PushText( unsigned value )
2821 {
2822 char buf[BUF_SIZE];
2823 XMLUtil::ToStr( value, buf, BUF_SIZE );
2824 PushText( buf, false );
2825 }
2826
2827
PushText(bool value)2828 void XMLPrinter::PushText( bool value )
2829 {
2830 char buf[BUF_SIZE];
2831 XMLUtil::ToStr( value, buf, BUF_SIZE );
2832 PushText( buf, false );
2833 }
2834
2835
PushText(float value)2836 void XMLPrinter::PushText( float value )
2837 {
2838 char buf[BUF_SIZE];
2839 XMLUtil::ToStr( value, buf, BUF_SIZE );
2840 PushText( buf, false );
2841 }
2842
2843
PushText(double value)2844 void XMLPrinter::PushText( double value )
2845 {
2846 char buf[BUF_SIZE];
2847 XMLUtil::ToStr( value, buf, BUF_SIZE );
2848 PushText( buf, false );
2849 }
2850
2851
PushComment(const char * comment)2852 void XMLPrinter::PushComment( const char* comment )
2853 {
2854 SealElementIfJustOpened();
2855 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2856 Putc( '\n' );
2857 PrintSpace( _depth );
2858 }
2859 _firstElement = false;
2860
2861 Write( "<!--" );
2862 Write( comment );
2863 Write( "-->" );
2864 }
2865
2866
PushDeclaration(const char * value)2867 void XMLPrinter::PushDeclaration( const char* value )
2868 {
2869 SealElementIfJustOpened();
2870 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2871 Putc( '\n' );
2872 PrintSpace( _depth );
2873 }
2874 _firstElement = false;
2875
2876 Write( "<?" );
2877 Write( value );
2878 Write( "?>" );
2879 }
2880
2881
PushUnknown(const char * value)2882 void XMLPrinter::PushUnknown( const char* value )
2883 {
2884 SealElementIfJustOpened();
2885 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2886 Putc( '\n' );
2887 PrintSpace( _depth );
2888 }
2889 _firstElement = false;
2890
2891 Write( "<!" );
2892 Write( value );
2893 Putc( '>' );
2894 }
2895
2896
VisitEnter(const XMLDocument & doc)2897 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2898 {
2899 _processEntities = doc.ProcessEntities();
2900 if ( doc.HasBOM() ) {
2901 PushHeader( true, false );
2902 }
2903 return true;
2904 }
2905
2906
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2907 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2908 {
2909 const XMLElement* parentElem = 0;
2910 if ( element.Parent() ) {
2911 parentElem = element.Parent()->ToElement();
2912 }
2913 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2914 OpenElement( element.Name(), compactMode );
2915 while ( attribute ) {
2916 PushAttribute( attribute->Name(), attribute->Value() );
2917 attribute = attribute->Next();
2918 }
2919 return true;
2920 }
2921
2922
VisitExit(const XMLElement & element)2923 bool XMLPrinter::VisitExit( const XMLElement& element )
2924 {
2925 CloseElement( CompactMode(element) );
2926 return true;
2927 }
2928
2929
Visit(const XMLText & text)2930 bool XMLPrinter::Visit( const XMLText& text )
2931 {
2932 PushText( text.Value(), text.CData() );
2933 return true;
2934 }
2935
2936
Visit(const XMLComment & comment)2937 bool XMLPrinter::Visit( const XMLComment& comment )
2938 {
2939 PushComment( comment.Value() );
2940 return true;
2941 }
2942
Visit(const XMLDeclaration & declaration)2943 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2944 {
2945 PushDeclaration( declaration.Value() );
2946 return true;
2947 }
2948
2949
Visit(const XMLUnknown & unknown)2950 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2951 {
2952 PushUnknown( unknown.Value() );
2953 return true;
2954 }
2955
2956 } // namespace tinyxml2
2957