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