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