1/* 2Original code by Lee Thomason (www.grinninglizard.com) 3 4This software is provided 'as-is', without any express or implied 5warranty. In no event will the authors be held liable for any 6damages arising from the use of this software. 7 8Permission is granted to anyone to use this software for any 9purpose, including commercial applications, and to alter it and 10redistribute it freely, subject to the following restrictions: 11 121. The origin of this software must not be misrepresented; you must 13not claim that you wrote the original software. If you use this 14software in a product, an acknowledgment in the product documentation 15would be appreciated but is not required. 16 172. Altered source versions must be plainly marked as such, and 18must not be misrepresented as being the original software. 19 203. This notice may not be removed or altered from any source 21distribution. 22*/ 23#include "tinyxml2.h" 24 25#include <cstdio> 26#include <cstdlib> 27#include <new> 28#include <cstddef> 29 30#include <fcntl.h> 31using namespace tinyxml2; 32 33static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF 34static const char LF = LINE_FEED; 35static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out 36static const char CR = CARRIAGE_RETURN; 37static const char SINGLE_QUOTE = '\''; 38static const char DOUBLE_QUOTE = '\"'; 39 40// Bunch of unicode info at: 41// http://www.unicode.org/faq/utf_bom.html 42// ef bb bf (Microsoft "lead bytes") - designates UTF-8 43 44static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 45static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 46static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 47 48 49#define DELETE_NODE( node ) { \ 50 if ( node ) { \ 51 MemPool* pool = node->memPool; \ 52 node->~XMLNode(); \ 53 pool->Free( node ); \ 54 } \ 55} 56#define DELETE_ATTRIBUTE( attrib ) { \ 57 if ( attrib ) { \ 58 MemPool* pool = attrib->memPool; \ 59 attrib->~XMLAttribute(); \ 60 pool->Free( attrib ); \ 61 } \ 62} 63 64struct Entity { 65 const char* pattern; 66 int length; 67 char value; 68}; 69 70static const int NUM_ENTITIES = 5; 71static const Entity entities[NUM_ENTITIES] = 72{ 73 { "quot", 4, DOUBLE_QUOTE }, 74 { "amp", 3, '&' }, 75 { "apos", 4, SINGLE_QUOTE }, 76 { "lt", 2, '<' }, 77 { "gt", 2, '>' } 78}; 79 80 81StrPair::~StrPair() 82{ 83 Reset(); 84} 85 86 87void StrPair::Reset() 88{ 89 if ( flags & NEEDS_DELETE ) { 90 delete [] start; 91 } 92 flags = 0; 93 start = 0; 94 end = 0; 95} 96 97 98void StrPair::SetStr( const char* str, int flags ) 99{ 100 Reset(); 101 size_t len = strlen( str ); 102 start = new char[ len+1 ]; 103 memcpy( start, str, len+1 ); 104 end = start + len; 105 this->flags = flags | NEEDS_DELETE; 106} 107 108 109char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) 110{ 111 TIXMLASSERT( endTag && *endTag ); 112 113 char* start = p; // fixme: hides a member 114 char endChar = *endTag; 115 size_t length = strlen( endTag ); 116 117 // Inner loop of text parsing. 118 while ( *p ) { 119 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { 120 Set( start, p, strFlags ); 121 return p + length; 122 } 123 ++p; 124 } 125 return 0; 126} 127 128 129char* StrPair::ParseName( char* p ) 130{ 131 char* start = p; 132 133 if ( !start || !(*start) ) { 134 return 0; 135 } 136 137 if ( !XMLUtil::IsAlpha( *p ) ) { 138 return 0; 139 } 140 141 while( *p && ( 142 XMLUtil::IsAlphaNum( (unsigned char) *p ) 143 || *p == '_' 144 || *p == '-' 145 || *p == '.' 146 || *p == ':' )) 147 { 148 ++p; 149 } 150 151 if ( p > start ) { 152 Set( start, p, 0 ); 153 return p; 154 } 155 return 0; 156} 157 158 159 160const char* StrPair::GetStr() 161{ 162 if ( flags & NEEDS_FLUSH ) { 163 *end = 0; 164 flags ^= NEEDS_FLUSH; 165 166 if ( flags ) { 167 char* p = start; // the read pointer 168 char* q = start; // the write pointer 169 170 while( p < end ) { 171 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { 172 // CR-LF pair becomes LF 173 // CR alone becomes LF 174 // LF-CR becomes LF 175 if ( *(p+1) == LF ) { 176 p += 2; 177 } 178 else { 179 ++p; 180 } 181 *q++ = LF; 182 } 183 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { 184 if ( *(p+1) == CR ) { 185 p += 2; 186 } 187 else { 188 ++p; 189 } 190 *q++ = LF; 191 } 192 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { 193 // Entities handled by tinyXML2: 194 // - special entities in the entity table [in/out] 195 // - numeric character reference [in] 196 // 中 or 中 197 198 if ( *(p+1) == '#' ) { 199 char buf[10] = { 0 }; 200 int len; 201 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) ); 202 for( int i=0; i<len; ++i ) { 203 *q++ = buf[i]; 204 } 205 TIXMLASSERT( q <= p ); 206 } 207 else { 208 int i=0; 209 for(; i<NUM_ENTITIES; ++i ) { 210 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0 211 && *(p+entities[i].length+1) == ';' ) 212 { 213 // Found an entity convert; 214 *q = entities[i].value; 215 ++q; 216 p += entities[i].length + 2; 217 break; 218 } 219 } 220 if ( i == NUM_ENTITIES ) { 221 // fixme: treat as error? 222 ++p; 223 ++q; 224 } 225 } 226 } 227 else { 228 *q = *p; 229 ++p; 230 ++q; 231 } 232 } 233 *q = 0; 234 } 235 flags = (flags & NEEDS_DELETE); 236 } 237 return start; 238} 239 240 241 242 243// --------- XMLUtil ----------- // 244 245const char* XMLUtil::ReadBOM( const char* p, bool* bom ) 246{ 247 *bom = false; 248 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p); 249 // Check for BOM: 250 if ( *(pu+0) == TIXML_UTF_LEAD_0 251 && *(pu+1) == TIXML_UTF_LEAD_1 252 && *(pu+2) == TIXML_UTF_LEAD_2 ) 253 { 254 *bom = true; 255 p += 3; 256 } 257 return p; 258} 259 260 261void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) 262{ 263 const unsigned long BYTE_MASK = 0xBF; 264 const unsigned long BYTE_MARK = 0x80; 265 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 266 267 if (input < 0x80) 268 *length = 1; 269 else if ( input < 0x800 ) 270 *length = 2; 271 else if ( input < 0x10000 ) 272 *length = 3; 273 else if ( input < 0x200000 ) 274 *length = 4; 275 else 276 { *length = 0; return; } // This code won't covert this correctly anyway. 277 278 output += *length; 279 280 // Scary scary fall throughs. 281 switch (*length) 282 { 283 case 4: 284 --output; 285 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 286 input >>= 6; 287 case 3: 288 --output; 289 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 290 input >>= 6; 291 case 2: 292 --output; 293 *output = (char)((input | BYTE_MARK) & BYTE_MASK); 294 input >>= 6; 295 case 1: 296 --output; 297 *output = (char)(input | FIRST_BYTE_MARK[*length]); 298 } 299} 300 301 302const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) 303{ 304 // Presume an entity, and pull it out. 305 *length = 0; 306 307 if ( *(p+1) == '#' && *(p+2) ) 308 { 309 unsigned long ucs = 0; 310 ptrdiff_t delta = 0; 311 unsigned mult = 1; 312 313 if ( *(p+2) == 'x' ) 314 { 315 // Hexadecimal. 316 if ( !*(p+3) ) return 0; 317 318 const char* q = p+3; 319 q = strchr( q, ';' ); 320 321 if ( !q || !*q ) return 0; 322 323 delta = q-p; 324 --q; 325 326 while ( *q != 'x' ) 327 { 328 if ( *q >= '0' && *q <= '9' ) 329 ucs += mult * (*q - '0'); 330 else if ( *q >= 'a' && *q <= 'f' ) 331 ucs += mult * (*q - 'a' + 10); 332 else if ( *q >= 'A' && *q <= 'F' ) 333 ucs += mult * (*q - 'A' + 10 ); 334 else 335 return 0; 336 mult *= 16; 337 --q; 338 } 339 } 340 else 341 { 342 // Decimal. 343 if ( !*(p+2) ) return 0; 344 345 const char* q = p+2; 346 q = strchr( q, ';' ); 347 348 if ( !q || !*q ) return 0; 349 350 delta = q-p; 351 --q; 352 353 while ( *q != '#' ) 354 { 355 if ( *q >= '0' && *q <= '9' ) 356 ucs += mult * (*q - '0'); 357 else 358 return 0; 359 mult *= 10; 360 --q; 361 } 362 } 363 // convert the UCS to UTF-8 364 ConvertUTF32ToUTF8( ucs, value, length ); 365 return p + delta + 1; 366 } 367 return p+1; 368} 369 370 371void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) 372{ 373 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); 374} 375 376 377void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) 378{ 379 TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); 380} 381 382 383void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) 384{ 385 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); 386} 387 388 389void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) 390{ 391 TIXML_SNPRINTF( buffer, bufferSize, "%f", v ); 392} 393 394 395void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) 396{ 397 TIXML_SNPRINTF( buffer, bufferSize, "%f", v ); 398} 399 400 401bool XMLUtil::ToInt( const char* str, int* value ) 402{ 403 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) 404 return true; 405 return false; 406} 407 408bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) 409{ 410 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) 411 return true; 412 return false; 413} 414 415bool XMLUtil::ToBool( const char* str, bool* value ) 416{ 417 int ival = 0; 418 if ( ToInt( str, &ival )) { 419 *value = (ival==0) ? false : true; 420 return true; 421 } 422 if ( StringEqual( str, "true" ) ) { 423 *value = true; 424 return true; 425 } 426 else if ( StringEqual( str, "false" ) ) { 427 *value = false; 428 return true; 429 } 430 return false; 431} 432 433 434bool XMLUtil::ToFloat( const char* str, float* value ) 435{ 436 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { 437 return true; 438 } 439 return false; 440} 441 442bool XMLUtil::ToDouble( const char* str, double* value ) 443{ 444 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { 445 return true; 446 } 447 return false; 448} 449 450 451char* XMLDocument::Identify( char* p, XMLNode** node ) 452{ 453 XMLNode* returnNode = 0; 454 char* start = p; 455 p = XMLUtil::SkipWhiteSpace( p ); 456 if( !p || !*p ) 457 { 458 return p; 459 } 460 461 // What is this thing? 462 // - Elements start with a letter or underscore, but xml is reserved. 463 // - Comments: <!-- 464 // - Decleration: <? 465 // - Everthing else is unknown to tinyxml. 466 // 467 468 static const char* xmlHeader = { "<?" }; 469 static const char* commentHeader = { "<!--" }; 470 static const char* dtdHeader = { "<!" }; 471 static const char* cdataHeader = { "<![CDATA[" }; 472 static const char* elementHeader = { "<" }; // and a header for everything else; check last. 473 474 static const int xmlHeaderLen = 2; 475 static const int commentHeaderLen = 4; 476 static const int dtdHeaderLen = 2; 477 static const int cdataHeaderLen = 9; 478 static const int elementHeaderLen = 1; 479 480#if defined(_MSC_VER) 481#pragma warning ( push ) 482#pragma warning ( disable : 4127 ) 483#endif 484 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool 485 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool 486#if defined(_MSC_VER) 487#pragma warning (pop) 488#endif 489 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) { 490 returnNode = new (commentPool.Alloc()) XMLDeclaration( this ); 491 returnNode->memPool = &commentPool; 492 p += xmlHeaderLen; 493 } 494 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { 495 returnNode = new (commentPool.Alloc()) XMLComment( this ); 496 returnNode->memPool = &commentPool; 497 p += commentHeaderLen; 498 } 499 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { 500 XMLText* text = new (textPool.Alloc()) XMLText( this ); 501 returnNode = text; 502 returnNode->memPool = &textPool; 503 p += cdataHeaderLen; 504 text->SetCData( true ); 505 } 506 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { 507 returnNode = new (commentPool.Alloc()) XMLUnknown( this ); 508 returnNode->memPool = &commentPool; 509 p += dtdHeaderLen; 510 } 511 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { 512 returnNode = new (elementPool.Alloc()) XMLElement( this ); 513 returnNode->memPool = &elementPool; 514 p += elementHeaderLen; 515 } 516 else { 517 returnNode = new (textPool.Alloc()) XMLText( this ); 518 returnNode->memPool = &textPool; 519 p = start; // Back it up, all the text counts. 520 } 521 522 *node = returnNode; 523 return p; 524} 525 526 527bool XMLDocument::Accept( XMLVisitor* visitor ) const 528{ 529 if ( visitor->VisitEnter( *this ) ) 530 { 531 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) 532 { 533 if ( !node->Accept( visitor ) ) 534 break; 535 } 536 } 537 return visitor->VisitExit( *this ); 538} 539 540 541// --------- XMLNode ----------- // 542 543XMLNode::XMLNode( XMLDocument* doc ) : 544 document( doc ), 545 parent( 0 ), 546 firstChild( 0 ), lastChild( 0 ), 547 prev( 0 ), next( 0 ) 548{ 549} 550 551 552XMLNode::~XMLNode() 553{ 554 DeleteChildren(); 555 if ( parent ) { 556 parent->Unlink( this ); 557 } 558} 559 560 561void XMLNode::SetValue( const char* str, bool staticMem ) 562{ 563 if ( staticMem ) 564 value.SetInternedStr( str ); 565 else 566 value.SetStr( str ); 567} 568 569 570void XMLNode::DeleteChildren() 571{ 572 while( firstChild ) { 573 XMLNode* node = firstChild; 574 Unlink( node ); 575 576 DELETE_NODE( node ); 577 } 578 firstChild = lastChild = 0; 579} 580 581 582void XMLNode::Unlink( XMLNode* child ) 583{ 584 TIXMLASSERT( child->parent == this ); 585 if ( child == firstChild ) 586 firstChild = firstChild->next; 587 if ( child == lastChild ) 588 lastChild = lastChild->prev; 589 590 if ( child->prev ) { 591 child->prev->next = child->next; 592 } 593 if ( child->next ) { 594 child->next->prev = child->prev; 595 } 596 child->parent = 0; 597} 598 599 600void XMLNode::DeleteChild( XMLNode* node ) 601{ 602 TIXMLASSERT( node->parent == this ); 603 DELETE_NODE( node ); 604} 605 606 607XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) 608{ 609 if ( lastChild ) { 610 TIXMLASSERT( firstChild ); 611 TIXMLASSERT( lastChild->next == 0 ); 612 lastChild->next = addThis; 613 addThis->prev = lastChild; 614 lastChild = addThis; 615 616 addThis->next = 0; 617 } 618 else { 619 TIXMLASSERT( firstChild == 0 ); 620 firstChild = lastChild = addThis; 621 622 addThis->prev = 0; 623 addThis->next = 0; 624 } 625 addThis->parent = this; 626 return addThis; 627} 628 629 630XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) 631{ 632 if ( firstChild ) { 633 TIXMLASSERT( lastChild ); 634 TIXMLASSERT( firstChild->prev == 0 ); 635 636 firstChild->prev = addThis; 637 addThis->next = firstChild; 638 firstChild = addThis; 639 640 addThis->prev = 0; 641 } 642 else { 643 TIXMLASSERT( lastChild == 0 ); 644 firstChild = lastChild = addThis; 645 646 addThis->prev = 0; 647 addThis->next = 0; 648 } 649 addThis->parent = this; 650 return addThis; 651} 652 653 654XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) 655{ 656 TIXMLASSERT( afterThis->parent == this ); 657 if ( afterThis->parent != this ) 658 return 0; 659 660 if ( afterThis->next == 0 ) { 661 // The last node or the only node. 662 return InsertEndChild( addThis ); 663 } 664 addThis->prev = afterThis; 665 addThis->next = afterThis->next; 666 afterThis->next->prev = addThis; 667 afterThis->next = addThis; 668 addThis->parent = this; 669 return addThis; 670} 671 672 673 674 675const XMLElement* XMLNode::FirstChildElement( const char* value ) const 676{ 677 for( XMLNode* node=firstChild; node; node=node->next ) { 678 XMLElement* element = node->ToElement(); 679 if ( element ) { 680 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { 681 return element; 682 } 683 } 684 } 685 return 0; 686} 687 688 689const XMLElement* XMLNode::LastChildElement( const char* value ) const 690{ 691 for( XMLNode* node=lastChild; node; node=node->prev ) { 692 XMLElement* element = node->ToElement(); 693 if ( element ) { 694 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { 695 return element; 696 } 697 } 698 } 699 return 0; 700} 701 702 703const XMLElement* XMLNode::NextSiblingElement( const char* value ) const 704{ 705 for( XMLNode* element=this->next; element; element = element->next ) { 706 if ( element->ToElement() 707 && (!value || XMLUtil::StringEqual( value, element->Value() ))) 708 { 709 return element->ToElement(); 710 } 711 } 712 return 0; 713} 714 715 716const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const 717{ 718 for( XMLNode* element=this->prev; element; element = element->prev ) { 719 if ( element->ToElement() 720 && (!value || XMLUtil::StringEqual( value, element->Value() ))) 721 { 722 return element->ToElement(); 723 } 724 } 725 return 0; 726} 727 728 729char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) 730{ 731 // This is a recursive method, but thinking about it "at the current level" 732 // it is a pretty simple flat list: 733 // <foo/> 734 // <!-- comment --> 735 // 736 // With a special case: 737 // <foo> 738 // </foo> 739 // <!-- comment --> 740 // 741 // Where the closing element (/foo) *must* be the next thing after the opening 742 // element, and the names must match. BUT the tricky bit is that the closing 743 // element will be read by the child. 744 // 745 // 'endTag' is the end tag for this node, it is returned by a call to a child. 746 // 'parentEnd' is the end tag for the parent, which is filled in and returned. 747 748 while( p && *p ) { 749 XMLNode* node = 0; 750 751 p = document->Identify( p, &node ); 752 if ( p == 0 || node == 0 ) { 753 break; 754 } 755 756 StrPair endTag; 757 p = node->ParseDeep( p, &endTag ); 758 if ( !p ) { 759 DELETE_NODE( node ); 760 node = 0; 761 if ( !document->Error() ) { 762 document->SetError( XML_ERROR_PARSING, 0, 0 ); 763 } 764 break; 765 } 766 767 // We read the end tag. Return it to the parent. 768 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { 769 if ( parentEnd ) { 770 *parentEnd = static_cast<XMLElement*>(node)->value; 771 } 772 DELETE_NODE( node ); 773 return p; 774 } 775 776 // Handle an end tag returned to this level. 777 // And handle a bunch of annoying errors. 778 XMLElement* ele = node->ToElement(); 779 if ( ele ) { 780 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { 781 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); 782 p = 0; 783 } 784 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { 785 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); 786 p = 0; 787 } 788 else if ( !endTag.Empty() ) { 789 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { 790 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); 791 p = 0; 792 } 793 } 794 } 795 if ( p == 0 ) { 796 DELETE_NODE( node ); 797 node = 0; 798 } 799 if ( node ) { 800 this->InsertEndChild( node ); 801 } 802 } 803 return 0; 804} 805 806// --------- XMLText ---------- // 807char* XMLText::ParseDeep( char* p, StrPair* ) 808{ 809 const char* start = p; 810 if ( this->CData() ) { 811 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 812 if ( !p ) { 813 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); 814 } 815 return p; 816 } 817 else { 818 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES ); 819 if ( !p ) { 820 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); 821 } 822 if ( p && *p ) { 823 return p-1; 824 } 825 } 826 return 0; 827} 828 829 830XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const 831{ 832 if ( !doc ) { 833 doc = document; 834 } 835 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? 836 text->SetCData( this->CData() ); 837 return text; 838} 839 840 841bool XMLText::ShallowEqual( const XMLNode* compare ) const 842{ 843 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); 844} 845 846 847bool XMLText::Accept( XMLVisitor* visitor ) const 848{ 849 return visitor->Visit( *this ); 850} 851 852 853// --------- XMLComment ---------- // 854 855XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) 856{ 857} 858 859 860XMLComment::~XMLComment() 861{ 862 //printf( "~XMLComment\n" ); 863} 864 865 866char* XMLComment::ParseDeep( char* p, StrPair* ) 867{ 868 // Comment parses as text. 869 const char* start = p; 870 p = value.ParseText( p, "-->", StrPair::COMMENT ); 871 if ( p == 0 ) { 872 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); 873 } 874 return p; 875} 876 877 878XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const 879{ 880 if ( !doc ) { 881 doc = document; 882 } 883 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? 884 return comment; 885} 886 887 888bool XMLComment::ShallowEqual( const XMLNode* compare ) const 889{ 890 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); 891} 892 893 894bool XMLComment::Accept( XMLVisitor* visitor ) const 895{ 896 return visitor->Visit( *this ); 897} 898 899 900// --------- XMLDeclaration ---------- // 901 902XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) 903{ 904} 905 906 907XMLDeclaration::~XMLDeclaration() 908{ 909 //printf( "~XMLDeclaration\n" ); 910} 911 912 913char* XMLDeclaration::ParseDeep( char* p, StrPair* ) 914{ 915 // Declaration parses as text. 916 const char* start = p; 917 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 918 if ( p == 0 ) { 919 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); 920 } 921 return p; 922} 923 924 925XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const 926{ 927 if ( !doc ) { 928 doc = document; 929 } 930 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? 931 return dec; 932} 933 934 935bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const 936{ 937 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); 938} 939 940 941 942bool XMLDeclaration::Accept( XMLVisitor* visitor ) const 943{ 944 return visitor->Visit( *this ); 945} 946 947// --------- XMLUnknown ---------- // 948 949XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) 950{ 951} 952 953 954XMLUnknown::~XMLUnknown() 955{ 956} 957 958 959char* XMLUnknown::ParseDeep( char* p, StrPair* ) 960{ 961 // Unknown parses as text. 962 const char* start = p; 963 964 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); 965 if ( !p ) { 966 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); 967 } 968 return p; 969} 970 971 972XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const 973{ 974 if ( !doc ) { 975 doc = document; 976 } 977 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? 978 return text; 979} 980 981 982bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const 983{ 984 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); 985} 986 987 988bool XMLUnknown::Accept( XMLVisitor* visitor ) const 989{ 990 return visitor->Visit( *this ); 991} 992 993// --------- XMLAttribute ---------- // 994char* XMLAttribute::ParseDeep( char* p, bool processEntities ) 995{ 996 // Parse using the name rules: bug fix, was using ParseText before 997 p = name.ParseName( p ); 998 if ( !p || !*p ) return 0; 999 1000 // Skip white space before = 1001 p = XMLUtil::SkipWhiteSpace( p ); 1002 if ( !p || *p != '=' ) return 0; 1003 1004 ++p; // move up to opening quote 1005 p = XMLUtil::SkipWhiteSpace( p ); 1006 if ( *p != '\"' && *p != '\'' ) return 0; 1007 1008 char endTag[2] = { *p, 0 }; 1009 ++p; // move past opening quote 1010 1011 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); 1012 return p; 1013} 1014 1015 1016void XMLAttribute::SetName( const char* n ) 1017{ 1018 name.SetStr( n ); 1019} 1020 1021 1022int XMLAttribute::QueryIntValue( int* value ) const 1023{ 1024 if ( XMLUtil::ToInt( Value(), value )) 1025 return XML_NO_ERROR; 1026 return XML_WRONG_ATTRIBUTE_TYPE; 1027} 1028 1029 1030int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const 1031{ 1032 if ( XMLUtil::ToUnsigned( Value(), value )) 1033 return XML_NO_ERROR; 1034 return XML_WRONG_ATTRIBUTE_TYPE; 1035} 1036 1037 1038int XMLAttribute::QueryBoolValue( bool* value ) const 1039{ 1040 if ( XMLUtil::ToBool( Value(), value )) { 1041 return XML_NO_ERROR; 1042 } 1043 return XML_WRONG_ATTRIBUTE_TYPE; 1044} 1045 1046 1047int XMLAttribute::QueryFloatValue( float* value ) const 1048{ 1049 if ( XMLUtil::ToFloat( Value(), value )) 1050 return XML_NO_ERROR; 1051 return XML_WRONG_ATTRIBUTE_TYPE; 1052} 1053 1054 1055int XMLAttribute::QueryDoubleValue( double* value ) const 1056{ 1057 if ( XMLUtil::ToDouble( Value(), value )) 1058 return XML_NO_ERROR; 1059 return XML_WRONG_ATTRIBUTE_TYPE; 1060} 1061 1062 1063void XMLAttribute::SetAttribute( const char* v ) 1064{ 1065 value.SetStr( v ); 1066} 1067 1068 1069void XMLAttribute::SetAttribute( int v ) 1070{ 1071 char buf[BUF_SIZE]; 1072 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1073 value.SetStr( buf ); 1074} 1075 1076 1077void XMLAttribute::SetAttribute( unsigned v ) 1078{ 1079 char buf[BUF_SIZE]; 1080 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1081 value.SetStr( buf ); 1082} 1083 1084 1085void XMLAttribute::SetAttribute( bool v ) 1086{ 1087 char buf[BUF_SIZE]; 1088 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1089 value.SetStr( buf ); 1090} 1091 1092void XMLAttribute::SetAttribute( double v ) 1093{ 1094 char buf[BUF_SIZE]; 1095 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1096 value.SetStr( buf ); 1097} 1098 1099void XMLAttribute::SetAttribute( float v ) 1100{ 1101 char buf[BUF_SIZE]; 1102 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1103 value.SetStr( buf ); 1104} 1105 1106 1107// --------- XMLElement ---------- // 1108XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), 1109 closingType( 0 ), 1110 rootAttribute( 0 ) 1111{ 1112} 1113 1114 1115XMLElement::~XMLElement() 1116{ 1117 while( rootAttribute ) { 1118 XMLAttribute* next = rootAttribute->next; 1119 DELETE_ATTRIBUTE( rootAttribute ); 1120 rootAttribute = next; 1121 } 1122} 1123 1124 1125XMLAttribute* XMLElement::FindAttribute( const char* name ) 1126{ 1127 XMLAttribute* a = 0; 1128 for( a=rootAttribute; a; a = a->next ) { 1129 if ( XMLUtil::StringEqual( a->Name(), name ) ) 1130 return a; 1131 } 1132 return 0; 1133} 1134 1135 1136const XMLAttribute* XMLElement::FindAttribute( const char* name ) const 1137{ 1138 XMLAttribute* a = 0; 1139 for( a=rootAttribute; a; a = a->next ) { 1140 if ( XMLUtil::StringEqual( a->Name(), name ) ) 1141 return a; 1142 } 1143 return 0; 1144} 1145 1146 1147const char* XMLElement::Attribute( const char* name, const char* value ) const 1148{ 1149 const XMLAttribute* a = FindAttribute( name ); 1150 if ( !a ) 1151 return 0; 1152 if ( !value || XMLUtil::StringEqual( a->Value(), value )) 1153 return a->Value(); 1154 return 0; 1155} 1156 1157 1158const char* XMLElement::GetText() const 1159{ 1160 if ( FirstChild() && FirstChild()->ToText() ) { 1161 return FirstChild()->ToText()->Value(); 1162 } 1163 return 0; 1164} 1165 1166 1167int XMLElement::QueryIntText( int* _value ) const 1168{ 1169 if ( FirstChild() && FirstChild()->ToText() ) { 1170 const char* t = FirstChild()->ToText()->Value(); 1171 if ( XMLUtil::ToInt( t, _value ) ) { 1172 return XML_SUCCESS; 1173 } 1174 return XML_CAN_NOT_CONVERT_TEXT; 1175 } 1176 return XML_NO_TEXT_NODE; 1177} 1178 1179 1180int XMLElement::QueryUnsignedText( unsigned* _value ) const 1181{ 1182 if ( FirstChild() && FirstChild()->ToText() ) { 1183 const char* t = FirstChild()->ToText()->Value(); 1184 if ( XMLUtil::ToUnsigned( t, _value ) ) { 1185 return XML_SUCCESS; 1186 } 1187 return XML_CAN_NOT_CONVERT_TEXT; 1188 } 1189 return XML_NO_TEXT_NODE; 1190} 1191 1192 1193int XMLElement::QueryBoolText( bool* _value ) const 1194{ 1195 if ( FirstChild() && FirstChild()->ToText() ) { 1196 const char* t = FirstChild()->ToText()->Value(); 1197 if ( XMLUtil::ToBool( t, _value ) ) { 1198 return XML_SUCCESS; 1199 } 1200 return XML_CAN_NOT_CONVERT_TEXT; 1201 } 1202 return XML_NO_TEXT_NODE; 1203} 1204 1205 1206int XMLElement::QueryDoubleText( double* _value ) const 1207{ 1208 if ( FirstChild() && FirstChild()->ToText() ) { 1209 const char* t = FirstChild()->ToText()->Value(); 1210 if ( XMLUtil::ToDouble( t, _value ) ) { 1211 return XML_SUCCESS; 1212 } 1213 return XML_CAN_NOT_CONVERT_TEXT; 1214 } 1215 return XML_NO_TEXT_NODE; 1216} 1217 1218 1219int XMLElement::QueryFloatText( float* _value ) const 1220{ 1221 if ( FirstChild() && FirstChild()->ToText() ) { 1222 const char* t = FirstChild()->ToText()->Value(); 1223 if ( XMLUtil::ToFloat( t, _value ) ) { 1224 return XML_SUCCESS; 1225 } 1226 return XML_CAN_NOT_CONVERT_TEXT; 1227 } 1228 return XML_NO_TEXT_NODE; 1229} 1230 1231 1232 1233XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) 1234{ 1235 XMLAttribute* last = 0; 1236 XMLAttribute* attrib = 0; 1237 for( attrib = rootAttribute; 1238 attrib; 1239 last = attrib, attrib = attrib->next ) 1240 { 1241 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { 1242 break; 1243 } 1244 } 1245 if ( !attrib ) { 1246 attrib = new (document->attributePool.Alloc() ) XMLAttribute(); 1247 attrib->memPool = &document->attributePool; 1248 if ( last ) { 1249 last->next = attrib; 1250 } 1251 else { 1252 rootAttribute = attrib; 1253 } 1254 attrib->SetName( name ); 1255 } 1256 return attrib; 1257} 1258 1259 1260void XMLElement::DeleteAttribute( const char* name ) 1261{ 1262 XMLAttribute* prev = 0; 1263 for( XMLAttribute* a=rootAttribute; a; a=a->next ) { 1264 if ( XMLUtil::StringEqual( name, a->Name() ) ) { 1265 if ( prev ) { 1266 prev->next = a->next; 1267 } 1268 else { 1269 rootAttribute = a->next; 1270 } 1271 DELETE_ATTRIBUTE( a ); 1272 break; 1273 } 1274 prev = a; 1275 } 1276} 1277 1278 1279char* XMLElement::ParseAttributes( char* p ) 1280{ 1281 const char* start = p; 1282 XMLAttribute* prevAttribute = 0; 1283 1284 // Read the attributes. 1285 while( p ) { 1286 p = XMLUtil::SkipWhiteSpace( p ); 1287 if ( !p || !(*p) ) { 1288 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); 1289 return 0; 1290 } 1291 1292 // attribute. 1293 if ( XMLUtil::IsAlpha( *p ) ) { 1294 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute(); 1295 attrib->memPool = &document->attributePool; 1296 1297 p = attrib->ParseDeep( p, document->ProcessEntities() ); 1298 if ( !p || Attribute( attrib->Name() ) ) { 1299 DELETE_ATTRIBUTE( attrib ); 1300 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); 1301 return 0; 1302 } 1303 // There is a minor bug here: if the attribute in the source xml 1304 // document is duplicated, it will not be detected and the 1305 // attribute will be doubly added. However, tracking the 'prevAttribute' 1306 // avoids re-scanning the attribute list. Preferring performance for 1307 // now, may reconsider in the future. 1308 if ( prevAttribute ) { 1309 prevAttribute->next = attrib; 1310 } 1311 else { 1312 rootAttribute = attrib; 1313 } 1314 prevAttribute = attrib; 1315 } 1316 // end of the tag 1317 else if ( *p == '/' && *(p+1) == '>' ) { 1318 closingType = CLOSED; 1319 return p+2; // done; sealed element. 1320 } 1321 // end of the tag 1322 else if ( *p == '>' ) { 1323 ++p; 1324 break; 1325 } 1326 else { 1327 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); 1328 return 0; 1329 } 1330 } 1331 return p; 1332} 1333 1334 1335// 1336// <ele></ele> 1337// <ele>foo<b>bar</b></ele> 1338// 1339char* XMLElement::ParseDeep( char* p, StrPair* strPair ) 1340{ 1341 // Read the element name. 1342 p = XMLUtil::SkipWhiteSpace( p ); 1343 if ( !p ) return 0; 1344 1345 // The closing element is the </element> form. It is 1346 // parsed just like a regular element then deleted from 1347 // the DOM. 1348 if ( *p == '/' ) { 1349 closingType = CLOSING; 1350 ++p; 1351 } 1352 1353 p = value.ParseName( p ); 1354 if ( value.Empty() ) return 0; 1355 1356 p = ParseAttributes( p ); 1357 if ( !p || !*p || closingType ) 1358 return p; 1359 1360 p = XMLNode::ParseDeep( p, strPair ); 1361 return p; 1362} 1363 1364 1365 1366XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const 1367{ 1368 if ( !doc ) { 1369 doc = document; 1370 } 1371 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? 1372 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { 1373 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? 1374 } 1375 return element; 1376} 1377 1378 1379bool XMLElement::ShallowEqual( const XMLNode* compare ) const 1380{ 1381 const XMLElement* other = compare->ToElement(); 1382 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { 1383 1384 const XMLAttribute* a=FirstAttribute(); 1385 const XMLAttribute* b=other->FirstAttribute(); 1386 1387 while ( a && b ) { 1388 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { 1389 return false; 1390 } 1391 a = a->Next(); 1392 b = b->Next(); 1393 } 1394 if ( a || b ) { 1395 // different count 1396 return false; 1397 } 1398 return true; 1399 } 1400 return false; 1401} 1402 1403 1404bool XMLElement::Accept( XMLVisitor* visitor ) const 1405{ 1406 if ( visitor->VisitEnter( *this, rootAttribute ) ) 1407 { 1408 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) 1409 { 1410 if ( !node->Accept( visitor ) ) 1411 break; 1412 } 1413 } 1414 return visitor->VisitExit( *this ); 1415} 1416 1417 1418// --------- XMLDocument ----------- // 1419XMLDocument::XMLDocument( bool _processEntities ) : 1420 XMLNode( 0 ), 1421 writeBOM( false ), 1422 processEntities( _processEntities ), 1423 errorID( 0 ), 1424 errorStr1( 0 ), 1425 errorStr2( 0 ), 1426 charBuffer( 0 ) 1427{ 1428 document = this; // avoid warning about 'this' in initializer list 1429} 1430 1431 1432XMLDocument::~XMLDocument() 1433{ 1434 DeleteChildren(); 1435 delete [] charBuffer; 1436 1437#if 0 1438 textPool.Trace( "text" ); 1439 elementPool.Trace( "element" ); 1440 commentPool.Trace( "comment" ); 1441 attributePool.Trace( "attribute" ); 1442#endif 1443 1444 TIXMLASSERT( textPool.CurrentAllocs() == 0 ); 1445 TIXMLASSERT( elementPool.CurrentAllocs() == 0 ); 1446 TIXMLASSERT( commentPool.CurrentAllocs() == 0 ); 1447 TIXMLASSERT( attributePool.CurrentAllocs() == 0 ); 1448} 1449 1450 1451void XMLDocument::InitDocument() 1452{ 1453 errorID = XML_NO_ERROR; 1454 errorStr1 = 0; 1455 errorStr2 = 0; 1456 1457 delete [] charBuffer; 1458 charBuffer = 0; 1459 1460} 1461 1462 1463XMLElement* XMLDocument::NewElement( const char* name ) 1464{ 1465 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this ); 1466 ele->memPool = &elementPool; 1467 ele->SetName( name ); 1468 return ele; 1469} 1470 1471 1472XMLComment* XMLDocument::NewComment( const char* str ) 1473{ 1474 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this ); 1475 comment->memPool = &commentPool; 1476 comment->SetValue( str ); 1477 return comment; 1478} 1479 1480 1481XMLText* XMLDocument::NewText( const char* str ) 1482{ 1483 XMLText* text = new (textPool.Alloc()) XMLText( this ); 1484 text->memPool = &textPool; 1485 text->SetValue( str ); 1486 return text; 1487} 1488 1489 1490XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) 1491{ 1492 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this ); 1493 dec->memPool = &commentPool; 1494 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); 1495 return dec; 1496} 1497 1498 1499XMLUnknown* XMLDocument::NewUnknown( const char* str ) 1500{ 1501 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this ); 1502 unk->memPool = &commentPool; 1503 unk->SetValue( str ); 1504 return unk; 1505} 1506 1507 1508int XMLDocument::LoadFile( const char* filename ) 1509{ 1510 DeleteChildren(); 1511 InitDocument(); 1512 1513#if defined(_MSC_VER) 1514#pragma warning ( push ) 1515#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated. 1516#endif 1517 FILE* fp = fopen( filename, "rb" ); 1518#if defined(_MSC_VER) 1519#pragma warning ( pop ) 1520#endif 1521 if ( !fp ) { 1522 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); 1523 return errorID; 1524 } 1525 LoadFile( fp ); 1526 fclose( fp ); 1527 return errorID; 1528} 1529 1530 1531int XMLDocument::LoadFile( FILE* fp ) 1532{ 1533 DeleteChildren(); 1534 InitDocument(); 1535 1536 fseek( fp, 0, SEEK_END ); 1537 unsigned size = ftell( fp ); 1538 fseek( fp, 0, SEEK_SET ); 1539 1540 if ( size == 0 ) { 1541 return errorID; 1542 } 1543 1544 charBuffer = new char[size+1]; 1545 size_t read = fread( charBuffer, 1, size, fp ); 1546 if ( read != size ) { 1547 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); 1548 return errorID; 1549 } 1550 1551 charBuffer[size] = 0; 1552 1553 const char* p = charBuffer; 1554 p = XMLUtil::SkipWhiteSpace( p ); 1555 p = XMLUtil::ReadBOM( p, &writeBOM ); 1556 if ( !p || !*p ) { 1557 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 1558 return errorID; 1559 } 1560 1561 ParseDeep( charBuffer + (p-charBuffer), 0 ); 1562 return errorID; 1563} 1564 1565 1566int XMLDocument::SaveFile( const char* filename ) 1567{ 1568#if defined(_MSC_VER) 1569#pragma warning ( push ) 1570#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated. 1571#endif 1572 int fd = open(filename, O_RDWR|O_CREAT, 0644); 1573 FILE* fp = fdopen(fd, "w"); 1574 //FILE* fp = fopen( filename, "w" ); 1575#if defined(_MSC_VER) 1576#pragma warning ( pop ) 1577#endif 1578 if ( !fp ) { 1579 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); 1580 return errorID; 1581 } 1582 SaveFile(fp); 1583 fclose( fp ); 1584 return errorID; 1585} 1586 1587 1588int XMLDocument::SaveFile( FILE* fp ) 1589{ 1590 XMLPrinter stream( fp ); 1591 Print( &stream ); 1592 return errorID; 1593} 1594 1595 1596int XMLDocument::Parse( const char* p ) 1597{ 1598 DeleteChildren(); 1599 InitDocument(); 1600 1601 if ( !p || !*p ) { 1602 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 1603 return errorID; 1604 } 1605 p = XMLUtil::SkipWhiteSpace( p ); 1606 p = XMLUtil::ReadBOM( p, &writeBOM ); 1607 if ( !p || !*p ) { 1608 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); 1609 return errorID; 1610 } 1611 1612 size_t len = strlen( p ); 1613 charBuffer = new char[ len+1 ]; 1614 memcpy( charBuffer, p, len+1 ); 1615 1616 1617 ParseDeep( charBuffer, 0 ); 1618 return errorID; 1619} 1620 1621 1622void XMLDocument::Print( XMLPrinter* streamer ) 1623{ 1624 XMLPrinter stdStreamer( stdout ); 1625 if ( !streamer ) 1626 streamer = &stdStreamer; 1627 Accept( streamer ); 1628} 1629 1630 1631void XMLDocument::SetError( int error, const char* str1, const char* str2 ) 1632{ 1633 errorID = error; 1634 errorStr1 = str1; 1635 errorStr2 = str2; 1636} 1637 1638 1639void XMLDocument::PrintError() const 1640{ 1641 if ( errorID ) { 1642 static const int LEN = 20; 1643 char buf1[LEN] = { 0 }; 1644 char buf2[LEN] = { 0 }; 1645 1646 if ( errorStr1 ) { 1647 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 ); 1648 } 1649 if ( errorStr2 ) { 1650 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 ); 1651 } 1652 1653 printf( "XMLDocument error id=%d str1=%s str2=%s\n", 1654 errorID, buf1, buf2 ); 1655 } 1656} 1657 1658 1659XMLPrinter::XMLPrinter( FILE* file, bool compact ) : 1660 elementJustOpened( false ), 1661 firstElement( true ), 1662 fp( file ), 1663 depth( 0 ), 1664 textDepth( -1 ), 1665 processEntities( true ), 1666 compactMode( compact ) 1667{ 1668 for( int i=0; i<ENTITY_RANGE; ++i ) { 1669 entityFlag[i] = false; 1670 restrictedEntityFlag[i] = false; 1671 } 1672 for( int i=0; i<NUM_ENTITIES; ++i ) { 1673 TIXMLASSERT( entities[i].value < ENTITY_RANGE ); 1674 if ( entities[i].value < ENTITY_RANGE ) { 1675 entityFlag[ (int)entities[i].value ] = true; 1676 } 1677 } 1678 restrictedEntityFlag[(int)'&'] = true; 1679 restrictedEntityFlag[(int)'<'] = true; 1680 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice 1681 buffer.Push( 0 ); 1682} 1683 1684 1685void XMLPrinter::Print( const char* format, ... ) 1686{ 1687 va_list va; 1688 va_start( va, format ); 1689 1690 if ( fp ) { 1691 vfprintf( fp, format, va ); 1692 } 1693 else { 1694 // This seems brutally complex. Haven't figured out a better 1695 // way on windows. 1696 #ifdef _MSC_VER 1697 int len = -1; 1698 int expand = 1000; 1699 while ( len < 0 ) { 1700 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va ); 1701 if ( len < 0 ) { 1702 expand *= 3/2; 1703 accumulator.PushArr( expand ); 1704 } 1705 } 1706 char* p = buffer.PushArr( len ) - 1; 1707 memcpy( p, accumulator.Mem(), len+1 ); 1708 #else 1709 int len = vsnprintf( 0, 0, format, va ); 1710 // Close out and re-start the va-args 1711 va_end( va ); 1712 va_start( va, format ); 1713 char* p = buffer.PushArr( len ) - 1; 1714 vsnprintf( p, len+1, format, va ); 1715 #endif 1716 } 1717 va_end( va ); 1718} 1719 1720 1721void XMLPrinter::PrintSpace( int depth ) 1722{ 1723 for( int i=0; i<depth; ++i ) { 1724 Print( " " ); 1725 } 1726} 1727 1728 1729void XMLPrinter::PrintString( const char* p, bool restricted ) 1730{ 1731 // Look for runs of bytes between entities to print. 1732 const char* q = p; 1733 const bool* flag = restricted ? restrictedEntityFlag : entityFlag; 1734 1735 if ( processEntities ) { 1736 while ( *q ) { 1737 // Remember, char is sometimes signed. (How many times has that bitten me?) 1738 if ( *q > 0 && *q < ENTITY_RANGE ) { 1739 // Check for entities. If one is found, flush 1740 // the stream up until the entity, write the 1741 // entity, and keep looking. 1742 if ( flag[(unsigned)(*q)] ) { 1743 while ( p < q ) { 1744 Print( "%c", *p ); 1745 ++p; 1746 } 1747 for( int i=0; i<NUM_ENTITIES; ++i ) { 1748 if ( entities[i].value == *q ) { 1749 Print( "&%s;", entities[i].pattern ); 1750 break; 1751 } 1752 } 1753 ++p; 1754 } 1755 } 1756 ++q; 1757 } 1758 } 1759 // Flush the remaining string. This will be the entire 1760 // string if an entity wasn't found. 1761 if ( !processEntities || (q-p > 0) ) { 1762 Print( "%s", p ); 1763 } 1764} 1765 1766 1767void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) 1768{ 1769 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; 1770 if ( writeBOM ) { 1771 Print( "%s", bom ); 1772 } 1773 if ( writeDec ) { 1774 PushDeclaration( "xml version=\"1.0\"" ); 1775 } 1776} 1777 1778 1779void XMLPrinter::OpenElement( const char* name ) 1780{ 1781 if ( elementJustOpened ) { 1782 SealElement(); 1783 } 1784 stack.Push( name ); 1785 1786 if ( textDepth < 0 && !firstElement && !compactMode ) { 1787 Print( "\n" ); 1788 PrintSpace( depth ); 1789 } 1790 1791 Print( "<%s", name ); 1792 elementJustOpened = true; 1793 firstElement = false; 1794 ++depth; 1795} 1796 1797 1798void XMLPrinter::PushAttribute( const char* name, const char* value ) 1799{ 1800 TIXMLASSERT( elementJustOpened ); 1801 Print( " %s=\"", name ); 1802 PrintString( value, false ); 1803 Print( "\"" ); 1804} 1805 1806 1807void XMLPrinter::PushAttribute( const char* name, int v ) 1808{ 1809 char buf[BUF_SIZE]; 1810 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1811 PushAttribute( name, buf ); 1812} 1813 1814 1815void XMLPrinter::PushAttribute( const char* name, unsigned v ) 1816{ 1817 char buf[BUF_SIZE]; 1818 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1819 PushAttribute( name, buf ); 1820} 1821 1822 1823void XMLPrinter::PushAttribute( const char* name, bool v ) 1824{ 1825 char buf[BUF_SIZE]; 1826 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1827 PushAttribute( name, buf ); 1828} 1829 1830 1831void XMLPrinter::PushAttribute( const char* name, double v ) 1832{ 1833 char buf[BUF_SIZE]; 1834 XMLUtil::ToStr( v, buf, BUF_SIZE ); 1835 PushAttribute( name, buf ); 1836} 1837 1838 1839void XMLPrinter::CloseElement() 1840{ 1841 --depth; 1842 const char* name = stack.Pop(); 1843 1844 if ( elementJustOpened ) { 1845 Print( "/>" ); 1846 } 1847 else { 1848 if ( textDepth < 0 && !compactMode) { 1849 Print( "\n" ); 1850 PrintSpace( depth ); 1851 } 1852 Print( "</%s>", name ); 1853 } 1854 1855 if ( textDepth == depth ) 1856 textDepth = -1; 1857 if ( depth == 0 && !compactMode) 1858 Print( "\n" ); 1859 elementJustOpened = false; 1860} 1861 1862 1863void XMLPrinter::SealElement() 1864{ 1865 elementJustOpened = false; 1866 Print( ">" ); 1867} 1868 1869 1870void XMLPrinter::PushText( const char* text, bool cdata ) 1871{ 1872 textDepth = depth-1; 1873 1874 if ( elementJustOpened ) { 1875 SealElement(); 1876 } 1877 if ( cdata ) { 1878 Print( "<![CDATA[" ); 1879 Print( "%s", text ); 1880 Print( "]]>" ); 1881 } 1882 else { 1883 PrintString( text, true ); 1884 } 1885} 1886 1887void XMLPrinter::PushText( int value ) 1888{ 1889 char buf[BUF_SIZE]; 1890 XMLUtil::ToStr( value, buf, BUF_SIZE ); 1891 PushText( buf, false ); 1892} 1893 1894 1895void XMLPrinter::PushText( unsigned value ) 1896{ 1897 char buf[BUF_SIZE]; 1898 XMLUtil::ToStr( value, buf, BUF_SIZE ); 1899 PushText( buf, false ); 1900} 1901 1902 1903void XMLPrinter::PushText( bool value ) 1904{ 1905 char buf[BUF_SIZE]; 1906 XMLUtil::ToStr( value, buf, BUF_SIZE ); 1907 PushText( buf, false ); 1908} 1909 1910 1911void XMLPrinter::PushText( float value ) 1912{ 1913 char buf[BUF_SIZE]; 1914 XMLUtil::ToStr( value, buf, BUF_SIZE ); 1915 PushText( buf, false ); 1916} 1917 1918 1919void XMLPrinter::PushText( double value ) 1920{ 1921 char buf[BUF_SIZE]; 1922 XMLUtil::ToStr( value, buf, BUF_SIZE ); 1923 PushText( buf, false ); 1924} 1925 1926 1927void XMLPrinter::PushComment( const char* comment ) 1928{ 1929 if ( elementJustOpened ) { 1930 SealElement(); 1931 } 1932 if ( textDepth < 0 && !firstElement && !compactMode) { 1933 Print( "\n" ); 1934 PrintSpace( depth ); 1935 } 1936 firstElement = false; 1937 Print( "<!--%s-->", comment ); 1938} 1939 1940 1941void XMLPrinter::PushDeclaration( const char* value ) 1942{ 1943 if ( elementJustOpened ) { 1944 SealElement(); 1945 } 1946 if ( textDepth < 0 && !firstElement && !compactMode) { 1947 Print( "\n" ); 1948 PrintSpace( depth ); 1949 } 1950 firstElement = false; 1951 Print( "<?%s?>", value ); 1952} 1953 1954 1955void XMLPrinter::PushUnknown( const char* value ) 1956{ 1957 if ( elementJustOpened ) { 1958 SealElement(); 1959 } 1960 if ( textDepth < 0 && !firstElement && !compactMode) { 1961 Print( "\n" ); 1962 PrintSpace( depth ); 1963 } 1964 firstElement = false; 1965 Print( "<!%s>", value ); 1966} 1967 1968 1969bool XMLPrinter::VisitEnter( const XMLDocument& doc ) 1970{ 1971 processEntities = doc.ProcessEntities(); 1972 if ( doc.HasBOM() ) { 1973 PushHeader( true, false ); 1974 } 1975 return true; 1976} 1977 1978 1979bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) 1980{ 1981 OpenElement( element.Name() ); 1982 while ( attribute ) { 1983 PushAttribute( attribute->Name(), attribute->Value() ); 1984 attribute = attribute->Next(); 1985 } 1986 return true; 1987} 1988 1989 1990bool XMLPrinter::VisitExit( const XMLElement& ) 1991{ 1992 CloseElement(); 1993 return true; 1994} 1995 1996 1997bool XMLPrinter::Visit( const XMLText& text ) 1998{ 1999 PushText( text.Value(), text.CData() ); 2000 return true; 2001} 2002 2003 2004bool XMLPrinter::Visit( const XMLComment& comment ) 2005{ 2006 PushComment( comment.Value() ); 2007 return true; 2008} 2009 2010bool XMLPrinter::Visit( const XMLDeclaration& declaration ) 2011{ 2012 PushDeclaration( declaration.Value() ); 2013 return true; 2014} 2015 2016 2017bool XMLPrinter::Visit( const XMLUnknown& unknown ) 2018{ 2019 PushUnknown( unknown.Value() ); 2020 return true; 2021} 2022