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