1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
16
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
19
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
23
24 #include "tinyxml2.h"
25
26 #include <new> // yes, this one new style header, is in the Android SDK.
27 #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
28 # include <stddef.h>
29 # include <stdarg.h>
30 #else
31 # include <cstddef>
32 # include <cstdarg>
33 #endif
34
35 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
36 // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
37 /*int _snprintf_s(
38 char *buffer,
39 size_t sizeOfBuffer,
40 size_t count,
41 const char *format [,
42 argument] ...
43 );*/
TIXML_SNPRINTF(char * buffer,size_t size,const char * format,...)44 static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
45 {
46 va_list va;
47 va_start( va, format );
48 const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
49 va_end( va );
50 return result;
51 }
52
TIXML_VSNPRINTF(char * buffer,size_t size,const char * format,va_list va)53 static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
54 {
55 const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
56 return result;
57 }
58
59 #define TIXML_VSCPRINTF _vscprintf
60 #define TIXML_SSCANF sscanf_s
61 #elif defined _MSC_VER
62 // Microsoft Visual Studio 2003 and earlier or WinCE
63 #define TIXML_SNPRINTF _snprintf
64 #define TIXML_VSNPRINTF _vsnprintf
65 #define TIXML_SSCANF sscanf
66 #if (_MSC_VER < 1400 ) && (!defined WINCE)
67 // Microsoft Visual Studio 2003 and not WinCE.
68 #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
69 #else
70 // Microsoft Visual Studio 2003 and earlier or WinCE.
TIXML_VSCPRINTF(const char * format,va_list va)71 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
72 {
73 int len = 512;
74 for (;;) {
75 len = len*2;
76 char* str = new char[len]();
77 const int required = _vsnprintf(str, len, format, va);
78 delete[] str;
79 if ( required != -1 ) {
80 TIXMLASSERT( required >= 0 );
81 len = required;
82 break;
83 }
84 }
85 TIXMLASSERT( len >= 0 );
86 return len;
87 }
88 #endif
89 #else
90 // GCC version 3 and higher
91 //#warning( "Using sn* functions." )
92 #define TIXML_SNPRINTF snprintf
93 #define TIXML_VSNPRINTF vsnprintf
TIXML_VSCPRINTF(const char * format,va_list va)94 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
95 {
96 int len = vsnprintf( 0, 0, format, va );
97 TIXMLASSERT( len >= 0 );
98 return len;
99 }
100 #define TIXML_SSCANF sscanf
101 #endif
102
103 #if defined(_WIN64)
104 #define TIXML_FSEEK _fseeki64
105 #define TIXML_FTELL _ftelli64
106 #elif defined(__APPLE__) || (__FreeBSD__) || defined(__DragonFly__)
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 _appendedData( NULL ),
1571 _appendedDataSize( 0 )
1572 {
1573 }
1574
1575
~XMLElement()1576 XMLElement::~XMLElement()
1577 {
1578 while( _rootAttribute ) {
1579 XMLAttribute* next = _rootAttribute->_next;
1580 DeleteAttribute( _rootAttribute );
1581 _rootAttribute = next;
1582 }
1583 delete [] _appendedData;
1584 }
1585
1586
FindAttribute(const char * name) const1587 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1588 {
1589 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1590 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1591 return a;
1592 }
1593 }
1594 return 0;
1595 }
1596
1597
Attribute(const char * name,const char * value) const1598 const char* XMLElement::Attribute( const char* name, const char* value ) const
1599 {
1600 const XMLAttribute* a = FindAttribute( name );
1601 if ( !a ) {
1602 return 0;
1603 }
1604 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1605 return a->Value();
1606 }
1607 return 0;
1608 }
1609
IntAttribute(const char * name,int defaultValue) const1610 int XMLElement::IntAttribute(const char* name, int defaultValue) const
1611 {
1612 int i = defaultValue;
1613 QueryIntAttribute(name, &i);
1614 return i;
1615 }
1616
UnsignedAttribute(const char * name,unsigned defaultValue) const1617 unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
1618 {
1619 unsigned i = defaultValue;
1620 QueryUnsignedAttribute(name, &i);
1621 return i;
1622 }
1623
Int64Attribute(const char * name,int64_t defaultValue) const1624 int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
1625 {
1626 int64_t i = defaultValue;
1627 QueryInt64Attribute(name, &i);
1628 return i;
1629 }
1630
Unsigned64Attribute(const char * name,uint64_t defaultValue) const1631 uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const
1632 {
1633 uint64_t i = defaultValue;
1634 QueryUnsigned64Attribute(name, &i);
1635 return i;
1636 }
1637
BoolAttribute(const char * name,bool defaultValue) const1638 bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
1639 {
1640 bool b = defaultValue;
1641 QueryBoolAttribute(name, &b);
1642 return b;
1643 }
1644
DoubleAttribute(const char * name,double defaultValue) const1645 double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
1646 {
1647 double d = defaultValue;
1648 QueryDoubleAttribute(name, &d);
1649 return d;
1650 }
1651
FloatAttribute(const char * name,float defaultValue) const1652 float XMLElement::FloatAttribute(const char* name, float defaultValue) const
1653 {
1654 float f = defaultValue;
1655 QueryFloatAttribute(name, &f);
1656 return f;
1657 }
1658
GetText() const1659 const char* XMLElement::GetText() const
1660 {
1661 if ( FirstChild() && FirstChild()->ToText() ) {
1662 return FirstChild()->Value();
1663 }
1664 return 0;
1665 }
1666
1667
SetText(const char * inText)1668 void XMLElement::SetText( const char* inText )
1669 {
1670 if ( FirstChild() && FirstChild()->ToText() )
1671 FirstChild()->SetValue( inText );
1672 else {
1673 XMLText* theText = GetDocument()->NewText( inText );
1674 InsertFirstChild( theText );
1675 }
1676 }
1677
1678
SetText(int v)1679 void XMLElement::SetText( int v )
1680 {
1681 char buf[BUF_SIZE];
1682 XMLUtil::ToStr( v, buf, BUF_SIZE );
1683 SetText( buf );
1684 }
1685
1686
SetText(unsigned v)1687 void XMLElement::SetText( unsigned v )
1688 {
1689 char buf[BUF_SIZE];
1690 XMLUtil::ToStr( v, buf, BUF_SIZE );
1691 SetText( buf );
1692 }
1693
1694
SetText(int64_t v)1695 void XMLElement::SetText(int64_t v)
1696 {
1697 char buf[BUF_SIZE];
1698 XMLUtil::ToStr(v, buf, BUF_SIZE);
1699 SetText(buf);
1700 }
1701
SetText(uint64_t v)1702 void XMLElement::SetText(uint64_t v) {
1703 char buf[BUF_SIZE];
1704 XMLUtil::ToStr(v, buf, BUF_SIZE);
1705 SetText(buf);
1706 }
1707
1708
SetText(bool v)1709 void XMLElement::SetText( bool v )
1710 {
1711 char buf[BUF_SIZE];
1712 XMLUtil::ToStr( v, buf, BUF_SIZE );
1713 SetText( buf );
1714 }
1715
1716
SetText(float v)1717 void XMLElement::SetText( float v )
1718 {
1719 char buf[BUF_SIZE];
1720 XMLUtil::ToStr( v, buf, BUF_SIZE );
1721 SetText( buf );
1722 }
1723
1724
SetText(double v)1725 void XMLElement::SetText( double v )
1726 {
1727 char buf[BUF_SIZE];
1728 XMLUtil::ToStr( v, buf, BUF_SIZE );
1729 SetText( buf );
1730 }
1731
1732
QueryIntText(int * ival) const1733 XMLError XMLElement::QueryIntText( int* ival ) const
1734 {
1735 if ( FirstChild() && FirstChild()->ToText() ) {
1736 const char* t = FirstChild()->Value();
1737 if ( XMLUtil::ToInt( t, ival ) ) {
1738 return XML_SUCCESS;
1739 }
1740 return XML_CAN_NOT_CONVERT_TEXT;
1741 }
1742 return XML_NO_TEXT_NODE;
1743 }
1744
1745
QueryUnsignedText(unsigned * uval) const1746 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1747 {
1748 if ( FirstChild() && FirstChild()->ToText() ) {
1749 const char* t = FirstChild()->Value();
1750 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1751 return XML_SUCCESS;
1752 }
1753 return XML_CAN_NOT_CONVERT_TEXT;
1754 }
1755 return XML_NO_TEXT_NODE;
1756 }
1757
1758
QueryInt64Text(int64_t * ival) const1759 XMLError XMLElement::QueryInt64Text(int64_t* ival) const
1760 {
1761 if (FirstChild() && FirstChild()->ToText()) {
1762 const char* t = FirstChild()->Value();
1763 if (XMLUtil::ToInt64(t, ival)) {
1764 return XML_SUCCESS;
1765 }
1766 return XML_CAN_NOT_CONVERT_TEXT;
1767 }
1768 return XML_NO_TEXT_NODE;
1769 }
1770
1771
QueryUnsigned64Text(uint64_t * ival) const1772 XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const
1773 {
1774 if(FirstChild() && FirstChild()->ToText()) {
1775 const char* t = FirstChild()->Value();
1776 if(XMLUtil::ToUnsigned64(t, ival)) {
1777 return XML_SUCCESS;
1778 }
1779 return XML_CAN_NOT_CONVERT_TEXT;
1780 }
1781 return XML_NO_TEXT_NODE;
1782 }
1783
1784
QueryBoolText(bool * bval) const1785 XMLError XMLElement::QueryBoolText( bool* bval ) const
1786 {
1787 if ( FirstChild() && FirstChild()->ToText() ) {
1788 const char* t = FirstChild()->Value();
1789 if ( XMLUtil::ToBool( t, bval ) ) {
1790 return XML_SUCCESS;
1791 }
1792 return XML_CAN_NOT_CONVERT_TEXT;
1793 }
1794 return XML_NO_TEXT_NODE;
1795 }
1796
1797
QueryDoubleText(double * dval) const1798 XMLError XMLElement::QueryDoubleText( double* dval ) const
1799 {
1800 if ( FirstChild() && FirstChild()->ToText() ) {
1801 const char* t = FirstChild()->Value();
1802 if ( XMLUtil::ToDouble( t, dval ) ) {
1803 return XML_SUCCESS;
1804 }
1805 return XML_CAN_NOT_CONVERT_TEXT;
1806 }
1807 return XML_NO_TEXT_NODE;
1808 }
1809
1810
QueryFloatText(float * fval) const1811 XMLError XMLElement::QueryFloatText( float* fval ) const
1812 {
1813 if ( FirstChild() && FirstChild()->ToText() ) {
1814 const char* t = FirstChild()->Value();
1815 if ( XMLUtil::ToFloat( t, fval ) ) {
1816 return XML_SUCCESS;
1817 }
1818 return XML_CAN_NOT_CONVERT_TEXT;
1819 }
1820 return XML_NO_TEXT_NODE;
1821 }
1822
IntText(int defaultValue) const1823 int XMLElement::IntText(int defaultValue) const
1824 {
1825 int i = defaultValue;
1826 QueryIntText(&i);
1827 return i;
1828 }
1829
UnsignedText(unsigned defaultValue) const1830 unsigned XMLElement::UnsignedText(unsigned defaultValue) const
1831 {
1832 unsigned i = defaultValue;
1833 QueryUnsignedText(&i);
1834 return i;
1835 }
1836
Int64Text(int64_t defaultValue) const1837 int64_t XMLElement::Int64Text(int64_t defaultValue) const
1838 {
1839 int64_t i = defaultValue;
1840 QueryInt64Text(&i);
1841 return i;
1842 }
1843
Unsigned64Text(uint64_t defaultValue) const1844 uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const
1845 {
1846 uint64_t i = defaultValue;
1847 QueryUnsigned64Text(&i);
1848 return i;
1849 }
1850
BoolText(bool defaultValue) const1851 bool XMLElement::BoolText(bool defaultValue) const
1852 {
1853 bool b = defaultValue;
1854 QueryBoolText(&b);
1855 return b;
1856 }
1857
DoubleText(double defaultValue) const1858 double XMLElement::DoubleText(double defaultValue) const
1859 {
1860 double d = defaultValue;
1861 QueryDoubleText(&d);
1862 return d;
1863 }
1864
FloatText(float defaultValue) const1865 float XMLElement::FloatText(float defaultValue) const
1866 {
1867 float f = defaultValue;
1868 QueryFloatText(&f);
1869 return f;
1870 }
1871
1872
FindOrCreateAttribute(const char * name)1873 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1874 {
1875 XMLAttribute* last = 0;
1876 XMLAttribute* attrib = 0;
1877 for( attrib = _rootAttribute;
1878 attrib;
1879 last = attrib, attrib = attrib->_next ) {
1880 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1881 break;
1882 }
1883 }
1884 if ( !attrib ) {
1885 attrib = CreateAttribute();
1886 TIXMLASSERT( attrib );
1887 if ( last ) {
1888 TIXMLASSERT( last->_next == 0 );
1889 last->_next = attrib;
1890 }
1891 else {
1892 TIXMLASSERT( _rootAttribute == 0 );
1893 _rootAttribute = attrib;
1894 }
1895 attrib->SetName( name );
1896 }
1897 return attrib;
1898 }
1899
1900
DeleteAttribute(const char * name)1901 void XMLElement::DeleteAttribute( const char* name )
1902 {
1903 XMLAttribute* prev = 0;
1904 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1905 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1906 if ( prev ) {
1907 prev->_next = a->_next;
1908 }
1909 else {
1910 _rootAttribute = a->_next;
1911 }
1912 DeleteAttribute( a );
1913 break;
1914 }
1915 prev = a;
1916 }
1917 }
1918
1919
ParseAttributes(char * p,int * curLineNumPtr)1920 char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
1921 {
1922 XMLAttribute* prevAttribute = 0;
1923
1924 // Read the attributes.
1925 while( p ) {
1926 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
1927 if ( !(*p) ) {
1928 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
1929 return 0;
1930 }
1931
1932 // attribute.
1933 if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) {
1934 XMLAttribute* attrib = CreateAttribute();
1935 TIXMLASSERT( attrib );
1936 attrib->_parseLineNum = _document->_parseCurLineNum;
1937
1938 const int attrLineNum = attrib->_parseLineNum;
1939
1940 p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
1941 if ( !p || Attribute( attrib->Name() ) ) {
1942 DeleteAttribute( attrib );
1943 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
1944 return 0;
1945 }
1946 // There is a minor bug here: if the attribute in the source xml
1947 // document is duplicated, it will not be detected and the
1948 // attribute will be doubly added. However, tracking the 'prevAttribute'
1949 // avoids re-scanning the attribute list. Preferring performance for
1950 // now, may reconsider in the future.
1951 if ( prevAttribute ) {
1952 TIXMLASSERT( prevAttribute->_next == 0 );
1953 prevAttribute->_next = attrib;
1954 }
1955 else {
1956 TIXMLASSERT( _rootAttribute == 0 );
1957 _rootAttribute = attrib;
1958 }
1959 prevAttribute = attrib;
1960 }
1961 // end of the tag
1962 else if ( *p == '>' ) {
1963 ++p;
1964 break;
1965 }
1966 // end of the tag
1967 else if ( *p == '/' && *(p+1) == '>' ) {
1968 _closingType = CLOSED;
1969 return p+2; // done; sealed element.
1970 }
1971 else {
1972 _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
1973 return 0;
1974 }
1975 }
1976 return p;
1977 }
1978
DeleteAttribute(XMLAttribute * attribute)1979 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1980 {
1981 if ( attribute == 0 ) {
1982 return;
1983 }
1984 MemPool* pool = attribute->_memPool;
1985 attribute->~XMLAttribute();
1986 pool->Free( attribute );
1987 }
1988
CreateAttribute()1989 XMLAttribute* XMLElement::CreateAttribute()
1990 {
1991 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1992 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1993 TIXMLASSERT( attrib );
1994 attrib->_memPool = &_document->_attributePool;
1995 attrib->_memPool->SetTracked();
1996 return attrib;
1997 }
1998
1999
InsertNewChildElement(const char * name)2000 XMLElement* XMLElement::InsertNewChildElement(const char* name)
2001 {
2002 XMLElement* node = _document->NewElement(name);
2003 return InsertEndChild(node) ? node : 0;
2004 }
2005
InsertNewComment(const char * comment)2006 XMLComment* XMLElement::InsertNewComment(const char* comment)
2007 {
2008 XMLComment* node = _document->NewComment(comment);
2009 return InsertEndChild(node) ? node : 0;
2010 }
2011
InsertNewText(const char * text)2012 XMLText* XMLElement::InsertNewText(const char* text)
2013 {
2014 XMLText* node = _document->NewText(text);
2015 return InsertEndChild(node) ? node : 0;
2016 }
2017
InsertNewDeclaration(const char * text)2018 XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text)
2019 {
2020 XMLDeclaration* node = _document->NewDeclaration(text);
2021 return InsertEndChild(node) ? node : 0;
2022 }
2023
InsertNewUnknown(const char * text)2024 XMLUnknown* XMLElement::InsertNewUnknown(const char* text)
2025 {
2026 XMLUnknown* node = _document->NewUnknown(text);
2027 return InsertEndChild(node) ? node : 0;
2028 }
2029
2030
2031
2032 //
2033 // <ele></ele>
2034 // <ele>foo<b>bar</b></ele>
2035 //
ParseDeep(char * p,StrPair * parentEndTag,int * curLineNumPtr)2036 char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
2037 {
2038 // Read the element name.
2039 p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
2040
2041 // The closing element is the </element> form. It is
2042 // parsed just like a regular element then deleted from
2043 // the DOM.
2044 if ( *p == '/' ) {
2045 _closingType = CLOSING;
2046 ++p;
2047 }
2048
2049 p = _value.ParseName( p );
2050 if ( _value.Empty() ) {
2051 return 0;
2052 }
2053
2054 p = ParseAttributes( p, curLineNumPtr );
2055 if ( !p || !*p || _closingType != OPEN ) {
2056 return p;
2057 }
2058
2059 if ( XMLUtil::StringEqual( _value.GetStr(), "AppendedData", 12 )
2060 && _rootAttribute
2061 && XMLUtil::StringEqual( _rootAttribute->Name(), "encoding", 8 )
2062 && XMLUtil::StringEqual( _rootAttribute->Value(), "raw", 3 ) ) {
2063 char *appendedDataStart = p;
2064 while ( p < _document->_charBuffer + _document->_len )
2065 {
2066 if ( *p == '<' && XMLUtil::StringEqual( p, "</AppendedData>", 15 ) ) {
2067 _appendedDataSize = p - appendedDataStart;
2068 _appendedData = new char[_appendedDataSize];
2069 memcpy(_appendedData, appendedDataStart, _appendedDataSize);
2070 break;
2071 }
2072 ++p;
2073 }
2074 if (_appendedData == NULL)
2075 {
2076 _document->SetError(XML_ERROR_PARSING_APPENDED_DATA, _parseLineNum, 0);
2077 }
2078 }
2079
2080 p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
2081 return p;
2082 }
2083
2084
2085
ShallowClone(XMLDocument * doc) const2086 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
2087 {
2088 if ( !doc ) {
2089 doc = _document;
2090 }
2091 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
2092 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
2093 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
2094 }
2095 return element;
2096 }
2097
2098
ShallowEqual(const XMLNode * compare) const2099 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
2100 {
2101 TIXMLASSERT( compare );
2102 const XMLElement* other = compare->ToElement();
2103 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
2104
2105 const XMLAttribute* a=FirstAttribute();
2106 const XMLAttribute* b=other->FirstAttribute();
2107
2108 while ( a && b ) {
2109 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
2110 return false;
2111 }
2112 a = a->Next();
2113 b = b->Next();
2114 }
2115 if ( a || b ) {
2116 // different count
2117 return false;
2118 }
2119 return true;
2120 }
2121 return false;
2122 }
2123
2124
Accept(XMLVisitor * visitor) const2125 bool XMLElement::Accept( XMLVisitor* visitor ) const
2126 {
2127 TIXMLASSERT( visitor );
2128 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
2129 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
2130 if ( !node->Accept( visitor ) ) {
2131 break;
2132 }
2133 }
2134 }
2135 return visitor->VisitExit( *this );
2136 }
2137
2138
2139 // --------- XMLDocument ----------- //
2140
2141 // Warning: List must match 'enum XMLError'
2142 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
2143 "XML_SUCCESS",
2144 "XML_NO_ATTRIBUTE",
2145 "XML_WRONG_ATTRIBUTE_TYPE",
2146 "XML_ERROR_FILE_NOT_FOUND",
2147 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
2148 "XML_ERROR_FILE_READ_ERROR",
2149 "XML_ERROR_PARSING_ELEMENT",
2150 "XML_ERROR_PARSING_ATTRIBUTE",
2151 "XML_ERROR_PARSING_TEXT",
2152 "XML_ERROR_PARSING_CDATA",
2153 "XML_ERROR_PARSING_COMMENT",
2154 "XML_ERROR_PARSING_DECLARATION",
2155 "XML_ERROR_PARSING_UNKNOWN",
2156 "XML_ERROR_EMPTY_DOCUMENT",
2157 "XML_ERROR_MISMATCHED_ELEMENT",
2158 "XML_ERROR_PARSING",
2159 "XML_CAN_NOT_CONVERT_TEXT",
2160 "XML_NO_TEXT_NODE",
2161 "XML_ELEMENT_DEPTH_EXCEEDED"
2162 };
2163
2164
XMLDocument(bool processEntities,Whitespace whitespaceMode)2165 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
2166 XMLNode( 0 ),
2167 _writeBOM( false ),
2168 _processEntities( processEntities ),
2169 _errorID(XML_SUCCESS),
2170 _whitespaceMode( whitespaceMode ),
2171 _errorStr(),
2172 _errorLineNum( 0 ),
2173 _charBuffer( 0 ),
2174 _len( 0 ),
2175 _parseCurLineNum( 0 ),
2176 _parsingDepth(0),
2177 _unlinked(),
2178 _elementPool(),
2179 _attributePool(),
2180 _textPool(),
2181 _commentPool()
2182 {
2183 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
2184 _document = this;
2185 }
2186
2187
~XMLDocument()2188 XMLDocument::~XMLDocument()
2189 {
2190 Clear();
2191 }
2192
2193
MarkInUse(const XMLNode * const node)2194 void XMLDocument::MarkInUse(const XMLNode* const node)
2195 {
2196 TIXMLASSERT(node);
2197 TIXMLASSERT(node->_parent == 0);
2198
2199 for (int i = 0; i < _unlinked.Size(); ++i) {
2200 if (node == _unlinked[i]) {
2201 _unlinked.SwapRemove(i);
2202 break;
2203 }
2204 }
2205 }
2206
Clear()2207 void XMLDocument::Clear()
2208 {
2209 DeleteChildren();
2210 while( _unlinked.Size()) {
2211 DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
2212 }
2213
2214 #ifdef TINYXML2_DEBUG
2215 const bool hadError = Error();
2216 #endif
2217 ClearError();
2218
2219 delete [] _charBuffer;
2220 _charBuffer = 0;
2221 _parsingDepth = 0;
2222
2223 #if 0
2224 _textPool.Trace( "text" );
2225 _elementPool.Trace( "element" );
2226 _commentPool.Trace( "comment" );
2227 _attributePool.Trace( "attribute" );
2228 #endif
2229
2230 #ifdef TINYXML2_DEBUG
2231 if ( !hadError ) {
2232 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
2233 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
2234 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
2235 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
2236 }
2237 #endif
2238 }
2239
2240
DeepCopy(XMLDocument * target) const2241 void XMLDocument::DeepCopy(XMLDocument* target) const
2242 {
2243 TIXMLASSERT(target);
2244 if (target == this) {
2245 return; // technically success - a no-op.
2246 }
2247
2248 target->Clear();
2249 for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
2250 target->InsertEndChild(node->DeepClone(target));
2251 }
2252 }
2253
NewElement(const char * name)2254 XMLElement* XMLDocument::NewElement( const char* name )
2255 {
2256 XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
2257 ele->SetName( name );
2258 return ele;
2259 }
2260
2261
NewComment(const char * str)2262 XMLComment* XMLDocument::NewComment( const char* str )
2263 {
2264 XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
2265 comment->SetValue( str );
2266 return comment;
2267 }
2268
2269
NewText(const char * str)2270 XMLText* XMLDocument::NewText( const char* str )
2271 {
2272 XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
2273 text->SetValue( str );
2274 return text;
2275 }
2276
2277
NewDeclaration(const char * str)2278 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
2279 {
2280 XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
2281 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
2282 return dec;
2283 }
2284
2285
NewUnknown(const char * str)2286 XMLUnknown* XMLDocument::NewUnknown( const char* str )
2287 {
2288 XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
2289 unk->SetValue( str );
2290 return unk;
2291 }
2292
callfopen(const char * filepath,const char * mode)2293 static FILE* callfopen( const char* filepath, const char* mode )
2294 {
2295 TIXMLASSERT( filepath );
2296 TIXMLASSERT( mode );
2297 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
2298 FILE* fp = 0;
2299 const errno_t err = fopen_s( &fp, filepath, mode );
2300 if ( err ) {
2301 return 0;
2302 }
2303 #else
2304 FILE* fp = fopen( filepath, mode );
2305 #endif
2306 return fp;
2307 }
2308
DeleteNode(XMLNode * node)2309 void XMLDocument::DeleteNode( XMLNode* node ) {
2310 TIXMLASSERT( node );
2311 TIXMLASSERT(node->_document == this );
2312 if (node->_parent) {
2313 node->_parent->DeleteChild( node );
2314 }
2315 else {
2316 // Isn't in the tree.
2317 // Use the parent delete.
2318 // Also, we need to mark it tracked: we 'know'
2319 // it was never used.
2320 node->_memPool->SetTracked();
2321 // Call the static XMLNode version:
2322 XMLNode::DeleteNode(node);
2323 }
2324 }
2325
2326
LoadFile(const char * filename)2327 XMLError XMLDocument::LoadFile( const char* filename )
2328 {
2329 if ( !filename ) {
2330 TIXMLASSERT( false );
2331 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2332 return _errorID;
2333 }
2334
2335 Clear();
2336 FILE* fp = callfopen( filename, "rb" );
2337 if ( !fp ) {
2338 SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
2339 return _errorID;
2340 }
2341 LoadFile( fp );
2342 fclose( fp );
2343 return _errorID;
2344 }
2345
LoadFile(FILE * fp)2346 XMLError XMLDocument::LoadFile( FILE* fp )
2347 {
2348 Clear();
2349
2350 TIXML_FSEEK( fp, 0, SEEK_SET );
2351 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
2352 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2353 return _errorID;
2354 }
2355
2356 TIXML_FSEEK( fp, 0, SEEK_END );
2357
2358 unsigned long long filelength;
2359 {
2360 const long long fileLengthSigned = TIXML_FTELL( fp );
2361 TIXML_FSEEK( fp, 0, SEEK_SET );
2362 if ( fileLengthSigned == -1L ) {
2363 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2364 return _errorID;
2365 }
2366 TIXMLASSERT( fileLengthSigned >= 0 );
2367 filelength = static_cast<unsigned long long>(fileLengthSigned);
2368 }
2369
2370 const size_t maxSizeT = static_cast<size_t>(-1);
2371 // We'll do the comparison as an unsigned long long, because that's guaranteed to be at
2372 // least 8 bytes, even on a 32-bit platform.
2373 if ( filelength >= static_cast<unsigned long long>(maxSizeT) ) {
2374 // Cannot handle files which won't fit in buffer together with null terminator
2375 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2376 return _errorID;
2377 }
2378
2379 if ( filelength == 0 ) {
2380 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2381 return _errorID;
2382 }
2383
2384 const size_t size = static_cast<size_t>(filelength);
2385 TIXMLASSERT( _charBuffer == 0 );
2386 _charBuffer = new char[size+1];
2387 const size_t read = fread( _charBuffer, 1, size, fp );
2388 if ( read != size ) {
2389 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
2390 return _errorID;
2391 }
2392
2393 _charBuffer[size] = 0;
2394 _len = size;
2395
2396 Parse();
2397 return _errorID;
2398 }
2399
2400
SaveFile(const char * filename,bool compact)2401 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
2402 {
2403 if ( !filename ) {
2404 TIXMLASSERT( false );
2405 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
2406 return _errorID;
2407 }
2408
2409 FILE* fp = callfopen( filename, "w" );
2410 if ( !fp ) {
2411 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
2412 return _errorID;
2413 }
2414 SaveFile(fp, compact);
2415 fclose( fp );
2416 return _errorID;
2417 }
2418
2419
SaveFile(FILE * fp,bool compact)2420 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2421 {
2422 // Clear any error from the last save, otherwise it will get reported
2423 // for *this* call.
2424 ClearError();
2425 XMLPrinter stream( fp, compact );
2426 Print( &stream );
2427 return _errorID;
2428 }
2429
2430
Parse(const char * p,size_t len)2431 XMLError XMLDocument::Parse( const char* p, size_t len )
2432 {
2433 Clear();
2434
2435 if ( len == 0 || !p || !*p ) {
2436 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2437 return _errorID;
2438 }
2439 if ( len == static_cast<size_t>(-1) ) {
2440 len = strlen( p );
2441 }
2442 TIXMLASSERT( _charBuffer == 0 );
2443 _charBuffer = new char[ len+1 ];
2444 memcpy( _charBuffer, p, len );
2445 _charBuffer[len] = 0;
2446 _len = len;
2447
2448 Parse();
2449 if ( Error() ) {
2450 // clean up now essentially dangling memory.
2451 // and the parse fail can put objects in the
2452 // pools that are dead and inaccessible.
2453 DeleteChildren();
2454 _elementPool.Clear();
2455 _attributePool.Clear();
2456 _textPool.Clear();
2457 _commentPool.Clear();
2458 }
2459 return _errorID;
2460 }
2461
2462
Print(XMLPrinter * streamer) const2463 void XMLDocument::Print( XMLPrinter* streamer ) const
2464 {
2465 if ( streamer ) {
2466 Accept( streamer );
2467 }
2468 else {
2469 XMLPrinter stdoutStreamer( stdout );
2470 Accept( &stdoutStreamer );
2471 }
2472 }
2473
2474
SetError(XMLError error,int lineNum,const char * format,...)2475 void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
2476 {
2477 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2478 _errorID = error;
2479 _errorLineNum = lineNum;
2480 _errorStr.Reset();
2481
2482 const size_t BUFFER_SIZE = 1000;
2483 char* buffer = new char[BUFFER_SIZE];
2484
2485 TIXMLASSERT(sizeof(error) <= sizeof(int));
2486 TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
2487
2488 if (format) {
2489 size_t len = strlen(buffer);
2490 TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
2491 len = strlen(buffer);
2492
2493 va_list va;
2494 va_start(va, format);
2495 TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
2496 va_end(va);
2497 }
2498 _errorStr.SetStr(buffer);
2499 delete[] buffer;
2500 }
2501
2502
ErrorIDToName(XMLError errorID)2503 /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
2504 {
2505 TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
2506 const char* errorName = _errorNames[errorID];
2507 TIXMLASSERT( errorName && errorName[0] );
2508 return errorName;
2509 }
2510
ErrorStr() const2511 const char* XMLDocument::ErrorStr() const
2512 {
2513 return _errorStr.Empty() ? "" : _errorStr.GetStr();
2514 }
2515
2516
PrintError() const2517 void XMLDocument::PrintError() const
2518 {
2519 printf("%s\n", ErrorStr());
2520 }
2521
ErrorName() const2522 const char* XMLDocument::ErrorName() const
2523 {
2524 return ErrorIDToName(_errorID);
2525 }
2526
Parse()2527 void XMLDocument::Parse()
2528 {
2529 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2530 TIXMLASSERT( _charBuffer );
2531 _parseCurLineNum = 1;
2532 _parseLineNum = 1;
2533 char* p = _charBuffer;
2534 p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
2535 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2536 if ( !*p ) {
2537 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2538 return;
2539 }
2540 ParseDeep(p, 0, &_parseCurLineNum );
2541 }
2542
PushDepth()2543 void XMLDocument::PushDepth()
2544 {
2545 _parsingDepth++;
2546 if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
2547 SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
2548 }
2549 }
2550
PopDepth()2551 void XMLDocument::PopDepth()
2552 {
2553 TIXMLASSERT(_parsingDepth > 0);
2554 --_parsingDepth;
2555 }
2556
XMLPrinter(FILE * file,bool compact,int depth)2557 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2558 _elementJustOpened( false ),
2559 _stack(),
2560 _firstElement( true ),
2561 _fp( file ),
2562 _depth( depth ),
2563 _textDepth( -1 ),
2564 _processEntities( true ),
2565 _compactMode( compact ),
2566 _buffer()
2567 {
2568 for( int i=0; i<ENTITY_RANGE; ++i ) {
2569 _entityFlag[i] = false;
2570 _restrictedEntityFlag[i] = false;
2571 }
2572 for( int i=0; i<NUM_ENTITIES; ++i ) {
2573 const char entityValue = entities[i].value;
2574 const unsigned char flagIndex = static_cast<unsigned char>(entityValue);
2575 TIXMLASSERT( flagIndex < ENTITY_RANGE );
2576 _entityFlag[flagIndex] = true;
2577 }
2578 _restrictedEntityFlag[static_cast<unsigned char>('&')] = true;
2579 _restrictedEntityFlag[static_cast<unsigned char>('<')] = true;
2580 _restrictedEntityFlag[static_cast<unsigned char>('>')] = true; // not required, but consistency is nice
2581 _buffer.Push( 0 );
2582 }
2583
2584
Print(const char * format,...)2585 void XMLPrinter::Print( const char* format, ... )
2586 {
2587 va_list va;
2588 va_start( va, format );
2589
2590 if ( _fp ) {
2591 vfprintf( _fp, format, va );
2592 }
2593 else {
2594 const int len = TIXML_VSCPRINTF( format, va );
2595 // Close out and re-start the va-args
2596 va_end( va );
2597 TIXMLASSERT( len >= 0 );
2598 va_start( va, format );
2599 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2600 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2601 TIXML_VSNPRINTF( p, len+1, format, va );
2602 }
2603 va_end( va );
2604 }
2605
2606
Write(const char * data,size_t size)2607 void XMLPrinter::Write( const char* data, size_t size )
2608 {
2609 if ( _fp ) {
2610 fwrite ( data , sizeof(char), size, _fp);
2611 }
2612 else {
2613 char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
2614 memcpy( p, data, size );
2615 p[size] = 0;
2616 }
2617 }
2618
2619
Putc(char ch)2620 void XMLPrinter::Putc( char ch )
2621 {
2622 if ( _fp ) {
2623 fputc ( ch, _fp);
2624 }
2625 else {
2626 char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
2627 p[0] = ch;
2628 p[1] = 0;
2629 }
2630 }
2631
2632
PrintSpace(int depth)2633 void XMLPrinter::PrintSpace( int depth )
2634 {
2635 for( int i=0; i<depth; ++i ) {
2636 Write( " " );
2637 }
2638 }
2639
2640
PrintString(const char * p,bool restricted)2641 void XMLPrinter::PrintString( const char* p, bool restricted )
2642 {
2643 // Look for runs of bytes between entities to print.
2644 const char* q = p;
2645
2646 if ( _processEntities ) {
2647 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2648 while ( *q ) {
2649 TIXMLASSERT( p <= q );
2650 // Remember, char is sometimes signed. (How many times has that bitten me?)
2651 if ( *q > 0 && *q < ENTITY_RANGE ) {
2652 // Check for entities. If one is found, flush
2653 // the stream up until the entity, write the
2654 // entity, and keep looking.
2655 if ( flag[static_cast<unsigned char>(*q)] ) {
2656 while ( p < q ) {
2657 const size_t delta = q - p;
2658 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2659 Write( p, toPrint );
2660 p += toPrint;
2661 }
2662 bool entityPatternPrinted = false;
2663 for( int i=0; i<NUM_ENTITIES; ++i ) {
2664 if ( entities[i].value == *q ) {
2665 Putc( '&' );
2666 Write( entities[i].pattern, entities[i].length );
2667 Putc( ';' );
2668 entityPatternPrinted = true;
2669 break;
2670 }
2671 }
2672 if ( !entityPatternPrinted ) {
2673 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2674 TIXMLASSERT( false );
2675 }
2676 ++p;
2677 }
2678 }
2679 ++q;
2680 TIXMLASSERT( p <= q );
2681 }
2682 // Flush the remaining string. This will be the entire
2683 // string if an entity wasn't found.
2684 if ( p < q ) {
2685 const size_t delta = q - p;
2686 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast<int>(delta);
2687 Write( p, toPrint );
2688 }
2689 }
2690 else {
2691 Write( p );
2692 }
2693 }
2694
2695
PushHeader(bool writeBOM,bool writeDec)2696 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2697 {
2698 if ( writeBOM ) {
2699 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2700 Write( reinterpret_cast< const char* >( bom ) );
2701 }
2702 if ( writeDec ) {
2703 PushDeclaration( "xml version=\"1.0\"" );
2704 }
2705 }
2706
PrepareForNewNode(bool compactMode)2707 void XMLPrinter::PrepareForNewNode( bool compactMode )
2708 {
2709 SealElementIfJustOpened();
2710
2711 if ( compactMode ) {
2712 return;
2713 }
2714
2715 if ( _firstElement ) {
2716 PrintSpace (_depth);
2717 } else if ( _textDepth < 0) {
2718 Putc( '\n' );
2719 PrintSpace( _depth );
2720 }
2721
2722 _firstElement = false;
2723 }
2724
OpenElement(const char * name,bool compactMode)2725 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2726 {
2727 PrepareForNewNode( compactMode );
2728 _stack.Push( name );
2729
2730 Write ( "<" );
2731 Write ( name );
2732
2733 _elementJustOpened = true;
2734 ++_depth;
2735 }
2736
2737
PushAttribute(const char * name,const char * value)2738 void XMLPrinter::PushAttribute( const char* name, const char* value )
2739 {
2740 TIXMLASSERT( _elementJustOpened );
2741 Putc ( ' ' );
2742 Write( name );
2743 Write( "=\"" );
2744 PrintString( value, false );
2745 Putc ( '\"' );
2746 }
2747
2748
PushAttribute(const char * name,int v)2749 void XMLPrinter::PushAttribute( const char* name, int v )
2750 {
2751 char buf[BUF_SIZE];
2752 XMLUtil::ToStr( v, buf, BUF_SIZE );
2753 PushAttribute( name, buf );
2754 }
2755
2756
PushAttribute(const char * name,unsigned v)2757 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2758 {
2759 char buf[BUF_SIZE];
2760 XMLUtil::ToStr( v, buf, BUF_SIZE );
2761 PushAttribute( name, buf );
2762 }
2763
2764
PushAttribute(const char * name,int64_t v)2765 void XMLPrinter::PushAttribute(const char* name, int64_t v)
2766 {
2767 char buf[BUF_SIZE];
2768 XMLUtil::ToStr(v, buf, BUF_SIZE);
2769 PushAttribute(name, buf);
2770 }
2771
2772
PushAttribute(const char * name,uint64_t v)2773 void XMLPrinter::PushAttribute(const char* name, uint64_t v)
2774 {
2775 char buf[BUF_SIZE];
2776 XMLUtil::ToStr(v, buf, BUF_SIZE);
2777 PushAttribute(name, buf);
2778 }
2779
2780
PushAttribute(const char * name,bool v)2781 void XMLPrinter::PushAttribute( const char* name, bool v )
2782 {
2783 char buf[BUF_SIZE];
2784 XMLUtil::ToStr( v, buf, BUF_SIZE );
2785 PushAttribute( name, buf );
2786 }
2787
2788
PushAttribute(const char * name,double v)2789 void XMLPrinter::PushAttribute( const char* name, double v )
2790 {
2791 char buf[BUF_SIZE];
2792 XMLUtil::ToStr( v, buf, BUF_SIZE );
2793 PushAttribute( name, buf );
2794 }
2795
2796
CloseElement(bool compactMode)2797 void XMLPrinter::CloseElement( bool compactMode )
2798 {
2799 --_depth;
2800 const char* name = _stack.Pop();
2801
2802 if ( _elementJustOpened ) {
2803 Write( "/>" );
2804 }
2805 else {
2806 if ( _textDepth < 0 && !compactMode) {
2807 Putc( '\n' );
2808 PrintSpace( _depth );
2809 }
2810 Write ( "</" );
2811 Write ( name );
2812 Write ( ">" );
2813 }
2814
2815 if ( _textDepth == _depth ) {
2816 _textDepth = -1;
2817 }
2818 if ( _depth == 0 && !compactMode) {
2819 Putc( '\n' );
2820 }
2821 _elementJustOpened = false;
2822 }
2823
2824
SealElementIfJustOpened()2825 void XMLPrinter::SealElementIfJustOpened()
2826 {
2827 if ( !_elementJustOpened ) {
2828 return;
2829 }
2830 _elementJustOpened = false;
2831 Putc( '>' );
2832 }
2833
2834
PushText(const char * text,bool cdata)2835 void XMLPrinter::PushText( const char* text, bool cdata )
2836 {
2837 _textDepth = _depth-1;
2838
2839 SealElementIfJustOpened();
2840 if ( cdata ) {
2841 Write( "<![CDATA[" );
2842 Write( text );
2843 Write( "]]>" );
2844 }
2845 else {
2846 PrintString( text, true );
2847 }
2848 }
2849
2850
PushText(int64_t value)2851 void XMLPrinter::PushText( int64_t value )
2852 {
2853 char buf[BUF_SIZE];
2854 XMLUtil::ToStr( value, buf, BUF_SIZE );
2855 PushText( buf, false );
2856 }
2857
2858
PushText(uint64_t value)2859 void XMLPrinter::PushText( uint64_t value )
2860 {
2861 char buf[BUF_SIZE];
2862 XMLUtil::ToStr(value, buf, BUF_SIZE);
2863 PushText(buf, false);
2864 }
2865
2866
PushText(int value)2867 void XMLPrinter::PushText( int value )
2868 {
2869 char buf[BUF_SIZE];
2870 XMLUtil::ToStr( value, buf, BUF_SIZE );
2871 PushText( buf, false );
2872 }
2873
2874
PushText(unsigned value)2875 void XMLPrinter::PushText( unsigned value )
2876 {
2877 char buf[BUF_SIZE];
2878 XMLUtil::ToStr( value, buf, BUF_SIZE );
2879 PushText( buf, false );
2880 }
2881
2882
PushText(bool value)2883 void XMLPrinter::PushText( bool value )
2884 {
2885 char buf[BUF_SIZE];
2886 XMLUtil::ToStr( value, buf, BUF_SIZE );
2887 PushText( buf, false );
2888 }
2889
2890
PushText(float value)2891 void XMLPrinter::PushText( float value )
2892 {
2893 char buf[BUF_SIZE];
2894 XMLUtil::ToStr( value, buf, BUF_SIZE );
2895 PushText( buf, false );
2896 }
2897
2898
PushText(double value)2899 void XMLPrinter::PushText( double value )
2900 {
2901 char buf[BUF_SIZE];
2902 XMLUtil::ToStr( value, buf, BUF_SIZE );
2903 PushText( buf, false );
2904 }
2905
2906
PushComment(const char * comment)2907 void XMLPrinter::PushComment( const char* comment )
2908 {
2909 PrepareForNewNode( _compactMode );
2910
2911 Write( "<!--" );
2912 Write( comment );
2913 Write( "-->" );
2914 }
2915
2916
PushDeclaration(const char * value)2917 void XMLPrinter::PushDeclaration( const char* value )
2918 {
2919 PrepareForNewNode( _compactMode );
2920
2921 Write( "<?" );
2922 Write( value );
2923 Write( "?>" );
2924 }
2925
2926
PushUnknown(const char * value)2927 void XMLPrinter::PushUnknown( const char* value )
2928 {
2929 PrepareForNewNode( _compactMode );
2930
2931 Write( "<!" );
2932 Write( value );
2933 Putc( '>' );
2934 }
2935
2936
VisitEnter(const XMLDocument & doc)2937 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2938 {
2939 _processEntities = doc.ProcessEntities();
2940 if ( doc.HasBOM() ) {
2941 PushHeader( true, false );
2942 }
2943 return true;
2944 }
2945
2946
VisitEnter(const XMLElement & element,const XMLAttribute * attribute)2947 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2948 {
2949 const XMLElement* parentElem = 0;
2950 if ( element.Parent() ) {
2951 parentElem = element.Parent()->ToElement();
2952 }
2953 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2954 OpenElement( element.Name(), compactMode );
2955 while ( attribute ) {
2956 PushAttribute( attribute->Name(), attribute->Value() );
2957 attribute = attribute->Next();
2958 }
2959 return true;
2960 }
2961
2962
VisitExit(const XMLElement & element)2963 bool XMLPrinter::VisitExit( const XMLElement& element )
2964 {
2965 CloseElement( CompactMode(element) );
2966 return true;
2967 }
2968
2969
Visit(const XMLText & text)2970 bool XMLPrinter::Visit( const XMLText& text )
2971 {
2972 PushText( text.Value(), text.CData() );
2973 return true;
2974 }
2975
2976
Visit(const XMLComment & comment)2977 bool XMLPrinter::Visit( const XMLComment& comment )
2978 {
2979 PushComment( comment.Value() );
2980 return true;
2981 }
2982
Visit(const XMLDeclaration & declaration)2983 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2984 {
2985 PushDeclaration( declaration.Value() );
2986 return true;
2987 }
2988
2989
Visit(const XMLUnknown & unknown)2990 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2991 {
2992 PushUnknown( unknown.Value() );
2993 return true;
2994 }
2995
2996 } // namespace tinyxml2
2997