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 );*/ 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 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); 49 va_end( va ); 50 return result; 51 } 52 53 static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) 54 { 55 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. 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 94 static inline int TIXML_VSCPRINTF( const char* format, va_list va ) 95 { 96 int len = vsnprintf( 0, 0, format, va ); 97 TIXMLASSERT( len >= 0 ); 98 return len; 99 } 100 #define TIXML_SSCANF sscanf 101 #endif 102 103 104 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF 105 static const char LF = LINE_FEED; 106 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out 107 static const char CR = CARRIAGE_RETURN; 108 static const char SINGLE_QUOTE = '\''; 109 static const char DOUBLE_QUOTE = '\"'; 110 111 // Bunch of unicode info at: 112 // http://www.unicode.org/faq/utf_bom.html 113 // ef bb bf (Microsoft "lead bytes") - designates UTF-8 114 115 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 116 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 117 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 118 119 namespace tinyxml2 120 { 121 122 struct Entity { 123 const char* pattern; 124 int length; 125 char value; 126 }; 127 128 static const int NUM_ENTITIES = 5; 129 static const Entity entities[NUM_ENTITIES] = { 130 { "quot", 4, DOUBLE_QUOTE }, 131 { "amp", 3, '&' }, 132 { "apos", 4, SINGLE_QUOTE }, 133 { "lt", 2, '<' }, 134 { "gt", 2, '>' } 135 }; 136 137 138 StrPair::~StrPair() 139 { 140 Reset(); 141 } 142 143 144 void StrPair::TransferTo( StrPair* other ) 145 { 146 if ( this == other ) { 147 return; 148 } 149 // This in effect implements the assignment operator by "moving" 150 // ownership (as in auto_ptr). 151 152 TIXMLASSERT( other->_flags == 0 ); 153 TIXMLASSERT( other->_start == 0 ); 154 TIXMLASSERT( other->_end == 0 ); 155 156 other->Reset(); 157 158 other->_flags = _flags; 159 other->_start = _start; 160 other->_end = _end; 161 162 _flags = 0; 163 _start = 0; 164 _end = 0; 165 } 166 167 void StrPair::Reset() 168 { 169 if ( _flags & NEEDS_DELETE ) { 170 delete [] _start; 171 } 172 _flags = 0; 173 _start = 0; 174 _end = 0; 175 } 176 177 178 void StrPair::SetStr( const char* str, int flags ) 179 { 180 TIXMLASSERT( str ); 181 Reset(); 182 size_t len = strlen( str ); 183 TIXMLASSERT( _start == 0 ); 184 _start = new char[ len+1 ]; 185 memcpy( _start, str, len+1 ); 186 _end = _start + len; 187 _flags = flags | NEEDS_DELETE; 188 } 189 190 191 char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) 192 { 193 TIXMLASSERT( endTag && *endTag ); 194 195 char* start = p; 196 char endChar = *endTag; 197 size_t length = strlen( endTag ); 198 199 // Inner loop of text parsing. 200 while ( *p ) { 201 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { 202 Set( start, p, strFlags ); 203 return p + length; 204 } 205 ++p; 206 } 207 return 0; 208 } 209 210 211 char* StrPair::ParseName( char* p ) 212 { 213 if ( !p || !(*p) ) { 214 return 0; 215 } 216 if ( !XMLUtil::IsNameStartChar( *p ) ) { 217 return 0; 218 } 219 220 char* const start = p; 221 ++p; 222 while ( *p && XMLUtil::IsNameChar( *p ) ) { 223 ++p; 224 } 225 226 Set( start, p, 0 ); 227 return p; 228 } 229 230 231 void StrPair::CollapseWhitespace() 232 { 233 // Adjusting _start would cause undefined behavior on delete[] 234 TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); 235 // Trim leading space. 236 _start = XMLUtil::SkipWhiteSpace( _start ); 237 238 if ( *_start ) { 239 char* p = _start; // the read pointer 240 char* q = _start; // the write pointer 241 242 while( *p ) { 243 if ( XMLUtil::IsWhiteSpace( *p )) { 244 p = XMLUtil::SkipWhiteSpace( p ); 245 if ( *p == 0 ) { 246 break; // don't write to q; this trims the trailing space. 247 } 248 *q = ' '; 249 ++q; 250 } 251 *q = *p; 252 ++q; 253 ++p; 254 } 255 *q = 0; 256 } 257 } 258 259 260 const char* StrPair::GetStr() 261 { 262 TIXMLASSERT( _start ); 263 TIXMLASSERT( _end ); 264 if ( _flags & NEEDS_FLUSH ) { 265 *_end = 0; 266 _flags ^= NEEDS_FLUSH; 267 268 if ( _flags ) { 269 char* p = _start; // the read pointer 270 char* q = _start; // the write pointer 271 272 while( p < _end ) { 273 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { 274 // CR-LF pair becomes LF 275 // CR alone becomes LF 276 // LF-CR becomes LF 277 if ( *(p+1) == LF ) { 278 p += 2; 279 } 280 else { 281 ++p; 282 } 283 *q++ = LF; 284 } 285 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { 286 if ( *(p+1) == CR ) { 287 p += 2; 288 } 289 else { 290 ++p; 291 } 292 *q++ = LF; 293 } 294 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { 295 // Entities handled by tinyXML2: 296 // - special entities in the entity table [in/out] 297 // - numeric character reference [in] 298 // 中 or 中 299 300 if ( *(p+1) == '#' ) { 301 const int buflen = 10; 302 char buf[buflen] = { 0 }; 303 int len = 0; 304 char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) ); 305 if ( adjusted == 0 ) { 306 *q = *p; 307 ++p; 308 ++q; 309 } 310 else { 311 TIXMLASSERT( 0 <= len && len <= buflen ); 312 TIXMLASSERT( q + len <= adjusted ); 313 p = adjusted; 314 memcpy( q, buf, len ); 315 q += len; 316 } 317 } 318 else { 319 bool entityFound = false; 320 for( int i = 0; i < NUM_ENTITIES; ++i ) { 321 const Entity& entity = entities[i]; 322 if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 323 && *( p + entity.length + 1 ) == ';' ) { 324 // Found an entity - convert. 325 *q = entity.value; 326 ++q; 327 p += entity.length + 2; 328 entityFound = true; 329 break; 330 } 331 } 332 if ( !entityFound ) { 333 // fixme: treat as error? 334 ++p; 335 ++q; 336 } 337 } 338 } 339 else { 340 *q = *p; 341 ++p; 342 ++q; 343 } 344 } 345 *q = 0; 346 } 347 // The loop below has plenty going on, and this 348 // is a less useful mode. Break it out. 349 if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { 350 CollapseWhitespace(); 351 } 352 _flags = (_flags & NEEDS_DELETE); 353 } 354 TIXMLASSERT( _start ); 355 return _start; 356 } 357 358 359 360 361 // --------- XMLUtil ----------- // 362 363 const char* XMLUtil::ReadBOM( const char* p, bool* bom ) 364 { 365 TIXMLASSERT( p ); 366 TIXMLASSERT( bom ); 367 *bom = false; 368 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p); 369 // Check for BOM: 370 if ( *(pu+0) == TIXML_UTF_LEAD_0 371 && *(pu+1) == TIXML_UTF_LEAD_1 372 && *(pu+2) == TIXML_UTF_LEAD_2 ) { 373 *bom = true; 374 p += 3; 375 } 376 TIXMLASSERT( p ); 377 return p; 378 } 379 380 381 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) 382 { 383 const unsigned long BYTE_MASK = 0xBF; 384 const unsigned long BYTE_MARK = 0x80; 385 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 386 387 if (input < 0x80) { 388 *length = 1; 389 } 390 else if ( input < 0x800 ) { 391 *length = 2; 392 } 393 else if ( input < 0x10000 ) { 394 *length = 3; 395 } 396 else if ( input < 0x200000 ) { 397 *length = 4; 398 } 399 else { 400 *length = 0; // This code won't convert this correctly anyway. 401 return; 402 } 403 404 output += *length; 405 406 // Scary scary fall throughs. 407 switch (*length) { 408 case 4: 409 --output; 410 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 411 input >>= 6; 412 case 3: 413 --output; 414 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 415 input >>= 6; 416 case 2: 417 --output; 418 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 419 input >>= 6; 420 case 1: 421 --output; 422 *output = (char)(input | FIRST_BYTE_MARK[*length]); 423 break; 424 default: 425 TIXMLASSERT( false ); 426 } 427 } 428 429 430 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) 431 { 432 // Presume an entity, and pull it out. 433 *length = 0; 434 435 if ( *(p+1) == '#' && *(p+2) ) { 436 unsigned long ucs = 0; 437 TIXMLASSERT( sizeof( ucs ) >= 4 ); 438 ptrdiff_t delta = 0; 439 unsigned mult = 1; 440 static const char SEMICOLON = ';'; 441 442 if ( *(p+2) == 'x' ) { 443 // Hexadecimal. 444 const char* q = p+3; 445 if ( !(*q) ) { 446 return 0; 447 } 448 449 q = strchr( q, SEMICOLON ); 450 451 if ( !q ) { 452 return 0; 453 } 454 TIXMLASSERT( *q == SEMICOLON ); 455 456 delta = q-p; 457 --q; 458 459 while ( *q != 'x' ) { 460 unsigned int digit = 0; 461 462 if ( *q >= '0' && *q <= '9' ) { 463 digit = *q - '0'; 464 } 465 else if ( *q >= 'a' && *q <= 'f' ) { 466 digit = *q - 'a' + 10; 467 } 468 else if ( *q >= 'A' && *q <= 'F' ) { 469 digit = *q - 'A' + 10; 470 } 471 else { 472 return 0; 473 } 474 TIXMLASSERT( digit < 16 ); 475 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); 476 const unsigned int digitScaled = mult * digit; 477 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); 478 ucs += digitScaled; 479 TIXMLASSERT( mult <= UINT_MAX / 16 ); 480 mult *= 16; 481 --q; 482 } 483 } 484 else { 485 // Decimal. 486 const char* q = p+2; 487 if ( !(*q) ) { 488 return 0; 489 } 490 491 q = strchr( q, SEMICOLON ); 492 493 if ( !q ) { 494 return 0; 495 } 496 TIXMLASSERT( *q == SEMICOLON ); 497 498 delta = q-p; 499 --q; 500 501 while ( *q != '#' ) { 502 if ( *q >= '0' && *q <= '9' ) { 503 const unsigned int digit = *q - '0'; 504 TIXMLASSERT( digit < 10 ); 505 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); 506 const unsigned int digitScaled = mult * digit; 507 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); 508 ucs += digitScaled; 509 } 510 else { 511 return 0; 512 } 513 TIXMLASSERT( mult <= UINT_MAX / 10 ); 514 mult *= 10; 515 --q; 516 } 517 } 518 // convert the UCS to UTF-8 519 ConvertUTF32ToUTF8( ucs, value, length ); 520 return p + delta + 1; 521 } 522 return p+1; 523 } 524 525 526 void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) 527 { 528 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); 529 } 530 531 532 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) 533 { 534 TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); 535 } 536 537 538 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) 539 { 540 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); 541 } 542 543 /* 544 ToStr() of a number is a very tricky topic. 545 https://github.com/leethomason/tinyxml2/issues/106 546 */ 547 void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) 548 { 549 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); 550 } 551 552 553 void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) 554 { 555 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); 556 } 557 558 559 bool XMLUtil::ToInt( const char* str, int* value ) 560 { 561 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { 562 return true; 563 } 564 return false; 565 } 566 567 bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) 568 { 569 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { 570 return true; 571 } 572 return false; 573 } 574 575 bool XMLUtil::ToBool( const char* str, bool* value ) 576 { 577 int ival = 0; 578 if ( ToInt( str, &ival )) { 579 *value = (ival==0) ? false : true; 580 return true; 581 } 582 if ( StringEqual( str, "true" ) ) { 583 *value = true; 584 return true; 585 } 586 else if ( StringEqual( str, "false" ) ) { 587 *value = false; 588 return true; 589 } 590 return false; 591 } 592 593 594 bool XMLUtil::ToFloat( const char* str, float* value ) 595 { 596 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { 597 return true; 598 } 599 return false; 600 } 601 602 bool XMLUtil::ToDouble( const char* str, double* value ) 603 { 604 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { 605 return true; 606 } 607 return false; 608 } 609 610 611 char* XMLDocument::Identify( char* p, XMLNode** node ) 612 { 613 TIXMLASSERT( node ); 614 TIXMLASSERT( p ); 615 char* const start = p; 616 p = XMLUtil::SkipWhiteSpace( p ); 617 if( !*p ) { 618 *node = 0; 619 TIXMLASSERT( p ); 620 return p; 621 } 622 623 // These strings define the matching patterns: 624 static const char* xmlHeader = { "<?" }; 625 static const char* commentHeader = { "<!--" }; 626 static const char* cdataHeader = { "<![CDATA[" }; 627 static const char* dtdHeader = { "<!" }; 628 static const char* elementHeader = { "<" }; // and a header for everything else; check last. 629 630 static const int xmlHeaderLen = 2; 631 static const int commentHeaderLen = 4; 632 static const int cdataHeaderLen = 9; 633 static const int dtdHeaderLen = 2; 634 static const int elementHeaderLen = 1; 635 636 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool 637 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool 638 XMLNode* returnNode = 0; 639 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) { 640 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); 641 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this ); 642 returnNode->_memPool = &_commentPool; 643 p += xmlHeaderLen; 644 } 645 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { 646 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); 647 returnNode = new (_commentPool.Alloc()) XMLComment( this ); 648 returnNode->_memPool = &_commentPool; 649 p += commentHeaderLen; 650 } 651 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { 652 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); 653 XMLText* text = new (_textPool.Alloc()) XMLText( this ); 654 returnNode = text; 655 returnNode->_memPool = &_textPool; 656 p += cdataHeaderLen; 657 text->SetCData( true ); 658 } 659 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { 660 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); 661 returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); 662 returnNode->_memPool = &_commentPool; 663 p += dtdHeaderLen; 664 } 665 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { 666 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); 667 returnNode = new (_elementPool.Alloc()) XMLElement( this ); 668 returnNode->_memPool = &_elementPool; 669 p += elementHeaderLen; 670 } 671 else { 672 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); 673 returnNode = new (_textPool.Alloc()) XMLText( this ); 674 returnNode->_memPool = &_textPool; 675 p = start; // Back it up, all the text counts. 676 } 677 678 TIXMLASSERT( returnNode ); 679 TIXMLASSERT( p ); 680 *node = returnNode; 681 return p; 682 } 683 684 685 bool XMLDocument::Accept( XMLVisitor* visitor ) const 686 { 687 TIXMLASSERT( visitor ); 688 if ( visitor->VisitEnter( *this ) ) { 689 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { 690 if ( !node->Accept( visitor ) ) { 691 break; 692 } 693 } 694 } 695 return visitor->VisitExit( *this ); 696 } 697 698 699 // --------- XMLNode ----------- // 700 701 XMLNode::XMLNode( XMLDocument* doc ) : 702 _document( doc ), 703 _parent( 0 ), 704 _firstChild( 0 ), _lastChild( 0 ), 705 _prev( 0 ), _next( 0 ), 706 _memPool( 0 ) 707 { 708 } 709 710 711 XMLNode::~XMLNode() 712 { 713 DeleteChildren(); 714 if ( _parent ) { 715 _parent->Unlink( this ); 716 } 717 } 718 719 const char* XMLNode::Value() const 720 { 721 // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr. 722 if ( this->ToDocument() ) 723 return 0; 724 return _value.GetStr(); 725 } 726 727 void XMLNode::SetValue( const char* str, bool staticMem ) 728 { 729 if ( staticMem ) { 730 _value.SetInternedStr( str ); 731 } 732 else { 733 _value.SetStr( str ); 734 } 735 } 736 737 738 void XMLNode::DeleteChildren() 739 { 740 while( _firstChild ) { 741 TIXMLASSERT( _lastChild ); 742 TIXMLASSERT( _firstChild->_document == _document ); 743 XMLNode* node = _firstChild; 744 Unlink( node ); 745 746 DeleteNode( node ); 747 } 748 _firstChild = _lastChild = 0; 749 } 750 751 752 void XMLNode::Unlink( XMLNode* child ) 753 { 754 TIXMLASSERT( child ); 755 TIXMLASSERT( child->_document == _document ); 756 TIXMLASSERT( child->_parent == this ); 757 if ( child == _firstChild ) { 758 _firstChild = _firstChild->_next; 759 } 760 if ( child == _lastChild ) { 761 _lastChild = _lastChild->_prev; 762 } 763 764 if ( child->_prev ) { 765 child->_prev->_next = child->_next; 766 } 767 if ( child->_next ) { 768 child->_next->_prev = child->_prev; 769 } 770 child->_parent = 0; 771 } 772 773 774 void XMLNode::DeleteChild( XMLNode* node ) 775 { 776 TIXMLASSERT( node ); 777 TIXMLASSERT( node->_document == _document ); 778 TIXMLASSERT( node->_parent == this ); 779 Unlink( node ); 780 DeleteNode( node ); 781 } 782 783 784 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) 785 { 786 TIXMLASSERT( addThis ); 787 if ( addThis->_document != _document ) { 788 TIXMLASSERT( false ); 789 return 0; 790 } 791 InsertChildPreamble( addThis ); 792 793 if ( _lastChild ) { 794 TIXMLASSERT( _firstChild ); 795 TIXMLASSERT( _lastChild->_next == 0 ); 796 _lastChild->_next = addThis; 797 addThis->_prev = _lastChild; 798 _lastChild = addThis; 799 800 addThis->_next = 0; 801 } 802 else { 803 TIXMLASSERT( _firstChild == 0 ); 804 _firstChild = _lastChild = addThis; 805 806 addThis->_prev = 0; 807 addThis->_next = 0; 808 } 809 addThis->_parent = this; 810 return addThis; 811 } 812 813 814 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) 815 { 816 TIXMLASSERT( addThis ); 817 if ( addThis->_document != _document ) { 818 TIXMLASSERT( false ); 819 return 0; 820 } 821 InsertChildPreamble( addThis ); 822 823 if ( _firstChild ) { 824 TIXMLASSERT( _lastChild ); 825 TIXMLASSERT( _firstChild->_prev == 0 ); 826 827 _firstChild->_prev = addThis; 828 addThis->_next = _firstChild; 829 _firstChild = addThis; 830 831 addThis->_prev = 0; 832 } 833 else { 834 TIXMLASSERT( _lastChild == 0 ); 835 _firstChild = _lastChild = addThis; 836 837 addThis->_prev = 0; 838 addThis->_next = 0; 839 } 840 addThis->_parent = this; 841 return addThis; 842 } 843 844 845 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) 846 { 847 TIXMLASSERT( addThis ); 848 if ( addThis->_document != _document ) { 849 TIXMLASSERT( false ); 850 return 0; 851 } 852 853 TIXMLASSERT( afterThis ); 854 855 if ( afterThis->_parent != this ) { 856 TIXMLASSERT( false ); 857 return 0; 858 } 859 860 if ( afterThis->_next == 0 ) { 861 // The last node or the only node. 862 return InsertEndChild( addThis ); 863 } 864 InsertChildPreamble( addThis ); 865 addThis->_prev = afterThis; 866 addThis->_next = afterThis->_next; 867 afterThis->_next->_prev = addThis; 868 afterThis->_next = addThis; 869 addThis->_parent = this; 870 return addThis; 871 } 872 873 874 875 876 const XMLElement* XMLNode::FirstChildElement( const char* name ) const 877 { 878 for( const XMLNode* node = _firstChild; node; node = node->_next ) { 879 const XMLElement* element = node->ToElement(); 880 if ( element ) { 881 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { 882 return element; 883 } 884 } 885 } 886 return 0; 887 } 888 889 890 const XMLElement* XMLNode::LastChildElement( const char* name ) const 891 { 892 for( const XMLNode* node = _lastChild; node; node = node->_prev ) { 893 const XMLElement* element = node->ToElement(); 894 if ( element ) { 895 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { 896 return element; 897 } 898 } 899 } 900 return 0; 901 } 902 903 904 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const 905 { 906 for( const XMLNode* node = _next; node; node = node->_next ) { 907 const XMLElement* element = node->ToElement(); 908 if ( element 909 && (!name || XMLUtil::StringEqual( name, element->Name() ))) { 910 return element; 911 } 912 } 913 return 0; 914 } 915 916 917 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const 918 { 919 for( const XMLNode* node = _prev; node; node = node->_prev ) { 920 const XMLElement* element = node->ToElement(); 921 if ( element 922 && (!name || XMLUtil::StringEqual( name, element->Name() ))) { 923 return element; 924 } 925 } 926 return 0; 927 } 928 929 930 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) 931 { 932 // This is a recursive method, but thinking about it "at the current level" 933 // it is a pretty simple flat list: 934 // <foo/> 935 // <!-- comment --> 936 // 937 // With a special case: 938 // <foo> 939 // </foo> 940 // <!-- comment --> 941 // 942 // Where the closing element (/foo) *must* be the next thing after the opening 943 // element, and the names must match. BUT the tricky bit is that the closing 944 // element will be read by the child. 945 // 946 // 'endTag' is the end tag for this node, it is returned by a call to a child. 947 // 'parentEnd' is the end tag for the parent, which is filled in and returned. 948 949 while( p && *p ) { 950 XMLNode* node = 0; 951 952 p = _document->Identify( p, &node ); 953 if ( node == 0 ) { 954 break; 955 } 956 957 StrPair endTag; 958 p = node->ParseDeep( p, &endTag ); 959 if ( !p ) { 960 DeleteNode( node ); 961 if ( !_document->Error() ) { 962 _document->SetError( XML_ERROR_PARSING, 0, 0 ); 963 } 964 break; 965 } 966 967 XMLDeclaration* decl = node->ToDeclaration(); 968 if ( decl ) { 969 // A declaration can only be the first child of a document. 970 // Set error, if document already has children. 971 if ( !_document->NoChildren() ) { 972 _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0); 973 DeleteNode( decl ); 974 break; 975 } 976 } 977 978 XMLElement* ele = node->ToElement(); 979 if ( ele ) { 980 // We read the end tag. Return it to the parent. 981 if ( ele->ClosingType() == XMLElement::CLOSING ) { 982 if ( parentEnd ) { 983 ele->_value.TransferTo( parentEnd ); 984 } 985 node->_memPool->SetTracked(); // created and then immediately deleted. 986 DeleteNode( node ); 987 return p; 988 } 989 990 // Handle an end tag returned to this level. 991 // And handle a bunch of annoying errors. 992 bool mismatch = false; 993 if ( endTag.Empty() ) { 994 if ( ele->ClosingType() == XMLElement::OPEN ) { 995 mismatch = true; 996 } 997 } 998 else { 999 if ( ele->ClosingType() != XMLElement::OPEN ) { 1000 mismatch = true; 1001 } 1002 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { 1003 mismatch = true; 1004 } 1005 } 1006 if ( mismatch ) { 1007 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 ); 1008 DeleteNode( node ); 1009 break; 1010 } 1011 } 1012 InsertEndChild( node ); 1013 } 1014 return 0; 1015 } 1016 1017 void XMLNode::DeleteNode( XMLNode* node ) 1018 { 1019 if ( node == 0 ) { 1020 return; 1021 } 1022 MemPool* pool = node->_memPool; 1023 node->~XMLNode(); 1024 pool->Free( node ); 1025 } 1026 1027 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const 1028 { 1029 TIXMLASSERT( insertThis ); 1030 TIXMLASSERT( insertThis->_document == _document ); 1031 1032 if ( insertThis->_parent ) 1033 insertThis->_parent->Unlink( insertThis ); 1034 else 1035 insertThis->_memPool->SetTracked(); 1036 } 1037 1038 // --------- XMLText ---------- // 1039 char* XMLText::ParseDeep( char* p, StrPair* ) 1040 { 1041 const char* start = p; 1042 if ( this->CData() ) { 1043 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 1044 if ( !p ) { 1045 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); 1046 } 1047 return p; 1048 } 1049 else { 1050 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; 1051 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { 1052 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; 1053 } 1054 1055 p = _value.ParseText( p, "<", flags ); 1056 if ( p && *p ) { 1057 return p-1; 1058 } 1059 if ( !p ) { 1060 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); 1061 } 1062 } 1063 return 0; 1064 } 1065 1066 1067 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const 1068 { 1069 if ( !doc ) { 1070 doc = _document; 1071 } 1072 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? 1073 text->SetCData( this->CData() ); 1074 return text; 1075 } 1076 1077 1078 bool XMLText::ShallowEqual( const XMLNode* compare ) const 1079 { 1080 const XMLText* text = compare->ToText(); 1081 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); 1082 } 1083 1084 1085 bool XMLText::Accept( XMLVisitor* visitor ) const 1086 { 1087 TIXMLASSERT( visitor ); 1088 return visitor->Visit( *this ); 1089 } 1090 1091 1092 // --------- XMLComment ---------- // 1093 1094 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) 1095 { 1096 } 1097 1098 1099 XMLComment::~XMLComment() 1100 { 1101 } 1102 1103 1104 char* XMLComment::ParseDeep( char* p, StrPair* ) 1105 { 1106 // Comment parses as text. 1107 const char* start = p; 1108 p = _value.ParseText( p, "-->", StrPair::COMMENT ); 1109 if ( p == 0 ) { 1110 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); 1111 } 1112 return p; 1113 } 1114 1115 1116 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const 1117 { 1118 if ( !doc ) { 1119 doc = _document; 1120 } 1121 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? 1122 return comment; 1123 } 1124 1125 1126 bool XMLComment::ShallowEqual( const XMLNode* compare ) const 1127 { 1128 TIXMLASSERT( compare ); 1129 const XMLComment* comment = compare->ToComment(); 1130 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); 1131 } 1132 1133 1134 bool XMLComment::Accept( XMLVisitor* visitor ) const 1135 { 1136 TIXMLASSERT( visitor ); 1137 return visitor->Visit( *this ); 1138 } 1139 1140 1141 // --------- XMLDeclaration ---------- // 1142 1143 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) 1144 { 1145 } 1146 1147 1148 XMLDeclaration::~XMLDeclaration() 1149 { 1150 //printf( "~XMLDeclaration\n" ); 1151 } 1152 1153 1154 char* XMLDeclaration::ParseDeep( char* p, StrPair* ) 1155 { 1156 // Declaration parses as text. 1157 const char* start = p; 1158 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 1159 if ( p == 0 ) { 1160 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); 1161 } 1162 return p; 1163 } 1164 1165 1166 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const 1167 { 1168 if ( !doc ) { 1169 doc = _document; 1170 } 1171 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? 1172 return dec; 1173 } 1174 1175 1176 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const 1177 { 1178 TIXMLASSERT( compare ); 1179 const XMLDeclaration* declaration = compare->ToDeclaration(); 1180 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); 1181 } 1182 1183 1184 1185 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const 1186 { 1187 TIXMLASSERT( visitor ); 1188 return visitor->Visit( *this ); 1189 } 1190 1191 // --------- XMLUnknown ---------- // 1192 1193 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) 1194 { 1195 } 1196 1197 1198 XMLUnknown::~XMLUnknown() 1199 { 1200 } 1201 1202 1203 char* XMLUnknown::ParseDeep( char* p, StrPair* ) 1204 { 1205 // Unknown parses as text. 1206 const char* start = p; 1207 1208 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 1209 if ( !p ) { 1210 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); 1211 } 1212 return p; 1213 } 1214 1215 1216 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const 1217 { 1218 if ( !doc ) { 1219 doc = _document; 1220 } 1221 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? 1222 return text; 1223 } 1224 1225 1226 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const 1227 { 1228 TIXMLASSERT( compare ); 1229 const XMLUnknown* unknown = compare->ToUnknown(); 1230 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); 1231 } 1232 1233 1234 bool XMLUnknown::Accept( XMLVisitor* visitor ) const 1235 { 1236 TIXMLASSERT( visitor ); 1237 return visitor->Visit( *this ); 1238 } 1239 1240 // --------- XMLAttribute ---------- // 1241 1242 const char* XMLAttribute::Name() const 1243 { 1244 return _name.GetStr(); 1245 } 1246 1247 const char* XMLAttribute::Value() const 1248 { 1249 return _value.GetStr(); 1250 } 1251 1252 char* XMLAttribute::ParseDeep( char* p, bool processEntities ) 1253 { 1254 // Parse using the name rules: bug fix, was using ParseText before 1255 p = _name.ParseName( p ); 1256 if ( !p || !*p ) { 1257 return 0; 1258 } 1259 1260 // Skip white space before = 1261 p = XMLUtil::SkipWhiteSpace( p ); 1262 if ( *p != '=' ) { 1263 return 0; 1264 } 1265 1266 ++p; // move up to opening quote 1267 p = XMLUtil::SkipWhiteSpace( p ); 1268 if ( *p != '\"' && *p != '\'' ) { 1269 return 0; 1270 } 1271 1272 char endTag[2] = { *p, 0 }; 1273 ++p; // move past opening quote 1274 1275 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); 1276 return p; 1277 } 1278 1279 1280 void XMLAttribute::SetName( const char* n ) 1281 { 1282 _name.SetStr( n ); 1283 } 1284 1285 1286 XMLError XMLAttribute::QueryIntValue( int* value ) const 1287 { 1288 if ( XMLUtil::ToInt( Value(), value )) { 1289 return XML_NO_ERROR; 1290 } 1291 return XML_WRONG_ATTRIBUTE_TYPE; 1292 } 1293 1294 1295 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const 1296 { 1297 if ( XMLUtil::ToUnsigned( Value(), value )) { 1298 return XML_NO_ERROR; 1299 } 1300 return XML_WRONG_ATTRIBUTE_TYPE; 1301 } 1302 1303 1304 XMLError XMLAttribute::QueryBoolValue( bool* value ) const 1305 { 1306 if ( XMLUtil::ToBool( Value(), value )) { 1307 return XML_NO_ERROR; 1308 } 1309 return XML_WRONG_ATTRIBUTE_TYPE; 1310 } 1311 1312 1313 XMLError XMLAttribute::QueryFloatValue( float* value ) const 1314 { 1315 if ( XMLUtil::ToFloat( Value(), value )) { 1316 return XML_NO_ERROR; 1317 } 1318 return XML_WRONG_ATTRIBUTE_TYPE; 1319 } 1320 1321 1322 XMLError XMLAttribute::QueryDoubleValue( double* value ) const 1323 { 1324 if ( XMLUtil::ToDouble( Value(), value )) { 1325 return XML_NO_ERROR; 1326 } 1327 return XML_WRONG_ATTRIBUTE_TYPE; 1328 } 1329 1330 1331 void XMLAttribute::SetAttribute( const char* v ) 1332 { 1333 _value.SetStr( v ); 1334 } 1335 1336 1337 void XMLAttribute::SetAttribute( int v ) 1338 { 1339 char buf[BUF_SIZE]; 1340 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1341 _value.SetStr( buf ); 1342 } 1343 1344 1345 void XMLAttribute::SetAttribute( unsigned v ) 1346 { 1347 char buf[BUF_SIZE]; 1348 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1349 _value.SetStr( buf ); 1350 } 1351 1352 1353 void XMLAttribute::SetAttribute( bool v ) 1354 { 1355 char buf[BUF_SIZE]; 1356 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1357 _value.SetStr( buf ); 1358 } 1359 1360 void XMLAttribute::SetAttribute( double v ) 1361 { 1362 char buf[BUF_SIZE]; 1363 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1364 _value.SetStr( buf ); 1365 } 1366 1367 void XMLAttribute::SetAttribute( float v ) 1368 { 1369 char buf[BUF_SIZE]; 1370 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1371 _value.SetStr( buf ); 1372 } 1373 1374 1375 // --------- XMLElement ---------- // 1376 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), 1377 _closingType( 0 ), 1378 _rootAttribute( 0 ) 1379 { 1380 } 1381 1382 1383 XMLElement::~XMLElement() 1384 { 1385 while( _rootAttribute ) { 1386 XMLAttribute* next = _rootAttribute->_next; 1387 DeleteAttribute( _rootAttribute ); 1388 _rootAttribute = next; 1389 } 1390 } 1391 1392 1393 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const 1394 { 1395 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { 1396 if ( XMLUtil::StringEqual( a->Name(), name ) ) { 1397 return a; 1398 } 1399 } 1400 return 0; 1401 } 1402 1403 1404 const char* XMLElement::Attribute( const char* name, const char* value ) const 1405 { 1406 const XMLAttribute* a = FindAttribute( name ); 1407 if ( !a ) { 1408 return 0; 1409 } 1410 if ( !value || XMLUtil::StringEqual( a->Value(), value )) { 1411 return a->Value(); 1412 } 1413 return 0; 1414 } 1415 1416 1417 const char* XMLElement::GetText() const 1418 { 1419 if ( FirstChild() && FirstChild()->ToText() ) { 1420 return FirstChild()->Value(); 1421 } 1422 return 0; 1423 } 1424 1425 1426 void XMLElement::SetText( const char* inText ) 1427 { 1428 if ( FirstChild() && FirstChild()->ToText() ) 1429 FirstChild()->SetValue( inText ); 1430 else { 1431 XMLText* theText = GetDocument()->NewText( inText ); 1432 InsertFirstChild( theText ); 1433 } 1434 } 1435 1436 1437 void XMLElement::SetText( int v ) 1438 { 1439 char buf[BUF_SIZE]; 1440 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1441 SetText( buf ); 1442 } 1443 1444 1445 void XMLElement::SetText( unsigned v ) 1446 { 1447 char buf[BUF_SIZE]; 1448 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1449 SetText( buf ); 1450 } 1451 1452 1453 void XMLElement::SetText( bool v ) 1454 { 1455 char buf[BUF_SIZE]; 1456 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1457 SetText( buf ); 1458 } 1459 1460 1461 void XMLElement::SetText( float v ) 1462 { 1463 char buf[BUF_SIZE]; 1464 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1465 SetText( buf ); 1466 } 1467 1468 1469 void XMLElement::SetText( double v ) 1470 { 1471 char buf[BUF_SIZE]; 1472 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1473 SetText( buf ); 1474 } 1475 1476 1477 XMLError XMLElement::QueryIntText( int* ival ) const 1478 { 1479 if ( FirstChild() && FirstChild()->ToText() ) { 1480 const char* t = FirstChild()->Value(); 1481 if ( XMLUtil::ToInt( t, ival ) ) { 1482 return XML_SUCCESS; 1483 } 1484 return XML_CAN_NOT_CONVERT_TEXT; 1485 } 1486 return XML_NO_TEXT_NODE; 1487 } 1488 1489 1490 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const 1491 { 1492 if ( FirstChild() && FirstChild()->ToText() ) { 1493 const char* t = FirstChild()->Value(); 1494 if ( XMLUtil::ToUnsigned( t, uval ) ) { 1495 return XML_SUCCESS; 1496 } 1497 return XML_CAN_NOT_CONVERT_TEXT; 1498 } 1499 return XML_NO_TEXT_NODE; 1500 } 1501 1502 1503 XMLError XMLElement::QueryBoolText( bool* bval ) const 1504 { 1505 if ( FirstChild() && FirstChild()->ToText() ) { 1506 const char* t = FirstChild()->Value(); 1507 if ( XMLUtil::ToBool( t, bval ) ) { 1508 return XML_SUCCESS; 1509 } 1510 return XML_CAN_NOT_CONVERT_TEXT; 1511 } 1512 return XML_NO_TEXT_NODE; 1513 } 1514 1515 1516 XMLError XMLElement::QueryDoubleText( double* dval ) const 1517 { 1518 if ( FirstChild() && FirstChild()->ToText() ) { 1519 const char* t = FirstChild()->Value(); 1520 if ( XMLUtil::ToDouble( t, dval ) ) { 1521 return XML_SUCCESS; 1522 } 1523 return XML_CAN_NOT_CONVERT_TEXT; 1524 } 1525 return XML_NO_TEXT_NODE; 1526 } 1527 1528 1529 XMLError XMLElement::QueryFloatText( float* fval ) const 1530 { 1531 if ( FirstChild() && FirstChild()->ToText() ) { 1532 const char* t = FirstChild()->Value(); 1533 if ( XMLUtil::ToFloat( t, fval ) ) { 1534 return XML_SUCCESS; 1535 } 1536 return XML_CAN_NOT_CONVERT_TEXT; 1537 } 1538 return XML_NO_TEXT_NODE; 1539 } 1540 1541 1542 1543 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) 1544 { 1545 XMLAttribute* last = 0; 1546 XMLAttribute* attrib = 0; 1547 for( attrib = _rootAttribute; 1548 attrib; 1549 last = attrib, attrib = attrib->_next ) { 1550 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { 1551 break; 1552 } 1553 } 1554 if ( !attrib ) { 1555 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); 1556 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); 1557 attrib->_memPool = &_document->_attributePool; 1558 if ( last ) { 1559 last->_next = attrib; 1560 } 1561 else { 1562 _rootAttribute = attrib; 1563 } 1564 attrib->SetName( name ); 1565 attrib->_memPool->SetTracked(); // always created and linked. 1566 } 1567 return attrib; 1568 } 1569 1570 1571 void XMLElement::DeleteAttribute( const char* name ) 1572 { 1573 XMLAttribute* prev = 0; 1574 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { 1575 if ( XMLUtil::StringEqual( name, a->Name() ) ) { 1576 if ( prev ) { 1577 prev->_next = a->_next; 1578 } 1579 else { 1580 _rootAttribute = a->_next; 1581 } 1582 DeleteAttribute( a ); 1583 break; 1584 } 1585 prev = a; 1586 } 1587 } 1588 1589 1590 char* XMLElement::ParseAttributes( char* p ) 1591 { 1592 const char* start = p; 1593 XMLAttribute* prevAttribute = 0; 1594 1595 // Read the attributes. 1596 while( p ) { 1597 p = XMLUtil::SkipWhiteSpace( p ); 1598 if ( !(*p) ) { 1599 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); 1600 return 0; 1601 } 1602 1603 // attribute. 1604 if (XMLUtil::IsNameStartChar( *p ) ) { 1605 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); 1606 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); 1607 attrib->_memPool = &_document->_attributePool; 1608 attrib->_memPool->SetTracked(); 1609 1610 p = attrib->ParseDeep( p, _document->ProcessEntities() ); 1611 if ( !p || Attribute( attrib->Name() ) ) { 1612 DeleteAttribute( attrib ); 1613 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); 1614 return 0; 1615 } 1616 // There is a minor bug here: if the attribute in the source xml 1617 // document is duplicated, it will not be detected and the 1618 // attribute will be doubly added. However, tracking the 'prevAttribute' 1619 // avoids re-scanning the attribute list. Preferring performance for 1620 // now, may reconsider in the future. 1621 if ( prevAttribute ) { 1622 prevAttribute->_next = attrib; 1623 } 1624 else { 1625 _rootAttribute = attrib; 1626 } 1627 prevAttribute = attrib; 1628 } 1629 // end of the tag 1630 else if ( *p == '>' ) { 1631 ++p; 1632 break; 1633 } 1634 // end of the tag 1635 else if ( *p == '/' && *(p+1) == '>' ) { 1636 _closingType = CLOSED; 1637 return p+2; // done; sealed element. 1638 } 1639 else { 1640 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); 1641 return 0; 1642 } 1643 } 1644 return p; 1645 } 1646 1647 void XMLElement::DeleteAttribute( XMLAttribute* attribute ) 1648 { 1649 if ( attribute == 0 ) { 1650 return; 1651 } 1652 MemPool* pool = attribute->_memPool; 1653 attribute->~XMLAttribute(); 1654 pool->Free( attribute ); 1655 } 1656 1657 // 1658 // <ele></ele> 1659 // <ele>foo<b>bar</b></ele> 1660 // 1661 char* XMLElement::ParseDeep( char* p, StrPair* strPair ) 1662 { 1663 // Read the element name. 1664 p = XMLUtil::SkipWhiteSpace( p ); 1665 1666 // The closing element is the </element> form. It is 1667 // parsed just like a regular element then deleted from 1668 // the DOM. 1669 if ( *p == '/' ) { 1670 _closingType = CLOSING; 1671 ++p; 1672 } 1673 1674 p = _value.ParseName( p ); 1675 if ( _value.Empty() ) { 1676 return 0; 1677 } 1678 1679 p = ParseAttributes( p ); 1680 if ( !p || !*p || _closingType ) { 1681 return p; 1682 } 1683 1684 p = XMLNode::ParseDeep( p, strPair ); 1685 return p; 1686 } 1687 1688 1689 1690 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const 1691 { 1692 if ( !doc ) { 1693 doc = _document; 1694 } 1695 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? 1696 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { 1697 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? 1698 } 1699 return element; 1700 } 1701 1702 1703 bool XMLElement::ShallowEqual( const XMLNode* compare ) const 1704 { 1705 TIXMLASSERT( compare ); 1706 const XMLElement* other = compare->ToElement(); 1707 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { 1708 1709 const XMLAttribute* a=FirstAttribute(); 1710 const XMLAttribute* b=other->FirstAttribute(); 1711 1712 while ( a && b ) { 1713 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { 1714 return false; 1715 } 1716 a = a->Next(); 1717 b = b->Next(); 1718 } 1719 if ( a || b ) { 1720 // different count 1721 return false; 1722 } 1723 return true; 1724 } 1725 return false; 1726 } 1727 1728 1729 bool XMLElement::Accept( XMLVisitor* visitor ) const 1730 { 1731 TIXMLASSERT( visitor ); 1732 if ( visitor->VisitEnter( *this, _rootAttribute ) ) { 1733 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { 1734 if ( !node->Accept( visitor ) ) { 1735 break; 1736 } 1737 } 1738 } 1739 return visitor->VisitExit( *this ); 1740 } 1741 1742 1743 // --------- XMLDocument ----------- // 1744 1745 // Warning: List must match 'enum XMLError' 1746 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { 1747 "XML_SUCCESS", 1748 "XML_NO_ATTRIBUTE", 1749 "XML_WRONG_ATTRIBUTE_TYPE", 1750 "XML_ERROR_FILE_NOT_FOUND", 1751 "XML_ERROR_FILE_COULD_NOT_BE_OPENED", 1752 "XML_ERROR_FILE_READ_ERROR", 1753 "XML_ERROR_ELEMENT_MISMATCH", 1754 "XML_ERROR_PARSING_ELEMENT", 1755 "XML_ERROR_PARSING_ATTRIBUTE", 1756 "XML_ERROR_IDENTIFYING_TAG", 1757 "XML_ERROR_PARSING_TEXT", 1758 "XML_ERROR_PARSING_CDATA", 1759 "XML_ERROR_PARSING_COMMENT", 1760 "XML_ERROR_PARSING_DECLARATION", 1761 "XML_ERROR_PARSING_UNKNOWN", 1762 "XML_ERROR_EMPTY_DOCUMENT", 1763 "XML_ERROR_MISMATCHED_ELEMENT", 1764 "XML_ERROR_PARSING", 1765 "XML_CAN_NOT_CONVERT_TEXT", 1766 "XML_NO_TEXT_NODE" 1767 }; 1768 1769 1770 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : 1771 XMLNode( 0 ), 1772 _writeBOM( false ), 1773 _processEntities( processEntities ), 1774 _errorID( XML_NO_ERROR ), 1775 _whitespace( whitespace ), 1776 _errorStr1( 0 ), 1777 _errorStr2( 0 ), 1778 _charBuffer( 0 ) 1779 { 1780 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) 1781 _document = this; 1782 } 1783 1784 1785 XMLDocument::~XMLDocument() 1786 { 1787 Clear(); 1788 } 1789 1790 1791 void XMLDocument::Clear() 1792 { 1793 DeleteChildren(); 1794 1795 #ifdef DEBUG 1796 const bool hadError = Error(); 1797 #endif 1798 _errorID = XML_NO_ERROR; 1799 _errorStr1 = 0; 1800 _errorStr2 = 0; 1801 1802 delete [] _charBuffer; 1803 _charBuffer = 0; 1804 1805 #if 0 1806 _textPool.Trace( "text" ); 1807 _elementPool.Trace( "element" ); 1808 _commentPool.Trace( "comment" ); 1809 _attributePool.Trace( "attribute" ); 1810 #endif 1811 1812 #ifdef DEBUG 1813 if ( !hadError ) { 1814 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); 1815 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); 1816 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); 1817 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); 1818 } 1819 #endif 1820 } 1821 1822 1823 XMLElement* XMLDocument::NewElement( const char* name ) 1824 { 1825 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); 1826 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); 1827 ele->_memPool = &_elementPool; 1828 ele->SetName( name ); 1829 return ele; 1830 } 1831 1832 1833 XMLComment* XMLDocument::NewComment( const char* str ) 1834 { 1835 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); 1836 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); 1837 comment->_memPool = &_commentPool; 1838 comment->SetValue( str ); 1839 return comment; 1840 } 1841 1842 1843 XMLText* XMLDocument::NewText( const char* str ) 1844 { 1845 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); 1846 XMLText* text = new (_textPool.Alloc()) XMLText( this ); 1847 text->_memPool = &_textPool; 1848 text->SetValue( str ); 1849 return text; 1850 } 1851 1852 1853 XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) 1854 { 1855 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); 1856 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); 1857 dec->_memPool = &_commentPool; 1858 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); 1859 return dec; 1860 } 1861 1862 1863 XMLUnknown* XMLDocument::NewUnknown( const char* str ) 1864 { 1865 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); 1866 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); 1867 unk->_memPool = &_commentPool; 1868 unk->SetValue( str ); 1869 return unk; 1870 } 1871 1872 static FILE* callfopen( const char* filepath, const char* mode ) 1873 { 1874 TIXMLASSERT( filepath ); 1875 TIXMLASSERT( mode ); 1876 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) 1877 FILE* fp = 0; 1878 errno_t err = fopen_s( &fp, filepath, mode ); 1879 if ( err ) { 1880 return 0; 1881 } 1882 #else 1883 FILE* fp = fopen( filepath, mode ); 1884 #endif 1885 return fp; 1886 } 1887 1888 void XMLDocument::DeleteNode( XMLNode* node ) { 1889 TIXMLASSERT( node ); 1890 TIXMLASSERT(node->_document == this ); 1891 if (node->_parent) { 1892 node->_parent->DeleteChild( node ); 1893 } 1894 else { 1895 // Isn't in the tree. 1896 // Use the parent delete. 1897 // Also, we need to mark it tracked: we 'know' 1898 // it was never used. 1899 node->_memPool->SetTracked(); 1900 // Call the static XMLNode version: 1901 XMLNode::DeleteNode(node); 1902 } 1903 } 1904 1905 1906 XMLError XMLDocument::LoadFile( const char* filename ) 1907 { 1908 Clear(); 1909 FILE* fp = callfopen( filename, "rb" ); 1910 if ( !fp ) { 1911 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); 1912 return _errorID; 1913 } 1914 LoadFile( fp ); 1915 fclose( fp ); 1916 return _errorID; 1917 } 1918 1919 // This is likely overengineered template art to have a check that unsigned long value incremented 1920 // by one still fits into size_t. If size_t type is larger than unsigned long type 1921 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit 1922 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check 1923 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long 1924 // types sizes relate to each other. 1925 template 1926 <bool = (sizeof(unsigned long) >= sizeof(size_t))> 1927 struct LongFitsIntoSizeTMinusOne { 1928 static bool Fits( unsigned long value ) 1929 { 1930 return value < (size_t)-1; 1931 } 1932 }; 1933 1934 template <> 1935 bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ ) 1936 { 1937 return true; 1938 } 1939 1940 XMLError XMLDocument::LoadFile( FILE* fp ) 1941 { 1942 Clear(); 1943 1944 fseek( fp, 0, SEEK_SET ); 1945 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { 1946 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); 1947 return _errorID; 1948 } 1949 1950 fseek( fp, 0, SEEK_END ); 1951 const long filelength = ftell( fp ); 1952 fseek( fp, 0, SEEK_SET ); 1953 if ( filelength == -1L ) { 1954 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); 1955 return _errorID; 1956 } 1957 TIXMLASSERT( filelength >= 0 ); 1958 1959 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { 1960 // Cannot handle files which won't fit in buffer together with null terminator 1961 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); 1962 return _errorID; 1963 } 1964 1965 if ( filelength == 0 ) { 1966 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 1967 return _errorID; 1968 } 1969 1970 const size_t size = filelength; 1971 TIXMLASSERT( _charBuffer == 0 ); 1972 _charBuffer = new char[size+1]; 1973 size_t read = fread( _charBuffer, 1, size, fp ); 1974 if ( read != size ) { 1975 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); 1976 return _errorID; 1977 } 1978 1979 _charBuffer[size] = 0; 1980 1981 Parse(); 1982 return _errorID; 1983 } 1984 1985 1986 XMLError XMLDocument::SaveFile( const char* filename, bool compact ) 1987 { 1988 FILE* fp = callfopen( filename, "w" ); 1989 if ( !fp ) { 1990 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); 1991 return _errorID; 1992 } 1993 SaveFile(fp, compact); 1994 fclose( fp ); 1995 return _errorID; 1996 } 1997 1998 1999 XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) 2000 { 2001 // Clear any error from the last save, otherwise it will get reported 2002 // for *this* call. 2003 SetError( XML_NO_ERROR, 0, 0 ); 2004 XMLPrinter stream( fp, compact ); 2005 Print( &stream ); 2006 return _errorID; 2007 } 2008 2009 2010 XMLError XMLDocument::Parse( const char* p, size_t len ) 2011 { 2012 Clear(); 2013 2014 if ( len == 0 || !p || !*p ) { 2015 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 2016 return _errorID; 2017 } 2018 if ( len == (size_t)(-1) ) { 2019 len = strlen( p ); 2020 } 2021 TIXMLASSERT( _charBuffer == 0 ); 2022 _charBuffer = new char[ len+1 ]; 2023 memcpy( _charBuffer, p, len ); 2024 _charBuffer[len] = 0; 2025 2026 Parse(); 2027 if ( Error() ) { 2028 // clean up now essentially dangling memory. 2029 // and the parse fail can put objects in the 2030 // pools that are dead and inaccessible. 2031 DeleteChildren(); 2032 _elementPool.Clear(); 2033 _attributePool.Clear(); 2034 _textPool.Clear(); 2035 _commentPool.Clear(); 2036 } 2037 return _errorID; 2038 } 2039 2040 2041 void XMLDocument::Print( XMLPrinter* streamer ) const 2042 { 2043 if ( streamer ) { 2044 Accept( streamer ); 2045 } 2046 else { 2047 XMLPrinter stdoutStreamer( stdout ); 2048 Accept( &stdoutStreamer ); 2049 } 2050 } 2051 2052 2053 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) 2054 { 2055 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); 2056 _errorID = error; 2057 _errorStr1 = str1; 2058 _errorStr2 = str2; 2059 } 2060 2061 const char* XMLDocument::ErrorName() const 2062 { 2063 TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT ); 2064 const char* errorName = _errorNames[_errorID]; 2065 TIXMLASSERT( errorName && errorName[0] ); 2066 return errorName; 2067 } 2068 2069 void XMLDocument::PrintError() const 2070 { 2071 if ( Error() ) { 2072 static const int LEN = 20; 2073 char buf1[LEN] = { 0 }; 2074 char buf2[LEN] = { 0 }; 2075 2076 if ( _errorStr1 ) { 2077 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); 2078 } 2079 if ( _errorStr2 ) { 2080 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); 2081 } 2082 2083 // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that 2084 // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning 2085 TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX ); 2086 printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n", 2087 static_cast<int>( _errorID ), ErrorName(), buf1, buf2 ); 2088 } 2089 } 2090 2091 void XMLDocument::Parse() 2092 { 2093 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously 2094 TIXMLASSERT( _charBuffer ); 2095 char* p = _charBuffer; 2096 p = XMLUtil::SkipWhiteSpace( p ); 2097 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) ); 2098 if ( !*p ) { 2099 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 2100 return; 2101 } 2102 ParseDeep(p, 0 ); 2103 } 2104 2105 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : 2106 _elementJustOpened( false ), 2107 _firstElement( true ), 2108 _fp( file ), 2109 _depth( depth ), 2110 _textDepth( -1 ), 2111 _processEntities( true ), 2112 _compactMode( compact ) 2113 { 2114 for( int i=0; i<ENTITY_RANGE; ++i ) { 2115 _entityFlag[i] = false; 2116 _restrictedEntityFlag[i] = false; 2117 } 2118 for( int i=0; i<NUM_ENTITIES; ++i ) { 2119 const char entityValue = entities[i].value; 2120 TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE ); 2121 _entityFlag[ (unsigned char)entityValue ] = true; 2122 } 2123 _restrictedEntityFlag[(unsigned char)'&'] = true; 2124 _restrictedEntityFlag[(unsigned char)'<'] = true; 2125 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice 2126 _buffer.Push( 0 ); 2127 } 2128 2129 2130 void XMLPrinter::Print( const char* format, ... ) 2131 { 2132 va_list va; 2133 va_start( va, format ); 2134 2135 if ( _fp ) { 2136 vfprintf( _fp, format, va ); 2137 } 2138 else { 2139 const int len = TIXML_VSCPRINTF( format, va ); 2140 // Close out and re-start the va-args 2141 va_end( va ); 2142 TIXMLASSERT( len >= 0 ); 2143 va_start( va, format ); 2144 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); 2145 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. 2146 TIXML_VSNPRINTF( p, len+1, format, va ); 2147 } 2148 va_end( va ); 2149 } 2150 2151 2152 void XMLPrinter::PrintSpace( int depth ) 2153 { 2154 for( int i=0; i<depth; ++i ) { 2155 Print( " " ); 2156 } 2157 } 2158 2159 2160 void XMLPrinter::PrintString( const char* p, bool restricted ) 2161 { 2162 // Look for runs of bytes between entities to print. 2163 const char* q = p; 2164 2165 if ( _processEntities ) { 2166 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; 2167 while ( *q ) { 2168 TIXMLASSERT( p <= q ); 2169 // Remember, char is sometimes signed. (How many times has that bitten me?) 2170 if ( *q > 0 && *q < ENTITY_RANGE ) { 2171 // Check for entities. If one is found, flush 2172 // the stream up until the entity, write the 2173 // entity, and keep looking. 2174 if ( flag[(unsigned char)(*q)] ) { 2175 while ( p < q ) { 2176 const size_t delta = q - p; 2177 // %.*s accepts type int as "precision" 2178 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; 2179 Print( "%.*s", toPrint, p ); 2180 p += toPrint; 2181 } 2182 bool entityPatternPrinted = false; 2183 for( int i=0; i<NUM_ENTITIES; ++i ) { 2184 if ( entities[i].value == *q ) { 2185 Print( "&%s;", entities[i].pattern ); 2186 entityPatternPrinted = true; 2187 break; 2188 } 2189 } 2190 if ( !entityPatternPrinted ) { 2191 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release 2192 TIXMLASSERT( false ); 2193 } 2194 ++p; 2195 } 2196 } 2197 ++q; 2198 TIXMLASSERT( p <= q ); 2199 } 2200 } 2201 // Flush the remaining string. This will be the entire 2202 // string if an entity wasn't found. 2203 TIXMLASSERT( p <= q ); 2204 if ( !_processEntities || ( p < q ) ) { 2205 Print( "%s", p ); 2206 } 2207 } 2208 2209 2210 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) 2211 { 2212 if ( writeBOM ) { 2213 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; 2214 Print( "%s", bom ); 2215 } 2216 if ( writeDec ) { 2217 PushDeclaration( "xml version=\"1.0\"" ); 2218 } 2219 } 2220 2221 2222 void XMLPrinter::OpenElement( const char* name, bool compactMode ) 2223 { 2224 SealElementIfJustOpened(); 2225 _stack.Push( name ); 2226 2227 if ( _textDepth < 0 && !_firstElement && !compactMode ) { 2228 Print( "\n" ); 2229 } 2230 if ( !compactMode ) { 2231 PrintSpace( _depth ); 2232 } 2233 2234 Print( "<%s", name ); 2235 _elementJustOpened = true; 2236 _firstElement = false; 2237 ++_depth; 2238 } 2239 2240 2241 void XMLPrinter::PushAttribute( const char* name, const char* value ) 2242 { 2243 TIXMLASSERT( _elementJustOpened ); 2244 Print( " %s=\"", name ); 2245 PrintString( value, false ); 2246 Print( "\"" ); 2247 } 2248 2249 2250 void XMLPrinter::PushAttribute( const char* name, int v ) 2251 { 2252 char buf[BUF_SIZE]; 2253 XMLUtil::ToStr( v, buf, BUF_SIZE ); 2254 PushAttribute( name, buf ); 2255 } 2256 2257 2258 void XMLPrinter::PushAttribute( const char* name, unsigned v ) 2259 { 2260 char buf[BUF_SIZE]; 2261 XMLUtil::ToStr( v, buf, BUF_SIZE ); 2262 PushAttribute( name, buf ); 2263 } 2264 2265 2266 void XMLPrinter::PushAttribute( const char* name, bool v ) 2267 { 2268 char buf[BUF_SIZE]; 2269 XMLUtil::ToStr( v, buf, BUF_SIZE ); 2270 PushAttribute( name, buf ); 2271 } 2272 2273 2274 void XMLPrinter::PushAttribute( const char* name, double v ) 2275 { 2276 char buf[BUF_SIZE]; 2277 XMLUtil::ToStr( v, buf, BUF_SIZE ); 2278 PushAttribute( name, buf ); 2279 } 2280 2281 2282 void XMLPrinter::CloseElement( bool compactMode ) 2283 { 2284 --_depth; 2285 const char* name = _stack.Pop(); 2286 2287 if ( _elementJustOpened ) { 2288 Print( "/>" ); 2289 } 2290 else { 2291 if ( _textDepth < 0 && !compactMode) { 2292 Print( "\n" ); 2293 PrintSpace( _depth ); 2294 } 2295 Print( "</%s>", name ); 2296 } 2297 2298 if ( _textDepth == _depth ) { 2299 _textDepth = -1; 2300 } 2301 if ( _depth == 0 && !compactMode) { 2302 Print( "\n" ); 2303 } 2304 _elementJustOpened = false; 2305 } 2306 2307 2308 void XMLPrinter::SealElementIfJustOpened() 2309 { 2310 if ( !_elementJustOpened ) { 2311 return; 2312 } 2313 _elementJustOpened = false; 2314 Print( ">" ); 2315 } 2316 2317 2318 void XMLPrinter::PushText( const char* text, bool cdata ) 2319 { 2320 _textDepth = _depth-1; 2321 2322 SealElementIfJustOpened(); 2323 if ( cdata ) { 2324 Print( "<![CDATA[%s]]>", text ); 2325 } 2326 else { 2327 PrintString( text, true ); 2328 } 2329 } 2330 2331 void XMLPrinter::PushText( int value ) 2332 { 2333 char buf[BUF_SIZE]; 2334 XMLUtil::ToStr( value, buf, BUF_SIZE ); 2335 PushText( buf, false ); 2336 } 2337 2338 2339 void XMLPrinter::PushText( unsigned value ) 2340 { 2341 char buf[BUF_SIZE]; 2342 XMLUtil::ToStr( value, buf, BUF_SIZE ); 2343 PushText( buf, false ); 2344 } 2345 2346 2347 void XMLPrinter::PushText( bool value ) 2348 { 2349 char buf[BUF_SIZE]; 2350 XMLUtil::ToStr( value, buf, BUF_SIZE ); 2351 PushText( buf, false ); 2352 } 2353 2354 2355 void XMLPrinter::PushText( float value ) 2356 { 2357 char buf[BUF_SIZE]; 2358 XMLUtil::ToStr( value, buf, BUF_SIZE ); 2359 PushText( buf, false ); 2360 } 2361 2362 2363 void XMLPrinter::PushText( double value ) 2364 { 2365 char buf[BUF_SIZE]; 2366 XMLUtil::ToStr( value, buf, BUF_SIZE ); 2367 PushText( buf, false ); 2368 } 2369 2370 2371 void XMLPrinter::PushComment( const char* comment ) 2372 { 2373 SealElementIfJustOpened(); 2374 if ( _textDepth < 0 && !_firstElement && !_compactMode) { 2375 Print( "\n" ); 2376 PrintSpace( _depth ); 2377 } 2378 _firstElement = false; 2379 Print( "<!--%s-->", comment ); 2380 } 2381 2382 2383 void XMLPrinter::PushDeclaration( const char* value ) 2384 { 2385 SealElementIfJustOpened(); 2386 if ( _textDepth < 0 && !_firstElement && !_compactMode) { 2387 Print( "\n" ); 2388 PrintSpace( _depth ); 2389 } 2390 _firstElement = false; 2391 Print( "<?%s?>", value ); 2392 } 2393 2394 2395 void XMLPrinter::PushUnknown( const char* value ) 2396 { 2397 SealElementIfJustOpened(); 2398 if ( _textDepth < 0 && !_firstElement && !_compactMode) { 2399 Print( "\n" ); 2400 PrintSpace( _depth ); 2401 } 2402 _firstElement = false; 2403 Print( "<!%s>", value ); 2404 } 2405 2406 2407 bool XMLPrinter::VisitEnter( const XMLDocument& doc ) 2408 { 2409 _processEntities = doc.ProcessEntities(); 2410 if ( doc.HasBOM() ) { 2411 PushHeader( true, false ); 2412 } 2413 return true; 2414 } 2415 2416 2417 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) 2418 { 2419 const XMLElement* parentElem = 0; 2420 if ( element.Parent() ) { 2421 parentElem = element.Parent()->ToElement(); 2422 } 2423 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; 2424 OpenElement( element.Name(), compactMode ); 2425 while ( attribute ) { 2426 PushAttribute( attribute->Name(), attribute->Value() ); 2427 attribute = attribute->Next(); 2428 } 2429 return true; 2430 } 2431 2432 2433 bool XMLPrinter::VisitExit( const XMLElement& element ) 2434 { 2435 CloseElement( CompactMode(element) ); 2436 return true; 2437 } 2438 2439 2440 bool XMLPrinter::Visit( const XMLText& text ) 2441 { 2442 PushText( text.Value(), text.CData() ); 2443 return true; 2444 } 2445 2446 2447 bool XMLPrinter::Visit( const XMLComment& comment ) 2448 { 2449 PushComment( comment.Value() ); 2450 return true; 2451 } 2452 2453 bool XMLPrinter::Visit( const XMLDeclaration& declaration ) 2454 { 2455 PushDeclaration( declaration.Value() ); 2456 return true; 2457 } 2458 2459 2460 bool XMLPrinter::Visit( const XMLUnknown& unknown ) 2461 { 2462 PushUnknown( unknown.Value() ); 2463 return true; 2464 } 2465 2466 } // namespace tinyxml2 2467 2468